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