Merge branch 'fed4fire' into onelab
authorJavier García <javier.garcial.external@atos.net>
Mon, 1 Dec 2014 12:03:09 +0000 (13:03 +0100)
committerJavier García <javier.garcial.external@atos.net>
Mon, 1 Dec 2014 12:03:09 +0000 (13:03 +0100)
Conflicts:
plugins/queryupdater/static/js/queryupdater.js
portal/templates/base.html

117 files changed:
README.manifold-tables [new file with mode: 0644]
manifoldapi/manifoldapi.py
manifoldapi/manifoldproxy.py
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
myslice/urls.py
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/googlemap/__init__.py
plugins/googlemap/static/js/googlemap.js
plugins/googlemap/templates/googlemap.html
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/queryupdater/templates/queryupdater.html
plugins/scheduler2/__init__.py
plugins/scheduler2/asdf.txt [deleted file]
plugins/scheduler2/static/css/scheduler2.css
plugins/scheduler2/static/js/scheduler-SchedulerCtrl.js [deleted file]
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/static/js/selectRangeWorker.js [deleted file]
plugins/scheduler2/templates/scheduler.html
plugins/testbeds/__init__.py
plugins/testbeds/static/js/testbeds.js
plugins/testbeds/templates/testbeds.html
plugins/univbrisfv/__init__1.py
portal/accountview.py
portal/actions.py
portal/contactview.py
portal/emailactivationview.py
portal/forms.py
portal/homeview.py
portal/institution.py
portal/joinview.py
portal/managementtabrequests.py [new file with mode: 0644]
portal/registrationview.py
portal/slicerequestview.py
portal/sliceresourceview.py
portal/sliceview.py
portal/sliceviewold.py
portal/static/css/jquery.qtip.min.css [new file with mode: 0644]
portal/static/css/onelab.css
portal/static/js/join.js
portal/static/js/jquery-ui.js [new file with mode: 0755]
portal/static/js/jquery.qtip.min.js [new file with mode: 0644]
portal/static/js/myslice-ui.js
portal/static/js/onelab_slice-resource-view.js
portal/supportview.py
portal/templates/activate_user.html
portal/templates/activate_user.txt
portal/templates/base.html
portal/templates/contact.html
portal/templates/email_activation.html
portal/templates/email_default_sender.txt
portal/templates/fed4fire/fed4fire_base.html
portal/templates/institution.html
portal/templates/join_complete.html [new file with mode: 0644]
portal/templates/join_view.html
portal/templates/management-tab-requests.html [new file with mode: 0644]
portal/templates/onelab/onelab_about.html [new file with mode: 0644]
portal/templates/onelab/onelab_account-view.html [new file with mode: 0644]
portal/templates/onelab/onelab_base.html
portal/templates/onelab/onelab_contact.html [new file with mode: 0644]
portal/templates/onelab/onelab_home-view.html
portal/templates/onelab/onelab_institution.html
portal/templates/onelab/onelab_management-tab-requests.html [new file with mode: 0644]
portal/templates/onelab/onelab_news.html
portal/templates/onelab/onelab_registration_view.html [new file with mode: 0644]
portal/templates/onelab/onelab_slice-user-view.html
portal/templates/onelab/onelab_slice-view.html
portal/templates/onelab/onelab_slicerequest_view.html [new file with mode: 0644]
portal/templates/onelab/onelab_supportview.html [new file with mode: 0644]
portal/templates/onelab/onelab_widget-login-user.html
portal/templates/onelab/onelab_widget-slice-sections.html
portal/templates/onelab/onelab_widget-topmenu.html
portal/templates/password_reset_confirm.html
portal/templates/password_reset_form.html
portal/templates/registration_view.html
portal/templates/slice-request-view.html
portal/templates/slice-resource-view.html
portal/templates/slice-tab-users-view.html
portal/templates/slice_request_email.html
portal/templates/slice_request_email.txt
portal/templates/slicerequest_view.html
portal/templates/supportview.html
portal/templates/termsview.html [new file with mode: 0644]
portal/templates/user_register_complete.html
portal/templates/user_request_email.html
portal/templates/user_request_email.txt
portal/termsview.py [new file with mode: 0644]
portal/urls.py
portal/validationview.py
portal/views/__init__.py [moved from portal/views.py with 100% similarity]
setup.py
third-party/bootstrap-datepicker [new symlink]
third-party/bootstrap-datepicker-1/bootstrap-datepicker.js [new file with mode: 0644]
third-party/bootstrap-datepicker-1/datepicker.css [new file with mode: 0644]
third-party/bootstrap-slider [new symlink]
third-party/bootstrap-slider-1/bootstrap-slider.js [new file with mode: 0644]
third-party/bootstrap-slider-1/slider.css [new file with mode: 0644]
to-be-integrated/plugins/pres_view/static/css/pres_view.css
ui/templates/messages-transient-header.html
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
index b105bf3..a8b2ab9 100644 (file)
@@ -4,6 +4,7 @@ import copy, xmlrpclib
 from myslice.configengine import ConfigEngine
 
 from django.contrib import messages
+from django.shortcuts import redirect
 from manifoldresult import ManifoldResult, ManifoldCode, ManifoldException
 from manifold.core.result_value import ResultValue
 
@@ -154,7 +155,7 @@ def execute_query(request, query):
         request.session.flush()
         #raise Exception, "User not authenticated"
         host = request.get_host()
-        return redirect(host)
+        return redirect('/')
     manifold_api_session_auth = request.session['manifold']['auth']
     return _execute_query(request, query, manifold_api_session_auth)
 
index c16ffde..99959ae 100644 (file)
@@ -40,7 +40,7 @@ with the query passed using POST"""
     # format_out: how to serve the results
     if format != 'json':
         print "manifoldproxy.proxy: unexpected format %s -- exiting"%format
-        return
+        return HttpResponse ({"ret":0}, mimetype="application/json")
     try:
         # translate incoming POST request into a query object
         if debug: print 'manifoldproxy.proxy: request.POST',request.POST
@@ -84,6 +84,7 @@ with the query passed using POST"""
         print "** PROXY ERROR **",e
         import traceback
         traceback.print_exc()
+        return HttpResponse ({"ret":0}, mimetype="application/json")
 
 #################### 
 # see CSRF_FAILURE_VIEW in settings.py
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..e261efe 100644 (file)
@@ -38,27 +38,60 @@ var FIELD_REMOVED  = 5;
 var CLEAR_FIELDS   = 6;
 var NEW_RECORD     = 7;
 var CLEAR_RECORDS  = 8;
+
+/**
+ * event: FIELD_STATE_CHANGED
+ *
+ * Parameters:
+ *   dict :
+ *      .state      : ???? used to be FIELD_REQUEST_ADD / FIELD_REQUEST_REMOVE
+ *      .key        : ??? the key fields of the record
+ *      .op         : the key of the record who has received an update
+ *      .value      : the new state of the record
+ */
 var FIELD_STATE_CHANGED = 9;
 
 var IN_PROGRESS    = 101;
-var DONE           = 102;
+var DONE           = 102; //XXX Should be harmonized with query state
 
 /* Update requests related to subqueries */
+
+/*
 var SET_ADD        = 201;
 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 +101,59 @@ 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_VALUE     = 1;
+var STATE_WARNINGS  = 2;
+var STATE_VISIBLE   = 3;
+
+// ACTIONS
+var STATE_SET_CHANGE = 0;
+var STATE_SET_ADD    = 1;
+var STATE_SET_REMOVE = 2;
+var STATE_SET_CLEAR  = 3;
+
+// 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;
+var STATE_VALUE_CHANGE_PENDING    = 8;
+var STATE_VALUE_CHANGE_SUCCESS    = 9;
+var STATE_VALUE_CHANGE_FAILURE    = 10;
+
+// STATE_WARNINGS : dict
+
+// STATE_VISIBLE : boolean
+
+/******************************************************************************
+ * CONSTRAINTS
+ ******************************************************************************/
+
+var CONSTRAINT_RESERVABLE_LEASE     = 0;
+
+var CONSTRAINT_RESERVABLE_LEASE_MSG = "Configuration required: this resource needs to be scheduled";
 
 // 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 +163,36 @@ 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 = [];
+
+    // XXX Until we find a better solution
+    this.num_pending = 0;
+    this.num_unconfigured = 0;
+
     // update_query null unless we are a main_query (aka parent_query == null); only main_query_fields can be updated...
 }
 
@@ -142,13 +241,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 +280,343 @@ 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, key, record_key;
+        query_ext = this.find_analyzed_query_ext(query_uuid);
+        
+        if (typeof(record) == 'object') {
+            key = manifold.metadata.get_key(query_ext.query.object);
+            record_key = manifold.record_get_value(record, key);
+        } else {
+            record_key = record;
+        }
+
+        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, key, record_key;
+        query_ext = this.find_analyzed_query_ext(query_uuid);
+        
+        if (typeof(record) == 'object') {
+            key = manifold.metadata.get_key(query_ext.query.object);
+            record_key = manifold.record_get_value(record, key);
+        } else {
+            record_key = record;
+        }
+
+        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.recount = function(query_uuid)
+    {
+        var query_ext;
+        var is_reserved, is_pending, in_set,  is_unconfigured;
+
+        query_ext = manifold.query_store.find_analyzed_query_ext(query_uuid);
+        query_ext.num_pending = 0;
+        query_ext.num_unconfigured = 0;
+
+        this.iter_records(query_uuid, function(record_key, record) {
+            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);
+
+            is_reserved = (record_state == STATE_SET_IN) 
+                       || (record_state == STATE_SET_OUT_PENDING)
+                       || (record_state == STATE_SET_IN_SUCCESS)
+                       || (record_state == STATE_SET_OUT_FAILURE);
+
+            is_pending = (record_state == STATE_SET_IN_PENDING) 
+                      || (record_state == STATE_SET_OUT_PENDING);
+
+            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
+
+            is_unconfigured = (in_set && !$.isEmptyObject(record_warnings));
+
+            /* Let's update num_pending and num_unconfigured at this stage */
+            if (is_pending)
+                query_ext.num_pending++;
+            if (is_unconfigured)
+                query_ext.num_unconfigured++;
+        });
+
+    }
+
+    this.apply_filters = function(query_uuid)
+    {
+        var start = new Date().getTime();
+
+        // Toggle visibility of records according to the different filters.
+
+        var self = this;
+        var filters = this.get_filters(query_uuid);
+        var col_value;
+        /* Let's update num_pending and num_unconfigured at this stage */
+
+        // Adapted from querytable._querytable_filter()
+
+        this.iter_records(query_uuid, function(record_key, record) {
+            var is_reserved, is_pending, in_set,  is_unconfigured;
+            var visible = true;
+
+            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);
+
+            is_reserved = (record_state == STATE_SET_IN) 
+                       || (record_state == STATE_SET_OUT_PENDING)
+                       || (record_state == STATE_SET_IN_SUCCESS)
+                       || (record_state == STATE_SET_OUT_FAILURE);
+
+            is_pending = (record_state == STATE_SET_IN_PENDING) 
+                      || (record_state == STATE_SET_OUT_PENDING);
+
+            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
+
+            is_unconfigured = (in_set && !$.isEmptyObject(record_warnings));
+
+            // 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
+                    }
+
+                    switch (value) {
+                        case 'reserved':
+                            // true  => ~ continue
+                            // false => ~ break
+                            visible = is_reserved;
+                            return visible;
+                        case 'unconfigured':
+                            visible = is_unconfigured;
+                            return visible;
+                        case 'pending':
+                            visible = is_pending;
+                            return visible;
+                    }
+                    return false; // ~ break
+                }
+
+                /* Normal filtering behaviour (according to the record content) follows... */
+                col_value = manifold.record_get_value(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);
+        });
+
+        var end = new Date().getTime();
+        console.log("APPLY FILTERS took", end - start, "ms");
+
+    }
+
 }
 
 /*!
@@ -201,6 +646,124 @@ 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(self, other, key_fields)
+    {
+        for (i=0; i < key_fields.length; i++) {
+            var this_value  = self[key_fields[i]];
+            var other_value = other[key_fields[i]];
+
+            var this_type = manifold.get_type(this_value);
+            var other_type = manifold.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, key_fields)))
+                        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, key_fields)))
+                            return false;
+                    break;
+            }
+        }
+        return true;
+    },
+
+    record_equals: function(key_fields)
+    {
+        return function(other) { 
+            return manifold._record_equals(this, other, key_fields); 
+        };
+    },
+
+    _in_array: function(element, array, key_fields)
+    {
+        if (key_fields.length > 1) {
+            for (i = 0; i < array.length; i++) {
+                if (manifold._record_equals(element, array[i], key_fields))
+                    return true;
+            }
+            return false;
+        } else {
+            // XXX TODO If we have a dict, extract the key first
+            return ($.inArray(element, array) != -1);
+        }
+    },
+
     /************************************************************************** 
      * Metadata management
      **************************************************************************/ 
@@ -221,6 +784,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 +818,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 +854,11 @@ var manifold = {
         // NEW API
         manifold.query_store.insert(query);
 
+        // Run
+        $(document).ready(function() {
+        manifold.run_query(query);
+        });
+
         // FORMER API
         if (query.analyzed_query == null) {
             query.analyze_subqueries();
@@ -316,21 +898,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 +1012,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,30 +1044,50 @@ 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);
     },
 
-    setup_update_query: function(query, records) {
+    setup_update_query: function(query, records) 
+    {
         // We don't prepare an update query if the result has more than 1 entry
         if (records.length != 1)
             return;
@@ -462,6 +1096,10 @@ var manifold = {
         var record = records[0];
 
         var update_query_ext = query_ext.update_query_ext;
+
+        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 +1118,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 +1134,47 @@ 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(record, key.sort());
+        record.equals   = manifold.record_equals(record, 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) XXX Bugs with fields declared string while they are not : network.version is a dict in fact
+                    if (subobject && subobject != 'string')
+                        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;
+            }
+        }
     },
 
     /**
@@ -534,7 +1203,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,15 +1214,14 @@ var manifold = {
                         // We assume it won't have changed
                         continue;
 
-                    var result_value = record[field];
                     if (!result_value)
                         throw "Internal error";
 
                     data = {
-                        request: FIELD_REQUEST_CHANGE,
+                        state : STATE_SET,
                         key   : field,
-                        value : update_value,
-                        status: (update_value == result_value) ? FIELD_REQUEST_SUCCESS : FIELD_REQUEST_FAILURE,
+                        op    : update_value,
+                        value : (update_value == result_value) ? STATE_VALUE_CHANGE_SUCCESS : STATE_VALUE_CHANGE_FAILURE,
                     }
                     manifold.raise_record_event(query_uuid, FIELD_STATE_CHANGED, data);
 
@@ -561,9 +1230,9 @@ var manifold = {
                     throw "Not implemented";
                     break;
 
-                case TYPE_LIST_OF_VALUES:
+                /*
+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 +1242,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,
@@ -611,7 +1280,13 @@ var manifold = {
 
 
                     break;
+                */
+                case TYPE_LIST_OF_VALUES: // XXX Until fixed
                 case TYPE_LIST_OF_RECORDS:
+                    var new_state,cur_query_uuid;
+
+                    cur_query_uuid = query.analyzed_query.subqueries[field].query_uuid;
+
                     // example: slice.resource
                     //  - update_query_orig.params.resource = resources in slice before update
                     //  - update_query.params.resource = resource requested in slice
@@ -619,65 +1294,67 @@ var manifold = {
                     var key = manifold.metadata.get_key(field);
                     if (!key)
                         continue;
+                    /*
                     if (key.length > 1) {
                         throw "Not implemented";
                         continue;
                     }
                     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); });
 
+                    // XXX All this could be deduced from record state : STATE_IN_PENDING and STATE_OUT_PENDING
+                    // what we had at the begining
                     var update_keys  = update_query_orig.params[field];
+                    // what we asked
                     var query_keys   = update_query.params[field];
-                    var added_keys   = $.grep(query_keys, function (x) { return $.inArray(x, update_keys) == -1 });
-                    var removed_keys = $.grep(update_keys, function (x) { return $.inArray(x, query_keys) == -1 });
+                    // what we added and removed
+                    var added_keys   = $.grep(query_keys,  function (x) { return (!(manifold._in_array(x, update_keys, key))); });
+                    var removed_keys = $.grep(update_keys, function (x) { return (!(manifold._in_array(x, query_keys,  key))); });
 
+                    // Send events related to parent query
+                    $.each(added_keys, function(i, added_key) {
+                        new_state = (manifold._in_array(added_key, result_keys, key)) ? STATE_SET_IN_SUCCESS : STATE_SET_IN_FAILURE;
 
-                    $.each(added_keys, function(i, key) {
-                        if ($.inArray(key, result_keys) == -1) {
-                            data = {
-                                request: FIELD_REQUEST_ADD,
-                                key   : field,
-                                value : key,
-                                status: FIELD_REQUEST_FAILURE,
-                            }
-                        } else {
-                            data = {
-                                request: FIELD_REQUEST_ADD,
-                                key   : field,
-                                value : key,
-                                status: FIELD_REQUEST_SUCCESS,
-                            }
-                        }
+                        // Update record state for children queries
+                        manifold.query_store.set_record_state(cur_query_uuid, added_key, STATE_SET, new_state);
+
+                        // XXX This could be optimized
+                        manifold.query_store.recount(cur_query_uuid); 
+
+                        data = { state: STATE_SET, key  : field, op   : new_state, value: added_key }
                         manifold.raise_record_event(query_uuid, FIELD_STATE_CHANGED, data);
                     });
-                    $.each(removed_keys, function(i, key) {
-                        if ($.inArray(key, result_keys) == -1) {
-                            data = {
-                                request: FIELD_REQUEST_REMOVE,
-                                key   : field,
-                                value : key,
-                                status: FIELD_REQUEST_SUCCESS,
-                            }
-                        } else {
-                            data = {
-                                request: FIELD_REQUEST_REMOVE,
-                                key   : field,
-                                value : key,
-                                status: FIELD_REQUEST_FAILURE,
-                            }
-                        }
+                    $.each(removed_keys, function(i, removed_key) {
+                        new_state = (manifold._in_array(removed_key, result_keys, key)) ? STATE_SET_OUT_FAILURE : STATE_SET_OUT_SUCCESS;
+
+                        // Update record state for children queries
+                        manifold.query_store.set_record_state(cur_query_uuid, removed_key, STATE_SET, new_state);
+
+                        // XXX This could be optimized
+                        manifold.query_store.recount(cur_query_uuid); 
+
+                        data = { state: STATE_SET, key  : field, op   : new_state, value: removed_key }
                         manifold.raise_record_event(query_uuid, FIELD_STATE_CHANGED, data);
                     });
 
-
                     break;
             }
         }
         
         // XXX Now we need to adapt 'update' and 'update_orig' queries as if we had done a get
         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;
+
+        // Send DONE message to plugins
+        query.iter_subqueries(function(sq, data, parent_query) {
+            manifold.raise_record_event(sq.query_uuid, DONE);
+        });
+
     },
 
     process_query_records: function(query, records) {
@@ -735,6 +1412,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 */
@@ -787,14 +1465,211 @@ var manifold = {
         manifold.raise_event_handler('record', query_uuid, event_type, value);
     },
 
+    /**
+     * Event handler helpers
+     */
+    _get_next_state_add: function(prev_state)
+    {
+        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;
+        }
+        return new_state;
+    },
+
+    _get_next_state_remove: function(prev_state)
+    {
+        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;
+        }
+        return new_state;
+    },
+
+    _grep_active_lease_callback: function(lease_query, resource_key) {
+        return function(lease_key_lease) {
+            var state, lease_key, lease;
+
+            lease_key = lease_key_lease[0];
+            lease = lease_key_lease[1];
+
+            if (lease['resource'] != resource_key)
+                return false;
+
+            state = manifold.query_store.get_record_state(lease_query.query_uuid, lease_key, STATE_SET);;
+            switch(state) {
+                case STATE_SET_IN:
+                case STATE_SET_IN_PENDING:
+                case STATE_SET_IN_SUCCESS:
+                case STATE_SET_OUT_FAILURE:
+                    return true;
+                case STATE_SET_OUT:
+                case STATE_SET_OUT_PENDING:
+                case STATE_SET_OUT_SUCCESS:
+                case STATE_SET_IN_FAILURE:
+                    return false;
+            }
+        }
+    },
+
+    _enforce_constraints: function(query_ext, record, resource_key, event_type)
+    {
+        var query, data;
+
+        query = query_ext.query;
+
+        switch(query.object) {
+
+            case 'resource':
+                // CONSTRAINT_RESERVABLE_LEASE
+                // 
+                // +) If a reservable node is added to the slice, then it should have a corresponding lease
+                // XXX Not always a resource
+                var is_reservable = (record.exclusive == true);
+                if (is_reservable) {
+                    var warnings = manifold.query_store.get_record_state(query.query_uuid, resource_key, STATE_WARNINGS);
+
+                    if (event_type == STATE_SET_ADD) {
+                        // We should have a lease_query associated
+                        var lease_query = query_ext.parent_query_ext.query.subqueries['lease']; // in  options
+                        var lease_query_ext = manifold.query_store.find_analyzed_query_ext(lease_query.query_uuid);
+                        // Do we have lease records (in) with this resource
+                        var lease_records = $.grep(lease_query_ext.records.entries(), this._grep_active_lease_callback(lease_query, resource_key));
+                        if (lease_records.length == 0) {
+                            // Sets a warning
+                            // XXX Need for a better function to manage warnings
+                            var warn = CONSTRAINT_RESERVABLE_LEASE_MSG;
+                            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.query_uuid, resource_key, STATE_WARNINGS, warnings);
+                }
+
+                /* This was redundant */
+                // manifold.query_store.recount(query.query_uuid); 
+
+                // Signal the change to plugins (even if the constraint does not apply, so that the plugin can display a checkmark)
+                data = {
+                    state:  STATE_WARNINGS,
+                    key   : resource_key,
+                    op    : null,
+                    value : warnings
+                }
+                manifold.raise_record_event(query.query_uuid, FIELD_STATE_CHANGED, data);
+                break;
+
+            case 'lease':
+                var resource_key = record.resource;
+                var resource_query = query_ext.parent_query_ext.query.subqueries['resource'];
+                var warnings = manifold.query_store.get_record_state(resource_query.query_uuid, resource_key, STATE_WARNINGS);
+
+                if (event_type == STATE_SET_ADD) {
+                     // A lease is added, it removes the constraint
+                    delete warnings[CONSTRAINT_RESERVABLE_LEASE];
+                } else {
+                    // A lease is removed, it might trigger the warning
+                    var lease_records = $.grep(query_ext.records.entries(), this._grep_active_lease_callback(query, resource_key));
+                    if (lease_records.length == 0) { // XXX redundant cases
+                        // Sets a warning
+                        // XXX Need for a better function to manage warnings
+                        var warn = CONSTRAINT_RESERVABLE_LEASE_MSG;
+                        warnings[CONSTRAINT_RESERVABLE_LEASE] = warn;
+                    } else {
+                        // Lease are defined, delete the warning in case it was set previously
+                        delete warnings[CONSTRAINT_RESERVABLE_LEASE];
+                    }
+                    
+                }
+
+                manifold.query_store.recount(resource_query.query_uuid); 
+
+                // Signal the change to plugins (even if the constraint does not apply, so that the plugin can display a checkmark)
+                data = {
+                    state:  STATE_WARNINGS,
+                    key   : resource_key,
+                    op    : null,
+                    value : warnings
+                }
+                manifold.raise_record_event(resource_query.query_uuid, FIELD_STATE_CHANGED, data);
+                break;
+        }
+
+        // -) 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 !
+
+    },
+
+    _get_query_path: function(query_ext) {
+        var path = "";
+        var sq = query_ext;
+        while (sq.parent_query_ext) {
+            if (path != "")
+                path = '.' + path;
+            path = sq.query.object + path;
+            sq = sq.parent_query_ext;
+        }
+        return path;
+    },
+
+
+    /**
+     * Handling events raised by plugins
+     */
+    raise_event: function(query_uuid, event_type, data) 
+    {
+        var query, query_ext;
 
-    raise_event: function(query_uuid, event_type, value) {
         // Query uuid has been updated with the key of a new element
         query_ext    = manifold.query_store.find_analyzed_query_ext(query_uuid);
         query = query_ext.query;
 
         switch(event_type) {
+
+            // XXX At some point, should be renamed to RECORD_STATE_CHANGED
             case FIELD_STATE_CHANGED:
+
                 // value is an object (request, key, value, status)
                 // update is only possible is the query is not pending, etc
                 // SET_ADD is on a subquery, FIELD_STATE_CHANGED on the query itself
@@ -806,104 +1681,105 @@ var manifold = {
                 update_query      = query_ext.main_query_ext.update_query_ext.query;
                 update_query_orig = query_ext.main_query_ext.update_query_orig_ext.query;
 
-                switch(value.request) {
-                    case FIELD_REQUEST_CHANGE:
-                        if (update_query.params[value.key] === undefined)
-                            update_query.params[value.key] = Array();
-                        update_query.params[value.key] = value.value;
-                        break;
-                    case FIELD_REQUEST_ADD:
-                        if ($.inArray(value.value, update_query_orig.params[value.key]) != -1)
-                            value.request = FIELD_REQUEST_ADD_RESET;
-                        if (update_query.params[value.key] === undefined)
-                            update_query.params[value.key] = Array();
-                        update_query.params[value.key].push(value.value);
+                switch(data.state) {
+            
+                    case STATE_VALUE:
+                        switch(data.op) {
+                            case STATE_CHANGE:
+                                /* Set parameter data.key in the update_query to VALUE */
+                                if (update_query.params[data.key] === undefined)
+                                    update_query.params[data.key] = Array();
+                                update_query.params[data.key] = value.value;
+                                break;
+
+                        }
                         break;
-                    case FIELD_REQUEST_REMOVE:
-                        if ($.inArray(value.value, update_query_orig.params[value.key]) == -1)
-                            value.request = FIELD_REQUEST_REMOVE_RESET;
 
-                        var arr = update_query.params[value.key];
-                        arr = $.grep(arr, function(x) { return x != value.value; });
-                        if (update_query.params[value.key] === undefined)
-                            update_query.params[value.key] = Array();
-                        update_query.params[value.key] = arr;
+                    case STATE_SET:
+                        var prev_state, new_state;
+                        var main_query, record, new_data, path;
+        
+                        // We only track state in the analyzed query
+                        prev_state = manifold.query_store.get_record_state(query_uuid, data.value, STATE_SET);
+                        if (prev_state === null)
+                            prev_state = STATE_SET_OUT;
+
+                        switch(data.op) {
+                            case STATE_SET_ADD:
+                                new_state = this._get_next_state_add(prev_state);
+
+                                /* data.value containts the resource key */
+                                manifold.query_store.add_record(query_uuid, data.value, new_state);
+                                record = manifold.query_store.get_record(query_uuid, data.value);
+                                this._enforce_constraints(query_ext, record, data.value, STATE_SET_ADD);
+                
+                                /* Process update query in parent */
+                                path =  this._get_query_path(query_ext);
+                                if (update_query.params[path] === undefined)
+                                    update_query.params[path] = Array();
+                                update_query.params[path].push(data.value);
+
+                                break;
+
+                            case STATE_SET_REMOVE:
+                                new_state = this._get_next_state_remove(prev_state);
+
+                                /* data.value contains the resource key */
+                                manifold.query_store.remove_record(query_uuid, data.value, new_state);
+                                record = manifold.query_store.get_record(query_uuid, data.value);
+                                this._enforce_constraints(query_ext, record, data.value, STATE_SET_REMOVE);
+                    
+                                /* Process update query in parent */
+                                path =  this._get_query_path(query_ext);
+                                arr = update_query.params[path];
+                                
+                                var key = manifold.metadata.get_key(query.object);
+                                arr = $.grep(arr, function(x) { return (!(manifold._record_equals(x, data.value, key))); });
+                                if (update_query.params[path] === undefined)
+                                    update_query.params[path] = Array();
+                                update_query.params[path] = arr;
+                                break;
+                        }
+
+                        /* Inform the parent query: important for update */
+                        new_data = {
+                            state : STATE_SET,
+                            key   : path,
+                            op    : new_state,
+                            value : data.value,
+                        };
+                        main_query = query_ext.main_query_ext.query;
+                        manifold.raise_record_event(main_query.query_uuid, event_type, new_data);
+                        /* Propagate the event to other plugins subscribed to the query */
+                        manifold.query_store.recount(query_uuid);
+                        new_data.key = ''
+                        manifold.raise_record_event(query_uuid, event_type, new_data);
 
-                        break;
-                    case FIELD_REQUEST_ADD_RESET:
-                    case FIELD_REQUEST_REMOVE_RESET:
-                        // XXX We would need to keep track of the original query
-                        throw "Not implemented";
                         break;
                 }
-
+/*
                 // 3. Inform others about the change
                 // a) the main query...
-                manifold.raise_record_event(query_uuid, event_type, value);
+                manifold.raise_record_event(query_uuid, event_type, data);
 
                 // b) subqueries eventually (dot in the key)
                 // Let's unfold 
-                var path_array = value.key.split('.');
-                var value_key = value.key.split('.');
 
                 var cur_query = query;
                 if (cur_query.analyzed_query)
                     cur_query = cur_query.analyzed_query;
-                $.each(path_array, function(i, method) {
-                    cur_query = cur_query.subqueries[method];
-                    value_key.shift(); // XXX check that method is indeed shifted
-                });
-                value.key = value_key;
-
-                manifold.raise_record_event(cur_query.query_uuid, event_type, value);
 
-                // XXX make this DOT a global variable... could be '/'
-                break;
-
-            case SET_ADD:
-            case SET_REMOVED:
-    
-                // update is only possible is the query is not pending, etc
-                // CHECK status !
-
-                // XXX we can only update subqueries of the main query. Check !
-                // assert query_ext.parent_query == query_ext.main_query
-                // old // update_query = query_ext.main_query_ext.update_query_ext.query;
-
-                // This SET_ADD is called on a subquery, so we have to
-                // recontruct the path of the key in the main_query
-                // We then call FIELD_STATE_CHANGED which is the equivalent for the main query
-
-                var path = "";
-                var sq = query_ext;
-                while (sq.parent_query_ext) {
-                    if (path != "")
-                        path = '.' + path;
-                    path = sq.query.object + path;
-                    sq = sq.parent_query_ext;
+                if (data.key) {
+                    var path_array = data.key.split('.');
+                    var value_key = data.key.split('.');
+                    $.each(path_array, function(i, method) {
+                        cur_query = cur_query.subqueries[method];
+                        value_key.shift(); // XXX check that method is indeed shifted
+                    });
+                    data.key = value_key;
                 }
-
-                main_query = query_ext.main_query_ext.query;
-                data = {
-                    request: (event_type == SET_ADD) ? FIELD_REQUEST_ADD : FIELD_REQUEST_REMOVE,
-                    key   : path,
-                    value : value,
-                    status: FIELD_REQUEST_PENDING,
-                };
-                this.raise_event(main_query.query_uuid, FIELD_STATE_CHANGED, data);
-
-                // old //update_query.params[path].push(value);
-                // old // console.log('Updated query params', update_query);
-                // NOTE: update might modify the fields in Get
-                // NOTE : we have to modify all child queries
-                // NOTE : parts of a query might not be started (eg slice.measurements, how to handle ?)
-
-                // if everything is done right, update_query should not be null. 
-                // It is updated when we received results from the get query
-                // object = the same as get
-                // filter = key : update a single object for now
-                // fields = the same as get
-                manifold.raise_query_event(query_uuid, event_type, value);
+                manifold.raise_record_event(cur_query.query_uuid, event_type, data);
+*/
 
                 break;
 
@@ -911,38 +1787,54 @@ var manifold = {
                 manifold.run_query(query_ext.main_query_ext.update_query_ext.query);
                 break;
 
+            /* QUERY STATE CHANGED */
+            
+            // FILTERS
+
             case FILTER_ADDED: 
-                manifold.raise_query_event(query_uuid, event_type, value);
+                /* Update internal record state */
+                manifold.query_store.add_filter(query_uuid, data);
+
+                /* Propagate the message to plugins */
+                manifold.raise_query_event(query_uuid, event_type, data);
+
                 break;
+
             case FILTER_REMOVED:
-                manifold.raise_query_event(query_uuid, event_type, value);
+                /* Update internal record state */
+                manifold.query_store.remove_filter(query_uuid, data);
+
+                /* Propagate the message to plugins */
+                manifold.raise_query_event(query_uuid, event_type, data);
+
                 break;
+
             case FIELD_ADDED:
                 main_query = query_ext.main_query_ext.query;
                 main_update_query = query_ext.main_query_ext.update_query;
-                query.select(value);
+                query.select(data);
 
                 // Here we need the full path through all subqueries
                 path = ""
                 // XXX We might need the query name in the QueryExt structure
-                main_query.select(value);
+                main_query.select(data);
 
                 // XXX When is an update query associated ?
                 // XXX main_update_query.select(value);
 
-                manifold.raise_query_event(query_uuid, event_type, value);
+                manifold.raise_query_event(query_uuid, event_type, data);
                 break;
 
             case FIELD_REMOVED:
                 query = query_ext.query;
                 main_query = query_ext.main_query_ext.query;
                 main_update_query = query_ext.main_query_ext.update_query;
-                query.unselect(value);
-                main_query.unselect(value);
+                query.unselect(data);
+                main_query.unselect(data);
 
                 // We need to inform about changes in these queries to the respective plugins
                 // Note: query & main_query have the same UUID
-                manifold.raise_query_event(query_uuid, event_type, value);
+                manifold.raise_query_event(query_uuid, event_type, data);
                 break;
         }
         // We need to inform about changes in these queries to the respective plugins
index 24d4112..76e1cb5 100644 (file)
@@ -1,3 +1,24 @@
+// Common parts for angularjs plugins
+// only one ng-app is allowed
+
+var ManifoldApp = angular.module('ManifoldApp', []);
+ManifoldApp.config(function ($interpolateProvider) {
+    $interpolateProvider.startSymbol('{[{').endSymbol('}]}');
+});
+
+ManifoldApp.factory('$exceptionHandler', function () {
+    return function (exception, cause) {
+        console.log(exception.message);
+    };
+});
+
+ManifoldApp.filter('offset', function() {
+  return function(input, start) {
+    start = parseInt(start, 10);
+    return input.slice(start);
+  };
+});
+
 // INHERITANCE
 // http://alexsexton.com/blog/2010/02/using-inheritance-patterns-to-organize-large-jquery-applications/
 // We will use John Resig's proposal
@@ -220,7 +241,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
index a49156e..68ec521 100644 (file)
@@ -16,7 +16,7 @@ import portal.platformsview
 import portal.dashboardview
 import portal.homeview
 import portal.newsview
-
+from portal.registrationview        import RegistrationView
 home_view=portal.homeview.HomeView.as_view()
 dashboard_view=portal.dashboardview.DashboardView.as_view()
 platforms_view=portal.platformsview.PlatformsView.as_view()
@@ -31,6 +31,8 @@ import portal.slicetabtestbeds
 import portal.slicetabusers
 import portal.slicetabmeasurements 
 
+import portal.managementtabrequests
+
 #### high level choices
 # main entry point (set to the / URL)
 # beware that if this view is broken you end up in an endless cycle...
@@ -90,6 +92,10 @@ urls = [
     (r'^testbeds/(?P<slicename>[^/]+)/?$', portal.slicetabtestbeds.SliceTabTestbeds.as_view()),
     (r'^measurements/(?P<slicename>[^/]+)/?$', portal.slicetabmeasurements.SliceTabMeasurements.as_view()),
     (r'^experiment/(?P<slicename>[^/]+)/?$', portal.slicetabexperiment.ExperimentView.as_view()),
+    #
+    (r'^management/requests/?$', portal.managementtabrequests.ManagementRequestsView.as_view()),
+    #
+    url(r'^register/?$', RegistrationView.as_view(), name='registration'),     
     url(r'^portal/', include('portal.urls')),
 
     # SLA
diff --git a/plugins/apply/__init__.py b/plugins/apply/__init__.py
new file mode 100644 (file)
index 0000000..540cf01
--- /dev/null
@@ -0,0 +1,51 @@
+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
+        self.username = str(settings['username'])
+
+    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/dataTables.js',
+                'js/apply.js'
+            ],
+            'css_files' : [
+                'css/dataTables.bootstrap.css',
+                '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', 'username']
+
+    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..1577f71
--- /dev/null
@@ -0,0 +1,31 @@
+.modal-dialog-large {
+    width: 800px;
+}
+
+div.resources-selected-spacer {
+    margin: 20px 15px;
+}
+table.resources-selected {
+    width: 100%;
+    margin: 10px;
+}
+table.resources-selected th {
+    background: url('../img/tablesort-header.png') repeat-x; 
+    font-weight: normal;
+    font-style: italic;
+}
+tr.add td{ 
+    background-color: #E3F6CE;
+}
+tr.remove td{ 
+    background-color: #F6CECE;
+}
+
+.ResourceSelectedClose{
+    cursor: pointer;
+}
+
+#pending__table_paginate {
+    margin-bottom: 10px;
+}
+
diff --git a/plugins/apply/static/js/apply.js b/plugins/apply/static/js/apply.js
new file mode 100644 (file)
index 0000000..efc9345
--- /dev/null
@@ -0,0 +1,498 @@
+/**
+ * MySlice Apply plugin
+ * Version: 0.1.0
+ * URL: http://www.myslice.info
+ * Description: display of selected resources
+ * Requires: 
+ * Author: The MySlice Team
+ * Copyright: Copyright 2012 UPMC Sorbonne Universités
+ * License: GPLv3
+ */
+
+(function( $ ){
+
+    var debug=false;
+    debug=true;
+
+    // XXX record selected (multiple selection ?)
+    // XXX record disabled ?
+    // XXX out of sync plugin ?
+    // XXX out of date record ?
+    // record tags ???
+    //
+    // criticality of the absence of a handler in a plugin
+    // non-critical only can have switch case
+    // 
+    // Record state through the query cycle
+
+
+    var ApplyPlugin = Plugin.extend({
+
+        /**************************************************************************
+         *                          CONSTRUCTOR
+         **************************************************************************/
+
+        init: function(options, element) {
+               this.classname="queryupdater";
+            this._super(options, element);
+
+            var self = this;
+
+            this.initial = Array();
+            this.selected_resources = Array();
+
+            this.table = this.elmt('table').dataTable({
+                sDom: "<'row'<'col-xs-5'l><'col-xs-1'r><'col-xs-6'f>>t>",
+                       bAutoWidth: true,
+            });
+            
+            //this.elmt('update').click(this, this.do_ok);
+            //this.elmt('refresh').click(this, this.do_cancel);
+
+            this.elmt('apply').on('shown.bs.modal', function() {
+                self.do_update();
+            })
+
+            this.listen_query(options.query_uuid);
+        },
+
+        /*************************** PLUGIN EVENTS ****************************/
+
+        /***************************** GUI EVENTS *****************************/
+
+        /************************** GUI MANIPULATION **************************/
+
+        populate_table: function()
+        {
+            var state;
+
+            // Loop over records and display pending ones
+            manifold.query_store.iter_records(this.options.query_uuid, function (record_key, record) {
+                state = manifold.query_store.get_record_state(this.options.query_uuid, null, STATE_SET);
+            
+            });
+        },
+        
+        set_button_state: function(name, state)
+        {
+            this.elmt(name).attr('disabled', state ? false : 'disabled');
+        },
+
+        clear: function()
+        {
+
+        },
+
+        find_row: function(value)
+        {
+            var KEY_POS, VALUE_POS, data, object_type, object_key;
+
+            TYPE_POS = 1;
+            // key in third position, column id = 2
+            VALUE_POS = 2;
+
+            var cols = $.grep(this.table.fnSettings().aoData, function(col) {
+                cur_value = col._aData[VALUE_POS];
+                object_type = col._aData[TYPE_POS];
+                object_key = manifold.metadata.get_key(object_type);
+
+                if (cur_value[0] == '{') {
+                    cur_value = JSON.parse(cur_value);
+                    return manifold._record_equals(cur_value, value, object_key);_
+                } else {
+                    return (cur_value == value);
+                }
+            } );
+
+            if (cols.length == 0)
+                return null;
+            if (cols.length > 1)
+                throw "Too many same-key rows in ResourceSelected plugin";
+
+            return cols[0];
+        },
+
+        _do_update: function(e) {
+            var self = e.data;
+            self.do_update;
+        },
+
+        do_update: function() {
+
+            var username = this.options.username;
+
+            this.spin();
+            console.log("do_update in progress");
+
+            manifold.raise_event(this.options.query_uuid, RUN_UPDATE);
+
+            // how to stop the spinning after the event? 
+            // this should be triggered by some on_updatequery_done ?
+
+        },
+
+        do_ok: function(e)
+        {
+            throw 'queryupdater.do_reset Not implemented';
+        },
+
+        do_cancel: function(e)
+        {
+            throw 'queryupdater.do_clear_annotations Not implemented';
+        },
+        
+       /**************************************************************************
+        *                           QUERY HANDLERS
+        **************************************************************************/ 
+
+        on_new_record: function(record)
+        {
+
+            // if (not and update) {
+
+                // initial['resource'], initial['lease'] ?
+                this.initial.push(record);
+
+            //this.set_record_state(record, RECORD_STATE_ATTACHED);
+                // We simply add to the table
+            // } else {
+                //                 \ this.initial_resources
+                //                  \
+                // this.             \
+                // current_resources  \    YES    |   NO
+                // --------------------+----------+---------
+                //       YES           | attached | added
+                //       NO            | removed  |   /
+                // 
+
+            // }
+        },
+
+        // QUERY STATUS
+        //                                      +-----------------+--------------+
+        //                                      v        R        |              |
+        // +-------+  ?G    +-------+        +-------+        +---+---+          |
+        // |       | -----> |       |  !G    |       |        |       |    DA    |
+        // |  ND   |        |  PG   | -----> |   D   | -----> |  PC   | <------+ |
+        // |       | <----- |       |  ~G    |       |   C    |       |        | | 
+        // +-------+   GE   +-------+        +-------+        +-------+      +------+
+        //                                       ^              ^  |         |      |
+        //                                       | DA        UE |  | ?U      | PCA  |
+        //                                       |              |  v         |      |
+        //                                   +-------+        +-------+      +------+
+        //                                   |       |   !U   |       |         ^
+        //                                   |  AD   | <----- |  PU   | --------+
+        //                                   |       |        |       |   ~U     
+        //                                   +-------+        +-------+          
+        //                                                                       
+        //
+        // LEGEND:
+        // 
+        // Plugins (i) receive state information, (ii) perform actions
+        //
+        // States:                                  Actions:
+        // ND : No data                             ?G : Get query
+        // PG : Pending Get                         !G : Get reply  
+        //  D : Data present                        ~G : Get partial reply
+        // PC : Pending changes                     GE : Get error                            
+        // PU : Pending update                       C : Change request
+        // PCA: Pending change with annotation       R : Reset request
+        // AD : Annotated data                      ?U : Update query
+        //                                          !U : Update reply
+        //                                          ~U : Update partial reply
+        //                                          UE : Update error            
+        //                                          DA : Delete annotation request
+        // NOTE:
+        // - D -> PU directly if the user chooses 'dynamic update'
+        // - Be careful for updates if partial Get success
+
+        // ND: No data == initialization state
+        
+        // PG : Pending get
+        // - on_query_in_progress
+        // NOTE: cannot distinguish get and update here. Is it important ?
+
+        on_query_in_progress: function()
+        {
+            this.spin();
+        },
+
+        on_query_done: function()
+        {
+            this.populate_table();
+            this.unspin();
+        },
+
+        // D : Data present
+        // - on_clear_records (Get)
+        // - on_new_record (shared with AD) XXX
+        // - on_query_done
+        // NOTE: cannot distinguish get and update here. Is it important ?
+        // NOTE: Would record key be sufficient for update ?
+
+        on_clear_records: function()
+        {
+            this.clear();
+        },
+
+        on_query_done: function()
+        {
+            this.unspin();
+        },
+
+        // PC : Pending changes
+        // NOTE: record_key could be sufficient 
+        on_added_record: function(record)
+        {
+            this.set_record_state(record, RECORD_STATE_ADDED);
+            // update pending number
+        },
+
+        on_removed_record: function(record_key)
+        {
+            this.set_record_state(RECORD_STATE_REMOVED);
+        },
+
+        // PU : Pending update
+        // - on_query_in_progress (already done)
+        
+        // PCA : Pending change with annotation
+        // NOTE: Manifold will inform the plugin about updates, and thus won't
+        // call new record, even if the same channel UUID is used...
+        // - TODO on_updated_record
+        // - Key and confirmation could be sufficient, or key and record state
+        // XXX move record state to the manifold plugin API
+
+        on_field_state_changed: function(data)
+        {
+            /*
+            if(result.request == FIELD_REQUEST_ADD){
+                this.selected_resources.push(result.value);
+            } else if(result.request == FIELD_REQUEST_REMOVE_RESET){
+                var i = this.selected_resources.indexOf(result.value);
+                if(i != -1){
+                    this.selected_resources.splice(i,1);
+                }
+            }
+            this.set_state(result);
+            */
+
+            var action, msg, row, status, button = '';
+
+            switch(data.state) {
+                case STATE_VALUE:
+                    switch(data.op) {
+                        // XXX other events missing !!
+                        case STATE_VALUE_CHANGE_PENDING:
+                            action = 'UPDATE';
+                            break;
+                    }
+                    break;
+
+                case STATE_SET:
+                    switch(data.op) {
+                        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
+                            row = this.find_row(data.value);
+                            if (row)
+                                this.table.fnDeleteRow(row.nTr);
+                            return;
+
+                        case STATE_SET_IN_SUCCESS:
+                            action = 'ADD';
+                            msg   = 'SUCCESS';
+                            break;
+                        case STATE_SET_IN_FAILURE:
+                            action = 'ADD';
+                            msg   = 'FAILURE';
+                            break;
+
+                        case STATE_SET_OUT_SUCCESS:
+                            action = 'REMOVE';
+                            msg   = 'SUCCESS';
+                            break;
+                        case STATE_SET_OUT_FAILURE:
+                            action = 'REMOVE';
+                            msg   = 'FAILURE';
+                            break;
+
+                    }
+                    break;
+
+                default:
+                    return;
+            }
+
+            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 ?
+
+            // 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 = [action, data.key, data.value, msg, button];
+            if (!row) {
+                // XXX second parameter refresh = false can improve performance. todo in querytable also
+                this.table.fnAddData(newline);
+                row = this.find_row(data.value);
+            } else {
+                // Update row text...
+                this.table.fnUpdate(newline, row.nTr);
+            }
+
+            // Change cell color according to status
+            if (row) {
+                $(row.nTr).removeClass('add remove')
+                var cls = action.toLowerCase();
+                if (cls)
+                    $(row.nTr).addClass(cls);
+            }
+        },
+
+        // XXX we will have the requests for change
+        // XXX + the requests to move into the query cycle = the buttons aforementioned
+
+        // XXX what happens in case of updates ? not implemented yet
+        // XXX we want resources and leases
+        // listen for SET_ADD and SET_REMOVE for slice query
+
+        /************************** PRIVATE METHODS ***************************/
+
+        _close_click: function(e)
+        {
+            var self = e.data;
+
+            //jQuery.publish('selected', 'add/'+key_value);
+            // this.parentNode is <td> this.parentNode.parentNode is <tr> 
+            // this.parentNode.parentNode.firstChild is the first cell <td> of this line <tr>
+            // this.parentNode.parentNode.firstChild.firstChild is the text in that cell
+            //var firstCellVal=this.parentNode.parentNode.firstChild.firstChild.data;
+            var remove_urn = this.id; 
+            var current_resources = event.data.instance.current_resources;
+            var list_resources = $.grep(current_resources, function(x) {return x.urn != remove_urn});
+            //jQuery.publish('selected', 'cancel/'+this.id+'/'+unfold.get_value(firstCellVal));
+            $.publish('/update-set/' + event.data.instance.options.resource_query_uuid, [list_resources, true]);
+        },
+
+        /******************************** TODO ********************************/
+
+        update_resources: function(resources, change)
+        {
+            messages.debug("update_resources");
+            var my_oTable = this.table.dataTable();
+            var prev_resources = this.current_resources; 
+
+            /*
+             * The first time the query is advertised, don't do anything.  The
+             * component will learn nodes in the slice through the manifest
+             * received through the other subscription 
+             */
+             if (!change)
+                return;
+             // ioi: Refubrished
+             var initial = this.initial;
+             //var r_removed  = []; //
+             /*-----------------------------------------------------------------------
+                TODO: remove this dirty hack !!!
+             */
+             resources = jQuery.map(resources, function(x){
+                if(!('timeslot' in x)){x.timeslot=0;}
+                return x;
+             });
+             /*
+                TODO: handle generic keys instead of specific stuff
+                      ex: urn
+                          urn-lease
+             */
+             var initial_urn = $.map(initial, function(x){return x.urn;});
+             var resources_urn = $.map(resources, function(x){return x.urn;});
+             var r_removed = $.grep(initial, function (x) { return $.inArray(x.urn, resources_urn) == -1 });
+             var r_attached = $.grep(initial, function (x) { return $.inArray(x.urn, resources_urn) > -1 });
+             var r_added = $.grep(resources, function (x) { return $.inArray(x.urn, initial_urn) == -1 });
+             exists = false; // ioi
+             /*-----------------------------------------------------------------------*/
+
+             my_oTable.fnClearTable();
+             /*
+                TODO: factorization of this code !!!
+             */
+             $.each(r_added, function(i, r) { 
+                //var type = (typeof initial == 'undefined' || r.node != initial.node) ? 'add' : 'attached';
+                var type = 'add';  
+                // Create the resource objects
+                // ioi: refubrished
+                var urn = r.urn;
+                time = r.timeslot;
+                              
+                var SPAN = "<span class='glyphicon glyphicon-remove ResourceSelectedClose' id='"+urn+"'/>";
+                var slot = "<span id='resource_"+urn+"'>" + time + "</span>"; //ioi
+                // ioi
+                var newline=Array();
+                newline.push(type, urn, slot, SPAN); // ioi
+                var line = my_oTable.fnAddData(newline);
+                var nTr = my_oTable.fnSettings().aoData[ line[0] ].nTr;
+                nTr.className = type;
+             });
+             $.each(r_attached, function(i, r) {  
+                //var type = (typeof initial == 'undefined' || r.node != initial.node) ? 'add' : 'attached';
+                var type = 'attached';
+                // Create the resource objects
+                // ioi: refubrished
+                var node = r.urn;
+                time = r.timeslot;
+
+                var SPAN = "<span class='glyphicon glyphicon-renomve ResourceSelectedClose' id='"+node+"'/>";
+                var slot = "<span id='resource_"+node+"'>" + time + "</span>"; //ioi
+                // ioi
+                var newline=Array();
+                newline.push(type, node, slot, SPAN); // ioi
+                var line = my_oTable.fnAddData(newline);
+                var nTr = my_oTable.fnSettings().aoData[ line[0] ].nTr;
+                nTr.className = type;
+             });
+             $.each(r_removed, function(i, r) { 
+                // The list contains objects
+                // ioi: refubrished
+                var node = r.urn;
+                var time = r.timeslot;
+                    
+                var SPAN = "<span class='glyphicon glyphicon-remove ResourceSelectedClose' id='"+node+"'/>";
+                var slot = "<span id='resource_"+node+"'>" + time + "</span>";
+                // ioi
+                var newline=Array();
+                newline.push('remove', node, slot, SPAN); // ioi
+                var line = my_oTable.fnAddData(newline);
+                var nTr = my_oTable.fnSettings().aoData[ line[0] ].nTr;
+                nTr.className = 'remove';
+             });
+
+             this.current_resources = $.merge(r_attached,r_added);
+
+             /* Allow the user to update the slice */
+             //jQuery('#updateslice-' + data.ResourceSelected.plugin_uuid).prop('disabled', false);
+
+        }, // update_resources
+
+    });
+
+    $.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..6507012
--- /dev/null
@@ -0,0 +1,41 @@
+<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">Applying pending changes...</h4>
+        </div>
+        <div class="modal-body">
+<div class='resources-selected-spacer'>
+<table class='resources-selected' id='{{domid}}__table'>
+  <thead class="resources-selected">
+    <tr> <th>action</th> <th>key</th> <th>value</th> <th>status</th> </tr>
+  </thead>
+  <tbody></tbody>
+<!--<tfoot class="resources-selected">
+<tr> <th>action</th> <th>key</th> <th>value</th> <th>status</th></tr>
+</tfoot>-->
+</table>
+</div>
+<!--not all buttons are expected to work for now, hence the disabled mark-->
+<!--
+<button type="button" id="{{domid}}__update"  class="btn btn-onelab">Update</button>
+<button type="button" id="{{domid}}__refresh"  class="btn btn-primary" disabled>Refresh</button>
+<button type="button" id="{{domid}}__reset"  class="btn btn-danger" disabled>Reset</button>
+<button type="button" id="{{domid}}__clear_annotations"  class="btn btn-danger" disabled>Clear Annotations</button>
+-->
+        </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-onelab btn-sm" data-toggle="modal" data-target="#{{domid}}__apply">Apply</button>
+  <button class="btn btn-default 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..3bcd15c
--- /dev/null
@@ -0,0 +1,138 @@
+/**
+ * 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;
+
+            /* Initialize tooltips */
+            $("[rel='tooltip']").tooltip();
+
+        },
+
+    /**************************************************************************
+     *                            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
+    },
+
+    on_field_state_changed: function(data) 
+    {
+        var query_ext;
+        
+        switch (data.state) {
+            case STATE_SET:
+            case STATE_WARNINGS:
+                /* Get the number of pending / unconfigured resources */
+                /* Let's store it in query_ext */
+                query_ext = manifold.query_store.find_analyzed_query_ext(this.options.query_uuid);
+
+                $('#badge-pending').text(query_ext.num_pending);
+                if (query_ext.num_pending > 0) {
+                                       $('#badge-pending').show();
+                } else {
+                                       $('#badge-pending').hide();
+                }
+
+                $('#badge-unconfigured').text(query_ext.num_unconfigured);
+                if (query_ext.num_unconfigured > 0) {
+                                       $('#badge-unconfigured').show();
+                } else {
+                                       $('#badge-unconfigured').hide();
+                }
+            default:
+                break;
+        }
+    },
+
+    /**************************************************************************
+     *                            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..498c149
--- /dev/null
@@ -0,0 +1,43 @@
+<div id={{ domid }}>
+  <span class="list-group-item-heading sl-resources">View:</span>
+  
+  <a href="#" 
+     class="list-group-item sl-resources active" 
+     style='display: inline-block !important;' 
+     id="{{ domid }}__all" 
+     data-status="all"
+        title="View resources that are available to be reserved."
+        rel='tooltip'>
+       Available
+  </a>
+  
+  <a href="#"
+     class="list-group-item sl-resources" 
+     style='display: inline-block !important;' 
+     id="{{ domid }}__reserved" 
+     data-status="reserved"
+     title="View resources that you have previously reserved for the slice."
+        rel='tooltip'>
+       Reserved
+  </a>
+  
+  <a href="#" class="list-group-item sl-resources"
+     style='display: inline-block !important;' 
+     id="{{ domid }}__unconfigured" 
+     data-status="unconfigured"
+     title="View resources that you have selected to add to your slice, that require configuration before they can be reserved. Hover you mouse over the symbol next to the checkbox for more details."
+     rel='tooltip'>
+       Unconfigured
+       <span class="badge" id="badge-unconfigured" style="display:none;"></span></a>
+  </a>
+  
+  <a href="#" class="list-group-item sl-resources" 
+     style='display: inline-block !important;' 
+     id="{{ domid }}__pending" 
+     data-status="pending"
+     title="View pending changes to your slice: resources that you have selected to add, and resources that you have selected to remove. Click on the Apply button to apply those changes, or on the Cancel button to cancel them."
+     rel='tooltip'>
+       Pending
+       <span class="badge" id="badge-pending" style="display:none;"></span></a>
+  </a>
+</div> 
index a769f7f..08f2a7b 100644 (file)
@@ -8,12 +8,10 @@ class GoogleMap (Plugin):
     # googlemap_key :   mandatory googlemap API v3 key
     # latitude,longitude, zoom : the starting point
     # apparently at some point there has been support for a boolean 'checkboxes' input arg but seems dropped
-    def __init__ (self, query, query_all, googlemap_api_key=None, latitude=43., longitude=7., zoom=4, **settings):
+    def __init__ (self, query, googlemap_api_key=None, latitude=43., longitude=7., zoom=4, **settings):
         Plugin.__init__ (self, **settings)
         self.query=query
-        self.query_all = query_all
         self.googlemap_api_key=googlemap_api_key
-        self.query_all_uuid = query_all.query_uuid if query_all else None
         self.latitude=latitude
         self.longitude=longitude
         self.zoom=zoom
@@ -45,7 +43,7 @@ class GoogleMap (Plugin):
 
     # the list of things passed to the js plugin
     def json_settings_list (self): 
-        return [ 'plugin_uuid', 'query_uuid', 'query_all_uuid',
+        return [ 'plugin_uuid', 'query_uuid',
                  'init_key',
                  'latitude', 'longitude', 'zoom', 
                  ]
index 8a740b2..5a15a3d 100644 (file)
@@ -8,6 +8,10 @@
  * - infowindow is not properly reopened when the maps does not have the focus
  */
 
+GOOGLEMAP_BGCOLOR_RESET   = 0;
+GOOGLEMAP_BGCOLOR_ADDED   = 1;
+GOOGLEMAP_BGCOLOR_REMOVED = 2;
+
 (function($){
 
     // events that happen in the once-per-view range
 
     var GoogleMap = Plugin.extend({
 
-        init: function(options, element) {
-           this.classname="googlemap";
+        /**************************************************************************
+         *                          CONSTRUCTOR
+         **************************************************************************/
+
+        init: function(options, element) 
+        {
             this._super(options, element);
 
             /* Member variables */
-            // query status
-            this.received_all = false;
-            this.received_set = false;
-            this.in_set_backlog = [];
-
-            // we keep a couple of global hashes
-           // lat_lon --> { marker, <ul> }
-           // id --> { <li>, <input> }
-           this.by_lat_lon = {};
-           // locating checkboxes by DOM selectors might be abstruse, as we cannot safely assume 
-           // all the items will belong under the toplevel <div>
-           this.by_id = {};
-           this.by_init_id = {};
+
+            /* we keep a couple of global hashes
+             * lat_lon --> { marker, <ul> }
+             * id --> { <li>, <input> }
+             */
+            this.by_lat_lon = {};
+            /* locating checkboxes by DOM selectors might be abstruse, as we cannot safely assume 
+             * all the items will belong under the toplevel <div>
+             */
+            this.by_id = {};
+            this.by_init_id = {};
+
+            this.markers = Array();
 
             /* Events */
-           // xx somehow non of these triggers at all for now
             this.elmt().on('show', this, this.on_show);
             this.elmt().on('shown.bs.tab', this, this.on_show);
             this.elmt().on('resize', this, this.on_resize);
             var query = manifold.query_store.find_analyzed_query(this.options.query_uuid);
             this.object = query.object;
 
-           // see querytable.js for an explanation
-           var keys = manifold.metadata.get_key(this.object);
-           this.canonical_key = (keys && keys.length == 1) ? keys[0] : undefined;
-           // 
-           this.init_key = this.options.init_key;
-           // have init_key default to canonical_key
-           this.init_key = this.init_key || this.canonical_key;
-           // sanity check
-           if ( ! this.init_key ) messages.warning ("QueryTable : cannot find init_key");
-           if ( ! this.canonical_key ) messages.warning ("QueryTable : cannot find canonical_key");
-           if (debug) messages.debug("googlemap: canonical_key="+this.canonical_key+" init_key="+this.init_key);
-
-            //// Setup query and record handlers 
-           // this query is the one about the slice itself 
-           // event related to this query will trigger callbacks like on_new_record
+            /* see querytable.js for an explanation */
+            var keys = manifold.metadata.get_key(this.object);
+            this.canonical_key = (keys && keys.length == 1) ? keys[0] : undefined;
+            this.init_key = this.options.init_key;
+            this.init_key = this.init_key || this.canonical_key;
+
+            /* sanity check */
+            if ( ! this.init_key ) 
+                messages.warning ("QueryTable : cannot find init_key");
+            if ( ! this.canonical_key )
+                messages.warning ("QueryTable : cannot find canonical_key");
+            if (debug)
+                messages.debug("googlemap: canonical_key="+this.canonical_key+" init_key="+this.init_key);
+
             this.listen_query(options.query_uuid);
-           // this one is the complete list of resources
-           // and will be bound to callbacks like on_all_new_record
-            this.listen_query(options.query_all_uuid, 'all');
 
             /* GUI setup and event binding */
             this.initialize_map();
+
         }, // init
 
-        /* PLUGIN EVENTS */
+        /**************************************************************************
+         *                         PLUGIN EVENTS
+         **************************************************************************/
 
         on_show: function(e) {
                if (debug) messages.debug("googlemap.on_show");
             var googlemap = e.data;
             google.maps.event.trigger(googlemap.map, 'resize');
         }, 
-       // dummy to see if this triggers at all
-        on_resize: function(e) {
-           if (debug) messages.debug("googlemap.on_resize ...");
-        }, 
 
-        /* GUI EVENTS */
+        /**************************************************************************
+         *                        GUI MANIPULATION
+         **************************************************************************/
 
-        /* GUI MANIPULATION */
-
-        initialize_map: function() {
+        initialize_map: function() 
+        {
             this.markerCluster = null;
             //create empty LatLngBounds object in order to automatically center the map on the displayed objects
             this.bounds = new google.maps.LatLngBounds();
             var center = new google.maps.LatLng(this.options.latitude, this.options.longitude);
 
-            console.log("GoogleMap zoom = "+this.options.zoom);
             var myOptions = {
                 zoom: this.options.zoom,
                 center: center,
                 mapTypeId: google.maps.MapTypeId.ROADMAP,
             }
            
-            var domid = this.options.plugin_uuid + '--' + 'googlemap';
-           var elmt = document.getElementById(domid);
+            var domid = this.id('googlemap');
+               var elmt = document.getElementById(domid);
             this.map = new google.maps.Map(elmt, myOptions);
             this.infowindow = new google.maps.InfoWindow();
+
         }, // initialize_map
 
-       // return { marker: gmap_marker, ul : <ul DOM> }
-       create_marker_struct: function (object,lat,lon) {
-           // the DOM fragment
-           var dom = $("<p>").addClass("geo").append(object+"(s)");
-           var ul = $("<ul>").addClass("geo");
-           dom.append(ul);
-           // add a gmap marker to the mix
-           var marker = new google.maps.Marker({
-               position: new google.maps.LatLng(lat, lon),
+        // return { marker: gmap_marker, ul : <ul DOM> }
+        create_marker_struct: function(object, lat, lon) 
+        {
+            /* the DOM fragment */
+            var dom = $("<p>").addClass("geo").append(object+"(s)");
+            var ul = $("<ul>").addClass("geo");
+            dom.append(ul);
+            /* add a gmap marker to the mix */
+            var marker = new google.maps.Marker({
+                position: new google.maps.LatLng(lat, lon),
                 title: object,
-               // gmap can deal with a DOM element but not a jquery object
+                /* gmap can deal with a DOM element but not a jquery object */
                 content: dom.get(0),
-        }); 
-        //extend the bounds to include each marker's position
-        this.bounds.extend(marker.position);
-           return {marker:marker, ul:ul};
-       },
-
-       // given an input <ul> element, this method inserts a <li> with embedded checkbox 
-       // for displaying/selecting the resource corresponding to the input record
-       // returns the created <input> element for further checkbox manipulation
-       create_record_checkbox: function (record,ul,checked) {
-           var checkbox = $("<input>", {type:'checkbox', checked:checked, class:'geo'});
-           var id=record[this.canonical_key];
-           var init_id=record[this.init_key];
-           // xxx use init_key to find out label - or should we explicitly accept an incoming label_key ?
-           var label=init_id;
-           ul.append($("<li>").addClass("geo").append(checkbox).
-                     append($("<span>").addClass("geo").append(label)));
-           // hash by id and by init_id 
-           this.by_id[id]=checkbox;
+                keys: Array(),
+            }); 
+            //extend the bounds to include each marker's position
+            this.bounds.extend(marker.position);
+            return { marker: marker, ul: ul };
+        },
+
+        /* given an input <ul> element, this method inserts a <li> with embedded checkbox 
+         * for displaying/selecting the resource corresponding to the input record
+         * returns the created <input> element for further checkbox manipulation
+         */
+        create_record_checkbox: function (record, ul, checked)
+        {
+            var key, key_value, data;
+
+            var checkbox = $("<input>", {type:'checkbox', checked:checked, class:'geo'});
+            var id = record[this.canonical_key];
+            var init_id = record[this.init_key];
+
+            // xxx use init_key to find out label - or should we explicitly accept an incoming label_key ?
+            var label = init_id;
+
+            key = this.canonical_key;
+            key_value = manifold.record_get_value(record, key);
+
+            ul.append($("<li>").addClass("geo")
+              .append($('<div>') // .addId(this.id_from_key(key, key_value))
+                .append(checkbox)
+                .append($("<span>").addClass("geo")
+                  .append(label)
+                )
+              )
+            );
+
+            // XXX STATE / BACKGROUND
+
+            // hash by id and by init_id 
+            this.by_id[id]=checkbox;
             this.by_init_id[init_id] = checkbox;
-           //
-           // the callback for when a user clicks
-           // NOTE: this will *not* be called for changes done by program
-           var self=this;
-           checkbox.change( function (e) {
-               manifold.raise_event (self.options.query_uuid, this.checked ? SET_ADD : SET_REMOVED, id);
-           });
-           return checkbox;
-       },
-           
-       warning: function (record,message) {
-           try {messages.warning (message+" -- "+this.key+"="+record[this.key]); }
-           catch (err) {messages.warning (message); }
-       },
-           
-       // retrieve DOM checkbox and make sure it is checked/unchecked
-       set_checkbox_from_record: function(record, checked) {
-           var init_id=record[this.init_key];
-           var checkbox = this.by_init_id [ init_id ];
-           if (checkbox) checkbox.prop('checked',checked);
-           else this.warning(record, "googlemap.set_checkbox_from_record - not found "+init_id);
-       }, 
-
-       set_checkbox_from_data: function(id, checked) {
-           var checkbox = this.by_id [ id ];
-           if (checkbox) checkbox.prop('checked',checked);
-           else messages.warning("googlemap.set_checkbox_from_data - id not found "+id);
-       }, 
-
-       // this record is *in* the slice
-        new_record: function(record) {
-           if (debug_deep) messages.debug ("googlemap.new_record");
-            if (!(record['latitude'])) return false;
-           
-            // get the coordinates
-            var latitude=unfold.get_value(record['latitude']);
-            var longitude=unfold.get_value(record['longitude']);
+
+            /* the callback for when a user clicks
+             * NOTE: this will *not* be called for changes done by program
+             */
+            var self=this;
+            checkbox.change( function (e) {
+                data = {
+                    state: STATE_SET,
+                    key  : null,
+                    op   : this.checked ? SET_ADD : SET_REMOVED,
+                    value: id
+                }
+                manifold.raise_event(self.options.query_uuid, FIELD_STATE_CHANGED, data);
+            });
+            return checkbox;
+        },
+            
+        set_checkbox_from_record_key: function (record_key, checked) 
+        {
+            if (checked === undefined) checked = true;
+
+            var checkbox = this.by_init_id [record_key];
+            if (!checkbox) {
+                console.log("googlemap.set_checkbox_from_record - not found " + record_key);
+                return;
+            }
+
+            checkbox.attr('checked', checked);
+        },
+
+
+        set_checkbox_from_data: function(id, checked) 
+        {
+            var checkbox = this.by_id[id];
+            if (!checkbox) {
+                console.log("googlemap.set_checkbox_from_data - id not found " + id);
+                return;
+            }
+            checkbox.attr('checked', checked);
+        }, 
+
+        set_bgcolor: function(key_value, class_name)
+        {
+            var elt = $(document.getElementById(this.id_from_key(this.canonical_key, key_value)))
+            if (class_name == GOOGLEMAP_BGCOLOR_RESET)
+                elt.removeClass('added removed');
+            else
+                elt.addClass((class_name == GOOGLEMAP_BGCOLOR_ADDED ? 'added' : 'removed'));
+        },
+
+
+        /**
+         * Populates both this.by_lat_lon and this.arm_marker arrays
+         */
+        new_record: function(record) 
+        {
+            var record_key;
+
+            if (!(record['latitude'])) 
+                return;
+
+            /* get the coordinates*/
+            var latitude  = unfold.get_value(record['latitude']);
+            var longitude = unfold.get_value(record['longitude']);
             var lat_lon = latitude + longitude;
 
            // check if we've seen anything at that place already
            // xxx might make sense to allow for some fuzziness, 
            // i.e. consider 2 places equal if not further away than 300m or so...
-           var marker_s = this.by_lat_lon [lat_lon];
+           var marker_s = this.by_lat_lon[lat_lon];
            if ( marker_s == null ) {
-               marker_s = this.create_marker_struct (this.object, latitude, longitude);
-               this.by_lat_lon [ lat_lon ] = marker_s;
-               this.arm_marker(marker_s.marker, this.map);
-           }
+                   marker_s = this.create_marker_struct(this.object, latitude, longitude);
+                   this.by_lat_lon[lat_lon] = marker_s;
+                   this.arm_marker(marker_s.marker, this.map);
+               }
+
+            /* Add key to the marker */
+            record_key = manifold.record_get_value(record, this.canonical_key);
+            marker_s.marker.keys.push(record_key);
            
            // now add a line for this resource in the marker
            // xxx should compute checked here ?
            // this is where the checkbox will be appended
-           var ul=marker_s.ul;
-           var checkbox = this.create_record_checkbox (record, ul, false);
+           var ul = marker_s.ul;
+           var checkbox = this.create_record_checkbox(record, ul, false);
         }, // new_record
 
-        arm_marker: function(marker, map) {
-           if (debug_deep) messages.debug ("arm_marker content="+marker.content);
-            var googlemap = this;
+        arm_marker: function(marker, map)
+        {
+            var self = this;
             google.maps.event.addListener(marker, 'click', function () {
-                googlemap.infowindow.close();
-                googlemap.infowindow.setContent(marker.content);
-                googlemap.infowindow.open(map, marker);
+                self.infowindow.close();
+                self.infowindow.setContent(marker.content);
+                self.infowindow.open(map, marker);
             });
         }, // arm_marker
 
-        /*************************** QUERY HANDLER ****************************/
-
-        /*************************** RECORD HANDLER ***************************/
-        on_new_record: function(record) {
-           if (debug_deep) messages.debug("googlemap.on_new_record");
-            if (this.received_all)
-                // update checkbox for record
-                this.set_checkbox_from_record(record, true);
-            else
-                // store for later update of checkboxes
-                this.in_set_backlog.push(record);
-        },
-
-        on_clear_records: function(record) {
-           if (debug_deep) messages.debug("googlemap.on_clear_records");
-        },
-
-        // Could be the default in parent
-        on_query_in_progress: function() {
-           if (debug) messages.debug("googlemap.on_query_in_progress (spinning)");
-            this.spin();
-        },
-
-        on_query_done: function() {
-               if (debug) messages.debug("googlemap.on_query_done");       
-            if (this.received_all) {
-                this.unspin();
-               }
-            this.received_set = true;
-        },
-
-        on_field_state_changed: function(data) {
-           if (debug_deep) messages.debug("googlemap.on_field_state_changed");     
-            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:
-                break;
-            }
-        },
-
-
-        // all : this 
-
-        on_all_new_record: function(record) {
-           if (debug_deep) messages.debug("googlemap.on_all_new_record");
-            this.new_record(record);
+        clear_map: function()
+        {
+            /* XXX */
         },
 
-        on_all_clear_records: function() {
-           if (debug) messages.debug("googlemap.on_all_clear_records");            
-        },
+        filter_map: function()
+        {
+            var self = this;
+            var visible;
+
+            /* Loop on every marker and sets it visible */
+            $.each(this.markers, function(i, marker) {
+                /* For a marker to be visible, at least one of its keys has to
+                 * be visible */
+                visible = false;
+                $.each(marker.keys, function(j, key) {
+                    visible = visible || manifold.query_store.get_record_state(self.options.query_uuid, key, STATE_VISIBLE);
+                });
+                marker.setVisible(visible);
+            });
 
-        on_all_query_in_progress: function() {
-           if (debug) messages.debug("googlemap.on_all_query_in_progress (spinning)");
-            // XXX parent
-            this.spin();
+            this.do_clustering();
         },
 
-        on_all_query_done: function() {
-           if (debug) messages.debug("googlemap.on_all_query_done");
-
-            // MarkerClusterer
-            var markers = [];
-            $.each(this.by_lat_lon, function (k, s) { markers.push(s.marker); });
-            this.markerCluster = new MarkerClusterer(this.map, markers, {zoomOnClick: false});
+        do_clustering: function()
+        {
+            this.markerCluster = new MarkerClusterer(this.map, this.markers, {zoomOnClick: false});
+            this.markerCluster.setIgnoreHidden(true);
             google.maps.event.addListener(this.markerCluster, "clusterclick", function (cluster) {
                 var cluster_markers = cluster.getMarkers();
                 var bounds  = new google.maps.LatLngBounds();
             //now fit the map to the bounds
             this.map.fitBounds(this.bounds);
             // Fix the zoom of fitBounds function, it's too close when there is only 1 marker
-            if(markers.length==1){
+            if (this.markers.length==1) {
                 this.map.setZoom(this.map.getZoom()-4);
             }
-            var googlemap = this;
-            if (this.received_set) {
-                /* ... and check the ones specified in the resource list */
-                $.each(this.in_set_backlog, function(i, record) {
-                    googlemap.set_checkbox_from_record(record, true);
-                });
-               // reset 
-               googlemap.in_set_backlog = [];
-                this.unspin();
+        },
+
+        redraw_map: function()
+        {
+            // Let's clear the table and only add lines that are visible
+            var self = this;
+            this.clear_map();
+
+            /* Add records to internal hash structure */
+            var record_keys = [];
+            manifold.query_store.iter_records(this.options.query_uuid, function (record_key, record) {
+                self.new_record(record);
+                record_keys.push(record_key);
+            });
+
+            /* Add markers to cluster */
+            this.markers = Array();
+            $.each(this.by_lat_lon, function (k, s) {
+                self.markers.push(s.marker); 
+            });
+
+            this.do_clustering();
+
+
+            /* Set checkbox and background color */
+            $.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:
+                        break;
+                    case STATE_SET_IN_PENDING:
+                        self.set_checkbox_from_record_key(record_key, true);
+                        self.set_bgcolor(record_key, GOOGLEMAP_BGCOLOR_ADDED);
+                        break;
+                    case STATE_SET_OUT_PENDING:
+                        self.set_bgcolor(record_key, GOOGLEMAP_BGCOLOR_REMOVED);
+                        break;
+                }
+                //self.change_status(record_key, warnings); // XXX will retrieve status again
+            });
+        },
+
+       /**************************************************************************
+        *                           QUERY HANDLERS
+        **************************************************************************/ 
+
+        on_filter_added: function(filter)
+        {
+            this.filter_map();
+        },
+
+        on_filter_removed: function(filter)
+        {
+            this.filter_map();
+        },
+        
+        on_filter_clear: function()
+        {
+            this.filter_map();
+        },
+
+        on_query_in_progress: function() 
+        {
+            this.spin();
+        },
+
+        on_query_done: function()
+        {
+            this.redraw_map();
+            this.unspin();
+        },
+
+        on_field_state_changed: function(data)
+        {
+            switch(data.state) {
+                case STATE_SET:
+                    switch(data.value) {
+                        case STATE_SET_IN:
+                        case STATE_SET_IN_SUCCESS:
+                        case STATE_SET_OUT_FAILURE:
+                            this.set_checkbox_from_data(data.key, true);
+                            this.set_bgcolor(data.key, QUERYTABLE_BGCOLOR_RESET);
+                            break;  
+                        case STATE_SET_OUT:
+                        case STATE_SET_OUT_SUCCESS:
+                        case STATE_SET_IN_FAILURE:
+                            this.set_checkbox_from_data(data.key, false);
+                            this.set_bgcolor(data.key, QUERYTABLE_BGCOLOR_RESET);
+                            break;
+                        case STATE_SET_IN_PENDING:
+                            this.set_checkbox_from_data(data.key, true);
+                            this.set_bgcolor(data.key, QUERYTABLE_BGCOLOR_ADDED);
+                            break;  
+                        case STATE_SET_OUT_PENDING:
+                            this.set_checkbox_from_data(data.key, false);
+                            this.set_bgcolor(data.key, QUERYTABLE_BGCOLOR_REMOVED);
+                            break;
+                    }
+                    break;
+
+                case STATE_WARNINGS:
+                    //this.change_status(data.key, data.value);
+                    break;
             }
-            this.received_all = true;
+        },
 
-        } // on_all_query_done
     });
-        /************************** PRIVATE METHODS ***************************/
 
     $.plugin('GoogleMap', GoogleMap);
 
index 15b82fb..e487ea1 100644 (file)
@@ -1 +1 @@
-<div id="{{domid}}--googlemap" class="geo"></div>
+<div id="{{domid}}__googlemap" class="geo"></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..2182e70 100644 (file)
@@ -33,6 +33,12 @@ Current implementation makes the following assumptions
   as we use 'aoColumnDefs' instead.
 """
 
+    MAP = {
+        'network_hrn'   :   'Testbed',
+        'hostname'      :   'Resource name',
+        'type'          :   'Type',
+    }
+
     def __init__ (self, query=None, query_all=None, 
                   checkboxes=False, columns=None, 
                   init_key=None,
@@ -43,20 +49,28 @@ Current implementation makes the following assumptions
         self.query_all      = query_all
         self.query_all_uuid = query_all.query_uuid if query_all else None
         self.checkboxes     = checkboxes
+
         # XXX We need to have some hidden columns until we properly handle dynamic queries
         if columns is not None:
-            self.columns=columns
-            self.hidden_columns = []
+            _columns = columns
+            _hidden_columns = []
         elif self.query:
-            self.columns = self.query.fields
+            _columns = [field for field in self.query.fields if not field == 'urn']
             if query_all:
                 # We need a list because sets are not JSON-serializable
-                self.hidden_columns = list(self.query_all.fields - self.query.fields)
+                _hidden_columns = list(self.query_all.fields - self.query.fields)
+                _hidden_columns.append('urn')
             else:
-                self.hidden_columns = []
+                _hidden_columns = []
         else:
-            self.columns = []
-            self.hidden_columns = []
+            _columns = []
+            _hidden_columns = []
+
+        print "_columns=", _columns
+        self.columns = { self.MAP.get(c, c) : c for c in _columns }
+        self.hidden_columns = { self.MAP.get(c, c) : c for c in _hidden_columns }
+        print "self.columns", self.columns
+
         self.init_key=init_key
         self.datatables_options=datatables_options
         # if checkboxes were required, we tell datatables about this column's type
@@ -96,7 +110,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..6879321 100644 (file)
@@ -1,61 +1,61 @@
-/* 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;
+tr.even {
+    background-color: #FAFAFA;
+}
+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 97fa4b4..4b9e546 100644 (file)
@@ -4,63 +4,61 @@
  * License: GPLv3
  */
 
+QUERYTABLE_BGCOLOR_RESET   = 0;
+QUERYTABLE_BGCOLOR_ADDED   = 1;
+QUERYTABLE_BGCOLOR_REMOVED = 2;
+
 (function($){
 
+    
+    var QUERYTABLE_MAP = {
+        'Testbed': 'network_hrn',
+        'Resource name': 'hostname',
+        'Type': 'type',
+    };
+
     var debug=false;
 //    debug=true
 
     var QueryTable = Plugin.extend({
 
         init: function(options, element) {
-           this.classname="querytable";
+        this.classname="querytable";
             this._super(options, element);
 
             /* Member variables */
-           // in general we expect 2 queries here
-           // query_uuid refers to a single object (typically a slice)
-           // query_all_uuid refers to a list (typically resources or users)
-           // these can return in any order so we keep track of which has been received yet
-            this.received_all_query = false;
-            this.received_query = false;
-
-            // We need to remember the active filter for datatables filtering
-            this.filters = Array(); 
-
-            // an internal buffer for records that are 'in' and thus need to be checked 
-            this.buffered_records_to_check = [];
-           // an internal buffer for keeping lines and display them in one call to fnAddData
-           this.buffered_lines = [];
+            var query = manifold.query_store.find_analyzed_query(this.options.query_uuid);
+            this.object = query.object; // XXX
 
             /* Events */
-           // xx somehow non of these triggers at all for now
             this.elmt().on('show', this, this.on_show);
             this.elmt().on('shown.bs.tab', this, this.on_show);
             this.elmt().on('resize', this, this.on_resize);
 
-            var query = manifold.query_store.find_analyzed_query(this.options.query_uuid);
-            this.object = query.object;
-
-           //// we need 2 different keys
-           // * canonical_key is the primary key as derived from metadata (typically: urn)
-           //   and is used to communicate about a given record with the other plugins
-           // * init_key is a key that both kinds of records 
-           //   (i.e. records returned by both queries) must have (typically: hrn or hostname)
-           //   in general query_all will return well populated records, but query
-           //   returns records with only the fields displayed on startup
-           var keys = manifold.metadata.get_key(this.object);
-           this.canonical_key = (keys && keys.length == 1) ? keys[0] : undefined;
-           // 
-           this.init_key = this.options.init_key;
-           // have init_key default to canonical_key
-           this.init_key = this.init_key || this.canonical_key;
-           // sanity check
-           if ( ! this.init_key ) messages.warning ("QueryTable : cannot find init_key");
-           if ( ! this.canonical_key ) messages.warning ("QueryTable : cannot find canonical_key");
-           if (debug) messages.debug("querytable: canonical_key="+this.canonical_key+" init_key="+this.init_key);
+            //// we need 2 different keys
+            // * canonical_key is the primary key as derived from metadata (typically: urn)
+            //   and is used to communicate about a given record with the other plugins
+            // * init_key is a key that both kinds of records 
+            //   (i.e. records returned by both queries) must have (typically: hrn or hostname)
+            //   in general query_all will return well populated records, but query
+            //   returns records with only the fields displayed on startup
+            var keys = manifold.metadata.get_key(this.object);
+            this.canonical_key = (keys && keys.length == 1) ? keys[0] : undefined;
+            // 
+            this.init_key = this.options.init_key;
+            // have init_key default to canonical_key
+            this.init_key = this.init_key || this.canonical_key;
+
+            /* sanity check */
+            if ( ! this.init_key ) 
+                messages.warning ("QueryTable : cannot find init_key");
+            if ( ! this.canonical_key ) 
+                messages.warning ("QueryTable : cannot find canonical_key");
+            if (debug)
+                messages.debug("querytable: canonical_key="+this.canonical_key+" init_key="+this.init_key);
 
             /* Setup query and record handlers */
             this.listen_query(options.query_uuid);
-            this.listen_query(options.query_all_uuid, 'all');
 
             /* GUI setup and event binding */
             this.initialize_table();
         /* PLUGIN EVENTS */
 
         on_show: function(e) {
-           if (debug) messages.debug("querytable.on_show");
+        if (debug) messages.debug("querytable.on_show");
             var self = e.data;
             self.table.fnAdjustColumnSizing();
-       },        
+    },        
 
         on_resize: function(e) {
-           if (debug) messages.debug("querytable.on_resize");
+        if (debug) messages.debug("querytable.on_resize");
             var self = e.data;
             self.table.fnAdjustColumnSizing();
-       },        
+    },        
 
         /* GUI EVENTS */
 
                 // we use a fluid row on top and another on the bottom, making sure we take 12 grid elt's each time
                 //sDom: "<'row'<'col-xs-5'l><'col-xs-1'r><'col-xs-6'f>>t<'row'<'col-xs-5'i><'col-xs-7'p>>",
                 sDom: "<'row'<'col-xs-5'f><'col-xs-1'r><'col-xs-6 columns_selector'>>t<'row'<'col-xs-5'l><'col-xs-7'p>>",
-               // XXX as of sept. 2013, I cannot locate a bootstrap3-friendly mode for now
-               // hopefully this would come with dataTables v1.10 ?
-               // in any case, search for 'sPaginationType' all over the code for more comments
+        // XXX as of sept. 2013, I cannot locate a bootstrap3-friendly mode for now
+        // hopefully this would come with dataTables v1.10 ?
+        // in any case, search for 'sPaginationType' all over the code for more comments
                 sPaginationType: 'bootstrap',
                 // Handle the null values & the error : Datatables warning Requested unknown parameter
                 // http://datatables.net/forums/discussion/5331/datatables-warning-...-requested-unknown-parameter/p2
-                aoColumnDefs: [{sDefaultContent: '',aTargets: [ '_all' ]}],
+                aoColumnDefs: [{sDefaultContent: '', aTargets: [ '_all' ]}],
                 // WARNING: this one causes tables in a 'tabs' that are not exposed at the time this is run to show up empty
                 // sScrollX: '100%',       /* Horizontal scrolling */
                 bProcessing: true,      /* Loading */
-                fnDrawCallback: function() { self._querytable_draw_callback.call(self); }
+                fnDrawCallback: function() { self._querytable_draw_callback.call(self); },
+                fnRowCallback: function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
+                    // This function is called on fnAddData to set the TR id. What about fnUpdate ?
+
+                    // Get the key from the raw data array aData
+                    var key = self.canonical_key;
+
+                    // Get the index of the key in the columns
+                    var cols = self._get_columns();
+                    var index = self.getColIndex(key, cols);
+                    if (index != -1) {
+                        // The key is found in the table, set the TR id after the data
+                        $(nRow).attr('id', self.id_from_key(key, aData[index]));
+                    }
+
+                    // That's the usual return value
+                    return nRow;
+                }
                 // XXX use $.proxy here !
             };
             // the intention here is that options.datatables_options as coming from the python object take precedence
-           // xxx DISABLED by jordan: was causing errors in datatables.js
-           // xxx turned back on by Thierry - this is the code that takes python-provided options into account
-           // check your datatables_options tag instead 
-           // however, we have to accumulate in aoColumnDefs from here (above) 
-           // and from the python wrapper (checkboxes management, plus any user-provided aoColumnDefs)
-           if ( 'aoColumnDefs' in this.options.datatables_options) {
-               actual_options['aoColumnDefs']=this.options.datatables_options['aoColumnDefs'].concat(actual_options['aoColumnDefs']);
-               delete this.options.datatables_options['aoColumnDefs'];
-           }
-           $.extend(actual_options, this.options.datatables_options );
+        // xxx DISABLED by jordan: was causing errors in datatables.js
+        // xxx turned back on by Thierry - this is the code that takes python-provided options into account
+        // check your datatables_options tag instead 
+        // however, we have to accumulate in aoColumnDefs from here (above) 
+        // and from the python wrapper (checkboxes management, plus any user-provided aoColumnDefs)
+        if ( 'aoColumnDefs' in this.options.datatables_options) {
+        actual_options['aoColumnDefs']=this.options.datatables_options['aoColumnDefs'].concat(actual_options['aoColumnDefs']);
+        delete this.options.datatables_options['aoColumnDefs'];
+        }
+        $.extend(actual_options, this.options.datatables_options );
             this.table = this.elmt('table').dataTable(actual_options);
 
             /* Setup the SelectAll button in the dataTable header */
 
             /* Processing hidden_columns */
             $.each(this.options.hidden_columns, function(i, field) {
-                //manifold.raise_event(self.options.query_all_uuid, FIELD_REMOVED, field);
                 self.hide_column(field);
             });
             $(".dataTables_filter").append("<div style='display:inline-block;height:27px;width:27px;padding-left:6px;padding-top:4px;'><span class='glyphicon glyphicon-search'></span></div>");
          * @param cols
          */
         getColIndex: function(key, cols) {
-            var tabIndex = $.map(cols, function(x, i) { if (x.sTitle == key) return i; });
+            var self = this;
+            var tabIndex = $.map(cols, function(x, i) { if (self._get_map(x.sTitle) == key) return i; });
             return (tabIndex.length > 0) ? tabIndex[0] : -1;
         }, // getColIndex
 
-       // create a checkbox <input> tag
-       // computes 'id' attribute from canonical_key
-       // computes 'init_id' from init_key for initialization phase
-       // no need to used convoluted ids with plugin-uuid or others, since
-       // we search using table.$ which looks only in this table
+    // create a checkbox <input> tag
+    // computes 'id' attribute from canonical_key
+    // computes 'init_id' from init_key for initialization phase
+    // no need to used convoluted ids with plugin-uuid or others, since
+    // we search using table.$ which looks only in this table
         checkbox_html : function (record) {
             var result="";
             // Prefix id with plugin_uuid
             result += "<input";
             result += " class='querytable-checkbox'";
-        // compute id from canonical_key
-           var id = record[this.canonical_key]
-        // compute init_id form init_key
-           var init_id=record[this.init_key];
-        // set id - for retrieving from an id, or for posting events upon user's clicks
-           result += " id='"+record[this.canonical_key]+"'";
-        // set init_id
-           result += "init_id='" + init_id + "'";
-        // wrap up
+     // compute id from canonical_key
+        var id = record[this.canonical_key]
+     // compute init_id form init_key
+        var init_id=record[this.init_key];
+     // set id - for retrieving from an id, or for posting events upon user's clicks
+        result += " id='"+record[this.canonical_key]+"'";
+     // set init_id
+        result += "init_id='" + init_id + "'";
+     // wrap up
             result += " type='checkbox'";
             result += " autocomplete='off'";
             result += "></input>";
 
         new_record: function(record)
         {
+            var self = this;
+
             // this models a line in dataTables, each element in the line describes a cell
             line = new Array();
      
             // go through table headers to get column names we want
             // in order (we have temporarily hack some adjustments in names)
-            var cols = this.table.fnSettings().aoColumns;
-            var colnames = cols.map(function(x) {return x.sTitle})
+            var cols = this._get_columns();
+            var colnames = cols.map(function(x) {return self._get_map(x.sTitle)})
             var nb_col = cols.length;
             /* if we've requested checkboxes, then forget about the checkbox column for now */
             //if (this.options.checkboxes) nb_col -= 1;
-                       // catch up with the last column if checkboxes were requested 
+            // catch up with the last column if checkboxes were requested 
             if (this.options.checkboxes) {
                 // Use a key instead of hostname (hard coded...)
                 line.push(this.checkbox_html(record));
-               }
-               
+            }
+            line.push('<span id="' + this.id_from_key('status', record[this.init_key]) + '"></span>'); // STATUS
+            
             /* fill in stuff depending on the column name */
-            for (var j = 1; j < nb_col; j++) {
+            for (var j = 2; j < nb_col - 1; j++) { // nb_col includes status
                 if (typeof colnames[j] == 'undefined') {
                     line.push('...');
                 } else if (colnames[j] == 'hostname') {
                 }
             }
     
-            
-    
-           // adding an array in one call is *much* more efficient
-               // this.table.fnAddData(line);
-               this.buffered_lines.push(line);
+            // adding an array in one call is *much* more efficient
+            // this.table.fnAddData(line);
+            return line;
         },
 
         clear_table: function()
                 this.table.fnSetColumnVis(index, false);
         },
 
-       // 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;
-           var init_id = record[this.init_key];
-           if (debug) messages.debug("querytable.set_checkbox_from_record, init_id="+init_id);
-           // using table.$ to search inside elements that are not visible
-           var element = this.table.$('[init_id="'+init_id+'"]');
-           element.attr('checked',checked);
-       },
-
-       // id relates to canonical_key
-       set_checkbox_from_data: function (id, checked) {
+        set_checkbox_from_record_key: function (record_key, checked) 
+        {
             if (checked === undefined) checked = true;
-           if (debug) messages.debug("querytable.set_checkbox_from_data, id="+id);
-           // using table.$ to search inside elements that are not visible
-           var element = this.table.$("[id='"+id+"']");
-           element.attr('checked',checked);
-       },
 
-        /*************************** QUERY HANDLER ****************************/
-
-        on_filter_added: function(filter)
-        {
-            this.filters.push(filter);
-            this.redraw_table();
+            // using table.$ to search inside elements that are not visible
+            var element = this.table.$('[init_id="'+record_key+'"]');
+            element.attr('checked',checked);
         },
 
-        on_filter_removed: function(filter)
-        {
-            // Remove corresponding filters
-            this.filters = $.grep(this.filters, function(x) {
-                return x == filter;
-            });
-            this.redraw_table();
-        },
-        
-        on_filter_clear: function()
+        // id relates to canonical_key
+        set_checkbox_from_data: function (id, checked) 
         {
-            // XXX
-            this.redraw_table();
+            if (checked === undefined) checked = true;
+
+            // using table.$ to search inside elements that are not visible
+            var element = this.table.$("[id='"+id+"']");
+            element.attr('checked',checked);
         },
 
-        on_field_added: function(field)
+        /**
+         * 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)
         {
-            this.show_column(field);
+            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'});
+
         },
 
-        on_field_removed: function(field)
+        set_bgcolor: function(key_value, class_name)
         {
-            this.hide_column(field);
+            var elt = $(document.getElementById(this.id_from_key(this.canonical_key, key_value)))
+            if (class_name == QUERYTABLE_BGCOLOR_RESET)
+                elt.removeClass('added removed');
+            else
+                elt.addClass((class_name == QUERYTABLE_BGCOLOR_ADDED ? 'added' : 'removed'));
         },
 
-        on_field_clear: function()
+        populate_table: function()
         {
-            alert('QueryTable::clear_fields() not implemented');
+            // Let's clear the table and only add lines that are visible
+            var self = this;
+            this.clear_table();
+
+            lines = Array();
+            var record_keys = [];
+            manifold.query_store.iter_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, QUERYTABLE_BGCOLOR_ADDED);
+                        break;
+                    case STATE_SET_OUT_PENDING:
+                        //self.set_checkbox_from_record_key(record_key, false);
+                        self.set_bgcolor(record_key, QUERYTABLE_BGCOLOR_REMOVED);
+                        break;
+                }
+                self.change_status(record_key, warnings); // XXX will retrieve status again
+            });
         },
 
-        /* XXX TODO: make this generic a plugin has to subscribe to a set of Queries to avoid duplicated code ! */
-        /*************************** ALL QUERY HANDLER ****************************/
+        /*************************** QUERY HANDLER ****************************/
 
-        on_all_filter_added: function(filter)
+        on_filter_added: function(filter)
         {
-            // XXX
             this.redraw_table();
         },
 
-        on_all_filter_removed: function(filter)
+        on_filter_removed: function(filter)
         {
-            // XXX
             this.redraw_table();
         },
         
-        on_all_filter_clear: function()
+        on_filter_clear: function()
         {
-            // XXX
             this.redraw_table();
         },
 
-        on_all_field_added: function(field)
+        on_field_added: function(field)
         {
             this.show_column(field);
         },
 
-        on_all_field_removed: function(field)
+        on_field_removed: function(field)
         {
             this.hide_column(field);
         },
 
-        on_all_field_clear: function()
+        on_field_clear: function()
         {
             alert('QueryTable::clear_fields() not implemented');
         },
 
-
         /*************************** RECORD HANDLER ***************************/
 
-        on_new_record: function(record)
-        {
-            if (this.received_all_query) {
-               // if the 'all' query has been dealt with already we may turn on the checkbox
-                this.set_checkbox_from_record(record, true);
-            } else {
-                this.buffered_records_to_check.push(record);
-            }
-        },
-
-        on_clear_records: function()
-        {
-        },
-
         // Could be the default in parent
         on_query_in_progress: function()
         {
 
         on_query_done: function()
         {
-            this.received_query = true;
-           // unspin once we have received both
-            if (this.received_all_query && this.received_query) this.unspin();
+            this.populate_table();
+            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:
+            // XXX We could get this from data.value
+            // var state = manifold.query_store.get_record_state(this.options.query_uuid, data.value, data.state);
+
+            switch(data.state) {
+                case STATE_SET:
+                    switch(data.op) {
+                        case STATE_SET_IN:
+                        case STATE_SET_IN_SUCCESS:
+                        case STATE_SET_OUT_FAILURE:
+                            this.set_checkbox_from_data(data.value, true);
+                            this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_RESET);
+                            break;  
+                        case STATE_SET_OUT:
+                        case STATE_SET_OUT_SUCCESS:
+                        case STATE_SET_IN_FAILURE:
+                            this.set_checkbox_from_data(data.value, false);
+                            this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_RESET);
+                            break;
+                        case STATE_SET_IN_PENDING:
+                            this.set_checkbox_from_data(data.key, true);
+                            this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_ADDED);
+                            break;  
+                        case STATE_SET_OUT_PENDING:
+                            this.set_checkbox_from_data(data.key, false);
+                            this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_REMOVED);
+                            break;
+                    }
                     break;
-            }
-        },
 
-        /* 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.key, data.value);
                     break;
             }
         },
 
-        on_all_new_record: function(record)
-        {
-            this.new_record(record);
-        },
+        /************************** PRIVATE METHODS ***************************/
 
-        on_all_clear_records: function()
+        _get_columns: function()
         {
-            this.clear_table();
-
+            return this.table.fnSettings().aoColumns;
+            // XXX return $.map(table.fnSettings().aoColumns, function(x, i) { return QUERYTABLE_MAP[x]; });
         },
 
-        on_all_query_in_progress: function()
-        {
-            // XXX parent
-            this.spin();
-        }, // on_all_query_in_progress
-
-        on_all_query_done: function()
+        _get_map: function(column_title) {
+            return (column_title in QUERYTABLE_MAP) ? QUERYTABLE_MAP[column_title] : column_title;
+        },
+        /** 
+         * @brief QueryTable filtering function, called for every line in the datatable.
+         * 
+         * Return value:
+         *   boolean determining whether the column is visible or not.
+         */
+        _querytable_filter: function(oSettings, aData, iDataIndex)
         {
-               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();
+            var key_col, record_key_value;
 
-        }, // on_all_query_done
+            /* Determine index of key in the table columns */
+            key_col = $.map(oSettings.aoColumns, function(x, i) {if (self._get_map(x.sTitle) == self.canonical_key) return i;})[0];
 
-        /************************** PRIVATE METHODS ***************************/
+            /* Unknown key: no filtering */
+            if (typeof(key_col) == 'undefined') {
+                console.log("Unknown key");
+                return true;
+            }
 
-        /** 
-         * @brief QueryTable filtering function
-         */
-        _querytable_filter: function(oSettings, aData, iDataIndex)
-        {
-            var ret = true;
-            $.each (this.filters, function(index, filter) { 
-                /* XXX How to manage checkbox ? */
-                var key = filter[0]; 
-                var op = filter[1];
-                var value = filter[2];
-
-                /* Determine index of key in the table columns */
-                var col = $.map(oSettings.aoColumns, function(x, i) {if (x.sTitle == key) return i;})[0];
-
-                /* Unknown key: no filtering */
-                if (typeof(col) == 'undefined')
-                    return;
-
-                col_value=unfold.get_value(aData[col]);
-                /* 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")
-                        ret = false;
-                }else if (op == 'included') {
-                    $.each(value, function(i,x) {
-                      if(x == col_value){
-                          ret = true;
-                          return false;
-                      }else{
-                          ret = false;
-                      }
-                    });
-                }else if (op == '!=') {
-                    if ( col_value == value || col_value==null || col_value=="" || col_value=="n/a")
-                        ret = false;
-                } else if(op=='<') {
-                    if ( parseFloat(col_value) >= value || col_value==null || col_value=="" || col_value=="n/a")
-                        ret = false;
-                } else if(op=='>') {
-                    if ( parseFloat(col_value) <= value || col_value==null || col_value=="" || col_value=="n/a")
-                        ret = false;
-                } else if(op=='<=' || op=='≤') {
-                    if ( parseFloat(col_value) > value || col_value==null || col_value=="" || col_value=="n/a")
-                        ret = false;
-                } else if(op=='>=' || op=='≥') {
-                    if ( parseFloat(col_value) < value || col_value==null || col_value=="" || col_value=="n/a")
-                        ret = false;
-                }else{
-                    // How to break out of a loop ?
-                    alert("filter not supported");
-                    return false;
-                }
+            record_key_value = unfold.get_value(aData[key_col]);
 
-            });
-            return ret;
+            return manifold.query_store.get_record_state(this.options.query_uuid, record_key_value, STATE_VISIBLE);
         },
 
         _querytable_draw_callback: function()
 
         _check_click: function(e) 
         {
-            e.stopPropagation();
-
+            var data;
             var self = e.data;
-           var id=this.id;
 
-            // this.id = key of object to be added... what about multiple keys ?
-           if (debug) messages.debug("querytable._check_click key="+this.canonical_key+"->"+id+" checked="+this.checked);
-            manifold.raise_event(self.options.query_uuid, this.checked?SET_ADD:SET_REMOVED, id);
+            e.stopPropagation();
+
+            data = {
+                state: STATE_SET,
+                key  : null,
+                op   : this.checked ? STATE_SET_ADD : STATE_SET_REMOVE,
+                value: this.id
+            }
+            manifold.raise_event(self.options.query_uuid, FIELD_STATE_CHANGED, data);
             //return false; // prevent checkbox to be checked, waiting response from manifold plugin api
             
         },
      was in fact given as a third argument, and not second 
      as the various online resources had it - go figure */
     $.fn.dataTableExt.afnSortData['dom-checkbox'] = function  ( oSettings, _, iColumn ) {
-               return $.map( oSettings.oApi._fnGetTrNodes(oSettings), function (tr, i) {
-                   return result=$('td:eq('+iColumn+') input', tr).prop('checked') ? '1' : '0';
-               });
+        return $.map( oSettings.oApi._fnGetTrNodes(oSettings), function (tr, i) {
+            return result=$('td:eq('+iColumn+') input', tr).prop('checked') ? '1' : '0';
+        });
     };
 
 })(jQuery);
index d0f18c0..c843926 100644 (file)
@@ -1,19 +1,21 @@
-<div id="main-{{ domid }}" class="">
+<div id="main-{{ domid }}">
   <table class="table dataTable" id="{{domid}}__table" width="100%">
     <thead>
       <tr>
-       {% if checkboxes %}<th class="checkbox">+/-</th>{% endif %}
-        {% for column in columns %} <th>{{ column }}</th> {% endfor %} 
-        {% for column in hidden_columns %} <th>{{ column }}</th> {% endfor %}
+       {% if checkboxes %}<th class="checkbox"><input type="checkbox" disabled/></th>{% endif %}
+        <th>&#9888;</th>
+        {% for column, field in columns.items %} <th>{{ column }}</th> {% endfor %} 
+        {% for column, field in hidden_columns.items %} <th>{{ column }}</th> {% endfor %}
       </tr>
     </thead> 
     <tbody>
     </tbody>
     <tfoot>
       <tr>
-       {% if checkboxes %} <th>+/-</th> {% endif %}
-        {% for column in columns %} <th>{{ column }}</th> {% endfor %} 
-        {% for column in hidden_columns %} <th>{{ column }}</th> {% endfor %} 
+       {% if checkboxes %} <th><input type="checkbox" disabled/></th> {% endif %}
+        <th>&#9888;</th>
+        {% for column, field in columns.items %} <th>{{ column }}</th> {% endfor %} 
+        {% for column, field in hidden_columns.items %} <th>{{ column }}</th> {% endfor %} 
       </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 2e73629..f1d6b32 100644 (file)
     // Record state through the query cycle
 
 
-    var QueryUpdater = Plugin.extend({
+    var QueryUpdaterPlugin = Plugin.extend({
+
+        /**************************************************************************
+         *                          CONSTRUCTOR
+         **************************************************************************/
 
         init: function(options, element) {
                this.classname="queryupdater";
             this.selected_resources = Array();
 
             this.table = this.elmt('table').dataTable({
-// the original querytable layout was
-//                sDom: "<'row'<'col-xs-5'l><'col-xs-1'r><'col-xs-6'f>>t<'row'<'col-xs-5'i><'col-xs-7'p>>",
-// however the bottom line with 'showing blabla...' and the navigation widget are not really helpful
                 sDom: "<'row'<'col-xs-5'l><'col-xs-1'r><'col-xs-6'f>>t>",
-// so this does not matter anymore now that the pagination area is turned off
-//                sPaginationType: 'bootstrap',
-                     bAutoWidth: true,
-//                bJQueryUI      : true,
-//                bRetrieve      : true,
-//                sScrollX       : '100%',                 // Horizontal scrolling 
-//                bSortClasses   : false,              // Disable style for the sorted column
-//                aaSorting      : [[ 0, 'asc' ]],        // Default sorting on URN
-//                fnDrawCallback: function() {      // Reassociate close click every time the table is redrawn
-//                    /* Prevent to loop on click while redrawing table  */
-//                    $('.ResourceSelectedClose').unbind('click');
-//                    /* Handle clicks on close span */
-//                    /* Reassociate close click every time the table is redrawn */
-//                    $('.ResourceSelectedClose').bind('click', self, self._close_click);
-//                }
-             });
+                       bAutoWidth: true,
+            });
             
-            // XXX This should not be done at init...
-            this.elmt('update').click(this, this.do_checksla);
-            this.elmt('refresh').click(this, this.do_refresh);
-            this.elmt('reset').click(this, this.do_reset);
-            this.elmt('clear_annotations').click(this, this.do_clear_annotations);
+            this.elmt('update').click(this, this.do_ok);
+            this.elmt('refresh').click(this, this.do_cancel);
 
             this.listen_query(options.query_uuid);
         },
                     $('#sla-table-body').empty();
                     //manifold.raise_event(self.options.query_uuid, RUN_UPDATE);
                 }
-            }); 
-        
-            $("#cancel_sla").unbind().click(function(){
+            });
+
+               $("#cancel_sla").unbind().click(function(){
                 $('#slamodal').modal('hide');
                 $('#sla-table-body').empty();
             }); 
         },
+               /************************** GUI MANIPULATION **************************/
 
-     
-        do_update: function(e) {
-            var self = e.data;
-
-            var username = e.data.options.username;
-            var urn = data.value;
-            // 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);
-
-            // how to stop the spinning after the event? 
-            // this should be triggered by some on_updatequery_done ?
-
-        },
-
-       // related buttons are also disabled in the html template
-        do_refresh: function(e)
-        {
-            throw 'resource_selected.do_refresh Not implemented';
-        },
-
-        do_reset: function(e)
+        populate_table: function()
         {
-            throw 'queryupdater.do_reset Not implemented';
-        },
+            var state;
 
-        do_clear_annotations: function(e)
-        {
-            throw 'queryupdater.do_clear_annotations Not implemented';
+            // Loop over records and display pending ones
+            manifold.query_store.iter_records(this.options.query_uuid, function (record_key, record) {
+                state = manifold.query_store.get_record_state(this.options.query_uuid, null, STATE_SET);
+            
+            });
         },
         
-        /************************** GUI MANIPULATION **************************/
-
-        
         set_button_state: function(name, state)
         {
             this.elmt(name).attr('disabled', state ? false : 'disabled');
             return cols[0];
         },
 
-        set_state: function(data)
-        {
-            console.log("function set_state");
-            var action;
-            var msg;
-            var button = '';
-
-            var row;
-           
-           // make sure the change is visible : toggle on the whole plugin
-           // this might have to be made an 'auto-toggle' option of this plugin..
-           // also it might be needed to be a little finer-grained here
-
-        // 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:
-                    // find line and delete it
-                    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:
-                    msg   = 'SUCCESS';
-                    break;
-                case FIELD_REQUEST_FAILURE:
-                    msg   = 'FAILURE';
-                    break;
-            }
+        do_update: function(e) {
+            var self = e.data;
 
-            var status = msg + status;
+            var username = e.data.options.username;
+            var urn = data.value;
+            // 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);
 
-            // 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);
-            data.selected_resources = this.selected_resources;
-            row = this.find_row(data.value);
-            newline = [
-                action,
-                data.key,
-                data.value,
-                msg,
-                button
-            ];
-            if (!row) {
-                // XXX second parameter refresh = false can improve performance. todo in querytable also
-                this.table.fnAddData(newline);
-                row = this.find_row(data.value);
-                $("#badge-pending").data('number', $("#badge-pending").data('number') + 1 );
-                $("#badge-pending").text($("#badge-pending").data('number'));
-            } else {
-                // Update row text...
-                this.table.fnUpdate(newline, row.nTr);
-            }
+            // how to stop the spinning after the event? 
+            // this should be triggered by some on_updatequery_done ?
 
-            // Change cell color according to status
-            if (row) {
-                $(row.nTr).removeClass('add remove')
-                var cls = action.toLowerCase();
-                if (cls)
-                    $(row.nTr).addClass(cls);
-            }
         },
 
-        /*************************** QUERY HANDLER ****************************/
-
-        // NONE
+        do_ok: function(e)
+        {
+            throw 'queryupdater.do_reset Not implemented';
+        },
 
-        /*************************** RECORD HANDLER ***************************/
+        do_cancel: function(e)
+        {
+            throw 'queryupdater.do_clear_annotations Not implemented';
+        },
+        
+       /**************************************************************************
+        *                           QUERY HANDLERS
+        **************************************************************************/ 
 
         on_new_record: function(record)
         {
-            console.log("query_updater on_new_record");
-            console.log(record);
 
             // if (not and update) {
 
 
         on_query_in_progress: function()
         {
-               messages.debug("queryupdater.on_query_in_progress");
             this.spin();
         },
 
+        on_query_done: function()
+        {
+            this.populate_table();
+            this.unspin();
+        },
+
         // D : Data present
         // - on_clear_records (Get)
         // - on_new_record (shared with AD) XXX
 
         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);
         },
 
         // - Key and confirmation could be sufficient, or key and record state
         // XXX move record state to the manifold plugin API
 
-        on_field_state_changed: function(result)
+        on_field_state_changed: function(data)
         {
-            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);
+            */
+
+            var action, msg, row, status, button = '';
+
+            switch(data.state) {
+                case STATE_VALUE:
+                    switch(data.op) {
+                        // XXX other events missing !!
+                        case STATE_VALUE_CHANGE_PENDING:
+                            action = 'UPDATE';
+                            break;
+                    }
+                    break;
+
+                case STATE_SET:
+                    switch(data.op) {
+                        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
+                            row = this.find_row(data.value);
+                            if (row)
+                                this.table.fnDeleteRow(row.nTr);
+                            return;
+
+                        case STATE_SET_IN_SUCCESS:
+                        case STATE_SET_OUT_SUCCESS:
+                            msg   = 'SUCCESS';
+                            break;
+
+                        case STATE_SET_IN_FAILURE:
+                        case STATE_SET_OUT_FAILURE:
+                            msg   = 'FAILURE';
+                            break;
+
+                    }
+                    break;
+
+                default:
+                    return;
+            }
+
+            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 ?
+
+            // 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 = [action, data.key, data.value, msg, button];
+            if (!row) {
+                // XXX second parameter refresh = false can improve performance. todo in querytable also
+                this.table.fnAddData(newline);
+                row = this.find_row(data.value);
+            } else {
+                // Update row text...
+                this.table.fnUpdate(newline, row.nTr);
+            }
+
+            // Change cell color according to status
+            if (row) {
+                $(row.nTr).removeClass('add remove')
+                var cls = action.toLowerCase();
+                if (cls)
+                    $(row.nTr).addClass(cls);
+            }
         },
 
         // XXX we will have the requests for change
 
     });
 
-    $.plugin('QueryUpdater', QueryUpdater);
+    $.plugin('QueryUpdaterPlugin', QueryUpdaterPlugin);
 
 })(jQuery);
index c4d018e..be29739 100644 (file)
@@ -10,7 +10,9 @@
 </table>
 </div>
 <!--not all buttons are expected to work for now, hence the disabled mark-->
-<button type="button" id="{{domid}}__update"  class="btn btn-default">Update</button>
-<button type="button" id="{{domid}}__refresh"  class="btn btn-default" disabled>Refresh</button>
-<button type="button" id="{{domid}}__reset"  class="btn btn-default" disabled>Reset</button>
-<button type="button" id="{{domid}}__clear_annotations"  class="btn btn-default" disabled>Clear Annotations</button>
+<!--
+<button type="button" id="{{domid}}__update"  class="btn btn-onelab">Update</button>
+<button type="button" id="{{domid}}__refresh"  class="btn btn-primary" disabled>Refresh</button>
+<button type="button" id="{{domid}}__reset"  class="btn btn-danger" disabled>Reset</button>
+<button type="button" id="{{domid}}__clear_annotations"  class="btn btn-danger" disabled>Clear Annotations</button>
+-->
index 040b274..abe8eea 100755 (executable)
@@ -5,15 +5,10 @@ from datetime import timedelta
 class Scheduler2 (Plugin):\r
 \r
 \r
-    def __init__ (self, query, query_lease, query_all_resources, query_all_leases, **settings):\r
+    def __init__ (self, query, query_lease, **settings):\r
         Plugin.__init__ (self, **settings)\r
         \r
         self.query=query\r
-        self.query_all_resources = query_all_resources\r
-        self.query_all_resources_uuid = query_all_resources.query_uuid\r
-\r
-        self.query_all_leases = query_all_leases\r
-        self.query_all_leases_uuid = query_all_leases.query_uuid\r
 \r
         self.query_lease = query_lease\r
         self.query_lease_uuid = query_lease.query_uuid\r
@@ -35,12 +30,9 @@ class Scheduler2 (Plugin):
     def requirements (self):\r
         reqs = {\r
             'js_files' : [\r
-                'js/angular/angular.min.js',\r
                 'js/scheduler2.js',\r
-                'js/scheduler-SchedulerCtrl.js',\r
                 #'js/slider/jquery-ui-1.10.3.slider.min.js',\r
                 'js/scheduler-helpers.js',\r
-                'js/scheduler-table-selector.js',\r
             ],\r
             'css_files': [\r
                 'css/scheduler2.css', \r
@@ -54,7 +46,7 @@ class Scheduler2 (Plugin):
         # query_uuid will pass self.query results to the javascript\r
         # and will be available as "record" in :\r
         # on_new_record: function(record)\r
-        return ['plugin_uuid', 'domid', 'query_uuid', 'time_slots', 'nodes', 'query_lease_uuid', 'query_all_resources_uuid', 'query_all_leases_uuid']\r
+        return ['plugin_uuid', 'domid', 'query_uuid', 'time_slots', 'nodes', 'query_lease_uuid']\r
     \r
 \r
     def export_json_settings (self):\r
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 e4de904..ffbe4f1 100755 (executable)
 \r
 /** tables css **/\r
 \r
+#scheduler-reservation-table > tbody > tr > th {\r
+       font-weight: normal;\r
+}\r
+#scheduler-reservation-table > thead > tr > th {\r
+       font-weight: normal;\r
+}\r
+\r
 /*#ShedulerNodes-scroll-container {\r
     float: left;\r
     overflow-x: scroll;\r
     border: none !important;\r
 }\r
 #scheduler-reservation-table tbody tr td{\r
-    background-color: #A6C9E2 ;\r
-    border: 1px solid #111111;\r
+    background-color: #FFFFFF; /*#A6C9E2 ;*/\r
+/*    border: 1px solid #111111;*/\r
 }\r
 \r
 #scheduler-reservation-table tbody tr.even td{\r
-    background-color: #E0E0E0 ;\r
+    background-color: #FAFAFA; /*E0E0E0 ;*/\r
 }\r
 \r
 #scheduler-reservation-table tbody tr th::selection {color:    #000000;background:transparent;}\r
 }\r
 \r
 #scheduler-reservation-table tbody tr td.reserved {\r
-    background: url("../img/closed-lock-15.png") no-repeat scroll 50% 50% #DD4444;\r
+    background: url("../img/closed-lock-15.png") no-repeat scroll 50% 50%; /* #DD4444;*/\r
     cursor: not-allowed;\r
 }\r
 \r
     background: url("../img/tools-15.png") no-repeat scroll 50% 50% #EDA428;\r
 }\r
 \r
+#scheduler-reservation-table tbody tr td.pendingin {\r
+    background: #FFFF99;\r
+}\r
+\r
+\r
+#scheduler-reservation-table tbody tr td.pendingout {\r
+    background: #E8E8E8;\r
+}\r
+\r
 #scheduler-reservation-table tbody tr td.free:hover ,#scheduler-reservation-table tbody tr td.selected, #scheduler-reservation-table tbody tr td.selected_tmp {\r
     background: #25BA25;\r
 }\r
@@ -241,12 +257,19 @@ td.no-image {
     filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#a6c9e2', endColorstr='#e0e0e0',GradientType=1 ); /* IE6-9 */\r
 \r
     padding: 3px 0 3px 30px;\r
-    width: 109px;\r
+    width: 150px;\r
 }\r
 \r
 .legend ol li.free{\r
 \r
 }\r
+.legend ol li.pendingin{\r
+background: #FFFF99;\r
+}\r
+.legend ol li.pendingout{\r
+background: #E8E8E8;\r
+}\r
+\r
 \r
 .legend ol li.closed{\r
     background: url("../img/closed-lock-15.png") no-repeat scroll 8px 50% #DD4444;\r
@@ -262,8 +285,17 @@ td.no-image {
 \r
 /* latest stuff */\r
 .sliderContainer {\r
-    margin: 10px 0;\r
+       margin-left: -15px;\r
+       margin-right: 15px;\r
 }\r
 .table-responsive{\r
     overflow: hidden !important;\r
-}
\ No newline at end of file
+}\r
+input {\r
+    min-width: 150px !important;\r
+       margin-left: 5px;\r
+}\r
+\r
+.slider.slider-horizontal {\r
+       width: 100%;\r
+}\r
diff --git a/plugins/scheduler2/static/js/scheduler-SchedulerCtrl.js b/plugins/scheduler2/static/js/scheduler-SchedulerCtrl.js
deleted file mode 100755 (executable)
index d89d8c0..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-var myApp = angular.module('myApp', []);\r
-myApp.config(function ($interpolateProvider) {\r
-    $interpolateProvider.startSymbol('{[{').endSymbol('}]}');\r
-});\r
-myApp.factory('$exceptionHandler', function () {\r
-    return function (exception, cause) {\r
-        if (exception.message.contains('leases')) {\r
-            console.log(exception.message);\r
-            \r
-            var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
-            //tmpScope.initSlots(_schedulerCurrentCellPosition, _schedulerCurrentCellPosition + SchedulerTotalVisibleCells);\r
-        }\r
-            \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
-(function (ng, app) {\r
-\r
-\r
-    // Define our Controller constructor.\r
-    function Controller($scope) {\r
-\r
-        // Store the scope so we can reference it in our\r
-        // class methods\r
-        this.scope = $scope;\r
-\r
-        // Set up the default scope value.\r
-        this.scope.errorMessage = null;\r
-        this.scope.name = "";\r
-        //Pagin\r
-        $scope.totalPages = 4;\r
-        $scope.curPage = 0;\r
-        this.scope.pageSize = 25;\r
-\r
-        $scope.resources = new Array();\r
-        $scope.slots = SchedulerSlotsViewData;\r
-        //$scope.msg = "hello";\r
-\r
-        angular.element(document).ready(function() {\r
-            //console.log('Hello World');\r
-            //alert('Hello World');\r
-            //afterAngularRendered();\r
-        });\r
-\r
-        $scope.clearStuff = function() {\r
-            $scope.resources = new Array();\r
-            $scope.$apply();\r
-        }\r
-\r
-        $scope.initSchedulerResources = function (pageSize) {\r
-            $scope.resources = new Array();\r
-\r
-            for (var k = 0; k < pageSize; 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.initSlots(0, SchedulerTotalVisibleCells);\r
-        };\r
-\r
-        $scope.setPage = function(page) {\r
-            var tmpFrm = $scope.pageSize * page;\r
-            var tmpTo = tmpFrm + $scope.pageSize;\r
-            tmpTo = SchedulerDataViewData.length < tmpTo ? SchedulerDataViewData.length : tmpTo;\r
-            $scope.curPage = page;\r
-            $scope.resources = [];\r
-            var j = 0;\r
-            for (var k = tmpFrm; k < tmpTo; k++) {\r
-                $scope.resources.push(jQuery.extend(true, {}, SchedulerDataViewData[k]));\r
-                $scope.resources[j].leases = [];\r
-                j++;\r
-            }\r
-            //fix slider\r
-            $('#tblSlider').slider('value', 0);\r
-            //init Slots\r
-            $scope.initSlots(0, SchedulerTotalVisibleCells);\r
-        };\r
-\r
-        $scope.initSlots = function (from, to) {\r
-            //init\r
-            $scope.slots = [];\r
-\r
-            var resourceIndex; //gia to paging\r
-            //set\r
-            for (var i = from; i < to; i++) {\r
-                $scope.slots.push(SchedulerSlots[i]);\r
-                resourceIndex = $scope.pageSize * $scope.curPage;\r
-                for (var j = 0; j < $scope.resources.length; j++) {\r
-                    if (i == from) {\r
-                        $scope.resources[j].leases = [];\r
-                    }\r
-                    $scope.resources[j].leases.push(SchedulerDataViewData[resourceIndex].leases[i]);\r
-                    resourceIndex++;\r
-                }\r
-            }\r
-            //apply\r
-            $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
-        $scope.getPageNumbers = function () {\r
-            var totalNumbersShowned = ($scope.totalPages > 10 ? 10 : $scope.totalPages + 1 );\r
-            var tmtNumDiv = totalNumbersShowned / 2;\r
-            //local\r
-            var numFrom = 1;\r
-            var numTo = totalNumbersShowned;\r
-            var rtrnArr = new Array();\r
-\r
-            if (totalNumbersShowned > 1) {\r
-                //set from - to\r
-                if ($scope.totalPages > totalNumbersShowned) {\r
-                    if ($scope.curPage <= tmtNumDiv) {\r
-                        //nothing\r
-                    } else if ($scope.curPage >= $scope.totalPages - tmtNumDiv) {\r
-                        numTo = $scope.totalPages;\r
-                        numFrom = numTo - totalNumbersShowned;\r
-                    } else {\r
-                        numFrom = $scope.curPage - tmtNumDiv;\r
-                        numTo = numFrom + totalNumbersShowned;\r
-                    }\r
-                }\r
-\r
-                for (var i = numFrom; i < numTo; i++)\r
-                    rtrnArr.push(i);\r
-            } else {\r
-                rtrnArr.push(1);\r
-            }\r
-            return rtrnArr;\r
-        };\r
-\r
-        // Return this object reference.\r
-        return (this);\r
-\r
-    }\r
-\r
-\r
-    // Define the Controller as the constructor function.\r
-    app.controller("SchedulerCtrl", Controller);\r
-\r
-\r
-})(angular, myApp);
\ No newline at end of file
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..0b5339f 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,390 @@ 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 = 200;\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
+\r
+/******************************************************************************\r
+ *                             ANGULAR CONTROLLER                             *\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
+(function (ng, app) {\r
+\r
+    // Define our Controller constructor.\r
+    function Controller($scope) {\r
+\r
+        // Store the scope so we can reference it in our\r
+        // class methods\r
+        this.scope = $scope;\r
+\r
+        // Set up the default scope value.\r
+        this.scope.errorMessage = null;\r
+        this.scope.name = "";\r
+\r
+        //Pagin\r
+        $scope.current_page = 1;\r
+        this.scope.items_per_page = 10;\r
+        $scope.from = 0; // JORDAN\r
+\r
+        $scope.instance = null;\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
+            //console.log('Hello World');\r
+            //alert('Hello World');\r
+            //afterAngularRendered();\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
+        {\r
+            // XXX need visible resources only\r
+            var query_ext, visible_resources_length;\r
+            if (!$scope.instance)\r
+                return 0;\r
+            query_ext = manifold.query_store.find_analyzed_query_ext($scope.instance.options.query_uuid);\r
+            var visible_resources_length = 0;\r
+            query_ext.state.each(function(i, state) {\r
+                if (state[STATE_VISIBLE])\r
+                    visible_resources_length++;\r
+            });\r
+            return Math.ceil(visible_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.instance.options.query_uuid, resource['urn'], STATE_VISIBLE);\r
+        };\r
+\r
+        // SELECTION\r
+\r
+        $scope._create_new_lease = function(resource_urn, start_time, end_time)\r
+        {\r
+            var lease_key, new_lease, data;\r
+\r
+            lease_key = manifold.metadata.get_key('lease');\r
+\r
+            new_lease = {\r
+                resource:   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
+            data = {\r
+                state: STATE_SET,\r
+                key  : null,\r
+                op   : STATE_SET_ADD,\r
+                value: new_lease\r
+            }\r
+            manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data);\r
+            /* Add to local cache also, unless we listen to events from outside */\r
+            if (!(resource_urn in $scope._leases_by_resource))\r
+                $scope._leases_by_resource[resource_urn] = [];\r
+            $scope._leases_by_resource[resource_urn].push(new_lease);\r
+        }\r
+\r
+        $scope._remove_lease = function(other)\r
+        {\r
+            var lease_key, other_key, data;\r
+\r
+            lease_key = manifold.metadata.get_key('lease');\r
+\r
+            // XXX This could be a manifold.record_get_value\r
+            other_key = {\r
+                resource:   other.resource,\r
+                start_time: other.start_time,\r
+                end_time:   other.end_time\r
+            }\r
+            other_key.hashCode = manifold.record_hashcode(lease_key.sort());\r
+            other_key.equals   = manifold.record_equals(lease_key);\r
+\r
+            data = {\r
+                state: STATE_SET,\r
+                key  : null,\r
+                op   : STATE_SET_REMOVE,\r
+                value: other_key\r
+            }\r
+            manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data);\r
+            /* Remove from local cache also, unless we listen to events from outside */\r
+            $scope._leases_by_resource[other.resource] = $.grep($scope._leases_by_resource[other.resource], function(x) { return x != other; });\r
+\r
+        }\r
+\r
+        $scope.select = function(index, model_lease, model_resource)\r
+        {\r
+            var data;\r
+\r
+            console.log("Selected", index, model_lease, model_resource);\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
+\r
+            switch (model_lease.status)\r
+            {\r
+                case 'free': // out\r
+                case 'pendingout':\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
+                            data = {\r
+                                state: STATE_SET,\r
+                                key  : null,\r
+                                op   : STATE_SET_REMOVE,\r
+                                value: other_key\r
+                            }\r
+                            manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data);\r
+                            /* Remove from local cache also, unless we listen to events from outside */\r
+                            $scope._leases_by_resource[model_resource.urn] = $.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
+                            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
+                            data = {\r
+                                state: STATE_SET,\r
+                                key  : null,\r
+                                op   : STATE_SET_REMOVE,\r
+                                value: other_key\r
+                            }\r
+                            manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data);\r
+                            /* Remove from local cache also, unless we listen to events from outside */\r
+                            $scope._leases_by_resource[model_resource.urn] = $.grep($scope._leases_by_resource[model_resource.urn], function(x) { return x != other; });\r
+                            return false; // ~ break\r
+                        });\r
+                    }\r
+        \r
+                    $scope._create_new_lease(model_resource.urn, start_time, end_time);\r
+                    model_lease.status = 'pendingin'; \r
+                    // unless the exact same lease already existed (pending_out status for the lease, not the cell !!)\r
+\r
+                    break;\r
+\r
+                case 'selected':\r
+                case 'pendingin':\r
+                    // We remove the cell\r
+\r
+                    /* We search for leases including this cell. Either 0, 1 or 2.\r
+                     * 0 : NOT POSSIBLE, should be checked.\r
+                     * 1 : either IN or OUT, we have make no change in the session\r
+                     * 2 : both will be pending, since we have made a change in the session\r
+                    * /!\ need to properly remove pending_in leases when removed again\r
+                     */\r
+                    if (resource_leases) {\r
+                        $.each(resource_leases, function(i, other) {\r
+                            if ((other.start_time <= start_time) && (other.end_time >= end_time)) {\r
+                                // The cell is part of this lease.\r
+\r
+                                // If the cell is not at the beginning of the lease, we recreate a lease with cells before\r
+                                if (start_time > other.start_time) {\r
+                                    $scope._create_new_lease(model_resource.urn, other.start_time, start_time);\r
+                                }\r
+\r
+                                // If the cell is not at the end of the lease, we recreate a lease with cells after\r
+                                if (end_time < other.end_time) {\r
+                                    $scope._create_new_lease(model_resource.urn, end_time, other.end_time);\r
+                                }\r
+                                \r
+                                // The other lease will be removed\r
+                                $scope._remove_lease(other);\r
+                            }\r
+                            // NOTE: We can interrupt the search if we know that there is a single lease (depending on the status).\r
+                        });\r
+                    }\r
+                \r
+                    // cf comment in previous switch case\r
+                    model_lease.status = 'pendingout'; \r
+\r
+                    break;\r
+\r
+                case 'reserved':\r
+                case 'maintainance':\r
+                    // Do nothing\r
+                    break;\r
+            }\r
+            \r
+\r
+            $scope._dump_leases();\r
+        };\r
+  \r
+        $scope._dump_leases = function()\r
+        {\r
+            // DEBUG: display all leases and their status in the log\r
+            var leases = manifold.query_store.get_records($scope.instance.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.instance.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
+        // Return this object reference.\r
+        return (this);\r
+\r
+    }\r
+\r
+    // Define the Controller as the constructor function.\r
+    app.controller("SchedulerCtrl", Controller);\r
+\r
+})(angular, ManifoldApp);\r
+\r
+/******************************************************************************\r
+ *                              MANIFOLD PLUGIN                               *\r
+ ******************************************************************************/\r
+\r
 (function($) {\r
         scheduler2 = Plugin.extend({\r
 \r
@@ -64,16 +430,24 @@ var tmpSchedulerLeases = [];
          *     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
+                var scope = this._get_scope()\r
+                scope.instance = this;\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 +457,210 @@ 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
+                this._resources_received = false;\r
+                this._leases_received = false;\r
+                \r
+                scope._leases_by_resource = {};\r
 \r
-                if (schedulerDebug) console.time("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
-                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
+                this.elmt().on('show', this, this.on_show);\r
+                this.elmt().on('shown.bs.tab', this, this.on_show);\r
+                this.elmt().on('resize', this, this.on_resize);\r
 \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
-            /* Handlers */\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.do_resize();\r
+    \r
+                scope.from = 0;\r
+\r
+                this._initUI();\r
 \r
-            /* all_ev QUERY HANDLERS Start */\r
-            on_all_ev_clear_records: function(data) {\r
-                //alert('all_ev clear_records');\r
-            },\r
-            on_all_ev_query_in_progress: function(data) {\r
-                // alert('all_ev query_in_progress');\r
-            },\r
-            on_all_ev_new_record: function(data) {\r
-                //alert('all_ev new_record');\r
-            },\r
-            on_all_ev_query_done: function(data) {\r
-                //alert('all_ev query_done');\r
-            },\r
-            //another plugin has modified something, that requires you to update your display. \r
-            on_all_ev_field_state_changed: function(data) {\r
-                //alert('all_ev query_done');\r
-            },\r
-            /* all_ev QUERY HANDLERS End */\r
-            /* all_resources QUERY HANDLERS Start */\r
-            on_all_resources_clear_records: function(data) {\r
-                //data is empty on load\r
-            },\r
-            on_all_resources_query_in_progress: function(data) {\r
-                //data is empty on load\r
             },\r
-            on_all_resources_new_record: function(data) {\r
-                //alert(data.toSource());\r
-                if (data.exclusive == true) {\r
-                    var tmpGran = schedulerDebug && data.granularity == null ? 1800 : data.granularity;\r
-                    SchedulerData.push({\r
-                        id: data.urn,\r
-                        index: SchedulerData.length,\r
-                        name: data.hrn,\r
-                        granularity: tmpGran,\r
-                        leases: schedulerGetLeases(60 / schedulerSlotsPerHour, tmpGran),\r
-                        type: data.type,\r
-                        org_resource: data\r
-                    });\r
-                    /*if (schedulerDebug && SchedulerData[SchedulerData.length - 1].org_resource.network_hrn == 'omf') {\r
-                        SchedulerData[SchedulerData.length - 1].granularity = 1800;\r
-                    }*/\r
+\r
+            do_resize: function()\r
+            {\r
+                var scope = this._get_scope();\r
+\r
+                $('#' + schedulerTblId + ' thead tr th:eq(0)').css("width", SCHEDULER_FIRST_COLWIDTH);\r
+                //self get width might need fix depending on the template \r
+                var tblwidth = $('#scheduler-reservation-table').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
+                // Slider max value\r
+\r
+                if ($('#tblSlider').data('slider') != undefined) {\r
+                    var new_max = (this._all_slots.length - this._num_visible_cells) / this._lcm_colspan;\r
+                    $('#tblSlider').slider('setAttribute', 'max', new_max);\r
                 }\r
-                //alert(data.toSource());\r
 \r
             },\r
-            on_all_resources_query_done: function(data) {\r
-                _resourcesDone = true;\r
-                this._initScheduler();\r
+\r
+            on_show: function(e)\r
+            {\r
+                var self = e.data;\r
+                self.do_resize();\r
+                self._get_scope().$apply();\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_resize: function(e)\r
+            {\r
+                var self = e.data;\r
+                self.do_resize();\r
+                self._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
-                //console.log(data.toSource()); console.log('lease_new_record');\r
+\r
+            /* Handlers */\r
+\r
+            _get_scope : function()\r
+            {\r
+                return angular.element(document.getElementById('SchedulerCtrl')).scope();\r
             },\r
-            on_all_leases_query_done: function(data) {\r
-                _leasesDone = true;\r
-                this._initScheduler();\r
-                // console.log('lease_query_done');\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
-            //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
+            _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
-            // no prefix\r
-            on_filter_added: function(filter) {\r
-                this.filters.push(filter);\r
-                this._SetFiletredResources(this.filters);\r
-                //angular and UI\r
-                var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
-                if (SchedulerDataViewData.length == 0) {\r
-                    $("#plugin-scheduler").hide();\r
-                    $("#plugin-scheduler-empty").show();\r
-                    tmpScope.clearStuff();\r
-                } else {\r
-                    $("#plugin-scheduler-empty").hide();\r
-                    $("#plugin-scheduler").show();\r
-                    tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
-                }\r
             },\r
 \r
-            on_filter_removed: function(filter) {\r
-                // Remove corresponding filters\r
-                this.filters = $.grep(this.filters, function(x) {\r
-                    return x == filter;\r
+            _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
-                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
+            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
+\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
             },\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
+            /* 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
+            /* 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
+            /* INTERNAL FUNCTIONS */\r
+\r
+/* XXX IN TEMPLATE XXX\r
                 if (SchedulerDataViewData.length == 0) {\r
                     $("#plugin-scheduler").hide();\r
                     $("#plugin-scheduler-empty").show();\r
@@ -238,296 +668,103 @@ 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
-                    SchedulerDateSelected = $("#DateToRes").datepicker("getDate");\r
-                    if (SchedulerDateSelected != null && SchedulerDateSelected != '') {\r
-                        for (var i = 0; i < SchedulerData.length; i++) {\r
-                            SchedulerData[i].leases = schedulerGetLeases(60 / schedulerSlotsPerHour, SchedulerData[i].granularity);\r
-                        }\r
-                        scheduler2Instance._FixLeases();\r
-                        $('#tblSlider').slider('value', 0);\r
-                        var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
-                        tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
-\r
-                        //console.log(SchedulerDateSelected);\r
-                        //console.log(SchedulerDateSelected.getTime()/1000);\r
-                        var tomorrow = new Date(SchedulerDateSelected);\r
-                        tomorrow.setDate(SchedulerDateSelected.getDate()+1);\r
-                        //console.log(tomorrow);\r
-                        //console.log(tomorrow.getTime()/1000);\r
-                        \r
-                        // Remove previous date interval\r
-                        manifold.raise_event(scheduler2Instance.options.query_all_leases_uuid, FILTER_REMOVED, ['start_time', '>']);\r
-                        manifold.raise_event(scheduler2Instance.options.query_all_leases_uuid, FILTER_REMOVED, ['start_time', '<']);\r
-\r
-                        // Add new date interval\r
-                        manifold.raise_event(scheduler2Instance.options.query_all_leases_uuid, FILTER_ADDED, ['start_time', '>', SchedulerDateSelected.getTime()/1000]);\r
-                        manifold.raise_event(scheduler2Instance.options.query_all_leases_uuid, FILTER_ADDED, ['start_time', '<', tomorrow.getTime()/1000]);\r
-                    } else {\r
-                        alert("Please select a date, so the scheduler can reserve leases.");\r
+                    onRender: function(date) {\r
+                        return date.valueOf() < now.valueOf() ? 'disabled' : '';\r
                     }\r
-                }).datepicker('setDate', SchedulerDateSelected);\r
-                /*.click(function () {\r
-                $("#ui-datepicker-div").css("z-index", 5);\r
-            })*/\r
-                //End init DatePicker\r
-\r
-                //init Table\r
-                this._FixTable();\r
-                //End init Table\r
+                }).on('changeDate', function(ev) {\r
+                    SchedulerDateSelected = new Date(ev.date);\r
+                    SchedulerDateSelected.setHours(0,0,0,0);\r
+                    // Set slider to origin\r
+                    $('#tblSlider').slider('setValue', 0); // XXX\r
+                    // Refresh leases\r
+                    self._scope_clear_leases();\r
+                    self._scope_set_leases();\r
+                    // Refresh display\r
+                    self._get_scope().$apply();\r
+                }).datepicker('setValue', SchedulerDateSelected); //.data('datepicker');\r
 \r
                 //init Slider\r
                 $('#tblSlider').slider({\r
                     min: 0,\r
-                    max: SchedulerTotalCells - SchedulerTotalVisibleCells,\r
+                    max: (self._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
+                }).on('slide', function(ev) {\r
+                    var scope = self._get_scope();\r
+                    scope.from = ev.value * self._lcm_colspan;\r
+                    scope.$apply();\r
                 });\r
-                //End init Slider\r
-\r
-\r
-                //btn Submit leases\r
-                $('#btnSchedulerSubmit').click(function () {\r
-                    console.log("click btnSchedulerSubmit");\r
-                    var leasesForCommit = new Array();\r
-                    var tmpDateTime = SchedulerDateSelected;\r
-                    console.log(SchedulerData);\r
-                    for (var i = 0; i < SchedulerData.length; i++)\r
-                    {\r
-                        var tpmR = SchedulerData[i];\r
-                        //for capturing start and end of the lease\r
-                        var newLeaseStarted = false;\r
-                        for (var j = 0; j < tpmR.leases.length; j++) {\r
-                            var tpmL = tpmR.leases[j];\r
-                            if (newLeaseStarted == false && tpmL.status == 'selected') {\r
-                                //get date of the slot\r
-                                tmpDateTime = schedulerGetDateTimeFromSlotId(tpmL.id, tmpDateTime);\r
-                                var unixStartTime = tmpDateTime.getTime() / 1000;\r
-                                //add lease object\r
-                                leasesForCommit.push({\r
-                                    resource: tpmR.id,\r
-                                    //granularity: tpmR.granularity,\r
-                                    //lease_type: null,\r
-                                    //slice: null,\r
-                                    start_time: unixStartTime,\r
-                                    end_time: null,\r
-                                    //duration: null\r
-                                });\r
-                                console.log(tpmR.id);\r
-                                newLeaseStarted = true;\r
-                            } else if (newLeaseStarted == true && tpmL.status != 'selected') {\r
-                                //get date of the slot\r
-                                tmpDateTime = schedulerGetDateTimeFromSlotId(tpmL.id, tmpDateTime);\r
-                                var unixEndTime = tmpDateTime.getTime() / 1000;\r
-                                //upate end_time\r
-                                var tmpCL = leasesForCommit[leasesForCommit.length - 1];\r
-                                tmpCL.end_time = unixEndTime;\r
-                                //tmpCL.duration = schedulerFindDuration(tmpCL.start_time, tmpCL.end_time, tmpCL.granularity);\r
-                                newLeaseStarted = false;\r
-                            }\r
-                        }\r
-                    }\r
-                    console.log(leasesForCommit);\r
-                    for (var i = 0; i < leasesForCommit.length; i++) {\r
-                        manifold.raise_event(scheduler2Instance.options.query_lease_uuid, SET_ADD, leasesForCommit[i]);\r
-                    }\r
-                });\r
-                //\r
-\r
-\r
-                //End btn Submit leases\r
 \r
-                //other stuff\r
                 $("#plugin-scheduler-loader").hide();\r
                 $("#plugin-scheduler").show();\r
-                //fixOddEvenClasses();\r
-                //$("#" + schedulerTblId + " td:not([class])").addClass("free");\r
-                if (schedulerDebug) console.timeEnd("_initUI");\r
             },\r
 \r
-        _FixLeases  : function () {\r
-            for (var i = 0; i < tmpSchedulerLeases.length; i++) {\r
-                var tmpLea = tmpSchedulerLeases[i];\r
-                if ((schedulerCompareOnDay(tmpLea.start_time, SchedulerDateSelected) == 0) ||\r
-                                (tmpLea.start_time <= SchedulerDateSelected && SchedulerDateSelected <= tmpLea.end_time) || \r
-                                (schedulerCompareOnDay(tmpLea.end_time, SchedulerDateSelected) == 0)) {\r
-                    var tmpRes = schedulerFindResourceById(SchedulerData, tmpLea.resource);\r
-                    if (tmpRes != null) {\r
-                        //Replace Lease with current lease from the manifold\r
-                        var orgLease = tmpRes.leases[tmpLea.id];\r
-                        tmpLea['groupid'] = orgLease.groupid;\r
-                        tmpLea['groupIndex'] = orgLease.groupIndex;\r
-                        if (orgLease.groupIndex != 0) {\r
-                            if (!window.console) {\r
-                                console.warn('there is an error with the leases of the resource :' + tmpRes.name + '\n The lease start in the middle of the granularity!' + '\n The Scheduler plugin might not work!');\r
-                            }\r
-                        }\r
-                        tmpRes.leases[tmpLea.id] = tmpLea;\r
-                        this._ExtractLeaseSlots(tmpRes, tmpRes.leases[tmpLea.id]);\r
-                    }\r
-                }\r
-            }\r
-        },\r
+        // PRIVATE METHODS\r
 \r
-        _ExtractLeaseSlots: function (tmpRes, lease) {\r
-            var tmpStartDate = lease.start_time;\r
-            var tmpEndDate = lease.end_time;\r
-            var startLoop; var toLoop;\r
-            if (schedulerCompareOnDay(lease.start_time,lease.end_time) == 0) {\r
-                //in the same date\r
-                startLoop = lease.id;\r
-                toLoop = lease.end_id;\r
-            } else if (lease.start_time < SchedulerDateSelected && SchedulerDateSelected < lease.end_time) {\r
-                //one hole day (more than 3days)\r
-                startLoop = 0;\r
-                toLoop = tmpRes.leases.length;\r
-            } else if (schedulerCompareOnDay(lease.start_time, SchedulerDateSelected) == 0) {\r
-                //the same day and extends\r
-                startLoop = lease.id;\r
-                toLoop = tmpRes.leases.length;\r
-            } else if (schedulerCompareOnDay(lease.end_time, SchedulerDateSelected) == 0) {\r
-                //extends to the last say\r
-                startLoop = 0;\r
-                toLoop = lease.end_id;\r
-            }\r
-            //var minutGran = tmpRes.granularity * 60;\r
-            for (var li = lease.id; li < toLoop; li++) {\r
-                tmpRes.leases[li].status = 'reserved';\r
-            }\r
-            \r
-            //reserved\r
-            //tmpRes.leases[tmpLea.id\r
+        /**\r
+         * Greatest common divisor\r
+         */\r
+        _gcd : function(x, y)\r
+        {\r
+            return (y==0) ? x : this._gcd(y, x % y);\r
         },\r
 \r
-        _FixTable: function () {\r
-            var colWidth = 50;\r
-            SchedulerTotalCells = SchedulerSlots.length;\r
-            $('#' + schedulerTblId + ' thead tr th:eq(0)').css("width", schedulerTblFirstColWidth); //.css("display", "block");\r
-            //this get width might need fix depending on the template \r
-            var tblwidth = $('#scheduler-tab').parent().outerWidth();\r
-            SchedulerTotalVisibleCells = parseInt((tblwidth - schedulerTblFirstColWidth) / colWidth);\r
-\r
-            //if (SchedulerData.length == 0) {\r
-            //    //puth some test data\r
-            //    SchedulerData.push({ name: 'xyz+aaa', leases: schedulerGetLeases(60 / schedulerSlotsPerHour), urn: 'xyz+aaa', type: 'node' });\r
-            //    SchedulerData.push({ name: 'xyz+bbb', leases: schedulerGetLeases(60 / schedulerSlotsPerHour), urn: 'xyz+bbb', type: 'node' });\r
-            //    SchedulerData.push({ name: 'xyz+ccc', leases: schedulerGetLeases(60 / schedulerSlotsPerHour), urn: 'xyz+ccc', type: 'node' });\r
-            //    SchedulerData.push({ name: 'nitos1', leases: schedulerGetLeases(60 / schedulerSlotsPerHour), urn: 'nitos1', type: 'node' });\r
-            //}\r
-            var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
-            tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
-\r
+        /**\r
+         * Least common multiple\r
+         */\r
+        _lcm : function(x, y)\r
+        {\r
+            return x * y / this._gcd(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
+        _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
diff --git a/plugins/scheduler2/static/js/selectRangeWorker.js b/plugins/scheduler2/static/js/selectRangeWorker.js
deleted file mode 100755 (executable)
index 5f28270..0000000
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
index d7cd16e..6edc88e 100755 (executable)
@@ -6,78 +6,78 @@
     <img src="../../static/img/no-data.png" alt="no data found" style="width:100px;" />\r
     <h3>no data found...</h3>\r
 </div>\r
-<div id="plugin-{{ domid }}" class="" ng-app="myApp" style="display:none;">\r
+<div id="plugin-{{ domid }}" class="">\r
     <div class="row m-b">\r
-        <div class="col-md-1">\r
-            <label for="inputEmail3" class="col-sm-2 control-label">Date</label>\r
-        </div>\r
-        <div class="col-md-9">\r
-            <input id="DateToRes" type="text" class="form-control" placeholder="Reservation Date">\r
-            <span class="glyphicon glyphicon-calendar"></span>\r
+        <div class="col-md-2">\r
+            <label for="inputEmail3" class="col-sm-2 control-label">Day:</label>\r
         </div>\r
-        <div class="col-md-2 text-center">\r
-            {% comment %}\r
-            <div id="TopologyModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">\r
-                <div class="modal-dialog">\r
-                    <div class="modal-header">\r
-                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>\r
-                        <h4 class="modal-title" id="myModalLabel">Topology</h4>\r
-                    </div>\r
-                    <div class="modal-body">\r
-                        <img src="../../static/img/nitos_topo.png" alt="nitos topology" style="width:100%;" />\r
-                    </div>\r
-                </div><!-- /.modal-dialog -->\r
-            </div><!-- /.modal TopologyModal -->\r
-            <button type="button" class="btn btn-primary btn-md" data-toggle="modal" data-target="#TopologyModal">Topology</button>\r
-            {% endcomment  %}\r
-            <button id="btnSchedulerSubmit" type="button" class="btn btn-primary btn-md">Submit Leases</button>\r
-\r
+        <div class="col-md-10">\r
+            <label for="inputEmail3" class="col-sm-2 control-label">Time of day:</label>\r
         </div>\r
     </div>\r
-    <!--<div class="row m-b">\r
-        <div class="col-md-1">\r
-            <label for="inputEmail3" class="col-sm-1 control-label">Time</label>\r
-        </div>\r
-        <div class="col-md-9">\r
-            <div id="time-range"></div>\r
-        </div>\r
-        <div class="col-md-2">\r
-            <span id="lbltime" class="label label-primary"></span>\r
-        </div>\r
-    </div>-->\r
+    <div class="row m-b">\r
+               <div class="col-md-2">\r
+               <input id="DateToRes" type="text" placeholder="Reservation Date">\r
+               <!-- <input id="DateToRes" type="text" class="form-control" placeholder="Reservation Date"> -->\r
+               <span class="glyphicon glyphicon-calendar"></span>\r
+               </div>\r
+               <div class="col-md-10">\r
+                       <div class="sliderContainer">\r
+                               <div id="tblSlider"></div>\r
+                       </div>\r
+               </div>\r
+    </div>\r
     <div id="SchedulerCtrl" ng-controller="SchedulerCtrl" class='query-editor-spacer'>\r
-        <div class="sliderContainer">\r
-            <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>Resource name</th>\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
 \r
             <div class="legend">\r
                 <ol>\r
-                    <li class="free">free</li>\r
-                    <li class="selected">selected</li>\r
-                    <li class="closed">reserved</li>\r
-                    <li class="maintenance">maintenance</li>\r
+                    <li class="free">Free</li>\r
+                    <li class="pendingin">Pending selected</li>\r
+                    <li class="pendingout">Pending deselected</li>\r
+                    <li class="selected">Selected</li>\r
+                    <li class="closed">Reserved</li>\r
+                    <li class="maintenance">Maintenance</li>\r
                 </ol>\r
             </div>\r
         </div><!-- / scheduler plugin -->\r
index a7dd15e..c72f667 100644 (file)
@@ -2,13 +2,13 @@ from unfold.plugin import Plugin
 
 class TestbedsPlugin(Plugin):
     
-    def __init__ (self, query=None, query_all=None, query_network=None, **settings):
+    def __init__ (self, query=None, query_networks=None, **settings):
         Plugin.__init__ (self, **settings)
 
         # Until we have a proper way to access queries in Python
         self.query              = query
-        self.query_all          = query_all
-        self.query_all_uuid     = query_all.query_uuid if query_all else None
+        self.query_networks          = query_networks
+        self.query_networks_uuid     = query_networks.query_uuid if query_networks else None
 
     def template_file (self):
         return "testbeds.html"
@@ -28,7 +28,7 @@ class TestbedsPlugin(Plugin):
         # 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', 'query_all_uuid']
+        return ['plugin_uuid', 'domid', 'query_uuid', 'query_networks_uuid']
 
     def export_json_settings (self):
         return True
index 5f990b9..623bc91 100644 (file)
  * License:     GPLv3
  */
 
-(function($){
+// XXX Inherit from an AngularPlugin class ?
+(function (ng, app) {
+
+    // Define our Controller constructor.
+    function Controller($scope) {
+        /* Contructor */
+
+        /* Plugin instance */
+        $scope.instance = null;
+
+        /* Models */
+        $scope.testbeds = Array();
 
+        /* Click event */
+        $scope.select = function(testbed)
+        {
+            var selected, prev_selected, num, num_selected, num_prev_selected, filter;
+
+            prev_selected = $.map($scope.testbeds, function(x, i) {
+                return x.active ? x.network_hrn : null;
+            });
+
+            testbed.active = !testbed.active;
+
+            selected = $.map($scope.testbeds, function(x, i) {
+                return x.active ? x.network_hrn : null;
+            });
+
+            num = $scope.testbeds.length;
+            prev_num_selected = prev_selected.length;
+            num_selected = selected.length;
+
+            
+            if ((prev_num_selected != 0) && (prev_num_selected != num)) {
+                // Remove previous filter
+                filter = ['network_hrn', 'included', prev_selected];
+                manifold.raise_event($scope.instance.options.query_uuid, FILTER_REMOVED, filter);
+            }
+
+            if ((num_selected != 0) && (num_selected != num)) {
+                filter = ['network_hrn', 'included', selected];
+                manifold.raise_event($scope.instance.options.query_uuid, FILTER_ADDED, filter);
+            }
+        };
+
+        /* Return object reference */
+        return (this);
+    }
+
+    // Define the Controller as the constructor function.
+    app.controller("TestbedsCtrl", Controller);
+
+})(angular, ManifoldApp);
+
+(function($){
     var TestbedsPlugin = Plugin.extend({
 
         /** XXX to check
          * @return : a jQuery collection of objects on which the plugin is
          *     applied, which allows to maintain chainability of calls
          */
-        init: function(options, element) {
-               // for debugging tools
-               this.classname="testbedsplugin";
+        init: function(options, element) 
+        {
             // Call the parent constructor, see FAQ when forgotten
             this._super(options, element);
 
             /* Member variables */
             this.filters = Array();
             this.testbeds = Array();
-            /* Plugin events */
 
-            /* Setup query and record handlers */
+            this._get_scope().instance = this;
 
-            // Explain this will allow query events to be handled
-            // What happens when we don't define some events ?
-            // Some can be less efficient
+            /* Handlers */
             this.listen_query(options.query_uuid);
-            this.listen_query(options.query_all_uuid, 'all');
-
-            /* GUI setup and event binding */
-            // call function
-
+            this.listen_query(options.query_networks_uuid, 'networks');
         },
 
-        /* PLUGIN EVENTS */
-        // on_show like in querytable
-
 
-        /* GUI EVENTS */
-
-        // a function to bind events here: click change
-        // how to raise manifold events
-
-
-        /* GUI MANIPULATION */
-
-        // We advise you to write function to change behaviour of the GUI
-        // Will use naming helpers to access content _inside_ the plugin
-        // always refer to these functions in the remaining of the code
-
-        show_hide_button: function() 
-        {
-            // this.id, this.el, this.cl, this.elts
-            // same output as a jquery selector with some guarantees
-        },
-
-        /* TEMPLATES */
-
-        // see in the html template
-        // How to load a template, use of mustache
-
-        /* QUERY HANDLERS */
-
-        // How to make sure the plugin is not desynchronized
-        // He should manifest its interest in filters, fields or records
-        // functions triggered only if the proper listen is done
-
-        // no prefix
+        /* HANDLERS */
 
         /* When a filter is added/removed, update the list of filters local to the plugin */
         on_filter_added: function(filter)
                 }else if(filter[1]=='=' || filter[1]=='=='){
                     $("#testbeds-filter_"+filter[2]).addClass("active");
                 }
+                // XXX NAMING
+                // XXX How to display unsupported filters
+                // XXX Constants for operators
             }
         },
         on_filter_removed: function(filter)
 
         // ... be sure to list all events here
 
-        /* RECORD HANDLERS */
-        on_all_new_record: function(record)
+        on_networks_query_done: function()
         {
-            var self = this;
-            // If the resource has a network_hrn
-            if(record["network_hrn"]!="None" && record["network_hrn"]!="" && record["network_hrn"]!=null){
-                // If this network_hrn is not listed yet
-                if(jQuery.inArray(record["network_hrn"],self.testbeds)==-1){
-                    row  = '<a href="#" class="list-group-item sl-platform" id="testbeds-filter_'+record["network_hrn"]+'" data-platform="'+record["network_hrn"]+'">';
-                    //row += '<span class="list-group-item-heading">'+record["platform"]+'</span>';
-                    //row += '<span class="list-group-item-heading">'+record["network_hrn"]+'</span></a>';
-                    row += '<p class="list-group-item-heading">'+record["network_hrn"]+'</p></a>';
-                    $('#testbeds-filter').append(row);
-                    self.testbeds.push(record["network_hrn"]);
-                }
-            }
+             this.set_networks();
         },
 
-        /* When the query is done, add the click event to the elements  */
-        on_all_query_done: function() {
+/*
             var self = this;
             console.log('query network DONE');
             $("[id^='testbeds-filter_']").on('click',function(e) {
                 return $(this).hasClass('active') ? self._addFilter(key, op, value) : self._removeFilter(key, op, value);
             });
            
-        },
+        },*/
 
         /* INTERNAL FUNCTIONS */
-        _dummy: function() {
-            // only convention, not strictly enforced at the moment
+
+        set_networks: function()
+        {
+            var scope = this._get_scope();
+            var query_ext = manifold.query_store.find_analyzed_query_ext(this.options.query_networks_uuid);
+            scope.testbeds = query_ext.records.values();
+            $.each(scope.testbeds, function(i, testbed) { testbed.active = true });
+            scope.$apply();
         },
+
+        _get_scope : function()
+        {
+            return angular.element('[ng-controller=TestbedsCtrl]').scope()
+        },
+
         _addFilter: function(key, op, value)
         {
-            console.log("add "+value);
-            var self = this;
             values = Array();
             // get the previous list of values for this key, ex: [ple,nitos]
             // remove the previous filter
             if(network_filter.length > 0){
                 $.each(network_filter, function(i,f){
                     values = f[2];
-                    manifold.raise_event(self.options.query_uuid, FILTER_REMOVED, [key, op, values]);
                 });
             }
             // Add the new value to list of values, ex: wilab
index fe8c2ea..289a556 100644 (file)
@@ -1,3 +1,16 @@
-<div id={{ domid }}>
+<div id={{ domid }} ng-controller="TestbedsCtrl">
+
 <div class="list-group-item sl-platform"><span class="list-group-item-heading">Testbeds</span></div>
+
+<div ng-repeat="testbed in testbeds"
+     ng-click="select(testbed)">
+       <a href="#" 
+          class="list-group-item sl-platform"
+       ng-class="{active: testbed.active}"
+          id="testbeds-filter_{[{ testbed.network_hrn }]}"
+          data-platform="{[{ testbed.network_hrn }]}">
+       <span class="list-group-item-heading">{[{ testbed.platform }]}</span>
+       <p class="list-group-item-heading">{[{ testbed.network_hrn }]}</p></a>
+</div>
+
 </div>
index 853bc1e..73cd552 100644 (file)
@@ -1,14 +1,15 @@
 from unfold.plugin import Plugin
 
 class UnivbrisFv (Plugin):
-"""
-////////////////////////////////////////
-modified querytable for univbris foam
-///////////////////////////////////////"""
+    """
+    ////////////////////////////////////////
+    modified querytable for univbris foam
+    ///////////////////////////////////////
+    """
 
     def __init__ (self, init_key=None,datatables_options={}, **settings):
-       Plugin.__init__ (self, **settings)
-       self.columns = list (['switch dpid','port no<->peer dpid/port no.','selected'])
+        Plugin.__init__ (self, **settings)
+        self.columns = list (['switch dpid','port no<->peer dpid/port no.','selected'])
         self.init_key=init_key
         self.datatables_options=datatables_options
 
index c36ff49..734485b 100644 (file)
@@ -31,7 +31,7 @@ class AccountView(LoginRequiredAutoLogoutView, ThemeView):
     def get_context_data(self, **kwargs):
         self.template_name = self.template
         page = Page(self.request)
-        page.add_js_files  ( [ "js/jquery.validate.js", "js/my_account.register.js", "js/my_account.edit_profile.js" ] )
+        page.add_js_files  ( [ "js/jquery.validate.js", "js/my_account.register.js", "js/my_account.edit_profile.js","js/jquery-ui.js" ] )
         page.add_css_files ( [ "css/onelab.css", "css/account_view.css","css/plugin.css" ] )
 
 
@@ -184,11 +184,32 @@ class AccountView(LoginRequiredAutoLogoutView, ThemeView):
         # we could use zip. this one is used if columns have unequal rows 
         platform_list = [{'platform_no_access': t[0]}
             for t in itertools.izip_longest(total_platform_list)]
+
+
+        ## check user is pi or not
+        platform_query  = Query().get('local:platform').select('platform_id','platform','gateway_type','disabled')
+        account_query  = Query().get('local:account').select('user_id','platform_id','auth_type','config')
+        platform_details = execute_query(self.request, platform_query)
+        account_details = execute_query(self.request, account_query)
+        for platform_detail in platform_details:
+            for account_detail in account_details:
+                if platform_detail['platform_id'] == account_detail['platform_id']:
+                    if 'config' in account_detail and account_detail['config'] is not '':
+                        account_config = json.loads(account_detail['config'])
+                        if 'myslice' in platform_detail['platform']:
+                            acc_auth_cred = account_config.get('delegated_authority_credentials','N/A')
+        # assigning values
+        if acc_auth_cred == {} or acc_auth_cred == 'N/A':
+            pi = "is_not_pi"
+        else:
+            pi = "is_pi"
+
         context = super(AccountView, self).get_context_data(**kwargs)
         context['principal_acc'] = principal_acc_list
         context['ref_acc'] = ref_acc_list
         context['platform_list'] = platform_list
         context['my_users'] = my_users
+        context['pi'] = pi
         context['my_slices'] = my_slices
         context['my_auths'] = my_auths
         context['user_status'] = user_status
@@ -549,7 +570,6 @@ def account_process(request):
             obj_gid = obj_cred.get_gid_object()
             str_cert = obj_gid.save_to_string()
             cert = crypto.load_certificate(crypto.FILETYPE_PEM, str_cert)
-            #ca = cert.get_issuer()
 
             user_private_key = account_config['user_private_key'].encode('ascii')
             pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, user_private_key)
@@ -557,11 +577,8 @@ def account_process(request):
             p12 = crypto.PKCS12()
             p12.set_privatekey(pkey)
             p12.set_certificate(cert)       
-            #p12.set_ca_certificates(ca)       
             pkcs12 = p12.export()
 
-            pkcs12 = p12.get_ca_certificates() 
-
             response = HttpResponse(pkcs12, content_type='text/plain')
             response['Content-Disposition'] = 'attachment; filename="user_pkcs.p12"'
             return response
@@ -572,7 +589,6 @@ def account_process(request):
             obj_gid = obj_cred.get_gid_object()
             str_cert = obj_gid.save_to_string()
             cert = crypto.load_certificate(crypto.FILETYPE_PEM, str_cert)
-            #ca = cert.get_issuer()
 
             user_private_key = account_config['user_private_key'].encode('ascii')
             pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, user_private_key)
@@ -580,9 +596,7 @@ def account_process(request):
             p12 = crypto.PKCS12()
             p12.set_privatekey(pkey)
             p12.set_certificate(cert)       
-            #p12.set_ca_certificates(ca)       
             pkcs12 = p12.export()
-            pkcs12 = p12.get_ca_certificates() 
 
             response = HttpResponse(pkcs12, content_type='text/plain')
             response['Content-Disposition'] = 'attachment; filename="user_pkcs.p12"'
index e68bd3f..baf4cca 100644 (file)
@@ -36,7 +36,7 @@ def authority_get_pi_emails(request, authority_hrn):
     pi_users = authority_get_pis(request,authority_hrn)
     print "pi_users = %s" % pi_users
 
-    if any(d['pi_users'] == None for d in pi_users):
+    if any(pi['pi_users'] == None or not pi['pi_users']  for pi in pi_users):
         #theme.template_name = 'email_default_recipients.txt' 
         #default_email = render_to_string(theme.template, request)
         #default_email = default_email.replace('\n', '')
@@ -608,7 +608,7 @@ def create_pending_user(wsgi_request, request, user_detail):
         
         theme.template_name = 'user_request_email.html'
         html_content = render_to_string(theme.template, request)
-    
         theme.template_name = 'user_request_email.txt'
         text_content = render_to_string(theme.template, request)
     
index 3da2e05..c41ee74 100644 (file)
@@ -6,6 +6,9 @@ from unfold.loginrequired       import FreeAccessView
 from ui.topmenu                 import topmenu_items, the_user
 from django.core.mail           import EmailMultiAlternatives, send_mail
 from portal.forms               import ContactForm
+from manifold.core.query                import Query
+from manifoldapi.manifoldapi            import execute_query
+import json
 
 from myslice.theme import ThemeView
 theme = ThemeView()
@@ -32,7 +35,7 @@ class ContactView (FreeAccessView, ThemeView):
             #theme.template_name = 'email_support.txt'
             #recipients = render_to_string(theme.template, form.cleaned_data)
             #recipients = subject.replace('\n', '')
-            recipients = ['support@myslice.info','contact@fed4fire.eu']
+            recipients = ['support@myslice.info']
             if cc_myself:
                 recipients.append(email)
             #recipients = ['support@myslice.info']
@@ -71,14 +74,33 @@ class ContactView (FreeAccessView, ThemeView):
         return self._display (request, ContactForm()) # A fresh unbound form
         
     def _display (self, request, form):
-        if request.user.is_authenticated() :
+        if request.user.is_authenticated():
             username = request.user.email
+            ## check user is pi or not
+            platform_query  = Query().get('local:platform').select('platform_id','platform','gateway_type','disabled')
+            account_query  = Query().get('local:account').select('user_id','platform_id','auth_type','config')
+            platform_details = execute_query(self.request, platform_query)
+            account_details = execute_query(self.request, account_query)
+            for platform_detail in platform_details:
+                for account_detail in account_details:
+                    if platform_detail['platform_id'] == account_detail['platform_id']:
+                        if 'config' in account_detail and account_detail['config'] is not '':
+                            account_config = json.loads(account_detail['config'])
+                            if 'myslice' in platform_detail['platform']:
+                                acc_auth_cred = account_config.get('delegated_authority_credentials','N/A')
+            # assigning values
+            if acc_auth_cred == {} or acc_auth_cred == 'N/A':
+                pi = "is_not_pi"
+            else:
+                pi = "is_pi"
         else :
             username = None
+            pi = "is_not_pi"
         return render(request, self.template, {
                 'form': form,
                 'topmenu_items': topmenu_items('Contact', request),
                 'theme' : self.theme,
                 'username': username,
+                'pi': pi,
                 'section': "Contact"
                 })
index dd3788a..2a3e617 100644 (file)
@@ -2,7 +2,7 @@ from unfold.loginrequired               import FreeAccessView
 #
 from manifold.core.query                import Query
 from manifoldapi.manifoldapi            import execute_query, execute_admin_query
-from portal.actions                     import manifold_update_user, manifold_update_account, manifold_add_account, manifold_delete_account, sfa_update_user
+from portal.actions                     import manifold_update_user, manifold_update_account, manifold_add_account, manifold_delete_account, sfa_update_user, authority_get_pi_emails
 #
 from unfold.page                        import Page    
 from ui.topmenu                         import topmenu_items_live, the_user
@@ -12,6 +12,9 @@ from django.contrib                     import messages
 from django.contrib.auth.decorators     import login_required
 from myslice.theme                      import ThemeView
 from portal.models                      import PendingUser
+from django.core.mail                   import EmailMultiAlternatives, send_mail
+from django.contrib.sites.models        import Site
+
 #
 import json, os, re, itertools
 
@@ -33,15 +36,50 @@ class ActivateEmailView(FreeAccessView, ThemeView):
             if key == "hash_code":
                 hash_code=value
        
-        if PendingUser.objects.filter(email_hash__iexact = hash_code):
+        if PendingUser.objects.filter(email_hash__iexact = hash_code):           
             #get_user = PendingUser.objects.filter(email_hash__iexact = hash_code)
             #get_user.status= 'True'
             #get_user.save()
+            #for user in PendingUser.objects.all():
+            #    first_name = user.first_name
+            #    last_name = user.last_name
+            #    authority_hrn = user.authority_hrn
+            #    public_key = user.public_key
+            #    email = user.email
+            #    user_hrn = user.user_hrn
             PendingUser.objects.filter(email_hash__iexact = hash_code).update(status='True')
             activation = 'success'
+            # sending email after activation success
+            #try:
+                # Send an email: the recipients are the PI of the authority
+                # If No PI is defined for this Authority, send to a default email (different for each theme)
+             #   recipients = authority_get_pi_emails(wsgi_request, authority_hrn)
+             #   theme.template_name = 'user_request_email.html'
+             #   html_content = render_to_string(theme.template, request)
+             #   theme.template_name = 'user_request_email.txt'
+             #   text_content = render_to_string(theme.template, request)
+             #   theme.template_name = 'user_request_email_subject.txt'
+             #   subject = render_to_string(theme.template, request)
+             #   subject = subject.replace('\n', '')
+             #   theme.template_name = 'email_default_sender.txt'
+             #   sender =  render_to_string(theme.template, request)
+             #   sender = sender.replace('\n', '')
+             #   msg = EmailMultiAlternatives(subject, text_content, sender, recipients)
+             #   msg.attach_alternative(html_content, "text/html")
+             #   msg.send()
+           # except Exception, e:
+             #   print "Failed to send email, please check the mail templates and the SMTP configuration of your server"
+             #   import traceback
+             #   traceback.print_exc()
+
         else:
             activation = 'failed'
+        
+        # get the domain url
+        current_site = Site.objects.get_current()
+        current_site = current_site.domain
 
+        
         context = super(ActivateEmailView, self).get_context_data(**kwargs)
         context['activation_status'] = activation
         # XXX This is repeated in all pages
@@ -51,6 +89,13 @@ class ActivateEmailView(FreeAccessView, ThemeView):
         context['topmenu_items'] = topmenu_items_live('My Account', page)
         # so we can sho who is logged
         context['username'] = the_user(self.request)
+        #context['first_name'] = first_name
+        #context['last_name'] = last_name
+        #context['authority_hrn'] = authority_hrn
+        #context['public_key'] = public_key
+        #context['email'] = email
+        #context['user_hrn'] = user_hrn
+        #context['current_site'] = current_site
         context['theme'] = self.theme
 #        context ['firstname'] = config['firstname']
         prelude_env = page.prelude_env()
index ecd2e6c..683cffb 100644 (file)
@@ -99,7 +99,7 @@ class ContactForm(forms.Form):
 class PassResetForm(forms.Form):
     email = forms.EmailField(widget=forms.TextInput(attrs={'class':'form-control'}))
 
-class SliceRequestForm(forms.Form):
+#class SliceRequestForm(forms.Form):
 #    slice_name = forms.CharField()
 #    authority_hrn = forms.ChoiceField(choices=[(1, 'un')])
 #    number_of_nodes  = forms.DecimalField()
@@ -107,52 +107,52 @@ class SliceRequestForm(forms.Form):
 #    purpose = forms.CharField(widget=forms.Textarea)
 #    email = forms.EmailField()
 #    cc_myself = forms.BooleanField(required=False)
-
-    slice_name = forms.CharField(
-        widget=forms.TextInput(attrs={'class':'form-control'}), 
-        help_text="The name for the slice you wish to create")
-    authority_hrn = forms.ChoiceField(
-        widget    = forms.Select(attrs={'class':'form-control'}),
-        choices   = [],
-        help_text = "An authority responsible for vetting your slice")
-    number_of_nodes = forms.DecimalField(
-        widget    = forms.TextInput(attrs={'class':'form-control'}),
-        help_text = "The number of nodes you expect to request (informative)")
-    type_of_nodes = forms.CharField(
-        widget    = forms.TextInput(attrs={'class':'form-control'}),
-        help_text = "The type of nodes you expect to request (informative)")
-    purpose = forms.CharField(
-        widget    = forms.Textarea(attrs={'class':'form-control'}),
-        help_text = "The purpose of your experiment (informative)")
-    email = forms.EmailField(
-        widget    = forms.TextInput(attrs={'class':'form-control'}),
-        help_text = "Your email address")
-    cc_myself = forms.BooleanField(
-        widget    = forms.CheckboxInput(attrs={'class':'form-control'}),
-        required  = False,
-        help_text = "If you'd like to be cc'ed on the request email")
-
-    def __init__(self, *args, **kwargs):
-        initial =  kwargs.get('initial', {})
-        authority_hrn = initial.get('authority_hrn', None)
-
-        # set just the initial value
-        # in the real form needs something like this {'authority_hrn':'a'}
-        # but in this case you want {'authority_hrn':('a', 'letter_a')}
-        if authority_hrn:
-            kwargs['initial']['authority_hrn'] = authority_hrn[0]
-
-        # create the form
-        super(SliceRequestForm, self).__init__(*args, **kwargs)
-
-        # self.fields only exist after, so a double validation is needed
-        if authority_hrn:# and authority_hrn[0] not in (c[0] for c in authority_hrn):
-            # XXX This does not work, the choicefield is not updated...
-            #self.fields['authority_hrn'].choices.extend(authority_hrn)
-            self.fields['authority_hrn'] = forms.ChoiceField(
-                widget    = forms.Select(attrs={'class':'form-control'}),
-                choices   = authority_hrn,
-                help_text = "An authority responsible for vetting your slice")
+#
+#    slice_name = forms.CharField(
+#        widget=forms.TextInput(attrs={'class':'form-control'}), 
+#        help_text="The name for the slice you wish to create")
+#    authority_hrn = forms.ChoiceField(
+#        widget    = forms.Select(attrs={'class':'form-control'}),
+#        choices   = [],
+#        help_text = "An authority responsible for vetting your slice")
+#    number_of_nodes = forms.DecimalField(
+#        widget    = forms.TextInput(attrs={'class':'form-control'}),
+#        help_text = "The number of nodes you expect to request (informative)")
+#    type_of_nodes = forms.CharField(
+#        widget    = forms.TextInput(attrs={'class':'form-control'}),
+#        help_text = "The type of nodes you expect to request (informative)")
+#    purpose = forms.CharField(
+#        widget    = forms.Textarea(attrs={'class':'form-control'}),
+#        help_text = "The purpose of your experiment (informative)")
+#    email = forms.EmailField(
+#        widget    = forms.TextInput(attrs={'class':'form-control'}),
+#        help_text = "Your email address")
+#    cc_myself = forms.BooleanField(
+#        widget    = forms.CheckboxInput(attrs={'class':'form-control'}),
+#        required  = False,
+#        help_text = "If you'd like to be cc'ed on the request email")
+#
+#    def __init__(self, *args, **kwargs):
+#        initial =  kwargs.get('initial', {})
+#        authority_hrn = initial.get('authority_hrn', None)
+#
+#        # set just the initial value
+#        # in the real form needs something like this {'authority_hrn':'a'}
+#        # but in this case you want {'authority_hrn':('a', 'letter_a')}
+#        if authority_hrn:
+#            kwargs['initial']['authority_hrn'] = authority_hrn[0]
+#
+#        # create the form
+#        super(SliceRequestForm, self).__init__(*args, **kwargs)
+#
+#        # self.fields only exist after, so a double validation is needed
+#        if authority_hrn:# and authority_hrn[0] not in (c[0] for c in authority_hrn):
+#            # XXX This does not work, the choicefield is not updated...
+#            #self.fields['authority_hrn'].choices.extend(authority_hrn)
+#            self.fields['authority_hrn'] = forms.ChoiceField(
+#                widget    = forms.Select(attrs={'class':'form-control'}),
+#                choices   = authority_hrn,
+#                help_text = "An authority responsible for vetting your slice")
 
 
 class PasswordResetForm(forms.Form):
index 1097570..d46ed65 100644 (file)
@@ -5,6 +5,7 @@ from django.contrib.auth import authenticate, login, logout
 from django.template import RequestContext
 from django.shortcuts import render_to_response
 from django.shortcuts import render
+import json
 
 from unfold.loginrequired import FreeAccessView
 
@@ -60,6 +61,26 @@ class HomeView (FreeAccessView, ThemeView):
                 if request.user.is_authenticated(): 
                     env['person'] = self.request.user
                     env['username'] = self.request.user
+                    
+                    ## check user is pi or not
+                    platform_query  = Query().get('local:platform').select('platform_id','platform','gateway_type','disabled')
+                    account_query  = Query().get('local:account').select('user_id','platform_id','auth_type','config')
+                    platform_details = execute_query(self.request, platform_query)
+                    account_details = execute_query(self.request, account_query)
+                    for platform_detail in platform_details:
+                        for account_detail in account_details:
+                            if platform_detail['platform_id'] == account_detail['platform_id']:
+                                if 'config' in account_detail and account_detail['config'] is not '':
+                                    account_config = json.loads(account_detail['config'])
+                                    if 'myslice' in platform_detail['platform']:
+                                        acc_auth_cred = account_config.get('delegated_authority_credentials','N/A')
+                    # assigning values
+                    if acc_auth_cred=={} or acc_auth_cred=='N/A':
+                        pi = "is_not_pi"
+                    else:
+                        pi = "is_pi"
+
+                    env['pi'] = pi                
                 else: 
                     env['person'] = None
                 return render_to_response(self.template,env, context_instance=RequestContext(request))
@@ -76,8 +97,26 @@ class HomeView (FreeAccessView, ThemeView):
 
     def get (self, request, state=None):
         env = self.default_env()
+        if request.user.is_authenticated():
+            ## check user is pi or not
+            platform_query  = Query().get('local:platform').select('platform_id','platform','gateway_type','disabled')
+            account_query  = Query().get('local:account').select('user_id','platform_id','auth_type','config')
+            platform_details = execute_query(self.request, platform_query)
+            account_details = execute_query(self.request, account_query)
+            for platform_detail in platform_details:
+                for account_detail in account_details:
+                    if platform_detail['platform_id'] == account_detail['platform_id']:
+                        if 'config' in account_detail and account_detail['config'] is not '':
+                            account_config = json.loads(account_detail['config'])
+                            if 'myslice' in platform_detail['platform']:
+                                acc_auth_cred = account_config.get('delegated_authority_credentials','N/A')
+            # assigning values
+            if acc_auth_cred=={} or acc_auth_cred=='N/A':
+                pi = "is_not_pi"
+            else:
+                pi = "is_pi"
 
-        if request.user.is_authenticated(): 
+            env['pi'] = pi     
             env['person'] = self.request.user
         else: 
             env['person'] = None
index c53318b..6dd1b20 100644 (file)
@@ -66,7 +66,7 @@ class InstitutionView (LoginRequiredAutoLogoutView, ThemeView):
                             if 'myslice' in platform_detail['platform']:
                                 acc_auth_cred = account_config.get('delegated_authority_credentials','N/A')
             # assigning values
-            if acc_auth_cred == {}:
+            if acc_auth_cred == {} or acc_auth_cred == 'N/A':
                 pi = "is_not_pi"
             else:
                 pi = "is_pi"
index 65d8351..5dd04c2 100644 (file)
@@ -43,8 +43,8 @@ class JoinView (FreeAccessView, ThemeView):
         root_authorities = sorted([a for a in authorities if '.' not in a['authority_hrn']])
 
         page = Page(request)
-        page.add_js_files  ( [ "js/jquery.validate.js", "js/join.js" ] )
-        page.add_css_files ( [ "css/onelab.css", "css/registration.css" ] )
+        page.add_js_files  ( [ "js/jquery.validate.js", "js/join.js", "js/jquery.qtip.min.js" ] )
+        page.add_css_files ( [ "css/onelab.css", "css/registration.css", "css/jquery.qtip.min.css" ] )
         page.add_css_files ( [ "https://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" ] )
 
         if method == 'POST':
@@ -216,8 +216,10 @@ class JoinView (FreeAccessView, ThemeView):
     
                 except Exception, e:
                     print "Failed to send email, please check the mail templates and the SMTP configuration of your server"
-
-                return render(request, 'user_register_complete.html') 
+                
+                self.template_name = 'join_complete.html'
+                return render(request, self.template, {'theme': self.theme})
+                #return render(request, 'user_register_complete.html') 
 
         template_env = {
           'topmenu_items': topmenu_items_live('join', page),
diff --git a/portal/managementtabrequests.py b/portal/managementtabrequests.py
new file mode 100644 (file)
index 0000000..50801e9
--- /dev/null
@@ -0,0 +1,181 @@
+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 django.views.generic.base      import TemplateView
+
+from unfold.loginrequired           import LoginRequiredView
+from django.http import HttpResponse
+from django.shortcuts import render
+
+from manifold.core.query             import Query, AnalyzedQuery
+from manifoldapi.manifoldapi         import execute_query
+
+from portal.actions             import get_requests
+
+from myslice.theme import ThemeView
+
+import json
+
+class ManagementRequestsView (LoginRequiredView, ThemeView):
+    template_name = "management-tab-requests.html"
+    
+    def get_context_data(self, **kwargs):
+       
+        ctx_my_authorities = {}
+        ctx_delegation_authorities = {}
+        ctx_sub_authorities = {}
+        dest = {}
+
+
+        # The user need to be logged in
+        if (self.request.user):
+           
+            user_query = Query().get('local:user').filter_by('email', '==', self.request.user.email).select('user_id')
+            user, = execute_query(self.request, user_query)
+            user_id = user['user_id']
+
+            # Query manifold to learn about available SFA platforms for more information
+            # In general we will at least have the portal
+            # For now we are considering all registries
+            all_authorities = []
+            platform_ids = []
+            sfa_platforms_query = Query().get('local:platform').filter_by('gateway_type', '==', 'sfa').select('platform_id', 'platform', 'auth_type')
+            sfa_platforms = execute_query(self.request, sfa_platforms_query)
+            for sfa_platform in sfa_platforms:
+                print "SFA PLATFORM > ", sfa_platform['platform']
+                if not 'auth_type' in sfa_platform:
+                    continue
+                auth = sfa_platform['auth_type']
+                if not auth in all_authorities:
+                    all_authorities.append(auth)
+                platform_ids.append(sfa_platform['platform_id'])
+
+            print "W: Hardcoding platform myslice"
+            # There has been a tweak on how new platforms are referencing a
+            # so-called 'myslice' platform for storing authentication tokens.
+            # XXX This has to be removed in final versions.
+            myslice_platforms_query = Query().get('local:platform').filter_by('platform', '==', 'myslice').select('platform_id')
+            myslice_platforms = execute_query(self.request, myslice_platforms_query)
+            if myslice_platforms:
+                myslice_platform, = myslice_platforms
+                platform_ids.append(myslice_platform['platform_id'])
+
+            # We can check on which the user has authoritity credentials = PI rights
+            credential_authorities = set()
+            credential_authorities_expired = set()
+
+            # User account on these registries
+            user_accounts_query = Query.get('local:account').filter_by('user_id', '==', user_id).filter_by('platform_id', 'included', platform_ids).select('auth_type', 'config')
+            user_accounts = execute_query(self.request, user_accounts_query)
+            
+            for user_account in user_accounts:
+
+                print "USER ACCOUNT", user_account
+                if user_account['auth_type'] == 'reference':
+                    continue # we hardcoded the myslice platform...
+
+                config = json.loads(user_account['config'])
+                creds = []
+                print "CONFIG KEYS", config.keys()
+                if 'authority_credentials' in config:
+                    print "***", config['authority_credentials'].keys()
+                    for authority_hrn, credential in config['authority_credentials'].items():
+                        credential_authorities.add(authority_hrn)
+                if 'delegated_authority_credentials' in config:
+                    print "***", config['delegated_authority_credentials'].keys()
+                    for authority_hrn, credential in config['delegated_authority_credentials'].items():
+                        credential_authorities.add(authority_hrn)
+
+            print 'credential_authorities =', credential_authorities
+            print 'credential_authorities_expired =', credential_authorities_expired
+
+            # ** Where am I a PI **
+            # For this we need to ask SFA (of all authorities) = PI function
+            pi_authorities_query = Query.get('user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities')
+            pi_authorities_tmp = execute_query(self.request, pi_authorities_query)
+            pi_authorities = set()
+            try:
+                for pa in pi_authorities_tmp:
+                    pi_authorities |= set(pa['pi_authorities'])
+            except:
+                print 'No pi_authorities'
+
+            pi_credential_authorities = pi_authorities & credential_authorities
+            pi_no_credential_authorities = pi_authorities - credential_authorities - credential_authorities_expired
+            pi_expired_credential_authorities = pi_authorities & credential_authorities_expired
+            # Authorities I've been delegated PI rights
+            pi_delegation_credential_authorities = credential_authorities - pi_authorities
+            pi_delegation_expired_authorities = credential_authorities_expired - pi_authorities
+
+            # Summary intermediary
+            pi_my_authorities = pi_credential_authorities | pi_no_credential_authorities | pi_expired_credential_authorities
+            pi_delegation_authorities = pi_delegation_credential_authorities | pi_delegation_expired_authorities
+
+            # Summary all
+            queried_pending_authorities = pi_my_authorities | pi_delegation_authorities #| pi_subauthorities
+
+            # iterate on the requests and check if the authority matches a prefix 
+            # startswith an authority on which the user is PI
+            requests = get_requests()
+            for r in requests:
+                auth_hrn = r['authority_hrn']
+                for my_auth in pi_my_authorities: 
+                    if auth_hrn.startswith(my_auth):
+                        dest = ctx_my_authorities
+                        r['allowed'] = 'allowed'
+                for my_auth in pi_delegation_authorities:
+                    if auth_hrn.startswith(my_auth):
+                        dest = ctx_delegation_authorities
+                        r['allowed'] = 'allowed'
+                if auth_hrn in pi_expired_credential_authorities:
+                    r['allowed'] = 'expired'
+                if 'allowed' not in r:
+                    r['allowed'] = 'denied'
+
+
+                if not auth_hrn in dest:
+                    dest[auth_hrn] = []
+                dest[auth_hrn].append(r)
+                
+              
+#         env = {}
+#         env['my_authorities']   = ctx_my_authorities
+#         env['sub_authorities']   = ctx_sub_authorities
+#         env['delegation_authorities'] = ctx_delegation_authorities
+# 
+#         # XXX This is repeated in all pages
+#         # more general variables expected in the template
+#         # the menu items on the top
+#         #env['topmenu_items'] = topmenu_items_live('Validation', page) 
+#         # so we can sho who is logged
+#         env['username'] = request.user
+#         env['pi'] = "is_pi"       
+#         env['theme'] = self.theme
+#         env['section'] = "Requests"
+        
+        context = super(ManagementRequestsView, self).get_context_data(**kwargs)
+        print "testing"
+        print ctx_my_authorities
+        context['my_authorities']   = ctx_my_authorities
+        context['sub_authorities']   = ctx_sub_authorities
+        context['delegation_authorities'] = ctx_delegation_authorities
+
+        # XXX This is repeated in all pages
+        # more general variables expected in the template
+        context['title'] = 'Test view that combines various plugins'
+        # the menu items on the top
+        #context['topmenu_items'] = topmenu_items_live('Validation', page) 
+        # so we can sho who is logged
+        context['username'] = self.request.user 
+        context['pi'] = "is_pi"       
+        context['theme'] = self.theme
+        context['section'] = "Requests"
+        # XXX We need to prepare the page for queries
+        #context.update(page.prelude_env())
+
+        return context
+    
+        #return render_to_response(self.template, env, context_instance=RequestContext(request))
index f731447..6a8322d 100644 (file)
@@ -45,17 +45,25 @@ class RegistrationView (FreeAccessView, ThemeView):
         
         # Page rendering
         page = Page(wsgi_request)
-        page.add_js_files  ( [ "js/jquery.validate.js", "js/my_account.register.js" ] )
-        page.add_css_files ( [ "css/onelab.css", "css/registration.css" ] )
+        page.add_js_files  ( [ "js/jquery.validate.js", "js/my_account.register.js", "js/jquery.qtip.min.js","js/jquery-ui.js" ] )
+        page.add_css_files ( [ "css/onelab.css", "css/registration.css", "css/jquery.qtip.min.css" ] )
         page.add_css_files ( [ "https://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" ] )
 
         if method == 'POST':
+            reg_form = {}
             # The form has been submitted
             
             # get the domain url
             current_site = Site.objects.get_current()
             current_site = current_site.domain
 
+            authorities_query = Query.get('authority').select('name', 'authority_hrn')
+            authorities = execute_admin_query(wsgi_request, authorities_query)
+    
+            for authority in authorities:
+                if authority['name'] == wsgi_request.POST.get('org_name', ''):
+                    authority_hrn = authority['authority_hrn']     
+
             post_email = wsgi_request.POST.get('email','').lower()
             salt = randint(1,100000)
             email_hash = md5(str(salt)+post_email).hexdigest()
@@ -63,12 +71,13 @@ class RegistrationView (FreeAccessView, ThemeView):
             user_request = {
                 'first_name'    : wsgi_request.POST.get('firstname',     ''),
                 'last_name'     : wsgi_request.POST.get('lastname',      ''),
-                'authority_hrn' : wsgi_request.POST.get('authority_hrn', ''),
+                'organization'  : wsgi_request.POST.get('org_name', ''),
+                'authority_hrn' : authority_hrn, 
                 'email'         : post_email,
                 'password'      : wsgi_request.POST.get('password',      ''),
                 'current_site'  : current_site,
                 'email_hash'    : email_hash,
-                'validation_link': 'https://' + current_site + '/portal/email_activation/'+ email_hash
+                'validation_link': 'http://' + current_site + '/portal/email_activation/'+ email_hash
             }
 
             # Construct user_hrn from email (XXX Should use common code)
@@ -144,6 +153,12 @@ class RegistrationView (FreeAccessView, ThemeView):
 
         else:
             user_request = {}
+            ## this is coming from onelab website onelab.eu
+            reg_form = {
+                'first_name':  wsgi_request.GET.get('first_name', ''),
+                'last_name': wsgi_request.GET.get('last_name', ''),
+                'email': wsgi_request.GET.get('email', ''),
+                }
 
         template_env = {
           'topmenu_items': topmenu_items_live('Register', page),
@@ -152,5 +167,6 @@ class RegistrationView (FreeAccessView, ThemeView):
           'theme': self.theme
           }
         template_env.update(user_request)
+        template_env.update(reg_form)
         template_env.update(page.prelude_env ())
         return render(wsgi_request, self.template,template_env)
index 5eb0725..8cd0af4 100644 (file)
@@ -8,7 +8,7 @@ from manifold.core.query        import Query
 from manifoldapi.manifoldapi    import execute_admin_query, execute_query
 
 from portal.actions             import is_pi, create_slice, create_pending_slice
-from portal.forms               import SliceRequestForm
+#from portal.forms               import SliceRequestForm
 from unfold.loginrequired       import LoginRequiredAutoLogoutView
 from ui.topmenu                 import topmenu_items_live, the_user
 
@@ -38,10 +38,19 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView):
         if authorities is not None:
             authorities = sorted(authorities)
 
-        # Get user_hrn (XXX Would deserve to be simplified)
-        user_query  = Query().get('local:user').select('email')
-        user_emails = execute_query(wsgi_request, user_query)
-        user_email = user_emails[0].get('email')
+        # Get user_email (XXX Would deserve to be simplified)
+        user_query  = Query().get('local:user').select('email','config')
+        user_details = execute_query(wsgi_request, user_query)
+        user_email = user_details[0].get('email')
+        # getting user_hrn
+        for user_detail in user_details:
+            user_config = json.loads(user_detail['config'])
+            user_authority = user_config.get('authority','N/A')              
+        # getting the org from authority        
+        for authority in authorities:
+            if authority['authority_hrn'] == user_authority:
+                authority_name = authority['name']
+
         #
         account_query  = Query().get('local:account').select('user_id','platform_id','auth_type','config')
         account_details = execute_query(wsgi_request, account_query)
@@ -62,7 +71,7 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView):
 
 
         # checking if pi or not
-        if acc_auth_cred == {}:
+        if acc_auth_cred == {} or acc_auth_cred == 'N/A':
             pi = "is_not_pi"
         else:
             pi = "is_pi"
@@ -70,6 +79,7 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView):
 
         # Page rendering
         page = Page(wsgi_request)
+        page.add_js_files  ( [ "js/jquery.validate.js", "js/jquery-ui.js" ] )
         page.add_css_files ( [ "https://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" ] )
 
         if method == 'POST':
@@ -78,6 +88,13 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView):
             # get the domain url
             current_site = Site.objects.get_current()
             current_site = current_site.domain
+            
+            # getting the authority_hrn from the selected organization           
+            for authority in authorities:
+                if authority['name'] == wsgi_request.POST.get('org_name', ''):
+                    authority_hrn = authority['authority_hrn']
+
+            
 
             slice_request = {
                 'type'              : 'slice',
@@ -85,7 +102,8 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView):
                 'user_hrn'          : user_hrn,
                 'email'             : user_email,
                 'timestamp'         : time.time(),
-                'authority_hrn'     : wsgi_request.POST.get('authority_hrn', ''),
+                'authority_hrn'     : authority_hrn,
+                'organization'      : wsgi_request.POST.get('org_name', ''),
                 'slice_name'        : wsgi_request.POST.get('slice_name', ''),
                 'number_of_nodes'   : wsgi_request.POST.get('number_of_nodes', ''),
                 'purpose'           : wsgi_request.POST.get('purpose', ''),
@@ -125,7 +143,8 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView):
             'errors': errors,
             'email': user_email,
             'user_hrn': user_hrn,
-            'pi': pi,        
+            'pi': pi,
+            'authority_name': authority_name,        
             'cc_myself': True,
             'authorities': authorities,
             'theme': self.theme,
index 5b8ce0f..8289f9e 100644 (file)
@@ -1,26 +1,30 @@
-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
+import json
 
-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 QueryUpdaterPlugin
+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,8 +52,9 @@ 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.urn',
                 'resource.hostname', 'resource.type',
                 'resource.network_hrn',
                 'lease.resource',
@@ -67,7 +72,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')
@@ -153,7 +158,6 @@ class SliceResourceView (LoginRequiredView, ThemeView):
             # tab's sons preferably turn this off
             togglable  = False,
             query      = sq_resource,
-            query_all  = query_resource_all,
             # this key is the one issued by google
             googlemap_api_key = ConfigEngine().googlemap_api_key(),
             # the key to use at init-time
@@ -176,24 +180,22 @@ class SliceResourceView (LoginRequiredView, ThemeView):
             # this is the query at the core of the slice list
             query = sq_resource,
             query_lease = sq_lease,
-            query_all_resources = query_resource_all,
-            query_all_leases = query_lease_all,
         )
 
         # --------------------------------------------------------------------------
         # 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,
-        )
+#         pending_resources = QueryUpdaterPlugin(
+            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,
+        )
 
         # --------------------------------------------------------------------------
         # NETWORKS
@@ -202,18 +204,17 @@ class SliceResourceView (LoginRequiredView, ThemeView):
         network_md = metadata.details_by_object('network')
         network_fields = [column['name'] for column in network_md['column']]
 
-        #query_network = Query.get('network').select(network_fields)
-        #page.enqueue_query(query_network)
+        query_networks = Query.get('network').select(network_fields)
+        page.enqueue_query(query_networks)
 
         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_networks  = query_networks,
+            init_key        = "network_hrn",
+            checkboxes      = True,
             datatables_options = {
                 'iDisplayLength': 25,
                 'bLengthChange' : True,
@@ -221,6 +222,19 @@ class SliceResourceView (LoginRequiredView, ThemeView):
                 },
         )
 
+        filter_status = FilterStatusPlugin(
+            page            = page,
+            domid           = "filter-status",
+            query           = sq_resource,
+        )
+        apply = ApplyPlugin(
+            page            = page,
+            domid           = "apply",
+            query           = main_query,
+            username            = request.user,
+        )
+            
+
         # --------------------------------------------------------------------------
         # SLA View and accept dialog
         
@@ -235,7 +249,25 @@ class SliceResourceView (LoginRequiredView, ThemeView):
             outline_complete    = True,
             username            = request.user,
         )
-
+        
+        ## check user is pi or not
+        platform_query  = Query().get('local:platform').select('platform_id','platform','gateway_type','disabled')
+        account_query  = Query().get('local:account').select('user_id','platform_id','auth_type','config')
+        platform_details = execute_query(self.request, platform_query)
+        account_details = execute_query(self.request, account_query)
+        for platform_detail in platform_details:
+            for account_detail in account_details:
+                if platform_detail['platform_id'] == account_detail['platform_id']:
+                    if 'config' in account_detail and account_detail['config'] is not '':
+                        account_config = json.loads(account_detail['config'])
+                        if 'myslice' in platform_detail['platform']:
+                            acc_auth_cred = account_config.get('delegated_authority_credentials','N/A')
+        # assigning values
+        if acc_auth_cred == {} or acc_auth_cred == 'N/A':
+            pi = "is_not_pi"
+        else:
+            pi = "is_pi"
+        
         template_env = {}
         template_env['list_resources'] = list_resources.render(self.request)
         template_env['list_reserved_resources'] = list_reserved_resources.render(self.request)
@@ -244,12 +276,16 @@ 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['sla_dialog'] = sla_dialog.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
+        template_env["pi"] = pi
         template_env["slice"] = slicename
         template_env["section"] = "resources"
         template_env["msg"] = msg
index e79b48e..309347c 100644 (file)
@@ -13,11 +13,28 @@ from django.shortcuts import render
 from unfold.page                     import Page
 from manifold.core.query             import Query, AnalyzedQuery
 from manifoldapi.manifoldapi         import execute_query
-
+import json
 from myslice.theme import ThemeView
 
 class SliceView (LoginRequiredView, ThemeView):
     template_name = "slice-view.html"
     
     def get(self, request, slicename):
-        return render_to_response(self.template, {"slice": slicename, "theme": self.theme, "username": request.user, "section": "Slice %s" % slicename }, context_instance=RequestContext(request))
+        ## check user is pi or not
+        platform_query  = Query().get('local:platform').select('platform_id','platform','gateway_type','disabled')
+        account_query  = Query().get('local:account').select('user_id','platform_id','auth_type','config')
+        platform_details = execute_query(self.request, platform_query)
+        account_details = execute_query(self.request, account_query)
+        for platform_detail in platform_details:
+            for account_detail in account_details:
+                if platform_detail['platform_id'] == account_detail['platform_id']:
+                    if 'config' in account_detail and account_detail['config'] is not '':
+                        account_config = json.loads(account_detail['config'])
+                        if 'myslice' in platform_detail['platform']:
+                            acc_auth_cred = account_config.get('delegated_authority_credentials','N/A')
+        # assigning values
+        if acc_auth_cred == {} or acc_auth_cred == 'N/A':
+            pi = "is_not_pi"
+        else:
+            pi = "is_pi"
+        return render_to_response(self.template, {"slice": slicename, "theme": self.theme, "username": request.user,"pi":pi, "section": "Slice %s" % slicename }, context_instance=RequestContext(request))
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
diff --git a/portal/static/css/jquery.qtip.min.css b/portal/static/css/jquery.qtip.min.css
new file mode 100644 (file)
index 0000000..fc172a4
--- /dev/null
@@ -0,0 +1,2 @@
+/* qTip2 v2.2.0 basic css3 | qtip2.com | Licensed MIT, GPL | Thu Nov 21 2013 20:35:00 */
+.qtip{position:absolute;left:-28000px;top:-28000px;display:none;max-width:280px;min-width:50px;font-size:10.5px;line-height:12px;direction:ltr;box-shadow:none;padding:0}.qtip-content{position:relative;padding:5px 9px;overflow:hidden;text-align:left;word-wrap:break-word}.qtip-titlebar{position:relative;padding:5px 35px 5px 10px;overflow:hidden;border-width:0 0 1px;font-weight:700}.qtip-titlebar+.qtip-content{border-top-width:0!important}.qtip-close{position:absolute;right:-9px;top:-9px;cursor:pointer;outline:medium none;border-width:1px;border-style:solid;border-color:transparent}.qtip-titlebar .qtip-close{right:4px;top:50%;margin-top:-9px}* html .qtip-titlebar .qtip-close{top:16px}.qtip-titlebar .ui-icon,.qtip-icon .ui-icon{display:block;text-indent:-1000em;direction:ltr}.qtip-icon,.qtip-icon .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;text-decoration:none}.qtip-icon .ui-icon{width:18px;height:14px;line-height:14px;text-align:center;text-indent:0;font:400 bold 10px/13px Tahoma,sans-serif;color:inherit;background:transparent none no-repeat -100em -100em}.qtip-focus{}.qtip-hover{}.qtip-default{border-width:1px;border-style:solid;border-color:#F1D031;background-color:#FFFFA3;color:#555}.qtip-default .qtip-titlebar{background-color:#FFEF93}.qtip-default .qtip-icon{border-color:#CCC;background:#F1F1F1;color:#777}.qtip-default .qtip-titlebar .qtip-close{border-color:#AAA;color:#111} .qtip-light{background-color:#fff;border-color:#E2E2E2;color:#454545}.qtip-light .qtip-titlebar{background-color:#f1f1f1} .qtip-dark{background-color:#505050;border-color:#303030;color:#f3f3f3}.qtip-dark .qtip-titlebar{background-color:#404040}.qtip-dark .qtip-icon{border-color:#444}.qtip-dark .qtip-titlebar .ui-state-hover{border-color:#303030} .qtip-cream{background-color:#FBF7AA;border-color:#F9E98E;color:#A27D35}.qtip-cream .qtip-titlebar{background-color:#F0DE7D}.qtip-cream .qtip-close .qtip-icon{background-position:-82px 0} .qtip-red{background-color:#F78B83;border-color:#D95252;color:#912323}.qtip-red .qtip-titlebar{background-color:#F06D65}.qtip-red .qtip-close .qtip-icon{background-position:-102px 0}.qtip-red .qtip-icon{border-color:#D95252}.qtip-red .qtip-titlebar .ui-state-hover{border-color:#D95252} .qtip-green{background-color:#CAED9E;border-color:#90D93F;color:#3F6219}.qtip-green .qtip-titlebar{background-color:#B0DE78}.qtip-green .qtip-close .qtip-icon{background-position:-42px 0} .qtip-blue{background-color:#E5F6FE;border-color:#ADD9ED;color:#5E99BD}.qtip-blue .qtip-titlebar{background-color:#D0E9F5}.qtip-blue .qtip-close .qtip-icon{background-position:-2px 0}.qtip-shadow{-webkit-box-shadow:1px 1px 3px 1px rgba(0,0,0,.15);-moz-box-shadow:1px 1px 3px 1px rgba(0,0,0,.15);box-shadow:1px 1px 3px 1px rgba(0,0,0,.15)}.qtip-rounded,.qtip-tipsy,.qtip-bootstrap{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.qtip-rounded .qtip-titlebar{-moz-border-radius:4px 4px 0 0;-webkit-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.qtip-youtube{-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 3px #333;-moz-box-shadow:0 0 3px #333;box-shadow:0 0 3px #333;color:#fff;border-width:0;background:#4A4A4A;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4A4A4A),color-stop(100%,#000));background-image:-webkit-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-moz-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-ms-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-o-linear-gradient(top,#4A4A4A 0,#000 100%)}.qtip-youtube .qtip-titlebar{background-color:#4A4A4A;background-color:rgba(0,0,0,0)}.qtip-youtube .qtip-content{padding:.75em;font:12px arial,sans-serif;filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#4a4a4a, EndColorStr=#000000);-ms-filter:"progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#4a4a4a, EndColorStr=#000000);"}.qtip-youtube .qtip-icon{border-color:#222}.qtip-youtube .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-jtools{background:#232323;background:rgba(0,0,0,.7);background-image:-webkit-gradient(linear,left top,left bottom,from(#717171),to(#232323));background-image:-moz-linear-gradient(top,#717171,#232323);background-image:-webkit-linear-gradient(top,#717171,#232323);background-image:-ms-linear-gradient(top,#717171,#232323);background-image:-o-linear-gradient(top,#717171,#232323);border:2px solid #ddd;border:2px solid rgba(241,241,241,1);-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 12px #333;-moz-box-shadow:0 0 12px #333;box-shadow:0 0 12px #333}.qtip-jtools .qtip-titlebar{background-color:transparent;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171, endColorstr=#4A4A4A);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171, endColorstr=#4A4A4A)"}.qtip-jtools .qtip-content{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A, endColorstr=#232323);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A, endColorstr=#232323)"}.qtip-jtools .qtip-titlebar,.qtip-jtools .qtip-content{background:transparent;color:#fff;border:0 dashed transparent}.qtip-jtools .qtip-icon{border-color:#555}.qtip-jtools .qtip-titlebar .ui-state-hover{border-color:#333}.qtip-cluetip{-webkit-box-shadow:4px 4px 5px rgba(0,0,0,.4);-moz-box-shadow:4px 4px 5px rgba(0,0,0,.4);box-shadow:4px 4px 5px rgba(0,0,0,.4);background-color:#D9D9C2;color:#111;border:0 dashed transparent}.qtip-cluetip .qtip-titlebar{background-color:#87876A;color:#fff;border:0 dashed transparent}.qtip-cluetip .qtip-icon{border-color:#808064}.qtip-cluetip .qtip-titlebar .ui-state-hover{border-color:#696952;color:#696952}.qtip-tipsy{background:#000;background:rgba(0,0,0,.87);color:#fff;border:0 solid transparent;font-size:11px;font-family:'Lucida Grande',sans-serif;font-weight:700;line-height:16px;text-shadow:0 1px #000}.qtip-tipsy .qtip-titlebar{padding:6px 35px 0 10px;background-color:transparent}.qtip-tipsy .qtip-content{padding:6px 10px}.qtip-tipsy .qtip-icon{border-color:#222;text-shadow:none}.qtip-tipsy .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-tipped{border:3px solid #959FA9;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-color:#F9F9F9;color:#454545;font-weight:400;font-family:serif}.qtip-tipped .qtip-titlebar{border-bottom-width:0;color:#fff;background:#3A79B8;background-image:-webkit-gradient(linear,left top,left bottom,from(#3A79B8),to(#2E629D));background-image:-webkit-linear-gradient(top,#3A79B8,#2E629D);background-image:-moz-linear-gradient(top,#3A79B8,#2E629D);background-image:-ms-linear-gradient(top,#3A79B8,#2E629D);background-image:-o-linear-gradient(top,#3A79B8,#2E629D);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8, endColorstr=#2E629D);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8, endColorstr=#2E629D)"}.qtip-tipped .qtip-icon{border:2px solid #285589;background:#285589}.qtip-tipped .qtip-icon .ui-icon{background-color:#FBFBFB;color:#555}.qtip-bootstrap{font-size:14px;line-height:20px;color:#333;padding:1px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.qtip-bootstrap .qtip-titlebar{padding:8px 14px;margin:0;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.qtip-bootstrap .qtip-titlebar .qtip-close{right:11px;top:45%;border-style:none}.qtip-bootstrap .qtip-content{padding:9px 14px}.qtip-bootstrap .qtip-icon{background:transparent}.qtip-bootstrap .qtip-icon .ui-icon{width:auto;height:auto;float:right;font-size:20px;font-weight:700;line-height:18px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.qtip-bootstrap .qtip-icon .ui-icon:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}.qtip:not(.ie9haxors) div.qtip-content,.qtip:not(.ie9haxors) div.qtip-titlebar{filter:none;-ms-filter:none}.qtip .qtip-tip{margin:0 auto;overflow:hidden;z-index:10}x:-o-prefocus,.qtip .qtip-tip{visibility:hidden}.qtip .qtip-tip,.qtip .qtip-tip .qtip-vml,.qtip .qtip-tip canvas{position:absolute;color:#123456;background:transparent;border:0 dashed transparent}.qtip .qtip-tip canvas{top:0;left:0}.qtip .qtip-tip .qtip-vml{behavior:url(#default#VML);display:inline-block;visibility:visible}#qtip-overlay{position:fixed;left:0;top:0;width:100%;height:100%}#qtip-overlay.blurs{cursor:pointer}#qtip-overlay div{position:absolute;left:0;top:0;width:100%;height:100%;background-color:#000;opacity:.7;filter:alpha(opacity=70);-ms-filter:"alpha(Opacity=70)"}.qtipmodal-ie6fix{position:absolute!important}
\ No newline at end of file
index 434a089..1104c98 100644 (file)
@@ -11,12 +11,13 @@ body {
 }
 a, a:active, a:focus {
     outline: 0;
+    text-decoration:none;
 }
 
 h1 {
     border-bottom:1px solid #DDDDDD;
     padding:0 0 0 0;
-    margin:0 0 0 0;
+    margin:15px 0 15px 0;
     font-size:14pt;
 }
 h1 img {
@@ -32,19 +33,18 @@ h3 {
     font-size:13pt;
     color:#201E62;
 }
-
-div.wrapper {
-    width:980px;
-    margin:0 auto;
-    position:relative;
-}
-div.container {
-    /*width:980px;
-    margin:25px auto;*/
+input[type=text], input[type=password], input[type=email], input[type=tel], input[type=number], select, option {
+    min-width:260px;
+    padding:6px;
+    border:1pt solid #22606D;
+    vertical-align:bottom;
+    border-radius:0;
 }
-div.wide {
-    margin:25px auto;
-    padding:0 25px;
+
+textarea {
+    padding:6px;
+    border:1pt solid #22606D !important;
+    border-radius:0 !important;
 }
 
 span.label {
@@ -53,101 +53,109 @@ span.label {
     font-weight:normal;
     padding:0;
 }
-/***** Notifications *****/
-.warning {
-    border: 1px solid red;
-    margin: 20px 60px;
-    padding: 10px 20px;
-    color: red;
-    background-color: #f2dbdb;
-    text-align: center;
+div.el {
+    padding-bottom:15px;
 }
-/* HEADER */
-#header {
-  -moz-box-shadow:    0 0 1px rgba(82,82,82,0.6);
-  -webkit-box-shadow: 0 0 1px rgba(82,82,82,0.6);
-  box-shadow:         0 0 1px rgba(82,82,82,0.6);
-  
-  height:61px;
-  background-color:white;
-  z-index:2;
+div.breadcrumbs {
+    margin:15px 0;
+    color:gray;
+    font-size:10pt;
 }
-
-
-div#navigation {
-    background-color:black;
-    width:100%;
-    height:40px;
+/* buttons */
+button.btn, input.btn {
+    padding:6px 10px;
+    border-radius:5px;
+    font-size:10pt;
+    font-weight:normal;
 }
-div#navigation div.wrapper {
-    text-align:center;
+button.btn span.glyphicon {
+    margin-right:6px;
 }
-div#navigation ul {
-    margin:0;
-    padding:0;
-    display: inline-block;
-    list-style-type: none;
-    white-space: nowrap;
+button.btn-default {
+    border-bottom:3px solid #cccccc;
 }
-
-div#navigation li {
-    color:white;
-    font-family:helvetica, sans-serif;
-    font-size:10pt ;
-    font-weight:normal;
-    line-height:0.8em;
-    letter-spacing:0.6pt;
-    list-style:none;
-    float:left;
-    padding:0;
-    margin:15px 50px 0 0;
+button.btn-default:hover {
+    background-color:white;
+    border:1px solid #ADADAD;
+    border-bottom:3px solid #ADADAD;
 }
-div#navigation li a {
+button.btn-default:active {
+    background-color:white;
+    border:1px solid #ADADAD;
+    border-bottom:1px solid #ADADAD;
+    margin-top:2px;
+    box-shadow:none;
+}
+button.btn-primary {
+    border-bottom:3px solid #3071A9;
+}
+button.btn-primary:hover {
+    box-shadow:none;
+    background-color:#428bca;
+    border:1px solid #357ebd;
+    border-bottom:3px solid #3071A9;
+}
+button.btn-primary:active {
+    box-shadow:none;
+    border-bottom:1px solid #3071A9;
+    margin-top:2px;
+}
+button.btn-danger {
+    border-bottom:3px solid #A13F3A;
+}
+button.btn-danger:hover {
+    box-shadow:none;
+    background-color:#d9534f;
+    border:1px solid #d43f3a;
+    border-bottom:3px solid #A13F3A;
+}
+button.btn-danger:active {
+    border:1px solid #d43f3a;
+    box-shadow:none;
+    margin-top:2px;
+}
+button.btn-onelab, input.btn-onelab {
+    border:0;
+    border-bottom:3px solid #760073;
+    background-color:#302562;
     color:white;
 }
-div#navigation li a:hover {
-    text-decoration:none;
-    color:#B8B2FF;
+button.btn-onelab:hover, input.btn-onelab:hover {
+    border:0;
+    border-bottom:3px solid #760073;
+    background-color:#302562;
+    color:white;
 }
-div#navigation li:last-child {
-    margin-right:0;
+button.btn-onelab:active, input.btn-onelab:active {
+    box-shadow:none;
+    border-bottom:1px solid #760073;
+    margin-top:2px;
 }
 
-div#navigation li.active {
-       padding:5px;
-    padding-top: 15px;
-    min-height: 40px;
-    margin-top: 0px;
-       background-color:gray;
-       color:#FF0000;
-}
-div#navigation li.slices {
-    position:relative;
-    cursor:pointer;
+.container-resource button {
+    padding:2px 4px;
+    border-radius:3px;
+    font-size:9pt;
+    font-weight:normal;
 }
-div.navigation .dropdown-menu {
-    color:black;
-    padding:0 15px 15px 15px;
-    margin-top:5px;
-    margin-left:-16px;
+.container-resource select,.container-resource option, .container-resource input {
+    padding:2px 4px;
+    font-size:9pt;
 }
-div.navigation .dropdown-menu a {
-    color:black;
+.badge {
+    font-size:9pt;
+    margin-left:4px;
 }
-
-div.menu-slice-list {
-    display:none;
-    position:absolute;
-    padding:15px;
-    left:-15px;
-    z-index:10;
+/***** Notifications *****/
+.warning {
+    border: 1px solid red;
+    margin: 20px 60px;
+    padding: 10px 20px;
+    color: red;
+    background-color: #f2dbdb;
+    text-align: center;
 }
 
-div.navigation .dropdown-menu li:first-child {
-    border-bottom:1px solid white;
-    padding-bottom:5px;
-     
-}
 /* HOME DASHBOARD */
 div#home-dashboard {
     color:black;
@@ -228,15 +236,6 @@ div#home-dashboard div#home-slice-list li {
 div.well {
 }
 /**/
-/* BUTTON */
-.btn.btn-default {
-    font-weight: bold;
-}
-
-.btn.btn-default:hover {
-    font-weight: bold;
-}
-/**/
 /* TABLE */
 table.table {
     margin:0;
@@ -285,26 +284,22 @@ div#ticket-request p {
     margin:20px 0;
 }
 
-ul.nav-tabs {}
+ul.nav-tabs {
+    margin:0 0 15px 0;
+}
 ul.nav-tabs ul {}
 ul.nav-tabs li {}
 
 ul.nav-section li a {
+    color:black;
     border-bottom:0;
 }
 ul.nav-section li:first-child {
     padding:0;
 }
 ul.nav-section li:first-child a {
-    font-weight:bold;
-    padding:6px 15px 4px 15px;
 }
 ul.nav-section li:first-child.active a {
-    padding:6px 15px 3px 15px;
-}
-ul.nav-section li:first-child img {
-    margin:0 4px 1px 0;
-    padding:0;
 }
 
 ul.nav-resources {
@@ -315,8 +310,13 @@ ul.nav-resources a {
 }
 
 /* SLICE VIEW */
-
-
+div.container-resource, div.container-slice {
+    padding-right:15px;
+    padding-left:15px;
+}
+.table th {
+    border-top:0 !important;
+}
 div#slice-view {
     margin:0;
 }
@@ -359,6 +359,23 @@ a.list-group-item p.list-group-item-text {
     color: black !important;
 }
 
+span.sl-resources {
+    font-size:9pt;
+    color:gray;
+}
+a.sl-resources, a.sl-resources:hover {
+    font-size:9pt;
+    border:0;
+    padding:2px 4px;
+    -moz-border-radius: 4px;
+    border-radius: 4px;
+}
+a.sl-resources.active, a.sl-resources.active:hover, a.sl-resources.active:focus {
+    border:0;
+    -moz-border-radius: 4px;
+    border-radius: 4px;
+}
+
 div#slice-info {
     margin-top:25px;
 }
@@ -448,7 +465,9 @@ div.dataTables_filter label{
     width:400px;
 }
 
-/**/
+/* HEADER */
+
+
 .header {
   -moz-box-shadow:    0 0 1px rgba(82,82,82,0.6);
   -webkit-box-shadow: 0 0 1px rgba(82,82,82,0.6);
@@ -456,6 +475,7 @@ div.dataTables_filter label{
   height:61px;
   background-color:white;
 }
+
 div.navigation {
     
 }
@@ -493,6 +513,50 @@ div.navigation li a:hover, div.navigation li a.current {
 div.navigation li:last-child {
     margin-right:0;
 }
+
+
+div.navigation .dropdown-menu {
+    color:black;
+    -moz-box-shadow:    1px 1px 0px 0 rgba(58, 48, 100,0.8);
+    -webkit-box-shadow: 1px 1px 0px 0 rgba(58, 48, 100,0.8);
+    box-shadow:         1px 1px 0px 0 rgba(58, 48, 100,0.8);
+    border-radius:2px;
+    padding:0 5px 5px 5px;
+    margin-top:5px;
+    margin-left:20px;
+}
+div.navigation .dropdown-menu ul {
+    margin:0;
+    padding:15px 5px 5px 5px;
+    display:list-item;
+}
+div.navigation .dropdown-menu li {
+    margin:0 10px 0 0;
+    padding:0 0 8px 0;
+    display:list-item;
+    float:none;
+    text-transform: none;
+}
+
+div.navigation .dropdown-menu a {
+    font-family:Helvetica,sans-serif;
+    font-size:10pt;
+    color:black;
+}
+div.navigation .dropdown-menu li.title {
+    margin-bottom:10px;
+}
+div.navigation .dropdown-menu li.title a {
+    font-family:open_sansbold, sans-serif;
+}
+
+
+div.navigation .dropdown-menu li:first-child {
+    border-bottom:1px solid white;
+    padding-bottom:5px;
+     
+}
+
 div.secondary {
     text-align:right;
 }
@@ -574,25 +638,12 @@ div.registration-form {
     padding-top:150px;
     text-align:center;
 }
-div.login-form input[type=text] {
-    
-    
 
-}
 .login-form input {
-    font-size:12pt;
     width:320px;
-    padding:8px;
-    border:1pt solid #22606D;
 }
 .login-form input[type=submit] {
-    border:0;
-    border-bottom:2px solid #540086;
-    background-color:#302562;
-    color:white;
-    width:100px;
-    border-radius:5px;
-    font-size:12pt;
+    width:108px;
 }
 
 .login-submit {
index 193a00a..44d8c75 100644 (file)
@@ -1,5 +1,7 @@
 jQuery(document).ready(function(){
 
+    jQuery('[title!=""]').qtip();
+    
     jQuery("#joinForm").validate({
         rules: {
           pi_password: {
diff --git a/portal/static/js/jquery-ui.js b/portal/static/js/jquery-ui.js
new file mode 100755 (executable)
index 0000000..b2fdf86
--- /dev/null
@@ -0,0 +1,13 @@
+/*! jQuery UI - v1.11.0 - 2014-06-26
+* http://jqueryui.com
+* Includes: core.js, widget.js, mouse.js, position.js, accordion.js, autocomplete.js, button.js, datepicker.js, dialog.js, draggable.js, droppable.js, effect.js, effect-blind.js, effect-bounce.js, effect-clip.js, effect-drop.js, effect-explode.js, effect-fade.js, effect-fold.js, effect-highlight.js, effect-puff.js, effect-pulsate.js, effect-scale.js, effect-shake.js, effect-size.js, effect-slide.js, effect-transfer.js, menu.js, progressbar.js, resizable.js, selectable.js, selectmenu.js, slider.js, sortable.js, spinner.js, tabs.js, tooltip.js
+* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
+
+(function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function t(t,s){var n,a,o,r=t.nodeName.toLowerCase();return"area"===r?(n=t.parentNode,a=n.name,t.href&&a&&"map"===n.nodeName.toLowerCase()?(o=e("img[usemap=#"+a+"]")[0],!!o&&i(o)):!1):(/input|select|textarea|button|object/.test(r)?!t.disabled:"a"===r?t.href||s:s)&&i(t)}function i(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}function s(e){for(var t,i;e.length&&e[0]!==document;){if(t=e.css("position"),("absolute"===t||"relative"===t||"fixed"===t)&&(i=parseInt(e.css("zIndex"),10),!isNaN(i)&&0!==i))return i;e=e.parent()}return 0}function n(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},e.extend(this._defaults,this.regional[""]),this.regional.en=e.extend(!0,{},this.regional[""]),this.regional["en-US"]=e.extend(!0,{},this.regional.en),this.dpDiv=a(e("<div id='"+this._mainDivId+"' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"))}function a(t){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return t.delegate(i,"mouseout",function(){e(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).removeClass("ui-datepicker-next-hover")}).delegate(i,"mouseover",function(){e.datepicker._isDisabledDatepicker(g.inline?t.parent()[0]:g.input[0])||(e(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),e(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).addClass("ui-datepicker-next-hover"))})}function o(t,i){e.extend(t,i);for(var s in i)null==i[s]&&(t[s]=i[s]);return t}function r(e){return function(){var t=this.element.val();e.apply(this,arguments),this._refresh(),t!==this.element.val()&&this._trigger("change")}}e.ui=e.ui||{},e.extend(e.ui,{version:"1.11.0",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({scrollParent:function(){var t=this.css("position"),i="absolute"===t,s=this.parents().filter(function(){var t=e(this);return i&&"static"===t.css("position")?!1:/(auto|scroll)/.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==t&&s.length?s:e(this[0].ownerDocument||document)},uniqueId:function(){var e=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++e)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(i){return t(i,!isNaN(e.attr(i,"tabindex")))},tabbable:function(i){var s=e.attr(i,"tabindex"),n=isNaN(s);return(n||s>=0)&&t(i,!n)}}),e("<a>").outerWidth(1).jquery||e.each(["Width","Height"],function(t,i){function s(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"padding"+this))||0,s&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],a=i.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+i]=function(t){return void 0===t?o["inner"+i].call(this):this.each(function(){e(this).css(a,s(this,t)+"px")})},e.fn["outer"+i]=function(t,n){return"number"!=typeof t?o["outer"+i].call(this,t):this.each(function(){e(this).css(a,s(this,t,!0,n)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("<a>").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.fn.extend({focus:function(t){return function(i,s){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),disableSelection:function(){var e="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.bind(e+".ui-disableSelection",function(e){e.preventDefault()})}}(),enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(t){if(void 0!==t)return this.css("zIndex",t);if(this.length)for(var i,s,n=e(this[0]);n.length&&n[0]!==document;){if(i=n.css("position"),("absolute"===i||"relative"===i||"fixed"===i)&&(s=parseInt(n.css("zIndex"),10),!isNaN(s)&&0!==s))return s;n=n.parent()}return 0}}),e.ui.plugin={add:function(t,i,s){var n,a=e.ui[t].prototype;for(n in s)a.plugins[n]=a.plugins[n]||[],a.plugins[n].push([i,s[n]])},call:function(e,t,i,s){var n,a=e.plugins[t];if(a&&(s||e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType))for(n=0;a.length>n;n++)e.options[a[n][0]]&&a[n][1].apply(e.element,i)}};var h=0,l=Array.prototype.slice;e.cleanData=function(t){return function(i){for(var s,n=0;null!=(s=i[n]);n++)try{e(s).triggerHandler("remove")}catch(a){}t(i)}}(e.cleanData),e.widget=function(t,i,s){var n,a,o,r,h={},l=t.split(".")[0];return t=t.split(".")[1],n=l+"-"+t,s||(s=i,i=e.Widget),e.expr[":"][n.toLowerCase()]=function(t){return!!e.data(t,n)},e[l]=e[l]||{},a=e[l][t],o=e[l][t]=function(e,t){return this._createWidget?(arguments.length&&this._createWidget(e,t),void 0):new o(e,t)},e.extend(o,a,{version:s.version,_proto:e.extend({},s),_childConstructors:[]}),r=new i,r.options=e.widget.extend({},r.options),e.each(s,function(t,s){return e.isFunction(s)?(h[t]=function(){var e=function(){return i.prototype[t].apply(this,arguments)},n=function(e){return i.prototype[t].apply(this,e)};return function(){var t,i=this._super,a=this._superApply;return this._super=e,this._superApply=n,t=s.apply(this,arguments),this._super=i,this._superApply=a,t}}(),void 0):(h[t]=s,void 0)}),o.prototype=e.widget.extend(r,{widgetEventPrefix:a?r.widgetEventPrefix||t:t},h,{constructor:o,namespace:l,widgetName:t,widgetFullName:n}),a?(e.each(a._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete a._childConstructors):i._childConstructors.push(o),e.widget.bridge(t,o),o},e.widget.extend=function(t){for(var i,s,n=l.call(arguments,1),a=0,o=n.length;o>a;a++)for(i in n[a])s=n[a][i],n[a].hasOwnProperty(i)&&void 0!==s&&(t[i]=e.isPlainObject(s)?e.isPlainObject(t[i])?e.widget.extend({},t[i],s):e.widget.extend({},s):s);return t},e.widget.bridge=function(t,i){var s=i.prototype.widgetFullName||t;e.fn[t]=function(n){var a="string"==typeof n,o=l.call(arguments,1),r=this;return n=!a&&o.length?e.widget.extend.apply(null,[n].concat(o)):n,a?this.each(function(){var i,a=e.data(this,s);return"instance"===n?(r=a,!1):a?e.isFunction(a[n])&&"_"!==n.charAt(0)?(i=a[n].apply(a,o),i!==a&&void 0!==i?(r=i&&i.jquery?r.pushStack(i.get()):i,!1):void 0):e.error("no such method '"+n+"' for "+t+" widget instance"):e.error("cannot call methods on "+t+" prior to initialization; "+"attempted to call method '"+n+"'")}):this.each(function(){var t=e.data(this,s);t?(t.option(n||{}),t._init&&t._init()):e.data(this,s,new i(n,this))}),r}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(t,i){i=e(i||this.defaultElement||this)[0],this.element=e(i),this.uuid=h++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this.bindings=e(),this.hoverable=e(),this.focusable=e(),i!==this&&(e.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===i&&this.destroy()}}),this.document=e(i.style?i.ownerDocument:i.document||i),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(t,i){var s,n,a,o=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(o={},s=t.split("."),t=s.shift(),s.length){for(n=o[t]=e.widget.extend({},this.options[t]),a=0;s.length-1>a;a++)n[s[a]]=n[s[a]]||{},n=n[s[a]];if(t=s.pop(),1===arguments.length)return void 0===n[t]?null:n[t];n[t]=i}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];o[t]=i}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!t),t&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(t,i,s){var n,a=this;"boolean"!=typeof t&&(s=i,i=t,t=!1),s?(i=n=e(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),e.each(s,function(s,o){function r(){return t||a.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?a[o]:o).apply(a,arguments):void 0}"string"!=typeof o&&(r.guid=o.guid=o.guid||r.guid||e.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+a.eventNamespace,u=h[2];u?n.delegate(u,l,r):i.bind(l,r)})},_off:function(e,t){t=(t||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.unbind(t).undelegate(t)},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,o=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(o)&&o.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var o,r=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),o=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),o&&e.effects&&e.effects.effect[r]?s[t](n):r!==t&&s[r]?s[r](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}}),e.widget;var u=!1;e(document).mouseup(function(){u=!1}),e.widget("ui.mouse",{version:"1.11.0",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(i){return!0===e.data(i.target,t.widgetName+".preventClickEvent")?(e.removeData(i.target,t.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(t){if(!u){this._mouseStarted&&this._mouseUp(t),this._mouseDownEvent=t;var i=this,s=1===t.which,n="string"==typeof this.options.cancel&&t.target.nodeName?e(t.target).closest(this.options.cancel).length:!1;return s&&!n&&this._mouseCapture(t)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(t)!==!1,!this._mouseStarted)?(t.preventDefault(),!0):(!0===e.data(t.target,this.widgetName+".preventClickEvent")&&e.removeData(t.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return i._mouseMove(e)},this._mouseUpDelegate=function(e){return i._mouseUp(e)},this.document.bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),t.preventDefault(),u=!0,!0)):!0}},_mouseMove:function(t){return e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button?this._mouseUp(t):t.which?this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted):this._mouseUp(t)},_mouseUp:function(t){return this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),u=!1,!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),function(){function t(e,t,i){return[parseFloat(e[0])*(p.test(e[0])?t/100:1),parseFloat(e[1])*(p.test(e[1])?i/100:1)]}function i(t,i){return parseInt(e.css(t,i),10)||0}function s(t){var i=t[0];return 9===i.nodeType?{width:t.width(),height:t.height(),offset:{top:0,left:0}}:e.isWindow(i)?{width:t.width(),height:t.height(),offset:{top:t.scrollTop(),left:t.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:t.outerWidth(),height:t.outerHeight(),offset:t.offset()}}e.ui=e.ui||{};var n,a,o=Math.max,r=Math.abs,h=Math.round,l=/left|center|right/,u=/top|center|bottom/,d=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,p=/%$/,f=e.fn.position;e.position={scrollbarWidth:function(){if(void 0!==n)return n;var t,i,s=e("<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),a=s.children()[0];return e("body").append(s),t=a.offsetWidth,s.css("overflow","scroll"),i=a.offsetWidth,t===i&&(i=s[0].clientWidth),s.remove(),n=t-i},getScrollInfo:function(t){var i=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),s=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),n="scroll"===i||"auto"===i&&t.width<t.element[0].scrollWidth,a="scroll"===s||"auto"===s&&t.height<t.element[0].scrollHeight;return{width:a?e.position.scrollbarWidth():0,height:n?e.position.scrollbarWidth():0}},getWithinInfo:function(t){var i=e(t||window),s=e.isWindow(i[0]),n=!!i[0]&&9===i[0].nodeType;return{element:i,isWindow:s,isDocument:n,offset:i.offset()||{left:0,top:0},scrollLeft:i.scrollLeft(),scrollTop:i.scrollTop(),width:s?i.width():i.outerWidth(),height:s?i.height():i.outerHeight()}}},e.fn.position=function(n){if(!n||!n.of)return f.apply(this,arguments);n=e.extend({},n);var p,m,g,v,y,b,_=e(n.of),x=e.position.getWithinInfo(n.within),w=e.position.getScrollInfo(x),k=(n.collision||"flip").split(" "),T={};return b=s(_),_[0].preventDefault&&(n.at="left top"),m=b.width,g=b.height,v=b.offset,y=e.extend({},v),e.each(["my","at"],function(){var e,t,i=(n[this]||"").split(" ");1===i.length&&(i=l.test(i[0])?i.concat(["center"]):u.test(i[0])?["center"].concat(i):["center","center"]),i[0]=l.test(i[0])?i[0]:"center",i[1]=u.test(i[1])?i[1]:"center",e=d.exec(i[0]),t=d.exec(i[1]),T[this]=[e?e[0]:0,t?t[0]:0],n[this]=[c.exec(i[0])[0],c.exec(i[1])[0]]}),1===k.length&&(k[1]=k[0]),"right"===n.at[0]?y.left+=m:"center"===n.at[0]&&(y.left+=m/2),"bottom"===n.at[1]?y.top+=g:"center"===n.at[1]&&(y.top+=g/2),p=t(T.at,m,g),y.left+=p[0],y.top+=p[1],this.each(function(){var s,l,u=e(this),d=u.outerWidth(),c=u.outerHeight(),f=i(this,"marginLeft"),b=i(this,"marginTop"),D=d+f+i(this,"marginRight")+w.width,S=c+b+i(this,"marginBottom")+w.height,M=e.extend({},y),N=t(T.my,u.outerWidth(),u.outerHeight());"right"===n.my[0]?M.left-=d:"center"===n.my[0]&&(M.left-=d/2),"bottom"===n.my[1]?M.top-=c:"center"===n.my[1]&&(M.top-=c/2),M.left+=N[0],M.top+=N[1],a||(M.left=h(M.left),M.top=h(M.top)),s={marginLeft:f,marginTop:b},e.each(["left","top"],function(t,i){e.ui.position[k[t]]&&e.ui.position[k[t]][i](M,{targetWidth:m,targetHeight:g,elemWidth:d,elemHeight:c,collisionPosition:s,collisionWidth:D,collisionHeight:S,offset:[p[0]+N[0],p[1]+N[1]],my:n.my,at:n.at,within:x,elem:u})}),n.using&&(l=function(e){var t=v.left-M.left,i=t+m-d,s=v.top-M.top,a=s+g-c,h={target:{element:_,left:v.left,top:v.top,width:m,height:g},element:{element:u,left:M.left,top:M.top,width:d,height:c},horizontal:0>i?"left":t>0?"right":"center",vertical:0>a?"top":s>0?"bottom":"middle"};d>m&&m>r(t+i)&&(h.horizontal="center"),c>g&&g>r(s+a)&&(h.vertical="middle"),h.important=o(r(t),r(i))>o(r(s),r(a))?"horizontal":"vertical",n.using.call(this,e,h)}),u.offset(e.extend(M,{using:l}))})},e.ui.position={fit:{left:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=e.left-t.collisionPosition.marginLeft,h=n-r,l=r+t.collisionWidth-a-n;t.collisionWidth>a?h>0&&0>=l?(i=e.left+h+t.collisionWidth-a-n,e.left+=h-i):e.left=l>0&&0>=h?n:h>l?n+a-t.collisionWidth:n:h>0?e.left+=h:l>0?e.left-=l:e.left=o(e.left-r,e.left)},top:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollTop:s.offset.top,a=t.within.height,r=e.top-t.collisionPosition.marginTop,h=n-r,l=r+t.collisionHeight-a-n;t.collisionHeight>a?h>0&&0>=l?(i=e.top+h+t.collisionHeight-a-n,e.top+=h-i):e.top=l>0&&0>=h?n:h>l?n+a-t.collisionHeight:n:h>0?e.top+=h:l>0?e.top-=l:e.top=o(e.top-r,e.top)}},flip:{left:function(e,t){var i,s,n=t.within,a=n.offset.left+n.scrollLeft,o=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=e.left-t.collisionPosition.marginLeft,u=l-h,d=l+t.collisionWidth-o-h,c="left"===t.my[0]?-t.elemWidth:"right"===t.my[0]?t.elemWidth:0,p="left"===t.at[0]?t.targetWidth:"right"===t.at[0]?-t.targetWidth:0,f=-2*t.offset[0];0>u?(i=e.left+c+p+f+t.collisionWidth-o-a,(0>i||r(u)>i)&&(e.left+=c+p+f)):d>0&&(s=e.left-t.collisionPosition.marginLeft+c+p+f-h,(s>0||d>r(s))&&(e.left+=c+p+f))},top:function(e,t){var i,s,n=t.within,a=n.offset.top+n.scrollTop,o=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=e.top-t.collisionPosition.marginTop,u=l-h,d=l+t.collisionHeight-o-h,c="top"===t.my[1],p=c?-t.elemHeight:"bottom"===t.my[1]?t.elemHeight:0,f="top"===t.at[1]?t.targetHeight:"bottom"===t.at[1]?-t.targetHeight:0,m=-2*t.offset[1];0>u?(s=e.top+p+f+m+t.collisionHeight-o-a,e.top+p+f+m>u&&(0>s||r(u)>s)&&(e.top+=p+f+m)):d>0&&(i=e.top-t.collisionPosition.marginTop+p+f+m-h,e.top+p+f+m>d&&(i>0||d>r(i))&&(e.top+=p+f+m))}},flipfit:{left:function(){e.ui.position.flip.left.apply(this,arguments),e.ui.position.fit.left.apply(this,arguments)},top:function(){e.ui.position.flip.top.apply(this,arguments),e.ui.position.fit.top.apply(this,arguments)}}},function(){var t,i,s,n,o,r=document.getElementsByTagName("body")[0],h=document.createElement("div");t=document.createElement(r?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},r&&e.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(o in s)t.style[o]=s[o];t.appendChild(h),i=r||document.documentElement,i.insertBefore(t,i.firstChild),h.style.cssText="position: absolute; left: 10.7432222px;",n=e(h).offset().left,a=n>10&&11>n,t.innerHTML="",i.removeChild(t)}()}(),e.ui.position,e.widget("ui.accordion",{version:"1.11.0",options:{active:0,animate:{},collapsible:!1,event:"click",header:"> li > :first-child,> :not(li):even",heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},hideProps:{borderTopWidth:"hide",borderBottomWidth:"hide",paddingTop:"hide",paddingBottom:"hide",height:"hide"},showProps:{borderTopWidth:"show",borderBottomWidth:"show",paddingTop:"show",paddingBottom:"show",height:"show"},_create:function(){var t=this.options;this.prevShow=this.prevHide=e(),this.element.addClass("ui-accordion ui-widget ui-helper-reset").attr("role","tablist"),t.collapsible||t.active!==!1&&null!=t.active||(t.active=0),this._processPanels(),0>t.active&&(t.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():e()}},_createIcons:function(){var t=this.options.icons;t&&(e("<span>").addClass("ui-accordion-header-icon ui-icon "+t.header).prependTo(this.headers),this.active.children(".ui-accordion-header-icon").removeClass(t.header).addClass(t.activeHeader),this.headers.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.removeClass("ui-accordion-icons").children(".ui-accordion-header-icon").remove()},_destroy:function(){var e;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.removeClass("ui-accordion-header ui-accordion-header-active ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("aria-controls").removeAttr("tabIndex").removeUniqueId(),this._destroyIcons(),e=this.headers.next().removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled").css("display","").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeUniqueId(),"content"!==this.options.heightStyle&&e.css("height","")},_setOption:function(e,t){return"active"===e?(this._activate(t),void 0):("event"===e&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(t)),this._super(e,t),"collapsible"!==e||t||this.options.active!==!1||this._activate(0),"icons"===e&&(this._destroyIcons(),t&&this._createIcons()),"disabled"===e&&(this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this.headers.add(this.headers.next()).toggleClass("ui-state-disabled",!!t)),void 0)},_keydown:function(t){if(!t.altKey&&!t.ctrlKey){var i=e.ui.keyCode,s=this.headers.length,n=this.headers.index(t.target),a=!1;switch(t.keyCode){case i.RIGHT:case i.DOWN:a=this.headers[(n+1)%s];break;case i.LEFT:case i.UP:a=this.headers[(n-1+s)%s];break;case i.SPACE:case i.ENTER:this._eventHandler(t);break;case i.HOME:a=this.headers[0];break;case i.END:a=this.headers[s-1]}a&&(e(t.target).attr("tabIndex",-1),e(a).attr("tabIndex",0),a.focus(),t.preventDefault())}},_panelKeyDown:function(t){t.keyCode===e.ui.keyCode.UP&&t.ctrlKey&&e(t.currentTarget).prev().focus()},refresh:function(){var t=this.options;this._processPanels(),t.active===!1&&t.collapsible===!0||!this.headers.length?(t.active=!1,this.active=e()):t.active===!1?this._activate(0):this.active.length&&!e.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(t.active=!1,this.active=e()):this._activate(Math.max(0,t.active-1)):t.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){this.headers=this.element.find(this.options.header).addClass("ui-accordion-header ui-state-default ui-corner-all"),this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom").filter(":not(.ui-accordion-content-active)").hide()},_refresh:function(){var t,i=this.options,s=i.heightStyle,n=this.element.parent();this.active=this._findActive(i.active).addClass("ui-accordion-header-active ui-state-active ui-corner-top").removeClass("ui-corner-all"),this.active.next().addClass("ui-accordion-content-active").show(),this.headers.attr("role","tab").each(function(){var t=e(this),i=t.uniqueId().attr("id"),s=t.next(),n=s.uniqueId().attr("id");t.attr("aria-controls",n),s.attr("aria-labelledby",i)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}).next().attr({"aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}).next().attr({"aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(i.event),"fill"===s?(t=n.height(),this.element.siblings(":visible").each(function(){var i=e(this),s=i.css("position");"absolute"!==s&&"fixed"!==s&&(t-=i.outerHeight(!0))}),this.headers.each(function(){t-=e(this).outerHeight(!0)}),this.headers.next().each(function(){e(this).height(Math.max(0,t-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===s&&(t=0,this.headers.next().each(function(){t=Math.max(t,e(this).css("height","").height())}).height(t))},_activate:function(t){var i=this._findActive(t)[0];i!==this.active[0]&&(i=i||this.active[0],this._eventHandler({target:i,currentTarget:i,preventDefault:e.noop}))},_findActive:function(t){return"number"==typeof t?this.headers.eq(t):e()},_setupEvents:function(t){var i={keydown:"_keydown"};t&&e.each(t.split(" "),function(e,t){i[t]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(t){var i=this.options,s=this.active,n=e(t.currentTarget),a=n[0]===s[0],o=a&&i.collapsible,r=o?e():n.next(),h=s.next(),l={oldHeader:s,oldPanel:h,newHeader:o?e():n,newPanel:r};t.preventDefault(),a&&!i.collapsible||this._trigger("beforeActivate",t,l)===!1||(i.active=o?!1:this.headers.index(n),this.active=a?e():n,this._toggle(l),s.removeClass("ui-accordion-header-active ui-state-active"),i.icons&&s.children(".ui-accordion-header-icon").removeClass(i.icons.activeHeader).addClass(i.icons.header),a||(n.removeClass("ui-corner-all").addClass("ui-accordion-header-active ui-state-active ui-corner-top"),i.icons&&n.children(".ui-accordion-header-icon").removeClass(i.icons.header).addClass(i.icons.activeHeader),n.next().addClass("ui-accordion-content-active")))},_toggle:function(t){var i=t.newPanel,s=this.prevShow.length?this.prevShow:t.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=i,this.prevHide=s,this.options.animate?this._animate(i,s,t):(s.hide(),i.show(),this._toggleComplete(t)),s.attr({"aria-hidden":"true"}),s.prev().attr("aria-selected","false"),i.length&&s.length?s.prev().attr({tabIndex:-1,"aria-expanded":"false"}):i.length&&this.headers.filter(function(){return 0===e(this).attr("tabIndex")}).attr("tabIndex",-1),i.attr("aria-hidden","false").prev().attr({"aria-selected":"true",tabIndex:0,"aria-expanded":"true"})},_animate:function(e,t,i){var s,n,a,o=this,r=0,h=e.length&&(!t.length||e.index()<t.index()),l=this.options.animate||{},u=h&&l.down||l,d=function(){o._toggleComplete(i)};return"number"==typeof u&&(a=u),"string"==typeof u&&(n=u),n=n||u.easing||l.easing,a=a||u.duration||l.duration,t.length?e.length?(s=e.show().outerHeight(),t.animate(this.hideProps,{duration:a,easing:n,step:function(e,t){t.now=Math.round(e)}}),e.hide().animate(this.showProps,{duration:a,easing:n,complete:d,step:function(e,i){i.now=Math.round(e),"height"!==i.prop?r+=i.now:"content"!==o.options.heightStyle&&(i.now=Math.round(s-t.outerHeight()-r),r=0)}}),void 0):t.animate(this.hideProps,a,n,d):e.animate(this.showProps,a,n,d)},_toggleComplete:function(e){var t=e.oldPanel;t.removeClass("ui-accordion-content-active").prev().removeClass("ui-corner-top").addClass("ui-corner-all"),t.length&&(t.parent()[0].className=t.parent()[0].className),this._trigger("activate",null,e)}}),e.widget("ui.menu",{version:"1.11.0",defaultElement:"<ul>",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},items:"> *",menus:"ul",position:{my:"left-1 top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item":function(e){e.preventDefault()},"click .ui-menu-item":function(t){var i=e(t.target);!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(t),t.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(t):!this.element.is(":focus")&&e(this.document[0].activeElement).closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(t){var i=e(t.currentTarget);i.siblings(".ui-state-active").removeClass("ui-state-active"),this.focus(t,i)},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(e,t){var i=this.active||this.element.find(this.options.items).eq(0);t||this.focus(e,i)},blur:function(t){this._delay(function(){e.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(t)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(e){this._closeOnDocumentClick(e)&&this.collapseAll(e),this.mouseHandled=!1
+}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeClass("ui-menu ui-widget ui-widget-content ui-menu-icons ui-front").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").removeUniqueId().removeClass("ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var t=e(this);t.data("ui-menu-submenu-carat")&&t.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(t){function i(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}var s,n,a,o,r,h=!0;switch(t.keyCode){case e.ui.keyCode.PAGE_UP:this.previousPage(t);break;case e.ui.keyCode.PAGE_DOWN:this.nextPage(t);break;case e.ui.keyCode.HOME:this._move("first","first",t);break;case e.ui.keyCode.END:this._move("last","last",t);break;case e.ui.keyCode.UP:this.previous(t);break;case e.ui.keyCode.DOWN:this.next(t);break;case e.ui.keyCode.LEFT:this.collapse(t);break;case e.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(t);break;case e.ui.keyCode.ENTER:case e.ui.keyCode.SPACE:this._activate(t);break;case e.ui.keyCode.ESCAPE:this.collapse(t);break;default:h=!1,n=this.previousFilter||"",a=String.fromCharCode(t.keyCode),o=!1,clearTimeout(this.filterTimer),a===n?o=!0:a=n+a,r=RegExp("^"+i(a),"i"),s=this.activeMenu.find(this.options.items).filter(function(){return r.test(e(this).text())}),s=o&&-1!==s.index(this.active.next())?this.active.nextAll(".ui-menu-item"):s,s.length||(a=String.fromCharCode(t.keyCode),r=RegExp("^"+i(a),"i"),s=this.activeMenu.find(this.options.items).filter(function(){return r.test(e(this).text())})),s.length?(this.focus(t,s),s.length>1?(this.previousFilter=a,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter):delete this.previousFilter}h&&t.preventDefault()},_activate:function(e){this.active.is(".ui-state-disabled")||(this.active.is("[aria-haspopup='true']")?this.expand(e):this.select(e))},refresh:function(){var t,i,s=this,n=this.options.icons.submenu,a=this.element.find(this.options.menus);this.element.toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length),a.filter(":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-front").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var t=e(this),i=t.parent(),s=e("<span>").addClass("ui-menu-icon ui-icon "+n).data("ui-menu-submenu-carat",!0);i.attr("aria-haspopup","true").prepend(s),t.attr("aria-labelledby",i.attr("id"))}),t=a.add(this.element),i=t.find(this.options.items),i.not(".ui-menu-item").each(function(){var t=e(this);s._isDivider(t)&&t.addClass("ui-widget-content ui-menu-divider")}),i.not(".ui-menu-item, .ui-menu-divider").addClass("ui-menu-item").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),i.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!e.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(e,t){"icons"===e&&this.element.find(".ui-menu-icon").removeClass(this.options.icons.submenu).addClass(t.submenu),"disabled"===e&&this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this._super(e,t)},focus:function(e,t){var i,s;this.blur(e,e&&"focus"===e.type),this._scrollIntoView(t),this.active=t.first(),s=this.active.addClass("ui-state-focus").removeClass("ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),this.active.parent().closest(".ui-menu-item").addClass("ui-state-active"),e&&"keydown"===e.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=t.children(".ui-menu"),i.length&&e&&/^mouse/.test(e.type)&&this._startOpening(i),this.activeMenu=t.parent(),this._trigger("focus",e,{item:t})},_scrollIntoView:function(t){var i,s,n,a,o,r;this._hasScroll()&&(i=parseFloat(e.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(e.css(this.activeMenu[0],"paddingTop"))||0,n=t.offset().top-this.activeMenu.offset().top-i-s,a=this.activeMenu.scrollTop(),o=this.activeMenu.height(),r=t.outerHeight(),0>n?this.activeMenu.scrollTop(a+n):n+r>o&&this.activeMenu.scrollTop(a+n-o+r))},blur:function(e,t){t||clearTimeout(this.timer),this.active&&(this.active.removeClass("ui-state-focus"),this.active=null,this._trigger("blur",e,{item:this.active}))},_startOpening:function(e){clearTimeout(this.timer),"true"===e.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(e)},this.delay))},_open:function(t){var i=e.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(t.parents(".ui-menu")).hide().attr("aria-hidden","true"),t.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(t,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:e(t&&t.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(t),this.activeMenu=s},this.delay)},_close:function(e){e||(e=this.active?this.active.parent():this.element),e.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find(".ui-state-active").not(".ui-state-focus").removeClass("ui-state-active")},_closeOnDocumentClick:function(t){return!e(t.target).closest(".ui-menu").length},_isDivider:function(e){return!/[^\-\u2014\u2013\s]/.test(e.text())},collapse:function(e){var t=this.active&&this.active.parent().closest(".ui-menu-item",this.element);t&&t.length&&(this._close(),this.focus(e,t))},expand:function(e){var t=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();t&&t.length&&(this._open(t.parent()),this._delay(function(){this.focus(e,t)}))},next:function(e){this._move("next","first",e)},previous:function(e){this._move("prev","last",e)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(e,t,i){var s;this.active&&(s="first"===e||"last"===e?this.active["first"===e?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[e+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.find(this.options.items)[t]()),this.focus(i,s)},nextPage:function(t){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=e(this),0>i.offset().top-s-n}),this.focus(t,i)):this.focus(t,this.activeMenu.find(this.options.items)[this.active?"last":"first"]())),void 0):(this.next(t),void 0)},previousPage:function(t){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=e(this),i.offset().top-s+n>0}),this.focus(t,i)):this.focus(t,this.activeMenu.find(this.options.items).first())),void 0):(this.next(t),void 0)},_hasScroll:function(){return this.element.outerHeight()<this.element.prop("scrollHeight")},select:function(t){this.active=this.active||e(t.target).closest(".ui-menu-item");var i={item:this.active};this.active.has(".ui-menu").length||this.collapseAll(t,!0),this._trigger("select",t,i)}}),e.widget("ui.autocomplete",{version:"1.11.0",defaultElement:"<input>",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var t,i,s,n=this.element[0].nodeName.toLowerCase(),a="textarea"===n,o="input"===n;this.isMultiLine=a?!0:o?!1:this.element.prop("isContentEditable"),this.valueMethod=this.element[a||o?"val":"text"],this.isNewMenu=!0,this.element.addClass("ui-autocomplete-input").attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return t=!0,s=!0,i=!0,void 0;t=!1,s=!1,i=!1;var a=e.ui.keyCode;switch(n.keyCode){case a.PAGE_UP:t=!0,this._move("previousPage",n);break;case a.PAGE_DOWN:t=!0,this._move("nextPage",n);break;case a.UP:t=!0,this._keyEvent("previous",n);break;case a.DOWN:t=!0,this._keyEvent("next",n);break;case a.ENTER:this.menu.active&&(t=!0,n.preventDefault(),this.menu.select(n));break;case a.TAB:this.menu.active&&this.menu.select(n);break;case a.ESCAPE:this.menu.element.is(":visible")&&(this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(t)return t=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),void 0;if(!i){var n=e.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(e){return s?(s=!1,e.preventDefault(),void 0):(this._searchTimeout(e),void 0)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,void 0):(clearTimeout(this.searching),this.close(e),this._change(e),void 0)}}),this._initSource(),this.menu=e("<ul>").addClass("ui-autocomplete ui-front").appendTo(this._appendTo()).menu({role:null}).hide().menu("instance"),this._on(this.menu.element,{mousedown:function(t){t.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur});var i=this.menu.element[0];e(t.target).closest(".ui-menu-item").length||this._delay(function(){var t=this;this.document.one("mousedown",function(s){s.target===t.element[0]||s.target===i||e.contains(i,s.target)||t.close()})})},menufocus:function(t,i){var s,n;return this.isNewMenu&&(this.isNewMenu=!1,t.originalEvent&&/^mouse/.test(t.originalEvent.type))?(this.menu.blur(),this.document.one("mousemove",function(){e(t.target).trigger(t.originalEvent)}),void 0):(n=i.item.data("ui-autocomplete-item"),!1!==this._trigger("focus",t,{item:n})&&t.originalEvent&&/^key/.test(t.originalEvent.type)&&this._value(n.value),s=i.item.attr("aria-label")||n.value,s&&jQuery.trim(s).length&&(this.liveRegion.children().hide(),e("<div>").text(s).appendTo(this.liveRegion)),void 0)},menuselect:function(e,t){var i=t.item.data("ui-autocomplete-item"),s=this.previous;this.element[0]!==this.document[0].activeElement&&(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s,this.selectedItem=i})),!1!==this._trigger("select",e,{item:i})&&this._value(i.value),this.term=this._value(),this.close(e),this.selectedItem=i}}),this.liveRegion=e("<span>",{role:"status","aria-live":"assertive","aria-relevant":"additions"}).addClass("ui-helper-hidden-accessible").appendTo(this.document[0].body),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(e,t){this._super(e,t),"source"===e&&this._initSource(),"appendTo"===e&&this.menu.element.appendTo(this._appendTo()),"disabled"===e&&t&&this.xhr&&this.xhr.abort()},_appendTo:function(){var t=this.options.appendTo;return t&&(t=t.jquery||t.nodeType?e(t):this.document.find(t).eq(0)),t&&t[0]||(t=this.element.closest(".ui-front")),t.length||(t=this.document[0].body),t},_initSource:function(){var t,i,s=this;e.isArray(this.options.source)?(t=this.options.source,this.source=function(i,s){s(e.ui.autocomplete.filter(t,i.term))}):"string"==typeof this.options.source?(i=this.options.source,this.source=function(t,n){s.xhr&&s.xhr.abort(),s.xhr=e.ajax({url:i,data:t,dataType:"json",success:function(e){n(e)},error:function(){n([])}})}):this.source=this.options.source},_searchTimeout:function(e){clearTimeout(this.searching),this.searching=this._delay(function(){var t=this.term===this._value(),i=this.menu.element.is(":visible"),s=e.altKey||e.ctrlKey||e.metaKey||e.shiftKey;(!t||t&&!i&&!s)&&(this.selectedItem=null,this.search(null,e))},this.options.delay)},search:function(e,t){return e=null!=e?e:this._value(),this.term=this._value(),e.length<this.options.minLength?this.close(t):this._trigger("search",t)!==!1?this._search(e):void 0},_search:function(e){this.pending++,this.element.addClass("ui-autocomplete-loading"),this.cancelSearch=!1,this.source({term:e},this._response())},_response:function(){var t=++this.requestIndex;return e.proxy(function(e){t===this.requestIndex&&this.__response(e),this.pending--,this.pending||this.element.removeClass("ui-autocomplete-loading")},this)},__response:function(e){e&&(e=this._normalize(e)),this._trigger("response",null,{content:e}),!this.options.disabled&&e&&e.length&&!this.cancelSearch?(this._suggest(e),this._trigger("open")):this._close()},close:function(e){this.cancelSearch=!0,this._close(e)},_close:function(e){this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.blur(),this.isNewMenu=!0,this._trigger("close",e))},_change:function(e){this.previous!==this._value()&&this._trigger("change",e,{item:this.selectedItem})},_normalize:function(t){return t.length&&t[0].label&&t[0].value?t:e.map(t,function(t){return"string"==typeof t?{label:t,value:t}:e.extend({},t,{label:t.label||t.value,value:t.value||t.label})})},_suggest:function(t){var i=this.menu.element.empty();this._renderMenu(i,t),this.isNewMenu=!0,this.menu.refresh(),i.show(),this._resizeMenu(),i.position(e.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next()},_resizeMenu:function(){var e=this.menu.element;e.outerWidth(Math.max(e.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(t,i){var s=this;e.each(i,function(e,i){s._renderItemData(t,i)})},_renderItemData:function(e,t){return this._renderItem(e,t).data("ui-autocomplete-item",t)},_renderItem:function(t,i){return e("<li>").text(i.label).appendTo(t)},_move:function(e,t){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(e)||this.menu.isLastItem()&&/^next/.test(e)?(this.isMultiLine||this._value(this.term),this.menu.blur(),void 0):(this.menu[e](t),void 0):(this.search(null,t),void 0)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(e,t){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(e,t),t.preventDefault())}}),e.extend(e.ui.autocomplete,{escapeRegex:function(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(t,i){var s=RegExp(e.ui.autocomplete.escapeRegex(i),"i");return e.grep(t,function(e){return s.test(e.label||e.value||e)})}}),e.widget("ui.autocomplete",e.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(e){return e+(e>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(t){var i;this._superApply(arguments),this.options.disabled||this.cancelSearch||(i=t&&t.length?this.options.messages.results(t.length):this.options.messages.noResults,this.liveRegion.children().hide(),e("<div>").text(i).appendTo(this.liveRegion))}}),e.ui.autocomplete;var d,c="ui-button ui-widget ui-state-default ui-corner-all",p="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",f=function(){var t=e(this);setTimeout(function(){t.find(":ui-button").button("refresh")},1)},m=function(t){var i=t.name,s=t.form,n=e([]);return i&&(i=i.replace(/'/g,"\\'"),n=s?e(s).find("[name='"+i+"'][type=radio]"):e("[name='"+i+"'][type=radio]",t.ownerDocument).filter(function(){return!this.form})),n};e.widget("ui.button",{version:"1.11.0",defaultElement:"<button>",options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset"+this.eventNamespace).bind("reset"+this.eventNamespace,f),"boolean"!=typeof this.options.disabled?this.options.disabled=!!this.element.prop("disabled"):this.element.prop("disabled",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var t=this,i=this.options,s="checkbox"===this.type||"radio"===this.type,n=s?"":"ui-state-active";null===i.label&&(i.label="input"===this.type?this.buttonElement.val():this.buttonElement.html()),this._hoverable(this.buttonElement),this.buttonElement.addClass(c).attr("role","button").bind("mouseenter"+this.eventNamespace,function(){i.disabled||this===d&&e(this).addClass("ui-state-active")}).bind("mouseleave"+this.eventNamespace,function(){i.disabled||e(this).removeClass(n)}).bind("click"+this.eventNamespace,function(e){i.disabled&&(e.preventDefault(),e.stopImmediatePropagation())}),this._on({focus:function(){this.buttonElement.addClass("ui-state-focus")},blur:function(){this.buttonElement.removeClass("ui-state-focus")}}),s&&this.element.bind("change"+this.eventNamespace,function(){t.refresh()}),"checkbox"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){return i.disabled?!1:void 0}):"radio"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){if(i.disabled)return!1;e(this).addClass("ui-state-active"),t.buttonElement.attr("aria-pressed","true");var s=t.element[0];m(s).not(s).map(function(){return e(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown"+this.eventNamespace,function(){return i.disabled?!1:(e(this).addClass("ui-state-active"),d=this,t.document.one("mouseup",function(){d=null}),void 0)}).bind("mouseup"+this.eventNamespace,function(){return i.disabled?!1:(e(this).removeClass("ui-state-active"),void 0)}).bind("keydown"+this.eventNamespace,function(t){return i.disabled?!1:((t.keyCode===e.ui.keyCode.SPACE||t.keyCode===e.ui.keyCode.ENTER)&&e(this).addClass("ui-state-active"),void 0)}).bind("keyup"+this.eventNamespace+" blur"+this.eventNamespace,function(){e(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(t){t.keyCode===e.ui.keyCode.SPACE&&e(this).click()})),this._setOption("disabled",i.disabled),this._resetButton()},_determineButtonType:function(){var e,t,i;this.type=this.element.is("[type=checkbox]")?"checkbox":this.element.is("[type=radio]")?"radio":this.element.is("input")?"input":"button","checkbox"===this.type||"radio"===this.type?(e=this.element.parents().last(),t="label[for='"+this.element.attr("id")+"']",this.buttonElement=e.find(t),this.buttonElement.length||(e=e.length?e.siblings():this.element.siblings(),this.buttonElement=e.filter(t),this.buttonElement.length||(this.buttonElement=e.find(t))),this.element.addClass("ui-helper-hidden-accessible"),i=this.element.is(":checked"),i&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.prop("aria-pressed",i)):this.buttonElement=this.element},widget:function(){return this.buttonElement},_destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(c+" ui-state-active "+p).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title")},_setOption:function(e,t){return this._super(e,t),"disabled"===e?(this.widget().toggleClass("ui-state-disabled",!!t),this.element.prop("disabled",!!t),t&&("checkbox"===this.type||"radio"===this.type?this.buttonElement.removeClass("ui-state-focus"):this.buttonElement.removeClass("ui-state-focus ui-state-active")),void 0):(this._resetButton(),void 0)},refresh:function(){var t=this.element.is("input, button")?this.element.is(":disabled"):this.element.hasClass("ui-button-disabled");t!==this.options.disabled&&this._setOption("disabled",t),"radio"===this.type?m(this.element[0]).each(function(){e(this).is(":checked")?e(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):e(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):"checkbox"===this.type&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if("input"===this.type)return this.options.label&&this.element.val(this.options.label),void 0;var t=this.buttonElement.removeClass(p),i=e("<span></span>",this.document[0]).addClass("ui-button-text").html(this.options.label).appendTo(t.empty()).text(),s=this.options.icons,n=s.primary&&s.secondary,a=[];s.primary||s.secondary?(this.options.text&&a.push("ui-button-text-icon"+(n?"s":s.primary?"-primary":"-secondary")),s.primary&&t.prepend("<span class='ui-button-icon-primary ui-icon "+s.primary+"'></span>"),s.secondary&&t.append("<span class='ui-button-icon-secondary ui-icon "+s.secondary+"'></span>"),this.options.text||(a.push(n?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||t.attr("title",e.trim(i)))):a.push("ui-button-text-only"),t.addClass(a.join(" "))}}),e.widget("ui.buttonset",{version:"1.11.0",options:{items:"button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(e,t){"disabled"===e&&this.buttons.button("option",e,t),this._super(e,t)},refresh:function(){var t="rtl"===this.element.css("direction"),i=this.element.find(this.options.items),s=i.filter(":ui-button");i.not(":ui-button").button(),s.button("refresh"),this.buttons=i.map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(t?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(t?"ui-corner-left":"ui-corner-right").end().end()},_destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy")}}),e.ui.button,e.extend(e.ui,{datepicker:{version:"1.11.0"}});var g;e.extend(n.prototype,{markerClassName:"hasDatepicker",maxRows:4,_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(e){return o(this._defaults,e||{}),this},_attachDatepicker:function(t,i){var s,n,a;s=t.nodeName.toLowerCase(),n="div"===s||"span"===s,t.id||(this.uuid+=1,t.id="dp"+this.uuid),a=this._newInst(e(t),n),a.settings=e.extend({},i||{}),"input"===s?this._connectDatepicker(t,a):n&&this._inlineDatepicker(t,a)},_newInst:function(t,i){var s=t[0].id.replace(/([^A-Za-z0-9_\-])/g,"\\\\$1");return{id:s,input:t,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:i,dpDiv:i?a(e("<div class='"+this._inlineClass+" ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")):this.dpDiv}},_connectDatepicker:function(t,i){var s=e(t);i.append=e([]),i.trigger=e([]),s.hasClass(this.markerClassName)||(this._attachments(s,i),s.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp),this._autoSize(i),e.data(t,"datepicker",i),i.settings.disabled&&this._disableDatepicker(t))},_attachments:function(t,i){var s,n,a,o=this._get(i,"appendText"),r=this._get(i,"isRTL");i.append&&i.append.remove(),o&&(i.append=e("<span class='"+this._appendClass+"'>"+o+"</span>"),t[r?"before":"after"](i.append)),t.unbind("focus",this._showDatepicker),i.trigger&&i.trigger.remove(),s=this._get(i,"showOn"),("focus"===s||"both"===s)&&t.focus(this._showDatepicker),("button"===s||"both"===s)&&(n=this._get(i,"buttonText"),a=this._get(i,"buttonImage"),i.trigger=e(this._get(i,"buttonImageOnly")?e("<img/>").addClass(this._triggerClass).attr({src:a,alt:n,title:n}):e("<button type='button'></button>").addClass(this._triggerClass).html(a?e("<img/>").attr({src:a,alt:n,title:n}):n)),t[r?"before":"after"](i.trigger),i.trigger.click(function(){return e.datepicker._datepickerShowing&&e.datepicker._lastInput===t[0]?e.datepicker._hideDatepicker():e.datepicker._datepickerShowing&&e.datepicker._lastInput!==t[0]?(e.datepicker._hideDatepicker(),e.datepicker._showDatepicker(t[0])):e.datepicker._showDatepicker(t[0]),!1}))},_autoSize:function(e){if(this._get(e,"autoSize")&&!e.inline){var t,i,s,n,a=new Date(2009,11,20),o=this._get(e,"dateFormat");o.match(/[DM]/)&&(t=function(e){for(i=0,s=0,n=0;e.length>n;n++)e[n].length>i&&(i=e[n].length,s=n);return s},a.setMonth(t(this._get(e,o.match(/MM/)?"monthNames":"monthNamesShort"))),a.setDate(t(this._get(e,o.match(/DD/)?"dayNames":"dayNamesShort"))+20-a.getDay())),e.input.attr("size",this._formatDate(e,a).length)}},_inlineDatepicker:function(t,i){var s=e(t);s.hasClass(this.markerClassName)||(s.addClass(this.markerClassName).append(i.dpDiv),e.data(t,"datepicker",i),this._setDate(i,this._getDefaultDate(i),!0),this._updateDatepicker(i),this._updateAlternate(i),i.settings.disabled&&this._disableDatepicker(t),i.dpDiv.css("display","block"))},_dialogDatepicker:function(t,i,s,n,a){var r,h,l,u,d,c=this._dialogInst;return c||(this.uuid+=1,r="dp"+this.uuid,this._dialogInput=e("<input type='text' id='"+r+"' style='position: absolute; top: -100px; width: 0px;'/>"),this._dialogInput.keydown(this._doKeyDown),e("body").append(this._dialogInput),c=this._dialogInst=this._newInst(this._dialogInput,!1),c.settings={},e.data(this._dialogInput[0],"datepicker",c)),o(c.settings,n||{}),i=i&&i.constructor===Date?this._formatDate(c,i):i,this._dialogInput.val(i),this._pos=a?a.length?a:[a.pageX,a.pageY]:null,this._pos||(h=document.documentElement.clientWidth,l=document.documentElement.clientHeight,u=document.documentElement.scrollLeft||document.body.scrollLeft,d=document.documentElement.scrollTop||document.body.scrollTop,this._pos=[h/2-100+u,l/2-150+d]),this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),c.settings.onSelect=s,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),e.blockUI&&e.blockUI(this.dpDiv),e.data(this._dialogInput[0],"datepicker",c),this},_destroyDatepicker:function(t){var i,s=e(t),n=e.data(t,"datepicker");s.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),e.removeData(t,"datepicker"),"input"===i?(n.append.remove(),n.trigger.remove(),s.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):("div"===i||"span"===i)&&s.removeClass(this.markerClassName).empty())},_enableDatepicker:function(t){var i,s,n=e(t),a=e.data(t,"datepicker");n.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),"input"===i?(t.disabled=!1,a.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().removeClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}))},_disableDatepicker:function(t){var i,s,n=e(t),a=e.data(t,"datepicker");n.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),"input"===i?(t.disabled=!0,a.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().addClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}),this._disabledInputs[this._disabledInputs.length]=t)},_isDisabledDatepicker:function(e){if(!e)return!1;for(var t=0;this._disabledInputs.length>t;t++)if(this._disabledInputs[t]===e)return!0;return!1},_getInst:function(t){try{return e.data(t,"datepicker")}catch(i){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(t,i,s){var n,a,r,h,l=this._getInst(t);return 2===arguments.length&&"string"==typeof i?"defaults"===i?e.extend({},e.datepicker._defaults):l?"all"===i?e.extend({},l.settings):this._get(l,i):null:(n=i||{},"string"==typeof i&&(n={},n[i]=s),l&&(this._curInst===l&&this._hideDatepicker(),a=this._getDateDatepicker(t,!0),r=this._getMinMaxDate(l,"min"),h=this._getMinMaxDate(l,"max"),o(l.settings,n),null!==r&&void 0!==n.dateFormat&&void 0===n.minDate&&(l.settings.minDate=this._formatDate(l,r)),null!==h&&void 0!==n.dateFormat&&void 0===n.maxDate&&(l.settings.maxDate=this._formatDate(l,h)),"disabled"in n&&(n.disabled?this._disableDatepicker(t):this._enableDatepicker(t)),this._attachments(e(t),l),this._autoSize(l),this._setDate(l,a),this._updateAlternate(l),this._updateDatepicker(l)),void 0)},_changeDatepicker:function(e,t,i){this._optionDatepicker(e,t,i)},_refreshDatepicker:function(e){var t=this._getInst(e);t&&this._updateDatepicker(t)},_setDateDatepicker:function(e,t){var i=this._getInst(e);i&&(this._setDate(i,t),this._updateDatepicker(i),this._updateAlternate(i))},_getDateDatepicker:function(e,t){var i=this._getInst(e);return i&&!i.inline&&this._setDateFromField(i,t),i?this._getDate(i):null},_doKeyDown:function(t){var i,s,n,a=e.datepicker._getInst(t.target),o=!0,r=a.dpDiv.is(".ui-datepicker-rtl");if(a._keyEvent=!0,e.datepicker._datepickerShowing)switch(t.keyCode){case 9:e.datepicker._hideDatepicker(),o=!1;break;case 13:return n=e("td."+e.datepicker._dayOverClass+":not(."+e.datepicker._currentClass+")",a.dpDiv),n[0]&&e.datepicker._selectDay(t.target,a.selectedMonth,a.selectedYear,n[0]),i=e.datepicker._get(a,"onSelect"),i?(s=e.datepicker._formatDate(a),i.apply(a.input?a.input[0]:null,[s,a])):e.datepicker._hideDatepicker(),!1;case 27:e.datepicker._hideDatepicker();break;case 33:e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(a,"stepBigMonths"):-e.datepicker._get(a,"stepMonths"),"M");break;case 34:e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(a,"stepBigMonths"):+e.datepicker._get(a,"stepMonths"),"M");break;case 35:(t.ctrlKey||t.metaKey)&&e.datepicker._clearDate(t.target),o=t.ctrlKey||t.metaKey;break;case 36:(t.ctrlKey||t.metaKey)&&e.datepicker._gotoToday(t.target),o=t.ctrlKey||t.metaKey;break;case 37:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,r?1:-1,"D"),o=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(a,"stepBigMonths"):-e.datepicker._get(a,"stepMonths"),"M");break;case 38:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,-7,"D"),o=t.ctrlKey||t.metaKey;break;case 39:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,r?-1:1,"D"),o=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(a,"stepBigMonths"):+e.datepicker._get(a,"stepMonths"),"M");break;case 40:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,7,"D"),o=t.ctrlKey||t.metaKey;break;default:o=!1}else 36===t.keyCode&&t.ctrlKey?e.datepicker._showDatepicker(this):o=!1;o&&(t.preventDefault(),t.stopPropagation())},_doKeyPress:function(t){var i,s,n=e.datepicker._getInst(t.target);return e.datepicker._get(n,"constrainInput")?(i=e.datepicker._possibleChars(e.datepicker._get(n,"dateFormat")),s=String.fromCharCode(null==t.charCode?t.keyCode:t.charCode),t.ctrlKey||t.metaKey||" ">s||!i||i.indexOf(s)>-1):void 0},_doKeyUp:function(t){var i,s=e.datepicker._getInst(t.target);if(s.input.val()!==s.lastVal)try{i=e.datepicker.parseDate(e.datepicker._get(s,"dateFormat"),s.input?s.input.val():null,e.datepicker._getFormatConfig(s)),i&&(e.datepicker._setDateFromField(s),e.datepicker._updateAlternate(s),e.datepicker._updateDatepicker(s))
+}catch(n){}return!0},_showDatepicker:function(t){if(t=t.target||t,"input"!==t.nodeName.toLowerCase()&&(t=e("input",t.parentNode)[0]),!e.datepicker._isDisabledDatepicker(t)&&e.datepicker._lastInput!==t){var i,n,a,r,h,l,u;i=e.datepicker._getInst(t),e.datepicker._curInst&&e.datepicker._curInst!==i&&(e.datepicker._curInst.dpDiv.stop(!0,!0),i&&e.datepicker._datepickerShowing&&e.datepicker._hideDatepicker(e.datepicker._curInst.input[0])),n=e.datepicker._get(i,"beforeShow"),a=n?n.apply(t,[t,i]):{},a!==!1&&(o(i.settings,a),i.lastVal=null,e.datepicker._lastInput=t,e.datepicker._setDateFromField(i),e.datepicker._inDialog&&(t.value=""),e.datepicker._pos||(e.datepicker._pos=e.datepicker._findPos(t),e.datepicker._pos[1]+=t.offsetHeight),r=!1,e(t).parents().each(function(){return r|="fixed"===e(this).css("position"),!r}),h={left:e.datepicker._pos[0],top:e.datepicker._pos[1]},e.datepicker._pos=null,i.dpDiv.empty(),i.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),e.datepicker._updateDatepicker(i),h=e.datepicker._checkOffset(i,h,r),i.dpDiv.css({position:e.datepicker._inDialog&&e.blockUI?"static":r?"fixed":"absolute",display:"none",left:h.left+"px",top:h.top+"px"}),i.inline||(l=e.datepicker._get(i,"showAnim"),u=e.datepicker._get(i,"duration"),i.dpDiv.css("z-index",s(e(t))+1),e.datepicker._datepickerShowing=!0,e.effects&&e.effects.effect[l]?i.dpDiv.show(l,e.datepicker._get(i,"showOptions"),u):i.dpDiv[l||"show"](l?u:null),e.datepicker._shouldFocusInput(i)&&i.input.focus(),e.datepicker._curInst=i))}},_updateDatepicker:function(t){this.maxRows=4,g=t,t.dpDiv.empty().append(this._generateHTML(t)),this._attachHandlers(t),t.dpDiv.find("."+this._dayOverClass+" a");var i,s=this._getNumberOfMonths(t),n=s[1],a=17;t.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),n>1&&t.dpDiv.addClass("ui-datepicker-multi-"+n).css("width",a*n+"em"),t.dpDiv[(1!==s[0]||1!==s[1]?"add":"remove")+"Class"]("ui-datepicker-multi"),t.dpDiv[(this._get(t,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),t===e.datepicker._curInst&&e.datepicker._datepickerShowing&&e.datepicker._shouldFocusInput(t)&&t.input.focus(),t.yearshtml&&(i=t.yearshtml,setTimeout(function(){i===t.yearshtml&&t.yearshtml&&t.dpDiv.find("select.ui-datepicker-year:first").replaceWith(t.yearshtml),i=t.yearshtml=null},0))},_shouldFocusInput:function(e){return e.input&&e.input.is(":visible")&&!e.input.is(":disabled")&&!e.input.is(":focus")},_checkOffset:function(t,i,s){var n=t.dpDiv.outerWidth(),a=t.dpDiv.outerHeight(),o=t.input?t.input.outerWidth():0,r=t.input?t.input.outerHeight():0,h=document.documentElement.clientWidth+(s?0:e(document).scrollLeft()),l=document.documentElement.clientHeight+(s?0:e(document).scrollTop());return i.left-=this._get(t,"isRTL")?n-o:0,i.left-=s&&i.left===t.input.offset().left?e(document).scrollLeft():0,i.top-=s&&i.top===t.input.offset().top+r?e(document).scrollTop():0,i.left-=Math.min(i.left,i.left+n>h&&h>n?Math.abs(i.left+n-h):0),i.top-=Math.min(i.top,i.top+a>l&&l>a?Math.abs(a+r):0),i},_findPos:function(t){for(var i,s=this._getInst(t),n=this._get(s,"isRTL");t&&("hidden"===t.type||1!==t.nodeType||e.expr.filters.hidden(t));)t=t[n?"previousSibling":"nextSibling"];return i=e(t).offset(),[i.left,i.top]},_hideDatepicker:function(t){var i,s,n,a,o=this._curInst;!o||t&&o!==e.data(t,"datepicker")||this._datepickerShowing&&(i=this._get(o,"showAnim"),s=this._get(o,"duration"),n=function(){e.datepicker._tidyDialog(o)},e.effects&&(e.effects.effect[i]||e.effects[i])?o.dpDiv.hide(i,e.datepicker._get(o,"showOptions"),s,n):o.dpDiv["slideDown"===i?"slideUp":"fadeIn"===i?"fadeOut":"hide"](i?s:null,n),i||n(),this._datepickerShowing=!1,a=this._get(o,"onClose"),a&&a.apply(o.input?o.input[0]:null,[o.input?o.input.val():"",o]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),e.blockUI&&(e.unblockUI(),e("body").append(this.dpDiv))),this._inDialog=!1)},_tidyDialog:function(e){e.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(t){if(e.datepicker._curInst){var i=e(t.target),s=e.datepicker._getInst(i[0]);(i[0].id!==e.datepicker._mainDivId&&0===i.parents("#"+e.datepicker._mainDivId).length&&!i.hasClass(e.datepicker.markerClassName)&&!i.closest("."+e.datepicker._triggerClass).length&&e.datepicker._datepickerShowing&&(!e.datepicker._inDialog||!e.blockUI)||i.hasClass(e.datepicker.markerClassName)&&e.datepicker._curInst!==s)&&e.datepicker._hideDatepicker()}},_adjustDate:function(t,i,s){var n=e(t),a=this._getInst(n[0]);this._isDisabledDatepicker(n[0])||(this._adjustInstDate(a,i+("M"===s?this._get(a,"showCurrentAtPos"):0),s),this._updateDatepicker(a))},_gotoToday:function(t){var i,s=e(t),n=this._getInst(s[0]);this._get(n,"gotoCurrent")&&n.currentDay?(n.selectedDay=n.currentDay,n.drawMonth=n.selectedMonth=n.currentMonth,n.drawYear=n.selectedYear=n.currentYear):(i=new Date,n.selectedDay=i.getDate(),n.drawMonth=n.selectedMonth=i.getMonth(),n.drawYear=n.selectedYear=i.getFullYear()),this._notifyChange(n),this._adjustDate(s)},_selectMonthYear:function(t,i,s){var n=e(t),a=this._getInst(n[0]);a["selected"+("M"===s?"Month":"Year")]=a["draw"+("M"===s?"Month":"Year")]=parseInt(i.options[i.selectedIndex].value,10),this._notifyChange(a),this._adjustDate(n)},_selectDay:function(t,i,s,n){var a,o=e(t);e(n).hasClass(this._unselectableClass)||this._isDisabledDatepicker(o[0])||(a=this._getInst(o[0]),a.selectedDay=a.currentDay=e("a",n).html(),a.selectedMonth=a.currentMonth=i,a.selectedYear=a.currentYear=s,this._selectDate(t,this._formatDate(a,a.currentDay,a.currentMonth,a.currentYear)))},_clearDate:function(t){var i=e(t);this._selectDate(i,"")},_selectDate:function(t,i){var s,n=e(t),a=this._getInst(n[0]);i=null!=i?i:this._formatDate(a),a.input&&a.input.val(i),this._updateAlternate(a),s=this._get(a,"onSelect"),s?s.apply(a.input?a.input[0]:null,[i,a]):a.input&&a.input.trigger("change"),a.inline?this._updateDatepicker(a):(this._hideDatepicker(),this._lastInput=a.input[0],"object"!=typeof a.input[0]&&a.input.focus(),this._lastInput=null)},_updateAlternate:function(t){var i,s,n,a=this._get(t,"altField");a&&(i=this._get(t,"altFormat")||this._get(t,"dateFormat"),s=this._getDate(t),n=this.formatDate(i,s,this._getFormatConfig(t)),e(a).each(function(){e(this).val(n)}))},noWeekends:function(e){var t=e.getDay();return[t>0&&6>t,""]},iso8601Week:function(e){var t,i=new Date(e.getTime());return i.setDate(i.getDate()+4-(i.getDay()||7)),t=i.getTime(),i.setMonth(0),i.setDate(1),Math.floor(Math.round((t-i)/864e5)/7)+1},parseDate:function(t,i,s){if(null==t||null==i)throw"Invalid arguments";if(i="object"==typeof i?""+i:i+"",""===i)return null;var n,a,o,r,h=0,l=(s?s.shortYearCutoff:null)||this._defaults.shortYearCutoff,u="string"!=typeof l?l:(new Date).getFullYear()%100+parseInt(l,10),d=(s?s.dayNamesShort:null)||this._defaults.dayNamesShort,c=(s?s.dayNames:null)||this._defaults.dayNames,p=(s?s.monthNamesShort:null)||this._defaults.monthNamesShort,f=(s?s.monthNames:null)||this._defaults.monthNames,m=-1,g=-1,v=-1,y=-1,b=!1,_=function(e){var i=t.length>n+1&&t.charAt(n+1)===e;return i&&n++,i},x=function(e){var t=_(e),s="@"===e?14:"!"===e?20:"y"===e&&t?4:"o"===e?3:2,n=RegExp("^\\d{1,"+s+"}"),a=i.substring(h).match(n);if(!a)throw"Missing number at position "+h;return h+=a[0].length,parseInt(a[0],10)},w=function(t,s,n){var a=-1,o=e.map(_(t)?n:s,function(e,t){return[[t,e]]}).sort(function(e,t){return-(e[1].length-t[1].length)});if(e.each(o,function(e,t){var s=t[1];return i.substr(h,s.length).toLowerCase()===s.toLowerCase()?(a=t[0],h+=s.length,!1):void 0}),-1!==a)return a+1;throw"Unknown name at position "+h},k=function(){if(i.charAt(h)!==t.charAt(n))throw"Unexpected literal at position "+h;h++};for(n=0;t.length>n;n++)if(b)"'"!==t.charAt(n)||_("'")?k():b=!1;else switch(t.charAt(n)){case"d":v=x("d");break;case"D":w("D",d,c);break;case"o":y=x("o");break;case"m":g=x("m");break;case"M":g=w("M",p,f);break;case"y":m=x("y");break;case"@":r=new Date(x("@")),m=r.getFullYear(),g=r.getMonth()+1,v=r.getDate();break;case"!":r=new Date((x("!")-this._ticksTo1970)/1e4),m=r.getFullYear(),g=r.getMonth()+1,v=r.getDate();break;case"'":_("'")?k():b=!0;break;default:k()}if(i.length>h&&(o=i.substr(h),!/^\s+/.test(o)))throw"Extra/unparsed characters found in date: "+o;if(-1===m?m=(new Date).getFullYear():100>m&&(m+=(new Date).getFullYear()-(new Date).getFullYear()%100+(u>=m?0:-100)),y>-1)for(g=1,v=y;;){if(a=this._getDaysInMonth(m,g-1),a>=v)break;g++,v-=a}if(r=this._daylightSavingAdjust(new Date(m,g-1,v)),r.getFullYear()!==m||r.getMonth()+1!==g||r.getDate()!==v)throw"Invalid date";return r},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:1e7*60*60*24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925)),formatDate:function(e,t,i){if(!t)return"";var s,n=(i?i.dayNamesShort:null)||this._defaults.dayNamesShort,a=(i?i.dayNames:null)||this._defaults.dayNames,o=(i?i.monthNamesShort:null)||this._defaults.monthNamesShort,r=(i?i.monthNames:null)||this._defaults.monthNames,h=function(t){var i=e.length>s+1&&e.charAt(s+1)===t;return i&&s++,i},l=function(e,t,i){var s=""+t;if(h(e))for(;i>s.length;)s="0"+s;return s},u=function(e,t,i,s){return h(e)?s[t]:i[t]},d="",c=!1;if(t)for(s=0;e.length>s;s++)if(c)"'"!==e.charAt(s)||h("'")?d+=e.charAt(s):c=!1;else switch(e.charAt(s)){case"d":d+=l("d",t.getDate(),2);break;case"D":d+=u("D",t.getDay(),n,a);break;case"o":d+=l("o",Math.round((new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime()-new Date(t.getFullYear(),0,0).getTime())/864e5),3);break;case"m":d+=l("m",t.getMonth()+1,2);break;case"M":d+=u("M",t.getMonth(),o,r);break;case"y":d+=h("y")?t.getFullYear():(10>t.getYear()%100?"0":"")+t.getYear()%100;break;case"@":d+=t.getTime();break;case"!":d+=1e4*t.getTime()+this._ticksTo1970;break;case"'":h("'")?d+="'":c=!0;break;default:d+=e.charAt(s)}return d},_possibleChars:function(e){var t,i="",s=!1,n=function(i){var s=e.length>t+1&&e.charAt(t+1)===i;return s&&t++,s};for(t=0;e.length>t;t++)if(s)"'"!==e.charAt(t)||n("'")?i+=e.charAt(t):s=!1;else switch(e.charAt(t)){case"d":case"m":case"y":case"@":i+="0123456789";break;case"D":case"M":return null;case"'":n("'")?i+="'":s=!0;break;default:i+=e.charAt(t)}return i},_get:function(e,t){return void 0!==e.settings[t]?e.settings[t]:this._defaults[t]},_setDateFromField:function(e,t){if(e.input.val()!==e.lastVal){var i=this._get(e,"dateFormat"),s=e.lastVal=e.input?e.input.val():null,n=this._getDefaultDate(e),a=n,o=this._getFormatConfig(e);try{a=this.parseDate(i,s,o)||n}catch(r){s=t?"":s}e.selectedDay=a.getDate(),e.drawMonth=e.selectedMonth=a.getMonth(),e.drawYear=e.selectedYear=a.getFullYear(),e.currentDay=s?a.getDate():0,e.currentMonth=s?a.getMonth():0,e.currentYear=s?a.getFullYear():0,this._adjustInstDate(e)}},_getDefaultDate:function(e){return this._restrictMinMax(e,this._determineDate(e,this._get(e,"defaultDate"),new Date))},_determineDate:function(t,i,s){var n=function(e){var t=new Date;return t.setDate(t.getDate()+e),t},a=function(i){try{return e.datepicker.parseDate(e.datepicker._get(t,"dateFormat"),i,e.datepicker._getFormatConfig(t))}catch(s){}for(var n=(i.toLowerCase().match(/^c/)?e.datepicker._getDate(t):null)||new Date,a=n.getFullYear(),o=n.getMonth(),r=n.getDate(),h=/([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,l=h.exec(i);l;){switch(l[2]||"d"){case"d":case"D":r+=parseInt(l[1],10);break;case"w":case"W":r+=7*parseInt(l[1],10);break;case"m":case"M":o+=parseInt(l[1],10),r=Math.min(r,e.datepicker._getDaysInMonth(a,o));break;case"y":case"Y":a+=parseInt(l[1],10),r=Math.min(r,e.datepicker._getDaysInMonth(a,o))}l=h.exec(i)}return new Date(a,o,r)},o=null==i||""===i?s:"string"==typeof i?a(i):"number"==typeof i?isNaN(i)?s:n(i):new Date(i.getTime());return o=o&&"Invalid Date"==""+o?s:o,o&&(o.setHours(0),o.setMinutes(0),o.setSeconds(0),o.setMilliseconds(0)),this._daylightSavingAdjust(o)},_daylightSavingAdjust:function(e){return e?(e.setHours(e.getHours()>12?e.getHours()+2:0),e):null},_setDate:function(e,t,i){var s=!t,n=e.selectedMonth,a=e.selectedYear,o=this._restrictMinMax(e,this._determineDate(e,t,new Date));e.selectedDay=e.currentDay=o.getDate(),e.drawMonth=e.selectedMonth=e.currentMonth=o.getMonth(),e.drawYear=e.selectedYear=e.currentYear=o.getFullYear(),n===e.selectedMonth&&a===e.selectedYear||i||this._notifyChange(e),this._adjustInstDate(e),e.input&&e.input.val(s?"":this._formatDate(e))},_getDate:function(e){var t=!e.currentYear||e.input&&""===e.input.val()?null:this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return t},_attachHandlers:function(t){var i=this._get(t,"stepMonths"),s="#"+t.id.replace(/\\\\/g,"\\");t.dpDiv.find("[data-handler]").map(function(){var t={prev:function(){e.datepicker._adjustDate(s,-i,"M")},next:function(){e.datepicker._adjustDate(s,+i,"M")},hide:function(){e.datepicker._hideDatepicker()},today:function(){e.datepicker._gotoToday(s)},selectDay:function(){return e.datepicker._selectDay(s,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return e.datepicker._selectMonthYear(s,this,"M"),!1},selectYear:function(){return e.datepicker._selectMonthYear(s,this,"Y"),!1}};e(this).bind(this.getAttribute("data-event"),t[this.getAttribute("data-handler")])})},_generateHTML:function(e){var t,i,s,n,a,o,r,h,l,u,d,c,p,f,m,g,v,y,b,_,x,w,k,T,D,S,M,N,C,A,I,P,z,H,F,E,j,O,W,L=new Date,R=this._daylightSavingAdjust(new Date(L.getFullYear(),L.getMonth(),L.getDate())),Y=this._get(e,"isRTL"),B=this._get(e,"showButtonPanel"),J=this._get(e,"hideIfNoPrevNext"),q=this._get(e,"navigationAsDateFormat"),K=this._getNumberOfMonths(e),V=this._get(e,"showCurrentAtPos"),U=this._get(e,"stepMonths"),Q=1!==K[0]||1!==K[1],G=this._daylightSavingAdjust(e.currentDay?new Date(e.currentYear,e.currentMonth,e.currentDay):new Date(9999,9,9)),X=this._getMinMaxDate(e,"min"),$=this._getMinMaxDate(e,"max"),Z=e.drawMonth-V,et=e.drawYear;if(0>Z&&(Z+=12,et--),$)for(t=this._daylightSavingAdjust(new Date($.getFullYear(),$.getMonth()-K[0]*K[1]+1,$.getDate())),t=X&&X>t?X:t;this._daylightSavingAdjust(new Date(et,Z,1))>t;)Z--,0>Z&&(Z=11,et--);for(e.drawMonth=Z,e.drawYear=et,i=this._get(e,"prevText"),i=q?this.formatDate(i,this._daylightSavingAdjust(new Date(et,Z-U,1)),this._getFormatConfig(e)):i,s=this._canAdjustMonth(e,-1,et,Z)?"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"e":"w")+"'>"+i+"</span></a>":J?"":"<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"e":"w")+"'>"+i+"</span></a>",n=this._get(e,"nextText"),n=q?this.formatDate(n,this._daylightSavingAdjust(new Date(et,Z+U,1)),this._getFormatConfig(e)):n,a=this._canAdjustMonth(e,1,et,Z)?"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click' title='"+n+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"w":"e")+"'>"+n+"</span></a>":J?"":"<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+n+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"w":"e")+"'>"+n+"</span></a>",o=this._get(e,"currentText"),r=this._get(e,"gotoCurrent")&&e.currentDay?G:R,o=q?this.formatDate(o,r,this._getFormatConfig(e)):o,h=e.inline?"":"<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>"+this._get(e,"closeText")+"</button>",l=B?"<div class='ui-datepicker-buttonpane ui-widget-content'>"+(Y?h:"")+(this._isInRange(e,r)?"<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'>"+o+"</button>":"")+(Y?"":h)+"</div>":"",u=parseInt(this._get(e,"firstDay"),10),u=isNaN(u)?0:u,d=this._get(e,"showWeek"),c=this._get(e,"dayNames"),p=this._get(e,"dayNamesMin"),f=this._get(e,"monthNames"),m=this._get(e,"monthNamesShort"),g=this._get(e,"beforeShowDay"),v=this._get(e,"showOtherMonths"),y=this._get(e,"selectOtherMonths"),b=this._getDefaultDate(e),_="",w=0;K[0]>w;w++){for(k="",this.maxRows=4,T=0;K[1]>T;T++){if(D=this._daylightSavingAdjust(new Date(et,Z,e.selectedDay)),S=" ui-corner-all",M="",Q){if(M+="<div class='ui-datepicker-group",K[1]>1)switch(T){case 0:M+=" ui-datepicker-group-first",S=" ui-corner-"+(Y?"right":"left");break;case K[1]-1:M+=" ui-datepicker-group-last",S=" ui-corner-"+(Y?"left":"right");break;default:M+=" ui-datepicker-group-middle",S=""}M+="'>"}for(M+="<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix"+S+"'>"+(/all|left/.test(S)&&0===w?Y?a:s:"")+(/all|right/.test(S)&&0===w?Y?s:a:"")+this._generateMonthYearHeader(e,Z,et,X,$,w>0||T>0,f,m)+"</div><table class='ui-datepicker-calendar'><thead>"+"<tr>",N=d?"<th class='ui-datepicker-week-col'>"+this._get(e,"weekHeader")+"</th>":"",x=0;7>x;x++)C=(x+u)%7,N+="<th scope='col'"+((x+u+6)%7>=5?" class='ui-datepicker-week-end'":"")+">"+"<span title='"+c[C]+"'>"+p[C]+"</span></th>";for(M+=N+"</tr></thead><tbody>",A=this._getDaysInMonth(et,Z),et===e.selectedYear&&Z===e.selectedMonth&&(e.selectedDay=Math.min(e.selectedDay,A)),I=(this._getFirstDayOfMonth(et,Z)-u+7)%7,P=Math.ceil((I+A)/7),z=Q?this.maxRows>P?this.maxRows:P:P,this.maxRows=z,H=this._daylightSavingAdjust(new Date(et,Z,1-I)),F=0;z>F;F++){for(M+="<tr>",E=d?"<td class='ui-datepicker-week-col'>"+this._get(e,"calculateWeek")(H)+"</td>":"",x=0;7>x;x++)j=g?g.apply(e.input?e.input[0]:null,[H]):[!0,""],O=H.getMonth()!==Z,W=O&&!y||!j[0]||X&&X>H||$&&H>$,E+="<td class='"+((x+u+6)%7>=5?" ui-datepicker-week-end":"")+(O?" ui-datepicker-other-month":"")+(H.getTime()===D.getTime()&&Z===e.selectedMonth&&e._keyEvent||b.getTime()===H.getTime()&&b.getTime()===D.getTime()?" "+this._dayOverClass:"")+(W?" "+this._unselectableClass+" ui-state-disabled":"")+(O&&!v?"":" "+j[1]+(H.getTime()===G.getTime()?" "+this._currentClass:"")+(H.getTime()===R.getTime()?" ui-datepicker-today":""))+"'"+(O&&!v||!j[2]?"":" title='"+j[2].replace(/'/g,"&#39;")+"'")+(W?"":" data-handler='selectDay' data-event='click' data-month='"+H.getMonth()+"' data-year='"+H.getFullYear()+"'")+">"+(O&&!v?"&#xa0;":W?"<span class='ui-state-default'>"+H.getDate()+"</span>":"<a class='ui-state-default"+(H.getTime()===R.getTime()?" ui-state-highlight":"")+(H.getTime()===G.getTime()?" ui-state-active":"")+(O?" ui-priority-secondary":"")+"' href='#'>"+H.getDate()+"</a>")+"</td>",H.setDate(H.getDate()+1),H=this._daylightSavingAdjust(H);M+=E+"</tr>"}Z++,Z>11&&(Z=0,et++),M+="</tbody></table>"+(Q?"</div>"+(K[0]>0&&T===K[1]-1?"<div class='ui-datepicker-row-break'></div>":""):""),k+=M}_+=k}return _+=l,e._keyEvent=!1,_},_generateMonthYearHeader:function(e,t,i,s,n,a,o,r){var h,l,u,d,c,p,f,m,g=this._get(e,"changeMonth"),v=this._get(e,"changeYear"),y=this._get(e,"showMonthAfterYear"),b="<div class='ui-datepicker-title'>",_="";if(a||!g)_+="<span class='ui-datepicker-month'>"+o[t]+"</span>";else{for(h=s&&s.getFullYear()===i,l=n&&n.getFullYear()===i,_+="<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>",u=0;12>u;u++)(!h||u>=s.getMonth())&&(!l||n.getMonth()>=u)&&(_+="<option value='"+u+"'"+(u===t?" selected='selected'":"")+">"+r[u]+"</option>");_+="</select>"}if(y||(b+=_+(!a&&g&&v?"":"&#xa0;")),!e.yearshtml)if(e.yearshtml="",a||!v)b+="<span class='ui-datepicker-year'>"+i+"</span>";else{for(d=this._get(e,"yearRange").split(":"),c=(new Date).getFullYear(),p=function(e){var t=e.match(/c[+\-].*/)?i+parseInt(e.substring(1),10):e.match(/[+\-].*/)?c+parseInt(e,10):parseInt(e,10);return isNaN(t)?c:t},f=p(d[0]),m=Math.max(f,p(d[1]||"")),f=s?Math.max(f,s.getFullYear()):f,m=n?Math.min(m,n.getFullYear()):m,e.yearshtml+="<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";m>=f;f++)e.yearshtml+="<option value='"+f+"'"+(f===i?" selected='selected'":"")+">"+f+"</option>";e.yearshtml+="</select>",b+=e.yearshtml,e.yearshtml=null}return b+=this._get(e,"yearSuffix"),y&&(b+=(!a&&g&&v?"":"&#xa0;")+_),b+="</div>"},_adjustInstDate:function(e,t,i){var s=e.drawYear+("Y"===i?t:0),n=e.drawMonth+("M"===i?t:0),a=Math.min(e.selectedDay,this._getDaysInMonth(s,n))+("D"===i?t:0),o=this._restrictMinMax(e,this._daylightSavingAdjust(new Date(s,n,a)));e.selectedDay=o.getDate(),e.drawMonth=e.selectedMonth=o.getMonth(),e.drawYear=e.selectedYear=o.getFullYear(),("M"===i||"Y"===i)&&this._notifyChange(e)},_restrictMinMax:function(e,t){var i=this._getMinMaxDate(e,"min"),s=this._getMinMaxDate(e,"max"),n=i&&i>t?i:t;return s&&n>s?s:n},_notifyChange:function(e){var t=this._get(e,"onChangeMonthYear");t&&t.apply(e.input?e.input[0]:null,[e.selectedYear,e.selectedMonth+1,e])},_getNumberOfMonths:function(e){var t=this._get(e,"numberOfMonths");return null==t?[1,1]:"number"==typeof t?[1,t]:t},_getMinMaxDate:function(e,t){return this._determineDate(e,this._get(e,t+"Date"),null)},_getDaysInMonth:function(e,t){return 32-this._daylightSavingAdjust(new Date(e,t,32)).getDate()},_getFirstDayOfMonth:function(e,t){return new Date(e,t,1).getDay()},_canAdjustMonth:function(e,t,i,s){var n=this._getNumberOfMonths(e),a=this._daylightSavingAdjust(new Date(i,s+(0>t?t:n[0]*n[1]),1));return 0>t&&a.setDate(this._getDaysInMonth(a.getFullYear(),a.getMonth())),this._isInRange(e,a)},_isInRange:function(e,t){var i,s,n=this._getMinMaxDate(e,"min"),a=this._getMinMaxDate(e,"max"),o=null,r=null,h=this._get(e,"yearRange");return h&&(i=h.split(":"),s=(new Date).getFullYear(),o=parseInt(i[0],10),r=parseInt(i[1],10),i[0].match(/[+\-].*/)&&(o+=s),i[1].match(/[+\-].*/)&&(r+=s)),(!n||t.getTime()>=n.getTime())&&(!a||t.getTime()<=a.getTime())&&(!o||t.getFullYear()>=o)&&(!r||r>=t.getFullYear())},_getFormatConfig:function(e){var t=this._get(e,"shortYearCutoff");return t="string"!=typeof t?t:(new Date).getFullYear()%100+parseInt(t,10),{shortYearCutoff:t,dayNamesShort:this._get(e,"dayNamesShort"),dayNames:this._get(e,"dayNames"),monthNamesShort:this._get(e,"monthNamesShort"),monthNames:this._get(e,"monthNames")}},_formatDate:function(e,t,i,s){t||(e.currentDay=e.selectedDay,e.currentMonth=e.selectedMonth,e.currentYear=e.selectedYear);var n=t?"object"==typeof t?t:this._daylightSavingAdjust(new Date(s,i,t)):this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return this.formatDate(this._get(e,"dateFormat"),n,this._getFormatConfig(e))}}),e.fn.datepicker=function(t){if(!this.length)return this;e.datepicker.initialized||(e(document).mousedown(e.datepicker._checkExternalClick),e.datepicker.initialized=!0),0===e("#"+e.datepicker._mainDivId).length&&e("body").append(e.datepicker.dpDiv);var i=Array.prototype.slice.call(arguments,1);return"string"!=typeof t||"isDisabled"!==t&&"getDate"!==t&&"widget"!==t?"option"===t&&2===arguments.length&&"string"==typeof arguments[1]?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i)):this.each(function(){"string"==typeof t?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this].concat(i)):e.datepicker._attachDatepicker(this,t)}):e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i))},e.datepicker=new n,e.datepicker.initialized=!1,e.datepicker.uuid=(new Date).getTime(),e.datepicker.version="1.11.0",e.datepicker,e.widget("ui.draggable",e.ui.mouse,{version:"1.11.0",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"!==this.options.helper||/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._setHandleClassName(),this._mouseInit()},_setOption:function(e,t){this._super(e,t),"handle"===e&&this._setHandleClassName()},_destroy:function(){return(this.helper||this.element).is(".ui-draggable-dragging")?(this.destroyOnClear=!0,void 0):(this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._removeHandleClassName(),this._mouseDestroy(),void 0)},_mouseCapture:function(t){var i=this.document[0],s=this.options;try{i.activeElement&&"body"!==i.activeElement.nodeName.toLowerCase()&&e(i.activeElement).blur()}catch(n){}return this.helper||s.disabled||e(t.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(t),this.handle?(e(s.iframeFix===!0?"iframe":s.iframeFix).each(function(){e("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>").css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(e(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(t){var i=this.options;return this.helper=this._createHelper(t),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),e.ui.ddmanager&&(e.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offsetParent=this.helper.offsetParent(),this.offsetParentCssPosition=this.offsetParent.css("position"),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.offset.scroll=!1,e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(t,!1),this.originalPageX=t.pageX,this.originalPageY=t.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",t)===!1?(this._clear(),!1):(this._cacheHelperProportions(),e.ui.ddmanager&&!i.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this._mouseDrag(t,!0),e.ui.ddmanager&&e.ui.ddmanager.dragStart(this,t),!0)},_mouseDrag:function(t,i){if("fixed"===this.offsetParentCssPosition&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(t,!0),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",t,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),!1},_mouseStop:function(t){var i=this,s=!1;return e.ui.ddmanager&&!this.options.dropBehaviour&&(s=e.ui.ddmanager.drop(this,t)),this.dropped&&(s=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||e.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?e(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",t)!==!1&&i._clear()}):this._trigger("stop",t)!==!1&&this._clear(),!1},_mouseUp:function(t){return e("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),e.ui.ddmanager&&e.ui.ddmanager.dragStop(this,t),this.element.focus(),e.ui.mouse.prototype._mouseUp.call(this,t)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(t){return this.options.handle?!!e(t.target).closest(this.element.find(this.options.handle)).length:!0},_setHandleClassName:function(){this._removeHandleClassName(),e(this.options.handle||this.element).addClass("ui-draggable-handle")},_removeHandleClassName:function(){this.element.find(".ui-draggable-handle").addBack().removeClass("ui-draggable-handle")},_createHelper:function(t){var i=this.options,s=e.isFunction(i.helper)?e(i.helper.apply(this.element[0],[t])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return s.parents("body").length||s.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s[0]===this.element[0]||/(fixed|absolute)/.test(s.css("position"))||s.css("position","absolute"),s},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_isRootNode:function(e){return/(html|body)/i.test(e.tagName)||e===this.document[0]},_getParentOffset:function(){var t=this.offsetParent.offset(),i=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==i&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var e=this.element.position(),t=this._isRootNode(this.scrollParent[0]);return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+(t?0:this.scrollParent.scrollTop()),left:e.left-(parseInt(this.helper.css("left"),10)||0)+(t?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,i,s,n=this.options,a=this.document[0];return this.relative_container=null,n.containment?"window"===n.containment?(this.containment=[e(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,e(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,e(window).scrollLeft()+e(window).width()-this.helperProportions.width-this.margins.left,e(window).scrollTop()+(e(window).height()||a.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):"document"===n.containment?(this.containment=[0,0,e(a).width()-this.helperProportions.width-this.margins.left,(e(a).height()||a.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):n.containment.constructor===Array?(this.containment=n.containment,void 0):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=e(n.containment),s=i[0],s&&(t="hidden"!==i.css("overflow"),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(t?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(t?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=i),void 0):(this.containment=null,void 0)},_convertPositionTo:function(e,t){t||(t=this.position);var i="absolute"===e?1:-1,s=this._isRootNode(this.scrollParent[0]);return{top:t.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.offset.scroll.top:s?0:this.offset.scroll.top)*i,left:t.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.offset.scroll.left:s?0:this.offset.scroll.left)*i}
+},_generatePosition:function(e,t){var i,s,n,a,o=this.options,r=this._isRootNode(this.scrollParent[0]),h=e.pageX,l=e.pageY;return r&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),t&&(this.containment&&(this.relative_container?(s=this.relative_container.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,e.pageX-this.offset.click.left<i[0]&&(h=i[0]+this.offset.click.left),e.pageY-this.offset.click.top<i[1]&&(l=i[1]+this.offset.click.top),e.pageX-this.offset.click.left>i[2]&&(h=i[2]+this.offset.click.left),e.pageY-this.offset.click.top>i[3]&&(l=i[3]+this.offset.click.top)),o.grid&&(n=o.grid[1]?this.originalPageY+Math.round((l-this.originalPageY)/o.grid[1])*o.grid[1]:this.originalPageY,l=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-o.grid[1]:n+o.grid[1]:n,a=o.grid[0]?this.originalPageX+Math.round((h-this.originalPageX)/o.grid[0])*o.grid[0]:this.originalPageX,h=i?a-this.offset.click.left>=i[0]||a-this.offset.click.left>i[2]?a:a-this.offset.click.left>=i[0]?a-o.grid[0]:a+o.grid[0]:a),"y"===o.axis&&(h=this.originalPageX),"x"===o.axis&&(l=this.originalPageY)),{top:l-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:r?0:this.offset.scroll.top),left:h-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:r?0:this.offset.scroll.left)}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_trigger:function(t,i,s){return s=s||this._uiHash(),e.ui.plugin.call(this,t,[i,s,this],!0),"drag"===t&&(this.positionAbs=this._convertPositionTo("absolute")),e.Widget.prototype._trigger.call(this,t,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),e.ui.plugin.add("draggable","connectToSortable",{start:function(t,i,s){var n=s.options,a=e.extend({},i,{item:s.element});s.sortables=[],e(n.connectToSortable).each(function(){var i=e(this).sortable("instance");i&&!i.options.disabled&&(s.sortables.push({instance:i,shouldRevert:i.options.revert}),i.refreshPositions(),i._trigger("activate",t,a))})},stop:function(t,i,s){var n=e.extend({},i,{item:s.element});e.each(s.sortables,function(){this.instance.isOver?(this.instance.isOver=0,s.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=this.shouldRevert),this.instance._mouseStop(t),this.instance.options.helper=this.instance.options._helper,"original"===s.options.helper&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",t,n))})},drag:function(t,i,s){var n=this;e.each(s.sortables,function(){var a=!1,o=this;this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this.instance._intersectsWith(this.instance.containerCache)&&(a=!0,e.each(s.sortables,function(){return this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this!==o&&this.instance._intersectsWith(this.instance.containerCache)&&e.contains(o.instance.element[0],this.instance.element[0])&&(a=!1),a})),a?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=e(n).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return i.helper[0]},t.target=this.instance.currentItem[0],this.instance._mouseCapture(t,!0),this.instance._mouseStart(t,!0,!0),this.instance.offset.click.top=s.offset.click.top,this.instance.offset.click.left=s.offset.click.left,this.instance.offset.parent.left-=s.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=s.offset.parent.top-this.instance.offset.parent.top,s._trigger("toSortable",t),s.dropped=this.instance.element,s.currentItem=s.element,this.instance.fromOutside=s),this.instance.currentItem&&this.instance._mouseDrag(t)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",t,this.instance._uiHash(this.instance)),this.instance._mouseStop(t,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),s._trigger("fromSortable",t),s.dropped=!1)})}}),e.ui.plugin.add("draggable","cursor",{start:function(t,i,s){var n=e("body"),a=s.options;n.css("cursor")&&(a._cursor=n.css("cursor")),n.css("cursor",a.cursor)},stop:function(t,i,s){var n=s.options;n._cursor&&e("body").css("cursor",n._cursor)}}),e.ui.plugin.add("draggable","opacity",{start:function(t,i,s){var n=e(i.helper),a=s.options;n.css("opacity")&&(a._opacity=n.css("opacity")),n.css("opacity",a.opacity)},stop:function(t,i,s){var n=s.options;n._opacity&&e(i.helper).css("opacity",n._opacity)}}),e.ui.plugin.add("draggable","scroll",{start:function(e,t,i){i.scrollParent[0]!==i.document[0]&&"HTML"!==i.scrollParent[0].tagName&&(i.overflowOffset=i.scrollParent.offset())},drag:function(t,i,s){var n=s.options,a=!1,o=s.document[0];s.scrollParent[0]!==o&&"HTML"!==s.scrollParent[0].tagName?(n.axis&&"x"===n.axis||(s.overflowOffset.top+s.scrollParent[0].offsetHeight-t.pageY<n.scrollSensitivity?s.scrollParent[0].scrollTop=a=s.scrollParent[0].scrollTop+n.scrollSpeed:t.pageY-s.overflowOffset.top<n.scrollSensitivity&&(s.scrollParent[0].scrollTop=a=s.scrollParent[0].scrollTop-n.scrollSpeed)),n.axis&&"y"===n.axis||(s.overflowOffset.left+s.scrollParent[0].offsetWidth-t.pageX<n.scrollSensitivity?s.scrollParent[0].scrollLeft=a=s.scrollParent[0].scrollLeft+n.scrollSpeed:t.pageX-s.overflowOffset.left<n.scrollSensitivity&&(s.scrollParent[0].scrollLeft=a=s.scrollParent[0].scrollLeft-n.scrollSpeed))):(n.axis&&"x"===n.axis||(t.pageY-e(o).scrollTop()<n.scrollSensitivity?a=e(o).scrollTop(e(o).scrollTop()-n.scrollSpeed):e(window).height()-(t.pageY-e(o).scrollTop())<n.scrollSensitivity&&(a=e(o).scrollTop(e(o).scrollTop()+n.scrollSpeed))),n.axis&&"y"===n.axis||(t.pageX-e(o).scrollLeft()<n.scrollSensitivity?a=e(o).scrollLeft(e(o).scrollLeft()-n.scrollSpeed):e(window).width()-(t.pageX-e(o).scrollLeft())<n.scrollSensitivity&&(a=e(o).scrollLeft(e(o).scrollLeft()+n.scrollSpeed)))),a!==!1&&e.ui.ddmanager&&!n.dropBehaviour&&e.ui.ddmanager.prepareOffsets(s,t)}}),e.ui.plugin.add("draggable","snap",{start:function(t,i,s){var n=s.options;s.snapElements=[],e(n.snap.constructor!==String?n.snap.items||":data(ui-draggable)":n.snap).each(function(){var t=e(this),i=t.offset();this!==s.element[0]&&s.snapElements.push({item:this,width:t.outerWidth(),height:t.outerHeight(),top:i.top,left:i.left})})},drag:function(t,i,s){var n,a,o,r,h,l,u,d,c,p,f=s.options,m=f.snapTolerance,g=i.offset.left,v=g+s.helperProportions.width,y=i.offset.top,b=y+s.helperProportions.height;for(c=s.snapElements.length-1;c>=0;c--)h=s.snapElements[c].left,l=h+s.snapElements[c].width,u=s.snapElements[c].top,d=u+s.snapElements[c].height,h-m>v||g>l+m||u-m>b||y>d+m||!e.contains(s.snapElements[c].item.ownerDocument,s.snapElements[c].item)?(s.snapElements[c].snapping&&s.options.snap.release&&s.options.snap.release.call(s.element,t,e.extend(s._uiHash(),{snapItem:s.snapElements[c].item})),s.snapElements[c].snapping=!1):("inner"!==f.snapMode&&(n=m>=Math.abs(u-b),a=m>=Math.abs(d-y),o=m>=Math.abs(h-v),r=m>=Math.abs(l-g),n&&(i.position.top=s._convertPositionTo("relative",{top:u-s.helperProportions.height,left:0}).top-s.margins.top),a&&(i.position.top=s._convertPositionTo("relative",{top:d,left:0}).top-s.margins.top),o&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h-s.helperProportions.width}).left-s.margins.left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l}).left-s.margins.left)),p=n||a||o||r,"outer"!==f.snapMode&&(n=m>=Math.abs(u-y),a=m>=Math.abs(d-b),o=m>=Math.abs(h-g),r=m>=Math.abs(l-v),n&&(i.position.top=s._convertPositionTo("relative",{top:u,left:0}).top-s.margins.top),a&&(i.position.top=s._convertPositionTo("relative",{top:d-s.helperProportions.height,left:0}).top-s.margins.top),o&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h}).left-s.margins.left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l-s.helperProportions.width}).left-s.margins.left)),!s.snapElements[c].snapping&&(n||a||o||r||p)&&s.options.snap.snap&&s.options.snap.snap.call(s.element,t,e.extend(s._uiHash(),{snapItem:s.snapElements[c].item})),s.snapElements[c].snapping=n||a||o||r||p)}}),e.ui.plugin.add("draggable","stack",{start:function(t,i,s){var n,a=s.options,o=e.makeArray(e(a.stack)).sort(function(t,i){return(parseInt(e(t).css("zIndex"),10)||0)-(parseInt(e(i).css("zIndex"),10)||0)});o.length&&(n=parseInt(e(o[0]).css("zIndex"),10)||0,e(o).each(function(t){e(this).css("zIndex",n+t)}),this.css("zIndex",n+o.length))}}),e.ui.plugin.add("draggable","zIndex",{start:function(t,i,s){var n=e(i.helper),a=s.options;n.css("zIndex")&&(a._zIndex=n.css("zIndex")),n.css("zIndex",a.zIndex)},stop:function(t,i,s){var n=s.options;n._zIndex&&e(i.helper).css("zIndex",n._zIndex)}}),e.ui.draggable,e.widget("ui.resizable",e.ui.mouse,{version:"1.11.0",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(e){return parseInt(e,10)||0},_isNumber:function(e){return!isNaN(parseInt(e,10))},_hasScroll:function(t,i){if("hidden"===e(t).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",n=!1;return t[s]>0?!0:(t[s]=1,n=t[s]>0,t[s]=0,n)},_create:function(){var t,i,s,n,a,o=this,r=this.options;if(this.element.addClass("ui-resizable"),e.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(e("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=r.handles||(e(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),t=this.handles.split(","),this.handles={},i=0;t.length>i;i++)s=e.trim(t[i]),a="ui-resizable-"+s,n=e("<div class='ui-resizable-handle "+a+"'></div>"),n.css({zIndex:r.zIndex}),"se"===s&&n.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(n);this._renderAxis=function(t){var i,s,n,a;t=t||this.element;for(i in this.handles)this.handles[i].constructor===String&&(this.handles[i]=this.element.children(this.handles[i]).first().show()),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)&&(s=e(this.handles[i],this.element),a=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),t.css(n,a),this._proportionallyResize()),e(this.handles[i]).length},this._renderAxis(this.element),this._handles=e(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){o.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),o.axis=n&&n[1]?n[1]:"se")}),r.autoHide&&(this._handles.hide(),e(this.element).addClass("ui-resizable-autohide").mouseenter(function(){r.disabled||(e(this).removeClass("ui-resizable-autohide"),o._handles.show())}).mouseleave(function(){r.disabled||o.resizing||(e(this).addClass("ui-resizable-autohide"),o._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var t,i=function(t){e(t).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),t=this.element,this.originalElement.css({position:t.css("position"),width:t.outerWidth(),height:t.outerHeight(),top:t.css("top"),left:t.css("left")}).insertAfter(t),t.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(t){var i,s,n=!1;for(i in this.handles)s=e(this.handles[i])[0],(s===t.target||e.contains(s,t.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(t){var i,s,n,a=this.options,o=this.element;return this.resizing=!0,this._renderProxy(),i=this._num(this.helper.css("left")),s=this._num(this.helper.css("top")),a.containment&&(i+=e(a.containment).scrollLeft()||0,s+=e(a.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:i,top:s},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:o.width(),height:o.height()},this.originalSize=this._helper?{width:o.outerWidth(),height:o.outerHeight()}:{width:o.width(),height:o.height()},this.originalPosition={left:i,top:s},this.sizeDiff={width:o.outerWidth()-o.width(),height:o.outerHeight()-o.height()},this.originalMousePosition={left:t.pageX,top:t.pageY},this.aspectRatio="number"==typeof a.aspectRatio?a.aspectRatio:this.originalSize.width/this.originalSize.height||1,n=e(".ui-resizable-"+this.axis).css("cursor"),e("body").css("cursor","auto"===n?this.axis+"-resize":n),o.addClass("ui-resizable-resizing"),this._propagate("start",t),!0},_mouseDrag:function(t){var i,s=this.helper,n={},a=this.originalMousePosition,o=this.axis,r=t.pageX-a.left||0,h=t.pageY-a.top||0,l=this._change[o];return this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height},l?(i=l.apply(this,[t,r,h]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(i=this._updateRatio(i,t)),i=this._respectSize(i,t),this._updateCache(i),this._propagate("resize",t),this.position.top!==this.prevPosition.top&&(n.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(n.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(n.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(n.height=this.size.height+"px"),s.css(n),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),e.isEmptyObject(n)||this._trigger("resize",t,this.ui()),!1):!1},_mouseStop:function(t){this.resizing=!1;var i,s,n,a,o,r,h,l=this.options,u=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&this._hasScroll(i[0],"left")?0:u.sizeDiff.height,a=s?0:u.sizeDiff.width,o={width:u.helper.width()-a,height:u.helper.height()-n},r=parseInt(u.element.css("left"),10)+(u.position.left-u.originalPosition.left)||null,h=parseInt(u.element.css("top"),10)+(u.position.top-u.originalPosition.top)||null,l.animate||this.element.css(e.extend(o,{top:h,left:r})),u.helper.height(u.size.height),u.helper.width(u.size.width),this._helper&&!l.animate&&this._proportionallyResize()),e("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(e){var t,i,s,n,a,o=this.options;a={minWidth:this._isNumber(o.minWidth)?o.minWidth:0,maxWidth:this._isNumber(o.maxWidth)?o.maxWidth:1/0,minHeight:this._isNumber(o.minHeight)?o.minHeight:0,maxHeight:this._isNumber(o.maxHeight)?o.maxHeight:1/0},(this._aspectRatio||e)&&(t=a.minHeight*this.aspectRatio,s=a.minWidth/this.aspectRatio,i=a.maxHeight*this.aspectRatio,n=a.maxWidth/this.aspectRatio,t>a.minWidth&&(a.minWidth=t),s>a.minHeight&&(a.minHeight=s),a.maxWidth>i&&(a.maxWidth=i),a.maxHeight>n&&(a.maxHeight=n)),this._vBoundaries=a},_updateCache:function(e){this.offset=this.helper.offset(),this._isNumber(e.left)&&(this.position.left=e.left),this._isNumber(e.top)&&(this.position.top=e.top),this._isNumber(e.height)&&(this.size.height=e.height),this._isNumber(e.width)&&(this.size.width=e.width)},_updateRatio:function(e){var t=this.position,i=this.size,s=this.axis;return this._isNumber(e.height)?e.width=e.height*this.aspectRatio:this._isNumber(e.width)&&(e.height=e.width/this.aspectRatio),"sw"===s&&(e.left=t.left+(i.width-e.width),e.top=null),"nw"===s&&(e.top=t.top+(i.height-e.height),e.left=t.left+(i.width-e.width)),e},_respectSize:function(e){var t=this._vBoundaries,i=this.axis,s=this._isNumber(e.width)&&t.maxWidth&&t.maxWidth<e.width,n=this._isNumber(e.height)&&t.maxHeight&&t.maxHeight<e.height,a=this._isNumber(e.width)&&t.minWidth&&t.minWidth>e.width,o=this._isNumber(e.height)&&t.minHeight&&t.minHeight>e.height,r=this.originalPosition.left+this.originalSize.width,h=this.position.top+this.size.height,l=/sw|nw|w/.test(i),u=/nw|ne|n/.test(i);return a&&(e.width=t.minWidth),o&&(e.height=t.minHeight),s&&(e.width=t.maxWidth),n&&(e.height=t.maxHeight),a&&l&&(e.left=r-t.minWidth),s&&l&&(e.left=r-t.maxWidth),o&&u&&(e.top=h-t.minHeight),n&&u&&(e.top=h-t.maxHeight),e.width||e.height||e.left||!e.top?e.width||e.height||e.top||!e.left||(e.left=null):e.top=null,e},_proportionallyResize:function(){if(this._proportionallyResizeElements.length){var e,t,i,s,n,a=this.helper||this.element;for(e=0;this._proportionallyResizeElements.length>e;e++){if(n=this._proportionallyResizeElements[e],!this.borderDif)for(this.borderDif=[],i=[n.css("borderTopWidth"),n.css("borderRightWidth"),n.css("borderBottomWidth"),n.css("borderLeftWidth")],s=[n.css("paddingTop"),n.css("paddingRight"),n.css("paddingBottom"),n.css("paddingLeft")],t=0;i.length>t;t++)this.borderDif[t]=(parseInt(i[t],10)||0)+(parseInt(s[t],10)||0);n.css({height:a.height()-this.borderDif[0]-this.borderDif[2]||0,width:a.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var t=this.element,i=this.options;this.elementOffset=t.offset(),this._helper?(this.helper=this.helper||e("<div style='overflow:hidden;'></div>"),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(e,t){return{width:this.originalSize.width+t}},w:function(e,t){var i=this.originalSize,s=this.originalPosition;return{left:s.left+t,width:i.width-t}},n:function(e,t,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(e,t,i){return{height:this.originalSize.height+i}},se:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},sw:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,i,s]))},ne:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},nw:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,i,s]))}},_propagate:function(t,i){e.ui.plugin.call(this,t,[i,this.ui()]),"resize"!==t&&this._trigger(t,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition,prevSize:this.prevSize,prevPosition:this.prevPosition}}}),e.ui.plugin.add("resizable","animate",{stop:function(t){var i=e(this).resizable("instance"),s=i.options,n=i._proportionallyResizeElements,a=n.length&&/textarea/i.test(n[0].nodeName),o=a&&i._hasScroll(n[0],"left")?0:i.sizeDiff.height,r=a?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-o},l=parseInt(i.element.css("left"),10)+(i.position.left-i.originalPosition.left)||null,u=parseInt(i.element.css("top"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(e.extend(h,u&&l?{top:u,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css("width"),10),height:parseInt(i.element.css("height"),10),top:parseInt(i.element.css("top"),10),left:parseInt(i.element.css("left"),10)};n&&n.length&&e(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",t)}})}}),e.ui.plugin.add("resizable","containment",{start:function(){var t,i,s,n,a,o,r,h=e(this).resizable("instance"),l=h.options,u=h.element,d=l.containment,c=d instanceof e?d.get(0):/parent/.test(d)?u.parent().get(0):d;c&&(h.containerElement=e(c),/document/.test(d)||d===document?(h.containerOffset={left:0,top:0},h.containerPosition={left:0,top:0},h.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}):(t=e(c),i=[],e(["Top","Right","Left","Bottom"]).each(function(e,s){i[e]=h._num(t.css("padding"+s))}),h.containerOffset=t.offset(),h.containerPosition=t.position(),h.containerSize={height:t.innerHeight()-i[3],width:t.innerWidth()-i[1]},s=h.containerOffset,n=h.containerSize.height,a=h.containerSize.width,o=h._hasScroll(c,"left")?c.scrollWidth:a,r=h._hasScroll(c)?c.scrollHeight:n,h.parentData={element:c,left:s.left,top:s.top,width:o,height:r}))},resize:function(t,i){var s,n,a,o,r=e(this).resizable("instance"),h=r.options,l=r.containerOffset,u=r.position,d=r._aspectRatio||t.shiftKey,c={top:0,left:0},p=r.containerElement,f=!0;p[0]!==document&&/static/.test(p.css("position"))&&(c=l),u.left<(r._helper?l.left:0)&&(r.size.width=r.size.width+(r._helper?r.position.left-l.left:r.position.left-c.left),d&&(r.size.height=r.size.width/r.aspectRatio,f=!1),r.position.left=h.helper?l.left:0),u.top<(r._helper?l.top:0)&&(r.size.height=r.size.height+(r._helper?r.position.top-l.top:r.position.top),d&&(r.size.width=r.size.height*r.aspectRatio,f=!1),r.position.top=r._helper?l.top:0),r.offset.left=r.parentData.left+r.position.left,r.offset.top=r.parentData.top+r.position.top,s=Math.abs((r._helper?r.offset.left-c.left:r.offset.left-l.left)+r.sizeDiff.width),n=Math.abs((r._helper?r.offset.top-c.top:r.offset.top-l.top)+r.sizeDiff.height),a=r.containerElement.get(0)===r.element.parent().get(0),o=/relative|absolute/.test(r.containerElement.css("position")),a&&o&&(s-=Math.abs(r.parentData.left)),s+r.size.width>=r.parentData.width&&(r.size.width=r.parentData.width-s,d&&(r.size.height=r.size.width/r.aspectRatio,f=!1)),n+r.size.height>=r.parentData.height&&(r.size.height=r.parentData.height-n,d&&(r.size.width=r.size.height*r.aspectRatio,f=!1)),f||(r.position.left=i.prevPosition.left,r.position.top=i.prevPosition.top,r.size.width=i.prevSize.width,r.size.height=i.prevSize.height)},stop:function(){var t=e(this).resizable("instance"),i=t.options,s=t.containerOffset,n=t.containerPosition,a=t.containerElement,o=e(t.helper),r=o.offset(),h=o.outerWidth()-t.sizeDiff.width,l=o.outerHeight()-t.sizeDiff.height;t._helper&&!i.animate&&/relative/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l}),t._helper&&!i.animate&&/static/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),e.ui.plugin.add("resizable","alsoResize",{start:function(){var t=e(this).resizable("instance"),i=t.options,s=function(t){e(t).each(function(){var t=e(this);t.data("ui-resizable-alsoresize",{width:parseInt(t.width(),10),height:parseInt(t.height(),10),left:parseInt(t.css("left"),10),top:parseInt(t.css("top"),10)})})};"object"!=typeof i.alsoResize||i.alsoResize.parentNode?s(i.alsoResize):i.alsoResize.length?(i.alsoResize=i.alsoResize[0],s(i.alsoResize)):e.each(i.alsoResize,function(e){s(e)})},resize:function(t,i){var s=e(this).resizable("instance"),n=s.options,a=s.originalSize,o=s.originalPosition,r={height:s.size.height-a.height||0,width:s.size.width-a.width||0,top:s.position.top-o.top||0,left:s.position.left-o.left||0},h=function(t,s){e(t).each(function(){var t=e(this),n=e(this).data("ui-resizable-alsoresize"),a={},o=s&&s.length?s:t.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(o,function(e,t){var i=(n[t]||0)+(r[t]||0);i&&i>=0&&(a[t]=i||null)}),t.css(a)})};"object"!=typeof n.alsoResize||n.alsoResize.nodeType?h(n.alsoResize):e.each(n.alsoResize,function(e,t){h(e,t)})},stop:function(){e(this).removeData("resizable-alsoresize")}}),e.ui.plugin.add("resizable","ghost",{start:function(){var t=e(this).resizable("instance"),i=t.options,s=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof i.ghost?i.ghost:""),t.ghost.appendTo(t.helper)},resize:function(){var t=e(this).resizable("instance");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=e(this).resizable("instance");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),e.ui.plugin.add("resizable","grid",{resize:function(){var t=e(this).resizable("instance"),i=t.options,s=t.size,n=t.originalSize,a=t.originalPosition,o=t.axis,r="number"==typeof i.grid?[i.grid,i.grid]:i.grid,h=r[0]||1,l=r[1]||1,u=Math.round((s.width-n.width)/h)*h,d=Math.round((s.height-n.height)/l)*l,c=n.width+u,p=n.height+d,f=i.maxWidth&&c>i.maxWidth,m=i.maxHeight&&p>i.maxHeight,g=i.minWidth&&i.minWidth>c,v=i.minHeight&&i.minHeight>p;i.grid=r,g&&(c+=h),v&&(p+=l),f&&(c-=h),m&&(p-=l),/^(se|s|e)$/.test(o)?(t.size.width=c,t.size.height=p):/^(ne)$/.test(o)?(t.size.width=c,t.size.height=p,t.position.top=a.top-d):/^(sw)$/.test(o)?(t.size.width=c,t.size.height=p,t.position.left=a.left-u):(p-l>0?(t.size.height=p,t.position.top=a.top-d):(t.size.height=l,t.position.top=a.top+n.height-l),c-h>0?(t.size.width=c,t.position.left=a.left-u):(t.size.width=h,t.position.left=a.left+n.width-h))}}),e.ui.resizable,e.widget("ui.dialog",{version:"1.11.0",options:{appendTo:"body",autoOpen:!0,buttons:[],closeOnEscape:!0,closeText:"Close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:null,maxWidth:null,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(t){var i=e(this).css(t).offset().top;0>i&&e(this).css("top",t.top-i)}},resizable:!0,show:null,title:null,width:300,beforeClose:null,close:null,drag:null,dragStart:null,dragStop:null,focus:null,open:null,resize:null,resizeStart:null,resizeStop:null},sizeRelatedOptions:{buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},resizableRelatedOptions:{maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},_create:function(){this.originalCss={display:this.element[0].style.display,width:this.element[0].style.width,minHeight:this.element[0].style.minHeight,maxHeight:this.element[0].style.maxHeight,height:this.element[0].style.height},this.originalPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.originalTitle=this.element.attr("title"),this.options.title=this.options.title||this.originalTitle,this._createWrapper(),this.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(this.uiDialog),this._createTitlebar(),this._createButtonPane(),this.options.draggable&&e.fn.draggable&&this._makeDraggable(),this.options.resizable&&e.fn.resizable&&this._makeResizable(),this._isOpen=!1,this._trackFocus()},_init:function(){this.options.autoOpen&&this.open()},_appendTo:function(){var t=this.options.appendTo;return t&&(t.jquery||t.nodeType)?e(t):this.document.find(t||"body").eq(0)},_destroy:function(){var e,t=this.originalPosition;this._destroyOverlay(),this.element.removeUniqueId().removeClass("ui-dialog-content ui-widget-content").css(this.originalCss).detach(),this.uiDialog.stop(!0,!0).remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),e=t.parent.children().eq(t.index),e.length&&e[0]!==this.element[0]?e.before(this.element):t.parent.append(this.element)},widget:function(){return this.uiDialog},disable:e.noop,enable:e.noop,close:function(t){var i,s=this;if(this._isOpen&&this._trigger("beforeClose",t)!==!1){if(this._isOpen=!1,this._focusedElement=null,this._destroyOverlay(),this._untrackInstance(),!this.opener.filter(":focusable").focus().length)try{i=this.document[0].activeElement,i&&"body"!==i.nodeName.toLowerCase()&&e(i).blur()}catch(n){}this._hide(this.uiDialog,this.options.hide,function(){s._trigger("close",t)})}},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(t,i){var s=!1,n=this.uiDialog.siblings(".ui-front:visible").map(function(){return+e(this).css("z-index")}).get(),a=Math.max.apply(null,n);return a>=+this.uiDialog.css("z-index")&&(this.uiDialog.css("z-index",a+1),s=!0),s&&!i&&this._trigger("focus",t),s},open:function(){var t=this;return this._isOpen?(this._moveToTop()&&this._focusTabbable(),void 0):(this._isOpen=!0,this.opener=e(this.document[0].activeElement),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this._show(this.uiDialog,this.options.show,function(){t._focusTabbable(),t._trigger("focus")}),this._trigger("open"),void 0)},_focusTabbable:function(){var e=this._focusedElement;e||(e=this.element.find("[autofocus]")),e.length||(e=this.element.find(":tabbable")),e.length||(e=this.uiDialogButtonPane.find(":tabbable")),e.length||(e=this.uiDialogTitlebarClose.filter(":tabbable")),e.length||(e=this.uiDialog),e.eq(0).focus()},_keepFocus:function(t){function i(){var t=this.document[0].activeElement,i=this.uiDialog[0]===t||e.contains(this.uiDialog[0],t);i||this._focusTabbable()}t.preventDefault(),i.call(this),this._delay(i)},_createWrapper:function(){this.uiDialog=e("<div>").addClass("ui-dialog ui-widget ui-widget-content ui-corner-all ui-front "+this.options.dialogClass).hide().attr({tabIndex:-1,role:"dialog"}).appendTo(this._appendTo()),this._on(this.uiDialog,{keydown:function(t){if(this.options.closeOnEscape&&!t.isDefaultPrevented()&&t.keyCode&&t.keyCode===e.ui.keyCode.ESCAPE)return t.preventDefault(),this.close(t),void 0;if(t.keyCode===e.ui.keyCode.TAB&&!t.isDefaultPrevented()){var i=this.uiDialog.find(":tabbable"),s=i.filter(":first"),n=i.filter(":last");t.target!==n[0]&&t.target!==this.uiDialog[0]||t.shiftKey?t.target!==s[0]&&t.target!==this.uiDialog[0]||!t.shiftKey||(this._delay(function(){n.focus()}),t.preventDefault()):(this._delay(function(){s.focus()}),t.preventDefault())}},mousedown:function(e){this._moveToTop(e)&&this._focusTabbable()
+}}),this.element.find("[aria-describedby]").length||this.uiDialog.attr({"aria-describedby":this.element.uniqueId().attr("id")})},_createTitlebar:function(){var t;this.uiDialogTitlebar=e("<div>").addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(this.uiDialog),this._on(this.uiDialogTitlebar,{mousedown:function(t){e(t.target).closest(".ui-dialog-titlebar-close")||this.uiDialog.focus()}}),this.uiDialogTitlebarClose=e("<button type='button'></button>").button({label:this.options.closeText,icons:{primary:"ui-icon-closethick"},text:!1}).addClass("ui-dialog-titlebar-close").appendTo(this.uiDialogTitlebar),this._on(this.uiDialogTitlebarClose,{click:function(e){e.preventDefault(),this.close(e)}}),t=e("<span>").uniqueId().addClass("ui-dialog-title").prependTo(this.uiDialogTitlebar),this._title(t),this.uiDialog.attr({"aria-labelledby":t.attr("id")})},_title:function(e){this.options.title||e.html("&#160;"),e.text(this.options.title)},_createButtonPane:function(){this.uiDialogButtonPane=e("<div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),this.uiButtonSet=e("<div>").addClass("ui-dialog-buttonset").appendTo(this.uiDialogButtonPane),this._createButtons()},_createButtons:function(){var t=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),e.isEmptyObject(i)||e.isArray(i)&&!i.length?(this.uiDialog.removeClass("ui-dialog-buttons"),void 0):(e.each(i,function(i,s){var n,a;s=e.isFunction(s)?{click:s,text:i}:s,s=e.extend({type:"button"},s),n=s.click,s.click=function(){n.apply(t.element[0],arguments)},a={icons:s.icons,text:s.showText},delete s.icons,delete s.showText,e("<button></button>",s).button(a).appendTo(t.uiButtonSet)}),this.uiDialog.addClass("ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),void 0)},_makeDraggable:function(){function t(e){return{position:e.position,offset:e.offset}}var i=this,s=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(s,n){e(this).addClass("ui-dialog-dragging"),i._blockFrames(),i._trigger("dragStart",s,t(n))},drag:function(e,s){i._trigger("drag",e,t(s))},stop:function(n,a){var o=a.offset.left-i.document.scrollLeft(),r=a.offset.top-i.document.scrollTop();s.position={my:"left top",at:"left"+(o>=0?"+":"")+o+" "+"top"+(r>=0?"+":"")+r,of:i.window},e(this).removeClass("ui-dialog-dragging"),i._unblockFrames(),i._trigger("dragStop",n,t(a))}})},_makeResizable:function(){function t(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}var i=this,s=this.options,n=s.resizable,a=this.uiDialog.css("position"),o="string"==typeof n?n:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:s.maxWidth,maxHeight:s.maxHeight,minWidth:s.minWidth,minHeight:this._minHeight(),handles:o,start:function(s,n){e(this).addClass("ui-dialog-resizing"),i._blockFrames(),i._trigger("resizeStart",s,t(n))},resize:function(e,s){i._trigger("resize",e,t(s))},stop:function(n,a){var o=i.uiDialog.offset(),r=o.left-i.document.scrollLeft(),h=o.top-i.document.scrollTop();s.height=i.uiDialog.height(),s.width=i.uiDialog.width(),s.position={my:"left top",at:"left"+(r>=0?"+":"")+r+" "+"top"+(h>=0?"+":"")+h,of:i.window},e(this).removeClass("ui-dialog-resizing"),i._unblockFrames(),i._trigger("resizeStop",n,t(a))}}).css("position",a)},_trackFocus:function(){this._on(this.widget(),{focusin:function(t){this._untrackInstance(),this._trackingInstances().unshift(this),this._focusedElement=e(t.target)}})},_untrackInstance:function(){var t=this._trackingInstances(),i=e.inArray(this,t);-1!==i&&t.splice(i,1)},_trackingInstances:function(){var e=this.document.data("ui-dialog-instances");return e||(e=[],this.document.data("ui-dialog-instances",e)),e},_minHeight:function(){var e=this.options;return"auto"===e.height?e.minHeight:Math.min(e.minHeight,e.height)},_position:function(){var e=this.uiDialog.is(":visible");e||this.uiDialog.show(),this.uiDialog.position(this.options.position),e||this.uiDialog.hide()},_setOptions:function(t){var i=this,s=!1,n={};e.each(t,function(e,t){i._setOption(e,t),e in i.sizeRelatedOptions&&(s=!0),e in i.resizableRelatedOptions&&(n[e]=t)}),s&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",n)},_setOption:function(e,t){var i,s,n=this.uiDialog;"dialogClass"===e&&n.removeClass(this.options.dialogClass).addClass(t),"disabled"!==e&&(this._super(e,t),"appendTo"===e&&this.uiDialog.appendTo(this._appendTo()),"buttons"===e&&this._createButtons(),"closeText"===e&&this.uiDialogTitlebarClose.button({label:""+t}),"draggable"===e&&(i=n.is(":data(ui-draggable)"),i&&!t&&n.draggable("destroy"),!i&&t&&this._makeDraggable()),"position"===e&&this._position(),"resizable"===e&&(s=n.is(":data(ui-resizable)"),s&&!t&&n.resizable("destroy"),s&&"string"==typeof t&&n.resizable("option","handles",t),s||t===!1||this._makeResizable()),"title"===e&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var e,t,i,s=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),s.minWidth>s.width&&(s.width=s.minWidth),e=this.uiDialog.css({height:"auto",width:s.width}).outerHeight(),t=Math.max(0,s.minHeight-e),i="number"==typeof s.maxHeight?Math.max(0,s.maxHeight-e):"none","auto"===s.height?this.element.css({minHeight:t,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,s.height-e)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var t=e(this);return e("<div>").css({position:"absolute",width:t.outerWidth(),height:t.outerHeight()}).appendTo(t.parent()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(t){return e(t.target).closest(".ui-dialog").length?!0:!!e(t.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var t=!0;this._delay(function(){t=!1}),this.document.data("ui-dialog-overlays")||this._on(this.document,{focusin:function(e){t||this._allowInteraction(e)||(e.preventDefault(),this._trackingInstances()[0]._focusTabbable())}}),this.overlay=e("<div>").addClass("ui-widget-overlay ui-front").appendTo(this._appendTo()),this._on(this.overlay,{mousedown:"_keepFocus"}),this.document.data("ui-dialog-overlays",(this.document.data("ui-dialog-overlays")||0)+1)}},_destroyOverlay:function(){if(this.options.modal&&this.overlay){var e=this.document.data("ui-dialog-overlays")-1;e?this.document.data("ui-dialog-overlays",e):this.document.unbind("focusin").removeData("ui-dialog-overlays"),this.overlay.remove(),this.overlay=null}}}),e.widget("ui.droppable",{version:"1.11.0",widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var t,i=this.options,s=i.accept;this.isover=!1,this.isout=!0,this.accept=e.isFunction(s)?s:function(e){return e.is(s)},this.proportions=function(){return arguments.length?(t=arguments[0],void 0):t?t:t={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight}},this._addToManager(i.scope),i.addClasses&&this.element.addClass("ui-droppable")},_addToManager:function(t){e.ui.ddmanager.droppables[t]=e.ui.ddmanager.droppables[t]||[],e.ui.ddmanager.droppables[t].push(this)},_splice:function(e){for(var t=0;e.length>t;t++)e[t]===this&&e.splice(t,1)},_destroy:function(){var t=e.ui.ddmanager.droppables[this.options.scope];this._splice(t),this.element.removeClass("ui-droppable ui-droppable-disabled")},_setOption:function(t,i){if("accept"===t)this.accept=e.isFunction(i)?i:function(e){return e.is(i)};else if("scope"===t){var s=e.ui.ddmanager.droppables[this.options.scope];this._splice(s),this._addToManager(i)}this._super(t,i)},_activate:function(t){var i=e.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),i&&this._trigger("activate",t,this.ui(i))},_deactivate:function(t){var i=e.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),i&&this._trigger("deactivate",t,this.ui(i))},_over:function(t){var i=e.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",t,this.ui(i)))},_out:function(t){var i=e.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",t,this.ui(i)))},_drop:function(t,i){var s=i||e.ui.ddmanager.current,n=!1;return s&&(s.currentItem||s.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var t=e(this).droppable("instance");return t.options.greedy&&!t.options.disabled&&t.options.scope===s.options.scope&&t.accept.call(t.element[0],s.currentItem||s.element)&&e.ui.intersect(s,e.extend(t,{offset:t.element.offset()}),t.options.tolerance)?(n=!0,!1):void 0}),n?!1:this.accept.call(this.element[0],s.currentItem||s.element)?(this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",t,this.ui(s)),this.element):!1):!1},ui:function(e){return{draggable:e.currentItem||e.element,helper:e.helper,position:e.position,offset:e.positionAbs}}}),e.ui.intersect=function(){function e(e,t,i){return e>=t&&t+i>e}return function(t,i,s){if(!i.offset)return!1;var n,a,o=(t.positionAbs||t.position.absolute).left,r=(t.positionAbs||t.position.absolute).top,h=o+t.helperProportions.width,l=r+t.helperProportions.height,u=i.offset.left,d=i.offset.top,c=u+i.proportions().width,p=d+i.proportions().height;switch(s){case"fit":return o>=u&&c>=h&&r>=d&&p>=l;case"intersect":return o+t.helperProportions.width/2>u&&c>h-t.helperProportions.width/2&&r+t.helperProportions.height/2>d&&p>l-t.helperProportions.height/2;case"pointer":return n=(t.positionAbs||t.position.absolute).left+(t.clickOffset||t.offset.click).left,a=(t.positionAbs||t.position.absolute).top+(t.clickOffset||t.offset.click).top,e(a,d,i.proportions().height)&&e(n,u,i.proportions().width);case"touch":return(r>=d&&p>=r||l>=d&&p>=l||d>r&&l>p)&&(o>=u&&c>=o||h>=u&&c>=h||u>o&&h>c);default:return!1}}}(),e.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(t,i){var s,n,a=e.ui.ddmanager.droppables[t.options.scope]||[],o=i?i.type:null,r=(t.currentItem||t.element).find(":data(ui-droppable)").addBack();e:for(s=0;a.length>s;s++)if(!(a[s].options.disabled||t&&!a[s].accept.call(a[s].element[0],t.currentItem||t.element))){for(n=0;r.length>n;n++)if(r[n]===a[s].element[0]){a[s].proportions().height=0;continue e}a[s].visible="none"!==a[s].element.css("display"),a[s].visible&&("mousedown"===o&&a[s]._activate.call(a[s],i),a[s].offset=a[s].element.offset(),a[s].proportions({width:a[s].element[0].offsetWidth,height:a[s].element[0].offsetHeight}))}},drop:function(t,i){var s=!1;return e.each((e.ui.ddmanager.droppables[t.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&e.ui.intersect(t,this,this.options.tolerance)&&(s=this._drop.call(this,i)||s),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],t.currentItem||t.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,i)))}),s},dragStart:function(t,i){t.element.parentsUntil("body").bind("scroll.droppable",function(){t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,i)})},drag:function(t,i){t.options.refreshPositions&&e.ui.ddmanager.prepareOffsets(t,i),e.each(e.ui.ddmanager.droppables[t.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var s,n,a,o=e.ui.intersect(t,this,this.options.tolerance),r=!o&&this.isover?"isout":o&&!this.isover?"isover":null;r&&(this.options.greedy&&(n=this.options.scope,a=this.element.parents(":data(ui-droppable)").filter(function(){return e(this).droppable("instance").options.scope===n}),a.length&&(s=e(a[0]).droppable("instance"),s.greedyChild="isover"===r)),s&&"isover"===r&&(s.isover=!1,s.isout=!0,s._out.call(s,i)),this[r]=!0,this["isout"===r?"isover":"isout"]=!1,this["isover"===r?"_over":"_out"].call(this,i),s&&"isout"===r&&(s.isout=!1,s.isover=!0,s._over.call(s,i)))}})},dragStop:function(t,i){t.element.parentsUntil("body").unbind("scroll.droppable"),t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,i)}},e.ui.droppable;var v="ui-effects-";e.effects={effect:{}},function(e,t){function i(e,t,i){var s=d[t.type]||{};return null==e?i||!t.def?null:t.def:(e=s.floor?~~e:parseFloat(e),isNaN(e)?t.def:s.mod?(e+s.mod)%s.mod:0>e?0:e>s.max?s.max:e)}function s(i){var s=l(),n=s._rgba=[];return i=i.toLowerCase(),f(h,function(e,a){var o,r=a.re.exec(i),h=r&&a.parse(r),l=a.space||"rgba";return h?(o=s[l](h),s[u[l].cache]=o[u[l].cache],n=s._rgba=o._rgba,!1):t}),n.length?("0,0,0,0"===n.join()&&e.extend(n,a.transparent),s):a[i]}function n(e,t,i){return i=(i+1)%1,1>6*i?e+6*(t-e)*i:1>2*i?t:2>3*i?e+6*(t-e)*(2/3-i):e}var a,o="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",r=/^([\-+])=\s*(\d+\.?\d*)/,h=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[e[1],e[2],e[3],e[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[2.55*e[1],2.55*e[2],2.55*e[3],e[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(e){return[parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(e){return[parseInt(e[1]+e[1],16),parseInt(e[2]+e[2],16),parseInt(e[3]+e[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(e){return[e[1],e[2]/100,e[3]/100,e[4]]}}],l=e.Color=function(t,i,s,n){return new e.Color.fn.parse(t,i,s,n)},u={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},d={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},c=l.support={},p=e("<p>")[0],f=e.each;p.style.cssText="background-color:rgba(1,1,1,.5)",c.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(u,function(e,t){t.cache="_"+e,t.props.alpha={idx:3,type:"percent",def:1}}),l.fn=e.extend(l.prototype,{parse:function(n,o,r,h){if(n===t)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=e(n).css(o),o=t);var d=this,c=e.type(n),p=this._rgba=[];return o!==t&&(n=[n,o,r,h],c="array"),"string"===c?this.parse(s(n)||a._default):"array"===c?(f(u.rgba.props,function(e,t){p[t.idx]=i(n[t.idx],t)}),this):"object"===c?(n instanceof l?f(u,function(e,t){n[t.cache]&&(d[t.cache]=n[t.cache].slice())}):f(u,function(t,s){var a=s.cache;f(s.props,function(e,t){if(!d[a]&&s.to){if("alpha"===e||null==n[e])return;d[a]=s.to(d._rgba)}d[a][t.idx]=i(n[e],t,!0)}),d[a]&&0>e.inArray(null,d[a].slice(0,3))&&(d[a][3]=1,s.from&&(d._rgba=s.from(d[a])))}),this):t},is:function(e){var i=l(e),s=!0,n=this;return f(u,function(e,a){var o,r=i[a.cache];return r&&(o=n[a.cache]||a.to&&a.to(n._rgba)||[],f(a.props,function(e,i){return null!=r[i.idx]?s=r[i.idx]===o[i.idx]:t})),s}),s},_space:function(){var e=[],t=this;return f(u,function(i,s){t[s.cache]&&e.push(i)}),e.pop()},transition:function(e,t){var s=l(e),n=s._space(),a=u[n],o=0===this.alpha()?l("transparent"):this,r=o[a.cache]||a.to(o._rgba),h=r.slice();return s=s[a.cache],f(a.props,function(e,n){var a=n.idx,o=r[a],l=s[a],u=d[n.type]||{};null!==l&&(null===o?h[a]=l:(u.mod&&(l-o>u.mod/2?o+=u.mod:o-l>u.mod/2&&(o-=u.mod)),h[a]=i((l-o)*t+o,n)))}),this[n](h)},blend:function(t){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=l(t)._rgba;return l(e.map(i,function(e,t){return(1-s)*n[t]+s*e}))},toRgbaString:function(){var t="rgba(",i=e.map(this._rgba,function(e,t){return null==e?t>2?1:0:e});return 1===i[3]&&(i.pop(),t="rgb("),t+i.join()+")"},toHslaString:function(){var t="hsla(",i=e.map(this.hsla(),function(e,t){return null==e&&(e=t>2?1:0),t&&3>t&&(e=Math.round(100*e)+"%"),e});return 1===i[3]&&(i.pop(),t="hsl("),t+i.join()+")"},toHexString:function(t){var i=this._rgba.slice(),s=i.pop();return t&&i.push(~~(255*s)),"#"+e.map(i,function(e){return e=(e||0).toString(16),1===e.length?"0"+e:e}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),l.fn.parse.prototype=l.fn,u.hsla.to=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t,i,s=e[0]/255,n=e[1]/255,a=e[2]/255,o=e[3],r=Math.max(s,n,a),h=Math.min(s,n,a),l=r-h,u=r+h,d=.5*u;return t=h===r?0:s===r?60*(n-a)/l+360:n===r?60*(a-s)/l+120:60*(s-n)/l+240,i=0===l?0:.5>=d?l/u:l/(2-u),[Math.round(t)%360,i,d,null==o?1:o]},u.hsla.from=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t=e[0]/360,i=e[1],s=e[2],a=e[3],o=.5>=s?s*(1+i):s+i-s*i,r=2*s-o;return[Math.round(255*n(r,o,t+1/3)),Math.round(255*n(r,o,t)),Math.round(255*n(r,o,t-1/3)),a]},f(u,function(s,n){var a=n.props,o=n.cache,h=n.to,u=n.from;l.fn[s]=function(s){if(h&&!this[o]&&(this[o]=h(this._rgba)),s===t)return this[o].slice();var n,r=e.type(s),d="array"===r||"object"===r?s:arguments,c=this[o].slice();return f(a,function(e,t){var s=d["object"===r?e:t.idx];null==s&&(s=c[t.idx]),c[t.idx]=i(s,t)}),u?(n=l(u(c)),n[o]=c,n):l(c)},f(a,function(t,i){l.fn[t]||(l.fn[t]=function(n){var a,o=e.type(n),h="alpha"===t?this._hsla?"hsla":"rgba":s,l=this[h](),u=l[i.idx];return"undefined"===o?u:("function"===o&&(n=n.call(this,u),o=e.type(n)),null==n&&i.empty?this:("string"===o&&(a=r.exec(n),a&&(n=u+parseFloat(a[2])*("+"===a[1]?1:-1))),l[i.idx]=n,this[h](l)))})})}),l.hook=function(t){var i=t.split(" ");f(i,function(t,i){e.cssHooks[i]={set:function(t,n){var a,o,r="";if("transparent"!==n&&("string"!==e.type(n)||(a=s(n)))){if(n=l(a||n),!c.rgba&&1!==n._rgba[3]){for(o="backgroundColor"===i?t.parentNode:t;(""===r||"transparent"===r)&&o&&o.style;)try{r=e.css(o,"backgroundColor"),o=o.parentNode}catch(h){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{t.style[i]=n}catch(h){}}},e.fx.step[i]=function(t){t.colorInit||(t.start=l(t.elem,i),t.end=l(t.end),t.colorInit=!0),e.cssHooks[i].set(t.elem,t.start.transition(t.end,t.pos))}})},l.hook(o),e.cssHooks.borderColor={expand:function(e){var t={};return f(["Top","Right","Bottom","Left"],function(i,s){t["border"+s+"Color"]=e}),t}},a=e.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(jQuery),function(){function t(t){var i,s,n=t.ownerDocument.defaultView?t.ownerDocument.defaultView.getComputedStyle(t,null):t.currentStyle,a={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(a[e.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(a[i]=n[i]);return a}function i(t,i){var s,a,o={};for(s in i)a=i[s],t[s]!==a&&(n[s]||(e.fx.step[s]||!isNaN(parseFloat(a)))&&(o[s]=a));return o}var s=["add","remove","toggle"],n={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};e.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(t,i){e.fx.step[i]=function(e){("none"!==e.end&&!e.setAttr||1===e.pos&&!e.setAttr)&&(jQuery.style(e.elem,i,e.end),e.setAttr=!0)}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e.effects.animateClass=function(n,a,o,r){var h=e.speed(a,o,r);return this.queue(function(){var a,o=e(this),r=o.attr("class")||"",l=h.children?o.find("*").addBack():o;l=l.map(function(){var i=e(this);return{el:i,start:t(this)}}),a=function(){e.each(s,function(e,t){n[t]&&o[t+"Class"](n[t])})},a(),l=l.map(function(){return this.end=t(this.el[0]),this.diff=i(this.start,this.end),this}),o.attr("class",r),l=l.map(function(){var t=this,i=e.Deferred(),s=e.extend({},h,{queue:!1,complete:function(){i.resolve(t)}});return this.el.animate(this.diff,s),i.promise()}),e.when.apply(e,l.get()).done(function(){a(),e.each(arguments,function(){var t=this.el;e.each(this.diff,function(e){t.css(e,"")})}),h.complete.call(o[0])})})},e.fn.extend({addClass:function(t){return function(i,s,n,a){return s?e.effects.animateClass.call(this,{add:i},s,n,a):t.apply(this,arguments)}}(e.fn.addClass),removeClass:function(t){return function(i,s,n,a){return arguments.length>1?e.effects.animateClass.call(this,{remove:i},s,n,a):t.apply(this,arguments)}}(e.fn.removeClass),toggleClass:function(t){return function(i,s,n,a,o){return"boolean"==typeof s||void 0===s?n?e.effects.animateClass.call(this,s?{add:i}:{remove:i},n,a,o):t.apply(this,arguments):e.effects.animateClass.call(this,{toggle:i},s,n,a)}}(e.fn.toggleClass),switchClass:function(t,i,s,n,a){return e.effects.animateClass.call(this,{add:i,remove:t},s,n,a)}})}(),function(){function t(t,i,s,n){return e.isPlainObject(t)&&(i=t,t=t.effect),t={effect:t},null==i&&(i={}),e.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||e.fx.speeds[i])&&(n=s,s=i,i={}),e.isFunction(s)&&(n=s,s=null),i&&e.extend(t,i),s=s||i.duration,t.duration=e.fx.off?0:"number"==typeof s?s:s in e.fx.speeds?e.fx.speeds[s]:e.fx.speeds._default,t.complete=n||i.complete,t}function i(t){return!t||"number"==typeof t||e.fx.speeds[t]?!0:"string"!=typeof t||e.effects.effect[t]?e.isFunction(t)?!0:"object"!=typeof t||t.effect?!1:!0:!0}e.extend(e.effects,{version:"1.11.0",save:function(e,t){for(var i=0;t.length>i;i++)null!==t[i]&&e.data(v+t[i],e[0].style[t[i]])},restore:function(e,t){var i,s;for(s=0;t.length>s;s++)null!==t[s]&&(i=e.data(v+t[s]),void 0===i&&(i=""),e.css(t[s],i))},setMode:function(e,t){return"toggle"===t&&(t=e.is(":hidden")?"show":"hide"),t},getBaseline:function(e,t){var i,s;switch(e[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=e[0]/t.height}switch(e[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=e[1]/t.width}return{x:s,y:i}},createWrapper:function(t){if(t.parent().is(".ui-effects-wrapper"))return t.parent();var i={width:t.outerWidth(!0),height:t.outerHeight(!0),"float":t.css("float")},s=e("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:t.width(),height:t.height()},a=document.activeElement;try{a.id}catch(o){a=document.body}return t.wrap(s),(t[0]===a||e.contains(t[0],a))&&e(a).focus(),s=t.parent(),"static"===t.css("position")?(s.css({position:"relative"}),t.css({position:"relative"})):(e.extend(i,{position:t.css("position"),zIndex:t.css("z-index")}),e.each(["top","left","bottom","right"],function(e,s){i[s]=t.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),t.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),t.css(n),s.css(i).show()},removeWrapper:function(t){var i=document.activeElement;return t.parent().is(".ui-effects-wrapper")&&(t.parent().replaceWith(t),(t[0]===i||e.contains(t[0],i))&&e(i).focus()),t},setTransition:function(t,i,s,n){return n=n||{},e.each(i,function(e,i){var a=t.cssUnit(i);a[0]>0&&(n[i]=a[0]*s+a[1])}),n}}),e.fn.extend({effect:function(){function i(t){function i(){e.isFunction(a)&&a.call(n[0]),e.isFunction(t)&&t()}var n=e(this),a=s.complete,r=s.mode;(n.is(":hidden")?"hide"===r:"show"===r)?(n[r](),i()):o.call(n[0],s,i)}var s=t.apply(this,arguments),n=s.mode,a=s.queue,o=e.effects.effect[s.effect];return e.fx.off||!o?n?this[n](s.duration,s.complete):this.each(function(){s.complete&&s.complete.call(this)}):a===!1?this.each(i):this.queue(a||"fx",i)},show:function(e){return function(s){if(i(s))return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="show",this.effect.call(this,n)}}(e.fn.show),hide:function(e){return function(s){if(i(s))return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="hide",this.effect.call(this,n)}}(e.fn.hide),toggle:function(e){return function(s){if(i(s)||"boolean"==typeof s)return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="toggle",this.effect.call(this,n)}}(e.fn.toggle),cssUnit:function(t){var i=this.css(t),s=[];return e.each(["em","px","%","pt"],function(e,t){i.indexOf(t)>0&&(s=[parseFloat(i),t])}),s}})}(),function(){var t={};e.each(["Quad","Cubic","Quart","Quint","Expo"],function(e,i){t[i]=function(t){return Math.pow(t,e+2)}}),e.extend(t,{Sine:function(e){return 1-Math.cos(e*Math.PI/2)},Circ:function(e){return 1-Math.sqrt(1-e*e)},Elastic:function(e){return 0===e||1===e?e:-Math.pow(2,8*(e-1))*Math.sin((80*(e-1)-7.5)*Math.PI/15)},Back:function(e){return e*e*(3*e-2)},Bounce:function(e){for(var t,i=4;((t=Math.pow(2,--i))-1)/11>e;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*t-2)/22-e,2)}}),e.each(t,function(t,i){e.easing["easeIn"+t]=i,e.easing["easeOut"+t]=function(e){return 1-i(1-e)},e.easing["easeInOut"+t]=function(e){return.5>e?i(2*e)/2:1-i(-2*e+2)/2}})}(),e.effects,e.effects.effect.blind=function(t,i){var s,n,a,o=e(this),r=/up|down|vertical/,h=/up|left|vertical|horizontal/,l=["position","top","bottom","left","right","height","width"],u=e.effects.setMode(o,t.mode||"hide"),d=t.direction||"up",c=r.test(d),p=c?"height":"width",f=c?"top":"left",m=h.test(d),g={},v="show"===u;o.parent().is(".ui-effects-wrapper")?e.effects.save(o.parent(),l):e.effects.save(o,l),o.show(),s=e.effects.createWrapper(o).css({overflow:"hidden"}),n=s[p](),a=parseFloat(s.css(f))||0,g[p]=v?n:0,m||(o.css(c?"bottom":"right",0).css(c?"top":"left","auto").css({position:"absolute"}),g[f]=v?a:n+a),v&&(s.css(p,0),m||s.css(f,a+n)),s.animate(g,{duration:t.duration,easing:t.easing,queue:!1,complete:function(){"hide"===u&&o.hide(),e.effects.restore(o,l),e.effects.removeWrapper(o),i()}})},e.effects.effect.bounce=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","height","width"],h=e.effects.setMode(o,t.mode||"effect"),l="hide"===h,u="show"===h,d=t.direction||"up",c=t.distance,p=t.times||5,f=2*p+(u||l?1:0),m=t.duration/f,g=t.easing,v="up"===d||"down"===d?"top":"left",y="up"===d||"left"===d,b=o.queue(),_=b.length;for((u||l)&&r.push("opacity"),e.effects.save(o,r),o.show(),e.effects.createWrapper(o),c||(c=o["top"===v?"outerHeight":"outerWidth"]()/3),u&&(a={opacity:1},a[v]=0,o.css("opacity",0).css(v,y?2*-c:2*c).animate(a,m,g)),l&&(c/=Math.pow(2,p-1)),a={},a[v]=0,s=0;p>s;s++)n={},n[v]=(y?"-=":"+=")+c,o.animate(n,m,g).animate(a,m,g),c=l?2*c:c/2;l&&(n={opacity:0},n[v]=(y?"-=":"+=")+c,o.animate(n,m,g)),o.queue(function(){l&&o.hide(),e.effects.restore(o,r),e.effects.removeWrapper(o),i()}),_>1&&b.splice.apply(b,[1,0].concat(b.splice(_,f+1))),o.dequeue()},e.effects.effect.clip=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","height","width"],h=e.effects.setMode(o,t.mode||"hide"),l="show"===h,u=t.direction||"vertical",d="vertical"===u,c=d?"height":"width",p=d?"top":"left",f={};e.effects.save(o,r),o.show(),s=e.effects.createWrapper(o).css({overflow:"hidden"}),n="IMG"===o[0].tagName?s:o,a=n[c](),l&&(n.css(c,0),n.css(p,a/2)),f[c]=l?a:0,f[p]=l?0:a/2,n.animate(f,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){l||o.hide(),e.effects.restore(o,r),e.effects.removeWrapper(o),i()}})},e.effects.effect.drop=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","opacity","height","width"],o=e.effects.setMode(n,t.mode||"hide"),r="show"===o,h=t.direction||"left",l="up"===h||"down"===h?"top":"left",u="up"===h||"left"===h?"pos":"neg",d={opacity:r?1:0};e.effects.save(n,a),n.show(),e.effects.createWrapper(n),s=t.distance||n["top"===l?"outerHeight":"outerWidth"](!0)/2,r&&n.css("opacity",0).css(l,"pos"===u?-s:s),d[l]=(r?"pos"===u?"+=":"-=":"pos"===u?"-=":"+=")+s,n.animate(d,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}})},e.effects.effect.explode=function(t,i){function s(){b.push(this),b.length===d*c&&n()}function n(){p.css({visibility:"visible"}),e(b).remove(),m||p.hide(),i()}var a,o,r,h,l,u,d=t.pieces?Math.round(Math.sqrt(t.pieces)):3,c=d,p=e(this),f=e.effects.setMode(p,t.mode||"hide"),m="show"===f,g=p.show().css("visibility","hidden").offset(),v=Math.ceil(p.outerWidth()/c),y=Math.ceil(p.outerHeight()/d),b=[];for(a=0;d>a;a++)for(h=g.top+a*y,u=a-(d-1)/2,o=0;c>o;o++)r=g.left+o*v,l=o-(c-1)/2,p.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-o*v,top:-a*y}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:v,height:y,left:r+(m?l*v:0),top:h+(m?u*y:0),opacity:m?0:1}).animate({left:r+(m?0:l*v),top:h+(m?0:u*y),opacity:m?1:0},t.duration||500,t.easing,s)},e.effects.effect.fade=function(t,i){var s=e(this),n=e.effects.setMode(s,t.mode||"toggle");s.animate({opacity:n},{queue:!1,duration:t.duration,easing:t.easing,complete:i})},e.effects.effect.fold=function(t,i){var s,n,a=e(this),o=["position","top","bottom","left","right","height","width"],r=e.effects.setMode(a,t.mode||"hide"),h="show"===r,l="hide"===r,u=t.size||15,d=/([0-9]+)%/.exec(u),c=!!t.horizFirst,p=h!==c,f=p?["width","height"]:["height","width"],m=t.duration/2,g={},v={};e.effects.save(a,o),a.show(),s=e.effects.createWrapper(a).css({overflow:"hidden"}),n=p?[s.width(),s.height()]:[s.height(),s.width()],d&&(u=parseInt(d[1],10)/100*n[l?0:1]),h&&s.css(c?{height:0,width:u}:{height:u,width:0}),g[f[0]]=h?n[0]:u,v[f[1]]=h?n[1]:0,s.animate(g,m,t.easing).animate(v,m,t.easing,function(){l&&a.hide(),e.effects.restore(a,o),e.effects.removeWrapper(a),i()})},e.effects.effect.highlight=function(t,i){var s=e(this),n=["backgroundImage","backgroundColor","opacity"],a=e.effects.setMode(s,t.mode||"show"),o={backgroundColor:s.css("backgroundColor")};"hide"===a&&(o.opacity=0),e.effects.save(s,n),s.show().css({backgroundImage:"none",backgroundColor:t.color||"#ffff99"}).animate(o,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===a&&s.hide(),e.effects.restore(s,n),i()}})},e.effects.effect.size=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","width","height","overflow","opacity"],h=["position","top","bottom","left","right","overflow","opacity"],l=["width","height","overflow"],u=["fontSize"],d=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],c=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],p=e.effects.setMode(o,t.mode||"effect"),f=t.restore||"effect"!==p,m=t.scale||"both",g=t.origin||["middle","center"],v=o.css("position"),y=f?r:h,b={height:0,width:0,outerHeight:0,outerWidth:0};"show"===p&&o.show(),s={height:o.height(),width:o.width(),outerHeight:o.outerHeight(),outerWidth:o.outerWidth()},"toggle"===t.mode&&"show"===p?(o.from=t.to||b,o.to=t.from||s):(o.from=t.from||("show"===p?b:s),o.to=t.to||("hide"===p?b:s)),a={from:{y:o.from.height/s.height,x:o.from.width/s.width},to:{y:o.to.height/s.height,x:o.to.width/s.width}},("box"===m||"both"===m)&&(a.from.y!==a.to.y&&(y=y.concat(d),o.from=e.effects.setTransition(o,d,a.from.y,o.from),o.to=e.effects.setTransition(o,d,a.to.y,o.to)),a.from.x!==a.to.x&&(y=y.concat(c),o.from=e.effects.setTransition(o,c,a.from.x,o.from),o.to=e.effects.setTransition(o,c,a.to.x,o.to))),("content"===m||"both"===m)&&a.from.y!==a.to.y&&(y=y.concat(u).concat(l),o.from=e.effects.setTransition(o,u,a.from.y,o.from),o.to=e.effects.setTransition(o,u,a.to.y,o.to)),e.effects.save(o,y),o.show(),e.effects.createWrapper(o),o.css("overflow","hidden").css(o.from),g&&(n=e.effects.getBaseline(g,s),o.from.top=(s.outerHeight-o.outerHeight())*n.y,o.from.left=(s.outerWidth-o.outerWidth())*n.x,o.to.top=(s.outerHeight-o.to.outerHeight)*n.y,o.to.left=(s.outerWidth-o.to.outerWidth)*n.x),o.css(o.from),("content"===m||"both"===m)&&(d=d.concat(["marginTop","marginBottom"]).concat(u),c=c.concat(["marginLeft","marginRight"]),l=r.concat(d).concat(c),o.find("*[width]").each(function(){var i=e(this),s={height:i.height(),width:i.width(),outerHeight:i.outerHeight(),outerWidth:i.outerWidth()};
+f&&e.effects.save(i,l),i.from={height:s.height*a.from.y,width:s.width*a.from.x,outerHeight:s.outerHeight*a.from.y,outerWidth:s.outerWidth*a.from.x},i.to={height:s.height*a.to.y,width:s.width*a.to.x,outerHeight:s.height*a.to.y,outerWidth:s.width*a.to.x},a.from.y!==a.to.y&&(i.from=e.effects.setTransition(i,d,a.from.y,i.from),i.to=e.effects.setTransition(i,d,a.to.y,i.to)),a.from.x!==a.to.x&&(i.from=e.effects.setTransition(i,c,a.from.x,i.from),i.to=e.effects.setTransition(i,c,a.to.x,i.to)),i.css(i.from),i.animate(i.to,t.duration,t.easing,function(){f&&e.effects.restore(i,l)})})),o.animate(o.to,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){0===o.to.opacity&&o.css("opacity",o.from.opacity),"hide"===p&&o.hide(),e.effects.restore(o,y),f||("static"===v?o.css({position:"relative",top:o.to.top,left:o.to.left}):e.each(["top","left"],function(e,t){o.css(t,function(t,i){var s=parseInt(i,10),n=e?o.to.left:o.to.top;return"auto"===i?n+"px":s+n+"px"})})),e.effects.removeWrapper(o),i()}})},e.effects.effect.scale=function(t,i){var s=e(this),n=e.extend(!0,{},t),a=e.effects.setMode(s,t.mode||"effect"),o=parseInt(t.percent,10)||(0===parseInt(t.percent,10)?0:"hide"===a?0:100),r=t.direction||"both",h=t.origin,l={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()},u={y:"horizontal"!==r?o/100:1,x:"vertical"!==r?o/100:1};n.effect="size",n.queue=!1,n.complete=i,"effect"!==a&&(n.origin=h||["middle","center"],n.restore=!0),n.from=t.from||("show"===a?{height:0,width:0,outerHeight:0,outerWidth:0}:l),n.to={height:l.height*u.y,width:l.width*u.x,outerHeight:l.outerHeight*u.y,outerWidth:l.outerWidth*u.x},n.fade&&("show"===a&&(n.from.opacity=0,n.to.opacity=1),"hide"===a&&(n.from.opacity=1,n.to.opacity=0)),s.effect(n)},e.effects.effect.puff=function(t,i){var s=e(this),n=e.effects.setMode(s,t.mode||"hide"),a="hide"===n,o=parseInt(t.percent,10)||150,r=o/100,h={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()};e.extend(t,{effect:"scale",queue:!1,fade:!0,mode:n,complete:i,percent:a?o:100,from:a?h:{height:h.height*r,width:h.width*r,outerHeight:h.outerHeight*r,outerWidth:h.outerWidth*r}}),s.effect(t)},e.effects.effect.pulsate=function(t,i){var s,n=e(this),a=e.effects.setMode(n,t.mode||"show"),o="show"===a,r="hide"===a,h=o||"hide"===a,l=2*(t.times||5)+(h?1:0),u=t.duration/l,d=0,c=n.queue(),p=c.length;for((o||!n.is(":visible"))&&(n.css("opacity",0).show(),d=1),s=1;l>s;s++)n.animate({opacity:d},u,t.easing),d=1-d;n.animate({opacity:d},u,t.easing),n.queue(function(){r&&n.hide(),i()}),p>1&&c.splice.apply(c,[1,0].concat(c.splice(p,l+1))),n.dequeue()},e.effects.effect.shake=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","height","width"],o=e.effects.setMode(n,t.mode||"effect"),r=t.direction||"left",h=t.distance||20,l=t.times||3,u=2*l+1,d=Math.round(t.duration/u),c="up"===r||"down"===r?"top":"left",p="up"===r||"left"===r,f={},m={},g={},v=n.queue(),y=v.length;for(e.effects.save(n,a),n.show(),e.effects.createWrapper(n),f[c]=(p?"-=":"+=")+h,m[c]=(p?"+=":"-=")+2*h,g[c]=(p?"-=":"+=")+2*h,n.animate(f,d,t.easing),s=1;l>s;s++)n.animate(m,d,t.easing).animate(g,d,t.easing);n.animate(m,d,t.easing).animate(f,d/2,t.easing).queue(function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}),y>1&&v.splice.apply(v,[1,0].concat(v.splice(y,u+1))),n.dequeue()},e.effects.effect.slide=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","width","height"],o=e.effects.setMode(n,t.mode||"show"),r="show"===o,h=t.direction||"left",l="up"===h||"down"===h?"top":"left",u="up"===h||"left"===h,d={};e.effects.save(n,a),n.show(),s=t.distance||n["top"===l?"outerHeight":"outerWidth"](!0),e.effects.createWrapper(n).css({overflow:"hidden"}),r&&n.css(l,u?isNaN(s)?"-"+s:-s:s),d[l]=(r?u?"+=":"-=":u?"-=":"+=")+s,n.animate(d,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}})},e.effects.effect.transfer=function(t,i){var s=e(this),n=e(t.to),a="fixed"===n.css("position"),o=e("body"),r=a?o.scrollTop():0,h=a?o.scrollLeft():0,l=n.offset(),u={top:l.top-r,left:l.left-h,height:n.innerHeight(),width:n.innerWidth()},d=s.offset(),c=e("<div class='ui-effects-transfer'></div>").appendTo(document.body).addClass(t.className).css({top:d.top-r,left:d.left-h,height:s.innerHeight(),width:s.innerWidth(),position:a?"fixed":"absolute"}).animate(u,t.duration,t.easing,function(){c.remove(),i()})},e.widget("ui.progressbar",{version:"1.11.0",options:{max:100,value:0,change:null,complete:null},min:0,_create:function(){this.oldValue=this.options.value=this._constrainedValue(),this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min}),this.valueDiv=e("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this._refreshValue()},_destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove()},value:function(e){return void 0===e?this.options.value:(this.options.value=this._constrainedValue(e),this._refreshValue(),void 0)},_constrainedValue:function(e){return void 0===e&&(e=this.options.value),this.indeterminate=e===!1,"number"!=typeof e&&(e=0),this.indeterminate?!1:Math.min(this.options.max,Math.max(this.min,e))},_setOptions:function(e){var t=e.value;delete e.value,this._super(e),this.options.value=this._constrainedValue(t),this._refreshValue()},_setOption:function(e,t){"max"===e&&(t=Math.max(this.min,t)),"disabled"===e&&this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this._super(e,t)},_percentage:function(){return this.indeterminate?100:100*(this.options.value-this.min)/(this.options.max-this.min)},_refreshValue:function(){var t=this.options.value,i=this._percentage();this.valueDiv.toggle(this.indeterminate||t>this.min).toggleClass("ui-corner-right",t===this.options.max).width(i.toFixed(0)+"%"),this.element.toggleClass("ui-progressbar-indeterminate",this.indeterminate),this.indeterminate?(this.element.removeAttr("aria-valuenow"),this.overlayDiv||(this.overlayDiv=e("<div class='ui-progressbar-overlay'></div>").appendTo(this.valueDiv))):(this.element.attr({"aria-valuemax":this.options.max,"aria-valuenow":t}),this.overlayDiv&&(this.overlayDiv.remove(),this.overlayDiv=null)),this.oldValue!==t&&(this.oldValue=t,this._trigger("change")),t===this.options.max&&this._trigger("complete")}}),e.widget("ui.selectable",e.ui.mouse,{version:"1.11.0",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var t,i=this;this.element.addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){t=e(i.options.filter,i.element[0]),t.addClass("ui-selectee"),t.each(function(){var t=e(this),i=t.offset();e.data(this,"selectable-item",{element:this,$element:t,left:i.left,top:i.top,right:i.left+t.outerWidth(),bottom:i.top+t.outerHeight(),startselected:!1,selected:t.hasClass("ui-selected"),selecting:t.hasClass("ui-selecting"),unselecting:t.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=t.addClass("ui-selectee"),this._mouseInit(),this.helper=e("<div class='ui-selectable-helper'></div>")},_destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled"),this._mouseDestroy()},_mouseStart:function(t){var i=this,s=this.options;this.opos=[t.pageX,t.pageY],this.options.disabled||(this.selectees=e(s.filter,this.element[0]),this._trigger("start",t),e(s.appendTo).append(this.helper),this.helper.css({left:t.pageX,top:t.pageY,width:0,height:0}),s.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var s=e.data(this,"selectable-item");s.startselected=!0,t.metaKey||t.ctrlKey||(s.$element.removeClass("ui-selected"),s.selected=!1,s.$element.addClass("ui-unselecting"),s.unselecting=!0,i._trigger("unselecting",t,{unselecting:s.element}))}),e(t.target).parents().addBack().each(function(){var s,n=e.data(this,"selectable-item");return n?(s=!t.metaKey&&!t.ctrlKey||!n.$element.hasClass("ui-selected"),n.$element.removeClass(s?"ui-unselecting":"ui-selected").addClass(s?"ui-selecting":"ui-unselecting"),n.unselecting=!s,n.selecting=s,n.selected=s,s?i._trigger("selecting",t,{selecting:n.element}):i._trigger("unselecting",t,{unselecting:n.element}),!1):void 0}))},_mouseDrag:function(t){if(this.dragged=!0,!this.options.disabled){var i,s=this,n=this.options,a=this.opos[0],o=this.opos[1],r=t.pageX,h=t.pageY;return a>r&&(i=r,r=a,a=i),o>h&&(i=h,h=o,o=i),this.helper.css({left:a,top:o,width:r-a,height:h-o}),this.selectees.each(function(){var i=e.data(this,"selectable-item"),l=!1;i&&i.element!==s.element[0]&&("touch"===n.tolerance?l=!(i.left>r||a>i.right||i.top>h||o>i.bottom):"fit"===n.tolerance&&(l=i.left>a&&r>i.right&&i.top>o&&h>i.bottom),l?(i.selected&&(i.$element.removeClass("ui-selected"),i.selected=!1),i.unselecting&&(i.$element.removeClass("ui-unselecting"),i.unselecting=!1),i.selecting||(i.$element.addClass("ui-selecting"),i.selecting=!0,s._trigger("selecting",t,{selecting:i.element}))):(i.selecting&&((t.metaKey||t.ctrlKey)&&i.startselected?(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.$element.addClass("ui-selected"),i.selected=!0):(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.startselected&&(i.$element.addClass("ui-unselecting"),i.unselecting=!0),s._trigger("unselecting",t,{unselecting:i.element}))),i.selected&&(t.metaKey||t.ctrlKey||i.startselected||(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,s._trigger("unselecting",t,{unselecting:i.element})))))}),!1}},_mouseStop:function(t){var i=this;return this.dragged=!1,e(".ui-unselecting",this.element[0]).each(function(){var s=e.data(this,"selectable-item");s.$element.removeClass("ui-unselecting"),s.unselecting=!1,s.startselected=!1,i._trigger("unselected",t,{unselected:s.element})}),e(".ui-selecting",this.element[0]).each(function(){var s=e.data(this,"selectable-item");s.$element.removeClass("ui-selecting").addClass("ui-selected"),s.selecting=!1,s.selected=!0,s.startselected=!0,i._trigger("selected",t,{selected:s.element})}),this._trigger("stop",t),this.helper.remove(),!1}}),e.widget("ui.selectmenu",{version:"1.11.0",defaultElement:"<select>",options:{appendTo:null,disabled:null,icons:{button:"ui-icon-triangle-1-s"},position:{my:"left top",at:"left bottom",collision:"none"},width:null,change:null,close:null,focus:null,open:null,select:null},_create:function(){var e=this.element.uniqueId().attr("id");this.ids={element:e,button:e+"-button",menu:e+"-menu"},this._drawButton(),this._drawMenu(),this.options.disabled&&this.disable()},_drawButton:function(){var t=this,i=this.element.attr("tabindex");this.label=e("label[for='"+this.ids.element+"']").attr("for",this.ids.button),this._on(this.label,{click:function(e){this.button.focus(),e.preventDefault()}}),this.element.hide(),this.button=e("<span>",{"class":"ui-selectmenu-button ui-widget ui-state-default ui-corner-all",tabindex:i||this.options.disabled?-1:0,id:this.ids.button,role:"combobox","aria-expanded":"false","aria-autocomplete":"list","aria-owns":this.ids.menu,"aria-haspopup":"true"}).insertAfter(this.element),e("<span>",{"class":"ui-icon "+this.options.icons.button}).prependTo(this.button),this.buttonText=e("<span>",{"class":"ui-selectmenu-text"}).appendTo(this.button),this._setText(this.buttonText,this.element.find("option:selected").text()),this._setOption("width",this.options.width),this._on(this.button,this._buttonEvents),this.button.one("focusin",function(){t.menuItems||t._refreshMenu()}),this._hoverable(this.button),this._focusable(this.button)},_drawMenu:function(){var t=this;this.menu=e("<ul>",{"aria-hidden":"true","aria-labelledby":this.ids.button,id:this.ids.menu}),this.menuWrap=e("<div>",{"class":"ui-selectmenu-menu ui-front"}).append(this.menu).appendTo(this._appendTo()),this.menuInstance=this.menu.menu({role:"listbox",select:function(e,i){e.preventDefault(),t._select(i.item.data("ui-selectmenu-item"),e)},focus:function(e,i){var s=i.item.data("ui-selectmenu-item");null!=t.focusIndex&&s.index!==t.focusIndex&&(t._trigger("focus",e,{item:s}),t.isOpen||t._select(s,e)),t.focusIndex=s.index,t.button.attr("aria-activedescendant",t.menuItems.eq(s.index).attr("id"))}}).menu("instance"),this.menu.addClass("ui-corner-bottom").removeClass("ui-corner-all"),this.menuInstance._off(this.menu,"mouseleave"),this.menuInstance._closeOnDocumentClick=function(){return!1},this.menuInstance._isDivider=function(){return!1}},refresh:function(){this._refreshMenu(),this._setText(this.buttonText,this._getSelectedItem().text()),this._setOption("width",this.options.width)},_refreshMenu:function(){this.menu.empty();var e,t=this.element.find("option");t.length&&(this._parseOptions(t),this._renderMenu(this.menu,this.items),this.menuInstance.refresh(),this.menuItems=this.menu.find("li").not(".ui-selectmenu-optgroup"),e=this._getSelectedItem(),this.menuInstance.focus(null,e),this._setAria(e.data("ui-selectmenu-item")),this._setOption("disabled",this.element.prop("disabled")))},open:function(e){this.options.disabled||(this.menuItems?(this.menu.find(".ui-state-focus").removeClass("ui-state-focus"),this.menuInstance.focus(null,this._getSelectedItem())):this._refreshMenu(),this.isOpen=!0,this._toggleAttr(),this._resizeMenu(),this._position(),this._on(this.document,this._documentClick),this._trigger("open",e))},_position:function(){this.menuWrap.position(e.extend({of:this.button},this.options.position))},close:function(e){this.isOpen&&(this.isOpen=!1,this._toggleAttr(),this._off(this.document),this._trigger("close",e))},widget:function(){return this.button},menuWidget:function(){return this.menu},_renderMenu:function(t,i){var s=this,n="";e.each(i,function(i,a){a.optgroup!==n&&(e("<li>",{"class":"ui-selectmenu-optgroup ui-menu-divider"+(a.element.parent("optgroup").prop("disabled")?" ui-state-disabled":""),text:a.optgroup}).appendTo(t),n=a.optgroup),s._renderItemData(t,a)})},_renderItemData:function(e,t){return this._renderItem(e,t).data("ui-selectmenu-item",t)},_renderItem:function(t,i){var s=e("<li>");return i.disabled&&s.addClass("ui-state-disabled"),this._setText(s,i.label),s.appendTo(t)},_setText:function(e,t){t?e.text(t):e.html("&#160;")},_move:function(e,t){var i,s,n=".ui-menu-item";this.isOpen?i=this.menuItems.eq(this.focusIndex):(i=this.menuItems.eq(this.element[0].selectedIndex),n+=":not(.ui-state-disabled)"),s="first"===e||"last"===e?i["first"===e?"prevAll":"nextAll"](n).eq(-1):i[e+"All"](n).eq(0),s.length&&this.menuInstance.focus(t,s)},_getSelectedItem:function(){return this.menuItems.eq(this.element[0].selectedIndex)},_toggle:function(e){this[this.isOpen?"close":"open"](e)},_documentClick:{mousedown:function(t){this.isOpen&&(e(t.target).closest(".ui-selectmenu-menu, #"+this.ids.button).length||this.close(t))}},_buttonEvents:{click:"_toggle",keydown:function(t){var i=!0;switch(t.keyCode){case e.ui.keyCode.TAB:case e.ui.keyCode.ESCAPE:this.close(t),i=!1;break;case e.ui.keyCode.ENTER:this.isOpen&&this._selectFocusedItem(t);break;case e.ui.keyCode.UP:t.altKey?this._toggle(t):this._move("prev",t);break;case e.ui.keyCode.DOWN:t.altKey?this._toggle(t):this._move("next",t);break;case e.ui.keyCode.SPACE:this.isOpen?this._selectFocusedItem(t):this._toggle(t);break;case e.ui.keyCode.LEFT:this._move("prev",t);break;case e.ui.keyCode.RIGHT:this._move("next",t);break;case e.ui.keyCode.HOME:case e.ui.keyCode.PAGE_UP:this._move("first",t);break;case e.ui.keyCode.END:case e.ui.keyCode.PAGE_DOWN:this._move("last",t);break;default:this.menu.trigger(t),i=!1}i&&t.preventDefault()}},_selectFocusedItem:function(e){var t=this.menuItems.eq(this.focusIndex);t.hasClass("ui-state-disabled")||this._select(t.data("ui-selectmenu-item"),e)},_select:function(e,t){var i=this.element[0].selectedIndex;this.element[0].selectedIndex=e.index,this._setText(this.buttonText,e.label),this._setAria(e),this._trigger("select",t,{item:e}),e.index!==i&&this._trigger("change",t,{item:e}),this.close(t)},_setAria:function(e){var t=this.menuItems.eq(e.index).attr("id");this.button.attr({"aria-labelledby":t,"aria-activedescendant":t}),this.menu.attr("aria-activedescendant",t)},_setOption:function(e,t){"icons"===e&&this.button.find("span.ui-icon").removeClass(this.options.icons.button).addClass(t.button),this._super(e,t),"appendTo"===e&&this.menuWrap.appendTo(this._appendTo()),"disabled"===e&&(this.menuInstance.option("disabled",t),this.button.toggleClass("ui-state-disabled",t).attr("aria-disabled",t),this.element.prop("disabled",t),t?(this.button.attr("tabindex",-1),this.close()):this.button.attr("tabindex",0)),"width"===e&&(t||(t=this.element.outerWidth()),this.button.outerWidth(t))},_appendTo:function(){var t=this.options.appendTo;return t&&(t=t.jquery||t.nodeType?e(t):this.document.find(t).eq(0)),t&&t[0]||(t=this.element.closest(".ui-front")),t.length||(t=this.document[0].body),t},_toggleAttr:function(){this.button.toggleClass("ui-corner-top",this.isOpen).toggleClass("ui-corner-all",!this.isOpen).attr("aria-expanded",this.isOpen),this.menuWrap.toggleClass("ui-selectmenu-open",this.isOpen),this.menu.attr("aria-hidden",!this.isOpen)},_resizeMenu:function(){this.menu.outerWidth(Math.max(this.button.outerWidth(),this.menu.width("").outerWidth()+1))},_getCreateOptions:function(){return{disabled:this.element.prop("disabled")}},_parseOptions:function(t){var i=[];t.each(function(t,s){var n=e(s),a=n.parent("optgroup");i.push({element:n,index:t,value:n.attr("value"),label:n.text(),optgroup:a.attr("label")||"",disabled:a.prop("disabled")||n.prop("disabled")})}),this.items=i},_destroy:function(){this.menuWrap.remove(),this.button.remove(),this.element.show(),this.element.removeUniqueId(),this.label.attr("for",this.ids.element)}}),e.widget("ui.slider",e.ui.mouse,{version:"1.11.0",widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},numPages:5,_create:function(){this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"),this._refresh(),this._setOption("disabled",this.options.disabled),this._animateOff=!1},_refresh:function(){this._createRange(),this._createHandles(),this._setupEvents(),this._refreshValue()},_createHandles:function(){var t,i,s=this.options,n=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),a="<span class='ui-slider-handle ui-state-default ui-corner-all' tabindex='0'></span>",o=[];for(i=s.values&&s.values.length||1,n.length>i&&(n.slice(i).remove(),n=n.slice(0,i)),t=n.length;i>t;t++)o.push(a);this.handles=n.add(e(o.join("")).appendTo(this.element)),this.handle=this.handles.eq(0),this.handles.each(function(t){e(this).data("ui-slider-handle-index",t)})},_createRange:function(){var t=this.options,i="";t.range?(t.range===!0&&(t.values?t.values.length&&2!==t.values.length?t.values=[t.values[0],t.values[0]]:e.isArray(t.values)&&(t.values=t.values.slice(0)):t.values=[this._valueMin(),this._valueMin()]),this.range&&this.range.length?this.range.removeClass("ui-slider-range-min ui-slider-range-max").css({left:"",bottom:""}):(this.range=e("<div></div>").appendTo(this.element),i="ui-slider-range ui-widget-header ui-corner-all"),this.range.addClass(i+("min"===t.range||"max"===t.range?" ui-slider-range-"+t.range:""))):(this.range&&this.range.remove(),this.range=null)},_setupEvents:function(){this._off(this.handles),this._on(this.handles,this._handleEvents),this._hoverable(this.handles),this._focusable(this.handles)},_destroy:function(){this.handles.remove(),this.range&&this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-widget ui-widget-content ui-corner-all"),this._mouseDestroy()},_mouseCapture:function(t){var i,s,n,a,o,r,h,l,u=this,d=this.options;return d.disabled?!1:(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),i={x:t.pageX,y:t.pageY},s=this._normValueFromMouse(i),n=this._valueMax()-this._valueMin()+1,this.handles.each(function(t){var i=Math.abs(s-u.values(t));(n>i||n===i&&(t===u._lastChangedValue||u.values(t)===d.min))&&(n=i,a=e(this),o=t)}),r=this._start(t,o),r===!1?!1:(this._mouseSliding=!0,this._handleIndex=o,a.addClass("ui-state-active").focus(),h=a.offset(),l=!e(t.target).parents().addBack().is(".ui-slider-handle"),this._clickOffset=l?{left:0,top:0}:{left:t.pageX-h.left-a.width()/2,top:t.pageY-h.top-a.height()/2-(parseInt(a.css("borderTopWidth"),10)||0)-(parseInt(a.css("borderBottomWidth"),10)||0)+(parseInt(a.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(t,o,s),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(e){var t={x:e.pageX,y:e.pageY},i=this._normValueFromMouse(t);return this._slide(e,this._handleIndex,i),!1},_mouseStop:function(e){return this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(e,this._handleIndex),this._change(e,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation="vertical"===this.options.orientation?"vertical":"horizontal"},_normValueFromMouse:function(e){var t,i,s,n,a;return"horizontal"===this.orientation?(t=this.elementSize.width,i=e.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(t=this.elementSize.height,i=e.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),s=i/t,s>1&&(s=1),0>s&&(s=0),"vertical"===this.orientation&&(s=1-s),n=this._valueMax()-this._valueMin(),a=this._valueMin()+s*n,this._trimAlignValue(a)},_start:function(e,t){var i={handle:this.handles[t],value:this.value()};return this.options.values&&this.options.values.length&&(i.value=this.values(t),i.values=this.values()),this._trigger("start",e,i)},_slide:function(e,t,i){var s,n,a;this.options.values&&this.options.values.length?(s=this.values(t?0:1),2===this.options.values.length&&this.options.range===!0&&(0===t&&i>s||1===t&&s>i)&&(i=s),i!==this.values(t)&&(n=this.values(),n[t]=i,a=this._trigger("slide",e,{handle:this.handles[t],value:i,values:n}),s=this.values(t?0:1),a!==!1&&this.values(t,i))):i!==this.value()&&(a=this._trigger("slide",e,{handle:this.handles[t],value:i}),a!==!1&&this.value(i))},_stop:function(e,t){var i={handle:this.handles[t],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(t),i.values=this.values()),this._trigger("stop",e,i)},_change:function(e,t){if(!this._keySliding&&!this._mouseSliding){var i={handle:this.handles[t],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(t),i.values=this.values()),this._lastChangedValue=t,this._trigger("change",e,i)}},value:function(e){return arguments.length?(this.options.value=this._trimAlignValue(e),this._refreshValue(),this._change(null,0),void 0):this._value()},values:function(t,i){var s,n,a;if(arguments.length>1)return this.options.values[t]=this._trimAlignValue(i),this._refreshValue(),this._change(null,t),void 0;if(!arguments.length)return this._values();if(!e.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(t):this.value();for(s=this.options.values,n=arguments[0],a=0;s.length>a;a+=1)s[a]=this._trimAlignValue(n[a]),this._change(null,a);this._refreshValue()},_setOption:function(t,i){var s,n=0;switch("range"===t&&this.options.range===!0&&("min"===i?(this.options.value=this._values(0),this.options.values=null):"max"===i&&(this.options.value=this._values(this.options.values.length-1),this.options.values=null)),e.isArray(this.options.values)&&(n=this.options.values.length),"disabled"===t&&this.element.toggleClass("ui-state-disabled",!!i),this._super(t,i),t){case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue();break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":for(this._animateOff=!0,this._refreshValue(),s=0;n>s;s+=1)this._change(null,s);this._animateOff=!1;break;case"min":case"max":this._animateOff=!0,this._refreshValue(),this._animateOff=!1;break;case"range":this._animateOff=!0,this._refresh(),this._animateOff=!1}},_value:function(){var e=this.options.value;return e=this._trimAlignValue(e)},_values:function(e){var t,i,s;if(arguments.length)return t=this.options.values[e],t=this._trimAlignValue(t);if(this.options.values&&this.options.values.length){for(i=this.options.values.slice(),s=0;i.length>s;s+=1)i[s]=this._trimAlignValue(i[s]);return i}return[]},_trimAlignValue:function(e){if(this._valueMin()>=e)return this._valueMin();if(e>=this._valueMax())return this._valueMax();var t=this.options.step>0?this.options.step:1,i=(e-this._valueMin())%t,s=e-i;return 2*Math.abs(i)>=t&&(s+=i>0?t:-t),parseFloat(s.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var t,i,s,n,a,o=this.options.range,r=this.options,h=this,l=this._animateOff?!1:r.animate,u={};this.options.values&&this.options.values.length?this.handles.each(function(s){i=100*((h.values(s)-h._valueMin())/(h._valueMax()-h._valueMin())),u["horizontal"===h.orientation?"left":"bottom"]=i+"%",e(this).stop(1,1)[l?"animate":"css"](u,r.animate),h.options.range===!0&&("horizontal"===h.orientation?(0===s&&h.range.stop(1,1)[l?"animate":"css"]({left:i+"%"},r.animate),1===s&&h.range[l?"animate":"css"]({width:i-t+"%"},{queue:!1,duration:r.animate})):(0===s&&h.range.stop(1,1)[l?"animate":"css"]({bottom:i+"%"},r.animate),1===s&&h.range[l?"animate":"css"]({height:i-t+"%"},{queue:!1,duration:r.animate}))),t=i}):(s=this.value(),n=this._valueMin(),a=this._valueMax(),i=a!==n?100*((s-n)/(a-n)):0,u["horizontal"===this.orientation?"left":"bottom"]=i+"%",this.handle.stop(1,1)[l?"animate":"css"](u,r.animate),"min"===o&&"horizontal"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({width:i+"%"},r.animate),"max"===o&&"horizontal"===this.orientation&&this.range[l?"animate":"css"]({width:100-i+"%"},{queue:!1,duration:r.animate}),"min"===o&&"vertical"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({height:i+"%"},r.animate),"max"===o&&"vertical"===this.orientation&&this.range[l?"animate":"css"]({height:100-i+"%"},{queue:!1,duration:r.animate}))},_handleEvents:{keydown:function(t){var i,s,n,a,o=e(t.target).data("ui-slider-handle-index");switch(t.keyCode){case e.ui.keyCode.HOME:case e.ui.keyCode.END:case e.ui.keyCode.PAGE_UP:case e.ui.keyCode.PAGE_DOWN:case e.ui.keyCode.UP:case e.ui.keyCode.RIGHT:case e.ui.keyCode.DOWN:case e.ui.keyCode.LEFT:if(t.preventDefault(),!this._keySliding&&(this._keySliding=!0,e(t.target).addClass("ui-state-active"),i=this._start(t,o),i===!1))return}switch(a=this.options.step,s=n=this.options.values&&this.options.values.length?this.values(o):this.value(),t.keyCode){case e.ui.keyCode.HOME:n=this._valueMin();break;case e.ui.keyCode.END:n=this._valueMax();break;case e.ui.keyCode.PAGE_UP:n=this._trimAlignValue(s+(this._valueMax()-this._valueMin())/this.numPages);break;case e.ui.keyCode.PAGE_DOWN:n=this._trimAlignValue(s-(this._valueMax()-this._valueMin())/this.numPages);break;case e.ui.keyCode.UP:case e.ui.keyCode.RIGHT:if(s===this._valueMax())return;n=this._trimAlignValue(s+a);break;case e.ui.keyCode.DOWN:case e.ui.keyCode.LEFT:if(s===this._valueMin())return;n=this._trimAlignValue(s-a)}this._slide(t,o,n)},keyup:function(t){var i=e(t.target).data("ui-slider-handle-index");this._keySliding&&(this._keySliding=!1,this._stop(t,i),this._change(t,i),e(t.target).removeClass("ui-state-active"))}}}),e.widget("ui.sortable",e.ui.mouse,{version:"1.11.0",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(e,t,i){return e>=t&&t+i>e},_isFloating:function(e){return/left|right/.test(e.css("float"))||/inline|table-cell/.test(e.css("display"))},_create:function(){var e=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?"x"===e.axis||this._isFloating(this.items[0].item):!1,this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(e,t){this._super(e,t),"handle"===e&&this._setHandleClassName()},_setHandleClassName:function(){this.element.find(".ui-sortable-handle").removeClass("ui-sortable-handle"),e.each(this.items,function(){(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item).addClass("ui-sortable-handle")})},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").find(".ui-sortable-handle").removeClass("ui-sortable-handle"),this._mouseDestroy();for(var e=this.items.length-1;e>=0;e--)this.items[e].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(t,i){var s=null,n=!1,a=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(t),e(t.target).parents().each(function(){return e.data(this,a.widgetName+"-item")===a?(s=e(this),!1):void 0}),e.data(t.target,a.widgetName+"-item")===a&&(s=e(t.target)),s?!this.options.handle||i||(e(this.options.handle,s).find("*").addBack().each(function(){this===t.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(t,i,s){var n,a,o=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(t),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,o.cursorAt&&this._adjustOffsetFromHelper(o.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),o.containment&&this._setContainment(),o.cursor&&"auto"!==o.cursor&&(a=this.document.find("body"),this.storedCursor=a.css("cursor"),a.css("cursor",o.cursor),this.storedStylesheet=e("<style>*{ cursor: "+o.cursor+" !important; }</style>").appendTo(a)),o.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",o.opacity)),o.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",o.zIndex)),this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",t,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",t,this._uiHash(this));
+return e.ui.ddmanager&&(e.ui.ddmanager.current=this),e.ui.ddmanager&&!o.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(t),!0},_mouseDrag:function(t){var i,s,n,a,o=this.options,r=!1;for(this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-t.pageY<o.scrollSensitivity?this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop+o.scrollSpeed:t.pageY-this.overflowOffset.top<o.scrollSensitivity&&(this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop-o.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-t.pageX<o.scrollSensitivity?this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft+o.scrollSpeed:t.pageX-this.overflowOffset.left<o.scrollSensitivity&&(this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft-o.scrollSpeed)):(t.pageY-e(document).scrollTop()<o.scrollSensitivity?r=e(document).scrollTop(e(document).scrollTop()-o.scrollSpeed):e(window).height()-(t.pageY-e(document).scrollTop())<o.scrollSensitivity&&(r=e(document).scrollTop(e(document).scrollTop()+o.scrollSpeed)),t.pageX-e(document).scrollLeft()<o.scrollSensitivity?r=e(document).scrollLeft(e(document).scrollLeft()-o.scrollSpeed):e(window).width()-(t.pageX-e(document).scrollLeft())<o.scrollSensitivity&&(r=e(document).scrollLeft(e(document).scrollLeft()+o.scrollSpeed))),r!==!1&&e.ui.ddmanager&&!o.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t)),this.positionAbs=this._convertPositionTo("absolute"),this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),i=this.items.length-1;i>=0;i--)if(s=this.items[i],n=s.item[0],a=this._intersectsWithPointer(s),a&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===a?"next":"prev"]()[0]!==n&&!e.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!e.contains(this.element[0],n):!0)){if(this.direction=1===a?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(t,s),this._trigger("change",t,this._uiHash());break}return this._contactContainers(t),e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),this._trigger("sort",t,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(t,i){if(t){if(e.ui.ddmanager&&!this.options.dropBehaviour&&e.ui.ddmanager.drop(this,t),this.options.revert){var s=this,n=this.placeholder.offset(),a=this.options.axis,o={};a&&"x"!==a||(o.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollLeft)),a&&"y"!==a||(o.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,e(this.helper).animate(o,parseInt(this.options.revert,10)||500,function(){s._clear(t)})}else this._clear(t,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null}),"original"===this.options.helper?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var t=this.containers.length-1;t>=0;t--)this.containers[t]._trigger("deactivate",null,this._uiHash(this)),this.containers[t].containerCache.over&&(this.containers[t]._trigger("out",null,this._uiHash(this)),this.containers[t].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),e.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?e(this.domPosition.prev).after(this.currentItem):e(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(t){var i=this._getItemsAsjQuery(t&&t.connected),s=[];return t=t||{},e(i).each(function(){var i=(e(t.item||this).attr(t.attribute||"id")||"").match(t.expression||/(.+)[\-=_](.+)/);i&&s.push((t.key||i[1]+"[]")+"="+(t.key&&t.expression?i[1]:i[2]))}),!s.length&&t.key&&s.push(t.key+"="),s.join("&")},toArray:function(t){var i=this._getItemsAsjQuery(t&&t.connected),s=[];return t=t||{},i.each(function(){s.push(e(t.item||this).attr(t.attribute||"id")||"")}),s},_intersectsWith:function(e){var t=this.positionAbs.left,i=t+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,a=e.left,o=a+e.width,r=e.top,h=r+e.height,l=this.offset.click.top,u=this.offset.click.left,d="x"===this.options.axis||s+l>r&&h>s+l,c="y"===this.options.axis||t+u>a&&o>t+u,p=d&&c;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>e[this.floating?"width":"height"]?p:t+this.helperProportions.width/2>a&&o>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(e){var t="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,e.top,e.height),i="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,e.left,e.width),s=t&&i,n=this._getDragVerticalDirection(),a=this._getDragHorizontalDirection();return s?this.floating?a&&"right"===a||"down"===n?2:1:n&&("down"===n?2:1):!1},_intersectsWithSides:function(e){var t=this._isOverAxis(this.positionAbs.top+this.offset.click.top,e.top+e.height/2,e.height),i=this._isOverAxis(this.positionAbs.left+this.offset.click.left,e.left+e.width/2,e.width),s=this._getDragVerticalDirection(),n=this._getDragHorizontalDirection();return this.floating&&n?"right"===n&&i||"left"===n&&!i:s&&("down"===s&&t||"up"===s&&!t)},_getDragVerticalDirection:function(){var e=this.positionAbs.top-this.lastPositionAbs.top;return 0!==e&&(e>0?"down":"up")},_getDragHorizontalDirection:function(){var e=this.positionAbs.left-this.lastPositionAbs.left;return 0!==e&&(e>0?"right":"left")},refresh:function(e){return this._refreshItems(e),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var e=this.options;return e.connectWith.constructor===String?[e.connectWith]:e.connectWith},_getItemsAsjQuery:function(t){function i(){r.push(this)}var s,n,a,o,r=[],h=[],l=this._connectWith();if(l&&t)for(s=l.length-1;s>=0;s--)for(a=e(l[s]),n=a.length-1;n>=0;n--)o=e.data(a[n],this.widgetFullName),o&&o!==this&&!o.options.disabled&&h.push([e.isFunction(o.options.items)?o.options.items.call(o.element):e(o.options.items,o.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),o]);for(h.push([e.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):e(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=h.length-1;s>=0;s--)h[s][0].each(i);return e(r)},_removeCurrentsFromItems:function(){var t=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=e.grep(this.items,function(e){for(var i=0;t.length>i;i++)if(t[i]===e.item[0])return!1;return!0})},_refreshItems:function(t){this.items=[],this.containers=[this];var i,s,n,a,o,r,h,l,u=this.items,d=[[e.isFunction(this.options.items)?this.options.items.call(this.element[0],t,{item:this.currentItem}):e(this.options.items,this.element),this]],c=this._connectWith();if(c&&this.ready)for(i=c.length-1;i>=0;i--)for(n=e(c[i]),s=n.length-1;s>=0;s--)a=e.data(n[s],this.widgetFullName),a&&a!==this&&!a.options.disabled&&(d.push([e.isFunction(a.options.items)?a.options.items.call(a.element[0],t,{item:this.currentItem}):e(a.options.items,a.element),a]),this.containers.push(a));for(i=d.length-1;i>=0;i--)for(o=d[i][1],r=d[i][0],s=0,l=r.length;l>s;s++)h=e(r[s]),h.data(this.widgetName+"-item",o),u.push({item:h,instance:o,width:0,height:0,left:0,top:0})},refreshPositions:function(t){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,a;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?e(this.options.toleranceElement,s.item):s.item,t||(s.width=n.outerWidth(),s.height=n.outerHeight()),a=n.offset(),s.left=a.left,s.top=a.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)a=this.containers[i].element.offset(),this.containers[i].containerCache.left=a.left,this.containers[i].containerCache.top=a.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(t){t=t||this;var i,s=t.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=t.currentItem[0].nodeName.toLowerCase(),n=e("<"+s+">",t.document[0]).addClass(i||t.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper");return"tr"===s?t.currentItem.children().each(function(){e("<td>&#160;</td>",t.document[0]).attr("colspan",e(this).attr("colspan")||1).appendTo(n)}):"img"===s&&n.attr("src",t.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(e,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(t.currentItem.innerHeight()-parseInt(t.currentItem.css("paddingTop")||0,10)-parseInt(t.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(t.currentItem.innerWidth()-parseInt(t.currentItem.css("paddingLeft")||0,10)-parseInt(t.currentItem.css("paddingRight")||0,10)))}}),t.placeholder=e(s.placeholder.element.call(t.element,t.currentItem)),t.currentItem.after(t.placeholder),s.placeholder.update(t,t.placeholder)},_contactContainers:function(t){var i,s,n,a,o,r,h,l,u,d,c=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!e.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(c&&e.contains(this.containers[i].element[0],c.element[0]))continue;c=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",t,this._uiHash(this)),this.containers[i].containerCache.over=0);if(c)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",t,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,a=null,u=c.floating||this._isFloating(this.currentItem),o=u?"left":"top",r=u?"width":"height",d=u?"clientX":"clientY",s=this.items.length-1;s>=0;s--)e.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(h=this.items[s].item.offset()[o],l=!1,t[d]-h>this.items[s][r]/2&&(l=!0),n>Math.abs(t[d]-h)&&(n=Math.abs(t[d]-h),a=this.items[s],this.direction=l?"up":"down"));if(!a&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return;a?this._rearrange(t,a,null,!0):this._rearrange(t,null,this.containers[p].element,!0),this._trigger("change",t,this._uiHash()),this.containers[p]._trigger("change",t,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",t,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(t){var i=this.options,s=e.isFunction(i.helper)?e(i.helper.apply(this.element[0],[t,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||e("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var t=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&e.ui.ie)&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var e=this.currentItem.position();return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:e.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,e("document"===n.containment?document:window).width()-this.helperProportions.width-this.margins.left,(e("document"===n.containment?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(t=e(n.containment)[0],i=e(n.containment).offset(),s="hidden"!==e(t).css("overflow"),this.containment=[i.left+(parseInt(e(t).css("borderLeftWidth"),10)||0)+(parseInt(e(t).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(e(t).css("borderTopWidth"),10)||0)+(parseInt(e(t).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(t.scrollWidth,t.offsetWidth):t.offsetWidth)-(parseInt(e(t).css("borderLeftWidth"),10)||0)-(parseInt(e(t).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(t.scrollHeight,t.offsetHeight):t.offsetHeight)-(parseInt(e(t).css("borderTopWidth"),10)||0)-(parseInt(e(t).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(t,i){i||(i=this.position);var s="absolute"===t?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,a=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():a?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():a?0:n.scrollLeft())*s}},_generatePosition:function(t){var i,s,n=this.options,a=t.pageX,o=t.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==document&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(t.pageX-this.offset.click.left<this.containment[0]&&(a=this.containment[0]+this.offset.click.left),t.pageY-this.offset.click.top<this.containment[1]&&(o=this.containment[1]+this.offset.click.top),t.pageX-this.offset.click.left>this.containment[2]&&(a=this.containment[2]+this.offset.click.left),t.pageY-this.offset.click.top>this.containment[3]&&(o=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((o-this.originalPageY)/n.grid[1])*n.grid[1],o=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((a-this.originalPageX)/n.grid[0])*n.grid[0],a=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:o-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:a-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(e,t,i,s){i?i[0].appendChild(this.placeholder[0]):t.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?t.item[0]:t.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(e,t){function i(e,t,i){return function(s){i._trigger(e,s,t._uiHash(t))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!t&&n.push(function(e){this._trigger("receive",e,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||t||n.push(function(e){this._trigger("update",e,this._uiHash())}),this!==this.currentContainer&&(t||(n.push(function(e){this._trigger("remove",e,this._uiHash())}),n.push(function(e){return function(t){e._trigger("receive",t,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(e){return function(t){e._trigger("update",t,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)t||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,this.cancelHelperRemoval){if(!t){for(this._trigger("beforeStop",e,this._uiHash()),s=0;n.length>s;s++)n[s].call(this,e);this._trigger("stop",e,this._uiHash())}return this.fromOutside=!1,!1}if(t||this._trigger("beforeStop",e,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null,!t){for(s=0;n.length>s;s++)n[s].call(this,e);this._trigger("stop",e,this._uiHash())}return this.fromOutside=!1,!0},_trigger:function(){e.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(t){var i=t||this;return{helper:i.helper,placeholder:i.placeholder||e([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:t?t.element:null}}}),e.widget("ui.spinner",{version:"1.11.0",defaultElement:"<input>",widgetEventPrefix:"spin",options:{culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),""!==this.value()&&this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var t={},i=this.element;return e.each(["min","max","step"],function(e,s){var n=i.attr(s);void 0!==n&&n.length&&(t[s]=n)}),t},_events:{keydown:function(e){this._start(e)&&this._keydown(e)&&e.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,void 0):(this._stop(),this._refresh(),this.previous!==this.element.val()&&this._trigger("change",e),void 0)},mousewheel:function(e,t){if(t){if(!this.spinning&&!this._start(e))return!1;this._spin((t>0?1:-1)*this.options.step,e),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(e)},100),e.preventDefault()}},"mousedown .ui-spinner-button":function(t){function i(){var e=this.element[0]===this.document[0].activeElement;e||(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s}))}var s;s=this.element[0]===this.document[0].activeElement?this.previous:this.element.val(),t.preventDefault(),i.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,i.call(this)}),this._start(t)!==!1&&this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(t){return e(t.currentTarget).hasClass("ui-state-active")?this._start(t)===!1?!1:(this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t),void 0):void 0},"mouseleave .ui-spinner-button":"_stop"},_draw:function(){var e=this.uiSpinner=this.element.addClass("ui-spinner-input").attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml());this.element.attr("role","spinbutton"),this.buttons=e.find(".ui-spinner-button").attr("tabIndex",-1).button().removeClass("ui-corner-all"),this.buttons.height()>Math.ceil(.5*e.height())&&e.height()>0&&e.height(e.height()),this.options.disabled&&this.disable()},_keydown:function(t){var i=this.options,s=e.ui.keyCode;switch(t.keyCode){case s.UP:return this._repeat(null,1,t),!0;case s.DOWN:return this._repeat(null,-1,t),!0;case s.PAGE_UP:return this._repeat(null,i.page,t),!0;case s.PAGE_DOWN:return this._repeat(null,-i.page,t),!0}return!1},_uiSpinnerHtml:function(){return"<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>"},_buttonHtml:function(){return"<a class='ui-spinner-button ui-spinner-up ui-corner-tr'><span class='ui-icon "+this.options.icons.up+"'>&#9650;</span>"+"</a>"+"<a class='ui-spinner-button ui-spinner-down ui-corner-br'>"+"<span class='ui-icon "+this.options.icons.down+"'>&#9660;</span>"+"</a>"},_start:function(e){return this.spinning||this._trigger("start",e)!==!1?(this.counter||(this.counter=1),this.spinning=!0,!0):!1},_repeat:function(e,t,i){e=e||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,t,i)},e),this._spin(t*this.options.step,i)},_spin:function(e,t){var i=this.value()||0;this.counter||(this.counter=1),i=this._adjustValue(i+e*this._increment(this.counter)),this.spinning&&this._trigger("spin",t,{value:i})===!1||(this._value(i),this.counter++)},_increment:function(t){var i=this.options.incremental;return i?e.isFunction(i)?i(t):Math.floor(t*t*t/5e4-t*t/500+17*t/200+1):1},_precision:function(){var e=this._precisionOf(this.options.step);return null!==this.options.min&&(e=Math.max(e,this._precisionOf(this.options.min))),e},_precisionOf:function(e){var t=""+e,i=t.indexOf(".");return-1===i?0:t.length-i-1},_adjustValue:function(e){var t,i,s=this.options;return t=null!==s.min?s.min:0,i=e-t,i=Math.round(i/s.step)*s.step,e=t+i,e=parseFloat(e.toFixed(this._precision())),null!==s.max&&e>s.max?s.max:null!==s.min&&s.min>e?s.min:e},_stop:function(e){this.spinning&&(clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",e))},_setOption:function(e,t){if("culture"===e||"numberFormat"===e){var i=this._parse(this.element.val());return this.options[e]=t,this.element.val(this._format(i)),void 0}("max"===e||"min"===e||"step"===e)&&"string"==typeof t&&(t=this._parse(t)),"icons"===e&&(this.buttons.first().find(".ui-icon").removeClass(this.options.icons.up).addClass(t.up),this.buttons.last().find(".ui-icon").removeClass(this.options.icons.down).addClass(t.down)),this._super(e,t),"disabled"===e&&(this.widget().toggleClass("ui-state-disabled",!!t),this.element.prop("disabled",!!t),this.buttons.button(t?"disable":"enable"))},_setOptions:r(function(e){this._super(e)}),_parse:function(e){return"string"==typeof e&&""!==e&&(e=window.Globalize&&this.options.numberFormat?Globalize.parseFloat(e,10,this.options.culture):+e),""===e||isNaN(e)?null:e},_format:function(e){return""===e?"":window.Globalize&&this.options.numberFormat?Globalize.format(e,this.options.numberFormat,this.options.culture):e},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},isValid:function(){var e=this.value();return null===e?!1:e===this._adjustValue(e)},_value:function(e,t){var i;""!==e&&(i=this._parse(e),null!==i&&(t||(i=this._adjustValue(i)),e=this._format(i))),this.element.val(e),this._refresh()},_destroy:function(){this.element.removeClass("ui-spinner-input").prop("disabled",!1).removeAttr("autocomplete").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:r(function(e){this._stepUp(e)}),_stepUp:function(e){this._start()&&(this._spin((e||1)*this.options.step),this._stop())},stepDown:r(function(e){this._stepDown(e)}),_stepDown:function(e){this._start()&&(this._spin((e||1)*-this.options.step),this._stop())},pageUp:r(function(e){this._stepUp((e||1)*this.options.page)}),pageDown:r(function(e){this._stepDown((e||1)*this.options.page)}),value:function(e){return arguments.length?(r(this._value).call(this,e),void 0):this._parse(this.element.val())},widget:function(){return this.uiSpinner}}),e.widget("ui.tabs",{version:"1.11.0",delay:300,options:{active:null,collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_isLocal:function(){var e=/#.*$/;return function(t){var i,s;t=t.cloneNode(!1),i=t.href.replace(e,""),s=location.href.replace(e,"");try{i=decodeURIComponent(i)}catch(n){}try{s=decodeURIComponent(s)}catch(n){}return t.hash.length>1&&i===s}}(),_create:function(){var t=this,i=this.options;this.running=!1,this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all").toggleClass("ui-tabs-collapsible",i.collapsible).delegate(".ui-tabs-nav > li","mousedown"+this.eventNamespace,function(t){e(this).is(".ui-state-disabled")&&t.preventDefault()}).delegate(".ui-tabs-anchor","focus"+this.eventNamespace,function(){e(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this._processTabs(),i.active=this._initialActive(),e.isArray(i.disabled)&&(i.disabled=e.unique(i.disabled.concat(e.map(this.tabs.filter(".ui-state-disabled"),function(e){return t.tabs.index(e)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(i.active):e(),this._refresh(),this.active.length&&this.load(i.active)},_initialActive:function(){var t=this.options.active,i=this.options.collapsible,s=location.hash.substring(1);return null===t&&(s&&this.tabs.each(function(i,n){return e(n).attr("aria-controls")===s?(t=i,!1):void 0}),null===t&&(t=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===t||-1===t)&&(t=this.tabs.length?0:!1)),t!==!1&&(t=this.tabs.index(this.tabs.eq(t)),-1===t&&(t=i?!1:0)),!i&&t===!1&&this.anchors.length&&(t=0),t},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):e()}},_tabKeydown:function(t){var i=e(this.document[0].activeElement).closest("li"),s=this.tabs.index(i),n=!0;if(!this._handlePageNav(t)){switch(t.keyCode){case e.ui.keyCode.RIGHT:case e.ui.keyCode.DOWN:s++;break;case e.ui.keyCode.UP:case e.ui.keyCode.LEFT:n=!1,s--;break;case e.ui.keyCode.END:s=this.anchors.length-1;break;case e.ui.keyCode.HOME:s=0;break;case e.ui.keyCode.SPACE:return t.preventDefault(),clearTimeout(this.activating),this._activate(s),void 0;case e.ui.keyCode.ENTER:return t.preventDefault(),clearTimeout(this.activating),this._activate(s===this.options.active?!1:s),void 0;default:return}t.preventDefault(),clearTimeout(this.activating),s=this._focusNextTab(s,n),t.ctrlKey||(i.attr("aria-selected","false"),this.tabs.eq(s).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",s)},this.delay))}},_panelKeydown:function(t){this._handlePageNav(t)||t.ctrlKey&&t.keyCode===e.ui.keyCode.UP&&(t.preventDefault(),this.active.focus())},_handlePageNav:function(t){return t.altKey&&t.keyCode===e.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):t.altKey&&t.keyCode===e.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):void 0},_findNextTab:function(t,i){function s(){return t>n&&(t=0),0>t&&(t=n),t}for(var n=this.tabs.length-1;-1!==e.inArray(s(),this.options.disabled);)t=i?t+1:t-1;return t},_focusNextTab:function(e,t){return e=this._findNextTab(e,t),this.tabs.eq(e).focus(),e},_setOption:function(e,t){return"active"===e?(this._activate(t),void 0):"disabled"===e?(this._setupDisabled(t),void 0):(this._super(e,t),"collapsible"===e&&(this.element.toggleClass("ui-tabs-collapsible",t),t||this.options.active!==!1||this._activate(0)),"event"===e&&this._setupEvents(t),"heightStyle"===e&&this._setupHeightStyle(t),void 0)},_sanitizeSelector:function(e){return e?e.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var t=this.options,i=this.tablist.children(":has(a[href])");t.disabled=e.map(i.filter(".ui-state-disabled"),function(e){return i.index(e)}),this._processTabs(),t.active!==!1&&this.anchors.length?this.active.length&&!e.contains(this.tablist[0],this.active[0])?this.tabs.length===t.disabled.length?(t.active=!1,this.active=e()):this._activate(this._findNextTab(Math.max(0,t.active-1),!1)):t.active=this.tabs.index(this.active):(t.active=!1,this.active=e()),this._refresh()},_refresh:function(){this._setupDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-hidden":"true"}),this.active.length?(this.active.addClass("ui-tabs-active ui-state-active").attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}),this._getPanelForTab(this.active).show().attr({"aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var t=this;this.tablist=this._getList().addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").attr("role","tablist"),this.tabs=this.tablist.find("> li:has(a[href])").addClass("ui-state-default ui-corner-top").attr({role:"tab",tabIndex:-1}),this.anchors=this.tabs.map(function(){return e("a",this)[0]
+}).addClass("ui-tabs-anchor").attr({role:"presentation",tabIndex:-1}),this.panels=e(),this.anchors.each(function(i,s){var n,a,o,r=e(s).uniqueId().attr("id"),h=e(s).closest("li"),l=h.attr("aria-controls");t._isLocal(s)?(n=s.hash,o=n.substring(1),a=t.element.find(t._sanitizeSelector(n))):(o=h.attr("aria-controls")||e({}).uniqueId()[0].id,n="#"+o,a=t.element.find(n),a.length||(a=t._createPanel(o),a.insertAfter(t.panels[i-1]||t.tablist)),a.attr("aria-live","polite")),a.length&&(t.panels=t.panels.add(a)),l&&h.data("ui-tabs-aria-controls",l),h.attr({"aria-controls":o,"aria-labelledby":r}),a.attr("aria-labelledby",r)}),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").attr("role","tabpanel")},_getList:function(){return this.tablist||this.element.find("ol,ul").eq(0)},_createPanel:function(t){return e("<div>").attr("id",t).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)},_setupDisabled:function(t){e.isArray(t)&&(t.length?t.length===this.anchors.length&&(t=!0):t=!1);for(var i,s=0;i=this.tabs[s];s++)t===!0||-1!==e.inArray(s,t)?e(i).addClass("ui-state-disabled").attr("aria-disabled","true"):e(i).removeClass("ui-state-disabled").removeAttr("aria-disabled");this.options.disabled=t},_setupEvents:function(t){var i={};t&&e.each(t.split(" "),function(e,t){i[t]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(!0,this.anchors,{click:function(e){e.preventDefault()}}),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(t){var i,s=this.element.parent();"fill"===t?(i=s.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var t=e(this),s=t.css("position");"absolute"!==s&&"fixed"!==s&&(i-=t.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=e(this).outerHeight(!0)}),this.panels.each(function(){e(this).height(Math.max(0,i-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===t&&(i=0,this.panels.each(function(){i=Math.max(i,e(this).height("").height())}).height(i))},_eventHandler:function(t){var i=this.options,s=this.active,n=e(t.currentTarget),a=n.closest("li"),o=a[0]===s[0],r=o&&i.collapsible,h=r?e():this._getPanelForTab(a),l=s.length?this._getPanelForTab(s):e(),u={oldTab:s,oldPanel:l,newTab:r?e():a,newPanel:h};t.preventDefault(),a.hasClass("ui-state-disabled")||a.hasClass("ui-tabs-loading")||this.running||o&&!i.collapsible||this._trigger("beforeActivate",t,u)===!1||(i.active=r?!1:this.tabs.index(a),this.active=o?e():a,this.xhr&&this.xhr.abort(),l.length||h.length||e.error("jQuery UI Tabs: Mismatching fragment identifier."),h.length&&this.load(this.tabs.index(a),t),this._toggle(t,u))},_toggle:function(t,i){function s(){a.running=!1,a._trigger("activate",t,i)}function n(){i.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),o.length&&a.options.show?a._show(o,a.options.show,s):(o.show(),s())}var a=this,o=i.newPanel,r=i.oldPanel;this.running=!0,r.length&&this.options.hide?this._hide(r,this.options.hide,function(){i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),n()}):(i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),r.hide(),n()),r.attr("aria-hidden","true"),i.oldTab.attr({"aria-selected":"false","aria-expanded":"false"}),o.length&&r.length?i.oldTab.attr("tabIndex",-1):o.length&&this.tabs.filter(function(){return 0===e(this).attr("tabIndex")}).attr("tabIndex",-1),o.attr("aria-hidden","false"),i.newTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_activate:function(t){var i,s=this._findActive(t);s[0]!==this.active[0]&&(s.length||(s=this.active),i=s.find(".ui-tabs-anchor")[0],this._eventHandler({target:i,currentTarget:i,preventDefault:e.noop}))},_findActive:function(t){return t===!1?e():this.tabs.eq(t)},_getIndex:function(e){return"string"==typeof e&&(e=this.anchors.index(this.anchors.filter("[href$='"+e+"']"))),e},_destroy:function(){this.xhr&&this.xhr.abort(),this.element.removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible"),this.tablist.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").removeAttr("role"),this.anchors.removeClass("ui-tabs-anchor").removeAttr("role").removeAttr("tabIndex").removeUniqueId(),this.tabs.add(this.panels).each(function(){e.data(this,"ui-tabs-destroy")?e(this).remove():e(this).removeClass("ui-state-default ui-state-active ui-state-disabled ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel").removeAttr("tabIndex").removeAttr("aria-live").removeAttr("aria-busy").removeAttr("aria-selected").removeAttr("aria-labelledby").removeAttr("aria-hidden").removeAttr("aria-expanded").removeAttr("role")}),this.tabs.each(function(){var t=e(this),i=t.data("ui-tabs-aria-controls");i?t.attr("aria-controls",i).removeData("ui-tabs-aria-controls"):t.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(t){var i=this.options.disabled;i!==!1&&(void 0===t?i=!1:(t=this._getIndex(t),i=e.isArray(i)?e.map(i,function(e){return e!==t?e:null}):e.map(this.tabs,function(e,i){return i!==t?i:null})),this._setupDisabled(i))},disable:function(t){var i=this.options.disabled;if(i!==!0){if(void 0===t)i=!0;else{if(t=this._getIndex(t),-1!==e.inArray(t,i))return;i=e.isArray(i)?e.merge([t],i).sort():[t]}this._setupDisabled(i)}},load:function(t,i){t=this._getIndex(t);var s=this,n=this.tabs.eq(t),a=n.find(".ui-tabs-anchor"),o=this._getPanelForTab(n),r={tab:n,panel:o};this._isLocal(a[0])||(this.xhr=e.ajax(this._ajaxSettings(a,i,r)),this.xhr&&"canceled"!==this.xhr.statusText&&(n.addClass("ui-tabs-loading"),o.attr("aria-busy","true"),this.xhr.success(function(e){setTimeout(function(){o.html(e),s._trigger("load",i,r)},1)}).complete(function(e,t){setTimeout(function(){"abort"===t&&s.panels.stop(!1,!0),n.removeClass("ui-tabs-loading"),o.removeAttr("aria-busy"),e===s.xhr&&delete s.xhr},1)})))},_ajaxSettings:function(t,i,s){var n=this;return{url:t.attr("href"),beforeSend:function(t,a){return n._trigger("beforeLoad",i,e.extend({jqXHR:t,ajaxSettings:a},s))}}},_getPanelForTab:function(t){var i=e(t).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+i))}}),e.widget("ui.tooltip",{version:"1.11.0",options:{content:function(){var t=e(this).attr("title")||"";return e("<a>").text(t).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,tooltipClass:null,track:!1,close:null,open:null},_addDescribedBy:function(t,i){var s=(t.attr("aria-describedby")||"").split(/\s+/);s.push(i),t.data("ui-tooltip-id",i).attr("aria-describedby",e.trim(s.join(" ")))},_removeDescribedBy:function(t){var i=t.data("ui-tooltip-id"),s=(t.attr("aria-describedby")||"").split(/\s+/),n=e.inArray(i,s);-1!==n&&s.splice(n,1),t.removeData("ui-tooltip-id"),s=e.trim(s.join(" ")),s?t.attr("aria-describedby",s):t.removeAttr("aria-describedby")},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.options.disabled&&this._disable(),this.liveRegion=e("<div>").attr({role:"log","aria-live":"assertive","aria-relevant":"additions"}).addClass("ui-helper-hidden-accessible").appendTo(this.document[0].body)},_setOption:function(t,i){var s=this;return"disabled"===t?(this[i?"_disable":"_enable"](),this.options[t]=i,void 0):(this._super(t,i),"content"===t&&e.each(this.tooltips,function(e,t){s._updateContent(t)}),void 0)},_disable:function(){var t=this;e.each(this.tooltips,function(i,s){var n=e.Event("blur");n.target=n.currentTarget=s[0],t.close(n,!0)}),this.element.find(this.options.items).addBack().each(function(){var t=e(this);t.is("[title]")&&t.data("ui-tooltip-title",t.attr("title")).removeAttr("title")})},_enable:function(){this.element.find(this.options.items).addBack().each(function(){var t=e(this);t.data("ui-tooltip-title")&&t.attr("title",t.data("ui-tooltip-title"))})},open:function(t){var i=this,s=e(t?t.target:this.element).closest(this.options.items);s.length&&!s.data("ui-tooltip-id")&&(s.attr("title")&&s.data("ui-tooltip-title",s.attr("title")),s.data("ui-tooltip-open",!0),t&&"mouseover"===t.type&&s.parents().each(function(){var t,s=e(this);s.data("ui-tooltip-open")&&(t=e.Event("blur"),t.target=t.currentTarget=this,i.close(t,!0)),s.attr("title")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr("title")},s.attr("title",""))}),this._updateContent(s,t))},_updateContent:function(e,t){var i,s=this.options.content,n=this,a=t?t.type:null;return"string"==typeof s?this._open(t,e,s):(i=s.call(e[0],function(i){e.data("ui-tooltip-open")&&n._delay(function(){t&&(t.type=a),this._open(t,e,i)})}),i&&this._open(t,e,i),void 0)},_open:function(t,i,s){function n(e){l.of=e,a.is(":hidden")||a.position(l)}var a,o,r,h,l=e.extend({},this.options.position);if(s){if(a=this._find(i),a.length)return a.find(".ui-tooltip-content").html(s),void 0;i.is("[title]")&&(t&&"mouseover"===t.type?i.attr("title",""):i.removeAttr("title")),a=this._tooltip(i),this._addDescribedBy(i,a.attr("id")),a.find(".ui-tooltip-content").html(s),this.liveRegion.children().hide(),s.clone?(h=s.clone(),h.removeAttr("id").find("[id]").removeAttr("id")):h=s,e("<div>").html(h).appendTo(this.liveRegion),this.options.track&&t&&/^mouse/.test(t.type)?(this._on(this.document,{mousemove:n}),n(t)):a.position(e.extend({of:i},this.options.position)),a.hide(),this._show(a,this.options.show),this.options.show&&this.options.show.delay&&(r=this.delayedShow=setInterval(function(){a.is(":visible")&&(n(l.of),clearInterval(r))},e.fx.interval)),this._trigger("open",t,{tooltip:a}),o={keyup:function(t){if(t.keyCode===e.ui.keyCode.ESCAPE){var s=e.Event(t);s.currentTarget=i[0],this.close(s,!0)}}},i[0]!==this.element[0]&&(o.remove=function(){this._removeTooltip(a)}),t&&"mouseover"!==t.type||(o.mouseleave="close"),t&&"focusin"!==t.type||(o.focusout="close"),this._on(!0,i,o)}},close:function(t){var i=this,s=e(t?t.currentTarget:this.element),n=this._find(s);this.closing||(clearInterval(this.delayedShow),s.data("ui-tooltip-title")&&!s.attr("title")&&s.attr("title",s.data("ui-tooltip-title")),this._removeDescribedBy(s),n.stop(!0),this._hide(n,this.options.hide,function(){i._removeTooltip(e(this))}),s.removeData("ui-tooltip-open"),this._off(s,"mouseleave focusout keyup"),s[0]!==this.element[0]&&this._off(s,"remove"),this._off(this.document,"mousemove"),t&&"mouseleave"===t.type&&e.each(this.parents,function(t,s){e(s.element).attr("title",s.title),delete i.parents[t]}),this.closing=!0,this._trigger("close",t,{tooltip:n}),this.closing=!1)},_tooltip:function(t){var i=e("<div>").attr("role","tooltip").addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content "+(this.options.tooltipClass||"")),s=i.uniqueId().attr("id");return e("<div>").addClass("ui-tooltip-content").appendTo(i),i.appendTo(this.document[0].body),this.tooltips[s]=t,i},_find:function(t){var i=t.data("ui-tooltip-id");return i?e("#"+i):e()},_removeTooltip:function(e){e.remove(),delete this.tooltips[e.attr("id")]},_destroy:function(){var t=this;e.each(this.tooltips,function(i,s){var n=e.Event("blur");n.target=n.currentTarget=s[0],t.close(n,!0),e("#"+i).remove(),s.data("ui-tooltip-title")&&(s.attr("title")||s.attr("title",s.data("ui-tooltip-title")),s.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}})});
\ No newline at end of file
diff --git a/portal/static/js/jquery.qtip.min.js b/portal/static/js/jquery.qtip.min.js
new file mode 100644 (file)
index 0000000..3ae7bbe
--- /dev/null
@@ -0,0 +1,4 @@
+/* qTip2 v2.2.0 tips modal viewport svg imagemap ie6 | qtip2.com | Licensed MIT, GPL | Thu Nov 21 2013 20:34:59 */
+(function(t,e,i){(function(t){"use strict";"function"==typeof define&&define.amd?define(["jquery"],t):jQuery&&!jQuery.fn.qtip&&t(jQuery)})(function(s){"use strict";function o(t,e,i,o){this.id=i,this.target=t,this.tooltip=E,this.elements={target:t},this._id=X+"-"+i,this.timers={img:{}},this.options=e,this.plugins={},this.cache={event:{},target:s(),disabled:k,attr:o,onTooltip:k,lastClass:""},this.rendered=this.destroyed=this.disabled=this.waiting=this.hiddenDuringWait=this.positioning=this.triggering=k}function n(t){return t===E||"object"!==s.type(t)}function r(t){return!(s.isFunction(t)||t&&t.attr||t.length||"object"===s.type(t)&&(t.jquery||t.then))}function a(t){var e,i,o,a;return n(t)?k:(n(t.metadata)&&(t.metadata={type:t.metadata}),"content"in t&&(e=t.content,n(e)||e.jquery||e.done?e=t.content={text:i=r(e)?k:e}:i=e.text,"ajax"in e&&(o=e.ajax,a=o&&o.once!==k,delete e.ajax,e.text=function(t,e){var n=i||s(this).attr(e.options.content.attr)||"Loading...",r=s.ajax(s.extend({},o,{context:e})).then(o.success,E,o.error).then(function(t){return t&&a&&e.set("content.text",t),t},function(t,i,s){e.destroyed||0===t.status||e.set("content.text",i+": "+s)});return a?n:(e.set("content.text",n),r)}),"title"in e&&(n(e.title)||(e.button=e.title.button,e.title=e.title.text),r(e.title||k)&&(e.title=k))),"position"in t&&n(t.position)&&(t.position={my:t.position,at:t.position}),"show"in t&&n(t.show)&&(t.show=t.show.jquery?{target:t.show}:t.show===W?{ready:W}:{event:t.show}),"hide"in t&&n(t.hide)&&(t.hide=t.hide.jquery?{target:t.hide}:{event:t.hide}),"style"in t&&n(t.style)&&(t.style={classes:t.style}),s.each(R,function(){this.sanitize&&this.sanitize(t)}),t)}function h(t,e){for(var i,s=0,o=t,n=e.split(".");o=o[n[s++]];)n.length>s&&(i=o);return[i||t,n.pop()]}function l(t,e){var i,s,o;for(i in this.checks)for(s in this.checks[i])(o=RegExp(s,"i").exec(t))&&(e.push(o),("builtin"===i||this.plugins[i])&&this.checks[i][s].apply(this.plugins[i]||this,e))}function c(t){return G.concat("").join(t?"-"+t+" ":" ")}function d(i){return i&&{type:i.type,pageX:i.pageX,pageY:i.pageY,target:i.target,relatedTarget:i.relatedTarget,scrollX:i.scrollX||t.pageXOffset||e.body.scrollLeft||e.documentElement.scrollLeft,scrollY:i.scrollY||t.pageYOffset||e.body.scrollTop||e.documentElement.scrollTop}||{}}function p(t,e){return e>0?setTimeout(s.proxy(t,this),e):(t.call(this),i)}function u(t){return this.tooltip.hasClass(ee)?k:(clearTimeout(this.timers.show),clearTimeout(this.timers.hide),this.timers.show=p.call(this,function(){this.toggle(W,t)},this.options.show.delay),i)}function f(t){if(this.tooltip.hasClass(ee))return k;var e=s(t.relatedTarget),i=e.closest(U)[0]===this.tooltip[0],o=e[0]===this.options.show.target[0];if(clearTimeout(this.timers.show),clearTimeout(this.timers.hide),this!==e[0]&&"mouse"===this.options.position.target&&i||this.options.hide.fixed&&/mouse(out|leave|move)/.test(t.type)&&(i||o))try{t.preventDefault(),t.stopImmediatePropagation()}catch(n){}else this.timers.hide=p.call(this,function(){this.toggle(k,t)},this.options.hide.delay,this)}function g(t){return this.tooltip.hasClass(ee)||!this.options.hide.inactive?k:(clearTimeout(this.timers.inactive),this.timers.inactive=p.call(this,function(){this.hide(t)},this.options.hide.inactive),i)}function m(t){this.rendered&&this.tooltip[0].offsetWidth>0&&this.reposition(t)}function v(t,i,o){s(e.body).delegate(t,(i.split?i:i.join(he+" "))+he,function(){var t=T.api[s.attr(this,H)];t&&!t.disabled&&o.apply(t,arguments)})}function y(t,i,n){var r,h,l,c,d,p=s(e.body),u=t[0]===e?p:t,f=t.metadata?t.metadata(n.metadata):E,g="html5"===n.metadata.type&&f?f[n.metadata.name]:E,m=t.data(n.metadata.name||"qtipopts");try{m="string"==typeof m?s.parseJSON(m):m}catch(v){}if(c=s.extend(W,{},T.defaults,n,"object"==typeof m?a(m):E,a(g||f)),h=c.position,c.id=i,"boolean"==typeof c.content.text){if(l=t.attr(c.content.attr),c.content.attr===k||!l)return k;c.content.text=l}if(h.container.length||(h.container=p),h.target===k&&(h.target=u),c.show.target===k&&(c.show.target=u),c.show.solo===W&&(c.show.solo=h.container.closest("body")),c.hide.target===k&&(c.hide.target=u),c.position.viewport===W&&(c.position.viewport=h.container),h.container=h.container.eq(0),h.at=new z(h.at,W),h.my=new z(h.my),t.data(X))if(c.overwrite)t.qtip("destroy",!0);else if(c.overwrite===k)return k;return t.attr(Y,i),c.suppress&&(d=t.attr("title"))&&t.removeAttr("title").attr(se,d).attr("title",""),r=new o(t,c,i,!!l),t.data(X,r),t.one("remove.qtip-"+i+" removeqtip.qtip-"+i,function(){var t;(t=s(this).data(X))&&t.destroy(!0)}),r}function b(t){return t.charAt(0).toUpperCase()+t.slice(1)}function w(t,e){var s,o,n=e.charAt(0).toUpperCase()+e.slice(1),r=(e+" "+be.join(n+" ")+n).split(" "),a=0;if(ye[e])return t.css(ye[e]);for(;s=r[a++];)if((o=t.css(s))!==i)return ye[e]=s,o}function _(t,e){return Math.ceil(parseFloat(w(t,e)))}function x(t,e){this._ns="tip",this.options=e,this.offset=e.offset,this.size=[e.width,e.height],this.init(this.qtip=t)}function q(t,e){this.options=e,this._ns="-modal",this.init(this.qtip=t)}function C(t){this._ns="ie6",this.init(this.qtip=t)}var T,j,z,M,I,W=!0,k=!1,E=null,S="x",L="y",A="width",B="height",D="top",F="left",O="bottom",P="right",N="center",$="flipinvert",V="shift",R={},X="qtip",Y="data-hasqtip",H="data-qtip-id",G=["ui-widget","ui-tooltip"],U="."+X,Q="click dblclick mousedown mouseup mousemove mouseleave mouseenter".split(" "),J=X+"-fixed",K=X+"-default",Z=X+"-focus",te=X+"-hover",ee=X+"-disabled",ie="_replacedByqTip",se="oldtitle",oe={ie:function(){for(var t=3,i=e.createElement("div");(i.innerHTML="<!--[if gt IE "+ ++t+"]><i></i><![endif]-->")&&i.getElementsByTagName("i")[0];);return t>4?t:0/0}(),iOS:parseFloat((""+(/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))||k};j=o.prototype,j._when=function(t){return s.when.apply(s,t)},j.render=function(t){if(this.rendered||this.destroyed)return this;var e,i=this,o=this.options,n=this.cache,r=this.elements,a=o.content.text,h=o.content.title,l=o.content.button,c=o.position,d=("."+this._id+" ",[]);return s.attr(this.target[0],"aria-describedby",this._id),this.tooltip=r.tooltip=e=s("<div/>",{id:this._id,"class":[X,K,o.style.classes,X+"-pos-"+o.position.my.abbrev()].join(" "),width:o.style.width||"",height:o.style.height||"",tracking:"mouse"===c.target&&c.adjust.mouse,role:"alert","aria-live":"polite","aria-atomic":k,"aria-describedby":this._id+"-content","aria-hidden":W}).toggleClass(ee,this.disabled).attr(H,this.id).data(X,this).appendTo(c.container).append(r.content=s("<div />",{"class":X+"-content",id:this._id+"-content","aria-atomic":W})),this.rendered=-1,this.positioning=W,h&&(this._createTitle(),s.isFunction(h)||d.push(this._updateTitle(h,k))),l&&this._createButton(),s.isFunction(a)||d.push(this._updateContent(a,k)),this.rendered=W,this._setWidget(),s.each(R,function(t){var e;"render"===this.initialize&&(e=this(i))&&(i.plugins[t]=e)}),this._unassignEvents(),this._assignEvents(),this._when(d).then(function(){i._trigger("render"),i.positioning=k,i.hiddenDuringWait||!o.show.ready&&!t||i.toggle(W,n.event,k),i.hiddenDuringWait=k}),T.api[this.id]=this,this},j.destroy=function(t){function e(){if(!this.destroyed){this.destroyed=W;var t=this.target,e=t.attr(se);this.rendered&&this.tooltip.stop(1,0).find("*").remove().end().remove(),s.each(this.plugins,function(){this.destroy&&this.destroy()}),clearTimeout(this.timers.show),clearTimeout(this.timers.hide),this._unassignEvents(),t.removeData(X).removeAttr(H).removeAttr(Y).removeAttr("aria-describedby"),this.options.suppress&&e&&t.attr("title",e).removeAttr(se),this._unbind(t),this.options=this.elements=this.cache=this.timers=this.plugins=this.mouse=E,delete T.api[this.id]}}return this.destroyed?this.target:(t===W&&"hide"!==this.triggering||!this.rendered?e.call(this):(this.tooltip.one("tooltiphidden",s.proxy(e,this)),!this.triggering&&this.hide()),this.target)},M=j.checks={builtin:{"^id$":function(t,e,i,o){var n=i===W?T.nextid:i,r=X+"-"+n;n!==k&&n.length>0&&!s("#"+r).length?(this._id=r,this.rendered&&(this.tooltip[0].id=this._id,this.elements.content[0].id=this._id+"-content",this.elements.title[0].id=this._id+"-title")):t[e]=o},"^prerender":function(t,e,i){i&&!this.rendered&&this.render(this.options.show.ready)},"^content.text$":function(t,e,i){this._updateContent(i)},"^content.attr$":function(t,e,i,s){this.options.content.text===this.target.attr(s)&&this._updateContent(this.target.attr(i))},"^content.title$":function(t,e,s){return s?(s&&!this.elements.title&&this._createTitle(),this._updateTitle(s),i):this._removeTitle()},"^content.button$":function(t,e,i){this._updateButton(i)},"^content.title.(text|button)$":function(t,e,i){this.set("content."+e,i)},"^position.(my|at)$":function(t,e,i){"string"==typeof i&&(t[e]=new z(i,"at"===e))},"^position.container$":function(t,e,i){this.rendered&&this.tooltip.appendTo(i)},"^show.ready$":function(t,e,i){i&&(!this.rendered&&this.render(W)||this.toggle(W))},"^style.classes$":function(t,e,i,s){this.rendered&&this.tooltip.removeClass(s).addClass(i)},"^style.(width|height)":function(t,e,i){this.rendered&&this.tooltip.css(e,i)},"^style.widget|content.title":function(){this.rendered&&this._setWidget()},"^style.def":function(t,e,i){this.rendered&&this.tooltip.toggleClass(K,!!i)},"^events.(render|show|move|hide|focus|blur)$":function(t,e,i){this.rendered&&this.tooltip[(s.isFunction(i)?"":"un")+"bind"]("tooltip"+e,i)},"^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)":function(){if(this.rendered){var t=this.options.position;this.tooltip.attr("tracking","mouse"===t.target&&t.adjust.mouse),this._unassignEvents(),this._assignEvents()}}}},j.get=function(t){if(this.destroyed)return this;var e=h(this.options,t.toLowerCase()),i=e[0][e[1]];return i.precedance?i.string():i};var ne=/^position\.(my|at|adjust|target|container|viewport)|style|content|show\.ready/i,re=/^prerender|show\.ready/i;j.set=function(t,e){if(this.destroyed)return this;var o,n=this.rendered,r=k,c=this.options;return this.checks,"string"==typeof t?(o=t,t={},t[o]=e):t=s.extend({},t),s.each(t,function(e,o){if(n&&re.test(e))return delete t[e],i;var a,l=h(c,e.toLowerCase());a=l[0][l[1]],l[0][l[1]]=o&&o.nodeType?s(o):o,r=ne.test(e)||r,t[e]=[l[0],l[1],o,a]}),a(c),this.positioning=W,s.each(t,s.proxy(l,this)),this.positioning=k,this.rendered&&this.tooltip[0].offsetWidth>0&&r&&this.reposition("mouse"===c.position.target?E:this.cache.event),this},j._update=function(t,e){var i=this,o=this.cache;return this.rendered&&t?(s.isFunction(t)&&(t=t.call(this.elements.target,o.event,this)||""),s.isFunction(t.then)?(o.waiting=W,t.then(function(t){return o.waiting=k,i._update(t,e)},E,function(t){return i._update(t,e)})):t===k||!t&&""!==t?k:(t.jquery&&t.length>0?e.empty().append(t.css({display:"block",visibility:"visible"})):e.html(t),this._waitForContent(e).then(function(t){t.images&&t.images.length&&i.rendered&&i.tooltip[0].offsetWidth>0&&i.reposition(o.event,!t.length)}))):k},j._waitForContent=function(t){var e=this.cache;return e.waiting=W,(s.fn.imagesLoaded?t.imagesLoaded():s.Deferred().resolve([])).done(function(){e.waiting=k}).promise()},j._updateContent=function(t,e){this._update(t,this.elements.content,e)},j._updateTitle=function(t,e){this._update(t,this.elements.title,e)===k&&this._removeTitle(k)},j._createTitle=function(){var t=this.elements,e=this._id+"-title";t.titlebar&&this._removeTitle(),t.titlebar=s("<div />",{"class":X+"-titlebar "+(this.options.style.widget?c("header"):"")}).append(t.title=s("<div />",{id:e,"class":X+"-title","aria-atomic":W})).insertBefore(t.content).delegate(".qtip-close","mousedown keydown mouseup keyup mouseout",function(t){s(this).toggleClass("ui-state-active ui-state-focus","down"===t.type.substr(-4))}).delegate(".qtip-close","mouseover mouseout",function(t){s(this).toggleClass("ui-state-hover","mouseover"===t.type)}),this.options.content.button&&this._createButton()},j._removeTitle=function(t){var e=this.elements;e.title&&(e.titlebar.remove(),e.titlebar=e.title=e.button=E,t!==k&&this.reposition())},j.reposition=function(i,o){if(!this.rendered||this.positioning||this.destroyed)return this;this.positioning=W;var n,r,a=this.cache,h=this.tooltip,l=this.options.position,c=l.target,d=l.my,p=l.at,u=l.viewport,f=l.container,g=l.adjust,m=g.method.split(" "),v=h.outerWidth(k),y=h.outerHeight(k),b=0,w=0,_=h.css("position"),x={left:0,top:0},q=h[0].offsetWidth>0,C=i&&"scroll"===i.type,T=s(t),j=f[0].ownerDocument,z=this.mouse;if(s.isArray(c)&&2===c.length)p={x:F,y:D},x={left:c[0],top:c[1]};else if("mouse"===c)p={x:F,y:D},!z||!z.pageX||!g.mouse&&i&&i.pageX?i&&i.pageX||((!g.mouse||this.options.show.distance)&&a.origin&&a.origin.pageX?i=a.origin:(!i||i&&("resize"===i.type||"scroll"===i.type))&&(i=a.event)):i=z,"static"!==_&&(x=f.offset()),j.body.offsetWidth!==(t.innerWidth||j.documentElement.clientWidth)&&(r=s(e.body).offset()),x={left:i.pageX-x.left+(r&&r.left||0),top:i.pageY-x.top+(r&&r.top||0)},g.mouse&&C&&z&&(x.left-=(z.scrollX||0)-T.scrollLeft(),x.top-=(z.scrollY||0)-T.scrollTop());else{if("event"===c?i&&i.target&&"scroll"!==i.type&&"resize"!==i.type?a.target=s(i.target):i.target||(a.target=this.elements.target):"event"!==c&&(a.target=s(c.jquery?c:this.elements.target)),c=a.target,c=s(c).eq(0),0===c.length)return this;c[0]===e||c[0]===t?(b=oe.iOS?t.innerWidth:c.width(),w=oe.iOS?t.innerHeight:c.height(),c[0]===t&&(x={top:(u||c).scrollTop(),left:(u||c).scrollLeft()})):R.imagemap&&c.is("area")?n=R.imagemap(this,c,p,R.viewport?m:k):R.svg&&c&&c[0].ownerSVGElement?n=R.svg(this,c,p,R.viewport?m:k):(b=c.outerWidth(k),w=c.outerHeight(k),x=c.offset()),n&&(b=n.width,w=n.height,r=n.offset,x=n.position),x=this.reposition.offset(c,x,f),(oe.iOS>3.1&&4.1>oe.iOS||oe.iOS>=4.3&&4.33>oe.iOS||!oe.iOS&&"fixed"===_)&&(x.left-=T.scrollLeft(),x.top-=T.scrollTop()),(!n||n&&n.adjustable!==k)&&(x.left+=p.x===P?b:p.x===N?b/2:0,x.top+=p.y===O?w:p.y===N?w/2:0)}return x.left+=g.x+(d.x===P?-v:d.x===N?-v/2:0),x.top+=g.y+(d.y===O?-y:d.y===N?-y/2:0),R.viewport?(x.adjusted=R.viewport(this,x,l,b,w,v,y),r&&x.adjusted.left&&(x.left+=r.left),r&&x.adjusted.top&&(x.top+=r.top)):x.adjusted={left:0,top:0},this._trigger("move",[x,u.elem||u],i)?(delete x.adjusted,o===k||!q||isNaN(x.left)||isNaN(x.top)||"mouse"===c||!s.isFunction(l.effect)?h.css(x):s.isFunction(l.effect)&&(l.effect.call(h,this,s.extend({},x)),h.queue(function(t){s(this).css({opacity:"",height:""}),oe.ie&&this.style.removeAttribute("filter"),t()})),this.positioning=k,this):this},j.reposition.offset=function(t,i,o){function n(t,e){i.left+=e*t.scrollLeft(),i.top+=e*t.scrollTop()}if(!o[0])return i;var r,a,h,l,c=s(t[0].ownerDocument),d=!!oe.ie&&"CSS1Compat"!==e.compatMode,p=o[0];do"static"!==(a=s.css(p,"position"))&&("fixed"===a?(h=p.getBoundingClientRect(),n(c,-1)):(h=s(p).position(),h.left+=parseFloat(s.css(p,"borderLeftWidth"))||0,h.top+=parseFloat(s.css(p,"borderTopWidth"))||0),i.left-=h.left+(parseFloat(s.css(p,"marginLeft"))||0),i.top-=h.top+(parseFloat(s.css(p,"marginTop"))||0),r||"hidden"===(l=s.css(p,"overflow"))||"visible"===l||(r=s(p)));while(p=p.offsetParent);return r&&(r[0]!==c[0]||d)&&n(r,1),i};var ae=(z=j.reposition.Corner=function(t,e){t=(""+t).replace(/([A-Z])/," $1").replace(/middle/gi,N).toLowerCase(),this.x=(t.match(/left|right/i)||t.match(/center/)||["inherit"])[0].toLowerCase(),this.y=(t.match(/top|bottom|center/i)||["inherit"])[0].toLowerCase(),this.forceY=!!e;var i=t.charAt(0);this.precedance="t"===i||"b"===i?L:S}).prototype;ae.invert=function(t,e){this[t]=this[t]===F?P:this[t]===P?F:e||this[t]},ae.string=function(){var t=this.x,e=this.y;return t===e?t:this.precedance===L||this.forceY&&"center"!==e?e+" "+t:t+" "+e},ae.abbrev=function(){var t=this.string().split(" ");return t[0].charAt(0)+(t[1]&&t[1].charAt(0)||"")},ae.clone=function(){return new z(this.string(),this.forceY)},j.toggle=function(t,i){var o=this.cache,n=this.options,r=this.tooltip;if(i){if(/over|enter/.test(i.type)&&/out|leave/.test(o.event.type)&&n.show.target.add(i.target).length===n.show.target.length&&r.has(i.relatedTarget).length)return this;o.event=d(i)}if(this.waiting&&!t&&(this.hiddenDuringWait=W),!this.rendered)return t?this.render(1):this;if(this.destroyed||this.disabled)return this;var a,h,l,c=t?"show":"hide",p=this.options[c],u=(this.options[t?"hide":"show"],this.options.position),f=this.options.content,g=this.tooltip.css("width"),m=this.tooltip.is(":visible"),v=t||1===p.target.length,y=!i||2>p.target.length||o.target[0]===i.target;return(typeof t).search("boolean|number")&&(t=!m),a=!r.is(":animated")&&m===t&&y,h=a?E:!!this._trigger(c,[90]),this.destroyed?this:(h!==k&&t&&this.focus(i),!h||a?this:(s.attr(r[0],"aria-hidden",!t),t?(o.origin=d(this.mouse),s.isFunction(f.text)&&this._updateContent(f.text,k),s.isFunction(f.title)&&this._updateTitle(f.title,k),!I&&"mouse"===u.target&&u.adjust.mouse&&(s(e).bind("mousemove."+X,this._storeMouse),I=W),g||r.css("width",r.outerWidth(k)),this.reposition(i,arguments[2]),g||r.css("width",""),p.solo&&("string"==typeof p.solo?s(p.solo):s(U,p.solo)).not(r).not(p.target).qtip("hide",s.Event("tooltipsolo"))):(clearTimeout(this.timers.show),delete o.origin,I&&!s(U+'[tracking="true"]:visible',p.solo).not(r).length&&(s(e).unbind("mousemove."+X),I=k),this.blur(i)),l=s.proxy(function(){t?(oe.ie&&r[0].style.removeAttribute("filter"),r.css("overflow",""),"string"==typeof p.autofocus&&s(this.options.show.autofocus,r).focus(),this.options.show.target.trigger("qtip-"+this.id+"-inactive")):r.css({display:"",visibility:"",opacity:"",left:"",top:""}),this._trigger(t?"visible":"hidden")},this),p.effect===k||v===k?(r[c](),l()):s.isFunction(p.effect)?(r.stop(1,1),p.effect.call(r,this),r.queue("fx",function(t){l(),t()})):r.fadeTo(90,t?1:0,l),t&&p.target.trigger("qtip-"+this.id+"-inactive"),this))},j.show=function(t){return this.toggle(W,t)},j.hide=function(t){return this.toggle(k,t)},j.focus=function(t){if(!this.rendered||this.destroyed)return this;var e=s(U),i=this.tooltip,o=parseInt(i[0].style.zIndex,10),n=T.zindex+e.length;return i.hasClass(Z)||this._trigger("focus",[n],t)&&(o!==n&&(e.each(function(){this.style.zIndex>o&&(this.style.zIndex=this.style.zIndex-1)}),e.filter("."+Z).qtip("blur",t)),i.addClass(Z)[0].style.zIndex=n),this},j.blur=function(t){return!this.rendered||this.destroyed?this:(this.tooltip.removeClass(Z),this._trigger("blur",[this.tooltip.css("zIndex")],t),this)},j.disable=function(t){return this.destroyed?this:("toggle"===t?t=!(this.rendered?this.tooltip.hasClass(ee):this.disabled):"boolean"!=typeof t&&(t=W),this.rendered&&this.tooltip.toggleClass(ee,t).attr("aria-disabled",t),this.disabled=!!t,this)},j.enable=function(){return this.disable(k)},j._createButton=function(){var t=this,e=this.elements,i=e.tooltip,o=this.options.content.button,n="string"==typeof o,r=n?o:"Close tooltip";e.button&&e.button.remove(),e.button=o.jquery?o:s("<a />",{"class":"qtip-close "+(this.options.style.widget?"":X+"-icon"),title:r,"aria-label":r}).prepend(s("<span />",{"class":"ui-icon ui-icon-close",html:"&times;"})),e.button.appendTo(e.titlebar||i).attr("role","button").click(function(e){return i.hasClass(ee)||t.hide(e),k})},j._updateButton=function(t){if(!this.rendered)return k;var e=this.elements.button;t?this._createButton():e.remove()},j._setWidget=function(){var t=this.options.style.widget,e=this.elements,i=e.tooltip,s=i.hasClass(ee);i.removeClass(ee),ee=t?"ui-state-disabled":"qtip-disabled",i.toggleClass(ee,s),i.toggleClass("ui-helper-reset "+c(),t).toggleClass(K,this.options.style.def&&!t),e.content&&e.content.toggleClass(c("content"),t),e.titlebar&&e.titlebar.toggleClass(c("header"),t),e.button&&e.button.toggleClass(X+"-icon",!t)},j._storeMouse=function(t){(this.mouse=d(t)).type="mousemove"},j._bind=function(t,e,i,o,n){var r="."+this._id+(o?"-"+o:"");e.length&&s(t).bind((e.split?e:e.join(r+" "))+r,s.proxy(i,n||this))},j._unbind=function(t,e){s(t).unbind("."+this._id+(e?"-"+e:""))};var he="."+X;s(function(){v(U,["mouseenter","mouseleave"],function(t){var e="mouseenter"===t.type,i=s(t.currentTarget),o=s(t.relatedTarget||t.target),n=this.options;e?(this.focus(t),i.hasClass(J)&&!i.hasClass(ee)&&clearTimeout(this.timers.hide)):"mouse"===n.position.target&&n.hide.event&&n.show.target&&!o.closest(n.show.target[0]).length&&this.hide(t),i.toggleClass(te,e)}),v("["+H+"]",Q,g)}),j._trigger=function(t,e,i){var o=s.Event("tooltip"+t);return o.originalEvent=i&&s.extend({},i)||this.cache.event||E,this.triggering=t,this.tooltip.trigger(o,[this].concat(e||[])),this.triggering=k,!o.isDefaultPrevented()},j._bindEvents=function(t,e,o,n,r,a){if(n.add(o).length===n.length){var h=[];e=s.map(e,function(e){var o=s.inArray(e,t);return o>-1?(h.push(t.splice(o,1)[0]),i):e}),h.length&&this._bind(o,h,function(t){var e=this.rendered?this.tooltip[0].offsetWidth>0:!1;(e?a:r).call(this,t)})}this._bind(o,t,r),this._bind(n,e,a)},j._assignInitialEvents=function(t){function e(t){return this.disabled||this.destroyed?k:(this.cache.event=d(t),this.cache.target=t?s(t.target):[i],clearTimeout(this.timers.show),this.timers.show=p.call(this,function(){this.render("object"==typeof t||o.show.ready)},o.show.delay),i)}var o=this.options,n=o.show.target,r=o.hide.target,a=o.show.event?s.trim(""+o.show.event).split(" "):[],h=o.hide.event?s.trim(""+o.hide.event).split(" "):[];/mouse(over|enter)/i.test(o.show.event)&&!/mouse(out|leave)/i.test(o.hide.event)&&h.push("mouseleave"),this._bind(n,"mousemove",function(t){this._storeMouse(t),this.cache.onTarget=W}),this._bindEvents(a,h,n,r,e,function(){clearTimeout(this.timers.show)}),(o.show.ready||o.prerender)&&e.call(this,t)},j._assignEvents=function(){var i=this,o=this.options,n=o.position,r=this.tooltip,a=o.show.target,h=o.hide.target,l=n.container,c=n.viewport,d=s(e),p=(s(e.body),s(t)),v=o.show.event?s.trim(""+o.show.event).split(" "):[],y=o.hide.event?s.trim(""+o.hide.event).split(" "):[];s.each(o.events,function(t,e){i._bind(r,"toggle"===t?["tooltipshow","tooltiphide"]:["tooltip"+t],e,null,r)}),/mouse(out|leave)/i.test(o.hide.event)&&"window"===o.hide.leave&&this._bind(d,["mouseout","blur"],function(t){/select|option/.test(t.target.nodeName)||t.relatedTarget||this.hide(t)}),o.hide.fixed?h=h.add(r.addClass(J)):/mouse(over|enter)/i.test(o.show.event)&&this._bind(h,"mouseleave",function(){clearTimeout(this.timers.show)}),(""+o.hide.event).indexOf("unfocus")>-1&&this._bind(l.closest("html"),["mousedown","touchstart"],function(t){var e=s(t.target),i=this.rendered&&!this.tooltip.hasClass(ee)&&this.tooltip[0].offsetWidth>0,o=e.parents(U).filter(this.tooltip[0]).length>0;e[0]===this.target[0]||e[0]===this.tooltip[0]||o||this.target.has(e[0]).length||!i||this.hide(t)}),"number"==typeof o.hide.inactive&&(this._bind(a,"qtip-"+this.id+"-inactive",g),this._bind(h.add(r),T.inactiveEvents,g,"-inactive")),this._bindEvents(v,y,a,h,u,f),this._bind(a.add(r),"mousemove",function(t){if("number"==typeof o.hide.distance){var e=this.cache.origin||{},i=this.options.hide.distance,s=Math.abs;(s(t.pageX-e.pageX)>=i||s(t.pageY-e.pageY)>=i)&&this.hide(t)}this._storeMouse(t)}),"mouse"===n.target&&n.adjust.mouse&&(o.hide.event&&this._bind(a,["mouseenter","mouseleave"],function(t){this.cache.onTarget="mouseenter"===t.type}),this._bind(d,"mousemove",function(t){this.rendered&&this.cache.onTarget&&!this.tooltip.hasClass(ee)&&this.tooltip[0].offsetWidth>0&&this.reposition(t)})),(n.adjust.resize||c.length)&&this._bind(s.event.special.resize?c:p,"resize",m),n.adjust.scroll&&this._bind(p.add(n.container),"scroll",m)},j._unassignEvents=function(){var i=[this.options.show.target[0],this.options.hide.target[0],this.rendered&&this.tooltip[0],this.options.position.container[0],this.options.position.viewport[0],this.options.position.container.closest("html")[0],t,e];this._unbind(s([]).pushStack(s.grep(i,function(t){return"object"==typeof t})))},T=s.fn.qtip=function(t,e,o){var n=(""+t).toLowerCase(),r=E,h=s.makeArray(arguments).slice(1),l=h[h.length-1],c=this[0]?s.data(this[0],X):E;return!arguments.length&&c||"api"===n?c:"string"==typeof t?(this.each(function(){var t=s.data(this,X);if(!t)return W;if(l&&l.timeStamp&&(t.cache.event=l),!e||"option"!==n&&"options"!==n)t[n]&&t[n].apply(t,h);else{if(o===i&&!s.isPlainObject(e))return r=t.get(e),k;t.set(e,o)}}),r!==E?r:this):"object"!=typeof t&&arguments.length?i:(c=a(s.extend(W,{},t)),this.each(function(t){var e,o;return o=s.isArray(c.id)?c.id[t]:c.id,o=!o||o===k||1>o.length||T.api[o]?T.nextid++:o,e=y(s(this),o,c),e===k?W:(T.api[o]=e,s.each(R,function(){"initialize"===this.initialize&&this(e)}),e._assignInitialEvents(l),i)}))},s.qtip=o,T.api={},s.each({attr:function(t,e){if(this.length){var i=this[0],o="title",n=s.data(i,"qtip");if(t===o&&n&&"object"==typeof n&&n.options.suppress)return 2>arguments.length?s.attr(i,se):(n&&n.options.content.attr===o&&n.cache.attr&&n.set("content.text",e),this.attr(se,e))}return s.fn["attr"+ie].apply(this,arguments)},clone:function(t){var e=(s([]),s.fn["clone"+ie].apply(this,arguments));return t||e.filter("["+se+"]").attr("title",function(){return s.attr(this,se)}).removeAttr(se),e}},function(t,e){if(!e||s.fn[t+ie])return W;var i=s.fn[t+ie]=s.fn[t];s.fn[t]=function(){return e.apply(this,arguments)||i.apply(this,arguments)}}),s.ui||(s["cleanData"+ie]=s.cleanData,s.cleanData=function(t){for(var e,i=0;(e=s(t[i])).length;i++)if(e.attr(Y))try{e.triggerHandler("removeqtip")}catch(o){}s["cleanData"+ie].apply(this,arguments)}),T.version="2.2.0",T.nextid=0,T.inactiveEvents=Q,T.zindex=15e3,T.defaults={prerender:k,id:k,overwrite:W,suppress:W,content:{text:W,attr:"title",title:k,button:k},position:{my:"top left",at:"bottom right",target:k,container:k,viewport:k,adjust:{x:0,y:0,mouse:W,scroll:W,resize:W,method:"flipinvert flipinvert"},effect:function(t,e){s(this).animate(e,{duration:200,queue:k})}},show:{target:k,event:"mouseenter",effect:W,delay:90,solo:k,ready:k,autofocus:k},hide:{target:k,event:"mouseleave",effect:W,delay:0,fixed:k,inactive:k,leave:"window",distance:k},style:{classes:"",widget:k,width:k,height:k,def:W},events:{render:E,move:E,show:E,hide:E,toggle:E,visible:E,hidden:E,focus:E,blur:E}};var le,ce="margin",de="border",pe="color",ue="background-color",fe="transparent",ge=" !important",me=!!e.createElement("canvas").getContext,ve=/rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i,ye={},be=["Webkit","O","Moz","ms"];if(me)var we=t.devicePixelRatio||1,_e=function(){var t=e.createElement("canvas").getContext("2d");return t.backingStorePixelRatio||t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||1}(),xe=we/_e;else var qe=function(t,e,i){return"<qtipvml:"+t+' xmlns="urn:schemas-microsoft.com:vml" class="qtip-vml" '+(e||"")+' style="behavior: url(#default#VML); '+(i||"")+'" />'};s.extend(x.prototype,{init:function(t){var e,i;i=this.element=t.elements.tip=s("<div />",{"class":X+"-tip"}).prependTo(t.tooltip),me?(e=s("<canvas />").appendTo(this.element)[0].getContext("2d"),e.lineJoin="miter",e.miterLimit=1e5,e.save()):(e=qe("shape",'coordorigin="0,0"',"position:absolute;"),this.element.html(e+e),t._bind(s("*",i).add(i),["click","mousedown"],function(t){t.stopPropagation()},this._ns)),t._bind(t.tooltip,"tooltipmove",this.reposition,this._ns,this),this.create()},_swapDimensions:function(){this.size[0]=this.options.height,this.size[1]=this.options.width},_resetDimensions:function(){this.size[0]=this.options.width,this.size[1]=this.options.height},_useTitle:function(t){var e=this.qtip.elements.titlebar;return e&&(t.y===D||t.y===N&&this.element.position().top+this.size[1]/2+this.options.offset<e.outerHeight(W))},_parseCorner:function(t){var e=this.qtip.options.position.my;return t===k||e===k?t=k:t===W?t=new z(e.string()):t.string||(t=new z(t),t.fixed=W),t},_parseWidth:function(t,e,i){var s=this.qtip.elements,o=de+b(e)+"Width";return(i?_(i,o):_(s.content,o)||_(this._useTitle(t)&&s.titlebar||s.content,o)||_(s.tooltip,o))||0},_parseRadius:function(t){var e=this.qtip.elements,i=de+b(t.y)+b(t.x)+"Radius";return 9>oe.ie?0:_(this._useTitle(t)&&e.titlebar||e.content,i)||_(e.tooltip,i)||0},_invalidColour:function(t,e,i){var s=t.css(e);return!s||i&&s===t.css(i)||ve.test(s)?k:s},_parseColours:function(t){var e=this.qtip.elements,i=this.element.css("cssText",""),o=de+b(t[t.precedance])+b(pe),n=this._useTitle(t)&&e.titlebar||e.content,r=this._invalidColour,a=[];return a[0]=r(i,ue)||r(n,ue)||r(e.content,ue)||r(e.tooltip,ue)||i.css(ue),a[1]=r(i,o,pe)||r(n,o,pe)||r(e.content,o,pe)||r(e.tooltip,o,pe)||e.tooltip.css(o),s("*",i).add(i).css("cssText",ue+":"+fe+ge+";"+de+":0"+ge+";"),a},_calculateSize:function(t){var e,i,s,o=t.precedance===L,n=this.options.width,r=this.options.height,a="c"===t.abbrev(),h=(o?n:r)*(a?.5:1),l=Math.pow,c=Math.round,d=Math.sqrt(l(h,2)+l(r,2)),p=[this.border/h*d,this.border/r*d];return p[2]=Math.sqrt(l(p[0],2)-l(this.border,2)),p[3]=Math.sqrt(l(p[1],2)-l(this.border,2)),e=d+p[2]+p[3]+(a?0:p[0]),i=e/d,s=[c(i*n),c(i*r)],o?s:s.reverse()},_calculateTip:function(t,e,i){i=i||1,e=e||this.size;var s=e[0]*i,o=e[1]*i,n=Math.ceil(s/2),r=Math.ceil(o/2),a={br:[0,0,s,o,s,0],bl:[0,0,s,0,0,o],tr:[0,o,s,0,s,o],tl:[0,0,0,o,s,o],tc:[0,o,n,0,s,o],bc:[0,0,s,0,n,o],rc:[0,0,s,r,0,o],lc:[s,0,s,o,0,r]};return a.lt=a.br,a.rt=a.bl,a.lb=a.tr,a.rb=a.tl,a[t.abbrev()]},_drawCoords:function(t,e){t.beginPath(),t.moveTo(e[0],e[1]),t.lineTo(e[2],e[3]),t.lineTo(e[4],e[5]),t.closePath()},create:function(){var t=this.corner=(me||oe.ie)&&this._parseCorner(this.options.corner);return(this.enabled=!!this.corner&&"c"!==this.corner.abbrev())&&(this.qtip.cache.corner=t.clone(),this.update()),this.element.toggle(this.enabled),this.corner},update:function(e,i){if(!this.enabled)return this;var o,n,r,a,h,l,c,d,p=this.qtip.elements,u=this.element,f=u.children(),g=this.options,m=this.size,v=g.mimic,y=Math.round;e||(e=this.qtip.cache.corner||this.corner),v===k?v=e:(v=new z(v),v.precedance=e.precedance,"inherit"===v.x?v.x=e.x:"inherit"===v.y?v.y=e.y:v.x===v.y&&(v[e.precedance]=e[e.precedance])),n=v.precedance,e.precedance===S?this._swapDimensions():this._resetDimensions(),o=this.color=this._parseColours(e),o[1]!==fe?(d=this.border=this._parseWidth(e,e[e.precedance]),g.border&&1>d&&!ve.test(o[1])&&(o[0]=o[1]),this.border=d=g.border!==W?g.border:d):this.border=d=0,c=this.size=this._calculateSize(e),u.css({width:c[0],height:c[1],lineHeight:c[1]+"px"}),l=e.precedance===L?[y(v.x===F?d:v.x===P?c[0]-m[0]-d:(c[0]-m[0])/2),y(v.y===D?c[1]-m[1]:0)]:[y(v.x===F?c[0]-m[0]:0),y(v.y===D?d:v.y===O?c[1]-m[1]-d:(c[1]-m[1])/2)],me?(r=f[0].getContext("2d"),r.restore(),r.save(),r.clearRect(0,0,6e3,6e3),a=this._calculateTip(v,m,xe),h=this._calculateTip(v,this.size,xe),f.attr(A,c[0]*xe).attr(B,c[1]*xe),f.css(A,c[0]).css(B,c[1]),this._drawCoords(r,h),r.fillStyle=o[1],r.fill(),r.translate(l[0]*xe,l[1]*xe),this._drawCoords(r,a),r.fillStyle=o[0],r.fill()):(a=this._calculateTip(v),a="m"+a[0]+","+a[1]+" l"+a[2]+","+a[3]+" "+a[4]+","+a[5]+" xe",l[2]=d&&/^(r|b)/i.test(e.string())?8===oe.ie?2:1:0,f.css({coordsize:c[0]+d+" "+(c[1]+d),antialias:""+(v.string().indexOf(N)>-1),left:l[0]-l[2]*Number(n===S),top:l[1]-l[2]*Number(n===L),width:c[0]+d,height:c[1]+d}).each(function(t){var e=s(this);e[e.prop?"prop":"attr"]({coordsize:c[0]+d+" "+(c[1]+d),path:a,fillcolor:o[0],filled:!!t,stroked:!t}).toggle(!(!d&&!t)),!t&&e.html(qe("stroke",'weight="'+2*d+'px" color="'+o[1]+'" miterlimit="1000" joinstyle="miter"'))})),t.opera&&setTimeout(function(){p.tip.css({display:"inline-block",visibility:"visible"})},1),i!==k&&this.calculate(e,c)},calculate:function(t,e){if(!this.enabled)return k;var i,o,n=this,r=this.qtip.elements,a=this.element,h=this.options.offset,l=(r.tooltip.hasClass("ui-widget"),{});return t=t||this.corner,i=t.precedance,e=e||this._calculateSize(t),o=[t.x,t.y],i===S&&o.reverse(),s.each(o,function(s,o){var a,c,d;o===N?(a=i===L?F:D,l[a]="50%",l[ce+"-"+a]=-Math.round(e[i===L?0:1]/2)+h):(a=n._parseWidth(t,o,r.tooltip),c=n._parseWidth(t,o,r.content),d=n._parseRadius(t),l[o]=Math.max(-n.border,s?c:h+(d>a?d:-a)))
+}),l[t[i]]-=e[i===S?0:1],a.css({margin:"",top:"",bottom:"",left:"",right:""}).css(l),l},reposition:function(t,e,s){function o(t,e,i,s,o){t===V&&l.precedance===e&&c[s]&&l[i]!==N?l.precedance=l.precedance===S?L:S:t!==V&&c[s]&&(l[e]=l[e]===N?c[s]>0?s:o:l[e]===s?o:s)}function n(t,e,o){l[t]===N?g[ce+"-"+e]=f[t]=r[ce+"-"+e]-c[e]:(a=r[o]!==i?[c[e],-r[e]]:[-c[e],r[e]],(f[t]=Math.max(a[0],a[1]))>a[0]&&(s[e]-=c[e],f[e]=k),g[r[o]!==i?o:e]=f[t])}if(this.enabled){var r,a,h=e.cache,l=this.corner.clone(),c=s.adjusted,d=e.options.position.adjust.method.split(" "),p=d[0],u=d[1]||d[0],f={left:k,top:k,x:0,y:0},g={};this.corner.fixed!==W&&(o(p,S,L,F,P),o(u,L,S,D,O),l.string()===h.corner.string()||h.cornerTop===c.top&&h.cornerLeft===c.left||this.update(l,k)),r=this.calculate(l),r.right!==i&&(r.left=-r.right),r.bottom!==i&&(r.top=-r.bottom),r.user=this.offset,(f.left=p===V&&!!c.left)&&n(S,F,P),(f.top=u===V&&!!c.top)&&n(L,D,O),this.element.css(g).toggle(!(f.x&&f.y||l.x===N&&f.y||l.y===N&&f.x)),s.left-=r.left.charAt?r.user:p!==V||f.top||!f.left&&!f.top?r.left+this.border:0,s.top-=r.top.charAt?r.user:u!==V||f.left||!f.left&&!f.top?r.top+this.border:0,h.cornerLeft=c.left,h.cornerTop=c.top,h.corner=l.clone()}},destroy:function(){this.qtip._unbind(this.qtip.tooltip,this._ns),this.qtip.elements.tip&&this.qtip.elements.tip.find("*").remove().end().remove()}}),le=R.tip=function(t){return new x(t,t.options.style.tip)},le.initialize="render",le.sanitize=function(t){if(t.style&&"tip"in t.style){var e=t.style.tip;"object"!=typeof e&&(e=t.style.tip={corner:e}),/string|boolean/i.test(typeof e.corner)||(e.corner=W)}},M.tip={"^position.my|style.tip.(corner|mimic|border)$":function(){this.create(),this.qtip.reposition()},"^style.tip.(height|width)$":function(t){this.size=[t.width,t.height],this.update(),this.qtip.reposition()},"^content.title|style.(classes|widget)$":function(){this.update()}},s.extend(W,T.defaults,{style:{tip:{corner:W,mimic:k,width:6,height:6,border:W,offset:0}}});var Ce,Te,je="qtip-modal",ze="."+je;Te=function(){function t(t){if(s.expr[":"].focusable)return s.expr[":"].focusable;var e,i,o,n=!isNaN(s.attr(t,"tabindex")),r=t.nodeName&&t.nodeName.toLowerCase();return"area"===r?(e=t.parentNode,i=e.name,t.href&&i&&"map"===e.nodeName.toLowerCase()?(o=s("img[usemap=#"+i+"]")[0],!!o&&o.is(":visible")):!1):/input|select|textarea|button|object/.test(r)?!t.disabled:"a"===r?t.href||n:n}function i(t){1>c.length&&t.length?t.not("body").blur():c.first().focus()}function o(t){if(h.is(":visible")){var e,o=s(t.target),a=n.tooltip,l=o.closest(U);e=1>l.length?k:parseInt(l[0].style.zIndex,10)>parseInt(a[0].style.zIndex,10),e||o.closest(U)[0]===a[0]||i(o),r=t.target===c[c.length-1]}}var n,r,a,h,l=this,c={};s.extend(l,{init:function(){return h=l.elem=s("<div />",{id:"qtip-overlay",html:"<div></div>",mousedown:function(){return k}}).hide(),s(e.body).bind("focusin"+ze,o),s(e).bind("keydown"+ze,function(t){n&&n.options.show.modal.escape&&27===t.keyCode&&n.hide(t)}),h.bind("click"+ze,function(t){n&&n.options.show.modal.blur&&n.hide(t)}),l},update:function(e){n=e,c=e.options.show.modal.stealfocus!==k?e.tooltip.find("*").filter(function(){return t(this)}):[]},toggle:function(t,o,r){var c=(s(e.body),t.tooltip),d=t.options.show.modal,p=d.effect,u=o?"show":"hide",f=h.is(":visible"),g=s(ze).filter(":visible:not(:animated)").not(c);return l.update(t),o&&d.stealfocus!==k&&i(s(":focus")),h.toggleClass("blurs",d.blur),o&&h.appendTo(e.body),h.is(":animated")&&f===o&&a!==k||!o&&g.length?l:(h.stop(W,k),s.isFunction(p)?p.call(h,o):p===k?h[u]():h.fadeTo(parseInt(r,10)||90,o?1:0,function(){o||h.hide()}),o||h.queue(function(t){h.css({left:"",top:""}),s(ze).length||h.detach(),t()}),a=o,n.destroyed&&(n=E),l)}}),l.init()},Te=new Te,s.extend(q.prototype,{init:function(t){var e=t.tooltip;return this.options.on?(t.elements.overlay=Te.elem,e.addClass(je).css("z-index",T.modal_zindex+s(ze).length),t._bind(e,["tooltipshow","tooltiphide"],function(t,i,o){var n=t.originalEvent;if(t.target===e[0])if(n&&"tooltiphide"===t.type&&/mouse(leave|enter)/.test(n.type)&&s(n.relatedTarget).closest(Te.elem[0]).length)try{t.preventDefault()}catch(r){}else(!n||n&&"tooltipsolo"!==n.type)&&this.toggle(t,"tooltipshow"===t.type,o)},this._ns,this),t._bind(e,"tooltipfocus",function(t,i){if(!t.isDefaultPrevented()&&t.target===e[0]){var o=s(ze),n=T.modal_zindex+o.length,r=parseInt(e[0].style.zIndex,10);Te.elem[0].style.zIndex=n-1,o.each(function(){this.style.zIndex>r&&(this.style.zIndex-=1)}),o.filter("."+Z).qtip("blur",t.originalEvent),e.addClass(Z)[0].style.zIndex=n,Te.update(i);try{t.preventDefault()}catch(a){}}},this._ns,this),t._bind(e,"tooltiphide",function(t){t.target===e[0]&&s(ze).filter(":visible").not(e).last().qtip("focus",t)},this._ns,this),i):this},toggle:function(t,e,s){return t&&t.isDefaultPrevented()?this:(Te.toggle(this.qtip,!!e,s),i)},destroy:function(){this.qtip.tooltip.removeClass(je),this.qtip._unbind(this.qtip.tooltip,this._ns),Te.toggle(this.qtip,k),delete this.qtip.elements.overlay}}),Ce=R.modal=function(t){return new q(t,t.options.show.modal)},Ce.sanitize=function(t){t.show&&("object"!=typeof t.show.modal?t.show.modal={on:!!t.show.modal}:t.show.modal.on===i&&(t.show.modal.on=W))},T.modal_zindex=T.zindex-200,Ce.initialize="render",M.modal={"^show.modal.(on|blur)$":function(){this.destroy(),this.init(),this.qtip.elems.overlay.toggle(this.qtip.tooltip[0].offsetWidth>0)}},s.extend(W,T.defaults,{show:{modal:{on:k,effect:W,blur:W,stealfocus:W,escape:W}}}),R.viewport=function(i,s,o,n,r,a,h){function l(t,e,i,o,n,r,a,h,l){var c=s[n],p=_[t],b=x[t],w=i===V,q=p===n?l:p===r?-l:-l/2,C=b===n?h:b===r?-h:-h/2,T=v[n]+y[n]-(f?0:u[n]),j=T-c,z=c+l-(a===A?g:m)-T,M=q-(_.precedance===t||p===_[e]?C:0)-(b===N?h/2:0);return w?(M=(p===n?1:-1)*q,s[n]+=j>0?j:z>0?-z:0,s[n]=Math.max(-u[n]+y[n],c-M,Math.min(Math.max(-u[n]+y[n]+(a===A?g:m),c+M),s[n],"center"===p?c-q:1e9))):(o*=i===$?2:0,j>0&&(p!==n||z>0)?(s[n]-=M+o,d.invert(t,n)):z>0&&(p!==r||j>0)&&(s[n]-=(p===N?-M:M)+o,d.invert(t,r)),v>s[n]&&-s[n]>z&&(s[n]=c,d=_.clone())),s[n]-c}var c,d,p,u,f,g,m,v,y,b=o.target,w=i.elements.tooltip,_=o.my,x=o.at,q=o.adjust,C=q.method.split(" "),T=C[0],j=C[1]||C[0],z=o.viewport,M=o.container,I=i.cache,W={left:0,top:0};return z.jquery&&b[0]!==t&&b[0]!==e.body&&"none"!==q.method?(u=M.offset()||W,f="static"===M.css("position"),c="fixed"===w.css("position"),g=z[0]===t?z.width():z.outerWidth(k),m=z[0]===t?z.height():z.outerHeight(k),v={left:c?0:z.scrollLeft(),top:c?0:z.scrollTop()},y=z.offset()||W,("shift"!==T||"shift"!==j)&&(d=_.clone()),W={left:"none"!==T?l(S,L,T,q.x,F,P,A,n,a):0,top:"none"!==j?l(L,S,j,q.y,D,O,B,r,h):0},d&&I.lastClass!==(p=X+"-pos-"+d.abbrev())&&w.removeClass(i.cache.lastClass).addClass(i.cache.lastClass=p),W):W},R.polys={polygon:function(t,e){var i,s,o,n={width:0,height:0,position:{top:1e10,right:0,bottom:0,left:1e10},adjustable:k},r=0,a=[],h=1,l=1,c=0,d=0;for(r=t.length;r--;)i=[parseInt(t[--r],10),parseInt(t[r+1],10)],i[0]>n.position.right&&(n.position.right=i[0]),i[0]<n.position.left&&(n.position.left=i[0]),i[1]>n.position.bottom&&(n.position.bottom=i[1]),i[1]<n.position.top&&(n.position.top=i[1]),a.push(i);if(s=n.width=Math.abs(n.position.right-n.position.left),o=n.height=Math.abs(n.position.bottom-n.position.top),"c"===e.abbrev())n.position={left:n.position.left+n.width/2,top:n.position.top+n.height/2};else{for(;s>0&&o>0&&h>0&&l>0;)for(s=Math.floor(s/2),o=Math.floor(o/2),e.x===F?h=s:e.x===P?h=n.width-s:h+=Math.floor(s/2),e.y===D?l=o:e.y===O?l=n.height-o:l+=Math.floor(o/2),r=a.length;r--&&!(2>a.length);)c=a[r][0]-n.position.left,d=a[r][1]-n.position.top,(e.x===F&&c>=h||e.x===P&&h>=c||e.x===N&&(h>c||c>n.width-h)||e.y===D&&d>=l||e.y===O&&l>=d||e.y===N&&(l>d||d>n.height-l))&&a.splice(r,1);n.position={left:a[0][0],top:a[0][1]}}return n},rect:function(t,e,i,s){return{width:Math.abs(i-t),height:Math.abs(s-e),position:{left:Math.min(t,i),top:Math.min(e,s)}}},_angles:{tc:1.5,tr:7/4,tl:5/4,bc:.5,br:.25,bl:.75,rc:2,lc:1,c:0},ellipse:function(t,e,i,s,o){var n=R.polys._angles[o.abbrev()],r=0===n?0:i*Math.cos(n*Math.PI),a=s*Math.sin(n*Math.PI);return{width:2*i-Math.abs(r),height:2*s-Math.abs(a),position:{left:t+r,top:e+a},adjustable:k}},circle:function(t,e,i,s){return R.polys.ellipse(t,e,i,i,s)}},R.svg=function(t,i,o){for(var n,r,a,h,l,c,d,p,u,f,g,m=s(e),v=i[0],y=s(v.ownerSVGElement),b=1,w=1,_=!0;!v.getBBox;)v=v.parentNode;if(!v.getBBox||!v.parentNode)return k;n=y.attr("width")||y.width()||parseInt(y.css("width"),10),r=y.attr("height")||y.height()||parseInt(y.css("height"),10);var x=(parseInt(i.css("stroke-width"),10)||0)/2;switch(x&&(b+=x/n,w+=x/r),v.nodeName){case"ellipse":case"circle":f=R.polys.ellipse(v.cx.baseVal.value,v.cy.baseVal.value,(v.rx||v.r).baseVal.value+x,(v.ry||v.r).baseVal.value+x,o);break;case"line":case"polygon":case"polyline":for(u=v.points||[{x:v.x1.baseVal.value,y:v.y1.baseVal.value},{x:v.x2.baseVal.value,y:v.y2.baseVal.value}],f=[],p=-1,c=u.numberOfItems||u.length;c>++p;)d=u.getItem?u.getItem(p):u[p],f.push.apply(f,[d.x,d.y]);f=R.polys.polygon(f,o);break;default:f=v.getBoundingClientRect(),f={width:f.width,height:f.height,position:{left:f.left,top:f.top}},_=!1}return g=f.position,y=y[0],_&&(y.createSVGPoint&&(a=v.getScreenCTM(),u=y.createSVGPoint(),u.x=g.left,u.y=g.top,h=u.matrixTransform(a),g.left=h.x,g.top=h.y),y.viewBox&&(l=y.viewBox.baseVal)&&l.width&&l.height&&(b*=n/l.width,w*=r/l.height)),g.left+=m.scrollLeft(),g.top+=m.scrollTop(),f},R.imagemap=function(t,e,i){e.jquery||(e=s(e));var o,n,r,a,h,l=e.attr("shape").toLowerCase().replace("poly","polygon"),c=s('img[usemap="#'+e.parent("map").attr("name")+'"]'),d=s.trim(e.attr("coords")),p=d.replace(/,$/,"").split(",");if(!c.length)return k;if("polygon"===l)a=R.polys.polygon(p,i);else{if(!R.polys[l])return k;for(r=-1,h=p.length,n=[];h>++r;)n.push(parseInt(p[r],10));a=R.polys[l].apply(this,n.concat(i))}return o=c.offset(),o.left+=Math.ceil((c.outerWidth(k)-c.width())/2),o.top+=Math.ceil((c.outerHeight(k)-c.height())/2),a.position.left+=o.left,a.position.top+=o.top,a};var Me,Ie='<iframe class="qtip-bgiframe" frameborder="0" tabindex="-1" src="javascript:\'\';"  style="display:block; position:absolute; z-index:-1; filter:alpha(opacity=0); -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";"></iframe>';s.extend(C.prototype,{_scroll:function(){var e=this.qtip.elements.overlay;e&&(e[0].style.top=s(t).scrollTop()+"px")},init:function(i){var o=i.tooltip;1>s("select, object").length&&(this.bgiframe=i.elements.bgiframe=s(Ie).appendTo(o),i._bind(o,"tooltipmove",this.adjustBGIFrame,this._ns,this)),this.redrawContainer=s("<div/>",{id:X+"-rcontainer"}).appendTo(e.body),i.elements.overlay&&i.elements.overlay.addClass("qtipmodal-ie6fix")&&(i._bind(t,["scroll","resize"],this._scroll,this._ns,this),i._bind(o,["tooltipshow"],this._scroll,this._ns,this)),this.redraw()},adjustBGIFrame:function(){var t,e,i=this.qtip.tooltip,s={height:i.outerHeight(k),width:i.outerWidth(k)},o=this.qtip.plugins.tip,n=this.qtip.elements.tip;e=parseInt(i.css("borderLeftWidth"),10)||0,e={left:-e,top:-e},o&&n&&(t="x"===o.corner.precedance?[A,F]:[B,D],e[t[1]]-=n[t[0]]()),this.bgiframe.css(e).css(s)},redraw:function(){if(1>this.qtip.rendered||this.drawing)return this;var t,e,i,s,o=this.qtip.tooltip,n=this.qtip.options.style,r=this.qtip.options.position.container;return this.qtip.drawing=1,n.height&&o.css(B,n.height),n.width?o.css(A,n.width):(o.css(A,"").appendTo(this.redrawContainer),e=o.width(),1>e%2&&(e+=1),i=o.css("maxWidth")||"",s=o.css("minWidth")||"",t=(i+s).indexOf("%")>-1?r.width()/100:0,i=(i.indexOf("%")>-1?t:1)*parseInt(i,10)||e,s=(s.indexOf("%")>-1?t:1)*parseInt(s,10)||0,e=i+s?Math.min(Math.max(e,s),i):e,o.css(A,Math.round(e)).appendTo(r)),this.drawing=0,this},destroy:function(){this.bgiframe&&this.bgiframe.remove(),this.qtip._unbind([t,this.qtip.tooltip],this._ns)}}),Me=R.ie6=function(t){return 6===oe.ie?new C(t):k},Me.initialize="render",M.ie6={"^content|style$":function(){this.redraw()}}})})(window,document);
+//@ sourceMappingURL=http://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.0/jquery.qtip.min.map
\ No newline at end of file
index 564e34a..b8b331a 100644 (file)
@@ -15,6 +15,9 @@ function mysliceAlert(msg, level, timeout) {
 };
 /* Table initialisation */
 $(document).ready(function() {
+       $('button[type=submit]').click(function() {
+               $('form').submit();
+       });
        
        var platformParameters = {};
        
@@ -43,10 +46,12 @@ $(document).ready(function() {
                                        var el = $('*[data-key="'+myslice.pending[i]+'"]');
                                        el.addClass("active");
                                        el.find('input[type=checkbox]').prop('checked', true);
+                    /*
                                        if (myslice.count() > 0) {
                                                $('#badge-pending').text(myslice.count());
                                                $('#badge-pending').show();
                                        }
+                    */
                                }
                    }
                } );
@@ -60,18 +65,20 @@ $(document).ready(function() {
                                row.removeClass("active");
                                myslice.del(id);
                                cnt = myslice.count();
+                /*
                                $('#badge-pending').text(cnt);
                                if (cnt <= 0) {
                                        $('#badge-pending').hide();
-                               }
+                               }*/
                        } else {
                                row.addClass("active");
                                myslice.add(id);
+                /*
                                cnt = myslice.count();
                                $('#badge-pending').text(cnt);
                                if (cnt > 0) {
                                        $('#badge-pending').show();
-                               }
+                               }*/
                        }
                });
        });
index 1f997d0..7e77184 100644 (file)
@@ -27,6 +27,8 @@ $(document).ready(function() {
        
     });
     
+    // Unused "List of testbeds" tab
+    /*
     $.get("/rest/network", function(data) {
                var list = '<div class="list-group-item sl-platform"><span class="list-group-item-heading">Testbeds</span></div>';
                for(i=0; i<data.length;i++) {
@@ -36,7 +38,8 @@ $(document).ready(function() {
        }).done(function() {
                
        });
-       
+       */
+
        $('button#ApplyPendind').click(function() {
                myslice.apply();
                // $.each(myslice.pending, function(k, p) {
index 5bd0d75..80653e8 100644 (file)
@@ -5,10 +5,12 @@ from django.contrib.auth import authenticate, login, logout
 from django.template import RequestContext
 from django.shortcuts import render_to_response
 from django.shortcuts import render
-
+import json
 from unfold.loginrequired import FreeAccessView
 
 from manifoldapi.manifoldresult import ManifoldResult
+from manifold.core.query                import Query
+from manifoldapi.manifoldapi            import execute_query
 from ui.topmenu import topmenu_items, the_user
 from myslice.configengine import ConfigEngine
 
@@ -29,6 +31,24 @@ class SupportView (FreeAccessView, ThemeView):
 
         if request.user.is_authenticated(): 
             env['person'] = self.request.user
+            ## check user is pi or not
+            platform_query  = Query().get('local:platform').select('platform_id','platform','gateway_type','disabled')
+            account_query  = Query().get('local:account').select('user_id','platform_id','auth_type','config')
+            platform_details = execute_query(self.request, platform_query)
+            account_details = execute_query(self.request, account_query)
+            for platform_detail in platform_details:
+                for account_detail in account_details:
+                    if platform_detail['platform_id'] == account_detail['platform_id']:
+                        if 'config' in account_detail and account_detail['config'] is not '':
+                            account_config = json.loads(account_detail['config'])
+                            if 'myslice' in platform_detail['platform']:
+                                acc_auth_cred = account_config.get('delegated_authority_credentials','N/A')
+            # assigning values
+            if acc_auth_cred == {} or acc_auth_cred == 'N/A':
+                pi = "is_not_pi"
+            else:
+                pi = "is_pi"
+            env['pi'] = pi
         else: 
             env['person'] = None
     
index 362f879..cbad91c 100644 (file)
@@ -1,8 +1,28 @@
 <img src="https://onelab.eu/templates/onelab2/images/logo.png">
 <br>
-<h1>You have registered to {{current_site}}</h1>
-<br>
-<h1>Please validate your email address by clicking the following link</h1>
-<br>
-<a href={{validation_link}}>{{validation_link}}</a>
+<p>We have received a user signup request for your email address at {{current_site}}</p>
+<p>You have the following user details:</p>
+
+Organization: {{organization}}<br>
+First name: {{first_name}}<br>
+Last name: {{last_name}}<br>
+Email: {{email}}<br>
+
+<p></p>
+<p>
+You may now log in to the portal using your email address and the password that you provided, but your access will be limited. To gain full access, two steps are required:
+</p>
 
+<ul>
+<li>1. You confirm that you have indeed made this request by clicking on the following link: <br>
+       <a href={{validation_link}}>{{validation_link}}</a> 
+               <ul><li>If you did not make this request, we apologise. You may disregard this email or you may advise us the error by replying to this email.</li></ul>
+</li>
+<li>
+2. A manager from your organization validates your request. Upon confirmation of your signup request, we will send an email to the managers at your organization with a validation request.
+</li>
+</ul>
+<p>
+We look forward to welcoming you to OneLab. You will find answers to frequently asked questions <a href="http://{{current_site}}/portal/support/">here</a>. 
+Please don't hesitate to <a href="http://{{current_site}}/portal/contact/">contact us</a> with any additional questions that you might have.
+</p>
index b5eb224..ac31ec2 100644 (file)
@@ -1,7 +1,24 @@
-You have registered to {{current_site}}
-Please validate your email address by clicking the following link:
+We have received a user signup request for your email address at {{current_site}}
 
-{{validation_link}}
+You have the following user details:
+
+Organization: {{organization}}
+First name: {{first_name}}
+Last name: {{last_name}}
+Email: {{email}}
+
+You may now log in to the portal using your email address and the password that you provided, but your access will be limited. To gain full access, two steps are required:
+
+       1. You confirm that you have indeed made this request by clicking on the following link:
+
+               {{validation_link}}
+
+               If you did not make this request, we apologise. You may disregard this email or you may advise us the error by replying to this email.
+
+       2. A manager from your organization validates your request. Upon confirmation of your signup request, we will send an email to the managers at your organization with a validation request.
+
+We look forward to welcoming you to OneLab. 
+Please don't hesitate to contact us at support@myslice.info with any additional questions that you might have.
 
 
 
index f4e4762..237cfa6 100644 (file)
@@ -3,12 +3,14 @@
 <html lang="en"><head>
 <title>{{theme}} portal - {{ section }}</title>
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
-<link rel="shortcut icon" href="/static/img/favicon_fed4fire.ico">
+<link rel="shortcut icon" href="/static/img/favicon.ico">
 {# This is where insert_str will end up #}{% media_container prelude %}
 {% include 'messages-transient-header.html' %}
 <script type="text/javascript"> {# raw js code - use {% insert prelude_js %} ... {% endinsert %} #} {% container prelude_js %}</script>
+<!--<script src="{{ STATIC_URL }}js/jquery-ui.js"></script>-->
 <script src="{{ STATIC_URL }}js/jquery.dataTables.min.js"></script>
-<script src="{{ STATIC_URL }}js/dataTables.bootstrap.js"></script>
+<script src="{{ STATIC_URL }}js/jquery.qtip.min.js"></script>
+<script src="{{ STATIC_URL }}js/bootstrap.datatables.js"></script>
 <!-- <script src="{{ STATIC_URL }}js/stash.min.js"></script> -->
 <script src="{{ STATIC_URL }}js/myslice.js"></script>
 <script src="{{ STATIC_URL }}js/myslice-ui.js"></script>
 {% block head %} {% endblock head %}
 {# let's add these ones no matter what #}
 {% insert_str prelude "js/jquery.min.js" %}
+{% insert_str prelude "js/angular/angular.min.js" %}
 {% insert_str prelude "js/jquery.html5storage.min.js" %}
 {% insert_str prelude "js/messages-runtime.js" %}
 {% 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" %}
 {% insert_str prelude "css/plugin.css" %}
 {% insert_str prelude "js/bootstrap.js" %}
 {% insert_str prelude "css/bootstrap.css" %}
+{% insert_str prelude "js/bootstrap-datepicker.js" %}
+{% insert_str prelude "css/datepicker.css" %}
+{% insert_str prelude "js/bootstrap-slider.js" %}
+{% insert_str prelude "css/slider.css" %}
 {% insert_str prelude "css/topmenu.css" %}
 {% insert_str prelude "js/logout.js" %}
-<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/dataTables.bootstrap.css">
 <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/{{ theme }}.css">
+<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/jquery.qtip.min.css">
+
 
 <script type="text/javascript">
 /*
@@ -48,11 +57,13 @@ $(document).ready(function() {
     if($.isEmptyObject(user)){
         $.post("/rest/user/",{'filters':{'user_hrn':'$user_hrn'}}, function( data ) {
             if(data.length > 0){
-                slices = data[0].slices;  
+                drawSlices(data[0].slices);  
             }else{
+               $("div#home-slice-list").html(
+                                       "<div>You do not yet have a slice</div>");
+                       $("ul#dropdown-slice-list").append("<li>no slice</li>");
                 slices.push("no slice");
             }
-            drawSlices(slices);
        });
     }else{
         slices = user.slices;
@@ -60,17 +71,19 @@ $(document).ready(function() {
     }
     function drawSlices(slices){
         var items = [];
+               
         $.each( slices, function(i, val) {
-            items.push( "<li><a href=\"/slice/"+val+"\">" + val + "</a></li>" );
+            items.push( "<li><a href=\"/resources/"+val+"\">" + val + "</a></li>" );
         });
         $("div#home-slice-list").html($( "<ul/>", { html: items.join( "" ) }));
         $("ul#dropdown-slice-list").append(items.join( "" ));
     }
     {% endif %}
+       jQuery('[title!=""]').qtip();
 });
 </script>
 </head>
-<body>
+<body ng-app="ManifoldApp">
 {% block container %}
        {% block topmenu %}
        {% widget "_widget-topmenu.html" %}
index d9d2c51..fdb5d0d 100644 (file)
@@ -33,7 +33,7 @@
                {{ field.errors }} {{ field }}
            </div>
            {% endfor %}
-               <button type="submit" class="btn btn-primary">Create Ticket</button>
+               <button type="submit" class="btn btn-onelab">Create Ticket</button>
                </form>
        </div>
 </div>
index b9df80e..43feab2 100644 (file)
@@ -3,20 +3,18 @@
 {% block content %}
 
 <div class="row">
-       <h1><img src="{{ STATIC_URL }}img/icon_user_small.png" alt="User Registration" /> Experimenter registration</h1>
+       <h1><img src="{{ STATIC_URL }}img/icon_user_small.png" alt="User Registration" />User sign-up</h1>
 </div>
 <div class="row">
        {%if activation_status == 'success'%}
-               <h3>Email activation complete !</h3>
-               <p>Your account is pending for validation.</p>
-               <p>PI of your institution is responsible for validating your account.</p>
-               <p>You will be notified once your account is validated.</p>
-               <p>In the meantime, you can login to the portal with your email and password and browse through the limited access features.</p>
+               <h3>Signup request confirmed.</h3>
+               <p>You are currently able to log in to the portal using your email address and the password that you provided, but your access is still limited.</p> 
+               <p>You will have full access as soon as your account is validated by a manager at your organization. We have sent an email to the managers with a validation request.</p>
        {%else%}
-               <h3>Email activation Failed !</h3>
-               <p>Your email address is not registered in our server.</p>
-               <p>Please <a href="/portal/register">register</a> to get access to the portal.</p> 
-               <p>If you have already registered and still having this message, please <a href="/portal/contact/">contact support.</a> </p>
+               <h3>Signup confirmation failed.</h3>
+               <p>You have probably arrived at this page by clicking a confirmation link in an email that we have sent to you. However, 
+               we have been unable to match the link that you have clicked to a signup request in our database.</p>
+               <p>Please <a href="/portal/contact/">contact support</a> so that we can help you complete the signup process.</p>
        {%endif%}
  </div>
 
index 00492cd..7eaaf05 100644 (file)
@@ -7,7 +7,7 @@
 {% include 'messages-transient-header.html' %}
 <script type="text/javascript"> {# raw js code - use {% insert prelude_js %} ... {% endinsert %} #} {% container prelude_js %}</script>
 <script src="{{ STATIC_URL }}js/jquery.dataTables.min.js"></script>
-<script src="{{ STATIC_URL }}js/dataTables.bootstrap.js"></script>
+<script src="{{ STATIC_URL }}js/bootstrap.datatables.js"></script>
 <script src="{{ STATIC_URL }}js/myslice.js"></script>
 <script src="{{ STATIC_URL }}js/myslice-ui.js"></script>
 <style type="text/css">{# In case we need to add raw css code #}{% container prelude_css %}</style>
 {% block head %} {% endblock head %}
 {# let's add these ones no matter what #}
 {% insert_str prelude "js/jquery.min.js" %}
+{% insert_str prelude "js/angular/angular.min.js" %}
 {% insert_str prelude "js/jquery.html5storage.min.js" %}
 {% insert_str prelude "js/messages-runtime.js" %}
 {% 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" %}
 {% insert_str prelude "css/plugin.css" %}
 {% insert_str prelude "js/bootstrap.js" %}
 {% insert_str prelude "css/bootstrap.css" %}
+{% insert_str prelude "js/bootstrap-datepicker.js" %}
+{% insert_str prelude "css/datepicker.css" %}
+{% insert_str prelude "js/bootstrap-slider.js" %}
+{% insert_str prelude "css/slider.css" %}
 {% insert_str prelude "css/topmenu.css" %}
 {% insert_str prelude "js/logout.js" %}
-<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/dataTable.bootstrap.css">
 <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/{{ theme }}.css">
 </head>
-<body>
+<body ng-app="ManifoldApp">
 {% block container %}
        {% block topmenu %}
        {% include theme|add:"__widget-topmenu.html" %}
index a127595..c627e5b 100644 (file)
@@ -1,22 +1,20 @@
-{% extends "layout_wide.html" %}
+{% extends "layout.html" %}
 
 {% block head %} 
 <script type="text/javascript" src="{{STATIC_URL}}/js/institution.js"></script>
 {% endblock head %}
 
 {% block content %}
-<div class="container">
-       <div class="row">
-               <div class="col-md-12">
-                       <ul class="nav nav-tabs nav-section">
-                               <li class="active"><a href="#info"><img src="{{ STATIC_URL }}icons/authority-xs.png" alt="Institution" /> Institution {{user_details.parent_authority}}</a></li>
-                               <li><a href="#users">Users</a></li>
-                               <li><a href="#slices">Slices</a></li>
-                       </ul>
-           </div>
-       </div>
+<div class="row">
+       <div class="col-md-12">
+               <ul class="nav nav-tabs nav-section">
+                       <li class="active"><a href="#info"><img src="{{ STATIC_URL }}icons/authority-xs.png" alt="Institution" /> Institution {{user_details.parent_authority}}</a></li>
+                       <li><a href="#users">Users</a></li>
+                       <li><a href="#slices">Slices</a></li>
+               </ul>
+    </div>
 </div>
-<div class="container tab-content">
+<div class="tab-content">
        <div class="tab-pane active row" id="info">
                <div class="col-md-12">
                        <div id="authority-tab-loading"><img src="{{ STATIC_URL }}img/loading.gif" alt="Loading Authority" /></div>
diff --git a/portal/templates/join_complete.html b/portal/templates/join_complete.html
new file mode 100644 (file)
index 0000000..43be76e
--- /dev/null
@@ -0,0 +1,17 @@
+{% extends "layout.html" %}
+
+{% block content %}
+
+<div class="row">
+       <h1><img src="{{ STATIC_URL }}img/icon_user_small.png" alt="User Registration" />Request to add organization</h1>
+</div>
+<div class="row">
+  <h3>Organization information received.</h3>
+<p>
+We will contact the manager that you have listed with any questions that we might have.<br> 
+Once we have added the organization to the list, we will alert the manager.<br>
+At that time users from your organization will be able to sign up to join OneLab.
+</p>
+</div>
+
+{% endblock %}
index fecc7fb..57d8034 100644 (file)
@@ -5,7 +5,7 @@
 
 <div class="row">
        <div class="col-md-12">
-       <h1><img src="{{ STATIC_URL }}icons/testbed-xs.png" alt="Join Federation" /> Add organization</h1>
+       <h1><img src="{{ STATIC_URL }}icons/testbed-xs.png" alt="Join Federation" /> Request to add organization</h1>
        </div>
 </div>
 <div class="row">
 </div>
        <div class="form-group" >
                <p></p>
-               <button type="submit" class="btn btn-primary">Submit</button>
+               <button type="submit" class="btn btn-onelab">Submit</button>
        </div>
 </form>
 <script>
+
 /*
 jQuery(document).ready(function(){
   var availableTags = [
diff --git a/portal/templates/management-tab-requests.html b/portal/templates/management-tab-requests.html
new file mode 100644 (file)
index 0000000..3679796
--- /dev/null
@@ -0,0 +1,228 @@
+<script type="text/javascript">
+       $(document).ready(function() {
+               $("li#nav-request").addClass("active");
+       });
+       function on_click_event() {
+               var ids = []; 
+               $('.portal__validate__checkbox').each(function(i, el) {
+                       if ($(el).prop('checked')) {
+                               // portal__validate__checkbox__slice__2
+                               var id_array = $(el).attr('id').split('__');
+                               // push(slice__2)
+                               ids.push(id_array[3] + '__' + id_array[4]);
+                       }
+               });
+               if (ids.length > 0) {
+                       var id_str = ids.join('/');
+
+                       // XXX spinner
+
+                       $.getJSON('/portal/validate_action/' + id_str,
+                               function(status) {
+                                       $.each(status, function(request_type__id, request_status) {
+                                               // request_status: NAME -> dict (status, description)
+                                               var status_str = '';
+                                               $.each(request_status, function(name, result) {
+                                                       if (status_str != '')
+                                                               status_str += ' -- ';
+
+                                                       if (result.status) {
+                                                               status_str += '<font color="green">OK</font>';
+                                                               $('#portal__validate__checkbox__' + request_type__id).hide();
+                                                       } else {
+                                                               status_str += '<font color="red">ERROR: ' + result.description + '</font>';
+                                                       }
+                                               });
+                                               $('#portal__status__' + request_type__id).html(status_str);
+
+
+                                       });
+                               }
+                       );
+               }
+       }
+</script>
+
+<div class="col-md-12">
+       <h2>Authorities</h2>
+</div>
+{% if my_authorities %}
+       
+       {% for authority, requests in my_authorities.items %}
+       
+       <div class="col-md-12">
+               <h2>{{authority}}</h2>
+       </div>
+       
+    <table class="table">
+      <th>
+        <td>Type</td>
+        <td>Id</td>
+        <td>Details</td>
+        <td>Timestamp</td>
+        <td>Status</td>
+      </th>
+    {% for request in requests %}
+
+         <tr>
+               <td>
+               {% if request.allowed == 'allowed' %}
+               <input class='portal__validate__checkbox' id='portal__validate__checkbox__{{request.type}}__{{request.id}}' type='checkbox'/>
+               {% else %}
+                       {% if request.allowed == 'expired' %}
+                               expired
+                       {% else %} {# denied #}
+                               denied
+                       {% endif %}
+               {% endif %}
+               </td>
+               <td>{{ request.type }}</td>
+               <td>{{ request.id }}</td>
+               <td>
+        {% if request.type == 'user' %}
+        Login: {{request.login}} -- First name: {{request.first_name}} -- Last name: {{request.last_name}} -- Email: {{request.email}}
+        {% else %}
+            {% if request.type == 'slice' %}
+        Slice name: {{request.slice_name}} -- Number of nodes: {{request.number_of_nodes}} -- Type of nodes: {{request.type_of_nodes}} -- Purpose: {{request.purpose}}
+            {% else %} {# authority #}
+        Authority name: {{request.site_name}} -- authority_hrn: {{request.site_authority}} -- City: {{request.address_city}} -- Country: {{request.address_country}}
+            {% endif %}
+        {% endif %}
+               </td>
+               <td>{{ request.timestamp }}</td>
+               
+               <td><span id='portal__status__{{request.type}}__{{request.id}}'></span></td>
+
+    <!--<div class='portal_validate_request {{request.type}} {% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}'> -->
+         </tr>
+
+    {% endfor %}
+       </table>
+       </div>
+       {% endfor %}
+
+{% else %}
+       <div class="col-md-12">
+               <i>There is no pending request waiting for validation.</i>
+       </div>
+{% endif %}
+
+<div class="col-md-12">
+       <h2>Sub-Authorities</h2>
+</div>
+{% if sub_authorities %}
+       
+       {% for authority, requests in sub_authorities.items %}
+       <div class="col-md-12">
+       <h3>{{authority}}</h3>
+           <table class="table">
+             <th>
+               <td>Type</td>
+               <td>Id</td>
+               <td>Details</td>
+               <td>Timestamp</td>
+               <td>Status</td>
+             </th>
+           {% for request in requests %}
+                 <tr>
+                       <td>
+                       {% if request.allowed == 'allowed' %}
+                       <input class='portal__validate__checkbox' id='portal__validate__checkbox__{{request.type}}__{{request.id}}' type='checkbox'/>
+                       {% else %}
+                               {% if request.allowed == 'expired' %}
+                                       expired
+                               {% else %} {# denied #}
+                                       denied
+                               {% endif %}
+                       {% endif %}
+                       </td>
+                       <td>{{ request.type }}</td>
+                       <td>{{ request.id }}</td>
+                       <td>
+               {% if request.type == 'user' %}
+               Login: {{request.login}} -- First name: {{request.first_name}} -- Last name: {{request.last_name}} -- Email: {{request.email}}
+               {% else %}
+                   {% if request.type == 'slice' %}
+               Slice name: {{request.slice_name}} -- Number of nodes: {{request.number_of_nodes}} -- Type of nodes: {{request.type_of_nodes}} -- Purpose: {{request.purpose}}
+                   {% else %} {# authority #}
+               Authority name: {{request.site_name}} -- authority_hrn: {{request.site_authority}} -- City: {{request.address_city}} -- Country: {{request.address_country}}
+                   {% endif %}
+               {% endif %}
+                       </td>
+                       <td>{{ request.timestamp }}</td>
+                       
+                       <td><span id='portal__status__{{request.type}}__{{request.id}}'></span></td>
+       
+           <!--<div class='portal_validate_request {{request.type}} {% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}'> -->
+                 </tr>
+           {% endfor %}
+           </table>
+       </div>
+       {% endfor %}
+{% else %}
+<div class="col-md-12">
+       <i>There is no pending request waiting for validation.</i>
+</div>
+{% endif %}
+
+<div class="col-md-12">
+       <h2>Authorities with delegation</h2>
+</div>
+
+{% if delegation_authorities %}
+       
+       {% for authority, requests in delegation_authorities.items %}
+       <div class="col-md-12">
+               <h3>{{authority}}</h3>
+                   <table class="table">
+                     <th>
+                       <td>Type</td>
+                       <td>Id</td>
+                       <td>Details</td>
+                       <td>Timestamp</td>
+                       <td>Status</td>
+                     </th>
+                   {% for request in requests %}
+                         <tr>
+                               <td>
+                               {% if request.allowed == 'allowed' %}
+                               <input class='portal__validate__checkbox' id='portal__validate__checkbox__{{request.type}}__{{request.id}}' type='checkbox'/>
+                               {% else %}
+                                       {% if request.allowed == 'expired' %}
+                                               expired
+                                       {% else %} {# denied #}
+                                               denied
+                                       {% endif %}
+                               {% endif %}
+                               </td>
+                               <td>{{ request.type }}</td>
+                               <td>{{ request.id }}</td>
+                               <td>
+                       {% if request.type == 'user' %}
+                       Login: {{request.login}} -- First name: {{request.first_name}} -- Last name: {{request.last_name}} -- Email: {{request.email}}
+                       {% else %}
+                           {% if request.type == 'slice' %}
+                       Slice name: {{request.slice_name}} -- Number of nodes: {{request.number_of_nodes}} -- Type of nodes: {{request.type_of_nodes}} -- Purpose: {{request.purpose}}
+                           {% else %} {# authority #}
+                       Authority name: {{request.site_name}} -- authority_hrn: {{request.site_authority}} -- City: {{request.address_city}} -- Country: {{request.address_country}}
+                           {% endif %}
+                      {% endif %}
+                               </td>
+                               <td>{{ request.timestamp }}</td>
+                               
+                               <td><span id='portal__status__{{request.type}}__{{request.id}}'></span></td>
+               
+                   <!--<div class='portal_validate_request {{request.type}} {% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}'> -->
+                         </tr>
+                   {% endfor %}
+                   </table>
+               </div>
+               {% endfor %}
+{% else %}
+<div class="col-md-12">
+       <i>There is no pending request waiting for validation.</i>
+</div>
+{% endif %}
+<div class="col-md-12">
+       <button class="btn btn-onelab" type="button" id="portal__validate" onclick="on_click_event();"><span class="glyphicon glyphicon-ok"></span> Validate</button>
+</div>
diff --git a/portal/templates/onelab/onelab_about.html b/portal/templates/onelab/onelab_about.html
new file mode 100644 (file)
index 0000000..5b7eb96
--- /dev/null
@@ -0,0 +1,104 @@
+{% extends "layout.html" %}
+
+{% block content %}
+<div class="row">
+       <div class="col-md-12">
+                <div class="breadcrumbs">
+                </div>
+       </div>
+</div>
+<div class="row">
+    <div class="col-md-12">
+        <ul class="nav nav-tabs nav-section">
+            <li class="active"><a href="#about">About</a></li>
+            <li><a href="#components">Underlying technologies</a></li>
+        </ul>
+    </div>
+</div>
+
+
+<div class="tab-content">
+    <div class="tab-pane active row" id="about">
+               <div class="col-md-12">
+                       <p>
+                               OneLab Portal is a central place to get acess to all OneLab testbeds.In order to get access to the portal,
+                               an experimenter needs to  <a href="/portal/register">register</a> to the portal. The portal administrative body
+                               is responsible to accept or reject newly registered users.   
+                       </p>
+                       <p>
+                               To learn more about OneLab visit:  <a href="http://onelab.eu/" target="_blank">http://onelab.eu/</a>                    
+                       </p>
+                       <p>
+                               If you have any questions regarding using the portal visit: <a href="/portal/support">OneLab support</a>
+                       </p>
+                       <p>
+                               OneLab portal is a community effot. To get more information about OneLab portal team visit: 
+                               <a href="http://myslice.info/community" target="_blank">http://myslice.info/community</a>
+                       </p>
+               </div>
+       </div>
+       <div class="tab-pane row" id="components">
+               <div class="col-md-12">
+                       <h3>A ready-made and easily customisable user interface for your testbed.</h3>
+                               <p>
+                                       MySlice is an ambitious project aiming to support researchers throughout the lifecycle of experiments that can run on a variety 
+                                       of testbeds spanning different administrative domains and networking technologies. Its basic principle is to bring together 
+                                       available resources with useful information (characteristics, performance, network measurements).
+                               </p>
+                               <p>
+                                       MySlice inititiave started in Janury 2011 by offering annotation services for the first ederated experimental resources. Today, 
+                                       MySlice has taken a big step toward becoming a tand-alone web framework, which will present all available resources from testbeds 
+                                       across the world, interconnected through the Slice-based Facility Architecture (SFA) and annotated by the TopHat measurement system.
+                               </p>
+                               <p>
+                                       Our framework is built with standard programming tools (python and javascript for the front-end and python for the back-end) 
+                                       and has a modular structure based on the concept of plugins for implementing different core functionalities (query editing, 
+                                       data display, and resource allocation).
+                               </p>
+                               <p>
+                                       The goal is to enable developers with expertise on different testbed technologies and different experimental 
+                                       practices to work in parallel for optimizing the tools presented to the users allowing them for a wide range of choices 
+                                       according to their own requirements. Opening in this way the development of web-based user tools for experimentation and 
+                                       sharing effort and information can increase significantly the chances for the achievement of our challenging objective.
+                               </p>
+               </div>
+               <div class="col-md-12">
+                       <h3>Portal Components</h3>
+                               <h5>Myslice (Web Frontend)</h5>
+                                       <p>
+                                               A full-fledged and modular web portal for a testbed federation allowing user to register, 
+                                               request slices and browse and book resources, with a strong emphasis on measurements.
+                                       </p>
+                                       <p> More Info: <a href="http://myslice.info/" target="_blank">http://myslice.info/</a></p>
+                                       <p> Code: <a href="http://git.onelab.eu/?p=myslice.git;a=summary" target="_blank">Git Repository</a> (read only)</p> 
+                                       <p> 
+                                               If you need write access to the git repository you need first to send your public key to <a href="mailto:support@myslice.info">support@myslice.info</a>.
+                                       </p>
+
+
+                               <h5>Manifold (Portal backend)</h5>
+                                       <p>
+                                               Manifold is the backend that is running behind the portal. It is a component allowing the integration of distributed 
+                                               and heterogeneous data sources, such as measurement platforms or data repositories.
+                                       </p>
+                                       <p> Documentation: <a href="http://trac.myslice.info/" target="_blank">http://trac.myslice.info/</a></p>
+                                       <p> Code: <a href="https://git.top-hat.info/?p=tophat.git;a=shortlog;h=refs/heads/devel" target="_blank">Git Repository</a> (read only)</p>     
+
+                               <h5>OneLab Registry</h5>
+                                       <p>It's a SFA registry. SFA Registry is a specific installation mode of the SFAWrapper (Registry Only mode).</p>
+                                       <p> More Info: <a href="http://svn.planet-lab.org/wiki/SfaDeveloperRegistryTutorial#RunninginRegistry-Onlymode" target="_blank">SFA Registry</a></p>
+       </div>
+   </div>
+</div>
+
+<script>
+$(document).ready(function() {
+    $('.nav-tabs a').click(function (e) {
+        e.preventDefault();
+        $(this).tab('show');
+    });
+});
+</script>
+
+
+{% endblock %}
diff --git a/portal/templates/onelab/onelab_account-view.html b/portal/templates/onelab/onelab_account-view.html
new file mode 100644 (file)
index 0000000..9941e97
--- /dev/null
@@ -0,0 +1,332 @@
+{% extends "layout.html" %}
+{% block content %}
+
+<div class="row">
+       <div class="col-md-12">
+                <div class="breadcrumbs">
+                        Account &nbsp;>&nbsp; {{ person.email }}
+                </div>
+       </div>
+</div>
+{% if messages %}
+<ul class="messages">
+    {% for message in messages %}
+    <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
+    {% endfor %}
+</ul>
+{% endif %}
+<div class="row">
+       <div class="col-md-12">
+               <ul class="nav nav-tabs nav-section">
+                       <li class="active"><a href="#profile">User Profile</a></li>
+                       <li><a href="#account">Account</a></li>
+                       <li><a href="#access">Testbed Access</a></li>
+               </ul>
+    </div>
+</div>
+<div class="tab-content">
+       <div class="tab-pane active row" id="profile">
+               
+               <div class="col-md-12">
+
+                       <form id="editForm" method="post" action="account_process" enctype="multipart/form-data">
+                               {% csrf_token %}
+                                       <table class="profile">          
+                                       <tr>
+                                               <td colspan="2">
+                                                               <div><h3>Platform: Myslice</h3></div>
+                                               </td>
+                                       </tr>
+                                       <tr>
+                                               <td class="key">Email</td>
+                                               <td class="value">
+                                                               <span id="emailval" class="value" >{{ person.email }}</span>
+                                                               <button class="btn btn-default" type="button" id="edit_email" onclick="editAlert();"  title="To change your affiliation please contact the administrator">
+                                                               <span class="glyphicon glyphicon-question-sign"></span> Edit
+                                                               </button>
+                                                       </td>
+                                       </tr>
+                                       <tr class="odd">
+                                                       <td class="key">Password</td>
+                                                       <td class="value"> 
+                                                               <button class="btn btn-default btn-xs" type="button" title="Password" name="edit_pass" id="edit_pass">
+                                                                       <span class="glyphicon glyphicon-edit"></span> Edit
+                                                               </button>
+                                                               <span id="passval"class="value">******** </span>
+                                                               <span class="hide_this" id="span_pass">
+                                                               <button type="button" class="btn btn-default btn-xs" title="Cancel" id="cancel_pass_change"> Cancel </button>
+                                                               <div style='display:none;' id="pass_form">
+                                                               <input type='hidden'  value='' /></div>
+                                                               <table id="edit_password">
+                                                                       <tr>
+                                                                                       <td>Enter password: </td>
+                                                                                       <td class="field"> <input type="password" name="password" id="password" /> </td>
+                                                                               </tr>
+                                                                               <tr>
+                                                                                       <td>Confirm password: </td>
+                                                                                       <td class="field"> 
+                                                                                               <input type="password" name="confirmpassword" id="confirmpassword" /> 
+                                                                                               <input type="submit" class="btn btn-default btn-xs" name="submit_pass" value="Save"/> 
+                                                                                       </td>
+                                                                       </tr>
+                                                               </table>
+                                                               </span> 
+                                                       </td>
+                                       </tr>
+                                       <tr class="even">
+                                                       <td class="key">Full Name</td>
+                                                       <td class="value">
+                                                               <span id="nameval" class="value" >{{ fullname }} </span>
+                                                               <span class="hide_this" id="span_name">
+                                                               <button type="button" class="btn btn-default btn-xs" title="Cancel" id="cancel_name_change"> Cancel </button> 
+                                                               <div style='display:none;'><input type='hidden'  name='nameform'  /></div>
+                                                               <input id="fname" type="text" name="fname" class="required"  maxlength="200" value="{{firstname}}" />
+                                                               <input id="lname" type="text" name="lname" class="required"  maxlength="200" value="{{lastname}}" />
+                                                               <input type="submit" class="btn btn-default btn-xs" name="submit_name" value="Save"/>
+                                                               </span>
+                                                               <button class="btn btn-default btn-xs" type="button"title="Full Name" id="edit_name">
+                                                                       <span class="glyphicon glyphicon-edit"></span> Edit
+                                                               </button>
+                                                       </td>
+                                       </tr>
+                                       <tr class="odd">
+                                                       <td class="key">Authority</td>
+                                                       <td class="value">
+                                                               <span id="affval" class="value">{{ authority }}</span>
+                                                                <button class="btn btn-default btn-xs" type="button" id="edit_auth" onclick="editAlert()"  title="To change your authority please contact the administrator">
+                                                               <span class="glyphicon glyphicon-question-sign"></span> Edit
+                                                                </button>
+                                                       </td>
+                                               </tr>
+                                               {%if 'Enabled'  in user_status %}
+                                               <tr class="even">
+                                                       <td class="key">Generate Keys</td>
+                                                       <td>
+                                                               <input type="submit" name="generate" class="btn btn-primary" value="Generate a new Key Pair" id="generate_keypair" 
+                                                                          onclick="return confirm('Are you sure? If you do so, your current credentials will be overwritten.');" 
+                                                                          title="It will generate a new key Pair and your current credentials will be overwritten."/>
+                                               </td> 
+                                       </tr>
+                                       <tr class="odd">
+                                               <td class="key">Public Key</td>
+                                               <td class="value">
+                                                               <span id="keyval" class="value">******** </span>
+                                                               <span class="hide_this" id="span_upload">
+                                                                       <button type="button" class="btn btn-default" title="Cancel" id="cancel_upload"> Cancel </button>
+                                                                       <div style='display:none;'>
+                                                                               <input type='hidden'  name='upload'  /></div>
+                                                                               <input type="file" name="pubkey" class="required" id="pubkey"/>  
+                                                                               <input class="btn btn-default btn-xs" name="upload_key" id="upload_key"  type="submit" title="Upload your public key" value="Upload"
+                                                                                  onclick="return confirm('Are you sure? It will overwrite your current credentials and you have delegate it manually.');"/>
+                                                               </span>
+                                                               <div style='display:none;'> <input type='hidden'  name='dload'  /> </div> 
+                                                               <button type="submit" name="dl_pubkey" class="btn btn-default btn-xs" title="Download your public key" id="dl_file">
+                                                                       <span class="glyphicon glyphicon-download"></span> Download
+                                                               </button>
+                                                               <button class="btn btn-default btn-xs" id="upload_file" type="button" title="Upload a public key">
+                                                                       <span class="glyphicon glyphicon-upload"></span> Upload
+                                                               </button>       
+                                               </td>
+                                       </tr>
+                                       <tr class="even" id="pkey_row">
+                                                {%if 'N/A' not in user_private_key%}
+                                               <td class="key">Private Key </td> <!-- Hide if priv_key doesn't exist in myslice platform   -->
+                                               <td class="value">********<a href="#"></a>
+                                                       <button type="submit" name="dl_pkey" class="btn btn-default" title="Download your privaye key" id="dl_pkey">
+                                                                       <span class="glyphicon glyphicon-download"></span> Download     
+                                                               </button>
+                                                       <input class="btn btn-danger btn-xs" id="delete" name="delete" type="submit"  value="Delete" title="Delete your private key"
+                                                                               onclick="return confirm('Are you sure? If you do so, you have to delegate your credentials manually.');"/> 
+                                               </td>
+                                                 {%else%}
+                                                       <td class="key">Private Key </td> <!-- Hide if priv_key doesn't exist in myslice platform   -->
+                                                       <td class="value">********<a href="#"></a>
+                                                       <button type="submit" name="dl_pkey" class="btn btn-default disabled" title="Download your privaye key" id="dl_pkey">
+                                                               <span class="glyphicon glyphicon-download"></span> Download 
+                                                       </button>
+                                                       <input class="btn btn-danger btn-xs disabled" id="delete" name="delete" type="submit" title="Delete your private key" value="Delete" />
+                                                       </td>
+                                                {%endif%}              
+                                               </tr>
+                                               <tr class="even">
+                                               <td colspan="2">
+                                                       <p class="message" id="pkey_del_msg"><b> Tradeoff:</b> Ease-of-use vs Security.<br>
+                                                                       <b>Ease-of-use:</b> Automatic account delegation. Don't delete private key.<br>
+                                                                       <b>Security:</b> Manual account delegation. Download & Delete private key.
+                                                               </p>
+                                               </td>
+                                               </tr>
+                                               {%endif%}
+                                       </table>
+                       
+               </div>
+       </div>
+
+       <div class="tab-pane row" id="account">
+               <div class="col-md-12">
+       
+               <h3>Principal Account <small>Account used for delegating credentials</small></h3>
+               <table class="table"> 
+                       <tr class="odd"> 
+                       <th>Platform</th> 
+                       <th>Account Type</th>
+                               <th>Account Delegation</th>
+                       <th>User hrn</th>
+                               <th>User Status</th>
+                       <!--<th>Pub Key</th> -->
+               </tr>   
+                       {% for row in principal_acc %}         
+                       <tr class="border_bottom">
+                       <td class="odd"> {{ row.platform_name }} </td>
+                       <td class="odd"> {{ row.account_type }} </td>
+                               <td class="odd"> {{ row.delegation_type }} </td>
+                               <td class="odd"> {{ row.usr_hrn }}  </td>
+                               <td class="odd"> {{ row.user_status }}  </td>
+               <!--    <td class="even"> {{ row.usr_pubkey }} </td> -->
+               </tr> 
+                       {%endfor%}               
+               </table>
+       
+               </div>
+       
+
+       {%if 'Enabled'  in user_status %}
+               <div class="col-md-12">
+               <h3>Credentials <small>Delegated to Principal Account</small></h3>
+                       <table class="table">
+                                       <caption><b>Delegated User Credential</b></caption> 
+                           <tr class="odd"> 
+                               <th>Expiration Date</th>
+                                               <th>Download</th>
+                           </tr>
+                                       {% for row in my_users %}         
+                                       <tr class="border_bottom">
+                                       <td class="odd"> {{ row.cred_exp }} </td>
+                                               <td class="odd">
+                                                       <button class="btn btn-default btn-xs" name= "dl_user_cred" type="submit" title="Download User Credential">
+                                                               <span class="glyphicon glyphicon-download"></span> Credential
+                                                       </button>
+                                                       <button class="btn btn-default btn-xs" name= "dl_user_cert" type="submit" title="Download User Certificate">
+                                                               <span class="glyphicon glyphicon-download"></span> Certificate
+                                                       </button>
+                             <button class="btn btn-default btn-xs" name= "dl_user_p12" type="submit" title="Download User PKCS12">
+                                 <span class="glyphicon glyphicon-download"></span> PKCS p12
+                             </button>
+                                               </td>
+                                       </tr>
+                                       {%endfor%}
+                                </table>
+                               <p></p>
+                               <table class="mytable table table-bordered table-hover">
+                                       <caption><b>Delegated Slice Credentials</b></caption>  
+                               <tr class="odd"> 
+                                               <th>Slice Name</th> 
+                                       <th>Expiration Date</th>
+                                               <th>Download</th>
+                               </tr>
+                                       {% for row in my_slices %}     
+                               <tr class="border_bottom">
+                                       <td class="odd"> {{ row.slice_name }} </td>
+                                               <td class="odd"> {{ row.cred_exp }} </td>
+                                               <td class="odd"> 
+                                                       <button class="btn btn-default btn-xs" name= "dl_{{row.slice_name}}" type="submit" title="Download Slice Credentials">
+                                                               <span class="glyphicon glyphicon-download"></span> Download
+                                                       </button> 
+                                               </td>
+                               </tr>
+                               {%endfor%}
+                               </table>
+                               <p></p>
+                               <table class="mytable table table-bordered table-hover">
+                                       <caption><b>Delegated Authority Credentials</b></caption>
+                                       <tr class="odd"> 
+                                       <th>Authority Name</th> 
+                                       <th>Expiration Date</th>
+                                               <th>Download</th>
+                                       </tr>
+                                       {% for row in my_auths %}
+                                       <tr class="border_bottom">
+                                       <td class="odd"> {{ row.auth_name }} </td>
+                                       <td class="odd"> {{ row.cred_exp }} </td>
+                                               <td class="odd">
+                                                       <button class="btn btn-default btn-xs" name= "dl_{{row.auth_name}}" type="submit" title="Download Authority Credentials">
+                                                               <span class="glyphicon glyphicon-download"></span> Download
+                                                       </button>
+                                               </td>
+                                       </tr>
+                                       {%endfor%}
+                               </table>
+                               <p></p>
+                                {%if '' not in my_users%}      
+                               <p><button class="btn btn-danger btn-lg btn-block"   name= "clear_cred" type="submit" title="Clear All Credentials">Clear Credentials</button></p>
+                               {%else%}
+                               <p><button class="btn btn-danger btn-lg btn-block disabled"   name= "clear_cred" type="submit" title="Clear All Credentials">Clear Credentials</button></p>
+                               {%endif%}
+               </div>
+       </div>
+
+       <div class="tab-pane row" id="access">
+               <div class="col-md-12">
+       
+               <h3>Testbed Access <small>Reference Accounts in the following testbeds</small></h3>
+        <table class="mytable table table-bordered table-hover"> 
+            <tr class="odd"> 
+                <th>Platform</th> 
+                <th>Account Type</th>
+                               <th>Reference to</th>
+                               <th>Remove Account</th>
+            </tr>   
+            {% for row in ref_acc %}         
+            <tr class="border_bottom">
+                <td class="odd"> {{ row.platform_name }} </td>
+                <td class="odd"> {{ row.account_type }} </td>
+                               <td class="odd"> {{ row.account_reference }} </td>
+                               <td class="odd">
+                               <button class="btn btn-danger" name="delete_{{row.platform_name}}" type="submit" title="Delete account from this platform">
+                                               <span class="glyphicon glyphicon-minus"></span>
+                                       </button>
+                               </td>
+            </tr> 
+            {%endfor%}               
+        </table>               
+               
+               
+               <h3>Add reference account to the following testbeds</h3>
+        <table class="mytable table table-bordered table-hover"> 
+            <tr class="odd"> 
+                <th>Platforms</th> 
+                <th>Add Account</th>
+            </tr>   
+            {% for platform in platform_list %}         
+            <tr class="border_bottom">
+                <td class="odd"> {{ platform.platform_no_access }} </td>
+                <td class="odd">
+                                       <button class="btn btn-success btn-sm" name= "add_{{platform.platform_no_access}}" type="submit" title="Add account to this platform">
+                                               <span class="glyphicon glyphicon-plus"></span>
+                                       </button>
+                               </td>
+            </tr> 
+            {%endfor%}               
+        </table>
+       </div>
+{%endif%} 
+</div>
+</form>
+</div>
+
+<script>
+    $(document).ready(function() {
+       $('.nav-tabs a').click(function (e) {
+                       e.preventDefault();
+                       $(this).tab('show');
+                       id = $(this).attr('href').substr(1);
+               
+               });
+               
+        $('button#createslice').click(function() {
+            window.location="/portal/slice_request/";
+        });
+    });
+</script>
+
+{% endblock %}
index 8513951..fdab352 100644 (file)
 {% block head %} {% endblock head %}
 {# let's add these ones no matter what #}
 {% insert_str prelude "js/jquery.min.js" %}
+{% insert_str prelude "js/angular/angular.min.js" %}
 {% insert_str prelude "js/jquery.html5storage.min.js" %}
 {% insert_str prelude "js/messages-runtime.js" %}
 {% 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" %}
 {% insert_str prelude "css/plugin.css" %}
 {% insert_str prelude "js/bootstrap.js" %}
 {% insert_str prelude "css/bootstrap.css" %}
+{% insert_str prelude "js/bootstrap-datepicker.js" %}
+{% insert_str prelude "css/datepicker.css" %}
+{% insert_str prelude "js/bootstrap-slider.js" %}
+{% insert_str prelude "css/slider.css" %}
 {% insert_str prelude "css/topmenu.css" %}
 {% insert_str prelude "js/logout.js" %}
 <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/{{ theme }}.css">
 </head>
-<body>
+<body ng-app="ManifoldApp">
 {% block container %}
        {% block topmenu %}
        {% include theme|add:"__widget-topmenu.html" %}
diff --git a/portal/templates/onelab/onelab_contact.html b/portal/templates/onelab/onelab_contact.html
new file mode 100644 (file)
index 0000000..21608da
--- /dev/null
@@ -0,0 +1,40 @@
+{% extends "layout.html" %}
+
+{% block head %}
+{{ wizard.form.media }}
+{% endblock %}
+
+{% block content %}
+<div class="row">
+       <div class="col-md-12">
+                <div class="breadcrumbs">
+                        Support &nbsp;>&nbsp; Contact
+                </div>
+       </div>
+</div>
+<div class="row">
+       <div class="col-md-12">
+       <p>Please check our <a href="/portal/support/">FAQ</a> section. Most of the basic problems are explained there.</p>
+       <p>
+       If you haven't find your answes in the FAQ, please contact us by filling the form below.<br />
+       You can also <a href="mailto:support@myslice.info">e-mail</a> us directly.
+       </p>
+       </div>
+</div>
+
+<div class="row">
+       <div class="col-md-4">
+               <form role="form" method="post">
+               {% csrf_token %}
+               {% for field in form %}
+           <div class="form-group">
+               <label for="{{ field.html_name }}" class="control-label">{{ field.label }}</label>
+               {{ field.errors }} {{ field }}
+           </div>
+           {% endfor %}
+               <button type="submit" class="btn btn-onelab">Create ticket</button>
+               </form>
+       </div>
+</div>
+{% endblock %}
+
index 11fa2ee..f5d6cf1 100644 (file)
@@ -8,6 +8,7 @@
 {% if username %}
 <div class="container dashboard">
        <div class="row">
+               {%if 'is_pi'  in pi %}
                <div class="col-md-3">
                        <h3>
                                EXPERIMENT
                        <div>
                                <button id="slicerequestbtn" type="button" class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> Request Slice</button>
                        </div>
+                       <div>
+                               <p><strong>Your slices </strong>
+                                       <span title="A slice is a set of testbed resources on which you can conduct an experiment. 
+                                       Either ask your colleagues to give you access to an existing slice or request a new slice by clicking 'Request Slice'." 
+                                       class="glyphicon glyphicon-info-sign">
+                                       </span>
+                               </p>
+                       </div>
                        <div>   
                                <div id="home-slice-list"><img src="{{ STATIC_URL }}img/loading.gif" alt="Loading Slices" /></div>
                        </div>
                                {% if person.last_name %}
                                        {{person.first_name}} {{person.last_name}}<br />
                                {% endif %}
-                       <span class="label">Email:</span> <a href='/portal/account/'>{{person.email}}</a>
+                       <span class="label">Username:</span> <a href='/portal/account/' title="Click here to see and edit your account details.">{{person.email}}</a>
+               </div>
+               </div>
+       </div>
+       {%else%}
+       <div class="row">
+               <div class="col-md-4">
+                       <h3>
+                               EXPERIMENT
+                       </h3>
+                       <div>
+                               <a href="#"><img src="{{ STATIC_URL }}img/icon_slices.png" alt="" /></a>
+                       </div>
+                       <div>
+                               <button id="slicerequestbtn" type="button" class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> Request Slice</button>
+                       </div>
+                       <div>
+                               <p><strong>Your slices </strong>
+                               <span title="A slice is a set of testbed resources on which you can conduct an experiment. 
+                                       Either ask your colleagues to give you access to an existing slice or request a new slice by clicking 'Request Slice'." 
+                                       class="glyphicon glyphicon-info-sign">
+                               </span>
+                               </p>
+                       </div>
+                       <div>   
+                               <div id="home-slice-list"><img src="{{ STATIC_URL }}img/loading.gif" alt="Loading Slices" /></div>
+                       </div>
+               </div>
+               <div class="col-md-4">
+                       <h3>
+                               SUPPORT
+                       </h3>
+                       <div>
+                               <a href="/portal/support"><img src="{{ STATIC_URL }}img/icon_support.png" alt="" /></a>
+                       </div>
+                       <div>
+                               <button id="ticketbtn" type="button" class="btn btn-default"><span class="glyphicon glyphicon-envelope"></span> Contact</button>
+                       </div>
+               </div>
+               
+               <div class="col-md-4">
+                       <h3>
+                               ACCOUNT
+                       </h3>
+                       <div>
+                               <a href="/portal/account/"><img src="{{ STATIC_URL }}img/icon_user_color.png" alt="" /></a>
+                       </div>
+                       <div>
+                               <button id="logoutbtn" type="button" class="btn btn-default" data-username="{{ username }}"><span class="glyphicon glyphicon-off"></span> Logout</button>
+                       </div>
+                       <div>
+                               {% if person.last_name %}
+                                       {{person.first_name}} {{person.last_name}}<br />
+                               {% endif %}
+                       <span class="label">Username:</span> <a href='/portal/account/' title="Click here to see and edit your account details.">{{person.email}}</a>
                </div>
                </div>
        </div>
+       {%endif%}
+
 </div>
 {% else %}
 <div class="container-fluid home">
                        $('div#'+$(this).data('panel')).show();
                });
                $('button#validaterequestbtn').click(function() {
-                       window.location="/portal/validate/";
+                       window.location="/portal/institution#requests";
                });
                $('button#ticketbtn').click(function() {
                        window.location="/portal/contact/";
index 409c13f..6c35812 100644 (file)
@@ -5,20 +5,30 @@
 {% endblock head %}
 
 {% block content %}
+<div class="container">
+       <div class="row">
+               <div class="col-md-12">
+                        <div class="breadcrumbs">
+                                Management &nbsp;>&nbsp; Institution: <span id="authority_name"></span>
+                        </div>
+               </div>
+       </div>
+</div>
 <div class="container">
        <div class="row">
                <div class="col-md-12">
                        <ul class="nav nav-tabs nav-section">
-                               <li class="active"><a href="#info"><img src="{{ STATIC_URL }}icons/authority-xs.png" alt="Institution" /> Institution {{user_details.parent_authority}}</a></li>
+                               <li class="active"><a href="#info">About</a></li>
                                <li><a href="#users">Users</a></li>
                                <li><a href="#slices">Slices</a></li>
+                               <li><a href="#requests">Requests</a></li>
                        </ul>
            </div>
        </div>
 </div>
 <div class="container tab-content">
        <div class="tab-pane active row" id="info">
-               <div class="col-md-12">
+               <div class="col-md-12 el">
                        <div id="authority-tab-loading"><img src="{{ STATIC_URL }}img/loading.gif" alt="Loading Authority" /></div>
                    <div id="authority-tab-loaded" style="display:none;">
                        <div id="authority-data" style="float:left; width:50%;"></div>
        </div>
        
        <div class="tab-pane row" id="users" data-authority="{{user_details.parent_authority}}">
-               <div class="col-md-12">
+               <div class="col-md-12 el">
                        <div id="user-tab-loading"><img src="{{ STATIC_URL }}img/loading.gif" alt="Loading Slices" /></div>
                                <div id="user-tab-loaded" style="display:none;">
-                               <table id="user-tab">
+                               <table id="user-tab" class="table">
                                        <tr>
                                        <th>+/-</th>
-                                       <th>email</th>
-                                       <th>user_hrn</th>
-                                       <th>first name</th>
-                                       <th>last name</th>
-                                       <th>enabled</th>
+                                       <th>Email</th>
+                                       <th>User hrn</th>
+                                       <th>First name</th>
+                                       <th>Last name</th>
+                                       <th>Enabled</th>
                                        </tr>
                                </table>
-                               {%if 'is_pi'  in pi %}  
-                               <div>
-                                       <button id="deleteusers" type="button" class="btn btn-default"><span class="glyphicon glyphicon-remove"></span> Delete Users</button>
-                               </div>
-                               {%endif%}
+                               
+                       </div>
+                       {%if 'is_pi'  in pi %}  
+                       <div>
+                               <button id="deleteusers" type="button" class="btn btn-danger"><span class="glyphicon glyphicon-remove"></span> Delete selected users</button>
                        </div>
+                       {% endif %}
                </div>
        </div>
 
        <div class="tab-pane row" id="slices">
-               {%if 'is_pi'  in pi %}
-               <button id="createslice" type="button" class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> create slice</button>
-               {%else%}
-               <button id="createslice" type="button" class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> request slice</button>
-               {%endif%}
+               <div class="col-md-12 el">
+               {% if 'is_pi'  in pi %}
+               <button id="createslice" type="button" class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> Create slice</button>
+               {% else %}
+               <button id="createslice" type="button" class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> Request slice</button>
+               {% endif %}
+               <br /><br />
            <div id="slice-tab-loading"><img src="{{ STATIC_URL }}img/loading.gif" alt="Loading Slices" /></div>
            <div id="slice-tab-loaded" style="display:none;">
-               <table id="slice-tab">
+               <table id="slice-tab" class="table">
                    <tr>
                        <th>+/-</th>
-                       <th>slice_hrn</th>
-                       <th>users</th>
-                       <th>url</th>
+                       <th>Slice hrn</th>
+                       <th>Users</th>
+                       <th>Url</th>
                        <!-- <th>nodes</th> -->
-                       <th>expiration</th>
+                       <th>Expiration</th>
                    </tr>
-               </table>
-               <br>
-                       {%if 'is_pi'  in pi %}
-               <div>
-                   <button id="renewslices" type="button" class="btn btn-default"><span class="glyphicon glyphicon-refresh"></span> Renew Slices</button>
-                   <button id="deleteslices" type="button" class="btn btn-default"><span class="glyphicon glyphicon-remove"></span> Delete Slices</button>
-               </div>
-                       {%endif%} 
+               </table>                        
            </div>
+       {% if 'is_pi'  in pi %}
+        <div>
+            <button id="renewslices" type="button" class="btn btn-primary"><span class="glyphicon glyphicon-refresh"></span> Renew Slices</button>
+            <button id="deleteslices" type="button" class="btn btn-danger"><span class="glyphicon glyphicon-remove"></span> Delete Slices</button>
+        </div>
+               {% endif %} 
+          </div>
+       </div>
+       <div class="tab-pane row" id="requests">
        </div>
 </div>
 <script>
@@ -87,11 +102,13 @@ $(document).ready(function() {
     {% if user_details.parent_authority %}
         
         $.post("/rest/authority/",{'filters':{'authority_hrn':'{{user_details.parent_authority}}'}}, function( data ) {
+               
             var authority_data = [];
             var onelab_data = [];
                        /* 'city','enabled','legal','longitude','onelab_membership','address','parent_authority','slice','user','country',
             'tech','abbreviated_name','url','postcode','description','scientific','authority_hrn','latitude','name'    */
             $.each( data, function( key, val ) {
+               $('#authority_name').text(val.name);
                 authority_row = "<img src='{{ STATIC_URL }}img/institutions/{{user_details.parent_authority}}.gif' alt='' /><br>";
                 authority_row += "<br>";
                 authority_row += "<b>authority:</b> "+val.authority_hrn+"<br>";
@@ -213,5 +230,19 @@ $(document).ready(function() {
     {% endif %}
 
 }); // End document.ready
+
+$(document).ready(function() {
+       $('.nav-tabs a').click(function (e) {
+               e.preventDefault();
+               $(this).tab('show');
+       var id = $(this).attr('href').substr(1);
+       if (id == 'requests') 
+               $("#" + id).load('/management/' + id);
+       });
+       var hash = window.location.hash;
+       if (hash) {
+               $('.nav-tabs a[href='+hash+']').click();
+       }
+});
 </script>
 {% endblock %}
diff --git a/portal/templates/onelab/onelab_management-tab-requests.html b/portal/templates/onelab/onelab_management-tab-requests.html
new file mode 100644 (file)
index 0000000..3679796
--- /dev/null
@@ -0,0 +1,228 @@
+<script type="text/javascript">
+       $(document).ready(function() {
+               $("li#nav-request").addClass("active");
+       });
+       function on_click_event() {
+               var ids = []; 
+               $('.portal__validate__checkbox').each(function(i, el) {
+                       if ($(el).prop('checked')) {
+                               // portal__validate__checkbox__slice__2
+                               var id_array = $(el).attr('id').split('__');
+                               // push(slice__2)
+                               ids.push(id_array[3] + '__' + id_array[4]);
+                       }
+               });
+               if (ids.length > 0) {
+                       var id_str = ids.join('/');
+
+                       // XXX spinner
+
+                       $.getJSON('/portal/validate_action/' + id_str,
+                               function(status) {
+                                       $.each(status, function(request_type__id, request_status) {
+                                               // request_status: NAME -> dict (status, description)
+                                               var status_str = '';
+                                               $.each(request_status, function(name, result) {
+                                                       if (status_str != '')
+                                                               status_str += ' -- ';
+
+                                                       if (result.status) {
+                                                               status_str += '<font color="green">OK</font>';
+                                                               $('#portal__validate__checkbox__' + request_type__id).hide();
+                                                       } else {
+                                                               status_str += '<font color="red">ERROR: ' + result.description + '</font>';
+                                                       }
+                                               });
+                                               $('#portal__status__' + request_type__id).html(status_str);
+
+
+                                       });
+                               }
+                       );
+               }
+       }
+</script>
+
+<div class="col-md-12">
+       <h2>Authorities</h2>
+</div>
+{% if my_authorities %}
+       
+       {% for authority, requests in my_authorities.items %}
+       
+       <div class="col-md-12">
+               <h2>{{authority}}</h2>
+       </div>
+       
+    <table class="table">
+      <th>
+        <td>Type</td>
+        <td>Id</td>
+        <td>Details</td>
+        <td>Timestamp</td>
+        <td>Status</td>
+      </th>
+    {% for request in requests %}
+
+         <tr>
+               <td>
+               {% if request.allowed == 'allowed' %}
+               <input class='portal__validate__checkbox' id='portal__validate__checkbox__{{request.type}}__{{request.id}}' type='checkbox'/>
+               {% else %}
+                       {% if request.allowed == 'expired' %}
+                               expired
+                       {% else %} {# denied #}
+                               denied
+                       {% endif %}
+               {% endif %}
+               </td>
+               <td>{{ request.type }}</td>
+               <td>{{ request.id }}</td>
+               <td>
+        {% if request.type == 'user' %}
+        Login: {{request.login}} -- First name: {{request.first_name}} -- Last name: {{request.last_name}} -- Email: {{request.email}}
+        {% else %}
+            {% if request.type == 'slice' %}
+        Slice name: {{request.slice_name}} -- Number of nodes: {{request.number_of_nodes}} -- Type of nodes: {{request.type_of_nodes}} -- Purpose: {{request.purpose}}
+            {% else %} {# authority #}
+        Authority name: {{request.site_name}} -- authority_hrn: {{request.site_authority}} -- City: {{request.address_city}} -- Country: {{request.address_country}}
+            {% endif %}
+        {% endif %}
+               </td>
+               <td>{{ request.timestamp }}</td>
+               
+               <td><span id='portal__status__{{request.type}}__{{request.id}}'></span></td>
+
+    <!--<div class='portal_validate_request {{request.type}} {% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}'> -->
+         </tr>
+
+    {% endfor %}
+       </table>
+       </div>
+       {% endfor %}
+
+{% else %}
+       <div class="col-md-12">
+               <i>There is no pending request waiting for validation.</i>
+       </div>
+{% endif %}
+
+<div class="col-md-12">
+       <h2>Sub-Authorities</h2>
+</div>
+{% if sub_authorities %}
+       
+       {% for authority, requests in sub_authorities.items %}
+       <div class="col-md-12">
+       <h3>{{authority}}</h3>
+           <table class="table">
+             <th>
+               <td>Type</td>
+               <td>Id</td>
+               <td>Details</td>
+               <td>Timestamp</td>
+               <td>Status</td>
+             </th>
+           {% for request in requests %}
+                 <tr>
+                       <td>
+                       {% if request.allowed == 'allowed' %}
+                       <input class='portal__validate__checkbox' id='portal__validate__checkbox__{{request.type}}__{{request.id}}' type='checkbox'/>
+                       {% else %}
+                               {% if request.allowed == 'expired' %}
+                                       expired
+                               {% else %} {# denied #}
+                                       denied
+                               {% endif %}
+                       {% endif %}
+                       </td>
+                       <td>{{ request.type }}</td>
+                       <td>{{ request.id }}</td>
+                       <td>
+               {% if request.type == 'user' %}
+               Login: {{request.login}} -- First name: {{request.first_name}} -- Last name: {{request.last_name}} -- Email: {{request.email}}
+               {% else %}
+                   {% if request.type == 'slice' %}
+               Slice name: {{request.slice_name}} -- Number of nodes: {{request.number_of_nodes}} -- Type of nodes: {{request.type_of_nodes}} -- Purpose: {{request.purpose}}
+                   {% else %} {# authority #}
+               Authority name: {{request.site_name}} -- authority_hrn: {{request.site_authority}} -- City: {{request.address_city}} -- Country: {{request.address_country}}
+                   {% endif %}
+               {% endif %}
+                       </td>
+                       <td>{{ request.timestamp }}</td>
+                       
+                       <td><span id='portal__status__{{request.type}}__{{request.id}}'></span></td>
+       
+           <!--<div class='portal_validate_request {{request.type}} {% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}'> -->
+                 </tr>
+           {% endfor %}
+           </table>
+       </div>
+       {% endfor %}
+{% else %}
+<div class="col-md-12">
+       <i>There is no pending request waiting for validation.</i>
+</div>
+{% endif %}
+
+<div class="col-md-12">
+       <h2>Authorities with delegation</h2>
+</div>
+
+{% if delegation_authorities %}
+       
+       {% for authority, requests in delegation_authorities.items %}
+       <div class="col-md-12">
+               <h3>{{authority}}</h3>
+                   <table class="table">
+                     <th>
+                       <td>Type</td>
+                       <td>Id</td>
+                       <td>Details</td>
+                       <td>Timestamp</td>
+                       <td>Status</td>
+                     </th>
+                   {% for request in requests %}
+                         <tr>
+                               <td>
+                               {% if request.allowed == 'allowed' %}
+                               <input class='portal__validate__checkbox' id='portal__validate__checkbox__{{request.type}}__{{request.id}}' type='checkbox'/>
+                               {% else %}
+                                       {% if request.allowed == 'expired' %}
+                                               expired
+                                       {% else %} {# denied #}
+                                               denied
+                                       {% endif %}
+                               {% endif %}
+                               </td>
+                               <td>{{ request.type }}</td>
+                               <td>{{ request.id }}</td>
+                               <td>
+                       {% if request.type == 'user' %}
+                       Login: {{request.login}} -- First name: {{request.first_name}} -- Last name: {{request.last_name}} -- Email: {{request.email}}
+                       {% else %}
+                           {% if request.type == 'slice' %}
+                       Slice name: {{request.slice_name}} -- Number of nodes: {{request.number_of_nodes}} -- Type of nodes: {{request.type_of_nodes}} -- Purpose: {{request.purpose}}
+                           {% else %} {# authority #}
+                       Authority name: {{request.site_name}} -- authority_hrn: {{request.site_authority}} -- City: {{request.address_city}} -- Country: {{request.address_country}}
+                           {% endif %}
+                      {% endif %}
+                               </td>
+                               <td>{{ request.timestamp }}</td>
+                               
+                               <td><span id='portal__status__{{request.type}}__{{request.id}}'></span></td>
+               
+                   <!--<div class='portal_validate_request {{request.type}} {% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}'> -->
+                         </tr>
+                   {% endfor %}
+                   </table>
+               </div>
+               {% endfor %}
+{% else %}
+<div class="col-md-12">
+       <i>There is no pending request waiting for validation.</i>
+</div>
+{% endif %}
+<div class="col-md-12">
+       <button class="btn btn-onelab" type="button" id="portal__validate" onclick="on_click_event();"><span class="glyphicon glyphicon-ok"></span> Validate</button>
+</div>
index a06b125..69b417d 100644 (file)
@@ -4,15 +4,17 @@
 <div class="container">
     <div class="row">
         <div class="col-md-12">
-               <h1><a href="#about"><img src="{{ STATIC_URL }}icons/slices-xs.png" alt="News" />News</a></h1>
+               <h1><img src="{{ STATIC_URL }}icons/slices-xs.png" alt="News" />News</h1>
                <br />
         </div>
     </div>
  
        <div class="row">
         <div class="col-md-12">
-
-               The <b>OneLab</b> Portal opens with the <b>PlanetLab Europe</b>, <b>IOTLab</b> and <b>NITOS</b> testbeds!
+               <b>15 July 2014</b> - The <b>OneLab</b> Portal opens with the <b>PlanetLab Europe</b>, <b>IOTLab</b> and <b>NITOS</b> testbeds!
+               <p>
+                       Existing PlanetLab users will be able to open an account with their existing PLE credentials.
+               </p>
                </div>
        </div>
 </div>
diff --git a/portal/templates/onelab/onelab_registration_view.html b/portal/templates/onelab/onelab_registration_view.html
new file mode 100644 (file)
index 0000000..ec3c6e7
--- /dev/null
@@ -0,0 +1,540 @@
+{% extends "layout.html" %}
+
+{% block content %}        
+
+<div class="row">
+       <div class="col-md-12">
+       <h1><img src="{{ STATIC_URL }}icons/user-xs.png" alt="User Registration" />User sign-up</h1>
+       </div>
+</div>
+<div class="row">
+       <div class="col-md-12">
+               <p><strong>Questions? <a href="/portal/contact" >Contact us.</a></strong></p>
+  </div>
+</div>
+{% if errors %}
+<ul>
+  {% for error in errors %}
+  <li>{{ error }}</li>
+  {% endfor %}
+</ul>
+{% endif %}
+
+
+<form action="/register" class="cmxform form-horizontal" method="post" enctype="multipart/form-data">
+<div class="row">
+       
+       <div class="col-md-12">
+                       
+                       <div class="form-group">
+                               {% csrf_token %}
+                               <label for="authority_hrn" class="control-label">Organization</label>
+                               <p></p>
+                               <input id="authority_hrn" name="org_name" class="form-control" style="width:590px" value="{{ organization }}" 
+                               title="Choose your organization (company/university) from the list that apears when you click in the field and start to type.
+                                Use the arrow keys to scroll through the list; type part of the name to narrow down the list. If it is not in the list, 
+                               please request its addition by clicking the link below. We will send an email to the managers that we have on record for 
+                               your organization, asking them to validate your sign-up request." required />
+                               <p></p>
+                               <p>Organization not listed? <a href="/portal/join">Request its addition now.</a></p>
+                       </div>
+       </div>
+</div>
+
+<div class="row">
+       <div class="col-md-6">
+       
+           <div class="form-group">
+                       <label for="firstname" class="control-label">Personal information</label>
+                       <p></p>
+                       <input type="text" name="firstname" class="form-control" style="width:350px" minlength="2" value="{{ first_name }}" placeholder="First name" required />
+           </div>
+           <div class="form-group">
+                       <input type="text" name="lastname" size="25" class="form-control" style="width:350px" minlength="2" value="{{ last_name }}" placeholder="Last name" required />
+           </div>
+               <div class="form-group">
+               <input type="email" name="email" size="25"  class="form-control" style="width:350px" value="{{ email }}" 
+                       title="Your e-mail address will be your identifier for logging in. We contact you to verify your account and then, occasionally, for important issues."
+                       placeholder="Email" required />
+               </div>
+       </div>  
+
+       <div class="col-md-6">
+           <div class="form-group">
+             <label for="password" class="control-label">Authentication</label>
+                 <p></p>       
+             <input type="password"  id="password" name="password"   class="form-control" style="width:250px" minlength="4" value="{{ password }}" 
+                       title="Your password allows you to log in to this portal."
+                       placeholder="Password" required />
+           </div>
+           <div class="form-group">
+                       <input type="password"  id="confirmpassword" name="confirmpassword" style="width:250px"  minlength="4" class="form-control" value="" 
+                               placeholder="Confirm password" required />
+           </div>
+           <div class="form-group">
+                       <select name="question" class="form-control" style="width:350px" id="key-policy" 
+                               title="Your public/private key pair allows you to access the testbeds." required >        
+                               <option value="generate">Generate my keys for me (recommended)</option>
+                               <option value="upload">Upload my public key (advanced users only)</option>
+                       </select>
+           </div>
+           <div class="form-group" style="display:none;" id="upload_key">
+               <label for="file" class="control-label">Upload public key</label>
+               <input type="file" name="user_public_key" class="form-control" style="width:200px" id="user_public_key" />
+               <br />
+                       <div class="alert alert-danger" id="pkey_del_msg">
+                               In order for the portal to contact testbeds on your behalf, so as to list and reserve resources, you will need to 
+                               <a href="http://trac.myslice.info/wiki/InstallSfa" target="_blank">delegate your public key to the portal.</a>
+                       </div>
+           </div>
+       </div>
+</div>
+<div class="row">
+       <div class="col-md-12">
+               <div class="form-group">
+                       <p></p>
+                       <input type="checkbox" name="agreement" value="agreement" required />&nbsp;&nbsp; I agree to the 
+                       <a href="#" data-toggle="modal" data-target="#myModal">terms and conditions.</a> 
+               </div>
+       </div>
+</div>
+<div class="row">
+       <div class="col-md-10">
+               <div class="form-group" id="register">
+                       <span class="glyphicon glyphicon-info-sign"></span> &nbsp;
+               If you are a <b>PlanetLab Europe</b> fill in this form using the same email address that you currently use for your PlanetLab Europe account,
+               your existing credentials will be used to validate your OneLab account.
+               Please be sure to specify a different password for your new OneLab account.
+           </div>
+       </div>
+</div>
+<div class="row">
+       <div class="col-md-12">
+               <div class="form-group" id="register">
+                       <p></p> 
+               <input class="submit btn btn-onelab" type="submit" value="Sign up" />
+
+           </div>
+       </div>
+</div>
+</form>
+
+       <!-- Modal - columns selector -->
+<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+                       <div class="modal-dialog">
+                       <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="myModalLabel">Terms & Conditions</h4>
+                               </div>
+                               <div class="modal-body">
+                                               <p align="left">
+                                       for OneLab Basic level service
+                                       <br/>
+                                       Version 0.6 of 20 May 2014
+                                               <br>
+                                               <a href="/portal/terms" target="_blank">[Printable format]</a>
+                                               </p>
+                                       <h1 align="left">1 Context</h1>
+                                       <h2 align="left">1.1 OneLab</h2>
+                                       <p align="left">
+                               OneLab is an experimental facility for testing new ideas and new technologies in the area of computer networking. It consists of a variety of types of
+                               platforms, including:</p>
+                                       <ul type="disc">
+                               <li>
+                               <strong>internet overlay testbeds</strong>
+                               , testbeds that offer virtual machines distributed across locations in different countries, allowing users to deploy overlays on the internet;
+                               </li>
+                               <li>
+                               <strong>wireless testbeds</strong>
+                               , testbeds that consist of clusters of computers that are within Wi-Fi communication range of each other, either in an office environment or in an
+                               isolated setting;
+                               </li>
+                               <li>
+                               <strong>internet of things testbeds</strong>
+                               , testbeds that consist of embedded computing nodes with sensor capabilities, communicating wirelessly in an isolated environment;
+                               </li>
+                               <li>
+                               <strong>emulation testbeds,</strong>
+                               computing clusters that offer virtual machines on servers that are interconnected by a high speed switch, enabling large scale network emulation.
+                               </li>
+                                       </ul>
+                                       <p align="left">
+                               This list of types of platforms is subject to change, and the current list, along with the identities of the specific platforms of each type, can be found
+                               on the OneLab website (onelab.eu).</p>
+                                       <p align="left">
+                               Each platform has its own owners, and OneLab is the grouping of these platforms through a consortium of institutions. The OneLab consortium is coordinated
+                               by UPMC Sorbonne Universités. It operates on a not-for-profit basis.</p>
+                                       <p align="left">
+                               Access to OneLab may also provide access to additional platforms that are not part of OneLab, due to a federation agreement between OneLab and the owners
+                               of those platforms.</p>
+                                       <h2 align="left">1.2 Fee-free Basic level service</h2>
+                                       <p align="left">These terms and conditions define and apply to OneLab's Basic level service, which is available free of charge.</p>
+                                       <p align="left">
+                               Users who would like additional services are encouraged to contact support@onelab.eu. Some additional services require a written agreement, but are
+                               otherwise free. Others require the payment of fees or in-kind contributions. (An example of an in-kind contribution is the hosting of a PlanetLab Europe
+                               server node.)</p>
+                                       <h2 align="left">1.3 Managers and standard users</h2>
+                                       <p align="left">
+                               There are two classes of OneLab user: the manager and the standard user. OneLab grants access rights to managers, who, in turn, provide access rights to
+                                       standard users. Examples are: for a small enterprise, an executive may be the manager and the employees may be standard users; for a research team, a
+                               senior scientist (faculty member or research scientist) may be a manager and doctoral students and other members of the team may be standard users; for a
+                               university course, a professor may be a manager and the students may be standard users.</p>
+                                       <h2 align="left">1.4 These terms and conditions</h2>
+                                       <p align="left">
+                               Acceptance of these terms and conditions is a condition of obtaining OneLab Basic level user service. They are posted to the OneLab portal site
+                               (portal.onelab.eu). They may be changed without other notice than the posting of a new version to the portal site.</p>
+                                       <h1 align="left">2 Services provided by OneLab</h1>
+                                       <h2 align="left">2.1 Access to the experimental facility</h2>
+                                       <p align="left">
+                               OneLab provides users with access to the platforms that make up the experimental facility. Each platform owner determines the specifics of this access (for
+                               example, how many nodes are available to a user, what happens in case of oversubscription, etc.), with the proviso that Basic level service requires that
+                                       users be able to conduct meaningful experiments on every OneLab testbed.</p>
+                                       <p align="left">
+                               Basic level service may also provide access to platforms that are federated with OneLab, but such access depends upon the terms of the federation
+                               agreements with those platforms, which may require that the user have a higher level of service in order to gain access. For example, Basic level service
+                               provides access to PlanetLab Europe, a OneLab platform, without providing access to PlanetLab Central, a federated platform. Users wanting full access
+                               across the global PlanetLab system should contact support@onelab.eu to arrange to enter into a PlanetLab Europe membership agreement.</p>
+                                       <p align="left">OneLab's role is to facilitate access to the platforms. Specifically, it provides each user with:</p>
+                                       <ul>
+                               <li align="left">
+                               <strong>a single account,</strong>
+                               the credentials for which can be used to access all of the OneLab testbeds;
+                               </li>
+                               <li align="left">
+                               <strong>tools through which to access the testbeds</strong>
+                               , including, notably, a web-based portal (portal.onelab.eu) that allows a user to see the resources available on each testbed and to reserve them,
+                               along with a number of experiment control tools that a user can employ to deploy an experiment on those resources;
+                               </li>
+                               <li align="left">
+                               <strong>support</strong>
+                               , with documentation on how to use the tools, pointers to documentation for individual testbeds, and a helpdesk to respond to user questions.
+                               </li>
+                                       </ul>
+<p align="left">
+    Additional support, such as accompaniment through the design and deployment of experiments and the interpretation of their results, is available through
+    higher levels of service.
+</p>
+<h2 align="left">
+    2.2 Best effort, without guarantees
+</h2>
+<p align="left">
+    OneLab and the owners of the individual OneLab testbeds do their best to provide the services outlined here, with the understanding that Basic level
+    service offers no guarantees. Users should clearly understand the following limitations.
+</p>
+<ul type="disc">
+    <li>
+        <strong>Reliability:</strong>
+        OneLab does not provide any guarantees with respect to the reliability of the portal, of other tools, or of the individual nodes on platforms. These
+        may be taken down for maintenance, rebooted, or reinstalled at any time. Reinstallation implies that disks are wiped, meaning that users should not
+        consider a local disk to be a persistent form of storage.
+    </li>
+    <li>
+        <strong>Fitness:</strong>
+        OneLab does not guarantee that the platforms are suitable for the experiments that users intend to conduct. There may be limitations in the
+        technologies that are offered that prevent certain types of experiments from being carried out.
+    </li>
+    <li>
+        <strong>Privacy</strong>
+        : OneLab does not guarantee the privacy of traffic generated on the platforms (e.g., wireless signals, packets). Unless otherwise specified by an
+        individual platform owner, users should assume that traffic is monitored and logged. Such monitoring may be done intentionally, for example, to allow
+        platform administrators as well as other users to investigate abuse.
+    </li>
+</ul>
+<p align="left">
+    Users who seek such guarantees are invited to consider a higher level of service.
+</p>
+<h2 align="left">
+    2.3 Limitedliability
+</h2>
+<p align="left">
+    In no event shall the partners of the OneLab consortium be liable to any user for any consequential, incidental, punitive, or lost profit damages, or for
+    any damages arising out of loss of use or loss of data, to the extent that such damages arise out of the activities of OneLab consortium partners, or any
+    breach of the present terms and conditions, even if the consortium partner has been advised of the possibility of such damages.
+</p>
+<p align="left">
+    Nothing contained in these terms and conditions shall be deemed as creating any rights or liabilities in or for third parties who are not Basic level users
+    of OneLab.
+</p>
+<h1 align="left">
+    3 Acceptable use policy
+</h1>
+<h2 align="left">
+    3.1 Responsibilities of managers and standard users
+</h2>
+<p align="left">
+    OneLab creates and administers accounts for managers and delegates to managers the responsibility for creating and administering accounts for standard
+    users. Both managers and standard users are required to follow OneLab's acceptable use policy. In addition, managers are fully responsible for the
+    activities of the standard users whose accounts they create.
+</p>
+<p align="left">
+    A manager is expected to grant user access only an individual with whom he or she has a working relationship. In general, this means an individual who
+    works for the same institution as the manager, or, in the case of higher education and research, an individual who is a student at the university where the
+    manager works. Managers may also grant access to individuals from other institutions, provided that they are collaborating on a common project on OneLab.
+    If there is a doubt, a manager should refer the question to support@onelab.eu.
+</p>
+<h2 align="left">
+    3.2 Types of use
+</h2>
+<p align="left">
+    OneLab may be used by enterprise, by scientific researchers, and by educators.
+</p>
+<p align="left">
+    OneLab may be used for pre-commercial research and development. In keeping with OneLab's not-for-profit status, it may not be used to deploy services that
+    are designed to generate a commercial profit.
+</p>
+<p align="left">
+    Not-for-profit use of OneLab to deploy services that are designed to generate revenue requires prior approval through a written agreement, and thus may not
+    be carried out on a Basic level account. Interested users are invited to contact support@onelab.eu.
+</p>
+<p align="left">
+    OneLab may be used for scientific research.
+</p>
+<p align="left">
+    OneLab may be used to host lab exercises for university courses.
+</p>
+<p align="left">
+    Questions about other types of use should be addressed to support@onelab.eu.
+</p>
+<h2 align="left">
+    3.3 Applicable laws and regulations
+</h2>
+<p align="left">
+    OneLab is managed, and the portal is hosted, in France. Information regarding the countries in which individual testbeds are managed and hosted is
+    available from those testbeds. Users are responsible for being aware of the countries in which their experiments are deployed and for ensuring that their
+    use of OneLab fully conforms to the laws and regulations of those countries, as well as the laws and regulations of the country in which they themselves
+    are present when conducting their experiments.
+</p>
+<p align="left">
+    Above and beyond specific national laws, the activities email spamming, phishing through web services, and all types of Internet fraud are prohibited on
+    OneLab.
+</p>
+<h2>
+    3.4 Security and accounting mechanisms
+</h2>
+<p align="left">
+    Users are expected to respect the security and accounting mechanisms put in place by OneLab, its platforms, and federated platforms. For example, access to
+    PlanetLab Europe is designed to take place through the SSH cryptographically-secured connection protocol, which uses public/private key pair
+    authentication, and so users should not attempt to bypass this mechanism. As another example, OneLab's notion of a "slice" associates a set of resources
+    with the group of users who have reserved those resources, and users should not attempt to obscure the identities of participants in a slice.
+</p>
+<p align="left">
+    Hacking attempts against the OneLab portal and testbeds are not permitted. This includes "red team" (hacker test) experiments.
+</p>
+<h2>
+    3.5 Sharing of resources
+</h2>
+<p align="left">
+    OneLab is intended for ambitious experiments. Large numbers of resources and extended leases on resources may legitimately be granted in order to carry
+    these out. At the same time, OneLab and its testbeds are shared environments, and when there is contention for resources, limits must be imposed.
+</p>
+<p align="left">
+    Each OneLab platform sets its own policies for handling resource contention. As a general rule, users are encouraged to design their experiments to use
+    resources efficiently. In particular, spinning/busy-waiting techniques for extended periods of time are strongly discouraged. Some resource contention
+    policies (e.g., PlanetLab Europe's) terminate the jobs that are using the most resources in the case of contention.
+</p>
+<h2>
+    3.6 Internet-connected platforms
+</h2>
+<p align="left">
+    Some of OneLab's platforms allow experiments to take place on resources that have access to the public internet. These experiments can potentially generate
+    traffic to, and receive traffic from, any host or router in the internet.<a></a><a id="_anchor_1" href="#_msocom_1" name="_msoanchor_1">[LB1]</a>
+</p>
+<p align="left">
+    Furthermore, some internet-connected platforms (e.g., PlanetLab Europe) consist of servers that are hosted by a large number of member institutions.
+</p>
+<p align="left">
+    The accessibility of internet-connected platforms and the distributed hosting model of some of these platforms imply certain responsibilities on the part
+    of users, as detailed below.
+</p>
+<h3>
+    3.6.1 General guidance
+</h3>
+<p align="left">
+    A good litmus test when considering whether an experiment is appropriate for such internet-connected platforms is to ask what the network administrator at
+    one's own organisation would say about the experiment running locally. If the experiment disrupts local activity (e.g., uses more than its share of the
+    site's internet bandwidth) or triggers complaints from remote network administrators (e.g., performs systematic port scans), then it is not appropriate for
+    such internet-connected platforms.
+</p>
+<p align="left">
+    It is the responsibility of the user and the user's manager to ensure that an application that will run on an internet-connected platform is tested and
+    debugged in a controlled environment, to better understand its behaviour prior to deployment.
+</p>
+<h3>
+    3.6.2 Standards of network etiquette
+</h3>
+<p align="left">
+    Internet-connected platforms are designed to support experiments that generate unusual traffic, such as network measurements. However, it is expected that
+    all users adhere to widely accepted standards of network etiquette in an effort to minimise complaints from network administrators. Activities that have
+    been interpreted as worm and denial-of-service attacks in the past (and should be avoided) include sending SYN packets to port 80 on random machines,
+    probing random IP addresses, repeatedly pinging routers, overloading bottleneck links with measurement traffic, and probing a single target machine from
+    many nodes.
+</p>
+<p align="left">
+    For internet-connected platforms that have a distributed hosting model, each host institution will have its own acceptable use policy. Users should not
+    knowingly violate such local policies. Conflicts between local policies and OneLab's stated goal of supporting research into wide-area networks should be
+    brought to the attention of OneLab administrators at support@onelab.eu.
+</p>
+<h3>
+    3.6.3 Specific network usage rules
+</h3>
+<p align="left">
+    It is not allowed to use one or more nodes of an internet-connected platform to generate a high number of network flows or flood a site with high traffic
+    to the point of interfering with its normal operation. Use of congestion-controlled flows for large transfers is highly encouraged.
+</p>
+<p align="left">
+    It is not allowed to perform systematic or random port or address block scans from an internet-connected platform.
+</p>
+<p align="left">
+    For internet-connected platforms that use a distributed hosting model, it is not allowed to spoof or sniff traffic on a hosted server or on the network the
+    server belongs to.
+</p>
+<p align="left">
+    Access to a server on a distributed hosting platform may not be used to gain access to other servers or networked equipment that are not part of the
+    testbed.
+</p>
+<h2>
+    3.7 Wireless platforms
+</h2>
+<p align="left">
+    Wireless-connected platforms give users access to nodes that communicate via Wi-Fi and other wireless technologies. They may be capable of detecting
+    wireless activity in the neighbourhood of those nodes: traffic generated by other users of the platform or by individuals not associated with the platform.
+    In general, much of the traffic will be encrypted, with certain aspects (such as SSIDs) not encrypted, but it is also possible that there will be fully
+    unencrypted traffic. They may also be capable of generating wireless activity that reaches equipment outside of the testbed.
+</p>
+<p align="left">
+    Furthermore, some wireless-connected platforms may have built-in limitations to prevent them from generating signals at a strength that exceeds health and
+    safety regulations.
+</p>
+<p align="left">
+    These characteristics of wireless-connected platforms imply certain responsibilities on the part of users, as detailed below.
+</p>
+<h3>
+    3.7.1 Specific network usage rules
+</h3>
+<p align="left">
+    Experimenters may make no attempt to defeat the encryption of encrypted third-party traffic. Furthermore, experimenters must treat with utmost discretion
+    any unencrypted traffic. Limited metadata can be recorded for the bona fide purposes of an experiment, but under no case should third party communications
+    be recorded.
+</p>
+<p align="left">
+    No attempt may be made to reverse engineer traffic in order to learn the identities of the parties who have generated the traffic.
+</p>
+<p align="left">
+    Wireless-connected platforms may not be used to gain access to any network equipment that is not part of the testbed itself.
+</p>
+<p align="left">
+    It is not allowed to perform systematic or random scans of wireless networks that are not part of a wireless-connected platform. Similarly, it is not
+    allowed to spoof or sniff wireless traffic of the institution that hosts a wireless-connected platform or of other networks in the proximity.
+</p>
+<p align="left">
+    Care must be taken so that traffic on wireless-connected platforms does not interfere with the normal functioning of network equipment that is not part of
+    the testbed itself.
+</p>
+<p align="left">
+    No attempt may be made to defeat the mechanisms that limit signal strength on wireless-connected platforms.
+</p>
+<h2>
+    3.8 Handling suspected violations
+</h2>
+<p align="left">
+    Suspected violations of the OneLab acceptable use policy should be reported to support@onelab.eu.
+</p>
+<p align="left">
+    Upon notification or detection of a possible violation, OneLab management will attempt to understand if a violation has in fact occurred. To do so,
+    management will freely communicate with the users concerned, the operators of the platforms concerned, as well as any third parties that might be involved.
+    An example of a third party is a network operator who detects what they believe to be unauthorized traffic emanating from a OneLab platform.
+</p>
+<p align="left">
+    The priority is to resolve any real or apparent violations amicably. However, if OneLab management believes that a violation may have occurred, it can, at
+    its sole discretion, and without prior notice, apply any of the following measures:
+</p>
+<ul type="disc">
+    <li>
+        notification of the users of the concerned slice (set of resources);
+    </li>
+    <li>
+        disabling of the concerned slice;
+    </li>
+    <li>
+        disabling an individual user's account;
+    </li>
+    <li>
+        reporting of the user's activity to his/her manager;
+    </li>
+    <li>
+        disabling of the manager's account and all user accounts for which the manager is responsible;
+    </li>
+    <li>
+        disabling of all accounts associated with the user's institution.
+    </li>
+</ul>
+<p align="left">
+    In the case of suspected illegal activity, OneLab management might need, without prior notice, to notify the relevant authorities.
+</p>
+
+                               </div>
+                               <div class="modal-footer">
+                                       <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+                               </div>
+                       </div>
+                       </div>
+</div>
+
+    
+<script>
+$(document).ready(function(){
+    var availableTags = [
+    {% if authorities %}
+        {% for authority in authorities %}
+            {% if authority.name %}
+                {value:"{{ authority.name }}",label:"{{authority.name}}"},
+                       // to show only full name
+           // {% else %}
+           //     {value:"{{ authority.authority_hrn }}",label:"{{authority.authority_hrn}}"},
+            {% endif %}
+        {% endfor %}    
+    {% else %}
+        {value:"",label:"No authority found !!!"}
+    {% endif %}
+    ];
+       // sorting the list
+       availableTags.sort(function(a,b){
+               var nameA=a.value.toLowerCase(), nameB=b.value.toLowerCase();
+               if (nameA < nameB) {
+               return -1;
+               }
+               if (nameA > nameB) {
+               return 1;
+               }
+       return 0;
+       }); 
+       // auto-complete the form
+    $( "#authority_hrn" ).autocomplete({
+      source: availableTags,
+      minLength: 0,
+      change: function (event, ui) {
+          if(!ui.item){
+              //http://api.jqueryui.com/autocomplete/#event-change -
+              // The item selected from the menu, if any. Otherwise the property is null
+              //so clear the item for force selection
+              jQuery("#authority_hrn").val("");
+          }
+      }
+      //select: function( event, ui ) {console.log(jQuery(this))}
+    });
+       // for hover texts
+       $('[title!=""]').qtip();
+       $("form").validate();
+       $("form").submit(function() {
+               if ($('select option:selected').val() == 'upload') {
+                       if ($('input[name=user_public_key]').val() == '') {
+                               alert('Please specify the key file to upload');
+                               return false;
+                       }
+               }
+       });
+});
+</script>
+{% endblock %}
+
index 1751ce9..0a239f1 100644 (file)
@@ -1,37 +1,35 @@
-       <div class="col-md-2">
-               <div id="select-platform" class="list-group">
-               </div>
-                       
-               <ul class="list-group">
-                 <li class="list-group-item">Authority:<b> {{user_details.parent_authority}}</b>
-                       <!--<select id="auth_list">
-                               <option value="ple.upmc">UPMC</option>
-                               <option value="ple.inria">INRIA</option>
-                               <option value="ple.nitos">NITOS</option>
-                               <option value="ple.iminds">iMinds</option>
-                       </select> -->
-                 </li>
-                 <li class="list-group-item">Filter: slice</li>
-                 <li class="list-group-item">...</li>
-                 <li class="list-group-item">...</li>
-                 <li class="list-group-item">...</li>
-               </ul>
-       
+
+<div class="col-md-2">
+       <div id="select-platform" class="list-group">
        </div>
-       <div class="col-md-10">
-               <div class="row">
-               </div>
-               <div id="user-tab-loading"><img src="{{ STATIC_URL }}img/loading.gif" alt="Loading Useres" /></div>
-               <div id="user-tab-loaded" style="display:none;">
-               <table id="user-tab">
-                       <tr>
-                       <th>+/-</th>
-                       <th>Email</th>
-                       <th>user_hrn</th>
-                       <th>Enabled</th>
-                       </tr>
-               </table>
-               </div>
+               
+       <ul class="list-group">
+         <li class="list-group-item">Authority:<b> {{user_details.parent_authority}}</b>
+               <!--<select id="auth_list">
+                       <option value="ple.upmc">UPMC</option>
+                       <option value="ple.inria">INRIA</option>
+                       <option value="ple.nitos">NITOS</option>
+                       <option value="ple.iminds">iMinds</option>
+               </select> -->
+         </li>
+         <li class="list-group-item">Filter: slice</li>
+         
+       </ul>
+
+</div>
+<div class="col-md-10">
+       <div id="user-tab-loading"><img src="{{ STATIC_URL }}img/loading.gif" alt="Loading Useres" /></div>
+       <div id="user-tab-loaded" style="display:none;">
+               <table id="user-tab" class="table">
+               <tr>
+                       <th>+/-</th>
+                       <th>Email</th>
+                       <th>User hrn</th>
+                       <th>Enabled</th>
+               </tr>
+               </table>
+       </div>
+</div>
 <script>
     $(document).ready(function() {
                        //console.log("the value you selected: " + selectedValue);
@@ -43,7 +41,7 @@
                            user_urn, user_last_name, user_phone, user_hrn, user_email, user_type
                                */
                $.each( data, function( key, val ) {
-                       list_users.push( "<li><a href=\"portal/user/"+val.user_email+"\">" + val.user_email + "</a></li>" );
+                       //list_users.push( "<li><a href=\"portal/user/"+val.user_email+"\">" + val.user_email + "</a></li>" );
                        user_row = "<tr>";
                                        // checking the box for the users that belong to the selected slice
                                        var flag_checkbox = 0;
index e8f84f0..8057609 100644 (file)
@@ -6,14 +6,9 @@
 
 {% block content %}
 
-<div class="container">
-       <div class="row">
-               <div class="col-md-12">
-                       {% include theme|add:"_widget-slice-sections.html" %}
-           </div>
-       </div>
-</div>
-<div class="container-fluid tab-content">
+{% include theme|add:"_widget-slice-sections.html" %}
+         
+<div class="container-fluid tab-content container-slice">
   <div class="tab-pane active row" id="info">...</div>
   <div class="tab-pane row" id="testbeds">...</div>
   <div class="tab-pane row" id="resources">...</div>
diff --git a/portal/templates/onelab/onelab_slicerequest_view.html b/portal/templates/onelab/onelab_slicerequest_view.html
new file mode 100644 (file)
index 0000000..5d0cf56
--- /dev/null
@@ -0,0 +1,100 @@
+{% extends "layout.html" %}
+{% load i18n %}
+
+{% block content %}
+       <div class="row">
+               <div class="col-md-12">
+                        <div class="breadcrumbs">
+                                Experiment &nbsp;>&nbsp; Request a new Slice
+                        </div>
+               </div>
+       </div>
+
+       {% if errors %}
+       <div class="row">
+               <div class="col-md-12">
+               <ul>
+                 {% for error in errors %}
+                 <li>{{ error }}</li>
+                 {% endfor %}
+               </ul>
+               </div>
+       </div>
+       {% endif %}
+       
+       <div class="row">
+               <div class="col-md-8 el">
+                       <form role="form" method="post">
+                       {% csrf_token %}
+                         <div class="form-group" style="display:none">
+                           <input type="email" class="form-control" id="email" style="width:300px" value="{{ email }}" readonly="readonly">
+                         </div>
+                         <div class="form-group">
+                           <input type="text" class="form-control" name="slice_name" id="slice_name" style="width:300px" placeholder="Slice Name" 
+                               title="Please enter a name for your slice"required="required">
+                         </div>
+                         <div class="form-group">
+                               {%if 'is_pi'  in pi %}
+                               <input type="text" class="form-control" id="authority_hrn" name="org_name" style="width:300px" placeholder="Organization" 
+                               title="An authority responsible for vetting your slice" required="required">
+                               {%else%}
+                           <input type="text" class="form-control" id="authority_hrn" name="org_name" placeholder="Organization" style="width:300px;" 
+                               title="An authority responsible for vetting your slice" required="required" readonly>
+                               {%endif%}
+                         </div>
+                         <div class="form-group">
+                           <input type="number" class="form-control" name="number_of_nodes" id="number_of_nodes" style="width:300px" placeholder="Number of Nodes"
+                               title="Number of nodes you expect to request (informative)">
+                         </div>
+                         <div class="form-group">
+                               <textarea id="purpose" name="purpose" class="form-control" rows="6" placeholder="Experiment Purpose" style="width:300px" 
+                               title="Purpose of your experiment (informative)" required="required">{{ purpose }}</textarea>
+                         </div>
+                         <button type="submit" class="btn btn-onelab"><span class="glyphicon glyphicon-plus"></span> Request Slice</button>
+                       </form>
+       
+               </div>
+       </div>
+               
+<script>
+jQuery(document).ready(function(){
+       
+       $("#authority_hrn").load("/rest/user/", {"fields" : ["parent_authority"], "filters": {"user_hrn": "{{ user_hrn }}"}}, function(data) {
+               var jsonData = JSON.parse(data);
+               // getting the authority from the view not rest
+        $(this).attr("value", "{{authority_name}}");
+    });
+       var availableTags = [
+    {% if authorities %}
+        {% for authority in authorities %}
+            {% if authority.name %}
+                {value:"{{ authority.name }}",label:"{{authority.name}}"},
+                       // to show only full name
+           // {% else %}
+           //     {value:"{{ authority.authority_hrn }}",label:"{{authority.authority_hrn}}"},
+            {% endif %}
+        {% endfor %}    
+    {% else %}
+        {value:"",label:"No authority found !!!"}
+    {% endif %}
+    ];
+       // sorting the list
+       availableTags.sort(function(a,b){
+               var nameA=a.value.toLowerCase(), nameB=b.value.toLowerCase();
+               if (nameA < nameB) {
+               return -1;
+               }
+               if (nameA > nameB) {
+               return 1;
+               }
+       return 0;
+       }); 
+    $( "#authority_hrn" ).autocomplete({
+      source: availableTags,
+      minLength: 0,
+      select: function( event, ui ) {console.log(jQuery(this));}
+    });
+});
+</script>
+{% endblock %}
+
diff --git a/portal/templates/onelab/onelab_supportview.html b/portal/templates/onelab/onelab_supportview.html
new file mode 100644 (file)
index 0000000..e33127c
--- /dev/null
@@ -0,0 +1,143 @@
+{% extends "layout_wide.html" %}
+
+{% block content %}
+<div class="container">
+       <div class="row">
+               <div class="col-md-12">
+                        <div class="breadcrumbs">
+                                Support
+                        </div>
+               </div>
+       </div>
+</div>
+<div class="container">
+       <div class="row">
+               <div class="col-md-12">
+                       <ul class="nav nav-tabs nav-section">
+                               <li class="active"><a href="#support">Tickets</a></li>
+                               <li><a href="#faq">FAQ</a></li>
+                               <li><a href="#contact">Contact</a></li>
+                       </ul>
+           </div>
+       </div>
+</div>
+
+<div class="container tab-content">
+       <div class="tab-pane active row" id="support">
+               <div class="col-md-12">
+                       <h2>Report a Bug</h2>
+                       <p>If you have found a bug or having difficulties accesing some features or found some anomalies, please report it using our ticketing system.</p>
+                       <button id="ticketbtn" type="button" class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> Create Ticket</button>
+<!--                   <h3>Unresolved Tickets</h3>
+                       <table class="mytable table table-bordered table-hover">
+                       <tr>
+                           <th>Ticket No</th>
+                                       <th>Reported By</th>
+                                       <th>Description</th>
+                           <th>Status</th>
+                       </tr>
+                       <tr>
+                           <td>1</td>
+                                       <td>yasin.upmc@gmail.com</td>
+                                       <td> Slice_request page is not working </td>
+                                       <td> Unresolved</td>
+                       </tr>
+                               <tr>
+                                       <td>2</td>
+                                       <td>azerty@lip6.fr</td>
+                                       <td>Unable to Register</td>
+                                       <td>Unresolved</td>
+                               </tr> 
+               
+                   </table> -->
+               </div>
+         </div>
+       <div class="tab-pane row" id="faq">
+               <div class="col-md-12">
+                       <h2>Frequently Asked Questions (FAQs)<h2>
+
+                       <h3>Users</h3>
+                       <ul>
+                       <li><h4>Who is a user?</h4></li>
+                       <p>A user is an experimenter who registers to the OneLab portal and able to use all the facilites that the portal has to offer. However, a user does not
+                       have the right to do any admin operation such as managing slices, users and resources.</p>
+                       
+                       
+                       <li><h4>How do I register?</h4></li>
+                       <p>In order to register you must go to the <a href="/portal/register">Registration</a> page. You have to choose your corresponding institution (authority). After registration you have to wait until the PI validates your account. However upon registration, you will be able login to the portal with a limited access. Do not try to re-register with the same email address.</p>
+                       
+                       <li><h4>Why can't I register with my email?</h4></li>
+                       <p>If you have already registered then you won't be able to register again with the same email address. However, if you have never registered and still you are not able to use your email then please 
+                       <a href="/portal/contact">Contact Support</a> and mention the error message that you are getting while trying to register.</p>
+                       
+                       <li><h4>In registration, in "My keys" option, what should i choose?</h4></li>
+                       <p>There are two choices.</p> 
+                               <p class="text-justify"><b>1. Generate Key Pairs:</b> This option is for users who have no knowledge about SFA and MySlice i.e., new users. We offer convenience to the new users in order to avoid addtional efforts to delegate keys manually. If you choose this option, the portal will automatically handle your credentials and you would be able use the portal as soon as the PI validates your account. However, you can delete your private key from the portal if you are concerned about privacy issues. In that case once your current credentials expire, you have to delegate your credentials manually using <a href="http://trac.myslice.info/wiki/InstallSfa" target="_blank">SFA</a>. As this needs advance knowledge about SFA and Myslice, we highly recommend to the new users to keep both private and public keys in the portal in order to keep the process automatic. </p>
+                               <p class="text-justify"><b>2. Upload My Public Key:</b> This option is for users who have experience with MySlice and SFA. If you choose this option, once the PI validates your account, you have to delegate your credentials manually using <a href="http://trac.myslice.info/wiki/InstallSfa" target="_blank">SFA</a>. You have to repeat the same process everytime your credentials expire. Normally, credentials expire every one month. You can see the expiration date in your Account page. </p>
+                       
+                       <li><h4>Who is responsible for validating my account?</h4></li>
+                       <p>When you register, you choose an authority fromt he list of authorities. For each authority there is a Principal Investigator (PI). PI of your authority will verify your identity and if he finds that you are from his institution, he will validate your account otherwise he will reject your account.</p>
+                       
+                       <li><h4>How long I have to wait for validation?</h4></li>
+                       <p>It depends on the PI of your authority. In general, it should not take more than a week. If you are waiting more than usual, please <a href="/portal/contact">Contact Support</a> and explain your problem.</p>
+                       <li><h4>I just registered. Why can't I see any slices and resources?</h4></li>
+                       <p>Once you register, you can login to your account with limited access. It means that you can view your account details, modify your name and password. You can also view other pages. However, you will not be able to see any slices as well as resources before your account validation. But you can <a href="/portal/slice_request/">Request Slice</a> before being validated. Therefore, the PI will validate your account as well as your requested slice. Once validated, you will be able to see your slice and if you click on your slice, you will be able to see resources in that slice and you can reserve nodes and start your experiment.</p>
+                       
+                       <li><h4>How can I get access to a slice?</h4></li>
+                       <p>If you are a completely new user, you have to <a href="/portal/slice_request/">Request Slice</a>. It is upto the PI of your authority to accept/reject your slice request. </br>On the other hand, if you are a new user to the portal but you already have an account in OneLab SFA registry and you have access to slices, you will be able to see all your slices once your account is validated by the PI.</p> 
+                       
+                       <li><h4>I forgot my password, how to recover it?</h4></li>
+                       <p>If you have an account in the portal but you forgot the password, you can always <a href="/portal/pass_reset/">Reset your password</a>.</p></ul>
+                       
+                       <h3>Managers</h3>
+                       <ul>
+                       <li><h4>Who is a manager?</h4></li>
+                       <p>A manager is the Principal Investigator (PI) of the institution. Each PI has authority over his own institution. A PI can add, delete, validate users/ 
+                       slices that belong to his institution.</p>
+                       
+                       <li><h4>What is pending users/slices?</h4></li>
+                       <p>In <a href="/portal/validate">Requests</a> page you will be able to see all the users that registered under your authority and the slices that users of your authority has requested. Therefore, pending users/slices are those users and slices that are yet to be validated. You can validate/reject these requests based on the policy of your institution.</p>
+                       
+                       <li><h4>How can I manage the users/slices that belong to my institution?</h4></li>
+                       <p>In <a href="/portal/institution">Instution</a> page, under "Users" tab, you will be able to see all the users that belong to your authority. You can delete the users that you don't want anymore. Under "Slices" tab, you will be able to see all the slices that belong to your authority. You can renew/delete the slices based on your requirements. As a PI you can also <a href="/portal/slice_request/">Create Slice</a>. Just fill the form of request slice and the slice will be automatically validated if it is requested by a PI. </p>
+                       
+                       </ul>
+               </div>
+         </div>
+
+       <div class="tab-pane row" id="contact">
+               <div class="col-md-12">
+                       <h2>Contact Us</h2>
+               
+                       <h3>Mailing List</h3>
+                       <p>You can subscribe to our mailing list by sending a request to: <b>support</b> AT <b>myslice</b> DOT <b>info</b></p>
+                       <p>Also you can adress any issues in the same email address.</p>
+                       
+                       <h3>Mailing Address</h3>
+                       <address>
+                       UPMC - LIP6<br> 
+                       Boîte courrier 16 <br>
+                       Couloir 26-00, Etage 01, Bureau 102<br>
+                       4 place Jussieu<br>
+                       75252 PARIS CEDEX 05<br>
+                       France<br> 
+                       </address>
+               </div>
+         </div>
+</div>
+
+<script>
+$(document).ready(function() {
+    $('button#ticketbtn').click(function() {
+        window.location="/portal/contact/";
+    });
+       $('.nav-tabs a').click(function (e) {
+               e.preventDefault();
+               $(this).tab('show');
+       });
+});
+</script>
+
+
+{% endblock %}
+
index fad0c26..d7342f8 100644 (file)
@@ -14,7 +14,7 @@
        <input type="password" name="password" placeholder="Password">
        </div>
        <div class="login-submit">
-               <input type="submit" value="Sign In" />
+               <input type="submit" class="btn btn-onelab" value="Sign In" />
                <span class="lost-password">&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="/portal/pass_reset/">Can't access your account?</a></span>
        </div>
        <div class="login-signup">
index 0d0673f..01c8d82 100644 (file)
@@ -1,23 +1,36 @@
+<div class="container">
+       <div class="row">
+               <div class="col-md-12">
+                        <div class="breadcrumbs">
+                                Experiment &nbsp;>&nbsp; {{ slice }}
+                        </div>
+               </div>
+       </div>
+</div>
+<div class="container-fluid container-slice">
+<div class="row">
+               <div class="col-md-12">
 {% if section == "resources" %}
 <ul class="nav nav-tabs nav-section">
-       <li><a href="/slice/{{ slice }}#info"><img src="{{ STATIC_URL }}icons/slices-xs.png" alt="About MySlice" /> {{ slice }}</a></li>
+       <li><a href="/slice/{{ slice }}#info">Information</a></li>
        <!-- <li><a href="/slice/{{ slice }}#testbeds">Testbeds</a></li> -->
        <li class="active"><a class="link" href="/resources/{{ slice }}">Resources</a></li>
        <li><a href="/slice/{{ slice }}#users">Users</a></li>
        <!-- <li><a href="/slice/{{ slice }}#experiment">Statistics</a></li> -->
        <!-- <li><a href="/slice/{{ slice }}#experiment">Measurements</a></li> -->
-       <li><a href="/slice/{{ slice }}#experiment" data-toggle="tab">Experiment</a></li>
+       <li><a href="/slice/{{ slice }}#experiment">Tools</a></li>
 </ul>
 {% else %}
 <ul class="nav nav-tabs nav-section">
-       <li class="active"><a href="#info"><img src="{{ STATIC_URL }}icons/slices-xs.png" alt="About MySlice" /> {{ slice }}</a></li>
+       <li class="active"><a href="#info">Information</a></li>
        <!-- <li class="testbeds"><a href="#testbeds">Testbeds</a></li> -->
        <li><a class="link" href="/resources/{{ slice }}">Resources</a></li>
        <li class="users"><a href="#users">Users</a></li>
        <!-- <li class="statistics"><a href="#experiment">Statistics</a></li> -->
        <!-- <li class="measurements"><a href="#experiment">Measurements</a></li> -->
-       <li class="experiment"><a href="#experiment" data-toggle="tab">Experiment</a></li>
+       <li class="experiment"><a href="#experiment">Tools</a></li>
 </ul>
+
 <script>
 $(document).ready(function() {
        $('.nav-tabs a').click(function (e) {
@@ -27,7 +40,16 @@ $(document).ready(function() {
        var id = $(this).attr('href').substr(1);        
        $("#" + id).load('/' + id + '/{{ slice }}/');
        });
-       $('div#info').load('/info/{{ slice }}/');
+       
+       var hash = window.location.hash;
+       if (hash) {
+               $('.nav-tabs a[href='+hash+']').click();
+       } else {
+               $('div#info').load('/info/{{ slice }}/');
+       }
 });
 </script>
-{% endif %}
\ No newline at end of file
+{% endif %}
+</div>
+</div>
+</div>
\ No newline at end of file
index e81121b..a4fae39 100644 (file)
@@ -6,7 +6,7 @@
                        <a href="/"><img src="{{ STATIC_URL }}img/onelab-portal.png" alt="OneLab Portal - Future Internet Testbeds" /></a>
                </div>
                {% if username %}
-               <div class="col-sm-5 col-md-5 navigation">
+               <div class="col-sm-4 col-md-4 navigation">
                        <ul>
                                <li>
                                        <a class="dropdown-toggle" data-toggle="dropdown" href="#">
                                        
                                        <div class="dropdown-menu" style="z-index:99;">
                                                        <ul id="dropdown-slice-list">
-                                                               <li><a href="/portal/slice_request/">Request Slice</a></li>
+                                                               <li class="title"><a href="/portal/slice_request/">Request Slice</a></li>
                                                        </ul>
                                        </div>
                                </li>
+                               {%if 'is_pi'  in pi %}  
                                <li id="nav-institution" class=""><a href="/portal/institution">MANAGEMENT</a></li>
-                               <li id="nav-request"><a href="/portal/validate">REQUESTS</a></li>
+                                {%endif%}
                                <li><a href="/portal/support/">SUPPORT</a></li>
-                               
-                               
-
                        </ul>
                </div>
                {% else %}
-               <div class="col-sm-5 col-md-5 navigation">
+               <div class="col-sm-4 col-md-4 navigation">
                </div>
                {% endif %}
-               <div class="col-sm-4 col-md-4 secondary">
+               <div class="col-sm-5 col-md-5 secondary">
                        <ul>
-                               
                                <li><a href="/news">News</a></li>
                                <li><a href="/portal/about">About</a></li>
-                               <li><a target="_blank" href="http://www.onelab.eu">Public Website</a></li>
+                               <li><a target="_blank" href="http://new.onelab.eu">Public Website</a></li>
                                <li><a target="_blank" href="http://intranet.onelab.eu">Intranet</a></li>
                        </ul>
                        {% if username %}
index 955193b..868f818 100644 (file)
        <h3>Secured Password reset wizard</h3>
 
        <p>Please enter your new password twice so we can verify you typed it in correctly.</p>
-       
-       <form action="" method="post">{% csrf_token %}
-       {{ form.new_password1.errors }}
-       <p class="aligned wide"><label for="id_new_password1">{% trans 'New password:' %}</label>{{ form.new_password1 }}</p>
-       {{ form.new_password2.errors }}
-       <p class="aligned wide"><label for="id_new_password2">{% trans 'Confirm password:' %}</label>{{ form.new_password2 }}</p>
-       <p><input type="submit" value="{% trans 'Change my password' %}" /></p>
+       </div>
+</div>
+<div class="row">
+       <div class="col-md-3">
+               <form action="" method="post">{% csrf_token %}
+               {{ form.new_password1.errors }}
+               <label for="id_new_password1">{% trans 'New password:' %}</label>
+       </div>
+       <div class="col-md-3">
+               {{ form.new_password1 }}
+       </div>
+</div>
+<div class="row">
+       <div class="col-md-3">
+               {{ form.new_password2.errors }}
+               <label for="id_new_password2">{% trans 'Confirm password:' %}</label>
+       </div>
+       <div class="col-md-3">  
+               {{ form.new_password2 }}
+       </div>
+</div>
+<div class="row">      
+       <div class="col-md-3 col-md-offset-3">
+               <button type="submit" class="btn btn-onelab">Change my password</button>
        </form>
+       </div>
+</div>
 
 {% else %}
-       
-       <h3>Onelab secured Password reset wizard</h3>
-
+<div class="row">
+       <div class="col-md-12">
+       <h1><img src="{{ STATIC_URL }}icons/user-xs.png" alt="Password reset" /> Password reset</h1>
+       </div>
+</div>
+<div class="row">
+       <div class="col-md-12">
        <p> The password reset link was invalid, possibly because it has already been used.  Please request a new <a href="/portal/pass_reset/">password reset.</a></p>
 
        <p>If you still encounter problem please <a href="/portal/contact/">Contact Support</a> for password recovery.</p> 
index 6198523..0e0de7e 100644 (file)
@@ -13,7 +13,7 @@
        
        <form action="/portal/pass_reset/" method="post">{% csrf_token %}
        {{ form.email.errors }}
-       <p><label for="id_email">Email address:</label> {{ form.email }} <input type="submit" value="Reset my password" /></p>
+       <p><label for="id_email">Email address:</label> {{ form.email }} <input type="submit" class="btn btn-onelab" value="Reset my password" /></p>
        </form>
        </div>
 </div>
index 3ab8efc..ab6776c 100644 (file)
@@ -3,11 +3,14 @@
 {% block content %}        
 
 <div class="row">
+       <div class="col-md-12">
        <h1><img src="{{ STATIC_URL }}icons/user-xs.png" alt="User Registration" />User sign-up</h1>
+       </div>
 </div>
 <div class="row">
-               <p></p>
+       <div class="col-md-12">
                <p><strong>Questions? <a href="/portal/contact" >Contact us.</a></strong></p>
+  </div>
 </div>
 {% if errors %}
 <ul>
   {% endfor %}
 </ul>
 {% endif %}
-<div class="row"><div class="col-sm-12">
-<form class="cmxform form-horizontal" id="registrationForm" method="post" enctype="multipart/form-data" role="form">
-{% csrf_token %}
-       <div class="form-group">
-               <label for="authority_hrn" class="control-label">Organization</label>
-               <p></p>
-               <input id="authority_hrn" name="authority_hrn" class="form-control" style="width:530px" value="{{ authority_hrn }}" required>
-               <p class="help-block">Choose your organization (company/university) from the list that apears when you<br> click in the field above and start to type.
-                Use the arrow keys to scroll through the list;<br>type part of the name to narrow down the list. If it is not in the list, 
-               <a href="/portal/join">please add it.</a></p>
+<div class="row">
+       <div class="col-md-12">
+               
+                       <div class="form-group">
+                               <form class="cmxform form-horizontal" id="registrationForm" method="post" enctype="multipart/form-data" role="form">
+                       {% csrf_token %}
+                               <label for="authority_hrn" class="control-label">Organization</label>
+                               <p></p>
+                               <input id="authority_hrn" name="org_name" class="form-control" style="width:590px" value="{{ organization }}" 
+                               title="Choose your organization (company/university) from the list that apears when you click in the field and start to type.
+                                Use the arrow keys to scroll through the list; type part of the name to narrow down the list. If it is not in the list, 
+                               please request its addition by clicking the link below. We will send an email to the managers that we have on record for 
+                               your organization, asking them to validate your sign-up request." required/>
+                               <p></p>
+                               <p>Organization not listed? <a href="/portal/join">Request its addition now.</a></p>
+                       </div>
        </div>
+</div>
 
-       <div class="row">
-       <div class="col-md-4">
+<div class="row">
+       <div class="col-md-6">
        
            <div class="form-group">
                        <label for="firstname" class="control-label">Personal information</label>
                        <p></p>
-                       <input type="text" name="firstname" class="form-control" style="width:200px" minlength="2" value="{{ first_name }}" placeholder="First name" required />
+                       <input type="text" name="firstname" class="form-control" style="width:350px" minlength="2" value="{{ first_name }}" placeholder="First name" required />
            </div>
            <div class="form-group">
-                       <input type="text" name="lastname" size="25" class="form-control" style="width:200px" minlength="2" value="{{ last_name }}" placeholder="Last name" required />
+                       <input type="text" name="lastname" size="25" class="form-control" style="width:350px" minlength="2" value="{{ last_name }}" placeholder="Last name" required />
            </div>
                <div class="form-group">
-               <input type="email" name="email" size="25"  class="form-control" style="width:200px" value="{{ email }}" 
+               <input type="email" name="email" size="25"  class="form-control" style="width:350px" value="{{ email }}" 
                        title="Your e-mail address will be your identifier for logging in. We contact you to verify your account and then, occasionally, for important issues."
                        placeholder="Email" required/>
                </div>
       <div class="col-xs-6"><p class="form-hint">Enter your login</p></div>
     </div>
        -->
-       <div class="col-md-4">
+       <div class="col-md-6">
            <div class="form-group">
              <label for="password" class="control-label">Authentication</label>
                  <p></p>       
-             <input type="password"  id="password" name="password"   class="form-control" style="width:200px" minlength="4" value="{{ password }}" 
+             <input type="password"  id="password" name="password"   class="form-control" style="width:250px" minlength="4" value="{{ password }}" 
                        title="Your password allows you to log in to this portal."
                        placeholder="Password" required/>
            </div>
            <div class="form-group">
-                       <input type="password"  id="confirmpassword" name="confirmpassword" style="width:200px"  minlength="4" class="form-control" value="" 
+                       <input type="password"  id="confirmpassword" name="confirmpassword" style="width:250px"  minlength="4" class="form-control" value="" 
                                placeholder="Confirm password" required/>
            </div>
            <div class="form-group">
                <!--<label for="question" class="control-label">Keys</label> -->
-                       <select name="question" class="form-control" style="width:200px" id="key-policy" 
+                       <select name="question" class="form-control" style="width:350px" id="key-policy" 
                                title="Your public/private key pair allows you to access the testbeds." required>        
                                <option value="generate">Generate my keys for me (recommended)</option>
                                <option value="upload">Upload my public key (advanced users only)</option>
                        </div>
            </div>
        </div>
+</div>
+<div class="row">
+       <div class="col-md-12">
+               <div class="form-group">
+                       <p></p>
+                       <input type="checkbox" name="agreement" value="agreement" required/>&nbsp;&nbsp; I agree to the 
+                       <a href="#" data-toggle="modal" data-target="#myModal">terms and conditions.</a> 
+               </div>
        </div>
-
-       <div class="form-group">
-               <p></p>
-               <input type="checkbox" name="agreement" value="agreement" required/> I agree to the 
-               <button class="btn btn-default btn-xs" data-toggle="modal" data-target="#myModal">terms and conditions.</button> 
+</div>
+<div class="row">
+       <div class="col-md-12">
+               <div class="form-group" id="register">
+                       <p></p> 
+               <button class="submit btn btn-onelab" type="submit">Sign up</button>
+           </div>
+       </form> 
        </div>
+</div>
+       
+       
        <!-- Modal - columns selector -->
                <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
                        <div class="modal-dialog">
                                </div>
                                <div class="modal-body">
                                                <p align="left">
-                                       TERMS AND CONDITIONS
-                                       <br/>
                                        for OneLab Basic level service
                                        <br/>
                                        Version 0.6 of 20 May 2014
+                                               <br>
+                                               <a href="/portal/terms" target="_blank">[Printable format]</a>
                                                </p>
                                        <h1 align="left">1 Context</h1>
                                        <h2 align="left">1.1 OneLab</h2>
     Users who seek such guarantees are invited to consider a higher level of service.
 </p>
 <h2 align="left">
-    <a name="_Toc261537715">2.3 Limited</a>
-    liability
+    2.3 Limitedliability
 </h2>
 <p align="left">
     In no event shall the partners of the OneLab consortium be liable to any user for any consequential, incidental, punitive, or lost profit damages, or for
                        </div>
                </div>
 
-    <div class="form-group" id="register">
-               <p></p> 
-       <button class="submit btn btn-primary" type="submit">Sign up</button>
-    </div>
-</form>  
-</div>
+    
 <script>
 jQuery(document).ready(function(){
     var availableTags = [
     {% if authorities %}
         {% for authority in authorities %}
             {% if authority.name %}
-                {value:"{{ authority.authority_hrn }}",label:"{{authority.name}}"},
-            {% else %}
-                {value:"{{ authority.authority_hrn }}",label:"{{authority.authority_hrn}}"},
+                {value:"{{ authority.name }}",label:"{{authority.name}}"},
+                       // to show only full name
+           // {% else %}
+           //     {value:"{{ authority.authority_hrn }}",label:"{{authority.authority_hrn}}"},
             {% endif %}
         {% endfor %}    
     {% else %}
         {value:"",label:"No authority found !!!"}
     {% endif %}
     ];
+       // sorting the list
+       availableTags.sort(function(a,b){
+               var nameA=a.value.toLowerCase(), nameB=b.value.toLowerCase();
+               if (nameA < nameB) {
+               return -1;
+               }
+               if (nameA > nameB) {
+               return 1;
+               }
+       return 0;
+       }); 
+       // auto-complete the form
     jQuery( "#authority_hrn" ).autocomplete({
       source: availableTags,
       minLength: 0,
@@ -497,6 +528,8 @@ jQuery(document).ready(function(){
       }
       //select: function( event, ui ) {console.log(jQuery(this))}
     });
+       // for hover texts
+       jQuery('[title!=""]').qtip();
 });
 </script>
 {% endblock %}
index 838b9b4..830c7ee 100644 (file)
@@ -3,13 +3,12 @@
 
 {% block head %}
 {{ wizard.form.media }}
+<link rel="stylesheet" type="text/css" href="{{STATIC_URL}}/css/registration.css" />
 {% endblock %}
 
 {% block content %}
 
-<link rel="stylesheet" type="text/css" href="{{STATIC_URL}}/css/onelab.css" />
-<!-- xxx ideally only onelab.css but ... xxx -->
-<link rel="stylesheet" type="text/css" href="{{STATIC_URL}}/css/registration.css" />
+
 
 <div class="onelab-title well-lg">
   <h2>Request a Slice</h2>
@@ -25,7 +24,7 @@
       <div class="col-xs-6"> <p class="form-hint">{{ field.help_text }}</p> </div>
     </div>
     {% endfor %}
-    <button class="submit btn btn-default col-xs-12" type="submit">Request Slice</button>
+    <button class="submit btn btn-default" type="submit"><span class="glyphicon glyphicon-plus"></span> Request Slice</button>
   </fieldset>
   </form>
 </div>
index 062908d..a8a9ac9 100644 (file)
@@ -6,26 +6,26 @@
 <script src="{{ STATIC_URL }}js/onelab_slice-resource-view.js"></script>
 <script>
        //myslice.slice = "{{ slice }}";
+
+$(document).ready(function() {
+            $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
+        // find the plugin object inside the tab content referenced by the current tabs
+        $('.plugin', $($(e.target).attr('href'))).trigger('shown.bs.tab');
+        $('.plugin', $($(e.target).attr('href'))).trigger('show');
+            });
+});
 </script>
 {% endblock %}
 
 {% block content %}
+{% widget '_widget-slice-sections.html' %}
+<div class="container-fluid container-resource">
+       <div class="row">
        <div class="col-md-2">
-               
-               <div class="list-group-item list-resources">
-                       <span class="list-group-item-heading">View</span>
-                       <a class="list-group-item active" data-panel="resources" href="#">All</a>
-                       <a class="list-group-item" data-panel="reserved" href="#">Reserved</a>
-                       <a class="list-group-item" data-panel="pending" href="#">Pending <span class="badge" id="badge-pending" data-number="0"></span></a>
-               </div>
-               
                <!-- <div id="select-platform" class="list-group"></div> -->
                {{filter_testbeds}}
        </div>
        <div class="col-md-10" style="height:100%;">
-               <div class="row">
-                       {% widget '_widget-slice-sections.html' %}
-               </div>
                <!-- <div class="row slice-pending">
                        <ul class="nav nav-pills">
                                <li><a href="">Unreserved</a></li>
                        <div class="col-md-12"><p class="alert-success">{{ msg }}</p></div>
                        {% endif %}
                </div>
+       
                <div class="row">
-                       <ul class="nav nav-pills nav-resources">
-                         <li class="active"><a data-panel="resources" href="#">Table</a></li>
-                         <li id="GoogleMap"><a data-panel="map" href="#">Map</a></li>
-                         <li id="Scheduler"><a data-panel="scheduler-tab" href="#">Scheduler</a></li>
-                       </ul>
+                       <div class="col-md-4">
+                               {{ filter_status }}
+                       </div>
+                       <div class="col-md-2">
+                               {{ apply }}
+                       </div>
                </div>
+               <!--
+               <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">
+                       <div class="col-md-12">
+                       <ul class="nav nav-tabs">
+                         <li class="active"><a href="#resourcelist" role="tab" data-toggle="tab">Table</a></li>
+                         <li> <a href="#resourcemap" role="tab" data-toggle="tab">Map</a></li>
+                         <li> <a href="#resourcescheduler" role="tab" data-toggle="tab">Scheduler</a></li>
+                       </ul>
+                       </div>
+               </div>
+               
                <!-- Modal - columns selector -->
                <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
                        <div class="modal-dialog">
                        </div>
                </div>
                
-               <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>
-                {{list_resources}}
-                               <!-- <table cellpadding="0" cellspacing="0" border="0" class="table" id="objectList"></table> -->
-                       </div>
-                       <div id="reserved" class="panel" style="height:370px;display:none;">
-                <table width="80%">
-                    <tr><th width="50%" style="text-align:center;">resources</th><th width="50%" style="text-align:center;">leases</th></tr>
-                    <tr>
-                        <td style="text-align:center">{{list_reserved_resources}}</td>
-                        <td style="text-align:center">{{list_reserved_leases}}</td>
-                    </tr>
-                </table>
-                       </div>
-                       <div id="pending" class="panel" style="height:370px;display:none;">
-                {{pending_resources}}
-                       </div>
-                       <div id="sla_dialog" class="panel" style="height:370px;display:none;">
-                {{sla_dialog}}
-                       </div>
-                       <div id="map" class="panel" style="height:370px;display:none;">
-                {{map_resources}}
-                       </div>
-                       <div id="scheduler-tab" class="panel" style="height:370px;display:none;">
-                {{scheduler}}
+               
+               <div class="row">
+                       <div class="col-md-12">
+                       <div class="tab-content" style="height:100%;">
+                               <div class="tab-pane active" id="resourcelist">
+                                        <!-- Button trigger modal - columns selector -->
+                                       <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>
+                               <div class="tab-pane" id="resourcemap">
+                       {{map_resources}}
+                               </div>
+                               <div class="tab-pane" id="resourcescheduler">
+                       {{scheduler}}
+                               </div>
+       
+                               <!--
+                               <div id="reserved" class="tab-pane" style="height:370px;display:none;">
+                       <table width="80%">
+                           <tr><th width="50%" style="text-align:center;">resources</th><th width="50%" style="text-align:center;">leases</th></tr>
+                           <tr>
+                               <td style="text-align:center">{{list_reserved_resources}}</td>
+                               <td style="text-align:center">{{list_reserved_leases}}</td>
+                           </tr>
+                       </table>
+                               </div>
+                               <div id="pending" class="tab-pane" style="height:370px;display:none;">
+                       {{pending_resources}}
+                               </div>
+                               <div id="sla_dialog" class="tab-pane" style="height:370px;display:none;">
+                       {{sla_dialog}}
+                               </div>
+       -->
+       
                        </div>
                </div>
        </div>
+       </div>
+       </div>
+</div>
 {% endblock %}
index 6942861..f0cb9b0 100644 (file)
@@ -12,9 +12,7 @@
                        </select> -->
                  </li>
                  <li class="list-group-item">Filter: slice</li>
-                 <li class="list-group-item">...</li>
-                 <li class="list-group-item">...</li>
-                 <li class="list-group-item">...</li>
+                
                </ul>
        
        </div>
                </div>
                <div id="user-tab-loading"><img src="{{ STATIC_URL }}img/loading.gif" alt="Loading Useres" /></div>
                <div id="user-tab-loaded" style="display:none;">
-               <table id="user-tab">
+               <table id="user-tab" class="table">
                        <tr>
                        <th>+/-</th>
                        <th>Email</th>
-                       <th>user_hrn</th>
+                       <th>User hrn</th>
                        <th>Enabled</th>
                        </tr>
                </table>
index 18afa0f..417a8bd 100644 (file)
@@ -5,6 +5,7 @@
 <b>slice name      :</b> {{slice_name}}</br>
 <b>number of nodes :</b> {{number_of_nodes}}</br>
 <b>purpose         :</b> {{purpose}}</br>
+<b>organization           :</b> {{organization}}
 <b>email           :</b> {{email}}</br>
 <b>Portal url     :</b> {{current_site}}</br> 
 
index bf30214..6cd5a2d 100644 (file)
@@ -4,5 +4,6 @@ slice name      : {{slice_name}}
 number of nodes : {{number_of_nodes}}
 purpose         : {{purpose}}
 email           : {{email}}
+organization   : {{organization}}
 Portal url             : {{current_site}}
 
index 9bbc22b..880521b 100644 (file)
@@ -4,55 +4,51 @@
 {% block content %}
        <div class="row">
                <div class="col-md-12">
-               <h1><img src="{{ STATIC_URL }}icons/slices-xs.png" alt="Request a Slice" /> Request a new Slice</h1>
+                       <h1><img src="{{ STATIC_URL }}icons/slices-xs.png" alt="Request a Slice" /> Request a new slice</h1>
                </div>
        </div>
 
        {% if errors %}
-       <ul>
-         {% for error in errors %}
-         <li>{{ error }}</li>
-         {% endfor %}
-       </ul>
-       {% endif %}
        <div class="row">
-               <div class="col-md-12">&nbsp;</div>
+               <div class="col-md-12">
+               <ul>
+                 {% for error in errors %}
+                 <li>{{ error }}</li>
+                 {% endfor %}
+               </ul>
+               </div>
        </div>
+       {% endif %}
+       
        <div class="row">
-               <div class="col-md-2"></div>
-               <div class="col-md-8">
+               <div class="col-md-8 el">
                        <form role="form" method="post">
                        {% csrf_token %}
-                         <div class="form-group">
-                           <label for="email">Experimenter</label>
-                           <input type="email" class="form-control" id="email" value="{{ email }}" readonly="readonly">
+                         <div class="form-group" style="display:none">
+                           <input type="email" class="form-control" id="email" style="width:300px" value="{{ email }}" readonly="readonly">
                          </div>
                          <div class="form-group">
-                           <label for="slice_name">Slice Name</label>
-                           <input type="text" class="form-control" name="slice_name" id="slice_name" placeholder="Slice Name" required="required">
-                               <p class="help-block"></p>
+                           <input type="text" class="form-control" name="slice_name" id="slice_name" style="width:300px" placeholder="Slice name" 
+                               title="Please enter a name for your slice"required="required">
                          </div>
                          <div class="form-group">
-                           <label for="authority">Authority</label>
                                {%if 'is_pi'  in pi %}
-                               <input type="text" class="form-control" id="authority_hrn" name="authority_hrn" placeholder="Authority" required="required">
-                               <p class="help-block">An authority responsible for vetting your slice</p>
+                               <input type="text" class="form-control" id="authority_hrn" name="authority_hrn" style="width:300px" placeholder="Authority" 
+                               title="An authority responsible for vetting your slice" required="required">
                                {%else%}
-                           <input type="text" class="form-control" id="authority_hrn" name="authority_hrn" placeholder="Authority" required="required" readonly="readonly">
-                               <p class="help-block">An authority responsible for vetting your slice</p>
+                           <input type="text" class="form-control" id="authority_hrn" name="authority_hrn" placeholder="Authority" style="width:300px; display:none;" 
+                               title="An authority responsible for vetting your slice" required="required" readonly="readonly">
                                {%endif%}
                          </div>
                          <div class="form-group">
-                           <label for="number_of_nodes">Number of Nodes</label>
-                           <input type="number" class="form-control" name="number_of_nodes" id="number_of_nodes" placeholder="Number of Nodes">
-                               <p class="help-block">Number of nodes you expect to request (informative)</p>
+                           <input type="number" class="form-control" name="number_of_nodes" id="number_of_nodes" style="width:300px" placeholder="Number of nodes"
+                               title="Number of nodes you expect to request (informative)">
                          </div>
                          <div class="form-group">
-                           <label for="purpose">Experiment Purpose</label>
-                               <textarea id="purpose" name="purpose" class="form-control" rows="6" placeholder="Experiment Purpose" required="required">{{ purpose }}</textarea>
-                           <p class="help-block">Purpose of your experiment (informative)</p>
+                               <textarea id="purpose" name="purpose" class="form-control" rows="6" placeholder="Experiment purpose" style="width:300px" 
+                               title="Purpose of your experiment (informative)" required="required">{{ purpose }}</textarea>
                          </div>
-                         <button type="submit" class="btn btn-default">Request Slice</button>
+                         <button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> Request Slice</button>
                        </form>
        
                </div>
@@ -62,7 +58,7 @@
 jQuery(document).ready(function(){
        
        $("#authority_hrn").load("/rest/user/", {"fields" : ["parent_authority"], "filters": {"user_hrn": "{{ user_hrn }}"}}, function(data) {
-               var jsonData = JSON.parse(data)
+               var jsonData = JSON.parse(data);
         $(this).attr("value", jsonData[0]['parent_authority']);
     });
     var availableTags = [
@@ -81,7 +77,7 @@ jQuery(document).ready(function(){
     $( "#authority_hrn" ).autocomplete({
       source: availableTags,
       minLength: 0,
-      select: function( event, ui ) {console.log(jQuery(this))}
+      select: function( event, ui ) {console.log(jQuery(this));}
     });
 });
 </script>
index e9768b7..ff51376 100644 (file)
@@ -19,7 +19,7 @@
                        <h2>Report a Bug</h2>
                        <p>If you have found a bug or having difficulties accesing some features or found some anomalies, please report it using our ticketing system.</p>
                        <button id="ticketbtn" type="button" class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> Create Ticket</button>
-                       <h3>Unresolved Tickets</h3>
+<!--                   <h3>Unresolved Tickets</h3>
                        <table class="mytable table table-bordered table-hover">
                        <tr>
                            <th>Ticket No</th>
@@ -27,7 +27,7 @@
                                        <th>Description</th>
                            <th>Status</th>
                        </tr>
-                       <!-- <tr>
+                       <tr>
                            <td>1</td>
                                        <td>yasin.upmc@gmail.com</td>
                                        <td> Slice_request page is not working </td>
@@ -38,9 +38,9 @@
                                        <td>azerty@lip6.fr</td>
                                        <td>Unable to Register</td>
                                        <td>Unresolved</td>
-                               </tr> -->
+                               </tr> 
                
-                   </table>
+                   </table> -->
                </div>
          </div>
        <div class="tab-pane row" id="faq">
diff --git a/portal/templates/termsview.html b/portal/templates/termsview.html
new file mode 100644 (file)
index 0000000..d89e8fb
--- /dev/null
@@ -0,0 +1,395 @@
+{% extends "layout.html" %}
+
+{% block content %}
+<div class="row">
+       <div class="col-md-12">
+       <h1> Terms and conditions</h1>
+       <p align="left">
+    <a name="_Ref238698453"></a>
+    <a name="_Ref238699060"></a>
+    <a name="_Ref249598367"></a>
+    <a name="_Ref254443731"></a>
+    <a name="_Ref254443916"></a>
+    TERMS AND CONDITIONS
+    <br/>
+    for OneLab Basic level service
+    <br/>
+    Version 0.6 of 20 May 2014
+</p>
+<h1 align="left">Context</h1>
+<h2 align="left">
+    1.1 OneLab
+</h2>
+<p align="left">
+    OneLab is an experimental facility for testing new ideas and new technologies in the area of computer networking. It consists of a variety of types of
+    platforms, including:
+</p>
+<ul type="disc">
+    <li>
+        <strong>internet overlay testbeds</strong>
+        , testbeds that offer virtual machines distributed across locations in different countries, allowing users to deploy overlays on the internet;
+    </li>
+    <li>
+        <strong>wireless testbeds</strong>
+        , testbeds that consist of clusters of computers that are within Wi-Fi communication range of each other, either in an office environment or in an
+        isolated setting;
+    </li>
+    <li>
+        <strong>internet of things testbeds</strong>
+        , testbeds that consist of embedded computing nodes with sensor capabilities, communicating wirelessly in an isolated environment;
+    </li>
+    <li>
+        <strong>emulation testbeds,</strong>
+        computing clusters that offer virtual machines on servers that are interconnected by a high speed switch, enabling large scale network emulation.
+    </li>
+</ul>
+<p align="left">
+    This list of types of platforms is subject to change, and the current list, along with the identities of the specific platforms of each type, can be found
+    on the OneLab website (onelab.eu).
+</p>
+<p align="left">
+    Each platform has its own owners, and OneLab is the grouping of these platforms through a consortium of institutions. The OneLab consortium is coordinated
+    by UPMC Sorbonne Universités. It operates on a not-for-profit basis.
+</p>
+<p align="left">
+    Access to OneLab may also provide access to additional platforms that are not part of OneLab, due to a federation agreement between OneLab and the owners
+    of those platforms.
+</p>
+<h2 align="left">
+    1.2 Fee-free Basic level service
+</h2>
+<p align="left">
+    These terms and conditions define and apply to OneLab's Basic level service, which is available free of charge.
+</p>
+<p align="left">
+    Users who would like additional services are encouraged to contact support@onelab.eu. Some additional services require a written agreement, but are
+    otherwise free. Others require the payment of fees or in-kind contributions. (An example of an in-kind contribution is the hosting of a PlanetLab Europe
+    server node.)
+</p>
+<h2 align="left">
+    1.3 Managers and standard users
+</h2>
+<p align="left">
+    There are two classes of OneLab user: the manager and the standard user. OneLab grants access rights to managers, who, in turn, provide access rights to
+    standard users. Examples are: for a small enterprise, an executive may be the manager and the employees may be standard users; for a research team, a
+    senior scientist (faculty member or research scientist) may be a manager and doctoral students and other members of the team may be standard users; for a
+    university course, a professor may be a manager and the students may be standard users.
+</p>
+<h2 align="left">
+    1.4 These terms and conditions
+</h2>
+<p align="left">
+    Acceptance of these terms and conditions is a condition of obtaining OneLab Basic level user service. They are posted to the OneLab portal site
+    (portal.onelab.eu). They may be changed without other notice than the posting of a new version to the portal site.
+</p>
+<h1 align="left">
+    2 Services provided by OneLab
+</h1>
+<h2 align="left">
+    2.1 Access to the experimental facility
+</h2>
+<p align="left">
+    OneLab provides users with access to the platforms that make up the experimental facility. Each platform owner determines the specifics of this access (for
+    example, how many nodes are available to a user, what happens in case of oversubscription, etc.), with the proviso that Basic level service requires that
+    users be able to conduct meaningful experiments on every OneLab testbed.
+</p>
+<p align="left">
+    Basic level service may also provide access to platforms that are federated with OneLab, but such access depends upon the terms of the federation
+    agreements with those platforms, which may require that the user have a higher level of service in order to gain access. For example, Basic level service
+    provides access to PlanetLab Europe, a OneLab platform, without providing access to PlanetLab Central, a federated platform. Users wanting full access
+    across the global PlanetLab system should contact support@onelab.eu to arrange to enter into a PlanetLab Europe membership agreement.
+</p>
+<p align="left">
+    OneLab's role is to facilitate access to the platforms. Specifically, it provides each user with:
+</p>
+<ul>
+    <li align="left">
+        <strong>a single account,</strong>
+        the credentials for which can be used to access all of the OneLab testbeds;
+    </li>
+    <li align="left">
+        <strong>tools through which to access the testbeds</strong>
+        , including, notably, a web-based portal (portal.onelab.eu) that allows a user to see the resources available on each testbed and to reserve them,
+        along with a number of experiment control tools that a user can employ to deploy an experiment on those resources;
+    </li>
+    <li align="left">
+        <strong>support</strong>
+        , with documentation on how to use the tools, pointers to documentation for individual testbeds, and a helpdesk to respond to user questions.
+    </li>
+</ul>
+<p align="left">
+    Additional support, such as accompaniment through the design and deployment of experiments and the interpretation of their results, is available through
+    higher levels of service.
+</p>
+<h2 align="left">
+    2.2 Best effort, without guarantees
+</h2>
+<p align="left">
+    OneLab and the owners of the individual OneLab testbeds do their best to provide the services outlined here, with the understanding that Basic level
+    service offers no guarantees. Users should clearly understand the following limitations.
+</p>
+<ul type="disc">
+    <li>
+        <strong>Reliability:</strong>
+        OneLab does not provide any guarantees with respect to the reliability of the portal, of other tools, or of the individual nodes on platforms. These
+        may be taken down for maintenance, rebooted, or reinstalled at any time. Reinstallation implies that disks are wiped, meaning that users should not
+        consider a local disk to be a persistent form of storage.
+    </li>
+    <li>
+        <strong>Fitness:</strong>
+        OneLab does not guarantee that the platforms are suitable for the experiments that users intend to conduct. There may be limitations in the
+        technologies that are offered that prevent certain types of experiments from being carried out.
+    </li>
+    <li>
+        <strong>Privacy</strong>
+        : OneLab does not guarantee the privacy of traffic generated on the platforms (e.g., wireless signals, packets). Unless otherwise specified by an
+        individual platform owner, users should assume that traffic is monitored and logged. Such monitoring may be done intentionally, for example, to allow
+        platform administrators as well as other users to investigate abuse.
+    </li>
+</ul>
+<p align="left">
+    Users who seek such guarantees are invited to consider a higher level of service.
+</p>
+<h2 align="left">
+    2.3 Limitedliability
+</h2>
+<p align="left">
+    In no event shall the partners of the OneLab consortium be liable to any user for any consequential, incidental, punitive, or lost profit damages, or for
+    any damages arising out of loss of use or loss of data, to the extent that such damages arise out of the activities of OneLab consortium partners, or any
+    breach of the present terms and conditions, even if the consortium partner has been advised of the possibility of such damages.
+</p>
+<p align="left">
+    Nothing contained in these terms and conditions shall be deemed as creating any rights or liabilities in or for third parties who are not Basic level users
+    of OneLab.
+</p>
+<h1 align="left">
+    3 Acceptable use policy
+</h1>
+<h2 align="left">
+    3.1 Responsibilities of managers and standard users
+</h2>
+<p align="left">
+    OneLab creates and administers accounts for managers and delegates to managers the responsibility for creating and administering accounts for standard
+    users. Both managers and standard users are required to follow OneLab's acceptable use policy. In addition, managers are fully responsible for the
+    activities of the standard users whose accounts they create.
+</p>
+<p align="left">
+    A manager is expected to grant user access only an individual with whom he or she has a working relationship. In general, this means an individual who
+    works for the same institution as the manager, or, in the case of higher education and research, an individual who is a student at the university where the
+    manager works. Managers may also grant access to individuals from other institutions, provided that they are collaborating on a common project on OneLab.
+    If there is a doubt, a manager should refer the question to support@onelab.eu.
+</p>
+<h2 align="left">
+    3.2 Types of use
+</h2>
+<p align="left">
+    OneLab may be used by enterprise, by scientific researchers, and by educators.
+</p>
+<p align="left">
+    OneLab may be used for pre-commercial research and development. In keeping with OneLab's not-for-profit status, it may not be used to deploy services that
+    are designed to generate a commercial profit.
+</p>
+<p align="left">
+    Not-for-profit use of OneLab to deploy services that are designed to generate revenue requires prior approval through a written agreement, and thus may not
+    be carried out on a Basic level account. Interested users are invited to contact support@onelab.eu.
+</p>
+<p align="left">
+    OneLab may be used for scientific research.
+</p>
+<p align="left">
+    OneLab may be used to host lab exercises for university courses.
+</p>
+<p align="left">
+    Questions about other types of use should be addressed to support@onelab.eu.
+</p>
+<h2 align="left">
+    3.3 Applicable laws and regulations
+</h2>
+<p align="left">
+    OneLab is managed, and the portal is hosted, in France. Information regarding the countries in which individual testbeds are managed and hosted is
+    available from those testbeds. Users are responsible for being aware of the countries in which their experiments are deployed and for ensuring that their
+    use of OneLab fully conforms to the laws and regulations of those countries, as well as the laws and regulations of the country in which they themselves
+    are present when conducting their experiments.
+</p>
+<p align="left">
+    Above and beyond specific national laws, the activities email spamming, phishing through web services, and all types of Internet fraud are prohibited on
+    OneLab.
+</p>
+<h2>
+    3.4 Security and accounting mechanisms
+</h2>
+<p align="left">
+    Users are expected to respect the security and accounting mechanisms put in place by OneLab, its platforms, and federated platforms. For example, access to
+    PlanetLab Europe is designed to take place through the SSH cryptographically-secured connection protocol, which uses public/private key pair
+    authentication, and so users should not attempt to bypass this mechanism. As another example, OneLab's notion of a "slice" associates a set of resources
+    with the group of users who have reserved those resources, and users should not attempt to obscure the identities of participants in a slice.
+</p>
+<p align="left">
+    Hacking attempts against the OneLab portal and testbeds are not permitted. This includes "red team" (hacker test) experiments.
+</p>
+<h2>
+    3.5 Sharing of resources
+</h2>
+<p align="left">
+    OneLab is intended for ambitious experiments. Large numbers of resources and extended leases on resources may legitimately be granted in order to carry
+    these out. At the same time, OneLab and its testbeds are shared environments, and when there is contention for resources, limits must be imposed.
+</p>
+<p align="left">
+    Each OneLab platform sets its own policies for handling resource contention. As a general rule, users are encouraged to design their experiments to use
+    resources efficiently. In particular, spinning/busy-waiting techniques for extended periods of time are strongly discouraged. Some resource contention
+    policies (e.g., PlanetLab Europe's) terminate the jobs that are using the most resources in the case of contention.
+</p>
+<h2>
+    3.6 Internet-connected platforms
+</h2>
+<p align="left">
+    Some of OneLab's platforms allow experiments to take place on resources that have access to the public internet. These experiments can potentially generate
+    traffic to, and receive traffic from, any host or router in the internet.<a></a><a id="_anchor_1" href="#_msocom_1" name="_msoanchor_1">[LB1]</a>
+</p>
+<p align="left">
+    Furthermore, some internet-connected platforms (e.g., PlanetLab Europe) consist of servers that are hosted by a large number of member institutions.
+</p>
+<p align="left">
+    The accessibility of internet-connected platforms and the distributed hosting model of some of these platforms imply certain responsibilities on the part
+    of users, as detailed below.
+</p>
+<h3>
+    3.6.1 General guidance
+</h3>
+<p align="left">
+    A good litmus test when considering whether an experiment is appropriate for such internet-connected platforms is to ask what the network administrator at
+    one's own organisation would say about the experiment running locally. If the experiment disrupts local activity (e.g., uses more than its share of the
+    site's internet bandwidth) or triggers complaints from remote network administrators (e.g., performs systematic port scans), then it is not appropriate for
+    such internet-connected platforms.
+</p>
+<p align="left">
+    It is the responsibility of the user and the user's manager to ensure that an application that will run on an internet-connected platform is tested and
+    debugged in a controlled environment, to better understand its behaviour prior to deployment.
+</p>
+<h3>
+    3.6.2 Standards of network etiquette
+</h3>
+<p align="left">
+    Internet-connected platforms are designed to support experiments that generate unusual traffic, such as network measurements. However, it is expected that
+    all users adhere to widely accepted standards of network etiquette in an effort to minimise complaints from network administrators. Activities that have
+    been interpreted as worm and denial-of-service attacks in the past (and should be avoided) include sending SYN packets to port 80 on random machines,
+    probing random IP addresses, repeatedly pinging routers, overloading bottleneck links with measurement traffic, and probing a single target machine from
+    many nodes.
+</p>
+<p align="left">
+    For internet-connected platforms that have a distributed hosting model, each host institution will have its own acceptable use policy. Users should not
+    knowingly violate such local policies. Conflicts between local policies and OneLab's stated goal of supporting research into wide-area networks should be
+    brought to the attention of OneLab administrators at support@onelab.eu.
+</p>
+<h3>
+    3.6.3 Specific network usage rules
+</h3>
+<p align="left">
+    It is not allowed to use one or more nodes of an internet-connected platform to generate a high number of network flows or flood a site with high traffic
+    to the point of interfering with its normal operation. Use of congestion-controlled flows for large transfers is highly encouraged.
+</p>
+<p align="left">
+    It is not allowed to perform systematic or random port or address block scans from an internet-connected platform.
+</p>
+<p align="left">
+    For internet-connected platforms that use a distributed hosting model, it is not allowed to spoof or sniff traffic on a hosted server or on the network the
+    server belongs to.
+</p>
+<p align="left">
+    Access to a server on a distributed hosting platform may not be used to gain access to other servers or networked equipment that are not part of the
+    testbed.
+</p>
+<h2>
+    3.7 Wireless platforms
+</h2>
+<p align="left">
+    Wireless-connected platforms give users access to nodes that communicate via Wi-Fi and other wireless technologies. They may be capable of detecting
+    wireless activity in the neighbourhood of those nodes: traffic generated by other users of the platform or by individuals not associated with the platform.
+    In general, much of the traffic will be encrypted, with certain aspects (such as SSIDs) not encrypted, but it is also possible that there will be fully
+    unencrypted traffic. They may also be capable of generating wireless activity that reaches equipment outside of the testbed.
+</p>
+<p align="left">
+    Furthermore, some wireless-connected platforms may have built-in limitations to prevent them from generating signals at a strength that exceeds health and
+    safety regulations.
+</p>
+<p align="left">
+    These characteristics of wireless-connected platforms imply certain responsibilities on the part of users, as detailed below.
+</p>
+<h3>
+    3.7.1 Specific network usage rules
+</h3>
+<p align="left">
+    Experimenters may make no attempt to defeat the encryption of encrypted third-party traffic. Furthermore, experimenters must treat with utmost discretion
+    any unencrypted traffic. Limited metadata can be recorded for the bona fide purposes of an experiment, but under no case should third party communications
+    be recorded.
+</p>
+<p align="left">
+    No attempt may be made to reverse engineer traffic in order to learn the identities of the parties who have generated the traffic.
+</p>
+<p align="left">
+    Wireless-connected platforms may not be used to gain access to any network equipment that is not part of the testbed itself.
+</p>
+<p align="left">
+    It is not allowed to perform systematic or random scans of wireless networks that are not part of a wireless-connected platform. Similarly, it is not
+    allowed to spoof or sniff wireless traffic of the institution that hosts a wireless-connected platform or of other networks in the proximity.
+</p>
+<p align="left">
+    Care must be taken so that traffic on wireless-connected platforms does not interfere with the normal functioning of network equipment that is not part of
+    the testbed itself.
+</p>
+<p align="left">
+    No attempt may be made to defeat the mechanisms that limit signal strength on wireless-connected platforms.
+</p>
+<h2>
+    3.8 Handling suspected violations
+</h2>
+<p align="left">
+    Suspected violations of the OneLab acceptable use policy should be reported to support@onelab.eu.
+</p>
+<p align="left">
+    Upon notification or detection of a possible violation, OneLab management will attempt to understand if a violation has in fact occurred. To do so,
+    management will freely communicate with the users concerned, the operators of the platforms concerned, as well as any third parties that might be involved.
+    An example of a third party is a network operator who detects what they believe to be unauthorized traffic emanating from a OneLab platform.
+</p>
+<p align="left">
+    The priority is to resolve any real or apparent violations amicably. However, if OneLab management believes that a violation may have occurred, it can, at
+    its sole discretion, and without prior notice, apply any of the following measures:
+</p>
+<ul type="disc">
+    <li>
+        notification of the users of the concerned slice (set of resources);
+    </li>
+    <li>
+        disabling of the concerned slice;
+    </li>
+    <li>
+        disabling an individual user's account;
+    </li>
+    <li>
+        reporting of the user's activity to his/her manager;
+    </li>
+    <li>
+        disabling of the manager's account and all user accounts for which the manager is responsible;
+    </li>
+    <li>
+        disabling of all accounts associated with the user's institution.
+    </li>
+</ul>
+<p align="left">
+    In the case of suspected illegal activity, OneLab management might need, without prior notice, to notify the relevant authorities.
+</p>
+<div>
+    <div>
+        <div id="_com_1">
+        </div>
+    </div>
+</div> 
+</div>
+</div>
+<div class="row">
+       <div class="col-md-12">
+
+</div>
+</div>
+{% endblock %}
+
index fb48e00..2bd2f0a 100644 (file)
@@ -11,8 +11,7 @@
   <p>To gain full access, two steps are required:</p>
   <ul>
        <li>1. You confirm your signup request by clicking on the link in the email that we have sent to your email address.</li>
-       <li>2. A manager from your organization validates your request. Upon confirmation of your signup request,<br> 
-               we will send an email to the managers at your organization with a validation request.</li>
+       <li>2. A manager from your organization validates your request. (We have sent email to your organisation's registered managers.)</li>
   </ul>                
 </div>
 
index 4e332ff..c198fee 100644 (file)
@@ -4,8 +4,13 @@
 <br>
 <b>First name   :</b> {{first_name}}<br>
 <b>Last name    :</b> {{last_name}}<br>
+<b>Organization :</b> {{organization}}<br>
 <b>Authority hrn:</b> {{authority_hrn}}<br>
 <b>Public key   :</b> {{public_key}}<br>
 <b>Email        :</b> {{email}}<br>
 <b>User hrn     :</b> {{user_hrn}}<br>
 <b>Portal url  :</b> {{ current_site }}<br>
+<p></p>
+<p>You can validate the user <a href="http://{{current_site}}/portal/validate">here</a>.<p>
+<br>
+<p>Please note that the validation request will only become visible once the user has confirmed his/her email address.</p>
index cce80cb..17ce184 100644 (file)
@@ -2,8 +2,13 @@ NEW USER REQUEST
 
 First name   : {{first_name}}
 Last name    : {{last_name}} 
+Organization :{{organization}}
 Authority hrn: {{authority_hrn}}
 Public key   : {{public_key}}
 Email        : {{email}}
 User hrn     : {{user_hrn}}
 Portal url   : {{ current_site }}
+
+Please note that the validation request will only become visible once the user has confirmed his/her email address.
+
+
diff --git a/portal/termsview.py b/portal/termsview.py
new file mode 100644 (file)
index 0000000..1705369
--- /dev/null
@@ -0,0 +1,51 @@
+# this somehow is not used anymore - should it not be ?
+from django.core.context_processors import csrf
+from django.http import HttpResponseRedirect
+from django.contrib.auth import authenticate, login, logout
+from django.template import RequestContext
+from django.shortcuts import render_to_response
+from django.shortcuts import render
+
+from unfold.loginrequired import FreeAccessView
+
+from manifoldapi.manifoldresult import ManifoldResult
+from ui.topmenu import topmenu_items, the_user
+from myslice.configengine import ConfigEngine
+
+from myslice.theme import ThemeView
+
+class TermsView (FreeAccessView, ThemeView):
+    template_name = 'termsview.html'
+        
+    # expose this so we can mention the backend URL on the welcome page
+    def default_env (self):
+        return { 
+                 'MANIFOLD_URL':ConfigEngine().manifold_url(),
+                 }
+
+    def post (self,request):
+        env = self.default_env()
+        env['theme'] = self.theme
+        return render_to_response(self.template, env, context_instance=RequestContext(request))
+
+    def get (self, request, state=None):
+        env = self.default_env()
+
+        if request.user.is_authenticated(): 
+            env['person'] = self.request.user
+        else: 
+            env['person'] = None
+    
+        env['theme'] = self.theme
+        env['section'] = "About"
+
+        env['username']=the_user(request)
+        env['topmenu_items'] = topmenu_items(None, request)
+        if state: env['state'] = state
+        elif not env['username']: env['state'] = None
+        # use one or two columns for the layout - not logged in users will see the login prompt
+        env['layout_1_or_2']="layout-unfold2.html" if not env['username'] else "layout-unfold1.html"
+        
+        
+        return render_to_response(self.template, env, context_instance=RequestContext(request))
+
index 157c6dd..e6e63e6 100644 (file)
@@ -42,7 +42,7 @@ from portal.joinview                import JoinView
 from portal.sliceviewold            import SliceView
 from portal.validationview          import ValidatePendingView
 #from portal.experimentview         import ExperimentView
-
+from portal.termsview               import TermsView
 from portal.univbrisview            import UnivbrisView
 
 from portal.servicedirectory         import ServiceDirectoryView
@@ -93,6 +93,7 @@ urlpatterns = patterns('',
     #url(r'^pass_reset/?$', PassResetView.as_view(), name='pass_rest'),
     # Slice request
     url(r'^slice_request/?$', SliceRequestView.as_view(), name='slice_request'),
+    url(r'^terms/?$', TermsView.as_view(), name='terms'),
     # Validate pending requests
     url(r'^validate/?$', ValidatePendingView.as_view()),
     # http://stackoverflow.com/questions/2360179/django-urls-how-to-pass-a-list-of-items-via-clean-urls
index f8788d0..b26ba0f 100644 (file)
@@ -50,6 +50,7 @@ class ValidatePendingView(LoginRequiredAutoLogoutView, ThemeView):
     template_name = "validate_pending.html"
 
     def get_context_data(self, **kwargs):
+        pi = ""
         # We might have slices on different registries with different user accounts 
         # We note that this portal could be specific to a given registry, to which we register users, but i'm not sure that simplifies things
         # Different registries mean different identities, unless we identify via SFA HRN or have associated the user email to a single hrn
@@ -256,9 +257,11 @@ class ValidatePendingView(LoginRequiredAutoLogoutView, ThemeView):
 
                 if not auth_hrn in dest:
                     dest[auth_hrn] = []
-                dest[auth_hrn].append(request) 
+                dest[auth_hrn].append(request)
         
         context = super(ValidatePendingView, self).get_context_data(**kwargs)
+        print "testing"
+        print ctx_my_authorities
         context['my_authorities']   = ctx_my_authorities
         context['sub_authorities']   = ctx_sub_authorities
         context['delegation_authorities'] = ctx_delegation_authorities
@@ -270,7 +273,7 @@ class ValidatePendingView(LoginRequiredAutoLogoutView, ThemeView):
         context['topmenu_items'] = topmenu_items_live('Validation', page) 
         # so we can sho who is logged
         context['username'] = the_user(self.request) 
-        
+        context['pi'] = "is_pi"       
         context['theme'] = self.theme
         context['section'] = "Requests"
         # XXX We need to prepare the page for queries
similarity index 100%
rename from portal/views.py
rename to portal/views/__init__.py
index 07e121b..a63e0cc 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,6 @@ from distutils.core import setup
 
 # we don't have a final list so let's keep it simple for now
 packages= [ os.path.dirname(init) for init in (glob("*/__init__.py")+glob("*/*/__init__.py")) ]
-print packages
 
 # Avoid troubles : clean /usr/share/unfold/
 #shutil.rmtree('/usr/share/unfold/')
@@ -24,6 +23,24 @@ def javascript (dir):
 def stylesheets (dir):
     return glob( dir+"/*.css")
 
+# looks like data_files requires actual files and cannot cope with 
+# a whole subdir like we have for fonts
+# returns a list of tuples suitable to add to data_files
+from operator import add
+
+def scan_fonts (install_topdir, topdir, extensions):
+    def subdir_tuples (subdir, extensions):
+        return [ (install_topdir+subdir, glob (subdir+"/*.%s"%extension), ) 
+                 for extension in extensions 
+                 if glob(subdir+"/*.%s"%extension)
+             ]
+    def subdirs (topdir):
+        return [x[0] for x in os.walk(topdir)]
+    return reduce (add, [ subdir_tuples (subdir, extensions) for subdir in subdirs(topdir) ] )
+
+fonts_tuples = scan_fonts ('/usr/share/unfold/static/fonts', 
+                           'static/fonts',
+                           ('otf','eot','svg','ttf','woff'))
 
 setup(packages = packages,
       # xxx somehow this does not seem to show up in debian packaging
@@ -35,9 +52,8 @@ setup(packages = packages,
 # for portal/          
           ( '/usr/share/unfold/static/img/institutions', images ('static/img/institutions')),
           ( '/usr/share/unfold/static/img/testbeds', images ('static/img/testbeds')),
-          ( '/usr/share/unfold/static/fonts', glob ('static/fonts/*')),
           ( '/usr/share/unfold/templates', glob ('templates/*')),
           ( 'apache', [ 'apache/unfold.conf', 'apache/unfold-ssl.conf', 'apache/unfold.wsgi' ]),
           ( '/etc/unfold/trusted_roots', []),
           ( '/var/unfold', []),
-        ])
+        ] + fonts_tuples )
diff --git a/third-party/bootstrap-datepicker b/third-party/bootstrap-datepicker
new file mode 120000 (symlink)
index 0000000..86a0496
--- /dev/null
@@ -0,0 +1 @@
+bootstrap-datepicker-1/
\ No newline at end of file
diff --git a/third-party/bootstrap-datepicker-1/bootstrap-datepicker.js b/third-party/bootstrap-datepicker-1/bootstrap-datepicker.js
new file mode 100644 (file)
index 0000000..4766bba
--- /dev/null
@@ -0,0 +1,962 @@
+/* =========================================================
+ * bootstrap-datepicker.js
+ * http://www.eyecon.ro/bootstrap-datepicker
+ * =========================================================
+ * Copyright 2012 Stefan Petre
+ * Improvements by Andrew Rowls
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================= */
+
+!function( $ ) {
+
+       function UTCDate(){
+               return new Date(Date.UTC.apply(Date, arguments));
+       }
+       function UTCToday(){
+               var today = new Date();
+               return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
+       }
+
+       // Picker object
+
+       var Datepicker = function(element, options) {
+               var that = this;
+
+               this.element = $(element);
+               this.language = options.language||this.element.data('date-language')||"en";
+               this.language = this.language in dates ? this.language : "en";
+               this.isRTL = dates[this.language].rtl||false;
+               this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy');
+               this.isInline = false;
+               this.isInput = this.element.is('input');
+               this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
+               this.hasInput = this.component && this.element.find('input').length;
+               if(this.component && this.component.length === 0)
+                       this.component = false;
+
+               this._attachEvents();
+
+               this.forceParse = true;
+               if ('forceParse' in options) {
+                       this.forceParse = options.forceParse;
+               } else if ('dateForceParse' in this.element.data()) {
+                       this.forceParse = this.element.data('date-force-parse');
+               }
+                
+
+               this.picker = $(DPGlobal.template)
+                                                       .appendTo(this.isInline ? this.element : 'body')
+                                                       .on({
+                                                               click: $.proxy(this.click, this),
+                                                               mousedown: $.proxy(this.mousedown, this)
+                                                       });
+
+               if(this.isInline) {
+                       this.picker.addClass('datepicker-inline');
+               } else {
+                       this.picker.addClass('datepicker-dropdown dropdown-menu');
+               }
+               if (this.isRTL){
+                       this.picker.addClass('datepicker-rtl');
+                       this.picker.find('.prev i, .next i')
+                                               .toggleClass('icon-arrow-left icon-arrow-right');
+               }
+               $(document).on('mousedown', function (e) {
+                       // Clicked outside the datepicker, hide it
+                       if ($(e.target).closest('.datepicker').length === 0) {
+                               that.hide();
+                       }
+               });
+
+               this.autoclose = false;
+               if ('autoclose' in options) {
+                       this.autoclose = options.autoclose;
+               } else if ('dateAutoclose' in this.element.data()) {
+                       this.autoclose = this.element.data('date-autoclose');
+               }
+
+               this.keyboardNavigation = true;
+               if ('keyboardNavigation' in options) {
+                       this.keyboardNavigation = options.keyboardNavigation;
+               } else if ('dateKeyboardNavigation' in this.element.data()) {
+                       this.keyboardNavigation = this.element.data('date-keyboard-navigation');
+               }
+
+               this.viewMode = this.startViewMode = 0;
+               switch(options.startView || this.element.data('date-start-view')){
+                       case 2:
+                       case 'decade':
+                               this.viewMode = this.startViewMode = 2;
+                               break;
+                       case 1:
+                       case 'year':
+                               this.viewMode = this.startViewMode = 1;
+                               break;
+               }
+
+               this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false);
+               this.todayHighlight = (options.todayHighlight||this.element.data('date-today-highlight')||false);
+
+               this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7);
+               this.weekEnd = ((this.weekStart + 6) % 7);
+               this.startDate = -Infinity;
+               this.endDate = Infinity;
+               this.daysOfWeekDisabled = [];
+               this.setStartDate(options.startDate||this.element.data('date-startdate'));
+               this.setEndDate(options.endDate||this.element.data('date-enddate'));
+               this.setDaysOfWeekDisabled(options.daysOfWeekDisabled||this.element.data('date-days-of-week-disabled'));
+               this.fillDow();
+               this.fillMonths();
+               this.update();
+               this.showMode();
+
+               if(this.isInline) {
+                       this.show();
+               }
+       };
+
+       Datepicker.prototype = {
+               constructor: Datepicker,
+
+               _events: [],
+               _attachEvents: function(){
+                       this._detachEvents();
+                       if (this.isInput) { // single input
+                               this._events = [
+                                       [this.element, {
+                                               focus: $.proxy(this.show, this),
+                                               keyup: $.proxy(this.update, this),
+                                               keydown: $.proxy(this.keydown, this)
+                                       }]
+                               ];
+                       }
+                       else if (this.component && this.hasInput){ // component: input + button
+                               this._events = [
+                                       // For components that are not readonly, allow keyboard nav
+                                       [this.element.find('input'), {
+                                               focus: $.proxy(this.show, this),
+                                               keyup: $.proxy(this.update, this),
+                                               keydown: $.proxy(this.keydown, this)
+                                       }],
+                                       [this.component, {
+                                               click: $.proxy(this.show, this)
+                                       }]
+                               ];
+                       }
+                                               else if (this.element.is('div')) {  // inline datepicker
+                                                       this.isInline = true;
+                                               }
+                       else {
+                               this._events = [
+                                       [this.element, {
+                                               click: $.proxy(this.show, this)
+                                       }]
+                               ];
+                       }
+                       for (var i=0, el, ev; i<this._events.length; i++){
+                               el = this._events[i][0];
+                               ev = this._events[i][1];
+                               el.on(ev);
+                       }
+               },
+               _detachEvents: function(){
+                       for (var i=0, el, ev; i<this._events.length; i++){
+                               el = this._events[i][0];
+                               ev = this._events[i][1];
+                               el.off(ev);
+                       }
+                       this._events = [];
+               },
+
+               show: function(e) {
+                       this.picker.show();
+                       this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
+                       this.update();
+                       this.place();
+                       $(window).on('resize', $.proxy(this.place, this));
+                       if (e ) {
+                               e.stopPropagation();
+                               e.preventDefault();
+                       }
+                       this.element.trigger({
+                               type: 'show',
+                               date: this.date
+                       });
+               },
+
+               hide: function(e){
+                       if(this.isInline) return;
+                       this.picker.hide();
+                       $(window).off('resize', this.place);
+                       this.viewMode = this.startViewMode;
+                       this.showMode();
+                       if (!this.isInput) {
+                               $(document).off('mousedown', this.hide);
+                       }
+
+                       if (
+                               this.forceParse &&
+                               (
+                                       this.isInput && this.element.val() ||
+                                       this.hasInput && this.element.find('input').val()
+                               )
+                       )
+                               this.setValue();
+                       this.element.trigger({
+                               type: 'hide',
+                               date: this.date
+                       });
+               },
+
+               remove: function() {
+                       this._detachEvents();
+                       this.picker.remove();
+                       delete this.element.data().datepicker;
+               },
+
+               getDate: function() {
+                       var d = this.getUTCDate();
+                       return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
+               },
+
+               getUTCDate: function() {
+                       return this.date;
+               },
+
+               setDate: function(d) {
+                       this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
+               },
+
+               setUTCDate: function(d) {
+                       this.date = d;
+                       this.setValue();
+               },
+
+               setValue: function() {
+                       var formatted = this.getFormattedDate();
+                       if (!this.isInput) {
+                               if (this.component){
+                                       this.element.find('input').val(formatted);
+                               }
+                               this.element.data('date', formatted);
+                       } else {
+                               this.element.val(formatted);
+                       }
+               },
+
+               getFormattedDate: function(format) {
+                       if (format === undefined)
+                               format = this.format;
+                       return DPGlobal.formatDate(this.date, format, this.language);
+               },
+
+               setStartDate: function(startDate){
+                       this.startDate = startDate||-Infinity;
+                       if (this.startDate !== -Infinity) {
+                               this.startDate = DPGlobal.parseDate(this.startDate, this.format, this.language);
+                       }
+                       this.update();
+                       this.updateNavArrows();
+               },
+
+               setEndDate: function(endDate){
+                       this.endDate = endDate||Infinity;
+                       if (this.endDate !== Infinity) {
+                               this.endDate = DPGlobal.parseDate(this.endDate, this.format, this.language);
+                       }
+                       this.update();
+                       this.updateNavArrows();
+               },
+
+               setDaysOfWeekDisabled: function(daysOfWeekDisabled){
+                       this.daysOfWeekDisabled = daysOfWeekDisabled||[];
+                       if (!$.isArray(this.daysOfWeekDisabled)) {
+                               this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
+                       }
+                       this.daysOfWeekDisabled = $.map(this.daysOfWeekDisabled, function (d) {
+                               return parseInt(d, 10);
+                       });
+                       this.update();
+                       this.updateNavArrows();
+               },
+
+               place: function(){
+                                               if(this.isInline) return;
+                       var zIndex = parseInt(this.element.parents().filter(function() {
+                                                       return $(this).css('z-index') != 'auto';
+                                               }).first().css('z-index'))+10;
+                       var offset = this.component ? this.component.offset() : this.element.offset();
+                       var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(true);
+                       this.picker.css({
+                               top: offset.top + height,
+                               left: offset.left,
+                               zIndex: zIndex
+                       });
+               },
+
+               update: function(){
+                       var date, fromArgs = false;
+                       if(arguments && arguments.length && (typeof arguments[0] === 'string' || arguments[0] instanceof Date)) {
+                               date = arguments[0];
+                               fromArgs = true;
+                       } else {
+                               date = this.isInput ? this.element.val() : this.element.data('date') || this.element.find('input').val();
+                       }
+
+                       this.date = DPGlobal.parseDate(date, this.format, this.language);
+
+                       if(fromArgs) this.setValue();
+
+                       var oldViewDate = this.viewDate;
+                       if (this.date < this.startDate) {
+                               this.viewDate = new Date(this.startDate);
+                       } else if (this.date > this.endDate) {
+                               this.viewDate = new Date(this.endDate);
+                       } else {
+                               this.viewDate = new Date(this.date);
+                       }
+
+                       if (oldViewDate && oldViewDate.getTime() != this.viewDate.getTime()){
+                               this.element.trigger({
+                                       type: 'changeDate',
+                                       date: this.viewDate
+                               });
+                       }
+                       this.fill();
+               },
+
+               fillDow: function(){
+                       var dowCnt = this.weekStart,
+                       html = '<tr>';
+                       while (dowCnt < this.weekStart + 7) {
+                               html += '<th class="dow">'+dates[this.language].daysMin[(dowCnt++)%7]+'</th>';
+                       }
+                       html += '</tr>';
+                       this.picker.find('.datepicker-days thead').append(html);
+               },
+
+               fillMonths: function(){
+                       var html = '',
+                       i = 0;
+                       while (i < 12) {
+                               html += '<span class="month">'+dates[this.language].monthsShort[i++]+'</span>';
+                       }
+                       this.picker.find('.datepicker-months td').html(html);
+               },
+
+               fill: function() {
+                       var d = new Date(this.viewDate),
+                               year = d.getUTCFullYear(),
+                               month = d.getUTCMonth(),
+                               startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
+                               startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
+                               endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
+                               endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
+                               currentDate = this.date && this.date.valueOf(),
+                               today = new Date();
+                       this.picker.find('.datepicker-days thead th:eq(1)')
+                                               .text(dates[this.language].months[month]+' '+year);
+                       this.picker.find('tfoot th.today')
+                                               .text(dates[this.language].today)
+                                               .toggle(this.todayBtn !== false);
+                       this.updateNavArrows();
+                       this.fillMonths();
+                       var prevMonth = UTCDate(year, month-1, 28,0,0,0,0),
+                               day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
+                       prevMonth.setUTCDate(day);
+                       prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
+                       var nextMonth = new Date(prevMonth);
+                       nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
+                       nextMonth = nextMonth.valueOf();
+                       var html = [];
+                       var clsName;
+                       while(prevMonth.valueOf() < nextMonth) {
+                               if (prevMonth.getUTCDay() == this.weekStart) {
+                                       html.push('<tr>');
+                               }
+                               clsName = '';
+                               if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
+                                       clsName += ' old';
+                               } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
+                                       clsName += ' new';
+                               }
+                               // Compare internal UTC date with local today, not UTC today
+                               if (this.todayHighlight &&
+                                       prevMonth.getUTCFullYear() == today.getFullYear() &&
+                                       prevMonth.getUTCMonth() == today.getMonth() &&
+                                       prevMonth.getUTCDate() == today.getDate()) {
+                                       clsName += ' today';
+                               }
+                               if (currentDate && prevMonth.valueOf() == currentDate) {
+                                       clsName += ' active';
+                               }
+                               if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
+                                       $.inArray(prevMonth.getUTCDay(), this.daysOfWeekDisabled) !== -1) {
+                                       clsName += ' disabled';
+                               }
+                               html.push('<td class="day'+clsName+'">'+prevMonth.getUTCDate() + '</td>');
+                               if (prevMonth.getUTCDay() == this.weekEnd) {
+                                       html.push('</tr>');
+                               }
+                               prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
+                       }
+                       this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
+                       var currentYear = this.date && this.date.getUTCFullYear();
+
+                       var months = this.picker.find('.datepicker-months')
+                                               .find('th:eq(1)')
+                                                       .text(year)
+                                                       .end()
+                                               .find('span').removeClass('active');
+                       if (currentYear && currentYear == year) {
+                               months.eq(this.date.getUTCMonth()).addClass('active');
+                       }
+                       if (year < startYear || year > endYear) {
+                               months.addClass('disabled');
+                       }
+                       if (year == startYear) {
+                               months.slice(0, startMonth).addClass('disabled');
+                       }
+                       if (year == endYear) {
+                               months.slice(endMonth+1).addClass('disabled');
+                       }
+
+                       html = '';
+                       year = parseInt(year/10, 10) * 10;
+                       var yearCont = this.picker.find('.datepicker-years')
+                                                               .find('th:eq(1)')
+                                                                       .text(year + '-' + (year + 9))
+                                                                       .end()
+                                                               .find('td');
+                       year -= 1;
+                       for (var i = -1; i < 11; i++) {
+                               html += '<span class="year'+(i == -1 || i == 10 ? ' old' : '')+(currentYear == year ? ' active' : '')+(year < startYear || year > endYear ? ' disabled' : '')+'">'+year+'</span>';
+                               year += 1;
+                       }
+                       yearCont.html(html);
+               },
+
+               updateNavArrows: function() {
+                       var d = new Date(this.viewDate),
+                               year = d.getUTCFullYear(),
+                               month = d.getUTCMonth();
+                       switch (this.viewMode) {
+                               case 0:
+                                       if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
+                                               this.picker.find('.prev').css({visibility: 'hidden'});
+                                       } else {
+                                               this.picker.find('.prev').css({visibility: 'visible'});
+                                       }
+                                       if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
+                                               this.picker.find('.next').css({visibility: 'hidden'});
+                                       } else {
+                                               this.picker.find('.next').css({visibility: 'visible'});
+                                       }
+                                       break;
+                               case 1:
+                               case 2:
+                                       if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
+                                               this.picker.find('.prev').css({visibility: 'hidden'});
+                                       } else {
+                                               this.picker.find('.prev').css({visibility: 'visible'});
+                                       }
+                                       if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
+                                               this.picker.find('.next').css({visibility: 'hidden'});
+                                       } else {
+                                               this.picker.find('.next').css({visibility: 'visible'});
+                                       }
+                                       break;
+                       }
+               },
+
+               click: function(e) {
+                       e.stopPropagation();
+                       e.preventDefault();
+                       var target = $(e.target).closest('span, td, th');
+                       if (target.length == 1) {
+                               switch(target[0].nodeName.toLowerCase()) {
+                                       case 'th':
+                                               switch(target[0].className) {
+                                                       case 'switch':
+                                                               this.showMode(1);
+                                                               break;
+                                                       case 'prev':
+                                                       case 'next':
+                                                               var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1);
+                                                               switch(this.viewMode){
+                                                                       case 0:
+                                                                               this.viewDate = this.moveMonth(this.viewDate, dir);
+                                                                               break;
+                                                                       case 1:
+                                                                       case 2:
+                                                                               this.viewDate = this.moveYear(this.viewDate, dir);
+                                                                               break;
+                                                               }
+                                                               this.fill();
+                                                               break;
+                                                       case 'today':
+                                                               var date = new Date();
+                                                               date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
+
+                                                               this.showMode(-2);
+                                                               var which = this.todayBtn == 'linked' ? null : 'view';
+                                                               this._setDate(date, which);
+                                                               break;
+                                               }
+                                               break;
+                                       case 'span':
+                                               if (!target.is('.disabled')) {
+                                                       this.viewDate.setUTCDate(1);
+                                                       if (target.is('.month')) {
+                                                               var month = target.parent().find('span').index(target);
+                                                               this.viewDate.setUTCMonth(month);
+                                                               this.element.trigger({
+                                                                       type: 'changeMonth',
+                                                                       date: this.viewDate
+                                                               });
+                                                       } else {
+                                                               var year = parseInt(target.text(), 10)||0;
+                                                               this.viewDate.setUTCFullYear(year);
+                                                               this.element.trigger({
+                                                                       type: 'changeYear',
+                                                                       date: this.viewDate
+                                                               });
+                                                       }
+                                                       this.showMode(-1);
+                                                       this.fill();
+                                               }
+                                               break;
+                                       case 'td':
+                                               if (target.is('.day') && !target.is('.disabled')){
+                                                       var day = parseInt(target.text(), 10)||1;
+                                                       var year = this.viewDate.getUTCFullYear(),
+                                                               month = this.viewDate.getUTCMonth();
+                                                       if (target.is('.old')) {
+                                                               if (month === 0) {
+                                                                       month = 11;
+                                                                       year -= 1;
+                                                               } else {
+                                                                       month -= 1;
+                                                               }
+                                                       } else if (target.is('.new')) {
+                                                               if (month == 11) {
+                                                                       month = 0;
+                                                                       year += 1;
+                                                               } else {
+                                                                       month += 1;
+                                                               }
+                                                       }
+                                                       this._setDate(UTCDate(year, month, day,0,0,0,0));
+                                               }
+                                               break;
+                               }
+                       }
+               },
+
+               _setDate: function(date, which){
+                       if (!which || which == 'date')
+                               this.date = date;
+                       if (!which || which  == 'view')
+                               this.viewDate = date;
+                       this.fill();
+                       this.setValue();
+                       this.element.trigger({
+                               type: 'changeDate',
+                               date: this.date
+                       });
+                       var element;
+                       if (this.isInput) {
+                               element = this.element;
+                       } else if (this.component){
+                               element = this.element.find('input');
+                       }
+                       if (element) {
+                               element.change();
+                               if (this.autoclose && (!which || which == 'date')) {
+                                       this.hide();
+                               }
+                       }
+               },
+
+               moveMonth: function(date, dir){
+                       if (!dir) return date;
+                       var new_date = new Date(date.valueOf()),
+                               day = new_date.getUTCDate(),
+                               month = new_date.getUTCMonth(),
+                               mag = Math.abs(dir),
+                               new_month, test;
+                       dir = dir > 0 ? 1 : -1;
+                       if (mag == 1){
+                               test = dir == -1
+                                       // If going back one month, make sure month is not current month
+                                       // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
+                                       ? function(){ return new_date.getUTCMonth() == month; }
+                                       // If going forward one month, make sure month is as expected
+                                       // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
+                                       : function(){ return new_date.getUTCMonth() != new_month; };
+                               new_month = month + dir;
+                               new_date.setUTCMonth(new_month);
+                               // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
+                               if (new_month < 0 || new_month > 11)
+                                       new_month = (new_month + 12) % 12;
+                       } else {
+                               // For magnitudes >1, move one month at a time...
+                               for (var i=0; i<mag; i++)
+                                       // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
+                                       new_date = this.moveMonth(new_date, dir);
+                               // ...then reset the day, keeping it in the new month
+                               new_month = new_date.getUTCMonth();
+                               new_date.setUTCDate(day);
+                               test = function(){ return new_month != new_date.getUTCMonth(); };
+                       }
+                       // Common date-resetting loop -- if date is beyond end of month, make it
+                       // end of month
+                       while (test()){
+                               new_date.setUTCDate(--day);
+                               new_date.setUTCMonth(new_month);
+                       }
+                       return new_date;
+               },
+
+               moveYear: function(date, dir){
+                       return this.moveMonth(date, dir*12);
+               },
+
+               dateWithinRange: function(date){
+                       return date >= this.startDate && date <= this.endDate;
+               },
+
+               keydown: function(e){
+                       if (this.picker.is(':not(:visible)')){
+                               if (e.keyCode == 27) // allow escape to hide and re-show picker
+                                       this.show();
+                               return;
+                       }
+                       var dateChanged = false,
+                               dir, day, month,
+                               newDate, newViewDate;
+                       switch(e.keyCode){
+                               case 27: // escape
+                                       this.hide();
+                                       e.preventDefault();
+                                       break;
+                               case 37: // left
+                               case 39: // right
+                                       if (!this.keyboardNavigation) break;
+                                       dir = e.keyCode == 37 ? -1 : 1;
+                                       if (e.ctrlKey){
+                                               newDate = this.moveYear(this.date, dir);
+                                               newViewDate = this.moveYear(this.viewDate, dir);
+                                       } else if (e.shiftKey){
+                                               newDate = this.moveMonth(this.date, dir);
+                                               newViewDate = this.moveMonth(this.viewDate, dir);
+                                       } else {
+                                               newDate = new Date(this.date);
+                                               newDate.setUTCDate(this.date.getUTCDate() + dir);
+                                               newViewDate = new Date(this.viewDate);
+                                               newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
+                                       }
+                                       if (this.dateWithinRange(newDate)){
+                                               this.date = newDate;
+                                               this.viewDate = newViewDate;
+                                               this.setValue();
+                                               this.update();
+                                               e.preventDefault();
+                                               dateChanged = true;
+                                       }
+                                       break;
+                               case 38: // up
+                               case 40: // down
+                                       if (!this.keyboardNavigation) break;
+                                       dir = e.keyCode == 38 ? -1 : 1;
+                                       if (e.ctrlKey){
+                                               newDate = this.moveYear(this.date, dir);
+                                               newViewDate = this.moveYear(this.viewDate, dir);
+                                       } else if (e.shiftKey){
+                                               newDate = this.moveMonth(this.date, dir);
+                                               newViewDate = this.moveMonth(this.viewDate, dir);
+                                       } else {
+                                               newDate = new Date(this.date);
+                                               newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
+                                               newViewDate = new Date(this.viewDate);
+                                               newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
+                                       }
+                                       if (this.dateWithinRange(newDate)){
+                                               this.date = newDate;
+                                               this.viewDate = newViewDate;
+                                               this.setValue();
+                                               this.update();
+                                               e.preventDefault();
+                                               dateChanged = true;
+                                       }
+                                       break;
+                               case 13: // enter
+                                       this.hide();
+                                       e.preventDefault();
+                                       break;
+                               case 9: // tab
+                                       this.hide();
+                                       break;
+                       }
+                       if (dateChanged){
+                               this.element.trigger({
+                                       type: 'changeDate',
+                                       date: this.date
+                               });
+                               var element;
+                               if (this.isInput) {
+                                       element = this.element;
+                               } else if (this.component){
+                                       element = this.element.find('input');
+                               }
+                               if (element) {
+                                       element.change();
+                               }
+                       }
+               },
+
+               showMode: function(dir) {
+                       if (dir) {
+                               this.viewMode = Math.max(0, Math.min(2, this.viewMode + dir));
+                       }
+                       /*
+                               vitalets: fixing bug of very special conditions:
+                               jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover.
+                               Method show() does not set display css correctly and datepicker is not shown.
+                               Changed to .css('display', 'block') solve the problem.
+                               See https://github.com/vitalets/x-editable/issues/37
+
+                               In jquery 1.7.2+ everything works fine.
+                       */
+                       //this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
+                       this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block');
+                       this.updateNavArrows();
+               }
+       };
+
+       $.fn.datepicker = function ( option ) {
+               var args = Array.apply(null, arguments);
+               args.shift();
+               return this.each(function () {
+                       var $this = $(this),
+                               data = $this.data('datepicker'),
+                               options = typeof option == 'object' && option;
+                       if (!data) {
+                               $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options))));
+                       }
+                       if (typeof option == 'string' && typeof data[option] == 'function') {
+                               data[option].apply(data, args);
+                       }
+               });
+       };
+
+       $.fn.datepicker.defaults = {
+       };
+       $.fn.datepicker.Constructor = Datepicker;
+       var dates = $.fn.datepicker.dates = {
+               en: {
+                       days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
+                       daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
+                       daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
+                       months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+                       monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+                       today: "Today"
+               }
+       };
+
+       var DPGlobal = {
+               modes: [
+                       {
+                               clsName: 'days',
+                               navFnc: 'Month',
+                               navStep: 1
+                       },
+                       {
+                               clsName: 'months',
+                               navFnc: 'FullYear',
+                               navStep: 1
+                       },
+                       {
+                               clsName: 'years',
+                               navFnc: 'FullYear',
+                               navStep: 10
+               }],
+               isLeapYear: function (year) {
+                       return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
+               },
+               getDaysInMonth: function (year, month) {
+                       return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
+               },
+               validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
+               nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
+               parseFormat: function(format){
+                       // IE treats \0 as a string end in inputs (truncating the value),
+                       // so it's a bad format delimiter, anyway
+                       var separators = format.replace(this.validParts, '\0').split('\0'),
+                               parts = format.match(this.validParts);
+                       if (!separators || !separators.length || !parts || parts.length === 0){
+                               throw new Error("Invalid date format.");
+                       }
+                       return {separators: separators, parts: parts};
+               },
+               parseDate: function(date, format, language) {
+                       if (date instanceof Date) return date;
+                       if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) {
+                               var part_re = /([\-+]\d+)([dmwy])/,
+                                       parts = date.match(/([\-+]\d+)([dmwy])/g),
+                                       part, dir;
+                               date = new Date();
+                               for (var i=0; i<parts.length; i++) {
+                                       part = part_re.exec(parts[i]);
+                                       dir = parseInt(part[1]);
+                                       switch(part[2]){
+                                               case 'd':
+                                                       date.setUTCDate(date.getUTCDate() + dir);
+                                                       break;
+                                               case 'm':
+                                                       date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir);
+                                                       break;
+                                               case 'w':
+                                                       date.setUTCDate(date.getUTCDate() + dir * 7);
+                                                       break;
+                                               case 'y':
+                                                       date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir);
+                                                       break;
+                                       }
+                               }
+                               return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
+                       }
+                       var parts = date && date.match(this.nonpunctuation) || [],
+                               date = new Date(),
+                               parsed = {},
+                               setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
+                               setters_map = {
+                                       yyyy: function(d,v){ return d.setUTCFullYear(v); },
+                                       yy: function(d,v){ return d.setUTCFullYear(2000+v); },
+                                       m: function(d,v){
+                                               v -= 1;
+                                               while (v<0) v += 12;
+                                               v %= 12;
+                                               d.setUTCMonth(v);
+                                               while (d.getUTCMonth() != v)
+                                                       d.setUTCDate(d.getUTCDate()-1);
+                                               return d;
+                                       },
+                                       d: function(d,v){ return d.setUTCDate(v); }
+                               },
+                               val, filtered, part;
+                       setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
+                       setters_map['dd'] = setters_map['d'];
+                       date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
+                       var fparts = format.parts.slice();
+                       // Remove noop parts
+                       if (parts.length != fparts.length) {
+                               fparts = $(fparts).filter(function(i,p){
+                                       return $.inArray(p, setters_order) !== -1;
+                               }).toArray();
+                       }
+                       // Process remainder
+                       if (parts.length == fparts.length) {
+                               for (var i=0, cnt = fparts.length; i < cnt; i++) {
+                                       val = parseInt(parts[i], 10);
+                                       part = fparts[i];
+                                       if (isNaN(val)) {
+                                               switch(part) {
+                                                       case 'MM':
+                                                               filtered = $(dates[language].months).filter(function(){
+                                                                       var m = this.slice(0, parts[i].length),
+                                                                               p = parts[i].slice(0, m.length);
+                                                                       return m == p;
+                                                               });
+                                                               val = $.inArray(filtered[0], dates[language].months) + 1;
+                                                               break;
+                                                       case 'M':
+                                                               filtered = $(dates[language].monthsShort).filter(function(){
+                                                                       var m = this.slice(0, parts[i].length),
+                                                                               p = parts[i].slice(0, m.length);
+                                                                       return m == p;
+                                                               });
+                                                               val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
+                                                               break;
+                                               }
+                                       }
+                                       parsed[part] = val;
+                               }
+                               for (var i=0, s; i<setters_order.length; i++){
+                                       s = setters_order[i];
+                                       if (s in parsed && !isNaN(parsed[s]))
+                                               setters_map[s](date, parsed[s]);
+                               }
+                       }
+                       return date;
+               },
+               formatDate: function(date, format, language){
+                       var val = {
+                               d: date.getUTCDate(),
+                               D: dates[language].daysShort[date.getUTCDay()],
+                               DD: dates[language].days[date.getUTCDay()],
+                               m: date.getUTCMonth() + 1,
+                               M: dates[language].monthsShort[date.getUTCMonth()],
+                               MM: dates[language].months[date.getUTCMonth()],
+                               yy: date.getUTCFullYear().toString().substring(2),
+                               yyyy: date.getUTCFullYear()
+                       };
+                       val.dd = (val.d < 10 ? '0' : '') + val.d;
+                       val.mm = (val.m < 10 ? '0' : '') + val.m;
+                       var date = [],
+                               seps = $.extend([], format.separators);
+                       for (var i=0, cnt = format.parts.length; i < cnt; i++) {
+                               if (seps.length)
+                                       date.push(seps.shift());
+                               date.push(val[format.parts[i]]);
+                       }
+                       return date.join('');
+               },
+               headTemplate: '<thead>'+
+                                                       '<tr>'+
+                                                               '<th class="prev"><i class="icon-arrow-left"/></th>'+
+                                                               '<th colspan="5" class="switch"></th>'+
+                                                               '<th class="next"><i class="icon-arrow-right"/></th>'+
+                                                       '</tr>'+
+                                               '</thead>',
+               contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
+               footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr></tfoot>'
+       };
+       DPGlobal.template = '<div class="datepicker">'+
+                                                       '<div class="datepicker-days">'+
+                                                               '<table class=" table-condensed">'+
+                                                                       DPGlobal.headTemplate+
+                                                                       '<tbody></tbody>'+
+                                                                       DPGlobal.footTemplate+
+                                                               '</table>'+
+                                                       '</div>'+
+                                                       '<div class="datepicker-months">'+
+                                                               '<table class="table-condensed">'+
+                                                                       DPGlobal.headTemplate+
+                                                                       DPGlobal.contTemplate+
+                                                                       DPGlobal.footTemplate+
+                                                               '</table>'+
+                                                       '</div>'+
+                                                       '<div class="datepicker-years">'+
+                                                               '<table class="table-condensed">'+
+                                                                       DPGlobal.headTemplate+
+                                                                       DPGlobal.contTemplate+
+                                                                       DPGlobal.footTemplate+
+                                                               '</table>'+
+                                                       '</div>'+
+                                               '</div>';
+
+       $.fn.datepicker.DPGlobal = DPGlobal;
+
+}( window.jQuery );
diff --git a/third-party/bootstrap-datepicker-1/datepicker.css b/third-party/bootstrap-datepicker-1/datepicker.css
new file mode 100644 (file)
index 0000000..6f061df
--- /dev/null
@@ -0,0 +1,514 @@
+/*!
+ * Datepicker for Bootstrap
+ *
+ * Copyright 2012 Stefan Petre
+ * Improvements by Andrew Rowls
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+.datepicker {
+  padding: 4px;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+  direction: ltr;
+  /*.dow {
+               border-top: 1px solid #ddd !important;
+       }*/
+}
+.datepicker-inline {
+  width: 220px;
+}
+.datepicker.datepicker-rtl {
+  direction: rtl;
+}
+.datepicker.datepicker-rtl table tr td span {
+  float: right;
+}
+.datepicker-dropdown {
+  top: 0;
+  left: 0;
+}
+.datepicker-dropdown:before {
+  content: '';
+  display: inline-block;
+  border-left: 7px solid transparent;
+  border-right: 7px solid transparent;
+  border-bottom: 7px solid #ccc;
+  border-top: 0;
+  border-bottom-color: rgba(0, 0, 0, 0.2);
+  position: absolute;
+}
+.datepicker-dropdown:after {
+  content: '';
+  display: inline-block;
+  border-left: 6px solid transparent;
+  border-right: 6px solid transparent;
+  border-bottom: 6px solid #ffffff;
+  border-top: 0;
+  position: absolute;
+}
+.datepicker-dropdown.datepicker-orient-left:before {
+  left: 6px;
+}
+.datepicker-dropdown.datepicker-orient-left:after {
+  left: 7px;
+}
+.datepicker-dropdown.datepicker-orient-right:before {
+  right: 6px;
+}
+.datepicker-dropdown.datepicker-orient-right:after {
+  right: 7px;
+}
+.datepicker-dropdown.datepicker-orient-top:before {
+  top: -7px;
+}
+.datepicker-dropdown.datepicker-orient-top:after {
+  top: -6px;
+}
+.datepicker-dropdown.datepicker-orient-bottom:before {
+  bottom: -7px;
+  border-bottom: 0;
+  border-top: 7px solid #999;
+}
+.datepicker-dropdown.datepicker-orient-bottom:after {
+  bottom: -6px;
+  border-bottom: 0;
+  border-top: 6px solid #ffffff;
+}
+.datepicker > div {
+  display: none;
+}
+.datepicker.days div.datepicker-days {
+  display: block;
+}
+.datepicker.months div.datepicker-months {
+  display: block;
+}
+.datepicker.years div.datepicker-years {
+  display: block;
+}
+.datepicker table {
+  margin: 0;
+  -webkit-touch-callout: none;
+  -webkit-user-select: none;
+  -khtml-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+.datepicker td,
+.datepicker th {
+  text-align: center;
+  width: 20px;
+  height: 20px;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+  border: none;
+}
+.table-striped .datepicker table tr td,
+.table-striped .datepicker table tr th {
+  background-color: transparent;
+}
+.datepicker table tr td.day:hover,
+.datepicker table tr td.day.focused {
+  background: #eeeeee;
+  cursor: pointer;
+}
+.datepicker table tr td.old,
+.datepicker table tr td.new {
+  color: #999999;
+}
+.datepicker table tr td.disabled,
+.datepicker table tr td.disabled:hover {
+  background: none;
+  color: #999999;
+  cursor: default;
+}
+.datepicker table tr td.today,
+.datepicker table tr td.today:hover,
+.datepicker table tr td.today.disabled,
+.datepicker table tr td.today.disabled:hover {
+  background-color: #fde19a;
+  background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a);
+  background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
+  background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a);
+  background-image: -o-linear-gradient(top, #fdd49a, #fdf59a);
+  background-image: linear-gradient(top, #fdd49a, #fdf59a);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
+  border-color: #fdf59a #fdf59a #fbed50;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+  color: #000;
+}
+.datepicker table tr td.today:hover,
+.datepicker table tr td.today:hover:hover,
+.datepicker table tr td.today.disabled:hover,
+.datepicker table tr td.today.disabled:hover:hover,
+.datepicker table tr td.today:active,
+.datepicker table tr td.today:hover:active,
+.datepicker table tr td.today.disabled:active,
+.datepicker table tr td.today.disabled:hover:active,
+.datepicker table tr td.today.active,
+.datepicker table tr td.today:hover.active,
+.datepicker table tr td.today.disabled.active,
+.datepicker table tr td.today.disabled:hover.active,
+.datepicker table tr td.today.disabled,
+.datepicker table tr td.today:hover.disabled,
+.datepicker table tr td.today.disabled.disabled,
+.datepicker table tr td.today.disabled:hover.disabled,
+.datepicker table tr td.today[disabled],
+.datepicker table tr td.today:hover[disabled],
+.datepicker table tr td.today.disabled[disabled],
+.datepicker table tr td.today.disabled:hover[disabled] {
+  background-color: #fdf59a;
+}
+.datepicker table tr td.today:active,
+.datepicker table tr td.today:hover:active,
+.datepicker table tr td.today.disabled:active,
+.datepicker table tr td.today.disabled:hover:active,
+.datepicker table tr td.today.active,
+.datepicker table tr td.today:hover.active,
+.datepicker table tr td.today.disabled.active,
+.datepicker table tr td.today.disabled:hover.active {
+  background-color: #fbf069 \9;
+}
+.datepicker table tr td.today:hover:hover {
+  color: #000;
+}
+.datepicker table tr td.today.active:hover {
+  color: #fff;
+}
+.datepicker table tr td.range,
+.datepicker table tr td.range:hover,
+.datepicker table tr td.range.disabled,
+.datepicker table tr td.range.disabled:hover {
+  background: #eeeeee;
+  -webkit-border-radius: 0;
+  -moz-border-radius: 0;
+  border-radius: 0;
+}
+.datepicker table tr td.range.today,
+.datepicker table tr td.range.today:hover,
+.datepicker table tr td.range.today.disabled,
+.datepicker table tr td.range.today.disabled:hover {
+  background-color: #f3d17a;
+  background-image: -moz-linear-gradient(top, #f3c17a, #f3e97a);
+  background-image: -ms-linear-gradient(top, #f3c17a, #f3e97a);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a));
+  background-image: -webkit-linear-gradient(top, #f3c17a, #f3e97a);
+  background-image: -o-linear-gradient(top, #f3c17a, #f3e97a);
+  background-image: linear-gradient(top, #f3c17a, #f3e97a);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0);
+  border-color: #f3e97a #f3e97a #edde34;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+  -webkit-border-radius: 0;
+  -moz-border-radius: 0;
+  border-radius: 0;
+}
+.datepicker table tr td.range.today:hover,
+.datepicker table tr td.range.today:hover:hover,
+.datepicker table tr td.range.today.disabled:hover,
+.datepicker table tr td.range.today.disabled:hover:hover,
+.datepicker table tr td.range.today:active,
+.datepicker table tr td.range.today:hover:active,
+.datepicker table tr td.range.today.disabled:active,
+.datepicker table tr td.range.today.disabled:hover:active,
+.datepicker table tr td.range.today.active,
+.datepicker table tr td.range.today:hover.active,
+.datepicker table tr td.range.today.disabled.active,
+.datepicker table tr td.range.today.disabled:hover.active,
+.datepicker table tr td.range.today.disabled,
+.datepicker table tr td.range.today:hover.disabled,
+.datepicker table tr td.range.today.disabled.disabled,
+.datepicker table tr td.range.today.disabled:hover.disabled,
+.datepicker table tr td.range.today[disabled],
+.datepicker table tr td.range.today:hover[disabled],
+.datepicker table tr td.range.today.disabled[disabled],
+.datepicker table tr td.range.today.disabled:hover[disabled] {
+  background-color: #f3e97a;
+}
+.datepicker table tr td.range.today:active,
+.datepicker table tr td.range.today:hover:active,
+.datepicker table tr td.range.today.disabled:active,
+.datepicker table tr td.range.today.disabled:hover:active,
+.datepicker table tr td.range.today.active,
+.datepicker table tr td.range.today:hover.active,
+.datepicker table tr td.range.today.disabled.active,
+.datepicker table tr td.range.today.disabled:hover.active {
+  background-color: #efe24b \9;
+}
+.datepicker table tr td.selected,
+.datepicker table tr td.selected:hover,
+.datepicker table tr td.selected.disabled,
+.datepicker table tr td.selected.disabled:hover {
+  background-color: #9e9e9e;
+  background-image: -moz-linear-gradient(top, #b3b3b3, #808080);
+  background-image: -ms-linear-gradient(top, #b3b3b3, #808080);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080));
+  background-image: -webkit-linear-gradient(top, #b3b3b3, #808080);
+  background-image: -o-linear-gradient(top, #b3b3b3, #808080);
+  background-image: linear-gradient(top, #b3b3b3, #808080);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0);
+  border-color: #808080 #808080 #595959;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+  color: #fff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+.datepicker table tr td.selected:hover,
+.datepicker table tr td.selected:hover:hover,
+.datepicker table tr td.selected.disabled:hover,
+.datepicker table tr td.selected.disabled:hover:hover,
+.datepicker table tr td.selected:active,
+.datepicker table tr td.selected:hover:active,
+.datepicker table tr td.selected.disabled:active,
+.datepicker table tr td.selected.disabled:hover:active,
+.datepicker table tr td.selected.active,
+.datepicker table tr td.selected:hover.active,
+.datepicker table tr td.selected.disabled.active,
+.datepicker table tr td.selected.disabled:hover.active,
+.datepicker table tr td.selected.disabled,
+.datepicker table tr td.selected:hover.disabled,
+.datepicker table tr td.selected.disabled.disabled,
+.datepicker table tr td.selected.disabled:hover.disabled,
+.datepicker table tr td.selected[disabled],
+.datepicker table tr td.selected:hover[disabled],
+.datepicker table tr td.selected.disabled[disabled],
+.datepicker table tr td.selected.disabled:hover[disabled] {
+  background-color: #808080;
+}
+.datepicker table tr td.selected:active,
+.datepicker table tr td.selected:hover:active,
+.datepicker table tr td.selected.disabled:active,
+.datepicker table tr td.selected.disabled:hover:active,
+.datepicker table tr td.selected.active,
+.datepicker table tr td.selected:hover.active,
+.datepicker table tr td.selected.disabled.active,
+.datepicker table tr td.selected.disabled:hover.active {
+  background-color: #666666 \9;
+}
+.datepicker table tr td.active,
+.datepicker table tr td.active:hover,
+.datepicker table tr td.active.disabled,
+.datepicker table tr td.active.disabled:hover {
+  background-color: #006dcc;
+  background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
+  background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
+  background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
+  background-image: -o-linear-gradient(top, #0088cc, #0044cc);
+  background-image: linear-gradient(top, #0088cc, #0044cc);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
+  border-color: #0044cc #0044cc #002a80;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+  color: #fff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+.datepicker table tr td.active:hover,
+.datepicker table tr td.active:hover:hover,
+.datepicker table tr td.active.disabled:hover,
+.datepicker table tr td.active.disabled:hover:hover,
+.datepicker table tr td.active:active,
+.datepicker table tr td.active:hover:active,
+.datepicker table tr td.active.disabled:active,
+.datepicker table tr td.active.disabled:hover:active,
+.datepicker table tr td.active.active,
+.datepicker table tr td.active:hover.active,
+.datepicker table tr td.active.disabled.active,
+.datepicker table tr td.active.disabled:hover.active,
+.datepicker table tr td.active.disabled,
+.datepicker table tr td.active:hover.disabled,
+.datepicker table tr td.active.disabled.disabled,
+.datepicker table tr td.active.disabled:hover.disabled,
+.datepicker table tr td.active[disabled],
+.datepicker table tr td.active:hover[disabled],
+.datepicker table tr td.active.disabled[disabled],
+.datepicker table tr td.active.disabled:hover[disabled] {
+  background-color: #0044cc;
+}
+.datepicker table tr td.active:active,
+.datepicker table tr td.active:hover:active,
+.datepicker table tr td.active.disabled:active,
+.datepicker table tr td.active.disabled:hover:active,
+.datepicker table tr td.active.active,
+.datepicker table tr td.active:hover.active,
+.datepicker table tr td.active.disabled.active,
+.datepicker table tr td.active.disabled:hover.active {
+  background-color: #003399 \9;
+}
+.datepicker table tr td span {
+  display: block;
+  width: 23%;
+  height: 54px;
+  line-height: 54px;
+  float: left;
+  margin: 1%;
+  cursor: pointer;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+}
+.datepicker table tr td span:hover {
+  background: #eeeeee;
+}
+.datepicker table tr td span.disabled,
+.datepicker table tr td span.disabled:hover {
+  background: none;
+  color: #999999;
+  cursor: default;
+}
+.datepicker table tr td span.active,
+.datepicker table tr td span.active:hover,
+.datepicker table tr td span.active.disabled,
+.datepicker table tr td span.active.disabled:hover {
+  background-color: #006dcc;
+  background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
+  background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
+  background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
+  background-image: -o-linear-gradient(top, #0088cc, #0044cc);
+  background-image: linear-gradient(top, #0088cc, #0044cc);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
+  border-color: #0044cc #0044cc #002a80;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+  color: #fff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+.datepicker table tr td span.active:hover,
+.datepicker table tr td span.active:hover:hover,
+.datepicker table tr td span.active.disabled:hover,
+.datepicker table tr td span.active.disabled:hover:hover,
+.datepicker table tr td span.active:active,
+.datepicker table tr td span.active:hover:active,
+.datepicker table tr td span.active.disabled:active,
+.datepicker table tr td span.active.disabled:hover:active,
+.datepicker table tr td span.active.active,
+.datepicker table tr td span.active:hover.active,
+.datepicker table tr td span.active.disabled.active,
+.datepicker table tr td span.active.disabled:hover.active,
+.datepicker table tr td span.active.disabled,
+.datepicker table tr td span.active:hover.disabled,
+.datepicker table tr td span.active.disabled.disabled,
+.datepicker table tr td span.active.disabled:hover.disabled,
+.datepicker table tr td span.active[disabled],
+.datepicker table tr td span.active:hover[disabled],
+.datepicker table tr td span.active.disabled[disabled],
+.datepicker table tr td span.active.disabled:hover[disabled] {
+  background-color: #0044cc;
+}
+.datepicker table tr td span.active:active,
+.datepicker table tr td span.active:hover:active,
+.datepicker table tr td span.active.disabled:active,
+.datepicker table tr td span.active.disabled:hover:active,
+.datepicker table tr td span.active.active,
+.datepicker table tr td span.active:hover.active,
+.datepicker table tr td span.active.disabled.active,
+.datepicker table tr td span.active.disabled:hover.active {
+  background-color: #003399 \9;
+}
+.datepicker table tr td span.old,
+.datepicker table tr td span.new {
+  color: #999999;
+}
+.datepicker th.datepicker-switch {
+  width: 145px;
+}
+.datepicker thead tr:first-child th,
+.datepicker tfoot tr th {
+  cursor: pointer;
+}
+.datepicker thead tr:first-child th:hover,
+.datepicker tfoot tr th:hover {
+  background: #eeeeee;
+}
+.datepicker .cw {
+  font-size: 10px;
+  width: 12px;
+  padding: 0 2px 0 5px;
+  vertical-align: middle;
+}
+.datepicker thead tr:first-child th.cw {
+  cursor: default;
+  background-color: transparent;
+}
+.input-append.date .add-on i,
+.input-prepend.date .add-on i {
+  cursor: pointer;
+  width: 16px;
+  height: 16px;
+}
+.input-daterange input {
+  text-align: center;
+}
+.input-daterange input:first-child {
+  -webkit-border-radius: 3px 0 0 3px;
+  -moz-border-radius: 3px 0 0 3px;
+  border-radius: 3px 0 0 3px;
+}
+.input-daterange input:last-child {
+  -webkit-border-radius: 0 3px 3px 0;
+  -moz-border-radius: 0 3px 3px 0;
+  border-radius: 0 3px 3px 0;
+}
+.input-daterange .add-on {
+  display: inline-block;
+  width: auto;
+  min-width: 16px;
+  height: 20px;
+  padding: 4px 5px;
+  font-weight: normal;
+  line-height: 20px;
+  text-align: center;
+  text-shadow: 0 1px 0 #ffffff;
+  vertical-align: middle;
+  background-color: #eeeeee;
+  border: 1px solid #ccc;
+  margin-left: -5px;
+  margin-right: -5px;
+}
+.datepicker.dropdown-menu {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  z-index: 1000;
+  float: left;
+  display: none;
+  min-width: 160px;
+  list-style: none;
+  background-color: #ffffff;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0, 0, 0, 0.2);
+  -webkit-border-radius: 5px;
+  -moz-border-radius: 5px;
+  border-radius: 5px;
+  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+  -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+  -webkit-background-clip: padding-box;
+  -moz-background-clip: padding;
+  background-clip: padding-box;
+  *border-right-width: 2px;
+  *border-bottom-width: 2px;
+  color: #333333;
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 13px;
+  line-height: 20px;
+}
+.datepicker.dropdown-menu th,
+.datepicker.datepicker-inline th,
+.datepicker.dropdown-menu td,
+.datepicker.datepicker-inline td {
+  padding: 4px 5px;
+}
diff --git a/third-party/bootstrap-slider b/third-party/bootstrap-slider
new file mode 120000 (symlink)
index 0000000..cb49b67
--- /dev/null
@@ -0,0 +1 @@
+bootstrap-slider-1
\ No newline at end of file
diff --git a/third-party/bootstrap-slider-1/bootstrap-slider.js b/third-party/bootstrap-slider-1/bootstrap-slider.js
new file mode 100644 (file)
index 0000000..a101dc3
--- /dev/null
@@ -0,0 +1,776 @@
+/* =========================================================
+ * bootstrap-slider.js v3.0.0
+ * =========================================================
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================= */
+
+(function( $ ) {
+
+       var ErrorMsgs = {
+               formatInvalidInputErrorMsg : function(input) {
+                       return "Invalid input value '" + input + "' passed in";
+               },
+               callingContextNotSliderInstance : "Calling context element does not have instance of Slider bound to it. Check your code to make sure the JQuery object returned from the call to the slider() initializer is calling the method"
+       };
+
+       var Slider = function(element, options) {
+               var el = this.element = $(element).hide();
+               var origWidth =  $(element)[0].style.width;
+
+               var updateSlider = false;
+               var parent = this.element.parent();
+
+
+               if (parent.hasClass('slider') === true) {
+                       updateSlider = true;
+                       this.picker = parent;
+               } else {
+                       this.picker = $('<div class="slider">'+
+                                                               '<div class="slider-track">'+
+                                                                       '<div class="slider-selection"></div>'+
+                                                                       '<div class="slider-handle min-slider-handle"></div>'+
+                                                                       '<div class="slider-handle max-slider-handle"></div>'+
+                                                               '</div>'+
+                                                               '<div id="tooltip" class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'+
+                                                               '<div id="tooltip_min" class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'+
+                                                               '<div id="tooltip_max" class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'+
+                                                       '</div>')
+                                                               .insertBefore(this.element)
+                                                               .append(this.element);
+               }
+
+               this.id = this.element.data('slider-id')||options.id;
+               if (this.id) {
+                       this.picker[0].id = this.id;
+               }
+
+               if (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch) {
+                       this.touchCapable = true;
+               }
+
+               var tooltip = this.element.data('slider-tooltip')||options.tooltip;
+
+               this.tooltip = this.picker.find('#tooltip');
+               this.tooltipInner = this.tooltip.find('div.tooltip-inner');
+
+               this.tooltip_min = this.picker.find('#tooltip_min');
+               this.tooltipInner_min = this.tooltip_min.find('div.tooltip-inner');
+
+               this.tooltip_max = this.picker.find('#tooltip_max');
+               this.tooltipInner_max= this.tooltip_max.find('div.tooltip-inner');
+
+               if (updateSlider === true) {
+                       // Reset classes
+                       this.picker.removeClass('slider-horizontal');
+                       this.picker.removeClass('slider-vertical');
+                       this.tooltip.removeClass('hide');
+                       this.tooltip_min.removeClass('hide');
+                       this.tooltip_max.removeClass('hide');
+
+               }
+
+               this.orientation = this.element.data('slider-orientation')||options.orientation;
+               switch(this.orientation) {
+                       case 'vertical':
+                               this.picker.addClass('slider-vertical');
+                               this.stylePos = 'top';
+                               this.mousePos = 'pageY';
+                               this.sizePos = 'offsetHeight';
+                               this.tooltip.addClass('right')[0].style.left = '100%';
+                               this.tooltip_min.addClass('right')[0].style.left = '100%';
+                               this.tooltip_max.addClass('right')[0].style.left = '100%';
+                               break;
+                       default:
+                               this.picker
+                                       .addClass('slider-horizontal')
+                                       .css('width', origWidth);
+                               this.orientation = 'horizontal';
+                               this.stylePos = 'left';
+                               this.mousePos = 'pageX';
+                               this.sizePos = 'offsetWidth';
+                               this.tooltip.addClass('top')[0].style.top = -this.tooltip.outerHeight() - 14 + 'px';
+                               this.tooltip_min.addClass('top')[0].style.top = -this.tooltip_min.outerHeight() - 14 + 'px';
+                               this.tooltip_max.addClass('top')[0].style.top = -this.tooltip_max.outerHeight() - 14 + 'px';
+                               break;
+               }
+
+               var self = this;
+               $.each(['min',
+                               'max',
+                               'step',
+                               'precision',
+                               'value',
+                               'reversed',
+                               'handle'
+                       ], function(i, attr) {
+                               if (typeof el.data('slider-' + attr) !== 'undefined') {
+                                       self[attr] = el.data('slider-' + attr);
+                               } else if (typeof options[attr] !== 'undefined') {
+                                       self[attr] = options[attr];
+                               } else if (typeof el.prop(attr) !== 'undefined') {
+                                       self[attr] = el.prop(attr);
+                               } else {
+                                       self[attr] = 0; // to prevent empty string issues in calculations in IE
+                               }
+               });
+
+               if (this.value instanceof Array) {
+                       if (updateSlider && !this.range) {
+                               this.value = this.value[0];
+                       } else {
+                               this.range = true;
+                       }
+               } else if (this.range) {
+                       // User wants a range, but value is not an array
+                       this.value = [this.value, this.max];
+               }
+
+               this.selection = this.element.data('slider-selection')||options.selection;
+               this.selectionEl = this.picker.find('.slider-selection');
+               if (this.selection === 'none') {
+                       this.selectionEl.addClass('hide');
+               }
+
+               this.selectionElStyle = this.selectionEl[0].style;
+
+               this.handle1 = this.picker.find('.slider-handle:first');
+               this.handle1Stype = this.handle1[0].style;
+
+               this.handle2 = this.picker.find('.slider-handle:last');
+               this.handle2Stype = this.handle2[0].style;
+
+               if (updateSlider === true) {
+                       // Reset classes
+                       this.handle1.removeClass('round triangle');
+                       this.handle2.removeClass('round triangle hide');
+               }
+
+               var availableHandleModifiers = ['round', 'triangle', 'custom'];
+               if (availableHandleModifiers.indexOf(this.handle) !== -1){
+                       this.handle1.addClass(this.handle);
+                       this.handle2.addClass(this.handle);
+               }
+
+               this.offset = this.picker.offset();
+               this.size = this.picker[0][this.sizePos];
+               this.formater = options.formater;
+               
+               this.tooltip_separator = options.tooltip_separator;
+               this.tooltip_split = options.tooltip_split;
+
+               this.setValue(this.value);
+
+               this.handle1.on({
+                       keydown: $.proxy(this.keydown, this, 0)
+               });
+               this.handle2.on({
+                       keydown: $.proxy(this.keydown, this, 1)
+               });
+
+               if (this.touchCapable) {
+                       // Touch: Bind touch events:
+                       this.picker.on({
+                               touchstart: $.proxy(this.mousedown, this)
+                       });
+               }
+               // Bind mouse events:
+               this.picker.on({
+                       mousedown: $.proxy(this.mousedown, this)
+               });
+
+               if(tooltip === 'hide') {
+                       this.tooltip.addClass('hide');
+                       this.tooltip_min.addClass('hide');
+                       this.tooltip_max.addClass('hide');
+               } else if(tooltip === 'always') {
+                       this.showTooltip();
+                       this.alwaysShowTooltip = true;
+               } else {
+                       this.picker.on({
+                               mouseenter: $.proxy(this.showTooltip, this),
+                               mouseleave: $.proxy(this.hideTooltip, this)
+                       });
+                       this.handle1.on({
+                               focus: $.proxy(this.showTooltip, this),
+                               blur: $.proxy(this.hideTooltip, this)
+                       });
+                       this.handle2.on({
+                               focus: $.proxy(this.showTooltip, this),
+                               blur: $.proxy(this.hideTooltip, this)
+                       });
+               }
+
+               this.enabled = options.enabled &&
+                                               (this.element.data('slider-enabled') === undefined || this.element.data('slider-enabled') === true);
+               if(this.enabled) {
+                       this.enable();
+               } else {
+                       this.disable();
+               }
+               this.natural_arrow_keys = this.element.data('slider-natural_arrow_keys') || options.natural_arrow_keys;
+       };
+
+       Slider.prototype = {
+               constructor: Slider,
+
+               over: false,
+               inDrag: false,
+
+               showTooltip: function(){
+            if (this.tooltip_split === false ){
+                this.tooltip.addClass('in');
+            } else {
+                this.tooltip_min.addClass('in');
+                this.tooltip_max.addClass('in');
+            }
+
+                       this.over = true;
+               },
+
+               hideTooltip: function(){
+                       if (this.inDrag === false && this.alwaysShowTooltip !== true) {
+                               this.tooltip.removeClass('in');
+                               this.tooltip_min.removeClass('in');
+                               this.tooltip_max.removeClass('in');
+                       }
+                       this.over = false;
+               },
+
+               layout: function(){
+                       var positionPercentages;
+
+                       if(this.reversed) {
+                               positionPercentages = [ 100 - this.percentage[0], this.percentage[1] ];
+                       } else {
+                               positionPercentages = [ this.percentage[0], this.percentage[1] ];
+                       }
+
+                       this.handle1Stype[this.stylePos] = positionPercentages[0]+'%';
+                       this.handle2Stype[this.stylePos] = positionPercentages[1]+'%';
+
+                       if (this.orientation === 'vertical') {
+                               this.selectionElStyle.top = Math.min(positionPercentages[0], positionPercentages[1]) +'%';
+                               this.selectionElStyle.height = Math.abs(positionPercentages[0] - positionPercentages[1]) +'%';
+                       } else {
+                               this.selectionElStyle.left = Math.min(positionPercentages[0], positionPercentages[1]) +'%';
+                               this.selectionElStyle.width = Math.abs(positionPercentages[0] - positionPercentages[1]) +'%';
+
+                var offset_min = this.tooltip_min[0].getBoundingClientRect();
+                var offset_max = this.tooltip_max[0].getBoundingClientRect();
+
+                if (offset_min.right > offset_max.left) {
+                    this.tooltip_max.removeClass('top');
+                    this.tooltip_max.addClass('bottom')[0].style.top = 18 + 'px';
+                } else {
+                    this.tooltip_max.removeClass('bottom');
+                    this.tooltip_max.addClass('top')[0].style.top = -30 + 'px';
+                }
+                       }
+
+                       if (this.range) {
+                               this.tooltipInner.text(
+                                       this.formater(this.value[0]) + this.tooltip_separator + this.formater(this.value[1])
+                               );
+                               this.tooltip[0].style[this.stylePos] = (positionPercentages[1] + positionPercentages[0])/2 + '%';
+                               if (this.orientation === 'vertical') {
+                                       this.tooltip.css('margin-top', -this.tooltip.outerHeight() / 2 + 'px');
+                               } else {
+                                       this.tooltip.css('margin-left', -this.tooltip.outerWidth() / 2 + 'px');
+                               }
+                               
+                               if (this.orientation === 'vertical') {
+                                       this.tooltip.css('margin-top', -this.tooltip.outerHeight() / 2 + 'px');
+                               } else {
+                                       this.tooltip.css('margin-left', -this.tooltip.outerWidth() / 2 + 'px');
+                               }
+                               this.tooltipInner_min.text(
+                                       this.formater(this.value[0])
+                               );
+                               this.tooltipInner_max.text(
+                                       this.formater(this.value[1])
+                               );
+
+                               this.tooltip_min[0].style[this.stylePos] = positionPercentages[0] + '%';
+                               if (this.orientation === 'vertical') {
+                                       this.tooltip_min.css('margin-top', -this.tooltip_min.outerHeight() / 2 + 'px');
+                               } else {
+                                       this.tooltip_min.css('margin-left', -this.tooltip_min.outerWidth() / 2 + 'px');
+                               }
+                               this.tooltip_max[0].style[this.stylePos] = positionPercentages[1] + '%';
+                               if (this.orientation === 'vertical') {
+                                       this.tooltip_max.css('margin-top', -this.tooltip_max.outerHeight() / 2 + 'px');
+                               } else {
+                                       this.tooltip_max.css('margin-left', -this.tooltip_max.outerWidth() / 2 + 'px');
+                               }
+                       } else {
+                               this.tooltipInner.text(
+                                       this.formater(this.value[0])
+                               );
+                               this.tooltip[0].style[this.stylePos] = positionPercentages[0] + '%';
+                               if (this.orientation === 'vertical') {
+                                       this.tooltip.css('margin-top', -this.tooltip.outerHeight() / 2 + 'px');
+                               } else {
+                                       this.tooltip.css('margin-left', -this.tooltip.outerWidth() / 2 + 'px');
+                               }
+                       }
+               },
+
+               mousedown: function(ev) {
+                       if(!this.isEnabled()) {
+                               return false;
+                       }
+                       // Touch: Get the original event:
+                       if (this.touchCapable && ev.type === 'touchstart') {
+                               ev = ev.originalEvent;
+                       }
+
+                       this.triggerFocusOnHandle();
+
+                       this.offset = this.picker.offset();
+                       this.size = this.picker[0][this.sizePos];
+
+                       var percentage = this.getPercentage(ev);
+
+                       if (this.range) {
+                               var diff1 = Math.abs(this.percentage[0] - percentage);
+                               var diff2 = Math.abs(this.percentage[1] - percentage);
+                               this.dragged = (diff1 < diff2) ? 0 : 1;
+                       } else {
+                               this.dragged = 0;
+                       }
+
+                       this.percentage[this.dragged] = this.reversed ? 100 - percentage : percentage;
+                       this.layout();
+
+                       if (this.touchCapable) {
+                               // Touch: Bind touch events:
+                               $(document).on({
+                                       touchmove: $.proxy(this.mousemove, this),
+                                       touchend: $.proxy(this.mouseup, this)
+                               });
+                       }
+                       // Bind mouse events:
+                       $(document).on({
+                               mousemove: $.proxy(this.mousemove, this),
+                               mouseup: $.proxy(this.mouseup, this)
+                       });
+
+                       this.inDrag = true;
+                       var val = this.calculateValue();
+                       this.element.trigger({
+                                       type: 'slideStart',
+                                       value: val
+                               })
+                               .data('value', val)
+                               .prop('value', val);
+                       this.setValue(val);
+                       return true;
+               },
+
+               triggerFocusOnHandle: function(handleIdx) {
+                       if(handleIdx === 0) {
+                               this.handle1.focus();
+                       }
+                       if(handleIdx === 1) {
+                               this.handle2.focus();
+                       }
+               },
+
+               keydown: function(handleIdx, ev) {
+                       if(!this.isEnabled()) {
+                               return false;
+                       }
+
+                       var dir;
+                       switch (ev.which) {
+                               case 37: // left
+                               case 40: // down
+                                       dir = -1;
+                                       break;
+                               case 39: // right
+                               case 38: // up
+                                       dir = 1;
+                                       break;
+                       }
+                       if (!dir) {
+                               return;
+                       }
+
+                       // use natural arrow keys instead of from min to max
+                       if (this.natural_arrow_keys) {
+                               if ((this.orientation === 'vertical' && !this.reversed) || (this.orientation === 'horizontal' && this.reversed)) {
+                                       dir = dir * -1;
+                               }
+                       }
+
+                       var oneStepValuePercentageChange = dir * this.percentage[2];
+                       var percentage = this.percentage[handleIdx] + oneStepValuePercentageChange;
+
+                       if (percentage > 100) {
+                               percentage = 100;
+                       } else if (percentage < 0) {
+                               percentage = 0;
+                       }
+
+                       this.dragged = handleIdx;
+                       this.adjustPercentageForRangeSliders(percentage);
+                       this.percentage[this.dragged] = percentage;
+                       this.layout();
+
+                       var val = this.calculateValue();
+                       
+                       this.element.trigger({
+                                       type: 'slideStart',
+                                       value: val
+                               })
+                               .data('value', val)
+                               .prop('value', val);
+
+                       this.setValue(val, true);
+
+                       this.element
+                               .trigger({
+                                       type: 'slideStop',
+                                       value: val
+                               })
+                               .data('value', val)
+                               .prop('value', val);
+                       return false;
+               },
+
+               mousemove: function(ev) {
+                       if(!this.isEnabled()) {
+                               return false;
+                       }
+                       // Touch: Get the original event:
+                       if (this.touchCapable && ev.type === 'touchmove') {
+                               ev = ev.originalEvent;
+                       }
+
+                       var percentage = this.getPercentage(ev);
+                       this.adjustPercentageForRangeSliders(percentage);
+                       this.percentage[this.dragged] = this.reversed ? 100 - percentage : percentage;
+                       this.layout();
+
+                       var val = this.calculateValue();
+                       this.setValue(val, true);
+
+                       return false;
+               },
+               adjustPercentageForRangeSliders: function(percentage) {
+                       if (this.range) {
+                               if (this.dragged === 0 && this.percentage[1] < percentage) {
+                                       this.percentage[0] = this.percentage[1];
+                                       this.dragged = 1;
+                               } else if (this.dragged === 1 && this.percentage[0] > percentage) {
+                                       this.percentage[1] = this.percentage[0];
+                                       this.dragged = 0;
+                               }
+                       }
+               },
+
+               mouseup: function() {
+                       if(!this.isEnabled()) {
+                               return false;
+                       }
+                       if (this.touchCapable) {
+                               // Touch: Unbind touch event handlers:
+                               $(document).off({
+                                       touchmove: this.mousemove,
+                                       touchend: this.mouseup
+                               });
+                       }
+                       // Unbind mouse event handlers:
+                       $(document).off({
+                               mousemove: this.mousemove,
+                               mouseup: this.mouseup
+                       });
+
+                       this.inDrag = false;
+                       if (this.over === false) {
+                               this.hideTooltip();
+                       }
+                       var val = this.calculateValue();
+                       this.layout();
+                       this.element
+                               .data('value', val)
+                               .prop('value', val)
+                               .trigger({
+                                       type: 'slideStop',
+                                       value: val
+                               });
+                       return false;
+               },
+
+               calculateValue: function() {
+                       var val;
+                       if (this.range) {
+                               val = [this.min,this.max];
+                if (this.percentage[0] !== 0){
+                    val[0] = (Math.max(this.min, this.min + Math.round((this.diff * this.percentage[0]/100)/this.step)*this.step));
+                    val[0] = this.applyPrecision(val[0]);
+                }
+                if (this.percentage[1] !== 100){
+                    val[1] = (Math.min(this.max, this.min + Math.round((this.diff * this.percentage[1]/100)/this.step)*this.step));
+                    val[1] = this.applyPrecision(val[1]);
+                }
+                               this.value = val;
+                       } else {
+                               val = (this.min + Math.round((this.diff * this.percentage[0]/100)/this.step)*this.step);
+                               if (val < this.min) {
+                                       val = this.min;
+                               }
+                               else if (val > this.max) {
+                                       val = this.max;
+                               }
+                               val = parseFloat(val);
+                               val = this.applyPrecision(val);
+                               this.value = [val, this.value[1]];
+                       }
+                       return val;
+               },
+               applyPrecision: function(val) {
+                       var precision = this.precision || this.getNumDigitsAfterDecimalPlace(this.step);
+                       return this.applyToFixedAndParseFloat(val, precision);
+               },
+               /*
+                       Credits to Mike Samuel for the following method!
+                       Source: http://stackoverflow.com/questions/10454518/javascript-how-to-retrieve-the-number-of-decimals-of-a-string-number
+               */
+               getNumDigitsAfterDecimalPlace: function(num) {
+                       var match = (''+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
+                       if (!match) { return 0; }
+                       return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0));
+               },
+
+               applyToFixedAndParseFloat: function(num, toFixedInput) {
+                       var truncatedNum = num.toFixed(toFixedInput);
+                       return parseFloat(truncatedNum);
+               },
+
+               getPercentage: function(ev) {
+                       if (this.touchCapable && (ev.type === 'touchstart' || ev.type === 'touchmove')) {
+                               ev = ev.touches[0];
+                       }
+                       var percentage = (ev[this.mousePos] - this.offset[this.stylePos])*100/this.size;
+                       percentage = Math.round(percentage/this.percentage[2])*this.percentage[2];
+                       return Math.max(0, Math.min(100, percentage));
+               },
+
+               getValue: function() {
+                       if (this.range) {
+                               return this.value;
+                       }
+                       return this.value[0];
+               },
+
+               setValue: function(val, triggerSlideEvent) {
+                       if (!val) {
+                               val = 0;
+                       }
+                       this.value = this.validateInputValue(val);
+
+                       if (this.range) {
+                               this.value[0] = this.applyPrecision(this.value[0]);
+                               this.value[1] = this.applyPrecision(this.value[1]); 
+
+                               this.value[0] = Math.max(this.min, Math.min(this.max, this.value[0]));
+                               this.value[1] = Math.max(this.min, Math.min(this.max, this.value[1]));
+                       } else {
+                               this.value = this.applyPrecision(this.value);
+                               this.value = [ Math.max(this.min, Math.min(this.max, this.value))];
+                               this.handle2.addClass('hide');
+                               if (this.selection === 'after') {
+                                       this.value[1] = this.max;
+                               } else {
+                                       this.value[1] = this.min;
+                               }
+                       }
+
+                       this.diff = this.max - this.min;
+                       if (this.diff > 0) {
+                               this.percentage = [
+                                       (this.value[0] - this.min) * 100 / this.diff,
+                                       (this.value[1] - this.min) * 100 / this.diff,
+                                       this.step * 100 / this.diff
+                               ];
+                       } else {
+                               this.percentage = [0, 0, 100];
+                       }
+
+                       this.layout();
+
+
+                       if(triggerSlideEvent === true) {
+                               var slideEventValue = this.range ? this.value : this.value[0];
+                               this.element
+                                       .trigger({
+                                               'type': 'slide',
+                                               'value': slideEventValue
+                                       })
+                                       .data('value', slideEventValue)
+                                       .prop('value', slideEventValue);
+                       }
+               },
+
+               validateInputValue : function(val) {
+                       if(typeof val === 'number') {
+                               return val;
+                       } else if(val instanceof Array) {
+                               $.each(val, function(i, input) { if (typeof input !== 'number') { throw new Error( ErrorMsgs.formatInvalidInputErrorMsg(input) ); }});
+                               return val;
+                       } else {
+                               throw new Error( ErrorMsgs.formatInvalidInputErrorMsg(val) );
+                       }
+               },
+
+               destroy: function(){
+                       this.handle1.off();
+                       this.handle2.off();
+                       this.element.off().show().insertBefore(this.picker);
+                       this.picker.off().remove();
+                       $(this.element).removeData('slider');
+               },
+
+               disable: function() {
+                       this.enabled = false;
+                       this.handle1.removeAttr("tabindex");
+                       this.handle2.removeAttr("tabindex");
+                       this.picker.addClass('slider-disabled');
+                       this.element.trigger('slideDisabled');
+               },
+
+               enable: function() {
+                       this.enabled = true;
+                       this.handle1.attr("tabindex", 0);
+                       this.handle2.attr("tabindex", 0);
+                       this.picker.removeClass('slider-disabled');
+                       this.element.trigger('slideEnabled');
+               },
+
+               toggle: function() {
+                       if(this.enabled) {
+                               this.disable();
+                       } else {
+                               this.enable();
+                       }
+               },
+
+               isEnabled: function() {
+                       return this.enabled;
+               },
+
+               setAttribute: function(attribute, value) {
+                       this[attribute] = value;
+               },
+
+               getAttribute: function(attribute) {
+                       return this[attribute];
+               }
+
+       };
+
+       var publicMethods = {
+               getValue : Slider.prototype.getValue,
+               setValue : Slider.prototype.setValue,
+               setAttribute : Slider.prototype.setAttribute,
+               getAttribute : Slider.prototype.getAttribute,
+               destroy : Slider.prototype.destroy,
+               disable : Slider.prototype.disable,
+               enable : Slider.prototype.enable,
+               toggle : Slider.prototype.toggle,
+               isEnabled: Slider.prototype.isEnabled
+       };
+
+       $.fn.slider = function (option) {
+               if (typeof option === 'string' && option !== 'refresh') {
+                       var args = Array.prototype.slice.call(arguments, 1);
+                       return invokePublicMethod.call(this, option, args);
+               } else {
+                       return createNewSliderInstance.call(this, option);
+               }
+       };
+
+       function invokePublicMethod(methodName, args) {
+               if(publicMethods[methodName]) {
+                       var sliderObject = retrieveSliderObjectFromElement(this);
+                       var result = publicMethods[methodName].apply(sliderObject, args);
+
+                       if (typeof result === "undefined") {
+                               return $(this);
+                       } else {
+                               return result;
+                       }
+               } else {
+                       throw new Error("method '" + methodName + "()' does not exist for slider.");
+               }
+       }
+
+       function retrieveSliderObjectFromElement(element) {
+               var sliderObject = $(element).data('slider');
+               if(sliderObject && sliderObject instanceof Slider) {
+                       return sliderObject;
+               } else {
+                       throw new Error(ErrorMsgs.callingContextNotSliderInstance);
+               }
+       }
+
+       function createNewSliderInstance(opts) {
+               var $this = $(this);
+               $this.each(function() {
+                       var $this = $(this),
+                               slider = $this.data('slider'),
+                               options = typeof opts === 'object' && opts;
+
+                       // If slider already exists, use its attributes
+                       // as options so slider refreshes properly
+                       if (slider && !options) {
+                               options = {};
+
+                               $.each($.fn.slider.defaults, function(key) {
+                                       options[key] = slider[key];
+                               });
+                       }
+
+                       $this.data('slider', (new Slider(this, $.extend({}, $.fn.slider.defaults, options))));
+               });
+               return $this;
+       }
+
+       $.fn.slider.defaults = {
+               min: 0,
+               max: 10,
+               step: 1,
+               precision: 0,
+               orientation: 'horizontal',
+               value: 5,
+               range: false,
+               selection: 'before',
+               tooltip: 'show',
+               tooltip_separator: ':',
+               tooltip_split: false,
+               natural_arrow_keys: false,
+               handle: 'round',
+               reversed : false,
+               enabled: true,
+               formater: function(value) {
+                       return value;
+               }
+       };
+
+       $.fn.slider.Constructor = Slider;
+
+})( window.jQuery );
+
+/* vim: set noexpandtab tabstop=4 shiftwidth=4 autoindent: */
diff --git a/third-party/bootstrap-slider-1/slider.css b/third-party/bootstrap-slider-1/slider.css
new file mode 100644 (file)
index 0000000..b527aa8
--- /dev/null
@@ -0,0 +1,138 @@
+/*!
+ * Slider for Bootstrap
+ *
+ * Copyright 2012 Stefan Petre
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+.slider {
+  display: inline-block;
+  vertical-align: middle;
+  position: relative;
+}
+.slider.slider-horizontal {
+  width: 210px;
+  height: 20px;
+}
+.slider.slider-horizontal .slider-track {
+  height: 10px;
+  width: 100%;
+  margin-top: -5px;
+  top: 50%;
+  left: 0;
+}
+.slider.slider-horizontal .slider-selection {
+  height: 100%;
+  top: 0;
+  bottom: 0;
+}
+.slider.slider-horizontal .slider-handle {
+  margin-left: -10px;
+  margin-top: -5px;
+}
+.slider.slider-horizontal .slider-handle.triangle {
+  border-width: 0 10px 10px 10px;
+  width: 0;
+  height: 0;
+  border-bottom-color: #0480be;
+  margin-top: 0;
+}
+.slider.slider-vertical {
+  height: 210px;
+  width: 20px;
+}
+.slider.slider-vertical .slider-track {
+  width: 10px;
+  height: 100%;
+  margin-left: -5px;
+  left: 50%;
+  top: 0;
+}
+.slider.slider-vertical .slider-selection {
+  width: 100%;
+  left: 0;
+  top: 0;
+  bottom: 0;
+}
+.slider.slider-vertical .slider-handle {
+  margin-left: -5px;
+  margin-top: -10px;
+}
+.slider.slider-vertical .slider-handle.triangle {
+  border-width: 10px 0 10px 10px;
+  width: 1px;
+  height: 1px;
+  border-left-color: #0480be;
+  margin-left: 0;
+}
+.slider input {
+  display: none;
+}
+.slider .tooltip-inner {
+  white-space: nowrap;
+}
+.slider-track {
+  position: absolute;
+  cursor: pointer;
+  background-color: #f7f7f7;
+  background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
+  background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+  -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+}
+.slider-selection {
+  position: absolute;
+  background-color: #f7f7f7;
+  background-image: -moz-linear-gradient(top, #f9f9f9, #f5f5f5);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f9f9f9), to(#f5f5f5));
+  background-image: -webkit-linear-gradient(top, #f9f9f9, #f5f5f5);
+  background-image: -o-linear-gradient(top, #f9f9f9, #f5f5f5);
+  background-image: linear-gradient(to bottom, #f9f9f9, #f5f5f5);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0);
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+  -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+}
+.slider-handle {
+  position: absolute;
+  width: 20px;
+  height: 20px;
+  background-color: #0e90d2;
+  background-image: -moz-linear-gradient(top, #149bdf, #0480be);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
+  background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
+  background-image: -o-linear-gradient(top, #149bdf, #0480be);
+  background-image: linear-gradient(to bottom, #149bdf, #0480be);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+  -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+  box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+  opacity: 0.8;
+  border: 0px solid transparent;
+}
+.slider-handle.round {
+  -webkit-border-radius: 20px;
+  -moz-border-radius: 20px;
+  border-radius: 20px;
+}
+.slider-handle.triangle {
+  background: transparent none;
+}
\ No newline at end of file
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 151ab1c..2c2495c 100644 (file)
@@ -1,7 +1,7 @@
-<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.js" type="text/javascript"></script>
+<!--<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.js" type="text/javascript"></script>-->
 <!-- <script type="text/javascript">{{ STATIC_URL }}js/ui.widget.js</script> -->
-<script src="{{ STATIC_URL }}js/jquery.notify.js" type="text/javascript"></script>
-<link rel='stylesheet' href='{{ STATIC_URL }}css/ui.notify.css' type='text/css' />
+<!--<script src="{{ STATIC_URL }}js/jquery.notify.js" type="text/javascript"></script>-->
+<!--<link rel='stylesheet' href='{{ STATIC_URL }}css/ui.notify.css' type='text/css' />-->
 
 <script type="text/javascript">
 function create( template, vars, opts ){
@@ -13,7 +13,9 @@ $(function(){
        // the defaults will apply to any notification created within this
        // container, but can be overwritten on notification-by-notification
        // basis.
-       $container = $("#notifications").notify();
+
+       // XXX disabled since jquery ui conflicts with bootstrap!
+       //$container = $("#notifications").notify();
        
        // create two when the pg loads
        //create("default", { title:'Default Notification', text:'Example of a default notification.  I will fade out after 5 seconds'});
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);
+//})