manifold: fix in plugin.js
authorJordan Augé <jordan.auge@lip6.fr>
Thu, 8 Aug 2013 15:18:01 +0000 (17:18 +0200)
committerJordan Augé <jordan.auge@lip6.fr>
Thu, 8 Aug 2013 15:18:01 +0000 (17:18 +0200)
plugins: revamped query_editor to accept filter updates + fixes in active_filters

manifold/js/plugin.js
plugins/active_filters/active_filters.js
plugins/query_editor/__init__.py
plugins/query_editor/filter_input_integer.html
plugins/query_editor/filter_input_others.html
plugins/query_editor/filter_input_string.html
plugins/query_editor/filter_input_string_values.html
plugins/query_editor/query_editor.html
plugins/query_editor/query_editor.js

index 5d3df74..84ebbc4 100644 (file)
@@ -77,12 +77,14 @@ var Plugin = Class.extend({
     },
 
     listen_query: function(query_uuid, prefix) {
-        this.query_handler_prefix = (typeof prefix === 'undefined') ? '' : (prefix + '_');
+        this.query_handler_prefix = (typeof prefix === 'undefined') ? '' : (prefix + manifold.separator);
         this.$element.on(manifold.get_query_channel(query_uuid), $.proxy(this._query_handler, this));
     },
 
     default_options: {},
 
+    /* Helper functions for naming HTML elements (ID, classes), with support for filters and fields */
+
     id: function()
     {
         var ret = this.options.plugin_uuid;
@@ -129,4 +131,28 @@ var Plugin = Class.extend({
         return filter[0] + ' ' + filter[1] + ' ' + filter[2];
     },
 
+    array_from_id: function(id)
+    {
+        var ret = id.split(manifold.separator);
+        ret.shift(); // remove plugin_uuid at the beginning
+        return ret;
+    },
+
+    id_from_field: function(field)
+    {
+        return 'field' + manifold.separator + field;
+    },
+
+    field_from_id: function(id)
+    {
+        var array;
+        if (typeof id === 'string') {
+            array = id.split(manifold.separator);
+        } else { // We suppose we have an array ('object')
+            array = id;
+        }
+        // array = ['field', FIELD_NAME]
+        return array[1];
+    },
+
 });
index fe61924..89ebef2 100644 (file)
         show_clear_button: function()
         {
             this.el('clearFilters').show();
-        }
+        },
 
         hide_clear_button: function()
         {
             this.el('clearFilters').hide();
-        }
+        },
 
         check_and_hide_clear_button: function()
         {
@@ -64,7 +64,7 @@
             if (count == 1) { // Including the template
                 this.el('clearFilters').hide();
             }
-        }
+        },
 
         clear_filters: function() 
         {
@@ -92,7 +92,7 @@
             this.show_clear_button();
         },
         
