Revised version of the resource page + related plugins
authorJordan Augé <jordan.auge@lip6.fr>
Tue, 1 Jul 2014 08:12:40 +0000 (10:12 +0200)
committerJordan Augé <jordan.auge@lip6.fr>
Tue, 1 Jul 2014 08:12:40 +0000 (10:12 +0200)
34 files changed:
README.manifold-tables [new file with mode: 0644]
manifoldapi/static/js/hashtable.js [new file with mode: 0644]
manifoldapi/static/js/manifold-query.js
manifoldapi/static/js/manifold.js
manifoldapi/static/js/plugin.js
plugins/apply/__init__.py [new file with mode: 0644]
plugins/apply/static/css/apply.css [new file with mode: 0644]
plugins/apply/static/js/apply.js [new file with mode: 0644]
plugins/apply/templates/apply.html [new file with mode: 0644]
plugins/filter_status/__init__.py [new file with mode: 0644]
plugins/filter_status/static/js/filter_status.js [new file with mode: 0644]
plugins/filter_status/templates/filter_status.html [new file with mode: 0644]
plugins/maddash/static/css/maddash.css
plugins/querytable/__init__.py
plugins/querytable/static/css/querytable.css
plugins/querytable/static/js/querytable.js
plugins/querytable/templates/querytable.html
plugins/queryupdater/__init__.py
plugins/queryupdater/static/js/queryupdater.js
plugins/scheduler2/asdf.txt [deleted file]
plugins/scheduler2/static/js/scheduler-SchedulerCtrl.js
plugins/scheduler2/static/js/scheduler-helpers.js
plugins/scheduler2/static/js/scheduler-table-selector.js [deleted file]
plugins/scheduler2/static/js/scheduler2.js
plugins/scheduler2/static/js/scheduler2_old.js [deleted file]
plugins/scheduler2/templates/scheduler.html
portal/sliceresourceview.py
portal/sliceviewold.py
portal/templates/base.html
portal/templates/fed4fire/fed4fire_base.html
portal/templates/onelab/onelab_base.html
portal/templates/slice-resource-view.html
to-be-integrated/plugins/pres_view/static/css/pres_view.css
unfold/templates/page-queries.js

diff --git a/README.manifold-tables b/README.manifold-tables
new file mode 100644 (file)
index 0000000..6852dee
--- /dev/null
@@ -0,0 +1 @@
+manifold-tables -A -o authority -f \* -a R -j CACHE
diff --git a/manifoldapi/static/js/hashtable.js b/manifoldapi/static/js/hashtable.js
new file mode 100644 (file)
index 0000000..3922671
--- /dev/null
@@ -0,0 +1,402 @@
+/**\r
+ * @license jahashtable, a JavaScript implementation of a hash table. It creates a single constructor function called\r
+ * Hashtable in the global scope.\r
+ *\r
+ * http://www.timdown.co.uk/jshashtable/\r
+ * Copyright %%build:year%% Tim Down.\r
+ * Version: %%build:version%%\r
+ * Build date: %%build:date%%\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+var Hashtable = (function(UNDEFINED) {\r
+    var FUNCTION = "function", STRING = "string", UNDEF = "undefined";\r
+\r
+    // Require Array.prototype.splice, Object.prototype.hasOwnProperty and encodeURIComponent. In environments not\r
+    // having these (e.g. IE <= 5), we bail out now and leave Hashtable null.\r
+    if (typeof encodeURIComponent == UNDEF ||\r
+            Array.prototype.splice === UNDEFINED ||\r
+            Object.prototype.hasOwnProperty === UNDEFINED) {\r
+        return null;\r
+    }\r
+\r
+    function toStr(obj) {\r
+        return (typeof obj == STRING) ? obj : "" + obj;\r
+    }\r
+\r
+    function hashObject(obj) {\r
+        var hashCode;\r
+        if (typeof obj == STRING) {\r
+            return obj;\r
+        } else if (typeof obj.hashCode == FUNCTION) {\r
+            // Check the hashCode method really has returned a string\r
+            hashCode = obj.hashCode();\r
+            return (typeof hashCode == STRING) ? hashCode : hashObject(hashCode);\r
+        } else {\r
+            return toStr(obj);\r
+        }\r
+    }\r
+    \r
+    function merge(o1, o2) {\r
+        for (var i in o2) {\r
+            if (o2.hasOwnProperty(i)) {\r
+                o1[i] = o2[i];\r
+            }\r
+        }\r
+    }\r
+\r
+    function equals_fixedValueHasEquals(fixedValue, variableValue) {\r
+        return fixedValue.equals(variableValue);\r
+    }\r
+\r
+    function equals_fixedValueNoEquals(fixedValue, variableValue) {\r
+        return (typeof variableValue.equals == FUNCTION) ?\r
+            variableValue.equals(fixedValue) : (fixedValue === variableValue);\r
+    }\r
+\r
+    function createKeyValCheck(kvStr) {\r
+        return function(kv) {\r
+            if (kv === null) {\r
+                throw new Error("null is not a valid " + kvStr);\r
+            } else if (kv === UNDEFINED) {\r
+                throw new Error(kvStr + " must not be undefined");\r
+            }\r
+        };\r
+    }\r
+\r
+    var checkKey = createKeyValCheck("key"), checkValue = createKeyValCheck("value");\r
+\r
+    /*----------------------------------------------------------------------------------------------------------------*/\r
+\r
+    function Bucket(hash, firstKey, firstValue, equalityFunction) {\r
+        this[0] = hash;\r
+        this.entries = [];\r
+        this.addEntry(firstKey, firstValue);\r
+\r
+        if (equalityFunction !== null) {\r
+            this.getEqualityFunction = function() {\r
+                return equalityFunction;\r
+            };\r
+        }\r
+    }\r
+\r
+    var EXISTENCE = 0, ENTRY = 1, ENTRY_INDEX_AND_VALUE = 2;\r
+\r
+    function createBucketSearcher(mode) {\r
+        return function(key) {\r
+            var i = this.entries.length, entry, equals = this.getEqualityFunction(key);\r
+            while (i--) {\r
+                entry = this.entries[i];\r
+                if ( equals(key, entry[0]) ) {\r
+                    switch (mode) {\r
+                        case EXISTENCE:\r
+                            return true;\r
+                        case ENTRY:\r
+                            return entry;\r
+                        case ENTRY_INDEX_AND_VALUE:\r
+                            return [ i, entry[1] ];\r
+                    }\r
+                }\r
+            }\r
+            return false;\r
+        };\r
+    }\r
+\r
+    function createBucketLister(entryProperty) {\r
+        return function(aggregatedArr) {\r
+            var startIndex = aggregatedArr.length;\r
+            for (var i = 0, entries = this.entries, len = entries.length; i < len; ++i) {\r
+                aggregatedArr[startIndex + i] = entries[i][entryProperty];\r
+            }\r
+        };\r
+    }\r
+\r
+    Bucket.prototype = {\r
+        getEqualityFunction: function(searchValue) {\r
+            return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals;\r
+        },\r
+\r
+        getEntryForKey: createBucketSearcher(ENTRY),\r
+\r
+        getEntryAndIndexForKey: createBucketSearcher(ENTRY_INDEX_AND_VALUE),\r
+\r
+        removeEntryForKey: function(key) {\r
+            var result = this.getEntryAndIndexForKey(key);\r
+            if (result) {\r
+                this.entries.splice(result[0], 1);\r
+                return result[1];\r
+            }\r
+            return null;\r
+        },\r
+\r
+        addEntry: function(key, value) {\r
+            this.entries.push( [key, value] );\r
+        },\r
+\r
+        keys: createBucketLister(0),\r
+\r
+        values: createBucketLister(1),\r
+\r
+        getEntries: function(destEntries) {\r
+            var startIndex = destEntries.length;\r
+            for (var i = 0, entries = this.entries, len = entries.length; i < len; ++i) {\r
+                // Clone the entry stored in the bucket before adding to array\r
+                destEntries[startIndex + i] = entries[i].slice(0);\r
+            }\r
+        },\r
+\r
+        containsKey: createBucketSearcher(EXISTENCE),\r
+\r
+        containsValue: function(value) {\r
+            var entries = this.entries, i = entries.length;\r
+            while (i--) {\r
+                if ( value === entries[i][1] ) {\r
+                    return true;\r
+                }\r
+            }\r
+            return false;\r
+        }\r
+    };\r
+\r
+    /*----------------------------------------------------------------------------------------------------------------*/\r
+\r
+    // Supporting functions for searching hashtable buckets\r
+\r
+    function searchBuckets(buckets, hash) {\r
+        var i = buckets.length, bucket;\r
+        while (i--) {\r
+            bucket = buckets[i];\r
+            if (hash === bucket[0]) {\r
+                return i;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    function getBucketForHash(bucketsByHash, hash) {\r
+        var bucket = bucketsByHash[hash];\r
+\r
+        // Check that this is a genuine bucket and not something inherited from the bucketsByHash's prototype\r
+        return ( bucket && (bucket instanceof Bucket) ) ? bucket : null;\r
+    }\r
+\r
+    /*----------------------------------------------------------------------------------------------------------------*/\r
+\r
+    function Hashtable() {\r
+        var buckets = [];\r
+        var bucketsByHash = {};\r
+        var properties = {\r
+            replaceDuplicateKey: true,\r
+            hashCode: hashObject,\r
+            equals: null\r
+        };\r
+\r
+        var arg0 = arguments[0], arg1 = arguments[1];\r
+        if (arg1 !== UNDEFINED) {\r
+            properties.hashCode = arg0;\r
+            properties.equals = arg1;\r
+        } else if (arg0 !== UNDEFINED) {\r
+            merge(properties, arg0);\r
+        }\r
+\r
+        var hashCode = properties.hashCode, equals = properties.equals;\r
+\r
+        this.properties = properties;\r
+\r
+        this.put = function(key, value) {\r
+            checkKey(key);\r
+            checkValue(value);\r
+            var hash = hashCode(key), bucket, bucketEntry, oldValue = null;\r
+\r
+            // Check if a bucket exists for the bucket key\r
+            bucket = getBucketForHash(bucketsByHash, hash);\r
+            if (bucket) {\r
+                // Check this bucket to see if it already contains this key\r
+                bucketEntry = bucket.getEntryForKey(key);\r
+                if (bucketEntry) {\r
+                    // This bucket entry is the current mapping of key to value, so replace the old value.\r
+                    // Also, we optionally replace the key so that the latest key is stored.\r
+                    if (properties.replaceDuplicateKey) {\r
+                        bucketEntry[0] = key;\r
+                    }\r
+                    oldValue = bucketEntry[1];\r
+                    bucketEntry[1] = value;\r
+                } else {\r
+                    // The bucket does not contain an entry for this key, so add one\r
+                    bucket.addEntry(key, value);\r
+                }\r
+            } else {\r
+                // No bucket exists for the key, so create one and put our key/value mapping in\r
+                bucket = new Bucket(hash, key, value, equals);\r
+                buckets.push(bucket);\r
+                bucketsByHash[hash] = bucket;\r
+            }\r
+            return oldValue;\r
+        };\r
+\r
+        this.get = function(key) {\r
+            checkKey(key);\r
+\r
+            var hash = hashCode(key);\r
+\r
+            // Check if a bucket exists for the bucket key\r
+            var bucket = getBucketForHash(bucketsByHash, hash);\r
+            if (bucket) {\r
+                // Check this bucket to see if it contains this key\r
+                var bucketEntry = bucket.getEntryForKey(key);\r
+                if (bucketEntry) {\r
+                    // This bucket entry is the current mapping of key to value, so return the value.\r
+                    return bucketEntry[1];\r
+                }\r
+            }\r
+            return null;\r
+        };\r
+\r
+        this.containsKey = function(key) {\r
+            checkKey(key);\r
+            var bucketKey = hashCode(key);\r
+\r
+            // Check if a bucket exists for the bucket key\r
+            var bucket = getBucketForHash(bucketsByHash, bucketKey);\r
+\r
+            return bucket ? bucket.containsKey(key) : false;\r
+        };\r
+\r
+        this.containsValue = function(value) {\r
+            checkValue(value);\r
+            var i = buckets.length;\r
+            while (i--) {\r
+                if (buckets[i].containsValue(value)) {\r
+                    return true;\r
+                }\r
+            }\r
+            return false;\r
+        };\r
+\r
+        this.clear = function() {\r
+            buckets.length = 0;\r
+            bucketsByHash = {};\r
+        };\r
+\r
+        this.isEmpty = function() {\r
+            return !buckets.length;\r
+        };\r
+\r
+        var createBucketAggregator = function(bucketFuncName) {\r
+            return function() {\r
+                var aggregated = [], i = buckets.length;\r
+                while (i--) {\r
+                    buckets[i][bucketFuncName](aggregated);\r
+                }\r
+                return aggregated;\r
+            };\r
+        };\r
+\r
+        this.keys = createBucketAggregator("keys");\r
+        this.values = createBucketAggregator("values");\r
+        this.entries = createBucketAggregator("getEntries");\r
+\r
+        this.remove = function(key) {\r
+            checkKey(key);\r
+\r
+            var hash = hashCode(key), bucketIndex, oldValue = null;\r
+\r
+            // Check if a bucket exists for the bucket key\r
+            var bucket = getBucketForHash(bucketsByHash, hash);\r
+\r
+            if (bucket) {\r
+                // Remove entry from this bucket for this key\r
+                oldValue = bucket.removeEntryForKey(key);\r
+                if (oldValue !== null) {\r
+                    // Entry was removed, so check if bucket is empty\r
+                    if (bucket.entries.length == 0) {\r
+                        // Bucket is empty, so remove it from the bucket collections\r
+                        bucketIndex = searchBuckets(buckets, hash);\r
+                        buckets.splice(bucketIndex, 1);\r
+                        delete bucketsByHash[hash];\r
+                    }\r
+                }\r
+            }\r
+            return oldValue;\r
+        };\r
+\r
+        this.size = function() {\r
+            var total = 0, i = buckets.length;\r
+            while (i--) {\r
+                total += buckets[i].entries.length;\r
+            }\r
+            return total;\r
+        };\r
+    }\r
+\r
+    Hashtable.prototype = {\r
+        each: function(callback) {\r
+            var entries = this.entries(), i = entries.length, entry;\r
+            while (i--) {\r
+                entry = entries[i];\r
+                callback(entry[0], entry[1]);\r
+            }\r
+        },\r
+\r
+        equals: function(hashtable) {\r
+            var keys, key, val, count = this.size();\r
+            if (count == hashtable.size()) {\r
+                keys = this.keys();\r
+                while (count--) {\r
+                    key = keys[count];\r
+                    val = hashtable.get(key);\r
+                    if (val === null || val !== this.get(key)) {\r
+                        return false;\r
+                    }\r
+                }\r
+                return true;\r
+            }\r
+            return false;\r
+        },\r
+\r
+        putAll: function(hashtable, conflictCallback) {\r
+            var entries = hashtable.entries();\r
+            var entry, key, value, thisValue, i = entries.length;\r
+            var hasConflictCallback = (typeof conflictCallback == FUNCTION);\r
+            while (i--) {\r
+                entry = entries[i];\r
+                key = entry[0];\r
+                value = entry[1];\r
+\r
+                // Check for a conflict. The default behaviour is to overwrite the value for an existing key\r
+                if ( hasConflictCallback && (thisValue = this.get(key)) ) {\r
+                    value = conflictCallback(key, thisValue, value);\r
+                }\r
+                this.put(key, value);\r
+            }\r
+        },\r
+\r
+        clone: function() {\r
+            var clone = new Hashtable(this.properties);\r
+            clone.putAll(this);\r
+            return clone;\r
+        }\r
+    };\r
+\r
+    Hashtable.prototype.toQueryString = function() {\r
+        var entries = this.entries(), i = entries.length, entry;\r
+        var parts = [];\r
+        while (i--) {\r
+            entry = entries[i];\r
+            parts[i] = encodeURIComponent( toStr(entry[0]) ) + "=" + encodeURIComponent( toStr(entry[1]) );\r
+        }\r
+        return parts.join("&");\r
+    };\r
+\r
+    return Hashtable;\r
+})();\r
index 3116f84..64eb3a7 100644 (file)
@@ -1,3 +1,15 @@
+var guid = (function() {
+  function s4() {
+    return Math.floor((1 + Math.random()) * 0x10000)
+               .toString(16)
+               .substring(1);
+  }
+  return function() {
+    return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
+           s4() + '-' + s4() + s4() + s4();
+  };
+})();
+
 function ManifoldQuery(action, object, timestamp, filters, params, fields, unique, query_uuid, aq, sq) {  
     // get, update, delete, create
     var action;
@@ -131,7 +143,7 @@ INSERT INTO object VALUES(field=value)
             });
         };
 
-        if (this.analyzed_query !== undefined)
+        if (!!this.analyzed_query)
             query = this.analyzed_query;
         else
             query = this;
@@ -257,7 +269,10 @@ INSERT INTO object VALUES(field=value)
     else
         this.unique = unique;
 
-    this.query_uuid = query_uuid;
+    if (typeof unique == "undefined")
+        this.query_uuid = guid();
+    else
+        this.query_uuid = query_uuid;
 
     if (typeof aq == "undefined")
         this.analyzed_query = null;
index fa5415a..15f2123 100644 (file)
@@ -38,27 +38,71 @@ var FIELD_REMOVED  = 5;
 var CLEAR_FIELDS   = 6;
 var NEW_RECORD     = 7;
 var CLEAR_RECORDS  = 8;
+
+/**
+ * event: FIELD_STATE_CHANGED
+ *
+ * Parameters:
+ *   dict :
+ *      .request    : ???? used to be FIELD_REQUEST_ADD / FIELD_REQUEST_REMOVE
+ *      .key        : ??? the key fields of the record
+ *      .value      : the key of the record who has received an update
+ *      .status     : the new state of the record
+ *        TODO rename to state, and use values from STATE_SET            
+ */
 var FIELD_STATE_CHANGED = 9;
 
 var IN_PROGRESS    = 101;
 var DONE           = 102;
 
 /* Update requests related to subqueries */
+
+/**
+ * event: SET_ADD
+ *
+ * Parameters:
+ *    string : The key of the element being added
+ */
 var SET_ADD        = 201;
+
+/**
+ * event: SET_REMOVED
+ *
+ * Parameters:
+ *    string : The key of the element being removed
+ */
 var SET_REMOVED    = 202;
 
+
 // request
 var FIELD_REQUEST_CHANGE  = 301;
 var FIELD_REQUEST_ADD     = 302;
 var FIELD_REQUEST_REMOVE  = 303;
 var FIELD_REQUEST_ADD_RESET = 304;
 var FIELD_REQUEST_REMOVE_RESET = 305;
-// status
+// status (XXX Should be deprecated)
 var FIELD_REQUEST_PENDING = 401;
 var FIELD_REQUEST_SUCCESS = 402;
 var FIELD_REQUEST_FAILURE = 403;
