SLA and Service Directory code added
[unfold.git] / plugins / queryupdater / static / js / queryupdater.js
1 /**
2  * MySlice QueryUpdater plugin
3  * Version: 0.1.0
4  * URL: http://www.myslice.info
5  * Description: display of selected resources
6  * Requires: 
7  * Author: The MySlice Team
8  * Copyright: Copyright 2012 UPMC Sorbonne Universités
9  * License: GPLv3
10  */
11
12 (function( $ ){
13
14     var debug=false;
15     debug=true;
16
17     // XXX record selected (multiple selection ?)
18     // XXX record disabled ?
19     // XXX out of sync plugin ?
20     // XXX out of date record ?
21     // record tags ???
22     //
23     // criticality of the absence of a handler in a plugin
24     // non-critical only can have switch case
25     // 
26     // Record state through the query cycle
27
28
29     var QueryUpdater = Plugin.extend({
30
31         init: function(options, element) {
32             this.classname="queryupdater";
33             this._super(options, element);
34
35             var self = this;
36             this.table = this.elmt('table').dataTable({
37 // the original querytable layout was
38 //                sDom: "<'row'<'col-xs-5'l><'col-xs-1'r><'col-xs-6'f>>t<'row'<'col-xs-5'i><'col-xs-7'p>>",
39 // however the bottom line with 'showing blabla...' and the navigation widget are not really helpful
40                 sDom: "<'row'<'col-xs-5'l><'col-xs-1'r><'col-xs-6'f>>t>",
41 // so this does not matter anymore now that the pagination area is turned off
42 //                sPaginationType: 'bootstrap',
43                 bAutoWidth: true,
44 //                bJQueryUI      : true,
45 //                bRetrieve      : true,
46 //                sScrollX       : '100%',                 // Horizontal scrolling 
47 //                bSortClasses   : false,              // Disable style for the sorted column
48 //                aaSorting      : [[ 0, 'asc' ]],        // Default sorting on URN
49 //                fnDrawCallback: function() {      // Reassociate close click every time the table is redrawn
50 //                    /* Prevent to loop on click while redrawing table  */
51 //                    $('.ResourceSelectedClose').unbind('click');
52 //                    /* Handle clicks on close span */
53 //                    /* Reassociate close click every time the table is redrawn */
54 //                    $('.ResourceSelectedClose').bind('click', self, self._close_click);
55 //                }
56              });
57             
58             // XXX This should not be done at init...
59             this.elmt('update').click(this, this.do_update);
60             this.elmt('refresh').click(this, this.do_refresh);
61             this.elmt('reset').click(this, this.do_reset);
62             this.elmt('clear_annotations').click(this, this.do_clear_annotations);
63
64             this.listen_query(options.query_uuid);
65         },
66
67         /*************************** PLUGIN EVENTS ****************************/
68
69         /***************************** GUI EVENTS *****************************/
70
71         do_update: function(e) {
72             var self = e.data;
73             var username = e.data.options.username;
74             var urn = data.value;
75             // XXX check that the query is not disabled
76
77             console.log("DATA VALUE: " + data.value);
78
79             if (data.value.toLowerCase().indexOf("iminds") >= 0){
80
81                 $('#sla_dialog').show();
82                 $('#slamodal').modal('show');
83                 
84                 $(document).ready(function() {
85                     $("#accept_sla").click(function(){
86                         console.log("SLA ACCEPTED");
87                         console.log("With username: " + username);
88                         if(urn.toLowerCase().indexOf("wall2") >= 0){ 
89                             $.post("/sla/agreements/simplecreate", 
90                                 { "template_id": "iMindsServiceVirtualwall",
91                                   "user": username,
92                                   "expiration_time": new Date() // jgarcia: FIXME
93                                 });
94                         } else if(urn.toLowerCase().indexOf("wilab2") >= 0){
95                             $.post("/sla/agreements/simplecreate", 
96                                 { "template_id":"iMindsServiceWiLab2",
97                                   "user":username,
98                                   "expiration_time": new Date() // jgarcia: FIXME
99                                 });
100                         }
101                         $('#slamodal').modal('hide');
102                         console.log("Executing raise_event after sending SLA");
103                         // manifold.raise_event(self.options.query_uuid, RUN_UPDATE);
104                     }); 
105                 });
106
107                 $(document).ready(function() {
108                     $("#dismiss_sla").click(function(){
109                         console.log("SLA NOT ACCEPTED");
110                         $('#slamodal').modal('hide');
111                     }); 
112                 });
113
114             } else {
115                 console.log("Executing raise_event");
116                 manifold.raise_event(self.options.query_uuid, RUN_UPDATE);
117             }
118         },
119
120         // related buttons are also disabled in the html template
121         do_refresh: function(e)
122         {
123             throw 'resource_selected.do_refresh Not implemented';
124         },
125
126         do_reset: function(e)
127         {
128             throw 'queryupdater.do_reset Not implemented';
129         },
130
131         do_clear_annotations: function(e)
132         {
133             throw 'queryupdater.do_clear_annotations Not implemented';
134         },
135         
136         /************************** GUI MANIPULATION **************************/
137
138         
139         set_button_state: function(name, state)
140         {
141             this.elmt(name).attr('disabled', state ? false : 'disabled');
142         },
143
144         clear: function()
145         {
146
147         },
148
149         find_row: function(key)
150         {
151             // key in third position, column id = 2
152             var KEY_POS = 2;
153
154             var cols = $.grep(this.table.fnSettings().aoData, function(col) {
155                 return (col._aData[KEY_POS] == key);
156             } );
157
158             if (cols.length == 0)
159                 return null;
160             if (cols.length > 1)
161                 throw "Too many same-key rows in ResourceSelected plugin";
162
163             return cols[0];
164         },
165
166         set_state: function(data)
167         {
168             var action;
169             var msg;
170             var button = '';
171
172             var row;
173             
174             // make sure the change is visible : toggle on the whole plugin
175             // this might have to be made an 'auto-toggle' option of this plugin..
176             // also it might be needed to be a little finer-grained here
177
178         // XXX we don't want to show automaticaly the pending when a checkbox is checked
179             //this.toggle_on();
180             
181             switch(data.request) {
182                 case FIELD_REQUEST_ADD_RESET:
183                 case FIELD_REQUEST_REMOVE_RESET:
184                     // find line and delete it
185                     row = this.find_row(data.value);
186                     if (row)
187                         this.table.fnDeleteRow(row.nTr);
188                         $("#badge-pending").data('number', $("#badge-pending").data('number') - 1 );
189                         $("#badge-pending").text($("#badge-pending").data('number'));
190                     return;
191                 case FIELD_REQUEST_CHANGE:
192                     action = 'UPDATE';
193                     break;
194                 case FIELD_REQUEST_ADD:
195                     action = 'ADD';
196                     break;
197                 case FIELD_REQUEST_REMOVE:
198                     action = 'REMOVE';
199                     break;
200             }
201
202             switch(data.status) {
203                 case FIELD_REQUEST_PENDING:
204                     msg   = 'PENDING';
205                     button = "<span class='glyphicon glyphicon-remove ResourceSelectedClose' id='" + data.key + "'/>";
206                     break;
207                 case FIELD_REQUEST_SUCCESS:
208                     msg   = 'SUCCESS';
209                     break;
210                 case FIELD_REQUEST_FAILURE:
211                     msg   = 'FAILURE';
212                     break;
213             }
214
215             var status = msg + status;
216
217             
218
219             // find line
220             // if no, create it, else replace it
221             // XXX it's not just about adding lines, but sometimes removing some
222             // XXX how do we handle status reset ?
223             data.value = JSON.stringify(data.value);
224             row = this.find_row(data.value);
225             newline = [
226                 action,
227                 data.key,
228                 data.value,
229                 msg,
230                 button
231             ];
232             if (!row) {
233                 // XXX second parameter refresh = false can improve performance. todo in querytable also
234                 this.table.fnAddData(newline);
235                 row = this.find_row(data.value);
236                 $("#badge-pending").data('number', $("#badge-pending").data('number') + 1 );
237                 $("#badge-pending").text($("#badge-pending").data('number'));
238             } else {
239                 // Update row text...
240                 this.table.fnUpdate(newline, row.nTr);
241             }
242
243             // Change cell color according to status
244             if (row) {
245                 $(row.nTr).removeClass('add remove')
246                 var cls = action.toLowerCase();
247                 if (cls)
248                     $(row.nTr).addClass(cls);
249             }
250         },
251
252         /*************************** QUERY HANDLER ****************************/
253
254         // NONE
255
256         /*************************** RECORD HANDLER ***************************/
257
258         on_new_record: function(record)
259         {
260             // if (not and update) {
261
262                 // initial['resource'], initial['lease'] ?
263                 this.initial.push(record.urn);
264
265                 // We simply add to the table
266             // } else {
267                 //                 \ this.initial_resources
268                 //                  \
269                 // this.             \
270                 // current_resources  \    YES    |   NO
271                 // --------------------+----------+---------
272                 //       YES           | attached | added
273                 //       NO            | removed  |   /
274                 // 
275
276             // }
277         },
278
279         // QUERY STATUS
280         //                                      +-----------------+--------------+
281         //                                      v        R        |              |
282         // +-------+  ?G    +-------+        +-------+        +---+---+          |
283         // |       | -----> |       |  !G    |       |        |       |    DA    |
284         // |  ND   |        |  PG   | -----> |   D   | -----> |  PC   | <------+ |
285         // |       | <----- |       |  ~G    |       |   C    |       |        | | 
286         // +-------+   GE   +-------+        +-------+        +-------+      +------+
287         //                                       ^              ^  |         |      |
288         //                                       | DA        UE |  | ?U      | PCA  |
289         //                                       |              |  v         |      |
290         //                                   +-------+        +-------+      +------+
291         //                                   |       |   !U   |       |         ^
292         //                                   |  AD   | <----- |  PU   | --------+
293         //                                   |       |        |       |   ~U     
294         //                                   +-------+        +-------+          
295         //                                                                       
296         //
297         // LEGEND:
298         // 
299         // Plugins (i) receive state information, (ii) perform actions
300         //
301         // States:                                  Actions:
302         // ND : No data                             ?G : Get query
303         // PG : Pending Get                         !G : Get reply  
304         //  D : Data present                        ~G : Get partial reply
305         // PC : Pending changes                     GE : Get error                            
306         // PU : Pending update                       C : Change request
307         // PCA: Pending change with annotation       R : Reset request
308         // AD : Annotated data                      ?U : Update query
309         //                                          !U : Update reply
310         //                                          ~U : Update partial reply
311         //                                          UE : Update error            
312         //                                          DA : Delete annotation request
313         // NOTE:
314         // - D -> PU directly if the user chooses 'dynamic update'
315         // - Be careful for updates if partial Get success
316
317         // ND: No data == initialization state
318         
319         // PG : Pending get
320         // - on_query_in_progress
321         // NOTE: cannot distinguish get and update here. Is it important ?
322
323         on_query_in_progress: function()
324         {
325             messages.debug("queryupdater.on_query_in_progress");
326             this.spin();
327         },
328
329         // D : Data present
330         // - on_clear_records (Get)
331         // - on_new_record (shared with AD) XXX
332         // - on_query_done
333         // NOTE: cannot distinguish get and update here. Is it important ?
334         // NOTE: Would record key be sufficient for update ?
335
336         on_clear_records: function()
337         {
338             this.clear();
339         },
340
341         on_new_record: function(record)
342         {
343         },
344
345         on_query_done: function()
346         {
347             this.unspin();
348         },
349
350         // PC : Pending changes
351         // NOTE: record_key could be sufficient 
352         on_added_record: function(record)
353         {
354             this.set_record_state(record, RECORD_STATE_ADDED);
355             // update pending number
356         },
357
358         on_removed_record: function(record_key)
359         {
360             this.set_record_state(RECORD_STATE_REMOVED);
361         },
362
363         // PU : Pending update
364         // - on_query_in_progress (already done)
365         
366         // PCA : Pending change with annotation
367         // NOTE: Manifold will inform the plugin about updates, and thus won't
368         // call new record, even if the same channel UUID is used...
369         // - TODO on_updated_record
370         // - Key and confirmation could be sufficient, or key and record state
371         // XXX move record state to the manifold plugin API
372
373         on_field_state_changed: function(result)
374         {
375             messages.debug(result)
376             /* this.set_state(result.request, result.key, result.value, result.status); */
377             this.set_state(result);
378         },
379
380         // XXX we will have the requests for change
381         // XXX + the requests to move into the query cycle = the buttons aforementioned
382
383         // XXX what happens in case of updates ? not implemented yet
384         // XXX we want resources and leases
385         // listen for SET_ADD and SET_REMOVE for slice query
386
387         /************************** PRIVATE METHODS ***************************/
388
389         _close_click: function(e)
390         {
391             var self = e.data;
392
393             //jQuery.publish('selected', 'add/'+key_value);
394             // this.parentNode is <td> this.parentNode.parentNode is <tr> 
395             // this.parentNode.parentNode.firstChild is the first cell <td> of this line <tr>
396             // this.parentNode.parentNode.firstChild.firstChild is the text in that cell
397             //var firstCellVal=this.parentNode.parentNode.firstChild.firstChild.data;
398             var remove_urn = this.id; 
399             var current_resources = event.data.instance.current_resources;
400             var list_resources = $.grep(current_resources, function(x) {return x.urn != remove_urn});
401             //jQuery.publish('selected', 'cancel/'+this.id+'/'+unfold.get_value(firstCellVal));
402             $.publish('/update-set/' + event.data.instance.options.resource_query_uuid, [list_resources, true]);
403         },
404
405         /******************************** TODO ********************************/
406
407         update_resources: function(resources, change)
408         {
409             messages.debug("update_resources");
410             var my_oTable = this.table.dataTable();
411             var prev_resources = this.current_resources; 
412
413             /*
414              * The first time the query is advertised, don't do anything.  The
415              * component will learn nodes in the slice through the manifest
416              * received through the other subscription 
417              */
418              if (!change)
419                 return;
420              // ioi: Refubrished
421              var initial = this.initial_resources;
422              //var r_removed  = []; //
423              /*-----------------------------------------------------------------------
424                 TODO: remove this dirty hack !!!
425              */
426              resources = jQuery.map(resources, function(x){
427                 if(!('timeslot' in x)){x.timeslot=0;}
428                 return x;
429              });
430              /*
431                 TODO: handle generic keys instead of specific stuff
432                       ex: urn
433                           urn-lease
434              */
435              var initial_urn = $.map(initial, function(x){return x.urn;});
436              var resources_urn = $.map(resources, function(x){return x.urn;});
437              var r_removed = $.grep(initial, function (x) { return $.inArray(x.urn, resources_urn) == -1 });
438              var r_attached = $.grep(initial, function (x) { return $.inArray(x.urn, resources_urn) > -1 });
439              var r_added = $.grep(resources, function (x) { return $.inArray(x.urn, initial_urn) == -1 });
440              exists = false; // ioi
441              /*-----------------------------------------------------------------------*/
442
443              my_oTable.fnClearTable();
444              /*
445                 TODO: factorization of this code !!!
446              */
447              $.each(r_added, function(i, r) { 
448                 //var type = (typeof initial == 'undefined' || r.node != initial.node) ? 'add' : 'attached';
449                 var type = 'add';  
450                 // Create the resource objects
451                 // ioi: refubrished
452                 var urn = r.urn;
453                 time = r.timeslot;
454                               
455                 var SPAN = "<span class='glyphicon glyphicon-remove ResourceSelectedClose' id='"+urn+"'/>";
456                 var slot = "<span id='resource_"+urn+"'>" + time + "</span>"; //ioi
457                 // ioi
458                 var newline=Array();
459                 newline.push(type, urn, slot, SPAN); // ioi
460                 var line = my_oTable.fnAddData(newline);
461                 var nTr = my_oTable.fnSettings().aoData[ line[0] ].nTr;
462                 nTr.className = type;
463              });
464              $.each(r_attached, function(i, r) {  
465                 //var type = (typeof initial == 'undefined' || r.node != initial.node) ? 'add' : 'attached';
466                 var type = 'attached';
467                 // Create the resource objects
468                 // ioi: refubrished
469                 var node = r.urn;
470                 time = r.timeslot;
471
472                 var SPAN = "<span class='glyphicon glyphicon-renomve ResourceSelectedClose' id='"+node+"'/>";
473                 var slot = "<span id='resource_"+node+"'>" + time + "</span>"; //ioi
474                 // ioi
475                 var newline=Array();
476                 newline.push(type, node, slot, SPAN); // ioi
477                 var line = my_oTable.fnAddData(newline);
478                 var nTr = my_oTable.fnSettings().aoData[ line[0] ].nTr;
479                 nTr.className = type;
480              });
481              $.each(r_removed, function(i, r) { 
482                 // The list contains objects
483                 // ioi: refubrished
484                 var node = r.urn;
485                 var time = r.timeslot;
486                     
487                 var SPAN = "<span class='glyphicon glyphicon-remove ResourceSelectedClose' id='"+node+"'/>";
488                 var slot = "<span id='resource_"+node+"'>" + time + "</span>";
489                 // ioi
490                 var newline=Array();
491                 newline.push('remove', node, slot, SPAN); // ioi
492                 var line = my_oTable.fnAddData(newline);
493                 var nTr = my_oTable.fnSettings().aoData[ line[0] ].nTr;
494                 nTr.className = 'remove';
495              });
496
497              this.current_resources = $.merge(r_attached,r_added);
498
499              /* Allow the user to update the slice */
500              //jQuery('#updateslice-' + data.ResourceSelected.plugin_uuid).prop('disabled', false);
501
502         }, // update_resources
503
504     });
505
506     $.plugin('QueryUpdater', QueryUpdater);
507
508 })(jQuery);