plugins: reworked the framework using inheritance + added active_filters
authorJordan Augé <jordan.auge@lip6.fr>
Thu, 8 Aug 2013 10:17:25 +0000 (12:17 +0200)
committerJordan Augé <jordan.auge@lip6.fr>
Thu, 8 Aug 2013 10:17:25 +0000 (12:17 +0200)
16 files changed:
manifold/js/class.js [new file with mode: 0644]
manifold/js/manifold.js
manifold/js/plugin-sample.html [new file with mode: 0644]
manifold/js/plugin.js [new file with mode: 0644]
manifold/util/predicate.py
plugins/active_filters/__init__.py [new file with mode: 0644]
plugins/active_filters/active_filters.css [new file with mode: 0644]
plugins/active_filters/active_filters.html [new file with mode: 0644]
plugins/active_filters/active_filters.js [new file with mode: 0644]
plugins/query_editor/query_editor.js
plugins/resources_selected/resources_selected.js
trash/sliceview.py
unfold/js/plugin_helper.js [moved from unfold/js/plugin.js with 71% similarity]
views/templates/base.html [new file with mode: 0644]
views/templates/layout-unfold1.html
views/templates/layout-unfold2.html

diff --git a/manifold/js/class.js b/manifold/js/class.js
new file mode 100644 (file)
index 0000000..63b570c
--- /dev/null
@@ -0,0 +1,64 @@
+/* Simple JavaScript Inheritance
+ * By John Resig http://ejohn.org/
+ * MIT Licensed.
+ */
+// Inspired by base2 and Prototype
+(function(){
+  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
+  // The base Class implementation (does nothing)
+  this.Class = function(){};
+  // Create a new Class that inherits from this class
+  Class.extend = function(prop) {
+    var _super = this.prototype;
+   
+    // Instantiate a base class (but only create the instance,
+    // don't run the init constructor)
+    initializing = true;
+    var prototype = new this();
+    initializing = false;
+   
+    // Copy the properties over onto the new prototype
+    for (var name in prop) {
+      // Check if we're overwriting an existing function
+      prototype[name] = typeof prop[name] == "function" &&
+        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
+        (function(name, fn){
+          return function() {
+            var tmp = this._super;
+           
+            // Add a new ._super() method that is the same method
+            // but on the super-class
+            this._super = _super[name];
+           
+            // The method only need to be bound temporarily, so we
+            // remove it when we're done executing
+            var ret = fn.apply(this, arguments);        
+            this._super = tmp;
+           
+            return ret;
+          };
+        })(name, prop[name]) :
+        prop[name];
+    }
+   
+    // The dummy class constructor
+    function Class() {
+      // All construction is actually done in the init method
+      if ( !initializing && this.init )
+        this.init.apply(this, arguments);
+    }
+   
+    // Populate our constructed prototype object
+    Class.prototype = prototype;
+   
+    // Enforce the constructor to be what we expect
+    Class.prototype.constructor = Class;
+    // And make this class extendable
+    Class.extend = arguments.callee;
+   
+    return Class;
+  };
+})();
index ef492f7..01c5531 100644 (file)
@@ -440,12 +440,6 @@ var manifold = {
                 // XXX When is an update query associated ?
                 // XXX main_update_query.select(value);
 
-                // We need to inform about changes in these queries to the respective plugins
-                // Note: query, main_query & update_query have the same UUID
-                manifold.raise_query_event(query_uuid, event_type, value);
-                // We are targeting the same object with get and update
-                // The notion of query is bad, we should have a notion of destination, and issue queries on the destination
-                // NOTE: Editing a subquery == editing a local view on the destination
                 break;
 
             case FIELD_REMOVED:
@@ -460,6 +454,13 @@ var manifold = {
                 manifold.raise_query_event(query_uuid, event_type, value);
                 break;
         }
+        // We need to inform about changes in these queries to the respective plugins
+        // Note: query, main_query & update_query have the same UUID
+        manifold.raise_query_event(query_uuid, event_type, value);
+        // We are targeting the same object with get and update
+        // The notion of query is bad, we should have a notion of destination, and issue queries on the destination
+        // NOTE: Editing a subquery == editing a local view on the destination
+
         // XXX We might need to run the new query again and manage the plugins in the meantime with spinners...
         // For the time being, we will collect all columns during the first query
     },
