22ba431ba9061abea9828a8834284eb73d6a3204
[myslice.git] / plugins / univbrisfoam / static / js / univbrisfoam.js
1 /**
2  * Description: display a query result in a datatables-powered <table>
3  * Copyright (c) 2012-2013 UPMC Sorbonne Universite - INRIA
4  * License: GPLv3
5  */
6
7 (function($){
8
9     var debug=false;
10     //debug=true
11
12     var UnivbrisFoam = Plugin.extend({
13
14         init: function(options, element) {
15             //alert("foam init called");
16             this.classname="univbrisfoam";
17             this._super(options, element);
18                 
19             //alert(this.options.hidden_columns);
20             /* Member variables */
21             // in general we expect 2 queries here
22             // query_uuid refers to a single object (typically a slice)
23             // query_all_uuid refers to a list (typically resources or users)
24             // these can return in any order so we keep track of which has been received yet
25             this.received_all_query = false;
26             this.received_query = false;
27
28             // We need to remember the active filter for datatables filtering
29             this.filters = Array(); 
30
31             // an internal buffer for records that are 'in' and thus need to be checked 
32             this.buffered_records_to_check = [];
33             // an internal buffer for keeping lines and display them in one call to fnAddData
34             this.buffered_lines = [];
35
36             /* Events */
37             // xx somehow non of these triggers at all for now
38             this.elmt().on('show', this, this.on_show);
39             this.elmt().on('shown.bs.tab', this, this.on_show);
40             this.elmt().on('resize', this, this.on_resize);
41
42             var query = manifold.query_store.find_analyzed_query(this.options.query_uuid);
43             this.object = query.object;
44
45             //// we need 2 different keys
46             // * canonical_key is the primary key as derived from metadata (typically: urn)
47             //   and is used to communicate about a given record with the other plugins
48             // * init_key is a key that both kinds of records 
49             //   (i.e. records returned by both queries) must have (typically: hrn or hostname)
50             //   in general query_all will return well populated records, but query
51             //   returns records with only the fields displayed on startup
52             var keys = manifold.metadata.get_key(this.object);
53             this.canonical_key = (keys && keys.length == 1) ? keys[0] : undefined;
54             // 
55             this.init_key = this.options.init_key;
56             // have init_key default to canonical_key
57             this.init_key = this.init_key || this.canonical_key;
58             // sanity check
59             if ( ! this.init_key ) messages.warning ("UnivbrisFoam : cannot find init_key");
60             if ( ! this.canonical_key ) messages.warning ("UnivbrisFoam : cannot find canonical_key");
61             if (debug) messages.debug("UnivbrisFoam: canonical_key="+this.canonical_key+" init_key="+this.init_key);
62             /* Setup query and record handlers */
63             this.listen_query(options.query_uuid);
64             this.listen_query(options.query_all_uuid, 'all');
65
66             /* GUI setup and event binding */
67             this.initialize_table();
68             
69         },
70
71         /* PLUGIN EVENTS */
72
73         on_show: function(e) {
74             if (debug) messages.debug("univbrisfoam.on_show");
75             var self = e.data;
76             self.table.fnAdjustColumnSizing();
77         },        
78
79         on_resize: function(e) {
80             if (debug) messages.debug("univbrisfoam.on_resize");
81             var self = e.data;
82             self.table.fnAdjustColumnSizing();
83         },        
84
85         /* GUI EVENTS */
86
87         /* GUI MANIPULATION */
88
89         initialize_table: function() 
90         {
91             /* Transforms the table into DataTable, and keep a pointer to it */
92                 //alert("init");
93             var self = this;
94             var actual_options = {
95                 // Customize the position of Datatables elements (length,filter,button,...)
96                 // we use a fluid row on top and another on the bottom, making sure we take 12 grid elt's each time
97                 //sDom: "<'row'<'col-xs-5'l><'col-xs-1'r><'col-xs-6'f>>t<'row'<'col-xs-5'i><'col-xs-7'p>>",
98                 sDom: "<'row'<'col-xs-2'l><'col-xs-9'r><'col-xs-2'f>>t<'row'<'col-xs-5'i><'col-xs-5'p>><'next'>",
99                 // XXX as of sept. 2013, I cannot locate a bootstrap3-friendly mode for now
100                 // hopefully this would come with dataTables v1.10 ?
101                 // in any case, search for 'sPaginationType' all over the code for more comments
102                 sPaginationType: 'bootstrap',
103                 // Handle the null values & the error : Datatables warning Requested unknown parameter
104                 // http://datatables.net/forums/discussion/5331/datatables-warning-...-requested-unknown-parameter/p2
105                 aoColumnDefs: [{sDefaultContent: '',aTargets: [ '_all' ]}],
106                 // WARNING: this one causes tables in a 'tabs' that are not exposed at the time this is run to show up empty
107                 // sScrollX: '100%',       /* Horizontal scrolling */
108                 bProcessing: true,      /* Loading */
109                 fnDrawCallback: function() { self._querytable_draw_callback.call(self);}
110                 //fnFooterCallback: function() {self._univbrisfoam_footer_callback.call(self,nFoot, aData, iStart, iEnd, aiDisplay)};}
111                 // XXX use $.proxy here !
112             };
113             // the intention here is that options.datatables_options as coming from the python object take precedence
114             // xxx DISABLED by jordan: was causing errors in datatables.js
115             // xxx turned back on by Thierry - this is the code that takes python-provided options into account
116             // check your datatables_options tag instead 
117             // however, we have to accumulate in aoColumnDefs from here (above) 
118             // and from the python wrapper (checkboxes management, plus any user-provided aoColumnDefs)
119             if ( 'aoColumnDefs' in this.options.datatables_options) {
120                 actual_options['aoColumnDefs']=this.options.datatables_options['aoColumnDefs'].concat(actual_options['aoColumnDefs']);
121                 delete this.options.datatables_options['aoColumnDefs'];
122             }
123             $.extend(actual_options, this.options.datatables_options );
124             this.table = $("#univbris_foam_ports_selection__table").dataTable(actual_options);
125         
126             //this.table = this.elmt("table").dataTable(actual_options);
127             //alert(this.table);
128
129             /* Setup the SelectAll button in the dataTable header */
130             /* xxx not sure this is still working */
131             var oSelectAll = $('#datatableSelectAll-'+ this.options.plugin_uuid);
132             oSelectAll.html("<span class='glyphicon glyphicon-ok' style='float:right;display:inline-block;'></span>Select All");
133             oSelectAll.button();
134             oSelectAll.css('font-size','11px');
135             oSelectAll.css('float','right');
136             oSelectAll.css('margin-right','15px');
137             oSelectAll.css('margin-bottom','5px');
138             oSelectAll.unbind('click');
139             oSelectAll.click(this._selectAll);
140
141             /* Add a filtering function to the current table 
142              * Note: we use closure to get access to the 'options'
143              */
144             $.fn.dataTableExt.afnFiltering.push(function( oSettings, aData, iDataIndex ) { 
145                 /* No filtering if the table does not match */
146                 if (oSettings.nTable.id != self.options.plugin_uuid + '__table')
147                     return true;
148                 return self._querytable_filter.call(self, oSettings, aData, iDataIndex);
149             });
150
151            //alert(this.options.hidden_columns);
152
153             /* Processing hidden_columns */
154             $.each(this.options.hidden_columns, function(i, field) {
155                 //manifold.raise_event(self.options.query_all_uuid, FIELD_REMOVED, field);
156                 //alert (field);
157                 self.hide_column(field);
158                 //self.hide_column(field);
159             });
160
161             $('<button id="next" type="button" style="height: 50px; width: 300px" onclick="fnButnext()">select flowspace</button>').appendTo('div.next');
162
163             //type="submit"
164
165               //$('<a href="http://localhost:8000/login/" id="next_link">next link</a>').appendTo('div.next');
166             //$("#univbris_flowspace_selection").style.display="none";
167             
168
169         }, // initialize_table
170
171
172         fnButnext:function(e){
173                 e.stopPropagation();
174                 //var sData = this.table.$('input').serialize();
175                 //var sData = $(this).closest('checkboxes__table').find('input').serialize();
176                 //var sData = $("form").find('input').serialize();
177                 //var element=this.table.$("id="'NEC A/80<->NEC B/1080'"");
178                 //var element=this.table.$("[id='"+"NEC A/80<->NEC B/1080"+"']");
179                 //var x = element.checked;
180                 //var uuid=this.id.split("-");
181                 //var oTable=$('checkboxes__table').dataTable();
182                 //var oTable=$.fn.dataTable();
183                 //this.elts("table").unbind('click').click(this, this.fnButnext);
184                 //var sData = $('input', oTable.fnGetNodes()).serialize();
185                 //$.fn.dataTable()
186                 //sData="test";
187                 ///var self = e.data;
188                 //self=self.options.query_uuid;
189                 //var oTable=$("#querytable-"+self).dataTable();
190                 //var sData = $('input', oTable.fnGetNodes()).serialize();
191                 //e.stopPropagation();
192                 //var oTable=$('#myTable').dataTable();
193                 //var sData = $('input', $("#uob_form")).serialize();
194                 //$("#univbris_flowspace_selection").style.visibility='visible';
195                 var sData=$("#uob_form").find("input").serialize();
196                 //alert("clicked: "+sData);
197                 jQuery( "#univbris_flowspace_selection" ).show();
198                 //$("#multi_flowpspace_ports_selected").append('<label><input type="checkbox" name="option[]" value="1" />chocolate</label>');
199
200                 $("form#uob_form :input[type=checkbox]").each(function(){
201                         var input = $(this); // This is the jquery object of the input, do what you will
202                         //alert("id: "+ input.attr('id') + " checked: "+ input.is(':checked'));
203                         if(input.is(':checked')==true){
204                                 //alert("got true");
205                                 $("#multi_flowpspace_ports_selected").append('<label><input type="checkbox" name="'+input.attr('id')+'" />'+input.attr('id')+'</label>');
206                         }
207                 });
208
209                 jQuery( "#univbris_foam_ports_selection" ).hide();
210                 //jQuery( "#univbris_flowspace_selection" ).hide();
211
212                 //var url = "http://localhost:8000/login/";    
213                 //$(location).attr('href',url);
214                 //window.location.href = "http://localhost:8000/login/"
215                 //window.location.replace("http://localhost:8000/login/");
216                 //UnivbrisFv(options,elements);
217
218                         //$("#next_link").click();
219                         
220                 },
221
222         
223
224         /**
225          * @brief Determine index of key in the table columns 
226          * @param key
227          * @param cols
228          */
229         getColIndex: function(key, cols) {
230             var tabIndex = $.map(cols, function(x, i) { if (x.sTitle == key) return i; });
231             return (tabIndex.length > 0) ? tabIndex[0] : -1;
232         }, // getColIndex
233
234         // create a checkbox <input> tag
235         // computes 'id' attribute from canonical_key
236         // computes 'init_id' from init_key for initialization phase
237         // no need to used convoluted ids with plugin-uuid or others, since
238         // we search using table.$ which looks only in this table
239         checkbox_html : function (record) {
240             var result="";
241             // Prefix id with plugin_uuid
242             result += "<input";
243             result += " class='univbrisfoam-checkbox'";
244          // compute id from canonical_key
245             var id = record[this.canonical_key]
246          // compute init_id form init_key
247             var init_id=record[this.init_key];
248          // set id - for retrieving from an id, or for posting events upon user's clicks
249             result += " id='"+record[this.canonical_key]+"'";
250          // set init_id
251             result += "init_id='" + init_id + "'";
252          // wrap up
253             result += " type='checkbox'";
254             result += " autocomplete='off'";
255             result += "></input>";
256             return result;
257         }, 
258
259          fake_checkbox_html : function (record) {
260             //alert("fake fun called");
261             var result="";
262             // Prefix id with plugin_uuid
263             result += "<input";
264             //result += " class='univbrisfoam-checkbox'";
265          // set id - for retrieving from an id, or for posting events upon user's clicks
266             result += " id='"+ record +"'";
267             result += " name='"+ record +"'";
268          // set init_id
269             result += " init_id='" + record + "'";
270          // wrap up
271             result += " type='checkbox'";
272             result += " autocomplete='off'";
273             result += "></input>";
274             ///alert(result);
275             return result;
276         }, 
277
278
279         new_record: function(record)
280         {
281            
282          // this models a line in dataTables, each element in the line describes a cell
283             line = new Array();
284      
285             // go through table headers to get column names we want
286             // in order (we have temporarily hack some adjustments in names)
287             var cols = this.table.fnSettings().aoColumns;
288             var colnames = cols.map(function(x) {return x.sTitle})
289             var nb_col = cols.length;
290             /* if we've requested checkboxes, then forget about the checkbox column for now */
291             if (this.options.checkboxes) nb_col -= 1;
292
293
294             var urn = record['urn'];
295             //alert(urn);
296             var pos = urn.search('link');
297             //filter records to add links only  
298             if (pos!=-1){
299                     //alert(urn);
300                     var link = urn.substring(pos+5);
301                     pos = link.search("_");
302                     var head_node=link.substring(0,pos);
303                     link=link.substring(pos+1);
304                     pos = link.search("_");
305                     var head_port=link.substring(0,pos);
306                     link=link.substring(pos+1);
307                     pos = link.search("_");
308                     var tail_node=link.substring(0,pos);
309                     link=link.substring(pos+1);
310                     //pos = link.search("_");
311                     var tail_port=link;
312                 
313                     
314                     //var head_node=urn.substring(pos+5,pos+28);
315                     //var head_port=urn.substring(pos+29,pos+31);
316                     //var tail_node=urn.substring(pos+32,pos+55);
317                     //var tail_port=urn.substring(pos+56,pos+58);
318                     
319                     //link type int 0-packet, 1-optical, 2-compute
320                     var link_type=0;
321                     if (urn.search('circuit')!=-1){
322                         link_type=1;
323                     }
324                     else if (urn.search('compute')!=-1){
325                         link_type=2;
326                     }
327
328                     //get island name
329                     pos = urn.search('ofam');
330                     var testbed=urn.substring(pos+5);
331                     testbed=testbed.substring(0, testbed.search('link')-1);
332
333                     //alert(urn);
334
335                     //alert("h: "+head_node+"/"+head_port);   
336                     //alert("t: "+tail_node+"/"+tail_port);
337
338
339                     //alert(colnames);
340                     /*replace production*/
341                     /* fill in stuff depending on the column name */
342                     for (var j = 0; j < nb_col; j++) {
343                         if (typeof colnames[j] == 'undefined') {
344                             line.push('...');
345                         } else if (colnames[j] == 'testbed') {
346                            line.push(testbed);
347                         } else if (colnames[j] == 'head node id/port') {
348                             line.push(head_node+"/"+head_port);
349                         }
350                           else if (colnames[j] == 'tail node id/port'){
351                             line.push(tail_node+"/"+tail_port);
352                         }
353                           else if (colnames[j] == 'link type'){
354                             switch (link_type){
355                                 case 0:
356                                         line.push("packet");
357                                         break;
358
359                                 case 1:
360                                         line.push("optical");
361                                         break;
362         
363                                 case 2:
364                                         line.push("compute");
365                                         break;
366                             }
367                         }
368                           else if (colnames[j] == 'selected'){
369                             
370                             line.push(this.fake_checkbox_html(urn));
371                         }
372
373                     }
374                    
375             
376                     // catch up with the last column if checkboxes were requested 
377                     if (this.options.checkboxes) {
378                         // Use a key instead of hostname (hard coded...)
379                         line.push(this.checkbox_html(record));
380                         }
381             
382                     // adding an array in one call is *much* more efficient
383                         // this.table.fnAddData(line);
384                         this.buffered_lines.push(line);
385
386             }
387         },
388
389         clear_table: function()
390         {
391             this.table.fnClearTable();
392         },
393
394         redraw_table: function()
395         {
396             this.table.fnDraw();
397         },
398
399         show_column: function(field)
400         {
401             var oSettings = this.table.fnSettings();
402             var cols = oSettings.aoColumns;
403             var index = this.getColIndex(field,cols);
404             if (index != -1)
405                 this.table.fnSetColumnVis(index, true);
406         },
407
408         hide_column: function(field)
409         {
410             var oSettings = this.table.fnSettings();
411             var cols = oSettings.aoColumns;
412             var index = this.getColIndex(field,cols);
413             //index=-1;
414             //alert(field + ": index: " + index );
415             if (index != -1)
416                 //alert(field + ": hidden with index: " + index );
417                 this.table.fnSetColumnVis(index, false);
418         },
419
420         // this is used at init-time, at which point only init_key can make sense
421         // (because the argument record, if it comes from query, might not have canonical_key set
422         set_checkbox_from_record: function (record, checked) {
423             if (checked === undefined) checked = true;
424             var init_id = record[this.init_key];
425             if (debug) messages.debug("univbrisfoam.set_checkbox_from_record, init_id="+init_id);
426             // using table.$ to search inside elements that are not visible
427             var element = this.table.$('[init_id="'+init_id+'"]');
428             element.attr('checked',checked);
429         },
430
431         // id relates to canonical_key
432         set_checkbox_from_data: function (id, checked) {
433             if (checked === undefined) checked = true;
434             if (debug) messages.debug("univbrisfoam.set_checkbox_from_data, id="+id);
435             // using table.$ to search inside elements that are not visible
436             var element = this.table.$("[id='"+id+"']");
437             element.attr('checked',checked);
438         },
439
440         /*************************** QUERY HANDLER ****************************/
441
442         on_filter_added: function(filter)
443         {
444             this.filters.push(filter);
445             this.redraw_table();
446         },
447
448         on_filter_removed: function(filter)
449         {
450             // Remove corresponding filters
451             this.filters = $.grep(this.filters, function(x) {
452                 return x != filter;
453             });
454             this.redraw_table();
455         },
456         
457         on_filter_clear: function()
458         {
459             // XXX
460             this.redraw_table();
461         },
462
463         on_field_added: function(field)
464         {
465             this.show_column(field);
466         },
467
468         on_field_removed: function(field)
469         {
470             this.hide_column(field);
471         },
472
473         on_field_clear: function()
474         {
475             alert('UnivbrisFoam::clear_fields() not implemented');
476         },
477
478         /* XXX TODO: make this generic a plugin has to subscribe to a set of Queries to avoid duplicated code ! */
479         /*************************** ALL QUERY HANDLER ****************************/
480
481         on_all_filter_added: function(filter)
482         {
483             // XXX
484             this.redraw_table();
485         },
486
487         on_all_filter_removed: function(filter)
488         {
489             // XXX
490             this.redraw_table();
491         },
492         
493         on_all_filter_clear: function()
494         {
495             // XXX
496             this.redraw_table();
497         },
498
499         on_all_field_added: function(field)
500         {
501             this.show_column(field);
502         },
503
504         on_all_field_removed: function(field)
505         {
506             this.hide_column(field);
507         },
508
509         on_all_field_clear: function()
510         {
511             alert('UnivbrisFoam::clear_fields() not implemented');
512         },
513
514
515         /*************************** RECORD HANDLER ***************************/
516
517         on_new_record: function(record)
518         {
519             if (this.received_all_query) {
520                 // if the 'all' query has been dealt with already we may turn on the checkbox
521                 this.set_checkbox_from_record(record, true);
522             } else {
523                 this.buffered_records_to_check.push(record);
524             }
525         },
526
527         on_clear_records: function()
528         {
529         },
530
531         // Could be the default in parent
532         on_query_in_progress: function()
533         {
534             this.spin();
535         },
536
537         on_query_done: function()
538         {
539             this.received_query = true;
540             // unspin once we have received both
541             if (this.received_all_query && this.received_query) this.unspin();
542         },
543         
544         on_field_state_changed: function(data)
545         {
546             switch(data.request) {
547                 case FIELD_REQUEST_ADD:
548                 case FIELD_REQUEST_ADD_RESET:
549                     this.set_checkbox_from_data(data.value, true);
550                     break;
551                 case FIELD_REQUEST_REMOVE:
552                 case FIELD_REQUEST_REMOVE_RESET:
553                     this.set_checkbox_from_data(data.value, false);
554                     break;
555                 default:
556                     break;
557             }
558         },
559
560         /* XXX TODO: make this generic a plugin has to subscribe to a set of Queries to avoid duplicated code ! */
561         // all
562         on_all_field_state_changed: function(data)
563         {
564             switch(data.request) {
565                 case FIELD_REQUEST_ADD:
566                 case FIELD_REQUEST_ADD_RESET:
567                     this.set_checkboxfrom_data(data.value, true);
568                     break;
569                 case FIELD_REQUEST_REMOVE:
570                 case FIELD_REQUEST_REMOVE_RESET:
571                     this.set_checkbox_from_data(data.value, false);
572                     break;
573                 default:
574                     break;
575             }
576         },
577
578         on_all_new_record: function(record)
579         {
580             this.new_record(record);
581         },
582
583         on_all_clear_records: function()
584         {
585             this.clear_table();
586
587         },
588
589         on_all_query_in_progress: function()
590         {
591             // XXX parent
592             this.spin();
593         }, // on_all_query_in_progress
594
595         on_all_query_done: function()
596         {
597             if (debug) messages.debug("1-shot initializing dataTables content with " + this.buffered_lines.length + " lines");
598             this.table.fnAddData (this.buffered_lines);
599             this.buffered_lines=[];
600             
601             var self = this;
602             // if we've already received the slice query, we have not been able to set 
603             // checkboxes on the fly at that time (dom not yet created)
604             $.each(this.buffered_records_to_check, function(i, record) {
605                 if (debug) messages.debug ("querytable delayed turning on checkbox " + i + " record= " + record);
606                 self.set_checkbox_from_record(record, true);
607             });
608             this.buffered_records_to_check = [];
609
610             this.received_all_query = true;
611             // unspin once we have received both
612             if (this.received_all_query && this.received_query) this.unspin();
613
614         }, // on_all_query_done
615
616         /************************** PRIVATE METHODS ***************************/
617
618         /** 
619          * @brief QueryTable filtering function
620          */
621         _querytable_filter: function(oSettings, aData, iDataIndex)
622         {
623             var ret = true;
624             $.each (this.filters, function(index, filter) { 
625                 /* XXX How to manage checkbox ? */
626                 var key = filter[0]; 
627                 var op = filter[1];
628                 var value = filter[2];
629
630                 /* Determine index of key in the table columns */
631                 var col = $.map(oSettings.aoColumns, function(x, i) {if (x.sTitle == key) return i;})[0];
632
633                 /* Unknown key: no filtering */
634                 if (typeof(col) == 'undefined')
635                     return;
636
637                 col_value=unfold.get_value(aData[col]);
638                 /* Test whether current filter is compatible with the column */
639                 if (op == '=' || op == '==') {
640                     if ( col_value != value || col_value==null || col_value=="" || col_value=="n/a")
641                         ret = false;
642                 }else if (op == '!=') {
643                     if ( col_value == value || col_value==null || col_value=="" || col_value=="n/a")
644                         ret = false;
645                 } else if(op=='<') {
646                     if ( parseFloat(col_value) >= value || col_value==null || col_value=="" || col_value=="n/a")
647                         ret = false;
648                 } else if(op=='>') {
649                     if ( parseFloat(col_value) <= value || col_value==null || col_value=="" || col_value=="n/a")
650                         ret = false;
651                 } else if(op=='<=' || op=='≤') {
652                     if ( parseFloat(col_value) > value || col_value==null || col_value=="" || col_value=="n/a")
653                         ret = false;
654                 } else if(op=='>=' || op=='≥') {
655                     if ( parseFloat(col_value) < value || col_value==null || col_value=="" || col_value=="n/a")
656                         ret = false;
657                 }else{
658                     // How to break out of a loop ?
659                     alert("filter not supported");
660                     return false;
661                 }
662
663             });
664             return ret;
665         },
666
667         _querytable_draw_callback: function()
668         {
669             /* 
670              * Handle clicks on checkboxes: reassociate checkbox click every time
671              * the table is redrawn 
672              */
673             this.elts('querytable-checkbox').unbind('click').click(this, this._check_click);
674             this.elts("next").unbind('click').click(this, this.fnButnext);
675             
676
677             if (!this.table)
678                 return;
679
680             /* Remove pagination if we show only a few results */
681             var wrapper = this.table; //.parent().parent().parent();
682             var rowsPerPage = this.table.fnSettings()._iDisplayLength;
683             var rowsToShow = this.table.fnSettings().fnRecordsDisplay();
684             var minRowsPerPage = this.table.fnSettings().aLengthMenu[0];
685
686             if ( rowsToShow <= rowsPerPage || rowsPerPage == -1 ) {
687                 $('.querytable_paginate', wrapper).css('visibility', 'hidden');
688             } else {
689                 $('.querytable_paginate', wrapper).css('visibility', 'visible');
690             }
691
692             if ( rowsToShow <= minRowsPerPage ) {
693                 $('.querytable_length', wrapper).css('visibility', 'hidden');
694             } else {
695                 $('.querytable_length', wrapper).css('visibility', 'visible');
696             }
697
698         },
699
700
701
702         _check_click: function(e) 
703         {
704             e.stopPropagation();
705
706             var self = e.data;
707             var id=this.id;
708
709             // this.id = key of object to be added... what about multiple keys ?
710             if (debug) messages.debug("querytable._check_click key="+this.canonical_key+"->"+id+" checked="+this.checked);
711             manifold.raise_event(self.options.query_uuid, this.checked?SET_ADD:SET_REMOVED, id);
712             //return false; // prevent checkbox to be checked, waiting response from manifold plugin api
713             
714         },
715
716         _selectAll: function() 
717         {
718             // requires jQuery id
719             var uuid=this.id.split("-");
720             var oTable=$("#querytable-"+uuid[1]).dataTable();
721             // Function available in QueryTable 1.9.x
722             // Filter : displayed data only
723             var filterData = oTable._('tr', {"filter":"applied"});   
724             /* TODO: WARNING if too many nodes selected, use filters to reduce nuber of nodes */        
725             if(filterData.length<=100){
726                 $.each(filterData, function(index, obj) {
727                     var last=$(obj).last();
728                     var key_value=unfold.get_value(last[0]);
729                     if(typeof($(last[0]).attr('checked'))=="undefined"){
730                         $.publish('selected', 'add/'+key_value);
731                     }
732                 });
733             }
734         },
735
736     });
737
738     $.plugin('UnivbrisFoam', UnivbrisFoam);
739
740   /* define the 'dom-checkbox' type for sorting in datatables 
741      http://datatables.net/examples/plug-ins/dom_sort.html
742      using trial and error I found that the actual column number
743      was in fact given as a third argument, and not second 
744      as the various online resources had it - go figure */
745     $.fn.dataTableExt.afnSortData['dom-checkbox'] = function  ( oSettings, _, iColumn ) {
746         return $.map( oSettings.oApi._fnGetTrNodes(oSettings), function (tr, i) {
747             return result=$('td:eq('+iColumn+') input', tr).prop('checked') ? '1' : '0';
748         } );
749     }
750
751 })(jQuery);
752