-        remove_filter = function(filter)
+        remove_filter: function(filter)
         {
             this.el(this.id_from_filter(filter, false)).remove();
             this.check_and_hide_clear_button();
index e46a033..98e7f9f 100644 (file)
@@ -2,6 +2,8 @@ from unfold.plugin import Plugin
 
 from django.template.loader import render_to_string
 
+# XXX We need naming helpers in the python Plugin class also, used in template
+
 class QueryEditor(Plugin):
 
     def template_file(self):
@@ -10,6 +12,7 @@ class QueryEditor(Plugin):
     def requirements (self):
         reqs = {
             'js_files' : [
+                # XXX datatables
                 'js/query_editor.js',
             ] ,
             'css_files': [
@@ -43,18 +46,32 @@ class QueryEditor(Plugin):
                         v_desc = v.split('-')
                         options.append(v_desc[0])
 
-                    env = {'options': options}
+                    env = {
+                        'domid': self.domid,
+                        'options': options
+                    }
                     filter_input = render_to_string('filter_input_string_values.html', env)
                 else:
-                    env = {'filter_id': "%s-filter-%s" % (self.domid, md_field['name'])}
+                    env = {
+                        'domid': self.domid,
+                        'field': md_field['name']
+                    }
                     filter_input = render_to_string('filter_input_string.html', env)
                     
             elif md_field['type'] == 'int':
                 allowed_values = md_field.get('allowed_values', '0,0').split(',')
-                env = {'min': allowed_values[0], 'max': allowed_values[1]}
+                env = {
+                    'domid': self.domid,
+                    'field': md_field['name'],
+                    'min'  : allowed_values[0],
+                    'max'  : allowed_values[1]
+                }
                 filter_input = render_to_string('filter_input_integer.html', env)
             else:
-                env = {'filter_id': "%s-filter-%s" % (self.domid, md_field['name'])}
+                env = {
+                    'domid': self.domid,
+                    'field': md_field['name']
+                }
                 filter_input = render_to_string('filter_input_others.html', env)
 
             fields.append({
index ab80a64..bf1a70c 100644 (file)
@@ -1,2 +1,2 @@
-<input class='queryeditor-filter-min' id='$filter_id-min' type=text size=3 value='{{ min }}'></input>&nbsp;-&nbsp;
-<input class='queryeditor-filter-max' id='$filter_id-max' type=text size=3 value='{{ max }}'></input>
+<input class='queryeditor-filter-min' id='{{domid}}__field__{{field}}__min' type=text size=3 value='{{ min }}'></input>&nbsp;-&nbsp;
+<input class='queryeditor-filter-max' id='{{domid}}__field__{{field}}__max' type=text size=3 value='{{ max }}'></input>
index 3ccaae9..cd4ba26 100644 (file)
@@ -1,4 +1,4 @@
 <div class='ui-widget content' style='margin-top:0px;'>
-    <label for='{{ filter_id }}'></label>
-    <input class='queryeditor-auto-filter' id='{{ filter_id }}' type=text></input>
+    <label for='{{field}}'></label>
+    <input class='queryeditor-auto-filter' id='{{domid}}__field__{{field}}' type=text></input>
 </div>
index 12971d9..b0d580c 100644 (file)
@@ -1,4 +1,4 @@
 <div class='ui-widget content' style='margin-top:0px;'>
-  <label for='{{ filter_id }}'></label>
-  <input class='queryeditor-auto-filter' id='{{ filter_id }}' type=text></input>
+  <label for='{{domid}}__field__{{field}}'></label>
+  <input class='queryeditor-auto-filter' id='{{domid}}__field__{{field}}' type=text></input>
 </div>
index f0a10df..6a8aeca 100644 (file)
@@ -1,5 +1,6 @@
 <select class='queryeditor-filter' id='$filter_id'>
     <option value=''>Show all</option>
-    _filter_input.append("<option>%s</option>" % v_desc[0])
-_filter_input.append('</select>')
-filter_input = "\n".join(_filter_input)
+       {% for option in options %}
+       <option>{{option}}</option>
+       {% endfor %}
+</select>
index 6cd3ff2..e366a64 100644 (file)
@@ -8,7 +8,7 @@ EOF;
 EOF; -->
 
 <div>
-  <table id='{{domid}}-table' class='display'>
+  <table id='{{domid}}__table' class='display'>
     <thead>
       <tr>
         <th class='center'>Field</th>
@@ -29,7 +29,7 @@ EOF; -->
         <td class='center'> {{ field.type }}</td>
         <td class='center'> {{ field.filter_input }}</td>
         <td class='center'>
-          <input class='queryeditor-check' id='check_{{ field.name }}' name='{{ field.header }}' type='checkbox' autocomplete='off' value='{{ field.name }}' {% if field.checked %} checked {% endif %}></input>
+          <input class='queryeditor-check' id='{{domid}}__check__{{ field.name }}' name='{{ field.header }}' type='checkbox' autocomplete='off' value='{{ field.name }}' {% if field.checked %} checked {% endif %}></input>
         </td>
       </tr>
                
index 9ef90fd..9f130f9 100644 (file)
  * License: GPLv3
  */
 
+// XXX TODO This plugin will be interested in changes in metadata
+// What if we remove a filter, is it removed in the right min/max field ???
+//  -> no on_filter_removed is not yet implemented
+// XXX if a plugin has not declared a handler, it might become inconsistent,
+// and the interface should either reset or disable it
 (function($){
 
     var QueryEditor = Plugin.extend({
 
+        event_filter_added: function(op, suffix) {
+            suffix = (typeof suffix === 'undefined') ? '' : manifold.separator + suffix;
+            var self = this;
+            return function(e) {
+                var array = self.array_from_id(e.target.id);
+                var key   = self.field_from_id(array); // No need to remove suffix...
+                var value = e.target.value;
+
+                if (value) {
+                    // XXX This should be handled by manifold
+                    //manifold.raise_event(object.options.query_uuid, FILTER_UPDATED, [key, op, value]);
+                    manifold.raise_event(self.options.query_uuid, FILTER_ADDED, [key, op, value]);
+                } else {
+                    // XXX This should be handled by manifold
+                    manifold.raise_event(self.options.query_uuid, FILTER_REMOVED, [key, op]);
+                }
+            }
+        },
+
         init: function(options, element) {
-            $('.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;
+            this._super(options, element);
+
+            this.listen_query(options.query_uuid);
 
-                manifold.raise_event(object.options.query_uuid, FILTER_ADDED, [key, op, value]);
+            this.els('queryeditor-auto-filter').change(this.event_filter_added('='));
+            this.els('queryeditor-filter').change(this.event_filter_added('='));
+            this.els('queryeditor-filter-min').change(this.event_filter_added('>'));
+            this.els('queryeditor-filter-max').change(this.event_filter_added('<'));
+
+            var self = this;
+            this.els('queryeditor-check').click(function() { 
+                manifold.raise_event(self.options.query_uuid, this.checked?FIELD_ADDED:FIELD_REMOVED, this.value);
             });
-            
-            jQuery('.queryeditor-filter').change(function(event) { 
-                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]);
+            /* The following code adds an expandable column for the table */
+            // XXX Why isn't it done statically ?
+            var nCloneTh = document.createElement( 'th' );
+            var nCloneTd = document.createElement( 'td' );
+            nCloneTd.innerHTML = "<span class='ui-icon ui-icon-triangle-1-e' style='cursor:pointer'></span>";
+            //nCloneTd.innerHTML = '<img src="/components/com_tophat/images/details_open.png">';
+            nCloneTh.innerHTML = '<b>Info</b>';
+            nCloneTd.className = "center";
+            nCloneTh.className = "center";
+            // XXX
+            jQuery('#'+this.options.plugin_uuid+'_fields thead tr').each( function () {
+                this.insertBefore( nCloneTh, this.childNodes[0] );
             });
-            jQuery('.queryeditor-filter-min').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);
+            // XXX
+            jQuery('#'+this.options.plugin_uuid+'_fields tbody tr').each( function () {
+                this.insertBefore(  nCloneTd.cloneNode( true ), this.childNodes[0] );
+            });
+         
+            // We are currently using a DataTable display, but another browsing component could be better
+            //jQuery('#'+this.options.plugin_uuid+'-table').dataTable...
+            var  metaTable = this.el('table').dataTable({
+                bFilter     : false,
+                bPaginate   : false,
+                bInfo       : false,
+                sScrollX    : '100%',         // Horizontal scrolling
+                sScrollY    : '200px',
+                bJQueryUI   : true,           // Use jQuery UI
+                bProcessing : true,           // Loading
+                aaSorting   : [[ 1, "asc" ]], // sort by column fields on load
+                aoColumnDefs: [
+                    { 'bSortable': false, 'aTargets': [ 0 ]},
+                    { 'sWidth': '8px', 'aTargets': [ 0 ] },
+                    { 'sWidth': '8px', 'aTargets': [ 4 ] } // XXX NB OF COLS
+                ]
             });
-            jQuery('.queryeditor-filter-max').change(function(event) {
-                query = data.current_query;
-                var key=getKeySplitId(event.target.id,"-");
-                var op='<';
-                var value=event.target.value;
+
+            var self = this;
+            // Actions on the newly added fields
+            this.el('table tbody td span').on('click', function() {
+                var nTr = this.parentNode.parentNode;
+                // use jQuery UI instead of images to keep a common UI
+                // class="ui-icon treeclick ui-icon-triangle-1-s tree-minus"
+                // East oriented Triangle class="ui-icon-triangle-1-e"
+                // South oriented Triangle class="ui-icon-triangle-1-s"
                 
-                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,'<');
+                if (this.className=="ui-icon ui-icon-triangle-1-e") {
+                    this.removeClass("ui-icon-triangle-1-e").addClass("ui-icon-triangle-1-s");
+                    // XXX ??????
+                    metaTable.fnOpen(nTr, this.fnFormatDetails(metaTable, nTr, self.options.plugin_uuid+'_div'), 'details' );
+                } else {
+                    this.removeClass("ui-icon-triangle-1-s").addClass("ui-icon-triangle-1-e");
+                    metaTable.fnClose(nTr);
                 }
-                // Publish the query changed, the other plugins with subscribe will get the changes
-                jQuery.publish('/query/' + query.uuid + '/changed', query);
             });
 
-            jQuery('.queryeditor-check').click(function() { 
-                manifold.raise_event(object.options.query_uuid, this.checked?FIELD_ADDED:FIELD_REMOVED, this.value);
-                /*
-                    var column = this.id.substring(6);
-                    query = data.current_query;
-                    if (this.checked) {
-                        if (jQuery.inArray(column, query.fields) == -1) {
-                            query.fields.push(column);
-                            jQuery.publish('/query/' + query.uuid + '/changed', query);
-                        }
-                    } else {
-                        query.fields = jQuery.grep(query.fields, function(value) {return value != column;});
-                        jQuery.publish('/query/' + query.uuid + '/changed', query);
-                    }
-                */
-                });
-
-            //onFunctionAvailable('jQuery.fn.dataTable', function() {
-
-                var nCloneTh = document.createElement( 'th' );
-                var nCloneTd = document.createElement( 'td' );
-                nCloneTd.innerHTML = "<span class='ui-icon ui-icon-triangle-1-e' style='cursor:pointer'></span>";
-                //nCloneTd.innerHTML = '<img src="/components/com_tophat/images/details_open.png">';
-                nCloneTh.innerHTML = '<b>Info</b>';
-                nCloneTd.className = "center";
-                nCloneTh.className = "center";
-         
-                jQuery('#'+this.options.plugin_uuid+'_fields thead tr').each( function () {
-                    this.insertBefore( nCloneTh, this.childNodes[0] );
-                });
-         
-                jQuery('#'+this.options.plugin_uuid+'_fields tbody tr').each( function () {
-                    this.insertBefore(  nCloneTd.cloneNode( true ), this.childNodes[0] );
-                });
-         
-                var  metaTable = jQuery('#'+this.options.plugin_uuid+'-table').dataTable( {
-                    bFilter: false,
-                    bPaginate: false,
-                    bInfo: false,
-                    sScrollX: '100%',       /* Horizontal scrolling */
-                    sScrollY: "200px",
-                    bJQueryUI: true, // Use jQuery UI
-                    bProcessing: true, // Loading
-                    aaSorting: [[ 1, "asc" ]], // sort by column fields on load
-                    aoColumnDefs: [ {"bSortable": false, "aTargets": [ 0 ]},
-                                      { "sWidth": "8px", "aTargets": [ 0 ] },
-                                      { "sWidth": "8px", "aTargets": [ 4 ] }
-                    ]
-                });
-
-                jQuery('#'+this.options.plugin_uuid+'_fields tbody td span').live('click', function () {
-                    var nTr = this.parentNode.parentNode;
-                    // use jQuery UI instead of images to keep a common UI
-                    // class="ui-icon treeclick ui-icon-triangle-1-s tree-minus"
-                    //East oriented Triangle class="ui-icon-triangle-1-e"
-                    //South oriented Triangle class="ui-icon-triangle-1-s"
-                    
-                    if(this.className=="ui-icon ui-icon-triangle-1-e"){
-                        this.removeClass("ui-icon-triangle-1-e");
-                        this.addClass("ui-icon-triangle-1-s");
-                        metaTable.fnOpen( nTr, this.fnFormatDetails(metaTable, nTr, this.options.plugin_uuid+'_div'), 'details' );
-                    }else{
-                        this.removeClass("ui-icon-triangle-1-s");
-                        this.addClass("ui-icon-triangle-1-e");
-                        metaTable.fnClose( nTr );
-                    }
-                    /*
-                    if ( this.src.match('details_close') ) {
-                        this.src = "/components/com_tophat/images/details_open.png";
-                        metaTable.fnClose( nTr );
-                    }
-                    else {
-                        this.src = "/components/com_tophat/images/details_close.png";
-                        metaTable.fnOpen( nTr, this.fnFormatDetails(metaTable, nTr, this.options.plugin_uuid+'_div'), 'details' );
-                    }
-                    */
-                });
-
-                jQuery('#'+this.options.plugin_uuid+'_fields_wrapper').css({'padding-top':'0em','padding-bottom':'0em'});
+            this.el('table_wrapper').css({
+                'padding-top'   : '0em',
+                'padding-bottom': '0em'
+            });
 
-            //}); // onfunctionAvailable
         }, // init
 
         /* UI management */
             this.el('check', field).attr('checked', false);
         },
 
+        update_filter_value: function(filter, removed)
+        {
+            removed = !(typeof removed === 'undefined'); // default = False
+
+            var key   = filter[0];
+            var op    = filter[1];
+            var value = filter[2];
+
+            var id = this.id_from_field(key);
+
+            if (op == '=') {
+                var element = this.el(id);
+            } else {
+                var suffix;
+                if (op == '<') {
+                    this.el(id, 'max').val(value);
+                } else if (op == '>') {
+                    this.el(id, 'min').val(value);
+                } else {
+                    return;
+                }
+                var element = this.el(id, suffix);
+            }
+
+            element.val(removed?null:value);
+
+        },
+
         /* Events */
 
         on_filter_added: function(filter)
         {
-//                    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]);
-//                    }
+            this.update_filter_value(filter);
         },
 
         on_filter_removed: function(filter)
         {
-//                    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);
-//                    }
+            this.update_filter_value(filter, true);
         },
 
         on_field_added: function(field)
          
             return sOut;
         }
-       
-})( jQuery );
+    });
+
+    $.plugin('QueryEditor', QueryEditor);
+
+})(jQuery);