diff --git a/manifold/js/plugin-sample.html b/manifold/js/plugin-sample.html
new file mode 100644 (file)
index 0000000..975270f
--- /dev/null
@@ -0,0 +1,103 @@
+<!doctype html>
+<html>
+<head>
+       <meta charset="utf-8">
+       <title>Extensible jQuery</title>
+       
+       <script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
+       <script type="text/javascript" src="simple-inheritance.js"></script>
+       <script type="text/javascript">
+       $.plugin = function(name, object) {
+               $.fn[name] = function(options) {
+                       var args = Array.prototype.slice.call(arguments, 1);
+                       return this.each(function() {
+                               var instance = $.data(this, name);
+                               if (instance) {
+                                       instance[options].apply(instance, args);
+                               } else {
+                                       instance = $.data(this, name, new object(options, this));
+                               }
+                       });
+               };
+       };
+       
+       var Stepper = Class.extend({
+               init: function(options, element) {
+                       this.options = $.extend({
+                               value: 0,
+                               stepSize: 1
+                       }, options);
+                       this.element = $(element);
+                       this.display();
+               },
+               stepUp: function(steps) {
+                       this.options.value += this.options.stepSize * steps;
+                       this.display();
+               },
+               stepDown: function(steps) {
+                       this.options.value -= this.options.stepSize * steps;
+                       this.display();
+               },
+               value: function() {
+                       return this.options.value;
+               },
+               display: function() {
+                       this.element.html(this.options.value);
+               }
+       });
+       
+       var Pager = Stepper.extend({
+               init: function(options, element) {
+                       this._super(options, element);
+                       this.options.pageSize = this.options.pageSize || 10;
+               },
+               pageUp: function() {
+                       this.options.value += this.options.pageSize * this.options.stepSize;
+                       this.display();
+               },
+               pageDown: function() {
+                       this.options.value -= this.options.pageSize * this.options.stepSize;
+                       this.display();
+               }
+       });
+       
+       $.plugin('stepper', Stepper);
+       $.plugin('pager', Pager);
+       
+       $(document).ready(function() {
+               
+               // instantiate and use a stepper via jQuery
+               $('#stepper').stepper({ value: 5 });
+               var stepper = $('#stepper').data('stepper');
+               
+               console.log(stepper.value());
+               
+               $('#stepper').stepper('stepUp', 3);
+               console.log(stepper.value());
+               
+               
+               // instantiate and use a pager via jQuery
+               $('#pager').pager({ value: 30 });
+               var pager = $('#pager').data('pager');
+               
+               console.log(pager.value());
+               
+               $('#pager').pager('stepUp', 3);
+               console.log(pager.value());
+               
+               $('#pager').pager('pageUp');
+               console.log(pager.value());
+               
+               
+               // instantiate and use a stepper directly
+               var stepper2 = new Stepper({ value: 20 }, '#stepper2');
+               
+               console.log(stepper2.value());
+               
+               stepper2.stepDown(2);
+               console.log(stepper2.value());
+               
+               
+               // modify stepper
+               Function.prototype.partial = function() {
+                       var fn = this,
diff --git a/manifold/js/plugin.js b/manifold/js/plugin.js
new file mode 100644 (file)
index 0000000..11aefad
--- /dev/null
@@ -0,0 +1,95 @@
+// INHERITANCE
+// http://alexsexton.com/blog/2010/02/using-inheritance-patterns-to-organize-large-jquery-applications/
+// We will use John Resig's proposal
+
+// http://pastie.org/517177
+
+// NOTE: missing a destroy function
+
+$.plugin = function(name, object) {
+    $.fn[name] = function(options) {
+        var args = Array.prototype.slice.call(arguments, 1);
+        return this.each(function() {
+            var instance = $.data(this, name);
+            if (instance) {
+                instance[options].apply(instance, args);
+            } else {
+                instance = $.data(this, name, new object(options, this));
+            }
+        });
+    };
+};
+
+var Plugin = Class.extend({
+
+    init: function(options, element)
+    {
+        // Mix in the passed in options with the default options
+        this.options = $.extend({}, this.default_options, options);
+
+        // Save the element reference, both as a jQuery
+        // reference and a normal reference
+        this.element  = element;
+        this.$element = $(element);
+
+        // return this so we can chain/use the bridge with less code.
+        return this;
+    },
+
+    has_query_handler: function() {
+        return (typeof this.on_filter_added === 'function');
+    },
+
+    _query_handler: function(e, event_type, data)
+    {
+        // We suppose this.query_handler_prefix has been defined if this
+        // callback is triggered    
+        var fn;
+        switch(event_type) {
+            case FILTER_ADDED:
+                fn = 'filter_added';
+                break;
+            case FILTER_REMOVED:
+                fn = 'filter_removed';
+                break;
+            case CLEAR_FILTERS:
+                fn = 'filter_clear';
+                break;
+            case FIELD_ADDED:
+                fn = 'field_added';
+                break;
+            case FIELD_REMOVED:
+                fn = 'field_removed';
+                break;
+            case CLEAR_FIELDS:
+                fn = 'field_clear';
+                break;
+            default:
+                return;
+        } // switch
+        
+        fn = 'on_' + this.query_handler_prefix + fn;
+        if (typeof this[fn] === 'function') {
+            // call with data as parameter
+            // XXX implement anti loop
+            this[fn](data);
+        }
+    },
+
+    listen_query: function(query_uuid, prefix) {
+        this.query_handler_prefix = (typeof prefix === 'undefined') ? '' : (prefix + '_');
+        this.$element.on(manifold.get_query_channel(query_uuid), $.proxy(this._query_handler, this));
+    },
+
+    default_options: {},
+
+    speak: function(msg){
+        // You have direct access to the associated and cached jQuery element
+        this.$element.append('<p>'+msg+'</p>');
+    },
+
+    register: function() {
+        // Registers the plugin to jQuery
+    },
+
+});
index c9e2856..aa09379 100644 (file)
@@ -1,34 +1,41 @@
+from types import StringTypes
+
 from operator import (
     and_, or_, inv, add, mul, sub, mod, truediv, lt, le, ne, gt, ge, eq, neg
     )
-from manifold.util.misc import contains
-from types import StringTypes
+
+# Define the inclusion operators
+class contains(type): pass
+class included(type): pass
+
 
 # New modifier: { contains 
 class Predicate:
 
     operators = {
-        "=="       : eq,
-        "!="       : ne,
-        "<"        : lt,
-        "<="       : le,
-        ">"        : gt,
-        ">="       : ge,
-        "&&"       : and_,
-        "||"       : or_,
-        "contains" : contains
+        '=='       : eq,
+        '!='       : ne,
+        '<'        : lt,
+        '<='       : le,
+        '>'        : gt,
+        '>='       : ge,
+        '&&'       : and_,
+        '||'       : or_,
+        'CONTAINS' : contains,
+        'INCLUDED' : included
     }
 
     operators_short = {
-        "=" : eq,
-        "~" : ne,
-        "<" : lt,
-        "[" : le,
-        ">" : gt,
-        "]" : ge,
-        "&" : and_,
-        "|" : or_,
-        "}" : contains
+        '=' : eq,
+        '~' : ne,
+        '<' : lt,
+        '[' : le,
+        '>' : gt,
+        ']' : ge,
+        '&' : and_,
+        '|' : or_,
+        '}' : contains,
+        '{' : included
     }
 
     def __init__(self, *args, **kwargs):
@@ -48,28 +55,60 @@ class Predicate:
         elif len(args) == 1 and isinstance(args[0], Predicate):
             key, op, value = args[0].get_tuple()
         else:
-            raise Exception, "Bad initializer for Predicate"
+            raise Exception, "Bad initializer for Predicate (args=%r)" % args
+
+        assert not isinstance(value, (frozenset, dict, set)), "Invalid value type (the only valid containers are tuples and lists) (type = %r)" % type(value)
+        if isinstance(value, list):
+            value = tuple(value)
+
         self.key = key
+        if isinstance(op, StringTypes):
+            op = op.upper()
         if op in self.operators.keys():
             self.op = self.operators[op]
         elif op in self.operators_short.keys():
             self.op = self.operators_short[op]
         else:
             self.op = op
-        if isinstance(value, (list, set)):
+
+        if isinstance(value, list):
             self.value = tuple(value)
         else:
             self.value = value
 
     def __str__(self):
-        return "Pred(%s, %s, %s)" % self.get_str_tuple()
+        key, op, value = self.get_str_tuple()
+        if isinstance(value, (tuple, list, set, frozenset)):
+            value = [repr(v) for v in value]
+            value = "[%s]" % ", ".join(value)
+        return "%s %s %r" % (key, op, value) 
 
     def __repr__(self):
-        return self.__str__() 
+        return "Predicate<%s %s %r>" % self.get_str_tuple()
 
     def __hash__(self):
         return hash(self.get_tuple())
 
+    def __eq__(self, predicate):
+        if not predicate:
+            return False
+        return self.get_tuple() == predicate.get_tuple()
+
+    def get_key(self):
+        return self.key
+    
+    def set_key(self, key):
+        self.key = key
+
+    def get_op(self):
+        return self.op
+
+    def get_value(self):
+        return self.value
+
+    def set_value(self, value):
+        self.value = value
+
     def get_tuple(self):
         return (self.key, self.op, self.value)
 
@@ -130,8 +169,10 @@ class Predicate:
         elif self.op == contains:
             method, subfield = self.key.split('.', 1)
             return not not [ x for x in dic[method] if x[subfield] == self.value] 
+        elif self.op == included:
+            return dic[self.key] in self.value
         else:
-            raise Exception, "Unexpected table format: %r", dic
+            raise Exception, "Unexpected table format: %r" % dic
 
     def filter(self, dic):
         """
@@ -171,3 +212,14 @@ class Predicate:
             print "----"
             return dic if self.match(dic) else None
 
+    def get_field_names(self):
+        if isinstance(self.key, (list, tuple, set, frozenset)):
+            return set(self.key)
+        else:
+            return set([self.key])
+
+    def get_value_names(self):
+        if isinstance(self.value, (list, tuple, set, frozenset)):
+            return set(self.value)
+        else:
+            return set([self.value])
diff --git a/plugins/active_filters/__init__.py b/plugins/active_filters/__init__.py
new file mode 100644 (file)
index 0000000..c20847a
--- /dev/null
@@ -0,0 +1,61 @@
+from unfold.plugin import Plugin
+from manifold.util.predicate import eq, ne, lt, le, gt, ge, and_, or_, contains, included 
+
+# NOTE: Python should pass templates to javascript for creating new filters
+# - option variable
+# - or, better, hidden div in the page
+# In the meantime, templates are duplicated in the javascript code
+# RECIPES FOR PLUGINS
+
+# NOTE: having classes would help
+
+class ActiveFilters(Plugin):
+
+    def __init__ (self, query=None, **settings):
+        Plugin.__init__ (self, **settings)
+
+        self.query = query
+
+    def template_file (self):
+        return "active_filters.html"
+
+    def template_env (self, request):
+
+        def get_op_str(predicate):
+            map = {
+                eq      : 'eq',
+                ne      : 'ne',
+                lt      : 'lt',
+                le      : 'le',
+                gt      : 'gt',
+                ge      : 'ge',
+                and_    : 'and',
+                or_     : 'or',
+                contains: 'contains',
+                included: 'included'
+            }
+            return map[predicate.get_op()]
+
+        filters = [[f.get_key(), get_op_str(f), f.get_value()] for p in self.query.get_where()]
+        env={}
+        env.update(self.__dict__)
+        env['filters'] = filters
+        return env
+
+    def requirements (self):
+        reqs = {
+            'js_files' : [
+                'js/active_filters.js',
+             ],
+            'css_files': [ 
+                'css/demo_table.css',
+                'css/active_filters.css'
+            ]
+        }
+        return reqs
+
+    def json_settings_list (self):
+        return ['plugin_uuid', 'domid', 'query_uuid']
+
+    def export_json_settings (self):
+        return True
diff --git a/plugins/active_filters/active_filters.css b/plugins/active_filters/active_filters.css
new file mode 100644 (file)
index 0000000..83967d9
--- /dev/null
@@ -0,0 +1,20 @@
+/* 
+    Document   : ActiveFilters
+    Created on : 26 juil. 2012, 11:19:10
+    Author     : loicbaron
+    Description:
+        Purpose of the stylesheet follows.
+*/
+
+.filterButton { 
+    border: 1px solid #AAA;
+    -webkit-border-radius: 5px;
+    -moz-border-radius: 5px;
+    padding: 2px 5px;
+    margin: 0 3px;
+    background-color:#D3D6FF;
+}
+.closeButton{
+    cursor: pointer;
+    vertical-align:middle;    
+}
\ No newline at end of file
diff --git a/plugins/active_filters/active_filters.html b/plugins/active_filters/active_filters.html
new file mode 100644 (file)
index 0000000..21e361e
--- /dev/null
@@ -0,0 +1,15 @@
+<div id='myActiveFilters' style='clear:both;'>
+       <div id='{{domid}}-template' class='filterButton' style='float:left;margin-bottom:10px;visibility: hidden;'>
+               <span id='{{domid}}-template-filter'>KEY OP VALUE</span>
+       </div>
+       {% for filter in filters %}
+       <div id='{{domid}}-filter-{{filter.key}}{{filter.op_str}}{{filter.value}}' class='filterButton' style='float:left;margin-bottom:10px;visibility: hidden;'>
+               <span id='{{domid}}-template-filter'>{{filter.key}}{{filter.op}}{{filter.value}}</span>
+       </div>
+       {% endfor %}
+</div>
+
+<div id='clearFilters' class='paging_full_numbers' style='clear:both;'>
+    <span class='paginate_button'>Clear</span>
+</div>
+<div style='clear:both;'></div>
diff --git a/plugins/active_filters/active_filters.js b/plugins/active_filters/active_filters.js
new file mode 100644 (file)
index 0000000..792c6e9
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * Description: ActiveFilters plugin
+ * Copyright (c) 2012-2013 UPMC Sorbonne Universite
+ * License: GPLv3
+ */
+
+(function($){
+
+    var ActiveFilters = Plugin.extend({
+
+        init: function(options, element) {
+            this._super(options, element);
+
+            this.listen_query(options.query_uuid);
+
+            $("#clearFilters").click(function () {
+                manifold.raise_event(options.query_uuid, CLEAR_FILTERS);
+            });
+        },
+
+        // This should be provided in the API
+        // make_id_from_filter, etc
+        getOperatorLabel: function(op)
+        {
+            if (op == "=" || op == "==") {
+                return 'eq';
+            } else if (op == "!=") {
+                return "ne";
+            } else if (op == ">") {
+                return "gt";
+            } else if (op == ">=") {
+                return "ge";
+            } else if (op == "<") {
+                return "lt";
+            } else if (op == "<=") {
+                return "le";
+            } else {
+                return false;
+            }
+        },
+
+        // Visual actions on the component
+
+        clear_filters: function() {
+            $("#clearFilters").hide();
+        },
+
+        on_filter_added: function(filter) {
+            var key    = filter[0];
+            var op     = filter[1];
+            var value  = filter[2];
+            var op_str = this.getOperatorLabel(op);
+            var id     = 'filter_' + key + "_" + op_str;
+
+            // Add a button for a filter            
+            $('#myActiveFilters').append("<div id='" + id + "' class='filterButton' style='float:left;margin-bottom:10px;'/>");
+            $('#' + id).append(key + op + value);
+
+            // Add a close button to remove the filter
+            $('#' + id).append("<img id='close-" + id + "' src='/all-static/img/details_close.png' class='closeButton' style='padding-left:3px;'/>");
+            // Add an event on click on the close button, call function removeFilter
+            $('#close-' + id).click(function(event) {
+                manifold.raise_event(options.query_uuid, FILTER_REMOVED, filter);
+            });
+            // If there are active filters, then show the clear filters button
+            $("#clearFilters").show();
+        },
+
+        on_filter_removed: function(filter) {
+            var key    = filter[0];
+            var op     = filter[1];
+            var value  = filter[2];
+            var op_str = this.getOperatorLabel(op);
+            var id     = 'filter_' + key + "_" + op_str;
+
+            $('#' + id).remove()
+            // Count the number of filter _inside_ the current plugin
+            count = $('.filterButton', $('#myActiveFilters')).length;
+            if (count == 0) {
+                jQuery("#clearFilters").hide();
+            }
+        },
+
+        on_filter_clear: function(filter) {
+            $("#clearFilters").hide();
+
+        },
+    });
+
+    $.plugin('ActiveFilters', ActiveFilters);
+
+})(jQuery);
index 7514b4a..4bd21c8 100644 (file)
@@ -48,6 +48,7 @@
                 $this.data('Manifold', plugin);
 
                 $this.set_query_handler(options.query_uuid, plugin.query_handler);
+                // This is used for autocomplete
                 $this.set_record_handler(options.query_uuid, plugin.record_handler); 
 
             }); // this.each
 
             return this.each(function() {
                 var $this = $(this);
-                var hazelnut = $this.data('Manifold');
+                var plugin = $this.data('Manifold');
 
                 // Unbind all events using namespacing
                 $(window).unbind(PLUGIN_NAME);
 
                 // Remove associated data
-                hazelnut.remove();
+                plugin.remove();
                 $this.removeData('Manifold');
 
-                $this.set_query_handler(options.query_uuid, hazelnut.query_handler);
-                $this.set_record_handler(options.query_uuid, hazelnut.record_handler); 
-
-                /* XXX Subscribe to query updates to maintain current state of query (multiple editors) */
-                jQuery.subscribe('/query/' + options.query_uuid + '/changed', {instance: $this}, query_changed);
-                jQuery.subscribe('/query/' + options.query_uuid + '/diff', {instance: $this}, query_changed_diff);
-                /* Subscribe to results in order to redraw the table when updates arrive */
-                jQuery.subscribe('/results/' + options.query_uuid + '/changed', {instance: $this}, update_autocomplete);
-
             });
         }, // destroy
 
         {
 
             var d = data;
+
+            jQuery('.queryeditor-auto-filter').change(function(event) { 
+                var key   = event.target.id.split('-')[4]; // Should be consistent with the naming of fields
+                var op    = '=';
+                var value = event.target.value;
+
+                manifold.raise_event(object.options.query_uuid, FILTER_ADDED, [key, op, value]);
+            });
             
             jQuery('.queryeditor-filter').change(function(event) { 
-                query = data.current_query;
-                var key=getKeySplitId(event.target.id,"-");
-                var op='=';
-                var value=event.target.value;
-                            
-                if(value){                
-                    query.update_filter(key, op, value);
-                    //add_ActiveFilter(event.target.id, '=',event.target.value,data);
-                }else{
-                    query.remove_filter(key,op,"");
-                    //remove_ActiveFilter(event, data, event.target.id,'=');
-                }
-                // Publish the query changed, the other plugins with subscribe will get the changes
-                jQuery.publish('/query/' + query.uuid + '/changed', query);
+                var key   = event.target.id.split('-')[4];
+                var op    = '=';
+                var value = event.target.value;
+
+                manifold.raise_event(object.options.query_uuid, FILTER_ADDED, [key, op, value]);
             });
             jQuery('.queryeditor-filter-min').change(function(event) {
                 query = data.current_query;
         {
             // This replaces the complex set_query function
             // The plugin does not need to remember the query anymore
-            switch(event_type) {
-                // Filters
-                // When Query changed, Then we need to update the filters of
-                // QueryEditor plugin if the filter is active, set the value
-                // (the update can come from another plugin) else set the
-                // filter value to null PB if the filter is composed of MIN/MAX
-                // values
-                case FILTER_ADDED:
-                    filter = data;
-                    // Set the value of the filter = to query filter value
-                    // Necessary if the filter has been modified by another plugin (QuickFilter)
-                    if(filter[1]=="="){
-                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]).val(filter[2]);
-                    }else if(filter[1]=="<"){
-                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-max').val(filter[2]);
-                    }else if(filter[1]==">"){
-                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-min').val(filter[2]);
-                    }
-                case FILTER_REMOVED:
-                    filter = data;
-                    if(filter[1]=="="){
-                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]).val(null);
-                    }else if(filter[1]=="<"){
-                        //502124d5a5848-filter-asn-max
-                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-max').val(null);
-                    }else if(filter[1]==">"){
-                        //502124d5a5848-filter-asn-min
-                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-min').val(null);
-                    }
-                case CLEAR_FILTERS:
-                    break;
-
-                // Fields
-                /* Hide/unhide columns to match added/removed fields */
-                // XXX WRONG IDENTIFIERS
-                case FIELD_ADDED:
-                    object.check(data);
-                    break;
-                case FIELD_REMOVED:
-                    object.uncheck(data);
-                    break;
-                case CLEAR_FIELDS:
-                    alert(PLUGIN_NAME + '::clear_fields() not implemented');
-                    break;
-            } // switch
+//            switch(event_type) {
+//                // Filters
+//                // When Query changed, Then we need to update the filters of
+//                // QueryEditor plugin if the filter is active, set the value
+//                // (the update can come from another plugin) else set the
+//                // filter value to null PB if the filter is composed of MIN/MAX
+//                // values
+//                case FILTER_ADDED:
+//                    filter = data;
+//                    // Set the value of the filter = to query filter value
+//                    // Necessary if the filter has been modified by another plugin (QuickFilter)
+//                    if(filter[1]=="="){
+//                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]).val(filter[2]);
+//                    }else if(filter[1]=="<"){
+//                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-max').val(filter[2]);
+//                    }else if(filter[1]==">"){
+//                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-min').val(filter[2]);
+//                    }
+//                case FILTER_REMOVED:
+//                    filter = data;
+//                    if(filter[1]=="="){
+//                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]).val(null);
+//                    }else if(filter[1]=="<"){
+//                        //502124d5a5848-filter-asn-max
+//                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-max').val(null);
+//                    }else if(filter[1]==">"){
+//                        //502124d5a5848-filter-asn-min
+//                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-min').val(null);
+//                    }
+//                case CLEAR_FILTERS:
+//                    break;
+//
+//                // Fields
+//                /* Hide/unhide columns to match added/removed fields */
+//                // XXX WRONG IDENTIFIERS
+//                case FIELD_ADDED:
+//                    object.check(data);
+//                    break;
+//                case FIELD_REMOVED:
+//                    object.uncheck(data);
+//                    break;
+//                case CLEAR_FIELDS:
+//                    alert(PLUGIN_NAME + '::clear_fields() not implemented');
+//                    break;
+//            } // switch
 
 
         }
index e00cda5..77d023c 100644 (file)
@@ -89,9 +89,7 @@
     function ResourcesSelected(options)
     {
         /* member variables */
-
         this.options = options;
-
         var object = this;
 
         /* The resources that are in the slice */
index 9d08cee..a0790c0 100644 (file)
@@ -21,6 +21,7 @@ from plugins.googlemap.googlemap     import GoogleMap
 from plugins.senslabmap.senslabmap   import SensLabMap
 from plugins.querycode.querycode     import QueryCode
 from plugins.query_editor            import QueryEditor
+from plugins.active_filters          import ActiveFilters
 from plugins.quickfilter.quickfilter import QuickFilter
 from plugins.messages.messages       import Messages
 from plugins.updater.updater         import Updater
@@ -131,6 +132,12 @@ def _slice_view (request, slicename):
     )
     stack_resources.insert(resource_query_editor)
 
+    resource_active_filters = ActiveFilters(
+        page  = page,
+        query = sq_resource,
+    )
+    stack_resources.insert(resource_active_filters)
+
     # --------------------------------------------------------------------------
     # Different displays = DataTables + GoogleMaps
     #
similarity index 71%
rename from unfold/js/plugin.js
rename to unfold/js/plugin_helper.js
index 65d4d20..d15e7a7 100644 (file)
@@ -1,14 +1,14 @@
 //
 // storing toggle's status in localStorage
 // NOTE that localStorage only stores strings, so true becomes "true"
-var plugin = {
+var plugin_helper = {
 
     debug:false,
 
     ////////// use local storage to remember open/closed toggles
     store_status : function (domid,status) {
        var key='toggle.'+domid;
-       if (plugin.debug) messages.debug("storing toggle status " + status + " for " + domid + " key=" + key);
+       if (plugin_helper.debug) messages.debug("storing toggle status " + status + " for " + domid + " key=" + key);
        $.localStorage.setItem(key,status);
     },
     // restore last status
@@ -18,7 +18,7 @@ var plugin = {
        var retrieved=$.localStorage.getItem(key);
        // set default to true
        if (retrieved==null) retrieved="true";
-       if (plugin.debug) messages.debug ("retrieved toggle status for " + domid + " (key=" + key + ") -> " + retrieved);
+       if (plugin_helper.debug) messages.debug ("retrieved toggle status for " + domid + " (key=" + key + ") -> " + retrieved);
        return retrieved;
     },
     set_toggle_status : function (domid,status) {
@@ -27,12 +27,12 @@ var plugin = {
        var hidebtn=$('#hide-'+domid);
        if (status=="true")     { plugindiv.slideDown(); hidebtn.show(); showbtn.hide(); }
        else                    { plugindiv.slideUp();   hidebtn.hide(); showbtn.show(); }
-       plugin.store_status(domid,status);
+       plugin_helper.store_status(domid,status);
     },
     set_from_saved_status : function (domid) {
-       var previous_status=plugin.retrieve_last_status (domid);
-       if (plugin.debug) messages.debug("restoring initial status for domid " + domid + " -> " + previous_status);
-       plugin.set_toggle_status (domid,previous_status);
+       var previous_status=plugin_helper.retrieve_last_status (domid);
+       if (plugin_helper.debug) messages.debug("restoring initial status for domid " + domid + " -> " + previous_status);
+       plugin_helper.set_toggle_status (domid,previous_status);
     },
     // triggered upon $(document).ready
     init_all_plugins : function() {
@@ -40,19 +40,19 @@ var plugin = {
        // let us first make sure the right parts are turned on 
        $('.persistent-toggle').each(function() {
            var domid=this.id.replace('complete-','');
-           plugin.set_from_saved_status(domid);
+           plugin_helper.set_from_saved_status(domid);
        });
        // program the hide buttons so they do the right thing
        $('.plugin-hide').each(function() {
            $(this).click(function () { 
                var domid=this.id.replace('hide-','');
-               plugin.set_toggle_status(domid,"false");
+               plugin_helper.set_toggle_status(domid,"false");
            })});
        // same for show buttons
        $('.plugin-show').each(function() {
            $(this).click(function () { 
                var domid=this.id.replace('show-','');
-               plugin.set_toggle_status(domid,"true");
+               plugin_helper.set_toggle_status(domid,"true");
            })});
        // arm tooltips
        $('.plugin-tooltip').each(function(){ $(this).tooltip({'selector':'','placement':'right'}); });
@@ -62,5 +62,5 @@ var plugin = {
 /* upon document completion, we locate all the hide and show areas, 
  * and configure their behaviour 
  */
-$(document).ready(plugin.init_all_plugins)
+$(document).ready(plugin_helper.init_all_plugins)
 
diff --git a/views/templates/base.html b/views/templates/base.html
new file mode 100644 (file)
index 0000000..c297ea5
--- /dev/null
@@ -0,0 +1,36 @@
+{# This is required by insert_above #}{% insert_handler %}<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html lang="en"> <head>
+<title> MySlice - {{ title }} </title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+{# This is where insert_str will end up #}{% media_container prelude %}
+{% include 'messages-header.html' %}
+<script type="text/javascript"> {# raw js code - use {% insert prelude_js %} ... {% endinsert %} #} {% container prelude_js %}</script>
+<style type="text/css">{# In case we need to add raw css code #}{% container prelude_css %}</style>
+{{ header_prelude }}
+{% block head %} {% endblock head %}
+</head>{# let's add these ones no matter what #}
+{# not yet needed {% insert_str prelude "css/layout-unfold1.css" %} #}
+{% insert_str prelude "js/jquery.min.js" %}
+{% insert_str prelude "js/jquery.html5storage.min.js" %}
+{% insert_str prelude "js/messages-runtime.js" %}
+{% insert_str prelude "js/class.js" %}
+{% insert_str prelude "js/plugin_helper.js" %}
+{% insert_str prelude "js/plugin.js" %}
+{% insert_str prelude "js/manifold.js" %}
+<body>
+{% block container %}
+<div id="container">
+  {% block topmenu %}
+  {% include 'widget-topmenu.html' %}
+  {% endblock topmenu %}
+{% include 'messages.html' %}
+<div class="container-fluid">
+  <div class="row-fluid">
+
+   {% block base_content%}{% endblock %}
+
+  </div><!--raw-fluid-->
+</div><!--container-fluid-->
+{% endblock container %}
+</body>
+</html>
index ba1c01b..851de44 100644 (file)
@@ -1,35 +1,9 @@
-{# This is required by insert_above #}{% insert_handler %}<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html lang="en"> <head>
-<title> MySlice - {{ title }} </title>
-<meta name="viewport" content="width=device-width, initial-scale=1.0">
-{# This is where insert_str will end up #}{% media_container prelude %}
-{% include 'messages-header.html' %}
-<script type="text/javascript"> {# raw js code - use {% insert prelude_js %} ... {% endinsert %} #} {% container prelude_js %}</script>
-<style type="text/css">{# In case we need to add raw css code #}{% container prelude_css %}</style>
-{{ header_prelude }}
-{% block head %} {% endblock head %}
-</head>{# let's add these ones no matter what #}
-{# not yet needed {% insert_str prelude "css/layout-unfold1.css" %} #}
-{% insert_str prelude "js/jquery.min.js" %}
-{% insert_str prelude "js/jquery.html5storage.min.js" %}
-{% insert_str prelude "js/messages-runtime.js" %}
-{% insert_str prelude "js/plugin.js" %}
-<body>
-{% block container %}
-<div id="container">
-  {% block topmenu %}
-  {% include 'widget-topmenu.html' %}
-  {% endblock topmenu %}
-{% include 'messages.html' %}
-<div class="container-fluid">
-  <div class="row-fluid">
+{% extends "base.html" %}
+
+{% block base_content %}
     <div id="unfold1-main" class="span12 columns">
       {% block unfold1_main %}
       "The main content area (define block 'unfold1_main')"
       {% endblock unfold1_main %}
     </div><!--span12-->
-  </div><!--raw-fluid-->
-</div><!--container-fluid-->
-{% endblock container %}
-</body>
-</html>
+{% endblock %}
index b107b55..4754fa1 100644 (file)
@@ -1,25 +1,6 @@
-{# This is required by insert_above #}{% insert_handler %}<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html lang="en"> <head>
-<title> MySlice - {{ title }} </title>
-<meta name="viewport" content="width=device-width, initial-scale=1.0">
-{# This is where insert_str will end up #}{% media_container prelude %}
-<script type="text/javascript"> {# raw js code - use {% insert prelude_js %} ... {% endinsert %} #} {% container prelude_js %}</script>
-<style type="text/css">{# In case we need to add raw css code #}{% container prelude_css %}</style>
-{{ header_prelude }}
-</head>{# let's add these ones no matter what #}
-{% insert_str prelude "css/layout-unfold2.css" %}
-{% insert_str prelude "js/jquery.min.js" %}
-{% insert_str prelude "js/jquery.html5storage.min.js" %}
-{% insert_str prelude "js/messages-runtime.js" %}
-{% insert_str prelude "js/plugin.js" %}
-<body>
-{% block container %}
-<div id="container">
-  {% block topmenu %}
-  {% include 'widget-topmenu.html' %}
-  {% endblock topmenu %}
-<div class="container-fluid">
-  <div class="row-fluid">
+{% extends "base.html" %}
+
+{% block base_content %}
     <div id="unfold2-main" class="span9 columns">
       {% block unfold2_main %}
       "The main content area (define block 'unfold2_main')"
@@ -30,8 +11,4 @@
       "The related content area (define block 'related_main')"
       {% endblock unfold2_margin %}
     </div><!--span3-->
-  </div><!--raw-fluid-->
-</div><!--container-fluid-->
-{% endblock container %}
-</body>
-</html>
+{% endblock %}