Plugin VTAM by Frederic Francois (Univ Bristol) integrated into slice view
[myslice.git] / plugins / univbrisvtam / static / js / univbrisvtam.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     pk_mode=0;
15     fvf_add=1;
16     fvf_nrow=0;
17     
18
19     var UnivbrisVtam = Plugin.extend({
20
21         
22         init: function(options, element) {
23             this.classname="univbrisvtam";
24             this._super(options, element);
25                 
26                 this.elmt().on('show', this, this.on_show);
27             this.elmt().on('shown.bs.tab', this, this.on_show);
28             this.elmt().on('resize', this, this.on_resize);
29
30             // We need to remember the active filter for datatables filtering
31             this.filters = Array(); 
32
33             // an internal buffer for records that are 'in' and thus need to be checked 
34             this.buffered_records_to_check = [];
35             // an internal buffer for keeping lines and display them in one call to fnAddData
36             this.buffered_lines = [];
37
38             var keys = manifold.metadata.get_key(this.object);
39             this.canonical_key = (keys && keys.length == 1) ? keys[0] : undefined;
40             this.init_key = this.options.init_key;
41             // have init_key default to canonical_key
42             this.init_key = this.init_key || this.canonical_key;
43             // sanity check
44             if ( ! this.init_key ) messages.warning ("UnivbrisVtam : cannot find init_key");
45             if ( ! this.canonical_key ) messages.warning ("UnivbrisVtam : cannot find canonical_key");
46             if (debug) messages.debug("UnivbrisVtam: canonical_key="+this.canonical_key+" init_key="+this.init_key);
47
48             /* GUI setup and event binding */
49             this.initialize_table();
50             
51         },
52
53         /* PLUGIN EVENTS */
54
55         on_show: function(e) {
56             if (debug) messages.debug("UnivbrisVtam.on_show");
57             var self = e.data;
58             self.table.fnAdjustColumnSizing();
59         },        
60
61         on_resize: function(e) {
62             if (debug) messages.debug("UnivbrisVtam.on_resize");
63             var self = e.data;
64             self.table.fnAdjustColumnSizing();
65         },        
66
67         /* GUI EVENTS */
68
69         /* GUI MANIPULATION */
70
71         initialize_table: function() 
72         {
73
74             
75             /* Transforms the table into DataTable, and keep a pointer to it */
76             var self = this;
77             //alert(self.options);
78             var actual_options = {
79                 // Customize the position of Datatables elements (length,filter,button,...)
80                 // we use a fluid row on top and another on the bottom, making sure we take 12 grid elt's each time
81                 //sDom: "<'row'<'col-xs-5'l><'col-xs-1'r><'col-xs-6'f>>t<'row'<'col-xs-5'i><'col-xs-7'p>>",
82                 sDom: "<'row'<'col-xs-5'l><'col-xs-5'r><'col-xs-1'r><'col-xs-6'f>>t<'row'<'col-xs-5'i><'col-xs-7'p>><'buttons'>",
83                 //sDom: "<'row'<'col-xs-9'r>t<'buttons'>",
84                 // XXX as of sept. 2013, I cannot locate a bootstrap3-friendly mode for now
85                 // hopefully this would come with dataTables v1.10 ?
86                 // in any case, search for 'sPaginationType' all over the code for more comments
87                 sPaginationType: 'bootstrap',
88                 // Handle the null values & the error : Datatables warning Requested unknown parameter
89                 // http://datatables.net/forums/discussion/5331/datatables-warning-...-requested-unknown-parameter/p2
90                 aoColumnDefs: [{sDefaultContent: '',aTargets: [ '_all' ]}],
91                 // WARNING: this one causes tables in a 'tabs' that are not exposed at the time this is run to show up empty
92                 // sScrollX: '100%',       /* Horizontal scrolling */
93                 bProcessing: true,      /* Loading */
94                 fnDrawCallback: function() { self._querytable_draw_callback.call(self);}
95                 //fnFooterCallback: function() {self._UnivbrisVtam_footer_callback.call(self,nFoot, aData, iStart, iEnd, aiDisplay)};}
96                 // XXX use $.proxy here !
97             };
98             // the intention here is that options.datatables_options as coming from the python object take precedence
99             // xxx DISABLED by jordan: was causing errors in datatables.js
100             // xxx turned back on by Thierry - this is the code that takes python-provided options into account
101             // check your datatables_options tag instead 
102             // however, we have to accumulate in aoColumnDefs from here (above) 
103             // and from the python wrapper (checkboxes management, plus any user-provided aoColumnDefs)
104             if ( 'aoColumnDefs' in this.options.datatables_options) {
105                 actual_options['aoColumnDefs']=this.options.datatables_options['aoColumnDefs'].concat(actual_options['aoColumnDefs']);
106                 delete this.options.datatables_options['aoColumnDefs'];
107             }
108             $.extend(actual_options, this.options.datatables_options );
109
110             
111             this.table = $("#univbris_vtam__table").dataTable(actual_options);  
112             
113             
114             
115             //alert(this.table.$("name"));
116
117             /* Setup the SelectAll button in the dataTable header */
118             /* xxx not sure this is still working */
119             var oSelectAll = $('#datatableSelectAll-'+ this.options.plugin_uuid);
120             oSelectAll.html("<span class='glyphicon glyphicon-ok' style='float:right;display:inline-block;'></span>Select All");
121             oSelectAll.button();
122             oSelectAll.css('font-size','11px');
123             oSelectAll.css('float','right');
124             oSelectAll.css('margin-right','15px');
125             oSelectAll.css('margin-bottom','5px');
126             oSelectAll.unbind('click');
127             oSelectAll.click(this._selectAll);
128
129             /* Add a filtering function to the current table 
130              * Note: we use closure to get access to the 'options'
131              */
132             $.fn.dataTableExt.afnFiltering.push(function( oSettings, aData, iDataIndex ) { 
133                 /* No filtering if the table does not match */
134                 if (oSettings.nTable.id != self.options.plugin_uuid + '__table')
135                     return true;
136                 return self._querytable_filter.call(self, oSettings, aData, iDataIndex);
137             });
138
139             /* Processing hidden_columns */
140             $.each(this.options.hidden_columns, function(i, field) {
141                 //manifold.raise_event(self.options.query_all_uuid, FIELD_REMOVED, field);
142                 //alert (field);
143                 self.hide_column(field);
144                 //self.hide_column(field);
145             });
146
147
148             this._querytable_draw_callback();   
149
150         }, // initialize_table
151
152
153         fnCreateVms:function(e){
154                 console.log("building json to send to backend");
155
156                 var testbeds=[];
157                 
158
159                 try{
160                          var rows = $("#univbris_vtam__table").dataTable().fnGetNodes();
161                          if (rows.length<=0){
162                                 throw("no vm specified");
163                          }
164                          else{
165                                 for(var i=0;i<rows.length;i++){
166                                         //console.log(rows[i].cells[2].outerHTML.text());
167                                         var htmlStr=rows[i].cells[2].outerHTML;
168                                         var parser=new DOMParser();
169                                         var htmlDoc=parser.parseFromString(htmlStr, "text/html");
170                                         var para=htmlDoc.getElementsByTagName('p');
171                                         //console.log(para.item(0).id);
172                                         
173                                         //build row json
174                                         var tp_row=para.item(0).id.split(",");
175                                         var d_row={};
176                                         for (var a=0;a<tp_row.length;a++){
177                                                 var tp=tp_row[a].split(":");
178                                                 d_row[tp[0]]=tp[1];
179                                         }
180
181                                         function findTestbedIndex( _testbeds, _name){
182                                                 var index=-1;
183                                                 for(var a=0;a<_testbeds.length;a++){
184                                                         if(_testbeds[a].name==_name){
185                                                                 index=a;
186                                                         }
187                                                 }
188                                                 return index;
189                                         }
190
191                                         function findServerIndex( _testbeds, _testbedindex, _name){
192                                                 var index=-1;
193                                                 for(var a=0;a<_testbeds[_testbedindex].vt_servers.length;a++){
194                                                         if(_testbeds[_testbedindex].vt_servers[a].name==_name){
195                                                                 index=a;
196                                                         }
197                                                 }
198                                                 return index;
199                                         }
200                                         
201                                         var testbedindex=findTestbedIndex(testbeds,d_row['testbed']);
202                                         if (testbedindex!=-1){
203                                                 var serverindex=findServerIndex(testbeds,testbedindex,d_row['vt_server']);
204                                                 if (serverindex!=-1){
205                                                         //console.log("server:",serverindex);
206                                                         //console.log(testbeds[testbedindex].vt_servers[serverindex].vm_names);
207                                                         testbeds[testbedindex].vt_servers[serverindex].vm_names.push(d_row['vm_name']);
208                                                 }
209                                                 else{
210                                                         var vt_server={};
211                                                         vt_server['name']=d_row['vt_server'];
212                                                         vt_server['vm_names']=[];
213                                                         vt_server['vm_names'].push(d_row['vm_name']);
214                                                         testbeds[testbedindex].vt_servers.push(vt_server);
215                                                 }
216
217                                         }
218                                         else{
219                                                 var testbed={};
220                                                 testbed['name']=d_row['testbed'];
221                                                 testbed['vt_servers']=[];
222                                                 var vt_server={};
223                                                 vt_server['name']=d_row['vt_server'];
224                                                 vt_server['vm_names']=[];
225                                                 vt_server['vm_names'].push(d_row['vm_name']);
226                                                 testbed['vt_servers'].push(vt_server);
227                                                 testbeds.push(testbed);
228                                         }
229
230                                 }
231
232                                 var rspecs = JSON.stringify(testbeds);
233                                 console.log(rspecs);
234                         }
235
236                 }
237                 catch(err){
238                         alert(err)
239                 }               
240         },
241
242
243
244         fnAddVm:function(e){
245                 //this.hideFvfError(e);
246                 $('#uob_vm_name').removeClass('error');
247                 $("#uob_vm_name").val("");
248                 jQuery('#uob_vm_name_error').hide();
249                 jQuery("#univbris_vtam").hide();
250                 jQuery("#univbris_vtam_form").show();
251         },
252
253         
254
255
256
257
258         
259
260         fnDelete:function(e){
261                         e.preventDefault();
262                         var nRow = $(this).parents('tr')[0];
263                         this.table = $("#univbris_vtam__table").dataTable();
264                         this.table.fnDeleteRow( nRow );
265         },
266
267
268
269
270         
271
272         /**
273          * @brief Determine index of key in the table columns 
274          * @param key
275          * @param cols
276          */
277         getColIndex: function(key, cols) {
278             var tabIndex = $.map(cols, function(x, i) { if (x.sTitle == key) return i; });
279             return (tabIndex.length > 0) ? tabIndex[0] : -1;
280         }, // getColIndex
281
282
283         
284
285         new_record: function(record)
286         {
287            
288         },
289
290         clear_table: function()
291         {
292             this.table.fnClearTable();
293         },
294
295         redraw_table: function()
296         {
297             this.table.fnDraw();
298         },
299
300         show_column: function(field)
301         {
302             var oSettings = this.table.fnSettings();
303             var cols = oSettings.aoColumns;
304             var index = this.getColIndex(field,cols);
305             if (index != -1)
306                 this.table.fnSetColumnVis(index, true);
307         },
308
309         hide_column: function(field)
310         {
311             var oSettings = this.table.fnSettings();
312             var cols = oSettings.aoColumns;
313             var index = this.getColIndex(field,cols);
314             //index=-1;
315             //alert(field + ": index: " + index );
316             if (index != -1)
317                 //alert(field + ": hidden with index: " + index );
318                 this.table.fnSetColumnVis(index, false);
319         },
320
321         // this is used at init-time, at which point only init_key can make sense
322         // (because the argument record, if it comes from query, might not have canonical_key set
323         set_checkbox_from_record: function (record, checked) {
324             if (checked === undefined) checked = true;
325             var init_id = record[this.init_key];
326             if (debug) messages.debug("UnivbrisVtam.set_checkbox_from_record, init_id="+init_id);
327             // using table.$ to search inside elements that are not visible
328             var element = this.table.$('[init_id="'+init_id+'"]');
329             element.attr('checked',checked);
330         },
331
332         // id relates to canonical_key
333         set_checkbox_from_data: function (id, checked) {
334             if (checked === undefined) checked = true;
335             if (debug) messages.debug("UnivbrisVtam.set_checkbox_from_data, id="+id);
336             // using table.$ to search inside elements that are not visible
337             var element = this.table.$("[id='"+id+"']");
338             element.attr('checked',checked);
339         },
340
341         /*************************** QUERY HANDLER ****************************/
342
343         on_filter_added: function(filter)
344         {
345             this.filters.push(filter);
346             this.redraw_table();
347         },
348
349         on_filter_removed: function(filter)
350         {
351             // Remove corresponding filters
352             this.filters = $.grep(this.filters, function(x) {
353                 return x != filter;
354             });
355             this.redraw_table();
356         },
357         
358         on_filter_clear: function()
359         {
360             // XXX
361             this.redraw_table();
362         },
363
364         on_field_added: function(field)
365         {
366             this.show_column(field);
367         },
368
369         on_field_removed: function(field)
370         {
371             this.hide_column(field);
372         },
373
374         on_field_clear: function()
375         {
376             alert('UnivbrisVtam::clear_fields() not implemented');
377         },
378
379         /* XXX TODO: make this generic a plugin has to subscribe to a set of Queries to avoid duplicated code ! */
380         /*************************** ALL QUERY HANDLER ****************************/
381
382         on_all_filter_added: function(filter)
383         {
384             // XXX
385             this.redraw_table();
386         },
387
388         on_all_filter_removed: function(filter)
389         {
390             // XXX
391             this.redraw_table();
392         },
393         
394         on_all_filter_clear: function()
395         {
396             // XXX
397             this.redraw_table();
398         },
399
400         on_all_field_added: function(field)
401         {
402             this.show_column(field);
403         },
404
405         on_all_field_removed: function(field)
406         {
407             this.hide_column(field);
408         },
409
410         on_all_field_clear: function()
411         {
412             alert('UnivbrisVtam::clear_fields() not implemented');
413         },
414
415
416         /*************************** RECORD HANDLER ***************************/
417
418         on_new_record: function(record)
419         {
420         },
421
422         on_clear_records: function()
423         {
424         },
425
426         // Could be the default in parent
427         on_query_in_progress: function()
428         {
429             this.spin();
430         },
431
432         on_query_done: function()
433         {
434             this.received_query = true;
435             // unspin once we have received both
436             if (this.received_all_query && this.received_query) this.unspin();
437         },
438         
439         on_field_state_changed: function(data)
440         {
441             switch(data.request) {
442                 case FIELD_REQUEST_ADD:
443                 case FIELD_REQUEST_ADD_RESET:
444                     this.set_checkbox_from_data(data.value, true);
445                     break;
446                 case FIELD_REQUEST_REMOVE:
447                 case FIELD_REQUEST_REMOVE_RESET:
448                     this.set_checkbox_from_data(data.value, false);
449                     break;
450                 default:
451                     break;
452             }
453         },
454
455         /* XXX TODO: make this generic a plugin has to subscribe to a set of Queries to avoid duplicated code ! */
456         // all
457         on_all_field_state_changed: function(data)
458         {
459             switch(data.request) {
460                 case FIELD_REQUEST_ADD:
461                 case FIELD_REQUEST_ADD_RESET:
462                     this.set_checkboxfrom_data(data.value, true);
463                     break;
464                 case FIELD_REQUEST_REMOVE:
465                 case FIELD_REQUEST_REMOVE_RESET:
466                     this.set_checkbox_from_data(data.value, false);
467                     break;
468                 default:
469                     break;
470             }
471         },
472
473         on_all_new_record: function(record)
474         {
475             this.new_record(record);
476         },
477
478         on_all_clear_records: function()
479         {
480             this.clear_table();
481
482         },
483
484         on_all_query_in_progress: function()
485         {
486             // XXX parent
487             this.spin();
488         }, // on_all_query_in_progress
489
490         on_all_query_done: function()
491         {
492             if (debug) messages.debug("1-shot initializing dataTables content with " + this.buffered_lines.length + " lines");
493
494
495             //this.table.fnAddData (this.buffered_lines);
496             this.buffered_lines=[];
497             
498             var self = this;
499             // if we've already received the slice query, we have not been able to set 
500             // checkboxes on the fly at that time (dom not yet created)
501             $.each(this.buffered_records_to_check, function(i, record) {
502                 if (debug) messages.debug ("querytable delayed turning on checkbox " + i + " record= " + record);
503                 self.set_checkbox_from_record(record, true);
504             });
505             this.buffered_records_to_check = [];
506
507             this.received_all_query = true;
508             // unspin once we have received both
509             if (this.received_all_query && this.received_query) this.unspin();
510
511         }, // on_all_query_done
512
513         /************************** PRIVATE METHODS ***************************/
514
515         /** 
516          * @brief QueryTable filtering function
517          */
518         _querytable_filter: function(oSettings, aData, iDataIndex)
519         {
520             var ret = true;
521             $.each (this.filters, function(index, filter) { 
522                 /* XXX How to manage checkbox ? */
523                 var key = filter[0]; 
524                 var op = filter[1];
525                 var value = filter[2];
526
527                 /* Determine index of key in the table columns */
528                 var col = $.map(oSettings.aoColumns, function(x, i) {if (x.sTitle == key) return i;})[0];
529
530                 /* Unknown key: no filtering */
531                 if (typeof(col) == 'undefined')
532                     return;
533
534                 col_value=unfold.get_value(aData[col]);
535                 /* Test whether current filter is compatible with the column */
536                 if (op == '=' || op == '==') {
537                     if ( col_value != value || col_value==null || col_value=="" || col_value=="n/a")
538                         ret = false;
539                 }else if (op == '!=') {
540                     if ( col_value == value || col_value==null || col_value=="" || col_value=="n/a")
541                         ret = false;
542                 } else if(op=='<') {
543                     if ( parseFloat(col_value) >= value || col_value==null || col_value=="" || col_value=="n/a")
544                         ret = false;
545                 } else if(op=='>') {
546                     if ( parseFloat(col_value) <= value || col_value==null || col_value=="" || col_value=="n/a")
547                         ret = false;
548                 } else if(op=='<=' || op=='≤') {
549                     if ( parseFloat(col_value) > value || col_value==null || col_value=="" || col_value=="n/a")
550                         ret = false;
551                 } else if(op=='>=' || op=='≥') {
552                     if ( parseFloat(col_value) < value || col_value==null || col_value=="" || col_value=="n/a")
553                         ret = false;
554                 }else{
555                     // How to break out of a loop ?
556                     alert("filter not supported");
557                     return false;
558                 }
559
560             });
561             return ret;
562         },
563
564         _querytable_draw_callback: function()
565         {
566             /* 
567              * Handle clicks on checkboxes: reassociate checkbox click every time
568              * the table is redrawn    
569              */
570             this.elts('querytable-checkbox').unbind('click').click(this, this._check_click);
571             $("#add_vm").unbind('click').click(this, this.fnAddVm);
572             $("#submit_vms").unbind('click').click(this, this.fnCreateVms);
573             $('#univbris_vtam__table .delete').unbind('click').click(this, this.fnDelete);
574             /*var edits=this.elts.all.find('.edit');
575             edits.each(function(){
576                 this.unbind('click').click(this,this.fnEdit);
577              }
578             */
579             if (!this.table)
580                 return;
581
582             /* Remove pagination if we show only a few results */
583             var wrapper = this.table; //.parent().parent().parent();
584             var rowsPerPage = this.table.fnSettings()._iDisplayLength;
585             var rowsToShow = this.table.fnSettings().fnRecordsDisplay();
586             var minRowsPerPage = this.table.fnSettings().aLengthMenu[0];
587
588             if ( rowsToShow <= rowsPerPage || rowsPerPage == -1 ) {
589                 $('.querytable_paginate', wrapper).css('visibility', 'hidden');
590             } else {
591                 $('.querytable_paginate', wrapper).css('visibility', 'visible');
592             }
593
594             if ( rowsToShow <= minRowsPerPage ) {
595                 $('.querytable_length', wrapper).css('visibility', 'hidden');
596             } else {
597                 $('.querytable_length', wrapper).css('visibility', 'visible');
598             }
599
600             
601
602
603         },
604
605
606
607         _check_click: function(e) 
608         {
609             e.stopPropagation();
610
611             var self = e.data;
612             var id=this.id;
613
614             // this.id = key of object to be added... what about multiple keys ?
615             if (debug) messages.debug("querytable._check_click key="+this.canonical_key+"->"+id+" checked="+this.checked);
616             //manifold.raise_event(self.options.query_uuid, this.checked?SET_ADD:SET_REMOVED, id);
617             //return false; // prevent checkbox to be checked, waiting response from manifold plugin api
618             
619         },
620
621         _selectAll: function() 
622         {
623             // requires jQuery id
624             var uuid=this.id.split("-");
625             var oTable=$("#querytable-"+uuid[1]).dataTable();
626             // Function available in QueryTable 1.9.x
627             // Filter : displayed data only
628             var filterData = oTable._('tr', {"filter":"applied"});   
629             /* TODO: WARNING if too many nodes selected, use filters to reduce nuber of nodes */        
630             if(filterData.length<=100){
631                 $.each(filterData, function(index, obj) {
632                     var last=$(obj).last();
633                     var key_value=unfold.get_value(last[0]);
634                     if(typeof($(last[0]).attr('checked'))=="undefined"){
635                         $.publish('selected', 'add/'+key_value);
636                     }
637                 });
638             }
639         },
640
641     });
642
643    
644
645     $.plugin('UnivbrisVtam', UnivbrisVtam);
646
647   /* define the 'dom-checkbox' type for sorting in datatables 
648      http://datatables.net/examples/plug-ins/dom_sort.html
649      using trial and error I found that the actual column number
650      was in fact given as a third argument, and not second 
651      as the various online resources had it - go figure */
652     $.fn.dataTableExt.afnSortData['dom-checkbox'] = function  ( oSettings, _, iColumn ) {
653         return $.map( oSettings.oApi._fnGetTrNodes(oSettings), function (tr, i) {
654             return result=$('td:eq('+iColumn+') input', tr).prop('checked') ? '1' : '0';
655         } );
656     }
657
658    
659
660 })(jQuery);
661
662
663