e00cda5b133a9592c20d00f9b7fd66ebf4659099
[myslice.git] / plugins / resources_selected / 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 /*
13  * It's a best practice to pass jQuery to an IIFE (Immediately Invoked Function
14  * Expression) that maps it to the dollar sign so it can't be overwritten by
15  * another library in the scope of its execution.
16  */
17 (function( $ ){
18
19     var PLUGIN_NAME = 'ResourcesSelected';
20
21     // Routing calls
22     jQuery.fn.ResourcesSelected = function( method ) {
23         if ( methods[method] ) {
24             return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
25         } else if ( typeof method === 'object' || ! method ) {
26             return methods.init.apply( this, arguments );
27         } else {
28             jQuery.error( 'Method ' +  method + ' does not exist on jQuery.' + PLUGIN_NAME );
29         }    
30
31     };
32
33     /***************************************************************************
34      * Public methods
35      ***************************************************************************/
36
37     var methods = {
38
39         /**
40          * @brief Plugin initialization
41          * @param options : an associative array of setting values
42          * @return : a jQuery collection of objects on which the plugin is
43          *     applied, which allows to maintain chainability of calls
44          */
45         init : function( options ) {
46
47             return this.each(function(){
48
49                 var $this = $(this);
50
51                 /* An object that will hold private variables and methods */
52                 var plugin = new ResourcesSelected(options);
53                 $(this).data('Manifold', plugin);
54
55                 //$this.set_query_handler(options.query_uuid, hazelnut.query_handler);
56                 //$this.set_record_handler(options.query_uuid, hazelnut.record_handler); 
57
58                 //var RESULTS_RESOURCES = '/results/' + options.resource_query_uuid + '/changed';
59                 //var UPDATE_RESOURCES  = '/update-set/' + options.resource_query_uuid;
60                 //$.subscribe(RESULTS_RESOURCES, function(e, resources) { s.set_resources(resources);    });
61                 //$.subscribe(UPDATE_RESOURCES,  function(e, resources, change) { s.update_resources(resources, change); });
62                 
63             }); // this.each
64         }, // init
65
66         /**
67          * @brief Plugin destruction
68          * @return : a jQuery collection of objects on which the plugin is
69          *     applied, which allows to maintain chainability of calls
70          */
71         destroy : function( ) {
72
73             return this.each(function() {
74                 var $this = $(this);
75                 var plugin = $this.data('Manifold');
76
77                 // Remove associated data
78                 plugin.remove();
79                 $this.removeData('Manifold');
80             });
81         }, // destroy
82
83     }; // var methods
84
85     /***************************************************************************
86      * Plugin object
87      ***************************************************************************/
88
89     function ResourcesSelected(options)
90     {
91         /* member variables */
92
93         this.options = options;
94
95         var object = this;
96
97         /* The resources that are in the slice */
98         this.current_resources = null;
99
100         /* The resources that are in the slice before any edit */
101         this.initial_resources = null;
102
103         var rs = this;
104
105         /* constructor */
106             // ioi: resources table id
107         var TABLE_NAME = '#table-' + options.plugin_uuid;
108         this.table = $(TABLE_NAME).dataTable({
109             //sPaginationType: 'full_numbers',  // Use pagination
110             sPaginationType: 'bootstrap',
111             //bJQueryUI: true,
112             //bRetrieve: true,
113             sScrollX: '100%',                 // Horizontal scrolling 
114             bSortClasses: false,              // Disable style for the sorted column
115             aaSorting: [[ 1, "asc" ]],        // Default sorting on URN
116             fnDrawCallback: function() {      // Reassociate close click every time the table is redrawn
117                 /* Prevent to loop on click while redrawing table  */
118                 jQuery('.ResourceSelectedClose').unbind('click');
119                 /* Handle clicks on close span */
120                 /* Reassociate close click every time the table is redrawn */
121                 $('.ResourceSelectedClose').bind('click',{instance: rs}, object.close_click);
122             }
123          });
124
125         /* methods */
126
127         this.set_resources = function(resources)
128         {
129             console.log("set_resources");
130             /* Some sanity checks on the API results */
131             if(resources.length==0){
132                 this.table.html(errorDisplay("No Result"));   
133                 return;
134             }
135
136             if (typeof(resources[0].error) != 'undefined') {
137                 this.table.html(errorDisplay(resources[0].error));
138                 return;
139             }
140
141             /* Update the table with resources in the slice */
142             //var slivers = $.grep(resources, function(i) {return typeof(i['sliver']) != 'undefined';})
143             var slivers = resources;
144             var sliver_urns = Array();
145             // ioi : refubrished
146                 $.each(resources, function(i, x) { sliver_urns.push({urn:x.urn, timeslot:"0"}); }); // ioi
147
148             this.initial_resources = sliver_urns; // We make a copy of the object // ioi
149                 // ioi
150             
151             if (this.current_resources == null) {
152                 this.current_resources = sliver_urns;
153
154                 /* We simply add to the ResourceSelected table */
155                 var newlines=Array();
156                 $.each(sliver_urns, function(index, elt) {
157                     newlines.push(Array('attached', elt.urn, elt.timeslot, "<span class='ui-icon ui-icon-close ResourceSelectedClose' id='"+elt.urn+"'/>")); // ioi: added last element
158                 });
159                 this.table.dataTable().fnAddData(newlines);
160             } else {
161                 alert('Slice updated. Refresh not yet implemented!');
162             }
163         }
164
165         this.update_resources = function(resources, change) {
166             console.log("update_resources");
167             var my_oTable = this.table.dataTable();
168             var prev_resources = this.current_resources; 
169             /*      \ this.initial_resources
170              *           \
171              * this.          \
172              * current_resources  \    YES    |   NO
173              * --------------------+----------+---------
174              *       YES           | attached | added
175              *       NO            | removed  |   /
176              */
177
178             /*
179              * The first time the query is advertised, don't do anything.  The
180              * component will learn nodes in the slice through the manifest
181              * received through the other subscription 
182              */
183              if (!change)
184                 return;
185              // ioi: Refubrished
186              var initial = this.initial_resources;
187              //var r_removed  = []; //
188              /*-----------------------------------------------------------------------
189                 TODO: remove this dirty hack !!!
190              */
191              resources = jQuery.map(resources, function(x){
192                 if(!('timeslot' in x)){x.timeslot=0;}
193                 return x;
194              });
195              /*
196                 TODO: handle generic keys instead of specific stuff
197                       ex: urn
198                           urn-lease
199              */
200              var initial_urn = $.map(initial, function(x){return x.urn;});
201              var resources_urn = $.map(resources, function(x){return x.urn;});
202              var r_removed = $.grep(initial, function (x) { return $.inArray(x.urn, resources_urn) == -1 });
203              var r_attached = $.grep(initial, function (x) { return $.inArray(x.urn, resources_urn) > -1 });
204              var r_added = $.grep(resources, function (x) { return $.inArray(x.urn, initial_urn) == -1 });
205              exists = false; // ioi
206              /*-----------------------------------------------------------------------*/
207
208              my_oTable.fnClearTable();
209              /*
210                 TODO: factorization of this code !!!
211              */
212              $.each(r_added, function(i, r) { 
213                 //var type = (typeof initial == 'undefined' || r.node != initial.node) ? 'add' : 'attached';
214                 var type = 'add';  
215                 // Create the resource objects
216                 // ioi: refubrished
217                 var urn = r.urn;
218                 time = r.timeslot;
219                               
220                 var SPAN = "<span class='ui-icon ui-icon-close ResourceSelectedClose' id='"+urn+"'/>";
221                 var slot = "<span id='resource_"+urn+"'>" + time + "</span>"; //ioi
222                 // ioi
223                 var newline=Array();
224                 newline.push(type, urn, slot, SPAN); // ioi
225                 var line = my_oTable.fnAddData(newline);
226                 var nTr = my_oTable.fnSettings().aoData[ line[0] ].nTr;
227                 nTr.className = type;
228              });
229              $.each(r_attached, function(i, r) {  
230                 //var type = (typeof initial == 'undefined' || r.node != initial.node) ? 'add' : 'attached';
231                 var type = 'attached';
232                 // Create the resource objects
233                 // ioi: refubrished
234                 var node = r.urn;
235                 time = r.timeslot;
236
237                 var SPAN = "<span class='ui-icon ui-icon-close ResourceSelectedClose' id='"+node+"'/>";
238                 var slot = "<span id='resource_"+node+"'>" + time + "</span>"; //ioi
239                 // ioi
240                 var newline=Array();
241                 newline.push(type, node, slot, SPAN); // ioi
242                 var line = my_oTable.fnAddData(newline);
243                 var nTr = my_oTable.fnSettings().aoData[ line[0] ].nTr;
244                 nTr.className = type;
245              });
246              $.each(r_removed, function(i, r) { 
247                 // The list contains objects
248                 // ioi: refubrished
249                 var node = r.urn;
250                 var time = r.timeslot;
251                     
252                 var SPAN = "<span class='ui-icon ui-icon-close ResourceSelectedClose' id='"+node+"'/>";
253                 var slot = "<span id='resource_"+node+"'>" + time + "</span>";
254                 // ioi
255                 var newline=Array();
256                 newline.push('remove', node, slot, SPAN); // ioi
257                 var line = my_oTable.fnAddData(newline);
258                 var nTr = my_oTable.fnSettings().aoData[ line[0] ].nTr;
259                 nTr.className = 'remove';
260              });
261
262              this.current_resources = $.merge(r_attached,r_added);
263
264              /* Allow the user to update the slice */
265              //jQuery('#updateslice-' + data.ResourceSelected.plugin_uuid).prop('disabled', false);
266
267         } // update_resources
268
269         this.record_handler = function(e, event_type, record)
270         {
271             // elements in set
272             switch(event_type) {
273                 case NEW_RECORD:
274                     /* NOTE in fact we are doing a join here */
275                     if (object.received_all)
276                         // update checkbox for record
277                         object.set_checkbox(record);
278                     else
279                         // store for later update of checkboxes
280                         object.in_set_buffer.push(record);
281                     break;
282                 case CLEAR_RECORDS:
283                     // nothing to do here
284                     break;
285                 case IN_PROGRESS:
286                     manifold.spin($(this));
287                     break;
288                 case DONE:
289                     if (object.received_all)
290                         manifold.spin($(this), false);
291                     object.received_set = true;
292                     break;
293             }
294         };
295
296         this.record_handler_all = function(e, event_type, record)
297         {
298             // all elements
299             switch(event_type) {
300                 case NEW_RECORD:
301                     // Add the record to the table
302                     object.new_record(record);
303                     break;
304                 case CLEAR_RECORDS:
305                     object.table.fnClearTable();
306                     break;
307                 case IN_PROGRESS:
308                     manifold.spin($(this));
309                     break;
310                 case DONE:
311                     if (object.received_set) {
312                         /* XXX needed ? XXX We uncheck all checkboxes ... */
313                         $("[id^='datatables-checkbox-" + object.options.plugin_uuid +"']").attr('checked', false);
314
315                         /* ... and check the ones specified in the resource list */
316                         $.each(object.in_set_buffer, function(i, record) {
317                             object.set_checkbox(record);
318                         });
319
320                         manifold.spin($(this), false);
321                     }
322                     object.received_all = true;
323                     break;
324             }
325         };
326
327         this.query_handler = function(e, event_type, data)
328         {
329             // This replaces the complex set_query function
330             // The plugin does not need to remember the query anymore
331             switch(event_type) {
332                 // Filters
333                 case FILTER_ADDED:
334                 case FILTER_REMOVED:
335                 case CLEAR_FILTERS:
336                     // XXX Here we might need to maintain the list of filters !
337                     /* Process updates in filters / current_query must be updated before this call for filtering ! */
338                     object.table.fnDraw();
339                     break;
340
341                 // Fields
342                 /* Hide/unhide columns to match added/removed fields */
343                 case FIELD_ADDED:
344                     var field = data;
345                     var oSettings = object.table.fnSettings();
346                     var cols = oSettings.aoColumns;
347                     var index = object.getColIndex(field,cols);
348                     if(index != -1)
349                         object.table.fnSetColumnVis(index, true);
350                     break;
351                 case FIELD_REMOVED:
352                     var field = data;
353                     var oSettings = object.table.fnSettings();
354                     var cols = oSettings.aoColumns;
355                     var index = object.getColIndex(field,cols);
356                     if(index != -1)
357                         object.table.fnSetColumnVis(index, false);
358                     break;
359                 case CLEAR_FIELDS:
360                     alert('Hazelnut::clear_fields() not implemented');
361                     break;
362             } // switch
363         }
364
365     } // ResourcesSelected
366
367
368     /***************************************************************************
369      * Private methods
370      ***************************************************************************/
371
372     /* Callbacks */    
373     function close_click(event){
374         //jQuery.publish('selected', 'add/'+key_value);
375         // this.parentNode is <td> this.parentNode.parentNode is <tr> 
376         // this.parentNode.parentNode.firstChild is the first cell <td> of this line <tr>
377         // this.parentNode.parentNode.firstChild.firstChild is the text in that cell
378         //var firstCellVal=this.parentNode.parentNode.firstChild.firstChild.data;
379         var remove_urn = this.id; 
380         var current_resources = event.data.instance.current_resources;
381         var list_resources = $.grep(current_resources, function(x) {return x.urn != remove_urn});
382         //jQuery.publish('selected', 'cancel/'+this.id+'/'+get_value(firstCellVal));
383         $.publish('/update-set/' + event.data.instance.options.resource_query_uuid, [list_resources, true]);
384     }
385
386 })(jQuery);