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