3 # Copyright (c) 2013 NITLab, University of Thessaly, CERTH, Greece
\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
12 # The above copyright notice and this permission notice shall be included in
\r
13 # all copies or substantial portions of the Software.
\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
24 # This is a MySlice plugin for the NITOS Scheduler
\r
25 # Nitos Scheduler v1
\r
29 // XXX groupid = all slots those that go with a min granularity
\r
33 var scheduler2Instance;
\r
34 //is ctrl keyboard button pressed
\r
35 var schedulerCtrlPressed = false;
\r
37 var schedulerTblId = "scheduler-reservation-table";
\r
38 var SCHEDULER_FIRST_COLWIDTH = 200;
\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
47 var schedulerMaxRows = 12;
\r
50 var SchedulerData = [];
\r
53 var SchedulerSlots = [];
\r
55 var SchedulerDateSelected = new Date();
\r
56 // Round to midnight
\r
57 SchedulerDateSelected.setHours(0,0,0,0);
\r
59 /* Filtered resources */
\r
60 var SchedulerDataViewData = [];
\r
62 var SchedulerSlotsViewData = [];
\r
64 var _schedulerCurrentCellPosition = 0;
\r
66 var schedulerDebug = true;
\r
68 var tmpSchedulerLeases = [];
\r
70 var SCHEDULER_COLWIDTH = 50;
\r
73 /******************************************************************************
\r
74 * ANGULAR CONTROLLER *
\r
75 ******************************************************************************/
\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
82 // Define our Controller constructor.
\r
83 function Controller($scope) {
\r
85 // Store the scope so we can reference it in our
\r
87 this.scope = $scope;
\r
89 // Set up the default scope value.
\r
90 this.scope.errorMessage = null;
\r
91 this.scope.name = "";
\r
94 $scope.current_page = 1;
\r
95 this.scope.items_per_page = 10;
\r
96 $scope.from = 0; // JORDAN
\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
104 angular.element(document).ready(function() {
\r
105 //console.log('Hello World');
\r
106 //alert('Hello World');
\r
107 //afterAngularRendered();
\r
112 $scope.range = function() {
\r
113 var range_size = $scope.page_count() > DEFAULT_PAGE_RANGE ? DEFAULT_PAGE_RANGE : $scope.page_count();
\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
122 for (var i=start; i<start+range_size; i++) {
\r
128 $scope.prevPage = function() {
\r
129 if ($scope.current_page > 1) {
\r
130 $scope.current_page--;
\r
134 $scope.prevPageDisabled = function() {
\r
135 return $scope.current_page === 1 ? "disabled" : "";
\r
138 $scope.page_count = function()
\r
140 // XXX need visible resources only
\r
141 var query_ext, visible_resources_length;
\r
142 if (!$scope.instance)
\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
150 return Math.ceil(visible_resources_length/$scope.items_per_page);
\r
153 $scope.nextPage = function() {
\r
154 if ($scope.current_page < $scope.page_count()) {
\r
155 $scope.current_page++;
\r
159 $scope.nextPageDisabled = function() {
\r
160 return $scope.current_page === $scope.page_count() ? "disabled" : "";
\r
163 $scope.setPage = function(n) {
\r
164 $scope.current_page = n;
\r
170 $scope.filter_visible = function(resource)
\r
172 return manifold.query_store.get_record_state($scope.instance.options.query_uuid, resource['urn'], STATE_VISIBLE);
\r
177 $scope._create_new_lease = function(resource_urn, start_time, end_time)
\r
179 var lease_key, new_lease;
\r
181 lease_key = manifold.metadata.get_key('lease');
\r
184 resource: resource_urn,
\r
185 start_time: start_time,
\r
186 end_time: end_time,
\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
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
200 $scope._remove_lease = function(other)
\r
202 var lease_key, other_key;
\r
204 lease_key = manifold.metadata.get_key('lease');
\r
206 // XXX This could be a manifold.record_get_value
\r
208 resource: other.resource,
\r
209 start_time: other.start_time,
\r
210 end_time: other.end_time
\r
212 other_key.hashCode = manifold.record_hashcode(lease_key.sort());
\r
213 other_key.equals = manifold.record_equals(lease_key);
\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
221 $scope.select = function(index, model_lease, model_resource)
\r
223 console.log("Selected", index, model_lease, model_resource);
\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
231 var lease_key = manifold.metadata.get_key('lease');
\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
236 switch (model_lease.status)
\r
238 case 'free': // out
\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
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
251 resource: other.resource,
\r
252 start_time: other.start_time,
\r
253 end_time: other.end_time
\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
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
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
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
275 resource: other.resource,
\r
276 start_time: other.start_time,
\r
277 end_time: other.end_time
\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
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
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
298 // We remove the cell
\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
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
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
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
321 // The other lease will be removed
\r
322 $scope._remove_lease(other);
\r
324 // NOTE: We can interrupt the search if we know that there is a single lease (depending on the status).
\r
328 // cf comment in previous switch case
\r
329 model_lease.status = 'pendingout';
\r
334 case 'maintainance':
\r
340 //$scope._dump_leases();
\r
343 $scope._dump_leases = function()
\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
355 state_str = 'STATE_SET_IN';
\r
357 case STATE_SET_OUT:
\r
358 state_str = 'STATE_SET_OUT';
\r
360 case STATE_SET_IN_PENDING:
\r
361 state_str = 'STATE_SET_IN_PENDING';
\r
363 case STATE_SET_OUT_PENDING:
\r
364 state_str = 'STATE_SET_OUT_PENDING';
\r
366 case STATE_SET_IN_SUCCESS:
\r
367 state_str = 'STATE_SET_IN_SUCCESS';
\r
369 case STATE_SET_OUT_SUCCESS:
\r
370 state_str = 'STATE_SET_OUT_SUCCESS';
\r
372 case STATE_SET_IN_FAILURE:
\r
373 state_str = 'STATE_SET_IN_FAILURE';
\r
375 case STATE_SET_OUT_FAILURE:
\r
376 state_str = 'STATE_SET_OUT_FAILURE';
\r
379 console.log("LEASE", new Date(lease.start_time * 1000), new Date(lease.end_time * 1000), lease.resource, state_str);
\r
383 // Return this object reference.
\r
388 // Define the Controller as the constructor function.
\r
389 app.controller("SchedulerCtrl", Controller);
\r
391 })(angular, ManifoldApp);
\r
393 /******************************************************************************
\r
394 * MANIFOLD PLUGIN *
\r
395 ******************************************************************************/
\r
398 scheduler2 = Plugin.extend({
\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
407 init: function(options, element) {
\r
408 // Call the parent constructor, see FAQ when forgotten
\r
409 this._super(options, element);
\r
411 var scope = this._get_scope()
\r
412 scope.instance = this;
\r
415 scheduler2Instance = this;
\r
417 // We need to remember the active filter for datatables filtering
\r
419 this.filters = Array();
\r
422 $(window).delegate('*', 'keypress', function (evt){
\r
426 $(window).keydown(function(evt) {
\r
427 if (evt.which == 17) { // ctrl
\r
428 schedulerCtrlPressed = true;
\r
430 }).keyup(function(evt) {
\r
431 if (evt.which == 17) { // ctrl
\r
432 schedulerCtrlPressed = false;
\r
437 //$("#" + schedulerTblId).on('mousedown', 'td', rangeMouseDown).on('mouseup', 'td', rangeMouseUp).on('mousemove', 'td', rangeMouseMove);
\r
439 this._resources_received = false;
\r
440 this._leases_received = false;
\r
442 scope._leases_by_resource = {};
\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
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
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
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
470 do_resize: function()
\r
472 var scope = this._get_scope();
\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
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
488 // Slider max value
\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
497 on_show: function(e)
\r
501 self._get_scope().$apply();
\r
504 on_resize: function(e)
\r
508 self._get_scope().$apply();
\r
513 _get_scope : function()
\r
515 return angular.element(document.getElementById('SchedulerCtrl')).scope();
\r
518 _scope_set_resources : function()
\r
521 var scope = this._get_scope();
\r
523 var records = manifold.query_store.get_records(this.options.query_uuid);
\r
525 scope.resources = [];
\r
527 $.each(records, function(i, record) {
\r
528 if (!record.exclusive)
\r
529 return true; // ~ continue
\r
531 // copy not to modify original record
\r
532 var resource = jQuery.extend(true, {}, record);
\r
535 resource.granularity = typeof(resource.granularity) == "number" ? resource.granularity : RESOURCE_DEFAULT_GRANULARITY;
\r
536 resource.leases = []; // a list of occupied timeslots
\r
538 self.scope_resources_by_key[resource['urn']] = resource;
\r
539 scope.resources.push(resource);
\r
543 _scope_clear_leases: function()
\r
546 var scope = this._get_scope();
\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
555 status: 'free', // 'selected', 'reserved', 'maintenance' XXX pending ??
\r
562 _scope_set_leases: function()
\r
565 var scope = this._get_scope();
\r
567 var leases = manifold.query_store.get_records(this.options.query_lease_uuid);
\r
568 $.each(leases, function(i, lease) {
\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
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
579 var resource = self.scope_resources_by_key[lease.resource];
\r
580 var day_timestamp = SchedulerDateSelected.getTime() / 1000;
\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
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
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
599 // RESERVED vs SELECTED !
\r
602 resource.leases[i].status = 'selected';
\r
606 on_resources_query_done: function(data)
\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
614 this._get_scope().$apply();
\r
617 on_leases_query_done: function(data)
\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
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
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
636 /* INTERNAL FUNCTIONS */
\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
644 $("#plugin-scheduler-empty").hide();
\r
645 $("#plugin-scheduler").show();
\r
646 // initSchedulerResources
\r
647 tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);
\r
652 * Initialize the date picker, the table, the slider and the buttons. Once done, display scheduler.
\r
654 _initUI: function()
\r
658 $("#DateToRes").datepicker({
\r
659 onRender: function(date) {
\r
660 return date.valueOf() < now.valueOf() ? 'disabled' : '';
\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
668 self._scope_clear_leases();
\r
669 self._scope_set_leases();
\r
671 self._get_scope().$apply();
\r
672 }).datepicker('setValue', SchedulerDateSelected); //.data('datepicker');
\r
675 $('#tblSlider').slider({
\r
677 max: (self._all_slots.length - self._num_visible_cells) / self._lcm_colspan,
\r
679 }).on('slide', function(ev) {
\r
680 var scope = self._get_scope();
\r
681 scope.from = ev.value * self._lcm_colspan;
\r
685 $("#plugin-scheduler-loader").hide();
\r
686 $("#plugin-scheduler").show();
\r
692 _on_submit : function()
\r
694 var leasesForCommit = new Array();
\r
695 var tmpDateTime = SchedulerDateSelected;
\r
696 for (var i = 0; i < SchedulerData.length; i++)
\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
708 leasesForCommit.push({
\r
710 //granularity: tpmR.granularity,
\r
711 //lease_type: null,
\r
713 start_time: unixStartTime,
\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
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
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
740 * Greatest common divisor
\r
742 _gcd : function(x, y)
\r
744 return (y==0) ? x : this._gcd(y, x % y);
\r
748 * Least common multiple
\r
750 _lcm : function(x, y)
\r
752 return x * y / this._gcd(x, y);
\r
755 _pad_str : function(i)
\r
757 return (i < 10) ? "0" + i : "" + i;
\r
761 * Member variables used:
\r
765 * A list of {id, time} dictionaries.
\r
767 _generate_all_slots: function()
\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
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
788 /* Plugin registration */
\r
789 $.plugin('Scheduler2', scheduler2);
\r