major updates to slice reservation page and plugins
[myslice.git] / plugins / scheduler2 / static / js / scheduler2.js
1 /*\r
2 #\r
3 # Copyright (c) 2013 NITLab, University of Thessaly, CERTH, Greece\r
4 #\r
5 # Permission is hereby granted, free of charge, to any person obtaining a copy\r
6 # of this software and associated documentation files (the "Software"), to deal\r
7 # in the Software without restriction, including without limitation the rights\r
8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
9 # copies of the Software, and to permit persons to whom the Software is\r
10 # furnished to do so, subject to the following conditions:\r
11 #\r
12 # The above copyright notice and this permission notice shall be included in\r
13 # all copies or substantial portions of the Software.\r
14 #\r
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\r
18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
21 # THE SOFTWARE.\r
22 #\r
23 #\r
24 # This is a MySlice plugin for the NITOS Scheduler\r
25 # Nitos Scheduler v1\r
26 #\r
27 */\r
28 \r
29 // XXX groupid = all slots those that go with a min granularity\r
30 \r
31 /* some params */\r
32 var scheduler2;\r
33 var scheduler2Instance;\r
34 //is ctrl keyboard button pressed\r
35 var schedulerCtrlPressed = false;\r
36 //table Id\r
37 var schedulerTblId = "scheduler-reservation-table";\r
38 var SCHEDULER_FIRST_COLWIDTH = 200;\r
39 \r
40 \r
41 /* Number of scheduler slots per hour. Used to define granularity. Should be inferred from resources XXX */\r
42 var schedulerSlotsPerHour = 6;\r
43 var RESOURCE_DEFAULT_GRANULARITY    = 1800 /* s */; // should be computed automatically from resource information\r
44 var DEFAULT_GRANULARITY             = 1800 /* s */; // should be computed automatically from resource information. Test with 600\r
45 var DEFAULT_PAGE_RANGE = 5;\r
46 \r
47 var schedulerMaxRows = 12;\r
48 \r
49 /* All resources */\r
50 var SchedulerData = [];\r
51 \r
52 /* ??? */\r
53 var SchedulerSlots = [];\r
54 \r
55 var SchedulerDateSelected = new Date();\r
56 // Round to midnight\r
57 SchedulerDateSelected.setHours(0,0,0,0);\r
58 \r
59 /* Filtered resources */\r
60 var SchedulerDataViewData = [];\r
61 \r
62 var SchedulerSlotsViewData = [];\r
63 //Help Variables\r
64 var _schedulerCurrentCellPosition = 0;\r
65 //Enable Debug\r
66 var schedulerDebug = true;\r
67 //tmp to delete\r
68 var tmpSchedulerLeases = [];\r
69 \r
70 var SCHEDULER_COLWIDTH = 50;\r
71 \r
72 \r
73 /******************************************************************************\r
74  *                             ANGULAR CONTROLLER                             *\r
75  ******************************************************************************/\r
76 \r
77 // Create a private execution space for our controller. When\r
78 // executing this function expression, we're going to pass in\r
79 // the Angular reference and our application module.\r
80 (function (ng, app) {\r
81 \r
82     // Define our Controller constructor.\r
83     function Controller($scope) {\r
84 \r
85         // Store the scope so we can reference it in our\r
86         // class methods\r
87         this.scope = $scope;\r
88 \r
89         // Set up the default scope value.\r
90         this.scope.errorMessage = null;\r
91         this.scope.name = "";\r
92 \r
93         //Pagin\r
94         $scope.current_page = 1;\r
95         this.scope.items_per_page = 10;\r
96         $scope.from = 0; // JORDAN\r
97 \r
98         $scope.instance = null;\r
99         $scope.resources = new Array();\r
100         $scope.slots = SchedulerSlotsViewData;\r
101         $scope.granularity = DEFAULT_GRANULARITY; /* Will be setup */\r
102         //$scope.msg = "hello";\r
103 \r
104         angular.element(document).ready(function() {\r
105             //console.log('Hello World');\r
106             //alert('Hello World');\r
107             //afterAngularRendered();\r
108         });\r
109 \r
110         // Pagination\r
111 \r
112         $scope.range = function() {\r
113             var range_size = $scope.page_count() > DEFAULT_PAGE_RANGE ? DEFAULT_PAGE_RANGE : $scope.page_count();\r
114             var ret = [];\r
115             var start;\r
116 \r
117             start = $scope.current_page;\r
118             if ( start > $scope.page_count()-range_size ) {\r
119               start = $scope.page_count()-range_size+1;\r
120             }\r
121 \r
122             for (var i=start; i<start+range_size; i++) {\r
123               ret.push(i);\r
124             }\r
125             return ret;\r
126         };\r
127 \r
128         $scope.prevPage = function() {\r
129           if ($scope.current_page > 1) {\r
130             $scope.current_page--;\r
131           }\r
132         };\r
133 \r
134         $scope.prevPageDisabled = function() {\r
135           return $scope.current_page === 1 ? "disabled" : "";\r
136         };\r
137   \r
138         $scope.page_count = function()\r
139         {\r
140             // XXX need visible resources only\r
141             var query_ext, visible_resources_length;\r
142             if (!$scope.instance)\r
143                 return 0;\r
144             query_ext = manifold.query_store.find_analyzed_query_ext($scope.instance.options.query_uuid);\r
145             var visible_resources_length = 0;\r
146             query_ext.state.each(function(i, state) {\r
147                 if (state[STATE_VISIBLE])\r
148                     visible_resources_length++;\r
149             });\r
150             return Math.ceil(visible_resources_length/$scope.items_per_page);\r
151         };\r
152   \r
153         $scope.nextPage = function() {\r
154           if ($scope.current_page < $scope.page_count()) {\r
155             $scope.current_page++;\r
156           }\r
157         };\r
158   \r
159         $scope.nextPageDisabled = function() {\r
160           return $scope.current_page === $scope.page_count() ? "disabled" : "";\r
161         }; \r
162 \r
163         $scope.setPage = function(n) {\r
164             $scope.current_page = n;\r
165         };\r
166         // END pagination\r
167 \r
168         // FILTER\r
169 \r
170         $scope.filter_visible = function(resource)\r
171         {\r
172             return manifold.query_store.get_record_state($scope.instance.options.query_uuid, resource['urn'], STATE_VISIBLE);\r
173         };\r
174 \r
175         // SELECTION\r
176 \r
177         $scope._create_new_lease = function(resource_urn, start_time, end_time)\r
178         {\r
179             var lease_key, new_lease;\r
180 \r
181             lease_key = manifold.metadata.get_key('lease');\r
182 \r
183             new_lease = {\r
184                 resource:   resource_urn,\r
185                 start_time: start_time,\r
186                 end_time:   end_time,\r
187             };\r
188 \r
189             // This is needed to create a hashable object\r
190             new_lease.hashCode = manifold.record_hashcode(lease_key.sort());\r
191             new_lease.equals   = manifold.record_equals(lease_key);\r
192 \r
193             manifold.raise_event($scope.instance.options.query_lease_uuid, SET_ADD, new_lease);\r
194             /* Add to local cache also, unless we listen to events from outside */\r
195             if (!(resource_urn in $scope._leases_by_resource))\r
196                 $scope._leases_by_resource[resource_urn] = [];\r
197             $scope._leases_by_resource[resource_urn].push(new_lease);\r
198         }\r
199 \r
200         $scope._remove_lease = function(other)\r
201         {\r
202             var lease_key, other_key;\r
203 \r
204             lease_key = manifold.metadata.get_key('lease');\r
205 \r
206             // XXX This could be a manifold.record_get_value\r
207             other_key = {\r
208                 resource:   other.resource,\r
209                 start_time: other.start_time,\r
210                 end_time:   other.end_time\r
211             }\r
212             other_key.hashCode = manifold.record_hashcode(lease_key.sort());\r
213             other_key.equals   = manifold.record_equals(lease_key);\r
214 \r
215             manifold.raise_event($scope.instance.options.query_lease_uuid, SET_REMOVED, other_key);\r
216             /* Remove from local cache also, unless we listen to events from outside */\r
217             $.grep($scope._leases_by_resource[other.resource], function(x) { return x != other; });\r
218 \r
219         }\r
220 \r
221         $scope.select = function(index, model_lease, model_resource)\r
222         {\r
223             console.log("Selected", index, model_lease, model_resource);\r
224 \r
225             var day_timestamp = SchedulerDateSelected.getTime() / 1000;\r
226             var start_time = day_timestamp + index       * model_resource.granularity;\r
227             var end_time   = day_timestamp + (index + 1) * model_resource.granularity;\r
228             var start_date = new Date(start_time * 1000);\r
229             var end_date   = new Date(end_time   * 1000);\r
230 \r
231             var lease_key = manifold.metadata.get_key('lease');\r
232 \r
233             // We search for leases in the cache we previously constructed\r
234             var resource_leases = $scope._leases_by_resource[model_resource.urn];\r
235 \r
236             switch (model_lease.status)\r
237             {\r
238                 case 'free': // out\r
239                 case 'pendingout':\r
240                     if (resource_leases) {\r
241                         /* Search for leases before */\r
242                         $.each(resource_leases, function(i, other) {\r
243                             if (other.end_time != start_time)\r
244                                 return true; // ~ continue\r
245         \r
246                             /* The lease 'other' is just before, and there should not exist\r
247                              * any other lease before it */\r
248                             start_time = other.start_time;\r
249         \r
250                             other_key = {\r
251                                 resource:   other.resource,\r
252                                 start_time: other.start_time,\r
253                                 end_time:   other.end_time\r
254                             }\r
255                             // This is needed to create a hashable object\r
256                             other_key.hashCode = manifold.record_hashcode(lease_key.sort());\r
257                             other_key.equals   = manifold.record_equals(lease_key);\r
258         \r
259                             manifold.raise_event($scope.instance.options.query_lease_uuid, SET_REMOVED, other_key);\r
260                             /* Remove from local cache also, unless we listen to events from outside */\r
261                             $.grep($scope._leases_by_resource[model_resource.urn], function(x) { return x != other; });\r
262                             return false; // ~ break\r
263                         });\r
264         \r
265                         /* Search for leases after */\r
266                         $.each(resource_leases, function(i, other) {\r
267                             if (other.start_time != end_time)\r
268                                 return true; // ~ continue\r
269         \r
270                             /* The lease 'other' is just after, and there should not exist\r
271                              * any other lease after it */\r
272                             end_time = other.end_time;\r
273                             // XXX SET_ADD and SET_REMOVE should accept full objects\r
274                             other_key = {\r
275                                 resource:   other.resource,\r
276                                 start_time: other.start_time,\r
277                                 end_time:   other.end_time\r
278                             }\r
279                             // This is needed to create a hashable object\r
280                             other_key.hashCode = manifold.record_hashcode(lease_key.sort());\r
281                             other_key.equals   = manifold.record_equals(lease_key);\r
282         \r
283                             manifold.raise_event($scope.instance.options.query_lease_uuid, SET_REMOVED, other_key);\r
284                             /* Remove from local cache also, unless we listen to events from outside */\r
285                             $.grep($scope._leases_by_resource[model_resource.urn], function(x) { return x != other; });\r
286                             return false; // ~ break\r
287                         });\r
288                     }\r
289         \r
290                     $scope._create_new_lease(model_resource.urn, start_time, end_time);\r
291                     model_lease.status = 'pendingin'; \r
292                     // unless the exact same lease already existed (pending_out status for the lease, not the cell !!)\r
293 \r
294                     break;\r
295 \r
296                 case 'selected':\r
297                 case 'pendingin':\r
298                     // We remove the cell\r
299 \r
300                     /* We search for leases including this cell. Either 0, 1 or 2.\r
301                      * 0 : NOT POSSIBLE, should be checked.\r
302                      * 1 : either IN or OUT, we have make no change in the session\r
303                      * 2 : both will be pending, since we have made a change in the session\r
304                     * /!\ need to properly remove pending_in leases when removed again\r
305                      */\r
306                     if (resource_leases) {\r
307                         $.each(resource_leases, function(i, other) {\r
308                             if ((other.start_time <= start_time) && (other.end_time >= end_time)) {\r
309                                 // The cell is part of this lease.\r
310 \r
311                                 // If the cell is not at the beginning of the lease, we recreate a lease with cells before\r
312                                 if (start_time > other.start_time) {\r
313                                     $scope._create_new_lease(model_resource.urn, other.start_time, start_time);\r
314                                 }\r
315 \r
316                                 // If the cell is not at the end of the lease, we recreate a lease with cells after\r
317                                 if (end_time < other.end_time) {\r
318                                     $scope._create_new_lease(model_resource.urn, end_time, other.end_time);\r
319                                 }\r
320                                 \r
321                                 // The other lease will be removed\r
322                                 $scope._remove_lease(other);\r
323                             }\r
324                             // NOTE: We can interrupt the search if we know that there is a single lease (depending on the status).\r
325                         });\r
326                     }\r
327                 \r
328                     // cf comment in previous switch case\r
329                     model_lease.status = 'pendingout'; \r
330 \r
331                     break;\r
332 \r
333                 case 'reserved':\r
334                 case 'maintainance':\r
335                     // Do nothing\r
336                     break;\r
337             }\r
338             \r
339 \r
340             //$scope._dump_leases();\r
341         };\r
342   \r
343         $scope._dump_leases = function()\r
344         {\r
345             // DEBUG: display all leases and their status in the log\r
346             var leases = manifold.query_store.get_records($scope.instance.options.query_lease_uuid);\r
347             console.log("--------------------");\r
348             $.each(leases, function(i, lease) {\r
349                 var key = manifold.metadata.get_key('lease');\r
350                 var lease_key = manifold.record_get_value(lease, key);\r
351                 var state = manifold.query_store.get_record_state($scope.instance.options.query_lease_uuid, lease_key, STATE_SET);\r
352                 var state_str;\r
353                 switch(state) {\r
354                     case STATE_SET_IN:\r
355                         state_str = 'STATE_SET_IN';\r
356                         break;\r
357                     case STATE_SET_OUT:\r
358                         state_str = 'STATE_SET_OUT';\r
359                         break;\r
360                     case STATE_SET_IN_PENDING:\r
361                         state_str = 'STATE_SET_IN_PENDING';\r
362                         break;\r
363                     case STATE_SET_OUT_PENDING:\r
364                         state_str = 'STATE_SET_OUT_PENDING';\r
365                         break;\r
366                     case STATE_SET_IN_SUCCESS:\r
367                         state_str = 'STATE_SET_IN_SUCCESS';\r
368                         break;\r
369                     case STATE_SET_OUT_SUCCESS:\r
370                         state_str = 'STATE_SET_OUT_SUCCESS';\r
371                         break;\r
372                     case STATE_SET_IN_FAILURE:\r
373                         state_str = 'STATE_SET_IN_FAILURE';\r
374                         break;\r
375                     case STATE_SET_OUT_FAILURE:\r
376                         state_str = 'STATE_SET_OUT_FAILURE';\r
377                         break;\r
378                 }\r
379                 console.log("LEASE", new Date(lease.start_time * 1000), new Date(lease.end_time * 1000), lease.resource, state_str);\r
380             });\r
381         };\r
382 \r
383         // Return this object reference.\r
384         return (this);\r
385 \r
386     }\r
387 \r
388     // Define the Controller as the constructor function.\r
389     app.controller("SchedulerCtrl", Controller);\r
390 \r
391 })(angular, ManifoldApp);\r
392 \r
393 /******************************************************************************\r
394  *                              MANIFOLD PLUGIN                               *\r
395  ******************************************************************************/\r
396 \r
397 (function($) {\r
398         scheduler2 = Plugin.extend({\r
399 \r
400             /** XXX to check\r
401          * @brief Plugin constructor\r
402          * @param options : an associative array of setting values\r
403          * @param element : \r
404          * @return : a jQuery collection of objects on which the plugin is\r
405          *     applied, which allows to maintain chainability of calls\r
406          */\r
407             init: function(options, element) {\r
408                 // Call the parent constructor, see FAQ when forgotten\r
409                 this._super(options, element);\r
410 \r
411                 var scope = this._get_scope()\r
412                 scope.instance = this;\r
413 \r
414                 // XXX not needed\r
415                 scheduler2Instance = this;\r
416 \r
417                 // We need to remember the active filter for datatables filtering\r
418                 // XXX not needed\r
419                 this.filters = Array();\r
420 \r
421                 // XXX BETTER !!!!\r
422                 $(window).delegate('*', 'keypress', function (evt){\r
423                         alert("erm");\r
424                       });\r
425 \r
426                 $(window).keydown(function(evt) {\r
427                     if (evt.which == 17) { // ctrl\r
428                         schedulerCtrlPressed = true;\r
429                     }\r
430                 }).keyup(function(evt) {\r
431                     if (evt.which == 17) { // ctrl\r
432                         schedulerCtrlPressed = false;\r
433                     }\r
434                 });\r
435 \r
436                 // XXX naming\r
437                 //$("#" + schedulerTblId).on('mousedown', 'td', rangeMouseDown).on('mouseup', 'td', rangeMouseUp).on('mousemove', 'td', rangeMouseMove);\r
438 \r
439                 this._resources_received = false;\r
440                 this._leases_received = false;\r
441                 \r
442                 scope._leases_by_resource = {};\r
443 \r
444                 /* Listening to queries */\r
445                 this.listen_query(options.query_uuid, 'resources');\r
446                 this.listen_query(options.query_lease_uuid, 'leases');\r
447 \r
448                 this.elmt().on('show', this, this.on_show);\r
449                 this.elmt().on('shown.bs.tab', this, this.on_show);\r
450                 this.elmt().on('resize', this, this.on_resize);\r
451 \r
452                 /* Generate slots according to the default granularity. Should\r
453                  * be updated when resources arrive.  Should be the pgcd in fact XXX */\r
454                 this._granularity = DEFAULT_GRANULARITY;\r
455                 scope.granularity = this._granularity;\r
456                 this._all_slots = this._generate_all_slots();\r
457 \r
458                 // A list of {id, time} dictionaries representing the slots for the given day\r
459                 scope.slots = this._all_slots;\r
460                 this.scope_resources_by_key = {};\r
461 \r
462                 this.do_resize();\r
463     \r
464                 scope.from = 0;\r
465 \r
466                 this._initUI();\r
467 \r
468             },\r
469 \r
470             do_resize: function()\r
471             {\r
472                 var scope = this._get_scope();\r
473 \r
474                 $('#' + schedulerTblId + ' thead tr th:eq(0)').css("width", SCHEDULER_FIRST_COLWIDTH);\r
475                 //self get width might need fix depending on the template \r
476                 var tblwidth = $('#scheduler-reservation-table').parent().outerWidth();\r
477 \r
478                 /* Number of visible cells...*/\r
479                 this._num_visible_cells = parseInt((tblwidth - SCHEDULER_FIRST_COLWIDTH) / SCHEDULER_COLWIDTH);\r
480                 /* ...should be a multiple of the lcm of all encountered granularities. */\r
481                 // XXX Should be updated everytime a new resource is added\r
482                 this._lcm_colspan = this._lcm(this._granularity, RESOURCE_DEFAULT_GRANULARITY) / this._granularity;\r
483                 this._num_visible_cells = this._num_visible_cells - this._num_visible_cells % this._lcm_colspan;\r
484                 /* scope also needs this value */\r
485                 scope.num_visible_cells = this._num_visible_cells;\r
486                 scope.lcm_colspan = this._lcm_colspan;\r
487 \r
488                 // Slider max value\r
489 \r
490                 if ($('#tblSlider').data('slider') != undefined) {\r
491                     var new_max = (this._all_slots.length - this._num_visible_cells) / this._lcm_colspan;\r
492                     $('#tblSlider').slider('setAttribute', 'max', new_max);\r
493                 }\r
494 \r
495             },\r
496 \r
497             on_show: function(e)\r
498             {\r
499                 var self = e.data;\r
500                 self.do_resize();\r
501                 self._get_scope().$apply();\r
502             },\r
503 \r
504             on_resize: function(e)\r
505             {\r
506                 var self = e.data;\r
507                 self.do_resize();\r
508                 self._get_scope().$apply();\r
509             },\r
510 \r
511             /* Handlers */\r
512 \r
513             _get_scope : function()\r
514             {\r
515                 return angular.element(document.getElementById('SchedulerCtrl')).scope();\r
516             },\r
517             \r
518             _scope_set_resources : function()\r
519             {\r
520                 var self = this;\r
521                 var scope = this._get_scope();\r
522 \r
523                 var records = manifold.query_store.get_records(this.options.query_uuid);\r
524 \r
525                 scope.resources = [];\r
526 \r
527                 $.each(records, function(i, record) {\r
528                     if (!record.exclusive)\r
529                         return true; // ~ continue\r
530 \r
531                     // copy not to modify original record\r
532                     var resource = jQuery.extend(true, {}, record);\r
533 \r
534                     // Fix granularity\r
535                     resource.granularity = typeof(resource.granularity) == "number" ? resource.granularity : RESOURCE_DEFAULT_GRANULARITY;\r
536                     resource.leases = []; // a list of occupied timeslots\r
537 \r
538                     self.scope_resources_by_key[resource['urn']] = resource;\r
539                     scope.resources.push(resource);\r
540                 });\r
541             },\r
542 \r
543             _scope_clear_leases: function()\r
544             {\r
545                 var self = this;\r
546                 var scope = this._get_scope();\r
547 \r
548                 // Setup leases with a default free status...\r
549                 $.each(this.scope_resources_by_key, function(resource_key, resource) {\r
550                     resource.leases = [];\r
551                     var colspan_lease = resource.granularity / self._granularity; //eg. 3600 / 1800 => 2 cells\r
552                     for (i=0; i < self._all_slots.length / colspan_lease; i++) { // divide by granularity\r
553                         resource.leases.push({\r
554                             id:     'coucou',\r
555                             status: 'free', // 'selected', 'reserved', 'maintenance' XXX pending ??\r
556                         });\r
557                     }\r
558                 });\r
559 \r
560             },\r
561 \r
562             _scope_set_leases: function()\r
563             {\r
564                 var self = this;\r
565                 var scope = this._get_scope();\r
566             \r
567                 var leases = manifold.query_store.get_records(this.options.query_lease_uuid);\r
568                 $.each(leases, function(i, lease) {\r
569 \r
570                     console.log("SET LEASES", new Date(lease.start_time* 1000));\r
571                     console.log("          ", new Date(lease.end_time* 1000));\r
572                     // XXX We should ensure leases are correctly merged, otherwise our algorithm won't work\r
573 \r
574                     // Populate leases by resource array: this will help us merging leases later\r
575                     if (!(lease.resource in scope._leases_by_resource))\r
576                         scope._leases_by_resource[lease.resource] = [];\r
577                     scope._leases_by_resource[lease.resource].push(lease);\r
578 \r
579                     var resource = self.scope_resources_by_key[lease.resource];\r
580                     var day_timestamp = SchedulerDateSelected.getTime() / 1000;\r
581 \r
582                     var id_start = (lease.start_time - day_timestamp) / resource.granularity;\r
583                     if (id_start < 0) {\r
584                         /* Some leases might be in the past */\r
585                         id_start = 0;\r
586                     }\r
587     \r
588                     var id_end   = (lease.end_time   - day_timestamp) / resource.granularity - 1;\r
589                     var colspan_lease = resource.granularity / self._granularity; //eg. 3600 / 1800 => 2 cells\r
590                     if (id_end >= self._all_slots.length / colspan_lease) {\r
591                         /* Limit the display to the current day */\r
592                         id_end = self._all_slots.length / colspan_lease\r
593                     }\r
594 \r
595                     for (i = id_start; i <= id_end; i++)\r
596                         // the same slots might be affected multiple times.\r
597                         // PENDING_IN + PENDING_OUT => IN \r
598                         //\r
599                         // RESERVED vs SELECTED !\r
600                         //\r
601                         // PENDING !!\r
602                         resource.leases[i].status = 'selected'; \r
603                 });\r
604             },\r
605 \r
606             on_resources_query_done: function(data)\r
607             {\r
608                 this._resources_received = true;\r
609                 this._scope_set_resources();\r
610                 this._scope_clear_leases();\r
611                 if (this._leases_received)\r
612                     this._scope_set_leases();\r
613                     \r
614                 this._get_scope().$apply();\r
615             },\r
616 \r
617             on_leases_query_done: function(data)\r
618             {\r
619                 this._leases_received = true;\r
620                 if (this._resources_received) {\r
621                     this._scope_set_leases();\r
622                     this._get_scope().$apply();\r
623                 }\r
624             },\r
625 \r
626             /* Filters on resources */\r
627             on_resources_filter_added:   function(filter) { this._get_scope().$apply(); },\r
628             on_resources_filter_removed: function(filter) { this._get_scope().$apply(); },\r
629             on_resources_filter_clear:   function()       { this._get_scope().$apply(); },\r
630 \r
631             /* Filters on leases ? */\r
632             on_leases_filter_added:      function(filter) { this._get_scope().$apply(); },\r
633             on_leases_filter_removed:    function(filter) { this._get_scope().$apply(); },\r
634             on_leases_filter_clear:      function()       { this._get_scope().$apply(); },\r
635 \r
636             /* INTERNAL FUNCTIONS */\r
637 \r
638 /* XXX IN TEMPLATE XXX\r
639                 if (SchedulerDataViewData.length == 0) {\r
640                     $("#plugin-scheduler").hide();\r
641                     $("#plugin-scheduler-empty").show();\r
642                     tmpScope.clearStuff();\r
643                 } else {\r
644                     $("#plugin-scheduler-empty").hide();\r
645                     $("#plugin-scheduler").show();\r
646                     // initSchedulerResources\r
647                     tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
648                 }\r
649 */\r
650 \r
651             /**\r
652              * Initialize the date picker, the table, the slider and the buttons. Once done, display scheduler.\r
653              */\r
654             _initUI: function() \r
655             {\r
656                 var self = this;\r
657 \r
658                 $("#DateToRes").datepicker({\r
659                     onRender: function(date) {\r
660                         return date.valueOf() < now.valueOf() ? 'disabled' : '';\r
661                     }\r
662                 }).on('changeDate', function(ev) {\r
663                     SchedulerDateSelected = new Date(ev.date);\r
664                     SchedulerDateSelected.setHours(0,0,0,0);\r
665                     // Set slider to origin\r
666                     $('#tblSlider').slider('setValue', 0); // XXX\r
667                     // Refresh leases\r
668                     self._scope_clear_leases();\r
669                     self._scope_set_leases();\r
670                     // Refresh display\r
671                     self._get_scope().$apply();\r
672                 }).datepicker('setValue', SchedulerDateSelected); //.data('datepicker');\r
673 \r
674                 //init Slider\r
675                 $('#tblSlider').slider({\r
676                     min: 0,\r
677                     max: (self._all_slots.length - self._num_visible_cells) / self._lcm_colspan,\r
678                     value: 0,\r
679                 }).on('slide', function(ev) {\r
680                     var scope = self._get_scope();\r
681                     scope.from = ev.value * self._lcm_colspan;\r
682                     scope.$apply();\r
683                 });\r
684 \r
685                 $("#plugin-scheduler-loader").hide();\r
686                 $("#plugin-scheduler").show();\r
687             },\r
688 \r
689         // GUI EVENTS\r
690 \r
691         // TO BE REMOVED\r
692         _on_submit : function()\r
693         {\r
694             var leasesForCommit = new Array();\r
695             var tmpDateTime = SchedulerDateSelected;\r
696             for (var i = 0; i < SchedulerData.length; i++)\r
697             {\r
698                 var tpmR = SchedulerData[i];\r
699                 //for capturing start and end of the lease\r
700                 var newLeaseStarted = false;\r
701                 for (var j = 0; j < tpmR.leases.length; j++) {\r
702                     var tpmL = tpmR.leases[j];\r
703                     if (newLeaseStarted == false && tpmL.status == 'selected') {\r
704                         //get date of the slot\r
705                         tmpDateTime = schedulerGetDateTimeFromSlotId(tpmL.id, tmpDateTime);\r
706                         var unixStartTime = tmpDateTime.getTime() / 1000;\r
707                         //add lease object\r
708                         leasesForCommit.push({\r
709                             resource: tpmR.id,\r
710                             //granularity: tpmR.granularity,\r
711                             //lease_type: null,\r
712                             //slice: null,\r
713                             start_time: unixStartTime,\r
714                             end_time: null,\r
715                             //duration: null\r
716                         });\r
717                         console.log(tpmR.id);\r
718                         newLeaseStarted = true;\r
719                     } else if (newLeaseStarted == true && tpmL.status != 'selected') {\r
720                         //get date of the slot\r
721                         tmpDateTime = schedulerGetDateTimeFromSlotId(tpmL.id, tmpDateTime);\r
722                         var unixEndTime = tmpDateTime.getTime() / 1000;\r
723                         //upate end_time\r
724                         var tmpCL = leasesForCommit[leasesForCommit.length - 1];\r
725                         tmpCL.end_time = unixEndTime;\r
726                         //tmpCL.duration = schedulerFindDuration(tmpCL.start_time, tmpCL.end_time, tmpCL.granularity);\r
727                         newLeaseStarted = false;\r
728                     }\r
729                 }\r
730             }\r
731             console.log(leasesForCommit);\r
732             for (var i = 0; i < leasesForCommit.length; i++) {\r
733                 manifold.raise_event(scheduler2Instance.options.query_lease_uuid, SET_ADD, leasesForCommit[i]);\r
734             }\r
735         },\r
736         \r
737         // PRIVATE METHODS\r
738 \r
739         /**\r
740          * Greatest common divisor\r
741          */\r
742         _gcd : function(x, y)\r
743         {\r
744             return (y==0) ? x : this._gcd(y, x % y);\r
745         },\r
746 \r
747         /**\r
748          * Least common multiple\r
749          */\r
750         _lcm : function(x, y)\r
751         {\r
752             return x * y / this._gcd(x, y);\r
753         },\r
754     \r
755         _pad_str : function(i)\r
756         {\r
757             return (i < 10) ? "0" + i : "" + i;\r
758         },\r
759 \r
760         /**\r
761          * Member variables used:\r
762          *   _granularity\r
763          * \r
764          * Returns:\r
765          *   A list of {id, time} dictionaries.\r
766          */\r
767         _generate_all_slots: function()\r
768         {\r
769             var slots = [];\r
770             // Start with a random date (a first of a month), only time will matter\r
771             var d = new Date(2014, 1, 1, 0, 0, 0, 0);\r
772             var i = 0;\r
773             // Loop until we change the day\r
774             while (d.getDate() == 1) {\r
775                 // Nicely format the time...\r
776                 var tmpTime = this._pad_str(d.getHours()) + ':' + this._pad_str(d.getMinutes());\r
777                 /// ...and add the slot to the list of results\r
778                 slots.push({ id: i, time: tmpTime });\r
779                 // Increment the date with the granularity\r
780                 d = new Date(d.getTime() + this._granularity * 1000);\r
781                 i++;\r
782             }\r
783             return slots;\r
784 \r
785         },\r
786     });\r
787 \r
788     /* Plugin registration */\r
789     $.plugin('Scheduler2', scheduler2);\r
790 \r
791 })(jQuery);\r
792 \r
793 \r
794 \r