56a49af6b349e02902d28f4b2660e3abc6a1a069
[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             switch(data.request) {
124                 case FIELD_REQUEST_ADD_RESET:
125                 case FIELD_REQUEST_REMOVE_RESET:
126                     // find line and delete it
127                     row = this.find_row(data.value);
128                     if (row)
129                         this.table.fnDeleteRow(row.nTr);
130                     return;
131                 case FIELD_REQUEST_CHANGE:
132                     action = 'UPDATE';
133                     break;
134                 case FIELD_REQUEST_ADD:
135                     action = 'ADD';
136                     break;
137                 case FIELD_REQUEST_REMOVE:
138                     action = 'REMOVE';
139                     break;
140             }
141
142             switch(data.status) {
143                 case FIELD_REQUEST_PENDING:
144                     msg   = 'PENDING';
145                     button = "<span class='ui-icon ui-icon-close ResourceSelectedClose' id='" + data.key + "'/>";
146                     break;
147                 case FIELD_REQUEST_SUCCESS:
148                     msg   = 'SUCCESS';
149                     break;
150                 case FIELD_REQUEST_FAILURE:
151                     msg   = 'FAILURE';
152                     break;
153             }
154
155             var status = msg + status;
156
157             // find line
158             // if no, create it, else replace it
159             // XXX it's not just about adding lines, but sometimes removing some
160             // XXX how do we handle status reset ?
161             row = this.find_row(data.value);
162             newline = [
163                 action,
164                 data.key,
165                 data.value,
166                 msg,
167                 button
168             ];
169             if (!row) {
170                 // XXX second parameter refresh = false can improve performance. todo in hazelnut also
171                 this.table.fnAddData(newline);
172                 row = this.find_row(data.value);
173             } else {
174                 // Update row text...
175                 this.table.fnUpdate(newline, row.nTr);
176             }
177
178             // Change cell color according to status
179             if (row) {
180                 $(row.nTr).removeClass('add remove')
181                 var cls = action.toLowerCase();
182                 if (cls)
183                     $(row.nTr).addClass(cls);
184             }
185         },
186
187         /*************************** QUERY HANDLER ****************************/
188
189         // NONE
190
191         /*************************** RECORD HANDLER ***************************/
192
193         on_new_record: function(record)
194         {
195             // if (not and update) {
196
197                 // initial['resource'], initial['lease'] ?
198                 this.initial.push(record.urn);
199
200                 // We simply add to the table
201             // } else {
202                 //                 \ this.initial_resources
203                 //                  \
204                 // this.             \
205                 // current_resources  \    YES    |   NO
206                 // --------------------+----------+---------
207                 //       YES           | attached | added
208                 //       NO            | removed  |   /
209                 // 
210
211             // }
212         },
213
214         // QUERY STATUS
215         //                                      +-----------------+--------------+
216         //                                      v        R        |              |
217         // +-------+  ?G    +-------+        +-------+        +---+---+          |
218         // |       | -----> |       |  !G    |       |        |       |    DA    |
219         // |  ND   |        |  PG   | -----> |   D   | -----> |  PC   | <------+ |
220         // |       | <----- |       |  ~G    |       |   C    |       |        | | 
221         // +-------+   GE   +-------+        +-------+        +-------+      +------+
222         //                                       ^              ^  |         |      |
223         //                                       | DA        UE |  | ?U      | PCA  |
224         //                                       |              |  v         |      |
225         //                                   +-------+        +-------+      +------+
226         //                                   |       |   !U   |       |         ^
227         //                                   |  AD   | <----- |  PU   | --------+
228         //                                   |       |        |       |   ~U     
229         //                                   +-------+        +-------+          
230         //                                                                       
231         //
232         // LEGEND:
233         // 
234         // Plugins (i) receive state information, (ii) perform actions
235         //
236         // States:                                  Actions:
237         // ND : No data                             ?G : Get query
238         // PG : Pending Get                         !G : Get reply  
239         //  D : Data present                        ~G : Get partial reply
240         // PC : Pending changes                     GE : Get error                            
241         // PU : Pending update                       C : Change request
242         // PCA: Pending change with annotation       R : Reset request
243         // AD : Annotated data                      ?U : Update query
244         //                                          !U : Update reply
245         //                                          ~U : Update partial reply
246         //                                          UE : Update error            
247         //                                          DA : Delete annotation request
248         // NOTE:
249         // - D -> PU directly if the user chooses 'dynamic update'
250         // - Be careful for updates if partial Get success
251
252         // ND: No data == initialization state
253         
254         // PG : Pending get
255         // - on_query_in_progress
256         // NOTE: cannot distinguish get and update here. Is it important ?
257
258         on_query_in_progress: function()
259         {
260             this.spin();
261         },
262
263         // D : Data present
264         // - on_clear_records (Get)
265         // - on_new_record (shared with AD) XXX
266         // - on_query_done
267         // NOTE: cannot distinguish get and update here. Is it important ?
268         // NOTE: Would record key be sufficient for update ?
269
270         on_clear_records: function()
271         {
272             this.clear();
273         },
274
275         on_new_record: function(record)
276         {
277         },
278
279         on_query_done: function()
280         {
281             this.unspin();
282         },
283
284         // PC : Pending changes
285         // NOTE: record_key could be sufficient 
286         on_added_record: function(record)
287         {
288             this.set_record_state(record, RECORD_STATE_ADDED);
289         },
290
291         on_removed_record: function(record_key)
292         {
293             this.set_record_state(RECORD_STATE_REMOVED);
294         },
295
296         // PU : Pending update
297         // - on_query_in_progress (already done)
298         
299         // PCA : Pending change with annotation
300         // NOTE: Manifold will inform the plugin about updates, and thus won't
301         // call new record, even if the same channel UUID is used...
302         // - TODO on_updated_record
303         // - Key and confirmation could be sufficient, or key and record state
304         // XXX move record state to the manifold plugin API
305
306         on_field_state_changed: function(request, key, value, status)
307         {
308             this.set_state(request, key, value, status);
309         },
310
311         // XXX we will have the requests for change
312         // XXX + the requests to move into the query cycle = the buttons aforementioned
313
314         // XXX what happens in case of updates ? not implemented yet
315         // XXX we want resources and leases
316         // listen for SET_ADD and SET_REMOVE for slice query
317
318         /************************** PRIVATE METHODS ***************************/
319
320         _close_click: function(e)
321         {
322             var self = e.data;
323
324             //jQuery.publish('selected', 'add/'+key_value);
325             // this.parentNode is <td> this.parentNode.parentNode is <tr> 
326             // this.parentNode.parentNode.firstChild is the first cell <td> of this line <tr>
327             // this.parentNode.parentNode.firstChild.firstChild is the text in that cell
328             //var firstCellVal=this.parentNode.parentNode.firstChild.firstChild.data;
329             var remove_urn = this.id; 
330             var current_resources = event.data.instance.current_resources;
331             var list_resources = $.grep(current_resources, function(x) {return x.urn != remove_urn});
332             //jQuery.publish('selected', 'cancel/'+this.id+'/'+get_value(firstCellVal));
333             $.publish('/update-set/' + event.data.instance.options.resource_query_uuid, [list_resources, true]);
334         },
335
336         /******************************** TODO ********************************/
337
338         update_resources: function(resources, change)
339         {
340             console.log("update_resources");
341             var my_oTable = this.table.dataTable();
342             var prev_resources = this.current_resources; 
343
344             /*
345              * The first time the query is advertised, don't do anything.  The
346              * component will learn nodes in the slice through the manifest
347              * received through the other subscription 
348              */
349              if (!change)
350                 return;
351              // ioi: Refubrished
352              var initial = this.initial_resources;
353              //var r_removed  = []; //
354              /*-----------------------------------------------------------------------
355                 TODO: remove this dirty hack !!!
356              */
357              resources = jQuery.map(resources, function(x){
358                 if(!('timeslot' in x)){x.timeslot=0;}
359                 return x;
360              });
361              /*
362                 TODO: handle generic keys instead of specific stuff
363                       ex: urn
364                           urn-lease
365              */
366              var initial_urn = $.map(initial, function(x){return x.urn;});
367              var resources_urn = $.map(resources, function(x){return x.urn;});
368              var r_removed = $.grep(initial, function (x) { return $.inArray(x.urn, resources_urn) == -1 });
369              var r_attached = $.grep(initial, function (x) { return $.inArray(x.urn, resources_urn) > -1 });
370              var r_added = $.grep(resources, function (x) { return $.inArray(x.urn, initial_urn) == -1 });
371              exists = false; // ioi
372              /*-----------------------------------------------------------------------*/
373
374              my_oTable.fnClearTable();
375              /*
376                 TODO: factorization of this code !!!
377              */
378              $.each(r_added, function(i, r) { 
379                 //var type = (typeof initial == 'undefined' || r.node != initial.node) ? 'add' : 'attached';
380                 var type = 'add';  
381                 // Create the resource objects
382                 // ioi: refubrished
383                 var urn = r.urn;
384                 time = r.timeslot;
385                               
386                 var SPAN = "<span class='ui-icon ui-icon-close ResourceSelectedClose' id='"+urn+"'/>";
387                 var slot = "<span id='resource_"+urn+"'>" + time + "</span>"; //ioi
388                 // ioi
389                 var newline=Array();
390                 newline.push(type, urn, slot, SPAN); // ioi
391                 var line = my_oTable.fnAddData(newline);
392                 var nTr = my_oTable.fnSettings().aoData[ line[0] ].nTr;
393                 nTr.className = type;
394              });
395              $.each(r_attached, function(i, r) {  
396                 //var type = (typeof initial == 'undefined' || r.node != initial.node) ? 'add' : 'attached';
397                 var type = 'attached';
398                 // Create the resource objects
399                 // ioi: refubrished
400                 var node = r.urn;
401                 time = r.timeslot;
402
403                 var SPAN = "<span class='ui-icon ui-icon-close ResourceSelectedClose' id='"+node+"'/>";
404                 var slot = "<span id='resource_"+node+"'>" + time + "</span>"; //ioi
405                 // ioi
406                 var newline=Array();
407                 newline.push(type, node, slot, SPAN); // ioi
408                 var line = my_oTable.fnAddData(newline);
409                 var nTr = my_oTable.fnSettings().aoData[ line[0] ].nTr;
410                 nTr.className = type;
411              });
412              $.each(r_removed, function(i, r) { 
413                 // The list contains objects
414                 // ioi: refubrished
415                 var node = r.urn;
416                 var time = r.timeslot;
417                     
418                 var SPAN = "<span class='ui-icon ui-icon-close ResourceSelectedClose' id='"+node+"'/>";
419                 var slot = "<span id='resource_"+node+"'>" + time + "</span>";
420                 // ioi
421                 var newline=Array();
422                 newline.push('remove', node, slot, SPAN); // ioi
423                 var line = my_oTable.fnAddData(newline);
424                 var nTr = my_oTable.fnSettings().aoData[ line[0] ].nTr;
425                 nTr.className = 'remove';
426              });
427
428              this.current_resources = $.merge(r_attached,r_added);
429
430              /* Allow the user to update the slice */
431              //jQuery('#updateslice-' + data.ResourceSelected.plugin_uuid).prop('disabled', false);
432
433         }, // update_resources
434
435     });
436
437     $.plugin('ResourcesSelected', ResourcesSelected);
438
439 })(jQuery);