+var STATUS_OKAY           = 404;
+var STATUS_SET_WARNING    = 405;
+var STATUS_ADD_WARNING    = 406;
+var STATUS_REMOVE_WARNING = 407;
+var STATUS_RESET          = 408;
+
+/* Requests for query cycle */
+var RUN_UPDATE     = 601;
+
+/* MANIFOLD types */
+var TYPE_VALUE  = 1;
+var TYPE_RECORD = 2;
+var TYPE_LIST_OF_VALUES = 3;
+var TYPE_LIST_OF_RECORDS = 4;
+
+/******************************************************************************
+ * QUERY STATUS (for manifold events)
+ ******************************************************************************/
 
-/* Query status */
 var STATUS_NONE               = 500; // Query has not been started yet
 var STATUS_GET_IN_PROGRESS    = 501; // Query has been sent, no result has been received
 var STATUS_GET_RECEIVED       = 502; // Success
@@ -68,19 +112,47 @@ var STATUS_UPDATE_IN_PROGRESS = 505;
 var STATUS_UPDATE_RECEIVED    = 506;
 var STATUS_UPDATE_ERROR       = 507;
 
-/* Requests for query cycle */
-var RUN_UPDATE     = 601;
+/******************************************************************************
+ * QUERY STATE (for query_store)
+ ******************************************************************************/
 
-/* MANIFOLD types */
-var TYPE_VALUE  = 1;
-var TYPE_RECORD = 2;
-var TYPE_LIST_OF_VALUES = 3;
-var TYPE_LIST_OF_RECORDS = 4;
+// XXX Rendundant with query status ?
+
+var QUERY_STATE_INIT        = 0;
+var QUERY_STATE_INPROGRESS  = 1;
+var QUERY_STATE_DONE        = 2;
+
+/******************************************************************************
+ * RECORD STATES (for query_store)
+ ******************************************************************************/
+
+var STATE_SET       = 0;
+var STATE_WARNINGS  = 1;
+var STATE_VISIBLE   = 2;
 
+// STATE_SET : enum
+var STATE_SET_IN            = 0;
+var STATE_SET_OUT           = 1;
+var STATE_SET_IN_PENDING    = 2;
+var STATE_SET_OUT_PENDING   = 3;
+var STATE_SET_IN_SUCCESS    = 4;
+var STATE_SET_OUT_SUCCESS   = 5;
+var STATE_SET_IN_FAILURE    = 6;
+var STATE_SET_OUT_FAILURE   = 7;
+
+// STATE_WARNINGS : dict
+
+// STATE_VISIBLE : boolean
+
+/******************************************************************************
+ * CONSTRAINTS
+ ******************************************************************************/
+
+var CONSTRAINT_RESERVABLE_LEASE     = 0;
 
 // A structure for storing queries
 
