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