Scheduler: adding/removing resources enforce warnings and recount number of unconfigu...
[myslice.git] / plugins / scheduler2 / static / js / scheduler2.js
index 10eaa9a..933f32f 100755 (executable)
-/*\r
-#\r
-# Copyright (c) 2013 NITLab, University of Thessaly, CERTH, Greece\r
-#\r
-# Permission is hereby granted, free of charge, to any person obtaining a copy\r
-# of this software and associated documentation files (the "Software"), to deal\r
-# in the Software without restriction, including without limitation the rights\r
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
-# copies of the Software, and to permit persons to whom the Software is\r
-# furnished to do so, subject to the following conditions:\r
-#\r
-# The above copyright notice and this permission notice shall be included in\r
-# all copies or substantial portions of the Software.\r
-#\r
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\r
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
-# THE SOFTWARE.\r
-#\r
-#\r
-# This is a MySlice plugin for the NITOS Scheduler\r
-# Nitos Scheduler v1\r
-#\r
-*/\r
-\r
-/* some params */\r
-var scheduler2;\r
-var scheduler2Instance;\r
-//is ctrl keyboard button pressed\r
-var schedulerCtrlPressed = false;\r
-//table Id\r
-var schedulerTblId = "scheduler-reservation-table";\r
-var schedulerTblFirstColWidth = 150;\r
-//Some Data\r
-var schedulerSlotsPerHour = 6;\r
-var schedulerMaxRows = 12;\r
-var SchedulerData = [];\r
-var SchedulerSlots = [];\r
-var SchedulerDateSelected = new Date();\r
-var SchedulerDataViewData = [];\r
-var SchedulerSlotsViewData = [];\r
-var SchedulerTotalCells;\r
-var SchedulerTotalVisibleCells;\r
-//Help Variables\r
-var _schedulerCurrentCellPosition = 0;\r
-var _leasesDone = false;\r
-var _resourcesDone = false;\r
-//Enable Debug\r
-var schedulerDebug = true;\r
-//tmp to delete\r
-var tmpSchedulerLeases = [];\r
-\r
-(function($) {\r
-        scheduler2 = Plugin.extend({\r
-\r
-            /** XXX to check\r
-         * @brief Plugin constructor\r
-         * @param options : an associative array of setting values\r
-         * @param element : \r
-         * @return : a jQuery collection of objects on which the plugin is\r
-         *     applied, which allows to maintain chainability of calls\r
-         */\r
-            init: function(options, element) {\r
-                this.classname = "scheduler2";\r
-                // Call the parent constructor, see FAQ when forgotten\r
-                this._super(options, element);\r
-                scheduler2Instance = this;\r
-                // We need to remember the active filter for datatables filtering\r
-                this.filters = Array();\r
-\r
-\r
-                SchedulerSlots = schedulerGetSlots(60 / schedulerSlotsPerHour);\r
-                //selection from table \r
-                $(window).keydown(function(evt) {\r
-                    if (evt.which == 17) { // ctrl\r
-                        schedulerCtrlPressed = true;\r
-                    }\r
-                }).keyup(function(evt) {\r
-                    if (evt.which == 17) { // ctrl\r
-                        schedulerCtrlPressed = false;\r
-                    }\r
-                });\r
-                $("#" + schedulerTblId).on('mousedown', 'td', rangeMouseDown).on('mouseup', 'td', rangeMouseUp).on('mousemove', 'td', rangeMouseMove);\r
-\r
-                // Explain this will allow query events to be handled\r
-                // What happens when we don't define some events ?\r
-                // Some can be less efficient\r
-\r
-                if (schedulerDebug) console.time("Listening_to_queries");\r
-                /* Listening to queries */\r
-\r
-                this.listen_query(options.query_uuid);\r
-                //this.listen_query(options.query_all_uuid, 'all');\r
-                this.listen_query(options.query_all_resources_uuid, 'all_resources');\r
-                this.listen_query(options.query_lease_uuid, 'lease');\r
-                this.listen_query(options.query_all_leases_uuid, 'all_leases');\r
-                if (schedulerDebug) console.timeEnd("Listening_to_queries");\r
-\r
-            },\r
-\r
-            /* Handlers */\r
-\r
-            /* all_ev QUERY HANDLERS Start */\r
-            on_all_ev_clear_records: function(data) {\r
-                //alert('all_ev clear_records');\r
-            },\r
-            on_all_ev_query_in_progress: function(data) {\r
-                // alert('all_ev query_in_progress');\r
-            },\r
-            on_all_ev_new_record: function(data) {\r
-                //alert('all_ev new_record');\r
-            },\r
-            on_all_ev_query_done: function(data) {\r
-                //alert('all_ev query_done');\r
-            },\r
-            //another plugin has modified something, that requires you to update your display. \r
-            on_all_ev_field_state_changed: function(data) {\r
-                //alert('all_ev query_done');\r
-            },\r
-            /* all_ev QUERY HANDLERS End */\r
-            /* all_resources QUERY HANDLERS Start */\r
-            on_all_resources_clear_records: function(data) {\r
-                //data is empty on load\r
-            },\r
-            on_all_resources_query_in_progress: function(data) {\r
-                //data is empty on load\r
-            },\r
-            on_all_resources_new_record: function(data) {\r
-                //alert(data.toSource());\r
-                if (data.exclusive == true) {\r
-                    var tmpGran = schedulerDebug && data.granularity == null ? 1800 : data.granularity;\r
-                    SchedulerData.push({\r
-                        id: data.urn,\r
-                        index: SchedulerData.length,\r
-                        name: data.hrn,\r
-                        granularity: tmpGran,\r
-                        leases: schedulerGetLeases(60 / schedulerSlotsPerHour, tmpGran),\r
-                        type: data.type,\r
-                        org_resource: data\r
-                    });\r
-                    /*if (schedulerDebug && SchedulerData[SchedulerData.length - 1].org_resource.network_hrn == 'omf') {\r
-                        SchedulerData[SchedulerData.length - 1].granularity = 1800;\r
-                    }*/\r
-                }\r
-                //alert(data.toSource());\r
-\r
-            },\r
-            on_all_resources_query_done: function(data) {\r
-                _resourcesDone = true;\r
-                this._initScheduler();\r
-            },\r
-            //another plugin has modified something, that requires you to update your display. \r
-            on_all_resources_field_state_changed: function(data) {\r
-                //alert('all_resources query_done');\r
-            },\r
-            /* all_resources QUERY HANDLERS End */\r
-            /* lease QUERY HANDLERS Start */\r
-            on_lease_clear_records: function(data) { console.log('clear_records'); },\r
-            on_lease_query_in_progress: function(data) { console.log('lease_query_in_progress'); },\r
-            on_all_leases_new_record: function(data) {\r
-                if (data.resource.indexOf("nitos") > -1) {\r
-                    tmpSchedulerLeases.push({\r
-                        id: schedulerGetSlotId(data.start_time, data.duration, data.granularity),\r
-                        end_id: schedulerGetSlotId(data.end_time, data.duration, data.granularity),\r
-                        slice: data.slice,\r
-                        status: 'reserved',\r
-                        resource: data.resource,\r
-                        network: data.network,\r
-                        start_time: new Date(data.start_time * 1000),\r
-                        start_time_unixtimestamp: data.start_time,\r
-                        end_time: new Date(data.end_time * 1000),\r
-                        end_time_unixtimestamp: data.end_time,\r
-                        lease_type: data.lease_type,\r
-                        granularity: data.granularity,\r
-                        duration: data.duration\r
-                    });\r
-                }\r
-                //console.log(data.toSource()); console.log('lease_new_record');\r
-            },\r
-            on_all_leases_query_done: function(data) {\r
-                _leasesDone = true;\r
-                this._initScheduler();\r
-                // console.log('lease_query_done');\r
-            },\r
-            //another plugin has modified something, that requires you to update your display. \r
-            on_lease_field_state_changed: function(data) { console.log('lease_field_state_changed'); },\r
-            /* lease QUERY HANDLERS End */\r
-\r
-\r
-            // no prefix\r
-            on_filter_added: function(filter) {\r
-                this.filters.push(filter);\r
-                this._SetFiletredResources(this.filters);\r
-                //angular and UI\r
-                var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
-                if (SchedulerDataViewData.length == 0) {\r
-                    $("#plugin-scheduler").hide();\r
-                    $("#plugin-scheduler-empty").show();\r
-                    tmpScope.clearStuff();\r
-                } else {\r
-                    $("#plugin-scheduler-empty").hide();\r
-                    $("#plugin-scheduler").show();\r
-                    tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
-                }\r
-            },\r
-\r
-            on_filter_removed: function(filter) {\r
-                // Remove corresponding filters\r
-                this.filters = $.grep(this.filters, function(x) {\r
-                    return x == filter;\r
-                });\r
-                this._SetFiletredResources(this.filters);\r
-                //angular and UI\r
-                var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
-                if (SchedulerDataViewData.length == 0) {\r
-                    $("#plugin-scheduler").hide();\r
-                    $("#plugin-scheduler-empty").show();\r
-                    tmpScope.clearStuff();\r
-                } else {\r
-                    $("#plugin-scheduler-empty").hide();\r
-                    $("#plugin-scheduler").show();\r
-                    tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
-                }\r
-            },\r
-\r
-            on_filter_clear: function() {\r
-                this.filters = [];\r
-                this._SetFiletredResources(this.filters);\r
-                //angular and UI\r
-                var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
-                if (SchedulerDataViewData.length == 0) {\r
-                    $("#plugin-scheduler").hide();\r
-                    $("#plugin-scheduler-empty").show();\r
-                    tmpScope.clearStuff();\r
-                } else {\r
-                    $("#plugin-scheduler-empty").hide();\r
-                    $("#plugin-scheduler").show();\r
-                    tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
-                }\r
-            },\r
-\r
-            on_all_leases_filter_added: function(filter) {\r
-                console.log("Filter on Leases added !");\r
-            },\r
-\r
-            // ... be sure to list all events here\r
-\r
-            /* RECORD HANDLERS */\r
-            on_all_new_record: function(record) {\r
-                //alert('on_all_new_record');\r
-            },\r
-\r
-            debug: function(logTxt) {\r
-                if (typeof window.console != 'undefined') {\r
-                    console.debug(logTxt);\r
-                }\r
-            },\r
-\r
-            /* INTERNAL FUNCTIONS */\r
-            _initScheduler: function() {\r
-                if (_resourcesDone && _leasesDone) {\r
-                    SchedulerDataViewData = SchedulerData;\r
-                    /* GUI setup and event binding */\r
-                    this._FixLeases();\r
-                    this._initUI();\r
-                }\r
-            },\r
-\r
-            _initUI: function() {\r
-                //alert(1);\r
-                if (schedulerDebug) console.time("_initUI");\r
-                //init DatePicker Start\r
-                $("#DateToRes").datepicker({\r
-                    dateFormat: "yy-mm-dd",\r
-                    minDate: 0,\r
-                    numberOfMonths: 3\r
-                }).change(function() {\r
-                    //Scheduler2.loadWithDate();\r
-                    SchedulerDateSelected = $("#DateToRes").datepicker("getDate");\r
-                    if (SchedulerDateSelected != null && SchedulerDateSelected != '') {\r
-                        for (var i = 0; i < SchedulerData.length; i++) {\r
-                            SchedulerData[i].leases = schedulerGetLeases(60 / schedulerSlotsPerHour, SchedulerData[i].granularity);\r
-                        }\r
-                        scheduler2Instance._FixLeases();\r
-                        $('#tblSlider').slider('value', 0);\r
-                        var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
-                        tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
-\r
-                        //console.log(SchedulerDateSelected);\r
-                        //console.log(SchedulerDateSelected.getTime()/1000);\r
-                        var tomorrow = new Date(SchedulerDateSelected);\r
-                        tomorrow.setDate(SchedulerDateSelected.getDate()+1);\r
-                        //console.log(tomorrow);\r
-                        //console.log(tomorrow.getTime()/1000);\r
-                        \r
-                        // Remove previous date interval\r
-                        manifold.raise_event(scheduler2Instance.options.query_all_leases_uuid, FILTER_REMOVED, ['start_time', '>']);\r
-                        manifold.raise_event(scheduler2Instance.options.query_all_leases_uuid, FILTER_REMOVED, ['start_time', '<']);\r
-\r
-                        // Add new date interval\r
-                        manifold.raise_event(scheduler2Instance.options.query_all_leases_uuid, FILTER_ADDED, ['start_time', '>', SchedulerDateSelected.getTime()/1000]);\r
-                        manifold.raise_event(scheduler2Instance.options.query_all_leases_uuid, FILTER_ADDED, ['start_time', '<', tomorrow.getTime()/1000]);\r
-                    } else {\r
-                        alert("Please select a date, so the scheduler can reserve leases.");\r
-                    }\r
-                }).datepicker('setDate', SchedulerDateSelected);\r
-                /*.click(function () {\r
-                $("#ui-datepicker-div").css("z-index", 5);\r
-            })*/\r
-                //End init DatePicker\r
-\r
-                //init Table\r
-                this._FixTable();\r
-                //End init Table\r
-\r
-                //init Slider\r
-                $('#tblSlider').slider({\r
-                    min: 0,\r
-                    max: SchedulerTotalCells - SchedulerTotalVisibleCells,\r
-                    value: 0,\r
-                    slide: function(event, ui) {\r
-                        //$("#amount").val("$" + ui.values[0] + " - $" + ui.values[1]);\r
-                        //console.log(ui.value);\r
-                        var angScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
-                        if (_schedulerCurrentCellPosition > ui.value) {\r
-                            angScope.moveBackSlot(ui.value, ui.value + SchedulerTotalVisibleCells);\r
-                        } else if (_schedulerCurrentCellPosition < ui.value) {\r
-                            angScope.moveFrontSlot(ui.value, ui.value + SchedulerTotalVisibleCells);\r
-                        }\r
-                        _schedulerCurrentCellPosition = ui.value;\r
-                    }\r
-                });\r
-                //End init Slider\r
-\r
-\r
-                //btn Submit leases\r
-                $('#btnSchedulerSubmit').click(function () {\r
-                    console.log("click btnSchedulerSubmit");\r
-                    var leasesForCommit = new Array();\r
-                    var tmpDateTime = SchedulerDateSelected;\r
-                    console.log(SchedulerData);\r
-                    for (var i = 0; i < SchedulerData.length; i++)\r
-                    {\r
-                        var tpmR = SchedulerData[i];\r
-                        //for capturing start and end of the lease\r
-                        var newLeaseStarted = false;\r
-                        for (var j = 0; j < tpmR.leases.length; j++) {\r
-                            var tpmL = tpmR.leases[j];\r
-                            if (newLeaseStarted == false && tpmL.status == 'selected') {\r
-                                //get date of the slot\r
-                                tmpDateTime = schedulerGetDateTimeFromSlotId(tpmL.id, tmpDateTime);\r
-                                var unixStartTime = tmpDateTime.getTime() / 1000;\r
-                                //add lease object\r
-                                leasesForCommit.push({\r
-                                    resource: tpmR.id,\r
-                                    //granularity: tpmR.granularity,\r
-                                    //lease_type: null,\r
-                                    //slice: null,\r
-                                    start_time: unixStartTime,\r
-                                    end_time: null,\r
-                                    //duration: null\r
-                                });\r
-                                console.log(tpmR.id);\r
-                                newLeaseStarted = true;\r
-                            } else if (newLeaseStarted == true && tpmL.status != 'selected') {\r
-                                //get date of the slot\r
-                                tmpDateTime = schedulerGetDateTimeFromSlotId(tpmL.id, tmpDateTime);\r
-                                var unixEndTime = tmpDateTime.getTime() / 1000;\r
-                                //upate end_time\r
-                                var tmpCL = leasesForCommit[leasesForCommit.length - 1];\r
-                                tmpCL.end_time = unixEndTime;\r
-                                //tmpCL.duration = schedulerFindDuration(tmpCL.start_time, tmpCL.end_time, tmpCL.granularity);\r
-                                newLeaseStarted = false;\r
-                            }\r
-                        }\r
-                    }\r
-                    console.log(leasesForCommit);\r
-                    for (var i = 0; i < leasesForCommit.length; i++) {\r
-                        manifold.raise_event(scheduler2Instance.options.query_lease_uuid, SET_ADD, leasesForCommit[i]);\r
-                    }\r
-                });\r
-                //\r
-\r
-\r
-                //End btn Submit leases\r
-\r
-                //other stuff\r
-                $("#plugin-scheduler-loader").hide();\r
-                $("#plugin-scheduler").show();\r
-                //fixOddEvenClasses();\r
-                //$("#" + schedulerTblId + " td:not([class])").addClass("free");\r
-                if (schedulerDebug) console.timeEnd("_initUI");\r
-            },\r
-\r
-        _FixLeases  : function () {\r
-            for (var i = 0; i < tmpSchedulerLeases.length; i++) {\r
-                var tmpLea = tmpSchedulerLeases[i];\r
-                if ((schedulerCompareOnDay(tmpLea.start_time, SchedulerDateSelected) == 0) ||\r
-                                (tmpLea.start_time <= SchedulerDateSelected && SchedulerDateSelected <= tmpLea.end_time) || \r
-                                (schedulerCompareOnDay(tmpLea.end_time, SchedulerDateSelected) == 0)) {\r
-                    var tmpRes = schedulerFindResourceById(SchedulerData, tmpLea.resource);\r
-                    if (tmpRes != null) {\r
-                        //Replace Lease with current lease from the manifold\r
-                        var orgLease = tmpRes.leases[tmpLea.id];\r
-                        tmpLea['groupid'] = orgLease.groupid;\r
-                        tmpLea['groupIndex'] = orgLease.groupIndex;\r
-                        if (orgLease.groupIndex != 0) {\r
-                            if (!window.console) {\r
-                                console.warn('there is an error with the leases of the resource :' + tmpRes.name + '\n The lease start in the middle of the granularity!' + '\n The Scheduler plugin might not work!');\r
-                            }\r
-                        }\r
-                        tmpRes.leases[tmpLea.id] = tmpLea;\r
-                        this._ExtractLeaseSlots(tmpRes, tmpRes.leases[tmpLea.id]);\r
-                    }\r
-                }\r
-            }\r
-        },\r
-\r
-        _ExtractLeaseSlots: function (tmpRes, lease) {\r
-            var tmpStartDate = lease.start_time;\r
-            var tmpEndDate = lease.end_time;\r
-            var startLoop; var toLoop;\r
-            if (schedulerCompareOnDay(lease.start_time,lease.end_time) == 0) {\r
-                //in the same date\r
-                startLoop = lease.id;\r
-                toLoop = lease.end_id;\r
-            } else if (lease.start_time < SchedulerDateSelected && SchedulerDateSelected < lease.end_time) {\r
-                //one hole day (more than 3days)\r
-                startLoop = 0;\r
-                toLoop = tmpRes.leases.length;\r
-            } else if (schedulerCompareOnDay(lease.start_time, SchedulerDateSelected) == 0) {\r
-                //the same day and extends\r
-                startLoop = lease.id;\r
-                toLoop = tmpRes.leases.length;\r
-            } else if (schedulerCompareOnDay(lease.end_time, SchedulerDateSelected) == 0) {\r
-                //extends to the last say\r
-                startLoop = 0;\r
-                toLoop = lease.end_id;\r
-            }\r
-            //var minutGran = tmpRes.granularity * 60;\r
-            for (var li = lease.id; li < toLoop; li++) {\r
-                tmpRes.leases[li].status = 'reserved';\r
-            }\r
-            \r
-            //reserved\r
-            //tmpRes.leases[tmpLea.id\r
-        },\r
-\r
-        _FixTable: function () {\r
-            var colWidth = 50;\r
-            SchedulerTotalCells = SchedulerSlots.length;\r
-            $('#' + schedulerTblId + ' thead tr th:eq(0)').css("width", schedulerTblFirstColWidth); //.css("display", "block");\r
-            //this get width might need fix depending on the template \r
-            var tblwidth = $('#scheduler-tab').parent().outerWidth();\r
-            SchedulerTotalVisibleCells = parseInt((tblwidth - schedulerTblFirstColWidth) / colWidth);\r
-\r
-            //if (SchedulerData.length == 0) {\r
-            //    //puth some test data\r
-            //    SchedulerData.push({ name: 'xyz+aaa', leases: schedulerGetLeases(60 / schedulerSlotsPerHour), urn: 'xyz+aaa', type: 'node' });\r
-            //    SchedulerData.push({ name: 'xyz+bbb', leases: schedulerGetLeases(60 / schedulerSlotsPerHour), urn: 'xyz+bbb', type: 'node' });\r
-            //    SchedulerData.push({ name: 'xyz+ccc', leases: schedulerGetLeases(60 / schedulerSlotsPerHour), urn: 'xyz+ccc', type: 'node' });\r
-            //    SchedulerData.push({ name: 'nitos1', leases: schedulerGetLeases(60 / schedulerSlotsPerHour), urn: 'nitos1', type: 'node' });\r
-            //}\r
-            var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
-            tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
-\r
-        },\r
-\r
-        _SetFiletredResources : function (filters) {\r
-            if (filters.length > 0) {\r
-                SchedulerDataViewData = new Array();\r
-                var tmpAddIt = true;\r
-                for (var i = 0; i < SchedulerData.length; i++) {\r
-                    loopfilters:\r
-                    for (var f = 0; f < filters.length; f++) {\r
-                        tmpAddIt = this._FilterResource(SchedulerData[i], filters[f]);\r
-                        if (tmpAddIt == false) break loopfilters;\r
-                    }\r
-                    if (tmpAddIt) {\r
-                        SchedulerDataViewData.push(SchedulerData[i]);\r
-                    }\r
-                }\r
-            } else {\r
-                SchedulerDataViewData = SchedulerData;\r
-            }\r
-        },\r
-\r
-        _FilterResource: function (resource, filter) {\r
-            var key = filter[0];\r
-            var op = filter[1];\r
-            var value = filter[2];\r
-            var colValue = resource.org_resource[key];\r
-            var ret = true;\r
-            if (schedulerDebug &&  colValue == 'omf') colValue = 'nitos';\r
-\r
-            if (op == '=' || op == '==') {\r
-                if (colValue != value || colValue == null || colValue == "" || colValue == "n/a")\r
-                    ret = false;\r
-            } else if (op == 'included') {\r
-                $.each(value, function (i, x) {\r
-                    if (x == colValue) {\r
-                        ret = true;\r
-                        return false;\r
-                    } else {\r
-                        ret = false;\r
-                    }\r
-                });\r
-            } else if (op == '!=') {\r
-                if (colValue == value || colValue == null || colValue == "" || colValue == "n/a")\r
-                    ret = false;\r
-            }\r
-\r
-            return ret;\r
-        },\r
-\r
-        _SetPeriodInPage: function (start, end) {\r
-        }\r
-    });\r
-\r
-    //Sched2 = new Scheduler2();\r
-\r
-    /* Plugin registration */\r
-    $.plugin('Scheduler2', scheduler2);\r
-\r
-    // TODO Here use cases for instanciating plugins in different ways like in the pastie.\r
-\r
-\r
-})(jQuery);\r
-\r
-\r
-\r
+/*
+#
+# Copyright (c) 2013 NITLab, University of Thessaly, CERTH, Greece
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+#
+# This is a MySlice plugin for the NITOS Scheduler
+# Nitos Scheduler v1
+#
+*/
+
+// XXX groupid = all slots those that go with a min granularity
+
+/* some params */
+var scheduler2;
+var scheduler2Instance;
+//is ctrl keyboard button pressed
+var schedulerCtrlPressed = false;
+//table Id
+var schedulerTblId = "scheduler-reservation-table";
+var SCHEDULER_FIRST_COLWIDTH = 200;
+
+
+/* Number of scheduler slots per hour. Used to define granularity. Should be inferred from resources XXX */
+var schedulerSlotsPerHour = 6;
+var RESOURCE_DEFAULT_GRANULARITY    = 1800 /* s */; // should be computed automatically from resource information
+var DEFAULT_GRANULARITY             = 1800 /* s */; // should be computed automatically from resource information. Test with 600
+var DEFAULT_PAGE_RANGE = 5;
+
+var schedulerMaxRows = 12;
+
+/* All resources */
+var SchedulerData = [];
+
+/* ??? */
+var SchedulerSlots = [];
+
+var SchedulerDateSelected = new Date();
+// Round to midnight
+SchedulerDateSelected.setHours(0,0,0,0);
+
+/* Filtered resources */
+var SchedulerDataViewData = [];
+
+var SchedulerSlotsViewData = [];
+//Help Variables
+var _schedulerCurrentCellPosition = 0;
+//Enable Debug
+var schedulerDebug = true;
+//tmp to delete
+var tmpSchedulerLeases = [];
+
+var SCHEDULER_COLWIDTH = 50;
+
+
+/******************************************************************************
+ *                             ANGULAR CONTROLLER                             *
+ ******************************************************************************/
+
+// Create a private execution space for our controller. When
+// executing this function expression, we're going to pass in
+// the Angular reference and our application module.
+(function (ng, app) {
+
+    // Define our Controller constructor.
+    function Controller($scope) {
+
+        // Store the scope so we can reference it in our
+        // class methods
+        this.scope = $scope;
+
+        // Set up the default scope value.
+        this.scope.errorMessage = null;
+        this.scope.name = "";
+
+        //Pagin
+        $scope.current_page = 1;
+        this.scope.items_per_page = 10;
+        $scope.from = 0; // JORDAN
+
+        $scope.instance = null;
+        $scope.resources = new Array();
+        $scope.slots = SchedulerSlotsViewData;
+        $scope.granularity = DEFAULT_GRANULARITY; /* Will be setup */
+        //$scope.msg = "hello";
+
+        angular.element(document).ready(function() {
+            //console.log('Hello World');
+            //alert('Hello World');
+            //afterAngularRendered();
+        });
+
+        // Pagination
+
+        $scope.range = function() {
+            var range_size = $scope.page_count() > DEFAULT_PAGE_RANGE ? DEFAULT_PAGE_RANGE : $scope.page_count();
+            var ret = [];
+            var start;
+
+            start = $scope.current_page;
+            if ( start > $scope.page_count()-range_size ) {
+              start = $scope.page_count()-range_size+1;
+            }
+
+            for (var i=start; i<start+range_size; i++) {
+              ret.push(i);
+            }
+            return ret;
+        };
+
+        $scope.prevPage = function() {
+          if ($scope.current_page > 1) {
+            $scope.current_page--;
+          }
+        };
+
+        $scope.prevPageDisabled = function() {
+          return $scope.current_page === 1 ? "disabled" : "";
+        };
+  
+        $scope.page_count = function()
+        {
+            // XXX need visible resources only
+            var query_ext, visible_resources_length;
+            if (!$scope.instance)
+                return 0;
+            query_ext = manifold.query_store.find_analyzed_query_ext($scope.instance.options.query_uuid);
+            var visible_resources_length = 0;
+            query_ext.state.each(function(i, state) {
+                if (state[STATE_VISIBLE])
+                    visible_resources_length++;
+            });
+            return Math.ceil(visible_resources_length/$scope.items_per_page);
+        };
+  
+        $scope.nextPage = function() {
+          if ($scope.current_page < $scope.page_count()) {
+            $scope.current_page++;
+          }
+        };
+  
+        $scope.nextPageDisabled = function() {
+          return $scope.current_page === $scope.page_count() ? "disabled" : "";
+        }; 
+
+        $scope.setPage = function(n) {
+            $scope.current_page = n;
+        };
+        // END pagination
+
+        // FILTER
+
+        $scope.filter_visible = function(resource)
+        {
+            return manifold.query_store.get_record_state($scope.instance.options.query_uuid, resource['urn'], STATE_VISIBLE);
+        };
+
+        // SELECTION
+
+        $scope._create_new_lease = function(resource_urn, start_time, end_time)
+        {
+            var lease_key, new_lease, data;
+
+            lease_key = manifold.metadata.get_key('lease');
+
+            new_lease = {
+                resource:   resource_urn,
+                start_time: start_time,
+                end_time:   end_time,
+            };
+
+            // This is needed to create a hashable object
+            new_lease.hashCode = manifold.record_hashcode(lease_key.sort());
+            new_lease.equals   = manifold.record_equals(lease_key);
+
+            data = {
+                state: STATE_SET,
+                key  : null,
+                op   : STATE_SET_ADD,
+                value: new_lease
+            }
+            prev_state = manifold.query_store.get_record_state($scope.instance.options.query_uuid, resource_urn, STATE_SET);
+            manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data);
+
+            /* Add to local cache also, unless we listen to events from outside */
+            if (!(resource_urn in $scope._leases_by_resource)){
+                $scope._leases_by_resource[resource_urn] = [];
+                /* Add the resource of the selected timeslot to the pending list */
+                data_resource = {
+                    state: STATE_SET,
+                    key  : null,
+                    op   : STATE_SET_ADD,
+                    value: resource_urn
+                };
+                /* Send the message to the list of resources, depending on the previous state */
+                prev_state = manifold.query_store.get_record_state($scope.instance.options.query_uuid, data_resource.value, STATE_SET);
+                if(jQuery.inArray(prev_state,[STATE_SET_OUT,STATE_SET_OUT_SUCCESS,STATE_SET_OUT_PENDING,STATE_SET_IN_FAILURE])>-1){
+                    manifold.raise_event($scope.instance.options.query_uuid, FIELD_STATE_CHANGED, data_resource);
+                }
+                /* Remove the warning on resource as we have added Leases to it */
+                //manifold.raise_event($scope.instance.options.query_uuid, STATUS_REMOVE_WARNING, data_resource);
+            }
+            $scope._leases_by_resource[resource_urn].push(new_lease);
+
+        }
+
+        $scope._remove_lease = function(other)
+        {
+            var lease_key, other_key, data;
+
+            lease_key = manifold.metadata.get_key('lease');
+
+            // XXX This could be a manifold.record_get_value
+            other_key = {
+                resource:   other.resource,
+                start_time: other.start_time,
+                end_time:   other.end_time
+            }
+            other_key.hashCode = manifold.record_hashcode(lease_key.sort());
+            other_key.equals   = manifold.record_equals(lease_key);
+
+            data = {
+                state: STATE_SET,
+                key  : null,
+                op   : STATE_SET_REMOVE,
+                value: other_key
+            }
+            prev_state = manifold.query_store.get_record_state($scope.instance.options.query_uuid, other.resource, STATE_SET);
+            manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data);
+            /* Remove Lease from local cache also, unless we listen to events from outside */
+            $scope._leases_by_resource[other.resource] = $.grep($scope._leases_by_resource[other.resource], function(x) { return x != other; });
+            /* Last lease removed for this resource -> remove the resource from the list */
+            if($scope._leases_by_resource.hasOwnProperty(other.resource) && $scope._leases_by_resource[other.resource].length == 0){
+                /* remove resource from the list of selected resources */
+                 data_resource = {
+                    state: STATE_SET,
+                    key  : null,
+                    op   : STATE_SET_REMOVE,
+                    value: other.resource
+                };
+
+                prev_state = manifold.query_store.get_record_state($scope.instance.options.query_uuid, data_resource.value, STATE_SET);
+                /* Remove Resource from local cache */
+                delete $scope._leases_by_resource[data_resource.value]
+                /* Send the message to the list of resources, depending on the previous state */
+                if(jQuery.inArray(prev_state,[STATE_SET_IN,STATE_SET_IN_SUCCESS,STATE_SET_IN_PENDING,STATE_SET_OUT_FAILURE])>-1){
+                    manifold.raise_event($scope.instance.options.query_uuid, FIELD_STATE_CHANGED, data_resource);
+                    //manifold.raise_event($scope.instance.options.query_uuid, STATUS_REMOVE_WARNING, data_resource);
+                }
+               
+            }
+        }
+
+        $scope.select = function(index, model_lease, model_resource)
+        {
+            var data, resource_granularity;
+
+            //resource_granularity = model_resource.granularity === undefined ? RESOURCE_DEFAULT_GRANULARITY : model_resource.granularity;
+
+            console.log("Selected", index, model_lease, model_resource);
+
+            var day_timestamp = SchedulerDateSelected.getTime() / 1000;
+            var start_time = day_timestamp + index       * model_resource.granularity; // XXX resource_granularity
+            var end_time   = day_timestamp + (index + 1) * model_resource.granularity; //
+            var start_date = new Date(start_time * 1000);
+            var end_date   = new Date(end_time   * 1000);
+
+            var lease_key = manifold.metadata.get_key('lease');
+
+            // We search for leases in the cache we previously constructed
+            var resource_leases = $scope._leases_by_resource[model_resource.urn];
+
+            switch (model_lease.status)
+            {
+                case 'free': // out
+                case 'pendingout':
+                    if (resource_leases) {
+                        /* Search for leases before */
+                        $.each(resource_leases, function(i, other) {
+                            if (other.end_time != start_time)
+                                return true; // ~ continue
+        
+                            /* The lease 'other' is just before, and there should not exist
+                             * any other lease before it */
+                            start_time = other.start_time;
+        
+                            other_key = {
+                                resource:   other.resource,
+                                start_time: other.start_time,
+                                end_time:   other.end_time
+                            }
+                            // This is needed to create a hashable object
+                            other_key.hashCode = manifold.record_hashcode(lease_key.sort());
+                            other_key.equals   = manifold.record_equals(lease_key);
+        
+                            data = {
+                                state: STATE_SET,
+                                key  : null,
+                                op   : STATE_SET_REMOVE,
+                                value: other_key
+                            }
+                            manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data);
+                            /* Remove from local cache also, unless we listen to events from outside */
+                            $scope._leases_by_resource[model_resource.urn] = $.grep($scope._leases_by_resource[model_resource.urn], function(x) { return x != other; });
+                            return false; // ~ break
+                        });
+        
+                        /* Search for leases after */
+                        $.each(resource_leases, function(i, other) {
+                            if (other.start_time != end_time)
+                                return true; // ~ continue
+        
+                            /* The lease 'other' is just after, and there should not exist
+                             * any other lease after it */
+                            end_time = other.end_time;
+                            other_key = {
+                                resource:   other.resource,
+                                start_time: other.start_time,
+                                end_time:   other.end_time
+                            }
+                            // This is needed to create a hashable object
+                            other_key.hashCode = manifold.record_hashcode(lease_key.sort());
+                            other_key.equals   = manifold.record_equals(lease_key);
+        
+                            data = {
+                                state: STATE_SET,
+                                key  : null,
+                                op   : STATE_SET_REMOVE,
+                                value: other_key
+                            }
+                            manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data);
+                            /* Remove from local cache also, unless we listen to events from outside */
+                            $scope._leases_by_resource[model_resource.urn] = $.grep($scope._leases_by_resource[model_resource.urn], function(x) { return x != other; });
+                            return false; // ~ break
+                        });
+                    }
+        
+                    $scope._create_new_lease(model_resource.urn, start_time, end_time);
+                    model_lease.status = (model_lease.status == 'free') ? 'pendingin' : 'selected';
+                    // unless the exact same lease already existed (pending_out status for the lease, not the cell !!)
+
+                    break;
+
+                case 'selected':
+                case 'pendingin':
+                    // We remove the cell
+
+                    /* We search for leases including this cell. Either 0, 1 or 2.
+                     * 0 : NOT POSSIBLE, should be checked.
+                     * 1 : either IN or OUT, we have make no change in the session
+                     * 2 : both will be pending, since we have made a change in the session
+                    * /!\ need to properly remove pending_in leases when removed again
+                     */
+                    if (resource_leases) {
+                        $.each(resource_leases, function(i, other) {
+                            if ((other.start_time <= start_time) && (other.end_time >= end_time)) {
+                                // The cell is part of this lease.
+
+                                // If the cell is not at the beginning of the lease, we recreate a lease with cells before
+                                if (start_time > other.start_time) {
+                                    $scope._create_new_lease(model_resource.urn, other.start_time, start_time);
+                                }
+
+                                // If the cell is not at the end of the lease, we recreate a lease with cells after
+                                if (end_time < other.end_time) {
+                                    $scope._create_new_lease(model_resource.urn, end_time, other.end_time);
+                                }
+                                
+                                // The other lease will be removed
+                                $scope._remove_lease(other);
+                            }
+                            // NOTE: We can interrupt the search if we know that there is a single lease (depending on the status).
+                        });
+                    }
+                
+                    // cf comment in previous switch case
+                    model_lease.status = (model_lease.status == 'selected') ? 'pendingout' : 'free';
+
+                    break;
+
+                case 'reserved':
+                case 'maintainance':
+                    // Do nothing
+                    break;
+            }
+            
+
+            //$scope._dump_leases();
+        };
+  
+        $scope._dump_leases = function()
+        {
+            // DEBUG: display all leases and their status in the log
+            var leases = manifold.query_store.get_records($scope.instance.options.query_lease_uuid);
+            console.log("--------------------");
+            $.each(leases, function(i, lease) {
+                var key = manifold.metadata.get_key('lease');
+                var lease_key = manifold.record_get_value(lease, key);
+                var state = manifold.query_store.get_record_state($scope.instance.options.query_lease_uuid, lease_key, STATE_SET);
+                var state_str;
+                switch(state) {
+                    case STATE_SET_IN:
+                        state_str = 'STATE_SET_IN';
+                        break;
+                    case STATE_SET_OUT:
+                        state_str = 'STATE_SET_OUT';
+                        break;
+                    case STATE_SET_IN_PENDING:
+                        state_str = 'STATE_SET_IN_PENDING';
+                        break;
+                    case STATE_SET_OUT_PENDING:
+                        state_str = 'STATE_SET_OUT_PENDING';
+                        break;
+                    case STATE_SET_IN_SUCCESS:
+                        state_str = 'STATE_SET_IN_SUCCESS';
+                        break;
+                    case STATE_SET_OUT_SUCCESS:
+                        state_str = 'STATE_SET_OUT_SUCCESS';
+                        break;
+                    case STATE_SET_IN_FAILURE:
+                        state_str = 'STATE_SET_IN_FAILURE';
+                        break;
+                    case STATE_SET_OUT_FAILURE:
+                        state_str = 'STATE_SET_OUT_FAILURE';
+                        break;
+                }
+                console.log("LEASE", new Date(lease.start_time * 1000), new Date(lease.end_time * 1000), lease.resource, state_str);
+            });
+        };
+
+        // Return this object reference.
+        return (this);
+
+    }
+
+    // Define the Controller as the constructor function.
+    app.controller("SchedulerCtrl", Controller);
+
+})(angular, ManifoldApp);
+
+/******************************************************************************
+ *                              MANIFOLD PLUGIN                               *
+ ******************************************************************************/
+
+(function($) {
+        scheduler2 = Plugin.extend({
+
+            /** XXX to check
+         * @brief Plugin constructor
+         * @param options : an associative array of setting values
+         * @param element : 
+         * @return : a jQuery collection of objects on which the plugin is
+         *     applied, which allows to maintain chainability of calls
+         */
+            init: function(options, element) {
+                // Call the parent constructor, see FAQ when forgotten
+                this._super(options, element);
+
+                var scope = this._get_scope()
+                scope.instance = this;
+
+                // XXX not needed
+                scheduler2Instance = this;
+
+                // We need to remember the active filter for datatables filtering
+                // XXX not needed
+                this.filters = Array();
+
+                // XXX BETTER !!!!
+                $(window).delegate('*', 'keypress', function (evt){
+                        alert("erm");
+                      });
+
+                $(window).keydown(function(evt) {
+                    if (evt.which == 17) { // ctrl
+                        schedulerCtrlPressed = true;
+                    }
+                }).keyup(function(evt) {
+                    if (evt.which == 17) { // ctrl
+                        schedulerCtrlPressed = false;
+                    }
+                });
+
+                // XXX naming
+                //$("#" + schedulerTblId).on('mousedown', 'td', rangeMouseDown).on('mouseup', 'td', rangeMouseUp).on('mousemove', 'td', rangeMouseMove);
+
+                this._resources_received = false;
+                this._leases_received = false;
+                
+                scope._leases_by_resource = {};
+
+                /* Listening to queries */
+                this.listen_query(options.query_uuid, 'resources');
+                this.listen_query(options.query_lease_uuid, 'leases');
+
+                this.elmt().on('show', this, this.on_show);
+                this.elmt().on('shown.bs.tab', this, this.on_show);
+                this.elmt().on('resize', this, this.on_resize);
+
+                /* Generate slots according to the default granularity. Should
+                 * be updated when resources arrive.  Should be the pgcd in fact XXX */
+                this._granularity = DEFAULT_GRANULARITY;
+                scope.granularity = this._granularity;
+                this.scope_resources_by_key = {};
+
+                this.do_resize();
+    
+                scope.from = 0;
+
+                this._initUI();
+
+            },
+
+            do_resize: function()
+            {
+                var scope = this._get_scope();
+                var num_hidden_cells, new_max, lcm;
+
+                // do_resize has to be called when the window is resized, or one parameter changes
+                // e.g. when new resources have been received
+                //
+                this.resource_granularities = [3600, 1800]; //, 2400]; /* s */
+
+                /* Compute the slot length to accommodate all resources. This
+                 * is the GCD of all resource granularities. */
+                this._slot_length = this._gcdn(this.resource_granularities);
+
+                $('#' + schedulerTblId + ' thead tr th:eq(0)').css("width", SCHEDULER_FIRST_COLWIDTH);
+                //self get width might need fix depending on the template 
+                var tblwidth = $('#scheduler-reservation-table').parent().outerWidth();
+
+                /* Number of visible cells...*/
+                this._num_visible_cells = parseInt((tblwidth - SCHEDULER_FIRST_COLWIDTH) / SCHEDULER_COLWIDTH);
+
+                /* ...should be a multiple of the lcm of all encountered granularities. */
+                lcm = this._lcmn(this.resource_granularities) / this._slot_length;
+                this._num_visible_cells = this._num_visible_cells - this._num_visible_cells % lcm;
+
+                // A list of {id, time} dictionaries representing the slots for the given day
+                this._all_slots = this._generate_all_slots();
+
+                /* scope also needs this value */
+                scope.slots = this._all_slots;
+                scope.slot_length = this._slot_length;
+                scope.num_visible_cells = this._num_visible_cells;
+                scope.lcm_colspan = this._lcmn(this.resource_granularities); // XXX WHY ?
+
+                /* Redraw... */
+                this._scope_clear_leases();
+                this._set_all_lease_slots();
+
+                // Slider max value
+                if ($('#tblSlider').data('slider') != undefined) {
+                    num_hidden_cells = this._all_slots.length - this._num_visible_cells;
+
+                    $('#tblSlider').slider('setAttribute', 'max', num_hidden_cells);
+                    $('#tblSlider').slider('setValue', scope.from, true);
+                }
+                this._get_scope().$apply();
+
+
+            },
+
+            on_show: function(e)
+            {
+                var self = e.data;
+                self.do_resize();
+                self._get_scope().$apply();
+            },
+
+            on_resize: function(e)
+            {
+                var self = e.data;
+                self.do_resize();
+                self._get_scope().$apply();
+            },
+
+            /* Handlers */
+
+            _get_scope : function()
+            {
+                return angular.element(document.getElementById('SchedulerCtrl')).scope();
+            },
+            
+            _scope_set_resources : function()
+            {
+                var self = this;
+                var scope = this._get_scope();
+
+                var records = manifold.query_store.get_records(this.options.query_uuid);
+
+                scope.resources = [];
+
+                $.each(records, function(i, record) {
+                    if (!record.exclusive)
+                        return true; // ~ continue
+
+                    // copy not to modify original record
+                    var resource = jQuery.extend(true, {}, record);
+
+                    // Fix granularity
+                    //resource_granularity = ((resource.granularity === undefined) || (typeof(resource.granularity) != "number")) ? RESOURCE_DEFAULT_GRANULARITY : resource.granularity;
+                    if (typeof(resource.granularity) != "number")
+                        resource.granularity = RESOURCE_DEFAULT_GRANULARITY;
+                    resource.leases = []; // a list of occupied timeslots
+
+                    self.scope_resources_by_key[resource['urn']] = resource;
+                    scope.resources.push(resource);
+                });
+            },
+
+            _scope_clear_leases: function()
+            {
+                var time, now;
+                var self = this;
+                var scope = this._get_scope();
+
+                now = new Date().getTime();
+
+                // Setup leases with a default free status...
+                $.each(this.scope_resources_by_key, function(resource_key, resource) {
+                    resource.leases = [];
+                    var colspan_lease = resource.granularity / self._slot_length; //eg. 3600 / 1800 => 2 cells
+                    time = SchedulerDateSelected.getTime();
+                    for (i=0; i < self._all_slots.length / colspan_lease; i++) { // divide by granularity
+                        resource.leases.push({
+                            id:     'coucou',
+                            status: (time < now) ? 'disabled':  'free', // 'selected', 'reserved', 'maintenance' XXX pending ??
+                        });
+                        time += resource.granularity * 1000;
+                    }
+                });
+
+            },
+
+            _scope_set_leases: function()
+            {
+                    var status;
+                var self = this;
+                var scope = this._get_scope();
+            
+                manifold.query_store.iter_records(this.options.query_lease_uuid, function(lease_key, lease) {
+                    //console.log("SET LEASES", lease.resource, new Date(lease.start_time* 1000), new Date(lease.end_time* 1000));
+                    // XXX We should ensure leases are correctly merged, otherwise our algorithm won't work
+
+                    // Populate leases by resource array: this will help us merging leases later
+
+                    // let's only put _our_ leases
+                    lease_status = manifold.query_store.get_record_state(self.options.query_lease_uuid, lease_key, STATE_SET);
+                    if (lease_status != STATE_SET_IN)
+                        return true; // ~continue
+                    if (!(lease.resource in scope._leases_by_resource))
+                        scope._leases_by_resource[lease.resource] = [];
+                    scope._leases_by_resource[lease.resource].push(lease);
+
+                });
+
+                this._set_all_lease_slots();
+            },
+
+            _set_all_lease_slots: function()
+            {
+                var self = this;
+            
+                manifold.query_store.iter_records(this.options.query_lease_uuid, function(lease_key, lease) {
+                    self._set_lease_slots(lease_key, lease);
+                });
+            },
+
+            on_resources_query_done: function(data)
+            {
+                this._resources_received = true;
+                this._scope_set_resources();
+                this._scope_clear_leases();
+                if (this._leases_received)
+                    this._scope_set_leases();
+                    
+                this._get_scope().$apply();
+            },
+
+            on_leases_query_done: function(data)
+            {
+                this._leases_received = true;
+                if (this._resources_received) {
+                    this._scope_set_leases();
+                    this._get_scope().$apply();
+                }
+            },
+
+            /* Filters on resources */
+            on_resources_filter_added:   function(filter) { this._get_scope().$apply(); },
+            on_resources_filter_removed: function(filter) { this._get_scope().$apply(); },
+            on_resources_filter_clear:   function()       { this._get_scope().$apply(); },
+
+            /* Filters on leases ? */
+            on_leases_filter_added:      function(filter) { this._get_scope().$apply(); },
+            on_leases_filter_removed:    function(filter) { this._get_scope().$apply(); },
+            on_leases_filter_clear:      function()       { this._get_scope().$apply(); },
+
+            on_resources_field_state_changed: function(data)
+            {
+                console.log('on_resources_field_state_changed');
+                console.log(data);
+                switch(data.state) {
+                    case STATE_SET:
+                        switch(data.op) {
+                            /*
+                            case STATE_SET_IN:
+                            case STATE_SET_IN_SUCCESS:
+                            case STATE_SET_OUT_FAILURE:
+                                //this.set_checkbox_from_data(data.value, true);
+                                //this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_RESET);
+                                break;  
+                            */
+                            case STATE_SET_OUT:
+                            case STATE_SET_OUT_SUCCESS:
+                            case STATE_SET_IN_FAILURE:
+                                // A resource has been removed
+                                console.log(this._get_scope()._leases_by_resource);
+                                s = this._get_scope();
+                                // loop over the list of leases by resource cached
+                                $.each(this._get_scope()._leases_by_resource, function(k,v){
+                                    // if the resource removed is in the list
+                                    // we need to remove all the leases corresponding to that resoruce
+                                    if(k == data.value){
+                                        console.log(k,v);
+                                        // loop each lease should be removed
+                                        $.each(v, function(i,lease){
+                                            console.log(i,lease);
+                                            s._remove_lease(lease);
+                                        });
+                                    }
+                                });
+                                break;
+                            /*
+                            case STATE_SET_IN_PENDING:
+                                this.set_checkbox_from_data(data.key, true);
+                                this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_ADDED);
+                                break;  
+                            case STATE_SET_OUT_PENDING:
+                                this.set_checkbox_from_data(data.key, false);
+                                this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_REMOVED);
+                                break;
+                            */
+                        }
+                        break;
+                    /*
+                    case STATE_WARNINGS:
+                        this.change_status(data.key, data.value);
+                        break;
+                    */
+                }
+            },
+
+
+            /* INTERNAL FUNCTIONS */
+
+            _set_lease_slots: function(lease_key, lease)
+            {
+                var resource, lease_status, lease_class;
+                var day_timestamp, id_start, id_end, colspan_lease;
+
+                resource = this.scope_resources_by_key[lease.resource];
+                day_timestamp = SchedulerDateSelected.getTime() / 1000;
+                if(resource === undefined){
+                    console.log('resource undefined = '+lease.resource);
+                    return;
+                }
+                id_start = Math.floor((lease.start_time - day_timestamp) / resource.granularity);
+
+                /* Some leases might be in the past */
+                if (id_start < 0)
+                    id_start = 0;
+                /* Leases in the future: ignore */
+                if (id_start >= this._all_slots.length)
+                    return true; // ~ continue
+
+                id_end   = Math.ceil((lease.end_time   - day_timestamp) / resource.granularity);
+                colspan_lease = resource.granularity / this._slot_length; //eg. 3600 / 1800 => 2 cells
+                if (id_end >= this._all_slots.length / colspan_lease) {
+                    /* Limit the display to the current day */
+                    id_end = this._all_slots.length / colspan_lease
+                }
+                lease_status = manifold.query_store.get_record_state(this.options.query_lease_uuid, lease_key, STATE_SET);
+                // the same slots might be affected multiple times.
+                // PENDING_IN + PENDING_OUT => IN 
+                //
+                // RESERVED vs SELECTED !
+                //
+                // PENDING !!
+                switch(lease_status) {
+                    case STATE_SET_IN:
+                        lease_class = 'selected'; // my leases
+                        lease_success = '';
+                        break;
+                    case STATE_SET_IN_SUCCESS:
+                        lease_class = 'selected'; // my leases
+                        lease_success = 'success';
+                    case STATE_SET_OUT_FAILURE:
+                        lease_class = 'selected'; // my leases
+                        lease_success = 'failure';
+                        break;
+                    case STATE_SET_OUT:
+                        lease_class = 'reserved'; // other leases
+                        lease_success = '';
+                        break;
+                    case STATE_SET_OUT_SUCCESS:
+                        lease_class = 'free'; // other leases
+                        lease_success = 'success';
+                        break;
+                    case STATE_SET_IN_FAILURE:
+                        lease_class = 'free'; // other leases
+                        lease_success = 'failure';
+                        break;
+                    case STATE_SET_IN_PENDING:
+                        lease_class = 'pendingin';
+                        lease_success = '';
+                        break;
+                    case STATE_SET_OUT_PENDING:
+                        // pending_in & pending_out == IN == replacement
+                        if (resource.leases[i].status == 'pendingin')
+                            lease_class = 'pendingin'
+                        else
+                            lease_class = 'pendingout';
+                        lease_success = '';
+                        break;
+                
+                }
+
+                for (i = id_start; i < id_end; i++) {
+                    resource.leases[i].status = lease_class;
+                    resource.leases[i].success = lease_success;
+                }
+            },
+
+/* XXX IN TEMPLATE XXX
+                if (SchedulerDataViewData.length == 0) {
+                    $("#plugin-scheduler").hide();
+                    $("#plugin-scheduler-empty").show();
+                    tmpScope.clearStuff();
+                } else {
+                    $("#plugin-scheduler-empty").hide();
+                    $("#plugin-scheduler").show();
+                    // initSchedulerResources
+                    tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);
+                }
+*/
+
+            /**
+             * Initialize the date picker, the table, the slider and the buttons. Once done, display scheduler.
+             */
+            _initUI: function() 
+            {
+                var self = this;
+                var scope = self._get_scope();
+
+                var num_hidden_cells;
+
+                $("#DateToRes").datepicker({
+                       dateFormat: "D, d M yy",
+                    onRender: function(date) {
+                        return date.valueOf() < now.valueOf() ? 'disabled' : '';
+                    }
+                }).on('changeDate', function(ev) {
+                    SchedulerDateSelected = new Date(ev.date);
+                    SchedulerDateSelected.setHours(0,0,0,0);
+                    // Set slider to origin
+                    //$('#tblSlider').slider('setValue', 0); // XXX
+                    // Refresh leases
+                    self._scope_clear_leases();
+                    self._set_all_lease_slots();
+                    // Refresh display
+                    self._get_scope().$apply();
+                }).datepicker('setValue', SchedulerDateSelected); //.data('datepicker');
+
+                //init Slider
+                num_hidden_cells = self._all_slots.length - self._num_visible_cells;
+                init_cell = (new Date().getHours() - 1) * 3600 / self._granularity;
+                if (init_cell > num_hidden_cells)
+                    init_cell = num_hidden_cells;
+
+                $('#tblSlider').slider({
+                    min: 0,
+                    max: num_hidden_cells,
+                    value: init_cell,
+                }).on('slide', function(ev) {
+                    var scope = self._get_scope();
+                    scope.from = ev.value;
+                    scope.$apply();
+                });
+                scope.from = init_cell;
+                scope.$apply();
+
+                $("#plugin-scheduler-loader").hide();
+                $("#plugin-scheduler").show();
+            },
+
+        // PRIVATE METHODS
+
+        /**
+         * Greatest common divisor
+         */
+        _gcd : function(x, y)
+        {
+            return (y==0) ? x : this._gcd(y, x % y);
+        },
+
+        _gcdn : function(array)
+        {
+            var self = this;
+            return array.reduce(function(prev, cur, idx, arr) { return self._gcd(prev, cur); });
+        },
+
+        /**
+         * Least common multiple
+         */
+        _lcm : function(x, y)
+        {
+            return x * y / this._gcd(x, y);
+        },
+
+        _lcmn : function(array)
+        {
+            var self = this;
+            return array.reduce(function(prev, cur, idx, arr) { return self._lcm(prev, cur); });
+        },
+    
+        _pad_str : function(i)
+        {
+            return (i < 10) ? "0" + i : "" + i;
+        },
+
+        /**
+         * Member variables used:
+         *   _granularity
+         * 
+         * Returns:
+         *   A list of {id, time} dictionaries.
+         */
+        _generate_all_slots: function()
+        {
+            var slots = [];
+            // Start with a random date (a first of a month), only time will matter
+            var d = new Date(2014, 1, 1, 0, 0, 0, 0);
+            var i = 0;
+            // Loop until we change the day
+            while (d.getDate() == 1) {
+                // Nicely format the time...
+                var tmpTime = this._pad_str(d.getHours()) + ':' + this._pad_str(d.getMinutes());
+                /// ...and add the slot to the list of results
+                slots.push({ id: i, time: tmpTime });
+                // Increment the date with the granularity
+                d = new Date(d.getTime() + this._slot_length * 1000);
+                i++;
+            }
+            return slots;
+
+        },
+    });
+
+    /* Plugin registration */
+    $.plugin('Scheduler2', scheduler2);
+
+})(jQuery);
+
+
+