X-Git-Url: http://git.onelab.eu/?p=myslice.git;a=blobdiff_plain;f=plugins%2Fquerytable%2Fstatic%2Fjs%2Fquerytable.js;h=cc1adf3754abf783040844eeceba6859d239bbac;hp=30c727b76a0ffd35f2beb30e231808c8137f1e7d;hb=ad89a80375c85ff22c2d58a571505cad7b1d1f92;hpb=f8a614d0271ddc3046e18d826df01f72a861257c diff --git a/plugins/querytable/static/js/querytable.js b/plugins/querytable/static/js/querytable.js index 30c727b7..cc1adf37 100644 --- a/plugins/querytable/static/js/querytable.js +++ b/plugins/querytable/static/js/querytable.js @@ -4,15 +4,16 @@ * License: GPLv3 */ -(function($){ +(function($) { var debug=false; -// debug=true + debug=true + var debug_deep=false; +// debug_deep=true; var QueryTable = Plugin.extend({ - init: function(options, element) - { + init: function(options, element) { this._super(options, element); /* Member variables */ @@ -23,13 +24,11 @@ this.received_all_query = false; this.received_query = false; - // We need to remember the active filter for datatables filtering - this.filters = Array(); +// // We need to remember the active filter for datatables filtering +// this.filters = Array(); // an internal buffer for records that are 'in' and thus need to be checked this.buffered_records_to_check = []; - // an internal buffer for keeping lines and display them in one call to fnAddData - this.buffered_lines = []; /* XXX Events XXX */ // this.$element.on('show.Datatables', this.on_show); @@ -41,8 +40,18 @@ var query = manifold.query_store.find_analyzed_query(this.options.query_uuid); this.method = query.object; - var keys = manifold.metadata.get_key(this.method); - this.key = (keys && keys.length == 1) ? keys[0] : null; + // xxx beware that this.key needs to contain a key that all records will have + // in general query_all will return well populated records, but query + // returns records with only the fields displayed on startup. + this.key = (this.options.id_key); + if (! this.key) { + // if not specified by caller, decide from metadata + var keys = manifold.metadata.get_key(this.method); + this.key = (keys && keys.length == 1) ? keys[0] : null; + } + if (! this.key) messages.warning("querytable.init could not kind valid key"); + + if (debug) messages.debug("querytable: key="+this.key); /* Setup query and record handlers */ this.listen_query(options.query_uuid); @@ -54,105 +63,77 @@ /* PLUGIN EVENTS */ - on_show: function(e) - { + on_show: function(e) { var self = e.data; - self.table.fnAdjustColumnSizing() - - /* Refresh dataTabeles if click on the menu to display it : fix dataTables 1.9.x Bug */ - /* temp disabled... useful ? -- jordan - $(this).each(function(i,elt) { - if (jQuery(elt).hasClass('dataTables')) { - var myDiv=jQuery('#querytable-' + this.id).parent(); - if(myDiv.height()==0) { - var oTable=$('#querytable-' + this.id).dataTable(); - oTable.fnDraw(); - } - } - }); - */ }, // on_show /* GUI EVENTS */ /* GUI MANIPULATION */ - initialize_table: function() - { - /* Transforms the table into DataTable, and keep a pointer to it */ - var self = this; - var actual_options = { - // Customize the position of Datatables elements (length,filter,button,...) - // we use a fluid row on top and another on the bottom, making sure we take 12 grid elt's each time - sDom: "<'row'<'col-xs-5'l><'col-xs-1'r><'col-xs-6'f>>t<'row'<'col-xs-5'i><'col-xs-7'p>>", - // XXX as of sept. 2013, I cannot locate a bootstrap3-friendly mode for now - // hopefully this would come with dataTables v1.10 ? - // in any case, search for 'sPaginationType' all over the code for more comments - sPaginationType: 'bootstrap', - // Handle the null values & the error : Datatables warning Requested unknown parameter - // http://datatables.net/forums/discussion/5331/datatables-warning-...-requested-unknown-parameter/p2 - aoColumnDefs: [{sDefaultContent: '',aTargets: [ '_all' ]}], - // WARNING: this one causes tables in a 'tabs' that are not exposed at the time this is run to show up empty - // sScrollX: '100%', /* Horizontal scrolling */ - bProcessing: true, /* Loading */ - fnDrawCallback: function() { self._querytable_draw_callback.call(self); } - // XXX use $.proxy here ! - }; - // the intention here is that options.datatables_options as coming from the python object take precedence - // xxx DISABLED by jordan: was causing errors in datatables.js - // xxx turned back on by Thierry - this is the code that takes python-provided options into account - // check your datatables_options tag instead - // however, we have to accumulate in aoColumnDefs from here (above) - // and from the python wrapper (checkboxes management, plus any user-provided aoColumnDefs) - if ( 'aoColumnDefs' in this.options.datatables_options) { - actual_options['aoColumnDefs']=this.options.datatables_options['aoColumnDefs'].concat(actual_options['aoColumnDefs']); - delete this.options.datatables_options['aoColumnDefs']; + initialize_table: function() { + // compute columns based on columns and hidden_columns + this.slick_columns = []; + var all_columns = this.options.columns; // .concat(this.options.hidden_columns) + // xxx would be helpful to support a column_renamings options arg + // for redefining some labels like 'network_hrn' that really are not meaningful + for (c in all_columns) { + var column=all_columns[c]; + this.slick_columns.push ( {id:column, name:column, field:column, + cssClass: "querytable-column-"+column, + width:100, minWidth:40, }); } - $.extend(actual_options, this.options.datatables_options ); - this.table = this.elmt('table').dataTable(actual_options); - - /* Setup the SelectAll button in the dataTable header */ - /* xxx not sure this is still working */ - var oSelectAll = $('#datatableSelectAll-'+ this.options.plugin_uuid); - oSelectAll.html("Select All"); - oSelectAll.button(); - oSelectAll.css('font-size','11px'); - oSelectAll.css('float','right'); - oSelectAll.css('margin-right','15px'); - oSelectAll.css('margin-bottom','5px'); - oSelectAll.unbind('click'); - oSelectAll.click(this._selectAll); - - /* Add a filtering function to the current table - * Note: we use closure to get access to the 'options' - */ - $.fn.dataTableExt.afnFiltering.push(function( oSettings, aData, iDataIndex ) { - /* No filtering if the table does not match */ - if (oSettings.nTable.id != self.options.plugin_uuid + '__table') - return true; - return self._querytable_filter.call(self, oSettings, aData, iDataIndex); - }); - /* Processing hidden_columns */ - $.each(this.options.hidden_columns, function(i, field) { - //manifold.raise_event(self.options.query_all_uuid, FIELD_REMOVED, field); - self.hide_column(field); - }); + // xxx should be extensible from caller with this.options.slickgrid_options + this.slick_options = { + enableCellNavigation: false, + enableColumnReorder: true, + }; + + this.slick_data = []; + this.slick_dataview = new Slick.Data.DataView(); + var self=this; + this.slick_dataview.onRowCountChanged.subscribe ( function (e,args) { + self.slick_grid.updateRowCount(); + self.slick_grid.autosizeColumns(); + self.slick_grid.render(); + }); + + var selector="#grid-"+this.options.domid; + if (debug_deep) { + messages.debug("slick grid selector is " + selector); + for (c in this.slick_columns) { + var col=this.slick_columns[c]; + var msg=""; + for (k in col) msg = msg+" col["+k+"]="+col[k]; + messages.debug("slick_column["+c+"]:"+msg); + } + } + // add a checkbox column + var checkbox_selector = new Slick.CheckboxSelectColumn({ + cssClass: "slick-cell-checkboxsel" + }); + this.slick_columns.push(checkbox_selector.getColumnDefinition()); + this.slick_grid = new Slick.Grid(selector, this.slick_dataview, this.slick_columns, this.slick_options); + this.slick_grid.setSelectionModel (new Slick.RowSelectionModel ({selectActiveRow: false})); + this.slick_grid.registerPlugin (checkbox_selector); + // autotooltips: for showing the full column name when ellipsed + var auto_tooltips = new Slick.AutoTooltips ({ enableForHeaderCells: true }); + this.slick_grid.registerPlugin (auto_tooltips); + + this.columnpicker = new Slick.Controls.ColumnPicker (this.slick_columns, this.slick_grid, this.slick_options) + }, // initialize_table - /** - * @brief Determine index of key in the table columns - * @param key - * @param cols - */ + // Determine index of key in the table columns getColIndex: function(key, cols) { var tabIndex = $.map(cols, function(x, i) { if (x.sTitle == key) return i; }); return (tabIndex.length > 0) ? tabIndex[0] : -1; }, // getColIndex - checkbox_html : function (key, value) - { + checkbox_html : function (key, value) { + if (debug_deep) messages.debug("checkbox_html, value="+value); var result=""; // Prefix id with plugin_uuid result += " '+record['hrn']); - } else { - if (record[colnames[j]]) - line.push(record[colnames[j]]); - else - line.push(''); - } - } - - // catch up with the last column if checkboxes were requested - if (this.options.checkboxes) { - // Use a key instead of hostname (hard coded...) - line.push(this.checkbox_html(this.key, record[this.key])); - } - - // adding an array in one call is *much* more efficient - // this.table.fnAddData(line); - this.buffered_lines.push(line); + }, + + new_record: function(record) { + // xxx having a field named 'id' is a requirement from dataview + record['id']=record[this.key]; + this.slick_data.push(record); }, - clear_table: function() - { - this.table.fnClearTable(); + clear_table: function() { + console.log("clear_table not implemented"); }, - redraw_table: function() - { + redraw_table: function() { this.table.fnDraw(); }, - show_column: function(field) - { + show_column: function(field) { var oSettings = this.table.fnSettings(); var cols = oSettings.aoColumns; var index = this.getColIndex(field,cols); @@ -231,55 +173,55 @@ this.table.fnSetColumnVis(index, true); }, - hide_column: function(field) - { - var oSettings = this.table.fnSettings(); - var cols = oSettings.aoColumns; - var index = this.getColIndex(field,cols); - if (index != -1) - this.table.fnSetColumnVis(index, false); + hide_column: function(field) { + console.log("hide_column not implemented - field="+field); }, - set_checkbox: function(record, checked) - { + set_checkbox: function(record, checked) { + console.log("set_checkbox not yet implemented with slickgrid"); + return; /* Default: checked = true */ if (checked === undefined) checked = true; - var key_value; + var id; /* The function accepts both records and their key */ switch (manifold.get_type(record)) { - case TYPE_VALUE: - key_value = record; - break; - case TYPE_RECORD: - /* XXX Test the key before ? */ - key_value = record[this.key]; - break; - default: - throw "Not implemented"; - break; + case TYPE_VALUE: + id = record; + break; + case TYPE_RECORD: + /* XXX Test the key before ? */ + id = record[this.key]; + break; + default: + throw "Not implemented"; + break; } - var checkbox_id = this.flat_id(this.id('checkbox', key_value)); + if (id === undefined) { + messages.warning("querytable.set_checkbox record has no id to figure which line to tick"); + return; + } + var checkbox_id = this.flat_id(this.id('checkbox', id)); // function escape_id(myid) is defined in portal/static/js/common.functions.js checkbox_id = escape_id(checkbox_id); - // using dataTables's $ to search also in nodes that are not currently displayed + // using dataTables's $ to search also in nodes that are not currently displayed var element = this.table.$(checkbox_id); - if (debug) messages.debug("set_checkbox checked=" + checked + " id=" + checkbox_id + " matches=" + element.length); + if (debug_deep) + messages.debug("set_checkbox checked=" + checked + + " id=" + checkbox_id + " matches=" + element.length); element.attr('checked', checked); }, /*************************** QUERY HANDLER ****************************/ - on_filter_added: function(filter) - { + on_filter_added: function(filter) { this.filters.push(filter); this.redraw_table(); }, - on_filter_removed: function(filter) - { + on_filter_removed: function(filter) { // Remove corresponding filters this.filters = $.grep(this.filters, function(x) { return x != filter; @@ -287,98 +229,81 @@ this.redraw_table(); }, - on_filter_clear: function() - { - // XXX + on_filter_clear: function() { this.redraw_table(); }, - on_field_added: function(field) - { + on_field_added: function(field) { this.show_column(field); }, - on_field_removed: function(field) - { + on_field_removed: function(field) { this.hide_column(field); }, - on_field_clear: function() - { + on_field_clear: function() { alert('QueryTable::clear_fields() not implemented'); }, /* XXX TODO: make this generic a plugin has to subscribe to a set of Queries to avoid duplicated code ! */ /*************************** ALL QUERY HANDLER ****************************/ - on_all_filter_added: function(filter) - { + on_all_filter_added: function(filter) { // XXX this.redraw_table(); }, - on_all_filter_removed: function(filter) - { + on_all_filter_removed: function(filter) { // XXX this.redraw_table(); }, - on_all_filter_clear: function() - { + on_all_filter_clear: function() { // XXX this.redraw_table(); }, - on_all_field_added: function(field) - { + on_all_field_added: function(field) { this.show_column(field); }, - on_all_field_removed: function(field) - { + on_all_field_removed: function(field) { this.hide_column(field); }, - on_all_field_clear: function() - { + on_all_field_clear: function() { alert('QueryTable::clear_fields() not implemented'); }, /*************************** RECORD HANDLER ***************************/ - on_new_record: function(record) - { + on_new_record: function(record) { if (this.received_all_query) { - // if the 'all' query has been dealt with already we may turn on the checkbox - if (debug) messages.debug("turning on checkbox for record "+record[this.key]); + // if the 'all' query has been dealt with already we may turn on the checkbox this.set_checkbox(record, true); - } else { - // otherwise we need to remember that and do it later on - if (debug) messages.debug("Remembering record to check " + record[this.key]); + } else { + // otherwise we need to remember that and do it later on + if (debug) messages.debug("Remembering record to check " + record[this.key]); this.buffered_records_to_check.push(record); - } + } }, - on_clear_records: function() - { + on_clear_records: function() { }, // Could be the default in parent - on_query_in_progress: function() - { + on_query_in_progress: function() { this.spin(); }, - on_query_done: function() - { + on_query_done: function() { this.received_query = true; // unspin once we have received both if (this.received_all_query && this.received_query) this.unspin(); }, - on_field_state_changed: function(data) - { + on_field_state_changed: function(data) { switch(data.request) { case FIELD_REQUEST_ADD: case FIELD_REQUEST_ADD_RESET: @@ -395,8 +320,7 @@ /* XXX TODO: make this generic a plugin has to subscribe to a set of Queries to avoid duplicated code ! */ // all - on_all_field_state_changed: function(data) - { + on_all_field_state_changed: function(data) { switch(data.request) { case FIELD_REQUEST_ADD: case FIELD_REQUEST_ADD_RESET: @@ -411,28 +335,30 @@ } }, - on_all_new_record: function(record) - { + on_all_new_record: function(record) { this.new_record(record); }, - on_all_clear_records: function() - { + on_all_clear_records: function() { this.clear_table(); }, - on_all_query_in_progress: function() - { + on_all_query_in_progress: function() { // XXX parent this.spin(); }, // on_all_query_in_progress - on_all_query_done: function() - { - if (debug) messages.debug("1-shot initializing dataTables content with " + this.buffered_lines.length + " lines"); - this.table.fnAddData (this.buffered_lines); - this.buffered_lines=[]; + on_all_query_done: function() { + if (debug) messages.debug("1-shot initializing dataTables content with " + this.slick_data.length + " lines"); + var start=new Date(); + this.slick_dataview.setItems (this.slick_data); + var duration=new Date()-start; + if (debug) messages.debug("setItems " + duration + " ms"); + if (debug_deep) { + // show full contents of first row app + for (k in this.slick_data[0]) messages.debug("slick_data[0]["+k+"]="+this.slick_data[0][k]); + } var self = this; // if we've already received the slice query, we have not been able to set @@ -454,8 +380,7 @@ /** * @brief QueryTable filtering function */ - _querytable_filter: function(oSettings, aData, iDataIndex) - { + _querytable_filter: function(oSettings, aData, iDataIndex) { var ret = true; $.each (this.filters, function(index, filter) { /* XXX How to manage checkbox ? */ @@ -500,8 +425,7 @@ return ret; }, - _querytable_draw_callback: function() - { + _querytable_draw_callback: function() { /* * Handle clicks on checkboxes: reassociate checkbox click every time * the table is redrawn @@ -530,8 +454,7 @@ } }, - _check_click: function(e) - { + _check_click: function(e) { e.stopPropagation(); var self = e.data; @@ -543,8 +466,7 @@ }, - _selectAll: function() - { + _selectAll: function() { // requires jQuery id var uuid=this.id.split("-"); var oTable=$("#querytable-"+uuid[1]).dataTable(); @@ -567,16 +489,16 @@ $.plugin('QueryTable', QueryTable); - /* define the 'dom-checkbox' type for sorting in datatables - http://datatables.net/examples/plug-ins/dom_sort.html - using trial and error I found that the actual column number - was in fact given as a third argument, and not second - as the various online resources had it - go figure */ - $.fn.dataTableExt.afnSortData['dom-checkbox'] = function ( oSettings, _, iColumn ) { - return $.map( oSettings.oApi._fnGetTrNodes(oSettings), function (tr, i) { - return result=$('td:eq('+iColumn+') input', tr).prop('checked') ? '1' : '0'; - } ); - } +// /* define the 'dom-checkbox' type for sorting in datatables +// http://datatables.net/examples/plug-ins/dom_sort.html +// using trial and error I found that the actual column number +// was in fact given as a third argument, and not second +// as the various online resources had it - go figure */ +// $.fn.dataTableExt.afnSortData['dom-checkbox'] = function ( oSettings, _, iColumn ) { +// return $.map( oSettings.oApi._fnGetTrNodes(oSettings), function (tr, i) { +// return result=$('td:eq('+iColumn+') input', tr).prop('checked') ? '1' : '0'; +// } ); +// } })(jQuery);