-function QueryExt(query, parent_query_ext, main_query_ext, update_query_ext, disabled) {
+function QueryExt(query, parent_query_ext, main_query_ext, update_query_ext, disabled, domain_query_ext) {
 
     /* Constructor */
     if (typeof query == "undefined")
@@ -90,10 +162,32 @@ function QueryExt(query, parent_query_ext, main_query_ext, update_query_ext, dis
     this.main_query_ext        = (typeof main_query_ext        == "undefined") ? null  : main_query_ext;
     this.update_query_ext      = (typeof update_query_ext      == "undefined") ? null  : update_query_ext;
     this.update_query_orig_ext = (typeof update_query_orig_ext == "undefined") ? null  : update_query_orig_ext;
-    this.disabled              = (typeof update_query_ext      == "undefined") ? false : disabled;
+    this.disabled              = (typeof disabled              == "undefined") ? false : disabled;
+
+    // A domain query is a query that is issued to retrieve all possible values for a set
+    // eg. all resources that can be attached to a slice
+    // It is null unless we are a subquery for which a domain query has been issued
+    this.domain_query_ext      = (typeof domain_query_ext      == "undefined") ? null  : domain_query_ext;
+
+    // Set members to buffer until the domain query is completed
+    // A list of keys
+    this.set_members = [];
+
+    // The set query is the query for which the domain query has been issued.
+    // It is null unless the query is a domain query
+    this.set_query_ext         = (typeof set_query_ext         == "undefined") ? null  : domain_query_ext;
     
-    this.status       = null;
-    this.results      = null;
+    this.query_state = QUERY_STATE_INIT;
+
+    // Results from a query consists in a dict that maps keys to records
+    this.records = new Hashtable();
+
+    // Status is a dict that maps keys to record status
+    this.state = new Hashtable();
+
+    // Filters that impact visibility in the local interface
+    this.filters = [];
+
     // update_query null unless we are a main_query (aka parent_query == null); only main_query_fields can be updated...
 }
 
@@ -142,13 +236,37 @@ function QueryStore() {
         // We also need to insert all queries and subqueries from the analyzed_query
         // XXX We need the root of all subqueries
         query.iter_subqueries(function(sq, data, parent_query) {
-            if (parent_query)
+            var parent_query_ext;
+            if (parent_query) {
                 parent_query_ext = manifold.query_store.find_analyzed_query_ext(parent_query.query_uuid);
-            else
+            } else {
                 parent_query_ext = null;
+            }
             // XXX parent_query_ext == false
             // XXX main.subqueries = {} # Normal, we need analyzed_query
             sq_ext = new QueryExt(sq, parent_query_ext, query_ext)
+
+            if (parent_query) {
+                /* Let's issue a query for the subquery domain. This query will not need any update etc.
+                   eg. for resources in a slice, we also query all resources */
+                var all_fields = manifold.metadata.get_field_names(sq.object);
+                var domain_query = new ManifoldQuery('get', sq.object, 'now', [], {}, all_fields); 
+                //var domain_query = new ManifoldQuery('get', sq.object); 
+
+                console.log("Created domain query", domain_query);
+                var domain_query_ext = new QueryExt(domain_query);
+
+                domain_query_ext.set_query_ext = sq_ext;
+                sq_ext.domain_query_ext = domain_query_ext;
+
+                // One of these two is useless ?
+                manifold.query_store.main_queries[domain_query.query_uuid] = domain_query_ext;
+                manifold.query_store.analyzed_queries[domain_query.query_uuid] = domain_query_ext;
+
+                // XXX This query is run before the plugins are initialized and listening
+                manifold.run_query(domain_query);
+            }
+
             manifold.query_store.analyzed_queries[sq.query_uuid] = sq_ext;
         });
 
@@ -157,21 +275,284 @@ function QueryStore() {
 
     /* Searching */
 
-    this.find_query_ext = function(query_uuid) {
+    this.find_query_ext = function(query_uuid)
+    {
         return this.main_queries[query_uuid];
     }
 
-    this.find_query = function(query_uuid) {
+    this.find_query = function(query_uuid) 
+    {
         return this.find_query_ext(query_uuid).query;
     }
 
-    this.find_analyzed_query_ext = function(query_uuid) {
+    this.find_analyzed_query_ext = function(query_uuid)
+    {
         return this.analyzed_queries[query_uuid];
     }
 
-    this.find_analyzed_query = function(query_uuid) {
+    this.find_analyzed_query = function(query_uuid) 
+    {
         return this.find_analyzed_query_ext(query_uuid).query;
     }
+
+    this.state_dict_create = function(default_set)
+    {
+        default_set = (default_set === undefined) ? STATE_SET_OUT : default_set;
+        var state_dict = {};
+        // We cannot use constants in literal definition, so...
+        state_dict[STATE_WARNINGS] = {};
+        state_dict[STATE_SET] = default_set;
+        state_dict[STATE_VISIBLE] = true;
+        return state_dict;
+    }
+
+    // RECORDS
+
+    this.set_records = function(query_uuid, records, default_set)
+    {
+        default_set = (default_set === undefined) ? STATE_SET_OUT : default_set;
+
+        var self = this;
+        var query_ext = this.find_analyzed_query_ext(query_uuid);
+        var record_key = manifold.metadata.get_key(query_ext.query.object);
+        $.each(records, function(i, record) {
+            var key = manifold.metadata.get_key(query_ext.query.object);
+            // ["start_time", "resource", "end_time"]
+            // ["urn"]
+            
+            var record_key_value = manifold.record_get_value(record, record_key);
+            query_ext.records.put(record_key_value, record);
+
+            if (!(query_ext.state.get(record_key_value)))
+                query_ext.state.put(record_key_value, self.state_dict_create(default_set));
+        });
+    }
+
+    this.get_records = function(query_uuid)
+    {
+        var query_ext = this.find_analyzed_query_ext(query_uuid);
+        return query_ext.records.values();
+    }
+
+    this.get_record = function(query_uuid, record_key)
+    {
+        var query_ext = this.find_analyzed_query_ext(query_uuid);
+        return query_ext.records.get(record_key);
+    }
+
+    this.add_record = function(query_uuid, record, new_state)
+    {
+        var query_ext = this.find_analyzed_query_ext(query_uuid);
+        
+        var key = manifold.metadata.get_key(query_ext.query.object);
+        var record_key = manifold.record_get_value(record, key);
+
+        var record_entry = query_ext.records.get(record_key);
+        if (!record_entry)
+            query_ext.records.put(record_key, record);
+
+        manifold.query_store.set_record_state(query_uuid, record_key, STATE_SET, new_state);
+    }
+
+    this.remove_record = function(query_uuid, record, new_state)
+    {
+        var query_ext = this.find_analyzed_query_ext(query_uuid);
+        
+        var key = manifold.metadata.get_key(query_ext.query.object);
+        var record_key = manifold.record_get_value(record, key);
+
+        manifold.query_store.set_record_state(query_uuid, record_key, STATE_SET, new_state);
+    }
+
+    this.iter_records = function(query_uuid, callback)
+    {
+        var query_ext = this.find_analyzed_query_ext(query_uuid);
+        query_ext.records.each(callback);
+        //callback = function(record_key, record)
+    }
+
+    this.iter_visible_records = function(query_uuid, callback)
+    {
+        var query_ext = this.find_analyzed_query_ext(query_uuid);
+        query_ext.records.each(function(record_key, record) {
+            if (query_ext.state.get(record_key)[STATE_VISIBLE]) // .STATE_VISIBLE would be for the string key
+                callback(record_key, record);
+        });
+        //callback = function(record_key, record)
+
+    }
+
+    // STATE
+
+    this.set_record_state = function(query_uuid, result_key, state, value)
+    {
+        var query_ext = this.find_analyzed_query_ext(query_uuid);
+        var state_dict = query_ext.state.get(result_key);
+        if (!state_dict)
+            state_dict = this.state_dict_create();
+
+        state_dict[state] = value;
+
+        query_ext.state.put(result_key, state_dict);
+    }
+
+    this.get_record_state = function(query_uuid, result_key, state)
+    {
+        var query_ext = this.find_analyzed_query_ext(query_uuid);
+        var state_dict = query_ext.state.get(result_key);
+        if (!state_dict)
+            return null;
+        return state_dict[state];
+    }
+
+    // FILTERS
+
+    this.add_filter = function(query_uuid, filter)
+    {
+        var query_ext = this.find_analyzed_query_ext(query_uuid);
+        // XXX When we update a filter
+        query_ext.filters.push(filter);
+
+        this.apply_filters(query_uuid);
+
+    }
+
+    this.update_filter = function(query_uuid, filter)
+    {
+        // XXX
+
+        this.apply_filters(query_uuid);
+    }
+
+    this.remove_filter = function(query_uuid, filter)
+    {
+        var query_ext = this.find_analyzed_query_ext(query_uuid);
+        query_ext.filters = $.grep(query_ext.filters, function(x) {
+            return x == filter;
+        });
+
+        this.apply_filters(query_uuid);
+    }
+
+    this.get_filters = function(query_uuid)
+    {
+        var query_ext = this.find_analyzed_query_ext(query_uuid);
+        return query_ext.filters;
+    }
+
+    this.apply_filters = function(query_uuid)
+    {
+        // Toggle visibility of records according to the different filters.
+
+        var self = this;
+        var filters = this.get_filters(query_uuid);
+        var col_value;
+
+        // Adapted from querytable._querytable_filter()
+
+        this.iter_records(query_uuid, function(record_key, record) {
+            var visible = true;
+
+            // We go through each filter and decide whether it affects the visibility of the record
+            $.each(filters, function(index, filter) {
+                var key = filter[0];
+                var op = filter[1];
+                var value = filter[2];
+
+
+                /* We do some special handling for the manifold:status filter
+                 * predicates. */
+
+                if (key == 'manifold:status') {
+                    if (op != '=' && op != '==') {
+                        // Unsupported filter, let's ignore it
+                        console.log("Unsupported filter on manifold:status. Should be EQUAL only.");
+                        return true; // ~ continue
+                    }
+
+                    var record_state = manifold.query_store.get_record_state(query_uuid, record_key, STATE_SET);
+                    var record_warnings = manifold.query_store.get_record_state(query_uuid, record_key, STATE_WARNINGS);
+
+                    switch (value) {
+                        case 'reserved':
+                            visible = (record_state == STATE_SET_IN) 
+                                   || (record_state == STATE_SET_OUT_PENDING)
+                                   || (record_state == STATE_SET_IN_SUCCESS)
+                                   || (record_state == STATE_SET_OUT_FAILURE);
+                            // visible = true  => ~ continue
+                            // visible = false => ~ break
+                            return visible; 
+
+                        case 'unconfigured':
+                            var in_set = (record_state == STATE_SET_IN) // should not have warnings
+                                   || (record_state == STATE_SET_IN_PENDING)
+                                   || (record_state == STATE_SET_IN_SUCCESS)
+                                   || (record_state == STATE_SET_OUT_FAILURE); // should not have warnings
+                            visible = (in_set && !$.isEmptyObject(record_warnings));
+                            return visible; 
+
+                        case 'pending':
+                            visible = (record_state == STATE_SET_IN_PENDING) 
+                                   || (record_state == STATE_SET_OUT_PENDING);
+                            return visible; 
+                    }
+                    return false; // ~ break
+                }
+
+                /* Normal filtering behaviour (according to the record content) follows... */
+                col_value = manifold.record_get_value(record, record_key);
+
+                // When the filter does not match, we hide the column by default
+                if (col_value === 'undefined') {
+                    visible = false;
+                    return false; // ~ break
+                }
+
+                // XXX This should accept pluggable filtering functions.
+
+
+                /* Test whether current filter is compatible with the column */
+                if (op == '=' || op == '==') {
+                    if ( col_value != value || col_value==null || col_value=="" || col_value=="n/a")
+                        visible = false;
+                }else if (op == 'included') {
+                    $.each(value, function(i,x) {
+                      if(x == col_value){
+                          visible = true;
+                          return false; // ~ break
+                      }else{
+                          visible = false;
+                      }
+                    });
+                }else if (op == '!=') {
+                    if ( col_value == value || col_value==null || col_value=="" || col_value=="n/a")
+                        visible = false;
+                } else if(op=='<') {
+                    if ( parseFloat(col_value) >= value || col_value==null || col_value=="" || col_value=="n/a")
+                        visible = false;
+                } else if(op=='>') {
+                    if ( parseFloat(col_value) <= value || col_value==null || col_value=="" || col_value=="n/a")
+                        visible = false;
+                } else if(op=='<=' || op=='≤') {
+                    if ( parseFloat(col_value) > value || col_value==null || col_value=="" || col_value=="n/a")
+                        visible = false;
+                } else if(op=='>=' || op=='≥') {
+                    if ( parseFloat(col_value) < value || col_value==null || col_value=="" || col_value=="n/a")
+                        visible = false;
+                }else{
+                    // How to break out of a loop ?
+                    alert("filter not supported");
+                    return false; // break
+                }
+
+            });
+
+            // Set the visibility status in the query store
+            self.set_record_state(query_uuid, record_key, STATE_VISIBLE, visible);
+        });
+
+    }
+
 }
 
 /*!
@@ -201,6 +582,108 @@ var manifold = {
         }
     },
 
+    /**
+     *  Args:
+     *      fields: A String instance (field name), or a set of String instances
+     *          (field names) # XXX tuple !!
+     *  Returns:
+     *      If fields is a String,  return the corresponding value.
+     *      If fields is a set, return a tuple of corresponding value.
+     *
+     *  Raises:
+     *      KeyError if at least one of the fields is not found
+     */
+    record_get_value: function(record, fields) 
+    {
+        if (typeof(fields) === 'string') {
+            if (fields.indexOf('.') != -1) {
+                key_subkey = key.split('.', 2);
+                key     = key_subkey[0]; 
+                subkey  = key_subkey[1];
+
+                if (record.indexOf(key) == -1) {
+                    return null;
+                }
+                // Tests if the following is an array (typeof would give object)
+                if (Object.prototype.toString.call(record[key]) === '[object Array]') {
+                    // Records
+                    return $.map(record[key], function(subrecord) { return manifold.record_get_value(subrecord, subkey) });
+                } else if (typeof(record) == 'object') {
+                    // Record
+                    return manifold.record_get_value(record[key], subkey);
+                } else {
+                    console.log('Unknown field');
+                }
+            } else {
+                return record[fields];
+            }
+        } else {
+            // see. get_map_entries
+            if (fields.length == 1)
+                return manifold.record_get_value(record, fields[0])
+
+            // Build a new record
+            var ret = {};
+            $.each(fields, function(i, field) {
+                ret[field] = manifold.record_get_value(record, field);
+            });
+            ret.hashCode = record.hashCode;
+            ret.equals = record.equals;
+            return ret;
+            // this was an array, we want a dictionary
+            //return $.map(fields, function(x) { manifold.record_get_value(record, x) });
+                
+        }
+    },
+
+    record_hashcode: function(key_fields)
+    {
+        return function() {
+            ret = "";
+            for (i=0; i < key_fields.length; i++)
+                ret += "@@" + this[key_fields[i]];
+            return ret;
+        };
+    },
+
+    record_equals: function(key_fields)
+    {
+        var self = this;
+
+        return function(other) {
+            for (i=0; i < key_fields.length; i++) {
+                var this_value  = this[key_fields[i]];
+                var other_value = other[key_fields[i]];
+
+                var this_type = self.get_type(this_value);
+                var other_type = self.get_type(other_value);
+                if (this_type != other_type)
+                    return false;
+
+                switch (this_type) {
+                    case TYPE_VALUE:
+                    case TYPE_LIST_OF_VALUES:
+                        if (this_value != other_value)
+                            return false;
+                        break;
+                    case TYPE_RECORD:
+                        if (!(record_equals(this_value, other_value)))
+                            return false;
+                        break;
+                    case TYPE_LIST_OF_RECORDS:
+                        if (this_value.length != other_value.length)
+                            return false;
+                        for (i = 0; i < this_value.length; i++)
+                            if (!(record_equals(this_value, other_value)))
+                                return false;
+                        break;
+                }
+            }
+            return true;
+        };
+    },
+
+
     /************************************************************************** 
      * Metadata management
      **************************************************************************/ 
@@ -221,6 +704,14 @@ var manifold = {
             return (typeof table.column === 'undefined') ? null : table.column;
         },
 
+        get_field_names: function(method)
+        {
+            var columns = this.get_columns(method);
+            if (!columns)
+                return null;
+            return $.map(columns, function (x) { return x.name });
+        },
+
         get_key: function(method) {
             var table = this.get_table(method);
             if (!table)
@@ -247,6 +738,12 @@ var manifold = {
             if (!table)
                 return null;
 
+            var match = $.grep(table.column, function(x) { return x.name == name });
+            if (match.length == 0) {
+                return undefined;
+            } else {
+                return match[0].type;
+            }
             return (typeof table.type === 'undefined') ? null : table.type;
         }
 
@@ -277,6 +774,9 @@ var manifold = {
         // NEW API
         manifold.query_store.insert(query);
 
+        // Run
+        manifold.run_query(query);
+
         // FORMER API
         if (query.analyzed_query == null) {
             query.analyze_subqueries();
@@ -316,21 +816,30 @@ var manifold = {
         }
     },
 
-    run_query: function(query, callback) {
+    run_query: function(query, callback)
+        {
         // default value for callback = null
         if (typeof callback === 'undefined')
             callback = null; 
 
+        var query_ext = manifold.query_store.find_query_ext(query.query_uuid);
+        query_ext.query_state = QUERY_STATE_INPROGRESS;
+
         var query_json = JSON.stringify(query);
 
-        /* Nothing related to pubsub here... for the moment at least. */
-        //query.iter_subqueries(function (sq) {
-        //    manifold.raise_record_event(sq.query_uuid, IN_PROGRESS);
-        //});
+        // Inform plugins about the progress
+        query.iter_subqueries(function (sq) {
+            var sq_query_ext = manifold.query_store.find_analyzed_query_ext(sq.query_uuid);
+            sq_query_ext.query_state = QUERY_STATE_INPROGRESS;
+
+            manifold.raise_record_event(sq.query_uuid, IN_PROGRESS);
+        });
+
 
         $.post(manifold.proxy_url, {'json': query_json} , manifold.success_closure(query, null, callback));
     },
 
+    // XXX DEPRECATED
     // Executes all async. queries - intended for the javascript header to initialize queries
     // input queries are specified as a list of {'query_uuid': <query_uuid> }
     // each plugin is responsible for managing its spinner through on_query_in_progress
@@ -421,6 +930,29 @@ var manifold = {
             messages.debug(".. publish_result (5) END q=" + query.__repr());
     },
 
+    store_records: function(query, records) {
+        // Store records
+        var query_ext = manifold.query_store.find_analyzed_query_ext(query.query_uuid);
+        if (query_ext.set_query_ext) {
+            // We have a domain query
+            // The results are stored in the corresponding set_query
+            manifold.query_store.set_records(query_ext.set_query_ext.query.query_uuid, records)
+            
+        } else if (query_ext.domain_query_ext) {
+            // We have a set query, it is only used to determine which objects are in the set, we should only retrieve the key
+            // Has it a domain query, and has it completed ?
+            $.each(records, function(i, record) {
+                var key = manifold.metadata.get_key(query.object);
+                var record_key = manifold.record_get_value(record, key);
+                manifold.query_store.set_record_state(query.query_uuid, record_key, STATE_SET, STATE_SET_IN);
+            });
+
+        } else {
+            // We have a normal query
+            manifold.query_store.set_records(query.query_uuid, records, STATE_SET_IN);
+        }
+    },
+
     /*!
      * Recursively publish result
      * \fn publish_result_rec(query, result)
@@ -430,24 +962,43 @@ var manifold = {
      *
      * Note: this function works on the analyzed query
      */
-    publish_result_rec: function(query, result) {
+    publish_result_rec: function(query, records) {
         /* If the result is not unique, only publish the top query;
          * otherwise, publish the main object as well as subqueries
          * XXX how much recursive are we ?
          */
         if (manifold.pubsub_debug)
-            messages.debug (">>>>> publish_result_rec " + query.object);
+             messages.debug (">>>>> publish_result_rec " + query.object);
         if (manifold.query_expects_unique_result(query)) {
             /* Also publish subqueries */
             $.each(query.subqueries, function(object, subquery) {
-                manifold.publish_result_rec(subquery, result[0][object]);
+                manifold.publish_result_rec(subquery, records[0][object]);
                 /* TODO remove object from result */
             });
         }
         if (manifold.pubsub_debug) 
             messages.debug ("===== publish_result_rec " + query.object);
 
-        manifold.publish_result(query, result);
+        var query_ext = manifold.query_store.find_analyzed_query_ext(query.query_uuid);
+        query_ext.query_state = QUERY_STATE_DONE;
+
+        this.store_records(query, records);
+
+        var pub_query;
+
+        if (query_ext.set_query_ext) {
+            if (query_ext.set_query_ext.query_state != QUERY_STATE_DONE)
+                return;
+            pub_query = query_ext.set_query_ext.query;
+        } else if (query_ext.domain_query_ext) {
+            if (query_ext.domain_query_ext.query_state != QUERY_STATE_DONE)
+                return;
+            pub_query = query;
+        } else {
+            pub_query = query;
+        }
+        // We can only publish results if the query (and its related domain query) is complete
+        manifold.publish_result(pub_query, records);
 
         if (manifold.pubsub_debug) 
             messages.debug ("<<<<< publish_result_rec " + query.object);
@@ -462,6 +1013,11 @@ var manifold = {
         var record = records[0];
 
         var update_query_ext = query_ext.update_query_ext;
+
+        console.log("Update case not handled yet!");
+        if (!update_query_ext)
+            return;
+
         var update_query = update_query_ext.query;
         var update_query_ext = query_ext.update_query_ext;
         var update_query_orig = query_ext.update_query_orig_ext.query;
@@ -480,13 +1036,7 @@ var manifold = {
                 if (!subrecords)
                     continue
                 $.each(subrecords, function (i, subrecord) {
-                    if (key.length == 1){
-                        key = key[0];
-                        sq_keys.push(subrecord[key]);
-                    }else{
-                        // more than what's necessary, but should work
-                        sq_keys.push(subrecord);
-                    }
+                    sq_keys.push(manifold.record_get_value(subrecord, key));
                 });
                 update_query.params[method] = sq_keys;
                 update_query_orig.params[method] = sq_keys.slice();
@@ -502,10 +1052,46 @@ var manifold = {
 
     process_get_query_records: function(query, records) {
         this.setup_update_query(query, records);
+        
+        var query_ext = manifold.query_store.find_query_ext(query.query_uuid);
+        query_ext.query_state = QUERY_STATE_DONE;
 
         /* Publish full results */
-        var tmp_query = manifold.find_query(query.query_uuid);
-        manifold.publish_result_rec(tmp_query.analyzed_query, records);
+        var tmp_query = manifold.query_store.find_analyzed_query(query.query_uuid);
+        manifold.publish_result_rec(tmp_query, records);
+    },
+
+    make_records: function(object, records)
+    {
+        $.each(records, function(i, record) {
+            manifold.make_record(object, record);
+        });
+    },
+
+    make_record: function(object, record)
+    {
+        // To make an object a record, we just add the hash function
+        var key = manifold.metadata.get_key(object);
+        record.hashCode = manifold.record_hashcode(key.sort());
+        record.equals   = manifold.record_equals(key);
+
+        // Looking after subrecords
+        for (var field in record) {
+            var result_value = record[field];
+
+            switch (this.get_type(result_value)) {
+                case TYPE_RECORD:
+                    var subobject = manifold.metadata.get_type(object, field);
+                    if (subobject)
+                        manifold.make_record(subobject, result_value);
+                    break;
+                case TYPE_LIST_OF_RECORDS:
+                    var subobject = manifold.metadata.get_type(object, field);
+                    if (subobject)
+                        manifold.make_records(subobject, result_value);
+                    break;
+            }
+        }
     },
 
     /**
@@ -523,6 +1109,21 @@ var manifold = {
      * previous 'process_get_query_records' function.
      */
     process_update_query_records: function(query, records) {
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
+        // XXX XXX XXX XXX
         // First issue: we request everything, and not only what we modify, so will will have to ignore some fields
         var query_uuid        = query.query_uuid;
         var query_ext         = manifold.query_store.find_analyzed_query_ext(query_uuid);
@@ -534,7 +1135,8 @@ var manifold = {
 
         // Let's iterate over the object properties
         for (var field in record) {
-            switch (this.get_type(record[field])) {
+            var result_value = record[field];
+            switch (this.get_type(result_value)) {
                 case TYPE_VALUE:
                     // Did we ask for a change ?
                     var update_value = update_query[field];
@@ -544,7 +1146,6 @@ var manifold = {
                         // We assume it won't have changed
                         continue;
 
-                    var result_value = record[field];
                     if (!result_value)
                         throw "Internal error";
 
@@ -563,7 +1164,6 @@ var manifold = {
 
                 case TYPE_LIST_OF_VALUES:
                     // Same as list of records, but we don't have to extract keys
-                    var result_keys  = record[field]
                     
                     // The rest of exactly the same (XXX factorize)
                     var update_keys  = update_query_orig.params[field];
@@ -573,7 +1173,7 @@ var manifold = {
 
 
                     $.each(added_keys, function(i, key) {
-                        if ($.inArray(key, result_keys) == -1) {
+                        if ($.inArray(key, result_value) == -1) {
                             data = {
                                 request: FIELD_REQUEST_ADD,
                                 key   : field,
@@ -626,7 +1226,7 @@ var manifold = {
                     key = key[0];
 
                     /* XXX should be modified for multiple keys */
-                    var result_keys  = $.map(record[field], function(x) { return x[key]; });
+                    var result_keys  = $.map(record[field], function(x) { return manifold.record_get_value(x, key); });
 
                     var update_keys  = update_query_orig.params[field];
                     var query_keys   = update_query.params[field];
@@ -735,6 +1335,7 @@ var manifold = {
         var result=data.value;
         if (result) {
             /* Eventually update the content of related queries (update, etc) */
+            manifold.make_records(query.object, result);
             this.process_query_records(query, result);
 
             /* Publish results: disabled here, done in the previous call */
@@ -794,6 +1395,7 @@ var manifold = {
         query = query_ext.query;
 
         switch(event_type) {
+
             case FIELD_STATE_CHANGED:
                 // value is an object (request, key, value, status)
                 // update is only possible is the query is not pending, etc
@@ -862,7 +1464,124 @@ var manifold = {
 
             case SET_ADD:
             case SET_REMOVED:
-    
+
+                /* An object has been added to / removed from a set : its
+                 * status become pending or reset to the original state. We
+                 * update the record status in the analyzed queries.
+                 *
+                 * XXX Shall we update something in the main_query ?
+                 */
+                var prev_state, new_state;
+
+                prev_state = manifold.query_store.get_record_state(query_uuid, value, STATE_SET);
+                if (prev_state === null)
+                    prev_state = STATE_SET_OUT;
+
+                if (event_type == SET_ADD) {
+                    switch (prev_state) {
+                        case STATE_SET_OUT:
+                        case STATE_SET_OUT_SUCCESS:
+                        case STATE_SET_IN_FAILURE:
+                            new_state = STATE_SET_IN_PENDING;
+                            break;
+
+                        case STATE_SET_OUT_PENDING:
+                            new_state = STATE_SET_IN;
+                            break;
+
+                        case STATE_SET_IN:
+                        case STATE_SET_IN_PENDING:
+                        case STATE_SET_IN_SUCCESS:
+                        case STATE_SET_OUT_FAILURE:
+                            console.log("Inconsistent state: already in");
+                            return;
+                    }
+                } else { // SET_REMOVE
+                    switch (prev_state) {
+                        case STATE_SET_IN:
+                        case STATE_SET_IN_SUCCESS:
+                        case STATE_SET_OUT_FAILURE:
+                            new_state = STATE_SET_OUT_PENDING;
+                            break;
+
+                        case STATE_SET_IN_PENDING:
+                            new_state = STATE_SET_OUT;
+                            break;  
+
+                        case STATE_SET_OUT:
+                        case STATE_SET_OUT_PENDING:
+                        case STATE_SET_OUT_SUCCESS:
+                        case STATE_SET_IN_FAILURE:
+                            console.log("Inconsistent state: already out");
+                            return;
+                    }
+                }
+
+                
+                var resource_key = value;
+
+                if (event_type == SET_ADD)
+                    manifold.query_store.add_record(query_uuid, resource_key, new_state);
+                else
+                    manifold.query_store.remove_record(query_uuid, resource_key, new_state);
+
+                var record = manifold.query_store.get_record(query_uuid, resource_key);
+
+                /* CONSTRAINTS */
+
+                // CONSTRAINT_RESERVABLE_LEASE
+                // 
+                // +) If a reservable node is added to the slice, then it should have a corresponding lease
+                var is_reservable = (record.exclusive == true);
+                if (is_reservable) {
+                    var warnings = manifold.query_store.get_record_state(query_uuid, resource_key, STATE_WARNINGS);
+
+                    if (event_type == SET_ADD) {
+                        // We should have a lease_query associated
+                        var lease_query = query_ext.parent_query_ext.query.subqueries['lease'];
+                        var lease_query_ext = manifold.query_store.find_analyzed_query_ext(lease_query.query_uuid);
+                        // Do we have lease records with this resource
+                        var lease_records = $.grep(lease_query_ext.records, function(lease_key, lease) {
+                            return lease['resource'] == value;
+                        });
+                        if (lease_records.length == 0) {
+                            // Sets a warning
+                            // XXX Need for a better function to manage warnings
+                            var warn = "No lease defined for this reservable resource.";
+                            warnings[CONSTRAINT_RESERVABLE_LEASE] = warn;
+                        } else {
+                            // Lease are defined, delete the warning in case it was set previously
+                            delete warnings[CONSTRAINT_RESERVABLE_LEASE];
+                        }
+                    } else {
+                        // Remove warnings attached to this resource
+                        delete warnings[CONSTRAINT_RESERVABLE_LEASE];
+                    }
+
+                    manifold.query_store.set_record_state(query_uuid, resource_key, STATE_WARNINGS, warnings);
+
+                }
+                // Signal the change to plugins (even if the constraint does not apply, so that the plugin can display a checkmark)
+                data = {
+                    request: null,
+                    key   : null,
+                    value : resource_key,
+                    status: STATE_WARNINGS
+                };
+                manifold.raise_record_event(query_uuid, FIELD_STATE_CHANGED, data);
+
+                // -) When a lease is added, it might remove the warning associated to a reservable node
+
+                // If a NITOS node is reserved, then at least a NITOS channel should be reserved
+                // - When a NITOS channel is added, it might remove a warning associated to all NITOS nodes
+
+                // If a NITOS channel is reserved, then at least a NITOS node should be reserved
+                // - When a NITOS node is added, it might remove a warning associated to all NITOS channels
+
+                // A lease is present while the resource has been removed => Require warnings on nodes not in set !
+
+                /* END CONSTRAINTS */
+
                 // update is only possible is the query is not pending, etc
                 // CHECK status !
 
@@ -888,7 +1607,7 @@ var manifold = {
                     request: (event_type == SET_ADD) ? FIELD_REQUEST_ADD : FIELD_REQUEST_REMOVE,
                     key   : path,
                     value : value,
-                    status: FIELD_REQUEST_PENDING,
+                    status: STATE_SET, // XXX used to be FIELD_REQUEST_PENDING, and not new_state
                 };
                 this.raise_event(main_query.query_uuid, FIELD_STATE_CHANGED, data);
 
@@ -911,12 +1630,26 @@ var manifold = {
                 manifold.run_query(query_ext.main_query_ext.update_query_ext.query);
                 break;
 
+            /* FILTERS */
+
             case FILTER_ADDED: 
+                /* Update internal record state */
+                manifold.query_store.add_filter(query_uuid, value);
+
+                /* Propagate the message to plugins */
                 manifold.raise_query_event(query_uuid, event_type, value);
+
                 break;
+
             case FILTER_REMOVED:
+                /* Update internal record state */
+                manifold.query_store.remove_filter(query_uuid, value);
+
+                /* Propagate the message to plugins */
                 manifold.raise_query_event(query_uuid, event_type, value);
+
                 break;
+
             case FIELD_ADDED:
                 main_query = query_ext.main_query_ext.query;
                 main_update_query = query_ext.main_query_ext.update_query;
index 24d4112..da26f1f 100644 (file)
@@ -220,7 +220,7 @@ var Plugin = Class.extend({
 
     id_from_key: function(key_field, value) {
         
-        return key_field + manifold.separator + this.escape_id(value).replace(/\\/g, '');
+        return key_field + manifold.separator + this.escape_id(value); //.replace(/\\/g, '');
     },
 
     // NOTE
diff --git a/plugins/apply/__init__.py b/plugins/apply/__init__.py
new file mode 100644 (file)
index 0000000..ceb3c9a
--- /dev/null
@@ -0,0 +1,48 @@
+from unfold.plugin import Plugin
+from plugins.queryupdater import QueryUpdaterPlugin
+
+class ApplyPlugin(Plugin):
+    
+    def __init__ (self, query=None, **settings):
+        Plugin.__init__ (self, **settings)
+        self.query              = query
+
+    def template_file (self):
+        return "apply.html"
+
+    def template_env(self, request):
+        query_updater = QueryUpdaterPlugin(
+            page                = self.page,
+            title               = 'Pending operations',
+            query               = self.query,
+            togglable           = False,
+            # start turned off, it will open up itself when stuff comes in
+            toggled             = True,
+            domid               = 'pending',
+            outline_complete    = True,
+            username            = request.user, # XXX ???
+        )
+
+        env = Plugin.template_env(self, request)
+        env.update({'query_updater': query_updater.render(request)})
+        return env
+
+    def requirements (self):
+        reqs = {
+            'js_files' : [
+                'js/apply.js'
+            ],
+            'css_files' : [
+                'css/apply.css'
+            ],
+        }
+        return reqs
+
+    def json_settings_list (self):
+        # query_uuid will pass self.query results to the javascript
+        # and will be available as "record" in :
+        # on_new_record: function(record)
+        return ['plugin_uuid', 'domid', 'query_uuid']
+
+    def export_json_settings (self):
+        return True
diff --git a/plugins/apply/static/css/apply.css b/plugins/apply/static/css/apply.css
new file mode 100644 (file)
index 0000000..97b2d59
--- /dev/null
@@ -0,0 +1,3 @@
+.modal-dialog-large {
+    width: 800px;
+}
diff --git a/plugins/apply/static/js/apply.js b/plugins/apply/static/js/apply.js
new file mode 100644 (file)
index 0000000..47dc8d8
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * TestbedsPlugin: List of testbeds plugin
+ * Version:     0.1
+ * Description: TODO -> generalize to a list of possible filters
+ *              This file is part of the Manifold project 
+ * Requires:    js/plugin.js
+ * URL:         http://www.myslice.info
+ * Author:      Loïc Baron <loic.baron@lip6.fr>
+ * Copyright:   Copyright 2012-2013 UPMC Sorbonne Universités
+ * License:     GPLv3
+ */
+
+(function($){
+
+    var ApplyPlugin = Plugin.extend({
+
+        /** 
+         * @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);
+        },
+
+
+    })
+
+    /* Plugin registration */
+    $.plugin('ApplyPlugin', ApplyPlugin);
+
+})(jQuery);
diff --git a/plugins/apply/templates/apply.html b/plugins/apply/templates/apply.html
new file mode 100644 (file)
index 0000000..2e5bab0
--- /dev/null
@@ -0,0 +1,24 @@
+<div id={{ domid }}>
+  <!-- Modal - columns selector -->
+  <div class="modal fade" id="{{domid}}__apply" tabindex="-1" role="dialog" aria-labelledby="{{domid}}__apply__label" aria-hidden="true">
+    <div class="modal-dialog modal-dialog-large">
+      <div class="modal-content">
+        <div class="modal-header">
+          <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+            <h4 class="modal-title" id="{{domid}}__apply__label">Columns selector</h4>
+        </div>
+        <div class="modal-body">
+          {{query_updater}}
+        </div>
+        <div class="modal-footer">
+          <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  
+  <!-- Button toolbar -->
+  <button class="btn btn-primary btn-sm" data-toggle="modal" data-target="#{{domid}}__apply">Apply</button>
+  <button class="btn btn-primary btn-sm" data-toggle="modal" data-target="#{{domid}}__cancel">Cancel</button>
+</div> 
diff --git a/plugins/filter_status/__init__.py b/plugins/filter_status/__init__.py
new file mode 100644 (file)
index 0000000..a0d269b
--- /dev/null
@@ -0,0 +1,30 @@
+from unfold.plugin import Plugin
+
+class FilterStatusPlugin(Plugin):
+    
+    def __init__ (self, query=None, **settings):
+        Plugin.__init__ (self, **settings)
+        self.query              = query
+
+    def template_file (self):
+        return "filter_status.html"
+
+    def requirements (self):
+        reqs = {
+            'js_files' : [
+                'js/filter_status.js'
+            ],
+#            'css_files': [
+#                'css/myplugin.css',
+#            ]
+        }
+        return reqs
+
+    def json_settings_list (self):
+        # query_uuid will pass self.query results to the javascript
+        # and will be available as "record" in :
+        # on_new_record: function(record)
+        return ['plugin_uuid', 'domid', 'query_uuid']
+
+    def export_json_settings (self):
+        return True
diff --git a/plugins/filter_status/static/js/filter_status.js b/plugins/filter_status/static/js/filter_status.js
new file mode 100644 (file)
index 0000000..9c3fe4f
--- /dev/null
@@ -0,0 +1,103 @@
+/**
+ * TestbedsPlugin: List of testbeds plugin
+ * Version:     0.1
+ * Description: TODO -> generalize to a list of possible filters
+ *              This file is part of the Manifold project 
+ * Requires:    js/plugin.js
+ * URL:         http://www.myslice.info
+ * Author:      Loïc Baron <loic.baron@lip6.fr>
+ * Copyright:   Copyright 2012-2013 UPMC Sorbonne Universités
+ * License:     GPLv3
+ */
+
+(function($){
+
+    var FilterStatusPlugin = Plugin.extend({
+
+        /** 
+         * @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);
+
+            /* Setup query and record handlers */
+            this.listen_query(options.query_uuid);
+
+            /* Setup click handlers */
+            this.elts('list-group-item').click({'instance': this}, this._on_click);
+
+            this.prev_filter_status = null;
+        },
+
+    /**************************************************************************
+     *                            GUI MANAGEMENT                              *
+     **************************************************************************/
+
+        select_tab: function(tab)
+        {
+            this.elts('list-group-item').removeClass('active');
+            this.elmt(tab).addClass('active');
+        },
+
+    /**************************************************************************
+     *                            EVENT HANDLERS                              *
+     **************************************************************************/
+
+    // These functions are here to react on external filters, which we don't
+    // use at the moment
+
+    on_filter_added: function(filter) {
+        // XXX
+    },
+
+    on_filter_removed: function(filter) {
+        // XXX
+    },
+
+    /**************************************************************************
+     *                            PRIVATE METHODS                             *
+     **************************************************************************/
+
+        /**
+         * @brief : Click event handler
+         */
+        _on_click: function(e)
+        {
+            var filter_status;
+            var filter;
+
+            // A pointer to the plugin instance, since 'this' is overriden here
+            self = e.data.instance;
+
+
+            // Select the tab...
+            filter_status = this.dataset['status'];
+            self.select_tab(filter_status);
+
+            // ... and communicate the appropriate filters to the manager
+            // NOTE: we use the manifold namespace for internal filters 
+            if (self.prev_filter_status)
+                manifold.raise_event(self.options.query_uuid, FILTER_REMOVED, self.prev_filter_status);
+
+            // XXX The datatables will be refreshed twice !
+            if (filter_status != 'all') {
+                // No filter for 'all'
+                var filter = ['manifold:status', '==', filter_status];
+                manifold.raise_event(self.options.query_uuid, FILTER_ADDED, filter);
+            }
+
+            self.prev_filter_status = filter_status;
+        },
+
+    });
+
+    /* Plugin registration */
+    $.plugin('FilterStatusPlugin', FilterStatusPlugin);
+
+})(jQuery);
diff --git a/plugins/filter_status/templates/filter_status.html b/plugins/filter_status/templates/filter_status.html
new file mode 100644 (file)
index 0000000..0023796
--- /dev/null
@@ -0,0 +1,7 @@
+<div id={{ domid }}>
+<span class="list-group-item-heading">Filter on status:</span>
+<a href="#" class="list-group-item sl-platform active" style='display: inline-block !important;' id="{{ domid }}__all" data-status="all"><p class="list-group-item-heading">All</p></a>
+<a href="#" class="list-group-item sl-platform" style='display: inline-block !important;' id="{{ domid }}__reserved" data-status="reserved"><p class="list-group-item-heading">Reserved</p></a>
+<a href="#" class="list-group-item sl-platform" style='display: inline-block !important;' id="{{ domid }}__unconfigured" data-status="unconfigured"><p class="list-group-item-heading">Unconfigured</p></a>
+<a href="#" class="list-group-item sl-platform" style='display: inline-block !important;' id="{{ domid }}__pending" data-status="pending"><p class="list-group-item-heading">Pending</p></a>
+</div> 
index 9b8abbc..d591a5b 100644 (file)
@@ -21,7 +21,7 @@
 .gtop{}
 .gsubcell{float:left;}
 rect{fill:none;}
-.tooltip{text-align:left;font-size:12px;}
+.Btooltip{text-align:left;font-size:12px;}
 .tooltip .top-tip{padding-bottom:2px;border-bottom:1px solid #fff}
 .tooltip .bottom-tip{padding-top:2px;}  
 .legends{
index 5a80bb9..2922906 100644 (file)
@@ -96,7 +96,7 @@ Current implementation makes the following assumptions
                            # hopefully temporary, when/if datatables supports sPaginationType=bootstrap3
                            # for now we use full_numbers, with our own ad hoc css 
                            #"css/dataTables.full_numbers.css",
-                           #"css/querytable.css" , 
+                           "css/querytable.css" , 
                            ],
             }
         return reqs
index a42ab1f..e4a3308 100644 (file)
@@ -1,61 +1,59 @@
-/* the bottom of the datatable needs more space */
-div.querytable-spacer { padding: 8px 4px 15px 4px; }
-
-div.QueryTable table.dataTable th {
-    font: bold 12px/22px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
-    color: #4f6b72;
-    border-right: 1px solid #C1DAD7;
-    border-bottom: 1px solid #C1DAD7;
-    border-top: 1px solid #C1DAD7;
-    letter-spacing: 1px;
-    text-transform: uppercase;
-    text-align: left;
-    padding: 8px 12px 4px 20px;
-    vertical-align:middle;
-/*    background: #CAE8EA url(../img/tablesort-header.jpg) no-repeat; */
-}
-
-div.QueryTable table.dataTable th.checkbox {
-}
-
-div.QueryTable table.dataTable td, div.QueryTable table.dataTable textarea, div.QueryTable table.dataTable input [type="text"] {
-    font: normal 12px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
-    border-right: 1px solid #C1DAD7;
-    border-bottom: 1px solid #C1DAD7;
-}
-div.QueryTable table.dataTable td {
-    padding: 4px 8px 4px 8px;
-    /* this applies on even rows only, odd ones have a setting in bootstrap of rbg 249.249.249 */
-    background-color: #f4f4f4;
-}
-div.QueryTable table.dataTable td a {
-    font-weight:normal;
-}
-/* these come from bootstrap */
-div.QueryTable div.dataTables_info {
-    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
-}
-
-/* one could think or using repeat-x here but that's not working because of the arrows
- * we might need to make these wider some day 
- * and/or to add background-color: #caebea
- * which would look less conspicuous in case of overflow
-*/
-
-div.QueryTable table.dataTable thead .sorting { background: url('../img/tablesort-header-sortable.png') no-repeat; }
-div.QueryTable table.dataTable thead .sorting_asc { background: url('../img/tablesort-header-up.png') no-repeat; }
-div.QueryTable table.dataTable thead .sorting_desc { background: url('../img/tablesort-header-down.png') no-repeat; }
-/* this icons set does not have that exact equivalent - using an approximation for now */
-div.QueryTable table.dataTable thead .sorting_asc_disabled { background: url('../img/tablesort-header.png') repeat-x; }
-div.QueryTable table.dataTable thead .sorting_desc_disabled { background: url('../img/tablesort-header.png') repeat-x; }
-
-/* the footers are not active */
-div.QueryTable table.dataTable tfoot { 
-    background: url('../img/tablesort-header.png') repeat-x;
-    background-color: #caebea;
-}
-/* and when sorting is turned off it's useful to set this on header too */
-div.QueryTable table.dataTable thead { 
-    background: url('../img/tablesort-header.png') repeat-x;
-    background-color: #caebea;
+
+div .added {
+       background-color: #FFFF99;
+}
+
+div .removed {
+       background-color: #E8E8E8;
+}
+
+.sidebar-nav {
+    padding: 9px 0;
+}
+
+.dropdown-menu .sub-menu {
+    left: 100%;
+    top: 0;
+    position: absolute;
+    visibility: hidden;
+    margin-top: -1px;
+}
+
+.dropdown-menu li:hover .sub-menu {
+    visibility: visible;
+}
+
+.dropdown:hover .dropdown-menu {
+    display: block;
+}
+
+.nav-tabs .dropdown-menu, .nav-pills .dropdown-menu, .navbar .dropdown-menu {
+    margin-top: 0;
+}
+
+.navbar .sub-menu:before {
+    border-bottom: 7px solid transparent;
+    border-left: none;
+    border-right: 7px solid rgba(0, 0, 0, 0.2);
+    border-top: 7px solid transparent;
+    left: -7px;
+    top: 10px;
+}
+.navbar .sub-menu:after {
+    border-top: 6px solid transparent;
+    border-left: none;
+    border-right: 6px solid #fff;
+    border-bottom: 6px solid transparent;
+    left: 10px;
+    top: 11px;
+    left: -6px;
+}
+
+.dropdown-menu-right {
+  left: auto; /* Reset the default from `.dropdown-menu` */
+  right: 0;
+}
+
+.nav > li > a.nopadding {
+  padding: 0 0;
 }
index c541141..3731f7a 100644 (file)
@@ -4,6 +4,10 @@
  * License: GPLv3
  */
 
+BGCOLOR_RESET   = 0;
+BGCOLOR_ADDED   = 1;
+BGCOLOR_REMOVED = 2;
+
 (function($){
 
     var debug=false;
@@ -60,7 +64,7 @@
 
             /* Setup query and record handlers */
             this.listen_query(options.query_uuid);
-            this.listen_query(options.query_all_uuid, 'all');
+            //this.listen_query(options.query_all_uuid, 'all');
 
             /* GUI setup and event binding */
             this.initialize_table();
                }
                
             /* fill in stuff depending on the column name */
-            for (var j = 1; j < nb_col; j++) {
+            for (var j = 1; j < nb_col - 1; j++) { // nb_col includes status
                 if (typeof colnames[j] == 'undefined') {
                     line.push('...');
                 } else if (colnames[j] == 'hostname') {
                         line.push('');
                 }
             }
-    
-            
+            line.push('<span id="' + this.id_from_key('status', record[this.init_key]) + '"></span>'); // STATUS
     
            // adding an array in one call is *much* more efficient
                // this.table.fnAddData(line);
-               this.buffered_lines.push(line);
+               return line;
         },
 
         clear_table: function()
        // this is used at init-time, at which point only init_key can make sense
        // (because the argument record, if it comes from query, might not have canonical_key set
        set_checkbox_from_record: function (record, checked) {
-            if (checked === undefined) checked = true;
+        if (checked === undefined) checked = true;
            var init_id = record[this.init_key];
-           if (debug) messages.debug("querytable.set_checkbox_from_record, init_id="+init_id);
+        this.set_checkbox_from_record_key(init_id, checked);
+       },
+
+       set_checkbox_from_record_key: function (record_key, checked) {
+        if (checked === undefined) checked = true;
+           if (debug) messages.debug("querytable.set_checkbox_from_record, record_key="+record_key);
            // using table.$ to search inside elements that are not visible
-           var element = this.table.$('[init_id="'+init_id+'"]');
+           var element = this.table.$('[init_id="'+record_key+'"]');
            element.attr('checked',checked);
        },
 
            element.attr('checked',checked);
        },
 
+        /**
+         * Arguments
+         *
+         * key_value: the key from which we deduce the id
+         * request: STATUS_OKAY, etc.
+         * content: some HTML content
+         */
+        change_status: function(key_value, warnings)
+        {
+            var msg;
+            
+            if ($.isEmptyObject(warnings)) { 
+                var state = manifold.query_store.get_record_state(this.options.query_uuid, key_value, STATE_SET);
+                switch(state) {
+                    case STATE_SET_IN:
+                    case STATE_SET_IN_SUCCESS:
+                    case STATE_SET_OUT_FAILURE:
+                    case STATE_SET_IN_PENDING:
+                        // Checkmark sign if no warning for an object in the set
+                        msg = '&#10003;';
+                        break;
+                    default:
+                        // Nothing is the object is not in the set
+                        msg = '';
+                        break;
+                }
+            } else {
+                msg = '<ul class="nav nav-pills">';
+                msg += '<li class="dropdown">'
+                msg += '<a href="#" data-toggle="dropdown" class="dropdown-toggle nopadding"><b>&#9888</b></a>';
+                msg += '  <ul class="dropdown-menu dropdown-menu-right" id="menu1">';
+                $.each(warnings, function(i,warning) {
+                    msg += '<li><a href="#">' + warning + '</a></li>';
+                });
+                msg += '  </ul>';
+                msg += '</li>';
+                msg += '</ul>';
+            }
+
+            $(document.getElementById(this.id_from_key('status', key_value))).html(msg);
+            $('[data-toggle="tooltip"]').tooltip({'placement': 'bottom'});
+
+        },
+
+        set_bgcolor: function(key_value, class_name)
+        {
+            var elt = $(document.getElementById(this.id_from_key(this.canonical_key, key_value)))
+            if (class_name == BGCOLOR_RESET)
+                elt.removeClass('added removed');
+            else
+                elt.addClass((class_name == BGCOLOR_ADDED ? 'added' : 'removed'));
+        },
+
+        do_filter: function()
+        {
+            // Let's clear the table and only add lines that are visible
+            var self = this;
+            this.clear_table();
+
+            // XXX Here we have lost checkboxes
+            // set checkbox from record.
+            // only the current plugin known that we have an element in a set
+
+            lines = Array();
+            var record_keys = [];
+            manifold.query_store.iter_visible_records(this.options.query_uuid, function (record_key, record) {
+                lines.push(self.new_record(record));
+                record_keys.push(record_key);
+            });
+               this.table.fnAddData(lines);
+            $.each(record_keys, function(i, record_key) {
+                var state = manifold.query_store.get_record_state(self.options.query_uuid, record_key, STATE_SET);
+                var warnings = manifold.query_store.get_record_state(self.options.query_uuid, record_key, STATE_WARNINGS);
+                switch(state) {
+                    // XXX The row and checkbox still does not exists !!!!
+                    case STATE_SET_IN:
+                    case STATE_SET_IN_SUCCESS:
+                    case STATE_SET_OUT_FAILURE:
+                        self.set_checkbox_from_record_key(record_key, true);
+                        break;
+                    case STATE_SET_OUT:
+                    case STATE_SET_OUT_SUCCESS:
+                    case STATE_SET_IN_FAILURE:
+                        //self.set_checkbox_from_record_key(record_key, false);
+                        break;
+                    case STATE_SET_IN_PENDING:
+                        self.set_checkbox_from_record_key(record_key, true);
+                        self.set_bgcolor(record_key, BGCOLOR_ADDED);
+                        break;
+                    case STATE_SET_OUT_PENDING:
+                        //self.set_checkbox_from_record_key(record_key, false);
+                        self.set_bgcolor(record_key, BGCOLOR_REMOVED);
+                        break;
+                }
+                self.change_status(record_key, warnings); // XXX will retrieve status again
+            });
+        },
+
         /*************************** QUERY HANDLER ****************************/
 
         on_filter_added: function(filter)
         {
+            this.do_filter();
+
+            /*
             this.filters.push(filter);
             this.redraw_table();
+            */
         },
 
         on_filter_removed: function(filter)
         {
+            this.do_filter();
+            /*
             // Remove corresponding filters
             this.filters = $.grep(this.filters, function(x) {
                 return x == filter;
             });
             this.redraw_table();
+            */
         },
         
         on_filter_clear: function()
         {
-            // XXX
-            this.redraw_table();
+            this.do_filter();
         },
 
         on_field_added: function(field)
 
         on_all_filter_added: function(filter)
         {
-            // XXX
-            this.redraw_table();
+            this.do_filter();
         },
 
         on_all_filter_removed: function(filter)
         {
-            // XXX
-            this.redraw_table();
+            this.do_filter();
         },
         
         on_all_filter_clear: function()
         {
-            // XXX
-            this.redraw_table();
+            this.do_filter();
         },
 
         on_all_field_added: function(field)
 
         on_query_done: function()
         {
+            this.do_filter();
+/*
             this.received_query = true;
            // unspin once we have received both
             if (this.received_all_query && this.received_query) this.unspin();
+*/
         },
         
         on_field_state_changed: function(data)
         {
-            switch(data.request) {
-                case FIELD_REQUEST_ADD:
-                case FIELD_REQUEST_ADD_RESET:
-                    this.set_checkbox_from_data(data.value, true);
-                    break;
-                case FIELD_REQUEST_REMOVE:
-                case FIELD_REQUEST_REMOVE_RESET:
-                    this.set_checkbox_from_data(data.value, false);
-                    break;
-                default:
+            var state = manifold.query_store.get_record_state(this.options.query_uuid, data.value, data.status);
+            switch(data.status) {
+                case STATE_SET:
+                    switch(state) {
+                        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, BGCOLOR_RESET);
+                            break;  
+                        case STATE_SET_OUT:
+                        case STATE_SET_OUT_SUCCESS:
+                        case STATE_SET_IN_FAILURE:
+                            this.set_checkbox_from_data(data.value, false);
+                            this.set_bgcolor(data.value, BGCOLOR_RESET);
+                            break;
+                        case STATE_SET_IN_PENDING:
+                            this.set_checkbox_from_data(data.value, true);
+                            this.set_bgcolor(data.value, BGCOLOR_ADDED);
+                            break;  
+                        case STATE_SET_OUT_PENDING:
+                            this.set_checkbox_from_data(data.value, false);
+                            this.set_bgcolor(data.value, BGCOLOR_REMOVED);
+                            break;
+                    }
                     break;
-            }
-        },
 
-        /* XXX TODO: make this generic a plugin has to subscribe to a set of Queries to avoid duplicated code ! */
-        // all
-        on_all_field_state_changed: function(data)
-        {
-            switch(data.request) {
-                case FIELD_REQUEST_ADD:
-                case FIELD_REQUEST_ADD_RESET:
-                    this.set_checkbox_from_data(data.value, true);
-                    break;
-                case FIELD_REQUEST_REMOVE:
-                case FIELD_REQUEST_REMOVE_RESET:
-                    this.set_checkbox_from_data(data.value, false);
-                    break;
-                default:
+                case STATE_WARNINGS:
+                    this.change_status(data.value, state);
                     break;
             }
         },
 
-        on_all_new_record: function(record)
-        {
-            this.new_record(record);
-        },
-
-        on_all_clear_records: function()
-        {
-            this.clear_table();
-
-        },
-
-        on_all_query_in_progress: function()
-        {
-            // XXX parent
-            this.spin();
-        }, // on_all_query_in_progress
-
-        on_all_query_done: function()
-        {
-               if (debug) messages.debug("1-shot initializing dataTables content with " + this.buffered_lines.length + " lines");
-               this.table.fnAddData (this.buffered_lines);
-               this.buffered_lines=[];
-
-            var self = this;
-           // if we've already received the slice query, we have not been able to set 
-           // checkboxes on the fly at that time (dom not yet created)
-            $.each(this.buffered_records_to_check, function(i, record) {
-                               if (debug) messages.debug ("querytable delayed turning on checkbox " + i + " record= " + record);
-                self.set_checkbox_from_record(record, true);
-            });
-               this.buffered_records_to_check = [];
-
-            this.received_all_query = true;
-           // unspin once we have received both
-            if (this.received_all_query && this.received_query) this.unspin();
-
-        }, // on_all_query_done
-
         /************************** PRIVATE METHODS ***************************/
 
         /** 
index d0f18c0..ddeb9cf 100644 (file)
@@ -5,6 +5,7 @@
        {% if checkboxes %}<th class="checkbox">+/-</th>{% endif %}
         {% for column in columns %} <th>{{ column }}</th> {% endfor %} 
         {% for column in hidden_columns %} <th>{{ column }}</th> {% endfor %}
+        <th class="checkbox">status</th>
       </tr>
     </thead> 
     <tbody>
@@ -14,6 +15,7 @@
        {% if checkboxes %} <th>+/-</th> {% endif %}
         {% for column in columns %} <th>{{ column }}</th> {% endfor %} 
         {% for column in hidden_columns %} <th>{{ column }}</th> {% endfor %} 
+        <th class="checkbox">status</th>
       </tr>
     </tfoot> 
   </table>
index fa09ffb..30dbcee 100644 (file)
@@ -1,6 +1,6 @@
 from unfold.plugin import Plugin
 
-class QueryUpdater(Plugin):
+class QueryUpdaterPlugin(Plugin):
 
     def __init__ (self, query=None, **settings):
         Plugin.__init__ (self, **settings)
index 3057cf5..167e381 100644 (file)
@@ -26,7 +26,7 @@
     // Record state through the query cycle
 
 
-    var QueryUpdater = Plugin.extend({
+    var QueryUpdaterPlugin = Plugin.extend({
 
         init: function(options, element) {
                this.classname="queryupdater";
                 // XXX check that the query is not disabled
 
                 self.spin();
-                console.log("do_update");
                 // XXX check that the query is not disabled
                 manifold.raise_event(self.options.query_uuid, RUN_UPDATE);
                 return;
 
         set_state: function(data)
         {
-            console.log("function set_state");
             var action;
             var msg;
             var button = '';
 
         // XXX we don't want to show automaticaly the pending when a checkbox is checked
            //this.toggle_on();
-           
-            switch(data.request) {
-                case FIELD_REQUEST_ADD_RESET:
-                case FIELD_REQUEST_REMOVE_RESET:
+
+            switch (data.status) {
+                case STATE_SET_IN_PENDING:
+                    action = 'ADD';
+                    msg   = 'PENDING';
+                    button = "<span class='glyphicon glyphicon-remove ResourceSelectedClose' id='" + data.key + "'/>";
+                    break;
+                case STATE_SET_OUT_PENDING:
+                    action = 'REMOVE';
+                    msg   = 'PENDING';
+                    button = "<span class='glyphicon glyphicon-remove ResourceSelectedClose' id='" + data.key + "'/>";
+                    break;
+                case STATE_SET_IN:
+                case STATE_SET_OUT:
                     // find line and delete it
+                    // XXX Naming is incorrect for badge-pending !!!!
+                    // XXX What is this badge ?
                     row = this.find_row(data.value);
                     if (row)
                         this.table.fnDeleteRow(row.nTr);
                         $("#badge-pending").data('number', $("#badge-pending").data('number') - 1 );
                         $("#badge-pending").text($("#badge-pending").data('number'));
                     return;
-                case FIELD_REQUEST_CHANGE:
-                    action = 'UPDATE';
-                    break;
-                case FIELD_REQUEST_ADD:
-                    action = 'ADD';
-                    break;
-                case FIELD_REQUEST_REMOVE:
-                    action = 'REMOVE';
-                    break;
-            }
-
-            switch(data.status) {
-                case FIELD_REQUEST_PENDING:
-                    msg   = 'PENDING';
-                    button = "<span class='glyphicon glyphicon-remove ResourceSelectedClose' id='" + data.key + "'/>";
-                    break;
-                case FIELD_REQUEST_SUCCESS:
+                    break;  
+                case STATE_SET_IN_SUCCESS:
+                case STATE_SET_OUT_SUCCESS:
                     msg   = 'SUCCESS';
                     break;
-                case FIELD_REQUEST_FAILURE:
+                case STATE_SET_IN_FAILURE:
+                case STATE_SET_OUT_FAILURE:
                     msg   = 'FAILURE';
                     break;
+                case STATE_CHANGE:
+                    action = 'UPDATE';
+                    break;
+                
             }
 
             var status = msg + status;
 
-            
-
             // find line
             // if no, create it, else replace it
             // XXX it's not just about adding lines, but sometimes removing some
             // XXX how do we handle status reset ?
-            data.value = JSON.stringify(data.value);
+
+            // Jordan : I don't understand this. I added this test otherwise we have string = ""..."" double quoted twice.
+            if (typeof(data.value) !== "string")
+                data.value = JSON.stringify(data.value);
             data.selected_resources = this.selected_resources;
             row = this.find_row(data.value);
             newline = [
 
         on_new_record: function(record)
         {
-            console.log("query_updater on_new_record");
-            console.log(record);
 
             // if (not and update) {
 
 
         on_query_done: function()
         {
-            console.log("on_query_done");
             this.unspin();
         },
 
         // NOTE: record_key could be sufficient 
         on_added_record: function(record)
         {
-            console.log("on_added_record = ",record);
             this.set_record_state(record, RECORD_STATE_ADDED);
             // update pending number
         },
 
         on_removed_record: function(record_key)
         {
-            console.log("on_removed_record = ",record_key);
             this.set_record_state(RECORD_STATE_REMOVED);
         },
 
 
         on_field_state_changed: function(result)
         {
-            console.log("on_field_state_changed");
-            console.log(result);
             if(result.request == FIELD_REQUEST_ADD){
                 this.selected_resources.push(result.value);
             } else if(result.request == FIELD_REQUEST_REMOVE_RESET){
                     this.selected_resources.splice(i,1);
                 }
             }
-            console.log("Resources: " + self.selected_resources);
             messages.debug(result)
-            /* this.set_state(result.request, result.key, result.value, result.status); */
+
             this.set_state(result);
         },
 
 
     });
 
-    $.plugin('QueryUpdater', QueryUpdater);
+    $.plugin('QueryUpdaterPlugin', QueryUpdaterPlugin);
 
 })(jQuery);
diff --git a/plugins/scheduler2/asdf.txt b/plugins/scheduler2/asdf.txt
deleted file mode 100755 (executable)
index 1050001..0000000
+++ /dev/null
@@ -1 +0,0 @@
-asd
\ No newline at end of file
index d89d8c0..2732044 100755 (executable)
@@ -14,6 +14,13 @@ myApp.factory('$exceptionHandler', function () {
     };\r
 });\r
 \r
+myApp.filter('offset', function() {\r
+  return function(input, start) {\r
+    start = parseInt(start, 10);\r
+    return input.slice(start);\r
+  };\r
+});\r
+\r
 // Create a private execution space for our controller. When\r
 // executing this function expression, we're going to pass in\r
 // the Angular reference and our application module.\r
@@ -30,13 +37,15 @@ myApp.factory('$exceptionHandler', function () {
         // Set up the default scope value.\r
         this.scope.errorMessage = null;\r
         this.scope.name = "";\r
+\r
         //Pagin\r
-        $scope.totalPages = 4;\r
-        $scope.curPage = 0;\r
-        this.scope.pageSize = 25;\r
+        $scope.current_page = 1;\r
+        this.scope.items_per_page = 10;\r
+        $scope.from = 0; // JORDAN\r
 \r
         $scope.resources = new Array();\r
         $scope.slots = SchedulerSlotsViewData;\r
+        $scope.granularity = DEFAULT_GRANULARITY; /* Will be setup */\r
         //$scope.msg = "hello";\r
 \r
         angular.element(document).ready(function() {\r
@@ -45,29 +54,274 @@ myApp.factory('$exceptionHandler', function () {
             //afterAngularRendered();\r
         });\r
 \r
+        // Jordan\r
+/*\r
+        $scope.redraw = function()\r
+        {\r
+\r
+            // Refresh slots\r
+            $scope.slots = [];\r
+            for (var i = $scope.from; i < $scope.from + SchedulerTotalVisibleCells; i++)\r
+                $scope.slots.push(SchedulerSlots[i]);\r
+\r
+\r
+            // Collect lease information. This could be made once if no refresh... \r
+            lease_by_resource = {};\r
+            manifold.query_store.iter_visible_records($scope.options.query_lease_uuid, function (record_key, record) {\r
+                lease_by_resource[record['resource']] = record;\r
+                // Need something to interrupt the loop\r
+            });\r
+\r
+            // Create resources\r
+            $scope.resources = [];\r
+            // current_page, items_per_page indicates which resources to show\r
+            manifold.query_store.iter_visible_records($scope.options.query_uuid, function (record_key, record) {\r
+                // copy not to modify original record\r
+                var resource = jQuery.extend(true, {}, record);\r
+                resource.leases = []; // a list of occupied timeslots\r
+\r
+                // How many timeslots ? SchedulerTotalVisibleCells\r
+                // SchedulerDateSelected\r
+                // from : to ??\r
+                // slot duration ?\r
+                for (i=0; i < SchedulerTotalVisibleCells; i++) {\r
+                    resource.leases.push({\r
+                        'id': 'coucou',\r
+                        'status': 'free', // 'selected', 'reserved', 'maintenance'\r
+                    });\r
+                }\r
+\r
+                // For each lease we need to mark slots appropriately\r
+                if (lease_by_resource[resource['urn']]) {\r
+                    $.each(lease_by_resource[resource['urn']], function(i, lease) {\r
+                        // $scope.from * GRANULARITY minutes since start\r
+                        $scope.from * GRANULARITY\r
+                        from_date = new Date(date.getTime() + ($scope.from * GRANULARITY) * 60000);\r
+                        to_date   = new Date(date.getTime() + (($scope.from + SchedulerTotalVisibleCells) * GRANULARITY) * 60000);\r
+                        // start_time, end_time\r
+                    });\r
+                }\r
+                \r
+                $scope.resources.push(resource);\r
+                $scope.$apply();\r
+            });\r
+        }\r
+*/\r
         $scope.clearStuff = function() {\r
             $scope.resources = new Array();\r
             $scope.$apply();\r
         }\r
 \r
-        $scope.initSchedulerResources = function (pageSize) {\r
+        // Called at initialization, after filtering, and after changing the date.\r
+        // this is like setpage(1) ???\r
+/*\r
+        $scope.initSchedulerResources = function (items_per_page) {\r
             $scope.resources = new Array();\r
 \r
-            for (var k = 0; k < pageSize; k++) {\r
+            for (var k = 0; k < items_per_page; k++) {\r
                 $scope.resources.push(jQuery.extend(true, {}, SchedulerDataViewData[k]));\r
                 $scope.resources[k].leases = [];\r
             }\r
-            $scope.pageSize = pageSize;\r
-            $scope.curPage = 0;\r
-            $scope.totalPages = parseInt(Math.ceil(SchedulerDataViewData.length / $scope.pageSize));\r
+            $scope.items_per_page = items_per_page;\r
+            $scope.current_page = 0;\r
+            $scope.totalPages = parseInt(Math.ceil(SchedulerDataViewData.length / $scope.items_per_page));\r
             $scope.initSlots(0, SchedulerTotalVisibleCells);\r
         };\r
+*/\r
+\r
+        // Pagination\r
+\r
+        $scope.range = function() {\r
+            var range_size = $scope.page_count() > DEFAULT_PAGE_RANGE ? DEFAULT_PAGE_RANGE : $scope.page_count();\r
+            var ret = [];\r
+            var start;\r
+\r
+            start = $scope.current_page;\r
+            if ( start > $scope.page_count()-range_size ) {\r
+              start = $scope.page_count()-range_size+1;\r
+            }\r
+\r
+            for (var i=start; i<start+range_size; i++) {\r
+              ret.push(i);\r
+            }\r
+            return ret;\r
+        };\r
+\r
+        $scope.prevPage = function() {\r
+          if ($scope.current_page > 1) {\r
+            $scope.current_page--;\r
+          }\r
+        };\r
 \r
+        $scope.prevPageDisabled = function() {\r
+          return $scope.current_page === 1 ? "disabled" : "";\r
+        };\r
+  \r
+        $scope.page_count = function() {\r
+          return Math.ceil($scope.resources.length/$scope.items_per_page);\r
+        };\r
+  \r
+        $scope.nextPage = function() {\r
+          if ($scope.current_page < $scope.page_count()) {\r
+            $scope.current_page++;\r
+          }\r
+        };\r
+  \r
+        $scope.nextPageDisabled = function() {\r
+          return $scope.current_page === $scope.page_count() ? "disabled" : "";\r
+        }; \r
+\r
+        $scope.setPage = function(n) {\r
+            $scope.current_page = n;\r
+        };\r
+        // END pagination\r
+\r
+        // FILTER\r
+\r
+        $scope.filter_visible = function(resource)\r
+        {\r
+            return manifold.query_store.get_record_state($scope.options.query_uuid, resource['urn'], STATE_VISIBLE);\r
+        };\r
+\r
+        // SELECTION\r
+\r
+        $scope.select = function(index, model_lease, model_resource)\r
+        {\r
+            // XXX\r
+            // XXX Events won't work until we properly handle sets with composite keys\r
+            // XXX\r
+            console.log("Selected", index, model_lease, model_resource);\r
+\r
+            if (model_lease.status != 'free') {\r
+                console.log("Already selected slot");\r
+                return;\r
+            }\r
+            \r
+            var day_timestamp = SchedulerDateSelected.getTime() / 1000;\r
+            var start_time = day_timestamp + index       * model_resource.granularity;\r
+            var end_time   = day_timestamp + (index + 1) * model_resource.granularity;\r
+            var start_date = new Date(start_time * 1000);\r
+            var end_date   = new Date(end_time   * 1000);\r
+\r
+            var lease_key = manifold.metadata.get_key('lease');\r
+\r
+            // We search for leases in the cache we previously constructed\r
+            var resource_leases = $scope._leases_by_resource[model_resource.urn];\r
+            if (resource_leases) {\r
+                /* Search for leases before */\r
+                $.each(resource_leases, function(i, other) {\r
+                    if (other.end_time != start_time)\r
+                        return true; // ~ continue\r
+\r
+                    /* The lease 'other' is just before, and there should not exist\r
+                     * any other lease before it */\r
+                    start_time = other.start_time;\r
+\r
+                    other_key = {\r
+                        resource:   other.resource,\r
+                        start_time: other.start_time,\r
+                        end_time:   other.end_time\r
+                    }\r
+                    // This is needed to create a hashable object\r
+                    other_key.hashCode = manifold.record_hashcode(lease_key.sort());\r
+                    other_key.equals   = manifold.record_equals(lease_key);\r
+\r
+                    manifold.raise_event($scope.options.query_lease_uuid, SET_REMOVED, other_key);\r
+                    /* Remove from local cache also, unless we listen to events from outside */\r
+                    $.grep($scope._leases_by_resource[model_resource.urn], function(x) { return x != other; });\r
+                    return false; // ~ break\r
+                });\r
+\r
+                /* Search for leases after */\r
+                $.each(resource_leases, function(i, other) {\r
+                    if (other.start_time != end_time)\r
+                        return true; // ~ continue\r
+\r
+                    /* The lease 'other' is just after, and there should not exist\r
+                     * any other lease after it */\r
+                    end_time = other.end_time;\r
+                    // XXX SET_ADD and SET_REMOVE should accept full objects\r
+                    other_key = {\r
+                        resource:   other.resource,\r
+                        start_time: other.start_time,\r
+                        end_time:   other.end_time\r
+                    }\r
+                    // This is needed to create a hashable object\r
+                    other_key.hashCode = manifold.record_hashcode(lease_key.sort());\r
+                    other_key.equals   = manifold.record_equals(lease_key);\r
+\r
+                    manifold.raise_event($scope.options.query_lease_uuid, SET_REMOVED, other_key);\r
+                    /* Remove from local cache also, unless we listen to events from outside */\r
+                    $.grep($scope._leases_by_resource[model_resource.urn], function(x) { return x != other; });\r
+                    return false; // ~ break\r
+                });\r
+            }\r
+\r
+            /* Create a new lease */\r
+            new_lease = {\r
+                resource:   model_resource.urn,\r
+                start_time: start_time,\r
+                end_time:   end_time,\r
+            };\r
+\r
+            // This is needed to create a hashable object\r
+            new_lease.hashCode = manifold.record_hashcode(lease_key.sort());\r
+            new_lease.equals   = manifold.record_equals(lease_key);\r
+\r
+            manifold.raise_event($scope.options.query_lease_uuid, SET_ADD, new_lease);\r
+            /* Add to local cache also, unless we listen to events from outside */\r
+            if (!(model_resource.urn in $scope._leases_by_resource))\r
+                $scope._leases_by_resource[model_resource.urn] = [];\r
+            $scope._leases_by_resource[model_resource.urn].push(new_lease);\r
+\r
+            // XXX Shall we set it or wait for manifold event ?\r
+            model_lease.status = 'reserved'; // XXX pending\r
+\r
+            // DEBUG: display all leases and their status in the log\r
+            var leases = manifold.query_store.get_records($scope.options.query_lease_uuid);\r
+            console.log("--------------------");\r
+            $.each(leases, function(i, lease) {\r
+                var key = manifold.metadata.get_key('lease');\r
+                var lease_key = manifold.record_get_value(lease, key);\r
+                var state = manifold.query_store.get_record_state($scope.options.query_lease_uuid, lease_key, STATE_SET);\r
+                var state_str;\r
+                switch(state) {\r
+                    case STATE_SET_IN:\r
+                        state_str = 'STATE_SET_IN';\r
+                        break;\r
+                    case STATE_SET_OUT:\r
+                        state_str = 'STATE_SET_OUT';\r
+                        break;\r
+                    case STATE_SET_IN_PENDING:\r
+                        state_str = 'STATE_SET_IN_PENDING';\r
+                        break;\r
+                    case STATE_SET_OUT_PENDING:\r
+                        state_str = 'STATE_SET_OUT_PENDING';\r
+                        break;\r
+                    case STATE_SET_IN_SUCCESS:\r
+                        state_str = 'STATE_SET_IN_SUCCESS';\r
+                        break;\r
+                    case STATE_SET_OUT_SUCCESS:\r
+                        state_str = 'STATE_SET_OUT_SUCCESS';\r
+                        break;\r
+                    case STATE_SET_IN_FAILURE:\r
+                        state_str = 'STATE_SET_IN_FAILURE';\r
+                        break;\r
+                    case STATE_SET_OUT_FAILURE:\r
+                        state_str = 'STATE_SET_OUT_FAILURE';\r
+                        break;\r
+                }\r
+                console.log("LEASE", new Date(lease.start_time * 1000), new Date(lease.end_time * 1000), lease.resource, state_str);\r
+            });\r
+        };\r
+  \r
+\r
+/*\r
         $scope.setPage = function(page) {\r
-            var tmpFrm = $scope.pageSize * page;\r
-            var tmpTo = tmpFrm + $scope.pageSize;\r
+            var tmpFrm = $scope.items_per_page * page;\r
+            var tmpTo = tmpFrm + $scope.items_per_page;\r
             tmpTo = SchedulerDataViewData.length < tmpTo ? SchedulerDataViewData.length : tmpTo;\r
-            $scope.curPage = page;\r
+            $scope.current_page = page;\r
             $scope.resources = [];\r
             var j = 0;\r
             for (var k = tmpFrm; k < tmpTo; k++) {\r
@@ -79,9 +333,12 @@ myApp.factory('$exceptionHandler', function () {
             $('#tblSlider').slider('value', 0);\r
             //init Slots\r
             $scope.initSlots(0, SchedulerTotalVisibleCells);\r
-        };\r
+        };*/\r
 \r
+        // Typically we will only init visible slots\r
         $scope.initSlots = function (from, to) {\r
+            return; // JORDAN !!!\r
+\r
             //init\r
             $scope.slots = [];\r
 \r
@@ -89,7 +346,7 @@ myApp.factory('$exceptionHandler', function () {
             //set\r
             for (var i = from; i < to; i++) {\r
                 $scope.slots.push(SchedulerSlots[i]);\r
-                resourceIndex = $scope.pageSize * $scope.curPage;\r
+                resourceIndex = $scope.items_per_page * $scope.current_page;\r
                 for (var j = 0; j < $scope.resources.length; j++) {\r
                     if (i == from) {\r
                         $scope.resources[j].leases = [];\r
@@ -102,39 +359,7 @@ myApp.factory('$exceptionHandler', function () {
             $scope.$apply();\r
         };\r
 \r
-        $scope.moveFrontSlot = function(from, to) {\r
-            //$scope.slots.shift();\r
-            //$scope.slots.push(SchedulerSlots[to]);\r
-            //for (var j = 0; j < $scope.resources.length; j++) {\r
-            //    $scope.resources[j].leases.shift();\r
-            //    $scope.resources[j].leases.push(SchedulerData[j].leases[to]);\r
-            //}\r
-            //try {\r
-            //    $scope.$digest();\r
-            //    //$scope.$apply();\r
-            //} catch (err) {\r
-            //    $scope.initSlots(from, to);\r
-            //}\r
-            $scope.initSlots(from, to);\r
-        };\r
-\r
-        $scope.moveBackSlot = function(from, to) {\r
-            //$scope.$apply(function() {\r
-                //try {\r
-                //    $scope.slots.pop();\r
-                //    $scope.slots.unshift(SchedulerSlots[from]);\r
-                //    for (var j = 0; j < $scope.resources.length; j++) {\r
-                //        $scope.resources[j].leases.pop();\r
-                //        $scope.resources[j].leases.unshift(SchedulerData[j].leases[from]);\r
-                //    }\r
-                //} catch (err) {\r
-                //    alert("error");\r
-                //}\r
-\r
-            $scope.initSlots(from, to);\r
-            //});\r
-        };\r
-\r
+/*\r
         $scope.getPageNumbers = function () {\r
             var totalNumbersShowned = ($scope.totalPages > 10 ? 10 : $scope.totalPages + 1 );\r
             var tmtNumDiv = totalNumbersShowned / 2;\r
@@ -146,13 +371,13 @@ myApp.factory('$exceptionHandler', function () {
             if (totalNumbersShowned > 1) {\r
                 //set from - to\r
                 if ($scope.totalPages > totalNumbersShowned) {\r
-                    if ($scope.curPage <= tmtNumDiv) {\r
+                    if ($scope.current_page <= tmtNumDiv) {\r
                         //nothing\r
-                    } else if ($scope.curPage >= $scope.totalPages - tmtNumDiv) {\r
+                    } else if ($scope.current_page >= $scope.totalPages - tmtNumDiv) {\r
                         numTo = $scope.totalPages;\r
                         numFrom = numTo - totalNumbersShowned;\r
                     } else {\r
-                        numFrom = $scope.curPage - tmtNumDiv;\r
+                        numFrom = $scope.current_page - tmtNumDiv;\r
                         numTo = numFrom + totalNumbersShowned;\r
                     }\r
                 }\r
@@ -164,7 +389,7 @@ myApp.factory('$exceptionHandler', function () {
             }\r
             return rtrnArr;\r
         };\r
-\r
+*/\r
         // Return this object reference.\r
         return (this);\r
 \r
@@ -175,4 +400,4 @@ myApp.factory('$exceptionHandler', function () {
     app.controller("SchedulerCtrl", Controller);\r
 \r
 \r
-})(angular, myApp);
\ No newline at end of file
+})(angular, myApp);\r
index 1daeee0..7275b75 100755 (executable)
@@ -46,20 +46,6 @@ function schedulerCloneArray(originalArray) {
     return clonedArray;\r
 }\r
 \r
-function schedulerGetSlots(slotSpan) {\r
-    if (slotSpan == 0) slotSpan = 10;\r
-    var slots = [];\r
-    var d = new Date(2014, 1, 1, 0, 0, 0, 0);\r
-    var i = 0;\r
-    while (d.getDate() == 1) {\r
-        var tmpTime = schedulerPadStr(d.getHours()) + ':' + schedulerPadStr(d.getMinutes());\r
-        slots.push({ id: i, time: tmpTime });\r
-        d = schedulerAddMinutes(d, slotSpan);\r
-        i++;\r
-    }\r
-    return slots;\r
-}\r
-\r
 function schedulerGetLeases(slotSpan, granularity) {\r
     granularity = granularity / 60;\r
     if (slotSpan == 0) slotSpan = 10;\r
@@ -187,7 +173,16 @@ function schedulerAddMinutes(date, minutes) {
     return new Date(date.getTime() + minutes * 60000);\r
 }\r
 \r
-function schedulerCompareOnDay(dateOne, dateTwo) {\r
+/**\r
+ * Compares two dates\r
+ *\r
+ * Returns:\r
+ *   0 if they are equal\r
+ *   -1 if the first is less than the second\r
+ *   1 if the first is more than the second\r
+ */\r
+function schedulerCompareOnDay(dateOne, dateTwo)\r
+{\r
     if (dateOne.getYear() == dateTwo.getYear() &&\r
         dateOne.getMonth() == dateTwo.getMonth() &&\r
         dateOne.getDate() == dateTwo.getDate()) {\r
diff --git a/plugins/scheduler2/static/js/scheduler-table-selector.js b/plugins/scheduler2/static/js/scheduler-table-selector.js
deleted file mode 100755 (executable)
index d69a2d4..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-////version 3\r
-var scheduler_table_dragStart_td = 0;\r
-var scheduler_table_dragStart_tr = 0;\r
-var scheduler_table_dragEnd_td = 0;\r
-var scheduler_table_dragEnd_tr = 0;\r
-//tmp gia ta loops & check gia to last state\r
-var tmp_scheduler_table_dragStart_td;\r
-var tmp_scheduler_table_dragStart_tr;\r
-var tmp_scheduler_table_dragEnd_td;\r
-var tmp_scheduler_table_dragEnd_tr;\r
-var schedulerTableIsDragging = false;\r
-// try stop \r
-var continueExecuting = false;\r
-var isExecuting = false;\r
-\r
-\r
-\r
-function rangeMouseDown(e) {\r
-    if (SchedulerData) console.time("mouse:rangeMouseDown");\r
-    if (schedulerIsRightClick(e)) {\r
-        return false;\r
-    } else {\r
-        scheduler_table_dragStart_tr = $(this).parent().index();\r
-        scheduler_table_dragStart_td = $(this).index() -1;\r
-        scheduler_table_dragEnd_tr = scheduler_table_dragStart_tr;\r
-        scheduler_table_dragEnd_td = scheduler_table_dragStart_td;\r
-        //alert(scheduler_table_dragStart_tr);\r
-        //var allCells = $("#tblReservation td");\r
-        //dragStart = allCells.index($(this));\r
-\r
-        if ( $(this).hasClass("free")){\r
-            $(this).addClass("selected_tmp");\r
-            $(this).siblings("td[data-groupid='" + $(this).data('groupid') + "']").addClass("selected_tmp");\r
-        }\r
-        schedulerTableIsDragging = true;\r
-        //selectRange();\r
-\r
-        if (typeof e.preventDefault != 'undefined') { e.preventDefault(); }\r
-        document.documentElement.onselectstart = function () { return false; };\r
-    }\r
-    if (SchedulerData) console.timeEnd("mouse:rangeMouseDown");\r
-}\r
-\r
-function rangeMouseUp(e) {\r
-    if (SchedulerData) console.time("mouse:rangeMouseUp");\r
-    if (schedulerIsRightClick(e)) {\r
-        return false;\r
-    } else {\r
-        //var allCells = $("#tblReservation td");\r
-        //dragEnd = allCells.index($(this));\r
-\r
-        scheduler_table_dragEnd_tr = $(this).parent().index();\r
-        scheduler_table_dragEnd_td = $(this).index() -1;\r
-\r
-        schedulerTableIsDragging = false;\r
-        selectRange(false);\r
-\r
-        document.documentElement.onselectstart = function () { return true; };\r
-    }\r
-    if (SchedulerData) console.timeEnd("mouse:rangeMouseUp");\r
-}\r
-\r
-function rangeMouseMove(e) {\r
-    //if (SchedulerData) console.time("mouse:rangeMouseMove");\r
-    if (schedulerTableIsDragging) {\r
-        scheduler_table_dragEnd_tr = $(this).parent().attr('data-trindex');\r
-        scheduler_table_dragEnd_td = $(this).attr('data-tdindex');\r
-\r
-        //if (SchedulerData) this.SchedulerData('foo');\r
-\r
-        if ((scheduler_table_dragEnd_tr != tmp_scheduler_table_dragEnd_tr) || (scheduler_table_dragEnd_td != tmp_scheduler_table_dragEnd_td)) {\r
-            //console.log(scheduler_table_dragEnd_tr + " - " + tmp_scheduler_table_dragEnd_tr);\r
-            //console.log(scheduler_table_dragEnd_td + " - " + tmp_scheduler_table_dragEnd_td);\r
-            //selectRange(true);\r
-        }\r
-    }\r
-    //if (SchedulerData) console.timeEnd("mouse:rangeMouseMove");\r
-}\r
-\r
-function selectRange(isTemp) {\r
-    if (SchedulerData) console.time("mouse:---selectRange");\r
-\r
-    if (!schedulerCtrlPressed)\r
-        $("#" + schedulerTblId + "  td.selected, #" + schedulerTblId + "  td.selected_tmp").each(function() {\r
-            $(this).removeClass('selected selected_tmp').addClass('free');\r
-            $(this).siblings("td[data-groupid='" + $(this).data('groupid') + "']").removeClass('selected selected_tmp').addClass("free");\r
-            schedulerFreeSlot($(this).data('slotid'), $(this).siblings('th').data('rowindex'), $(this).siblings('th').data('resourceindex'));\r
-        });\r
-\r
-    tmp_scheduler_table_dragStart_td = scheduler_table_dragStart_td;\r
-    tmp_scheduler_table_dragStart_tr = scheduler_table_dragStart_tr;\r
-    tmp_scheduler_table_dragEnd_td = scheduler_table_dragEnd_td;\r
-    tmp_scheduler_table_dragEnd_tr = scheduler_table_dragEnd_tr;\r
-\r
-    if (tmp_scheduler_table_dragStart_td > tmp_scheduler_table_dragEnd_td) {\r
-        var tmp = tmp_scheduler_table_dragStart_td;\r
-        tmp_scheduler_table_dragStart_td = tmp_scheduler_table_dragEnd_td;\r
-        tmp_scheduler_table_dragEnd_td = tmp;\r
-    }\r
-\r
-    if (tmp_scheduler_table_dragStart_tr > tmp_scheduler_table_dragEnd_tr) {\r
-        var tmp = tmp_scheduler_table_dragStart_tr;\r
-        tmp_scheduler_table_dragStart_tr = tmp_scheduler_table_dragEnd_tr;\r
-        tmp_scheduler_table_dragEnd_tr = tmp;\r
-    }\r
-    //var angularScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
-    //alert("tmp_scheduler_table_dragStart_td:" + tmp_scheduler_table_dragStart_td + "\n tmp_scheduler_table_dragStart_tr:" + tmp_scheduler_table_dragStart_tr + "\n tmp_scheduler_table_dragEnd_td:" + tmp_scheduler_table_dragEnd_td + "\n tmp_scheduler_table_dragEnd_tr:" + tmp_scheduler_table_dragEnd_tr);\r
-\r
-\r
-    for (var i = tmp_scheduler_table_dragStart_tr; i <= tmp_scheduler_table_dragEnd_tr; i++) {\r
-        for (var j = tmp_scheduler_table_dragStart_td; j <= tmp_scheduler_table_dragEnd_td; j++) {\r
-            //alert("i:" + i + "j:" + j);\r
-            var cell = $('#' + schedulerTblId + '  tbody tr:eq(' + i + ') td:eq(' + j + ')');\r
-            //$(cell)\r
-            var curClass = $(cell).attr("class");\r
-            curClass = curClass.replace('ng-scope','').trim();\r
-            //alert(curClass);\r
-            switch (curClass) {\r
-                case "free_tmp":\r
-                    $(cell).removeClass('selected_tmp selected free_tmp free');\r
-                    $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").removeClass("selected_tmp selected free_tmp free");\r
-                    if (isTemp){\r
-                        $(cell).addClass("free_tmp");\r
-                        $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("free");\r
-                    } else {\r
-                        schedulerFreeSlot($(cell).data('slotid'), $(cell).siblings('th').data('rowindex'), $(cell).siblings('th').data('resourceindex'));\r
-                        $(cell).addClass("free");\r
-                        $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("free");\r
-                    }\r
-                    break;\r
-                case "free":\r
-                    $(cell).removeClass('selected_tmp selected free_tmp free');\r
-                    $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").removeClass("selected_tmp selected free_tmp free");\r
-                    if (isTemp){\r
-                        $(cell).addClass("selected_tmp");\r
-                        $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("selected_tmp");\r
-                    }else {\r
-                        schedulerSelectSlot($(cell).data('slotid'), $(cell).siblings('th').data('rowindex'), $(cell).siblings('th').data('resourceindex'));\r
-                        $(cell).addClass("selected");\r
-                        $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("selected");\r
-                    }\r
-                    break;\r
-                case "selected_tmp":\r
-                    $(cell).removeClass('selected_tmp selected free_tmp free');\r
-                    $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").removeClass("selected_tmp selected free_tmp free");\r
-                    if (isTemp){\r
-                        $(cell).addClass("selected_tmp");\r
-                        $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("selected_tmp");\r
-                    } else {\r
-                        schedulerSelectSlot($(cell).data('slotid'), $(cell).siblings('th').data('rowindex'), $(cell).siblings('th').data('resourceindex'));\r
-                        $(cell).addClass("selected");\r
-                        $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("selected");\r
-                    }\r
-                    break;\r
-                case "selected":\r
-                    $(cell).removeClass('selected_tmp selected free_tmp free');\r
-                    $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").removeClass("selected_tmp selected free_tmp free");\r
-                    if (isTemp){\r
-                        $(cell).addClass("free_tmp");\r
-                        $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("free_tmp");\r
-                    } else {\r
-                        schedulerFreeSlot($(cell).data('slotid'), $(cell).siblings('th').data('rowindex'), $(cell).siblings('th').data('resourceindex'));\r
-                        $(cell).addClass("free");\r
-                        $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("free");\r
-                    }\r
-                    break;\r
-                case "closed":\r
-                    //do nothing\r
-                    //alert("not allowed!");\r
-                    break;\r
-            }\r
-        }\r
-    }\r
-\r
-\r
-    /*if (dragEnd + 1 < dragStart) { // reverse select\r
-    //alert(1);\r
-    $("#tblReservation td:not([class='info'])").slice(dragEnd, dragStart + 1).addClass('selected');\r
-    } else {\r
-    alert(dragStart + "-" + dragEnd);\r
-    $("#tblReservation td:not([class='info'])").slice(dragStart, dragEnd).addClass('selected');\r
-    }*/\r
-\r
-    if (SchedulerData) console.timeEnd("mouse:---selectRange");\r
-}\r
-\r
-function ClearTableSelection(){\r
-    $('#' + schedulerTblId + ' .selected').addClass("free").removeClass("selected");\r
-}\r
-\r
-\r
index 10eaa9a..fe8e3e8 100755 (executable)
@@ -26,6 +26,8 @@
 #\r
 */\r
 \r
+// XXX groupid = all slots those that go with a min granularity\r
+\r
 /* some params */\r
 var scheduler2;\r
 var scheduler2Instance;\r
@@ -33,26 +35,40 @@ var scheduler2Instance;
 var schedulerCtrlPressed = false;\r
 //table Id\r
 var schedulerTblId = "scheduler-reservation-table";\r
-var schedulerTblFirstColWidth = 150;\r
-//Some Data\r
+var SCHEDULER_FIRST_COLWIDTH = 150;\r
+\r
+\r
+/* Number of scheduler slots per hour. Used to define granularity. Should be inferred from resources XXX */\r
 var schedulerSlotsPerHour = 6;\r
+var RESOURCE_DEFAULT_GRANULARITY    = 1800 /* s */; // should be computed automatically from resource information\r
+var DEFAULT_GRANULARITY             = 1800 /* s */; // should be computed automatically from resource information. Test with 600\r
+var DEFAULT_PAGE_RANGE = 5;\r
+\r
 var schedulerMaxRows = 12;\r
+\r
+/* All resources */\r
 var SchedulerData = [];\r
+\r
+/* ??? */\r
 var SchedulerSlots = [];\r
+\r
 var SchedulerDateSelected = new Date();\r
+// Round to midnight\r
+SchedulerDateSelected.setHours(0,0,0,0);\r
+\r
+/* Filtered resources */\r
 var SchedulerDataViewData = [];\r
+\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
+var SCHEDULER_COLWIDTH = 50;\r
+\r
 (function($) {\r
         scheduler2 = Plugin.extend({\r
 \r
@@ -67,13 +83,21 @@ var tmpSchedulerLeases = [];
                 this.classname = "scheduler2";\r
                 // Call the parent constructor, see FAQ when forgotten\r
                 this._super(options, element);\r
+\r
+                var scope = this._get_scope()\r
+\r
+                // XXX not needed\r
                 scheduler2Instance = this;\r
+\r
                 // We need to remember the active filter for datatables filtering\r
+                // XXX not needed\r
                 this.filters = Array();\r
 \r
+                // XXX BETTER !!!!\r
+                $(window).delegate('*', 'keypress', function (evt){\r
+                        alert("erm");\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
@@ -83,154 +107,178 @@ var tmpSchedulerLeases = [];
                         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
+                // XXX naming\r
+                //$("#" + schedulerTblId).on('mousedown', 'td', rangeMouseDown).on('mouseup', 'td', rangeMouseUp).on('mousemove', 'td', rangeMouseMove);\r
 \r
-                if (schedulerDebug) console.time("Listening_to_queries");\r
-                /* Listening to queries */\r
+                this._resources_received = false;\r
+                this._leases_received = false;\r
+                \r
+                scope._leases_by_resource = {};\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
+                /* Listening to queries */\r
+                this.listen_query(options.query_uuid, 'resources');\r
+                this.listen_query(options.query_lease_uuid, 'leases');\r
+\r
+                /* Generate slots according to the default granularity. Should\r
+                 * be updated when resources arrive.  Should be the pgcd in fact XXX */\r
+                this._granularity = DEFAULT_GRANULARITY;\r
+                scope.granularity = this._granularity;\r
+                this._all_slots = this._generate_all_slots();\r
+\r
+                $('#' + schedulerTblId + ' thead tr th:eq(0)').css("width", SCHEDULER_FIRST_COLWIDTH);\r
+                //this get width might need fix depending on the template \r
+                var tblwidth = $('#scheduler-tab').parent().outerWidth();\r
+\r
+                /* Number of visible cells...*/\r
+                this._num_visible_cells = parseInt((tblwidth - SCHEDULER_FIRST_COLWIDTH) / SCHEDULER_COLWIDTH);\r
+                /* ...should be a multiple of the lcm of all encountered granularities. */\r
+                // XXX Should be updated everytime a new resource is added\r
+                this._lcm_colspan = this._lcm(this._granularity, RESOURCE_DEFAULT_GRANULARITY) / this._granularity;\r
+                this._num_visible_cells = this._num_visible_cells - this._num_visible_cells % this._lcm_colspan;\r
+                /* scope also needs this value */\r
+                scope.num_visible_cells = this._num_visible_cells;\r
+                scope.lcm_colspan = this._lcm_colspan;\r
+\r
+                scope.options = this.options;\r
+                scope.from = 0;\r
+\r
+                // A list of {id, time} dictionaries representing the slots for the given day\r
+                scope.slots = this._all_slots;\r
+                this.scope_resources_by_key = {};\r
+\r
+                this._initUI();\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
+            _get_scope : function()\r
+            {\r
+                return angular.element(document.getElementById('SchedulerCtrl')).scope();\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
+            _scope_set_resources : function()\r
+            {\r
+                var self = this;\r
+                var scope = this._get_scope();\r
+\r
+                var records = manifold.query_store.get_records(this.options.query_uuid);\r
+\r
+                scope.resources = [];\r
+\r
+                $.each(records, function(i, record) {\r
+                    if (!record.exclusive)\r
+                        return true; // ~ continue\r
+\r
+                    // copy not to modify original record\r
+                    var resource = jQuery.extend(true, {}, record);\r
+\r
+                    // Fix granularity\r
+                    resource.granularity = typeof(resource.granularity) == "number" ? resource.granularity : RESOURCE_DEFAULT_GRANULARITY;\r
+                    resource.leases = []; // a list of occupied timeslots\r
+\r
+                    self.scope_resources_by_key[resource['urn']] = resource;\r
+                    scope.resources.push(resource);\r
+                });\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
+            _scope_clear_leases: function()\r
+            {\r
+                var self = this;\r
+                var scope = this._get_scope();\r
+\r
+                // Setup leases with a default free status...\r
+                $.each(this.scope_resources_by_key, function(resource_key, resource) {\r
+                    resource.leases = [];\r
+                    var colspan_lease = resource.granularity / self._granularity; //eg. 3600 / 1800 => 2 cells\r
+                    for (i=0; i < self._all_slots.length / colspan_lease; i++) { // divide by granularity\r
+                        resource.leases.push({\r
+                            id:     'coucou',\r
+                            status: 'free', // 'selected', 'reserved', 'maintenance' XXX pending ??\r
+                        });\r
+                    }\r
+                });\r
 \r
             },\r
-            on_all_resources_query_done: function(data) {\r
-                _resourcesDone = true;\r
-                this._initScheduler();\r
+\r
+            _scope_set_leases: function()\r
+            {\r
+                var self = this;\r
+                var scope = this._get_scope();\r
+            \r
+                var leases = manifold.query_store.get_records(this.options.query_lease_uuid);\r
+                $.each(leases, function(i, lease) {\r
+\r
+                    console.log("SET LEASES", new Date(lease.start_time* 1000));\r
+                    console.log("          ", new Date(lease.end_time* 1000));\r
+                    // XXX We should ensure leases are correctly merged, otherwise our algorithm won't work\r
+\r
+                    // Populate leases by resource array: this will help us merging leases later\r
+                    if (!(lease.resource in scope._leases_by_resource))\r
+                        scope._leases_by_resource[lease.resource] = [];\r
+                    scope._leases_by_resource[lease.resource].push(lease);\r
+\r
+                    var resource = self.scope_resources_by_key[lease.resource];\r
+                    var day_timestamp = SchedulerDateSelected.getTime() / 1000;\r
+\r
+                    var id_start = (lease.start_time - day_timestamp) / resource.granularity;\r
+                    if (id_start < 0) {\r
+                        /* Some leases might be in the past */\r
+                        id_start = 0;\r
+                    }\r
+    \r
+                    var id_end   = (lease.end_time   - day_timestamp) / resource.granularity - 1;\r
+                    var colspan_lease = resource.granularity / self._granularity; //eg. 3600 / 1800 => 2 cells\r
+                    if (id_end >= self._all_slots.length / colspan_lease) {\r
+                        /* Limit the display to the current day */\r
+                        id_end = self._all_slots.length / colspan_lease\r
+                    }\r
+\r
+                    for (i = id_start; i <= id_end; i++)\r
+                        // the same slots might be affected multiple times.\r
+                        // PENDING_IN + PENDING_OUT => IN \r
+                        //\r
+                        // RESERVED vs SELECTED !\r
+                        //\r
+                        // PENDING !!\r
+                        resource.leases[i].status = 'selected'; \r
+                });\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
+            on_resources_query_done: function(data)\r
+            {\r
+                this._resources_received = true;\r
+                this._scope_set_resources();\r
+                this._scope_clear_leases();\r
+                if (this._leases_received)\r
+                    this._scope_set_leases();\r
+                    \r
+                this._get_scope().$apply();\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
+            on_leases_query_done: function(data)\r
+            {\r
+                this._leases_received = true;\r
+                if (this._resources_received) {\r
+                    this._scope_set_leases();\r
+                    this._get_scope().$apply();\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
+            /* Filters on resources */\r
+            on_resources_filter_added:   function(filter) { this._get_scope().$apply(); },\r
+            on_resources_filter_removed: function(filter) { this._get_scope().$apply(); },\r
+            on_resources_filter_clear:   function()       { this._get_scope().$apply(); },\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
+            /* Filters on leases ? */\r
+            on_leases_filter_added:      function(filter) { this._get_scope().$apply(); },\r
+            on_leases_filter_removed:    function(filter) { this._get_scope().$apply(); },\r
+            on_leases_filter_clear:      function()       { this._get_scope().$apply(); },\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
+            /* INTERNAL FUNCTIONS */\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
+/* XXX IN TEMPLATE XXX\r
                 if (SchedulerDataViewData.length == 0) {\r
                     $("#plugin-scheduler").hide();\r
                     $("#plugin-scheduler-empty").show();\r
@@ -238,296 +286,158 @@ var tmpSchedulerLeases = [];
                 } else {\r
                     $("#plugin-scheduler-empty").hide();\r
                     $("#plugin-scheduler").show();\r
+                    // initSchedulerResources\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
 \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
+             * Initialize the date picker, the table, the slider and the buttons. Once done, display scheduler.\r
+             */\r
+            _initUI: function() \r
+            {\r
+                var self = this;\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
+                    // the selected date\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
+                    if (SchedulerDateSelected == null || SchedulerDateSelected == '') {\r
                         alert("Please select a date, so the scheduler can reserve leases.");\r
+                        return;\r
                     }\r
+                    // Set slider to origin\r
+                    $('#tblSlider').slider('value', 0);\r
+                    // Refresh leases\r
+                    self._scope_clear_leases();\r
+                    self._scope_set_leases();\r
+                    // Refresh display\r
+                    self._get_scope().$apply();\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
+                    max: (this._all_slots.length - self._num_visible_cells) / self._lcm_colspan,\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
+                        var scope = self._get_scope();\r
+                        scope.from = ui.value * self._lcm_colspan;\r
+                        scope.$apply();\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
+                $('#btnSchedulerSubmit').click(this._on_submit);\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
+        // GUI EVENTS\r
+\r
+        // TO BE REMOVED\r
+        _on_submit : function()\r
+        {\r
+            var leasesForCommit = new Array();\r
+            var tmpDateTime = SchedulerDateSelected;\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
-        },\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
+            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
-            //reserved\r
-            //tmpRes.leases[tmpLea.id\r
         },\r
+        \r
+        // PRIVATE METHODS\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
+         * Greatest common divisor\r
+         */\r
+        _gcd : function(x, y)\r
+        {\r
+            return (y==0) ? x : this._gcd(y, x % y);\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
+         * Least common multiple\r
+         */\r
+        _lcm : function(x, y)\r
+        {\r
+            return x * y / this._gcd(x, y);\r
+        },\r
+    \r
+        _pad_str : function(i)\r
+        {\r
+            return (i < 10) ? "0" + i : "" + i;\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
+         * Member variables used:\r
+         *   _granularity\r
+         * \r
+         * Returns:\r
+         *   A list of {id, time} dictionaries.\r
+         */\r
+        _generate_all_slots: function()\r
+        {\r
+            var slots = [];\r
+            // Start with a random date (a first of a month), only time will matter\r
+            var d = new Date(2014, 1, 1, 0, 0, 0, 0);\r
+            var i = 0;\r
+            // Loop until we change the day\r
+            while (d.getDate() == 1) {\r
+                // Nicely format the time...\r
+                var tmpTime = this._pad_str(d.getHours()) + ':' + this._pad_str(d.getMinutes());\r
+                /// ...and add the slot to the list of results\r
+                slots.push({ id: i, time: tmpTime });\r
+                // Increment the date with the granularity\r
+                d = new Date(d.getTime() + this._granularity * 1000);\r
+                i++;\r
             }\r
+            return slots;\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
diff --git a/plugins/scheduler2/static/js/scheduler2_old.js b/plugins/scheduler2/static/js/scheduler2_old.js
deleted file mode 100755 (executable)
index ee833bf..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-/*\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 init_start_visible_index = 10;\r
-var init_end_visible_index = 21;\r
-var rsvrTblNm = "scheduler-reservation-table";\r
-var SchedulerResources = [];\r
-var schdlr_totalColums = 0;\r
-var SetPerFun = null;\r
-var Sched2 = null;\r
-var Debug = true;\r
-var schdlr_PartsInOneHour = 6;\r
-\r
-(function ($) {\r
-    var 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
-\r
-            schdlr_totalColums = $("#scheduler-reservation-table th").length;\r
-\r
-            //selection from table \r
-            $(window).keydown(function (evt) {\r
-                if (evt.which == 17) { // ctrl\r
-                    ctrlPressed = true;\r
-                }\r
-            }).keyup(function (evt) {\r
-                if (evt.which == 17) { // ctrl\r
-                    ctrlPressed = false;\r
-                }\r
-            });\r
-            $("#" + rsvrTblNm).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 (Debug) console.time("Listening_to_queries");\r
-            /* Listening to queries */\r
-            this.listen_query(options.query_uuid, 'all_ev');\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_lease_uuid, 'lease');\r
-            if (Debug) console.timeEnd("Listening_to_queries");\r
-\r
-            $("#ShedulerNodes tbody").html('<tr><td id="schdlr_frstTD" style="background:transparent; height:45px; border:none;"></td></tr>');\r
-        },\r
-\r
-        /* PLUGIN EVENTS */\r
-        // on_show like in querytable\r
-\r
-\r
-        /* GUI EVENTS */\r
-\r
-        // a function to bind events here: click change\r
-        // how to raise manifold events\r
-\r
-\r
-        /* GUI MANIPULATION */\r
-\r
-        // We advise you to write function to change behaviour of the GUI\r
-        // Will use naming helpers to access content _inside_ the plugin\r
-        // always refer to these functions in the remaining of the code\r
-\r
-        show_hide_button: function () {\r
-            // this.id, this.el, this.cl, this.elts\r
-            // same output as a jquery selector with some guarantees\r
-        },\r
-\r
-        //drawResources: function () {\r
-        drawLeases: function () {\r
-            \r
-            //if (Debug) this.debug('foo');\r
-            if (Debug) console.time("each:SchedulerResources");\r
-\r
-            //scheduler-reservation-table main table columns\r
-            totalColums = $("#scheduler-reservation-table thead tr th").length;\r
-            //var totalCell = [];\r
-            //for (var i = 0; i < totalColums; i++) { totalCell.push("<td></td>"); }\r
-            //var srt_body = [];\r
-            var totalCell = "";\r
-            for (var i = 0; i < totalColums; i++) totalCell +="<td></td>"; \r
-            var srt_body = "";\r
-            /*\r
-            $.each(SchedulerResources, function (i, group) {\r
-                console.log(group.groupName);\r
-                //var groupTR = $("#ShedulerNodes tbody").html('<tr><td class="no-image verticalIndex" rowspan="' + group.resources.length + '"><div class="verticalText">' + group.groupName + '</div></td><td id="schdlr_frstTD" class="info fixed"></td></tr>');\r
-                //var groupTR = $("#ShedulerNodes tbody").html('<tr><td class="no-image verticalIndex" rowspan="' + 30 + '"><div class="verticalText">' + group.groupName + '</div></td><td id="schdlr_frstTD" class="info fixed"></td></tr>');\r
-                var groupTR = $("#ShedulerNodes tbody").html('<tr><td id="schdlr_frstTD" class="info fixed"></td></tr>');\r
-                \r
-                //$.each(group.resources.slice(0,30), function (i, resource) {\r
-                $.each(group.resources, function (i, resource) {\r
-                    if (i == 0) {\r
-                        //$("#ShedulerNodes tbody tr:first").append('<td class="info fixed">' + resource.hostname + '</td>');\r
-                        $(groupTR).find("#schdlr_frstTD").html(resource.urn);\r
-                        //$(srt_body).html("<tr>" + totalCell + "</tr>");\r
-                    } else {\r
-                        $(groupTR).find("tr:last").after('<tr><td class="info fixed">' + resource.urn + '</td></tr>');\r
-                        //$(srt_body).find("tr:last").after("<tr>" + totalCell + "</tr>");\r
-                    }\r
-                    srt_body += "<tr>" + totalCell + "</tr>";\r
-                    //srt_body.push('<tr>'); srt_body = srt_body.concat(totalCell.concat()); srt_body.push('/<tr>');\r
-                });\r
-            });\r
-            */\r
-\r
-            srt_body += "<tr>" + totalCell + "</tr>";\r
-            //$("#scheduler-reservation-table tbody").html(srt_body.join(""));\r
-            //$("#scheduler-reservation-table tbody").append(srt_body);\r
-\r
-            if (Debug) console.timeEnd("each:SchedulerResources");\r
-            \r
-\r
-            $("#" + rsvrTblNm + " tbody tr").each(function (index) { $(this).attr("data-trindex", index); });\r
-\r
-        },\r
-\r
-        /* TEMPLATES */\r
-\r
-        // see in the html template\r
-        // How to load a template, use of mustache\r
-\r
-        /* QUERY HANDLERS */\r
-        loadWithDate: function () {\r
-            // only convention, not strictly enforced at the moment\r
-        },\r
-        // How to make sure the plugin is not desynchronized\r
-        // He should manifest its interest in filters, fields or records\r
-        // functions triggered only if the proper listen is done\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
-            $("#ShedulerNodes tbody").find("tr:last").after('<tr><td class="info fixed">' + data.urn + '</td></tr>');\r
-            this.drawLeases();\r
-            //console.log(data);\r
-            var tmpGroup = lookup(SchedulerResources, 'groupName', data.type);\r
-            if (tmpGroup == null) {\r
-                tmpGroup = { groupName: data.type, resources: [] };\r
-                SchedulerResources.push(tmpGroup);\r
-                //if (data.type != "node")  alert('not all node');\r
-            }\r
-            tmpGroup.resources.push(data);\r
-            //alert('new_record');\r
-        },\r
-        on_all_resources_query_done: function (data) {\r
-            //this.drawResources();\r
-            //data is empty on load\r
-            /* GUI setup and event binding */\r
-            this._initUI();\r
-            this._SetPeriodInPage(init_start_visible_index, init_end_visible_index);\r
-            this.loadWithDate();\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_lease_new_record: function (data) { console.log('lease_new_record'); },\r
-        on_lease_query_done: function (data) { console.log('lease_query_done'); },\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
-\r
-        on_filter_added: function (filter) {\r
-\r
-        },\r
-\r
-        // ... be sure to list all events here\r
-\r
-        /* RECORD HANDLERS */\r
-        on_all_new_record: function (record) {\r
-            //\r
-            alert('on_all_new_record');\r
-        },\r
-\r
-        debug : function (log_txt) {\r
-            if (typeof window.console != 'undefined') {\r
-                console.debug(log_txt);\r
-            }\r
-        },\r
-\r
-        /* INTERNAL FUNCTIONS */\r
-        _initUI: function () {\r
-            if (Debug) console.time("_initUI");\r
-            //fix margins in tables\r
-            mtNodesTbl = $("#" + rsvrTblNm + " tr:first").outerHeight() + 6;\r
-            mtSchrollCon = $("#nodes").outerWidth();\r
-            $("#nodes").css("margin-top", mtNodesTbl);\r
-            $("#reservation-table-scroll-container").css("margin-left", mtSchrollCon);\r
-            SetPerFun = this._SetPeriodInPage;\r
-            //slider\r
-            $("#time-range").slider({\r
-                range: true,\r
-                min: 0,\r
-                max: 24,\r
-                step: 0.5,\r
-                values: [init_start_visible_index, init_end_visible_index],\r
-                slide: function (event, ui) {\r
-                    SetPerFun(ui.values[0], ui.values[1]);\r
-                }\r
-            });\r
-            $("#DateToRes").datepicker({\r
-                dateFormat: "yy-mm-dd",\r
-                minDate: 0,\r
-                numberOfMonths: 3\r
-            }).change(function () {\r
-                //Scheduler2.loadWithDate();\r
-            }).click(function () {\r
-                $("#ui-datepicker-div").css("z-index", 5);\r
-            });\r
-            //other stuff\r
-            fixOddEvenClasses();\r
-            $("#" + rsvrTblNm + " td:not([class])").addClass("free");\r
-            if (Debug) console.timeEnd("_initUI");\r
-        },\r
-        _SetPeriodInPage: function (start, end) {\r
-            if (Debug) console.time("_SetPeriodInPage");\r
-            ClearTableSelection();\r
-            $("#lbltime").html(GetTimeFromInt(start) + " - " + GetTimeFromInt(end));\r
-            \r
-            var start_visible_index = (start * schdlr_PartsInOneHour) + 1;\r
-            var end_visible_index = (end * schdlr_PartsInOneHour);\r
-\r
-            //hide - show\r
-            for (i = 0; i < start_visible_index; i++) {\r
-                $("#" + rsvrTblNm + " td:nth-child(" + i + "), #" + rsvrTblNm + " th:nth-child(" + i + ")").hide();\r
-            }\r
-            for (i = end_visible_index + 1; i <= schdlr_totalColums; i++) {\r
-                $("#" + rsvrTblNm + " td:nth-child(" + i + "), #" + rsvrTblNm + " th:nth-child(" + i + ")").hide();\r
-            }\r
-            /*$("#" + rsvrTblNm + " td:not([class*='info']), #" + rsvrTblNm + " th:not([class*='fixed'])").hide();*/\r
-            for (i = start_visible_index; i <= end_visible_index; i++) {\r
-                $("#" + rsvrTblNm + " td:nth-child(" + i + "), #" + rsvrTblNm + " th:nth-child(" + i + ")").show();\r
-            }\r
-\r
-            if ($("#" + rsvrTblNm + " th:visible:first").width() > 105) {\r
-                $("#" + rsvrTblNm + " th span").css("display", "inline")\r
-            } else {\r
-                $("#" + rsvrTblNm + " th span").css("display", "block");\r
-            }\r
-            mtNodesTbl = $("#" + rsvrTblNm + " tr:first").outerHeight() + 6;\r
-            $("#nodes").css("margin-top", mtNodesTbl);\r
-            //$("#scroll_container").width($("#Search").width() - $("#nodes").width());\r
-            //$("#nodes th").height($("#tblReservation th:visible:first").height() - 2);\r
-            if (Debug) console.timeEnd("_SetPeriodInPage");\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
index d7cd16e..95c6ba7 100755 (executable)
             <div id="tblSlider"></div>\r
         </div>\r
         <div class="table-responsive">\r
+\r
             <table id="scheduler-reservation-table" class="table table-bordered table-condensed">\r
                 <thead>\r
                     <tr>\r
                         <th>#</th>\r
-                        <th ng-repeat="slot in slots">\r
+                        <th ng-repeat="slot in slots | offset: from | limitTo: num_visible_cells ">\r
                             {[{ slot.time }]}\r
                         </th>\r
                     </tr>\r
                 </thead>\r
                 <tbody>\r
-                    <tr ng-repeat="resource in resources" ng-class-odd="''" ng-class-even="'even'">\r
-                        <th data-resourceid="{[{ resource.id }]}" data-rowindex="{[{ $index }]}" data-resourceindex="{[{ resource.index }]}" style="word-wrap: break-word; word-break: break-all; ">{[{ resource.name }]}</th>\r
-                        <td ng-repeat="lease in resource.leases" data-slotid="{[{ lease.id }]}" data-groupid="{[{ lease.groupid }]}" ng-class="{{ 'lease.status' }}"></td>\r
+                    <tr ng-repeat="resource in resources | filter: filter_visible | offset: (current_page-1) * items_per_page | limitTo: items_per_page" \r
+                                               ng-class-odd="''" \r
+                                               ng-class-even="'even'">\r
+                        <th data-resourceid="{[{ resource.urn }]}" \r
+                                                       data-rowindex="{[{ $index }]}" \r
+                                                       data-resourceindex="{[{ resource.index }]}" \r
+                                                       style="word-wrap: break-word; word-break: break-all; ">\r
+                                                       {[{ resource.hostname }]}\r
+                                               </th>\r
+                        <td ng-repeat="lease in resource.leases | offset: from / lcm_colspan  | limitTo: num_visible_cells / lcm_colspan"\r
+                                                       data-slotid="{[{ lease.id }]}" \r
+                                                       data-groupid="{[{ lease.groupid }]}" \r
+                                                       ng-class="{{ 'lease.status' }}"\r
+                                                       colspan="{[{resource.granularity / granularity}]}"\r
+                                                       ng-click="select(from+$index, lease, $parent.resource)">\r
+                                               </td>\r
                     </tr>\r
                 </tbody>\r
             </table>\r
+\r
             <div class="row">\r
                 <div class="col-xs-5">\r
                     <div id="resources-list__table_length" class="dataTables_length">\r
-                        <label>total Pages : {[{totalPages}]}</label>\r
+                        <label>Total pages : {[{page_count()}]}</label>\r
                     </div>\r
                 </div>\r
                 <div class="col-xs-7">\r
                     <div class="dataTables_paginate paging_bootstrap">\r
                         <ul class="pagination">\r
-                            <li class="prev disabled"><a href="#">← Previous</a></li>\r
-                            <li ng-cloak ng-repeat="t in getPageNumbers() track by $index" ng-class="{active: t==curPage+1}"><a href="#" ng-click="setPage(t-1)">{[{t}]}</a></li>\r
-                            <li class="next disabled"><a href="#">Next → </a></li>\r
+                                                       <li ng-class="prevPageDisabled()">\r
+                                                         <a href ng-click="prevPage()">« Prev</a>\r
+                                                       </li>\r
+                            <li ng-repeat="n in range()" \r
+                                                           ng-class="{active: n==current_page}" \r
+                                                               ng-click="setPage(n)"><a href="#">{[{n}]}</a></li>\r
+                                                       <li ng-class="nextPageDisabled()">\r
+                                             <a href ng-click="nextPage()">Next »</a>\r
+                                           </li>\r
                         </ul>\r
                     </div>\r
                 </div>\r
index 5b8ce0f..498bdd2 100644 (file)
@@ -1,26 +1,29 @@
-from django.template                 import RequestContext
-from django.shortcuts                import render_to_response
+from django.template                    import RequestContext
+from django.shortcuts                   import render_to_response
 
-from manifold.core.query             import Query, AnalyzedQuery
-from manifoldapi.manifoldapi         import execute_query
+from manifold.core.query                import Query, AnalyzedQuery
+from manifoldapi.manifoldapi            import execute_query
 
-from django.views.generic.base      import TemplateView
+from django.views.generic.base          import TemplateView
 
-from unfold.loginrequired           import LoginRequiredView
+from unfold.loginrequired               import LoginRequiredView
 from django.http import HttpResponse
 from django.shortcuts import render
 
-from unfold.page                     import Page
+from unfold.page                        import Page
 
-from myslice.configengine            import ConfigEngine
-from plugins.querytable              import QueryTable
-from plugins.googlemap               import GoogleMap
-from plugins.queryupdater            import QueryUpdater
-from plugins.testbeds                import TestbedsPlugin
-from plugins.scheduler2              import Scheduler2
-from plugins.columns_editor          import ColumnsEditor
-from plugins.sladialog               import SlaDialog
-from plugins.lists.simplelist        import SimpleList
+from myslice.configengine               import ConfigEngine
+
+from plugins.apply                      import ApplyPlugin
+from plugins.querytable                 import QueryTable
+from plugins.googlemap                  import GoogleMap
+#from plugins.queryupdater               import QueryUpdater
+from plugins.filter_status              import FilterStatusPlugin
+from plugins.testbeds                   import TestbedsPlugin
+from plugins.scheduler2                 import Scheduler2
+from plugins.columns_editor             import ColumnsEditor
+from plugins.sladialog                  import SlaDialog
+from plugins.lists.simplelist           import SimpleList
 
 from myslice.theme import ThemeView
 
@@ -48,6 +51,7 @@ class SliceResourceView (LoginRequiredView, ThemeView):
         # Example: select slice_hrn, resource.urn, lease.resource, lease.start_time, lease.end_time from slice where slice_hrn == "ple.upmc.myslicedemo"
         main_query = Query.get('slice').filter_by('slice_hrn', '=', slicename)
         main_query.select(
+                'slice_urn', # XXX We need the key otherwise the storage of records bugs !
                 'slice_hrn',
                 'resource.urn', 
                 'resource.hostname', 'resource.type',
@@ -67,7 +71,7 @@ class SliceResourceView (LoginRequiredView, ThemeView):
         sq_lease       = aq.subquery('lease')
 
         query_resource_all = Query.get('resource').select(resource_fields)
-        page.enqueue_query(query_resource_all)
+        #page.enqueue_query(query_resource_all)
 
         # leases query
         lease_md = metadata.details_by_object('lease')
@@ -183,17 +187,17 @@ class SliceResourceView (LoginRequiredView, ThemeView):
         # --------------------------------------------------------------------------
         # QueryUpdater (Pending Operations)
  
-        pending_resources = QueryUpdater(
-            page                = page,
-            title               = 'Pending operations',
-            query               = main_query,
-            togglable           = False,
-            # start turned off, it will open up itself when stuff comes in
-            toggled             = False,
-            domid               = 'pending',
-            outline_complete    = True,
-            username            = request.user,
-        )
+#DEPRECATED|        pending_resources = QueryUpdater(
+#DEPRECATED|            page                = page,
+#DEPRECATED|            title               = 'Pending operations',
+#DEPRECATED|            query               = main_query,
+#DEPRECATED|            togglable           = False,
+#DEPRECATED|            # start turned off, it will open up itself when stuff comes in
+#DEPRECATED|            toggled             = False,
+#DEPRECATED|            domid               = 'pending',
+#DEPRECATED|            outline_complete    = True,
+#DEPRECATED|            username            = request.user,
+#DEPRECATED|        )
 
         # --------------------------------------------------------------------------
         # NETWORKS
@@ -206,14 +210,13 @@ class SliceResourceView (LoginRequiredView, ThemeView):
         #page.enqueue_query(query_network)
 
         filter_testbeds = TestbedsPlugin(
-            page          = page,
-            domid         = 'testbeds-filter',
-            title         = 'Filter by testbeds',
-            query         = sq_resource,
-            query_all     = query_resource_all,
-            #query_network = query_network,
-            init_key      = "network_hrn",
-            checkboxes    = True,
+            page            = page,
+            domid           = 'testbeds-filter',
+            title           = 'Filter by testbeds',
+            query           = sq_resource,
+            #query_network  = query_network,
+            init_key        = "network_hrn",
+            checkboxes      = True,
             datatables_options = {
                 'iDisplayLength': 25,
                 'bLengthChange' : True,
@@ -221,6 +224,18 @@ class SliceResourceView (LoginRequiredView, ThemeView):
                 },
         )
 
+        filter_status = FilterStatusPlugin(
+            page            = page,
+            domid           = "filter-status",
+            query           = sq_resource,
+        )
+        apply = ApplyPlugin(
+            page            = page,
+            domid           = "apply",
+            query           = sq_resource,
+        )
+            
+
         # --------------------------------------------------------------------------
         # SLA View and accept dialog
         
@@ -244,9 +259,12 @@ class SliceResourceView (LoginRequiredView, ThemeView):
         template_env['columns_editor'] = filter_column_editor.render(self.request)
 
         template_env['filter_testbeds'] = filter_testbeds.render(self.request)
+        template_env['filter_status'] = filter_status.render(self.request)
+        template_env['apply'] = apply.render(self.request)
+
         template_env['map_resources'] = map_resources.render(self.request)
         template_env['scheduler'] = resources_as_scheduler2.render(self.request)
-        template_env['pending_resources'] = pending_resources.render(self.request)
+#        template_env['pending_resources'] = pending_resources.render(self.request)
         template_env['sla_dialog'] = sla_dialog.render(self.request)
         template_env["theme"] = self.theme
         template_env["username"] = request.user
index 7cf80f9..e7f9c35 100644 (file)
@@ -15,7 +15,7 @@ from plugins.stack                   import Stack
 from plugins.tabs                    import Tabs
 from plugins.querytable              import QueryTable 
 from plugins.querygrid               import QueryGrid
-from plugins.queryupdater            import QueryUpdater
+#DEPRECATED|from plugins.queryupdater            import QueryUpdater
 from plugins.googlemap               import GoogleMap
 from plugins.senslabmap              import SensLabMap
 from plugins.scheduler               import Scheduler
@@ -146,18 +146,18 @@ class SliceView (LoginRequiredAutoLogoutView, ThemeView):
         )
     
         # --------------------------------------------------------------------------
-        # QueryUpdater (Pending Operations)
-
-        main_stack.insert(QueryUpdater(
-            page                = page,
-            title               = 'Pending operations',
-            query               = main_query,
-            togglable           = True,
-            # start turned off, it will open up itself when stuff comes in
-            toggled             = False, 
-            domid               = 'pending',
-            outline_complete    = True,
-        ))
+#DEPRECATED|        # QueryUpdater (Pending Operations)
+#DEPRECATED|
+#DEPRECATED|        main_stack.insert(QueryUpdater(
+#DEPRECATED|            page                = page,
+#DEPRECATED|            title               = 'Pending operations',
+#DEPRECATED|            query               = main_query,
+#DEPRECATED|            togglable           = True,
+#DEPRECATED|            # start turned off, it will open up itself when stuff comes in
+#DEPRECATED|            toggled             = False, 
+#DEPRECATED|            domid               = 'pending',
+#DEPRECATED|            outline_complete    = True,
+#DEPRECATED|        ))
 
         # --------------------------------------------------------------------------
         # Filter Resources
index af81a0e..d5ab2da 100644 (file)
@@ -22,6 +22,7 @@
 {% insert_str prelude "js/class.js" %}
 {% insert_str prelude "js/plugin-helper.js" %}
 {% insert_str prelude "js/mustache.js" %}
+{% insert_str prelude "js/hashtable.js" %}
 {% insert_str prelude "js/plugin.js" %}
 {% insert_str prelude "js/manifold.js" %}
 {% insert_str prelude "css/manifold.css" %}
index f411ec2..158f53b 100644 (file)
@@ -20,6 +20,7 @@
 {% insert_str prelude "js/class.js" %}
 {% insert_str prelude "js/plugin-helper.js" %}
 {% insert_str prelude "js/mustache.js" %}
+{% insert_str prelude "js/hashtable.js" %}
 {% insert_str prelude "js/plugin.js" %}
 {% insert_str prelude "js/manifold.js" %}
 {% insert_str prelude "css/manifold.css" %}
index 8513951..dcf6ee6 100644 (file)
@@ -20,6 +20,7 @@
 {% insert_str prelude "js/class.js" %}
 {% insert_str prelude "js/plugin-helper.js" %}
 {% insert_str prelude "js/mustache.js" %}
+{% insert_str prelude "js/hashtable.js" %}
 {% insert_str prelude "js/plugin.js" %}
 {% insert_str prelude "js/manifold.js" %}
 {% insert_str prelude "css/manifold.css" %}
index 42c769d..12af9bf 100644 (file)
                        <div class="col-md-12"><p class="alert-success">{{ msg }}</p></div>
                        {% endif %}
                </div>
+
+               {{ filter_status }}{{ apply }}
+               <!--
                <div class="list-group-item list-resources">
                        <span class="list-group-item-heading" style="padding-left: 0;">Resource status:</span>
                        <a class="list-group-item active" data-panel="resources" href="#" style='display: inline-block !important;'>All</a>
                        <a class="list-group-item" data-panel="reserved" href="#" style='display: inline-block !important;'>Reserved</a>
                        <a class="list-group-item" data-panel="pending" href="#" style='display: inline-block !important;'>Pending <span class="badge" id="badge-pending" data-number="0"></span></a>
                </div>
-               
+               -->
+
                <div class="row">
                        <ul class="nav nav-pills nav-resources">
                          <li class="active"><a data-panel="resources" href="#">Table</a></li>
@@ -74,7 +78,7 @@
                <div class="row" style="height:100%;">
                        <div id="resources" class="panel">
                                 <!-- Button trigger modal - columns selector -->
-                               <button class="btn btn-primary btn-sm" style="float:right;" data-toggle="modal" data-target="#myModal">Select columns</button>
+                               <button class="btn btn-primary btn-sm" style="float:right;" data-toggle="modal" data-target="#myModal">...</button>
                 {{list_resources}}
                                <!-- <table cellpadding="0" cellspacing="0" border="0" class="table" id="objectList"></table> -->
                        </div>
index 3de6090..e585414 100644 (file)
@@ -86,7 +86,7 @@ img {
 #interval_animation {
        margin: 5px;
 }
-.tooltip {
+.Atooltip {
        display: none;
        position: absolute;
        border: 1px solid #333;
index a7c3e22..610b056 100644 (file)
@@ -1,8 +1,8 @@
 {% for json in queries_json %}manifold.insert_query({{ json|safe }});
 {% endfor %}
-$(document).ready(function () {
-var query_exec_tuples = [];
-{% for tuple in query_exec_tuples %} query_exec_tuples.push({'query_uuid':"{{ tuple.query_uuid }}"}); 
-{% endfor %}
-manifold.asynchroneous_exec(query_exec_tuples);
-})
+//$(document).ready(function () {
+//var query_exec_tuples = [];
+//{% for tuple in query_exec_tuples %} query_exec_tuples.push({'query_uuid':"{{ tuple.query_uuid }}"}); 
+//{% endfor %}
+//manifold.asynchroneous_exec(query_exec_tuples);
+//})