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