From 411f48055d7053824d4708e4db2636470b7dc18f Mon Sep 17 00:00:00 2001 From: Chuck Hagenbuch Date: Sat, 2 Oct 2010 17:23:50 -0400 Subject: [PATCH] Remove glow library (only used in one place) --- horde/js/glow/1.5.1/core/core.debug.js | 10930 ------------------- horde/js/glow/1.5.1/core/core.js | 20 - .../1.5.1/widgets/images/carousel/darkdots.png | Bin 451 -> 0 bytes .../1.5.1/widgets/images/carousel/darkdotsv.png | Bin 391 -> 0 bytes .../glow/1.5.1/widgets/images/carousel/darkh.png | Bin 1307 -> 0 bytes .../glow/1.5.1/widgets/images/carousel/darkv.png | Bin 1202 -> 0 bytes .../1.5.1/widgets/images/carousel/lightdots.png | Bin 451 -> 0 bytes .../1.5.1/widgets/images/carousel/lightdotsv.png | Bin 391 -> 0 bytes .../glow/1.5.1/widgets/images/carousel/lighth.png | Bin 830 -> 0 bytes .../glow/1.5.1/widgets/images/carousel/lightv.png | Bin 725 -> 0 bytes .../js/glow/1.5.1/widgets/images/darkpanel/ab.png | Bin 574 -> 0 bytes .../js/glow/1.5.1/widgets/images/darkpanel/al.png | Bin 639 -> 0 bytes .../js/glow/1.5.1/widgets/images/darkpanel/ar.png | Bin 585 -> 0 bytes .../js/glow/1.5.1/widgets/images/darkpanel/at.png | Bin 532 -> 0 bytes .../js/glow/1.5.1/widgets/images/darkpanel/bg.gif | Bin 273 -> 0 bytes .../js/glow/1.5.1/widgets/images/darkpanel/cbl.png | Bin 172 -> 0 bytes .../js/glow/1.5.1/widgets/images/darkpanel/cbr.png | Bin 173 -> 0 bytes .../glow/1.5.1/widgets/images/darkpanel/close.png | Bin 186 -> 0 bytes .../js/glow/1.5.1/widgets/images/darkpanel/ctl.png | Bin 182 -> 0 bytes .../js/glow/1.5.1/widgets/images/darkpanel/ctr.png | Bin 175 -> 0 bytes .../1.5.1/widgets/images/darkpanel/transbg.png | Bin 80 -> 0 bytes .../widgets/images/editor/button-blockquote.png | Bin 1921 -> 0 bytes .../1.5.1/widgets/images/editor/button-bold.png | Bin 1818 -> 0 bytes .../1.5.1/widgets/images/editor/button-italics.png | Bin 1364 -> 0 bytes .../1.5.1/widgets/images/editor/button-strike.png | Bin 1596 -> 0 bytes .../images/editor/dark-button-blockquote.png | Bin 443 -> 0 bytes .../widgets/images/editor/dark-button-bold.png | Bin 452 -> 0 bytes .../widgets/images/editor/dark-button-italics.png | Bin 411 -> 0 bytes .../widgets/images/editor/dark-button-strike.png | Bin 845 -> 0 bytes .../images/editor/light-button-blockquote.png | Bin 1921 -> 0 bytes .../widgets/images/editor/light-button-bold.png | Bin 1818 -> 0 bytes .../widgets/images/editor/light-button-italics.png | Bin 1364 -> 0 bytes .../widgets/images/editor/light-button-strike.png | Bin 1596 -> 0 bytes .../js/glow/1.5.1/widgets/images/lightpanel/ab.png | Bin 584 -> 0 bytes .../js/glow/1.5.1/widgets/images/lightpanel/al.png | Bin 546 -> 0 bytes .../js/glow/1.5.1/widgets/images/lightpanel/ar.png | Bin 514 -> 0 bytes .../js/glow/1.5.1/widgets/images/lightpanel/at.png | Bin 522 -> 0 bytes .../js/glow/1.5.1/widgets/images/lightpanel/bg.gif | Bin 169 -> 0 bytes .../glow/1.5.1/widgets/images/lightpanel/cbl.png | Bin 202 -> 0 bytes .../glow/1.5.1/widgets/images/lightpanel/cbr.png | Bin 203 -> 0 bytes .../glow/1.5.1/widgets/images/lightpanel/close.png | Bin 186 -> 0 bytes .../glow/1.5.1/widgets/images/lightpanel/ctl.png | Bin 201 -> 0 bytes .../glow/1.5.1/widgets/images/lightpanel/ctr.png | Bin 192 -> 0 bytes .../1.5.1/widgets/images/lightpanel/transbg.png | Bin 79 -> 0 bytes .../js/glow/1.5.1/widgets/images/slider/darkh.png | Bin 1268 -> 0 bytes .../js/glow/1.5.1/widgets/images/slider/darkv.png | Bin 1301 -> 0 bytes .../js/glow/1.5.1/widgets/images/slider/lighth.png | Bin 1051 -> 0 bytes .../js/glow/1.5.1/widgets/images/slider/lightv.png | Bin 1112 -> 0 bytes horde/js/glow/1.5.1/widgets/images/t.gif | Bin 43 -> 0 bytes .../glow/1.5.1/widgets/images/timetable/darkh.png | Bin 474 -> 0 bytes .../glow/1.5.1/widgets/images/timetable/darkv.png | Bin 434 -> 0 bytes .../glow/1.5.1/widgets/images/timetable/lighth.png | Bin 486 -> 0 bytes .../glow/1.5.1/widgets/images/timetable/lightv.png | Bin 446 -> 0 bytes horde/js/glow/1.5.1/widgets/widgets.css | 16 - horde/js/glow/1.5.1/widgets/widgets.debug.css | 1780 --- horde/js/glow/1.5.1/widgets/widgets.debug.js | 9342 ---------------- horde/js/glow/1.5.1/widgets/widgets.js | 19 - horde/js/glow/LICENCE.txt | 177 - horde/js/glow/map.debug.js | 164 - horde/js/glow/map.js | 16 - 60 files changed, 22464 deletions(-) delete mode 100644 horde/js/glow/1.5.1/core/core.debug.js delete mode 100644 horde/js/glow/1.5.1/core/core.js delete mode 100644 horde/js/glow/1.5.1/widgets/images/carousel/darkdots.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/carousel/darkdotsv.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/carousel/darkh.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/carousel/darkv.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/carousel/lightdots.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/carousel/lightdotsv.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/carousel/lighth.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/carousel/lightv.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/darkpanel/ab.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/darkpanel/al.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/darkpanel/ar.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/darkpanel/at.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/darkpanel/bg.gif delete mode 100644 horde/js/glow/1.5.1/widgets/images/darkpanel/cbl.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/darkpanel/cbr.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/darkpanel/close.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/darkpanel/ctl.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/darkpanel/ctr.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/darkpanel/transbg.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/editor/button-blockquote.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/editor/button-bold.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/editor/button-italics.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/editor/button-strike.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/editor/dark-button-blockquote.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/editor/dark-button-bold.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/editor/dark-button-italics.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/editor/dark-button-strike.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/editor/light-button-blockquote.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/editor/light-button-bold.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/editor/light-button-italics.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/editor/light-button-strike.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/lightpanel/ab.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/lightpanel/al.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/lightpanel/ar.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/lightpanel/at.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/lightpanel/bg.gif delete mode 100644 horde/js/glow/1.5.1/widgets/images/lightpanel/cbl.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/lightpanel/cbr.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/lightpanel/close.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/lightpanel/ctl.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/lightpanel/ctr.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/lightpanel/transbg.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/slider/darkh.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/slider/darkv.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/slider/lighth.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/slider/lightv.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/t.gif delete mode 100644 horde/js/glow/1.5.1/widgets/images/timetable/darkh.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/timetable/darkv.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/timetable/lighth.png delete mode 100644 horde/js/glow/1.5.1/widgets/images/timetable/lightv.png delete mode 100644 horde/js/glow/1.5.1/widgets/widgets.css delete mode 100644 horde/js/glow/1.5.1/widgets/widgets.debug.css delete mode 100644 horde/js/glow/1.5.1/widgets/widgets.debug.js delete mode 100644 horde/js/glow/1.5.1/widgets/widgets.js delete mode 100644 horde/js/glow/LICENCE.txt delete mode 100644 horde/js/glow/map.debug.js delete mode 100644 horde/js/glow/map.js diff --git a/horde/js/glow/1.5.1/core/core.debug.js b/horde/js/glow/1.5.1/core/core.debug.js deleted file mode 100644 index 1e10e2501..000000000 --- a/horde/js/glow/1.5.1/core/core.debug.js +++ /dev/null @@ -1,10930 +0,0 @@ -/* - Copyright 2009 British Broadcasting Corporation - - 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. -*/ -/** -@name glow -@namespace -@version 1.5.1 (2008-09-22) -@description The glow namespace and core library. - - Includes common methods for running scripts onDomReady and user agent sniffing. -*/ -(function() { - /* - PrivateVar: moduleRegister - Holds info on which modules are registered {name:true} - */ - var moduleRegister = {glow: true}, - /* - PrivateVar: regexEscape - For escaping strings to go in regex - */ - regexEscape = /([$^\\\/()|?+*\[\]{}.-])/g, - /* - PrivateVar: ua - A lowercase representation of the user's useragent string - */ - ua = navigator.userAgent.toLowerCase(), - //glow version - version = "1.5.1", - // number of blockers blocking (when it's zero, we're ready) - blockersActive = 0, - /* - PrivateMethod: domReadyQueue - array of functions to call when dom is ready - */ - domReadyQueue = [], - domReadyQueueLen = 0, - /* - PrivateMethod: readyQueue - array of functions to call when all ready blockers are unblocked - */ - //we want to set isReady to true when this is first run - readyQueue = [], - readyQueueLen = 0, - // stops two instances of 'runReadyQueue' fighting on the call stack - processingReadyQueue = false, - glow = { - /** - @name glow.VERSION - @description Version of glow - This is in the format 1.2.3 - - @type String - - @see Glow's versioning scheme - */ - VERSION: version, - - /** - @name glow.UID - @description A unique ID for this instance of Glow - - This will be used in glow-specific property names - that need to be unique to this instance of glow. - - @type String - */ - UID: "glow" + Math.floor(Math.random() * (1<<30)), - - /** - @name glow.isDomReady - @description Is the DOM ready? - - If glow is loaded after the page has loaded (by means other than Gloader) - this value should be set manually. - - @type Boolean - */ - //check gloader to see if dom is already ready - isDomReady: window.gloader && gloader.isReady, - - /** - @name glow.isReady - @description Is Glow ready? - - Set to true when Glow is ready. This includes DOM ready, - a supported browser and any additional requirements. For example - Glow widgets will add the loading of their CSS file as a requirement. - - @type Boolean - */ - //check gloader to see if dom is already ready - isReady: window.gloader && gloader.isReady, - - /** - @name glow.env - @description Information about the browser / platform - @type Object - - @example - if (glow.env.ie < 7) { - //this only runs in IE 6 and below - } - if (glow.env.gecko < 1.9) { - //this only runs in Gecko versions less than 1.9 - //Wikipedia can be used to link engine versions to browser versions - } - */ - /** - @name glow.env.gecko - @description Gecko version number to one decimal place (eg 1.9) or NaN - @type Number - */ - /** - @name glow.env.ie - @description IE version number or NaN - @type Number - */ - /** - @name glow.env.opera - @description Opera version (eg 8.02) or NaN - @type Number - */ - /** - @name glow.env.webkit - @description Webkit version number to one decimal place (eg 419.3) or NaN - @type Number - */ - /** - @name glow.env.khtml - @description KHTML version number to one decimal place or NaN - @type Number - */ - /** - @name glow.env.standardsMode - @description True if the browser reports itself to be in 'standards mode' - @type Boolean - */ - /** - @name glow.env.version - @description Browser version as a string. Includes non-numerical data, eg "1.8.1" or "7b" - @type String - */ - env: function(){ - var nanArray = [0, NaN], - opera = (/opera[\s\/]([\w\.]+)/.exec(ua) || nanArray)[1], - ie = opera ? NaN : (/msie ([\w\.]+)/.exec(ua) || nanArray)[1], - gecko = (/rv:([\w\.]+).*gecko\//.exec(ua) || nanArray)[1], - webkit = (/applewebkit\/([\w\.]+)/.exec(ua) || nanArray)[1], - khtml = (/khtml\/([\w\.]+)/.exec(ua) || nanArray)[1], - toNum = parseFloat; - - return { - gecko : toNum(gecko), - ie : toNum(ie), - opera : toNum(opera), - webkit : toNum(webkit), - khtml : toNum(khtml), - version : ie || gecko || webkit || opera || khtml, - standardsMode : document.compatMode != "BackCompat" && (!ie || ie >= 6) - } - }(), - - /** - @name glow.module - @private - @function - @description Registers a new module with the library, checking version numbers & dependencies. - - @param {Object} meta - Object containing all of the following. This object is - compatible with gloader.module, hence some properties which seem - unnecessary here. This is all simplified by glow's module pattern. - - @param {String} meta.name Name of the module. - Eg. "glow.dom" or "glow.widgets.Panel" - - @param {String[]} meta.library Information about Glow. - This must be ["glow", "1.5.1"]. 1.5.1 should be - the version number of glow expected. - - @param {String[]} meta.depends The module's dependencies. - This must start ["glow", "1.5.1"], followed by modules - such as "glow.dom". 1.5.1 should be the version number - of glow expected. - - @param {Function} meta.builder The module's implementation. - A reference to glow will be passed in as the first parameter - to this function. Add to that object to create publicly - accessabile properties. Anything else in this function - will be private. - - @returns {Object} Glow - - @example - glow.module({ - name: "glow.anim", - library: ["glow", "1.0.0"], - depends: ["glow", "1.0.0", "glow.dom"], - builder: function(glow) { - glow.anim = { - //... - }; - } - }); - */ - module: function(meta) { - var i = 2, - depends = meta.depends[0] || [], - dependsLen = depends.length, - name = meta.name, - objRef = window.glow; //holds the parent object for the new module - - //check version number match core version - if (meta.library[1] != glow.VERSION) { - throw new Error("Cannot register " + name + ": Version mismatch"); - } - - //check dependencies loaded - if (depends[2]) { - for (; i < dependsLen; i++) { - //check exists - if (!moduleRegister[depends[i]]) { - //check again ondomready to detect if modules are being included in wrong order - throw new Error("Module " + depends[i] + " required before " + name); - } - } - } - - //create module - meta.builder(glow); - //register it as built - moduleRegister[name] = true; - return glow; - }, - - /** - @name glow.ready - @function - @description Calls a function when the DOM had loaded and the browser is supported - - "ready" also waits for glow's CSS file to load if it has been - requested. - - @param {Function} callback Function to call - - @returns {glow} - - @example - glow.ready(function() { - alert("DOM Ready!"); - }); - */ - ready: function(f) { - //just run function if already ready - if (this.isReady) { - f(); - } else { - readyQueue[readyQueueLen++] = f; - } - return this; - }, - - /** - @name glow._readyBlockers - @private - @object - @description A hash (by name) of blockers. - True if they are blocking, false if they've since unblocked - */ - _readyBlockers: {}, - - /** - @name glow._addReadyBlock - @private - @function - @description Adds a blocker to anything added via glow.ready. - Uncalled callbacks added via glow.ready will not fire until - this block is removed - - @param {String} name Name of blocker - - @returns {glow} - - @example - glow._addReadyBlock("widgetsCss"); - - // when CSS is ready... - glow._removeReadyBlock("widgetsCss"); - */ - _addReadyBlock: function(name) { - if (name in glow._readyBlockers) { - throw new Error("Blocker '" + name +"' already exists"); - } - glow._readyBlockers[name] = true; - glow.isReady = false; - blockersActive++; - return glow; - }, - - /** - @name glow._removeReadyBlock - @private - @function - @description Removes a ready blocker added via glow._addReadyBlock - - @param {String} name Name of blocker - - @returns {glow} - - @example - glow._addReadyBlock("widgetsCss"); - - // when CSS is ready... - glow._removeReadyBlock("widgetsCss"); - */ - _removeReadyBlock: function(name) { - if (glow._readyBlockers[name]) { - glow._readyBlockers[name] = false; - blockersActive--; - // if we're out of blockers - if (!blockersActive) { - // call our queue - glow.isReady = true; - runReadyQueue(); - } - } - return glow; - }, - - /** - @name glow.onDomReady - @function - @description Calls a function when / if the DOM is ready. - - This function does not wait for glow's CSS to load, nor - does it block unsupported browsers. If you want these features, - use {@link glow.ready} - - @param {Function} callback Function to call - - @returns {glow} - - @exmaple - glow.onDomReady(function() { - alert("DOM Ready!"); - }); - */ - onDomReady: function(f) { - //just run function if already ready - if (this.isDomReady) { - f(); - } else { - domReadyQueue[domReadyQueueLen++] = f; - } - }, - - /** - @name glow.lang - @namespace - @description Useful language functions. - @see Using glow.lang.clone - */ - lang: { - /** - @name glow.lang.trim - @function - @description Removes leading and trailing whitespace from a string - - @param {String} str String to trim - - @returns {String} - - String without leading and trailing whitespace - - @example - glow.lang.trim(" Hello World "); // "Hello World" - */ - trim: function(sStr) { - //this optimisation from http://blog.stevenlevithan.com/archives/faster-trim-javascript - return sStr.replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1'); - }, - - /** - @name glow.lang.toArray - @function - @description Converts an array-like object to a real array - - @param {Object} arrayLike Any array-like object - - @returns {Array} - - @example - var a = glow.lang.toArray(glow.dom.get("a")); - */ - toArray: function(aArrayLike) { - if (aArrayLike.constructor == Array) { - return aArrayLike; - } - //use array.slice if not IE? Could be faster - var r = [], i=0, len = aArrayLike.length; - for (; i < len; i++) { - r[i] = aArrayLike[i]; - } - return r; - }, - - /** - @name glow.lang.apply - @function - @description Copies properties from one object to another - - @param {Object} destination Destination object - - @param {Object} source Properties of this object will be copied onto a clone of destination - - @returns {Object} - - @example - var obj = glow.lang.apply({foo: "hello", bar: "world"}, {bar: "everyone"}); - //results in {foo: "hello", bar: "everyone"} - */ - apply: function(destination, source) { - for (var i in source) { - destination[i] = source[i]; - } - return destination; - }, - - /** - @name glow.lang.map - @function - @description Runs a function for each element of an array and returns an array of the results - - @param {Array} array Array to loop over - @param {Function} callback The function to run on each element. This function is passed three params, the array item, its index and the source array. - @param {Object} [context] The context for the callback function (the array is used if not specified) - - @returns {Array} - - Array containing one element for each value returned from the callback - - @example - var weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] - var weekdaysAbbr = glow.lang.map(weekdays, function (day) { - return day.slice(0, 3).toLowerCase(); - }); - // returns ["mon", "tue", "wed", "thu", "fri"] - */ - map: function (arr, callback, context) { - if (Array.prototype.map) { return Array.prototype.map.call(arr, callback, context || arr); } - if (! callback.call) { throw new TypeError(); } - - var len = arr.length, - res = [], - thisp = context || arr, - i = 0; - - for (; i < len; i++) { - if (i in arr) { - res[i] = callback.call(thisp, arr[i], i, arr); - } - } - return res; - }, - - /** - @name glow.lang.replace - @function - @description Makes a replacement in a string. - - Has the same interface as the builtin - String.prototype.replace method, but takes the input - string as the first parameter. In general the native string - method should be used unless you need to pass a function as the - second parameter, as this method will work accross our - supported browsers. - - @param {String} str Input string - - @param {String | RegExp} pattern String or regular expression to match against - - @param {String | Function} replacement String to make replacements with, or a function to generate the replacements - - @returns {String} - A new string with the replacement(s) made - - @example - var myDays = '1 3 6'; - var dayNames = glow.lang.replace(myDays, /(\d)/, function (day) { - return " MTWTFSS".charAt(day - 1); - }); - // dayNames now contains "M W S" - */ - replace: (function () { - var replaceBroken = "g".replace(/g/, function () { return 'l'; }) != 'l', - def = String.prototype.replace; - return function (inputString, re, replaceWith) { - var pos, match, last, buf; - if (! replaceBroken || typeof(replaceWith) != 'function') { - return def.call(inputString, re, replaceWith); - } - if (! (re instanceof RegExp)) { - pos = inputString.indexOf(re); - return pos == -1 ? - inputString : - def.call(inputString, re, replaceWith.call(null, re, pos, inputString)); - } - buf = []; - last = re.lastIndex = 0; - while ((match = re.exec(inputString)) != null) { - pos = match.index; - buf[buf.length] = inputString.slice(last, pos); - buf[buf.length] = replaceWith.apply(null, match); - if (re.global) { - last = re.lastIndex; - } else { - last = pos + match[0].length; - break; - } - } - buf[buf.length] = inputString.slice(last); - return buf.join(""); - }; - })(), - - /** - @name glow.lang.interpolate - @function - @description Replaces placeholders in a string with data from an object - - @param {String} template The string containing {placeholders} - @param {Object} data Object containing the data to be merged in to the template -

The object can contain nested data objects and arrays, with nested object properties and array elements are accessed using dot notation. eg foo.bar or foo.0.

-

The data labels in the object cannot contain characters used in the template delimiters, so if the data must be allowed to contain the default { and } delimiters, the delimters must be changed using the option below.

- @param {Object} opts Options object - @param {String} [opts.delimiter="{}"] Alternative label delimiter(s) for the template - The first character supplied will be the opening delimiter, and the second the closing. If only one character is supplied, it will be used for both ends. - - @returns {String} - - @example - var data = {name: "Domino", colours: ["black", "white"], family:{mum: "Spot", dad: "Patch", siblings: []}}; - var template = "My cat's name is {name}. His colours are {colours.0} & {colours.1}. His mum is {family.mum}, his dad is {family.dad} and he has {family.siblings.length} brothers or sisters."; - var result = glow.lang.interpolate(template, data); - //result == "My cat's name is Domino. His colours are black & white. His mum is Spot, his dad is Patch and he has 0 brothers or sisters." - */ - interpolate : function (template, data, opts) { - var rx, l, r; - - opts = opts || {}; - - if(opts.delimiter == undefined) { - rx = /\{[^{}]+\}/g; - } else { - l = opts.delimiter.substr(0, 1).replace(regexEscape, "\\$1"); - r = (opts.delimiter.length == 1)?l:opts.delimiter.substr(1, 1).replace(regexEscape, "\\$1"); - rx = new RegExp(l + "[^" + l + r + "]+" + r, "g"); - } - - return template.replace(rx, function (found) { - - var t = found.substring(1, found.length - 1), - exp = t.split("."), - val = data; - - if(data[t]) { - return data[t]; // need to be backwards compatible with "flattened" data. - } - - for(var i = 0, l = exp.length; i < l; i++) { - if(val[exp[i]] != undefined) { - val = val[exp[i]]; - } else { - return found; - } - } - - return val; - }); - }, - /** - @name glow.lang.hasOwnProperty - @function - @description Cross-browser implementation - - Safari 1.3 doesn't support - - Object.hasOwnProperty - , use this method instead. - - @param {Object} obj The object to check - - @param {String} property Property name - - @returns {Boolean} - - Returns false if a property doesn't exist in an object, or it - was inherited from the object's prototype. Otherwise, returns - true - */ - hasOwnProperty: {}.hasOwnProperty ? //not supported in Safari 1.3 - function(obj, prop) { - return obj.hasOwnProperty(prop); - } : - function(obj, prop) { - var propVal = obj[prop], //value of the property - objProto = obj.__proto__, //prototype of obj - protoVal = objProto ? objProto[prop] : {}; //prototype val - if (propVal !== protoVal) { - return true; - } - //try changing prototype and see if obj reacts - var restoreProtoVal = glow.lang.hasOwnProperty(objProto, prop), - tempObjProtoVal = objProto[prop] = {}, - hasOwn = (obj[prop] !== tempObjProtoVal); - - delete objProto[prop]; - if (restoreProtoVal) { - objProto[name] = tempObjProtoVal; - } - return hasOwn; - }, - - /** - @name glow.lang.extend - @function - @description Copies the prototype of one object to another. - - The 'subclass' can also access the 'base class' via subclass.base - - @param {Function} sub Class which inherits properties. - - @param {Function} base Class to inherit from. - - @param {Object} additionalProperties An object of properties and methods to add to the subclass. - - @example - function MyClass(arg) { - this.prop = arg; - } - MyClass.prototype = { - showProp: function() { alert(this.prop); } - }; - function MyOtherClass(arg) { - //call the base class's constructor - arguments.callee.base.apply(this, arguments); - } - glow.lang.extend(MyOtherClass, MyClass, { - setProp: function(newProp) { this.prop = newProp; } - }); - - var test = new MyOtherClass("hello"); - test.showProp(); // alerts "hello" - test.setProp("world"); - test.showProp(); // alerts "world" - * - */ - extend: function(sub, base, additionalProperties) { - var f = function () {}, p; - f.prototype = base.prototype; - p = new f(); - sub.prototype = p; - p.constructor = sub; - sub.base = base; - if (additionalProperties) { - glow.lang.apply(sub.prototype, additionalProperties); - } - }, - - /** - @name glow.lang.clone - @function - @description Deep clones an object / array - - @param {Object} Data Object to clone - - @returns {Object} - - @example - var firstObj = { name: "Bob", secondNames: ["is","your","uncle"] }; - var clonedObj = glow.lang.clone( firstObj ); - */ - clone: function( obj ) { - var index, _index, tmp; - obj = obj.valueOf(); - if ( typeof obj !== 'object' ) { - return obj; - } else { - if ( obj[0] || obj.concat ) { - tmp = [ ]; - index = obj.length; - while(index--) { - tmp[index] = arguments.callee( obj[index] ); - } - } else { - tmp = { }; - for ( index in obj ) { - tmp[index] = arguments.callee( obj[index] ); - } - } - return tmp; - } - - } - } - }, - env = glow.env, - d = document; - - //dom ready stuff - //run queued ready functions when DOM is ready - - function runDomReadyQueue() { - glow.isDomReady = true; - // run all functions in the array - for (var i = 0; i < domReadyQueueLen; i++) { - domReadyQueue[i](); - } - } - - function runReadyQueue() { - // if we're already processing the queue, just exit, the other instance will take care of it - if (processingReadyQueue) return; - processingReadyQueue = true; - for (var i = 0; i < readyQueueLen;) { - readyQueue[i](); - i++; - // check if the previous function has created a blocker - if (blockersActive) { - break; - } - } - // take items off the ready queue that have processed - readyQueue = readyQueue.slice(i); - // update len - readyQueueLen = readyQueueLen - i; - processingReadyQueue = false; - } - - (function(){ - //don't do this stuff if the dom is already ready - if (glow.isDomReady) { return; } - - glow._addReadyBlock("glow_domReady"); - if (env.ie) { - if (typeof window.frameElement != 'undefined') { - // we can't use doScroll if we're in an iframe... - d.attachEvent("onreadystatechange", function(){ - if (d.readyState == "complete") { - d.detachEvent("onreadystatechange", arguments.callee); - runDomReadyQueue(); - glow._removeReadyBlock("glow_domReady"); - } - }); - } else { - // polling for no errors - (function () { - try { - // throws errors until after ondocumentready - d.documentElement.doScroll('left'); - } catch (e) { - setTimeout(arguments.callee, 0); - return; - } - // no errors, fire - runDomReadyQueue(); - glow._removeReadyBlock("glow_domReady"); - })(); - } - } else if (glow.env.webkit < 525.13 && typeof d.readyState != 'undefined') { - var f = function(){ - if ( /loaded|complete/.test(d.readyState) ) { - runDomReadyQueue(); - glow._removeReadyBlock("glow_domReady"); - } else { - setTimeout(f, 0); - } - }; - f(); - } else { - var callback = function () { - if (callback.fired) { return; } - callback.fired = true; - runDomReadyQueue(); - glow._removeReadyBlock("glow_domReady"); - }; - d.addEventListener("DOMContentLoaded", callback, false); - var oldOnload = window.onload; - window.onload = function () { - if (oldOnload) { oldOnload(); } - callback(); - }; - } - })(); - - /** - @name glow.isSupported - @description Set to true in supported user agents - This will read false in 'level 2' browsers in BBC's Browser Support Guidelines - @type Boolean - - @see BBC's Browser Support Guidelines - */ - // TODO: for v2 we should switch this to 'notSupported' as it's a blacklist - glow.isSupported = !( - //here are the browsers we don't support - env.ie < 6 || - (env.gecko < 1.9 && !/^1\.8\.1/.test(env.version)) || - env.opera < 9 || - env.webkit < 412 - ); - // block 'ready' if browser isn't supported - if (!glow.isSupported) { - glow._addReadyBlock("glow_browserSupport"); - } - - if (window.gloader) { - gloader.library({ - name: "glow", - version: "1.5.1", - builder: function () { - return glow; - } - }); - } else if (window.glow) { - throw new Error("Glow global object already exists"); - } else { - window.glow = glow; - } - - // this helps IE cache background images - if (glow.ie) { - try { - document.execCommand("BackgroundImageCache", false, true); - } catch(e) {} - } -})(); -/*@cc_on @*/ -/*@if (@_jscript_version > 5.5)@*/ -/** -@name glow.dom -@namespace -@description Accessing and manipulating the DOM -@see Creating NodeLists -@see Working with NodeLists -@see XML NodeLists -*/ -(window.gloader || glow).module({ - name: "glow.dom", - library: ["glow", "1.5.1"], - depends: [], - builder: function(glow) { - //private - var env = glow.env, - lang = glow.lang, - /* - PrivateVar: cssRegex - For matching CSS selectors - */ - cssRegex = { - tagName: /^(\w+|\*)/, - combinator: /^\s*([>]?)\s*/, - //safari 1.3 is a bit dim when it comes to unicode stuff, only dot matches them (not even \S), so some negative lookalheads are needed - classNameOrId: (env.webkit < 417) ? new RegExp("^([\\.#])((?:(?![\\.#\\[:\\s\\\\]).|\\\\.)+)") : /^([\.#])((?:[^\.#\[:\\\s]+|\\.)+)/ - }, - //for escaping strings in regex - regexEscape = /([$^\\\/()|?+*\[\]{}.-])/g, - - /* - PrivateVar: cssCache - Cache of arrays representing an execution path for css selectors - */ - cssCache = {}, - - /* - PrivateVar: dom0PropertyMapping - Mapping of HTML attribute names to DOM0 property names. - */ - dom0PropertyMapping = { - checked : "checked", - "class" : "className", - "disabled" : "disabled", - "for" : "htmlFor", - maxlength : "maxLength" - }, - - /* - PrivateVar: dom0BooleanAttribute - The HTML attributes names that should be converted from true/false - to ATTRIBUTENAME/undefined (i.e. boolean attributes like checked="checked"). - */ - dom0BooleanAttribute = { - checked : true, - disabled : true - }, - - /* - PrivateVar: dom0AttributeMappings - Functions that map dom0 values to sane ones. - */ - dom0AttributeMappings = { - maxlength : function (val) { return val.toString() == "2147483647" ? undefined : val; } - }, - /* - PrivateVar: ucheck - Used by unique(), increased by 1 on each use - */ - ucheck = 1, - /* - PrivateVar: ucheckPropName - This is the property name used by unique checks - */ - ucheckPropName = "_unique" + glow.UID, - - /* - PrivateVar: htmlColorNames - Mapping of colour names to hex values - */ - htmlColorNames = { - black: 0, - silver: 0xc0c0c0, - gray: 0x808080, - white: 0xffffff, - maroon: 0x800000, - red: 0xff0000, - purple: 0x800080, - fuchsia: 0xff00ff, - green: 0x8000, - lime: 0xff00, - olive: 0x808000, - yellow: 0xffff00, - navy: 128, - blue: 255, - teal: 0x8080, - aqua: 0xffff, - orange: 0xffa500 - }, - /* - PrivateVar: usesYAxis - regex for detecting which css properties need to be calculated relative to the y axis - */ - usesYAxis = /height|top/, - colorRegex = /^rgb\(([\d\.]+)(%?),\s*([\d\.]+)(%?),\s*([\d\.]+)(%?)/i, - cssPropRegex = /^(?:(width|height)|(border-(top|bottom|left|right)-width))$/, - hasUnits = /width|height|top$|bottom$|left$|right$|spacing$|indent$|font-size/, - //append gets set to a function below - append, - //unique gets set to a function below - unique, - trbl = ["Top", "Right", "Bottom", "Left"], - trblLen = 4, - paddingStr = "padding", - marginStr = "margin", - borderStr = "border", - widthStr = "Width", - //we set this up at the end of the module - placeholderElm, - //getByTagName gets get to a function below - getByTagName, - win = window, - doc = document, - docBody, - docElm, - // true if properties of a dom node are cloned when the node is cloned (eg, true in IE) - nodePropertiesCloned, - // used to convert divs to strings - tmpDiv = doc.createElement("div"); - - // clean up IE's mess - if (env.ie) { - window.attachEvent("onunload", function() { - tmpDiv = null; - }); - } - - glow.ready(function() { - docBody = doc.body; - docElm = doc.documentElement; - }); - - - // test for nodePropertiesCloned - (function() { - var div = doc.createElement("div"); - div.a = 1; - nodePropertiesCloned = !!div.cloneNode(true).a; - })(); - - /* - PrivateMethod: removeClassRegex - Get a regex that can be used to remove a class from a space separated list of classes. - - Arguments: - name - (string) the name of the class. - - Returns: - The regex. - */ - function removeClassRegex (name) { - return new RegExp(["(^|\\s)", name.replace(regexEscape, "\\$1"), "($|\\s)"].join(""), "g"); - } - - - /* - PrivateMethod: stringToNodes - Creates an array of nodes from a string - */ - function stringToNodes(str) { - // TODO: need to change container for certain elements. Make a lookup table for - // {element: container}, for exceptions to div container - var r = [], rLen = 0; - - //we add a text node to the start of the string here to fix an IE bug - //when the string contains a link element - tmpDiv.innerHTML = "a" + str; - - while (tmpDiv.childNodes[1]) { - r[rLen++] = tmpDiv.removeChild(tmpDiv.childNodes[1]); - } - tmpDiv.innerHTML = ""; - return r; - } - - /* - PrivateMethod: nodelistToArray - Converts a w3 NodeList to an array - */ - function nodelistToArray(nodelist) { - var r = [], i = 0; - for (; nodelist[i]; i++) { - r[i] = nodelist[i]; - } - return r; - } - - /* - PrivateMethod: setAttribute - Sets the attribute in the nodelist using the supplied function. - - Arguments: - value - (String|Function) the value/value generator. - attributeSetter - (Function) a function that can be called to actually set the attribute. - - Returns: - The object. - */ - // is marginal having this separated out as it is only used twice and call is almost as big - // leaving it separate for now for once and only once, as well as in case attr does some more mutating stuff - // could be merged back with attr later - function setAttribute (value, attributeSetter) { - for (var that = this, i = 0, length = that.length; i < length; i++) { - attributeSetter.call( - that[i], - value.call ? - value.call(that[i], i) : - value - ); - } - return that; - } - - - /* - PrivateMethod: append - append the nodes in "b" to the array / list "a" - */ - //return different function for IE & Opera to deal with their stupid bloody expandos. Pah. - if (document.all) { - append = function(a, b) { - var i = 0, - ai = a.length, - length = b.length; - if (typeof b.length == "number") { - for (; i < length; i++) { - a[ai++] = b[i]; - } - } else { - for (; b[i]; i++) { - a[ai++] = b[i]; - } - } - }; - } else { - append = function(a, b) { - var i = 0, ai = a.length; - for (; b[i]; i++) { - a[ai++] = b[i]; - } - }; - } - - /* - PrivateMethod: isXml - Is this node an XML Document node or within an XML Document node - - Arguments: - node - - Returns: - Bool - */ - function isXml(node) { - //test for nodes within xml element - return (node.ownerDocument && !node.ownerDocument.body) || - //test for xml document elements - (node.documentElement && !node.documentElement.body); - } - - /* - PrivateMethod: unique - Get an array of nodes without duplicate nodes from an array of nodes. - - Arguments: - aNodes - (Array|) - - Returns: - An array of nodes without duplicates. - */ - //worth checking if it's an XML document? - if (env.ie) { - unique = function(aNodes) { - if (aNodes.length == 1) { return aNodes; } - - //remove duplicates - var r = [], - ri = 0, - i = 0; - - for (; aNodes[i]; i++) { - if (aNodes[i].getAttribute(ucheckPropName) != ucheck && aNodes[i].nodeType == 1) { - r[ri++] = aNodes[i]; - } - aNodes[i].setAttribute(ucheckPropName, ucheck); - } - for (i=0; aNodes[i]; i++) { - aNodes[i].removeAttribute(ucheckPropName); - } - ucheck++; - return r; - } - } else { - unique = function(aNodes) { - if (aNodes.length == 1) { return aNodes; } - - //remove duplicates - var r = [], - ri = 0, - i = 0; - - for (; aNodes[i]; i++) { - if (aNodes[i][ucheckPropName] != ucheck && aNodes[i].nodeType == 1) { - r[ri++] = aNodes[i]; - } - aNodes[i][ucheckPropName] = ucheck; - } - ucheck++; - return r; - } - } - - /* - PrivateMethod: getElementsByTag - Get elements by a specified tag name from a set of context objects. If multiple - context objects are passed, then the resulting array may contain duplicates. See - to remove duplicate nodes. - - Arguments: - tag - (string) Tag name. "*" for all. - contexts - (array) DOM Documents and/or DOM Elements to search in. - - Returns: - An array(like) collection of elements with the specified tag name. - */ - if (document.all) { //go the long way around for IE (and Opera) - getByTagName = function(tag, context) { - var r = [], i = 0; - for (; context[i]; i++) { - //need to check .all incase data is XML - //TODO: Drop IE5.5 - if (tag == "*" && context[i].all && !isXml(context[i])) { // IE 5.5 doesn't support getElementsByTagName("*") - append(r, context[i].all); - } else { - append(r, context[i].getElementsByTagName(tag)); - } - } - return r; - }; - } else { - getByTagName = function(tag, context) { - var r = [], i = 0, len = context.length; - for (; i < len; i++) { - append(r, context[i].getElementsByTagName(tag)); - } - return r; - }; - } - - /* - Get the child elements for an html node - */ - function getChildElms(node) { - var r = [], - childNodes = node.childNodes, - i = 0, - ri = 0; - - for (; childNodes[i]; i++) { - if (childNodes[i].nodeType == 1 && childNodes[i].nodeName != "!") { - r[ri++] = childNodes[i]; - } - } - return r; - } - - /* - PrivateMethod: getElmSize - Gets the size of an element as {width: x, height:y} - - Returns: - Size not including padding or border - */ - function getElmSize(elm) { - var r, - i = trblLen, - docElmOrBody = env.standardsMode ? docElm : docBody, - oldVal, - // the following are only used for IE7 - oldPaddingVals = [], - oldBorderVals = [], - // we use borderTop etc in most browsers, but we need to use borderTopWidth in IE7, else borders start vanishing (grr) - borderSuffix = env.ie == 7 ? "Width" : "", - borderZeroVal = env.ie == 7 ? "0" : "none"; - - if (elm.window) { - r = (env.webkit < 522.11 && {width: elm.innerWidth, height: elm.innerHeight}) || - (env.webkit && {width: docBody.clientWidth, height: elm.innerHeight}) || - (env.opera < 9.5 && {width: docBody.clientWidth, height: docBody.clientHeight}) || - /*else*/ {width: docElmOrBody.clientWidth, height: docElmOrBody.clientHeight}; - - } else if (elm.getElementById) { - r = { - width: Math.max( - docBody.scrollWidth, - docBody.offsetWidth, - docElm.clientWidth, - docElm.scrollWidth, - docElm.offsetWidth - ), - height: Math.max( - docBody.scrollHeight, - docBody.offsetHeight, - docElm.clientHeight, - docElm.scrollHeight, - docElm.offsetHeight - ) - }; - } else { - oldVal = elm.style.cssText; - //set border and padding to 0, backing up old vals - while (i--) { - if (env.ie == 7) { - oldPaddingVals[i] = elm.style[paddingStr + trbl[i]]; - oldBorderVals[i] = elm.style[borderStr + trbl[i]]; - } - elm.style[paddingStr + trbl[i]] = "0"; - //setting border-bottom(or whatever)-width to 0 doesn't seem to work in opera 9.5 - elm.style[borderStr + trbl[i] + borderSuffix] = borderZeroVal; - } - - //capture values - r = {width: elm.offsetWidth, height: elm.offsetHeight}; - - //reset old vals - if (env.ie != 7) { - elm.style.cssText = oldVal; - } else { - // IE7 doesn't like the above for some unknown reason - i = trblLen; - while (i--) { - elm.style[paddingStr + trbl[i]] = oldPaddingVals[i]; - elm.style[borderStr + trbl[i] + borderSuffix] = oldBorderVals[i]; - } - } - } - return r; - } - - /* - PrivateMethod: getBodyElm - Gets the body elm for a given element. Gets around IE5.5 nonsense. do getBodyElm(elm).parentNode to get documentElement - */ - function getBodyElm(elm) { - if (env.ie < 6) { - return elm.document.body; - } else { - return elm.ownerDocument.body; - } - } - - /* - PrivateMethod: setElmsSize - Set element's size - - Arguments: - elms - () Elements - val - (Mixed) Set element height / width. In px unless stated - type - (String) "height" or "width" - - Returns: - Nowt. - */ - function setElmsSize(elms, val, type) { - if (typeof val == "number" || /\d$/.test(val)) { - val += "px"; - } - for (var i = 0, len = elms.length; i < len; i++) { - elms[i].style[type] = val; - } - } - - /* - PrivateMethod: toStyleProp - Converts a css property name into its javascript name, such as "background-color" to "backgroundColor". - - Arguments: - prop - (String) CSS Property name - - Returns: - String, javascript style property name - */ - function toStyleProp(prop) { - if (prop == "float") { - return env.ie ? "styleFloat" : "cssFloat"; - } - return lang.replace(prop, /-(\w)/g, function(match, p1) { - return p1.toUpperCase(); - }); - } - - /* - PrivateMethod: tempBlock - Gives an element display:block (but keeps it hidden) and runs a function, then sets the element back how it was - - Arguments: - elm - element - func - function to run - - Returns: - Return value of the function - */ - function tempBlock(elm, func) { - //TODO: rather than recording individual style properties, just cache cssText? This was faster for getting the element size - var r, - elmStyle = elm.style, - oldDisp = elmStyle.display, - oldVis = elmStyle.visibility, - oldPos = elmStyle.position; - - elmStyle.visibility = "hidden"; - elmStyle.position = "absolute"; - elmStyle.display = "block"; - if (!isVisible(elm)) { - elmStyle.position = oldPos; - r = tempBlock(elm.parentNode, func); - elmStyle.display = oldDisp; - elmStyle.visibility = oldVis; - } else { - r = func(); - elmStyle.display = oldDisp; - elmStyle.position = oldPos; - elmStyle.visibility = oldVis; - } - return r; - } - - /* - PrivateMethod: isVisible - Is the element visible? - */ - function isVisible(elm) { - //this is a bit of a guess, if there's a better way to do this I'm interested! - return elm.offsetWidth || - elm.offsetHeight; - } - - /* - PrivateMethod: getCssValue - Get a computed css property - - Arguments: - elm - element - prop - css property or array of properties to add together - - Returns: - String, value - */ - function getCssValue(elm, prop) { - var r, //return value - total = 0, - i = 0, - propLen = prop.length, - compStyle = doc.defaultView && (doc.defaultView.getComputedStyle(elm, null) || doc.defaultView.getComputedStyle), - elmCurrentStyle = elm.currentStyle, - oldDisplay, - match, - propTest = prop.push || cssPropRegex.exec(prop) || []; - - - if (prop.push) { //multiple properties, add them up - for (; i < propLen; i++) { - total += parseInt(getCssValue(elm, prop[i]), 10) || 0; - } - return total + "px"; - } - //had fun with opera including padding in width, this seems safer TODO: retest this assumption - if (propTest[1]) { //is 'width' or 'height' - if (!isVisible(elm)) { //element may be display: none - return tempBlock(elm, function() { - return getElmSize(elm)[prop] + "px"; - }); - } else { - return getElmSize(elm)[prop] + "px"; - } - } else if (propTest[2] //is border-*-width - && glow.env.ie - && getCssValue(elm, "border-" + propTest[3] + "-style") == "none" - ) { - return "0"; - } else if (compStyle) { //W3 Method - //this returns computed values - if (typeof compStyle == "function") { - //safari returns null for compStyle - - oldDisplay = elm.style.display; - r = tempBlock(elm, function() { - if (prop == "display") { //get true value for display, since we've just fudged it - elm.style.display = oldDisplay; - if (!doc.defaultView.getComputedStyle(elm, null)) { - return "none"; - } - elm.style.display = "block"; - } - return getCssValue(elm, prop); - }); - } else { - // assume equal horizontal margins in safari 3 - // TODO put max version on this when http://bugs.webkit.org/show_bug.cgi?id=13343 is fixed - if (glow.env.webkit > 500 && prop == 'margin-right' && compStyle.getPropertyValue('position') != 'absolute') { - prop = 'margin-left'; - } - r = compStyle.getPropertyValue(prop); - } - } else if (elmCurrentStyle) { //IE method - if (prop == "opacity") { - match = /alpha\(opacity=([^\)]+)\)/.exec(elmCurrentStyle.filter); - return match ? String(parseInt(match[1], 10) / 100) : "1"; - } - //this returns cascaded values so needs fixing - r = String(elmCurrentStyle[toStyleProp(prop)]); - if (/^-?\d+[a-z%]+$/i.test(r) && prop != "font-size") { - r = getPixelValue(elm, r, usesYAxis.test(prop)) + "px"; - } - } - //some results need post processing - if (prop.indexOf("color") != -1) { //deal with colour values - r = normaliseCssColor(r).toString(); - } else if (r.indexOf("url") == 0) { //some browsers put quotes around the url, get rid - r = r.replace(/\"/g,""); - } - return r; - } - - /* - PrivateMethod: getPixelValue - Converts a relative value into an absolute pixel value. Only works in IE with dimention value (not stuff like relative font-size). - Based on some Dean Edward's code - - Arguments: - element - element used to calculate relative values - value - (string) relative value - useYAxis - (string) calulate relative values to the y axis rather than x - - Returns: - Number - */ - function getPixelValue(element, value, useYAxis) { - if (/^-?\d+(px)?$/i.test(value)) { return parseInt(value); } - var axisSetValue = useYAxis ? "top" : "left", - axisGetValue = useYAxis ? "Top" : "Left", - elmStyle = element.style, - origWidth = elmStyle.left, - origPos = elmStyle.overflow, - origMargin = elmStyle.margin; - elmStyle.position = "absolute"; - elmStyle.margin = "0"; - elmStyle[axisSetValue] = value || 0; - value = element["offset" + axisGetValue]; - elmStyle.position = origPos; - elmStyle[axisSetValue] = origWidth; - elmStyle.margin = origMargin; - return value; - } - - /* - PrivateMethod: normaliseCssColor - Converts a CSS colour into "rgb(255, 255, 255)" or "transparent" format - */ - - function normaliseCssColor(val) { - if (/^(transparent|rgba\(0, ?0, ?0, ?0\))$/.test(val)) { return 'transparent'; } - var match, //tmp regex match holder - r, g, b, //final colour vals - hex, //tmp hex holder - mathRound = Math.round, - parseIntFunc = parseInt, - parseFloatFunc = parseFloat; - - if (match = colorRegex.exec(val)) { //rgb() format, cater for percentages - r = match[2] ? mathRound(((parseFloatFunc(match[1]) / 100) * 255)) : parseIntFunc(match[1]); - g = match[4] ? mathRound(((parseFloatFunc(match[3]) / 100) * 255)) : parseIntFunc(match[3]); - b = match[6] ? mathRound(((parseFloatFunc(match[5]) / 100) * 255)) : parseIntFunc(match[5]); - } else { - if (typeof val == "number") { - hex = val; - } else if (val.charAt(0) == "#") { - if (val.length == "4") { //deal with #fff shortcut - val = "#" + val.charAt(1) + val.charAt(1) + val.charAt(2) + val.charAt(2) + val.charAt(3) + val.charAt(3); - } - hex = parseIntFunc(val.slice(1), 16); - } else { - hex = htmlColorNames[val]; - } - - r = (hex) >> 16; - g = (hex & 0x00ff00) >> 8; - b = (hex & 0x0000ff); - } - - val = new String("rgb(" + r + ", " + g + ", " + b + ")"); - val.r = r; - val.g = g; - val.b = b; - return val; - } - - /* - PrivateMethod: getTextNodeConcat - Take an element and returns a string of its text nodes combined. This is useful when a browser (Safari 2) goes mad and doesn't return anything for innerText or textContent - */ - function getTextNodeConcat(elm) { - var r = "", - nodes = elm.childNodes, - i = 0, - len = nodes.length; - for (; i < len; i++) { - if (nodes[i].nodeType == 3) { //text - //this function is used in safari only, string concatination is faster - r += nodes[i].nodeValue; - } else if (nodes[i].nodeType == 1) { //element - r += getTextNodeConcat(nodes[i]); - } - } - return r; - } - - /* - PrivateMethod: getNextOrPrev - This gets the next / previous sibling element of each node in a nodeset - and returns the new nodeset. - */ - function getNextOrPrev(nodelist, dir /* "next" or "previous" */) { - var ret = [], - ri = 0, - nextTmp, - i = 0, - length = nodelist.length; - - for (; i < length; i++) { - nextTmp = nodelist[i]; - while (nextTmp = nextTmp[dir + "Sibling"]) { - if (nextTmp.nodeType == 1 && nextTmp.nodeName != "!") { - ret[ri++] = nextTmp; - break; - } - } - } - return r.get(ret); - } - - /* - get the scroll offset. TODO: This should be replaced - with a public function which works on all elements - */ - function scrollPos() { - var win = window, - docElm = env.standardsMode ? doc.documentElement : docBody; - - return { - x: docElm.scrollLeft || win.pageXOffset || 0, - y: docElm.scrollTop || win.pageYOffset || 0 - }; - } - - //public - var r = {}; //object to be returned - - /** - @name glow.dom.get - @function - @description Returns a {@link glow.dom.NodeList NodeList} from CSS selectors and/or Elements. - - @param {String | String[] | Element | Element[] | glow.dom.NodeList} nodespec+ One or more CSS selector strings, Elements or {@link glow.dom.NodeList NodeLists}. - - Will also accept arrays of these types, or any combinations thereof. - - Supported CSS selectors: - -
    -
  • Universal selector "*".
  • -
  • Type selector "div"
  • -
  • Class selector ".myClass"
  • -
  • ID selector "#myDiv"
  • -
  • Child selector "ul > li"
  • -
  • Grouping "div, p"
  • -
- - @returns {glow.dom.NodeList} - - @example - // Nodelist with all links in element with id "nav" - var myNodeList = glow.dom.get("#nav a"); - - @example - // NodeList containing the nodes passed in - var myNodeList = glow.dom.get(someNode, anotherNode); - - @example - // NodeList containing elements in the first form - var myNodeList = glow.dom.get(document.forms[0].elements); - */ - r.get = function() { - var r = new glow.dom.NodeList(), - i = 0, - args = arguments, - argsLen = args.length; - - for (; i < argsLen; i++) { - if (typeof args[i] == "string") { - r.push(new glow.dom.NodeList().push(doc).get(args[i])); - } else { - r.push(args[i]); - } - } - return r; - }; - /** - @name glow.dom.create - @function - @description Returns a {@link glow.dom.NodeList NodeList} from an HTML fragment. - - @param {String} html An HTML string. - - All top-level nodes must be elements (i.e. text content in the - HTML must be wrapped in HTML tags). - - @returns {glow.dom.NodeList} - - @example - // NodeList of two elements - var myNodeList = glow.dom.create("
Hello
World
"); - */ - r.create = function(sHtml) { - var toCheck = stringToNodes(sHtml), - ret = [], - i = 0, - rLen = 0; - for (; toCheck[i]; i++) { - if (toCheck[i].nodeType == 1 && toCheck[i].nodeName != "!") { - ret[rLen++] = toCheck[i]; - } else if (toCheck[i].nodeType == 3 && lang.trim(toCheck[i].nodeValue) !== "") { - throw new Error("glow.dom.create - Text must be wrapped in an element"); - } - } - return new r.NodeList().push(ret); - }; - - /** - @name glow.dom.parseCssColor - @function - @description Returns an object representing a CSS colour string. - - @param {String} color A CSS colour. - - Examples of valid values are "red", "#f00", "#ff0000", - "rgb(255,0,0)", "rgb(100%, 0%, 0%)" - - @returns {Object} - - An object with properties named "r", "g" and "b", each will have - an integer value between 0 and 255. - - @example - glow.dom.parseCssColor("#ff0000"); - // returns {r:255, g:0, b:0} - */ - r.parseCssColor = function(cssColor) { - var normal = normaliseCssColor(cssColor); - return {r: normal.r, g: normal.g, b: normal.b}; - } - - /** - @name glow.dom.NodeList - @class - @description An array-like collection of DOM Elements. - - It is recommended you create a NodeList using {@link glow.dom.get glow.dom.get}, - or {@link glow.dom.create glow.dom.create}, but you can also use - the constructor to create an empty NodeList. - - Unless otherwise specified, all methods of NodeList return the - NodeList itself, allowing you to chain methods calls together. - - @example - // empty NodeList - var nodes = new glow.dom.NodeList(); - - @example - // using get to return a NodeList then chaining methods - glow.dom.get("p").addClass("eg").append("Hello!"); - - @see Creating NodeLists - @see Working with NodeLists - @see XML NodeLists - */ - r.NodeList = function() { - - /** - @name glow.dom.NodeList#length - @type Number - @description Number of nodes in the NodeList - @example - // get the number of paragraphs on the page - glow.dom.get("p").length; - */ - this.length = 0; //number of elements in NodeList - }; - - /* - Group: Methods - */ - r.NodeList.prototype = { - - /** - @name glow.dom.NodeList#item - @function - @description Returns a node from the NodeList. - - @param {Number} index The numeric index of the node to return. - - @returns {Element} - - @example - // get the fourth node - var node = myNodeList.item(3); - - @example - // another way to get the fourth node - var node = myNodeList[3]; - */ - item: function (nIndex) { - return this[nIndex]; - }, - - /** - @name glow.dom.NodeList#push - @function - @description Adds Elements to the NodeList. - - @param {Element | Element[] | glow.dom.NodeList} nodespec+ One or more Elements, Arrays of Elements or NodeLists. - - @returns {glow.dom.NodeList} - - @example - var myNodeList = glow.dom.create("
Hello world
"); - myNodeList.push("
Foo
", glow.dom.get("#myList li")); - */ - push: function() { - var args = arguments, - argsLen = args.length, - i = 0, - n, - nNodeListLength, - that = this, - arrayPush = Array.prototype.push; - - for (; i < argsLen; i++) { - if (!args[i]) { - continue; - } else if (args[i].nodeType == 1 || args[i].nodeType == 9 || args[i].document) { //is Node - arrayPush.call(that, args[i]); - } else if (args[i][0]) { //is array or array like - for (n = 0, nNodeListLength = args[i].length; n < nNodeListLength; n++) { - arrayPush.call(that, args[i][n]); - } - } - } - return that; - }, - - /** - @name glow.dom.NodeList#each - @function - @description Calls a function for each node. - - The supplied function will be called for each node in the NodeList. - - The index of the node will be provided as the first parameter, and - 'this' will refer to the node. - - @param {Function} callback The function to run for each node. - - @returns {glow.dom.NodeList} - - @example - var myNodeList = glow.dom.get("a"); - myNodeList.each(function(i){ - this == myNodeList[i]; - }); - */ - each: function (callback) { - for (var i = 0, that = this, length = that.length; i < length; i++) { - callback.call(that[i], i, that); - } - return that; - }, - - /** - @name glow.dom.NodeList#eq - @function - @description Compares the NodeList to an element, array of elements or another NodeList - - Returns true if the items are the same and are in the same - order. - - @param {Element | Element[] | glow.dom.NodeList} nodespec The element, array or NodeList the NodeList is being compared to. - - @returns {Boolean} - - @example - // the following returns true - glow.dom.get('#blah').eq( document.getElementById('blah') ); - */ - eq: function (nodelist) { - var that = this, i = 0, length = that.length; - - if (! nodelist.push) { nodelist = [nodelist]; } - if (nodelist.length != that.length) { return false; } - for (; i < length; i++) { - if (that[i] != nodelist[i]) { return false; } - } - return true; - }, - - /** - @name glow.dom.NodeList#isWithin - @function - @description Tests if all the nodes are decendents of an element. - - @param {Element | glow.dom.NodeList} nodespec The element or NodeList that the NodeList is being tested against. - - If nodespec is a NodeList, then the its first node will be used. - - @returns {glow.dom.NodeList} - - @example - var myNodeList = glow.dom.get("input"); - if (myNodeList.isWithin(glow.dom.get("form")) { - // do something - } - */ - isWithin: function (node) { - if (node.push) { node = node[0]; } - - // missing some nodes? Return false - if ( !node || !this.length ) { return false; } - - var that = this, - i = 0, - length = that.length, - toTest; //node to test in manual method - - if (node.contains && env.webkit >= 521) { //proprietary method, safari 2 has a wonky implementation of this, avoid, avoid, avoid - //loop through - for (; i < length; i++) { - // myNode.contains(myNode) returns true in most browsers - if (!(node.contains(that[i]) && that[i] != node)) { return false; } - } - } else if (that[0].compareDocumentPosition) { //w3 method - //loop through - for (; i < length; i++) { - //compare against bitmask - if (!(that[i].compareDocumentPosition(node) & 8)) { return false; } - } - } else { //manual method - for (; i < length; i++) { - toTest = that[i]; - while (toTest = toTest.parentNode) { - if (toTest == node) { break; } - } - if (!toTest) { return false; } - } - } - return true; - }, - - /** - @name glow.dom.NodeList#attr - @function - @description Gets or sets attributes - - When getting an attribute, it is retrieved from the first - node in the NodeList. Setting attributes applies the change - to each element in the NodeList. - - To set an attribute, pass in the name as the first - parameter and the value as a second parameter. - - To set multiple attributes in one call, pass in an object of - name/value pairs as a single parameter. - - For browsers that don't support manipulating attributes - using the DOM, this method will try to do the right thing - (i.e. don't expect the semantics of this method to be - consistent across browsers as this is not possible with - currently supported browsers). - - @param {String | Object} name The name of the attribute, or an object of name/value pairs - @param {String} [value] The value to set the attribute to. - - @returns {String | glow.dom.NodeList} - - When setting attributes it returns the NodeList, otherwise - returns the attribute value. - - @example - var myNodeList = glow.dom.get("myImgClass"); - - // get an attribute - myNodeList.attr("class") == "myImgClass" - - // set an attribute - myNodeList.attr("class", "anotherImgClass"); - - // set multiple attributes - myNodeList.attr({ src : "a.png", alt : "a PNG" }); - */ - attr: function (name /* , val */) { - var that = this, - args = arguments, - argsLen = args.length, - i, - value; - - if (that.length === 0) { - return argsLen > 1 ? that : undefined; - } - if (typeof name == 'object') { - for (i in name) { - if (lang.hasOwnProperty(name, i)) { - that.attr(i, name[i]); - } - } - return that; - } - if (env.ie && dom0PropertyMapping[name]) { - if (argsLen > 1) { - setAttribute.call( - that, - args[1], - // in the callback this is the dom node - function (val) { this[dom0PropertyMapping[name]] = val; } - ); - return that; - } - value = that[0][dom0PropertyMapping[name]]; - if (dom0BooleanAttribute[name]) { - return value ? name : undefined; - } - else if (dom0AttributeMappings[name]) { - return dom0AttributeMappings[name](value); - } - return value; - } - if (argsLen > 1) { - setAttribute.call( - that, - args[1], - // in the callback this is the dom node - function (val) { this.setAttribute(name, val); } - ); - return that; - } - //2nd parameter makes IE behave, but errors for XML nodes (and isn't needed for xml nodes) - return isXml(that[0]) ? that[0].getAttribute(name) : that[0].getAttribute(name, 2); - }, - - /** - @name glow.dom.NodeList#removeAttr - @function - @description Removes an attribute from each node. - - @param {String} name The name of the attribute to remove. - - @returns {glow.dom.NodeList} - - @example - glow.dom.get("a").removeAttr("target"); - */ - removeAttr: function (name) { - var mapping = env.ie && dom0PropertyMapping[name], - that = this, - i = 0, - length = that.length; - - for (; i < length; i++) { - if (mapping) { - that[i][mapping] = ""; - } else { - that[i].removeAttribute(name); - } - } - return that; - }, - - /** - @name glow.dom.NodeList#hasAttr - @function - @description Does the node have a particular attribute? - - The first node in the NodeList is tested - - @param {String} name The name of the attribute to test for. - - @returns {Boolean} - - @example - if ( glow.dom.get("#myImg").hasAttr("alt") ){ - // ... - } - */ - hasAttr: function (name) { - var firstNode = this[0], - attributes = firstNode.attributes; - - if (isXml(firstNode) && env.ie) { //getAttributeNode not supported for XML - var attributes = firstNode.attributes, - i = 0, - len = attributes.length; - - //named node map doesn't seem to work properly, so need to go by index - for (; i < len; i++) { - if (attributes[i].nodeName == name) { - return attributes[i].specified; - } - } - return false; - } else if (this[0].getAttributeNode) { - var attr = this[0].getAttributeNode(name); - return attr ? attr.specified : false; - } - - return typeof attributes[attr] != "undefined"; - }, - - /** - @name glow.dom.NodeList#hasClass - @function - @description Tests if a node has a given class. - - Will return true if any node in the NodeList has the supplied class - -

This method is not applicable to XML NodeLists.

- - @param {String} name The name of the class to test for. - - @returns {Boolean} - - @example - if (glow.dom.get("a").hasClass("termsLink")){ - // ... - } - */ - hasClass: function (name) { - for (var i = 0, length = this.length; i < length; i++) { - if ((" " + this[i].className + " ").indexOf(" " + name + " ") != -1) { - return true; - } - } - return false; - }, - - /** - @name glow.dom.NodeList#addClass - @function - @description Adds a class to each node. - -

This method is not applicable to XML NodeLists.

- - @param {String} name The name of the class to add. - - @returns {glow.dom.NodeList} - - @example - glow.dom.get("#login a").addClass("highlight"); - */ - addClass: function (name) { - for (var i = 0, length = this.length; i < length; i++) { - if ((" " + this[i].className + " ").indexOf(" " + name + " ") == -1) { - this[i].className += " " + name; - } - } - return this; - }, - - /** - @name glow.dom.NodeList#removeClass - @function - @description Removes a class from each node. - -

This method is not applicable to XML NodeLists.

- - @param {String} name The name of the class to remove. - - @returns {glow.dom.NodeList} - - @example - glow.dom.get("#footer #login a").removeClass("highlight"); - */ - removeClass: function (name) { - var re = removeClassRegex(name), - that = this, - i = 0, - length = that.length; - - for (; i < length; i++) { - that[i].className = that[i].className.replace(re, " "); - } - return that; - }, - - /** - @name glow.dom.NodeList#toggleClass - @function - @description Toggles a class on each node. - -

This method is not applicable to XML NodeLists.

- - @param {String} name The name of the class to toggle. - - @returns {glow.dom.NodeList} - - @example - glow.dom.get(".onOffSwitch").toggleClass("on"); - */ - toggleClass: function (name) { - var i = this.length, - paddedClassName, - paddedName = " " + name + " "; - - while (i--) { - paddedClassName = " " + this[i].className + " "; - - if (paddedClassName.indexOf(paddedName) != -1) { - this[i].className = paddedClassName.replace(paddedName, " "); - } else { - this[i].className += " " + name; - } - } - return this; - }, - - /** - @name glow.dom.NodeList#val - @function - @description Gets or sets form values for the first node. - -

This method is not applicable to XML NodeLists.

- -

Getting values from form elements

- - The returned value depends on the type of element, see below: - -
-
Radio button or checkbox
-
If checked, then the contents of the value attribute, otherwise an empty string.
-
Select
-
The contents of value attribute of the selected option
-
Select (multiple)
-
An array of selected option values.
-
Other form element
-
The value of the input.
-
- -

Getting values from a form

- - If the first element in the NodeList is a form, then an - object is returned containing the form data. Each item - property of the object is a value as above, apart from when - multiple elements of the same name exist, in which case the - it will contain an array of values. - -

Setting values for form elements

- - If a value is passed and the first element of the NodeList - is a form element, then the form element is given that value. - For select elements, this means that the first option that - matches the value will be selected. For selects that allow - multiple selection, the options which have a value that - exists in the array of values/match the value will be - selected and others will be deselected. - - Currently checkboxes and radio buttons are not checked or - unchecked, just their value is changed. This does mean that - this does not do exactly the reverse of getting the value - from the element (see above) and as such may be subject to - change - -

Setting values for forms

- - If the first element in the NodeList is a form and the - value is an object, then each element of the form has its - value set to the corresponding property of the object, using - the method described above. - - @param {String | Object} [value] The value to set the form element/elements to. - - @returns {glow.dom.NodeList | String | Object} - - When used to set a value it returns the NodeList, otherwise - returns the value as described above. - - @example - // get a value - var username = glow.dom.get("input#username").val(); - - @example - // get values from a form - var userDetails = glow.dom.get("form").val(); - - @example - // set a value - glow.dom.get("input#username").val("example username"); - - @example - // set values in a form - glow.dom.get("form").val({ - username : "another", - name : "A N Other" - }); - */ - val: function () { - - /* - PrivateFunction: elementValue - Get a value for a form element. - */ - function elementValue (el) { - var elType = el.type, - elChecked = el.checked, - elValue = el.value, - vals = [], - i = 0; - - if (elType == "radio") { - return elChecked ? elValue : ""; - } else if (elType == "checkbox") { - return elChecked ? elValue : ""; - - } else if (elType == "select-one") { - return el.selectedIndex > -1 ? - el.options[el.selectedIndex].value : ""; - - } else if (elType == "select-multiple") { - for (var length = el.options.length; i < length; i++) { - if (el.options[i].selected) { - vals[vals.length] = el.options[i].value; - } - } - return vals; - } else { - return elValue; - } - } - - /* - PrivateMethod: formValues - Get an object containing form data. - */ - function formValues (form) { - var vals = {}, - radios = {}, - formElements = form.elements, - i = 0, - length = formElements.length, - name, - formElement, - j, - radio, - nodeName; - - for (; i < length; i++) { - formElement = formElements[i]; - nodeName = formElement.nodeName.toLowerCase(); - name = formElement.name; - - // fieldsets & objects come back as form elements, but we don't care about these - // we don't bother with fields that don't have a name - // switch to whitelist? - if ( - nodeName == "fieldset" || - nodeName == "object" || - !name - ) { continue; } - - if (formElement.type == "checkbox" && ! formElement.checked) { - if (! name in vals) { - vals[name] = undefined; - } - } else if (formElement.type == "radio") { - if (radios[name]) { - radios[name][radios[name].length] = formElement; - } else { - radios[name] = [formElement]; - } - } else { - var value = elementValue(formElement); - if (name in vals) { - if (vals[name].push) { - vals[name][vals[name].length] = value; - } else { - vals[name] = [vals[name], value]; - } - } else { - vals[name] = value; - } - } - } - for (i in radios) { - j = 0; - for (length = radios[i].length; j < length; j++) { - radio = radios[i][j]; - name = radio.name; - if (radio.checked) { - vals[radio.name] = radio.value; - break; - } - } - if (! name in vals) { vals[name] = undefined; } - } - return vals; - } - - /* - PrivateFunction: setFormValues - Set values of a form to those in passed in object. - */ - function setFormValues (form, vals) { - var prop, currentField, - fields = {}, - storeType, i = 0, n, len, foundOne, currentFieldType; - - for (prop in vals) { - currentField = form[prop]; - if (currentField && currentField[0]) { // is array of fields - //normalise values to array of vals - vals[prop] = vals[prop] && vals[prop].push ? vals[prop] : [vals[prop]]; - //order the fields by types that matter - fields.radios = []; - fields.checkboxesSelects = []; - fields.multiSelects = []; - fields.other = []; - - for (i = 0; currentField[i]; i++) { - currentFieldType = currentField[i].type; - if (currentFieldType == "radio") { - storeType = "radios"; - } else if (currentFieldType == "select-one" || currentFieldType == "checkbox") { - storeType = "checkboxesSelects"; - } else if (currentFieldType == "select-multiple") { - storeType = "multiSelects"; - } else { - storeType = "other"; - } - //add it to the correct array - fields[storeType][fields[storeType].length] = currentField[i]; - } - - for (i = 0; fields.multiSelects[i]; i++) { - vals[prop] = setValue(fields.multiSelects[i], vals[prop]); - } - for (i = 0; fields.checkboxesSelects[i]; i++) { - setValue(fields.checkboxesSelects[i], ""); - for (n = 0, len = vals[prop].length; n < len; n++) { - if (setValue(fields.checkboxesSelects[i], vals[prop][n])) { - vals[prop].slice(n, 1); - break; - } - } - } - for (i = 0; fields.radios[i]; i++) { - fields.radios[i].checked = false; - foundOne = false; - for (n = 0, len = vals[prop].length; n < len; n++) { - if (setValue(fields.radios[i], vals[prop][n])) { - vals[prop].slice(n, 1); - foundOne = true; - break; - } - if (foundOne) { break; } - } - } - for (i = 0; fields.other[i] && vals[prop][i] !== undefined; i++) { - setValue(fields.other[i], vals[prop][i]); - } - } else if (currentField && currentField.nodeName) { // is single field, easy - setValue(currentField, vals[prop]); - } - } - } - - /* - PrivateFunction: setValue - Set the value of a form element. - - Returns: - values that weren't able to set if array of vals passed (for multi select). Otherwise true if val set, false if not - */ - function setValue (el, val) { - var i = 0, - length, - n = 0, - nlen, - elOption, - optionVal; - - if (el.type == "select-one") { - for (length = el.options.length; i < length; i++) { - if (el.options[i].value == val) { - el.selectedIndex = i; - return true; - } - } - return false; - } else if (el.type == "select-multiple") { - var isArray = !!val.push; - for (i = 0, length = el.options.length; i < length; i++) { - elOption = el.options[i]; - optionVal = elOption.value; - if (isArray) { - elOption.selected = false; - for (nlen = val.length; n < nlen; n++) { - if (optionVal == val[n]) { - elOption.selected = true; - val.splice(n, 1); - break; - } - } - } else { - return elOption.selected = val == optionVal; - } - } - return false; - } else if (el.type == "radio" || el.type == "checkbox") { - el.checked = val == el.value; - return val == el.value; - } else { - el.value = val; - return true; - } - } - - // toplevel implementation - return function (/* [value] */) { - var args = arguments, - val = args[0], - that = this, - i = 0, - length = that.length; - - if (args.length === 0) { - return that[0].nodeName == 'FORM' ? - formValues(that[0]) : - elementValue(that[0]); - } - if (that[0].nodeName == 'FORM') { - if (! typeof val == 'object') { - throw 'value for FORM must be object'; - } - setFormValues(that[0], val); - } else { - for (; i < length; i++) { - setValue(that[i], val); - } - } - return that; - }; - }(), - - /** - @name glow.dom.NodeList#slice - @function - @description Extracts nodes from a NodeList and returns them as a new NodeList. - - @param {Number} start The NodeList index at which to begin extraction. - - If negative, this param specifies a position measured from - the end of the NodeList - - @param {Number} [end] The NodeList index immediately after the end of the extraction. - - If not specified the extraction includes all nodes from the - start to the end of the NodeList. A Negative end specifies - an position measured from the end of the NodeList. - - @returns {glow.dom.NodeList} - - Returns a new NodeList containing the extracted nodes - - @example - var myNodeList = glow.dom.create("
"); - myNodeList = myNodeList.slice(1, 2); // just second div - */ - slice: function (/* start, end */) { - return new r.NodeList().push(Array.prototype.slice.apply(this, arguments)); - }, - - /** - @name glow.dom.NodeList#sort - @function - @description Returns a new NodeList containing the same nodes in order. - - Sort order defaults to document order if no sort function is passed in. - - @param {Function} [func] Function to determine sort order - - This function will be passed 2 nodes (a, b). The function - should return a number less than 0 to sort a lower than b - and greater than 0 to sort a higher than b. - - @returns {glow.dom.NodeList} - - Returns a new NodeList containing the sorted nodes - - @example - // heading elements in document order - var headings = glow.dom.get("h1, h2, h3, h4, h5, h6").sort(); - - //get links in alphabetical (well, lexicographical) order - var links = glow.dom.get("a").sort(function(a, b) { - return ((a.textContent || a.innerText) < (b.textContent || b.innerText)) ? -1 : 1; - }) - */ - sort: function(func) { - var that = this, i=0, aNodes; - - if (!that.length) { return that; } - if (!func) { - if (typeof that[0].sourceIndex == "number") { - // sourceIndex is IE proprietary (but Opera supports) - func = function(a, b) { - return a.sourceIndex - b.sourceIndex; - }; - } else if (that[0].compareDocumentPosition) { - // DOM3 method - func = function(a, b) { - return 3 - (a.compareDocumentPosition(b) & 6); - }; - } else { - // js emulation of sourceIndex - aNodes = getByTagName("*", [doc]); - for (; aNodes[i]; i++) { - aNodes[i]._sourceIndex = i; - } - func = function(a, b) { - return a._sourceIndex - b._sourceIndex; - }; - } - } - - return r.get([].sort.call(that, func)); - }, - - /** - @name glow.dom.NodeList#filter - @function - @description Filter the NodeList using a function - - The supplied function will be called for each node in the NodeList. - - The index of the node will be provided as the first parameter, and - 'this' will refer to the node. - - Return true to keep the node, or false to remove it. - - @param {Function} func Function to test each node - - @returns {glow.dom.NodeList} - - Returns a new NodeList containing the filtered nodes - - @example - // return images with a width greater than 320 - glow.dom.get("img").filter(function (i) { - return this.width > 320; - }); - */ - filter: function(callback) { - var ret = [], //result - ri = 0, - i = 0, - length = this.length; - for (; i < length; i++) { - if (callback.apply(this[i], [i])) { - ret[ri++] = this[i]; - } - } - return r.get(ret); - }, - - /** - @name glow.dom.NodeList#children - @function - @description Gets the child elements of each node as a new NodeList. - - @returns {glow.dom.NodeList} - - Returns a new NodeList containing all the child nodes - - @example - // get all list items - var items = glow.dom.get("ul, ol").children(); - */ - children: function() { - var ret = [], - ri = 0, - i = 0, - n = 0, - length = this.length, - childTmp; - - for (; i < length; i++) { - ret = ret.concat( getChildElms(this[i]) ); - } - return r.get(ret); - }, - - /** - @name glow.dom.NodeList#parent - @function - @description Gets the unique parent nodes of each node as a new NodeList. - - @returns {glow.dom.NodeList} - - Returns a new NodeList containing the parent nodes, with - duplicates removed - - @example - // elements which contain links - var parents = glow.dom.get("a").parent(); - */ - parent: function() { - var ret = [], - ri = 0, - i = 0, - length = this.length; - - for (; i < length; i++) { - ret[ri++] = this[i].parentNode; - } - return r.get(unique(ret)); - }, - - /** - @name glow.dom.NodeList#next - @function - @description Gets the next sibling element for each node as a new NodeList. - - @returns {glow.dom.NodeList} - - A new NodeList containing the next sibling elements. - - @example - // gets the element following #myLink (if there is one) - var next = glow.dom.get("#myLink").next(); - */ - next: function() { - return getNextOrPrev(this, "next"); - }, - - /** - @name glow.dom.NodeList#prev - @function - @description Gets the previous sibling element for each node as a new NodeList. - - @returns {glow.dom.NodeList} - - A new NodeList containing the previous sibling elements. - - @example - // gets the elements before #myLink (if there is one) - var previous = glow.dom.get("#myLink").previous(); - */ - prev: function() { - return getNextOrPrev(this, "previous"); - }, - - /** - @name glow.dom.NodeList#is - @function - @description Tests if all the nodes match a CSS selector. - - Jake: I'm deprecating this until we have time to make it faster (probably when we change our CSS selector engine) - - @deprecated - @param {String} selector A CSS selector string - - @returns {Boolean} - - @example - var bigHeadings = glow.dom.get("h1, h2, h3"); - - // true - if (bigHeadings.is("h1, h2, h3, h4, h5, h6")) ... - - // false - if (bigHeadings.is("a")) ... - */ - is: function (selector) { - // TODO - this implementation needs to be optimized - var nodes = glow.dom.get(selector), - i = 0, - iLen = this.length, - j, - jLen; - - node: - for (; i < iLen; i++) { - for (j = 0, jLen = nodes.length; j < jLen; j++) { - if (this[i] == nodes[j]) { - continue node; - } - } - return false; - } - return true; - }, - - /** - @name glow.dom.NodeList#text - @function - @description Gets the inner text of the first node, or set the inner text of all matched nodes. - - @param {String} [text] String to set as inner text of nodes - - @returns {glow.dom.NodeList | String} - - If the text argument is passed then the NodeList is - returned, otherwise the text is returned. - - @example - // set text - var div = glow.dom.create("
").text("Hello World!"); - - // get text - var greeting = div.text(); - */ - text: function (/* text */) { - var args = arguments, - i = 0, - that = this, - length = that.length; - - if (args.length > 0) { - for (; i < length; i++) { - that[i].innerHTML = ""; - that[i].appendChild(doc.createTextNode(args[0])); - } - return that; - } - //innerText (empty) and textContent (undefined) don't work in safari 2 for hidden elements - return that[0].innerText || that[0].textContent == undefined ? getTextNodeConcat(that[0]) : that[0].textContent; - }, - - /** - @name glow.dom.NodeList#empty - @function - @description Removes the contents of all the nodes. - - @returns {glow.dom.NodeList} - - @example - // remove the contents of all textareas - glow.dom.get("textarea").empty(); - */ - empty: function () { - /* - NOTE: I changed this to destroy all nodes within the parent, but - seemed backwards incompatible with our own timetable demo - so we best hadn't do it until Glow 2 - */ - var i = 0, - len = this.length; - - for (; i < len; i++) { - while(this[i].firstChild) { - this[i].removeChild(this[i].firstChild); - } - } - return this; - }, - - /** - @name glow.dom.NodeList#remove - @function - @description Removes each node from its parent node. - If you no longer need the nodes, consider using - {@link glow.dom.NodeList#destroy destroy} - - @returns {glow.dom.NodeList} - - @example - // take all the links out of a document - glow.dom.get("a").remove(); - */ - remove: function () { - for (var that = this, i = 0, length = that.length, parentNode; i < length; i++) { - if (parentNode = that[i].parentNode) { - parentNode.removeChild(that[i]); - } - } - return that; - }, - - /** - @name glow.dom.NodeList#destroy - @function - @description Removes each node from the DOM - The node will actually be destroyed to free up memory - - @returns {glow.dom.NodeList} An empty NodeList - - @example - // destroy all links in the document - glow.dom.get("a").destroy(); - */ - destroy: function () { - this.appendTo(tmpDiv); - // destroy nodes - tmpDiv.innerHTML = ""; - // empty the nodelist - Array.prototype.splice.call(this, 0, this.length); - return this; - }, - - /** - @name glow.dom.NodeList#clone - @function - @description Gets a new NodeList containing a clone of each node. - - @param {Boolean} [cloneListeners=false] Also clone any event listeners assigned using Glow - - @returns {glow.dom.NodeList} - - Returns a new NodeList containing clones of all the nodes in - the NodeList - - @example - // get a copy of all heading elements - var myClones = glow.dom.get("h1, h2, h3, h4, h5, h6").clone(); - - @example - // get a copy of all anchor elements with - var myAnchors = glow.dom.get("a").clone(true); - */ - clone: function (cloneListeners) { - var ret = [], - i = this.length, - allCloneElms, - eventIdProp = '__eventId' + glow.UID; - - while (i--) { - ret[i] = this[i].cloneNode(true); - } - - // some browsers (ie) also clone node properties as attributes - // we need to get rid of the eventId. - if (nodePropertiesCloned && !isXml(ret[0])) { - allCloneElms = r.get( ret ).get("*").push( ret ); - i = allCloneElms.length; - while(i--) { - allCloneElms[i][eventIdProp] = null; - } - } - - // shall we clone events too? - if (cloneListeners) { - // check the stuff we need is hanging around, we don't want - // glow.dom to be dependant on glow.events as it's a circular - // dependency - if ( !glow.events ) { - throw "glow.events required to clone event listeners"; - } - - glow.events._copyListeners( - this.get("*").push( this ), - allCloneElms || r.get( ret ).get("*").push( ret ) - ); - } - - return r.get(ret); - }, - - /** - @name glow.dom.NodeList#html - @function - @description Gets the HTML content of the first node, or set the HTML content of all nodes. - -

This method is not applicable to XML NodeLists.

- - @param {String} [html] String to set as inner HTML of nodes - - @returns {glow.dom.NodeList | String} - - If the html argument is passed, then the NodeList is - returned, otherwise the inner HTML of the first node is returned. - - @example - // get the html in #footer - var footerContents = glow.dom.get("#footer").html(); - - @example - // set a new footer - glow.dom.get("#footer").html("Hello World!"); - */ - html: function (newHtml) { - var i = 0, - length = this.length; - - if (newHtml != undefined) { - for (; i < length; i++) { - this[i].innerHTML = newHtml; - } - return this; - } - return this[0].innerHTML; - }, - - /** - @name glow.dom.NodeList#width - @function - @description Gets the width of the first node in pixels or sets the width of all nodes - -

This method is not applicable to XML NodeLists.

- - Return value does not include the padding or border of the - element in browsers supporting the correct box model. - - You can use this to easily get the width of the document or - window, see example below. - - @param {Number} [width] New width in pixels. - - @returns {glow.dom.NodeList | Number} - - Width of first element in pixels, or NodeList when setting widths - - @example - // get the width of #myDiv - glow.dom.get("#myDiv").width(); - - @example - // set the width of list items in #myDiv to 200 pixels - glow.dom.get("#myDiv li").width(200); - - @example - // get the height of the document - glow.dom.get(document).width() - - @example - // get the height of the window - glow.dom.get(window).width() - */ - width: function(width) { - if (width == undefined) { - return getElmSize(this[0]).width; - } - setElmsSize(this, width, "width"); - return this; - }, - - /** - @name glow.dom.NodeList#height - @function - @description Gets the height of the first element in pixels or sets the height of all nodes - -

This method is not applicable to XML NodeLists.

- - Return value does not include the padding or border of the element in - browsers supporting the correct box model. - - You can use this to easily get the height of the document or - window, see example below. - - @param {Number} [height] New height in pixels. - - @returns {glow.dom.NodeList | Number} - - Height of first element in pixels, or NodeList when setting heights. - - @example - // get the height of #myDiv - glow.dom.get("#myDiv").height(); - - @example - // set the height of list items in #myDiv to 200 pixels - glow.dom.get("#myDiv li").height(200); - - @example - // get the height of the document - glow.dom.get(document).height() - - @example - // get the height of the window - glow.dom.get(window).height() - */ - height: function(height) { - if (height == undefined) { - return getElmSize(this[0]).height; - } - setElmsSize(this, height, "height"); - return this; - }, - - /** - @name glow.dom.NodeList#show - @function - @description Shows all hidden items in the NodeList. - @returns {glow.dom.NodeList} - @example - // Show element with ID myDiv - glow.dom.get("#myDiv").show(); - @example - // Show all list items within #myDiv - glow.dom.get("#myDiv li").show(); - */ - show: function() { - var i = 0, - len = this.length, - currItem, - itemStyle; - for (; i < len; i++) { - /* Create a NodeList for the current item */ - currItem = r.get(this[i]); - itemStyle = currItem[0].style; - if (currItem.css("display") == "none") { - itemStyle.display = ""; - itemStyle.visibility = "visible"; - /* If display is still none, set to block */ - if (currItem.css("display") == "none") { - itemStyle.display = "block"; - } - } - } - return this; - }, - - /** - @name glow.dom.NodeList#hide - @function - @description Hides all items in the NodeList. - @returns {glow.dom.NodeList} - @example - // Hides all list items within #myDiv - glow.dom.get("#myDiv li").hide(); - */ - hide: function() { - var i = 0, - len = this.length, - currItem; - for (; i < len; i++) { - currItem = r.get(this[i]); - currItem[i].style.display = "none"; - currItem[i].style.visibility = "hidden"; - } - return this; - }, - - /** - @name glow.dom.NodeList#css - @function - @description Gets a CSS property for the first node or sets a CSS property on all nodes. - -

This method is not applicable to XML NodeLists.

- - If a single property name is passed, the corresponding value - from the first node will be returned. - - If a single property name is passed with a value, that value - will be set on all nodes on the NodeList. - - If an array of properties name is passed with no value, the return - value is the sum of those values from the first node in the NodeList. - This can be useful for getting the total of left and right padding, - for example. - - Return values are strings. For instance, "height" will return - "25px" for an element 25 pixels high. You may want to use - parseInt to convert these values. - - Here are the compatible properties you can get, if you use one - which isn't on this list the return value may differ between browsers. - -
-
width
Returns pixel width, eg "25px". Can be used even if width has not been set with CSS.
-
height
Returns pixel height, eg "25px". Can be used even if height has not been set with CSS.
-
top, right, bottom, left
Pixel position relative to positioned parent, or original location if position:relative, eg "10px". These can be used even if position:static.
-
padding-top
Pixel size of top padding, eg "5px"
-
padding-right
Pixel size of right padding, eg "5px"
-
padding-bottom
Pixel size of bottom padding, eg "5px"
-
padding-left
Pixel size of left padding, eg "5px"
-
margin-top
Pixel size of top margin, eg "5px"
-
margin-right
Pixel size of right margin, eg "5px"
-
margin-bottom
Pixel size of bottom margin, eg "5px"
-
margin-left
Pixel size of left margin, eg "5px"
-
border-top-width
Pixel size of top border, eg "5px"
-
border-right-width
Pixel size of right border, eg "5px"
-
border-bottom-width
Pixel size of bottom border, eg "5px"
-
border-left-width
Pixel size of left border, eg "5px"
-
border-*-style
eg "dotted"
-
border-*-color
returns colour in format "rgb(255, 255, 255)", return value also has properties r, g & b to get individual values as integers
-
color
returns colour in format "rgb(255, 255, 255)", return value also has properties r, g & b to get individual values as integers
-
list-style-position
eg "outside"
-
list-style-type
eg "square"
-
list-style-image
Returns full image path, eg "url(http://www.bbc.co.uk/lifestyle/images/bullets/1.gif)" or "none"
-
background-image
Returns full image path, eg "url(http://www.bbc.co.uk/lifestyle/images/bgs/1.gif)" or "none"
-
background-attachment
eg "scroll"
-
background-repeat
eg "repeat-x"
-
direction
eg "ltr"
-
font-style
eg "italic"
-
font-variant
eg "small-caps"
-
line-height
Pixel height, eg "30px". Note, Opera may return value with 2 decimal places, eg "30.00px"
-
letter-spacing
Pixel spacing, eg "10px"
-
text-align
eg "right"
-
text-decoration
eg "underline"
-
text-indent
Pixel indent, eg "10px"
-
white-space
eg "nowrap"
-
word-spacing
Pixel spacing, eg "5px"
-
float
eg "left"
-
clear
eg "right"
-
opacity
Value between 0 & 1. In IE, this value comes from the filter property (/100)
-
position
eg "relative"
-
z-index
eg "32"
-
display
eg "block"
-
text-transform
eg "uppercase"
-
- - The following return values that may be usable but have differences in browsers - -
-
font-family
Some browsers return the font used ("Verdana"), others return the full list found in the declaration ("madeup, verdana, sans-serif")
-
font-size
Returns size as pixels except in IE, which will return the value in the same units it was set in ("0.9em")
-
font-weight
Returns named values in some browsers ("bold"), returns computed weight in others ("700")
-
- - @param {String | String[]} property The CSS property name or array of names - - @param {String} [value] The value to apply - - @returns {glow.dom.NodeList | String} - - Returns the CSS value from the first node, or NodeList when setting values - - @example - // get value from first node - glow.dom.get("#myDiv").css("display"); - - @example - // set left padding to 10px on all nodes - glow.dom.get("#myDiv").css("padding-left", "10px"); - - @example - // "30px", total of left & right padding - glow.dom.get("#myDiv").css(["padding-left", "padding-right"]); - - @example - // where appropriate, px is assumed when no unit is passed - glow.dom.get("#myDiv").css("height", 300); - */ - css: function(prop, val) { - var that = this, - thisStyle, - i = 0, - len = that.length, - originalProp = prop; - - if (val != undefined) { //setting stuff - prop = toStyleProp(prop); - for (; i < len; i++) { - thisStyle = that[i].style; - - if (typeof val == "number" && hasUnits.test(originalProp)) { - val = val.toString() + "px"; - } - if (prop == "opacity" && env.ie) { - //in IE the element needs hasLayout for opacity to work - thisStyle.zoom = "1"; - if (val === "") { - thisStyle.filter = ""; - } else { - thisStyle.filter = "alpha(opacity=" + Math.round(Number(val, 10) * 100) + ")"; - } - } else { - thisStyle[prop] = val; - } - } - return that; - } else { //getting stuff - if (!len) { return; } - return getCssValue(that[0], prop); - } - }, - - /** - @name glow.dom.NodeList#offset - @function - @description Gets the offset from the top left of the document. - - If the NodeList contains multiple items, the offset of the - first item is returned. - - @returns {Object} - Returns an object with "top" & "left" properties in pixels - - @example - glow.dom.get("#myDiv").offset().top - */ - offset: function () { - // http://weblogs.asp.net/bleroy/archive/2008/01/29/getting-absolute-coordinates-from-a-dom-element.aspx - great bit of research, most bugfixes identified here (and also jquery trac) - var elm = this[0], - docScrollPos = scrollPos(); - - //this is simple(r) if we can use 'getBoundingClientRect' - if (elm.getBoundingClientRect) { - var rect = elm.getBoundingClientRect(); - return { - top: rect.top - /* - getBoundingClientRect is realive to top left of - the viewport, so we need to sort out scrolling offset - */ - + docScrollPos.y - /* - IE adds the html element's border to the value. We can - deduct this value using client(Top|Left). However, if - the user has done html{border:0} clientTop will still - report a 2px border in IE quirksmode so offset will be off by 2. - Hopefully this is an edge case but we may have to revisit this - in future - */ - - docElm.clientTop, - - left: rect.left //see above for docs on all this stuff - + docScrollPos.x - - docElm.clientLeft - }; - } else { //damnit, let's go the long way around - var top = 0, - left = 0, - originalElm = elm, - nodeNameLower, - //does the parent chain - involvesFixedElement = false, - offsetParentBeforeBody; - - //add up all the offset positions - do { - left += elm.offsetLeft; - top += elm.offsetTop; - - //if css position is fixed, we need to add in the scroll offset too, catch it here - if (getCssValue(elm, "position") == "fixed") { - involvesFixedElement = true; - } - - //gecko & webkit (safari 3) don't add on the border for absolutely positioned items - if (env.gecko || env.webkit > 500) { - left += parseInt(getCssValue(elm, "border-left-width")); - top += parseInt(getCssValue(elm, "border-top-width")); - } - - //we need the offset parent (before body) later - if (elm.nodeName.toLowerCase != "body") { - offsetParentBeforeBody = elm; - } - - } while (elm = elm.offsetParent); - - //deduct all the scroll offsets - elm = originalElm; - while ((elm = elm.parentNode) && (elm != docBody) && (elm != docElm)) { - left -= elm.scrollLeft; - top -= elm.scrollTop; - - //FIXES - //gecko doesn't add the border of contained elements to the offset (overflow!=visible) - if (env.gecko && getCssValue(elm, "overflow") != "visible") { - left += parseInt(getCssValue(elm, "border-left-width")); - top += parseInt(getCssValue(elm, "border-top-width")); - } - } - - //if we found a fixed position element we need to add the scroll offsets - if (involvesFixedElement) { - left += docScrollPos.x; - top += docScrollPos.y; - } - - //FIXES - // Webkit < 500 body's offset gets counted twice for absolutely-positioned elements (or if there's a fixed element) - // Gecko - non-absolutely positioned elements that are direct children of body get the body offset counted twice - if ( - (env.webkit < 500 && (involvesFixedElement || getCssValue(offsetParentBeforeBody, "position") == "absolute")) || - (env.gecko && getCssValue(offsetParentBeforeBody, "position") != "absolute") - ) { - left -= docBody.offsetLeft; - top -= docBody.offsetTop; - } - - return {left:left, top:top}; - } - }, - - /** - @name glow.dom.NodeList#append - @function - @description Appends the given elements to each node. - - If there is more than one node in the NodeList, then the given elements - are appended to the first node and clones are appended to the other - nodes. - -

String nodespecs cannot be used with XML NodeLists

- - @param {String | Element | glow.dom.NodeList} nodespec HTML string, Element or NodeList to append to each node. - - @returns {glow.dom.NodeList} - - @example - // ends every paragraph with '...' - glow.dom.get('p').append( - '...' - ); - */ - append: function (nodeSpec) { - var that = this, - j = 0, - i = 1, - length = that.length, - nodes; - - if (length == 0) { return that; } - nodes = typeof nodeSpec == "string" ? nodelistToArray(stringToNodes(nodeSpec)) : - nodeSpec.nodeType ? [nodeSpec] : nodelistToArray(nodeSpec); - - for (; nodes[j]; j++) { - that[0].appendChild(nodes[j]); - } - for (; i < length; i++) { - for (j = 0; nodes[j]; j++) { - that[i].appendChild(nodes[j].cloneNode(true)); - } - } - return that; - }, - - /** - @name glow.dom.NodeList#prepend - @function - @description Prepends the given elements to each node. - - If there is more than one node in the NodeList, then the given elements - are prepended to the first node and clones are prepended to the other - nodes. - -

String nodespecs cannot be used with XML NodeLists

- - @param {String | Element | glow.dom.NodeList} nodespec HTML string, Element or NodeList to prepend to each node. - - @returns {glow.dom.NodeList} - - @example - // prepends every paragraph with 'Paragraph: ' - glow.dom.get('p').prepend( - 'Paragraph: ' - ); - */ - prepend: function (nodeSpec) { - var that = this, - j = 0, - i = 1, - length = that.length, - nodes, - first; - - if (length == 0) { return that; } - - nodes = typeof nodeSpec == "string" ? nodelistToArray(stringToNodes(nodeSpec)) : - nodeSpec.nodeType ? [nodeSpec] : nodelistToArray(nodeSpec); - - first = that[0].firstChild; - - for (; nodes[j]; j++) { - that[0].insertBefore(nodes[j], first); - } - - for (; i < length; i++) { - first = that[i].firstChild; - for (j = 0; nodes[j]; j++) { - that[i].insertBefore(nodes[j].cloneNode(true), first); - } - } - return that; - }, - - /** - @name glow.dom.NodeList#appendTo - @function - @description Appends the NodeList to elements. - - If more than one element is given (i.e. if the nodespec argument - is an array of nodes or a NodeList) the NodeList will be - appended to the first element and clones to each subsequent - element. - - @param {String | Element | glow.dom.NodeList} nodespec CSS selector string, Element or NodeList to append the NodeList to. - - @returns {glow.dom.NodeList} - - @example - // appends '...' to every paragraph - glow.dom.create('...').appendTo('p'); - */ - appendTo: function (nodes) { - if (! (nodes instanceof r.NodeList)) { nodes = r.get(nodes); } - nodes.append(this); - return this; - }, - - /** - @name glow.dom.NodeList#prependTo - @function - @description Prepends the NodeList to elements. - - If more than one element is given (i.e. if the nodespec argument - is an array of nodes or a NodeList) the NodeList will be - prepended to the first element and clones to each subsequent - element. - - @param {String | Element | glow.dom.NodeList} nodespec CSS selector string, Element or NodeList to prepend the NodeList to. - - @returns {glow.dom.NodeList} - - @example - // prepends 'Paragraph: ' to every paragraph - glow.dom.create('Paragraph: ').prependTo('p'); - */ - prependTo: function (nodes) { - if (! (nodes instanceof r.NodeList)) { nodes = r.get(nodes); } - nodes.prepend(this); - return this; - }, - - /** - @name glow.dom.NodeList#after - @function - @description Inserts elements after each node. - - If there is more than one node in the NodeList, the elements - will be inserted after the first node and clones inserted - after each subsequent node. - - @param {String | Element | glow.dom.NodeList} nodespec HTML string, Element or NodeList to insert after each node - - @returns {glow.dom.NodeList} - - @example - // adds a paragraph after each heading - glow.dom.get('h1, h2, h3').after('

...

'); - */ - after: function (nodeSpec) { - var that = this, - length = that.length, - nodes, - nodesLen, - j, - i = 1, - cloned; - - if (length == 0) { return that; } - - nodes = typeof nodeSpec == "string" ? r.create(nodeSpec) : - nodeSpec instanceof r.NodeList ? nodeSpec : - r.get(nodeSpec); - - nodesLen = nodes.length; - - for (j = nodesLen - 1; j >= 0; j--) { - that[0].parentNode.insertBefore(nodes[j], that[0].nextSibling); - } - for (; i < length; i++) { - cloned = nodes.clone(); - - for (j = nodesLen - 1; j >= 0; j--) { - that[i].parentNode.insertBefore(cloned[j], that[i].nextSibling); - } - } - return that; - }, - - /** - @name glow.dom.NodeList#before - @function - @description Inserts elements before each node. - - If there is more than one node in the NodeList, the elements - will be inserted before the first node and clones inserted - before each subsequent node. - - @param {String | Element | glow.dom.NodeList} nodespec HTML string, Element or NodeList to insert before each node - - @returns {glow.dom.NodeList} - - @example - // adds a heading before each - glow.dom.get('p').before('

Paragraph:

'); - */ - before: function (nodeSpec) { - var that = this, - length = that.length, - j = 0, - i = 1, - nodes, - nodesLen, - cloned; - - if (length == 0) { return that; } - - nodes = typeof nodeSpec == "string" ? r.create(nodeSpec) : - nodeSpec instanceof r.NodeList ? nodeSpec : - r.get(nodeSpec); - - nodesLen = nodes.length; - - for (; j < nodesLen; j++) { - that[0].parentNode.insertBefore(nodes[j], that[0]); - } - for (; i < length; i++) { - cloned = nodes.clone(); - for (j = 0; j < nodesLen; j++) { - that[i].parentNode.insertBefore(cloned[j], that[i]); - } - } - return that; - }, - - /** - @name glow.dom.NodeList#insertAfter - @function - @description Insert the NodeList after each of the given elements. - - If more than one element is passed in, the NodeList will be - inserted after the first element and clones inserted after each - subsequent element. - - @param {String | Element | glow.dom.NodeList} nodespec CSS selector string, Element or NodeList to insert the NodeList after - - @returns {glow.dom.NodeList} - - @example - // adds a paragraph after each heading - glow.dom.create('

HAI!

').insertAfter('h1, h2, h3'); - */ - insertAfter: function (nodes) { - if (! (nodes instanceof r.NodeList)) { nodes = r.get(nodes); } - nodes.after(this); - return this; - }, - - /** - @name glow.dom.NodeList#insertBefore - @function - @description Insert the NodeList before each of the given elements. - - If more than one element is passed in, the NodeList will be - inserted before the first element and clones inserted before each - subsequent element. - - @param {String | Element | glow.dom.NodeList} nodespec CSS selector string, Element or NodeList to insert the NodeList before - - @returns {glow.dom.NodeList} - - @example - // adds a heading before each paragraph - glow.dom.create('

Paragraph:

').insertBefore('p'); - */ - insertBefore: function (nodes) { - if (! (nodes instanceof r.NodeList)) { nodes = r.get(nodes); } - nodes.before(this); - return this; - }, - - - /** - @name glow.dom.NodeList#replaceWith - @function - @description Replace nodes on the page with given elements. - - @param {glow.dom.NodeList | String} nodespec Elements to insert into the document. - - If more than one node is to be replaced then nodespec will be - cloned for additional elements. If a string is provided it will - be treated as HTML and converted into elements. - - @returns {glow.dom.NodeList} - Returns a new NodeList containing the nodes which have been removed - */ - replaceWith: function (nodeSpec) { - /* - we need to put a placeholder in first in case the new stuff - has the same ids as stuff being replaced. This causes issues - in safari 2, the new element can't be picked up with getElementById - */ - if (env.webkit < 500) { - this.after(placeholderElm).remove(); - r.get("u.glow-placeholder").after(nodeSpec).remove(); - } else { - this.after(nodeSpec).remove() - } - return this; - }, - - /** - @name glow.dom.NodeList#get - @function - @description Gets decendents of nodes that match a CSS selector. - - @param {String} selector CSS selector - - @returns {glow.dom.NodeList} - Returns a new NodeList containing matched elements - - @example - // create a new NodeList - var myNodeList = glow.dom.create(""); - - // get 'a' tags that are decendants of the NodeList nodes - myNewNodeList = myNodeList.get("a"); - */ - get: function() { - - /* - PrivateFunction: compileSelector - Compile a CSS selector to an AST. - - Arguments: - sSelector - A string containing the CSS selector (or comma separated group of selectors). - - Returns: - An array containing an AST for each selector in the group. - */ - function compileSelector(sSelector) { - //return from cache if possible - if (cssCache[sSelector]) { - return cssCache[sSelector]; - } - - var r = [], //array of result objects - ri = 0, //results index - comb, //current combinator - tagTmp, - idTmp, - aRx, //temp regex result - matchedCondition, //have we matched a condition? - sLastSelector, //holds last copy of selector to prevent infinite loop - firstLoop = true; - - while (sSelector && sSelector != sLastSelector) { - tagTmp = ""; - idTmp = ""; - //protect us from infinite loop - sLastSelector = sSelector; - - //start by getting the scope (combinator) - if (aRx = cssRegex.combinator.exec(sSelector)) { - comb = aRx[1]; - sSelector = sSelector.slice(aRx[0].length); - } - //look for optimal id & tag searching - if (aRx = cssRegex.tagName.exec(sSelector)) { - tagTmp = aRx[1]; - sSelector = sSelector.slice(aRx[0].length); - } - if (aRx = cssRegex.classNameOrId.exec(sSelector)) { - if (aRx[1] == "#") { - idTmp = aRx[2]; - sSelector = sSelector.slice(aRx[0].length); - } - } - if (!comb) { //use native stuff - if (idTmp && firstLoop) { - r[ri++] = [getByIdQuick, [idTmp.replace(/\\/g, ""), tagTmp || "*", null]]; - } else { - r[ri++] = [getByTagName, [tagTmp || "*", null]]; - if (idTmp) { - r[ri++] = [hasId, [idTmp.replace(/\\/g, ""), null]]; - } - } - } else if (comb == ">") { - r[ri++] = [getChildren, [null]]; - if (idTmp) { - r[ri++] = [hasId, [idTmp.replace(/\\/g, ""), null]]; - } - if (tagTmp && tagTmp != "*") { //uses tag - r[ri++] = [isTag, [tagTmp, null]]; - } - } - - //other conditions can appear in any order, so here we go: - matchedCondition = true; - while (matchedCondition) { - //look for class or ID - if (sSelector.charAt(0) == "#" || sSelector.charAt(0) == ".") { - if (aRx = cssRegex.classNameOrId.exec(sSelector)) { - if (sSelector.charAt(0) == "#") { //is ID - //define ID, remove escape chars - r[ri++] = [hasId, [aRx[2].replace(/\\/g, ""), null]]; - } else { //is class - r[ri++] = [hasClassName, [aRx[2].replace(/\\/g, ""), null]]; - } - sSelector = sSelector.slice(aRx[0].length); - } else { - //make this more user friendly? - throw new Error("Invalid Selector"); - } - } else { - matchedCondition = false; - } - } - - firstLoop = false; - } - - if (sSelector !== "") { - //make this more user friendly? - throw new Error("Invalid Selector"); - } - - //add to cache and return - return cssCache[sSelector] = r; - } - - /* - PrivateFunction: fetchElements - Get elements which match array of compiled conditions based on - an initial context. - - Arguments: - a - (object) CSS selector AST object. - initialContext - DOM Element or DOM Document to search within. - - Returns: - An array of matching elements. - */ - function fetchElements(a, initialContext) { - var context = initialContext; //elements to look within - - for (var i = 0, al = a.length; i < al; i++) { - a[i][1][a[i][1].length - 1] = context; - context = a[i][0].apply(this, a[i][1]); - } - return context; - } - - /* - PrivateFunction: getByIdQuick - Get an element with a specific tag name, within a context. - - Arguments: - id - (string) the id of the element. - tagName - (string) the name of the element. - context - DOM Element or DOM Document in which to find the element. - - Returns: - A DOM Element matching the specified criteria. - */ - function getByIdQuick(id, tagName, context) { - var r = [], ri = 0, notQuick = [], notQuicki = 0, tmpNode; - for (var i = 0, length = context.length; i < length; i++) { - if (context[i].getElementById) { - tmpNode = context[i].getElementById(id); - if (tmpNode && (tmpNode.tagName == tagName.toUpperCase() || tagName == "*" || tmpNode.tagName == tagName)) { - r[ri++] = tmpNode; - } - } else { - notQuick[notQuicki++] = context[i]; - } - } - //deal with the ones we couldn't do quick - if (notQuick[0]) { - notQuick = getByTagName(tagName, notQuick); - notQuick = hasId(id, notQuick); - } - return r.concat(notQuick); - } - - function getChildren(context) { - var r = [], - i = 0, - len = context.length; - - for (; i < len; i++) { - append( r, getChildElms(context[i]) ); - } - return r; - } - - function hasId(id, context) { - for (var i = 0, length = context.length; i < length; i++) { - if (context[i].id == id) { - //is this a safe optimisation? - return [context[i]]; - } - } - return []; - } - - /* - PrivateFunction: isTag - Get an array of elements within an array that have a given tag name. - - Arguments: - tagName - (string) the name of the element. - context - (array) elements to match. - - Returns: - An array of matching elements. - */ - function isTag(tagName, context) { - var r = [], ri = 0; - for (var i = 0, length = context.length; i < length; i++) { - if (context[i].tagName == tagName.toUpperCase() || context[i].tagName == tagName) { - r[ri++] = context[i]; - } - } - return r; - } - - /* - PrivateFunction: hasClassName - Get elements that have a given class name from a provided array of elements. - - Arguments: - className - (string) the name of the class. - context - (array) the DOM Elements to match. - - Returns: - An array of matching DOM Elements. - */ - function hasClassName(className, context) { - var r = [], ri = 0; - for (var i = 0, length = context.length; i < length; i++) { - if ((" " + context[i].className + " ").indexOf(" " + className + " ") != -1) { - r[ri++] = context[i]; - } - } - return r; - } - - /* - PrivateFunction: getBySelector - Get elements within a context by a CSS selector. - - Arugments: - sSelector - (string) CSS selector. - context - DOM Document or DOM Element to search within. - - Returns: - An array of DOM Elements. - */ - function getBySelector(sSelector, context) { - var aCompiledCSS; // holds current compiled css statement - var r = []; - //split multiple selectors up - var aSelectors = sSelector.split(","); - //process each - for (var i = 0, nSelLen = aSelectors.length; i < nSelLen; i++) { - aCompiledCSS = compileSelector(glow.lang.trim(aSelectors[i])); - //get elements from DOM - r = r.concat(fetchElements(aCompiledCSS, context)); - } - return r; - } - - /* - PrivateFunction: getIfWithinContext - Get elements from a set of elements that are within at least one of another - set of DOM Elements. - - Arguments: - nodes - DOM Elements to take the results from. - context - DOM Elements that returned elements must be within. - - Returns: - An array of DOM Elements. - */ - function getIfWithinContext(nodes, context) { - nodes = nodes.length ? nodes : [nodes]; - var r = []; //to return - var nl; - - //loop through nodes - for (var i = 0; nodes[i]; i++) { - nl = glow.dom.get(nodes[i]); - //loop through context nodes - for (var n = 0; context[n]; n++) { - if (nl.isWithin(context[n])) { - r[r.length] = nl[0]; - break; - } - } - } - return r; - } - - // main implementation - return function(sSelector) { - // no point trying if there's no current context - if (!this.length) { return this; } - - var r = []; //nodes to return - // decide what to do by arg - for (var i = 0, argLen = arguments.length; i < argLen; i++) { - if (typeof arguments[i] == "string") { // css selector string - r = r.concat(getBySelector(arguments[i], this)); - // append(r, getBySelector(arguments[i], this)); - } else { // nodelist, array, node - r = r.concat(getIfWithinContext(arguments[i], this)); - // append(r, getIfWithinContext(arguments[i], this)); - } - } - - // strip out duplicates, wrap in nodelist - return glow.dom.get(unique(r)); - }; - }() - }; - - //some init stuff. Using u to make it quicker to get by tag name :) - placeholderElm = r.create(''); - //set up glow.dom - glow.dom = r; - - } -}); -/** -@name glow.events -@namespace -@description Native browser and custom events -@see Using event listeners -@see Firing event listeners -@see Removing event listeners -*/ -(window.gloader || glow).module({ - name: "glow.events", - library: ["glow", "1.5.1"], - depends: [["glow", "1.5.1", 'glow.dom']], - builder: function(glow) { - - var $ = glow.dom.get; - var r = {}; - var eventid = 1; - var objid = 1; - // object (keyed by obj id), containing object (keyed by event name), containing arrays of listeners - var listenersByObjId = {}; - // object (keyed by ident) containing listeners - var listenersByEventId = {}; - var domListeners = {}; - var psuedoPrivateEventKey = '__eventId' + glow.UID; - var psuedoPreventDefaultKey = psuedoPrivateEventKey + 'PreventDefault'; - var psuedoStopPropagationKey = psuedoPrivateEventKey + 'StopPropagation'; - - var topKeyListeners = {}; - var keyListenerId = 1; - var keyListeners = {}; - var keyTypes = {}; - - var CTRL = 1; - var ALT = 2; - var SHIFT = 4; - - var specialPrintables = { - TAB : '\t', - SPACE : ' ', - ENTER : '\n', - BACKTICK : '`' - }; - - var keyNameAliases = { - '96' : 223 - }; - - var keyNameToCode = { - CAPSLOCK : 20, NUMLOCK : 144, SCROLLLOCK : 145, BREAK : 19, - BACKTICK : 223, BACKSPACE : 8, PRINTSCREEN : 44, MENU : 93, SPACE : 32, - SHIFT : 16, CTRL : 17, ALT : 18, - ESC : 27, TAB : 9, META : 91, RIGHTMETA : 92, ENTER : 13, - F1 : 112, F2 : 113, F3 : 114, F4 : 115, - F5 : 116, F6 : 117, F7 : 118, F8 : 119, - F9 : 120, F10 : 121, F11 : 122, F12 : 123, - INS : 45, HOME : 36, PAGEUP : 33, - DEL : 46, END : 35, PAGEDOWN : 34, - LEFT : 37, UP : 38, RIGHT : 39, DOWN : 40 - }; - var codeToKeyName = {}; - for (var i in keyNameToCode) { - codeToKeyName['' + keyNameToCode[i]] = i; - } - - var operaBrokenChars = '0123456789=;\'\\\/#,.-'; - - /* - PrivateMethod: removeKeyListener - Removes a listener for a key combination. - - Arguments: - ident - identifier returned from addKeyListener. - */ - - function removeKeyListener (ident) { - var keyType = keyTypes[ident]; - if (! keyType) { return false; } - var listeners = keyListeners[keyType]; - if (! listeners) { return false; } - for (var i = 0, len = listeners.length; i < len; i++) { - if (listeners[i][0] == ident) { - listeners.splice(i, 1); - return true; - } - } - return false; - } - - /* - PrivateMethod: initTopKeyListner - Adds an event listener for keyboard events, for key listeners added by addKeyListener. - - Arguments: - type - press|down|up - the type of key event. - */ - - function initTopKeyListener (type) { - topKeyListeners[type] = r.addListener(document, 'key' + type, function (e) { - var mods = 0; - if (e.ctrlKey) { mods += CTRL; } - if (e.altKey) { mods += ALT; } - if (e.shiftKey) { mods += SHIFT; } - var keyType = e.chr ? e.chr.toLowerCase() : e.key ? e.key.toLowerCase() : e.keyCode; - var eventType = mods + ':' + keyType + ':' + type; - var listeners = keyListeners[eventType] ? keyListeners[eventType].slice(0) : []; - // if the user pressed shift, but event didn't specify that, include it - if (e.shiftKey) { // upper-case letter, should match regardless of shift - var shiftEventType = (mods & ~ SHIFT) + ':' + keyType + ':' + type; - if (keyListeners[shiftEventType]) { - for (var i = 0, len = keyListeners[shiftEventType].length; i < len; i++) { - listeners[listeners.length] = keyListeners[shiftEventType][i]; - } - } - } - - if (! listeners) { return; } - - for (var i = 0, len = listeners.length; i < len; i++) { - //call listener and look out for preventing the default - if (listeners[i][2].call(listeners[i][3] || this, e) === false) { - e.preventDefault(); - } - } - return !e.defaultPrevented(); - }); - } - - /* - PrivateMethod: clearEvents - Removes all current listeners to avoid IE mem leakage - */ - function clearEvents() { - var ident; - for (ident in listenersByEventId) { - r.removeListener(ident); - } - } - - // for opera madness - var previousKeyDownKeyCode; - - var operaResizeListener, - operaDocScrollListener; - - /* - PrivateMethod: addDomListener - Adds an event listener to a browser object. Massages differences with certain events. - - Arguments: - attachTo - the browser object to attach the event to. - name - the generic name of the event (inc. mousewheel) - */ - function addDomListener (attachTo, name) { - var wheelEventName; - - if (glow.env.opera) { - if (name.toLowerCase() == 'resize' && !operaResizeListener && attachTo == window) { - operaResizeListener = r.addListener(window.document.body, 'resize', function (e) { r.fire(window, 'resize', e); }); - } else if (name.toLowerCase() == 'scroll' && !operaDocScrollListener && attachTo == window) { - operaDocScrollListener = r.addListener(window.document, 'scroll', function (e) { r.fire(window, 'scroll', e); }); - } - } - - var callback = function (e) { - if (! e) { e = window.event; } - var event = new r.Event(), - lowerCaseName = name.toLowerCase(); - event.nativeEvent = e; - event.source = e.target || e.srcElement; - - event.relatedTarget = e.relatedTarget || (lowerCaseName == "mouseover" ? e.fromElement : e.toElement); - event.button = glow.env.ie ? (e.button & 1 ? 0 : e.button & 2 ? 2 : 1) : e.button; - if (e.pageX || e.pageY) { - event.pageX = e.pageX; - event.pageY = e.pageY; - } else if (e.clientX || e.clientY) { - event.pageX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; - event.pageY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; - } - if (lowerCaseName == 'mousewheel') { - // this works in latest opera, but have read that it needs to be switched in direction - // if there was an opera bug, I can't find which version it was fixed in - event.wheelDelta = - e.wheelDelta ? e.wheelDelta / 120 : - e.detail ? - e.detail / 3 : - 0; - if (event.wheelDelta == 0) { return; } - } - if (lowerCaseName.indexOf("key") != -1) { - event.altKey = !! e.altKey; - event.ctrlKey = !! e.ctrlKey; - event.shiftKey = !! e.shiftKey; - - if (name == 'keydown') { - previousKeyDownKeyCode = e.keyCode; - } - - event.charCode = e.keyCode && e.charCode !== 0 ? undefined : e.charCode; - - if (lowerCaseName == 'keypress') { - if (typeof(event.charCode) == 'undefined') { - event.charCode = e.keyCode; - } - - if (glow.env.opera && event.charCode && event.charCode == previousKeyDownKeyCode && - operaBrokenChars.indexOf(String.fromCharCode(event.charCode)) == -1 - ) { - event.charCode = undefined; - event.keyCode = previousKeyDownKeyCode; - } - } - - // make things a little more sane in opera - if (event.charCode && event.charCode <= 49) { event.charCode = undefined; } - - if (event.charCode) { - event.chr = String.fromCharCode(event.charCode); - } - else if (e.keyCode) { - event.charCode = undefined; - event.keyCode = keyNameAliases[e.keyCode.toString()] || e.keyCode; - event.key = codeToKeyName[event.keyCode]; - if (specialPrintables[event.key]) { - event.chr = specialPrintables[event.key]; - event.charCode = event.chr.charCodeAt(0); - } - } - - if (event.chr) { - event.capsLock = - event.chr.toUpperCase() != event.chr ? // is lower case - event.shiftKey : - event.chr.toLowerCase() != event.chr ? // is upper case - ! event.shiftKey : - undefined; // can only tell for keys with case - } - } - - r.fire(this, name, event); - if (event.defaultPrevented()) { - return false; - } - }; - - if (attachTo.addEventListener && (!glow.env.webkit || glow.env.webkit > 418)) { - attachTo.addEventListener(name.toLowerCase() == 'mousewheel' && glow.env.gecko ? 'DOMMouseScroll' : name, callback, false); - } else { - var onName = 'on' + name; - var existing = attachTo[onName]; - if (existing) { - attachTo[onName] = function () { - // we still need to return false if either the existing or new callback returns false - var existingReturn = existing.apply(this, arguments), - callbackReturn = callback.apply(this, arguments); - - return (existingReturn !== false) && (callbackReturn !== false); - }; - } else { - attachTo[onName] = callback; - } - } - attachTo = null; - } - - /** - @name glow.events._copyListeners - @function - @private - @description Maps event listeners from one set of nodes to another in the order they appear in each NodeList. - Note, it doesn't copy events from a node's children. - - - @param {NodeList} from NodeList to copy events from - @param {NodeList} to NodeList to copy events to - - @returns {Boolean} - - @example - var listener = glow.events.addListener(...); - glow.events.removeListener(listener); - */ - r._copyListeners = function(from, to) { - // grab all the elements (including children) - var i = from.length, - // events attached to a particular element - elementEvents, - // name of the current event we're looking at - eventName, - // current listener index - listenerIndex, - // number of listeners to an event - listenersLen, - // listener definition from listenersByObjId - listener; - - // loop through all items - while(i--) { - // has a glow event been assigned to this node? - if ( from[i][psuedoPrivateEventKey] ) { - // get listeners for that event - elementEvents = listenersByObjId[ from[i][psuedoPrivateEventKey] ]; - // loop through event names - for (eventName in elementEvents) { - listenerIndex = 0; - listenersLen = elementEvents[eventName].length; - // loop through listeners to that event - for (; listenerIndex < listenersLen; listenerIndex++) { - listener = elementEvents[eventName][listenerIndex]; - // listen to them on the clone - r.addListener(to[i], eventName, listener[2], listener[3]); - } - } - } - } - } - - /** - @name glow.events.addListener - @function - @description Adds an event listener to an object (e.g. a DOM Element or Glow widget). - - @param {String | NodeList | Object} attachTo The object to attach the event listener to. - - If the parameter is a string, then it is treated as a CSS selector - and the listener is attached to all matching elements. - - If the parameter is a {@link glow.dom.NodeList}, then the listener - is attached to all elements in the NodeList. - - @param {String} name The event name. - - Listeners for DOM events should not begin with 'on' (i.e. 'click' - rather than 'onclick') - - @param {Function} callback The function to be called when the event fires. - - @param {Object} [context] The execution scope of the callback. - - If this parameter is not passed then the attachTo object will be the - scope of the callback. - - @returns {Number | Undefined} - - A unique identifier for the event suitable for passing to - {@link glow.events.removeListener}. If an empty NodeList or CSS - selector that returns no elements is passed, then undefined is returned. - - @example - glow.events.addListener('#nav', 'click', function () { - alert('nav clicked'); - }); - - glow.events.addListener(myLightBox, 'close', this.showSurvey, this); - */ - r.addListener = function (attachTo, name, callback, context) { - if (! attachTo) { throw 'no attachTo paramter passed to addListener'; } - - if (typeof attachTo == 'string') { - if (! glow.dom) { throw "glow.dom must be loaded to use a selector as the first argument to glow.events.addListener"; } - attachTo = $(attachTo); - } - - if (glow.dom && attachTo instanceof glow.dom.NodeList) { - var listenerIds = [], - i = attachTo.length; - - //attach the event for each element, return an array of listener ids - while (i--) { - listenerIds[i] = r.addListener(attachTo[i], name, callback, context); - } - - return listenerIds; - } - - var objIdent; - if (! (objIdent = attachTo[psuedoPrivateEventKey])) { - objIdent = attachTo[psuedoPrivateEventKey] = objid++; - } - var ident = eventid++; - var listener = [ objIdent, name, callback, context, ident ]; - listenersByEventId[ident] = listener; - - var objListeners = listenersByObjId[objIdent]; - if (! objListeners) { objListeners = listenersByObjId[objIdent] = {}; } - var objEventListeners = objListeners[name]; - if (! objEventListeners) { objEventListeners = objListeners[name] = []; } - objEventListeners[objEventListeners.length] = listener; - - if ((attachTo.addEventListener || attachTo.attachEvent) && ! domListeners[objIdent + ':' + name]) { - addDomListener(attachTo, name); - domListeners[objIdent + ':' + name] = true; - } - return ident; - }; - - /** - @name glow.events.removeListener - @function - @description Removes a listener created with addListener - - @param {Number} ident An identifier returned from {@link glow.events.addListener}. - - @returns {Boolean} - - @example - var listener = glow.events.addListener(...); - glow.events.removeListener(listener); - */ - r.removeListener = function (ident) { - if (ident && ident.toString().indexOf('k:') != -1) { - return removeKeyListener(ident); - } - if (ident instanceof Array) { - //call removeListener for each array member - var i = ident.length; while(i--) { - r.removeListener(ident[i]); - } - return true; - } - var listener = listenersByEventId[ident]; - if (! listener) { return false; } - delete listenersByEventId[ident]; - var listeners = listenersByObjId[listener[0]][listener[1]]; - for (var i = 0, len = listeners.length; i < len; i++) { - if (listeners[i] == listener) { - listeners.splice(i, 1); - break; - } - } - if (! listeners.length) { - delete listenersByObjId[listener[0]][listener[1]]; - } - var listenersLeft = false; - for (var i in listenersByObjId[listener[0]]) { listenersLeft = true; break; } - if (! listenersLeft) { - delete listenersByObjId[listener[0]]; - } - return true; - }; - - /** - @name glow.events.removeAllListeners - @function - @description Removes all listeners attached to a given object - - @param {String | glow.dom.NodeList | Object | Object[] } detachFrom The object(s) to remove listeners from. - - If the parameter is a string, then it is treated as a CSS selector, - listeners are removed from all nodes. - - @returns glow.events - - @example - glow.events.removeAllListeners("#myDiv"); - */ - r.removeAllListeners = function(obj) { - var i, - objId, - listenerIds = [], - listenerIdsLen = 0, - eventName, - events; - - // cater for selector - if (typeof obj == "string") { - // get nodes - obj = $(obj); - } - // cater for arrays & nodelists - if (obj instanceof Array || obj instanceof glow.dom.NodeList) { - //call removeAllListeners for each array member - i = obj.length; while(i--) { - r.removeAllListeners(obj[i]); - } - return r; - } - - // get the objects id - objId = obj[psuedoPrivateEventKey]; - - // if it doesn't have an id it doesn't have events... return - if (!objId) { - return r; - } - events = listenersByObjId[objId]; - for (eventName in events) { - i = events[eventName].length; while(i--) { - listenerIds[listenerIdsLen++] = events[eventName][i][4]; - } - } - // remove listeners for that object - if (listenerIds.length) { - r.removeListener( listenerIds ); - } - return r; - } - - /** - @name glow.events.fire - @function - @description Fires an event on an object. - - @param {Object} attachedTo The object that the event is associated with. - - @param {String} name The name of the event. - - Event names should not start with the word 'on'. - - @param {Object | glow.events.Event} [event] An event object or properties to add to a default event object - - If not specified, a generic event object is created. If you provide a simple - object, a default Event will be created with the properties from the provided object. - - @returns {Object} - - The event object. - - @example - // firing a custom event - Ball.prototype.move = function () { - // move the ball... - // check its position - if (this._height == 0) { - var event = glow.events.fire(this, 'bounce', { - bounceCount: this._bounceCount - }); - - // handle what to do if a listener returned false - if ( event.defaultPrevented() ) { - this.stopMoving(); - } - } - }; - - @example - // listening to a custom event - var myBall = new Ball(); - - glow.events.addListener(myBall, "bounce", function(event) { - if (event.bounceCount == 3) { - // stop bouncing after 3 bounces - return false; - } - }); - */ - r.fire = function (attachedTo, name, e) { - if (! attachedTo) throw 'glow.events.fire: required parameter attachedTo not passed (name: ' + name + ')'; - if (! name) throw 'glow.events.fire: required parameter name not passed'; - if (! e) { e = new r.Event(); } - if ( e.constructor === Object ) { e = new r.Event( e ) } - - e.type = name; - e.attachedTo = attachedTo; - if (! e.source) { e.source = attachedTo; } - - var objIdent, - objListeners, - objEventListeners = objListeners && objListeners[name]; - - // 3 assignments, but stop assigning if any of them are false - (objIdent = attachedTo[psuedoPrivateEventKey]) && - (objListeners = listenersByObjId[objIdent]) && - (objEventListeners = objListeners[name]); - - if (! objEventListeners) { return e; } - - var listener; - - // we make a copy of the listeners before calling them, as the event handlers may - // remove themselves (took me a while to track this one down) - var listeners = objEventListeners.slice(0); - for (var i = 0, len = listeners.length; i < len; i++) { - listener = listeners[i]; - if ( listener[2].call(listener[3] || attachedTo, e) === false ) { - e.preventDefault(); - } - } - return e; - }; - - /** - @private - @name glow.events.addKeyListener - @deprecated - @function - @description Adds an event listener for a keyboard event HELLO. - -

Notes for Opera

- - It is currently impossible to differentiate certain key events in - Opera (for example the RIGHT (the right arrow key) and the - apostrope (') result in the same code). For this reason pressing - either of these keys will result in key listeners specified as - "RIGHT" and/or "'" to be fired. - -

Key Identifiers

- - The key param uses the following strings to refer to special keys, - i.e. non alpha-numeric keys. - -
    -
  • CAPSLOCK
  • -
  • NUMLOCK
  • -
  • SCROLLLOCK
  • -
  • BREAK
  • -
  • BACKTICK
  • -
  • BACKSPACE
  • -
  • PRINTSCREEN
  • -
  • MENU
  • -
  • SPACE
  • -
  • ESC
  • -
  • TAB
  • -
  • META
  • -
  • RIGHTMETA
  • -
  • ENTER
  • -
  • F1
  • -
  • F2
  • -
  • F3
  • -
  • F4
  • -
  • F5
  • -
  • F6
  • -
  • F7
  • -
  • F8
  • -
  • F9
  • -
  • F10
  • -
  • F11
  • -
  • F12
  • -
  • INS
  • -
  • HOME
  • -
  • PAGEUP
  • -
  • DEL
  • -
  • END
  • -
  • PAGEDOWN
  • -
  • LEFT
  • -
  • UP
  • -
  • RIGHT
  • -
  • DOWN
  • -
- - @param {String} key The key or key combination to listen to. - - This parameter starts with modifier keys 'CTRL', 'ALT' and - 'SHIFT'. Modifiers can appear in any combination and order and are - separated by a '+'. - - Following any modifiers is the key character. To specify a - character code, use the appropriate escape sequence (e.g. - "CTRL+\u0065" = CTRL+e"). - - To specify a special key, the key character should be replaced with - a key identifier, see description below (e.g. "RIGHT" specifies the - right arrow key). - - @param {String} type The type of key press to listen to. - - Possible values for this parameter are: - -
-
press
the key is pressed (comparable to a mouse click)
-
down
the key is pushed down
-
up
the key is released
-
- - @param {Function} callback The function to be called when the event fires. - - @param {Object} [context] The execution scope of the callback. - - If this parameter is not passed then the attachTo object will be the - context of the callback. - - @returns {Number} - - A unique identifier for the event suitable for passing to - {@link glow.events.removeListener}. - - @example - glow.events.addKeyListener("CTRL+ALT+a", "press", - function () { alert("CTRL+ALT+a pressed"); } - ); - glow.events.addKeyListener("SHIFT+\u00A9", "down", - function () { alert("SHIFT+� pushed") } - ); - */ - var keyRegex = /^((?:(?:ctrl|alt|shift)\+)*)(?:(\w+|.)|[\n\r])$/i; - r.addKeyListener = function (key, type, callback, context) { - type.replace(/^key/i, ""); - type = type.toLowerCase(); - if (! (type == 'press' || type == 'down' || type == 'up')) { - throw 'event type must be press, down or up'; - } - if (! topKeyListeners[type]) { initTopKeyListener(type); } - var res = key.match(keyRegex), - mods = 0, - charCode; - if (! res) { throw 'key format not recognised'; } - if (res[1].toLowerCase().indexOf('ctrl') != -1) { mods += CTRL; } - if (res[1].toLowerCase().indexOf('alt') != -1) { mods += ALT; } - if (res[1].toLowerCase().indexOf('shift') != -1) { mods += SHIFT; } - var eventKey = mods + ':' + (res[2] ? res[2].toLowerCase() : '\n') + ':' + type; - var ident = 'k:' + keyListenerId++; - keyTypes[ident] = eventKey; - var listeners = keyListeners[eventKey]; - if (! listeners) { listeners = keyListeners[eventKey] = []; } - listeners[listeners.length] = [ident, type, callback, context]; - return ident; - }; - - /** - @name glow.events.Event - @class - @param {Object} [properties] Properties to add to the Event instance. - Each key-value pair in the object will be added to the Event as - properties - @description Object passed into all events - - Some of the properties described below are only available for - certain event types. These are listed in the property descriptions - where applicable. - -

Notes for Opera

- - The information returned from Opera about key events does not allow - certain keys to be differentiated. This mainly applies to special - keys, such as the arrow keys, function keys, etc. which conflict - with some printable characters. - - */ - r.Event = function ( obj ) { - if( obj ) { - glow.lang.apply( this, obj ); - } - }; - - /** - @name glow.events.Event#attachedTo - @type Object | Element - @description The object/element that the listener is attached to. - - See the description for 'source' for more details. - */ - - /** - @name glow.events.Event#source - @type Element - @description The actual object/element that the event originated from. - - For example, you could attach a listener to an 'ol' element to - listen for clicks. If the user clicked on an 'li' the source property - would be the 'li' element, and 'attachedTo' would be the 'ol'. - */ - - /** - @name glow.events.Event#pageX - @type Number - @description The horizontal position of the mouse pointer in the page in pixels. - -

Only available for mouse events.

- */ - - /** - @name glow.events.Event#pageY - @type Number - @description The vertical position of the mouse pointer in the page in pixels. - -

Only available for mouse events.

- */ - - /** - @name glow.events.Event#button - @type Number - @description A number representing which button was pressed. - -

Only available for mouse events.

- - 0 for the left button, 1 for the middle button or 2 for the right button. - */ - - /** - @name glow.events.Event#relatedTarget - @type Element - @description The element that the mouse has come from or is going to. - -

Only available for mouse over/out events.

- */ - - /** - @name glow.events.Event#wheelDelta - @type Number - @description The number of clicks up (positive) or down (negative) that the user moved the wheel. - -

Only available for mouse wheel events.

- */ - - /** - @name glow.events.Event#ctrlKey - @type Boolean - @description Whether the ctrl key was pressed during the key event. - -

Only available for keyboard events.

- */ - - /** - @name glow.events.Event#shiftKey - @type Boolean - @description Whether the shift key was pressed during the key event. - -

Only available for keyboard events.

- */ - - /** - @name glow.events.Event#altKey - @type Boolean - @description Whether the alt key was pressed during the key event. - -

Only available for keyboard events.

- */ - - /** - @name glow.events.Event#capsLock - @type Boolean | Undefined - @description Whether caps-lock was on during the key event - -

Only available for keyboard events.

- - If the key is not alphabetic, this property will be undefined - as it is not possible to tell if caps-lock is on in this scenario. - */ - - /** - @name glow.events.Event#keyCode - @type Number - @description An integer number represention of the keyboard key that was pressed. - -

Only available for keyboard events.

- */ - - /** - @name glow.events.Event#key - @type String | Undefined - @description A short identifier for the key for special keys. - -

Only available for keyboard events.

- - If the key was not a special key this property will be undefined. - - See the list of key identifiers in {@link glow.events.addKeyListener} - */ - - /** - @name glow.events.Event#charCode - @type Number | Undefined - @description The unicode character code for a printable character. - -

Only available for keyboard events.

- - This will be undefined if the key was not a printable character. - */ - - /** - @name glow.events.Event#chr - @type String - @description A printable character string. - -

Only available for keyboard events.

- - The string of the key that was pressed, for example 'j' or 's'. - - This will be undefined if the key was not a printable character. - */ - - - /** - @name glow.events.Event#preventDefault - @function - @description Prevent the default action for events. - - This can also be achieved by returning false from an event callback - - */ - r.Event.prototype.preventDefault = function () { - if (this[psuedoPreventDefaultKey]) { return; } - this[psuedoPreventDefaultKey] = true; - if (this.nativeEvent && this.nativeEvent.preventDefault) { - this.nativeEvent.preventDefault(); - this.nativeEvent.returnValue = false; - } - }; - - /** - @name glow.events.Event#defaultPrevented - @function - @description Test if the default action has been prevented. - - @returns {Boolean} - - True if the default action has been prevented. - */ - r.Event.prototype.defaultPrevented = function () { - return !! this[psuedoPreventDefaultKey]; - }; - - /** - @name glow.events.Event#stopPropagation - @function - @description Stops the event propagating. - - For DOM events, this stops the event bubbling up through event - listeners added to parent elements. The event object is marked as - having had propagation stopped (see - {@link glow.events.Event#propagationStopped propagationStopped}). - - @example - // catch all click events that are not links - glow.events.addListener( - document, - 'click', - function () { alert('document clicked'); } - ); - - glow.events.addListener( - 'a', - 'click', - function (e) { e.stopPropagation(); } - ); - */ - r.Event.prototype.stopPropagation = function () { - if (this[psuedoStopPropagationKey]) { return; } - this[psuedoStopPropagationKey] = true; - var e = this.nativeEvent; - if (e) { - e.cancelBubble = true; - if (e.stopPropagation) { e.stopPropagation(); } - } - }; - - /** - @name glow.events.Event#propagationStopped - @function - @description Tests if propagation has been stopped for this event. - - @returns {Boolean} - - True if event propagation has been prevented. - - */ - r.Event.prototype.propagationStopped = function () { - return !! this[psuedoStopPropagationKey]; - }; - - //cleanup to avoid mem leaks in IE - r.addListener(window, "unload", clearEvents); - - glow.events = r; - } -}); -/** -@name glow.data -@namespace -@description Serialising and de-serialising data -@see Using glow.data -*/ -(window.gloader || glow).module({ - name: "glow.data", - library: ["glow", "1.5.1"], - depends: [["glow", "1.5.1", "glow.dom"]], - builder: function(glow) { - //private - - /* - PrivateProperty: TYPES - hash of strings representing data types - */ - var TYPES = { - UNDEFINED : "undefined", - OBJECT : "object", - NUMBER : "number", - BOOLEAN : "boolean", - STRING : "string", - ARRAY : "array", - FUNCTION : "function", - NULL : "null" - } - - /* - PrivateProperty: TEXT - hash of strings used in encoding/decoding - */ - var TEXT = { - AT : "@", - EQ : "=", - DOT : ".", - EMPTY : "", - AND : "&", - OPEN : "(", - CLOSE : ")" - } - - /* - PrivateProperty: JSON - nested hash of strings and regular expressions used in encoding/decoding Json - */ - var JSON = { - HASH : { - START : "{", - END : "}", - SHOW_KEYS : true - }, - - ARRAY : { - START : "[", - END : "]", - SHOW_KEYS : false - }, - - DATA_SEPARATOR : ",", - KEY_SEPARATOR : ":", - KEY_DELIMITER : "\"", - STRING_DELIMITER : "\"", - - SAFE_PT1 : /^[\],:{}\s]*$/, - SAFE_PT2 : /\\./g, - SAFE_PT3 : /\"[^\"\\\n\r]*\"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g, - SAFE_PT4 : /(?:^|:|,)(?:\s*\[)+/g - } - - /* - PrivateProperty: SLASHES - hash of strings and regular expressions used in encoding strings - */ - var SLASHES = { - TEST : /[\b\n\r\t\\\f\"]/g, - B : {PLAIN : "\b", ESC : "\\b"}, - N : {PLAIN : "\n", ESC : "\\n"}, - R : {PLAIN : "\r", ESC : "\\r"}, - T : {PLAIN : "\t", ESC : "\\t"}, - F : {PLAIN : "\f", ESC : "\\f"}, - SL : {PLAIN : "\\", ESC : "\\\\"}, - QU : {PLAIN : "\"", ESC : "\\\""} - } - - /* - PrivateMethod: _getType - Callback function for glow.lang.replace to escape appropriate characters - - Arguments: - s - the regex match to be tested - - Returns: - The escaped version of the input. - */ - function _replaceSlashes(s) { - switch (s) { - case SLASHES.B.PLAIN: return SLASHES.B.ESC; - case SLASHES.N.PLAIN: return SLASHES.N.ESC; - case SLASHES.R.PLAIN: return SLASHES.R.ESC; - case SLASHES.T.PLAIN: return SLASHES.T.ESC; - case SLASHES.F.PLAIN: return SLASHES.F.ESC; - case SLASHES.SL.PLAIN: return SLASHES.SL.ESC; - case SLASHES.QU.PLAIN: return SLASHES.QU.ESC; - default: return s; - } - } - - /* - PrivateMethod: _getType - Returns the data type of the object - - Arguments: - object - the object to be tested - - Returns: - A one of the TYPES constant properties that represents the data type of the object. - */ - function _getType(object) { - if((typeof object) == TYPES.OBJECT) { - if (object == null) { - return TYPES.NULL; - } else { - return (object instanceof Array)?TYPES.ARRAY:TYPES.OBJECT; - } - } else { - return (typeof object); - } - } - - //public - glow.data = { - /** - @name glow.data.encodeUrl - @function - @description Encodes an object for use as a query string. - - Returns a string representing the object suitable for use - as a query string, with all values suitably escaped. - It does not include the initial question mark. Where the - input field was an array, the key is repeated in the output. - - @param {Object} object The object to be encoded. - - This must be a hash whose values can only be primitives or - arrays of primitives. - - @returns {String} - - @example - var getRef = glow.data.encodeUrl({foo: "Foo", bar: ["Bar 1", "Bar2"]}); - // will return "foo=Foo&bar=Bar%201&bar=Bar2" - */ - encodeUrl : function (object) { - var objectType = _getType(object); - var paramsList = []; - var listLength = 0; - - if (objectType != TYPES.OBJECT) { - throw new Error("glow.data.encodeUrl: cannot encode item"); - } else { - for (var key in object) { - switch(_getType(object[key])) { - case TYPES.FUNCTION: - case TYPES.OBJECT: - throw new Error("glow.data.encodeUrl: cannot encode item"); - break; - case TYPES.ARRAY: - for(var i = 0, l = object[key].length; i < l; i++) { - switch(_getType(object[key])[i]) { - case TYPES.FUNCTION: - case TYPES.OBJECT: - case TYPES.ARRAY: - throw new Error("glow.data.encodeUrl: cannot encode item"); - break; - default: - paramsList[listLength++] = key + TEXT.EQ + encodeURIComponent(object[key][i]); - } - } - break; - default: - paramsList[listLength++] = key + TEXT.EQ + encodeURIComponent(object[key]); - } - } - - return paramsList.join(TEXT.AND); - } - }, - /** - @name glow.data.decodeUrl - @function - @description Decodes a query string into an object. - - Returns an object representing the data given by the query - string, with all values suitably unescaped. All keys in the - query string are keys of the object. Repeated keys result - in an array. - - @param {String} string The query string to be decoded. - - It should not include the initial question mark. - - @returns {Object} - - @example - var getRef = glow.data.decodeUrl("foo=Foo&bar=Bar%201&bar=Bar2"); - // will return the object {foo: "Foo", bar: ["Bar 1", "Bar2"]} - */ - decodeUrl : function (text) { - if(_getType(text) != TYPES.STRING) { - throw new Error("glow.data.decodeUrl: cannot decode item"); - } else if (text === "") { - return {}; - } - - var result = {}; - var keyValues = text.split(TEXT.AND); - - var thisPair, key, value; - - for(var i = 0, l = keyValues.length; i < l; i++) { - thisPair = keyValues[i].split(TEXT.EQ); - if(thisPair.length != 2) { - throw new Error("glow.data.decodeUrl: cannot decode item"); - } else { - key = decodeURIComponent(thisPair[0]); - value = decodeURIComponent(thisPair[1]); - - switch (_getType(result[key])) { - case TYPES.ARRAY: - result[key][result[key].length] = value; - break; - case TYPES.UNDEFINED: - result[key] = value; - break; - default: - result[key] = [result[key], value]; - } - } - } - - return result; - }, - /** - @name glow.data.encodeJson - @function - @description Encodes an object into a string JSON representation. - - Returns a string representing the object as JSON. - - @param {Object} object The object to be encoded. - - This can be arbitrarily nested, but must not contain - functions or cyclical structures. - - @returns {Object} - - @example - var myObj = {foo: "Foo", bar: ["Bar 1", "Bar2"]}; - var getRef = glow.data.encodeJson(myObj); - // will return '{"foo": "Foo", "bar": ["Bar 1", "Bar2"]}' - */ - encodeJson : function (object, options) { - function _encode(object, options) - { - if(_getType(object) == TYPES.ARRAY) { - var type = JSON.ARRAY; - } else { - var type = JSON.HASH; - } - - var serial = [type.START]; - var len = 1; - var dataType; - var notFirst = false; - - for(var key in object) { - dataType = _getType(object[key]); - - if(dataType != TYPES.UNDEFINED) { /* ignore undefined data */ - if(notFirst) { - serial[len++] = JSON.DATA_SEPARATOR; - } - notFirst = true; - - if(type.SHOW_KEYS) { - serial[len++] = JSON.KEY_DELIMITER; - serial[len++] = key; - serial[len++] = JSON.KEY_DELIMITER; - serial[len++] = JSON.KEY_SEPARATOR; - } - - switch(dataType) { - case TYPES.FUNCTION: - throw new Error("glow.data.encodeJson: cannot encode item"); - break; - case TYPES.STRING: - default: - serial[len++] = JSON.STRING_DELIMITER; - serial[len++] = glow.lang.replace(object[key], SLASHES.TEST, _replaceSlashes); - serial[len++] = JSON.STRING_DELIMITER; - break; - case TYPES.NUMBER: - case TYPES.BOOLEAN: - serial[len++] = object[key]; - break; - case TYPES.OBJECT: - case TYPES.ARRAY: - serial[len++] = _encode(object[key], options); - break; - case TYPES.NULL: - serial[len++] = TYPES.NULL; - break; - } - } - } - serial[len++] = type.END; - - return serial.join(TEXT.EMPTY); - } - - options = options || {}; - var type = _getType(object); - - if((type == TYPES.OBJECT) || (type == TYPES.ARRAY)) { - return _encode(object, options); - } else { - throw new Error("glow.data.encodeJson: cannot encode item"); - } - }, - /** - @name glow.data.decodeJson - @function - @description Decodes a string JSON representation into an object. - - Returns a JavaScript object that mirrors the data given. - - @param {String} string The string to be decoded. - Must be valid JSON. - - @param {Object} opts - - Zero or more of the following as properties of an object: - @param {Boolean} [opts.safeMode=false] Whether the string should only be decoded if it is deemed "safe". - The json.org regular expression checks are used. - - @returns {Object} - - @example - var getRef = glow.data.decodeJson('{foo: "Foo", bar: ["Bar 1", "Bar2"]}'); - // will return {foo: "Foo", bar: ["Bar 1", "Bar2"]} - - var getRef = glow.data.decodeJson('foobar', {safeMode: true}); - // will throw an error - */ - decodeJson : function (text, options) { - if(_getType(text) != TYPES.STRING) { - throw new Error("glow.data.decodeJson: cannot decode item"); - } - - options = options || {}; - options.safeMode = options.safeMode || false; - - var canEval = true; - - if(options.safeMode) { - canEval = (JSON.SAFE_PT1.test(text.replace(JSON.SAFE_PT2, TEXT.AT).replace(JSON.SAFE_PT3, JSON.ARRAY.END).replace(JSON.SAFE_PT4, TEXT.EMPTY))); - } - - if(canEval) { - try { - return eval(TEXT.OPEN + text + TEXT.CLOSE); - } - catch(e) {/* continue to error */} - } - - throw new Error("glow.data.decodeJson: cannot decode item"); - }, - /** - @name glow.data.escapeHTML - @function - @description Escape HTML entities. - - Returns a string with HTML entities escaped. - - @param {String} string The string to be escaped. - - @returns {String} - - @example - // useful for protecting against XSS attacks: - var fieldName = '" onclick="alert(\'hacked\')" name="'; - - // but should be used in all cases like this: - glow.dom.create(''); - */ - escapeHTML : function (html) { - return glow.dom.create('
').text(html).html(); - } - }; - } -}); -/** -@name glow.net -@namespace -@description Sending data to & from the server -@see Using glow.net -*/ -(window.gloader || glow).module({ - name: "glow.net", - library: ["glow", "1.5.1"], - depends: [["glow", "1.5.1", "glow.data", "glow.events"]], - builder: function(glow) { - //private - - var STR = { - XML_ERR:"Cannot get response as XML, check the mime type of the data", - POST_DEFAULT_CONTENT_TYPE:'application/x-www-form-urlencoded;' - }, - /** - * @name glow.net.scriptElements - * @private - * @description Script elements that have been added via {@link glow.net.loadScript loadScript} - * @type Array - */ - scriptElements = [], - /** - * @name glow.net.callbackPrefix - * @private - * @description Callbacks in _jsonCbs will be named this + a number - * @type String - */ - callbackPrefix = "c", - /** - * @name glow.net.globalObjectName - * @private - * @description Name of the global object used to store loadScript callbacks - * @type String - */ - globalObjectName = "_" + glow.UID + "loadScriptCbs", - events = glow.events, - emptyFunc = function(){}; - - /** - * @name glow.net.xmlHTTPRequest - * @private - * @function - * @description Creates an xmlHTTPRequest transport - * @returns Object - */ - function xmlHTTPRequest() { - //try IE first. IE7's xmlhttprequest and XMLHTTP6 are broken. Avoid avoid avoid! - if (window.ActiveXObject) { - return (xmlHTTPRequest = function() { return new ActiveXObject("MSXML2.XMLHTTP"); })(); - } else { - return (xmlHTTPRequest = function() { return new XMLHttpRequest(); })(); - } - } - - /** - * @name glow.net.populateOptions - * @private - * @function - * @description Adds defaults to get / post option object - * @param {Object} opts Object to add defaults to - * @returns Object - */ - function populateOptions(opts) { - return glow.lang.apply( - { - onLoad: emptyFunc, - onError: emptyFunc, - onAbort: emptyFunc, - headers: {}, - async: true, - useCache: false, - data: null, - defer: false - }, - opts || {} - ); - } - - /* - PrivateMethod: noCacheUrl - Adds random numbers to the querystring of a url so the browser doesn't use a cached version - */ - - function noCacheUrl(url) { - return [url, (/\?/.test(url) ? "&" : "?"), "a", new Date().getTime(), parseInt(Math.random()*100000)].join(""); - } - - /* - PrivateMethod: makeXhrRequest - Makes an http request - */ - /** - * @name glow.net.makeXhrRequest - * @private - * @function - * @description Makes an xhr http request - * @param {String} method HTTP Method - * @param {String} url URL of the request - * @param {Object} Options, see options for {@link glow.net.get} - * @returns Object - */ - function makeXhrRequest(method, url, opts) { - var req = xmlHTTPRequest(), //request object - data = opts.data && (typeof opts.data == "string" ? opts.data : glow.data.encodeUrl(opts.data)), - i, - request = new Request(req, opts); - - if (!opts.useCache) { - url = noCacheUrl(url); - } - - //open needs to go first to maintain cross-browser support for readystates - req.open(method, url, opts.async); - - //add custom headers - for (i in opts.headers) { - req.setRequestHeader(i, opts.headers[i]); - } - - function send() { - request.send = emptyFunc; - if (opts.async) { - //sort out the timeout if there is one - if (opts.timeout) { - request._timeout = setTimeout(function() { - abortRequest(request); - var response = new Response(req, true); - events.fire(request, "error", response); - }, opts.timeout * 1000); - } - - req.onreadystatechange = function() { - if (req.readyState == 4) { - //clear the timeout - request._timeout && clearTimeout(request._timeout); - //set as completed - request.completed = true; - var response = new Response(req); - if (response.wasSuccessful) { - events.fire(request, "load", response); - } else { - events.fire(request, "error", response); - } - } - }; - req.send(data); - return request; - } else { - req.send(data); - request.completed = true; - var response = new Response(req); - if (response.wasSuccessful) { - events.fire(request, "load", response); - } else { - events.fire(request, "error", response); - } - return response; - } - } - - request.send = send; - return opts.defer ? request : send(); - } - - //public - var r = {}; //the module - - /** - @name glow.net.get - @function - @description Makes an HTTP GET request to a given url - - @param {String} url - Url to make the request to. This can be a relative path. You cannot make requests - for files on other domains, to do that you must put your data in a javascript - file and use {@link glow.net.loadScript} to fetch it. - @param {Object} opts - Options Object of options. - @param {Function} [opts.onLoad] Callback to execute when the request has sucessfully loaded - The callback is passed a Response object as its first parameter. - @param {Function} [opts.onError] Callback to execute if the request was unsucessful - The callback is passed a Response object as its first parameter. - This callback will also be run if a request times out. - @param {Function} [opts.onAbort] Callback to execute if the request is aborted - @param {Object} [opts.headers] A hash of headers to send along with the request - Eg {"Accept-Language": "en-gb"} - @param {Boolean} [opts.async=true] Should the request be performed asynchronously? - @param {Boolean} [opts.useCache=false] Allow a cached response - If false, a random number is added to the query string to ensure a fresh version of the file is being fetched - @param {Number} [opts.timeout] Time to allow for the request in seconds - No timeout is set by default. Only applies for async requests. Once - the time is reached, the error event will fire with a "408" status code. - @param {Boolean} [opts.defer=false] Do not send the request straight away - Deferred requests need to be triggered later using myRequest.send() - - @returns {glow.net.Request|glow.net.Response} - A response object for non-defered sync requests, otherwise a - request object is returned - - @example - var request = glow.net.get("myFile.html", { - onLoad: function(response) { - alert("Got file:\n\n" + response.text()); - }, - onError: function(response) { - alert("Error getting file: " + response.statusText()); - } - }); - */ - r.get = function(url, o) { - o = populateOptions(o); - return makeXhrRequest('GET', url, o); - }; - - /** - @name glow.net.post - @function - @description Makes an HTTP POST request to a given url - - @param {String} url - Url to make the request to. This can be a relative path. You cannot make requests - for files on other domains, to do that you must put your data in a javascript - file and use {@link glow.net.loadScript} to fetch it. - @param {Object|String} data - Data to post, either as a JSON-style object or a urlEncoded string - @param {Object} opts - Same options as {@link glow.net.get} - - @returns {Number|glow.net.Response} - An integer identifying the async request, or the response object for sync requests - - @example - var postRef = glow.net.post("myFile.html", - {key:"value", otherkey:["value1", "value2"]}, - { - onLoad: function(response) { - alert("Got file:\n\n" + response.text()); - }, - onError: function(response) { - alert("Error getting file: " + response.statusText()); - } - } - ); - */ - r.post = function(url, data, o) { - o = populateOptions(o); - o.data = data; - if (!o.headers["Content-Type"]) { - o.headers["Content-Type"] = STR.POST_DEFAULT_CONTENT_TYPE; - } - return makeXhrRequest('POST', url, o); - }; - - /** - @name glow.net.loadScript - @function - @description Loads data by adding a script element to the end of the page - This can be used cross domain, but should only be used with trusted - sources as any javascript included in the script will be executed. - - @param {String} url - Url of the script. Use "{callback}" in the querystring as the callback - name if the data source supports it, then you can use the options below - @param {Object} [opts] - An object of options to use if "{callback}" is specified in the url. - @param {Function} [opts.onLoad] Called when loadScript succeeds. - The parameters are passed in by the external data source - @param {Function} [opts.onError] Called on timeout - No parameters are passed - @param {Function} [opts.onAbort] Called if the request is aborted - @param {Boolean} [opts.useCache=false] Allow a cached response - @param {Number} [opts.timeout] Time to allow for the request in seconds - @param {String} [opts.charset] Charset attribute value for the script - - @returns {glow.net.Request} - - @example - glow.net.loadScript("http://www.server.com/json/tvshows.php?jsoncallback={callback}", { - onLoad: function(data) { - alert("Data loaded"); - } - }); - */ - r.loadScript = function(url, opts) { - //id of the request - var newIndex = scriptElements.length, - //script element that gets inserted on the page - script, - //generated name of the callback, may not be used - callbackName = callbackPrefix + newIndex, - opts = populateOptions(opts), - request = new Request(newIndex, opts), - url = opts.useCache ? url : noCacheUrl(url), - //the global property used to hide callbacks - globalObject = window[globalObjectName] || (window[globalObjectName] = {}); - - //assign onload - if (opts.onLoad) { - globalObject[callbackName] = function() { - //clear the timeout - request._timeout && clearTimeout(request._timeout); - //set as completed - request.completed = true; - opts.onLoad.apply(this, arguments); - // cleanup - glow.dom.get(script).destroy(); - // clean up references to prevent leaks - script = scriptElements[newIndex] = globalObject[callbackName] = undefined; - delete globalObject[callbackName]; - delete scriptElements[newIndex]; - } - url = glow.lang.interpolate(url, {callback: globalObjectName + "." + callbackName}); - } - - script = scriptElements[newIndex] = document.createElement("script"); - - if (opts.charset) { - script.charset = opts.charset; - } - - //add abort event - events.addListener(request, "abort", opts.onAbort); - - glow.ready(function() { - //sort out the timeout - if (opts.timeout) { - request._timeout = setTimeout(function() { - abortRequest(request); - opts.onError(); - }, opts.timeout * 1000); - } - //using setTimeout to stop Opera 9.0 - 9.26 from running the loaded script before other code - //in the current script block - if (glow.env.opera) { - setTimeout(function() { - if (script) { //script may have been removed already - script.src = url; - } - }, 0); - } else { - script.src = url; - } - //add script to page - document.body.appendChild(script); - }); - - return request; - } - - /** - * @name glow.net.abortRequest - * @private - * @function - * @description Aborts the request - * Doesn't trigger any events - * - * @param {glow.net.Request} req Request Object - * @returns this - */ - function abortRequest(req) { - var nativeReq = req.nativeRequest, - callbackIndex = req._callbackIndex; - - //clear timeout - req._timeout && clearTimeout(req._timeout); - //different if request came from loadScript - if (nativeReq) { - //clear listeners - nativeReq.onreadystatechange = emptyFunc; - nativeReq.abort(); - } else if (callbackIndex) { - //clear callback - window[globalObjectName][callbackPrefix + callbackIndex] = emptyFunc; - //remove script element - glow.dom.get(scriptElements[callbackIndex]).destroy(); - } - } - - /** - * @name glow.net.Request - * @class - * @description Returned by {@link glow.net.post post}, {@link glow.net.get get} async requests and {@link glow.net.loadScript loadScript} - * @glowPrivateConstructor There is no direct constructor, since {@link glow.net.post post} and {@link glow.net.get get} create the instances. - */ - - /** - * @name glow.net.Request#event:load - * @event - * @param {glow.events.Event} event Event Object - * @description Fired when the request is sucessful - * For a get / post request, this will be fired when request returns - * with an HTTP code of 2xx. loadScript requests will fire 'load' only - * if {callback} is used in the URL. - */ - - /** - * @name glow.net.Request#event:abort - * @event - * @param {glow.events.Event} event Event Object - * @description Fired when the request is aborted - * If you cancel the default (eg, by returning false) the request - * will continue. - * @description Returned by {@link glow.net.post glow.net.post}, {@link glow.net.get glow.net.get} async requests and {@link glow.net.loadScript glow.net.loadScript} - * @see Using glow.net - * @glowPrivateConstructor There is no direct constructor, since {@link glow.net.post glow.net.post} and {@link glow.net.get glow.net.get} create the instances. - */ - - /** - * @name glow.net.Request#event:error - * @event - * @param {glow.events.Event} event Event Object - * @description Fired when the request is unsucessful - * For a get/post request, this will be fired when request returns - * with an HTTP code which isn't 2xx or the request times out. loadScript - * calls will fire 'error' only if the request times out. - */ - - - /* - We don't want users to create instances of this class, so the constructor is documented - out of view of jsdoc - - @param {Object} requestObj - Object which represents the request type. - For XHR requests it should be an XmlHttpRequest object, for loadScript - requests it should be a number, the Index of the callback in glow.net._jsonCbs - @param {Object} opts - Zero or more of the following as properties of an object: - @param {Function} [opts.onLoad] Called when the request is sucessful - @param {Function} [opts.onError] Called when a request is unsucessful - @param {Function} [opts.onAbort] Called when a request is aborted - - */ - function Request(requestObj, opts) { - /** - * @name glow.net.Request#_timeout - * @private - * @description timeout ID. This is set by makeXhrRequest or loadScript - * @type Number - */ - this._timeout = null; - /** - * @name glow.net.Request#complete - * @description Boolean indicating whether the request has completed - * @example - // request.complete with an asynchronous call - var request = glow.net.get( - "myFile.html", - { - async: true, - onload: function(response) { - alert(request.complete); // returns true - } - } - ); - alert(request.complete); // returns boolean depending on timing of asynchronous call - - // request.complete with a synchronous call - var request = glow.net.get("myFile.html", {async: false;}); - alert(request.complete); // returns true - * @type Boolean - */ - this.complete = false; - - if (typeof requestObj == "number") { - /** - * @name glow.net.Request#_callbackIndex - * @private - * @description Index of the callback in glow.net._jsonCbs - * This is only relavent for requests made via loadscript using the - * {callback} placeholder - * @type Number - */ - this._callbackIndex = requestObj; - } else { - /** - * @name glow.net.Request#nativeRequest - * @description The request object from the browser. - * This may not have the same properties and methods across user agents. - * Also, this will be undefined if the request originated from loadScript. - * @example - var request = glow.net.get( - "myFile.html", - { - async: true, - onload: function(response) { - alert(request.NativeObject); // returns Object() - } - } - ); - * @type Object - */ - this.nativeRequest = requestObj; - } - - //assign events - var eventNames = ["Load", "Error", "Abort"], i=0; - - for (; i < 3; i++) { - events.addListener(this, eventNames[i].toLowerCase(), opts["on" + eventNames[i]]); - } - - } - Request.prototype = { - /** - @name glow.net.Request#send - @function - @description Sends the request. - This is done automatically unless the defer option is set - @example - var request = glow.net.get( - "myFile.html", - { - onload : function(response) {alert("Loaded");}, - defer: true - } - ); - request.send(); // returns "Loaded" - @returns {Object} - This for async requests or a response object for sync requests - */ - //this function is assigned by makeXhrRequest - send: function() {}, - /** - * @name glow.net.Request#abort - * @function - * @description Aborts an async request - * The load & error events will not fire. If the request has been - * made using {@link glow.net.loadScript loadScript}, the script - * may still be loaded but the callback will not be fired. - * @example - var request = glow.net.get( - "myFile.html", - { - async: true, - defer: true, - onabort: function() { - alert("Something bad happened. The request was aborted."); - } - } - ); - request.abort(); // returns "Something bad happened. The request was aborted" - * @returns this - */ - abort: function() { - if (!this.completed && !events.fire(this, "abort").defaultPrevented()) { - abortRequest(this); - } - return this; - } - }; - - /** - @name glow.net.Response - @class - @description Provided in callbacks to {@link glow.net.post glow.net.post} and {@link glow.net.get glow.net.get} - @see Using glow.net - - @glowPrivateConstructor There is no direct constructor, since {@link glow.net.post glow.net.post} and {@link glow.net.get glow.net.get} create the instances. - */ - /* - These params are hidden as we don't want users to try and create instances of this... - - @param {XMLHttpRequest} nativeResponse - @param {boolean} [timedOut=false] - */ - function Response(nativeResponse, timedOut) { - //run Event constructor - events.Event.call(this); - - /** - @name glow.net.Response#nativeResponse - @description The response object from the browser. - This may not have the same properties and methods across user agents. - @type Object - */ - this.nativeResponse = nativeResponse; - /** - @name glow.net.Response#status - @description HTTP status code of the response - @type Number - */ - //IE reports status as 1223 rather than 204, for laffs - this.status = timedOut ? 408 : - nativeResponse.status == 1223 ? 204 : nativeResponse.status; - - /** - * @name glow.net.Response#timedOut - * @description Boolean indicating if the requests time out was reached. - * @type Boolean - */ - this.timedOut = !!timedOut; - - /** - * @name glow.net.Response#wasSuccessful - * @description Boolean indicating if the request returned successfully. - * @type Boolean - */ - this.wasSuccessful = (this.status >= 200 && this.status < 300) || - //from cache - this.status == 304 || - //watch our for requests from file:// - (this.status == 0 && nativeResponse.responseText); - - } - //don't want to document this inheritance, it'll just confuse the user - glow.lang.extend(Response, events.Event, { - /** - @name glow.net.Response#text - @function - @description Gets the body of the response as plain text - @returns {String} - Response as text - */ - text: function() { - return this.nativeResponse.responseText; - }, - /** - @name glow.net.Response#xml - @function - @description Gets the body of the response as xml - @returns {xml} - Response as XML - */ - xml: function() { - if (!this.nativeResponse.responseXML) { - throw new Error(STR.XML_ERR); - } - return this.nativeResponse.responseXML; - }, - - /** - @name glow.net.Response#json - @function - @description Gets the body of the response as a json object - - @param {Boolean} [safeMode=false] - If true, the response will be parsed using a string parser which - will filter out non-JSON javascript, this will be slower but - recommended if you do not trust the data source. - - @returns {Object} - */ - json: function(safe) { - return glow.data.decodeJson(this.text(), {safeMode:safe}); - }, - - /** - @name glow.net.Response#header - @function - @description Gets a header from the response - - @param {String} name - Header name - - @returns {String} - Header value - - @example var contentType = myResponse.header("Content-Type"); - */ - header: function(name) { - return this.nativeResponse.getResponseHeader(name); - }, - - /** - @name glow.net.Response#statusText - @function - @description Gets the meaning of {@link glow.net.Response#status myResponse.status} - - @returns {String} - */ - statusText: function() { - return this.timedOut ? "Request Timeout" : this.nativeResponse.statusText; - } - }) - - glow.net = r; - } -}); - -/** -@name glow.tweens -@namespace -@description Functions for modifying animations -@see What are tweens? - -*/ -(window.gloader || glow).module({ - name: "glow.tweens", - library: ["glow", "1.5.1"], - depends: [], - builder: function(glow) { - - /* - PrivateMethod: _reverse - Takes a tween function and returns a function which does the reverse - */ - function _reverse(tween) { - return function(t) { - return 1 - tween(1 - t); - } - } - - glow.tweens = { - /** - @name glow.tweens.linear - @function - @description Returns linear tween. - - Will transition values from start to finish with no - acceleration or deceleration. - - @returns {Function} - */ - linear: function() { - return function(t) { return t; }; - }, - /** - @name glow.tweens.easeIn - @function - @description Creates a tween which starts off slowly and accelerates. - - @param {Number} [strength=2] How strong the easing is. - - A higher number means the animation starts off slower and - ends quicker. - - @returns {Function} - */ - easeIn: function(strength) { - strength = strength || 2; - return function(t) { - return Math.pow(1, strength - 1) * Math.pow(t, strength); - } - }, - /** - @name glow.tweens.easeOut - @function - @description Creates a tween which starts off fast and decelerates. - - @param {Number} [strength=2] How strong the easing is. - - A higher number means the animation starts off faster and - ends slower - - @returns {Function} - */ - easeOut: function(strength) { - return _reverse(this.easeIn(strength)); - }, - /** - @name glow.tweens.easeBoth - @function - @description Creates a tween which starts off slowly, accelerates then decelerates after the half way point. - - This produces a smooth and natural looking transition. - - @param {Number} [strength=2] How strong the easing is. - - A higher number produces a greater difference between - start / end speed and the mid speed. - - @returns {Function} - */ - easeBoth: function(strength) { - return this.combine(this.easeIn(strength), this.easeOut(strength)); - }, - /** - @name glow.tweens.overshootIn - @function - @description Returns the reverse of {@link glow.tweens.overshootOut overshootOut} - - @param {Number} [amount=1.70158] How much to overshoot. - - The default is 1.70158 which results in a 10% overshoot. - - @returns {Function} - */ - overshootIn: function(amount) { - return _reverse(this.overshootOut(amount)); - }, - /** - @name glow.tweens.overshootOut - @function - @description Creates a tween which overshoots its end point then returns to its end point. - - @param {Number} [amount=1.70158] How much to overshoot. - - The default is 1.70158 which results in a 10% overshoot. - - @returns {Function} - */ - overshootOut: function(amount) { - amount = amount || 1.70158; - return function(t) { - if (t == 0 || t == 1) { return t; } - return ((t -= 1)* t * ((amount + 1) * t + amount) + 1); - } - }, - /** - @name glow.tweens.overshootBoth - @function - @description Returns a combination of {@link glow.tweens.overshootIn overshootIn} and {@link glow.tweens.overshootOut overshootOut} - - @param {Number} [amount=1.70158] How much to overshoot. - - The default is 1.70158 which results in a 10% overshoot. - - @returns {Function} - */ - overshootBoth: function(amount) { - return this.combine(this.overshootIn(amount), this.overshootOut(amount)); - }, - /** - @name glow.tweens.bounceIn - @function - @description Returns the reverse of {@link glow.tweens.bounceOut bounceOut} - - @returns {Function} - */ - bounceIn: function() { - return _reverse(this.bounceOut()); - }, - /** - @name glow.tweens.bounceOut - @function - @description Returns a tween which bounces against the final value 3 times before stopping - - @returns {Function} - */ - bounceOut: function() { - return function(t) { - if (t < (1 / 2.75)) { - return 7.5625 * t * t; - } else if (t < (2 / 2.75)) { - return (7.5625 * (t -= (1.5 / 2.75)) * t + .75); - } else if (t < (2.5 / 2.75)) { - return (7.5625 * (t -= (2.25 / 2.75)) * t + .9375); - } else { - return (7.5625 * (t -= (2.625 / 2.75)) * t + .984375); - } - }; - }, - /** - @name glow.tweens.bounceBoth - @function - @description Returns a combination of {@link glow.tweens.bounceIn bounceIn} and {@link glow.tweens.bounceOut bounceOut} - - @returns {Function} - */ - bounceBoth: function() { - return this.combine(this.bounceIn(), this.bounceOut()); - }, - /** - @name glow.tweens.elasticIn - @function - @description Returns the reverse of {@link glow.tweens.elasticOut elasticOut} - - @param {Number} [amplitude=1] How strong the elasticity is. - - @param {Number} [period=0.3] The frequency period. - - @returns {Function} - */ - elasticIn: function(a, p) { - return _reverse(this.elasticOut(a, p)); - }, - /** - @name glow.tweens.elasticOut - @function - @description Creates a tween which has an elastic movement. - - You can tweak the tween using the parameters but you'll - probably find the defaults sufficient. - - @param {Number} [amplitude=1] How strong the elasticity is. - - @param {Number} [period=0.3] The frequency period. - - @returns {Function} - */ - elasticOut: function(a, p) { - return function (t) { - if (t == 0 || t == 1) { - return t; - } - if (!p) { - p = 0.3; - } - if (!a || a < 1) { - a = 1; - var s = p / 4; - } else { - var s = p / (2 * Math.PI) * Math.asin(1 / a); - } - return a * Math.pow(2, -10 * t) * Math.sin( (t-s) * (2 * Math.PI) / p) + 1; - } - }, - /** - @name glow.tweens.elasticBoth - @function - @description Returns a combination of {@link glow.tweens.elasticIn elasticIn} and {@link glow.tweens.elasticOut elasticOut} - - @param {Number} [amplitude=1] How strong the elasticity is. - - @param {Number} [period=0.3] The frequency period. - - @returns {Function} - */ - elasticBoth: function(a, p) { - p = p || 0.45; - return this.combine(this.elasticIn(a, p), this.elasticOut(a, p)); - }, - /** - @name glow.tweens.combine - @function - @description Create a tween from two tweens. - - This can be useful to make custom tweens which, for example, - start with an easeIn and end with an overshootOut. To keep - the motion natural, you should configure your tweens so the - first ends and the same velocity that the second starts. - - @param {Function} tweenIn Tween to use for the first half - - @param {Function} tweenOut Tween to use for the second half - - @example - // 4.5 has been chosen for the easeIn strength so it - // ends at the same velocity as overshootOut starts. - var myTween = glow.tweens.combine( - glow.tweens.easeIn(4.5), - glow.tweens.overshootOut() - ); - - @returns {Function} - */ - combine: function(tweenIn, tweenOut) { - return function (t) { - if (t < 0.5) { - return tweenIn(t * 2) / 2; - } else { - return tweenOut((t - 0.5) * 2) / 2 + 0.5; - } - } - } - }; - } -}); -/** -@name glow.anim -@namespace -@description Simple and powerful animations. -@requires glow, glow.tweens, glow.events, glow.dom -@see What are tweens? -@see Guide to creating animations, with interactive examples -*/ -(window.gloader || glow).module({ - name: "glow.anim", - library: ["glow", "1.5.1"], - depends: [["glow", "1.5.1", "glow.tweens", "glow.events", "glow.dom"]], - builder: function(glow) { - //private - var $ = glow.dom.get, - manager, - events = glow.events, - dom = glow.dom, - get = dom.get, - hasUnits = /width|height|top$|bottom$|left$|right$|spacing$|indent$|font-size/, - noNegatives = /width|height|padding|opacity/, - usesYAxis = /height|top/, - getUnit = /(\D+)$/, - testElement = dom.create('
'); - - (function() { - var queue = [], //running animations - queueLen = 0, - intervalTime = 1, //ms between intervals - interval; //holds the number for the interval - manager = { - /** - @name glow.anim-manager.addToQueue - @private - @function - @description Adds an animation to the queue. - */ - addToQueue: function(anim) { - //add the item to the queue - queue[queueLen++] = anim; - anim._playing = true; - anim._timeAnchor = anim._timeAnchor || new Date().valueOf(); - if (!interval) { - this.startInterval(); - } - }, - /** - @name glow.anim-manager.removeFromQueue - @private - @function - @description Removes an animation from the queue. - */ - removeFromQueue: function(anim) { - for (var i = 0; i < queueLen; i++) { - if (queue[i] == anim) { - queue.splice(i, 1); - anim._timeAnchor = null; - anim._playing = false; - //stop the queue if there's nothing in it anymore - if (--queueLen == 0) { - this.stopInterval(); - } - return; - } - } - }, - /** - @name glow.anim-manager.startInterval - @private - @function - @description Start processing the queue every interval. - */ - startInterval: function() { - interval = window.setInterval(this.processQueue, intervalTime); - }, - /** - @name glow.anim-manager.stopInterval - @private - @function - @description Stop processing the queue. - */ - stopInterval: function() { - window.clearInterval(interval); - interval = null; - }, - /** - @name glow.anim-manager.processQueue - @private - @function - @description Animate each animation in the queue. - */ - processQueue: function() { - var anim, i, now = new Date().valueOf(); - for (i = 0; i < queueLen; i++) { - anim = queue[i]; - if (anim.position == anim.duration) { - manager.removeFromQueue(anim); - //need to decrement the index because we've just removed an item from the queue - i--; - events.fire(anim, "complete"); - if (anim._opts.destroyOnComplete) { - anim.destroy(); - } - continue; - } - if (anim.useSeconds) { - anim.position = (now - anim._timeAnchor) / 1000; - if (anim.position > anim.duration) { - anim.position = anim.duration; - } - } else { - anim.position++; - } - anim.value = anim.tween(anim.position / anim.duration); - events.fire(anim, "frame"); - } - } - }; - })(); - - /** - @name glow.anim.convertCssUnit - @private - @function - @param {nodelist} element - @param {string|number} fromValue Assumed pixels. - @param {string} toUnit (em|%|pt...) - @param {string} axis (x|y) - @description Converts a css unit. - - We need to know the axis for calculating relative values, since they're - relative to the width / height of the parent element depending - on the situation. - - */ - function convertCssUnit(element, fromValue, toUnit, axis) { - var elmStyle = testElement[0].style, - axisProp = (axis == "x") ? "width" : "height", - startPixelValue, - toUnitPixelValue; - //reset stuff that may affect the width / height - elmStyle.margin = elmStyle.padding = elmStyle.border = "0"; - startPixelValue = testElement.css(axisProp, fromValue).insertAfter(element)[axisProp](); - //using 10 of the unit then dividing by 10 to increase accuracy - toUnitPixelValue = testElement.css(axisProp, 10 + toUnit)[axisProp]() / 10; - testElement.remove(); - return startPixelValue / toUnitPixelValue; - } - - /** - @name glow.anim.keepWithinRange - @private - @function - @param num - @param [start] - @param [end] - @description - Takes a number then an (optional) lower range and an (optional) upper range. If the number - is outside the range the nearest range boundary is returned, else the number is returned. - */ - function keepWithinRange(num, start, end) { - if (start !== undefined && num < start) { - return start; - } - if (end !== undefined && num > end) { - return end; - } - return num; - } - - /** - @name glow.anim.buildAnimFunction - @private - @function - @param element - @param spec - @description Builds a function for an animation. - */ - function buildAnimFunction(element, spec) { - var cssProp, - r = ["a=(function(){"], - rLen = 1, - fromUnit, - unitDefault = [0,"px"], - to, - from, - unit, - a; - - for (cssProp in spec) { - r[rLen++] = 'element.css("' + cssProp + '", '; - //fill in the blanks - if (typeof spec[cssProp] != "object") { - to = spec[cssProp]; - } else { - to = spec[cssProp].to; - } - if ((from = spec[cssProp].from) === undefined) { - if (cssProp == "font-size" || cssProp == "background-position") { - throw new Error("From value must be set for " + cssProp); - } - from = element.css(cssProp); - } - //TODO help some multi value things? - if (hasUnits.test(cssProp)) { - //normalise the units for unit-ed values - unit = (getUnit.exec(to) || unitDefault)[1]; - fromUnit = (getUnit.exec(from) || unitDefault)[1]; - //make them numbers, we have the units seperate - from = parseFloat(from) || 0; - to = parseFloat(to) || 0; - //if the units don't match, we need to have a play - if (from && unit != fromUnit) { - if (cssProp == "font-size") { - throw new Error("Units must be the same for font-size"); - } - from = convertCssUnit(element, from + fromUnit, unit, usesYAxis.test(cssProp) ? "y" : "x"); - } - if (noNegatives.test(cssProp)) { - r[rLen++] = 'keepWithinRange((' + (to - from) + ' * this.value) + ' + from + ', 0) + "' + unit + '"'; - } else { - r[rLen++] = '(' + (to - from) + ' * this.value) + ' + from + ' + "' + unit + '"'; - } - } else if (! (isNaN(from) || isNaN(to))) { //both pure numbers - from = Number(from); - to = Number(to); - r[rLen++] = '(' + (to - from) + ' * this.value) + ' + from; - } else if (cssProp.indexOf("color") != -1) { - to = dom.parseCssColor(to); - if (! glow.lang.hasOwnProperty(from, "r")) { - from = dom.parseCssColor(from); - } - r[rLen++] = '"rgb(" + keepWithinRange(Math.round(' + (to.r - from.r) + ' * this.value + ' + from.r + - '), 0, 255) + "," + keepWithinRange(Math.round(' + (to.g - from.g) + ' * this.value + ' + from.g + - '), 0, 255) + "," + keepWithinRange(Math.round(' + (to.b - from.b) + ' * this.value + ' + from.b + - '), 0, 255) + ")"'; - } else if (cssProp == "background-position") { - var vals = {}, - fromTo = ["from", "to"], - unit = (getUnit.exec(from) || unitDefault)[1]; - vals.fromOrig = from.toString().split(/\s/); - vals.toOrig = to.toString().split(/\s/); - - if (vals.fromOrig[1] === undefined) { - vals.fromOrig[1] = "50%"; - } - if (vals.toOrig[1] === undefined) { - vals.toOrig[1] = "50%"; - } - - for (var i = 0; i < 2; i++) { - vals[fromTo[i] + "X"] = parseFloat(vals[fromTo[i] + "Orig"][0]); - vals[fromTo[i] + "Y"] = parseFloat(vals[fromTo[i] + "Orig"][1]); - vals[fromTo[i] + "XUnit"] = (getUnit.exec(vals[fromTo[i] + "Orig"][0]) || unitDefault)[1]; - vals[fromTo[i] + "YUnit"] = (getUnit.exec(vals[fromTo[i] + "Orig"][1]) || unitDefault)[1]; - } - - if ((vals.fromXUnit !== vals.toXUnit) || (vals.fromYUnit !== vals.toYUnit)) { - throw new Error("Mismatched axis units cannot be used for " + cssProp); - } - - r[rLen++] = '(' + (vals.toX - vals.fromX) + ' * this.value + ' + vals.fromX + ') + "' + vals.fromXUnit + ' " + (' + - (vals.toY - vals.fromY) + ' * this.value + ' + vals.fromY + ') + "' + vals.fromYUnit + '"'; - } - r[rLen++] = ');'; - } - r[rLen++] = "})"; - return eval(r.join("")); - } - - //public - var r = {}; //return object - - /** - @name glow.anim.css - @function - @description Animates CSS properties of an element. - @param {String | glow.dom.NodeList | Element} element Element to animate. - - This can be a CSS selector (first match will be used), - {@link glow.dom.NodeList} (first node will be used), or a DOM element. - - @param {Number} duration Animation duration, in seconds by default. - - @param {Object} spec An object describing the properties to animate. - - This object should consist of property names corresponding to the - CSS properties you wish to animate, and values which are objects - with 'from' and 'to' properties with the values to animate between - or a number/string representing the value to animate to. - - If the 'from' property is absent, the elements current CSS value - will be used instead. - - See the spec example below for more information. - - @param {Object} opts Optional options object. - - @param {Boolean} [opts.useSeconds=true] Specifies whether duration should be in seconds rather than frames. - - @param {Function} [opts.tween=linear tween] The way the value moves through time. See {@link glow.tweens}. - - @example - // an example of an spec object - { - "height": {from: "10px", to: "100px"}, - "width": "100px", - "font-size": {from: "0.5em", to: "1.3em"} - } - - @example - // animate an elements height and opacity to 0 from current values over 1 second - glow.anim.css("#myElement", 1, { - "height" : 0, - "opacity" : 0 - }).start(); - - @returns {glow.anim.Animation} - */ - r.css = function(element, duration, spec, opts) { - - element = get(element); - - var anim = new r.Animation(duration, opts); - - // Fix for trac 156 - glow.anim.css should fail better if the element doesn't exist - if (element[0]) { - events.addListener(anim, "frame", buildAnimFunction(element, spec)); - } - return anim; - }; - - /** - @name glow.anim-slideElement - @private - @function - @param {String | glow.dom.NodeList} element Element to animate. CSS Selector can be used. - @param {Number} duration Animation duration in seconds. - @param {Function} [opts.tween=easeBoth tween] The way the value moves through time. See {@link glow.tweens}. - @param {Function} [opts.onStart] The function to be called when the first element in the NodeList starts the animation. - @param {Function} [opts.onComplete] The function to be called when the first element in the NodeList completes the animation. - @description Builds a function for an animation. - */ - slideElement = function slideElement(element, duration, action, opts) { - duration = duration || 0.5; - // normalise 'element' to NodeList - element = $(element); - - opts = glow.lang.apply({ - tween: glow.tweens.easeBoth(), - onStart: function(){}, - onComplete: function(){} - }, opts); - - var i = 0, - thatlength = element.length, - completeHeight, - fromHeight, - channels = [], - timeline; - - for(; i < thatlength; i++) { - if (action == "up" || (action == "toggle" && element.slice(i, i+1).height() > 0)) { - element[i].style.overflow = 'hidden'; - // give the element layout in IE - if (glow.env.ie < 8) { - element[i].style.zoom = 1; - } - completeHeight = 0; - fromHeight = element.slice(i, i+1).height(); - } else if (action == "down" || (action = "toggle" && element.slice(i, i+1).height() == 0)) { - fromHeight = element.slice(i, i+1).height(); - element[i].style.height = "auto"; - completeHeight = element.slice(i, i+1).height(); - element[i].style.height = fromHeight + "px"; - } - - channels[i] = [ - glow.anim.css(element[i], duration, { - 'height': {from: fromHeight, to: completeHeight} - }, { tween: opts.tween }) - ]; - - } - - timeline = new glow.anim.Timeline(channels); - - events.addListener(timeline, "complete", function() { - // return heights to "auto" for slide down - element.each(function() { - if (this.style.height != "0px") { - this.style.height = "auto"; - } - }) - }); - - events.addListener(timeline, "start", opts.onStart); - events.addListener(timeline, "complete", opts.onComplete); - - // return & start our new timeline - return timeline.start(); - }; - - /** - @name glow.anim.slideDown - @function - @description Slide a NodeList down from a height of 0 - - @param {String | glow.dom.NodeList} element Element to animate. CSS Selector can be used. - - @param {Number} duration Animation duration in seconds. - - @param {Function} opts Object - - @param {Function} [opts.tween=easeBoth tween] The way the value moves through time. See {@link glow.tweens}. - - @param {Function} [opts.onStart] The function to be called when the first element in the NodeList starts the animation. - - @param {Function} [opts.onComplete] The function to be called when the first element in the NodeList completes the animation. - - @returns {glow.anim.Timeline} - - A started timeline - - @example - - glow.anim.slideDown("#menu", 1); - - **/ - r.slideDown = function(element, duration, opts) { - return slideElement(element, duration, 'down', opts); - }; - - /** - @name glow.anim.slideUp - @function - @description Slide a NodeList up to a height of 0 - - @param {String | glow.dom.NodeList} element Element to animate. CSS Selector can be used. - - @param {Number} duration Animation duration in seconds. - - @param {Function} opts Object - - @param {Function} [opts.tween=easeBoth tween] The way the value moves through time. See {@link glow.tweens}. - - @param {Function} [opts.onStart] The function to be called when the first element in the NodeList starts the animation. - - @param {Function} [opts.onComplete] The function to be called when the first element in the NodeList completes the animation. - - @returns {glow.anim.Timeline} - - A started timeline - - @example - - glow.anim.slideUp("#menu", 1); - - **/ - r.slideUp = function(element, duration, opts) { - return slideElement(element, duration, 'up', opts); - }; - - /** - @name glow.anim.slideToggle - @function - @description Toggle a NodeList Up or Down depending on it's present state. - - @param {String | glow.dom.NodeList} element Element to animate. CSS Selector can be used. - - @param {Number} duration Animation duration in seconds. - - @param {Function} opts Object - - @param {Function} [opts.tween=easeBoth tween] The way the value moves through time. See {@link glow.tweens}. - - @param {Function} [opts.onStart] The function to be called when the first element in the NodeList starts the animation. - - @param {Function} [opts.onComplete] The function to be called when the first element in the NodeList completes the animation. - - @returns {glow.anim.Timeline} - - A started timeline - - @example - - glow.anim.slideToggle("#menu", 1); - - **/ - r.slideToggle = function(element, duration, opts) { - return slideElement(element, duration, 'toggle', opts); - }; - - - /** - @name glow.anim.fadeOut - @function - @description Fade out a set of elements - - @param {String | glow.dom.NodeList} element Element to animate. CSS Selector can be used. - - @param {Number} duration Animation duration in seconds. - - @param {Function} opts Object - - @param {Function} [opts.tween=easeBoth tween] The way the value moves through time. See {@link glow.tweens}. - - @param {Function} [opts.onStart] The function to be called when the first element in the NodeList starts the animation. - - @param {Function} [opts.onComplete] The function to be called when the first element in the NodeList completes the animation. - - @returns {glow.anim.Timeline} - - A started timeline - - @example - - glow.anim.fadeOut("#menu", 1); - - **/ - r.fadeOut = function(element, duration, opts) { - return r.fadeTo(element, 0, duration, opts) - }; - - /** - @name glow.anim.fadeIn - @function - @description Fade in a set of elements - - @param {String | glow.dom.NodeList} element Element to animate. CSS Selector can be used. - - @param {Number} duration Animation duration in seconds. - - @param {Function} opts Object - - @param {Function} [opts.tween=easeBoth tween] The way the value moves through time. See {@link glow.tweens}. - - @param {Function} [opts.onStart] The function to be called when the first element in the NodeList starts the animation. - - @param {Function} [opts.onComplete] The function to be called when the first element in the NodeList completes the animation. - - @returns {glow.anim.Timeline} - - A started timeline - - @example - - glow.anim.fadeIn("#menu", 1); - - **/ - r.fadeIn = function(element, duration, opts){ - r.fadeTo(element, 1, duration, opts); - }; - - /** - @name glow.anim.fadeTo - @function - @description Fade a set of elements to a given opacity - - @param {String | glow.dom.NodeList} element Element to animate. CSS Selector can be used. - - @param {Number} opacity fade to opacity level between 0 & 1. - - @param {Number} duration Animation duration in seconds. - - @param {Function} opts Object - - @param {Function} [opts.tween=easeBoth tween] The way the value moves through time. See {@link glow.tweens}. - - @param {Function} [opts.onStart] The function to be called when the first element in the NodeList starts the animation. - - @param {Function} [opts.onComplete] The function to be called when the first element in the NodeList completes the animation. - - @returns {glow.anim.Timeline} - - A started timeline - - @example - - glow.anim.fadeTo("#menu", 0.5, 1); - - **/ - r.fadeTo = function(element, opacity, duration, opts){ - duration = duration || 0.5; - // normalise 'element' to NodeList - element = $(element); - - opts = glow.lang.apply({ - tween: glow.tweens.easeBoth(), - onStart: function(){}, - onComplete: function(){} - }, opts); - - var i = 0, - thatlength = element.length, - channels = [], - timeline; - - for(; i < thatlength; i++) { - channels[i] = [ - glow.anim.css(element[i], duration, { - 'opacity': opacity - }, { tween: opts.tween }) - ]; - } - - timeline = new glow.anim.Timeline(channels); - - events.addListener(timeline, "start", opts.onStart); - events.addListener(timeline, "complete", opts.onComplete); - - // return & start our new timeline - return timeline.start(); - }; - - - /** - @name glow.anim.highlight - @function - @description Highlight an element by fading the background colour - - @param {String | glow.dom.NodeList} element Element to animate. CSS Selector can be used. - - @param {String} highlightColour highlight colour in hex, "rgb(r, g, b)" or css colour name. - - @param {Number} duration Animation duration in seconds. - - @param {Function} opts Object - - @param {Function} [opts.completeColour] The background colour of the element once the highlight is complete. - - If none supplied Glow assumes the element's existing background color (e.g. #336699), - if the element has no background color specified (e.g. Transparent) - the highlight will transition to white. - - @param {Function} [opts.tween=easeBoth tween] The way the value moves through time. See {@link glow.tweens}. - - @param {Function} [opts.onStart] The function to be called when the first element in the NodeList starts the animation. - - @param {Function} [opts.onComplete] The function to be called when the first element in the NodeList completes the animation. - - @returns {glow.anim.Timeline} - - A started timeline - - @example - - glow.anim.highlight("#textInput", "#ff0", 1); - **/ - r.highlight = function(element, highlightColour, duration, opts){ - // normalise element - element = $(element); - - duration = duration || 1; - highlightColour = highlightColour || '#ffff99'; - - opts = glow.lang.apply({ - tween: glow.tweens.easeBoth(), - onStart: function(){}, - onComplete: function(){} - }, opts); - - var i = 0, - transArray = [], - elmsLength = element.length, - completeColour, - channels = [], - timeline; - - for(; i < elmsLength; i++) { - - completeColour = opts.completeColour || element.slice(i, i+1).css("background-color"); - - if (completeColour == "transparent" || completeColour == "") { - completeColour = "#fff"; - } - channels[i] = [ - r.css(element[i], duration, { - "background-color" : {from:highlightColour, to:completeColour} - }, {tween: opts.tween}) - ]; - } - - timeline = new glow.anim.Timeline(channels); - - events.addListener(timeline, "start", opts.onStart); - events.addListener(timeline, "complete", opts.onComplete); - return timeline.start(); - }; - - /** - @name glow.anim.Animation - @class - @description Controls modifying values over time. - - You can create an animtion instance using the constructor, or use - one of the helper methods in {@link glow.anim}. - - Once you have created your animation instance, you can use - events such as "frame" to change values over time. - - @param {Number} duration Length of the animation in seconds / frames. - - Animations which are given a duration in seconds may drop frames to - finish in the given time. - - @param {Object} opts Object of options. - - @param {Boolean} [opts.useSeconds=true] Specifies whether duration should be in seconds rather than frames. - - @param {Function} [opts.tween=linear tween] The way the value moves through time. - - See {@link glow.tweens}. - - @param {Boolean} [opts.destroyOnComplete=false] Destroy the animation once it completes? - This will free any DOM references the animation may have created. Once - the animation completes, you won't be able to start it again. - - @example - var myAnim = new glow.anim.Animation(5, { - tween:glow.tweens.easeBoth() - }); - - */ - - /** - @name glow.anim.Animation#event:start - @event - @description Fired when the animation is started from the beginning. - @param {glow.events.Event} event Event Object - @example - var myAnim = new glow.anim.Animation(5, { - tween:glow.tweens.easeBoth() - }); - glow.events.addListener(myAnim, "start", function() { - alert("Started animation which lasts " + this.duration + " seconds"); - }); - myAnim.start(); - */ - - /** - @name glow.anim.Animation#event:frame - @event - @description Fired in each frame of the animation. - - This is where you'll specify what your animation does. - - @param {glow.events.Event} event Event Object - @example - var myAnim = new glow.anim.Animation(5, { - tween:glow.tweens.easeBoth() - }); - - var myDiv = glow.dom.get("#myDiv"), - divStartHeight = myDiv.height(), - divEndHeight = 500, - divHeightChange = divEndHeight - divStartHeight; - - glow.events.addListener(myAnim, "frame", function() { - myDiv.height(divStartHeight + (divHeightChange * this.value)); - }); - myAnim.start(); - */ - - /** - @name glow.anim.Animation#event:stop - @event - @description Fired when the animation is stopped before its end. - - If your listener prevents the default action (for instance, - by returning false) the animtion will not be stopped. - - @param {glow.events.Event} event Event Object - */ - - /** - @name glow.anim.Animation#event:complete - @event - @description Fired when the animation ends. - @param {glow.events.Event} event Event Object - */ - - /** - @name glow.anim.Animation#event:resume - @event - @description Fired when the animation resumes after being stopped. - - If your listener prevents the default action (for instance, by - returning false) the animation will not be resumed. - - @param {glow.events.Event} event Event Object - */ - r.Animation = function(duration, opts) { - this._opts = opts = glow.lang.apply({ - useSeconds: true, - tween: glow.tweens.linear(), - destroyOnComplete: false - }, opts); - - /** - @name glow.anim.Animation#_playing - @type Boolean - @private - @default false - @description Indicates whether the animation is playing. - */ - this._playing = false; - - /** - @name glow.anim.Animation#_timeAnchor - @type Number - @private - @default null - @description A timestamp used to keep the animation in the right position. - */ - this._timeAnchor = null; - - /** - @name glow.anim.Animation#duration - @type Number - @description Length of the animation in seconds / frames. - */ - this.duration = duration; - - /** - @name glow.anim.Animation#useSeconds - @type Boolean - @description Indicates whether duration is in seconds rather than frames. - */ - this.useSeconds = opts.useSeconds; - - /** - @name glow.anim.Animation#tween - @type Function - @description The tween used by the animation. - */ - this.tween = opts.tween; - - /** - @name glow.anim.Animation#position - @type Number - @default 0 - @description Seconds since starting, or current frame. - */ - this.position = 0; - - /** - @name glow.anim.Animation#value - @type Number - @default 0 - @description Current tweened value of the animtion, usually between 0 & 1. - The value may become greater than 1 or less than 0 depending - on the tween used. - - {@link glow.tweens.elasticOut} for instance will result - in values higher than 1, but will still end at 1. - */ - this.value = 0; - - }; - r.Animation.prototype = { - - /** - @name glow.anim.Animation#start - @function - @description Starts playing the animation from the beginning. - @example - var myAnim = new glow.anim.Animation(5, { - tween:glow.tweens.easeBoth() - }); - //attach events here - myAnim.start(); - @returns {glow.anim.Animation} - */ - start: function() { - if (this._playing) { - this.stop(); - } - var e = events.fire(this, "start"); - if (e.defaultPrevented()) { return this; } - this._timeAnchor = null; - this.position = 0; - manager.addToQueue(this); - - return this; - }, - - /** - @name glow.anim.Animation#stop - @function - @description Stops the animation playing. - @returns {glow.anim.Animation} - */ - stop: function() { - if (this._playing) { - var e = events.fire(this, "stop"); - if (e.defaultPrevented()) { return this; } - manager.removeFromQueue(this); - } - return this; - }, - - /** - @name glow.anim.Animation#destroy - @function - @description Destroys the animation & detatches references to DOM nodes - Call this on animations you no longer need to free memory. - @returns {glow.anim.Animation} - */ - destroy: function() { - // stop the animation in case it's still playing - this.stop(); - events.removeAllListeners(this); - return this; - }, - - /** - @name glow.anim.Animation#resume - @function - @description Resumes the animation from where it was stopped. - @returns {glow.anim.Animation} - */ - resume: function() { - if (! this._playing) { - var e = events.fire(this, "resume"); - if (e.defaultPrevented()) { return this; } - //set the start time to cater for the pause - this._timeAnchor = new Date().valueOf() - (this.position * 1000); - manager.addToQueue(this); - } - return this; - }, - - /** - @name glow.anim.Animation#isPlaying - @function - @description Returns true if the animation is playing. - @returns {Boolean} - */ - isPlaying: function() { - return this._playing; - }, - /** - @name glow.anim.Animation#goTo - @function - @description Goes to a specific point in the animation. - @param {Number} pos Position in the animation to go to. - - This should be in the same units as the duration of your - animation (seconds or frames). - - @example - var myAnim = new glow.anim.Animation(5, { - tween:glow.tweens.easeBoth() - }); - //attach events here - //start the animation from half way through - myAnim.goTo(2.5).resume(); - @returns this - */ - goTo: function(pos) { - this._timeAnchor = new Date().valueOf() - ((this.position = pos) * 1000); - this.value = this.tween(this.duration && this.position / this.duration); - events.fire(this, "frame"); - return this; - } - }; - - /** - @name glow.anim.Timeline - @class - @description Synchronises and chains animations. - @param {Array | Array[]} channels An array of channels or a single channel. - - A channel is defined as an array containing numbers, animations and - functions. - - Numbers indicate a number of seconds to wait before proceeding to - the next item. Animations will be played, when the animation is - complete the next item is processed. Functions will be called, then - the next item is processed. - - @param {Object} opts An object of options. - @param {Boolean} [opts.loop=false] Specifies whether the timeline loops. - - The "complete" event does not fire for looping animations. - - @param {Boolean} [opts.destroyOnComplete=false] Destroy the animation once it completes? - This will free any DOM references the animation may have created. Once - the animation completes, you won't be able to start it again. - - @example - // in the simplest form, a timeline can be used to - // string multiple animations together: - - - // make our animations - var moveUp = glow.anim.css(myDiv, { - "top": {to:"0"} - }); - var moveDown = glow.anim.css(myDiv, { - "top": {to:"100px"} - }); - // string them together - new glow.anim.Timeline([moveUp, moveDown]).start(); - - @example - // if you wanted a one second gap between the animations, the last line would be: - new glow.anim.Timeline([moveUp, 1, moveDown]).start(); - - @example - // you can run animations simutainiously with multiple channels. - new glow.anim.Timeline([ - [moveDivUp, 1, moveDivDown], - [moveListDown, 1, moveListUp] - ]).start(); - @see Creating a mexican wave with an animation timeline - */ - /** - @name glow.anim.Timeline#event:start - @event - @description Fired when the timeline is started from the beginning. - - This event will also trigger during each loop of a looping animation. - If your listener prevents the default action (for instance, by - returning false) the timeline will not start. - - @param {glow.events.Event} event Event Object - @example - var myTimeline = new glow.anim.Timeline([anim1, anim2]); - glow.events.addListener(myTimeline, "start", function() { - alert("Started timeline"); - }); - myTimeline.start(); - */ - /** - @name glow.anim.Timeline#event:stop - @event - @description Fired when the timeline is stopped before its end. - - If your listener prevents the default action (for instance, by - returning false) the timeline will not stop. - - @param {glow.events.Event} event Event Object - */ - /** - @name glow.anim.Timeline#event:complete - @event - @description Fired when the timeline ends. - - This event does not fire on looping timelines. - - @param {glow.events.Event} event Event Object - */ - /** - @name glow.anim.Timeline#event:resume - @event - @description Fired when the timeline resumes after being stopped. - - If your listener prevents the default action (for instance, by - returning false) the timeline will not resume. - - @param {glow.events.Event} event Event Object - */ - r.Timeline = function(channels, opts) { - this._opts = opts = glow.lang.apply({ - loop: false, - destroyOnComplete: false - }, opts); - /* - PrivateProperty: _channels - Array of channels - */ - //normalise channels so it's always an array of array(s) - this._channels = (channels[0] && channels[0].push) ? channels : [channels]; - /* - PrivateProperty: _channelPos - index of each currently playing animation - */ - this._channelPos = []; - /* - PrivateProperty: _playing - Is the timeline playing? - */ - this._playing = false; - - /** - @name glow.anim.Timeline#loop - @type Boolean - @description Inidcates whether the timeline loops. - - The "complete" event does not fire for looping animations. - This can be set while a timeline is playing. - */ - this.loop = opts.loop; - - var i, j, iLen, jLen, - channel, - allChannels = this._channels, - totalDuration = 0, - channelDuration; - - //process channels - for (i = 0, iLen = allChannels.length; i < iLen; i++) { - channel = allChannels[i]; - channelDuration = 0; - for (j = 0, jLen = channel.length; j < jLen; j++) { - //create a blank animation for time waiting - if (typeof channel[j] == "number") { - channel[j] = new r.Animation(channel[j]); - } - if (channel[j] instanceof r.Animation) { - if (! channel[j].useSeconds) { - throw new Error("Timelined animations must be timed in seconds"); - } - channel[j]._timelineOffset = channelDuration * 1000; - channelDuration += channel[j].duration; - channel[j]._channelIndex = i; - } - } - /** - @name glow.anim.Timeline#duration - @type Number - @description Length of the animation in seconds - */ - this.duration = totalDuration = Math.max(channelDuration, totalDuration); - } - /* - PrivateProperty: _controlAnim - This is used to keep the animation in time - */ - this._controlAnim = new r.Animation(totalDuration); - events.addListener(this._controlAnim, "frame", this._processFrame, this); - events.addListener(this._controlAnim, "complete", this._complete, this); - }; - r.Timeline.prototype = { - /* - PrivateMethod: _advanceChannel - Move to the next position in a particular channel - */ - _advanceChannel: function(i) { - //is there a next animation in the channel? - var currentAnim = this._channels[i][this._channelPos[i]], - nextAnim = this._channels[i][++this._channelPos[i]]; - - if (currentAnim && currentAnim._playing) { - currentAnim._playing = false; - events.fire(currentAnim, "complete"); - if (currentAnim._opts.destroyOnComplete) { - currentAnim.destroy(); - } - } - if ((nextAnim) !== undefined) { - if (typeof nextAnim == "function") { - nextAnim(); - this._advanceChannel(i); - } else { - nextAnim.position = 0; - nextAnim._channelIndex = i; - events.fire(nextAnim, "start"); - nextAnim._playing = true; - } - } - }, - _complete: function() { - if (this.loop) { - this.start(); - return; - } - this._playing = false; - events.fire(this, "complete"); - if (this._opts.destroyOnComplete) { - this.destroy(); - } - }, - _processFrame: function() { - var i, len, anim, controlAnim = this._controlAnim, - msFromStart = (new Date().valueOf()) - controlAnim._timeAnchor; - - for (i = 0, len = this._channels.length; i < len; i++) { - if (! (anim = this._channels[i][this._channelPos[i]])) { continue; } - anim.position = (msFromStart - anim._timelineOffset) / 1000; - if (anim.position > anim.duration) { - anim.position = anim.duration; - } - anim.value = anim.tween(anim.position / anim.duration); - events.fire(anim, "frame"); - if (anim.position == anim.duration) { - this._advanceChannel(i); - } - } - }, - - /** - @name glow.anim.Timeline#start - @function - @description Starts playing the timeline from the beginning. - @returns this - */ - start: function() { - var e = events.fire(this, "start"); - if (e.defaultPrevented()) { return this; } - var i, iLen, j, jLen, anim; - this._playing = true; - for (i = 0, iLen = this._channels.length; i < iLen; i++) { - this._channelPos[i] = -1; - this._advanceChannel(i); - for (j = this._channels[i].length; j; j--) { - anim = this._channels[i][j]; - if (anim instanceof r.Animation) { - anim.goTo(0); - } - } - } - this._controlAnim.start(); - return this; - }, - - /** - @name glow.anim.Timeline#stop - @function - @description Stops the timeline. - @returns this - */ - stop: function() { - if (this._playing) { - var e = events.fire(this, "stop"); - if (e.defaultPrevented()) { return this; } - this._playing = false; - var anim; - for (var i = 0, len = this._channels.length; i this.duration) { - // if the position is greater than the total, 'loop' the value - if (this.loop) { - pos = pos % this.duration; - } else { - pos = this.duration; - } - } - - // advance the control anim - this._controlAnim.goTo(pos); - - // loop through the animations in all the channels, find out which - // one to start playing from - for (i = 0; i < channelsLen; i++) { - - runningDuration = 0; - // go through animations in that channel - for (j = 0, channelLen = this._channels[i].length; j < channelLen; j++) { - anim = this._channels[i][j]; - - if (anim instanceof r.Animation) { - // we found an animation we should be playing - if ( (runningDuration + anim.duration) > pos) { - // record its position and leave the loop - this._channelPos[i] = j; - anim.goTo(pos - runningDuration); - break; - } - // we're moving to a position where this animation has - // finished playing, need to fire its final frame - anim.goTo(anim.duration); - // add that anim to the running total - runningDuration += anim.duration; - } - - } - // right, now we need to move all animations after this - // one to the start... - for (k = channelLen; k > j; k--) { - anim.goTo(0); - } - } - } else { - // ok, we've been provided with an object rather than a number of seconds - // Let's convert it to seconds and rerun this function - for (i = 0; i < channelsLen; i++) { - // let's count this to find out what "time" the item the user wants to play is at - runningDuration = 0; - - // let's loop through animations in that channel - for (j = 0, channelLen = this._channels[i].length; j < channelLen; j++) { - anim = this._channels[i][j]; - - if (anim === pos) { - // oh! We've found the thing they want to play - return this.goTo(runningDuration); - } - if (anim instanceof r.Animation) { - // add that anim to the running total - runningDuration += anim.duration; - } - } - } - throw "Animation not found in animation channels"; - } - return this; - } - }; - glow.anim = r; - } -}); -/** - @name glow.forms - @namespace - @see Validating Forms - @see Using default form feedback - @see Working example - @description Validating HTML Forms. - To get started, you'll need to create a {@link glow.forms.Form Form instance}. - */ -(window.gloader || glow).module({ - name: "glow.forms", - library: ["glow", "1.5.1"], - depends: [["glow", "1.5.1", 'glow.dom', 'glow.events', 'glow.anim', 'glow.net']], - builder: function(glow) { - - -glow.forms = {}; - -/** - @name glow.forms.Form - @constructor - @description Create an object to add tests to. - @see Validating Forms - @see Using default form feedback - @param {glow.dom.NodeList | Selector} formNode - @param {Object} [opts] - @param {Function} [opts.onValidate] Handles the 'validate' event when all tests are complete. Default is glow.forms.feedback.defaultFeedback. - @example - myForm = new glow.forms.Form( - glow.dom.get("#htmlFormId"), - { - onValidate: function(results) { - // ... - } - } - ); - */ -glow.forms.Form = function(formNode, opts) { /*debug*///console.log("glow.forms.Form#new("+formNode+", "+opts+")"); - /** - @name glow.forms.Form#formNode - @type glow.dom.NodeList - @description NodeList containing the form element - */ - this.formNode = glow.dom.get(formNode); - if (!this.formNode[0]) throw "Could not find form. Possibly run before DOM ready."; - this._fields = []; - this._result = null; - this.opts = opts || {}; - glow.events.addListener(this, "validate", this.opts.onValidate || feedback.defaultFeedback); - - this._idleTimer = null; - - // add event listener to form - var thisForm = this; - glow.events.addListener( - this.formNode, - "submit", - function(){ - thisForm.validate('submit'); - return false; - } - ); -} - -/** - @name glow.forms.Form#validate - @function - @description Run validation tests. - This is called automatically depending on the tests added to the form. - However, you can trigger validation manually - @param {String} [eventName='submit'] Run only tests tied to this eventname. - @param {String} [fieldName] Run only tests attached to the form element with this name. - */ -glow.forms.Form.prototype.validate = function(eventName, fieldName) { /*debug*///console.log("glow.forms.Form#validate("+eventName+", "+fieldName+")"); - this.eventName = eventName || 'submit'; // default - this._result = new glow.forms.ValidateResult(this.eventName); - this._result.form = this; - - this._fieldCur = 0; - this._testCur = -1; - - this._fieldName = fieldName; - this._nextTest(); -} - -/** - Advance the test cursor to get the next test in the queue and then run it. - @function - @name glow.forms.Form#_nextTest - @calledBy glow.forms.Form#validate - @calledBy glow.forms#_onTestResult - @private - */ -glow.forms.Form.prototype._nextTest = function() { /*debug*///console.log("glow.forms.Form#_nextTest()"); - this._testCur++; - if (this._testCur >= this._fields[this._fieldCur]._tests.length) { // run out of tests for the current field? - if (!this._nextField()) return false; - } - - var currentTest = this._fields[this._fieldCur]._tests[this._testCur]; // shortcut - - // get value from form element, normalize into an array - var fieldValue; - if (currentTest.opts.field) { // a conditional test - fieldValue = this.formNode.val()[currentTest.opts.field] || ""; - currentTest.isConditional = true; - } - else { - fieldValue = this.formNode.val()[this._fields[this._fieldCur].name] || ""; - } - - // values should always be an array - if (!fieldValue.join) fieldValue = [fieldValue]; - - var callback = function(o) { // closure - return function() { o._onTestResult.apply(o, arguments) }; - }(this); - - // only run tests that are tied to the eventName being validated - currentTest.opts.on = currentTest.opts.on || "submit"; - if ( - this._result.eventName - && (" "+currentTest.opts.on+" ").indexOf(" "+this._result.eventName+" ") != -1 // assume space delimited event names - ) { - // skip tests that are not tied to the fieldName being validated - if (this._fieldName && this._fieldName != currentTest.name) { - this._nextTest(); - return; - } - - // run the test, if it exists - if (typeof glow.forms.tests[currentTest.type] != "function") { - throw "Unimplemented test: no test exists of type '"+currentTest.type+"'."; - } - glow.forms.tests[currentTest.type](fieldValue, currentTest.opts, callback, this.formNode.val()); - } - else { - this._nextTest(); - } -} - -/** - Advance the field cursor to get the next field in the queue. - @function - @name glow.forms.Form#_nextField - @calledBy glow.forms.Form#_nextTest - @private - */ -glow.forms.Form.prototype._nextField = function() { /*debug*///console.log("glow.forms.Form#_nextField()"); - // start at the beginning of the next field - this._fieldCur++; - this._testCur = 0; - - if (this._fieldCur >= this._fields.length) { // run out of fields? - this._fieldCur = 0; - // ready to fire the validate event now - glow.events.fire(this, "validate", this._result); - if (this.eventName == "submit" && this._result && !this._result.defaultPrevented()) this.formNode[0].submit(); - return false; - } - - return true; -} - -/** - @name glow.forms.Form#_onTestResult - @function - @calledBy glow.forms.tests.* - @param {Number} result One of: glow.forms.PASS, glow.forms.FAIL - @param {String} message - @private - */ -glow.forms.Form.prototype._onTestResult = function(result, message) { /*debug*///console.log("glow.forms.Form#_onTestResult("+result+", "+message+")"); - // convert result from a boolean to glow.forms.FAIL / glow.forms.PASS - if (typeof result == "boolean") result = (result)? glow.forms.PASS : glow.forms.FAIL; - - // a conditional test has failed? - if (this._fields[this._fieldCur]._tests[this._testCur].isConditional && result === glow.forms.FAIL) { - result = glow.forms.SKIP; // failure of a conditional test becomes a skip - } - - this._result.fields.push( - { - name: this._fields[this._fieldCur].name, - result: result, - message: message - } - ); - - if (result !== glow.forms.PASS) { // might be a fail or a skip - if (result === glow.forms.FAIL) this._result.errorCount++; - - // skip over all further tests for this field - this._testCur = this._fields[this._fieldCur]._tests.length; - } - - this._nextTest(); -} - -/** - @name glow.forms.Form#addTests - @function - @description Add one or more tests to a field. - @param {String} fieldName The name of the field to add tests to. - @param {Array} [spec] - - Test specifications identify the type of test to be run on a field to - determine whether it contains desired data. See docs on the - {@link glow.forms.tests types of tests}. - - @example - //pattern for a test specification - [ - "testName", //name of the test to run - { - arg : 5, //an argument for the test, not all tests need this - on : "submit change", //when should this test be run? - message : "Incorrect value" //a custom error message to display - } - ] - - @example - //setting a form up for validation - var myForm = new glow.forms.Form(glow.dom.get("#myFormId")) - .addTests( - "username", - ["required"], - ["maxLen", { - arg: 12, - message: "Name must be les than 12 characters long." - }] - ) - .addTests( - "email", - ["isEmail"] - ); - */ -glow.forms.Form.prototype.addTests = function(fieldName /*...*/) { /*debug*///console.log("glow.forms.Form#addTests("+fieldName+", ...)"); - var field = {name: fieldName, _tests:[]}; - - var changeCallback = function(that) { - return function() { - that.validate.apply(that, ["change", fieldName]) - }; - }(this); - - var clickCallback = function(that) { - return function() { - that.validate.apply(that, ["click", fieldName]) - }; - }(this); - - var idleCallback = function(that) { - return function() { - that.validate.apply(that, ["idle", fieldName]); - }; - }(this); - - // loop over test specifications - for (var i = 1; i < arguments.length; i++) { - var testType = arguments[i][0]; - var testOpts = (arguments[i].length > 1)? arguments[i][1] : {}; // default opts - - field._tests.push({name: fieldName, type: testType, opts: testOpts}); - - // add event listeners to form fields for change events - if (!changeCallback.added && (" "+testOpts.on+" ").indexOf(" change ") != -1) { - var inputs = this.formNode.get("*").each(function (i) { - if (this.name == fieldName) { - glow.events.addListener(this, "change", changeCallback); - changeCallback.added = true; - } - }); - } - - // add event listeners to form fields for click events - if (!clickCallback.added && (" "+testOpts.on+" ").indexOf(" click ") != -1) { - var inputs = this.formNode.get("*").each(function (i) { - if (this.name == fieldName) { - glow.events.addListener(this, "click", clickCallback); - clickCallback.added = true; - } - }); - } - - if (!idleCallback.added && (" "+testOpts.on+" ").indexOf(" idle ") != -1) { - var idleDelay = (typeof testOpts.delay != "undefined")? parseInt(testOpts.delay) : 1000; // default delay before idle handler is run - - var inputs = this.formNode.get("*").each(function (i) { - if (this.name == fieldName) { - // FIXME: adding idleTimeoutID to HTML element, is this the best way? - glow.events.addListener(this, "keyup", function(t){ return function() {window.clearTimeout(this.idleTimeoutID); if (this.value) this.idleTimeoutID = window.setTimeout(idleCallback, t)} }(idleDelay)); - glow.events.addListener(this, "blur", function() {window.clearTimeout(this.idleTimeoutID)}); - - idleCallback.added = true; - } - }); - } - } - - this._fields.push(field); - - return this; // chained -} - -/** - @name glow.forms.ValidateResult - @constructor - @description This is created automatically by the running validation and passed to - the onValidate handler once all eligible tests have finished running. - @param {String} eventName - @property {String} eventName The name on the event that was associated with this validation. - @property {Number} errorCount The number of fields that had a failing test. - @property {Object[]} fields Each field object has a name, a value {-1, 0, 1}, and a message. - */ -glow.forms.ValidateResult = function(eventName) { - glow.events.Event.apply(this); - - this.eventName = eventName; - this.errorCount = 0; - this.value = undefined; - this.fields = []; -} - -glow.lang.extend(glow.forms.ValidateResult, glow.events.Event); - -/** - @name glow.forms.PASS - @type Number - @description Constant for a passed test. - You can use this when creating {@link glow.forms.tests.custom custom tests} - */ -glow.forms.PASS = 1; -/** - @name glow.forms.FAIL - @type Number - @description Constant for a failing test. - You can use this when creating {@link glow.forms.tests.custom custom tests} - */ -glow.forms.FAIL = 0; -/** - @name glow.forms.SKIP - @type Number - @description Constant for a skipped test. - You can use this when creating {@link glow.forms.tests.custom custom tests}. - */ -glow.forms.SKIP = -1; - -/** - @name glow.forms.tests - @namespace - @see Validating Forms - @see Working example - @description Collection of built-in tests that can be added to validate a form field. - -

You do not generally need to call these functions directly, their names are passed to the - Form {@link glow.forms.Form#addTests addTests} method.

- -

If you want to create your own custom functions, or to see what the parameters these - function take, you should refer to the Creating - Custom Tests section of the Validating Forms user guide.

- */ -glow.forms.tests = { - /** - @name glow.forms.tests.required - @function - @description The value must contain at least one non-whitespace character. - @example - myForm.addTests( - "fieldName", - ["required"] - ); - */ - required: function(values, opts, callback) { /*debug*///console.log("glow.forms.tests.required()"); - var message = opts.message || "Value is required"; - - for (var i = 0, len = values.length; i < len; i++) { - if (/^\s*$/.test(values[i])) { - callback(glow.forms.FAIL, message); - return; - } - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.isNumber - @function - @description The value must be a valid number. - @example - myForm.addTests( - "fieldName", - ["isNumber"] - ); - */ - isNumber: function(values, opts, callback) { /*debug*///console.log("glow.forms.tests.isNumber()"); - var message = opts.message || "Must be a number."; - - for (var i = 0, len = values.length; i < len; i++) { - if (values[i] == "" || isNaN(values[i])) { - callback(glow.forms.FAIL, message); - return; - } - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.min - @function - @description The numeric value must be at least the given value. - @example - myForm.addTests( - "fieldName", - ["min", { - arg: "1" - }] - ); - */ - min: function(values, opts, callback) { /*debug*///console.log("glow.forms.tests.min()"); - var message = opts.message || "The value must be at least "+opts.arg+"."; - for (var i = 0, len = values.length; i < len; i++) { - if (Number(values[i]) < Number(opts.arg)) { - callback(glow.forms.FAIL, message); - return; - } - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.max - @function - @description The numeric value must be no more than the given value. - @example - myForm.addTests( - "fieldName", - ["max", { - arg: "100" - }] - ); - */ - max: function(values, opts, callback) { /*debug*///console.log("glow.forms.tests.max()"); - var message = opts.message || "The value must be less than "+opts.arg+"."; - for (var i = 0, len = values.length; i < len; i++) { - if (Number(values[i]) > Number(opts.arg)) { - callback(glow.forms.FAIL, message); - return; - } - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.range - @function - @description The numeric value must be between x..y. - @example - myForm.addTests( - "fieldName", - ["range", { - arg: "18..118" - }] - ); - */ - range: function(values, opts, callback) { /*debug*///console.log("glow.forms.tests.range()"); - var minmax = opts.arg.split(".."); // like "0..10" - if (typeof minmax[0] == "undefined" || typeof minmax[1] == "undefined") { - throw "Range test requires a parameter like 0..10." - } - var message = opts.message || "The value must be "+minmax[0]+" or greater, and less than "+minmax[1]+"."; - - // cast to numbers to avoid stringy comparisons - minmax[0] *= 1; - minmax[1] *= 1; - - // reverse if the order is hi..lo - if (minmax[0] > minmax[1]) { - var temp = minmax[0]; - minmax[0] = minmax[1]; - minmax[1] = temp; - } - - for (var i = 0, len = values.length; i < len; i++) { - if (values[i] < minmax[0] || values[i] > minmax[1]) { - callback(glow.forms.FAIL, message); - return; - } - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.minCount - @function - @description There must be at least the given number of values submitted with this name. - This is useful for multiple selects and checkboxes that have the same name. - @example - myForm.addTests( - "fieldName", - ["minCount", { - arg: "1" - }] - ); - */ - minCount: function(values, opts, callback) { /*debug*///console.log("glow.forms.tests.minCount()"); - var message = opts.message || "Must be have at least "+opts.arg+" values."; - - var count = 0; - - for (var i = 0; i < values.length; i++ ) { - if (values[i] != "") count++; - } - - if (count < opts.arg) { - callback(glow.forms.FAIL, message); - return; - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.maxCount - @function - @description There must be no more than the given number of values submitted with this name. - This is useful for multiple selects and checkboxes that have the same name. - @example - myForm.addTests( - "fieldName", - ["maxCount", { - arg: "10" - }] - ); - */ - maxCount: function(values, opts, callback) { /*debug*///console.log("glow.forms.tests.maxCount()"); - var message = opts.message || "Must be have at most "+opts.arg+" values."; - - var count = 0; - - for (var i = 0; i < values.length; i++ ) { - if (values[i] != "") count++; - } - - if (count > opts.arg) { - callback(glow.forms.FAIL, message); - return; - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.count - @function - @description There must be exactly the given number of values submitted with this name. - This is useful for multiple selects and checkboxes that have the same name. - @example - myForm.addTests( - "fieldName", - ["count", { - arg: "2" - }] - ); - */ - count: function(values, opts, callback) { /*debug*///console.log("glow.forms.tests.count()"); - var message = opts.message || "Must have "+opts.arg+" values."; - - var count = 0; - - for (var i = 0; i < values.length; i++ ) { - if (values[i] != "") count++; - } - - if (count != opts.arg) { - callback(glow.forms.FAIL, message); - return; - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.regex - @function - @description The value must match the given regular expression. - @example - myForm.addTests( - "fieldName", - ["regex", { - arg: /^[A-Z0-9]*$/ - }] - ); - */ - regex: function(values, opts, callback) { /*debug*///console.log("glow.forms.tests.regex()"); - var message = opts.message || "Must be in the correct format."; - - var regex = (typeof opts.arg == "string")? new RegExp(opts.arg) : opts.arg; // if its not a string assume its a regex literal - for (var i = 0, len = values.length; i < len; i++) { - if (!regex.test(values[i])) { - callback(glow.forms.FAIL, message); - return; - } - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.minLen - @function - @description The value must be at least the given number of characters long. - @example - myForm.addTests( - "fieldName", - ["minLen", { - arg: "3" - }] - ); - */ - minLen: function(values, opts, callback) { /*debug*///console.log("glow.forms.tests.minLen()"); - var message = opts.message || "Must be at least "+opts.arg+" characters."; - - for (var i = 0, len = values.length; i < len; i++) { - if (values[i].length < opts.arg) { - callback(glow.forms.FAIL, message); - return; - } - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.maxLen - @function - @description The value must be at most the given number of characters long. - @example - myForm.addTests( - "fieldName", - ["maxLen", { - arg: "24" - }] - ); - */ - maxLen: function(values, opts, callback) { /*debug*///console.log("glow.forms.tests.maxLen()"); - var message = opts.message || "Must be at most "+opts.arg+" characters."; - - for (var i = 0, len = values.length; i < len; i++) { - if (values[i].length > opts.arg) { - callback(glow.forms.FAIL, message); - return; - } - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.isEmail - @function - @description The value must be a valid email address. - This checks the formatting of the address, not whether the address - exists. - @example - myForm.addTests( - "fieldName", - ["isEmail"] - ); - */ - isEmail: function(values, opts, callback) { /*debug*///console.log("glow.forms.tests.isEmail()"); - var message = opts.message || "Must be a valid email address."; - - for (var i = 0, len = values.length; i < len; i++) { - if (!/^[A-Za-z0-9](([_\.\-]*[a-zA-Z0-9]+)*)@([A-Za-z0-9]+)(([\.\-]?[a-zA-Z0-9]+)*)\.([A-Za-z]{2,})$/.test(values[i])) { - callback(glow.forms.FAIL, message); - return; - } - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.sameAs - @function - @description The value must be the same as the value in the given field. - @example - myForm.addTests( - "email_confirm", - ["sameAs", { - arg: "email" - }] - ); - */ - sameAs: function(values, opts, callback, formValues) { /*debug*///console.log("glow.forms.tests.sameAs()"); - var message = opts.message || "Must be the same as: "+opts.arg; - var compareTo = formValues[opts.arg]; - - for (var i = 0, len = values.length; i < len; i++) { - if (values[i] != compareTo) { - callback(glow.forms.FAIL, message); - return; - } - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.ajax - @function - @description Send the data to the server for testing. - A request to the given URL will be made and the response will be passed to the given callback. - - 'arg' is the function to handle the response from the server. - - 'url' is the url to call. You can use placeholders in here for form values (see example). - @example - function handleResponseText(response) { - if (response.text() == "OK") { - return glow.forms.PASS; - } else { - return glow.forms.FAIL; - } - } - - myForm.addTests( - "username", - ["ajax", { - arg: handleResponseText, - url: "/cgi/checkname.cgi?name={username}" - }] - ); - */ - ajax: function(values, opts, callback, formValues) { /*debug*///console.log("glow.forms.tests.ajax() - "+opts.url); - var queryValues = {}; - for (var p in formValues) { - if (typeof formValues[p] == "string") { - queryValues[p] = escape(formValues[p]); - } - else if (typeof formValues[p].push != "undefined") { - queryValues[p] = glow.lang.map(formValues[p], function(i) { return escape(i); }).join(","); - } - } - var url = glow.lang.interpolate(opts.url, queryValues); - - var request = glow.net.get(url, { - onLoad: function(response) { /*debug*///console.log("glow.forms.tests.ajax - onLoad()"); - callback(opts.arg(response), "server responded"); - }, - onError: function(response) { - alert("Error getting file: "+url); - } - }); - } - , - /** - @name glow.forms.tests.custom - @function - @description Create a custom test. - - 'arg' is a function which tests the form value. - - The function is given the following parameters: - -
-
values
-
- An array of values submitted for that form field. If you - are only expecting one value, it can be accessed via values[0] -
-
opts
-
- An object of any additional data included with the test -
-
callback
-
- This is a function used to tell Glow whether the test has - passed or not. A callback is used rather than 'return' to - allow async tests. The first parameter is either glow.forms.PASS - or glow.forms.FAIL, the second is the success or failure message. -
-
formData
-
- This is an object of all values captured in the form. -
-
- - @example - myForm.addTests( - "username", - ["custom", { - arg: function(values, opts, callback, formData) { - for (var i = 0, len = values.length; i < len; i++) { - if (values[i] == "Jake") { - callback(glow.forms.FAIL, "The name Jake is not allowed."); - return; - } - } - callback(glow.forms.PASS, "Good name."); - } - }] - ); - */ - custom: function(values, opts, callback) { /*debug*///console.log("glow.forms.tests.custom()"); - opts.arg.apply(this, arguments); - } - , - /** - @name glow.forms.tests.is - @function - @description The value must be equal to a particular value - @example - // this test ensures "other" is required *if* the "reason" field is equal to "otherReason" - myForm.addTests( - "other", - ["is", { - field: "reason", - arg: "otherReason" - }], - ["required"] - ); - */ - "is": function(values, opts, callback) { - var message = opts.message || "Must be " + opts.arg; - - for (var i = 0, len = values.length; i < len; i++) { - if (values[i] != opts.arg) { - callback(glow.forms.FAIL, message); - return; - } - } - callback(glow.forms.PASS, message); - } - , - /** - @name glow.forms.tests.isNot - @function - @description The value must not be equal to a particular value - @example - // you may have a dropdown select where the first option is "none" for serverside reasons - myForm.addTests( - "gender", - ["isNot", { - arg: "none" - }] - ); - */ - "isNot": function(values, opts, callback) { - var message = opts.message || "Must not be " + opts.arg; - - for (var i = 0, len = values.length; i < len; i++) { - if (values[i] == opts.arg) { - callback(glow.forms.FAIL, message); - return; - } - } - callback(glow.forms.PASS, message); - } -} - - -/** -@name glow.forms.feedback -@namespace -@description Collection of functions for displaying validation results to the user - -

These functions should be used as handlers for {@link glow.forms.Form}'s validate event. At the - moment there is only one provided handler, more may be added in the future.

- -

Of course, you don't have to use any of the methods here, you can provide your own.

- -@see Using the default form feedback -*/ - -var feedback = glow.forms.feedback = {}; - -/** -@name glow.forms.feedback.defaultFeedback -@function -@description Default handler used by {@link glow.forms.Form}. - -

This method outputs messages to the user informing them which fields - contain invalid data. The output is unstyled and flexible.

- -@param {glow.forms.ValidateResult} result Object provided by the validate event - -@see Using the default form feedback -*/ -feedback.defaultFeedback = (function() { - - //a hidden form element used to update a screenreader's buffer - var screenReaderBufferUpdater; - - //attempts to update the buffer of the screen reader - function updateScreenReaderBuffer() { - if (!screenReaderBufferUpdater) { - screenReaderBufferUpdater = glow.dom.create('').appendTo(document.body); - } - screenReaderBufferUpdater[0].value++; - } - - //write out the messages which appear next to (or near) the fields - function inlineErrors(response) { - var fields = response.fields, //field test results - fieldElm, //holder for the field element(s) being processed - msgContainer, //holder for contextual message holder - labelError, //error holder within label element - i, len; - - for (i = 0, len = fields.length; i < len; i++) { - fieldElm = glow.dom.get(response.form.formNode[0].elements[fields[i].name]); - //here's where we get the error container, which is the label by default - //also we need to escape invalid css chars CSS - msgContainer = glow.dom.get("." + fields[i].name.replace(/(\W)/g, "\\$1") + "-msgContainer"); - if (!msgContainer[0] && fieldElm.length == 1) { - //none found, try and get the label - msgContainer = response.form.formNode.get("label").filter(function() { return this.htmlFor == fieldElm[0].id }) - } - - labelError = msgContainer.get("span.glow-errorMsg"); - - if (fields[i].result) { - //clear error messages & classes - labelError.remove(); - fieldElm.removeClass("glow-invalid"); - } else { - if (msgContainer.length) { - //add the error span to the label if it isn't already there - if (!labelError[0]) { - msgContainer.append( (labelError = glow.dom.create('')) ); - } - labelError.text(fields[i].message); - fieldElm.addClass("glow-invalid"); - } - } - } - } - - //write out a list of errors to appear at the top of the form - function summaryError(response) { - var fields = response.fields, //field test results - fieldElm, //holder for the field element(s) being processed - errorSummary, //div containing error summary - errorList, //list of errors inside the error summary - promptContainer, //holds the 'question' of the field - prompt, //text to prefix each error line with - i, - len; - - //remove existing summary - response.form.formNode.get("div.glow-errorSummary").remove(); - //create a summary div - errorSummary = glow.dom.create('
    '); - errorList = errorSummary.get("ul"); - for (i = 0, len = fields.length; i < len; i++) { - fieldElm = glow.dom.get(response.form.formNode[0].elements[fields[i].name]); - promptContainer = glow.dom.get("." + fields[i].name.replace(/(\W)/g, "\\$1") + "-prompt"); - if (!promptContainer[0] && fieldElm.length == 1) { - //we don't have a default, get the label - promptContainer = response.form.formNode.get("label").filter(function() { return this.htmlFor == fieldElm[0].id }) - } - //did we get a prompt container? - if (promptContainer[0]) { - //get rid of superflous content in the prompt container (such as errors) - promptContainer.get("span.glow-errorMsg").remove(); - prompt = glow.lang.trim(promptContainer.text()); - if (prompt.slice(-1) == ":") { - prompt = prompt.slice(0, -1); - } - } else { - //else we just use the field name - prompt = fields[i].name.replace(/^\w/, function(s) { return s.toUpperCase() } ); - } - - if (!fields[i].result) { - errorList.append( glow.dom.create("
  • ").text(prompt + ": " + fields[i].message) ); - } - } - response.form.formNode.prepend(errorSummary.css("opacity", "0")); - glow.anim.css(errorSummary, "0.5", { - opacity: {from: 0, to: 1} - }, {tween: glow.tweens.easeOut()}).start(); - - // if the error summary has been hidden, IE7 throws an exception here - try { - errorSummary[0].focus(); - } catch (e) {} - - updateScreenReaderBuffer(); - } - - return function(response) { - if (response.eventName == "submit") { - //do we have any errors? - if (!response.errorCount) { - //remove existing summary - response.form.formNode.get("div.glow-errorSummary").remove(); - return; - } - summaryError(response); - } - // display inline errors - // we put this inside setTimeout to avoid an IE bug that can result - // in the cursor appearing outside the form element - setTimeout(function() { - inlineErrors(response); - }, 0); - - return false; - } -}()); - - } -}); -/** -@name glow.embed -@namespace -@description Detect and embed Flash objects -@see Flash embedding -*/ -(window.gloader || glow).module({ - name: "glow.embed", - library: ["glow", "1.5.1"], - depends: [["glow", "1.5.1", "glow.dom", "glow.data"]], - builder: function(glow) { - - - /** - @name to_attributes - @private - @function - @param object - @returns attribute string suitable for inclusion within Flash embed/object tag - @description converts a hash to a space delimited string of attribute assignments - - Simple values are assumed for the object properties, with the exception of - 'flashVars' which is treated as a special case. If 'flashVars' is itself an object, - it will be serialised in querystring format. - returns string representation of object as attribute assignments eg: - {id:"myId",name:"my-name"} becomes 'id="myId" name="my-name"' - - */ - function to_attributes(object){ - - var attributes = ""; - - for (var $param in object){ - if ($param.toLowerCase() == "flashvars" && typeof object[$param] == "object"){ - attributes += ' FlashVars="' + glow.data.encodeUrl(object[$param]) + '"'; - } - else { - attributes += ' ' + $param + '="' + object[$param] + '"'; - } - } - return attributes; - } - - /** - @name toParams - @private - @function - @param object - @returns string of param tags or an object element - @description converts a hash to a string of param tags - - Simple values are assumed for the object properties, with the exception of - 'flashVars' which is treated as a special case. If 'flashVars' is itself an object, - it will be serialised in querystring format. - */ - function toParams(object) { - var r = "", - key, - value; - - for (key in object) { - if (key.toLowerCase() == "flashvars" && typeof object[key] == "object"){ - value = glow.data.encodeUrl(object[key]); - } else { - value = object[key]; - } - - r += '\n'; - } - return r; - } - - /** - @name _set_defaults - @private - @function - @param target - @param options - @returns - @description applies a hash of default property values to a target object - - Properties on the defaults object are copied to the target object if no such property is present. - - */ - function _set_defaults(target,defaults){ - target = target || {}; - - for (var param in defaults) { - if (typeof target[param] == "undefined") { - // is it safe to assign an object reference or should it be cloned ? - target[param] = defaults[param]; - } - else if (typeof defaults[param] == "object") { - target[param] = _set_defaults(target[param],defaults[param]); - } - } - return target; - } - - /** - @name _platform - @private - @returns {string} 'win' or 'mac' or 'other' - @description identify the operating system - */ - function _platform(){ - var platform = (navigator.platform || navigator.userAgent); - return platform.match(/win/i) ? "win" : platform.match(/mac/i) ? "mac" : "other"; - } - - /** - @name _askFlashPlayerForVersion - @private - @function - @param {Shockwave.Flash} flash_player a reference to an ActiveX Shockwave Flash player object - @returns version object with structure: {major:n,minor:n,release:n,actual:"string"} - @description returns flash_player version, as reported by the GetVariable("$version") method - */ - function _askFlashPlayerForVersion(flash_player){ - - var $regexFLASH_VERSION = /^WIN (\d+),(\d+),(\d+),\d+$/; - var $version = flash_player.GetVariable("$version"); - if ($match = $regexFLASH_VERSION.exec($version)){ - return { - major : parseInt($match[1]), - minor : parseInt($match[2]), - release : parseInt($match[3]), - actual : $version - }; - } - else { - // throw an exception, something very strange going on if flash player returns version in any other format ? - - } - } - - /** - @name _getFlashPlayerVersion - @private - @function - @returns version object with structure: {major:n,minor:n,release:n,actual:"string"} - @description returns flash_player version - - Query installed Flash player version, using either ActiveX object creation (for Internet Explorer) or - navigator plugins collection. - - */ - function _getFlashPlayerVersion(){ - var $match, flash_player, NO_FLASH = {major : 0, minor : 0, release : 0}, result = NO_FLASH; - - if (glow.env.ie){ - try { - flash_player = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"); - result = _askFlashPlayerForVersion(flash_player); - } - catch(e){ - // Version 6 needs kid-glove treatment as releases 21 thru 29 crash if GetVariable("$version") is called - try { - flash_player = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"); - try { - // This works from release 47 onward...(first public release after 29) - flash_player.AllowScriptAccess = "always"; - result = _askFlashPlayerForVersion(flash_player); - } - catch(e){ - // we cannot safely test between releases 21...29, so assume the latest and hope for the best... - result = {major:6,minor:0,release:29}; - } - } - catch (e){ - // nothing more we can do, either no flash installed, flash player version is ancient or ActiveX is disabled - } - } - } - else { - var regexFLASH_VERSION = /^Shockwave Flash\s*(\d+)\.(\d+)\s*\w(\d+)$/; - - if ((flash_player = navigator.plugins["Shockwave Flash"]) && ($match = regexFLASH_VERSION.exec(flash_player.description))){ - result = { - major : parseInt($match[1]), - minor : parseInt($match[2]), - release : parseInt($match[3]), - actual : flash_player.description - }; - } - } - - result.toString = function(){return this.major ? [this.major,this.minor,this.release].join(".") : "No Flash player installed, or version is pre 6.0.0. Visit BBC Webwise for full instructions"}; - - return result; - - } - - /** - @name installed_flash_player - @private - @description version of installed Flash player, initialised at startup. - */ - var installed_flash_player = _getFlashPlayerVersion(); - - - /** - @name _meetsVersionRequirements - @private - @function - @param {string|object} requiredVersion string or version object - - major version must be specified. minor version is optional. release is optional, but both major and - minor versions must also be supplied if release is specified. - eg, as string: "9.0.0", "9.1", "9" - or, as object: {major:9,minor:0,release:0}, {major:9,minor:0}, {major:9} - - @returns {boolean}. - @description returns true if installed Flash player version meets requested version requirements - - */ - function _meetsVersionRequirements(requiredVersion){ - - if (typeof requiredVersion != "object"){ - var match = String(requiredVersion).match(/^(\d+)(?:\.(\d+)(?:\.(\d+))?)?$/); - if (!match){ - throw new Error('glow.embed._meetsVersionRequirements: invalid format for version string, require "n.n.n" or "n.n" or simply "n" where n is a numeric value'); - } - - requiredVersion = { - major : parseInt(match[1],10), - minor : parseInt(match[2]||0,10), - release : parseInt(match[3]||0,10) - } - } - - var v = installed_flash_player, rv = requiredVersion; - - // return true if we meet the minimum version requirement... - return (v.major > rv.major || - (v.major == rv.major && v.minor > rv.minor) || - (v.major == rv.major && v.minor == rv.minor && v.release >= rv.release)); - - } - - /** - @name _wrap_embedding_tag - @private - @function - @param {string} src - @param {object} attributes Hash of attributes to be included - @param {string} params Hash of params to be included. These will be included as attributes for all but IE - @returns {string} object or embed tag - @description returns a complete object or embed tag, as appropriate (ie browser dependent) - */ - var _wrap_embedding_tag = glow.env.ie ? _wrap_object_tag : _wrap_embed_tag; - function _wrap_embed_tag(src, attributes, params){ - return ''; - } - function _wrap_object_tag(src, attributes, params){ - return '' + toParams(params) + ''; - } - - var r = {}; - - /** - @name glow.embed.Flash - @class - @description A wrapper for a Flash movie so it can be embedded into a page - @see Flash embedding - - @param {String} src Absolute or relative URL of a Flash movie file to embed - @param {selector | glow.dom.NodeList} container The element to embed the movie into - -

    If a CSS selector is provided then the first matching element is used as the - container.

    - -

    If the parameter is a {@link glow.dom.NodeList}, then the first - element of the list is used as the container.

    - - @param {String|Object} minVersion The minimum required version of the Flash plugin. - -

    The Flash plugin has a version numbering scheme comprising of major, minor and - release numbers.

    - -

    This param can be a string with the major number only, major plus minor numbers, or - full three-part version number, e.g. "9", "9.1" , "6.0.55" are all valid values.

    - -

    If minVersion is set as an object, it must use a similar structure to the object - returned by the {@link glow.embed.Flash#version} method, e.g: {major: 9, minor:0, - release:0}.

    - - @param {Object} [opts] - - Hash of optional parameters. - - @param {String} [opts.width] Width of the Flash movie. Defaults to "100%" - @param {String} [opts.height] Height of the Flash movie. Defaults to "100%" - @param {String} [opts.id] Unique id to be assigned to Flash movie instance. - @param {String} [opts.className] CSS class to be assigned to the embedding element. - @param {Object} [opts.attributes] A hash of attributes to assign to the embedded element tag. - @param {Object} [opts.params] A hash of optional Flash-specific parameters. - For example quality, wmode, bgcolor, flashvars. - @param {String|Function} [opts.message] Error handling message or function. - - A message to display in the event that the Flash player is either not - installed or is an earlier version than the specified minVersion. This message will - be written into the container element instead of the Flash movie. - - If a function is supplied, it will be invoked and any return value will be used - as the message to write into the container. - - @example - var myFlash = new glow.embed.Flash("/path/to/flash.swf", "#flashContainer", "9"); - - @example - var myFlash = new glow.embed.Flash("/path/to/flash.swf", "#flashContainer", "9", { - width: "400px", - height: "300px" - }); - - @example - var myFlash = new glow.embed.Flash("/path/to/flash.swf", "#flashContainer", "9", { - width: "400px", - height: "300px", - params: { - wmode: "transparent", - flashvars: { - navColour: "red", - username: "Frankie" - } - } - }); - */ - r.Flash = function(src, container, minVersion, opts){ - - opts = _set_defaults(opts,{ - width : "100%", - height : "100%", - params : { - allowscriptaccess : "always", - allowfullscreen : "true", - quality : "high" - }, - attributes : {}, - //expressInstall : false, // TODO add this in a later release - message : "This content requires Flash Player version " + minVersion + " (installed version: " + installed_flash_player + ")" - } - ); - - /** - @name glow.embed.Flash#container - @type glow.dom.NodeList - @description The element containing the embedded movie. - */ - container = glow.dom.get(container); - if (!container.length){ - throw new Error("glow.embed.Flash unable to locate container"); - } - this.container = container; - - //this.expressInstall = opts.expressInstall; // TODO add this in a later release - - /** - @name glow.embed.Flash#movie - @type Shockwave.Flash - @description A reference to the actual Flash movie, for direct script access. - @example - myFlash.movie.exposedFlashMethod(); - */ - this.movie = null; - - this._displayErrorMessage = typeof opts.message == "function" ? opts.message : function(){return opts.message}; - - this.isSupported; - - // Check that the min version requirement is satisfied and store this status, so we don't later try to embed the thing - // if we don't can't meet the version requirements. - - if (this.isSupported = _meetsVersionRequirements(minVersion)){ - var attrs = opts.attributes, - overwrites = ["id", "width", "height"], - i = overwrites.length; - - // copies stuff like opts.id to attr.id - while (i--) { - if (opts[overwrites[i]]) { attrs[overwrites[i]] = opts[overwrites[i]]; } - } - - if (opts.className) { attrs["class"] = opts.className; } - this._embed_tag = _wrap_embedding_tag(src, attrs, opts.params); - } - /* - else if (this.expressInstall && _meetsVersionRequirements("6.0.65") && _platform().match(/^win|mac$/)) { - - // Callback to be invokes in case of express install error - window[glow.UID + "flashExpressInstallCancelled"] = function(){ - alert("Flash update cancelled"); - } - window[glow.UID + "flashExpressInstallFailed"] = function(){ - alert("Unable to complete update, please go to adobe.com..."); - } - window[glow.UID + "flashExpressInstallComplete"] = function(){ - - alert("New version of flash installed"); - - } - - new glow.embed.Flash("expressInstall.swf", - this.container, - "6.0.65", { - //TODO check minimum width/height - width:opts.width, - height:opts.height, - params : { - flashVars : { - MMredirectURL : window.location.toString(), - MMplayerType : glow.env.ie ? "ActiveX" : "PlugIn", - MMdoctitle : document.title, - GlowCallback : glow.UID + "flashExpressInstall" - } - } - } - ).embed(); - - this.expressInstalling = true; - }*/ - }; - - /** - @name glow.embed.Flash.version - @function - @description Get details of the current users Flash plugin - @returns An object with details of the currently installed Flash plugin. - -
    -
    major (number)
    Flash player major version mumber
    -
    minor (number)
    Flash player minor version mumber.
    -
    release (number)
    Flash player release version mumber.
    -
    actual (string)
    The Flash version exactly as reported by the Flash player.
    -
    toString (function)
    toString implementation in the form "major.minor.release" Eg "9.0.2"
    -
    - - @example - var version = glow.embed.Flash.version(); - alert("curr = " + version.major) // "curr = 9" - alert("curr = " + version) // "curr = 9.0.2" - */ - r.Flash.version = function(){ - return installed_flash_player; - }; - - /** - @name glow.embed.Flash#embed - @function - @description Embed the Flash movie into the document - @returns {glow.embed.Flash} - - @example - var myFlash = new glow.embed.Flash(...); - myFlash.embed(); - */ - r.Flash.prototype.embed = function(){ - var containerElm = this.container[0]; - if (this.isSupported){ - - containerElm.innerHTML = this._embed_tag; - - this.movie = containerElm.firstChild; - - }/* - else if (this.expressInstalling){ - // wait for expressInstall to complete - - }*/ - else { - var message = this._displayErrorMessage(); - if (message){ - containerElm.innerHTML = message; - } - } - - return this; - - }; - - glow.embed = r; - - } -}); -/** -@name glow.dragdrop -@namespace -@description Simplifying drag and drop behaviour -*/ -(window.gloader || glow).module({ - name: "glow.dragdrop", - library: ["glow", "1.5.1"], - depends: [["glow", "1.5.1", "glow.tweens", "glow.events", "glow.dom", "glow.anim"]], - builder: function(glow) { - var events = glow.events, - addListener = events.addListener, - fire = events.fire, - removeListener = events.removeListener, - dom = glow.dom, - $ = dom.get, - create = dom.create; - - //public - var r = {}, - _zIndex = 1000, - _ieStrict = (document.compatMode == "CSS1Compat" && glow.env.ie >= 5) ? true : false, - _ieTrans= (document.compatMode != "CSS1Compat" && glow.env.ie >= 5) ? true : false, - _ie = glow.env.ie >= 5; - - /* - PrivateFunction: memoize(clss, name) - - Replace a method with a version that caches the result after the first run. - - Arguments: - - *clss* (function) - - The class whose method is being memoized. - - *name* - - The name of the method to memoize. - */ - - function memoize (clss, name) { - var orig = clss.prototype[name]; - var cachedName = 'cached_' + name; - clss.prototype[name] = function () { - if (cachedName in this) return this[cachedName]; - return this[cachedName] = orig.apply(this, arguments); - }; - } - - /* - PrivateFunction: memoizeNamed(clss, methodName) - - Replace a method that takes a name with a version that caches the result for each name after the first run. - - Arguments: - - *clss* (function) - - The class whose method is being memoized. - - *methodName* - - The name of the method to memoize. - */ - - function memoizeNamed (clss, methodName) { - var orig = clss.prototype[methodName]; - var cachedName = 'cached_' + methodName; - clss.prototype[methodName] = function (name) { - if (! this[cachedName]) this[cachedName] = {}; - if (name in this[cachedName]) return this[cachedName][name]; - return this[cachedName][name] = orig.apply(this, arguments); - }; - } - - /* - PrivateFunction: reset(obj, names) - - Remove cached values for a set of memoized methods. - - Arguments: - - *obj* (object) - - The object containing cached values. - - *names* (array of strings) - - The names of methods whose values have been cached. - */ - - function reset (obj, names) { - for (var i = 0, l = names.length; i < l; i++) { - delete obj['cached_' + names[i]]; - } - } - - /* - PrivateFunction: resetNamed(obj, meth, names) - - Remove cached values for a set of named properties for a method. - - Arguments: - - *obj* (object) - - The object containing cached values. - - *meth* (string) - - The name of the method whose values have been cached. - - *names* (array of strings) - - The names of the cached properties. - - function resetNamed (obj, meth, names) { - var cache = obj['cached_' + meth]; - if (! cache) return; - for (var i = 0, l = names.length; i < l; i++) { - delete cache[names[i]]; - } - } - */ - - /* - PrivateClass: Box - - Calculates and caches information about an element in the box model. - - Constructor: - - (code) - new Box(el) - (end) - - Arguments: - - *el* (glow.dom.NodeList) - - The element that calculations will be performed on. - */ - - var Box = function (el) { - this.el = el; - }; - - Box.prototype = { - - /* - PrivateMethod: val - - Get an pixel value for a CSS style. - - Arguments: - - *style* (string) - - The name of a CSS style (e.g. "margin-top". - - Returns: - An integer number of pixels. - */ - - val: function (style) { - var val = parseInt(this.el.css(style)); - // TODO - fix dom so margin-left return value is always defined? -// if (isNaN(val)) throw 'got NaN in val for ' + style + ': ' + this.el.css(style); - return val || 0; -// return val; - }, - - /* - PrivateMethod: width - - Get the width of the element. - - Returns: - An integer number of pixels. - */ - - width: function () { - return this.borderWidth() - - this.val('border-left-width') - - this.val('border-right-width'); - }, - - /* - PrivateMethod: height - - Get the height of the element. - - Returns: - An integer number of pixels. - */ - - height: function () { - return this.borderHeight() - - this.val('border-top-width') - - this.val('border-bottom-width'); - }, - - /* - PrivateMethod: offsetParentPageTop - - Get the number of pixels from the top of nearest element with absolute, relative or fixed position to the - top of the page. - - Returns: - An integer number of pixels. - */ - offsetParentPageTop: function () { - var el = this.el[0], pos, top; - while (el = el.offsetParent) { - pos = $(el).css('position'); - if (pos == 'absolute' || pos == 'fixed' || pos == 'relative') break; - } - if (! el) return 0; - top = el.offsetTop; - while (el = el.offsetParent) { - top += el.offsetTop; - } - return top; - }, - - /* - PrivateMethod: offsetTop - - This gets what CSS 'top' would be if the element were position "absolute" - - Returns: - An integer number of pixels. - */ - offsetTop: function () { - var offsetParent = this.el[0].offsetParent, - marginDeduct = parseInt(this.el.css("margin-top")) || 0; - - if (glow.env.ie) { - while (offsetParent && offsetParent.currentStyle.position == "static") { - offsetParent = offsetParent.offsetParent; - } - } - return this.el.offset().top - $(offsetParent || document.body).offset().top - marginDeduct; - }, - - /* - PrivateMethod: offsetLeft - - This gets what CSS 'left' would be if the element were position "absolute" - - Returns: - An integer number of pixels. - */ - offsetLeft: function () { - var offsetParent = this.el[0].offsetParent, - r, - marginDeduct = parseInt(this.el.css("margin-left")) || 0, - //this is for IE6 only, which does absolute positions CACKLY (see below) - paddingLeftDeduct = 0; - - if (glow.env.ie) { - while (offsetParent && offsetParent.currentStyle.position == "static") { - offsetParent = offsetParent.offsetParent; - } - offsetParent = $(offsetParent || document.body); - - //IE6 positions things horizontally within the padding - if (glow.env.ie == 6) { - paddingLeftDeduct = parseInt(offsetParent.css("padding-left")); - } - } else { - offsetParent = $(offsetParent); - } - r = this.el.offset().left - offsetParent.offset().left - paddingLeftDeduct - marginDeduct; - return r; - }, - - /* - PrivateMethod: borderWidth - - Get the width of the element from the left edge of the left border to the right - edge of the right border. - - Returns: - An integer number of pixels. - */ - - borderWidth: function () { - var width = this.el[0].offsetWidth; - if (glow.env.khtml) { - width -= this.val('margin-left') - + this.val('margin-right') - + this.val('border-left-width') - + this.val('border-right-width'); - } - return width; - }, - - /* - PrivateMethod: borderHeight - - Get the height of an element from the top edge of the top border to the bottom - edge of the bottom border. - - Returns: - An integer number of pixels. - */ - - borderHeight: function () { - if (this._logicalBottom) { - return this._logicalBottom - this.offsetTop(); - } - var height = this.el[0].offsetHeight; - if (glow.env.khtml) { - height -= this.val('margin-top') - + this.val('margin-bottom') - + this.val('border-top-width') - + this.val('border-bottom-width'); - } - return height; - }, - - - /* - PrivateMethod: outerWidth - - Get the width of the element in including margin, borders and padding. - - Returns: - An integer number of pixels. - */ - - outerWidth: function () { - return this.borderWidth() + this.val('margin-left') + this.val('margin-right'); - }, - - /* - PrivateMethod: outerHeight - - Get the height of the element in including margin, borders and padding. This - does not take account of collapsable margins (i.e. it assumes the margins are - present). - - Returns: - An integer number of pixels. - */ - - outerHeight: function () { - return this.borderHeight() + this.val('margin-top') + this.val('margin-bottom'); - }, - - /* - PrivateMethod: innerLeftPos - - Get the offset of the left edge of the content of the box (i.e. excluding - margin, border and padding). - - Returns: - An integer number of pixels. - */ - - innerLeftPos: function () { - return this.offsetLeft() - + this.val('margin-left') - + this.val('border-left-width') - + this.val('padding-left'); - }, - - /* - PrivateMethod: innerTopPos - - Get the offset of the top edge of the content of the box (i.e. excluding - margin, border and padding). - - Returns: - An integer number of pixels. - */ - - innerTopPos: function () { - return this.offsetTop() - + this.val('margin-top') - + this.val('border-top-width') - + this.val('padding-top'); - }, - - /* - PrivateMethod: surroundWidth - - Get the combined width of the horizontal margins, borders and paddings. - - Returns: - An integer number of pixels. - */ - - surroundWidth: function () { - return this.val('margin-left') - + this.val('border-left-width') - + this.val('padding-left') - + this.val('padding-right') - + this.val('border-right-width') - + this.val('margin-right'); - }, - - /* - PrivateMethod: surroundHeight - - Get the combined height of the horizontal margins, borders and paddings. - - Returns: - An integer number of pixels. - */ - - surroundHeight: function () { - return this.val('margin-top') - + this.val('border-top-width') - + this.val('padding-top') - + this.val('padding-bottom') - + this.val('border-bottom-width') - + this.val('margin-bottom'); - }, - - /* - PrivateMethod: verticalCenter - - Get the vertical offset of the center of the element from it's offset parent. - - Returns: - An integer number of pixels. - */ - - verticalCenter: function () { - return this.offsetTop() + (this.outerHeight() / 2); - }, - - /* - PrivateMethod: verticalCenter - - Get the vertical offset of the center of the element from it's offset parent. - - Returns: - An integer number of pixels. - */ - - horizontalCenter: function () { - return this.offsetTop() + (this.outerWidth() / 2); - } - - }; - - for (var i in Box.prototype) { - if (i == 'val') memoizeNamed(Box, i); - else memoize(Box, i); - } - - glow.lang.apply(Box.prototype, { - - /* - PrivateMethod: resetPosition - - Reset cached position values for the element. - */ - - resetPosition: function () { - reset(this, [ - 'offsetTop', - 'offsetLeft', - 'borderTopPos', - 'borderLeftPos', - 'innerTopPos', - 'innerLeftPos', - 'verticalCenter', - 'horizontalCenter' - ]); - }, - - /* - PrivateMethod: setLogicalBottom - - Set the logical value for the position of the bottom of the border (offsetTop + offsetHeight). - - Arguments: - - *bottom* (integer) - - The value to use for the bottom of the box. - */ - setLogicalBottom: function (bottom) { - this._logicalBottom = bottom; - }, - - /* - PrivateMethod: boundsFor - - Get the the bounds for the left and top css properties of a child box to - ensure that it stays within this element. - - Arguments: - - *childBox* (Box) - - A Box object representing the space taken up by the child element. - - Returns: - An array of top, right, bottom and left pixel bounds for the top and left - css properties of the child element. - */ - - boundsFor: function (childBox) { - var top, left, pos = this.el.css('position'); - if (pos != 'static') { - top = left = 0; - } - else { - top = this.innerTopPos(); - left = this.innerLeftPos(); - } - return [ - top, // top - left + this.width() - childBox.outerWidth(), // right - top + this.height() - childBox.outerHeight(), // bottom - left // left - ]; - }, - - /* - PrivateMethod: outerBounds - - Get the top, right, bottom and left offsets of the outside edge of the border - of the box. - - Returns: - An array of integer pixel offsets for the top, right, bottom, left edges of the - boxes border. - */ - - outerBounds: function () { - var left = this.offsetLeft(), - top = this.offsetTop(); - return [ - top, - left + this.borderWidth(), - top + this.borderHeight(), - left - ]; - }, - - /* - PrivateMethod: intersectSize - - Get the intersection of this box with another box. - - Arguments: - - *that* (Box) - - A Box object to test for intersection with this box. - - *touches* (boolean) - - If true, then the boxes don't have to intersect but can merely touch. - - Returns: - An integer number of square pixels that the the outside of the - edge of the border of this box intersects with that of the passed - in box. - */ - - intersectSize: function (that, touches) { - var a = this.outerBounds(), b = that.outerBounds(); - if (touches) { - a[1]++; b[1]++; a[2]++; b[2]++; - } - return ( - a[2] < b[0] ? 0 : - b[2] < a[0] ? 0 : - a[0] < b[0] ? (a[2] < b[2] ? a[2] - b[0] : b[2] - b[0]) : - b[2] < a[2] ? b[2] - a[0] : a[2] - a[0] - ) * ( - a[1] < b[3] ? 0 : - b[1] < a[3] ? 0 : - a[3] < b[3] ? (a[1] < b[1] ? a[1] - b[3] : b[1] - b[3]) : - b[1] < a[1] ? b[1] - a[3] : a[1] - a[3] - ); - }, - - /* - PrivateMethod: sizePlaceholder - - Size and position a placeholder/drop indicator element to match that - of the element. - - Arguments: - - *placeholder* (glow.dom.NodeList) - - The element that will be sized. - - *pos* (optional string) - - The value for the placeholder's CSS position. Defaults to the position - of this element. - - *startLeft* (integer) - - The original left position of the element. - - *startTop* (integer) - - The original top position of the element. - */ - - sizePlaceholder: function (placeholder, pos, startLeft, startTop) { - var placeholderBox = new Box(placeholder), - el = this.el, - position = pos || el.css('position'); - placeholder.css('display', 'none'); - - el.after(placeholder); - - placeholder.css('width', (this.outerWidth() - placeholderBox.surroundWidth()) + 'px'); - placeholder.css('height', (this.outerHeight() - placeholderBox.surroundHeight()) + 'px'); - - placeholder.remove(); - - placeholder.css('display', 'block'); - - if (position != 'static') { - placeholder.css('left', startLeft + 'px'); - placeholder.css('top', startTop + 'px'); - } - placeholder.css('position', position); - }, - - /* - PrivateMethod: contains - - Check if a box is contained within this box. - - Arguments: - - *box* (Box) - - The box to test. - - Returns: - Boolean, true if contained. - */ - - contains: function (box) { - var bounds = this.boundsFor(box), top = box.offsetTop(), left = box.offsetLeft(); - return top >= bounds[0] // top - && left <= bounds[1] // right - && top <= bounds[2] // bottom - && left >= bounds[3]; // left - }, - - /* - PrivateMethod: containsPoint - - Arguments: - - *offset* (object) - - The offset to check - an object containing x and y integer pixel values. - - Returns: - Boolean, true if the point over the visible part of the element (i.e. including the borders). - */ - - containsPoint: function (offset) { - var elementOffset = this.el.offset(); - return offset.x >= elementOffset.left - && offset.y >= elementOffset.top - && offset.x <= elementOffset.left + this.borderWidth() - && offset.y <= elementOffset.top + this.borderHeight(); - }, - - /* - PrivateMethod: positionedAncestorBox - - Get a new Box for nearest ancestor of the element that has position 'absolute', 'fixed' or 'relative'. - - Returns: - An integer pixel offset. - */ - - positionedAncestorBox: function () { - var el = this.el.parent(), pos; - while (el[0]) { - pos = el.css('position') || 'static'; - if (pos == 'relative' || pos == 'absolute' || pos == 'fixed') - return new Box(el); - el = el.parent(); - } - return null; - } - }); - - function placeholderElement (el) { - var tag = el[0].tagName.toLowerCase() == 'li' ? 'li' : 'div'; - var placeholder = create('<' + tag + '>'); - if (tag == 'li') placeholder.css('list-style-type', 'none'); - return placeholder; - } - - /** - @name glow.dragdrop.Draggable - @class - @description An element that can be dragged using the mouse. - @see Draggable examples - - @param {String | Element | glow.dom.NodeList} element The element or CSS selector for an element to be made draggable. - - If a {@link glow.dom.NodeList NodeList} or CSS selector matching - multiple elements is passed only the first element is made draggable. - - @param {Object} [opts] - - An object of options. - - The opts object allows you to pass in functions to use as event - listeners. This is purely for convenience, you can also use - {@link glow.events.addListener} to add them the normal way. - - @param {String} [opts.placeholder=spacer] Defines what to leave in place of the draggable whilst being dragged. - - Possible values for this param are: - -
    -
    spacer
    an empty div is created where the draggable started.
    -
    clone
    an exact clone of the original element.
    -
    none
    no placeholder will be created.
    -
    - - @param {String} [opts.placeholderClass=glow-dragdrop-placeholder] A class be applied to the placeholder element. - - This can be used to to add styling for indicating where the element - has been dragged from, add opacity, etc. - - @param {Selector | Element | glow.dom.NodeList} [opts.handle] Restrict the drag 'handle' to an element within the draggable. - - @param {Selector | Element | glow.dom.NodeList} [opts.container] Constrain dragging to within the bounds of the specified element. - - @param {Array} [opts.dropTargets] An array of {@link glow.dragdrop.DropTarget DropTargets}. - - Specifies which {@link glow.dragdrop.DropTarget DropTargets} this draggable is associated with. - - @param {String} [opts.axis] Restrict dragging to an axis. - - Possible values for this param are: - -
    -
    x
    Restricts dragging to the x-axis
    -
    y
    Restricts dragging to the y-axis
    -
    - - @param {String[]} [opts.dragPrevention=input, textarea, button, select, option, a] Disables dragging from the specified array of element names - - By default dragging will not work when the user clicks in form - elements, otherwise these elements would be unusable. - - @param {Number|Object} [opts.step=1] The pixel interval the draggable snaps to. - If a number, the draggable will step by that number of pixels on the x and y axis. You - can provide an object in the form {x:2, y:4} to set different steps to each - axis. - - @param {Function} [opts.onDrag] An event listener that fires when the draggable starts being dragged. - - @param {Function} [opts.onEnter] An event listener that fires when the draggable is dragged over a drop target. - - @param {Function} [opts.onLeave] An event listener that fires when the draggable is dragged out of a drop target. - - @param {Function} [opts.onDrop] An event listener that fires when the draggable is dropped. - - @param {Function} [opts.onAfterDrop] An event listener that fires after the element has dropped, including any animations - - The default action is to animate the draggable back to it's start - position. This can be cancelled by returning false from the listener - or calling {@link glow.events.Event.preventDefault} on the - {@link glow.events.Event} param. - - @example - // create a draggable element with a corresponding DropTarget, - // container and two event listeners - var myDraggable = new glow.dragdrop.Draggable('#draggable', { - dropTargets : [ myDropTarget ], - container : '#container', - onDrag : function () { - this.element.css('opacity', '0.7'); - }, - onDrop : function () { - this.element.css('opacity', '1'); - } - }); - */ - /** - @name glow.dragdrop.Draggable#event:drag - @event - @description Fired when the draggable starts being dragged. - - Concelling this event results in the user being unable to pick up - the draggable. - - @param {glow.events.Event} event Event Object - */ - /** - @name glow.dragdrop.Draggable#event:enter - @event - @description Fired when the draggable is dragged over a drop target. - @param {glow.events.Event} event Event Object - */ - /** - @name glow.dragdrop.Draggable#event:leave - @event - @description Fired when the draggable is dragged out of a drop target. - @param {glow.events.Event} event Event Object - */ - /** - @name glow.dragdrop.Draggable#event:drop - @event - @description Fired when the draggable is dropped. - @param {glow.events.Event} event Event Object - */ - /** - @name glow.dragdrop.Draggable#event:afterDrop - @event - @description Fired after the element has dropped, including any animations - @param {glow.events.Event} event Event Object - */ - r.Draggable = function (el, opts) { - - /** - @name glow.dragdrop.Draggable#element - @type glow.dom.NodeList - @description glow.dom.NodeList containing the draggable element - */ - this.element = $(el); - this._opts = opts = glow.lang.apply({ - dragPrevention : ['input', 'textarea', 'button', 'select', 'option', 'a'], - placeholder : 'spacer', - placeholderClass : 'glow-dragdrop-placeholder', - step : {x:1, y:1} - }, opts || {}); - - //normalise the step param to an object - if (typeof opts.step == "number") { - opts.step = {x: opts.step, y: opts.step}; - } else { - opts.step.x = opts.step.x || 1; - opts.step.y = opts.step.y || 1; - } - - this._preventDrag = []; - for (var i = 0, l = opts.dragPrevention.length; i < l; i++) { - this._preventDrag[i] = opts.dragPrevention[i].toLowerCase(); - } - - if (opts.container) { this.container = $(opts.container); } - this._handle = opts.handle && this.element.get(opts.handle) || this.element; - - if (opts.dropTargets) this.dropTargets = $(opts.dropTargets); - - //used for IE literal edge case bug fix - //this._mouseUp = true; - //bug fix to get document.body.scrollTop to return true value (not 0) if using transitional 4.01 doctype - //get('body')[0].style.overflow = 'auto'; - //this._opts = o, this._targetCoords = [], this.isOverTarget = false; - - var listeners = this._listeners = [], - i = 0; - - if (opts.onDrag) listeners[i++] = addListener(this, 'drag', this._opts.onDrag, this); - if (opts.onEnter) listeners[i++] = addListener(this, 'enter', this._opts.onEnter, this); - if (opts.onLeave) listeners[i++] = addListener(this, 'leave', this._opts.onLeave, this); - if (opts.onDrop) listeners[i++] = addListener(this, 'drop', this._opts.onDrop, this); - - this._dragListener = addListener(this._handle, 'mousedown', this._startDragMouse, this); - - return; - }; - - - /* - Group: Methods - */ - - //var applyFloatBugfix = glow.env.ie; - - r.Draggable.prototype = { - - /* - PrivateMethod: _createPlaceholder - - Create an element that occupies the space where the draggable has been dragged from. - */ - - _createPlaceholder: function () { - var el = this.element, - placeholder, - box = this._box; - - if (this._opts.placeholder == 'clone') { - placeholder = el.clone(); - } - else { // placeholder == 'spacer' - placeholder = placeholderElement(el); - } - if (this._opts.placeholderClass) { - placeholder.addClass(this._opts.placeholderClass); - } - box.sizePlaceholder(placeholder, null, this._startLeft, this._startTop); - el.after(placeholder); - this._placeholder = placeholder; - }, - - /* - PrivateMethod: _removePlaceholder - - Removes the placeholder (see above) from the document. - */ - - _removePlaceholder: function () { - this._placeholder.remove(); - }, - - /* - PrivateMethod: _resetPosition - - Sets the position CSS property to what it started as without moving the draggable. If the - original position was 'static' and making it 'static' again would mean moving the draggable, - then the position is set to 'relative'. - */ - - _resetPosition: function () { - var origPos = this._preDragPosition, - el = this.element, - box = this._box, - startOffset = this._startOffset, - pos = el.css('position'), - newLeft, - newTop; - box.resetPosition(); - var offset = { - x: box.offsetLeft(), - y: box.offsetTop() - }; - - if (this._placeholder || this._dropIndicator) { - el.remove(); - } - if (origPos == 'static' && offset.y == startOffset.y && offset.x == startOffset.x) { - el.css('position', 'static'); - el.css('left', ''); - el.css('top', ''); - } - else { - el.css('z-index', this._preDragZIndex); - el.css('position', origPos == 'static' ? 'relative' : origPos); - if (origPos == 'static') { - newLeft = offset.x - startOffset.x; - newTop = offset.y - startOffset.y; - } - else if (origPos == 'relative' && pos != 'relative') { - newLeft = this._startLeft + (offset.x - startOffset.x); - newTop = this._startTop + (offset.y - startOffset.y); - } - if (pos != origPos) { - el.css('left', newLeft ? newLeft + 'px' : ''); - el.css('top', newTop ? newTop + 'px' : ''); - } - } - if (this._dropIndicator) { - var parent = this._dropIndicator.parent()[0]; - if (parent) parent.replaceChild(el[0], this._dropIndicator[0]); - delete this._dropIndicator; - if (this._placeholder) { - this._placeholder.remove(); - delete this._placeholder; - } - // this is canceling out some of the stuff done in the if statement above, could be done better - el.css('position', origPos); - if (origPos == 'relative' && pos != 'relative') { - el.css('left', this._startLeft); - el.css('top', this._startTop); - } - } - else if (this._placeholder) { - var parent = this._placeholder.parent()[0]; - if (parent) parent.replaceChild(el[0], this._placeholder[0]); - delete this._placeholder; - } - }, - - /* - PrivateFunction: _startDragMouse - - Start the draggable dragging when the mousedown event is fired. - - Arguments: - - *e* (glow.events.Event) - - The mousedown event that caused the listener to be fired. - */ - - _startDragMouse: function (e) { - var preventDrag = this._preventDrag, - source = e.source, - tag = source.tagName.toLowerCase(); - - for (var i = 0, l = preventDrag.length; i < l; i++) { - if (preventDrag[i] == tag) { - return; - } - } - - //fire the drag event - if (fire(this, 'drag').defaultPrevented()) { - //the default action was prevented, don't do any dragging - return; - } - - if (this._dragging == 1) - return this.endDrag(); - else if (this._dragging) - return; - - // _dragging set to 1 during drag, 2 while ending drag and back to 0 when ready for new drag - this._dragging = 1; - - var el = this.element, - container = this.container, - opts = this._opts, - box = this._box = new Box(el), - step = opts.step; - - this._preDragPosition = el.css('position'); - - var startOffset = this._startOffset = { - x: box.offsetLeft(), - y: box.offsetTop() - }; - - if (container) { - this._containerBox = new Box(container); - this._bounds = this._containerBox.boundsFor(box); - //we may need to decrease the bounds to keep the element in step (if it's using stepped dragging) - //basically makes the bounding box smaller to fit in with the stepping - if (step.x != 1) { - this._bounds[3] -= (this._bounds[3] - startOffset.x) % step.x; - this._bounds[1] -= (this._bounds[1] - startOffset.x) % step.x; - } - if (step.y != 1) { - this._bounds[0] -= (this._bounds[0] - startOffset.y) % step.y; - this._bounds[2] -= (this._bounds[2] - startOffset.y) % step.y; - } - } - else { - delete this._bounds; - } - - this._mouseStart = { - x: e.pageX, - y: e.pageY - }; - - - - this._preDragStyle = el.attr('style'); - - this._preDragZIndex = el.css('z-index'); - - el.css('z-index', _zIndex++); - - this._startLeft = el[0].style.left ? parseInt(el[0].style.left) : 0; - this._startTop = el[0].style.top ? parseInt(el[0].style.top) : 0; - - - if (opts.placeholder && opts.placeholder != 'none') { - this._createPlaceholder(); - } - - el.css('position', 'absolute'); - el.css('left', startOffset.x + 'px'); - el.css('top', startOffset.y + 'px'); - - if(_ieStrict) { - this._scrollY = document.documentElement.scrollTop; - this._innerHeight = document.documentElement.clientHeight; - } - else if(_ieTrans){ - this._scrollY = document.body.scrollTop; - this._innerHeight = document.body.clientHeight; - } - else { - this._scrollY = window.scrollY; - this._innerHeight = window.innerHeight; - } - - var cancelFunc = function () { return false }, - doc = document.documentElement; - - if (this.dropTargets) { - var event = new events.Event(); - event.draggable = this; - for (var i = 0, l = this.dropTargets.length; i < l; i++) { - fire(this.dropTargets[i], 'active', event); - } - - this._mousePos = { - x: e.pageX, - y: e.pageY - }; - this._testForDropTargets(); - } - - this._dragListeners = [ - addListener(doc, 'selectstart', cancelFunc), - addListener(doc, 'dragstart', cancelFunc), - addListener(doc, 'mousedown', cancelFunc), - addListener(doc, 'mousemove', this._dragMouse, this), - addListener(doc, 'mouseup', this._releaseElement, this) - ]; - return false; - }, - - /* - PrivateFunction: _dragMouse - - Move the draggable when a mousemove event is received. - - Arguments: - - *e* (glow.events.Event) - - The mousedown event that caused the listener to be fired. - */ - - _dragMouse: function (e) { - var element = this.element, - axis = this._opts.axis, - //do we need to do axis here, or just not apply the newX/Y if axis is used? May be faster - newX = axis == 'y' ? - this._startOffset.x: - (this._startOffset.x + e.pageX - this._mouseStart.x), - newY = axis == 'x' ? - this._startOffset.y: - (this._startOffset.y + e.pageY - this._mouseStart.y), - bounds = this._bounds, - step = this._opts.step; - - - //round position to the nearest step - if (step.x != 1) { - newX = Math.round((newX - this._startOffset.x) / step.x) * step.x + this._startOffset.x; - } - if (step.y != 1) { - newY = Math.round((newY - this._startOffset.y) / step.y) * step.y + this._startOffset.y; - } - - // only pay for the function call if we have a container or an axis - if (bounds) { - // only apply bounds on the axis we're using - if (axis != 'y') { - newX = newX < bounds[3] ? bounds[3] : newX > bounds[1] ? bounds[1] : newX; - } - if (axis != 'x') { - newY = newY < bounds[0] ? bounds[0] : newY > bounds[2] ? bounds[2] : newY; - } - } - - // set the new position - element[0].style.left = newX + 'px'; - element[0].style.top = newY + 'px'; - - //if there are dragTargets check if the draggable is over the target - if (this.dropTargets) { - this._mousePos = { x: e.pageX, y: e.pageY }; - } - // check for IE mouseup outside of page boundary - if(_ie && e.nativeEvent.button == 0) { - this._releaseElement(e); - return false; - }; - return false; - }, - - /* - PrivateFunction: _testForDropTarget - - Check if the draggable is over a drop target. Sets the activeTarget property of the draggable - to the drop target that the draggable is over, if any. - - Arguments: - - *mousePos* (object) - - The position of the mouse pointer relative to the document. The object has x and y integer - pixel properties. - */ - _testForDropTargets: function (fromTimeout) { - - if (! this._lock) this._lock = 0; - if (fromTimeout) this._lock--; - else if (this.lock) return; - - if (this._dragging != 1) return; - - var previousTarget = this.activeTarget, - activeTarget, - targets = this.dropTargets, - target, - targetBox, - box = this._box, - mousePos = this._mousePos; - - box.resetPosition(); - - var maxIntersectSize = 0; - for (var i = 0, l = targets.length; i < l; i++) { - target = targets[i]; - targetBox = target._box; - if (target._opts.tolerance == 'contained') { - if (targetBox.contains(box)) { - activeTarget = target; - break; - } - } - else if (target._opts.tolerance == 'cursor') { - if (targetBox.containsPoint(mousePos)) { - activeTarget = target; - break; - } - } - else { - var intersectSize = targetBox.intersectSize(box, true); - if (intersectSize > maxIntersectSize) { - maxIntersectSize = intersectSize; - activeTarget = target; - } - } - } - this.activeTarget = activeTarget; - - // enter events - if (activeTarget !== previousTarget) { - if (activeTarget) { - // enter on the target - var draggableEnterEvent = new events.Event(); - draggableEnterEvent.draggable = this; - fire(activeTarget, 'enter', draggableEnterEvent); - - // enter on this (the draggable) - var enterTargetEvent = new events.Event(); - enterTargetEvent.dropTarget = activeTarget; - fire(this, 'enter', enterTargetEvent); - } - - if (previousTarget) { - // leave on target - var draggableLeaveEvent = new events.Event(); - draggableLeaveEvent.draggable = this; - fire(previousTarget, 'leave', draggableLeaveEvent); - - // leave on this (draggable) - var leaveTargetEvent = new events.Event(); - leaveTargetEvent.dropTarget = previousTarget; - fire(this, 'leave', leaveTargetEvent); - } - } - // place the drop indicator in the drop target (not in the drop target class for speed) - if (activeTarget && activeTarget._opts.dropIndicator != 'none') { - var childBox, - childBoxes = activeTarget._childBoxes, - children = activeTarget._children; - box.resetPosition(); - var totalHeight = activeTarget._box.innerTopPos(); - var draggablePosition = mousePos.y - box.offsetParentPageTop(); - var placed = 0; - for (var i = 0, l = childBoxes.length; i < l; i++) { - if (children[i] == this.element[0]) continue; - childBox = childBoxes[i]; - totalHeight += childBox.outerHeight(); - if (draggablePosition <= totalHeight) { - if (activeTarget._dropIndicatorAt != i) { - $(childBox.el).before(activeTarget._dropIndicator); - activeTarget._dropIndicatorAt = i; - } - placed = 1; - break; - } - } - if (! placed) { - if (childBox) { - $(childBox.el).after(activeTarget._dropIndicator); - activeTarget._dropIndicatorAt = i + 1; - } - else { - activeTarget.element.append(activeTarget._dropIndicator); - activeTarget._dropIndicatorAt = 0; - } - } - } - - this._lock++; - var this_ = this; - setTimeout(function () { this_._testForDropTargets(1) }, 100); - }, - - /* - PrivateMethod: releaseElement - - Finish the drag when a mouseup event is recieved. - - Arguments: - - *e* (glow.events.Event) - - The mouseup event that caused the listener to be fired. - */ - - _releaseElement: function () { - if (this._dragging != 1) return; - this._dragging = 2; - - var i, l; - - //call the onInactive function on all the dropTargets for this draggable - var dropTargets = this.dropTargets, - activeTarget = this.activeTarget; - - if (dropTargets) { - for (i = 0, l = dropTargets.length; i < l; i++) { - var event = new events.Event(); - event.draggable = this; - event.droppedOnThis = activeTarget && activeTarget == dropTargets[i]; - fire(dropTargets[i], 'inactive', event); - } - } - - if (activeTarget) { - var event = new events.Event(); - event.draggable = this; - fire(activeTarget, 'drop', event); - } - - var dragListeners = this._dragListeners; - for (i = 0, l = dragListeners.length; i < l; i++) { - events.removeListener(dragListeners[i]); - } - - var dropEvent = fire(this, "drop"); - if (! dropEvent.defaultPrevented() && this.dropTargets) { - this.returnHome(); - } - else { - this.endDrag(); - } - - }, - - /* - Method: endDrag - - Finishes dragging the draggable. Removes the placeholder (if any) and resets the position CSS property - of the draggable. - - TODO - revist this code example - - N.B. This is called by default but if you overwrite the onDrop function then you will have to call it youoriginal - (code) - // empty NodeList - var myDraggable = new glow.dragdrop.Draggable('#draggable', { - onDrop = function(e){ - do some stuff that takes a while..... - this.endDrag(); - false; - } - }); - (end) - */ - - endDrag: function(){ - if (this._dragging != 2) return; - this._dragging = 0; - - //remove any helpers/placeholders - - if (this._reset) { - this._reset(); - delete this._reset; - } - - if (this.placeholder) { - this.placeholder.remove(); - } - this._resetPosition(); - delete this.activeTarget; - fire(this, "afterDrop"); - }, - - /* - Event: returnHome - - Animates the Draggable back to it's start position and calls endDrag() at the end of the - transition. This is called by default when the Draggable, that has a DragTarget, is dropped. - However if you override the default onDrop function you may want to call this function your - original - - Arguments - tween (function) - The animation you wish to used for easing the Draggable. See . This is optional, the default is a linear tween. e.g. glow.tweens.easeboth - */ - - returnHome: function(tween){ - var mytween = (tween) ? tween : glow.tweens.linear(), - leftDestination, - topDestination, - el = this.element, - distance = Math.pow( - Math.pow(this._startOffset.x - this._box.offsetLeft(), 2) - + Math.pow(this._startOffset.y - this._box.offsetTop(), 2), - 0.5 - ), - duration = 0.3 + (distance / 1000); - - var channels = [[ - glow.anim.css(el, duration, { - left: this._startOffset.x, - top : this._startOffset.y - }, { tween: mytween }) - ]]; - - if (this._dropIndicator) { - channels.push([glow.anim.css(this._dropIndicator, duration - 0.1, { opacity: { to: 0 } })]); - } - - var timeline = new glow.anim.Timeline(channels); - addListener(timeline, 'complete', function () { - this.endDrag(); - }, this); - timeline.start(); - return; - } - }; - - - var dropTargetId = 0; - - /** - @name glow.dragdrop.DropTarget - @class - @description An element that can react to Draggables. - @see DropTarget examples - - @param {String | Element | glow.dom.NodeList} element The element or CSS selector for an element to be made droppable. - - If a {@link glow.dom.NodeList NodeList} or CSS selector matching - multiple elements is passed only the first element is made droppable. - - @param {Object} [opts] - - An object of options. - - The opts object allows you to pass in functions to use as event - listeners. This is purely for convenience, you can also use - {@link glow.events.addListener} to add them the normal way. - - @param {String} [opts.tolerance=intersect] The point at which the target becomes active when a draggable moves over it. - - Possible values for this param are: - -
    -
    intersect
    The target becomes active as soon as any part of the draggable is over the target.
    -
    cursor
    The target becomes active when the cursor is over the target.
    -
    contained
    The target only becomes active once the whole draggable is within the target.
    -
    - - @param {String} [opts.dropIndicator=none] Whether to create an element when a Draggable is over the DropTarget. - - Possible values for this param are: - -
    -
    spacer
    an empty div will be added to the drop target to indicate where the Draggable will be dropped.
    -
    none
    no drop indicator will be created.
    -
    - - @param {String} [opts.dropIndicatorClass=glow-dragdrop-dropindicator] The class apply to the dropIndicator element. - - This is useful if you want to style the drop indicator. - - @param {Function} [opts.onEnter] An event listener to fire when an associated Draggable is dragged over the drop target. - - @param {Function} [opts.onLeave] An event listener to fire when an associated Draggable is dragged out of the drop target. - - @param {Function} [opts.onDrop] An event listener to fire when an associated Draggable is dropped on the drop target. - - @param {Function} [opts.onActive] An event listener to fire when an associated Draggable starts being dragged. - - @param {Function} [opts.onInactive] An event listener to fire when an associated Draggable stops being dragged. - - @example - var myDropTarget = new glow.dragdrop.DropTarget('#dropTarget', { - onActive: function(e){ - this.element.css('border', '2px solid blue'); - }, - onInactive: function(e){ - this.element.css('border', ''); - this.element.css('opacity', '1'); - }, - onEnter: function(e){ - this.element.css('opacity', '0.2'); - }, - onLeave: function(e){ - this.element.css('opacity', '1'); - }, - onDrop: function(e){ - this.element.css('backgroundColor', 'green'); - } - }); - */ - /** - @name glow.dragdrop.DropTarget#event:active - @event - @description Fired when a draggable linked to this drop target starts being dragged. - @param {glow.events.Event} event Event Object - */ - /** - @name glow.dragdrop.DropTarget#event:inactive - @event - @description Fired when a draggable linked to this drop target stops dragging. - @param {glow.events.Event} event Event Object - */ - /** - @name glow.dragdrop.DropTarget#event:enter - @event - @description Fired when a draggable linked to this drop target is dragged over the target. - @param {glow.events.Event} event Event Object - */ - /** - @name glow.dragdrop.DropTarget#event:leave - @event - @description Fired when a draggable linked to this drop target is dragged out of the target. - @param {glow.events.Event} event Event Object - */ - /** - @name glow.dragdrop.DropTarget#event:drop - @event - @description Fired when a draggable linked is dropped on this drop target. - @param {glow.events.Event} event Event Object - */ - r.DropTarget = function(el, opts) { - /** - @name glow.dragdrop.DropTarget#element - @type glow.dom.NodeList - @description glow.dom.NodeList containing the draggable element - */ - el = this.element = $(el); - if (! el.length) throw 'no element passed into DropTarget constuctor'; - if (el.length > 1) throw 'more than one element passed into DropTarget constructor'; - - // id is for indexing drop targets in an object for getting to them quickly - this._id = ++dropTargetId; - - this._opts = opts = glow.lang.apply({ - dropIndicator : 'none', - dropIndicatorClass : 'glow-dragdrop-dropindicator', - tolerance : 'intersect' - }, opts || {}); - - if (opts.onActive) addListener(this, 'active', opts.onActive); - if (opts.onInactive) addListener(this, 'inactive', opts.onInactive); - if (opts.onEnter) addListener(this, 'enter', opts.onEnter); - if (opts.onLeave) addListener(this, 'leave', opts.onLeave); - if (opts.onDrop) addListener(this, 'drop', opts.onDrop); - - addListener(this, 'active', this._onActive); - addListener(this, 'inactive', this._onInactive); - - return this; - }; - - - r.DropTarget.prototype = { - - /* - Method: setLogicalBottom(height) - - Set a bottom pos to use for detecting if a draggable is over the drop target to use - other than the actual bottom of the drop target (offsetTop + offsetHeight). - - Arguments: - - *bottom* (integer) - - The number of pixels to use for the bottom of the drop target. - */ - setLogicalBottom: function (bottom) { - this._logicalBottom = bottom; - }, - - /* - PrivateMethod: _onActive - - Respond to an associated draggable when it starts to be dragged. - - Arguments: - - *e* (glow.events.Event) - - The active event that caused the event listener to be fired. - */ - - _onActive: function (e) { - var draggable = e.draggable; - - this._box = new Box(this.element); - if (this._logicalBottom) this._box.setLogicalBottom(this._logicalBottom); - - if (this._opts.dropIndicator == 'none') return; - - this._onEnterListener = addListener(this, 'enter', this._onEnter); - this._onLeaveListener = addListener(this, 'leave', this._onLeave); - - this._dropIndicator = placeholderElement(draggable.element); - - if (this._opts.dropIndicatorClass) { - this._dropIndicator.addClass(this._opts.dropIndicatorClass); - } - draggable._box.sizePlaceholder(this._dropIndicator, 'relative', 0, 0); - - - var children = this._children = $(this.element.children()).filter(function () { - var el = $(this); - return (! e.draggable._placeholder || ! el.eq(e.draggable._placeholder)) - && (! this._dropIndicator || ! el.eq(this._dropIndicator)); - }); - var childBoxes = this._childBoxes = []; - children.each(function (i) { - childBoxes[i] = new Box($(children[i])); - }); - }, - - /* - PrivateMethod: _onInactive - - Respond to an associated draggable when it finishes being dragged. - - Arguments: - - *e* (glow.events.Event) - - The inactive event that caused the event listener to be fired. - */ - - _onInactive: function (e) { - removeListener(this._onEnterListener); - removeListener(this._onLeaveListener); - - delete this._box; - - if (this._opts.dropIndicator == 'none') return; - - if (! e.droppedOnThis && this._dropIndicator) { - this._dropIndicator.remove(); - delete this._dropIndicator; - } - delete this._childBoxes; - delete this._children; - }, - - /* - PrivateMethod: _onEnter - - Respond to an associated draggable being dragged over the drop target. - - Arguments: - - *e* (glow.events.Event) - - The enter event that caused the event listener to be fired. - */ - - _onEnter: function () { - this._dropIndicatorAt = -1; - }, - - /* - PrivateMethod: _onLeave - - Respond to an associated draggable being dragged out of the drop target. - - Arguments: - - *e* (glow.events.Event) - - The leave event that caused the event listener to be fired. - */ - - _onLeave: function () { - this._dropIndicator.remove(); - }, - - /* - Method: moveToPosition - - Insert the draggable's element within the drop target where the drop indicator currently is. Sets - the start offset of the drag to the position of the drop indicator so that it will be animated - to it's final location, rather than where the drag started. - */ - - moveToPosition : function (draggable) { - var dropIndicator = this._dropIndicator, - box = new Box(dropIndicator); - //dropIndicator.after(draggable.element); - var marginLeft = parseInt(dropIndicator.css('margin-left')) || 0, - marginTop = parseInt(dropIndicator.css('margin-top')) || 0; - - draggable._startOffset = { - x: box.offsetLeft() - marginLeft, - y: box.offsetTop() - marginTop - }; - draggable._dropIndicator = dropIndicator; - delete this._dropIndicator; - } - - } - glow.dragdrop = r; - - - } -}); -/*@end @*/ diff --git a/horde/js/glow/1.5.1/core/core.js b/horde/js/glow/1.5.1/core/core.js deleted file mode 100644 index 4f9eb44a5..000000000 --- a/horde/js/glow/1.5.1/core/core.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - Copyright 2009 British Broadcasting Corporation - - 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 f={glow:true},b=/([$^\\\/()|?+*\[\]{}.-])/g,a=navigator.userAgent.toLowerCase(),j="1.5.1",q=0,g=[],l=0,p=[],n=0,r=false,m={VERSION:j,UID:"glow"+Math.floor(Math.random()*(1<<30)),isDomReady:window.gloader&&gloader.isReady,isReady:window.gloader&&gloader.isReady,env:function(){var t=[0,NaN],d=(/opera[\s\/]([\w\.]+)/.exec(a)||t)[1],u=d?NaN:(/msie ([\w\.]+)/.exec(a)||t)[1],w=(/rv:([\w\.]+).*gecko\//.exec(a)||t)[1],e=(/applewebkit\/([\w\.]+)/.exec(a)||t)[1],s=(/khtml\/([\w\.]+)/.exec(a)||t)[1],v=parseFloat;return{gecko:v(w),ie:v(u),opera:v(d),webkit:v(e),khtml:v(s),version:u||w||e||d||s,standardsMode:document.compatMode!="BackCompat"&&(!u||u>=6)};}(),module:function(t){var s=2,v=t.depends[0]||[],d=v.length,e=t.name,u=window.glow;if(t.library[1]!=m.VERSION){throw new Error("Cannot register "+e+": Version mismatch");}if(v[2]){for(;s 5.5)@*/ -(window.gloader||glow).module({name:"glow.dom",library:["glow","1.5.1"],depends:[],builder:function(q){var j=q.env,g=q.lang,n={tagName:/^(\w+|\*)/,combinator:/^\s*([>]?)\s*/,classNameOrId:(j.webkit<417)?new RegExp("^([\\.#])((?:(?![\\.#\\[:\\s\\\\]).|\\\\.)+)"):/^([\.#])((?:[^\.#\[:\\\s]+|\\.)+)/},S=/([$^\\\/()|?+*\[\]{}.-])/g,z={},M={checked:"checked","class":"className",disabled:"disabled","for":"htmlFor",maxlength:"maxLength"},b={checked:true,disabled:true},Z={maxlength:function(r){return r.toString()=="2147483647"?undefined:r;}},X=1,u="_unique"+q.UID,F={black:0,silver:12632256,gray:8421504,white:16777215,maroon:8388608,red:16711680,purple:8388736,fuchsia:16711935,green:32768,lime:65280,olive:8421376,yellow:16776960,navy:128,blue:255,teal:32896,aqua:65535,orange:16753920},B=/height|top/,o=/^rgb\(([\d\.]+)(%?),\s*([\d\.]+)(%?),\s*([\d\.]+)(%?)/i,x=/^(?:(width|height)|(border-(top|bottom|left|right)-width))$/,A=/width|height|top$|bottom$|left$|right$|spacing$|indent$|font-size/,P,c,p=["Top","Right","Bottom","Left"],N=4,w="padding",m="margin",W="border",C="Width",H,E,V=window,h=document,Q,D,t,L=h.createElement("div");if(j.ie){window.attachEvent("onunload",function(){L=null;});}q.ready(function(){Q=h.body;D=h.documentElement;});(function(){var r=h.createElement("div");r.a=1;t=!!r.cloneNode(true).a;})();function s(r){return new RegExp(["(^|\\s)",r.replace(S,"\\$1"),"($|\\s)"].join(""),"g");}function J(ac){var ab=[],aa=0;L.innerHTML="a"+ac;while(L.childNodes[1]){ab[aa++]=L.removeChild(L.childNodes[1]);}L.innerHTML="";return ab;}function k(ac){var ab=[],aa=0;for(;ac[aa];aa++){ab[aa]=ac[aa];}return ab;}function d(ad,aa){for(var ac=this,r=0,ab=ac.length;r500&&ac=="margin-right"&&ae.getPropertyValue("position")!="absolute"){ac="margin-left";}ab=ae.getPropertyValue(ac);}}else{if(ad){if(ac=="opacity"){ah=/alpha\(opacity=([^\)]+)\)/.exec(ad.filter);return ah?String(parseInt(ah[1],10)/100):"1";}ab=String(ad[R(ac)]);if(/^-?\d+[a-z%]+$/i.test(ab)&&ac!="font-size"){ab=G(ai,ab,B.test(ac))+"px";}}}}}if(ac.indexOf("color")!=-1){ab=O(ab).toString();}else{if(ab.indexOf("url")==0){ab=ab.replace(/\"/g,"");}}return ab;}function G(ac,ae,r){if(/^-?\d+(px)?$/i.test(ae)){return parseInt(ae);}var ab=r?"top":"left",ad=r?"Top":"Left",af=ac.style,ag=af.left,ah=af.overflow,aa=af.margin;af.position="absolute";af.margin="0";af[ab]=ae||0;ae=ac["offset"+ad];af.position=ah;af[ab]=ag;af.margin=aa;return ae;}function O(ab){if(/^(transparent|rgba\(0, ?0, ?0, ?0\))$/.test(ab)){return"transparent";}var af,aa,ag,ah,ac,ae=Math.round,ai=parseInt,ad=parseFloat;if(af=o.exec(ab)){aa=af[2]?ae(((ad(af[1])/100)*255)):ai(af[1]);ag=af[4]?ae(((ad(af[3])/100)*255)):ai(af[3]);ah=af[6]?ae(((ad(af[5])/100)*255)):ai(af[5]);}else{if(typeof ab=="number"){ac=ab;}else{if(ab.charAt(0)=="#"){if(ab.length=="4"){ab="#"+ab.charAt(1)+ab.charAt(1)+ab.charAt(2)+ab.charAt(2)+ab.charAt(3)+ab.charAt(3);}ac=ai(ab.slice(1),16);}else{ac=F[ab];}}aa=(ac)>>16;ag=(ac&65280)>>8;ah=(ac&255);}ab=new String("rgb("+aa+", "+ag+", "+ah+")");ab.r=aa;ab.g=ag;ab.b=ah;return ab;}function i(ae){var ad="",ab=ae.childNodes,ac=0,aa=ab.length;for(;ac=521){for(;r1?ad:undefined;}if(typeof ab=="object"){for(ac in ab){if(g.hasOwnProperty(ab,ac)){ad.attr(ac,ab[ac]);}}return ad;}if(j.ie&&M[ab]){if(r>1){d.call(ad,aa[1],function(af){this[M[ab]]=af;});return ad;}ae=ad[0][M[ab]];if(b[ab]){return ae?ab:undefined;}else{if(Z[ab]){return Z[ab](ae);}}return ae;}if(r>1){d.call(ad,aa[1],function(af){this.setAttribute(ab,af);});return ad;}return I(ad[0])?ad[0].getAttribute(ab):ad[0].getAttribute(ab,2);},removeAttr:function(aa){var r=j.ie&&M[aa],ad=this,ab=0,ac=ad.length;for(;ab-1?ag.options[ag.selectedIndex].value:"";}else{if(ad=="select-multiple"){for(var ah=ag.options.length;af0){for(;aa500){r+=parseInt(U(ae,"border-left-width"));af+=parseInt(U(ae,"border-top-width"));}if(ae.nodeName.toLowerCase!="body"){ab=ae;}}while(ae=ae.offsetParent);ae=aa;while((ae=ae.parentNode)&&(ae!=Q)&&(ae!=D)){r-=ae.scrollLeft;af-=ae.scrollTop;if(j.gecko&&U(ae,"overflow")!="visible"){r+=parseInt(U(ae,"border-left-width"));af+=parseInt(U(ae,"border-top-width"));}}if(ad){r+=ac.x;af+=ac.y;}if((j.webkit<500&&(ad||U(ab,"position")=="absolute"))||(j.gecko&&U(ab,"position")!="absolute")){r-=Q.offsetLeft;af-=Q.offsetTop;}return{left:r,top:af};}},append:function(ae){var ad=this,aa=0,ab=1,ac=ad.length,r;if(ac==0){return ad;}r=typeof ae=="string"?k(J(ae)):ae.nodeType?[ae]:k(ae);for(;r[aa];aa++){ad[0].appendChild(r[aa]);}for(;ab=0;ac--){af[0].parentNode.insertBefore(ab[ac],af[0].nextSibling);}for(;ad=0;ac--){af[ad].parentNode.insertBefore(r[ac],af[ad].nextSibling);}}return af;},before:function(ag){var af=this,ae=af.length,ac=0,ad=1,ab,aa,r;if(ae==0){return af;}ab=typeof ag=="string"?T.create(ag):ag instanceof T.NodeList?ag:T.get(ag);aa=ab.length;for(;ac"){ai[ar++]=[ac,[null]];if(al){ai[ar++]=[ag,[al.replace(/\\/g,""),null]];}if(ap&&ap!="*"){ai[ar++]=[ah,[ap,null]];}}}aq=true;while(aq){if(ao.charAt(0)=="#"||ao.charAt(0)=="."){if(am=n.classNameOrId.exec(ao)){if(ao.charAt(0)=="#"){ai[ar++]=[ag,[am[2].replace(/\\/g,""),null]];}else{ai[ar++]=[af,[am[2].replace(/\\/g,""),null]];}ao=ao.slice(am[0].length);}else{throw new Error("Invalid Selector");}}else{aq=false;}}ak=false;}if(ao!==""){throw new Error("Invalid Selector");}return z[ao]=ai;}function ae(ai,am){var ak=am;for(var aj=0,an=ai.length;aj');q.dom=T;}});(window.gloader||glow).module({name:"glow.events",library:["glow","1.5.1"],depends:[["glow","1.5.1","glow.dom"]],builder:function(m){var h=m.dom.get;var z={};var u=1;var k=1;var l={};var a={};var n={};var j="__eventId"+m.UID;var e=j+"PreventDefault";var s=j+"StopPropagation";var B={};var d=1;var y={};var w={};var D=1;var b=2;var p=4;var f={TAB:"\t",SPACE:" ",ENTER:"\n",BACKTICK:"`"};var H={"96":223};var q={CAPSLOCK:20,NUMLOCK:144,SCROLLLOCK:145,BREAK:19,BACKTICK:223,BACKSPACE:8,PRINTSCREEN:44,MENU:93,SPACE:32,SHIFT:16,CTRL:17,ALT:18,ESC:27,TAB:9,META:91,RIGHTMETA:92,ENTER:13,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,INS:45,HOME:36,PAGEUP:33,DEL:46,END:35,PAGEDOWN:34,LEFT:37,UP:38,RIGHT:39,DOWN:40};var F={};for(var E in q){F[""+q[E]]=E;}var v="0123456789=;'\\/#,.-";function A(L){var K=w[L];if(!K){return false;}var J=y[K];if(!J){return false;}for(var I=0,r=J.length;I418)){J.addEventListener(I.toLowerCase()=="mousewheel"&&m.env.gecko?"DOMMouseScroll":I,L,false);}else{var r="on"+I;var K=J[r];if(K){J[r]=function(){var M=K.apply(this,arguments),N=L.apply(this,arguments);return(M!==false)&&(N!==false);};}else{J[r]=L;}}J=null;}z._copyListeners=function(O,N){var J=O.length,M,r,I,L,K;while(J--){if(O[J][j]){M=l[O[J][j]];for(r in M){I=0;L=M[r].length;for(;I").text(html).html();}};}});(window.gloader||glow).module({name:"glow.net",library:["glow","1.5.1"],depends:[["glow","1.5.1","glow.data","glow.events"]],builder:function(j){var d={XML_ERR:"Cannot get response as XML, check the mime type of the data",POST_DEFAULT_CONTENT_TYPE:"application/x-www-form-urlencoded;"},e=[],k="c",l="_"+j.UID+"loadScriptCbs",n=j.events,i=function(){};function o(){if(window.ActiveXObject){return(o=function(){return new ActiveXObject("MSXML2.XMLHTTP");})();}else{return(o=function(){return new XMLHttpRequest();})();}}function g(p){return j.lang.apply({onLoad:i,onError:i,onAbort:i,headers:{},async:true,useCache:false,data:null,defer:false},p||{});}function m(p){return[p,(/\?/.test(p)?"&":"?"),"a",new Date().getTime(),parseInt(Math.random()*100000)].join("");}function h(w,p,t){var s=o(),u=t.data&&(typeof t.data=="string"?t.data:j.data.encodeUrl(t.data)),q,r=new c(s,t);if(!t.useCache){p=m(p);}s.open(w,p,t.async);for(q in t.headers){s.setRequestHeader(q,t.headers[q]);}function v(){r.send=i;if(t.async){if(t.timeout){r._timeout=setTimeout(function(){f(r);var y=new b(s,true);n.fire(r,"error",y);},t.timeout*1000);}s.onreadystatechange=function(){if(s.readyState==4){r._timeout&&clearTimeout(r._timeout);r.completed=true;var y=new b(s);if(y.wasSuccessful){n.fire(r,"load",y);}else{n.fire(r,"error",y);}}};s.send(u);return r;}else{s.send(u);r.completed=true;var x=new b(s);if(x.wasSuccessful){n.fire(r,"load",x);}else{n.fire(r,"error",x);}return x;}}r.send=v;return t.defer?r:v();}var a={};a.get=function(p,q){q=g(q);return h("GET",p,q);};a.post=function(p,q,r){r=g(r);r.data=q;if(!r.headers["Content-Type"]){r.headers["Content-Type"]=d.POST_DEFAULT_CONTENT_TYPE;}return h("POST",p,r);};a.loadScript=function(q,t){var r=e.length,p,u=k+r,t=g(t),s=new c(r,t),q=t.useCache?q:m(q),v=window[l]||(window[l]={});if(t.onLoad){v[u]=function(){s._timeout&&clearTimeout(s._timeout);s.completed=true;t.onLoad.apply(this,arguments);j.dom.get(p).destroy();p=e[r]=v[u]=undefined;delete v[u];delete e[r];};q=j.lang.interpolate(q,{callback:l+"."+u});}p=e[r]=document.createElement("script");if(t.charset){p.charset=t.charset;}n.addListener(s,"abort",t.onAbort);j.ready(function(){if(t.timeout){s._timeout=setTimeout(function(){f(s);t.onError();},t.timeout*1000);}if(j.env.opera){setTimeout(function(){if(p){p.src=q;}},0);}else{p.src=q;}document.body.appendChild(p);});return s;};function f(q){var p=q.nativeRequest,r=q._callbackIndex;q._timeout&&clearTimeout(q._timeout);if(p){p.onreadystatechange=i;p.abort();}else{if(r){window[l][k+r]=i;j.dom.get(e[r]).destroy();}}}function c(s,q){this._timeout=null;this.complete=false;if(typeof s=="number"){this._callbackIndex=s;}else{this.nativeRequest=s;}var r=["Load","Error","Abort"],p=0;for(;p<3;p++){n.addListener(this,r[p].toLowerCase(),q["on"+r[p]]);}}c.prototype={send:function(){},abort:function(){if(!this.completed&&!n.fire(this,"abort").defaultPrevented()){f(this);}return this;}};function b(q,p){n.Event.call(this);this.nativeResponse=q;this.status=p?408:q.status==1223?204:q.status;this.timedOut=!!p;this.wasSuccessful=(this.status>=200&&this.status<300)||this.status==304||(this.status==0&&q.responseText);}j.lang.extend(b,n.Event,{text:function(){return this.nativeResponse.responseText;},xml:function(){if(!this.nativeResponse.responseXML){throw new Error(d.XML_ERR);}return this.nativeResponse.responseXML;},json:function(p){return j.data.decodeJson(this.text(),{safeMode:p});},header:function(p){return this.nativeResponse.getResponseHeader(p);},statusText:function(){return this.timedOut?"Request Timeout":this.nativeResponse.statusText;}});j.net=a;}});(window.gloader||glow).module({name:"glow.tweens",library:["glow","1.5.1"],depends:[],builder:function(b){function a(c){return function(d){return 1-c(1-d);};}b.tweens={linear:function(){return function(c){return c;};},easeIn:function(c){c=c||2;return function(d){return Math.pow(1,c-1)*Math.pow(d,c);};},easeOut:function(c){return a(this.easeIn(c));},easeBoth:function(c){return this.combine(this.easeIn(c),this.easeOut(c));},overshootIn:function(c){return a(this.overshootOut(c));},overshootOut:function(c){c=c||1.70158;return function(d){if(d==0||d==1){return d;}return((d-=1)*d*((c+1)*d+c)+1);};},overshootBoth:function(c){return this.combine(this.overshootIn(c),this.overshootOut(c));},bounceIn:function(){return a(this.bounceOut());},bounceOut:function(){return function(c){if(c<(1/2.75)){return 7.5625*c*c;}else{if(c<(2/2.75)){return(7.5625*(c-=(1.5/2.75))*c+0.75);}else{if(c<(2.5/2.75)){return(7.5625*(c-=(2.25/2.75))*c+0.9375);}else{return(7.5625*(c-=(2.625/2.75))*c+0.984375);}}}};},bounceBoth:function(){return this.combine(this.bounceIn(),this.bounceOut());},elasticIn:function(c,d){return a(this.elasticOut(c,d));},elasticOut:function(c,d){return function(e){if(e==0||e==1){return e;}if(!d){d=0.3;}if(!c||c<1){c=1;var f=d/4;}else{var f=d/(2*Math.PI)*Math.asin(1/c);}return c*Math.pow(2,-10*e)*Math.sin((e-f)*(2*Math.PI)/d)+1;};},elasticBoth:function(c,d){d=d||0.45;return this.combine(this.elasticIn(c,d),this.elasticOut(c,d));},combine:function(d,c){return function(e){if(e<0.5){return d(e*2)/2;}else{return c((e-0.5)*2)/2+0.5;}};}};}});(window.gloader||glow).module({name:"glow.anim",library:["glow","1.5.1"],depends:[["glow","1.5.1","glow.tweens","glow.events","glow.dom"]],builder:function(glow){var $=glow.dom.get,manager,events=glow.events,dom=glow.dom,get=dom.get,hasUnits=/width|height|top$|bottom$|left$|right$|spacing$|indent$|font-size/,noNegatives=/width|height|padding|opacity/,usesYAxis=/height|top/,getUnit=/(\D+)$/,testElement=dom.create('
    ');(function(){var queue=[],queueLen=0,intervalTime=1,interval;manager={addToQueue:function(anim){queue[queueLen++]=anim;anim._playing=true;anim._timeAnchor=anim._timeAnchor||new Date().valueOf();if(!interval){this.startInterval();}},removeFromQueue:function(anim){for(var i=0;ianim.duration){anim.position=anim.duration;}}else{anim.position++;}anim.value=anim.tween(anim.position/anim.duration);events.fire(anim,"frame");}}};})();function convertCssUnit(element,fromValue,toUnit,axis){var elmStyle=testElement[0].style,axisProp=(axis=="x")?"width":"height",startPixelValue,toUnitPixelValue;elmStyle.margin=elmStyle.padding=elmStyle.border="0";startPixelValue=testElement.css(axisProp,fromValue).insertAfter(element)[axisProp]();toUnitPixelValue=testElement.css(axisProp,10+toUnit)[axisProp]()/10;testElement.remove();return startPixelValue/toUnitPixelValue;}function keepWithinRange(num,start,end){if(start!==undefined&&numend){return end;}return num;}function buildAnimFunction(element,spec){var cssProp,r=["a=(function(){"],rLen=1,fromUnit,unitDefault=[0,"px"],to,from,unit,a;for(cssProp in spec){r[rLen++]='element.css("'+cssProp+'", ';if(typeof spec[cssProp]!="object"){to=spec[cssProp];}else{to=spec[cssProp].to;}if((from=spec[cssProp].from)===undefined){if(cssProp=="font-size"||cssProp=="background-position"){throw new Error("From value must be set for "+cssProp);}from=element.css(cssProp);}if(hasUnits.test(cssProp)){unit=(getUnit.exec(to)||unitDefault)[1];fromUnit=(getUnit.exec(from)||unitDefault)[1];from=parseFloat(from)||0;to=parseFloat(to)||0;if(from&&unit!=fromUnit){if(cssProp=="font-size"){throw new Error("Units must be the same for font-size");}from=convertCssUnit(element,from+fromUnit,unit,usesYAxis.test(cssProp)?"y":"x");}if(noNegatives.test(cssProp)){r[rLen++]="keepWithinRange(("+(to-from)+" * this.value) + "+from+', 0) + "'+unit+'"';}else{r[rLen++]="("+(to-from)+" * this.value) + "+from+' + "'+unit+'"';}}else{if(!(isNaN(from)||isNaN(to))){from=Number(from);to=Number(to);r[rLen++]="("+(to-from)+" * this.value) + "+from;}else{if(cssProp.indexOf("color")!=-1){to=dom.parseCssColor(to);if(!glow.lang.hasOwnProperty(from,"r")){from=dom.parseCssColor(from);}r[rLen++]='"rgb(" + keepWithinRange(Math.round('+(to.r-from.r)+" * this.value + "+from.r+'), 0, 255) + "," + keepWithinRange(Math.round('+(to.g-from.g)+" * this.value + "+from.g+'), 0, 255) + "," + keepWithinRange(Math.round('+(to.b-from.b)+" * this.value + "+from.b+'), 0, 255) + ")"';}else{if(cssProp=="background-position"){var vals={},fromTo=["from","to"],unit=(getUnit.exec(from)||unitDefault)[1];vals.fromOrig=from.toString().split(/\s/);vals.toOrig=to.toString().split(/\s/);if(vals.fromOrig[1]===undefined){vals.fromOrig[1]="50%";}if(vals.toOrig[1]===undefined){vals.toOrig[1]="50%";}for(var i=0;i<2;i++){vals[fromTo[i]+"X"]=parseFloat(vals[fromTo[i]+"Orig"][0]);vals[fromTo[i]+"Y"]=parseFloat(vals[fromTo[i]+"Orig"][1]);vals[fromTo[i]+"XUnit"]=(getUnit.exec(vals[fromTo[i]+"Orig"][0])||unitDefault)[1];vals[fromTo[i]+"YUnit"]=(getUnit.exec(vals[fromTo[i]+"Orig"][1])||unitDefault)[1];}if((vals.fromXUnit!==vals.toXUnit)||(vals.fromYUnit!==vals.toYUnit)){throw new Error("Mismatched axis units cannot be used for "+cssProp);}r[rLen++]="("+(vals.toX-vals.fromX)+" * this.value + "+vals.fromX+') + "'+vals.fromXUnit+' " + ('+(vals.toY-vals.fromY)+" * this.value + "+vals.fromY+') + "'+vals.fromYUnit+'"';}}}}r[rLen++]=");";}r[rLen++]="})";return eval(r.join(""));}var r={};r.css=function(element,duration,spec,opts){element=get(element);var anim=new r.Animation(duration,opts);if(element[0]){events.addListener(anim,"frame",buildAnimFunction(element,spec));}return anim;};slideElement=function slideElement(element,duration,action,opts){duration=duration||0.5;element=$(element);opts=glow.lang.apply({tween:glow.tweens.easeBoth(),onStart:function(){},onComplete:function(){}},opts);var i=0,thatlength=element.length,completeHeight,fromHeight,channels=[],timeline;for(;i0)){element[i].style.overflow="hidden";if(glow.env.ie<8){element[i].style.zoom=1;}completeHeight=0;fromHeight=element.slice(i,i+1).height();}else{if(action=="down"||(action="toggle"&&element.slice(i,i+1).height()==0)){fromHeight=element.slice(i,i+1).height();element[i].style.height="auto";completeHeight=element.slice(i,i+1).height();element[i].style.height=fromHeight+"px";}}channels[i]=[glow.anim.css(element[i],duration,{height:{from:fromHeight,to:completeHeight}},{tween:opts.tween})];}timeline=new glow.anim.Timeline(channels);events.addListener(timeline,"complete",function(){element.each(function(){if(this.style.height!="0px"){this.style.height="auto";}});});events.addListener(timeline,"start",opts.onStart);events.addListener(timeline,"complete",opts.onComplete);return timeline.start();};r.slideDown=function(element,duration,opts){return slideElement(element,duration,"down",opts);};r.slideUp=function(element,duration,opts){return slideElement(element,duration,"up",opts);};r.slideToggle=function(element,duration,opts){return slideElement(element,duration,"toggle",opts);};r.fadeOut=function(element,duration,opts){return r.fadeTo(element,0,duration,opts);};r.fadeIn=function(element,duration,opts){r.fadeTo(element,1,duration,opts);};r.fadeTo=function(element,opacity,duration,opts){duration=duration||0.5;element=$(element);opts=glow.lang.apply({tween:glow.tweens.easeBoth(),onStart:function(){},onComplete:function(){}},opts);var i=0,thatlength=element.length,channels=[],timeline;for(;ianim.duration){anim.position=anim.duration;}anim.value=anim.tween(anim.position/anim.duration);events.fire(anim,"frame");if(anim.position==anim.duration){this._advanceChannel(i);}}},start:function(){var e=events.fire(this,"start");if(e.defaultPrevented()){return this;}var i,iLen,j,jLen,anim;this._playing=true;for(i=0,iLen=this._channels.length;ithis.duration){if(this.loop){pos=pos%this.duration;}else{pos=this.duration;}}this._controlAnim.goTo(pos);for(i=0;ipos){this._channelPos[i]=j;anim.goTo(pos-runningDuration);break;}anim.goTo(anim.duration);runningDuration+=anim.duration;}}for(k=channelLen;k>j;k--){anim.goTo(0);}}}else{for(i=0;i=this._fields[this._fieldCur]._tests.length){if(!this._nextField()){return false;}}var d=this._fields[this._fieldCur]._tests[this._testCur];var c;if(d.opts.field){c=this.formNode.val()[d.opts.field]||"";d.isConditional=true;}else{c=this.formNode.val()[this._fields[this._fieldCur].name]||"";}if(!c.join){c=[c];}var e=function(f){return function(){f._onTestResult.apply(f,arguments);};}(this);d.opts.on=d.opts.on||"submit";if(this._result.eventName&&(" "+d.opts.on+" ").indexOf(" "+this._result.eventName+" ")!=-1){if(this._fieldName&&this._fieldName!=d.name){this._nextTest();return;}if(typeof b.forms.tests[d.type]!="function"){throw"Unimplemented test: no test exists of type '"+d.type+"'.";}b.forms.tests[d.type](c,d.opts,e,this.formNode.val());}else{this._nextTest();}};b.forms.Form.prototype._nextField=function(){this._fieldCur++;this._testCur=0;if(this._fieldCur>=this._fields.length){this._fieldCur=0;b.events.fire(this,"validate",this._result);if(this.eventName=="submit"&&this._result&&!this._result.defaultPrevented()){this.formNode[0].submit();}return false;}return true;};b.forms.Form.prototype._onTestResult=function(c,d){if(typeof c=="boolean"){c=(c)?b.forms.PASS:b.forms.FAIL;}if(this._fields[this._fieldCur]._tests[this._testCur].isConditional&&c===b.forms.FAIL){c=b.forms.SKIP;}this._result.fields.push({name:this._fields[this._fieldCur].name,result:c,message:d});if(c!==b.forms.PASS){if(c===b.forms.FAIL){this._result.errorCount++;}this._testCur=this._fields[this._fieldCur]._tests.length;}this._nextTest();};b.forms.Form.prototype.addTests=function(m){var l={name:m,_tests:[]};var d=function(i){return function(){i.validate.apply(i,["change",m]);};}(this);var h=function(i){return function(){i.validate.apply(i,["click",m]);};}(this);var j=function(i){return function(){i.validate.apply(i,["idle",m]);};}(this);for(var f=1;f1)?arguments[f][1]:{};l._tests.push({name:m,type:e,opts:k});if(!d.added&&(" "+k.on+" ").indexOf(" change ")!=-1){var g=this.formNode.get("*").each(function(n){if(this.name==m){b.events.addListener(this,"change",d);d.added=true;}});}if(!h.added&&(" "+k.on+" ").indexOf(" click ")!=-1){var g=this.formNode.get("*").each(function(n){if(this.name==m){b.events.addListener(this,"click",h);h.added=true;}});}if(!j.added&&(" "+k.on+" ").indexOf(" idle ")!=-1){var c=(typeof k.delay!="undefined")?parseInt(k.delay):1000;var g=this.formNode.get("*").each(function(n){if(this.name==m){b.events.addListener(this,"keyup",function(i){return function(){window.clearTimeout(this.idleTimeoutID);if(this.value){this.idleTimeoutID=window.setTimeout(j,i);}};}(c));b.events.addListener(this,"blur",function(){window.clearTimeout(this.idleTimeoutID);});j.added=true;}});}}this._fields.push(l);return this;};b.forms.ValidateResult=function(c){b.events.Event.apply(this);this.eventName=c;this.errorCount=0;this.value=undefined;this.fields=[];};b.lang.extend(b.forms.ValidateResult,b.events.Event);b.forms.PASS=1;b.forms.FAIL=0;b.forms.SKIP=-1;b.forms.tests={required:function(d,g,h){var f=g.message||"Value is required";for(var e=0,c=d.length;eNumber(g.arg)){h(b.forms.FAIL,f);return;}}h(b.forms.PASS,f);},range:function(e,j,k){var h=j.arg.split("..");if(typeof h[0]=="undefined"||typeof h[1]=="undefined"){throw"Range test requires a parameter like 0..10.";}var g=j.message||"The value must be "+h[0]+" or greater, and less than "+h[1]+".";h[0]*=1;h[1]*=1;if(h[0]>h[1]){var d=h[0];h[0]=h[1];h[1]=d;}for(var f=0,c=e.length;fh[1]){k(b.forms.FAIL,g);return;}}k(b.forms.PASS,g);},minCount:function(c,g,h){var f=g.message||"Must be have at least "+g.arg+" values.";var e=0;for(var d=0;dg.arg){h(b.forms.FAIL,f);return;}h(b.forms.PASS,f);},count:function(c,g,h){var f=g.message||"Must have "+g.arg+" values.";var e=0;for(var d=0;dg.arg){h(b.forms.FAIL,f);return;}}h(b.forms.PASS,f);},isEmail:function(d,g,h){var f=g.message||"Must be a valid email address.";for(var e=0,c=d.length;e').appendTo(document.body);}c[0].value++;}function e(j){var h=j.fields,m,l,n,k,g;for(k=0,g=h.length;k')));}n.text(h[k].message);m.addClass("glow-invalid");}}}}function d(k){var n=k.fields,q,h,j,m,g,l,o;k.form.formNode.get("div.glow-errorSummary").remove();h=b.dom.create('
      ');j=h.get("ul");for(l=0,o=n.length;l").text(g+": "+n[l].message));}}k.form.formNode.prepend(h.css("opacity","0"));b.anim.css(h,"0.5",{opacity:{from:0,to:1}},{tween:b.tweens.easeOut()}).start();try{h[0].focus();}catch(p){}f();}return function(g){if(g.eventName=="submit"){if(!g.errorCount){g.form.formNode.get("div.glow-errorSummary").remove();return;}d(g);}setTimeout(function(){e(g);},0);return false;};}());}});(window.gloader||glow).module({name:"glow.embed",library:["glow","1.5.1"],depends:[["glow","1.5.1","glow.dom","glow.data"]],builder:function(l){function b(o){var n="";for(var p in o){if(p.toLowerCase()=="flashvars"&&typeof o[p]=="object"){n+=' FlashVars="'+l.data.encodeUrl(o[p])+'"';}else{n+=" "+p+'="'+o[p]+'"';}}return n;}function m(n){var p="",o,q;for(o in n){if(o.toLowerCase()=="flashvars"&&typeof n[o]=="object"){q=l.data.encodeUrl(n[o]);}else{q=n[o];}p+='\n';}return p;}function g(o,n){o=o||{};for(var p in n){if(typeof o[p]=="undefined"){o[p]=n[p];}else{if(typeof n[p]=="object"){o[p]=g(o[p],n[p]);}}}return o;}function c(){var n=(navigator.platform||navigator.userAgent);return n.match(/win/i)?"win":n.match(/mac/i)?"mac":"other";}function j(n){var p=/^WIN (\d+),(\d+),(\d+),\d+$/;var o=n.GetVariable("$version");if($match=p.exec(o)){return{major:parseInt($match[1]),minor:parseInt($match[2]),release:parseInt($match[3]),actual:o};}else{}}function i(){var o,q,r={major:0,minor:0,release:0},n=r;if(l.env.ie){try{q=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");n=j(q);}catch(s){try{q=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");try{q.AllowScriptAccess="always";n=j(q);}catch(s){n={major:6,minor:0,release:29};}}catch(s){}}}else{var p=/^Shockwave Flash\s*(\d+)\.(\d+)\s*\w(\d+)$/;if((q=navigator.plugins["Shockwave Flash"])&&(o=p.exec(q.description))){n={major:parseInt(o[1]),minor:parseInt(o[2]),release:parseInt(o[3]),actual:q.description};}}n.toString=function(){return this.major?[this.major,this.minor,this.release].join("."):"No Flash player installed, or version is pre 6.0.0. Visit BBC Webwise for full instructions";};return n;}var k=i();function e(p){if(typeof p!="object"){var o=String(p).match(/^(\d+)(?:\.(\d+)(?:\.(\d+))?)?$/);if(!o){throw new Error('glow.embed._meetsVersionRequirements: invalid format for version string, require "n.n.n" or "n.n" or simply "n" where n is a numeric value');}p={major:parseInt(o[1],10),minor:parseInt(o[2]||0,10),release:parseInt(o[3]||0,10)};}var n=k,q=p;return(n.major>q.major||(n.major==q.major&&n.minor>q.minor)||(n.major==q.major&&n.minor==q.minor&&n.release>=q.release));}var d=l.env.ie?h:f;function f(p,n,o){return'";}function h(p,n,o){return''+m(o)+"";}var a={};a.Flash=function(t,n,s,r){r=g(r,{width:"100%",height:"100%",params:{allowscriptaccess:"always",allowfullscreen:"true",quality:"high"},attributes:{},message:"This content requires Flash Player version "+s+" (installed version: "+k+")"});n=l.dom.get(n);if(!n.length){throw new Error("glow.embed.Flash unable to locate container");}this.container=n;this.movie=null;this._displayErrorMessage=typeof r.message=="function"?r.message:function(){return r.message;};this.isSupported;if(this.isSupported=e(s)){var o=r.attributes,q=["id","width","height"],p=q.length;while(p--){if(r[q[p]]){o[q[p]]=r[q[p]];}}if(r.className){o["class"]=r.className;}this._embed_tag=d(t,o,r.params);}};a.Flash.version=function(){return k;};a.Flash.prototype.embed=function(){var o=this.container[0];if(this.isSupported){o.innerHTML=this._embed_tag;this.movie=o.firstChild;}else{var n=this._displayErrorMessage();if(n){o.innerHTML=n;}}return this;};l.embed=a;}});(window.gloader||glow).module({name:"glow.dragdrop",library:["glow","1.5.1"],depends:[["glow","1.5.1","glow.tweens","glow.events","glow.dom","glow.anim"]],builder:function(j){var c=j.events,k=c.addListener,t=c.fire,g=c.removeListener,q=j.dom,h=q.get,m=q.create;var n={},u=1000,a=(document.compatMode=="CSS1Compat"&&j.env.ie>=5)?true:false,v=(document.compatMode!="CSS1Compat"&&j.env.ie>=5)?true:false,d=j.env.ie>=5;function b(i,w){var x=i.prototype[w];var r="cached_"+w;i.prototype[w]=function(){if(r in this){return this[r];}return this[r]=x.apply(this,arguments);};}function f(i,r){var x=i.prototype[r];var w="cached_"+r;i.prototype[r]=function(y){if(!this[w]){this[w]={};}if(y in this[w]){return this[w][y];}return this[w][y]=x.apply(this,arguments);};}function s(y,x){for(var w=0,r=x.length;w=i[0]&&w<=i[1]&&x<=i[2]&&w>=i[3];},containsPoint:function(r){var i=this.el.offset();return r.x>=i.left&&r.y>=i.top&&r.x<=i.left+this.borderWidth()&&r.y<=i.top+this.borderHeight();},positionedAncestorBox:function(){var i=this.el.parent(),r;while(i[0]){r=i.css("position")||"static";if(r=="relative"||r=="absolute"||r=="fixed"){return new o(i);}i=i.parent();}return null;}});function e(r){var i=r[0].tagName.toLowerCase()=="li"?"li":"div";var w=m("<"+i+">");if(i=="li"){w.css("list-style-type","none");}return w;}n.Draggable=function(y,z){this.element=h(y);this._opts=z=j.lang.apply({dragPrevention:["input","textarea","button","select","option","a"],placeholder:"spacer",placeholderClass:"glow-dragdrop-placeholder",step:{x:1,y:1}},z||{});if(typeof z.step=="number"){z.step={x:z.step,y:z.step};}else{z.step.x=z.step.x||1;z.step.y=z.step.y||1;}this._preventDrag=[];for(var w=0,r=z.dragPrevention.length;wx[1]?x[1]:A;}if(r!="x"){y=yx[2]?x[2]:y;}}i[0].style.left=A+"px";i[0].style.top=y+"px";if(this.dropTargets){this._mousePos={x:z.pageX,y:z.pageY};}if(d&&z.nativeEvent.button==0){this._releaseElement(z);return false;}return false;},_testForDropTargets:function(F){if(!this._lock){this._lock=0;}if(F){this._lock--;}else{if(this.lock){return;}}if(this._dragging!=1){return;}var x=this.activeTarget,w,P=this.dropTargets,Q,A,D=this._box,I=this._mousePos;D.resetPosition();var B=0;for(var J=0,H=P.length;JB){B=K;w=Q;}}}}this.activeTarget=w;if(w!==x){if(w){var G=new c.Event();G.draggable=this;t(w,"enter",G);var r=new c.Event();r.dropTarget=w;t(this,"enter",r);}if(x){var C=new c.Event();C.draggable=this;t(x,"leave",C);var E=new c.Event();E.dropTarget=x;t(this,"leave",E);}}if(w&&w._opts.dropIndicator!="none"){var N,L=w._childBoxes,y=w._children;D.resetPosition();var O=w._box.innerTopPos();var R=I.y-D.offsetParentPageTop();var M=0;for(var J=0,H=L.length;J1){throw"more than one element passed into DropTarget constructor";}this._id=++l;this._opts=r=j.lang.apply({dropIndicator:"none",dropIndicatorClass:"glow-dragdrop-dropindicator",tolerance:"intersect"},r||{});if(r.onActive){k(this,"active",r.onActive);}if(r.onInactive){k(this,"inactive",r.onInactive);}if(r.onEnter){k(this,"enter",r.onEnter);}if(r.onLeave){k(this,"leave",r.onLeave);}if(r.onDrop){k(this,"drop",r.onDrop);}k(this,"active",this._onActive);k(this,"inactive",this._onInactive);return this;};n.DropTarget.prototype={setLogicalBottom:function(i){this._logicalBottom=i;},_onActive:function(x){var r=x.draggable;this._box=new o(this.element);if(this._logicalBottom){this._box.setLogicalBottom(this._logicalBottom);}if(this._opts.dropIndicator=="none"){return;}this._onEnterListener=k(this,"enter",this._onEnter);this._onLeaveListener=k(this,"leave",this._onLeave);this._dropIndicator=e(r.element);if(this._opts.dropIndicatorClass){this._dropIndicator.addClass(this._opts.dropIndicatorClass);}r._box.sizePlaceholder(this._dropIndicator,"relative",0,0);var w=this._children=h(this.element.children()).filter(function(){var y=h(this);return(!x.draggable._placeholder||!y.eq(x.draggable._placeholder))&&(!this._dropIndicator||!y.eq(this._dropIndicator));});var i=this._childBoxes=[];w.each(function(y){i[y]=new o(h(w[y]));});},_onInactive:function(i){g(this._onEnterListener);g(this._onLeaveListener);delete this._box;if(this._opts.dropIndicator=="none"){return;}if(!i.droppedOnThis&&this._dropIndicator){this._dropIndicator.remove();delete this._dropIndicator;}delete this._childBoxes;delete this._children;},_onEnter:function(){this._dropIndicatorAt=-1;},_onLeave:function(){this._dropIndicator.remove();},moveToPosition:function(i){var y=this._dropIndicator,r=new o(y);var x=parseInt(y.css("margin-left"))||0,w=parseInt(y.css("margin-top"))||0;i._startOffset={x:r.offsetLeft()-x,y:r.offsetTop()-w};i._dropIndicator=y;delete this._dropIndicator;}};j.dragdrop=n;}}); -/*@end @*/ diff --git a/horde/js/glow/1.5.1/widgets/images/carousel/darkdots.png b/horde/js/glow/1.5.1/widgets/images/carousel/darkdots.png deleted file mode 100644 index d68e5c1495eed0c8527301508fb3d9f14c6bf654..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 451 zcmV;!0X+VRP)-r4h=>{LrY3W2?+@k5)vF78zCScLP0(yB_kIX7Bn+4I5#y53JM?} zAAf#)pPig)X=oJ{6-r4-Ra8?dDk(reKB}muH8eDJb8?lFk&1|i?@;kc0003VNkl9-I~882~zd3%s4hU<0mYQV$4AJ)q=-xkHs6=^Y?eInc`7NY6Gc zW8;{z1`CMu)P0eYZQoaFY`O88m)60E7xc=7cT!X;>;K_!JvMi^YMmd%z7FPjy!q#k z2;upOaWaQY<2T+oQz5-C%gTo4sJd@C-?)HGK~4!493*pF{$!%1QCYZmMsX*~JwMop zyraVEm7H0gl)qSHnL;IT#Y)Viji|jvo`#r7zqrYV%8`|Okcb?!_E@Q~(owHmlKShF tot`$K_0A&hB^aHVeF*EwFPP11^8ucz7OlcFh86$-002ovPDHLkV1mKHySD%U diff --git a/horde/js/glow/1.5.1/widgets/images/carousel/darkdotsv.png b/horde/js/glow/1.5.1/widgets/images/carousel/darkdotsv.png deleted file mode 100644 index 89d7c652a6b701a3749d3fbab2489027b22dd695..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 391 zcmV;20eJq2P);KK|VM)H6b7$FD@)JGcgkq5*HU14h{_!6%|TJ zNmW!+Dk>>JKR&9crhk5X3knK~h=(;aG-_#Rm6MTmb8;XbAD^9^Y>BX*0002uNklY11zh5I4!JW1 z-_V|85RSqAV5JBpCnsMbeP&sHFe}waQSK?NEK#e8;%&^|lvZhj;a1Jmi)lJJ1!V*8 z=?7hvLBmrLbft!-mYq^VZ&nU!L>emp5H71(>Fl@4A!xN lw9wJEUaxc?W!{q$;00te6xw7(j)edK002ovPDHLkV1jugjW+-Q diff --git a/horde/js/glow/1.5.1/widgets/images/carousel/darkh.png b/horde/js/glow/1.5.1/widgets/images/carousel/darkh.png deleted file mode 100644 index 51677f014fae657989afbf0c589af2c9939f319d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1307 zcmeAS@N?(olHy`uVBq!ia0vp^Z9puZYfs7aS6*rKRoS;$~%S zV`OBcr>F1W;AmuITv=J^;prU^5L8uF6%ZJtrKM$PWE316=IQC>kfZ*!t>YADw|G;2vZEbU);Tq}zfx*7M{#x2Vv$fULHQd}itZZzwwROzRE!M1A zqoJX3|Ni}ukdUC@FlQGxOH0dLyLPQ!y*em3bmz{UJ9g{{3JLS|_4D-fEGQ^gxpJkw zy`z(pb8T(y)TvX=%q-M3G&D6emo8oEVDGqM#fr6S*RET)&dtru+zbdUHf`E8fByVc zt5&(Vy7Ke$`}p`oMMV`C7ptkNYHNdCq@}H^r*Caz%gM><X!rW=AWDExMKm|XI1^(#^o06pY7x=lwXJ@hxDww{y%kzTzz!vrU;+V zV>>z00!_GEg%e%e!{lO4obvec-(Xdd?Bs(Bq(qJ`Sg~sIs#Sem2QNyRt~gin zL1KkQ&eN_()6(}y?=R0hwm;bI&iB<FCan~^uF5B(IMm;bhgZQA3GrxrnOw`1LIX((UO;W%rbD0t#G&}wLHxH%$abUHYr?-$v*89MXb!jQHjn?@6h=@Pz{ix(yy$;tr<=nA{KNlj@oVp?}!FZ{sMuej$f98eVaP$cO#b8&?e zyB1G^OkDDl2}WPVoJBj&*LJhi+MD?jAjOkq-N-@O!2!e zl6vNRdV;mq2HEmcEB2l9Fx9Loo)nR9ae8x`(=}oB_#HeY3l?2zc`>oTLNo2 PK)J}%)z4*}Q$iB}Y+H6* diff --git a/horde/js/glow/1.5.1/widgets/images/carousel/darkv.png b/horde/js/glow/1.5.1/widgets/images/carousel/darkv.png deleted file mode 100644 index 8bca958e0e50e7957320ed94a4d91ab201d75562..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1202 zcmeAS@N?(olHy`uVBq!ia0vp^LO|Tc!3-pyKi4u~U|?Jl;1lBN;pr6|5_bOlc}pv+ zz`($SgoNeGmv7jxAv-5;?%a7ZX3Q+Fs9e2z&Gzj(7A;ylY4VhP`}Vc8wr$z6W%usg z2M!#VJbChzDN{CY-t6n^tE#GM@8D={ZR6wXZ)R?;uCDIl>ZYfs7aS6*rKRoS;$~%S zV`OBcr>F1W;AmuITv=J^;prU^5L8uF6%ZJtrKM$PWE316=IQC>kfZ*!t>YADw|G;2vZEbU);Tq}zfx*7M{#x2Vv$fULHQd}itZZzwwROzRE!M1A zqoJX3|Ni}ukdUC@FlQGxOH0dLyLPQ!y*em3bmz{UJ9g{{3JLS|_4D-fEGQ^gxpJkw zy`z(pb8T(y)TvX=%q-M3G&D6emo8oEVDGqM#fr6S*RET)&dtru+zbdUHf`E8fByVc zt5&(Vy7Ke$`}p`oMMV`C7ptkNYHNdCq@}H^r*Caz%gM><gwu>-I(w`c8$Zg`Dx}mw;z9U*P!M@xAC6-{WrJ&KE<@!v|ayh&feOe7q6_e zF0Zb>ea}8tWJ_*(`tQEC)lubJ-`=f#_osQE+O6w`#cct{O`U%8=U&{eDq|AM9vgKz ztNu^ym!Ax?m5(iW=AQpqJa}b@*=$(}37I`wE8d^HX}DJ)P}F7$F4d~|78O6k5;VI zv+WOi%Rh&wE=y{Cy=qm6+QOi|_0<;xh1o>g3+l-=?maR>N>V-|0|4UxLh$hE$>GFZM#QZ`J;@ zWV=}Wn7orwWA$oXkDw)tr!q4icp2PjV$0zA4Xo zQEaQ*G|fwTQSRl37X10}{Jt-h8jKQFVdQ I&MBb@0G1+1tN;K2 diff --git a/horde/js/glow/1.5.1/widgets/images/carousel/lightdots.png b/horde/js/glow/1.5.1/widgets/images/carousel/lightdots.png deleted file mode 100644 index c572a6ed2762d4f1a6382b48dc0e6251047254bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 451 zcmV;!0X+VRP)C4Q{$H>a`_4VK2 z;DCXHU}9sHmY3@4>b17Es;jKk)z-hj!BtmT%FD}&jE!n+ZgqEf6Mcv`0003VNkl9-I~882~zd3%s4hU<0mYQV$4AJ)q=-xkHs6=^Y?eInc`7NY6Gc zW8;{z1`CMu)P0eYZQoaFY`O88m)60E7xc=7cT!X;>;K_!JvMi^YMmd%z7FPjy!q#k z2;upOaWaQY<2T+oQz5-C%gTo4sJd@C-?)HGK~4!493*pF{$!%1QCYZmMsX*~JwMop zyraVEm7H0gl)qSHnL;IT#Y)Viji|jvo`#r7zqrYV%8`|Okcb?!_E@Q~(owHmlKShF tot`$K_0A&hB^aHVeF*EwFPP11^8ucz7OlcFh86$-002ovPDHLkV1hdS>|X!? diff --git a/horde/js/glow/1.5.1/widgets/images/carousel/lightdotsv.png b/horde/js/glow/1.5.1/widgets/images/carousel/lightdotsv.png deleted file mode 100644 index b81a5348230c7979f50ed53b33b957ef2b96e95a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 391 zcmV;20eJq2P)gu(& zwyLYF)z#L&z`<2lS%86q^!4?1cX-On%axXwYHV(bjE&#m;9z27kfh1r0002uNklY11zh5I4!JW1 z-_V|85RSqAV5JBpCnsMbeP&sHFe}waQSK?NEK#e8;%&^|lvZhj;a1Jmi)lJJ1!V*8 z=?7hvLBmrLbft!-mYq^VZ&nU!L>emp5H71(>Fl@4A!xN lw9wJEUaxc?W!{q$;00te6xw7(j)edK002ovPDHLkV1gtZzoP&E diff --git a/horde/js/glow/1.5.1/widgets/images/carousel/lighth.png b/horde/js/glow/1.5.1/widgets/images/carousel/lighth.png deleted file mode 100644 index 3d3dd792093cffa73239946c2fe81b61b90e8400..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 830 zcmeAS@N?(olHy`uVBq!ia0vp^Z9pueZ`n-@ZL@;>7v$=ij}1ck<-P z`}gl}-n{we&!6AFfB*93%gdK9KYjXi@#4h`7cT7Dwd>BEI~z7^c>VhI(W6Ii+_*7w z=FF#0pDtOlWc&8*1qB70Hf_3e>C&lFr*`bv@%i)T4*&H#OOzooPrNV%5;`2_<7sQ?&`&k4(CU|_uH>Eaktack|h<5iCg zMA{O~d32pUz@Nn2zX-#=s(HMe}}@vvLGd5aC6NVg`I zNS6d}G(WaBU#Z)8zHMJ*oWR|)%evPt+q_%j!$0Q5X7jDj)$Dt? zVftyEXuaICFMiHt4LttdP)=Up=p0E&FGbE*!J!^azt%|358UKYzRlxU?a3dmDJc#M z`&T*1cNzNp(!3aMlwz|(g{y~0!@wa>Qs>xZt3WBElNQQG#v5dmrzOW27cH8yUP9c( zjVm=G;(%3{&k1q~8@NtC%@1mwcps6;FjK_nexh!sJAIn!dx}jF- z_K^wwN4vGXcItoPoY;RfTF>igK9JS(XNpF6kFb*R;i-JfpJ=?>S(2jaUKnrqPe`<< zSV`GAMD`aOyGYVe3neWH`_4BPj;ydSINTU%v?p1qe{-Tyh7j+)b~U!L-zRtECnZ>m+~^B7l(O%Tn)CI~^{~%r?q{#9zHe;spkdLQ1}DRR0-kN+ z{nnEMvsYhSV%%jb>})-6{=$Z2g9}_I1%AIiRyQT*zliEY`SRArg|hrkHW{okxr(=< c6#p?YEI6U9p1t-BFugE%y85}Sb4q9e0RMg7;{X5v diff --git a/horde/js/glow/1.5.1/widgets/images/carousel/lightv.png b/horde/js/glow/1.5.1/widgets/images/carousel/lightv.png deleted file mode 100644 index e97e10ca75c6ffed531e0333ef48bc7d7bc63e79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 725 zcmeAS@N?(olHy`uVBq!ia0vp^LO|Tc!3-pyKi4teZ`n-@ZL@;>7v$=ij}1ck<-P z`}gl}-n{we&!6AFfB*93%gdK9KYjXi@#4h`7cT7Dwd>BEI~z7^c>VhI(W6Ii+_*7w z=FF#0pDtOlWc&8*1qB70Hf_3e>C&lFr*`bv@%i)T4*&H#OOzooPrNV%5;`2_<7sQ?&`&k4(CU|`Jfba4!+xb^l@dj25; z0k#LqoSY>WZ8arxoW3v6Hr(;>-+%wCTTkX_q;r1UDD?ig`=-?U)eYT2$L2F@Gfi2_ zGVkt7W;3a2sx1XF>RoM)=U+3-%Z+{ecAoqkGo6Pno<-Hc3pI9rm$uH-wanRefj6u_ux&{ zZ`+^!3p~B=j-}lmi<^EYXPZ6dVx9b2YiF)ok#qLM(;fV?t}oO-JO9kGr&*g>Iqu*6 z#Ps4?%0Z?{tR_FxZeFNY@m1;0^EK$sv`Lmzn0fxSi`Nv!ldFwR95r~I;?U&52IP7s zIs{4BOqw9spg(hhS->P)B$RNGmT+xpX=n+TF!N=hgJxnl1mPkQL_|vj4iQ8U5i}$XAv^?w2*Dxy z1H=|-aCxf;(UwC55wClK%Oy#C_uPAZHhgE(^ZVeu#~U+E^H&0a#XS;p1XJv_!uSsb z?n#`3@vOk{h`_59ZVk^0oZ1Y3AaQ|(56=o**#xgsI1bMW6m5Wem_ZEhBqoDV!HSO>#hzx0-IZdz4CTv}M2}k?32q z*}UM`PcsjATHui9$DK~+B#~Hu5rK!pEANfQ((CtOo4cO?RCiTg?=|b%Ui%O zj5FQ>nzsB=t36&L;M$-E77FBf3&8DKfrmocCl+q?Z8OLF=dD)j910AAS1Oeo1bmhE zRq5wl0yhk|{1-eJT>2`PZ*>GbhI-G77hrH**GpJ977q#VE`^(hhmzt8g~A;Io+NSL z@hlpq0Ngkdo@Q`yJVe91g^+Nkif@}JEKrzD3Ri)L#1z3Z6s{8QfC38O)Q+#|Z@l;1eSIVaO=3#R=ab@rdq*A*(>fB7B#^?Z>mYp@0Rr zSJqA#vI}JU!1qZeW$uHaqCgG@w;vDj{1jmDDHVHQs4TEe;#0Nb8`JG~5VD3cEC2ui M07*qoM6N<$f@h=q!2kdN diff --git a/horde/js/glow/1.5.1/widgets/images/darkpanel/al.png b/horde/js/glow/1.5.1/widgets/images/darkpanel/al.png deleted file mode 100644 index b2356ad429796459c450a38d7c1992814090fc5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 639 zcmV-_0)YLAP)x|D)Qp_JG(YK*4Z(<-G1r4*!eDS|juL?}gw z5U>vEP*fC`2s$X};F7@(krD(cLJ&dZ`V&h3kVoVD-|xZ=-zmraKSS=_yBBg@x02nl zy|$8wyXalekVf<#O#}&P5=3y2dxEfsM7pAmpNM7|E?FSnBHs`#20$u@_<&r$Agwag ztPEljxqd;~$|BHz5T6CXLmpgn|h#@UvXJ>ad9#0hIrW_=U94%sNdwZ^@H-5$<&_Be87LmerKP2|waJ`a(15cMMDA%gL`HNcQO z0r792f*}nk$3x7WTaOYG`inVWApBldzVzzQJ_j+YLJ?a?31*9c5Z^oINkAB7d5DR5 z?BQ6V7HyaizD_HMiTPnd_#Rn7%=3~Z1%x^`30Yv$=8t^s1|-0f=eFP>(*58eQj6`i Ze*g!E?BPnZ-Ln7y002ovPDHLkV1m~a8&d!P diff --git a/horde/js/glow/1.5.1/widgets/images/darkpanel/ar.png b/horde/js/glow/1.5.1/widgets/images/darkpanel/ar.png deleted file mode 100644 index 9be4571e81367e5e2c66a7634ad387a628aae266..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 585 zcmV-P0=E5$P)y4JL_&Lhg<_;1aaf(Acw>$tarqCzwjY3 zs)#X`=m0_vIRU~5(ylo%is0361Zh>Ap5b72Cyd!ZGC{!jvP{WrX)9&TZP(|$a`ml4J&iUaJ%cR(ow2{LeiU3hl` zTspuZ$IIbKLNwI~4baECphQdHROB!4P6k|k02dxrfm2}(yaO`0m}2pHPcR_F!0W)X z+0!#)V(LAgzdIBR2sVc&_^<>%ZcKiRju~cjI`$?UFw`tQ!hQ52cX?ccW3>T=!sCHp zK)?cC3r@vi@v3lupNkK3AGyA{J;EC&@YiPIv#-LBc)%(?#Qkx+>3X=*fd1d`K?xjh z8evwOn_qY-mCL)#w>F@@@9=|MINoqQoI9XWsq6>_^fuxH5;%T@2$8n9lr9Me^wjWv z$v-FFEWJfCGO} zg;SZ9;qexsdp<&cfE=#hz6?+BoGox_myO4dhTyihWerd86BUTORjc@nDnwqo(fSDo WVeYY8hSjtH0000g?E-R~>+kOF;pOe) z=O5th;~O3x>Er7c79J549OB{W<>uxd7!=~_>J}Ul>gww5>FE^`8s_BW;^N}w?(XU4 z=Hcz_>*eL+?;q&r7vSsX@8RJY5E$&}=;Y|+>>m&m7!({F5*8F3%0L-V{K*0~KnFyE z{KUYfcfjdjqJaYk3xmSsO)Hc{L|zs!89FUGviMRv0^rrSY7)WF=F$QaL HFjxZsqkKE? diff --git a/horde/js/glow/1.5.1/widgets/images/darkpanel/cbl.png b/horde/js/glow/1.5.1/widgets/images/darkpanel/cbl.png deleted file mode 100644 index 3f890a5b4e14efbd2ddb025b4030a4a6976f1605..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0y_Vow*xkcwM#FDddh81S?vstBD7 z+#sEFv_GY2lCr|_lNNmLGjuYYf>S;p(cf>9dGElPv%(jzCC5YxS5zfrUzc@C@lE&| zceb+rU%^Mm-E)LW8z;+0F)eL6B-tpX7+@V>!Ns;9ktM6e@pqZy0<&7~)w|w`*MDsg Ud34Hb8qj(MPgg&ebxsLQ0A-0i+5i9m diff --git a/horde/js/glow/1.5.1/widgets/images/darkpanel/cbr.png b/horde/js/glow/1.5.1/widgets/images/darkpanel/cbr.png deleted file mode 100644 index 7929c51c149bf0230d4f5d3a1427e46ba04d2efa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0y_5>FS$kcwM#FDw*1pup4m@Pt>2 z`#~qxE0?WRHYqE-Op#r#tZ-SOqgA2qXY4!HHMSd`|BV#fc!uRz#G9#~_DnV~=6U$* zScCV2IqUyUQs6Z@uu@b)GotY~*B4&Thj$sA8E-9_Fp=fcvPg;F-`xy?J1HayUHx3vIVCg!06T9(Bme*a diff --git a/horde/js/glow/1.5.1/widgets/images/darkpanel/close.png b/horde/js/glow/1.5.1/widgets/images/darkpanel/close.png deleted file mode 100644 index 168790feee35a6b3c8a6a3db31a5d6a006f8277d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQaiUB?$t_=+hxw(1m?d{jEUyqE6 zdH($Q`t|Go|NmbD0<}O8p#3-ws5r#a#WAGfR_%pIE(Svm=78&Y-|as*upV?%v)AQc zvNC1Qij<9l!YwZq)DqjgVqDx8`AyHZGY$NcB@-&o&08(PBY6Dd!hJ&1{{K~bW^zUU eW<~g|9Qj2JjQ?9VM1%s(XYh3Ob6Mw<&;$Uogho~X diff --git a/horde/js/glow/1.5.1/widgets/images/darkpanel/ctl.png b/horde/js/glow/1.5.1/widgets/images/darkpanel/ctl.png deleted file mode 100644 index 7f7182deb099d4dfacba6cbca08e84be838e4754..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0y_T2B|pkcwM#&+q1KFyL_w^zu5@ zGfD6U!)Jfa4#uEOj0S>_4Vad0?Dm+bB-s9_%&w}atz#0GS8&^=D`L!DOm<(o4;nED z&IrCN{z%D#V`&F>omZKrDn@Fuah@p*d={yjlzRB@*7s&{rA0! ZVTMz4tjc_sH$WFKc)I$ztaD0e0sx7IL6`sl diff --git a/horde/js/glow/1.5.1/widgets/images/darkpanel/transbg.png b/horde/js/glow/1.5.1/widgets/images/darkpanel/transbg.png deleted file mode 100644 index ec80a912db6816a8de03b567effa81743512abde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`0wizjKe`@BiF>*@hE&Ay9$@4J@|q0Nie@i9 beyf04oQ0Y3ZN|+VKtTpiS3j3^P67GWF#>9cG7}AqTw-F-7)&%Vn)srL5HN(GK}1bt9(nLV z5(%K--DuzeB_tCOj1PE;Mvy3C6m-nkAVb6S%v5(*SDmVJ&c3YW!_0I~n-HIMy?%SI zef$6Y(-43vXH_}NRgecj2xp2>o1iW>-Nq@kHU*qR03);3g^X1*kuC+}cEAa>rZ*GU zXvk}Ilh&St7Fo>MZxqEwmSa@fY2n)KH8;7QEv4S6ho6|~14k-@iniMtM~g2F*1tUd z$lQ&CYZtv4q;2Q|Z!V>Whqu_TWuKfs*=xgj8xm35=uiLrnb&^y`_G=pm6OdpQxK;X z08-iP{mt=r_EuiL;?_|=wpAx<*foX$ptCu5{b2vQ56|s6SS^lAw>zzgGm&WsrRlMbaMmTWFwN~O^;?QD zX8`DPBi(uR6=k>eKJV|?e;bv-T2DhLDMyZ-rU)@xbo9ivz>^1Zfil!YQbR7<2#5RL zHs=4V51W`c!}y!AvrP49eYw)&OtYvG&u%)u^_ug}n-2e4~QyC$%Sv zDd@Sj6It{#62!h4cVgjb7~Pf>L1KKN5pbe8TM$4noug^<;AmYh?mG#*W2O_%X2(z2$@0*M z7CWJr^fnsw4)!}88wK^&BMkxARupPCVaM9&CLbN-skH>CB|2BKxzS=~h%uIR#z{`r`=Gy9S~Kv^5%W%_ORkuQeV2D-|LuwTyLS~tn~_$+#q(kyq>N+$C}DkNa-iil)URAJPX>vJ1570V33lxd-f}F< z)FTf`0RUz_Mg>tUwu#s@F$H9Z-+j!d2OjJO#GY6Pf)zGoGY65wr#_b5T3rtYi2?z_ zY84XE6yJL32<}_I2hcY688iorVHhZ~__G&2zWS>_e0mi&mkc1l3M?b4&!jwd>`FTR z!vkLc3kw6arDT!FWMV(}3%u*SPw6#cg9C$*w>Xg196a)v8at=AVHHf1ZparJrslZ`K3|4LTEw4so87DZT5}GVzV|^*FsnXj0Jv4PP zj%PWl`Kgw5%KQC&dn$UZsB=VNLki04yzbX2Z-r7B66eMOw#Z08nGc4V1T8erMa+M3 zb{*1AtWbJy`-|MjjHyFTvP$c%b*d`26y zZo7E;vEOyqfAY*`PeWEySwNC90N4~hI=6K3r`!b>7Ld9q(zNo#mWGO__jChmJ2!H1 zP&IqwjABOS)yu5~fTHTez!AtsgJMkErasi;v*pS5uo6iN#A-Rf(9?to3fc zhMU7z^5$sQ1lCenHBbUWRR_&=2ag`Q?CoDVRJ`_vSTh0>6I2y&3Cj=N%m2Fn?lKNW zMW3~GObm!9#!i29`-RP;cU-!mPNz(#$o=w71Qigga<(~;#^!O>U4EZuieaveU#G)dd4_vbE*3bR{3pu+PK$|e3QXzEB=H$Nj{POGR(nmi~8I%QVPF6{kaCMm9^xn^X z7-v6`CnYfhjDN;ro(vk_3S7vuZn_)xz+KE&YkHxf4=v|iq6Fx_VC2cEtsi`M^}f4i z9dZl57-rgi{E0u}-gh11-mDvW&&|pY-RhrtxHYh|gkp*^AV$6qe%AMA#e zOaPsX<1`P8^CusBxdM=S`^~rC(C+ZU`3|%@4xMBRsPm;N6}u~i+AW!GM+fI&srH~D z$x;|<=Cuo9zD1f$0otUYF4J(6yP6aoXGU{}!Qds(hp{&eoai$L1 zv>K=&coM0(p&UW;mis^sI*WfFjQ>Nh!$cfGtuQpBy2zZohyw~VIde0A<>k?b=uLC! z6>FFDfwCHCgK}U4Tl%?$_2sP>=Lau;`>ZvK1q=j1%~hud#g`ASABq=VN|Bu_pa26= zkVfkprO;X;V+R3Hpb*SRx?0^!Dnf^~XEv=+AlAx}t?Htbty8c0wMQCbM`kFHIKZ-D z#O^C{vR-#Uw!7+K9-#oSR~PX(EFL*L{PlUc{MvTJ&urqe5z*W#esD`U>9 zD^Qu?2BJ5P+`$Ahb_HTBV0M4=5hL;niAD;H=T2{%!2JzKgvv{6`y!O=#CZK`Q751h zqiE4F`p!%J6)k5ze{|K=OZ6fQD0FPdmXDs3#DQjuUf@AUfLu{?gHaO_RpogGh3cFl zWeI?Yk1ZmS35Rwujaka0>!h)3=*WYAp%yhEIOF8z_uSL2yC0DF`DY$l#+6UHyY2kw z+*xdF|1^!&eb?Tnc-FV?3b;O7@BY;08RfYLpvn|f!DaEPtbY<}`?yevO&xCHa1sQ; zt6c8i!PrTXi`_v@>ggxG(LHd_5}G1=O@}RhdhNOL#bXDty`)e88)tv)R^8n0=(z#$WmgISHXiLGn;QZgwB@Z{tM29R_?HPiB`r8U4ofT9Q(puBVHKrOU0 z@ZU~Sc9CMWIc1Fb6xU<49-JxQK+V(>f5FfoYyH z$KHu0h*wniek(5|R?im1MG*tgrhD%2`NO`jWR~`f&wY}DU+esLnw@u+y|dMMYyD~QJ9R<2 ztb24jFETsxi*GO7`O$4*xb1-p@W(EF9v^$hQa7t*TK2#ZcK^-x$d|i!esX=+s_)4u zgofe+Z|RT!b{kFK0vyb=R5%SpsNk>${Lb6d6cE zWkoi6=8e_*%!~fEW5L^2;B<2G+=0pqXP)`~D~mfI=Xc+9VO5y!vv7y<(ZF^aeey3vivLQq`iU&z9Z;v#Oc z5f_3BMK?mkU#JTagux(&2%|w|n51L(^z?i0-g8b>xtQtK{W>>-JHguA#fQ4Ls?Pb| zZ_yqeh8B0t-iRIAECm34#Bg+>FdeBHd!;oW2ml>tb~A`)o##T?3UUD8L z6)82Z1E(`NYTMvhM~wsxvXbdhE_eIqxh3?T*{}lj!o6?aT)%MX<-A#3V+BeZy63)} zZvOf?Jm|RS3bYMs-Q)GyYf^JG4xH{YjzD^wZ{budGm|O#Z?ZP>5a@UQjA6W>HTj8tyo^JBdFT7}GcT=t zb9rRGJ~KaOgN}Vu=SiawkM{|R#MX@$G?|-~+(uhTZ6ZZIMxg_Z;p450#s3{_sU^2) zF5<@2R@?htkb$n}i}* zu-a$M@!;~8d`CWI5C0XVZWZhW))mXFv0*4^1bmZSpt0aURY9!sYm9 zH~it~{mfG8`_6|m3`X)G!hwL~>wO!TtbYt}xS&;R5(Pj8-QL&6E`tO~FlJp^p!R~& z1i&l{2RBp@fVJ|itF*TLZOe)P)P%)>j37AYFMb6#fBsb|)r%vw(1#Ve6y|d2ihlkn z6lExTP_U8o0U)dA_)GgVkl9?o4a@?c5Eupqy|VKJ00;w+6)yw+@y18qW$*#Ky8p>| z7umyqiBF7YD!{K?xH0Z4=+XcA=F~@F5Pn~u!8r78_$fXHU4c9Z8XWSCb1&jkUtd-m z+8ht={U{zrA2E;cM-e~VKj1^^9CLF+SL+%oJqlPjPqjP1x90&XBcLlQgiVba?l0-W(j3Ww14; zs4-c4Y$s*9M}MDFV$V&2!gCLa5S01WZsK)1q?|Yn4%A|eg;Kf~ zOvgN zvIlprZxV@kZGL813V*#k)k|<#DEmq#QT!H9R5uT3I-@e={3pFS^@t$0#ly8$BFLO& zf%fW?lb!`&_fBha4zNQgVrsYfT3!mNb&VK724+AOKU@E((aFARV0MCp6MgsTA0K_~ zJF={=QK$?=+#)Z&^abl2^_X%MXCOsHfPI$b@WfXlHZQ6VVCMf?X`_`rpOix8Bc8^MK;Sx6RUk(GvM z5M(5-1PSRVN` z3m}5a<-D!JQ6<0$w+;h9V|i63vJkAs6pG`p(^E>h5)P)BwP~iLKuS*O`y!igR1lpUIx*@6r!VQ^RW+|+IsJqd+yly#H9(OZjG)!x%%-lUsyc! zyYF?sybW)KU9?gR-~W=n_>0ed`o5R;r`JM;(>o8=sVevC=3<&yhJdeLmSOhRxN}p_ z{5Btl5~-Z`sXp4l;R4c6(%0EKb+O(yd(6G{Hu2e@8?@YOjFd{NwAtue-H;90s`;RbG`ctgW~l={PLhfSL(EvoZ!fZpnxUh>U<( zs_O1|v<3>2=)EMPH94F_`|l3p{|E*&L5Wj|IMS>WC5WYp|NLsN-fmmO*$*Fw7Mb!Z zvwOE+zw&{N-B>JCjHrSSRrokJFFKs#Gg zX{zMB=#0E~9$6+&F=9*H)us`ZKu#o~D|H~{uCXE6v{zt0+k5_XugjbBV5}}opo~f* zi@bQ{<7YB0rUGM5xhO~PyHw5nXyUURQ&|S;fJQ%HHu32U&0{QI$h&g)ee=WmILwuq zH`Ng$nk-{E85rdlb?$?~Zi!+4o1)e%ige;nq6iBn71%pkd}mN(gEEDx_sf^x{^+-F za9)4Usha3!y0SXI$xf6NspxBDHNnl7?Oe}8?$2*AI068*HNX(Z#?F|Ov+i%X0U$Ng z8HSz#2kz*NRLT`XFcQWZz))dS2q1$Pizy@}EiP^<&1jvdx&*?A%;3lcP|#>kEPnWh zhw$icp2V*>Py)sZ3J+0@_R(*~bNI{yt3Uk}rho>Rky#Ax_kM?1W}VL;S%83E$QTd= zjQB5vpjH@Xe-8#~gWTF>ETFPC5F+x4sw4v8s1~w{NNoxgDPyezcyNpo)Bfxl9Nc9C z6c=us+DA-E*c@VG??%L9_<6h;({nkeMJbUCXnBqHnyH>L2YP%GJBjvj#R% z8`Ddl{1LwLz|kCVWXQl;=t$X_fa-(KcMX;cX9yIjP<0p-fSb(Fm@J&(G0eQ8Lr!sO zX#g081W%S>kQojtU2w$q-x9(h0HG*E0S@f#Ze*inUcPay%t;{mi;+w%2YpTV^#UCQk(=1lUu1 zRYP-eFehQ_3z$1cLJ<@~;~_N+KVa>>43nse!nH9Yb1m9DFr%uKT3@5KOv#Pgy$#DA zoH>*;uVRyB`Sj0vc6QC{U%yaFxEIXEsg%rv{8TSK^~HGj2j4z?*2qlx?O8#CZLiX| zzC2u}kKKRn+T}_5>V;gVvtLC~c%-{O|GtT+msl5jN_$Q7s(b6~;}gpTWyW@2HMHus zDpV7g=c7lzHmsBaxn506mP5wb_4UFQXd<&9hdFpXCUC?S4|P zla861_PLl=y%#`71I_2qbw*AenFi$}Hy&D7loW1xh9*vuyD~Tyh7L^3jxh zHa3?@CKC%jljQ`GEMpGb5F`HQcD*cgH!od`veZ%VQKUe+HzYfFV8=W;&lO(= zx#Gz%S3DW!iYLQd@no1QKe}DK%DZyP6(FZv0dmR}bml8pfShs#HT24rHCH)B3zJaP z;hexjJ80jhCx|=>2~od@5+n0IG}v&iLyX>yqn5`&s05uWO-K(+)zoZyUHm8E`RPy loQE!Y?Mf3&U$fP&d;`}Cg}n!Vz)JuC002ovPDHLkV1h71!LtAW diff --git a/horde/js/glow/1.5.1/widgets/images/editor/dark-button-bold.png b/horde/js/glow/1.5.1/widgets/images/editor/dark-button-bold.png deleted file mode 100644 index 8360967f743ed6c0668be60a93d5a4372b59d2c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 452 zcmV;#0XzPQP)!dR*~WE@hNC~*|ylPYR4 z52H#SDj&uShF1$|lwxb-v?Am%ke?rQ5ntlLOu4Sp`DV22trFx?{&w{x4H1dOL{nKh znX#egY<>itzH*nr!T>njwleTB(13~u_0gWJBsOl$iJg==3S@zz(kOylCNuWZrj zE6p>{(_2G(u-6(al1%b3+05++HN0VsCN5mMMnKqEJl0QLv^GwCoG<<1eh`c zI{+N60M0C8QG+kDVlXu@l_`xwH-R}R-?UxIp~4@BVG&=QFSBy-dNJp9PNzM7EI%pe u%D2Z(g+oc1w5St8WuljhR1@*Ui*x~FlebHqqiA{n0000&c;9h diff --git a/horde/js/glow/1.5.1/widgets/images/editor/dark-button-italics.png b/horde/js/glow/1.5.1/widgets/images/editor/dark-button-italics.png deleted file mode 100644 index f9cfd781369dd5d51d1c7d35579eac1db2526bcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 411 zcmV;M0c8G(P)k3M!J9l$n}3Zp-l|s6G&p(n*cux>^Mqc<>7rlA;HQosJd)!HD;+0HG(ki% ztVAS38A)(yZ1X~s@W!Xm_*)qT3+r8>9&5bSGp#Rlbftcw!n@RMt&W!d4~fnvnjj(> z%1DMXlHk%<`yQ|uZpTP%SA;$bf0XsG;F!gZ%*GYtfe#_uD-zjW367?(~m4Q3CS7CAvS&Rk}ndBLr_0|1z=YO zwS)VMF=Dzn^!jVAbJ#P>*e~(=*R_&~d)#7pm|}$A&}(c*diSGbWlq zBPAYbq(m0UacOM#%cl~-L)+hHDDPv_*Z3#Q60TCxZI+6A6U=DbWPy3NlMT-dkkq9RwE#GC`@qg~@7t642Ws zns}F=%yd;@$E>$nC>Kf`(?|47t>O8V?Y&H(0T{7$18CGjRZ>ZU=`dSUttS8)0HY)q z+pJZ52(7-j3ReCYZ*6C5;?4Ft5=w$28Y7`G)K+Cxt4Tu1D%4ES3<4iT@b#=R55}SW zS!6HUN`vLaU7x@E^O5UI=|h8=klDK!`=e+JYUq@GK(ITC2R^q5N=#sER-mm4RG@1x za?pUn1P(<-L6;O_g5*-$1Q%A+mJ&0w0#UkSws%y$&=%WXgX?i)1E6|ffWgQnn2swq znm%h(jWmy%x=ppgVr!g>-JS_wvbD|+@n6Cnzx|vW`P@>R-MFy?O2B69Ft?9QaRD@y zn+MvQn`3~N8JY9Cs|?Z;u9_xuP~< zcI~CF3r=lQGMo<6w(45E*-psZjayb|I(R#F^pNW36*x*vvnx*iVC7v5X7!_Q$+ox%4PnsGLlD}qDq z5TgaO%!7D*)nL$D1_xy_b;83d-WKs{C9~_yftI{YpWES)ZcE4m#tF{bkMC^no5Lg~ zm^kSdZ45RPyMwDjo#5JlVeW)e*UR>zyzi)o;C-$8KJ+Cb$~&AJBmSkZJ~r-Epx*Nb XkVTZ|EJFW000000NkvXXu0mjfATx_$ diff --git a/horde/js/glow/1.5.1/widgets/images/editor/light-button-blockquote.png b/horde/js/glow/1.5.1/widgets/images/editor/light-button-blockquote.png deleted file mode 100644 index 530e1f6ff4c2c650d99e4f622c473a2bddfb46bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1921 zcmV-{2Y&d8P)7GWF#>9cG7}AqTw-F-7)&%Vn)srL5HN(GK}1bt9(nLV z5(%K--DuzeB_tCOj1PE;Mvy3C6m-nkAVb6S%v5(*SDmVJ&c3YW!_0I~n-HIMy?%SI zef$6Y(-43vXH_}NRgecj2xp2>o1iW>-Nq@kHU*qR03);3g^X1*kuC+}cEAa>rZ*GU zXvk}Ilh&St7Fo>MZxqEwmSa@fY2n)KH8;7QEv4S6ho6|~14k-@iniMtM~g2F*1tUd z$lQ&CYZtv4q;2Q|Z!V>Whqu_TWuKfs*=xgj8xm35=uiLrnb&^y`_G=pm6OdpQxK;X z08-iP{mt=r_EuiL;?_|=wpAx<*foX$ptCu5{b2vQ56|s6SS^lAw>zzgGm&WsrRlMbaMmTWFwN~O^;?QD zX8`DPBi(uR6=k>eKJV|?e;bv-T2DhLDMyZ-rU)@xbo9ivz>^1Zfil!YQbR7<2#5RL zHs=4V51W`c!}y!AvrP49eYw)&OtYvG&u%)u^_ug}n-2e4~QyC$%Sv zDd@Sj6It{#62!h4cVgjb7~Pf>L1KKN5pbe8TM$4noug^<;AmYh?mG#*W2O_%X2(z2$@0*M z7CWJr^fnsw4)!}88wK^&BMkxARupPCVaM9&CLbN-skH>CB|2BKxzS=~h%uIR#z{`r`=Gy9S~Kv^5%W%_ORkuQeV2D-|LuwTyLS~tn~_$+#q(kyq>N+$C}DkNa-iil)URAJPX>vJ1570V33lxd-f}F< z)FTf`0RUz_Mg>tUwu#s@F$H9Z-+j!d2OjJO#GY6Pf)zGoGY65wr#_b5T3rtYi2?z_ zY84XE6yJL32<}_I2hcY688iorVHhZ~__G&2zWS>_e0mi&mkc1l3M?b4&!jwd>`FTR z!vkLc3kw6arDT!FWMV(}3%u*SPw6#cg9C$*w>Xg196a)v8at=AVHHf1ZparJrslZ`K3|4LTEw4so87DZT5}GVzV|^*FsnXj0Jv4PP zj%PWl`Kgw5%KQC&dn$UZsB=VNLki04yzbX2Z-r7B66eMOw#Z08nGc4V1T8erMa+M3 zb{*1AtWbJy`-|MjjHyFTvP$c%b*d`26y zZo7E;vEOyqfAY*`PeWEySwNC90N4~hI=6K3r`!b>7Ld9q(zNo#mWGO__jChmJ2!H1 zP&IqwjABOS)yu5~fTHTez!AtsgJMkErasi;v*pS5uo6iN#A-Rf(9?to3fc zhMU7z^5$sQ1lCenHBbUWRR_&=2ag`Q?CoDVRJ`_vSTh0>6I2y&3Cj=N%m2Fn?lKNW zMW3~GObm!9#!i29`-RP;cU-!mPNz(#$o=w71Qigga<(~;#^!O>U4EZuieaveU#G)dd4_vbE*3bR{3pu+PK$|e3QXzEB=H$Nj{POGR(nmi~8I%QVPF6{kaCMm9^xn^X z7-v6`CnYfhjDN;ro(vk_3S7vuZn_)xz+KE&YkHxf4=v|iq6Fx_VC2cEtsi`M^}f4i z9dZl57-rgi{E0u}-gh11-mDvW&&|pY-RhrtxHYh|gkp*^AV$6qe%AMA#e zOaPsX<1`P8^CusBxdM=S`^~rC(C+ZU`3|%@4xMBRsPm;N6}u~i+AW!GM+fI&srH~D z$x;|<=Cuo9zD1f$0otUYF4J(6yP6aoXGU{}!Qds(hp{&eoai$L1 zv>K=&coM0(p&UW;mis^sI*WfFjQ>Nh!$cfGtuQpBy2zZohyw~VIde0A<>k?b=uLC! z6>FFDfwCHCgK}U4Tl%?$_2sP>=Lau;`>ZvK1q=j1%~hud#g`ASABq=VN|Bu_pa26= zkVfkprO;X;V+R3Hpb*SRx?0^!Dnf^~XEv=+AlAx}t?Htbty8c0wMQCbM`kFHIKZ-D z#O^C{vR-#Uw!7+K9-#oSR~PX(EFL*L{PlUc{MvTJ&urqe5z*W#esD`U>9 zD^Qu?2BJ5P+`$Ahb_HTBV0M4=5hL;niAD;H=T2{%!2JzKgvv{6`y!O=#CZK`Q751h zqiE4F`p!%J6)k5ze{|K=OZ6fQD0FPdmXDs3#DQjuUf@AUfLu{?gHaO_RpogGh3cFl zWeI?Yk1ZmS35Rwujaka0>!h)3=*WYAp%yhEIOF8z_uSL2yC0DF`DY$l#+6UHyY2kw z+*xdF|1^!&eb?Tnc-FV?3b;O7@BY;08RfYLpvn|f!DaEPtbY<}`?yevO&xCHa1sQ; zt6c8i!PrTXi`_v@>ggxG(LHd_5}G1=O@}RhdhNOL#bXDty`)e88)tv)R^8n0=(z#$WmgISHXiLGn;QZgwB@Z{tM29R_?HPiB`r8U4ofT9Q(puBVHKrOU0 z@ZU~Sc9CMWIc1Fb6xU<49-JxQK+V(>f5FfoYyH z$KHu0h*wniek(5|R?im1MG*tgrhD%2`NO`jWR~`f&wY}DU+esLnw@u+y|dMMYyD~QJ9R<2 ztb24jFETsxi*GO7`O$4*xb1-p@W(EF9v^$hQa7t*TK2#ZcK^-x$d|i!esX=+s_)4u zgofe+Z|RT!b{kFK0vyb=R5%SpsNk>${Lb6d6cE zWkoi6=8e_*%!~fEW5L^2;B<2G+=0pqXP)`~D~mfI=Xc+9VO5y!vv7y<(ZF^aeey3vivLQq`iU&z9Z;v#Oc z5f_3BMK?mkU#JTagux(&2%|w|n51L(^z?i0-g8b>xtQtK{W>>-JHguA#fQ4Ls?Pb| zZ_yqeh8B0t-iRIAECm34#Bg+>FdeBHd!;oW2ml>tb~A`)o##T?3UUD8L z6)82Z1E(`NYTMvhM~wsxvXbdhE_eIqxh3?T*{}lj!o6?aT)%MX<-A#3V+BeZy63)} zZvOf?Jm|RS3bYMs-Q)GyYf^JG4xH{YjzD^wZ{budGm|O#Z?ZP>5a@UQjA6W>HTj8tyo^JBdFT7}GcT=t zb9rRGJ~KaOgN}Vu=SiawkM{|R#MX@$G?|-~+(uhTZ6ZZIMxg_Z;p450#s3{_sU^2) zF5<@2R@?htkb$n}i}* zu-a$M@!;~8d`CWI5C0XVZWZhW))mXFv0*4^1bmZSpt0aURY9!sYm9 zH~it~{mfG8`_6|m3`X)G!hwL~>wO!TtbYt}xS&;R5(Pj8-QL&6E`tO~FlJp^p!R~& z1i&l{2RBp@fVJ|itF*TLZOe)P)P%)>j37AYFMb6#fBsb|)r%vw(1#Ve6y|d2ihlkn z6lExTP_U8o0U)dA_)GgVkl9?o4a@?c5Eupqy|VKJ00;w+6)yw+@y18qW$*#Ky8p>| z7umyqiBF7YD!{K?xH0Z4=+XcA=F~@F5Pn~u!8r78_$fXHU4c9Z8XWSCb1&jkUtd-m z+8ht={U{zrA2E;cM-e~VKj1^^9CLF+SL+%oJqlPjPqjP1x90&XBcLlQgiVba?l0-W(j3Ww14; zs4-c4Y$s*9M}MDFV$V&2!gCLa5S01WZsK)1q?|Yn4%A|eg;Kf~ zOvgN zvIlprZxV@kZGL813V*#k)k|<#DEmq#QT!H9R5uT3I-@e={3pFS^@t$0#ly8$BFLO& zf%fW?lb!`&_fBha4zNQgVrsYfT3!mNb&VK724+AOKU@E((aFARV0MCp6MgsTA0K_~ zJF={=QK$?=+#)Z&^abl2^_X%MXCOsHfPI$b@WfXlHZQ6VVCMf?X`_`rpOix8Bc8^MK;Sx6RUk(GvM z5M(5-1PSRVN` z3m}5a<-D!JQ6<0$w+;h9V|i63vJkAs6pG`p(^E>h5)P)BwP~iLKuS*O`y!igR1lpUIx*@6r!VQ^RW+|+IsJqd+yly#H9(OZjG)!x%%-lUsyc! zyYF?sybW)KU9?gR-~W=n_>0ed`o5R;r`JM;(>o8=sVevC=3<&yhJdeLmSOhRxN}p_ z{5Btl5~-Z`sXp4l;R4c6(%0EKb+O(yd(6G{Hu2e@8?@YOjFd{NwAtue-H;90s`;RbG`ctgW~l={PLhfSL(EvoZ!fZpnxUh>U<( zs_O1|v<3>2=)EMPH94F_`|l3p{|E*&L5Wj|IMS>WC5WYp|NLsN-fmmO*$*Fw7Mb!Z zvwOE+zw&{N-B>JCjHrSSRrokJFFKs#Gg zX{zMB=#0E~9$6+&F=9*H)us`ZKu#o~D|H~{uCXE6v{zt0+k5_XugjbBV5}}opo~f* zi@bQ{<7YB0rUGM5xhO~PyHw5nXyUURQ&|S;fJQ%HHu32U&0{QI$h&g)ee=WmILwuq zH`Ng$nk-{E85rdlb?$?~Zi!+4o1)e%ige;nq6iBn71%pkd}mN(gEEDx_sf^x{^+-F za9)4Usha3!y0SXI$xf6NspxBDHNnl7?Oe}8?$2*AI068*HNX(Z#?F|Ov+i%X0U$Ng z8HSz#2kz*NRLT`XFcQWZz))dS2q1$Pizy@}EiP^<&1jvdx&*?A%;3lcP|#>kEPnWh zhw$icp2V*>Py)sZ3J+0@_R(*~bNI{yt3Uk}rho>Rky#Ax_kM?1W}VL;S%83E$QTd= zjQB5vpjH@Xe-8#~gWTF>ETFPC5F+x4sw4v8s1~w{NNoxgDPyezcyNpo)Bfxl9Nc9C z6c=us+DA-E*c@VG??%L9_<6h;({nkeMJbUCXnBqHnyH>L2YP%GJBjvj#R% z8`Ddl{1LwLz|kCVWXQl;=t$X_fa-(KcMX;cX9yIjP<0p-fSb(Fm@J&(G0eQ8Lr!sO zX#g081W%S>kQojtU2w$q-x9(h0HG*E0S@f#Ze*inUcPay%t;{mi;+w%2YpTV^#UCQk(=1lUu1 zRYP-eFehQ_3z$1cLJ<@~;~_N+KVa>>43nse!nH9Yb1m9DFr%uKT3@5KOv#Pgy$#DA zoH>*;uVRyB`Sj0vc6QC{U%yaFxEIXEsg%rv{8TSK^~HGj2j4z?*2qlx?O8#CZLiX| zzC2u}kKKRn+T}_5>V;gVvtLC~c%-{O|GtT+msl5jN_$Q7s(b6~;}gpTWyW@2HMHus zDpV7g=c7lzHmsBaxn506mP5wb_4UFQXd<&9hdFpXCUC?S4|P zla861_PLI;X{Tjn>AT4O2;bno78o}cN&cn+B7d3!)NP`;Q0lBOa{1)Q0;=(~G zaBUc#5O95XS)j#Wfd`0-;=#cw5Z4S(Q{fmqPRJx=+G<=b*Ua7heL@F3`I35D3Wt{o z`$c-NfhaIV`LW+0n0t79dRBp_(wXHl9!?jvz-%zM zKrN8YWIrO2t#t<6UHQSv0@Kt2cz3P9v$_1I8eFE*EAFQL@%d0_5exhU--$*y81Mno zrxG6pbqU^Kyvya_xqRX4;P7aR0r#-J^Xdg8JQh3N=ECWCS)iY~6z?=XN`XH;JKtl# zy$~B8FAKOi1@MkD;XVOpTSBMF>YyaN{&L9E7$&i2(@T3ABL+}v+H;K1l0R!+x zss#tPfL9ZI6yo4GaHtgUDZ>2%&d2i$*s*{Dyh&w04jl!CiSRKREKmg(qCpBU-8VrCu(s)uLwxWRmNSk88 z5oIYN4#`bI5QwPA0o*Z!Pm=;5wi6>5k{9ieBuN2N2*S4QGa`B>SribvstCt%&VwN6 zQliKa)1uX^5tId@Uaw#Ddc8MNi3G8yifA+%mk5F;AZA4O5kwe<9|R?i$RP;Vb*~tK zB#t;h5dD5Xpj2VRoM=rI(QG!a2ZO;UrA;;n98yC*%;9kOMI0@VB~eEec`--W&y#pTCF=B80#O*5rUC?GtkO9k6=8{a~Y!nGB%|T(J8G3Vrr4IB$!#o zz{@@k!T&+{kI+)UKwASMdWRu8_bEiQA=-r?I(Kf-dr2@Bh^Yc6y@Kc~0FfRss!9;X zyhtDrW(PQg=NSV)z!M-t;+PHq!Op}Ckz*{4n2HjFZwa0lq7PG6f-t6?9w50%GJW%G kn};x_WFEqpV$x@S09-KU;ZQasaR2}S07*qoM6N<$g8S;*yZ`_I diff --git a/horde/js/glow/1.5.1/widgets/images/lightpanel/ar.png b/horde/js/glow/1.5.1/widgets/images/lightpanel/ar.png deleted file mode 100644 index 407e1d42572d6b00484a783d16110fbbaddf0d6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 514 zcmV+d0{#7oP)W*5X6CUpq62i8O(M$i^VDf!&T#w#d>AcYO$^hHp77{ zbCYep7N78y``+i1-2+d!_6y&Af8NY^JWf=}629Hn267}_P6OmbdMsU;JOr54|A1Uu zL4a9v4f)ND6$D*(2SM(vAZWoI1PQGmXpx7Qd9Z?@n>@tKqZI_*ati?_n;3$Yc!-%u zngt|r%RIzPTbfUdKr1}N%(Lftry7iOih!7jOxA!f>G%7w60;8sAXEl}!DqAC zyi#Cl(i|WuzyRVP!{P8txqxfVAfwUfJBp%v1;UpuW>SDyz#1VDtyb$+iI~S4Nsv_>D6yya6D@YYj2L=K}UhxRu27-sQfv|#9fS@5jSVAf;Cq_>g zL0Cb`Gy)LJK`bGq3C~$E8JaZ2OT*;3n1)$=!94)|0f`9a;Z${SRR91007*qoM6N<$ Ef_U@R8UO$Q diff --git a/horde/js/glow/1.5.1/widgets/images/lightpanel/at.png b/horde/js/glow/1.5.1/widgets/images/lightpanel/at.png deleted file mode 100644 index a9ec5fbdc8e4841f7b2123ecbd4f75a6fb470224..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 522 zcmV+l0`>igP)dX=x3i;nqecm)2WcTU%P)gSLi) zTrLfLeJ_G{MI3(Ldtdp7|LXhw;XTedAp}by%g{PB&2Rp z-1q&P@p$~!Znw_~0}6V01>dH?CzHv$fQ&|?SK@#zU3?jPXs6S;`h-U!2e__#Mi`Kf z^D)C!hQnb%9FRldAtme~p69vpj*FOtVfaFP#2ZuLAw}$u%R3!~GYwde#X|}dxV+N> zk-=c_)ND3SkY^o2*M7xAY%E;f;V2wCz;T>o!hqFpcu1ZCmyb|rBnW~&aX?nOWpb1+ zr@Xt0p*O&`?IWb&0J=i?OS}nv7G`q~&0sAQcwg|O2er8HSp{c{j~Rf0KZ?Ie*Cajz zeZU*|2hidCxuxM1yeIxQ9tCe9;M}>!;3fP{%vLU^#dpPQ=HjRI16IH0;Tda8^8f$< M07*qoM6N<$f)Cx~4*&oF diff --git a/horde/js/glow/1.5.1/widgets/images/lightpanel/bg.gif b/horde/js/glow/1.5.1/widgets/images/lightpanel/bg.gif deleted file mode 100644 index 279feaf15cf195f08b5e35d7393ef869376a5121..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 169 zcmZ?wbhEHbWMnX6IKsg2^5x5a|Ni~?^XK*J*RNi^`uX$c+qZAOef#$2&6`i3K7IW7 z@&Et-fB*jd_3PL7@85s_{{8jq*AE{)eEIU_-Me@1-@pI-`SXhxFMj;^!GHrO{$v5m z=zvI&oeZpM4>qx+958VDW#bzxGX1d(JBw$~RFy6v*C{8DBseIrc`z{^V2N;Ium%8# ComP7Q diff --git a/horde/js/glow/1.5.1/widgets/images/lightpanel/cbl.png b/horde/js/glow/1.5.1/widgets/images/lightpanel/cbl.png deleted file mode 100644 index b7345ed55f17cb7d339c3430f081d2ca05268de8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0y_iJmTwAr-ggp0VX?a*$|Av}y2A zmfd#AVaY@$uBRNAH07opnRQh9!}JC*wK)m0o@VneHr<{3M0dX71`gHY(9+Lu+S$cx z7w9ubR__0MsF!C?%FV5ryFW_LD)uPxx?FrPqOW=S!JjStMKgp9F0^jr6S;Akd` z$%V|-yev{WTLStO<{n7&ToJj0QK8ez@e${>nNGq|Gwwd}Rh#_2@%d}r`mM3{Ob4c3 zEM#-NJHVI-B>0mY+A+j9;F7$=y|P=o9bOs#c)u7(8A5T-G@yGywnw C1xpqH diff --git a/horde/js/glow/1.5.1/widgets/images/lightpanel/close.png b/horde/js/glow/1.5.1/widgets/images/lightpanel/close.png deleted file mode 100644 index 91e898eccfc8d2c104e29f5863fb980c23eef8e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQaiUB?$t_=+hd-m*UZ*RYT{rca( zf1f{pzJC4s|NsB@_4RdkcXxJnmdh|^0u_gNx;TbZ+^W40$;DvE!5nZs@4Nj62iAjb zYWBMPOID`rS&_0)P`Kr#f?8smSB#7MBERX`cBX-UvSdQ#xp}Kacm$7sT)0nY+W)_5 i&rGi9->e9~l_S5Xf$@LqhKNw0`3#<}elF{r5}E)T*+}sK diff --git a/horde/js/glow/1.5.1/widgets/images/lightpanel/ctl.png b/horde/js/glow/1.5.1/widgets/images/lightpanel/ctl.png deleted file mode 100644 index 73d10893e676f53045565f496844244b3b9ca955..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0y_37#&FAr-ggUf9UhY#?y#W10RX z$L&qCMPy0@+1Ve;DRu`R-;=g5U+BcMX9|re9q|he`&K<^{$IBLX`Z25;0@-z0o}`1 zKNc#@a64k{%xk}=C8CkdC8@(!(DTuYzGE^!#2EQDas~3eDOOBX`M)G6%kWsu+~Vw} ztt^>q?eG28{Wm>9z46+ucqW;1LK03lENvJ}T90KNi3olIbQ^=GtDnm{r-UW|m*-6N diff --git a/horde/js/glow/1.5.1/widgets/images/lightpanel/ctr.png b/horde/js/glow/1.5.1/widgets/images/lightpanel/ctr.png deleted file mode 100644 index 48f9ebc4b30a30204fac0f292e8a6b2bb7324e0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0y_c25__kcwM#FKpy$HV|ojxI?^f z!|VrYPVBK>?R-uB6ONuVT(9}Xm+9sm87~FNM}aNfemmuU9ILlv`hAF-tD;4~r6>2V zD?9I#IUiO%YJbA3X|Hng?gwU0eHBxCmGiq3y%jIZMP;Y2{i?E5#)7}Q;Ed&Vjitv< qY;f@~KK4iH@H3IB_x~p*ZfDw|b}TE(-D3;TNerH@elF{r5}E*{W=W#} diff --git a/horde/js/glow/1.5.1/widgets/images/lightpanel/transbg.png b/horde/js/glow/1.5.1/widgets/images/lightpanel/transbg.png deleted file mode 100644 index cc294a1c3a351bf430fcfc450f72ba69fbc0b700..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`0wizjKe`@BiFvv>hE&9{9$@4J@)$P=`~D9Q aTU@}T$KckfpSS@i#^CAd=d#Wzp$Pz401=b` diff --git a/horde/js/glow/1.5.1/widgets/images/slider/darkh.png b/horde/js/glow/1.5.1/widgets/images/slider/darkh.png deleted file mode 100644 index e3bb99f57bc28c93300b7238f03cecb4f622be14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1268 zcmV%p4GpKKr+|QfW@ctpRaNKb z=d!Z0(9qCcUS4c$Z7wb^KR-W+h=?dCDCFeiZ*OorJ3Df6av~xk;o;%l-rniy>1ko(ndx{cXxO3 z@$p<-T?YpT>+9>!&(9VX7Qes0eSLk~+uN|PuWzP@j7Z_&}wdU|@UuCCwT-`UyOadC3g)YM^NV#dbC%gf7$hlj_< z$BT=L!NI{`U}3_-!nU@y%X$-R00001bW%=J06^y0W&i*KB1uF+RCwC#+viRc0ThPe zgD!Su)&WfH4TJ@IcQHl`H9;T%F1=w2}wCl4e2wz8v&)I1l=8ng{*%U|(;V2R-c-bO0!# zxh&|FM$ju+&}Wu|KHLcUa31vEgMDcu=&7IsKoR|!ZN1Pt)?es7E_47WxcYSK552bj zhyKJ59RLch{p1`vQGK=< z^zK?@lD1~h-z1T#t_r%I13)p=e>Q@Crxv+nu@UrzBy!be(DfVuimE=72mM1W8j_EB z&=-?vs5XMG=KxS#^-o#QyKB*ubY?-nnnY7I54xTM{A=fKQWJ>501OXg_K5}Q4DAWL zfe7Nlo#Mh}rI+Z>{VT~3jQc_s&$Ie+f|qZ80zKP(zUSxdRr-4SPQ!!MJ?MH40Das2 zY=%bqO+#b#s^G?XHm!7BfBgl{*0RDlSE@LEn}kEJd$DKl%?H0J;^6kN7l%;uaP+SH%H< et6u?t|MLgG9^kV6{pVx=0000`$PtQ}T3)bJO?-)vMWpA(#?VBxjOOC) zZgR)OOEg~NC5-b@iPs6&gb`^L^L}3HZR!89yU#t(^V{=s_xTkS5(v>X(FFhig7_L9 zu4YF7&=PBFs8wM!`)>fal7m7<_+MUL;_>*%$Vi1kAr^}r9q+?nu!4euFG%#WjuS9B$Y~)N+kw^jfr^@9E>zDFfcVW)6>)Q@p&{qKfklH zL#0wfLs4;Y@!8qgGcz+}GTGSJSfx^lL?RlErlqB|yu2J6``zyDZa_d_e}6v`iKI{{ zi;Ig}TU#wHEm2WX>YS64lVM?D0|Ns%9FECk4h;>FNF+L)o|~K7-Q5j`!x;=lMMcHf z*cgG3Qc_Y z;NW0wZGCWXP+wnPSy>5zKsX#uPEO9<6|39os#i$iA&&u{jYT{FX!s%Ek0O3VZ;spD z$nyb!LE5$^QJ;+AI7PcgeuYQoi_cHv!RmgaVBQ~om+bJ4Dn3BJj98bGHFqAo@^LE~;+-QE~1E(nGyN7AqI_A_-jng&f64U)D*FJw8 z^!pD=BWcryhp*wF-7bXC<;xhd+Y=>mhq|Q?uT>9s*zYa})|x_&c{}8p8j1a3$_NXBSw&JlbU*dd(%}0{r=1YsI-`=bK34$t$ zzDR4Xc~jji7#I4!@qocxPV{4luY?X|M?A}N?+@bFaqGAY$_?c@{nZvhDZ5IENt G`_q4K9HRLE diff --git a/horde/js/glow/1.5.1/widgets/images/slider/lighth.png b/horde/js/glow/1.5.1/widgets/images/slider/lighth.png deleted file mode 100644 index 93fe2d5e67fa0570bfac5871485dbbb6e8bbb0a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1051 zcmV+$1mydPP)W@cvp|NrOb z=kW0G>gwvx&(G=U>F)0C>+9?6?Cj*^q@9&+RovW*>;o;%l-rkj!m87Jk zj*gD?_4U=&)xW>Ld3kwrb939<+w$`A^Yio4($e4G-^9ekhlhu`xw!xU0NB{rZfD*0dH?s~ zWZ5>YG4agokmoskZ{U~7?!QRZ0D!C&$?-`G0La+MaSD3p1^|$?P73i|I33U}xLa9!@u0|1b*t2^{I0Fbrn4!r~b zvR2)pZvp^WtM1UZ0f4Ms-J#zG05awd9RRL_qKN+#{!{<}4HHML-tOLDXZYaY=dbsUphKsi5JU>H5CFhp;V}+H!pTT!zKN+sCj7k1x-94ghmozL&ej z*WDuMIRH#=`E#M?05GrEjdSR3W>E)#iF>|YVslwz5sG{c%{ePN08Fd)e3ve*=V@1q zUVFZ!bL)B9vY-RNtZL6o#)q-?ykxwOf(`(a8hU>7F!sDX2s!}FsU-Zc#%?{7qL+kU zbLjFlZCTI(VAd0QPU#f8Q%bp>(Dx}FLw8JR?}QEjbBev_L(gLvie7u}9XbF^D)yoe zJ&&Omz4zQ#?f@{W*oxkRp6d`=(RXR`2h!> Vww9jsCdL2&002ovPDHLkV1nyhGwJ{U diff --git a/horde/js/glow/1.5.1/widgets/images/slider/lightv.png b/horde/js/glow/1.5.1/widgets/images/slider/lightv.png deleted file mode 100644 index f05809d10a7b52d3acd32c98c7742d3549a226f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1112 zcmeAS@N?(olHy`uVBq!ia0vp^dJGKg7dV)KtgeW;kAT#l0G|-o|NsA|rKJJM=g*&i z`0(M?t5+{yzC3^a{JVGW-n@D9`t|E4Po7-3aN+&?_Z=M_bLY;zckkZRsZ&?4UcF?= zl7|l;_Vx9B{rdIl)vLwD#kX(YuB@!wzkmOyPoM7Jzklh{rO%%~A31WQy}dmzFAr$p z`t|E?+_gZb?45Vw{PFhoH=vTrcEC|ethxb#hEi_N=r+7dU_TtSa9vywUUyO$B!R3G&C$* zwk%pdKo#gL#*!evUD}yny_UDT`}eQh7kKjUrF@u5?+CC@2WR%n8B5ieeZwl5BH5Nt|_PQv+jBjqw)A+vD}i} zQ$c_F*Xf@AFl*)1s?4Gf6K4OD_3&0UXAjt?Y1;p(LOGgY{TDae_Umh%IQev4?w)=A z=Fm#J#_~_BtFmg@eQTvReA7Sp;Ie|t^fiC)u)NsMGxOBPyIVRx?Q^`uJ^9nT^Dg?n&rgZ#GpfD_ zkooj6?&l%V#d<@>I`^GtSj(qh(2PIxc)iaK&gCTnPd~`swKZA3MPTCW{hq(s)&^B2 zzVd!!{W@VIe|(?3gF5G*mCfgu$J)d`W02bSe%G04e!R!5zx!V1n))C=@J{#7A58C~ zo6KH)t;&5jW6z$cx=Qy`udUfPx!wG2UBE{L=GA|CEj@o^sq*G}g_Y ZY4_<}|L1U3B?7YugQu&X%Q~loCICfrYwG|2 diff --git a/horde/js/glow/1.5.1/widgets/images/t.gif b/horde/js/glow/1.5.1/widgets/images/t.gif deleted file mode 100644 index 35d42e808f0a8017b8d52a06be2f8fec0b466a66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43 scmZ?wbhEHbWMp7uXkcLY|NlP&1B2pE7Dgb&paUX6G7L;iE{qJ;0LZEa`2YX_ diff --git a/horde/js/glow/1.5.1/widgets/images/timetable/darkh.png b/horde/js/glow/1.5.1/widgets/images/timetable/darkh.png deleted file mode 100644 index bb0b5724bfe30e186c9c87834dcbc0bf20d238c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 474 zcmV<00VV#4P)>;O92^`ZBO~YM=WJ|j=H})>KR+lZCzFzrBqSs`H#hqF`ZF>z_V)I|!osDbq$(;Z z{r&x^si`L>CSqY>)6&w`*49!{Q9C&~hlYmm@bIFdqA4jUK0Q6Nv$O2%>{wS<;NRaR zBqWE2hxhmQIXOAHy1MUM6a)YO0Ov_WK~#8N?b5|=13?e~QP(mVnVFg4|9>osl*x|Q z>zs4wIrZU5Emcp(G+nz{?)HsVHL8R`=H~@rC91ZJez)ATUDJGL!h{JECQO(xVI9Rf zf3a*su}1bI^M;@p482rlw0qD~?0aq-@65>o`GR7FJdkq|tXKkSbv;PE29$~#v#kO6 zqw#By@yLgPW&bj30!(MgZnG&cvErrk1+ZMbvegn;%=IAa%@+LKK4$x!58KT;5#(?L zPL5*E2{;~7@}4i~dAXd^uW-HHV|Kq?Ki>55RP6cq7vEBtFk!-k2@@vl3;K83$1E(( QlK=n!07*qoM6N<$g3{R8t^fc4 diff --git a/horde/js/glow/1.5.1/widgets/images/timetable/darkv.png b/horde/js/glow/1.5.1/widgets/images/timetable/darkv.png deleted file mode 100644 index 1ec7bd7be6e2048af8615d10e2f70a70ccce4bf9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 434 zcmV;j0ZsmiP)Z~)VBzYs^er^#-MkKa@th9}YW1wsf9TVc}b-LKLT?69jwE&zv1LCVSQ0dQo zJqfh@9*Q+E4fG_|8UTNzF))eMBvqQ_SS-_3fzM5@P)lqu+}!>B z{rLF#@$vEW^!4rS?dIm^-QC{eEPhtfPjEgQ&Xs@sQdf-t*x#~N=o73;)aHX z+S}W*va*GRh2-Sr*4Ec*YHDF&VZOh=^78Yksi{s*PJ@Gk($dr4-`{E5@mX?-BM@YxV$Zv0N+1c66``*$3008PqL_t(|UhUJxQUp;9g<<~NHtz23 z?!x;&7Xu5@^z^W0L9$ELw>c-3x5vbC`C_3|D_854tYy7_jb&vk^=i3RDirg%*aQ6~ zRH#s)LWK$yDs%)ozrnaC(2Ku>5f=o48wrJ(s13Hj&n=pHhKVGQN()SdmN7dGn6osDKMJ{;d};6C;l*tWA0M`Q@!|cY*iYzx)}63Kc3; cs8FH84?{YqE<1D8e*gdg07*qoM6N<$f)1DdcmMzZ diff --git a/horde/js/glow/1.5.1/widgets/images/timetable/lightv.png b/horde/js/glow/1.5.1/widgets/images/timetable/lightv.png deleted file mode 100644 index 0874b83fa5e43ec54470187098b1c399d9e3220b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 446 zcmV;v0YUzWP)o-`~Bxy{)aTT3T9#g@t{6eQIiIlarM4^7Gc#*I{8{zrVoy`}@nx%uY^D zQ&Usg+uOdszgbyX-rwI!N=j{QZK$ZIgM)*XmX?5kfTpIWZfk`T(^67W#m2^^rlxOiZ-$13CL_t(|UhUUKb_6jH1JF`44>L1U znC^eBbaI69d!xnpZ_2w`*_MRDst`$LU{=qJh-09Wj~x@@>NR#4j9dfa>74;IJpOOzyJUM diff --git a/horde/js/glow/1.5.1/widgets/widgets.css b/horde/js/glow/1.5.1/widgets/widgets.css deleted file mode 100644 index 1414c457b..000000000 --- a/horde/js/glow/1.5.1/widgets/widgets.css +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright 2009 British Broadcasting Corporation - - 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. -*/ -.glow151-cssTest{background:url(images/darkpanel/ctr.png);z-index:1234;}.glow151-panel-preload{height:1px;overflow:hidden;position:absolute;top:-10px;width:1px;left:-5px;}.glow151-panel-preload .panel-close{display:none;visibility:hidden;}.glow151-panel-preload .infoPanel-pointerT,.glow151-panel-preload .infoPanel-pointerR,.glow151-panel-preload .infoPanel-pointerB,.glow151-panel-preload .infoPanel-pointerL{display:block;}.glow151-overlay{visibility:hidden;display:none;overflow:hidden;position:absolute;}.glow151-overlay .overlay-focalPoint:focus{outline:none;}.glow151-overlay .overlay-focalPoint{overflow:hidden;width:0;height:0;position:absolute;}.glow151-overlay .panelAccess{position:absolute;overflow:hidden;height:0;width:0;top:-5000px;left:-5000px;}.glow151-panel .c{background:#57585A url(images/darkpanel/bg.gif) repeat-x bottom;color:#fff;margin:0 10px;}.glow151-panel .tr,.glow151-panel .tl,.glow151-panel .br,.glow151-panel .bl{height:14px;overflow:hidden;}.glow151-panel .tr,.glow151-panel .br{float:right;width:14px;}.glow151-panel .tr{background:url(images/darkpanel/ctr.png);}.glow151-panel .tl{background:url(images/darkpanel/ctl.png);}.glow151-panel .bl{background:url(images/darkpanel/cbl.png);}.glow151-panel .br{background:url(images/darkpanel/cbr.png);}.glow151-panel .tl,.glow151-panel .bl{width:14px;float:left;}.glow151-panel .tb,.glow151-panel .bb{border:4px solid #57585A;border-width:0 0 4px 0;overflow:hidden;font-size:0;}.glow151-panel .tb div,.glow151-panel .bb div{height:10px;background:#fff;opacity:.5;font-size:0;}.glow151-panel .bb{border-width:4px 0 0 0;border-color:#424243;}.glow151-panel .tc{overflow:hidden;position:relative;zoom:1;}.glow151-panel .bars{position:absolute;width:100%;padding-left:10px;height:2000px;background:#fff;opacity:.5;margin-left:-10px;}html .glow151-gecko .glow151-panel .bars{background:url(images/darkpanel/transbg.png);opacity:1;}.glow151-panel .c{position:relative;min-height:50px;overflow:auto;}.glow151-panel .panel-hd{border-bottom:1px solid #B0B0B0;padding:5px 10px 10px;font-weight:bold;}.glow151-panel .panel-hd *{margin:0;padding:0;}.glow151-panel .panel-bd,.glow151-panel .panel-ft{margin:0 10px;}.glow151-panel .panel-close{background:url(images/darkpanel/close.png);height:20px;width:20px;float:right;position:relative;margin:5px 10px 0 0;text-indent:-5000px;}.glow151-overlay .panel-noHeader .panel-hd{display:none;}.glow151-basic .glow151-panel .panel-close{text-indent:0;color:#fff;border:1px solid #fff;text-align:center;}.glow151-basic .glow151-panel .tb,.glow151-basic .glow151-panel .bb,.glow151-basic .glow151-panel .bars{visibility:hidden;}.glow151-ie .glow151-panel .bars,.glow151-ie .glow151-panel .tb div,.glow151-ie .glow151-panel .bb div{opacity:1;filter:alpha(opacity=50);}.glow151-ie .glow151-panel .tb,.glow151-ie .glow151-panel .bb{zoom:1;z-index:-1;position:relative;}.glow151-ie .glow151-panel .c{filter:alpha(opacity=100);}.glow151-ielt7 .glow151-panel .tr,.glow151-ielt7 .glow151-panel .br{margin-left:-3px;}.glow151-ielt7 .glow151-panel .tl,.glow151-ielt7 .glow151-panel .bl{margin-right:-3px;}.glow151-ielt7 .glow151-panel .c{height:50px;overflow:visible;}.glow151-ielt7 .glow151-panel .panel-bd{overflow:hidden;}.glow151-overlay .panel-light .c{background:#fff url(images/lightpanel/bg.gif) repeat-x bottom;color:#000;}.glow151-overlay .panel-light .tb{border-color:#fff;}.glow151-overlay .panel-light .bb{border-color:#E7E8E9;}.glow151-overlay .panel-light .bars,.glow151-overlay .panel-light .tb div,.glow151-overlay .panel-light .bb div{background:#000;opacity:.26;}.glow151-overlay .panel-light .tr{background:url(images/lightpanel/ctr.png);}.glow151-overlay .panel-light .tl{background:url(images/lightpanel/ctl.png);}.glow151-overlay .panel-light .bl{background:url(images/lightpanel/cbl.png);}.glow151-overlay .panel-light .br{background:url(images/lightpanel/cbr.png);}html .glow151-overlay .panel-light .infoPanel-pointerT{background:url(images/lightpanel/at.png);}html .glow151-overlay .panel-light .infoPanel-pointerR{background:url(images/lightpanel/ar.png);}html .glow151-overlay .panel-light .infoPanel-pointerB{background:url(images/lightpanel/ab.png);}html .glow151-overlay .panel-light .infoPanel-pointerL{background:url(images/lightpanel/al.png);}.glow151-overlay .panel-light .panel-close{background:url(images/lightpanel/close.png);}.glow151-gecko .glow151-overlay .panel-light .bars{background:url(images/lightpanel/transbg.png);}.glow151-ie .glow151-overlay .panel-light .bars,.glow151-ie .glow151-overlay .panel-light .tb div,.glow151-ie .glow151-overlay .panel-light .bb div{opacity:1;filter:alpha(opacity=26);}.glow151-infoPanel .c{min-height:125px;}.glow151-panel .infoPanel-pointerT,.glow151-panel .infoPanel-pointerB,.glow151-panel .infoPanel-pointerL,.glow151-panel .infoPanel-pointerR{display:none;}.glow151-infoPanel-pointT .defaultSkin .infoPanel-pointerT,.glow151-infoPanel-pointL .defaultSkin .infoPanel-pointerL,.glow151-infoPanel-pointR .defaultSkin .infoPanel-pointerR,.glow151-infoPanel-pointB .defaultSkin .infoPanel-pointerB{width:48px;height:34px;overflow:hidden;position:relative;display:block;}.glow151-infoPanel-pointL .defaultSkin .infoPanel-pointerL,.glow151-infoPanel-pointR .defaultSkin .infoPanel-pointerR{width:34px;height:47px;left:0;z-index:1;}.glow151-infoPanel-pointL .defaultSkin .infoPanel-pointerL{position:absolute;}.glow151-panel .infoPanel-pointerT{margin:0 0 -10px 45px;z-index:1000;background:url(images/darkpanel/at.png);}.glow151-panel .infoPanel-pointerB{margin:-10px 0 0 45px;background:url(images/darkpanel/ab.png);}.glow151-panel .infoPanel-pointerL{margin:54px 0 0 0;background:url(images/darkpanel/al.png);}.glow151-infoPanel-pointL .defaultSkin .pc{margin-left:24px;}.glow151-panel .infoPanel-pointerR{float:right;position:relative;margin:54px 0 0 -34px;background:url(images/darkpanel/ar.png);}.glow151-infoPanel-pointR .defaultSkin .pc{margin-right:24px;}.glow151-ielt7 .glow151-infoPanel .c{height:125px;}.glow151-panel-noHeader .defaultSkin .infoPanel-pointerL,.glow151-panel-noHeader .defaultSkin .infoPanel-pointerR{margin-top:33px;}.glow151-autoSuggest ul{margin:0;padding:0;list-style:none;color:#000;background-color:#FFF;border:1px solid #333;}.glow151-autoSuggest .autosuggest-dark{background-color:#000;color:#fff;}.glow151-autoSuggest li{padding:1px 9px;margin:0;zoom:1;cursor:pointer;}.glow151-autoSuggest li.odd{background-color:#D3D3D3;}.glow151-autoSuggest li.even{background-color:#FFF;}.glow151-autoSuggest li.active{background-color:#A9A9A9;}.glow151-autoSuggest .autosuggest-dark li.odd{background-color:#333;}.glow151-autoSuggest .autosuggest-dark li.even{background-color:#000;}.glow151-autoSuggest .autosuggest-dark li.active{background-color:#999;}.glow151-carousel,.glow151-vCarousel{position:relative;overflow:hidden;zoom:1;}.glow151-carousel .carousel-window,.glow151-vCarousel .carousel-window{position:relative;overflow:hidden;}.glow151-carousel .carousel-content,.glow151-vCarousel .carousel-content{position:relative;list-style-type:none;padding-left:0;margin:0;top:0;left:0;overflow:hidden!important;}.glow151-carousel .carousel-item,.glow151-vCarousel .carousel-item{float:left;display:inline;}.glow151-carousel .carousel-nav,.glow151-vCarousel .carousel-nav{position:relative;display:block;width:30px;height:100%;cursor:pointer;z-index:1;}.glow151-carousel .carousel-nav span,.glow151-vCarousel .carousel-nav span{position:absolute;top:0;left:0;display:block;width:100%;height:100%;}.glow151-carousel .carousel-light .carousel-window{background:#f0f0f0;}.glow151-carousel .carousel-light .carousel-nav span{background-image:url(images/carousel/lighth.png);}.glow151-carousel .carousel-light .carousel-nav{background:#f0f0f0;color:#000;}.glow151-carousel .carousel-dark .carousel-window{background:#282828;}.glow151-carousel .carousel-dark .carousel-nav{background:#282828;color:#fff;}.glow151-carousel .carousel-dark .carousel-nav span{background-image:url(images/carousel/darkh.png);}.glow151-carousel .carousel-window{float:left;}.glow151-carousel .carousel-nav{float:left;overflow:hidden;}.glow151-carousel .carousel-nav span{background-repeat:no-repeat;}.glow151-carousel .carousel-nav .carousel-background{top:0;background-position:-60px 0;background-repeat:repeat-y;}.glow151-carousel .carousel-nav .carousel-top{top:0;background-position:0 0;}.glow151-carousel .carousel-nav .carousel-bottom{bottom:0;background-position:-30px bottom;}.glow151-carousel .carousel-nav .carousel-arrow{width:10px;top:0;left:10px;background-repeat:no-repeat;}.glow151-carousel .carousel-prev .carousel-arrow{background-position:-90px center;}.glow151-carousel .carousel-next .carousel-arrow{background-position:-101px center;}.glow151-carousel .carousel-prev-disabled,.glow151-carousel .carousel-next-disabled,.glow151-vCarousel .carousel-prev-disabled,.glow151-vCarousel .carousel-next-disabled{cursor:default;}.glow151-carousel .carousel-prev-disabled .carousel-arrow{background-position:-112px center;}.glow151-carousel .carousel-next-disabled .carousel-arrow{background-position:-123px center;}.glow151-vCarousel .carousel-light .carousel-window{background:#f0f0f0;}.glow151-vCarousel .carousel-light .carousel-nav{background:#f0f0f0;color:#000;height:30px;}.glow151-vCarousel .carousel-light .carousel-nav span{background-image:url(images/carousel/lightv.png);}.glow151-vCarousel .carousel-dark .carousel-window{background:#282828;}.glow151-vCarousel .carousel-dark .carousel-nav{background:#282828;color:#fff;height:30px;}.glow151-vCarousel .carousel-dark .carousel-nav span{background-image:url(images/carousel/darkv.png);}.glow151-vCarousel .carousel-nav span{background-repeat:no-repeat;overflow:hidden;}.glow151-vCarousel .carousel-nav .carousel-background{background-position:0 -60px;background-repeat:repeat-x;}.glow151-vCarousel .carousel-nav .carousel-top{background-position:right 0;}.glow151-vCarousel .carousel-nav .carousel-bottom{background-position:left -30px;}.glow151-vCarousel .carousel-nav .carousel-arrow{top:10px;height:10px;}.glow151-vCarousel .carousel-prev .carousel-arrow{background-position:center -90px;}.glow151-vCarousel .carousel-next .carousel-arrow{background-position:center -101px;}.glow151-vCarousel .carousel-prev-disabled .carousel-arrow{background-position:center -112px;}.glow151-vCarousel .carousel-next-disabled .carousel-arrow{background-position:center -123px;}.glow151-vCarousel .carousel-window.paged{float:left;}.glow151-carousel .pageNav,.glow151-vCarousel .pageNav{display:block;padding:0;height:20px;width:auto;clear:both;margin:0 auto;}.glow151-vCarousel .pageNav{height:auto;float:left;width:20px;clear:none;margin:auto 0;}.glow151-carousel .dotLabel,.glow151-vCarousel .dotLabel{width:20px;height:20px;overflow:hidden;text-indent:-5000px;padding:0;margin:0;display:block;}.glow151-basic .glow151-carousel .dot .dotLabel,.glow151-basic .glow151-vCarousel .dot .dotLabel,{overflow:visible;text-indent:0;}.glow151-carousel .pageNav li,.glow151-vCarousel .pageNav li{width:20px;height:20px;padding:0;margin:0;float:left;list-style-type:none;color:#000;background-image:url(images/carousel/lightdots.png);background-repeat:no-repeat;background-position:-20px 0;}.glow151-carousel .pageNav .dot{cursor:pointer;}.glow151-carousel .carousel-dark .pageNav li,.glow151-vCarousel .carousel-dark .pageNav li{color:#fff;background-image:url(images/carousel/darkdots.png);}.glow151-vCarousel .pageNav li{background-image:url(images/carousel/lightdotsv.png);float:none;}.glow151-vCarousel .carousel-dark .pageNav li{background-image:url(images/carousel/darkdotsv.png);}.glow151-carousel .pageNav li.dotActive,.glow151-vCarousel .pageNav li.dotActive{background-position:-20px -20px;}.glow151-basic .glow151-carousel .pageNav li.dotActive,.glow151-basic .glow151-vCarousel .pageNav li.dotActive{text-decoration:underline;}.glow151-carousel .pageNav li#rightarrow,.glow151-vCarousel .pageNav li#rightarrow{background-position:-40px -20px;}.glow151-basic .glow151-carousel .pageNav li#rightarrow .dotLabel{width:0;height:0;border-top:8px solid #fff;border-right:8px solid #fff;border-bottom:8px solid #fff;border-left:8px solid #000;}.glow151-basic .glow151-vCarousel .pageNav li#rightarrow .dotLabel{width:0;height:0;border-top:8px solid #000;border-right:8px solid #fff;border-bottom:8px solid #fff;border-left:8px solid #fff;}.glow151-carousel .pageNav li.carousel-next-disabled#rightarrow,.glow151-vCarousel .pageNav li.carousel-next-disabled#rightarrow{background-position:-40px 0;}.glow151-basic .glow151-carousel .pageNav li.carousel-next-disabled#rightarrow .dotLabel,.glow151-basic .glow151-vCarousel .pageNav li.carousel-next-disabled#rightarrow .dotLabel{width:0;height:0;border-top:8px solid #fff;border-right:8px solid #fff;border-bottom:8px solid #fff;border-left:8px solid #ccc;}.glow151-basic .glow151-vCarousel .pageNav li.carousel-next-disabled#rightarrow .dotLabel{width:0;height:0;border-top:8px solid #ccc;border-right:8px solid #fff;border-bottom:8px solid #fff;border-left:8px solid #fff;}.glow151-carousel .pageNav li#leftarrow,.glow151-vCarousel .pageNav li#leftarrow{background-position:0 -20px;}.glow151-basic .glow151-carousel .pageNav li#leftarrow .dotLabel{width:0;height:0;border-top:8px solid #fff;border-right:8px solid #000;border-bottom:8px solid #fff;border-left:8px solid #fff;}.glow151-basic .glow151-vCarousel .pageNav li#leftarrow .dotLabel{width:0;height:0;border-top:8px solid #fff;border-right:8px solid #fff;border-bottom:8px solid #000;border-left:8px solid #fff;}.glow151-carousel .pageNav li.carousel-prev-disabled#leftarrow,.glow151-vCarousel .pageNav li.carousel-prev-disabled#leftarrow{background-position:0 0;}.glow151-basic .glow151-carousel .pageNav li.carousel-prev-disabled#leftarrow .dotLabel{width:0;height:0;border-top:8px solid #fff;border-right:8px solid #ccc;border-bottom:8px solid #fff;border-left:8px solid #fff;}.glow151-basic .glow151-vCarousel .pageNav li.carousel-prev-disabled#leftarrow .dotLabel{width:0;height:0;border-top:8px solid #fff;border-right:8px solid #fff;border-bottom:8px solid #ccc;border-left:8px solid #fff;}.glow151-slider,.glow151-vSlider{position:relative;zoom:1;}.glow151-slider .slider-container,.glow151-vSlider .slider-container{position:relative;zoom:1;padding:4px 17px 6px 17px;}.glow151-slider:focus,.glow151-vSlider:focus{outline:none;border:none;}.glow151-slider .slider-track,.glow151-vSlider .slider-track{position:relative;font-size:0;zoom:1;}.glow151-slider .slider-trackOff,.glow151-vSlider .slider-trackOff{height:8px;background:url(images/slider/lighth.png) 100% 0;margin-left:2px;font-size:0;}.glow151-slider .slider-trackOn,.glow151-vSlider .slider-trackOn{position:absolute;height:8px;top:0;left:0;background:url(images/slider/lighth.png) -2px -8px;z-index:2;overflow:hidden;}.glow151-slider .slider-handle,.glow151-vSlider .slider-handle{position:absolute;top:-4px;width:17px;height:18px;background:url(images/slider/lighth.png) 0 -16px;z-index:3;cursor:pointer;}.glow151-slider .slider-btn-bk,.glow151-slider .slider-btn-fwd,.glow151-vSlider .slider-btn-bk,.glow151-vSlider .slider-btn-fwd{position:absolute;width:12px;height:12px;top:2px;left:0;background:url(images/slider/lighth.png) 0 -34px;font-size:0;cursor:pointer;}.glow151-slider .slider-btn-fwd{background-position:-12px -34px;left:100%;margin-left:-12px;}.glow151-slider .slider-active .slider-handle{background-position:-17px -16px;}.glow151-slider .slider-disabled .slider-handle{background-position:-34px -16px;}.glow151-slider .slider-disabled .slider-btn-bk{background-position:-24px -34px;}.glow151-slider .slider-disabled .slider-btn-fwd{background-position:-36px -34px;}.glow151-slider .slider-disabled .slider-trackOn{background-position:-2px 0;}.glow151-slider .slider-disabled .slider-btn-bk,.glow151-slider .slider-disabled .slider-btn-fwd,.glow151-slider .slider-disabled .slider-handle,.glow151-vSlider .slider-disabled .slider-btn-bk,.glow151-vSlider .slider-disabled .slider-btn-fwd,.glow151-vSlider .slider-disabled .slider-handle{cursor:auto;}.glow151-slider .slider-noButtons .slider-container{padding-left:0;padding-right:0;}.glow151-slider .slider-noButtons .slider-btn-bk,.glow151-slider .slider-noButtons .slider-btn-fwd,.glow151-vSlider .slider-noButtons .slider-btn-bk,.glow151-vSlider .slider-noButtons .slider-btn-fwd{display:none;}.glow151-slider .slider-labels{margin:10px 8px 0;}.glow151-vSlider .slider-labels{position:absolute;left:22px;top:25px;}.glow151-vSlider .slider-noButtons .slider-labels{top:8px;}.glow151-slider .slider-labels .ruler-tickMajor,.glow151-slider .slider-labels .ruler-tickMinor,.glow151-vSlider .slider-labels .ruler-tickMajor,.glow151-vSlider .slider-labels .ruler-tickMinor{background:#6e6e6e;}.glow151-slider .slider-labels .ruler-label,.glow151-vSlider .slider-labels .ruler-label{color:#6e6e6e;cursor:pointer;}.glow151-vSlider .slider-trackOff,.glow151-vSlider .slider-trackOn,.glow151-vSlider .slider-handle,.glow151-vSlider .slider-btn-bk,.glow151-vSlider .slider-btn-fwd{background-image:url(images/slider/lightv.png);}.glow151-vSlider .slider-container{padding:17px 0;}.glow151-vSlider .slider-track{margin:0;overflow:hidden;zoom:1;width:20px;}.glow151-vSlider .slider-trackOff{width:8px;height:auto;background-position:0 0;position:absolute;margin:0;left:4px;top:0;z-index:2;}.glow151-vSlider .slider-trackOn{width:8px;height:0;position:relative;margin:0 0 0 4px;background-position:-8px 100%;top:2px;left:0;z-index:1;}.glow151-vSlider .slider-handle{top:0;left:0;width:18px;height:17px;background-position:-16px 100%;}.glow151-vSlider .slider-btn-bk,.glow151-vSlider .slider-btn-fwd{top:0;left:2px;background-position:-34px -1976px;}.glow151-vSlider .slider-btn-bk{background-position:-34px 100%;left:2px;top:100%;margin:-12px 0 0 0;}.glow151-vSlider .slider-active .slider-handle{background-position:-16px -1966px;}.glow151-vSlider .slider-disabled .slider-handle{background-position:-16px -1949px;}.glow151-vSlider .slider-disabled .slider-btn-bk{background-position:-34px -1964px;}.glow151-vSlider .slider-disabled .slider-btn-fwd{background-position:-34px -1952px;}.glow151-vSlider .slider-disabled .slider-trackOn{background-position:0 100%;}.glow151-vSlider .slider-noButtons .slider-container{padding-top:0;padding-bottom:0;}.glow151-slider .slider-dark .slider-trackOff,.glow151-slider .slider-dark .slider-trackOn,.glow151-slider .slider-dark .slider-handle,.glow151-slider .slider-dark .slider-btn-bk,.glow151-slider .slider-dark .slider-btn-fwd{background-image:url(images/slider/darkh.png);}.glow151-vSlider .slider-dark .slider-trackOff,.glow151-vSlider .slider-dark .slider-trackOn,.glow151-vSlider .slider-dark .slider-handle,.glow151-vSlider .slider-dark .slider-btn-bk,.glow151-vSlider .slider-dark .slider-btn-fwd{background-image:url(images/slider/darkv.png);}.glow151-slider .slider-dark .slider-labels .ruler-tickMajor,.glow151-slider .slider-dark .slider-labels .ruler-tickMinor,.glow151-vSlider .slider-dark .slider-labels .ruler-tickMajor,.glow151-vSlider .slider-dark .slider-labels .ruler-tickMinor{background:#e2e2e2;}.glow151-slider .slider-dark .slider-labels .ruler-label,.glow151-vSlider .slider-dark .slider-labels .ruler-label{color:#e2e2e2;}.glow151-basic .glow151-slider .slider-handle,.glow151-basic .glow151-vSlider .slider-handle{border-style:solid;border-color:#777 #fff;border-width:18px 8px 0 9px;width:0;height:0;font-size:0;}.glow151-basic .glow151-vSlider .slider-handle{border-width:8px 0 9px 18px;border-color:#fff #777;}.glow151-basic .glow151-slider .slider-trackOff,.glow151-basic .glow151-vSlider .slider-trackOff{background:#d6d6d6;}.glow151-basic .glow151-slider .slider-trackOn,.glow151-basic .glow151-vSlider .slider-trackOn{background:#777;}.glow151-basic .glow151-slider .slider-btn-bk,.glow151-basic .glow151-slider .slider-btn-fwd,.glow151-basic .glow151-vSlider .slider-btn-bk,.glow151-basic .glow151-vSlider .slider-btn-fwd{border-style:solid;border-color:#fff #777;border-width:6px 0 6px 12px;width:0;height:0;}.glow151-basic .glow151-slider .slider-btn-bk{border-width:6px 12px 6px 0;}.glow151-basic .glow151-vSlider .slider-btn-bk{border-color:#666 #fff;border-width:12px 6px 0 6px;}.glow151-basic .glow151-vSlider .slider-btn-fwd{border-color:#666 #fff;border-width:0 6px 12px 6px;}.glow151-basic .glow151-slider .slider-active .slider-handle{border-color:#000 #fff;}.glow151-basic .glow151-vSlider .slider-active .slider-handle{border-color:#fff #000;}.glow151-basic .glow151-slider .slider-disabled .slider-trackOn,.glow151-basic .glow151-vSlider .slider-disabled .slider-trackOn{background:#d6d6d6;}.glow151-basic .glow151-slider .slider-disabled .slider-handle{border-color:#d6d6d6 #fff;}.glow151-basic .glow151-vSlider .slider-disabled .slider-handle{border-color:#fff #d6d6d6;}.glow151-basic .glow151-slider .slider-disabled .slider-btn-bk,.glow151-basic .glow151-slider .slider-disabled .slider-btn-fwd{border-color:#fff #d6d6d6;}.glow151-basic .glow151-vSlider .slider-disabled .slider-btn-bk,.glow151-basic .glow151-vSlider .slider-disabled .slider-btn-fwd{border-color:#d6d6d6 #fff;}.glow151-basic .glow151-slider .slider-dark .slider-handle{border-color:#ccc #000;}.glow151-basic .glow151-vSlider .slider-dark .slider-handle{border-color:#000 #ccc;}.glow151-basic .glow151-slider .slider-dark .slider-trackOff,.glow151-basic .glow151-vSlider .slider-dark .slider-trackOff{background:#555;}.glow151-basic .glow151-slider .slider-dark .slider-trackOn,.glow151-basic .glow151-vSlider .slider-dark .slider-trackOn{background:#ccc;}.glow151-basic .glow151-slider .slider-dark .slider-btn-bk,.glow151-basic .glow151-slider .slider-dark .slider-btn-fwd{border-color:#000 #ccc;}.glow151-basic .glow151-vSlider .slider-dark .slider-btn-bk,.glow151-basic .glow151-vSlider .slider-dark .slider-btn-fwd{border-color:#ccc #000;}.glow151-basic .glow151-slider .slider-dark .slider-active .slider-handle{border-color:#fff #000;}.glow151-basic .glow151-vSlider .slider-dark .slider-active .slider-handle{border-color:#000 #fff;}.glow151-basic .glow151-slider .slider-dark .slider-disabled .slider-trackOn,.glow151-basic .glow151-vSlider .slider-dark .slider-disabled .slider-trackOn{background:#555;}.glow151-basic .glow151-slider .slider-dark .slider-disabled .slider-handle{border-color:#555 #000;}.glow151-basic .glow151-vSlider .slider-dark .slider-disabled .slider-handle{border-color:#000 #555;}.glow151-basic .glow151-slider .slider-dark .slider-disabled .slider-btn-bk,.glow151-basic .glow151-slider .slider-dark .slider-disabled .slider-btn-fwd{border-color:#000 #555;}.glow151-basic .glow151-vSlider .slider-dark .slider-disabled .slider-btn-bk,.glow151-basic .glow151-vSlider .slider-dark .slider-disabled .slider-btn-fwd{border-color:#555 #000;}.glow151-ruler,.glow151-vRuler{position:relative;zoom:1;}.glow151-ruler .ruler-tickMajor,.glow151-ruler .ruler-tickMinor,.glow151-vRuler .ruler-tickMajor,.glow151-vRuler .ruler-tickMinor{position:absolute;background:#000;width:1px;height:1px;top:0;left:0;overflow:hidden;}.glow151-ruler .ruler-tickMajor,.glow151-ruler .ruler-spacer{height:10px;}.glow151-ruler .ruler-tickMinor{height:2px;}.glow151-vRuler .ruler-tickMajor{width:10px;}.glow151-vRuler .ruler-tickMinor{width:2px;}.glow151-ruler .ruler-labels{position:relative;height:1.2em;}.glow151-vRuler .ruler-labels{position:absolute;top:0;left:15px;height:100%;}.glow151-ruler .ruler-label,.glow151-vRuler .ruler-label{position:absolute;top:0;left:0;}.glow151-ruler .ruler-label{text-align:center;}.glow151-ruler .ruler-label span{position:relative;left:-50%;}.glow151-vRuler .ruler-label{margin-top:-0.56em;}.glow151-vTimetable .timetable-container,.glow151-Timetable .timetable-container{overflow:hidden;position:relative;zoom:1;}.glow151-vTimetable .timetable-innerView,.glow151-Timetable .timetable-innerView{background:#fff;overflow:hidden;position:relative;zoom:1;}.glow151-vTimetable .timetable-innerView{float:left;}.glow151-vTimetable .timetable-dragArea,.glow151-vTimetable .timetable-track,.glow151-vTimetable .timetable-item,.glow151-vTimetable .timetable-bandOdd,.glow151-vTimetable .timetable-bandEven,.glow151-vTimetable .timetable-scale,.glow151-vTimetable .timetable-scaleItem,.glow151-Timetable .timetable-dragArea,.glow151-Timetable .timetable-track,.glow151-Timetable .timetable-item,.glow151-Timetable .timetable-bandOdd,.glow151-Timetable .timetable-bandEven,.glow151-Timetable .timetable-scale,.glow151-Timetable .timetable-scaleItem{position:absolute;top:0;left:0;overflow:hidden;width:100%;height:100%;z-index:2;}.glow151-vTimetable .timetable-dragRange,.glow151-Timetable .timetable-dragRange{height:100%;width:100%;}.glow151-vTimetable .timetable-track,.glow151-Timetable .timetable-track{border:1px solid #a0a0a0;border-width:0 1px;margin-right:2px;}.glow151-vTimetable .timetable-track{border-width:0 1px;margin-right:1px;}.glow151-Timetable .timetable-track{border-width:1px 0;margin-bottom:1px;}.glow151-vTimetable .timetable-scale,.glow151-Timetable .timetable-scale{color:#a0a0a0;font-size:x-large;border:1px solid #ccc;}.glow151-vTimetable .timetable-scale{text-align:center;}.glow151-vTimetable .timetable-scalePrimary{border-width:0 0 0 1px;}.glow151-vTimetable .timetable-scaleSecondary{border-width:0 1px 0 0;}.glow151-Timetable .timetable-scalePrimary{border-width:1px 0 0 0;}.glow151-Timetable .timetable-scaleSecondary{border-width:0 0 1px 0;}.glow151-vTimetable .timetable-track-footers{clear:both;}.glow151-vTimetable .timetable-accessibility-navigation,.glow151-Timetable .timetable-accessibility-navigation,.glow151-vTimetable .timetable-accessibility-hidden,.glow151-Timetable .timetable-accessibility-hidden{position:absolute;height:1px;width:1px;overflow:hidden;}.glow151-vTimetable .timetable-track-headers,.glow151-vTimetable .timetable-track-footers,.glow151-Timetable .timetable-track-headers,.glow151-Timetable .timetable-track-footers{position:relative;}.glow151-vTimetable .timetable-header-holder,.glow151-vTimetable .timetable-footer-holder,.glow151-Timetable .timetable-header-holder,.glow151-Timetable .timetable-footer-holder{position:absolute;top:0;left:0;overflow:hidden;color:#fff;background:#424242;text-align:center;}.glow151-Timetable .timetable-track-headers,.glow151-Timetable .timetable-track-footers,.glow151-Timetable .timetable-scrollView{float:left;}.glow151-Timetable .timetable-header-holder,.glow151-Timetable .timetable-footer-holder{width:100px;}.glow151-vTimetable .timetable-track-headers,.glow151-vTimetable .timetable-track-footers,.glow151-Timetable .timetable-track-headers,.glow151-Timetable .timetable-track-footers{background:#fff;border:1px solid #ccc;}.glow151-vTimetable .timetable-track-headers{border-bottom-width:0;}.glow151-vTimetable .timetable-track-footers{border-top-width:0;}.glow151-Timetable .timetable-track-headers{border-right-width:0;}.glow151-Timetable .timetable-track-footers{border-left-width:0;}.glow151-vTimetable .timetable-track-headers div.timetable-header-content,.glow151-vTimetable .timetable-track-footers div.timetable-footer-content,.glow151-Timetable .timetable-track-headers div.timetable-header-content,.glow151-Timetable .timetable-track-footers div.timetable-footer-content{padding:9px 0;}.glow151-Timetable .timetable-track-headers div.timetable-header-content,.glow151-Timetable .timetable-track-footers div.timetable-footer-content{padding:4px 5px;text-align:left;}.glow151-vTimetable .timetable-trackList,.glow151-Timetable .timetable-trackList{margin:0!important;padding:0!important;list-style:none;}.glow151-vTimetable .timetable-item,.glow151-Timetable .timetable-item{border:1px solid #ccc;margin:0!important;padding:0!important;display:block;}.glow151-vTimetable .timetable-item{border-width:1px 0;}.glow151-Timetable .timetable-item{border-width:0 1px;}.glow151-vTimetable .timetable-itemHangingOffStart .timetable-itemContent,.glow151-Timetable .timetable-itemHangingOffStart .timetable-itemContent{opacity:.3;filter:alpha(opacity=30);zoom:1;}.glow151-vTimetable .timetable-itemHangingClipping .timetable-itemContent,.glow151-Timetable .timetable-itemHangingClipping .timetable-itemContent{height:1px;overflow:hidden;}.glow151-vTimetable .timetable-hideitems .timetable-item{display:none;}.glow151-vTimetable .timetable-bandOdd,.glow151-vTimetable .timetable-bandEven,.glow151-Timetable .timetable-bandOdd,.glow151-Timetable .timetable-bandEven{z-index:1;}.glow151-vTimetable .timetable-bandOdd,.glow151-Timetable .timetable-bandOdd{background:#ebebeb;}.glow151-vTimetable .timetable-default-track-header{position:absolute;height:1px;width:1px;overflow:hidden;}.glow151-vTimetable .timetable-scrollbar1,.glow151-vTimetable .timetable-scrollbar2,.glow151-Timetable .timetable-scrollbar1,.glow151-Timetable .timetable-scrollbar2{display:none;}.glow151-vTimetable .timetable-scrollbar1,.glow151-vTimetable .timetable-scrollbar2{float:left;_width:0;}.glow151-vTimetable .timetable-scrollbar .slider-container,.glow151-Timetable .timetable-scrollbar .slider-container{padding:21px 0;border:1px solid #a0a0a0;}.glow151-Timetable .timetable-scrollbar .slider-container{padding:0 21px;}.glow151-vTimetable .timetable-scrollbar2 .slider-container{border-left-width:0;}.glow151-vTimetable .timetable-scrollbar1 .slider-container{border-right-width:0;}.glow151-Timetable .timetable-scrollbar2 .slider-container{border-top-width:0;}.glow151-Timetable .timetable-scrollbar1 .slider-container{border-bottom-width:0;}.glow151-vTimetable .timetable-scrollbar .slider-handle,.glow151-Timetable .timetable-scrollbar .slider-handle{width:100%;height:100%;background:#fff;opacity:0;filter:alpha(opacity=0);top:0;}.glow151-vTimetable .timetable-scrollbar .slider-track,.glow151-Timetable .timetable-scrollbar .slider-track{background:none;}.glow151-vTimetable .timetable-scrollbarLabels,.glow151-Timetable .timetable-scrollbarLabels{position:absolute;width:100%;height:100%;background:#fff;color:#227db7;font-size:small;text-align:center;top:0;left:0;overflow:hidden;}.glow151-vTimetable .slider-labels,.glow151-Timetable .slider-labels{display:none;}.glow151-vTimetable .timetable-scrollbarLabelsHighlight,.glow151-Timetable .timetable-scrollbarLabelsHighlight{background:#424242;color:#fff;z-index:1;overflow:hidden;}.glow151-vTimetable .timetable-scrollbar .slider-trackOn,.glow151-vTimetable .timetable-scrollbar .slider-trackOff,.glow151-Timetable .timetable-scrollbar .slider-trackOn,.glow151-Timetable .timetable-scrollbar .slider-trackOff{background:none;}.glow151-vTimetable .timetable-scrollbarItem,.glow151-Timetable .timetable-scrollbarItem{width:100%;height:100%;position:absolute;top:0;left:0;}.glow151-vTimetable .timetable-scrollbar .slider-btn-bk,.glow151-vTimetable .timetable-scrollbar .slider-btn-fwd,.glow151-Timetable .timetable-scrollbar .slider-btn-bk,.glow151-Timetable .timetable-scrollbar .slider-btn-fwd{background:url(images/timetable/lightv.png) top center;border:1px solid #a0a0a0;}.glow151-vTimetable .timetable-scrollbar .slider-btn-bk,.glow151-vTimetable .timetable-scrollbar .slider-btn-fwd{height:20px;left:0;border-width:0 0 1px 0;}.glow151-Timetable .timetable-scrollbar .slider-btn-bk,.glow151-Timetable .timetable-scrollbar .slider-btn-fwd{background:url(images/timetable/lighth.png) right center;width:20px;top:0;border-width:0 0 0 1px;}.glow151-vTimetable .timetable-scrollbar .slider-btn-bk{background-position:center bottom;border-width:1px 0 0 0;margin:-21px 0 0;}.glow151-Timetable .timetable-scrollbar .slider-btn-bk{background-position:left center;border-width:0 1px 0 0;z-index:1;}.glow151-Timetable .timetable-scrollbar .slider-btn-fwd{margin-left:-21px;}.glow151-vTimetable .timetable-dark .timetable-scrollbarLabels,.glow151-Timetable .timetable-dark .timetable-scrollbarLabels{background-color:#333;}.glow151-vTimetable .timetable-dark .timetable-header-holder,.glow151-vTimetable .timetable-dark .timetable-footer-holder,.glow151-Timetable .timetable-dark .timetable-header-holder,.glow151-Timetable .timetable-dark .timetable-footer-holder,.glow151-vTimetable .timetable-dark .timetable-scrollbarLabelsHighlight,.glow151-Timetable .timetable-dark .timetable-scrollbarLabelsHighlight{background:#121212;}.glow151-vTimetable .timetable-dark .timetable-innerView,.glow151-Timetable .timetable-dark .timetable-innerView{background:#333;color:#ccc;}.glow151-vTimetable .timetable-dark .timetable-scale,.glow151-Timetable .timetable-dark .timetable-scale{color:#ccc;}.glow151-vTimetable .timetable-dark .timetable-item,.glow151-Timetable .timetable-dark .timetable-item,.glow151-vTimetable .timetable-dark .timetable-track,.glow151-Timetable .timetable-dark .timetable-track,.glow151-vTimetable .timetable-dark .timetable-scrollbar .slider-btn-bk,.glow151-vTimetable .timetable-dark .timetable-scrollbar .slider-btn-fwd,.glow151-Timetable .timetable-dark .timetable-scrollbar .slider-btn-bk,.glow151-Timetable .timetable-dark .timetable-scrollbar .slider-btn-fwd,.glow151-vTimetable .timetable-dark .timetable-scrollbar .slider-container,.glow151-Timetable .timetable-dark .timetable-scrollbar .slider-container,.glow151-vTimetable .timetable-dark .timetable-scale,.glow151-Timetable .timetable-dark .timetable-scale{border-color:#141414;}.glow151-vTimetable .timetable-dark .timetable-bandOdd,.glow151-Timetable .timetable-dark .timetable-bandOdd{background-color:#242424;}.glow151-vTimetable .timetable-dark .timetable-scrollbar .slider-btn-bk,.glow151-vTimetable .timetable-dark .timetable-scrollbar .slider-btn-fwd{background-image:url(images/timetable/darkv.png);}.glow151-Timetable .timetable-dark .timetable-scrollbar .slider-btn-bk,.glow151-Timetable .timetable-dark .timetable-scrollbar .slider-btn-fwd{background-image:url(images/timetable/darkh.png);}.glow151-vTimetable .timetable-dark .timetable-track-headers,.glow151-vTimetable .timetable-dark .timetable-track-footers,.glow151-Timetable .timetable-dark .timetable-track-headers,.glow151-Timetable .timetable-dark .timetable-track-footers{border:none;background:#333;}.glow151-editor{border:1px solid #000;}.glow151-editor .editor-toolbar{border:none;display:block;margin:0;padding:0;background-color:#EFEFEF;}.glow151-editor .editor-toolbar legend{display:none;}.glow151-editor .editor-toolbar-tools h3{display:none;}.glow151-editor ul.editor-toolbar-tools{display:block;margin:0!important;padding:9px!important;overflow:hidden;zoom:1;}.glow151-editor ul.editor-toolbar-tools li{list-style:none;float:left;display:block;width:26px;margin:0!important;padding:0!important;}.glow151-editor .editor-toolbar-button{display:block;margin:0;padding:0;width:26px;overflow:hidden;}.glow151-editor .editor-toolbar-button a{outline:none;text-decoration:none;}.glow151-editor .editor-toolbar-button img{border:none;}.glow151-editor .editor-toolbar-icon{display:block;width:26px;height:24px;}.glow151-editor .editor-toolbar-icon span{display:block;height:1px;overflow:hidden;width:1px;}.glow151-editor .editor-toolbar-icon span{display:block;height:1px;overflow:hidden;width:1px;}.glow151-editor .editor-toolbar-item .bold-button{background-image:url(images/editor/button-bold.png);}.glow151-editor .editor-toolbar-item .default .bold-button{background-position:0 0;}.glow151-editor .editor-toolbar-item .disabled .bold-button{background-position:0 -28px;}.glow151-editor .editor-toolbar-item .active .bold-button{background-position:0 -85px;}.glow151-editor .editor-toolbar-item .hover .bold-button{background-position:0 -56px;}.glow151-editor .editor-toolbar-item .italics-button{background-image:url(images/editor/button-italics.png);}.glow151-editor .editor-toolbar-item .default .italics-button{background-position:0 0;}.glow151-editor .editor-toolbar-item .disabled .italics-button{background-position:0 -28px;}.glow151-editor .editor-toolbar-item .active .italics-button{background-position:0 -85px;}.glow151-editor .editor-toolbar-item .hover .italics-button{background-position:0 -56px;}.glow151-editor .editor-toolbar-item .strike-button{background-image:url(images/editor/button-strike.png);}.glow151-editor .editor-toolbar-item .default .strike-button{background-position:0 0;}.glow151-editor .editor-toolbar-item .disabled .strike-button{background-position:0 -28px;}.glow151-editor .editor-toolbar-item .active .strike-button{background-position:0 -85px;}.glow151-editor .editor-toolbar-item .hover .strike-button{background-position:0 -56px;}.glow151-editor .editor-toolbar-item .blockquote-button{background-image:url(images/editor/button-blockquote.png);}.glow151-editor .editor-toolbar-item .default .blockquote-button{background-position:0 0;}.glow151-editor .editor-toolbar-item .disabled .blockquote-button{background-position:0 -28px;}.glow151-editor .editor-toolbar-item .active .blockquote-button{background-position:0 -85px;}.glow151-editor .editor-toolbar-item .hover .blockquote-button{background-position:0 -56px;}.glow151-editor .glow151-hidden{position:absolute;left:-2500px;width:1px;}.glow151-basic .glow151-editor .editor-toolbar-icon span{border:none;height:auto;overflow:normal;width:auto;}.glow151-basic .glow151-editor .editor-toolbar-item .hover span span{border:1px solid #000;}.glow151-editor iframe{border:0;margin:0;padding:0;width:100%;}.editor-dark .editor-toolbar{background:#333;color:#fff;}.editor-dark .editor-toolbar-item .bold-button{background-image:url(images/editor/dark-button-bold.png);}.editor-dark .editor-toolbar-item .italics-button{background-image:url(images/editor/dark-button-italics.png);}.editor-dark .editor-toolbar-item .strike-button{background-image:url(images/editor/dark-button-strike.png);}.editor-dark .editor-toolbar-item .blockquote-button{background-image:url(images/editor/dark-button-blockquote.png);} \ No newline at end of file diff --git a/horde/js/glow/1.5.1/widgets/widgets.debug.css b/horde/js/glow/1.5.1/widgets/widgets.debug.css deleted file mode 100644 index 0f4c58c8a..000000000 --- a/horde/js/glow/1.5.1/widgets/widgets.debug.css +++ /dev/null @@ -1,1780 +0,0 @@ -/* - Copyright 2009 British Broadcasting Corporation - - 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. -*/ -.glow151-cssTest { - background: url(images/darkpanel/ctr.png); - z-index: 1234; -} - -/* preloader */ - .glow151-panel-preload { - height: 1px; - overflow: hidden; - position: absolute; - top: -10px; - width: 1px; - left: -5px; - } - .glow151-panel-preload .panel-close { - display: none; - visibility: hidden; - } - .glow151-panel-preload .infoPanel-pointerT, - .glow151-panel-preload .infoPanel-pointerR, - .glow151-panel-preload .infoPanel-pointerB, - .glow151-panel-preload .infoPanel-pointerL { - display: block; - } -/*end */ -.glow151-overlay { - visibility: hidden; - display: none; - overflow: hidden; - position: absolute; -} - -/* stop the dotted line around the overlay focal point when it has focus */ -.glow151-overlay .overlay-focalPoint:focus { - outline: none; -} -.glow151-overlay .overlay-focalPoint { - overflow: hidden; - width: 0; - height: 0; - position: absolute; -} - -/*hide stuff offscreen*/ -.glow151-overlay .panelAccess { - position: absolute; - overflow: hidden; - height: 0; - width: 0; - top: -5000px; - left: -5000px; -} -/*PANEL*/ - .glow151-panel .c { - background: #57585A url(images/darkpanel/bg.gif) repeat-x bottom; - color: #fff; - margin: 0 10px; - } - .glow151-panel .tr, .glow151-panel .tl, .glow151-panel .br, .glow151-panel .bl { - height: 14px; - overflow: hidden; - } - .glow151-panel .tr, .glow151-panel .br { - float: right; - width: 14px; - } - /* we couldn't sprite these because of IE being a pain */ - .glow151-panel .tr { background: url(images/darkpanel/ctr.png); } - .glow151-panel .tl { background: url(images/darkpanel/ctl.png); } - .glow151-panel .bl { background: url(images/darkpanel/cbl.png); } - .glow151-panel .br { background: url(images/darkpanel/cbr.png); } - - .glow151-panel .tl, .glow151-panel .bl { - width: 14px; - float: left; - } - .glow151-panel .tb, .glow151-panel .bb { - border: 4px solid #57585A; - border-width: 0 0 4px 0; - overflow: hidden; - font-size: 0; - } - .glow151-panel .tb div, .glow151-panel .bb div { - height: 10px; - background: #fff; - opacity: 0.5; - font-size: 0; - } - .glow151-panel .bb { - border-width: 4px 0 0 0; - border-color: #424243; - } - .glow151-panel .tc { - overflow: hidden; - position: relative; - zoom: 1; - } - .glow151-panel .bars { - position: absolute; - width: 100%; - padding-left: 10px; - height: 2000px; - background: #fff; - opacity: 0.5; - margin-left: -10px; - } - /* - annoying firefox bug - it sometimes doesn't obey the overflow by one px if - the opacity isn't 1. Firefox users get an extra http request. Oh well. -J - */ - html .glow151-gecko .glow151-panel .bars { - background: url(images/darkpanel/transbg.png); - opacity: 1; - } - .glow151-panel .c { - position: relative; - min-height: 50px; - overflow: auto; - } - - .glow151-panel .panel-hd { - border-bottom: 1px solid #B0B0B0; - padding: 5px 10px 10px; - font-weight: bold; - } - .glow151-panel .panel-hd * { - margin: 0; padding: 0; - } - - .glow151-panel .panel-bd, .glow151-panel .panel-ft { - margin: 0 10px; - } - - .glow151-panel .panel-close { - background: url(images/darkpanel/close.png); - height: 20px; - width: 20px; - float: right; - position: relative; - margin: 5px 10px 0 0; - text-indent: -5000px; - } - - /*without header*/ - .glow151-overlay .panel-noHeader .panel-hd { - display: none; - } - /*end*/ - - - /*without images*/ - .glow151-basic .glow151-panel .panel-close { - text-indent: 0; - color: #fff; - border: 1px solid #fff; - text-align: center; - } - .glow151-basic .glow151-panel .tb, - .glow151-basic .glow151-panel .bb, - .glow151-basic .glow151-panel .bars { - visibility: hidden; - } - /*end*/ - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - This block of CSS rules for IE only has been moved to panel.js to be output directly to - the page. It is there so that the path to the PNGs can be deduced from the location of - the widgets css file. - It is acceptable because this design will not be altered before version 2 of glow, and - at that point it will be modified to avoid the use of PNGs. - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - - .glow151-ie .glow151-panel .tr { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/darkpanel/ctr.png', sizingMethod='crop'); - } - .glow151-ie .glow151-panel .tl { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/darkpanel/ctl.png', sizingMethod='crop'); - } - .glow151-ie .glow151-panel .bl { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/darkpanel/cbl.png', sizingMethod='crop'); - } - .glow151-ie .glow151-panel .br { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/darkpanel/cbr.png', sizingMethod='crop'); - } - .glow151-ie .glow151-panel .infoPanel-pointerT { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/darkpanel/at.png', sizingMethod='crop'); - } - .glow151-ie .glow151-panel .infoPanel-pointerR { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/darkpanel/ar.png', sizingMethod='crop'); - } - .glow151-ie .glow151-panel .infoPanel-pointerB { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/darkpanel/ab.png', sizingMethod='crop'); - } - .glow151-ie .glow151-panel .infoPanel-pointerL { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/darkpanel/al.png', sizingMethod='crop'); - } - - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - .glow151-ie .glow151-panel .bars, - .glow151-ie .glow151-panel .tb div, - .glow151-ie .glow151-panel .bb div { - opacity: 1; - filter: alpha(opacity=50); - } - .glow151-ie .glow151-panel .tb, - .glow151-ie .glow151-panel .bb { - zoom: 1; - z-index: -1; - position: relative; - } - - /* - this solves a weird IE bug where the content vanishes if the panel is given an - opacity value - */ - .glow151-ie .glow151-panel .c { - filter: alpha(opacity=100); - } - - .glow151-ielt7 .glow151-panel .tr, - .glow151-ielt7 .glow151-panel .br { - margin-left: -3px; - } - .glow151-ielt7 .glow151-panel .tl, - .glow151-ielt7 .glow151-panel .bl { - margin-right: -3px; - } - .glow151-ielt7 .glow151-panel .c { - height: 50px; - overflow: visible; - } - .glow151-ielt7 .glow151-panel .panel-bd { - overflow: hidden; - } - /*DARK THEME*/ - /*Dark is the default*/ - /*LIGHT THEME*/ - .glow151-overlay .panel-light .c { - background: #fff url(images/lightpanel/bg.gif) repeat-x bottom; - color: #000; - } - .glow151-overlay .panel-light .tb { - border-color: #fff; - } - .glow151-overlay .panel-light .bb { - border-color: #E7E8E9; - } - .glow151-overlay .panel-light .bars, - .glow151-overlay .panel-light .tb div, - .glow151-overlay .panel-light .bb div { - background: #000; - opacity: 0.26; - } - .glow151-overlay .panel-light .tr { background: url(images/lightpanel/ctr.png); } - .glow151-overlay .panel-light .tl { background: url(images/lightpanel/ctl.png); } - .glow151-overlay .panel-light .bl { background: url(images/lightpanel/cbl.png); } - .glow151-overlay .panel-light .br { background: url(images/lightpanel/cbr.png); } - html .glow151-overlay .panel-light .infoPanel-pointerT { background: url(images/lightpanel/at.png); } - html .glow151-overlay .panel-light .infoPanel-pointerR { background: url(images/lightpanel/ar.png); } - html .glow151-overlay .panel-light .infoPanel-pointerB { background: url(images/lightpanel/ab.png); } - html .glow151-overlay .panel-light .infoPanel-pointerL { background: url(images/lightpanel/al.png); } - - .glow151-overlay .panel-light .panel-close { - background: url(images/lightpanel/close.png); - } - .glow151-gecko .glow151-overlay .panel-light .bars { - background: url(images/lightpanel/transbg.png); - } - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - This block of CSS rules for IE only has been moved to panel.js to be output directly to - the page. It is there so that the path to the PNGs can be deduced from the location of - the widgets css file. - It is acceptable because this design will not be altered before version 2 of glow, and - at that point it will be modified to avoid the use of PNGs. - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - - .glow151-ie .glow151-overlay .panel-light .tr { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/lightpanel/ctr.png', sizingMethod='crop'); - } - .glow151-ie .glow151-overlay .panel-light .tl { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/lightpanel/ctl.png', sizingMethod='crop'); - } - .glow151-ie .glow151-overlay .panel-light .bl { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/lightpanel/cbl.png', sizingMethod='crop'); - } - .glow151-ie .glow151-overlay .panel-light .br { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/lightpanel/cbr.png', sizingMethod='crop'); - } - .glow151-ie .glow151-overlay .panel-light .infoPanel-pointerT { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/lightpanel/at.png', sizingMethod='crop'); - } - .glow151-ie .glow151-overlay .panel-light .infoPanel-pointerR { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/lightpanel/ar.png', sizingMethod='crop'); - } - .glow151-ie .glow151-overlay .panel-light .infoPanel-pointerB { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/lightpanel/ab.png', sizingMethod='crop'); - } - .glow151-ie .glow151-overlay .panel-light .infoPanel-pointerL { - background:none; - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='//www.bbc.co.uk/glow/1.1.0/widgets/images/lightpanel/al.png', sizingMethod='crop'); - } - - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - .glow151-ie .glow151-overlay .panel-light .bars, - .glow151-ie .glow151-overlay .panel-light .tb div, - .glow151-ie .glow151-overlay .panel-light .bb div { - opacity: 1; - filter: alpha(opacity=26); - } -/*END*/ -/* info panel */ - .glow151-infoPanel .c { - min-height: 125px - } - - .glow151-panel .infoPanel-pointerT, - .glow151-panel .infoPanel-pointerB, - .glow151-panel .infoPanel-pointerL, - .glow151-panel .infoPanel-pointerR { - display: none; - } - .glow151-infoPanel-pointT .defaultSkin .infoPanel-pointerT, - .glow151-infoPanel-pointL .defaultSkin .infoPanel-pointerL, - .glow151-infoPanel-pointR .defaultSkin .infoPanel-pointerR, - .glow151-infoPanel-pointB .defaultSkin .infoPanel-pointerB { - width: 48px; - height: 34px; - overflow: hidden; - position: relative; - display: block; - } - .glow151-infoPanel-pointL .defaultSkin .infoPanel-pointerL, - .glow151-infoPanel-pointR .defaultSkin .infoPanel-pointerR { - width: 34px; - height: 47px; - left: 0; - z-index: 1; - } - .glow151-infoPanel-pointL .defaultSkin .infoPanel-pointerL { - position: absolute; - } - .glow151-panel .infoPanel-pointerT { - margin: 0 0 -10px 45px; - z-index: 1000; - background: url(images/darkpanel/at.png); - } - .glow151-panel .infoPanel-pointerB { - margin: -10px 0 0 45px; - background: url(images/darkpanel/ab.png); - } - .glow151-panel .infoPanel-pointerL { - margin: 54px 0 0 0; - background: url(images/darkpanel/al.png); - } - .glow151-infoPanel-pointL .defaultSkin .pc { - margin-left: 24px; - } - .glow151-panel .infoPanel-pointerR { - float: right; - position: relative; - margin: 54px 0 0 -34px; - background: url(images/darkpanel/ar.png); - } - .glow151-infoPanel-pointR .defaultSkin .pc { - margin-right: 24px; - } - .glow151-ielt7 .glow151-infoPanel .c { - height: 125px; - } - /*without header*/ - .glow151-panel-noHeader .defaultSkin .infoPanel-pointerL, - .glow151-panel-noHeader .defaultSkin .infoPanel-pointerR { - margin-top: 33px; - } - /*end*/ -/* AutoSuggest */ -.glow151-autoSuggest ul { /*TODO: handle embedded ul */ - margin: 0; - padding: 0; - list-style: none; - color: #000; - background-color: #FFF; - border: 1px solid #333; -} - -.glow151-autoSuggest .autosuggest-dark { - background-color: #000; - color: #fff; -} - -.glow151-autoSuggest li { /*TODO: handle embedded ul */ - padding: 1px 9px; - margin: 0; - zoom: 1; - cursor: pointer; -} - -.glow151-autoSuggest li.odd { - background-color: #D3D3D3; -} - -.glow151-autoSuggest li.even { - background-color: #FFF; -} - -.glow151-autoSuggest li.active { - background-color: #A9A9A9; -} - -.glow151-autoSuggest .autosuggest-dark li.odd { - background-color: #333; -} - -.glow151-autoSuggest .autosuggest-dark li.even { - background-color: #000; -} - -.glow151-autoSuggest .autosuggest-dark li.active { - background-color: #999; -} - -/* Carousel */ - -.glow151-carousel, .glow151-vCarousel { - position:relative; - overflow:hidden; - zoom:1; -} - -.glow151-carousel .carousel-window, .glow151-vCarousel .carousel-window { - position:relative; - overflow:hidden; -} - -.glow151-carousel .carousel-content, .glow151-vCarousel .carousel-content { - position:relative; - list-style-type:none; - padding-left:0; - margin:0; - top:0; - left:0; - overflow: hidden !important; -} - -.glow151-carousel .carousel-item, .glow151-vCarousel .carousel-item { - float:left; - /*_overflow:hidden;*/ - display: inline; -} - -.glow151-carousel .carousel-nav, .glow151-vCarousel .carousel-nav { - position:relative; - display:block; - width:30px; - height:100%; - cursor:pointer; - z-index:1; -} - -.glow151-carousel .carousel-nav span, .glow151-vCarousel .carousel-nav span { - position:absolute; - top:0; - left:0; - display:block; - width:100%; - height:100%; -} - -/* Light Theme */ -.glow151-carousel .carousel-light .carousel-window { - background:#f0f0f0; -} - -.glow151-carousel .carousel-light .carousel-nav span { - background-image:url(images/carousel/lighth.png); -} - -.glow151-carousel .carousel-light .carousel-nav { - background:#f0f0f0; - color:#000; -} - -/* Dark Theme */ - -.glow151-carousel .carousel-dark .carousel-window { - background:#282828; -} - -.glow151-carousel .carousel-dark .carousel-nav { - background:#282828; - color:#fff; -} - -.glow151-carousel .carousel-dark .carousel-nav span { - background-image:url(images/carousel/darkh.png); -} - -.glow151-carousel .carousel-window { - float:left; -} - -.glow151-carousel .carousel-nav { - float:left; - overflow:hidden; -} - -.glow151-carousel .carousel-nav span { - background-repeat:no-repeat; -} - -.glow151-carousel .carousel-nav .carousel-background { - top:0; - background-position:-60px 0; - background-repeat:repeat-y; -} - -.glow151-carousel .carousel-nav .carousel-top { - top:0; - background-position:0 0; -} - -.glow151-carousel .carousel-nav .carousel-bottom { - bottom:0; - background-position:-30px bottom; -} - -.glow151-carousel .carousel-nav .carousel-arrow { - width:10px; - top:0; - left:10px; - background-repeat:no-repeat; -} - -.glow151-carousel .carousel-prev .carousel-arrow { - background-position:-90px center; -} - -.glow151-carousel .carousel-next .carousel-arrow { - background-position:-101px center; -} - -.glow151-carousel .carousel-prev-disabled, -.glow151-carousel .carousel-next-disabled, -.glow151-vCarousel .carousel-prev-disabled, -.glow151-vCarousel .carousel-next-disabled { - cursor: default; -} - -.glow151-carousel .carousel-prev-disabled .carousel-arrow { - background-position:-112px center; -} - -.glow151-carousel .carousel-next-disabled .carousel-arrow { - background-position:-123px center; -} - -/* Vertical Themes */ - -/* Light Theme - Vertical */ - -.glow151-vCarousel .carousel-light .carousel-window { - background:#f0f0f0; -} - -.glow151-vCarousel .carousel-light .carousel-nav { - background:#f0f0f0; - color:#000; - height:30px; -} - -.glow151-vCarousel .carousel-light .carousel-nav span { - background-image:url(images/carousel/lightv.png); -} - -/* Dark Theme - Vertical */ -.glow151-vCarousel .carousel-dark .carousel-window { - background:#282828; -} - -.glow151-vCarousel .carousel-dark .carousel-nav { - background:#282828; - color:#fff; - height:30px; -} - -.glow151-vCarousel .carousel-dark .carousel-nav span { - background-image:url(images/carousel/darkv.png); -} - -.glow151-vCarousel .carousel-nav span { - background-repeat:no-repeat; - overflow:hidden; -} - -.glow151-vCarousel .carousel-nav .carousel-background { - background-position:0 -60px; - background-repeat:repeat-x; -} - -.glow151-vCarousel .carousel-nav .carousel-top { - background-position:right 0; -} - -.glow151-vCarousel .carousel-nav .carousel-bottom { - background-position:left -30px; -} - -.glow151-vCarousel .carousel-nav .carousel-arrow { - top:10px; - height:10px; -} - -.glow151-vCarousel .carousel-prev .carousel-arrow { - background-position:center -90px; -} - -.glow151-vCarousel .carousel-next .carousel-arrow { - background-position:center -101px; -} - -.glow151-vCarousel .carousel-prev-disabled .carousel-arrow { - background-position:center -112px; -} - -.glow151-vCarousel .carousel-next-disabled .carousel-arrow { - background-position:center -123px; -} - -/* Paged Carousel */ -.glow151-vCarousel .carousel-window.paged { float: left; } - -.glow151-carousel .pageNav, -.glow151-vCarousel .pageNav { - display: block; - padding: 0px; - height: 20px; - width: auto; - clear: both; - margin: 0 auto; -} - -.glow151-vCarousel .pageNav { - height: auto; - float: left; - width: 20px; - clear: none; - margin: auto 0; -} - -.glow151-carousel .dotLabel, -.glow151-vCarousel .dotLabel { - width: 20px; - height: 20px; - overflow: hidden; - text-indent: -5000px; /* move text away when images are enabled */ - padding: 0; - margin: 0; - display: block; -} - -.glow151-basic .glow151-carousel .dot .dotLabel, -.glow151-basic .glow151-vCarousel .dot .dotLabel, { - overflow: visible; - text-indent: 0; /* images are disabled, so move text back, but not for arrows */ -} - -.glow151-carousel .pageNav li, -.glow151-vCarousel .pageNav li { - width: 20px; - height: 20px; - padding: 0px; - margin: 0px; - float: left; - list-style-type: none; - color: #000; - background-image: url(images/carousel/lightdots.png); - background-repeat: no-repeat; - background-position: -20px 0; -} - -.glow151-carousel .pageNav .dot { - cursor: pointer; -} - -.glow151-carousel .carousel-dark .pageNav li, -.glow151-vCarousel .carousel-dark .pageNav li { - color: #fff; - background-image: url(images/carousel/darkdots.png); -} - -.glow151-vCarousel .pageNav li { - background-image: url(images/carousel/lightdotsv.png); - float: none; -} - -.glow151-vCarousel .carousel-dark .pageNav li { background-image: url(images/carousel/darkdotsv.png); } -.glow151-carousel .pageNav li.dotActive, -.glow151-vCarousel .pageNav li.dotActive { background-position: -20px -20px; } -.glow151-basic .glow151-carousel .pageNav li.dotActive, -.glow151-basic .glow151-vCarousel .pageNav li.dotActive { text-decoration: underline; } -.glow151-carousel .pageNav li#rightarrow, -.glow151-vCarousel .pageNav li#rightarrow { background-position: -40px -20px; } - -.glow151-basic .glow151-carousel .pageNav li#rightarrow .dotLabel { - width: 0; - height: 0; - border-top: 8px solid #fff; - border-right: 8px solid #fff; - border-bottom: 8px solid #fff; - border-left: 8px solid #000; -} - -.glow151-basic .glow151-vCarousel .pageNav li#rightarrow .dotLabel { - width: 0; - height: 0; - border-top: 8px solid #000; - border-right: 8px solid #fff; - border-bottom: 8px solid #fff; - border-left: 8px solid #fff; -} - -.glow151-carousel .pageNav li.carousel-next-disabled#rightarrow, -.glow151-vCarousel .pageNav li.carousel-next-disabled#rightarrow { background-position: -40px 0; } - -.glow151-basic .glow151-carousel .pageNav li.carousel-next-disabled#rightarrow .dotLabel, -.glow151-basic .glow151-vCarousel .pageNav li.carousel-next-disabled#rightarrow .dotLabel { - width: 0; - height: 0; - border-top: 8px solid #fff; - border-right: 8px solid #fff; - border-bottom: 8px solid #fff; - border-left: 8px solid #ccc; -} - -.glow151-basic .glow151-vCarousel .pageNav li.carousel-next-disabled#rightarrow .dotLabel { - width: 0; - height: 0; - border-top: 8px solid #ccc; - border-right: 8px solid #fff; - border-bottom: 8px solid #fff; - border-left: 8px solid #fff; -} - -.glow151-carousel .pageNav li#leftarrow, -.glow151-vCarousel .pageNav li#leftarrow { background-position: 0 -20px; } - -.glow151-basic .glow151-carousel .pageNav li#leftarrow .dotLabel { - width: 0; - height: 0; - border-top: 8px solid #fff; - border-right: 8px solid #000; - border-bottom: 8px solid #fff; - border-left: 8px solid #fff; -} - -.glow151-basic .glow151-vCarousel .pageNav li#leftarrow .dotLabel { - width: 0; - height: 0; - border-top: 8px solid #fff; - border-right: 8px solid #fff; - border-bottom: 8px solid #000; - border-left: 8px solid #fff; -} - -.glow151-carousel .pageNav li.carousel-prev-disabled#leftarrow, -.glow151-vCarousel .pageNav li.carousel-prev-disabled#leftarrow { background-position: 0 0; } - -.glow151-basic .glow151-carousel .pageNav li.carousel-prev-disabled#leftarrow .dotLabel { - width: 0; - height: 0; - border-top: 8px solid #fff; - border-right: 8px solid #ccc; - border-bottom: 8px solid #fff; - border-left: 8px solid #fff; -} - -.glow151-basic .glow151-vCarousel .pageNav li.carousel-prev-disabled#leftarrow .dotLabel { - width: 0; - height: 0; - border-top: 8px solid #fff; - border-right: 8px solid #fff; - border-bottom: 8px solid #ccc; - border-left: 8px solid #fff; -} -/* SLIDER */ - -.glow151-slider, -.glow151-vSlider { - position: relative; - zoom: 1; -} - -.glow151-slider .slider-container, -.glow151-vSlider .slider-container { - position: relative; - zoom: 1; - padding: 4px 17px 6px 17px; -} - -.glow151-slider:focus, -.glow151-vSlider:focus { - outline: none; - border: none; -} - -/* track */ - .glow151-slider .slider-track, - .glow151-vSlider .slider-track { - position: relative; - /* for IE */ - font-size: 0; - zoom: 1; - } - .glow151-slider .slider-trackOff, - .glow151-vSlider .slider-trackOff { - height: 8px; - background: url(images/slider/lighth.png) 100% 0; - margin-left: 2px; - /* for IE */ - font-size: 0; - } - .glow151-slider .slider-trackOn, - .glow151-vSlider .slider-trackOn { - /* width of this is set via js */ - position: absolute; - height: 8px; - top: 0; left: 0; - background: url(images/slider/lighth.png) -2px -8px; - z-index: 2; - overflow: hidden; - } - - .glow151-slider .slider-handle, - .glow151-vSlider .slider-handle { - /* left position of this is set via js */ - position: absolute; - top: -4px; - width: 17px; height: 18px; - background: url(images/slider/lighth.png) 0px -16px; - z-index: 3; - cursor: pointer; - } - -/* buttons */ - - .glow151-slider .slider-btn-bk, - .glow151-slider .slider-btn-fwd, - .glow151-vSlider .slider-btn-bk, - .glow151-vSlider .slider-btn-fwd { - position: absolute; - width: 12px; height: 12px; - top: 2px; left: 0; - background: url(images/slider/lighth.png) 0px -34px; - font-size: 0; - cursor: pointer; - } - .glow151-slider .slider-btn-fwd { - background-position: -12px -34px; - left: 100%; - margin-left: -12px; - } - -/* active state */ - - .glow151-slider .slider-active .slider-handle { - background-position: -17px -16px; - } - -/* disabled state */ - - .glow151-slider .slider-disabled .slider-handle { - background-position: -34px -16px; - } - .glow151-slider .slider-disabled .slider-btn-bk { - background-position: -24px -34px; - } - .glow151-slider .slider-disabled .slider-btn-fwd { - background-position: -36px -34px; - } - .glow151-slider .slider-disabled .slider-trackOn { - background-position: -2px 0; - } - - .glow151-slider .slider-disabled .slider-btn-bk, - .glow151-slider .slider-disabled .slider-btn-fwd, - .glow151-slider .slider-disabled .slider-handle, - .glow151-vSlider .slider-disabled .slider-btn-bk, - .glow151-vSlider .slider-disabled .slider-btn-fwd, - .glow151-vSlider .slider-disabled .slider-handle { - cursor: auto; - } - -/* no buttons */ - .glow151-slider .slider-noButtons .slider-container { - padding-left: 0; - padding-right: 0; - } - .glow151-slider .slider-noButtons .slider-btn-bk, - .glow151-slider .slider-noButtons .slider-btn-fwd, - .glow151-vSlider .slider-noButtons .slider-btn-bk, - .glow151-vSlider .slider-noButtons .slider-btn-fwd { - display: none; - } - -/* labels */ - .glow151-slider .slider-labels { - margin: 10px 8px 0; - } - .glow151-vSlider .slider-labels { - position: absolute; - left: 22px; top: 25px; - } - - .glow151-vSlider .slider-noButtons .slider-labels { - top: 8px; - } - - .glow151-slider .slider-labels .ruler-tickMajor, - .glow151-slider .slider-labels .ruler-tickMinor, - .glow151-vSlider .slider-labels .ruler-tickMajor, - .glow151-vSlider .slider-labels .ruler-tickMinor { - background: #6e6e6e; - } - .glow151-slider .slider-labels .ruler-label, - .glow151-vSlider .slider-labels .ruler-label { - color: #6e6e6e; - cursor: pointer; - } - - -/* vertical */ - .glow151-vSlider .slider-trackOff, - .glow151-vSlider .slider-trackOn, - .glow151-vSlider .slider-handle, - .glow151-vSlider .slider-btn-bk, - .glow151-vSlider .slider-btn-fwd { - background-image: url(images/slider/lightv.png); - } - - .glow151-vSlider .slider-container { - padding: 17px 0; - } - - .glow151-vSlider .slider-track { - margin: 0; - overflow: hidden; - zoom: 1; - width: 20px; - } - - .glow151-vSlider .slider-trackOff { - width: 8px; - height: auto; - background-position: 0 0; - position: absolute; - margin: 0; - left: 4px; top: 0; - z-index: 2; - } - - .glow151-vSlider .slider-trackOn { - width: 8px; - height: 0; - position: relative; - margin: 0 0 0 4px; - background-position: -8px 100%; - top: 2px; left: 0; - z-index: 1; - } - - .glow151-vSlider .slider-handle { - top: 0; left: 0; - width: 18px; height: 17px; - background-position: -16px 100%; - } - - .glow151-vSlider .slider-btn-bk, - .glow151-vSlider .slider-btn-fwd { - top: 0; left: 2px; - background-position: -34px -1976px; - } - - .glow151-vSlider .slider-btn-bk { - background-position: -34px 100%; - left: 2px; top: 100%; - margin: -12px 0 0 0; - } - - .glow151-vSlider .slider-active .slider-handle { - background-position: -16px -1966px; - } - .glow151-vSlider .slider-disabled .slider-handle { - background-position: -16px -1949px; - } - .glow151-vSlider .slider-disabled .slider-btn-bk { - background-position: -34px -1964px; - } - .glow151-vSlider .slider-disabled .slider-btn-fwd { - background-position: -34px -1952px; - } - .glow151-vSlider .slider-disabled .slider-trackOn { - background-position: 0 100%; - } - - .glow151-vSlider .slider-noButtons .slider-container { - padding-top: 0; - padding-bottom: 0; - } - -/* Dark theme */ - .glow151-slider .slider-dark .slider-trackOff, - .glow151-slider .slider-dark .slider-trackOn, - .glow151-slider .slider-dark .slider-handle, - .glow151-slider .slider-dark .slider-btn-bk, - .glow151-slider .slider-dark .slider-btn-fwd { - background-image: url(images/slider/darkh.png); - } - - .glow151-vSlider .slider-dark .slider-trackOff, - .glow151-vSlider .slider-dark .slider-trackOn, - .glow151-vSlider .slider-dark .slider-handle, - .glow151-vSlider .slider-dark .slider-btn-bk, - .glow151-vSlider .slider-dark .slider-btn-fwd { - background-image: url(images/slider/darkv.png); - } - - .glow151-slider .slider-dark .slider-labels .ruler-tickMajor, - .glow151-slider .slider-dark .slider-labels .ruler-tickMinor, - .glow151-vSlider .slider-dark .slider-labels .ruler-tickMajor, - .glow151-vSlider .slider-dark .slider-labels .ruler-tickMinor { - background: #e2e2e2; - } - .glow151-slider .slider-dark .slider-labels .ruler-label, - .glow151-vSlider .slider-dark .slider-labels .ruler-label { - color: #e2e2e2; - } - -/* Gosh! No Images! */ - - .glow151-basic .glow151-slider .slider-handle, - .glow151-basic .glow151-vSlider .slider-handle { - border-style: solid; - border-color: #777 #fff; - border-width: 18px 8px 0 9px; - width: 0; - height: 0; - font-size: 0; - } - - .glow151-basic .glow151-vSlider .slider-handle { - border-width: 8px 0 9px 18px; - border-color: #fff #777; - } - - .glow151-basic .glow151-slider .slider-trackOff, - .glow151-basic .glow151-vSlider .slider-trackOff { - background: #d6d6d6; - } - .glow151-basic .glow151-slider .slider-trackOn, - .glow151-basic .glow151-vSlider .slider-trackOn { - background: #777; - } - - .glow151-basic .glow151-slider .slider-btn-bk, - .glow151-basic .glow151-slider .slider-btn-fwd, - .glow151-basic .glow151-vSlider .slider-btn-bk, - .glow151-basic .glow151-vSlider .slider-btn-fwd { - border-style: solid; - border-color: #fff #777; - border-width: 6px 0 6px 12px; - width: 0; height: 0; - } - - .glow151-basic .glow151-slider .slider-btn-bk { - border-width: 6px 12px 6px 0; - } - - .glow151-basic .glow151-vSlider .slider-btn-bk { - border-color: #666 #fff; - border-width: 12px 6px 0 6px; - } - - .glow151-basic .glow151-vSlider .slider-btn-fwd { - border-color: #666 #fff; - border-width: 0 6px 12px 6px; - } - - /* active */ - .glow151-basic .glow151-slider .slider-active .slider-handle { - border-color: #000 #fff; - } - .glow151-basic .glow151-vSlider .slider-active .slider-handle { - border-color: #fff #000; - } - - /* disabled */ - .glow151-basic .glow151-slider .slider-disabled .slider-trackOn, - .glow151-basic .glow151-vSlider .slider-disabled .slider-trackOn { - background: #d6d6d6; - } - .glow151-basic .glow151-slider .slider-disabled .slider-handle { - border-color: #d6d6d6 #fff; - } - .glow151-basic .glow151-vSlider .slider-disabled .slider-handle { - border-color: #fff #d6d6d6; - } - .glow151-basic .glow151-slider .slider-disabled .slider-btn-bk, - .glow151-basic .glow151-slider .slider-disabled .slider-btn-fwd { - border-color: #fff #d6d6d6; - } - .glow151-basic .glow151-vSlider .slider-disabled .slider-btn-bk, - .glow151-basic .glow151-vSlider .slider-disabled .slider-btn-fwd { - border-color: #d6d6d6 #fff; - } - -/* no images - dark */ - .glow151-basic .glow151-slider .slider-dark .slider-handle { - border-color: #ccc #000; - } - - .glow151-basic .glow151-vSlider .slider-dark .slider-handle { - border-color: #000 #ccc; - } - - .glow151-basic .glow151-slider .slider-dark .slider-trackOff, - .glow151-basic .glow151-vSlider .slider-dark .slider-trackOff { - background: #555; - } - .glow151-basic .glow151-slider .slider-dark .slider-trackOn, - .glow151-basic .glow151-vSlider .slider-dark .slider-trackOn { - background: #ccc; - } - - .glow151-basic .glow151-slider .slider-dark .slider-btn-bk, - .glow151-basic .glow151-slider .slider-dark .slider-btn-fwd { - border-color: #000 #ccc; - } - - .glow151-basic .glow151-vSlider .slider-dark .slider-btn-bk, - .glow151-basic .glow151-vSlider .slider-dark .slider-btn-fwd { - border-color: #ccc #000; - } - - /* active */ - .glow151-basic .glow151-slider .slider-dark .slider-active .slider-handle { - border-color: #fff #000; - } - .glow151-basic .glow151-vSlider .slider-dark .slider-active .slider-handle { - border-color: #000 #fff; - } - - /* disabled */ - .glow151-basic .glow151-slider .slider-dark .slider-disabled .slider-trackOn, - .glow151-basic .glow151-vSlider .slider-dark .slider-disabled .slider-trackOn { - background: #555; - } - .glow151-basic .glow151-slider .slider-dark .slider-disabled .slider-handle { - border-color: #555 #000; - } - .glow151-basic .glow151-vSlider .slider-dark .slider-disabled .slider-handle { - border-color: #000 #555; - } - .glow151-basic .glow151-slider .slider-dark .slider-disabled .slider-btn-bk, - .glow151-basic .glow151-slider .slider-dark .slider-disabled .slider-btn-fwd { - border-color: #000 #555; - } - .glow151-basic .glow151-vSlider .slider-dark .slider-disabled .slider-btn-bk, - .glow151-basic .glow151-vSlider .slider-dark .slider-disabled .slider-btn-fwd { - border-color: #555 #000; - } - -/* RULER */ - - .glow151-ruler, - .glow151-vRuler { - position: relative; - zoom: 1; - } - - .glow151-ruler .ruler-tickMajor, - .glow151-ruler .ruler-tickMinor, - .glow151-vRuler .ruler-tickMajor, - .glow151-vRuler .ruler-tickMinor { - position: absolute; - background: #000; - width: 1px; height: 1px; - top: 0; left: 0; - overflow: hidden; - } - - .glow151-ruler .ruler-tickMajor, - .glow151-ruler .ruler-spacer { - height: 10px; - } - .glow151-ruler .ruler-tickMinor { - height: 2px; - } - .glow151-vRuler .ruler-tickMajor { - width: 10px; - } - .glow151-vRuler .ruler-tickMinor { - width: 2px; - } - - .glow151-ruler .ruler-labels { - position: relative; - height: 1.2em; - } - .glow151-vRuler .ruler-labels { - position: absolute; - top: 0; left: 15px; - height: 100%; - } - - .glow151-ruler .ruler-label, - .glow151-vRuler .ruler-label { - position: absolute; - top: 0; left: 0; - } - .glow151-ruler .ruler-label { - text-align: center; - } - .glow151-ruler .ruler-label span { - position: relative; - left: -50%; - } - .glow151-vRuler .ruler-label { - /*line-height: 0;*/ - margin-top: -0.56em; - } -/* TIMETABLE */ - .glow151-vTimetable .timetable-container, - .glow151-Timetable .timetable-container { - overflow: hidden; - position: relative; - zoom: 1; - } - .glow151-vTimetable .timetable-innerView, - .glow151-Timetable .timetable-innerView { - /*border: 2px solid #000;*/ - background: #fff; - overflow: hidden; - position: relative; - zoom: 1; - } - .glow151-vTimetable .timetable-innerView { - float: left; - } - .glow151-vTimetable .timetable-dragArea, - .glow151-vTimetable .timetable-track, - .glow151-vTimetable .timetable-item, - .glow151-vTimetable .timetable-bandOdd, - .glow151-vTimetable .timetable-bandEven, - .glow151-vTimetable .timetable-scale, - .glow151-vTimetable .timetable-scaleItem, - .glow151-Timetable .timetable-dragArea, - .glow151-Timetable .timetable-track, - .glow151-Timetable .timetable-item, - .glow151-Timetable .timetable-bandOdd, - .glow151-Timetable .timetable-bandEven, - .glow151-Timetable .timetable-scale, - .glow151-Timetable .timetable-scaleItem { - position: absolute; - top: 0; left: 0; - overflow: hidden; - width: 100%; - height: 100%; - z-index: 2; - } - - .glow151-vTimetable .timetable-dragRange, - .glow151-Timetable .timetable-dragRange { - height: 100%; - width: 100%; - } - - - .glow151-vTimetable .timetable-track, - .glow151-Timetable .timetable-track { - border: 1px solid #a0a0a0; - border-width: 0 1px; - margin-right: 2px; - } - .glow151-vTimetable .timetable-track { - border-width: 0 1px; - margin-right: 1px; - } - .glow151-Timetable .timetable-track { - border-width: 1px 0; - margin-bottom: 1px; - } - .glow151-vTimetable .timetable-scale, - .glow151-Timetable .timetable-scale { - color: #a0a0a0; - font-size: x-large; - border: 1px solid #ccc; - } - .glow151-vTimetable .timetable-scale { - text-align: center; - } - - .glow151-vTimetable .timetable-scalePrimary { - border-width: 0 0 0 1px; - } - .glow151-vTimetable .timetable-scaleSecondary { - border-width: 0 1px 0 0; - } - .glow151-Timetable .timetable-scalePrimary { - border-width: 1px 0 0 0; - } - .glow151-Timetable .timetable-scaleSecondary { - border-width: 0 0 1px 0; - } - - .glow151-vTimetable .timetable-track-footers { - clear: both; - } - -/* temporary values to make it visible but out of the way during dev - .glow151-vTimetable .timetable-accessibility-navigation, - .glow151-Timetable .timetable-accessibility-navigation { - position: absolute; - top: 0; - right: 0; - }*/ - - .glow151-vTimetable .timetable-accessibility-navigation, - .glow151-Timetable .timetable-accessibility-navigation, - .glow151-vTimetable .timetable-accessibility-hidden, - .glow151-Timetable .timetable-accessibility-hidden { - position: absolute; - height: 1px; - width: 1px; - overflow: hidden; - } - - .glow151-vTimetable .timetable-track-headers, - .glow151-vTimetable .timetable-track-footers, - .glow151-Timetable .timetable-track-headers, - .glow151-Timetable .timetable-track-footers { - position: relative; - } - - .glow151-vTimetable .timetable-header-holder, - .glow151-vTimetable .timetable-footer-holder, - .glow151-Timetable .timetable-header-holder, - .glow151-Timetable .timetable-footer-holder { - position: absolute; - top: 0; - left: 0; - overflow: hidden; - color: #fff; - background: #424242; - text-align: center; - } - .glow151-Timetable .timetable-track-headers, - .glow151-Timetable .timetable-track-footers, - .glow151-Timetable .timetable-scrollView { - float: left; - } - .glow151-Timetable .timetable-header-holder, - .glow151-Timetable .timetable-footer-holder { - width: 100px; - } - - .glow151-vTimetable .timetable-track-headers, - .glow151-vTimetable .timetable-track-footers, - .glow151-Timetable .timetable-track-headers, - .glow151-Timetable .timetable-track-footers { - background: #fff; - border: 1px solid #ccc; - } - .glow151-vTimetable .timetable-track-headers { - border-bottom-width: 0 - } - .glow151-vTimetable .timetable-track-footers { - border-top-width: 0 - } - .glow151-Timetable .timetable-track-headers { - border-right-width: 0 - } - .glow151-Timetable .timetable-track-footers { - border-left-width: 0 - } - - .glow151-vTimetable .timetable-track-headers div.timetable-header-content, - .glow151-vTimetable .timetable-track-footers div.timetable-footer-content, - .glow151-Timetable .timetable-track-headers div.timetable-header-content, - .glow151-Timetable .timetable-track-footers div.timetable-footer-content { - padding: 9px 0; - } - - .glow151-Timetable .timetable-track-headers div.timetable-header-content, - .glow151-Timetable .timetable-track-footers div.timetable-footer-content { - padding: 4px 5px; - text-align: left; - } - /* we use !important beacuse it's impoartant these items aren't affected by margins or padding */ - .glow151-vTimetable .timetable-trackList, - .glow151-Timetable .timetable-trackList { - margin: 0 !important; - padding: 0 !important; - list-style: none; - } - .glow151-vTimetable .timetable-item, - .glow151-Timetable .timetable-item { - border: 1px solid #ccc; - margin: 0 !important; - padding: 0 !important; - display: block; - } - .glow151-vTimetable .timetable-item { - border-width: 1px 0; - } - .glow151-Timetable .timetable-item { - border-width: 0 1px; - } - .glow151-vTimetable .timetable-itemHangingOffStart .timetable-itemContent, - .glow151-Timetable .timetable-itemHangingOffStart .timetable-itemContent { - opacity: 0.3; - filter: alpha(opacity=30); - zoom: 1; - } - .glow151-vTimetable .timetable-itemHangingClipping .timetable-itemContent, - .glow151-Timetable .timetable-itemHangingClipping .timetable-itemContent { - height: 1px; - overflow: hidden; - } - .glow151-vTimetable .timetable-hideitems .timetable-item { - display: none; - } - /* banding */ - .glow151-vTimetable .timetable-bandOdd, - .glow151-vTimetable .timetable-bandEven, - .glow151-Timetable .timetable-bandOdd, - .glow151-Timetable .timetable-bandEven { - z-index: 1; - } - .glow151-vTimetable .timetable-bandOdd, - .glow151-Timetable .timetable-bandOdd { - background: #ebebeb; - } - /* end banding */ - - .glow151-vTimetable .timetable-default-track-header { - position: absolute; - height: 1px; - width: 1px; - overflow: hidden; - } - - /* scrollbar nav */ - .glow151-vTimetable .timetable-scrollbar1, - .glow151-vTimetable .timetable-scrollbar2, - .glow151-Timetable .timetable-scrollbar1, - .glow151-Timetable .timetable-scrollbar2 { - display: none; - } - - .glow151-vTimetable .timetable-scrollbar1, - .glow151-vTimetable .timetable-scrollbar2 { - float: left; - /* this foces IE6 to make the scrollbars shrink to fit */ - _width: 0; - } - - .glow151-vTimetable .timetable-scrollbar .slider-container, - .glow151-Timetable .timetable-scrollbar .slider-container { - padding: 21px 0; - border: 1px solid #a0a0a0; - } - .glow151-Timetable .timetable-scrollbar .slider-container { - padding: 0 21px; - } - .glow151-vTimetable .timetable-scrollbar2 .slider-container { - border-left-width: 0; - } - .glow151-vTimetable .timetable-scrollbar1 .slider-container { - border-right-width: 0; - } - .glow151-Timetable .timetable-scrollbar2 .slider-container { - border-top-width: 0; - } - .glow151-Timetable .timetable-scrollbar1 .slider-container { - border-bottom-width: 0; - } - .glow151-vTimetable .timetable-scrollbar .slider-handle, - .glow151-Timetable .timetable-scrollbar .slider-handle { - /* using opacity because IE6 lets you click 'through' elements with a transparent bg */ - width: 100%; - height: 100%; - background: #fff; - opacity: 0; - filter: alpha(opacity=0); - top: 0; - } - .glow151-vTimetable .timetable-scrollbar .slider-track, - .glow151-Timetable .timetable-scrollbar .slider-track { - background: none; - } - .glow151-vTimetable .timetable-scrollbarLabels, - .glow151-Timetable .timetable-scrollbarLabels { - position: absolute; - width: 100%; - height: 100%; - background: #fff; - color: #227db7; - font-size: small; - text-align: center; - top: 0; left: 0; - overflow: hidden; - } - - .glow151-vTimetable .slider-labels, - .glow151-Timetable .slider-labels { - display: none; - } - - .glow151-vTimetable .timetable-scrollbarLabelsHighlight, - .glow151-Timetable .timetable-scrollbarLabelsHighlight { - background: #424242; - color: #fff; - z-index: 1; - overflow: hidden; - } - - .glow151-vTimetable .timetable-scrollbar .slider-trackOn, - .glow151-vTimetable .timetable-scrollbar .slider-trackOff, - .glow151-Timetable .timetable-scrollbar .slider-trackOn, - .glow151-Timetable .timetable-scrollbar .slider-trackOff { - background: none; - } - - .glow151-vTimetable .timetable-scrollbarItem, - .glow151-Timetable .timetable-scrollbarItem { - width: 100%; - height: 100%; - position: absolute; - top: 0; left: 0; - } - - .glow151-vTimetable .timetable-scrollbar .slider-btn-bk, - .glow151-vTimetable .timetable-scrollbar .slider-btn-fwd, - .glow151-Timetable .timetable-scrollbar .slider-btn-bk, - .glow151-Timetable .timetable-scrollbar .slider-btn-fwd { - background: url(images/timetable/lightv.png) top center; - border: 1px solid #a0a0a0; - } - - .glow151-vTimetable .timetable-scrollbar .slider-btn-bk, - .glow151-vTimetable .timetable-scrollbar .slider-btn-fwd { - height: 20px; - left: 0; - border-width: 0 0 1px 0; - } - .glow151-Timetable .timetable-scrollbar .slider-btn-bk, - .glow151-Timetable .timetable-scrollbar .slider-btn-fwd { - background: url(images/timetable/lighth.png) right center; - width: 20px; - top: 0; - border-width: 0 0 0 1px; - } - .glow151-vTimetable .timetable-scrollbar .slider-btn-bk { - background-position: center bottom; - border-width: 1px 0 0 0; - margin: -21px 0 0; - } - .glow151-Timetable .timetable-scrollbar .slider-btn-bk { - background-position: left center; - border-width: 0 1px 0 0; - z-index: 1; - } - .glow151-Timetable .timetable-scrollbar .slider-btn-fwd { - margin-left: -21px; - } - /* scrollbar nav end */ - - /* dark theme */ - .glow151-vTimetable .timetable-dark .timetable-scrollbarLabels, - .glow151-Timetable .timetable-dark .timetable-scrollbarLabels { - background-color: #333; - } - - .glow151-vTimetable .timetable-dark .timetable-header-holder, - .glow151-vTimetable .timetable-dark .timetable-footer-holder, - .glow151-Timetable .timetable-dark .timetable-header-holder, - .glow151-Timetable .timetable-dark .timetable-footer-holder, - .glow151-vTimetable .timetable-dark .timetable-scrollbarLabelsHighlight, - .glow151-Timetable .timetable-dark .timetable-scrollbarLabelsHighlight { - background: #121212; - } - - .glow151-vTimetable .timetable-dark .timetable-innerView, - .glow151-Timetable .timetable-dark .timetable-innerView { - background: #333333; - color: #ccc; - } - - .glow151-vTimetable .timetable-dark .timetable-scale, - .glow151-Timetable .timetable-dark .timetable-scale { - color: #ccc; - } - - .glow151-vTimetable .timetable-dark .timetable-item, - .glow151-Timetable .timetable-dark .timetable-item, - .glow151-vTimetable .timetable-dark .timetable-track, - .glow151-Timetable .timetable-dark .timetable-track, - .glow151-vTimetable .timetable-dark .timetable-scrollbar .slider-btn-bk, - .glow151-vTimetable .timetable-dark .timetable-scrollbar .slider-btn-fwd, - .glow151-Timetable .timetable-dark .timetable-scrollbar .slider-btn-bk, - .glow151-Timetable .timetable-dark .timetable-scrollbar .slider-btn-fwd, - .glow151-vTimetable .timetable-dark .timetable-scrollbar .slider-container, - .glow151-Timetable .timetable-dark .timetable-scrollbar .slider-container, - .glow151-vTimetable .timetable-dark .timetable-scale, - .glow151-Timetable .timetable-dark .timetable-scale { - border-color: #141414; - } - - .glow151-vTimetable .timetable-dark .timetable-bandOdd, - .glow151-Timetable .timetable-dark .timetable-bandOdd { - background-color: #242424; - } - - .glow151-vTimetable .timetable-dark .timetable-scrollbar .slider-btn-bk, - .glow151-vTimetable .timetable-dark .timetable-scrollbar .slider-btn-fwd { - background-image: url(images/timetable/darkv.png); - } - .glow151-Timetable .timetable-dark .timetable-scrollbar .slider-btn-bk, - .glow151-Timetable .timetable-dark .timetable-scrollbar .slider-btn-fwd { - background-image: url(images/timetable/darkh.png); - } - .glow151-vTimetable .timetable-dark .timetable-track-headers, - .glow151-vTimetable .timetable-dark .timetable-track-footers, - .glow151-Timetable .timetable-dark .timetable-track-headers, - .glow151-Timetable .timetable-dark .timetable-track-footers { - border: none; - background: #333; - } - /* dark theme end */ -/* END TIMETABLE */ -/* EDITOR */ - .glow151-editor {border: 1px solid #000;} - - .glow151-editor .editor-toolbar - { - border: none; - display: block; - margin: 0; - padding: 0; - background-color: #EFEFEF; - /*height: 28px;*/ - } - - .glow151-editor .editor-toolbar legend { display: none; } - .glow151-editor .editor-toolbar-tools h3 { display: none; } - - .glow151-editor ul.editor-toolbar-tools - { - display: block; - margin: 0 !important; - padding: 9px !important; - overflow: hidden; - zoom: 1; - } - - .glow151-editor ul.editor-toolbar-tools li - { - list-style: none; - float: left; - display: block; - width: 26px; - /*height: 28px;*/ - margin: 0 !important; - padding: 0 !important; - } - - .glow151-editor .editor-toolbar-button - { - display: block; - margin: 0; - padding: 0; - width: 26px; - /*height: 28px;*/ - overflow: hidden; - } - - .glow151-editor .editor-toolbar-button a { outline: none; text-decoration: none; } - .glow151-editor .editor-toolbar-button img { border: none; } - - /* with images */ - .glow151-editor .editor-toolbar-icon - { - display: block; - width: 26px; - height: 24px; - } - - .glow151-editor .editor-toolbar-icon span - { - display: block; - height: 1px; - overflow: hidden; - width: 1px; - } - - .glow151-editor .editor-toolbar-icon span { display: block; height: 1px; overflow: hidden; width: 1px; } .glow151-editor .editor-toolbar-item .bold-button { background-image: url(images/editor/button-bold.png); } - .glow151-editor .editor-toolbar-item .default .bold-button { background-position: 0px 0px; } - .glow151-editor .editor-toolbar-item .disabled .bold-button { background-position: 0px -28px; } - .glow151-editor .editor-toolbar-item .active .bold-button { background-position: 0px -85px; } - .glow151-editor .editor-toolbar-item .hover .bold-button { background-position: 0px -56px; } - - .glow151-editor .editor-toolbar-item .italics-button { background-image: url(images/editor/button-italics.png); } - .glow151-editor .editor-toolbar-item .default .italics-button { background-position: 0px 0px; } - .glow151-editor .editor-toolbar-item .disabled .italics-button { background-position: 0px -28px; } - .glow151-editor .editor-toolbar-item .active .italics-button { background-position: 0px -85px; } - .glow151-editor .editor-toolbar-item .hover .italics-button { background-position: 0px -56px; } - - .glow151-editor .editor-toolbar-item .strike-button { background-image: url(images/editor/button-strike.png); } - .glow151-editor .editor-toolbar-item .default .strike-button { background-position: 0px 0px; } - .glow151-editor .editor-toolbar-item .disabled .strike-button { background-position: 0px -28px; } - .glow151-editor .editor-toolbar-item .active .strike-button { background-position: 0px -85px; } - .glow151-editor .editor-toolbar-item .hover .strike-button { background-position: 0px -56px; } - - .glow151-editor .editor-toolbar-item .blockquote-button { background-image: url(images/editor/button-blockquote.png); } - .glow151-editor .editor-toolbar-item .default .blockquote-button { background-position: 0px 0px; } - .glow151-editor .editor-toolbar-item .disabled .blockquote-button { background-position: 0px -28px; } - .glow151-editor .editor-toolbar-item .active .blockquote-button { background-position: 0px -85px; } - .glow151-editor .editor-toolbar-item .hover .blockquote-button { background-position: 0px -56px; } - - .glow151-editor .glow151-hidden {position: absolute; left: -2500px; width: 1px;} - - - /* without images */ - .glow151-basic .glow151-editor .editor-toolbar-icon span { border: none; height: auto; overflow: normal; width: auto; } - .glow151-basic .glow151-editor .editor-toolbar-item .hover span span {border: 1px solid #000;} - - .glow151-editor iframe {border: 0; margin: 0; padding: 0; width: 100%;} - - /* dark theme */ - .editor-dark .editor-toolbar {background: #333; color: #fff;} - .editor-dark .editor-toolbar-item .bold-button { background-image: url(images/editor/dark-button-bold.png); } - .editor-dark .editor-toolbar-item .italics-button { background-image: url(images/editor/dark-button-italics.png); } - .editor-dark .editor-toolbar-item .strike-button { background-image: url(images/editor/dark-button-strike.png); } - .editor-dark .editor-toolbar-item .blockquote-button { background-image: url(images/editor/dark-button-blockquote.png); } - - -/* END EDITOR */ diff --git a/horde/js/glow/1.5.1/widgets/widgets.debug.js b/horde/js/glow/1.5.1/widgets/widgets.debug.js deleted file mode 100644 index 53e699034..000000000 --- a/horde/js/glow/1.5.1/widgets/widgets.debug.js +++ /dev/null @@ -1,9342 +0,0 @@ -/* - Copyright 2009 British Broadcasting Corporation - - 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. -*/ -/*@cc_on @*/ -/*@if (@_jscript_version > 5.5)@*/ -/** - * @name glow.widgets - * @namespace - * @description Widget core module. - * The glow.widgets module contains generic functionality used by our - * widgets, but currently no public API. - */ -(window.gloader || glow).module({ - name: "glow.widgets", - library: ["glow", "1.5.1"], - depends: [["glow", "1.5.1", 'glow.dom', 'glow.events']], - builder: function(glow) { - var doc, - docBody, - env = glow.env; - - glow.ready(function() { - doc = document; - docBody = doc.body; - - // block any further ready calls until our widgets CSS has loaded - glow._addReadyBlock("glow_widgetsCSS"); - - //check if css or images are disabled, add class name "glow-basic" to body if they aren't - var testDiv = glow.dom.create('').appendTo(docBody); - - if (testDiv.height() != 20) { - //css disabled - docBody.className += " glow151-basic"; - } else { - (function() { - if (testDiv.css("z-index") != "1234") { - //css hasn't loaded yet - setTimeout( arguments.callee, 10 ); - return; - } - // BRING ON THE WALLLLL! - glow._removeReadyBlock("glow_widgetsCSS"); - - if (testDiv.css("background-image").indexOf("ctr.png") == -1) { - docBody.className += " glow151-basic"; - } - })(); - } - - //add some IE class names to the body to help widget styling - env.ie && (docBody.className += " glow151-ie"); - //note: we apply the class "glow-ielt7" for IE7 if it's in quirks mode - (env.ie < 7 || !env.standardsMode) && (docBody.className += " glow151-ielt7"); - //some rounding issues in firefox when using opacity, so need to have a workaround - env.gecko && (docBody.className += " glow151-gecko"); - }); - - - glow.widgets = { - /* - PrivateMethod: _scrollPos - Get the scroll position of the document. Candidate for moving into glow.dom? - */ - _scrollPos: function() { - var win = window, - docElm = env.standardsMode ? doc.documentElement : docBody; - - return { - x: docElm.scrollLeft || win.pageXOffset || 0, - y: docElm.scrollTop || win.pageYOffset || 0 - }; - } - }; - } -}); -(window.gloader || glow).module({ - name: "glow.widgets.Mask", - library: ["glow", "1.5.1"], - depends: [[ - "glow", "1.5.1", - 'glow.dom', - 'glow.events', - 'glow.widgets' - ]], - builder: function(glow) { - var dom = glow.dom, - $ = dom.get, - events = glow.events, - widgets = glow.widgets, - bodyProperties, //this is a holding place for body padding & margins - htmlStr = '
      '; -/** -@name glow.widgets.Mask -@class -@description A semi transparent layer covering the page - -Use this if you're wanting to block out the main content of the page. -Anything you want to be on top of the mask needs to have a higher z-index (default: 9990). - -
      Widgets must be called in a glow.onReady() call.
      - -@see Mask user guide - -@param {Object} opts Object containing the attributes specified below. -@param {Number} [opts.opacity=0.7] The opacity of the mask (from 0 to 1). -@param {String} [opts.color=black] The colour of the mask -@param {Function} [opts.onClick] Shortcut to attach an event listener that is called when the user clicks on the mask. -@param {Number} [opts.zIndex=9990] The z-index of mask layer - -@example - var mask = new glow.widget.Mask({ - onClick : function () { - this.remove(); - } - }); - mask.add(); - -*/ - -/* -Deprecated glow.widgets.Mask() param - -@param {Boolean} [opts.disableScroll=false] If set to true, scrolling is disabled in the main document. - -This feature is experimental. It works by moving -the document into a new container, offsetting it and setting overflow -to none. Because this adds a new element between body and your document, -you may have problems if your scripts rely on certain elements. Children -of <body> which have class "glowNoMask" will be left as children of -<body>. - -*/ - -/** - @name glow.widgets.Mask#maskElement - @type glow.dom.NodeList - @description The node overlayed to create the mask. - @example - //create mask instance - var myMask = new glow.widgets.Mask(); - - //display mask - myMask.maskElement.css("background", "url(stripe.png)"); - */ - - function Mask(opts) { - this.opts = glow.lang.apply({ - color: '#000', - opacity: 0.7, - zIndex: 9900, - disableScroll: false - }, opts || {}); - - /* - Property: maskElement - The node overlayed to create the mask. Access this if you want to - change its properties on the fly - */ - var docBody = document.body, - mask = this.maskElement = dom.create( - htmlStr + 'z-index:' + this.opts.zIndex + ';background:' + this.opts.color + ';visibility:hidden">
      ' - ).appendTo(docBody), - that = this; - - mask.css("opacity", this.opts.opacity); - - if (glow.env.ie < 7) { - this._iframe = dom.create(iframeSrc).css("z-index", this.opts.zIndex - 1).appendTo(docBody); - } - - //add mask node click event, route it through to a Mask event - events.addListener(mask, "click", function() { - events.fire(that, "click"); - }); - if (this.opts.onClick) { - events.addListener(this, "click", opts.onClick); - } - } - - Mask.prototype = { - /** - @name glow.widgets.Mask#add - @function - @description Displays the mask. - @example - - // create the mask - var myMask = new glow.widgets.Mask(); - - // add the mask over the screen - myMask.add() - */ - add: function () { - var doc = $(document), - body = $(document.body), - win = $(window), - that = this; - - if (this.opts.disableScroll && !noScrollContainer) { //avoid blocking scrolling twice - noScrollContainer = glow.dom.create( - htmlStr + 'height:100%;overflow:hidden;">' + htmlStr + '">' - ); - - var scrollVals = widgets._scrollPos(), - bodyStyle = body[0].style, - clientHeight = win.height(), - clientWidth = win.width(), - noScroll = noScrollContainer.get("div"), - //get children which don't have class "glowNoMask" - bodyChildren = body.children().filter(function() { return (' ' + this.className + ' ').indexOf("glowNoMask") == -1 }); - - bodyProperties = { - margin: [body.css("margin-top"), body.css("margin-right"), body.css("margin-bottom"), body.css("margin-left")], - padding: [body.css("padding-top"), body.css("padding-right"), body.css("padding-bottom"), body.css("padding-left")], - height: body.css("height") - }; - - bodyStyle.margin = bodyStyle.padding = 0; - - bodyStyle.height = "100%"; - noScroll[0].style.zIndex = this.opts.zIndex - 1; - - noScrollContainer.appendTo(body); - - noScroll.css("margin", bodyProperties.margin.join(" ")). - css("padding", bodyProperties.padding.join(" ")). - css("top", -scrollVals.y - parseFloat(bodyProperties.margin[0]) + "px"). - css("left", -scrollVals.x + "px"). - append(bodyChildren); - } - - function resizeMask() { - var bodyHeight = body.height(); - - // we do this twice to catch scrollbars showing & hiding - for (var i = 0; i < 2; i++) { - that.maskElement.css("width", "100%"). - css("height", ( that.opts.disableScroll ? noScrollContainer.height() : Math.max(bodyHeight, win.height()) ) + "px"); - } - if (glow.env.ie < 7) { - var maskStyle = that.maskElement[0].style; - that._iframe.css("width", maskStyle.width).css("height", maskStyle.height); - } - } - this.maskElement.css("visibility", "visible").css("display", "block"); - if (glow.env.ie < 7) { - this._iframe.css("display", "block"); - } - resizeMask(); - this._resizeListener = events.addListener(window, "resize", resizeMask); - }, - /** - @name glow.widgets.Mask#remove - @function - @description Removes the mask. - @example - - // create the mask - var myMask = new glow.widgets.Mask(); - - // add the mask over the screen - myMask.add() - - // remove the mask from over the screen - myMask.remove() - */ - remove : function () { - this.maskElement.css("visibility", "hidden").css("display", "none"); - if (glow.env.ie < 7) { - this._iframe.css("display", "none"); - } - events.removeListener(this._resizeListener); - - if (this.opts.disableScroll) { - var body = $(document.body), - noScroll = noScrollContainer.children(); - - noScroll.children().appendTo(body); - window.scroll(-parseInt(noScroll.css("left")), -parseInt(noScroll.css("top"))); - noScrollContainer.remove(); - body.css("margin", bodyProperties.margin.join(" ")). - css("padding", bodyProperties.padding.join(" ")). - css("height", bodyProperties.height); - - delete noScrollContainer; - noScrollContainer = undefined; - } - - } - }; - - glow.widgets.Mask = Mask; - } -}); -(window.gloader || glow).module({ - name: "glow.widgets.Overlay", - library: ["glow", "1.5.1"], - depends: [[ - "glow", "1.5.1", - 'glow.dom', - 'glow.events', - 'glow.anim', - 'glow.widgets', - 'glow.widgets.Mask' - ]], - builder: function(glow) { - var dom = glow.dom, - $ = dom.get, - events = glow.events, - widgets = glow.widgets, - env = glow.env, - anim = glow.anim, - tweens = glow.tweens, - /* - includes focus hook for the user / other widgets to assign focus to. Tried assigning focus to whole container - but that selected all the text in opera 9.5 - */ - overlayHtml = '
      ', - overlayCount = 0, //number of overlays on the page, this is used to generate unique IDs - //this iframe code is duplicated in mask... shall we sort that out? - // javascript:false stops IE complaining over SSL - iframeSrc = '', - flashUrlTest = /.swf($|\?)/i, - wmodeTest = /]*(?:name=["'?]\bwmode["'?][\s\/>]|\bvalue=["'?](?:opaque|transparent)["'?][\s\/>])[^>]*){2}/i, - //fixed positioning isn't supported in IE6 (or quirks mode). Also, Safari 2 does some mental stuff with position:fixed so best to just avoid it - useFixed = (!env.ie && !(env.webkit < 522)) || (env.ie > 6 && env.standardsMode); - - /** - * @name glow.widgets.Overlay.hideElms - * @private - * @function - * @description Hides elements as the overlay shows - * - * @param {Overlay} overlay - */ - function hideElms(overlay) { - //return if elements have already been hidden, saves time - if (overlay._hiddenElements[0]) { return; } - - var thingsToHide = new glow.dom.NodeList(), - hideWhileShown = overlay.opts.hideWhileShown, - hideFilter = overlay.opts.hideFilter, - i = 0, - thingsToHideLen; - - //gather windowed flash - if (overlay.opts.hideWindowedFlash) { - thingsToHide.push( - $("object, embed").filter(function() { - return isWindowedFlash.call(this, overlay); - }) - ); - } - //gather other things to hide - if (hideWhileShown) { thingsToHide.push($(hideWhileShown)); } - //get rid of stuff the user doesn't want hidden - if (hideFilter) { thingsToHide = thingsToHide.filter(hideFilter); } - - overlay._hiddenElements = thingsToHide; - - for (var i = 0, thingsToHideLen = thingsToHide.length; i < thingsToHideLen; i++) { - // update how many times this item has been hidden by a glow overlay - // this lets multiple overlays hide the same element - thingsToHide[i].__glowOverlayHideCount = (Number(thingsToHide[i].__glowOverlayHideCount) || 0) + 1; - - if (thingsToHide[i].__glowOverlayHideCount == 1) { - // this is the first attempt to hide the element, so we need to actually hide it - // also store the current value for visibility - thingsToHide[i].__glowOverlayInitVis = thingsToHide[i].style.visibility; - thingsToHide[i].style.visibility = "hidden"; - } - } - } - - /** - * @name glow.widgets.Overlay.isWindowedFlash - * @private - * @function - * @description Is 'this' a windowed Flash element - * As in, will it show on top of everything. Called from glow.widgets.Overlay.hideElms - * as a filter function - * - */ - function isWindowedFlash(overlay) { - var that = this, wmode; - //we need to use getAttribute here because Opera & Safari don't copy the data to properties - if ( - (that.getAttribute("type") == "application/x-shockwave-flash" || - flashUrlTest.test(that.getAttribute("data") || that.getAttribute("src") || "") || - (that.getAttribute("classid") || "").toLowerCase() == "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000") && - !$(that).isWithin(overlay.content) - ) { - wmode = that.getAttribute("wmode"); - - return (that.nodeName == "OBJECT" && !wmodeTest.test(that.innerHTML)) || - (that.nodeName != "OBJECT" && wmode != "transparent" && wmode != "opaque"); - - } - return false; - } - - /** - * @name glow.widgets.Overlay.revertHiddenElms - * @private - * @function - * @description Revert items which were hidden when the overlay was shown - * - */ - function revertHiddenElms(overlay) { - var hiddenElements = overlay._hiddenElements, - i = 0, - len = hiddenElements.length; - - for (; i < len; i++) { - // only show the element again if its hide count reaches zero - if ( --hiddenElements[i].__glowOverlayHideCount == 0 ) { - hiddenElements[i].style.visibility = hiddenElements[i].__glowOverlayInitVis; - } - } - overlay._hiddenElements = []; - } - - /* - PrivateMethod: generatePresetAnimation - Generates an animation / timeline object from one of the presets - - Arguments: - overlay - reference to the overlay - preset - name of the preset animation - show - true for 'show' animation. Otherwise 'hide'. - */ - function generatePresetAnimation(overlay, show) { - var channels = [], - channel = [], - chanLen = 0, - chansLen = 0, - preset = overlay.opts.anim, - mask = overlay.opts.mask, - container = overlay.container, - maskOpacity, - finalHeight = 0; - - if (preset == "fade") { - container.css("opacity", (show ? 0 : 1)); - channels[chansLen++] = [ - anim.css(container, 0.3, { - opacity: { - from: (show ? 0 : 1), - to: (show ? 1 : 0) - } - } - ) - ]; - if (show) { - channels[chansLen - 1][1] = function() { container.css("opacity", "") }; - } - channels[chansLen++] = [generateMaskAnimation(overlay, show)]; - } else if (preset == "roll" || preset == "slide") { - if (show) { - container.css("height", ""); - finalHeight = container.height(); - container.css("height", "0"); - } - - channels[chansLen++] = [ - function() { - /* - safari doesn't properly recognise switches between 'hidden' - and 'auto' - */ - if (env.webkit < 522 && show) { - container.css("display", "none"); - setTimeout(function() { - container.css("overflow", "hidden").css("display", "block"); - }, 0); - } else { - container.css("overflow", "hidden"); - } - }, - anim.css(container, 0.3, { - height: {to: finalHeight} - }, {tween: show ? tweens.easeOut() : tweens.easeIn() }), - function() { - if (!show) { - container.css("visibility", "hidden"); - } - container.css("height", ""); - container.css("overflow", ""); - } - ]; - channels[chansLen++] = [generateMaskAnimation(overlay, show)]; - } - return new anim.Timeline(channels); - } - - /* - PrivateMethod: generateMaskAnimation - generates an animation for the mask of an overlay to go in a timeline - */ - function generateMaskAnimation(overlay, show) { - if (! overlay.opts.modal) { return 0; } - - var mask = overlay.opts.mask, - maskOpacity = mask.opts.opacity, - maskElement = mask.maskElement; - - maskElement.css("opacity", (show ? 0 : maskOpacity)); - return anim.css(maskElement, 0.1, { - opacity: { - from: (show ? 0 : maskOpacity), - to: (show ? maskOpacity : 0) - } - } - ) - } - - /* - PrivateMethod: closeOverlay - Hides the overlay. Separated out so this part can happen asyncronously (like after an animation) - */ - function closeOverlay(overlay) { - revertHiddenElms(overlay); - overlay.container.css("visibility", "").css("display", ""); - if (overlay.opts.modal) { - overlay.opts.mask.remove(); - } else if (glow.env.ie < 7) { - overlay._iframe.css("display", "none"); - } - events.removeListener(overlay._scrollEvt); - events.removeListener(overlay._resizeEvt); - } - - /** - @name glow.widgets.Overlay - @class - @description A container element displayed on top of the other page content - -
      Widgets must be called in a glow.onReady() call.
      - @see Overlay user guide - - @param {selector|Element|glow.dom.NodeList} content - the element that contains the contents of the overlay. If this is - in the document it will be moved to document.body. - - @param {Object} opts - Zero or more of the following as properties of an object: - @param {Boolean} [opts.modal="false"] Is the overlay modal? - If true then a default Mask will be created if one is not provided. - @param {glow.widgets.Mask} [opts.mask] Mask to use for modal overlays - used to indicate to the user that the overlay is modal. If provided then the modal property is set to true. - @param {Boolean} [opts.closeOnMaskClick="true"] if true then listens for a click event on the mask and hides when it fires - @param {String|Function} [opts.anim="null"] A transition for showing / hiding the panel - Can be "fade" or "slide", or a function which returns a glow.anim.Animation or glow.anim.Timeline. - The function is passed the overlay as the first parameter, and 'true' if the overlay is showing, 'false' if it's hiding. - @param {Number} [opts.zIndex="9991"] The z-index to set on the overlay - If the overlay is modal, the zIndex of the mask will be set to one less than the value of this attribute. - @param {Boolean} [opts.autoPosition="true"] Position the overlay relative to the viewport - If true, the overlay will be positioned to the viewport according to the x & y - options. If false, you will have to set the position manually by setting the left / right css styles of the - container property. - @param {Number|String} [opts.x="50%"] Distance of overlay from the left of the viewport - If the unit is a percentage then 0% is aligned to the left of - the viewport, 100% is aligned to the right of viewport and 50% - is centered. - @param {Number|String} [opts.y="50%"] Distance of overlay from the top of the viewport - If the unit is a percentage then 0% is aligned to the left of - the viewport, 100% is aligned to the right of viewport and 50% - is centered. - @param {String} [opts.ariaRole] The aria role of the overlay. - This is used for accessibility purposes. No role is defined by default. - @param {Object} [opts.ariaProperties] Key-value pairs of aria properties and values - These are applied to the overlay container for accessibility purposes. - By default the overlay is a polite live area. - @param {selector|Element|glow.dom.NodeList} [opts.returnTo] Element to give focus to when the overlay closes - For accessibility purposes you may want to set an element to give focus to when the overlay closes. - This meanss devices which present data to the user by the cursor position (such as screen readers) - will be sent somewhere useful. - @param {Boolean} [opts.hideWindowedFlash=true] Hide windowed Flash movies? - When set to true, any Flash movie without wmode "transparent" or "opaque" will be hidden when - the overlay shows. This is because they always appear on top of other elements on the page. Flash - movies inside the overlay are excluded from hiding. - @param {selector|Element|glow.dom.NodeList} [opts.hideWhileShown] Elements to hide while the overlay is shown - This is useful for hiding page elements which always appear on top of other page elements. - Flash movies can be handled easier using the hideWindowedFlash option. - @param {Function} [opts.hideFilter] Exclude elements from hiding - When provided this function is run for every element that may be hidden. This includes windowed - Flash movies if 'hideWindowedFlash' is true, and any matches for 'hideWhileShown'. In the function, - 'this' refers to the element. Return false to prevent this element being hidden. - @param {Boolean} [opts.focusOnShow=false] Give the overlay keyboard focus when it appears? - Use 'returnTo' to specify where to send focus when the overlay closes - @param {String} [opts.id] Value for the Overlay container's ID attribute - @param {String} [opts.className] Values for the Overlay container's class attribute. - - @example - var overlay = new glow.widgets.Overlay( - glow.dom.create( - '
      ' + - '

      Your Story has been saved.

      ' + - '
      ' - ) - ); - overlay.show(); - */ - /** - @name glow.widgets.Overlay#event:show - @event - @description Fired when the overlay is about to appear on the screen, before any animation. - - At this point you can access the content of the overlay and make changes - before it is shown to the user. If you prevent the default action of this - event (by returning false or calling event.preventDefault) the overlay - will not show. - - @param {glow.events.Event} event Event Object - */ - /** - @name glow.widgets.Overlay#event:afterShow - @event - @description Fired when the overlay is visible to the user and any 'show' animation is complete - - This event is ideal to assign focus to a particular part of the overlay. - If you want to change content of the overlay before it appears, see the - 'show' event. - - @param {glow.events.Event} event Event Object - */ - /** - @name glow.widgets.Overlay#event:hide - @event - @description Fired when the overlay is about to hide - - If you prevent the default action of this event (by returning false or - calling event.preventDefault) the overlay will not hide. - - @param {glow.events.Event} event Event Object - */ - /** - @name glow.widgets.Overlay#event:afterHide - @event - @description Fired when the overlay has fully hidden, after any hiding animation has completed - @param {glow.events.Event} event Event Object - */ - function Overlay(content, opts) { - opts = opts || {}; - //assume modal if mask provided - if (opts.mask) { opts.modal = true; } - - this.opts = opts = glow.lang.apply({ - modal: false, - closeOnMaskClick: true, - zIndex: 9990, - autoPosition: true, - x: "50%", - y: "50%", - ariaRole: "", - ariaProperties: { - live: "polite" - }, - hideWindowedFlash: true, - focusOnShow: false, - id: "glow151Overlay" + (++overlayCount) - }, opts); - - // generate a default mask if needed - if (opts.modal && !opts.mask) { - opts.mask = new glow.widgets.Mask(opts.zIndex ? {zIndex: opts.zIndex-1} : {}); - } - - /** - @name glow.widgets.Overlay#content - @description The content of the overlay - @type glow.dom.NodeList - */ - var contentNode = this.content = $(content), - that = this, - /** - @name glow.widgets.Overlay#container - @description The overlay's container. - Use this to alter the width of the overlay. You can also - manually position the overlay using this node when autoPosition is false. - @type glow.dom.NodeList - */ - overlayNode = this.container = dom.create(overlayHtml).css("z-index", opts.zIndex).attr("aria-hidden", "true"), - docBody = document.body, - i; - - /** - @name glow.widgets.Overlay#_focalPoint - @private - @description Dummy element at the start of the overlay to send focus to - @type glow.dom.NodeList - */ - this._focalPoint = overlayNode.get("div.overlay-focalPoint"); - - /** - @name glow.widgets.Overlay#_hiddenElements - @private - @description Stores elements hidden by this overlay - @type Node[] - */ - this._hiddenElements = []; - - /** - @name glow.widgets.Overlay#_blockScrollRepos - @private - @description Don't move the panel along with scrolling - This is used in setPosition to allow the user to scroll to - the rest of the content even if autoPosition is on - @type Object - */ - //created on show - - // ensure the overlay has a unique ID, this is used by aria to point at this overlay - overlayNode[0].id = opts.id; - - // add any class names from the user - overlayNode[0].className += " " + (opts.className || ""); - - /** - @name glow.widgets.Overlay#autoPosition - @description Position the overlay relative to the viewport - If true, the overlay will be positioned to the viewport according to the x & y - options. If false, you will have to set the position manually by setting the left / right css styles of the - container property. - @type Boolean - */ - this.autoPosition = opts.autoPosition; - - /** - @name glow.widgets.Overlay#isShown - @description True if the overlay is showing - @type Boolean - */ - this.isShown = false; - - /** - * @name glow.widgets.Overlay#returnTo - * @description Element to give focus to when the overlay closes - * For accessibility purposes you may want to set an element to give focus to when the overlay closes. - * This meanss devices which present data to the user by the cursor position (such as screen readers) - * will be sent somewhere useful. - * @type selector|Element|glow.dom.NodeList - */ - this.returnTo = opts.returnTo; - - //this is used to prevent show / hide commands while animations are underway - this._blockActions = false; - - //add the content to the page - overlayNode.append(contentNode).appendTo(docBody); - - //add close event to mask if needed - if (opts.closeOnMaskClick && opts.mask) { - events.addListener(opts.mask, "click", function() { - that.hide(); - }); - } - - //add IE iframe hack if needed - if (glow.env.ie < 7 && !opts.modal) { - this._iframe = dom.create(iframeSrc).css("z-index", opts.zIndex - 1).appendTo(docBody); - } - - //apply aria role - if (opts.ariaRole) { - overlayNode.attr("role", opts.ariaRole); - } - - //apply aria properties - for (i in opts.ariaProperties) { - overlayNode.attr("aria-" + i, opts.ariaProperties[i]); - } - } - - Overlay.prototype = { - /** - @name glow.widgets.Overlay#setPosition - @function - @description Change or recalculate the position of the overlay - Call with parameters to - change the position of the overlay or call without parameters to recalculate - the position of the overlay. You may need to call this without parameters - if relative positions become invalid. - - @param {Number|String} [x] - distance of overlay from the left of the viewport. If the unit is a percentage - then 0% is aligned to the left of the viewport, 100% is aligned to the right of viewport and 50% is centered. - @param {Number|String} [y] - distance of overlay from the top of the viewport. If the unit is a percentage - then 0% is aligned to the left of the viewport, 100% is aligned to the right of viewport and 50% is centered. - - @returns this - */ - setPosition: function(x, y) { - var container = this.container; - //don't use set position if autoPosition is false - if (this.autoPosition) { - //if values have been provided, set them. Make sure we're not being passed an event object! - if (x !== undefined && !(x.source)) { - this.opts.x = x; - this.opts.y = y; - } - var win = $(window), - x = this.opts.x, - y = this.opts.y, - xVal = parseFloat(this.opts.x), - yVal = parseFloat(this.opts.y), - blockScrollPos = this._blockScrollRepos, - useFixedThisTime = useFixed && (!blockScrollPos.x) && (!blockScrollPos.y), - extraOffset = ((this.opts.mask && this.opts.mask.opts.disableScroll) || useFixedThisTime) ? {x:0,y:0} : widgets._scrollPos(), - //these are only set if % are involved - winWidth, - winHeight, - containerWidth, - containerHeight; - - useFixedThisTime && container.css("position", "fixed"); - - if (typeof x == "string" && x.indexOf("%") != -1) { - winWidth = win.width(); - containerWidth = container[0].offsetWidth; - - //what if there's more panel than view? - if (containerWidth > winWidth) { - if (!blockScrollPos.x) { //set up the initial position - container.css("left", widgets._scrollPos().x + "px").css("position", "absolute"); - blockScrollPos.x = true; - } else if (this.opts.modal && $(document).width() < containerWidth) { //does the mask need to extend further? - this.opts.mask.maskElement.css("width", containerWidth + "px"); - } - } else { - blockScrollPos.x = false; - container.css("left", Math.max(((winWidth - containerWidth) * (xVal/100)) + extraOffset.x, extraOffset.x) + "px"); - } - } else { - container.css("left", xVal + extraOffset.x + "px"); - } - - if (typeof y == "string" && y.indexOf("%") != -1) { - winHeight = win.height(); - containerHeight = container[0].offsetHeight; - - //what if there's more panel than view? - if (containerHeight > winHeight) { - if (!blockScrollPos.y) { //set up the initial position - container.css("top", widgets._scrollPos().y + "px").css("position", "absolute"); - blockScrollPos.y = true; - } else if (this.opts.modal && $(document).height() < containerHeight) { - this.opts.mask.maskElement.css("height", containerHeight + "px"); - } - } else { - blockScrollPos.y = false; - container.css("top", Math.max(((winHeight - containerHeight) * (yVal/100)) + extraOffset.y, extraOffset.y) + "px"); - } - } else { - container.css("top", yVal + extraOffset.y + "px"); - } - } - - if (glow.env.ie < 7 && !this.opts.modal) { - var overlayStyle = container[0].style; - this._iframe.css("top", overlayStyle.top). - css("left", overlayStyle.left). - css("width", container[0].offsetWidth + "px"). - css("height", container[0].offsetHeight + "px"); - } - return this; - }, - /** - @name glow.widgets.Overlay#show - @function - @description Displays the overlay - - @returns this - */ - show: function() { - var that = this, - showAnim, - animOpt = that.opts.anim; - - if (that._blockActions || that.isShown) { return that; } - - if (events.fire(that, "show").defaultPrevented()) { - return that; - } - - //reset scroll blocking - this._blockScrollRepos = {x:false, y:false}; - - hideElms(that); - that.container.css("display", "block"); - if (that.opts.modal) { - that.opts.mask.add(); - } else if (glow.env.ie < 7) { - that._iframe.css("display", "block"); - } - that._scrollEvt = events.addListener(window, "scroll", that.setPosition, that); - that._resizeEvt = events.addListener(window, "resize", that.setPosition, that); - - that.setPosition(); - - //run the appropiate animation - if (typeof animOpt == "string") { - showAnim = generatePresetAnimation(that, true); - } else if (typeof animOpt == "function") { - showAnim = animOpt(that, true); - } else if (animOpt) { - showAnim = animOpt.show; - } - if (showAnim) { - if (! showAnim._overlayEvtAttached) { - events.addListener(showAnim, "complete", function() { - that._blockActions = false; - that.isShown = true; - that.container.attr("aria-hidden", "false"); - events.fire(that, "afterShow"); - }); - showAnim._overlayEvtAttached = true; - } - that._blockActions = true; - showAnim.start(); - that.container.css("visibility", "visible"); - } else { - that.container.css("visibility", "visible"); - that.isShown = true; - that.container.attr("aria-hidden", "false"); - events.fire(that, "afterShow"); - } - - //send keyboard focus - if (that.opts.focusOnShow) { - that._focalPoint[0].focus(); - } - - return that; - }, - /** - @name glow.widgets.Overlay#hide - @function - @description Hides the overlay - - @returns this - */ - hide: function() { - var that = this, - hideAnim, - animOpt = that.opts.anim, - returnTo = returnTo ? $(that.returnTo) : "", - returnNodeName; - - if (this._blockActions || !that.isShown) { return that; } - - if (events.fire(that, "hide").defaultPrevented()) { - return that; - } - - //run the appropiate animation - if (typeof animOpt == "string") { - hideAnim = generatePresetAnimation(that, false); - } else if (typeof animOpt == "function") { - hideAnim = animOpt(that, false); - } else if (animOpt) { - hideAnim = animOpt.hide; - } - if (hideAnim) { - if (! hideAnim._overlayEvtAttached) { - events.addListener(hideAnim, "complete", function() { - closeOverlay(that); - that._blockActions = false; - that.isShown = false; - events.fire(that, "afterHide"); - }); - hideAnim._overlayEvtAttached = true; - } - that._blockActions = true; - hideAnim.start(); - } else { - closeOverlay(that); - that.isShown = false; - events.fire(that, "afterHide"); - } - - - //update aria state - that.container.attr("aria-hidden", "true"); - - //move the focus if applicable - if (returnTo[0]) { - returnNodeName = returnTo[0].nodeName; - - //give the element a tab index if it needs one - if (returnTo[0].tabindex == undefined || - returnNodeName != "input" || - returnNodeName != "select" || - returnNodeName != "textarea" || - returnNodeName != "a") { - - returnTo.attr("tabindex", "-1"); - } - - returnTo[0].focus(); - } - - // Fix for trac 170 - Overlay: In IE, flash continues to play when overlay is hidden - // If flash content detected then reinsert the element into its existing position within the DOM. - // This causes IE to stop the flash movie playing without removing it from the DOM (which would leave us open to JS errors) - if (glow.env.ie) - { - that.content.get("object").each(function(i) { - - if ( - (this.getAttribute("type") == "application/x-shockwave-flash" || - flashUrlTest.test(this.getAttribute("data") || this.getAttribute("src") || "") || - (this.getAttribute("classid") || "").toLowerCase() == "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000") - ) - { - this.parentNode.insertBefore(this, this.nextSibling); - } - - }); - } - - return that; - } - }; - - glow.widgets.Overlay = Overlay; - } -}); -(window.gloader || glow).module({ - name: "glow.widgets.Panel", - library: ["glow", "1.5.1"], - depends: [[ - "glow", "1.5.1", - 'glow.dom', - 'glow.events', - 'glow.widgets.Overlay' - ]], - builder: function(glow) { - var dom = glow.dom, - $ = dom.get, - events = glow.events, - widgets = glow.widgets, - Overlay = widgets.Overlay, - lang = glow.lang, - env = glow.env, - defaultTemplate, - //a hash of themes, true if their images have been preloaded - themesPreloaded = {}, - accessAddition = dom.create('
      End of panel. Close Panel
      '); - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - This block outputs CSS rules for IE only directly to the page. - It is here so that the path to the PNGs can be deduced from the location of the widgets - css file. - It is acceptable because this design will not be altered before version 2 of glow, and - at that point it will be modified to avoid the use of PNGs. - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - if(glow.env.ie) { - glow.ready(function () { - var widgetSheet = function (sheets) { - var i=0, - len = sheets.length, - sheet; - for (; i < len; i++) { - if (sheets[i].href.indexOf("widgets/widgets") != -1) { - return sheets[i]; - } else { - if (sheets[i].imports.length && (sheet = arguments.callee(sheets[i].imports))) { - return sheet; - } - } - } - return false; - }(document.styleSheets), - - _ieCssRule = function(theme, className, image) { - return ".glow151-ie .glow151-overlay" + cssPngThemes[theme].className + " ." + - className + " {background:none;filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + - iePngRoot + "/images/" + cssPngThemes[theme].path + "/" + image + ".png', sizingMethod='crop');}"; - }, - - cssPngThemes = { - light : { - className : " .panel-light", - path : "lightpanel" - }, - dark : { - className : "", - path : "darkpanel" - } - }, - - iePngRoot = widgetSheet.href.substring(0, widgetSheet.href.lastIndexOf("/")), - - styleBlock = ""; - - glow.dom.get("head").append(glow.dom.create(styleBlock)); - }); - } - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - /* - PrivateMethod: generateDivString - generates
      for 2nd arg onwards if first arg is false. - generates
      for 2nd arg onwards if first arg is true. - */ - function generateDivString(nest) { - var insideDiv = nest ? '
      ' : ''; - for (var i = 1, len = arguments.length, r = []; i < len; i++) { - r[i-1] = '
      ' + insideDiv + '
      '; - } - return r.join(""); - } - - /* - PrivateMethod: closeClick - Run when one of the panel's close buttons are clicked - */ - function closeClick() { - this.hide(); - return false; - } - - /* - PrivateProperty: defaultTemplate - default template for a panel & infoPanel - */ - defaultTemplate = function() { - var r = [], rLen = 0; - r[rLen++] = '
      '; - r[rLen++] = '
      '; - r[rLen++] = generateDivString(false, "infoPanel-pointerT", "infoPanel-pointerL", "infoPanel-pointerR"); - r[rLen++] = '
      '; - r[rLen++] = generateDivString(false, "tr", "tl"); - r[rLen++] = generateDivString(true, "tb"); - r[rLen++] = '
      '; - r[rLen++] = generateDivString(false, "bars"); - r[rLen++] = '
      '; - r[rLen++] = 'X'; - r[rLen++] = generateDivString(false, "panel-hd", "panel-bd", "panel-ft"); - r[rLen++] = '
      '; - r[rLen++] = '
      '; - r[rLen++] = generateDivString(false, "br", "bl"); - r[rLen++] = generateDivString(true, "bb"); - r[rLen++] = '
      '; - r[rLen++] = generateDivString(false, "infoPanel-pointerB"); - r[rLen++] = '
      '; - r[rLen++] = '
      '; - return r.join(""); - }(); - /** - @name glow.widgets.Panel - @class - @description An overlay with default themes and addtional functionality. - Includes templating system. - @augments glow.widgets.Overlay - @see Panel user guide - - @param {selector|Element|glow.dom.NodeList} content - the element that contains the contents of the Panel. If this is - in the document it will be moved to document.body. If your content node has a child element with class "hd" - it will be added to the header of the panel. Similarly, an element with class "ft" will be added to the - footer of the panel. - - @param {Object} opts - Zero or more of the following as properties of an object: - @param {Number|String} [opts.width=400] Width of the panel - Default of 400px gives a content width of 360px in the default template - @param {String} [opts.theme="dark"] Visual theme - Only applies when using the default template. Currently supported themes are "dark" and "light". - @param {String} [opts.template] An html template to use to create the panel - @param {Boolean} [opts.modal=true] is the overlay modal? - If true then a default Mask will be created if one is not provided. - @param {String} [opts.ariaRole="dialog"] The aria role of the panel. - This is used for accessibility purposes, - the default is acceptable for panels which interupt the user and should - be dealt with before interacting with the rest of the page. - @param {Boolean} [opts.focusOnShow=true] Give the overlay keyboard focus when it appears? - Use 'returnTo' to specify where to send focus when the overlay closes - - */ - function Panel(content, opts) { - content = $(content); - opts = opts || {}; - - if (typeof opts.width == "number") { - opts.width += 'px'; - } - - if (opts.template) { - var customTemplate = true; - } - - //option defaults - opts = glow.lang.apply({ - template: defaultTemplate, - width: "400px", - modal: true, - theme: "dark", - ariaRole: "dialog", - focusOnShow: true - }, opts); - - //dress content in template - var fullContent = dom.create(opts.template), - headContent = content.get("> .hd"), - footerContent = content.get("> .ft"), - docBody = document.body, - that = this, - fullContentClone, - i, - accessLinks = accessAddition.clone(); - - if (!customTemplate) { - fullContent.addClass("panel-" + opts.theme); - //preload the images of the theme - if (!themesPreloaded[opts.theme] && docBody.className.indexOf("glow151-basic") == -1) { - fullContentClone = fullContent.clone().addClass("glow151-panel-preload").appendTo(docBody); - themesPreloaded[opts.theme] = true; - } - } - - - /* - if we've been passed more than one node it's possible the user - has ommited the container (usually if they're creating the panel - from a string), let's be kind and deal with that. - */ - if (content.length > 1) { - content.each(function() { - var elm = $(this); - if (elm.hasClass("hd")) { - headContent = elm; - } else if (elm.hasClass("ft")) { - footerContent = elm; - } - }); - } - - /** - @name glow.widgets.Panel#header - @description The panel's header element - @type glow.dom.NodeList - */ - this.header = fullContent.get(".panel-hd"); - /** - @name glow.widgets.Panel#footer - @description The panel's footer element - @type glow.dom.NodeList - */ - this.footer = fullContent.get(".panel-ft"); - /** - @name glow.widgets.Panel#body - @description The panel's body element - @type glow.dom.NodeList - */ - this.body = fullContent.get(".panel-bd"); - - if (content.isWithin(docBody)) { - fullContent.insertBefore(content); - } else { - fullContent.appendTo(docBody); - } - this.body.append(content); - if (headContent.length) { - this.header.append(headContent); - } else if (!customTemplate) { - fullContent.addClass("panel-noHeader"); - } - if (footerContent.length) { this.footer.append(footerContent); } - - //add listeners for close buttons - events.addListener(fullContent.get(".panel-close"), "click", closeClick, this); - events.addListener(accessLinks.get("a"), "click", closeClick, this); - - Overlay.call(this, fullContent, opts); - - //apply width - this.container.css("width", opts.width). - //add close button to end for accessability - append(accessLinks); - - } - lang.extend(Panel, Overlay); - - glow.widgets.Panel = Panel; - } -}); -(window.gloader || glow).module({ - name: "glow.widgets.Sortable", - library: ["glow", "1.5.1"], - depends: [[ - "glow", "1.5.1", - 'glow.dom', - 'glow.events', - 'glow.dragdrop', - 'glow.widgets' - ]], - builder: function(glow) { - - var $ = glow.dom.get, - events = glow.events, - fire = events.fire, - addListener = events.addListener; - /** - @private - @name glow.widgets.Sortable#offsetTop - @function - @description Get the offsetTop of an element in a way that works in IE. - @param {glow.dom.NodeList} element The element to get the offsetTop of. - @returns {Number} An integer pixel offset. - */ - // TODO - this is a copy of the one in dragdrop, which should be moved to dom, removing the need for this - function offsetTop (element) { - var offsetParent = element[0].offsetParent, - marginDeduct = parseInt(element.css("margin-top")) || 0; - - if (glow.env.ie) { - while (offsetParent.currentStyle.position == "static") { - offsetParent = offsetParent.offsetParent; - } - } - return element.offset().top - $(offsetParent).offset().top - marginDeduct; - } - /** - @name glow.widgets.Sortable - @constructor - @description Reorder page elements using drag and drop - @see Sortable user guide - - @param {glow.dom.NodeList | String | HTMLElement[]} containers The container or containers of the items to be made sortable. - @param {Object} [opts] A set of named options (see below). - @param {glow.dom.NodeList | String} [opts.constrainDragTo] Limit the dragging to within a specific element - @param {String} [opts.axis] Restrict dragging to a particular axis - @param {String} [opts.dropIndicatorClass=glow-sortable-dropindicator] The name of the class to apply to the element that indicates where an item will be dropped. - @param {Boolean} [opts.equaliseColumns=true] Make the bottom of each container the same. - @param {Object} [opts.draggableOptions] An options object to apply to each draggable. - See {@link glow.dragdrop.Draggable Draggable} for options - @param {Function} [opts.onSort] Create an event listener that is fired when the sortable is sorted - i.e. after one of the draggables has been dragged. - - */ - - /** - @name glow.widgets.Sortable#event:sort - @event - @description Fired when an item in the sortable has been dragged. - @param {glow.events.Event} event Event Object - */ - - /** - @name glow.widgets.Sortable#containers - @type glow.dom.NodeList - @description The elements that contain the sortable items. - @example - - var mySortable = new glow.widgets.Sortable('div#sections'); - alert(mySortable.containers); // Returns a nodeList of all the sortable items - - */ - - /** - @name glow.widgets.Sortable#draggables - @type glow.dragdrop.Draggables[] - @description Array of draggables that can be sorted. Read-only. - @example - - var mySortable = new glow.widgets.Sortable('div#sections'); - alert(mySortable.draggables); // Returns glow.dragdrop.Draggables[] - - */ - - /** - @name glow.widgets.Sortable#dropTargets - @type glow.dragdrop.DropTargets[] - @description Array of drop targets that draggable can be dragged to and sorted within. Read-only. - @example - - var mySortable = new glow.widgets.Sortable('div#sections'); - alert(mySortable.dropTargets); // Returns glow.dragdrop.DropTargets[] - */ - - var Sortable = function (containers, opts) { - this._opts = opts = glow.lang.apply({ - dropIndicatorClass : 'glow-sortable-dropindicator', - equaliseColumns : true, - draggableOptions : {} - }, opts || {}); - - this.constrainDragTo = opts.constrainDragTo; - this.axis = opts.axis; - this.draggables = []; - - var containers = this.containers = $(containers), - dropTargets = this.dropTargets = []; - - if (opts.onSort) { - addListener(this, "sort", opts.onSort); - } - - // drop targets - containers.each(function (i) { - dropTargets[i] = new glow.dragdrop.DropTarget(this, { - tolerance : 'intersect', - dropIndicator : 'spacer', - dropIndicatorClass : opts.dropIndicatorClass - }); - }); - - // draggables - this.addItems( containers.children() ); - }; - - - /** - @private - @name glow.widgets.Sortable#handleDrag - @function - @description Called when a drag operation is started. Can return false to prevent dragging - */ - function handleDrag() { - // if items are still in motion, prevent dragging - if (this._itemsInMotion) { - return false; - } - if (this._opts.equaliseColumns) { - equaliseColumns.call(this); - } - // stuff is in the air now... - this._itemsInMotion = true; - } - - /** - @private - @name glow.widgets.Sortable#equaliseColumns - @function - @description Sets the logical bottom of each drop target to the same - position on the page. - - This allows sortable items to be dragged sideways into a column that - does not extend as far as the column that the item is being dragged from. - - */ - - function equaliseColumns () { - var offsets = [], maxBottom = 0, bottom, dropTargets = this.dropTargets; - this.containers.each(function (i) { - var el = $(this); - offsets[i] = offsetTop(el); - bottom = offsets[i] + el[0].offsetHeight; - if (glow.env.khtml) bottom -= el.css('margin-top') + el.css('margin-bottom'); - if (bottom > maxBottom) maxBottom = bottom; - }); - for (var i = 0, l = this.dropTargets.length; i < l; i++) - this.dropTargets[i].setLogicalBottom(maxBottom); - } - - /** - @private - @name glow.widgets.Sortable#handleDrop - @function - @description Event handler that handles a draggable being dropped. - */ - - function handleDrop (e) { - var draggable = e.attachedTo, - el = draggable.element, - target = draggable.activeTarget; - - this._previous = el.prev(); - this._parent = el.parent(); - if (target) target.moveToPosition(draggable); - } - - /** - @private - @name glow.widgets.Sortable#handleAfterDrop - @function - @description Event handler that is called after a droppable is dropped. - - Fires the sort event. - - */ - - function handleAfterDrop (e) { - var draggable = e.attachedTo, - el = draggable.element; - if (! el.prev().eq(this._previous || []) || ! el.parent().eq(this._parent)) { - fire(this, "sort"); - } - // we're done moving - this._itemsInMotion = false; - delete this._previous; - delete this._parent; - } - - /* - Group: Functions - */ - - Sortable.prototype = { - /** - @name glow.widgets.Sortable#addItems - @function - @description Add items to the sortable. - - Should not contain items that are were a child of one of the containers when the sortable was created. - - @param {glow.dom.NodeList | String | Element[]} elements The elements to be added to the sortable. - */ - - - addItems : function (elements) { - var this_ = this, opts = this._opts.draggableOptions; - $(elements).each(function () { - var draggable = new glow.dragdrop.Draggable(this, - glow.lang.apply({ - placeholder : 'none', - axis : this_.axis, - container : this_.constrainDragTo, - dropTargets : this_.dropTargets - }, opts) - ); - - - addListener(draggable, 'drag', handleDrag, this_); - addListener(draggable, 'drop', handleDrop, this_); - addListener(draggable, 'afterDrop', handleAfterDrop, this_); - - this_.draggables.push(draggable); - }); - } - - }; - - glow.widgets.Sortable = Sortable; - } -}); -(window.gloader || glow).module({ - name: "glow.widgets.InfoPanel", - library: ["glow", "1.5.1"], - depends: [[ - "glow", "1.5.1", - 'glow.dom', - 'glow.events', - 'glow.widgets.Panel' - ]], - builder: function(glow) { - var dom = glow.dom, - $ = dom.get, - events = glow.events, - widgets = glow.widgets, - lang = glow.lang, - env = glow.env, - win, - positionRegex = /glow151\-infoPanel\-point[TRBL]/, - offsetInContextDefaults = { - T: {x:"50%", y:"100%"}, - R: {x:0, y:"50%"}, - B: {x:"50%", y:0}, - L: {x:"100%", y:"50%"} - }; - - glow.ready(function() { - win = $(window); - }); - - /* - PrivateMethod: resolveRelative - will work out x & y pixel values for percentage values within a context element. - Non percentage values will just be passed back - */ - function resolveRelative(point, context) { - var vals = [point.x, point.y], - axis = ["x", "y"], - sides = ["Width", "Height"], - i = 0; - - //calculate for x & y - for (; i < 2; i++) { - if (vals[i].slice) { - vals[i] = parseFloat(point[axis[i]]); - if (point[axis[i]].slice(-1) == "%") { - vals[i] = context[0]["offset" + sides[i]] * (vals[i]/100); - } - } - } - return {x: vals[0], y: vals[1]}; - } - - /* - PrivateMethod: calculateBestPointerSide - works out which side the pointer should appear on - */ - function calculateBestPointerSide(contextPos, contextSize) { - //right, we need to work out where to put the box ourselves - var scrollPos = widgets._scrollPos(), - winSize = {x: win.width(), y:win.height()}, - //let's see how much free space there is around the context - freeSpace = { - T: winSize.y - contextPos.top - contextSize.y + scrollPos.y, - R: contextPos.left - scrollPos.x, - B: contextPos.top - scrollPos.y, - L: winSize.x - contextPos.left - contextSize.x + scrollPos.x - }, - preferenceOrder = ["T", "R", "B", "L"]; - - preferenceOrder.sort(function(a, b) { - return freeSpace[b] - freeSpace[a]; - }); - - //could this be made clevererer? (which is why I did the preferenceOrder thing, the first may not always be best) - return preferenceOrder[0]; - } - /** - @name glow.widgets.InfoPanel - @class - @description A panel with content directed at a particular point on the page. - @augments glow.widgets.Panel - @see InfoPanel user guide - - @param {selector|Element|glow.dom.NodeList} content - the element that contains the contents of the Panel. If this is - in the document it will be moved to document.body. If your content node has a child element with class "hd" - it will be added to the header of the panel. Similarly, an element with class "ft" will be added to the - footer of the panel. - - @param {Object} opts - Zero or more of the following as properties of an object: - @param {String|Element|glow.dom.NodeList} [opts.context] Element to point at. - If no context is provided then the panel must be positioned manually using - the container property. - @param {String} [opts.pointerPosition] Restrict the point to a particular side. - The default is dependant on the context's position on the page. - The panel will try to be where it is most visible to the user. To restrict the position, - set this property to "t"/"r"/"b"/"l"; top / left / bottom / right respectively. - @param {String} [opts.theme="light"] Visual theme - Only applies when using the default template. Currently supported themes are "dark" and "light". - @param {Object} [opts.offsetInContext] Position of the pointer within the context. - In the format {x: 24, y: "50%"}. The default points to an edge of - the context, depending on the position of the pointer. - @param {Object} [opts.pointerRegisters] Identify the position of the point within pointer elements. - Only required for custom templates. - @param {Boolean} [opts.modal=false] is the overlay modal? - If true then a default Mask will be created if one is not provided. - @param {String} [opts.ariaRole="description"] The aria role of the panel. - This is used for accessibility purposes, - the default is acceptable for panels which provide descriptive - content for a page element - @param {selector|Element|glow.dom.NodeList} [opts.returnTo] Element to give focus to when the overlay closes - By default, this is the context element. - For accessibility purposes you may want to set an element to give focus to when the overlay closes. - This meanss devices which present data to the user by the cursor position (such as screen readers) - will be sent somewhere useful. - */ - function InfoPanel(content, opts) { - opts = opts || {}; - - if (opts.template) { - var customTemplate = true; - } - - opts = glow.lang.apply({ - modal: false, - theme: "light", - autoPosition: !!opts.context, - pointerRegisters: { - t: {x: "50%", y: 0}, - r: {x: "100%", y: "50%"}, - b: {x: "50%", y: "100%"}, - l: {x: 0, y: "50%"} - }, - ariaRole: "description", - returnTo: opts.context - }, opts); - - //deal with context if it's a selector - opts.context = opts.context && $(opts.context); - - widgets.Panel.call(this, content, opts); - - //add describedby aria bit - opts.context && opts.context.attr("aria-describedby", this.container[0].id); - - if (!customTemplate) { - this.content.addClass("glow151-infoPanel"); - } - - this.content.addClass("glow151-infoPanel-point" + (opts.pointerPosition || "t").slice(0,1).toUpperCase()); - } - lang.extend(InfoPanel, widgets.Panel); - - lang.apply(InfoPanel.prototype, { - /** - @name glow.widgets.InfoPanel#setPosition - @function - @description Change or recalculate the position of the InfoPanel - Call with parameters to - change the position of the InfoPanel or call without parameters to simply - reposition. You may need to call this without parameters if the element - the Panel is pointing at changes position. - - @param {Number|String} [x] - Pixel distance from the left of the document to point at. - @param {Number|String} [y] - Pixel distance from the top of the document to point at. - - @returns this - */ - setPosition: function(x, y) { - //don't use set position if autoPosition is false - var valsPassed = (x !== undefined && !(x.source)), - // a quick and dirty way to find out if the element is visible - isCurrentlyHidden = !this.container[0].offsetHeight; - - if (!(this.autoPosition || valsPassed)) { - return this; - } else if (valsPassed) { - this.autoPosition = false; - } - - if ( isCurrentlyHidden ) { - // Remove display:none; so container will render and - // we can find offSets - this.container.css("display","block"); - } - - var opts = this.opts, - contentNode = this.content[0], - pointerPosition = (opts.pointerPosition || "").slice(0,1), - context = opts.context, - container = this.container, - //here's what we need to position the pointer - pointerElm, - //this will hold the point the user passed, or the context's offset - contextOffset = valsPassed ? {left:x, top:y} : context.offset(), - contextSize = valsPassed ? {x:0, y:0} : {x:context[0].offsetWidth, y:context[0].offsetHeight}, - offsetInContext, - pointOffsetInPanel, - pointerInnerOffset, - panelOffset = container.offset(), - pointerOffset, - lastPointerPosition; - - if (!pointerPosition) { - //right, we need to work out where to put the box ourselves - pointerPosition = calculateBestPointerSide(contextOffset, contextSize); - if (lastPointerPosition != pointerPosition) { - lastPointerPosition = pointerPosition; - contentNode.className = contentNode.className.replace(positionRegex, "glow151-infoPanel-point" + pointerPosition); - pointerElm = container.get(".infoPanel-pointer" + pointerPosition); - } - } else { - pointerPosition = pointerPosition.toUpperCase(); - } - - if (!pointerElm) { - pointerElm = container.get(".infoPanel-pointer" + pointerPosition); - } - - //get default offset if there isn't one - offsetInContext = valsPassed ? {x:0, y:0} : resolveRelative(opts.offsetInContext || offsetInContextDefaults[pointerPosition], context); - pointerInnerOffset = resolveRelative(opts.pointerRegisters[pointerPosition.toLowerCase()], pointerElm); - pointerOffset = pointerElm.offset(); - pointOffsetInPanel = {left: pointerOffset.left - panelOffset.left + pointerInnerOffset.x, top: pointerOffset.top - panelOffset.top + pointerInnerOffset.y}; - - if ( isCurrentlyHidden ) { - // Leave things as we found them - this.container.css("display","none"); - } - - container.css("left", contextOffset.left + offsetInContext.x - pointOffsetInPanel.left + "px"). - css("top", contextOffset.top + offsetInContext.y - pointOffsetInPanel.top + "px"); - - if (env.ie < 7 && !opts.modal) { - var overlayStyle = container[0].style; - this._iframe.css("top", overlayStyle.top). - css("left", overlayStyle.left). - css("width", container[0].offsetWidth + "px"). - css("height", container[0].offsetHeight + "px"); - } - return this; - }, - /** - @name glow.widgets.InfoPanel#setContext - @function - @description Change element to point at. - If no context is provided then the panel must be positioned manually using - the container property. - - @param {String|HTMLElement|glow.dom.NodeList} context - Element to point at - - @returns this - */ - setContext: function(context) { - var currentContext = this.opts.context; - if (currentContext) { - //remove aria from current context - currentContext.removeAttr("describedby"); - //change the returnTo value if it's currently refering to the context - - if ($(this.returnTo)[0] == currentContext[0]) { - this.returnTo = context; - } - } - - //add aria to new context - this.opts.context = $(context).attr("describedby", this.container[0].id); - if (!this.returnTo) { - this.returnTo = this.opts.context; - } - this.autoPosition = true; - if (this.container[0].style.display == "block") { - this.setPosition(); - } - return this; - } - }); - - glow.widgets.InfoPanel = InfoPanel; - } -}); -(window.gloader || glow).module({ - // add the name of your new module - name: "glow.widgets.Slider", - library: ["glow", "1.5.1"], - depends: [[ - "glow", "1.5.1", - // add the names of modules this modules depends on here, eg: - "glow.dom", - "glow.events", - "glow.dragdrop", - "glow.anim", - "glow.widgets" - ]], - builder: function(glow) { - - // private vars - var $ = glow.dom.get, - events = glow.events, - env = glow.env, - // private reference to Ruler class - Ruler, - // private reference to the slider class - Slider, - // array of event names, used to apply event handlers passed in through opts - eventNames = ["slideStart", "slideStop", "change"], - // this holds the name of the last key to be pressed. - // Keep this, because we only want to react to the keyup for the very last keydown - // Also, if this is null we know there aren't any keys currently pressed - lastKeyDown = null, - //interval id for repeating nudges on buttons - nudgeButtonRepeater, - //interval id for repeating keyboard nav - keyRepeater, - //the os sliced to 3 chars - os = navigator.platform.slice(0, 3), - // this will store terms used which differ if the slider is horizontal or vertical - vocabs = [ - { - containerClassNamePart: "slider", - length: "width", - lengthUpper: "Width", - pos: "left", - trackToChange: "_trackOnElm", - axis: "x", - keyIncrement: "RIGHT", - keyDecrement: "LEFT", - pagePos: "pageX" - }, - { - containerClassNamePart: "vSlider", - length: "height", - lengthUpper: "Height", - pos: "top", - trackToChange: "_trackOffElm", - axis: "y", - keyIncrement: "UP", - keyDecrement: "DOWN", - pagePos: "pageY" - } - ], - /* - HTML for the slider - This still needs to be wrapped in
      or -
      before it's created - */ - SLIDER_TEMPLATE = '' + - '
      '+ - '
      '+ - '
      '+ - '
      '+ - '
      '+ - '
      '+ - '
      '+ - '
      '+ - '
      '+ - '
      '+ - '
      '+ - '
      '+ - '
      '+ - '
      '; - - /* - The below is a function that returns false, used in some listeners - */ - function returnFalse() { - return false; - } - - /* - Returns the lowest common divisor for all the arguments. Ignores any - zero params. - */ - function greatestCommonDivisor(/* numbers */) { - var i = 0, - len = arguments.length, - r, - b; - - // get first non-zero param - while ( !(r = arguments[i++]) && (i < len) ); - - for (; i < len; i++) { - b = arguments[i]; - // ignore zeros - if (!b) { continue; } - - while (1) { - r = r % b; - if (!r) { - r = b; - break; - } - b = b % r; - if (!b) { - break; - } - } - } - return r; - } - - // closure for Ruler - (function() { - var vocabs = [ - { - containerClassNamePart: "ruler", - length: "width", - pos: "left" - }, - { - containerClassNamePart: "vRuler", - length: "height", - pos: "top" - } - ]; - - - - /* - container - where to insert the ruler - opts.min - start val - opts.max - end val - opts.tickMajor - interval for major ticks - opts.tickMinor - interval for minor ticks - opts.labels - as with slider - opts.labelMapper - function which takes a label string and returns a new string. For adding stuff like % to the end - opts.vertical - vertical ruler - opts.reverse - makes the right / bottom of the ruler the start - opts.id - id on element - opts.className - additional class names for element - */ - Ruler = function(container, opts) { - container = $(container); - - // assign default options - opts = glow.lang.apply({ - size: 300, - min: 0, - max: 100 - }, opts); - - var vocab = vocabs[!!opts.vertical * 1], - // major tick template, this should be cloned before use - tickMajorElm = glow.dom.create('
      '), - // minor tick template, this should be cloned before use - tickMinorElm = glow.dom.create('
      '), - // label template, this should be cloned before use - labelElm = glow.dom.create('
      '), - // labels converted into a number. This will be NaN for objects - labelInterval = Number(opts.labels), - // this is the largest incement we can use to loop - divisor = greatestCommonDivisor(opts.tickMajor, opts.tickMinor, labelInterval), - // percent position - percentPos, - // tmp holder for an element - tmpElm, - // a couple of shortcuts - reverse = opts.reverse, - // difference between max & min - size = opts.max - opts.min, - // container element - element, - // labels container - labelsElm, - // a tmp clone of the label elm - labelElmClone, - // looping vars - labelPos, - i = opts.min; - - // create element - this.element = element = glow.dom.create(''); - labelsElm = element.get('div.ruler-labels'); - - // add custom ID / Class names - element[0].id = opts.id || ""; - element[0].className += " " + (opts.className || ""); - - for (; i <= opts.max; i += divisor) { - // work out which pixel position we're dealing with - percentPos = ((i - opts.min) / size) * 100; - percentPos = reverse ? 100 - percentPos : percentPos; - - // vocab.pos is top or left - - if ( opts.tickMajor && !((i - opts.min) % opts.tickMajor) ) { - // place a major tick - tickMajorElm.clone().css(vocab.pos, percentPos + "%").appendTo(element); - } else if ( opts.tickMinor && !((i - opts.min) % opts.tickMinor) ) { - // place a minor tick - tickMinorElm.clone().css(vocab.pos, percentPos + "%").appendTo(element); - } - - if (labelInterval && !((i - opts.min) % labelInterval)) { - // place a label - labelElmClone = labelElm.clone().css(vocab.pos, percentPos + "%"); - // set the value as a property of the element - labelElmClone[0]._labelVal = i; - // set the html of the inner span. We pass the label through labelMapper if it exists - labelElmClone.get('span').html( opts.labelMapper ? opts.labelMapper(i) : i ); - // add to labels - labelsElm.append(labelElmClone); - } - } - - // right, our labels are set per position - if (!labelInterval) { - for (labelPos in opts.labels) { - // work out which pixel position we're dealing with - percentPos = ((Number(labelPos) - opts.min) / size) * 100; - percentPos = reverse ? 100 - percentPos : percentPos; - - if (percentPos <= 100) { - // place a label - labelElmClone = labelElm.clone().css(vocab.pos, percentPos + "%"); - // set the value as a property of the element - labelElmClone[0]._labelVal = Number(labelPos); - // set the html of the inner span. We pass the label through labelMapper if it exists - labelElmClone.get('span').html( opts.labelMapper ? opts.labelMapper(opts.labels[labelPos]) : opts.labels[labelPos] ); - // add to labels - labelsElm.append(labelElmClone); - } - } - } - - // place it in the document - container.append(element); - }; - - })(); - - /* - This returns either the horizontal or vertical vocab. - A lot of lines are the same for a horizonal & vertical sliders - aside from a couple of keywords. The vocabs cater for these - keywords. - */ - function getVocab(slider) { - return vocabs[!!slider._opts.vertical * 1]; - } - - /* - This adjusts size the active / inactive part of the slider, according to the handle position - */ - function adjustTracks(slider) { - var vocab = getVocab(slider); - // size the active / inactive part of the slider - // trackToChange is _trackOnElm or _trackOffElm, length is width or height - slider[vocab.trackToChange][0].style[vocab.length] = parseInt(slider._handleElm[0].style[vocab.pos]) + (slider._handleSize / 2) + "px"; - } - - /* - This is called as the mouse moves when the slider is being dragged - */ - function handleMouseMove() { - adjustTracks(this); - if (this._opts.changeOnDrag) { - var newVal = valueForHandlePos(this); - change(this, newVal); - (this._boundInput[0] || {}).value = newVal; - } - } - - /* - This is called when the user interacts with the slider using keyboard (keydown) - */ - function handleKeyNav(event) { - - var vocab = getVocab(this) - that = this; - - if (lastKeyDown == "prevented") { - // this is a repeat of a prevented slideStart, just exit - return false; - } else if (lastKeyDown != event.key) { - // this isn't a repeating keydown event, it's a new slide - - // if lastKeyDown isn't null, key-sliding has changed direction but not actually restarting - if (!lastKeyDown && slideStart(this).defaultPrevented()) { - lastKeyDown = "prevented"; //this allows us to pick up repeats - return false; - } - - // set up repeating - // cancel any existing repeating - clearInterval(keyRepeater); - - keyRepeater = setTimeout(function() { - keyRepeater = setInterval(function() { - nudge(that, event.key == vocab.keyIncrement /* is true if the user pressed up / right */ ? 1 : -1 ); - }, 40); - }, 500); - - // do the initial nudge - nudge(that, event.key == vocab.keyIncrement /* is true if the user pressed up / right */ ? 1 : -1 ); - - // we use this to know which keyup to react to - lastKeyDown = event.key; - } - return false; - } - /* - This is called when the user stops interacting with keyboard (keyup) - */ - function handleKeyNavEnd(event) { - if (lastKeyDown == event.key) { - // if lastKeyDown != event.key, sliding hasn't actually finished - lastKeyDown = null; - // cancel any existing repeating - clearInterval(keyRepeater); - // stop sliding - slideStop(this); - } - } - - /* - Called when the back / fwd button is pressed down - */ - function handleButtonDown(event) { - if ( !this._disabled && !slideStart(this).defaultPrevented() ) { - //work out which button was pressed - var nudgeAmount = event.attachedTo.className.indexOf("-fwd") != -1 ? 1 : -1, - that = this; - - //nudge slider - nudge(this, nudgeAmount); - - //set the action to repeat - nudgeButtonRepeater = setTimeout(function() { - nudgeButtonRepeater = setInterval(function() { - nudge(that, nudgeAmount); - }, 40); - }, 500); - } - return false; - } - - /* - Called when the back / fwd button is released (or mouse out) - */ - function handleButtonUp(event) { - if (nudgeButtonRepeater) { - clearTimeout(nudgeButtonRepeater); - clearInterval(nudgeButtonRepeater); - nudgeButtonRepeater = null; - slideStop(this); - } - return false; - } - - /* - Move the slider by a step. Used by the nudge buttons and keyboard events. - stepsToNudge = number of steps to nudge - */ - function nudge(slider, stepsToNudge) { - // if the step is zero, we step by a pixel - var changeBy = (slider._opts.step || (1 / slider._pixelsPerVal)) * stepsToNudge; - // update the view - slider._nudgeVal = sanitiseVal(slider, slider._nudgeVal + changeBy); - updateSliderUi(slider, slider._nudgeVal); - if (slider._opts.changeOnDrag) { - change(slider, slider._nudgeVal); - (slider._boundInput[0] || {}).value = slider._val; - } - } - - /* - Update the slider UI for 'val', or the current value. Expects val to - be already sanitised. - */ - function updateSliderUi(slider, val) { - var valueAsPixels, - vocab = getVocab(slider), - val = val === undefined ? slider._val : val; - - //calculate the top / left position of the slider handle - valueAsPixels = slider._opts.vertical ? - // TODO copy the horizontal calculation and do the flipping afterwards - (slider._opts.max - val) * slider._pixelsPerVal : //vertical calculation - (val - slider._opts.min) * slider._pixelsPerVal; //horizontal calculation - - // move the handle - // vocab.pos = left or top - slider._handleElm[0].style[vocab.pos] = valueAsPixels + "px"; - - // change track sizes - adjustTracks(slider); - } - - /* - Generate value based on handle position. - This will calculate the value and round it to the - nearest valid value - */ - function valueForHandlePos(slider) { - var vocab = getVocab(slider), - //get the left or top value of the handle - handlePos = parseInt(slider._handleElm[0].style[vocab.pos]), - //calculate value - newVal = slider._opts.vertical ? - (slider._trackSize - slider._handleSize) - handlePos : - handlePos; - - //we've got the value from the start position in pixels, now we need the - //value in terms of the slider - newVal = (newVal / slider._pixelsPerVal) + slider._opts.min; - - //round to nearest step - return sanitiseVal(slider, newVal); - } - - /* - Takes a value and rounds it to the nearest value in step / slider range. - - NaN values will be treated as 0 - */ - function sanitiseVal(slider, val) { - var step = slider._opts.step, - min = slider._opts.min, - max = slider._opts.max; - - val = Number(val) || 0; - - //obey boundaries - if (val < min) { - return min; - } - if (val > max) { - // a little more maths this time, we need to work out the maximum value we can step to - return max - ((max - min) % (step || 1)); - } - - // we don't need to calculate anything if step is zero - if (step == 0) { return val; } - // else round to step - return Math.round( (val - min) / step ) * step + min; - } - - /* - Update the bound form field & fires an onchange if necessary. - If newVal is undefined it will be looked up (but passing in the value is faster) - */ - function change(slider, newVal) { - var currentVal = slider._val, - //calculate value if needed - newVal = (newVal === undefined) ? valueForHandlePos(slider) : newVal; - - //update value - slider.element.attr("aria-valuenow", newVal); - slider._val = newVal; - - //fire onchange if we have to - if (newVal != currentVal) { - events.fire(slider, "change"); - } - } - - /* - This is called when sliding starts. Fires the start event and returns an event object - */ - function slideStart(slider) { - //capture current value in case we need to reset it - slider._valBeforeSlide = slider._nudgeVal = slider._val; - return events.fire(slider, "slideStart"); - } - - /* - This is called when sliding stops. Fires event and resets value if default cancelled - */ - function slideStop(slider) { - var eventData = { - // current value - initialVal: slider._valBeforeSlide, - //get new value - currentVal: valueForHandlePos(slider) - }; - - if ( events.fire(slider, "slideStop", eventData).defaultPrevented() ) { - change(slider, slider._valBeforeSlide); - slider.val(slider._valBeforeSlide); - return; - } - change(slider, eventData.currentVal); - //if snaping ondrop is on, update handle position with val() - if (slider._opts.snapOnDrop) { - slider.val(eventData.currentVal); - } else { - (slider._boundInput[0] || {}).value = eventData.currentVal; - } - } - - /* - Works out how many pixels a value is, adjust the size so ticks land - on pixels - */ - function initUi(slider, ruler) { - var opts = slider._opts, - //get hold of the vocab we need to use - vocab = getVocab(slider), - //will hold a nodelist of the slider - element = slider.element, - //the height of the slider when the track is 0px. Basically any padding, margins and borders - verticalPaddingHeight, - //adjusted size after catering for fitting ticks and steps onto pixels - adjustedSize, - //value the slider should start on - startVal, - //we listen to the mouse moving while the handle is dragging - mouseMoveListener, - //glow.dragDrop.Draggable instance for handle - handleDraggable, - //draggable options - draggableOpts, - //get the smallest non-zero value for step (if we're snapping while dragging), labelMinor, labelMajor - smallestStepingVal = greatestCommonDivisor( (opts.step * opts.snapOnDrag), opts.tickMinor, opts.tickMajor ), - oldLengthStyle; - - - if (opts.vertical) { - verticalPaddingHeight = element.height(); - //apply height, we need to come up with a total of opts.size - slider._trackOnElm.height(opts.size - verticalPaddingHeight); - } else { - //apply width - element.width(opts.size); - } - - //vocab.length holds 'width' or 'height' - slider._trackSize = slider._trackElm[vocab.length](); - // get the handle size - // right, this gets complicated in IE if the handle element has a percentage legnth, the offset values come in too late - oldLengthStyle = slider._handleElm[0].style[vocab.length]; - if (glow.env.ie < 8) { - slider._handleElm[0].style[vocab.length] = slider._handleElm[0].currentStyle[vocab.length]; - slider._handleElm[0].style[vocab.length] = slider._handleElm[0].style["pixel" + vocab.lengthUpper]; - } - slider._handleSize = slider._handleElm[0]["offset" + vocab.lengthUpper]; - slider._handleElm[0].style[vocab.length] = oldLengthStyle; - - - - //apply the start value - if (opts.val != undefined) { //first use the option - startVal = opts.val - } else if (slider._boundInput[0] && slider._boundInput[0].value != "") { //then the form field - startVal = slider._boundInput[0].value - } else { //default to min val - startVal = opts.min; - } - - /*if (slider._handleElm.css("position") != "absolute") { - // uh oh, our CSS hasn't loaded yet - // put in a tmp value for _pixelsPerVal, to prevent errors - slider._pixelsPerVal = 1; - slider._val = startVal; - slider._trackOnElm[0].style.height = ""; - // rerun this function later - setTimeout(function() { - initUi(slider, ruler); - }, 0); - return; - }*/ - - //adjust size so labels / ticks sit on pixels if we have to - if (smallestStepingVal) { - adjustedSize = - // get the value of a pixel - ((slider._trackSize - slider._handleSize) / (opts.max - opts.min)) - // multiple by the smallest non-zero stepping value - * smallestStepingVal; - - adjustedSize = - ( - //floor the pixel step (we don't want fractions) - //get the new value of a pixel and work out the draggable pixel range - (Math.floor(adjustedSize) / smallestStepingVal) * (opts.max - opts.min) - //add on the handle size to get the new size of the inner track - ) + slider._handleSize; - - //apply the new size - if (opts.vertical) { - //apply the new size to the track - slider._trackOnElm.height(adjustedSize); - if (ruler) { - ruler.element.height(adjustedSize - slider._handleSize); - } - } else { - //work out the difference between the old track size and the new track size - //apply that difference to the element width - element.width(opts.size - (slider._trackSize - adjustedSize)); - } - slider._trackSize = slider._trackElm[vocab.length](); - } - slider._pixelsPerVal = ((slider._trackSize - slider._handleSize) / (opts.max - opts.min)); - //apply the start value - slider.val(startVal); - - // ARIA - initial setup - element.attr({ - "aria-valuenow": slider._val, - "aria-valuemin": opts.min, - "aria-valuemax": opts.max - }); - - //create draggable - draggableOpts = { - axis: vocab.axis, - container: slider._trackElm, - onDrag: function() { - if (slider._disabled || slideStart(slider).defaultPrevented()) { - return false; - } - slider._stateElm.addClass("slider-active"); - mouseMoveListener = events.addListener(document, "mousemove", handleMouseMove, slider); - }, - onDrop: function() { - slider._stateElm.removeClass("slider-active"); - events.removeListener(mouseMoveListener); - slideStop(slider); - } - }; - - if (opts.snapOnDrag) { - draggableOpts.step = slider._pixelsPerVal * opts.step; - } - - handleDraggable = new glow.dragdrop.Draggable(slider._handleElm, draggableOpts); - - // track clicking - if (opts.jumpOnClick) { - events.addListener(slider._trackElm, "mousedown", function(event) { - if (slider._disabled || event.source == slider._handleElm[0]) { - // don't react if slider is disabled or handle is being used - return; - } - // vocab.pos is top / left, vocab.pagePos is pageX / pageY - var vocab = getVocab(slider), - oldPagePos = event[vocab.pagePos]; - - // This is a bit cheeky... - // We're tricking the draggable into thinking the mouse is in the centre of the handle. - // This way, all the handling of stepping and bounds is handled by the draggable - // TODO: Move this functionality into Draggable - event[vocab.pagePos] = slider._handleElm.offset()[vocab.pos] + (slider._handleSize / 2); - - // so, make the draggable think the user has clicked in the centre of it - if (handleDraggable._startDragMouse.call(handleDraggable, event) === false) { - // now make it think the mouse has moved - event[vocab.pagePos] = oldPagePos; - handleDraggable._dragMouse.call(handleDraggable, event); - adjustTracks(slider); - // cancel default click - return false; - } - }); - } - } - - /** - @name glow.widgets.Slider - @class - @description Form control for setting a numerical value within a range. - -
      Widgets must be called in a glow.onReady() call.
      - - @param {glow.dom.NodeList | Selector | HTMLElement} container Container of Slider. - The Slider will be appended to this element, retaining existing contents. - - @param {Object} [opts] Options object - @param {Number} [opts.size=300] Pixel width / height of the slider - The size may be automatically reduced so that stepping sits on absolute pixels. - @param {glow.dom.NodeList | Selector | HTMLElement} [opts.bindTo] Form element to bind value to. - Changes to this form element will also cause the Slider to update - @param {Number} [opts.min=0] Minimum value for slider. - Can be negative but must be smaller than opts.max. - @param {Number} [opts.max=100] Maximum value for slider. - Can be negative but must be greater than opts.min. - @param {Number} [opts.step=1] Step between values. - 0 or fractional step values may result in fractional values. - @param {Boolean} [opts.snapOnDrag=false] If true, the slide handle will snap to each step during drag. - This is a visual effect, it will not impact the value of the slider. - @param {Boolean} [opts.snapOnDrop=false] If true, the slide handle will snap to a step position on drop. - This is a visual effect, it will not impact the value of the slider. - @param {Number} [opts.val] Start value. - By default, the value from the bound form element will be used. If none - exists, the minimum value will be used. - @param {Boolean} [opts.changeOnDrag=false] Fire the 'change' event during drag. - @param {String} [opts.id] Value for Slider's ID attribute - @param {String} [opts.className] Values for Slider's class attribute. - Space separated values. - @param {Object} [opts.labels] Labels for Slider values. - For numerical labels, a Number can be provided for the interval between labels. Text labels can - be specified in the form {"value": "label"}, - eg {"0": "Min", "50": "Medium", "100": "Maximum"}. Labels may contain - HTML, so images could be used. - @param {Number} [opts.tickMajor] The interval between each major tick mark. - @param {Number} [opts.tickMinor] The interval between each minor tick mark. - @param {Boolean} [opts.vertical=false] Create a vertical slider? - @param {String} [opts.theme="light"] Visual theme to use. - Current themes are "light" and "dark". - @param {Boolean} [opts.jumpOnClick=true] Does the track react to clicks? - If true, when the user clicks on the slider track the handle - will move to that position. Dragging can be initiated from anywhere - on the track. - @param {Boolean} [opts.buttons=true] Include fine-tuning buttons? - @param {Function} [opts.onSlideStart] Event shortcut. - See documentation below - @param {Function} [opts.onSlideStop] Event shortcut. - See documentation below - @param {Function} [opts.onChange] Event shortcut. - See documentation below - - @example - var mySlider = new glow.widgets.Slider("#sliderContainer", { - min: 5, - max: 80, - id: "ageSlider", - tickMajor: 5, - tickMinor: 1, - labels: 5 - }); - - @example - var mySlider = new glow.widgets.Slider("#fishLevelSlider", { - bindTo: 'numberOfFishInTheSea', - buttons: false, - className: 'deepBlue', - onSlideStart: function() { - glow.dom.get('img#fishes').toggleCss('moving'); - }, - onSlideStop: function() { - glow.dom.get('img#fishes').toggleCss('moving'); - }, - size: '600', - }); - - @example - var mySlider = new glow.widgets.Slider("#soundLevelHolder", { - min: 1, - max: 100, - id: "soundLevel", - onChange: function () { - updateFlash('sound', this.val()); - } - tickMajor: 10, - tickMinor: 5, - labels: 5, - vertical: true - }); - - @see Slider user guide - @see Restyling a Slider - @see Slider Demo: Colour Picker - */ - /** - @name glow.widgets.Slider#event:slideStart - @event - @description Fired when the user starts moving the slider. - - Fired on both keyboard and mouse interaction. Preventing the - default will prevent the user moving the slider. - - @param {glow.events.Event} event Event Object - */ - /** - @name glow.widgets.Slider#event:slideStop - @event - @description Fired when the user stops moving the slider. - - Fired on both keyboard and mouse interaction. Preventing the - default will return the slider to the position it was before - the user started dragging. - - The event object contains properties 'initialVal' and - 'currentVal', which contain the value before dragging and the - value about to be set respectively. - - @param {glow.events.Event} event Event Object - */ - /** - @name glow.widgets.Slider#event:change - @event - @description Fired when the slider value changes. - - This is usually fired when the user drops the handle, but - can be configured to fire as the user is dragging the slider, - via the 'changeOnDrag' option. Change also occurs when the value - in the bound form element is changed by the user. - - Change does not fire when the slider's value is set via - mySlider.val(). - - @param {glow.events.Event} event Event Object - */ - - Slider = glow.widgets.Slider = function(container, opts) { - //private vars - /** - @name glow.widgets.Sliders#_boundInput - @type glow.dom.NodeList - @private - @description Element the slider is bound to. - Can be an empty NodeList if no input was specified by the user - */ - /** - @name glow.widgets.Sliders#_stateElm - @private - @type glow.dom.NodeList - @description Element to apply state-related class names to, like slider-disabled - */ - /** - @name glow.widgets.Sliders#_trackElm - @private - @type glow.dom.NodeList - @description Element to apply state-related class names to, like slider-disabled - */ - /** - @name glow.widgets.Sliders#_trackOnElm - @private - @type glow.dom.NodeList - @description Element containing the "on" state of the track - */ - /** - @name glow.widgets.Sliders#_trackOffElm - @private - @type glow.dom.NodeList - @description Element containing the "on" state of the track - */ - /** - @name glow.widgets.Sliders#_handleElm - @private - @type glow.dom.NodeList - @description The slider handle - */ - /** - @name glow.widgets.Sliders#_trackSize - @private - @type Number - @description Pixel width / height for track - */ - /** - @name glow.widgets.Sliders#_handleSize - @private - @type Number - @description Pixel width / height for handle - */ - /** - @name glow.widgets.Sliders#_pixelsPerVal - @private - @type Number - @description How much is each pixel movement of the handle worth? - */ - /** - @name glow.widgets.Sliders#_val - @private - @type Number - @description Current value of the slider. Set and read by val() - */ - /** - @name glow.widgets.Sliders#_valBeforeSlide - @private - @type Number - @description The value of the slider when sliding last began - */ - /** - @name glow.widgets.Sliders#_nudgeVal - @private - @type Number - @description Sometimes we need to hold the value of the slider as it's moving. - We don't want to set the actual val, as the change isn't over yet - */ - /** - @name glow.widgets.Sliders#_disabled - @private - @type Boolean - @description Is the slider disabled - */ - this._disabled = false; - - //normalise container - container = $(container); - - //set up the default options - this._opts = opts = glow.lang.apply({ - min: 0, - max: 100, - step: 1, - theme: "light", - jumpOnClick: 1, - buttons: 1, - size: 300 - }, opts); - - var i, - //tmp holder of converted event names - onEventName, - //get hold of the vocab we need to use - vocab = getVocab(this), - //will hold a nodelist of the slider - element, - //the height of the slider when the track is 0px. Basically any padding, margins and borders - verticalPaddingHeight, - //adjusted size after catering for fitting ticks and steps onto pixels - adjustedSize, - //key listeners for keyboard nav, - keyListeners = [], - //'that' for event listeners - that = this, - //nodelist to the back & fwd buttons - buttonElms, - //reference to the ticks & labels ruler used - ruler, - //get the smallest non-zero value for step (if we're snapping while dragging), labelMinor, labelMajor - smallestStepingVal = greatestCommonDivisor( (opts.step * opts.snapOnDrag), opts.tickMinor, opts.tickMajor ); - - //apply the listeners passed in through opts - i = eventNames.length; - while (i--) { - //convert names. Eg "slideStart" to "onSlideStart" - onEventName = "on" + eventNames[i].charAt(0).toUpperCase() + eventNames[i].slice(1); - if (opts[onEventName]) { - events.addListener(this, eventNames[i], opts[onEventName]) - } - } - - // get bound input - this._boundInput = opts.bindTo ? $(opts.bindTo) : new glow.dom.NodeList(); - - /** - @name glow.widgets.Slider#element - @type glow.dom.NodeList - @description Slider HTML Element. - This can be used to perform DOM manipulation on the slider - - @example - //get the offset of a slider - mySlider.element.offset(); - */ - // add in wrapping element - this.element = element = glow.dom.create('
      ' + SLIDER_TEMPLATE + '
      '); - this._trackElm = element.get("div.slider-track"); - this._trackOnElm = element.get("div.slider-trackOn"); - this._trackOffElm = element.get("div.slider-trackOff"); - this._handleElm = this._trackElm.get("div.slider-handle"); - this._stateElm = element.get("div.slider-state") - - // add in the theme - element.get("div.slider-theme").addClass("slider-" + opts.theme); - - // removes buttons if the user doesn't want them - !opts.buttons && this._stateElm.addClass("slider-noButtons"); - - // add custom ID / Class names - element[0].id = opts.id || ""; - element[0].className += " " + (opts.className || ""); - - // add Ruler (ticks and labels) - if (opts.tickMajor || opts.tickMinor || opts.labels) { - opts.reverse = opts.vertical; - ruler = new Ruler(element.get("div.slider-labels"), opts); - } - - //add to page - this.element.appendTo(container); - - initUi(this, ruler); - - if (this._boundInput[0]) { - // listen for changes - events.addListener(this._boundInput, "change", function() { - var snappedValue = sanitiseVal(that, this.value); - change(that, snappedValue); - // using val() to do the UI & value updating results in sanitise being called twice, but saves code - that.val(snappedValue); - }) - } - - // keyboard nav - events.addListener(this.element, "focus", function() { - if ( !that._disabled ) { - that._stateElm.addClass("slider-active"); - // vocab.keyIncrement is "RIGHT" or "UP" - // vocab.keyDecrement is "LEFT" or "DOWN" - keyListeners[0] = events.addKeyListener(vocab.keyIncrement, "down", handleKeyNav, that); - keyListeners[1] = events.addKeyListener(vocab.keyDecrement, "down", handleKeyNav, that); - keyListeners[2] = events.addKeyListener(vocab.keyIncrement, "up", handleKeyNavEnd, that); - keyListeners[3] = events.addKeyListener(vocab.keyDecrement, "up", handleKeyNavEnd, that); - // the following prevents the arrow keys scrolling in some browsers (such as Opera) - keyListeners[4] = events.addKeyListener(vocab.keyIncrement, "press", returnFalse); - keyListeners[5] = events.addKeyListener(vocab.keyDecrement, "press", returnFalse); - } - }); - - events.addListener(this.element, "blur", function() { - that._stateElm.removeClass("slider-active"); - var i = keyListeners.length; - while (i--) { events.removeListener(keyListeners[i]) } - keyListeners = []; - }); - - // nudge buttons - buttonElms = this.element.get(".slider-btn-fwd, .slider-btn-bk"); - events.addListener(buttonElms, "mousedown", handleButtonDown, this); - events.addListener(buttonElms, "mouseup", handleButtonUp, this); - events.addListener(buttonElms, "mouseout", handleButtonUp, this); - - // label clicking - if (ruler) { - events.addListener(ruler.element, "mousedown", function(event) { - // exit if slider disabled - if (that._disabled) { return; } - - var labelElm = $(event.source), - snappedValue; - - // right, we need to turn labelElm (currently the event source) into the label element - while ( labelElm[0] != ruler.element[0] ) { - if ( labelElm.hasClass("ruler-label") ) { // yey, we found the label - // check the value is valid - snappedValue = sanitiseVal(that, labelElm[0]._labelVal); - change(that, snappedValue); - // update UI - that.val(snappedValue); - return false; - } - labelElm = labelElm.parent(); - } - // the user must have clicked outside a label - }); - } - }; - - Slider.prototype = { - /** - @name glow.widgets.Slider#disabled - @function - @description Get / Set the disabled state of the slider - Call without parameters to get the state, call with parameters - to set the state. - - Disabling the slider will also disable the bound form element. - - @param {Boolean} [disable] Disable the slider? - 'false' will enable a disabled slider. - - @returns - The slider instance when setting. Boolean when getting. - - @example - // disabling a slider - mySlider.disabled( true ); - - // toggling a slider - mySlider.disabled( !mySlider.disabled() ) - */ - disabled: function(disable) { - if (disable !== undefined) { - // setting... - - // cast to boolean and set - this._disabled = disable = !!disable; - - // ARIA update - this.element.attr("aria-disabled", disable); - - // add / remove the disabled class - this._stateElm[disable ? "addClass" : "removeClass"]("slider-disabled"); - - //disable the bound form field, if it's there - ( this._boundInput[0] || {} ).disabled = disable; - - return this; - } else { - // getting... - return this._disabled; - } - }, - /** - @name glow.widgets.Slider#val - @function - @description Get / Set the value the slider - Call without parameters to get the value, call with parameters - to set the value. - - @param {Number} [newVal] New value for the slider - - @returns - The slider instance when setting. Number when getting. - - @example - // getting the current value - var sliderVal = mySlider.val(); - - // setting the value - mySlider.val(50); - */ - /* - This param is a possible future enhancment - - @param {glow.anim.Animation | Boolean} [anim] Animate the slider to the new value. - Ommiting this parameter will simply switch the slider to the new value. - - True will create a basic easing animation, or a custom animation can - be provided. - - Animating to a new value simulates human interaction. The usual events - will be fired along the way. - */ - val: function(newVal) { - if (newVal != undefined) { - //setting... - - //sanitise and set - this._val = sanitiseVal(this, newVal); - //update aria - this.element.attr("aria-valuenow", this._val); - //update form - (this._boundInput[0] || {}).value = this._val; - //update UI - updateSliderUi(this); - - return this; - } else { - return this._val; - } - }, - /** - @name glow.widgets.Slider#valToLabel - @function - @description Get the label for a value. - - @param {Number} [val] The value. - If omitted, the current slider value is used. - - @returns {String} - Label text. - - @example - // find out the label the handle is nearest - var label = mySlider.valToLabel(); - */ - valToLabel: function(val) { - // pick the value up from the slider if there are no params - if (val === undefined) { - val = this._val; - } - - var labels = this._opts.labels, - // the lowest difference between labels - lowestDiff = Infinity, - // the lowest different passed through Math.abs - lowestDiffAbs = Infinity, - // label that has the lowest differece - currentLabel, - // tmp value - diffAbs, - i; - - // return null if there are no labels - if (labels === undefined) { return null; } - - // look out for interval labels - if (typeof labels == 'number') { - // round to the nearest interval label - return Math.round(val / labels) * labels; - } - - // deal with object labels - // is the value the actual position of a label? If so return it - if (labels[val]) { - return labels[val]; - } - - // bah, we have to look for the closest label - for (i in labels) { - // get the difference between the label and value - diffAbs = Math.abs(Number(i) - val); - - if (diffAbs < lowestDiffAbs) { - // the difference is less than ones found previously, we have a new winner - lowestDiffAbs = diffAbs; - lowestDiff = Number(i) - val; - currentLabel = labels[i]; - } else if (diffAbs == lowestDiffAbs) { - // uh oh, we have two diffs the same. Round up! - if (lowestDiff < 0) { - lowestDiffAbs = diffAbs - lowestDiff = Number(i) - val; - currentLabel = labels[i]; - } - } - } - return currentLabel; - }, - /** - @name glow.widgets.Slider#labelToVal - @function - @description Get the value for a particular label. - If the label doesn't exist in the slider, null is returned - - @param {String} [label] A label used in the slider - - @returns {Number} - Value. - - @example - // find out that value of "Medium" on the slider - var val = mySlider.labelToVal("Medium"); - */ - labelToVal: function(label) { - var i, - labels = this._opts.labels; - - // return null if there are no labels - if (labels === undefined) { return null; } - - // look out for interval labels - if (typeof labels == 'number') { - label = Number(label); - - // if the label is divisable by the interval, return it - if ( !(Number(label) % labels) && !isNaN(label) ) { - return label; - } - return null; - } - - // loop through the objects until we find a match - for (i in labels) { - if (label == labels[i]) { - return Number(i); - } - } - return null; - } - }; - } -}); - -(window.gloader || glow).module({ - name: 'glow.widgets.AutoSuggest', - library: ['glow', '1.5.1'], - depends: [[ - 'glow', '1.5.1', - 'glow.dom', - 'glow.events', - 'glow.anim', - 'glow.widgets', - 'glow.net', - 'glow.widgets.Overlay' - ]], - - builder: function(glow) { -/* private fields *************************************************************/ - var $ = glow.dom.get, // shortcuts - events = glow.events, - anim = glow.anim; - -/* private functions **********************************************************/ - /** - @private - @description Attach a text input element to this AutoSuggest instance. - */ - function bindTo(that, inputElement) { - that.inputElement = $(inputElement); - if (!that.inputElement[0].tagName.toLowerCase() == 'input') { - throw 'Argument "inputElement" must be set to an input HTMLElement.'; - } - - that.inputElement.attr('autocomplete', 'off'); // stop arrow keys from doing browsery things - } - - /** - @private - @description Uses AJAX to refresh the data from a server URL. - */ - function downloadData(that, url, callback) { /*debug*///console.log("downloadData("+url+", "+callback+")"); - if (that._lastDownload == url) { // no need to reload same url again - if (callback) callback.apply(that, arguments); - else that.find(); - } - else { - that._lastDownload = url; - - if (that._pendingRequest) that._pendingRequest.abort(); - that._pendingRequest = glow.net.get( - url, - { - onLoad: function(r) { - var dataObject = (that.opts.parseData)? that.opts.parseData.apply(that, [r]) : eval(r.text()); - - that._pendingRequest = null; - that.setData(dataObject); - - // create and populate Event instance - var e = new events.Event(); - e.data = dataObject; - e.text = r.text(); - events.fire(that, 'dataLoad', e); - - if (callback) callback.apply(that, arguments); - else that.find(); - }, - onError: function(r) { - var e = new events.Event(); - e.response = r; - events.fire(that, 'dataError', e); - }, - onAbort: function(r) { - var e = new events.Event(); - e.response = r; - events.fire(that, 'dataAbort', e); - } - } - ); - } - } - - /** - @private - @description Check whether the overlay is currently visible. - @type {Boolean} - */ - function isVisible(that) { - return ($(that.overlay.container).css('display') == 'block'); - } - - /** - @private - @description Position the overlay under the input element. - */ - function place(that) { - var inputOffset = that.inputElement.offset(); - - that.overlay.container - .css('left', parseInt(inputOffset.left) + 'px') - .css('top', parseInt(inputOffset.top + that.inputElement[0].offsetHeight-1) + 'px') - .css('width', ((that.opts.width)? that.opts.width : that.inputElement[0].offsetWidth + 'px')); - } - - /** - @private - */ - function buildIndexer(that) { // create a custom one from an opt value - if (that.opts.index) { - if (typeof that.opts.index == 'function') { - that._indexer = that.opts.index; // it's up to the user - } - else if (typeof that.opts.index == 'string') { // it's a field name - that._indexer = (function(index) { - return function(dataItem) { - return dataItem[index] - } - })(that.opts.index); - } - else if (typeof that.opts.index.push != 'undefined') { // it's an array of field names - that._indexer = (function(index) { - var l = index.length-1; // assumes the index length never changes - return function(dataItem) { - var result = []; - for (var i = l; i >= 0; i--) { - result[i] = dataItem[index[i]]; - } - return result; - } - })(that.opts.index); - } - else throw 'opts.index must be of type function, string or array, not ' + typeof that.opts.index + '.'; - } - else { // default - that._indexer = function (dataItem) { // the default indexer - return (dataItem['name'])? dataItem['name'] : dataItem.toString(); // TODO: what if there is no 'name' field? - } - } - } - - /** - @private - @description Make the next item in the results active. - */ - function nextItem(that) { - var currItem = $(that.overlay.container).get('.active'); - - if (currItem.length == 0) { - var items = $(that.overlay.container).get('li'); // TODO - if (items.length) activateItem(that, items[0]); - } - else { - var nextItem = currItem.next(); - - if (nextItem && !nextItem.is('ul')) { - deactivateItem(that, currItem); - activateItem(that, nextItem); - } - } - } - - /** - @private - @description Make the previous item in the results active. - */ - function prevItem(that) { - var currItem = $(that.overlay.container).get('.active'); - if (currItem.length == 0) { // no item is active so return the last item - var allItems = $(that.overlay.container).get('li'); - var lastItem = allItems[allItems.length-1]; - activateItem(that, lastItem); - } - else { - var prevItem = currItem.prev(); - - if (prevItem && !prevItem.is('ul')) { - deactivateItem(that, currItem); - activateItem(that, prevItem); - } - } - } - - /** - @private - @description Given an HTML element, return this AutoSuggest list. - */ - function getParentListItem(that, node) { /*debug*///console.log("getParentListItem("+node+")"); - var listItem = node; - while (listItem.parentNode && listItem.parentNode.parentNode) { - if ($(listItem.parentNode.parentNode).hasClass('glow151-autoSuggest')) break; - listItem = listItem.parentNode; - } - return (listItem.nodeName.toLowerCase() == 'li')? listItem : null; - } - - /** - @private - @description Make the given list item from the results active. - */ - function activateItem(that, listItem) { - deactivateItems(that, listItem); - $(listItem).addClass('active'); - - if (that._lastActive != listItem) { - that._lastActive = listItem; - events.fire(that, 'itemActive'); - } - } - - /** - @private - @description Make the item from the results at the given offeset active. - */ - function activateItemOffset(that, offset) { - var li = that.overlay.container.get('li')[offset]; // TODO - if (li) $(li).addClass('active'); - } - - /** - @private - @description Make the given list item not active. - */ - function deactivateItem(that, listItem) { - $(listItem).removeClass('active'); - } - - /** - @private - @description Make all list items not active. - */ - function deactivateItems(that, listItem) { - var list = (listItem) ? $(listItem).parent() : that.overlay.container.get('ul'); - list.get("li").each( - function(i) { - $(this).removeClass('active'); - } - ); - } - - /** - @private - @description Used internally to add all necessary events. - */ - function addEvents(that) { /*debug*///console.log('addEvents()'); - - events.addListener( // a result item has become active - that, - 'itemActive', - function(e) { // fire any onItemActive handlers in the opts - if (!isVisible(that)) return false; - var selectedOffset = that.getSelectedOffset(); - if (selectedOffset == -1) return false; - - if (that.opts.onItemActive) { - var e = new events.Event(); - e.activeItem = that._found[selectedOffset]; - that.opts.onItemActive.apply(that, [e]); - } - - return true; - } - ); - - events.addListener( // the mouse was clicked inside the input element - that.inputElement, - 'mousedown', - function(e) { // bail, but keep any hilighted suggestion - clearTimeout(that.findTimeout); - - // user accepts the hilited text - that._value = that.inputElement.val(); - valueChanged(that, true); - - that.hide(); - - that.value += that._selected; - that._selected = ''; - - return true; - } - ); - - events.addListener( // a result item was selected - that, - 'itemSelect', - function(e) { // fire any onItemSelect handlers in the opts - if (!isVisible(that)) return false; - - var selectedOffset = that.getSelectedOffset(); - if (selectedOffset == -1) return false; - - var e = new events.Event(); - e.source = $(that.overlay.container).get('.active'); - e.selectedItem = that._found[selectedOffset]; - if (that.opts.onItemSelect) { - that.opts.onItemSelect.apply(that, [e]); - - } - setCaretTo(that.inputElement[0], that.inputElement.val().length); - - valueChanged(that, /*without finding?*/true); - - that.hide(); - return true; - } - ); - - events.addListener( // the list of results was clicked (and thus a result item was selected) - that.overlay.container.get('ul')[0], - 'mousedown', - function(e) { - events.fire(that, 'itemSelect', e); - } - ); - - - events.addListener( // the window was resized while the results were showing? - window, - 'resize', - function(e){ - place(that); - } - ); - - // some code wot jake done wrote: - // prevent moz from hiding the input box when a link in the results is clicked - events.addListener(that.overlay.container, 'mousedown', function() { - return false; - }); - // same for IE... - events.addListener(that.overlay.container, 'beforedeactivate', function(event) { - if ($(event.nativeEvent.toElement).isWithin(that.overlay.container)) { - return false; - } - return true; - }); - - events.addListener( // the focus has moved away from the input element - that.inputElement, - 'blur', - function(e) { /*debug*///console.log("blur("+e+")"); - clearTimeout(that.findTimeout); - - // user accepts the hilited text - that._value = that.inputElement.val(); - valueChanged(that, true); - - that.hide(); - } - ); - - events.addListener( // the cursor is over a result item - that.overlay.container, - 'mouseover', - function(e) { /*debug*///console.log("mouseover("+e+")"); - var li = getParentListItem(that, e.source); - li && activateItem(that, li); - } - ); - - events.addListener( // the cursor has left a result item - that.overlay.container, - 'mouseout', - function(e) { /*debug*///console.log("mousmouseouteover("+e+")"); - var li = getParentListItem(that, e.source); - if (li && li != e.source) deactivateItem(that, li); - } - ); - - var ignoreInUp = false; // flag to tell the keyup handler if it should ignore already-handled key presses - var repeating = {ondown:0, onpress:0}; - - function keyDownHandler(e) { /*debug*///console.log("keydown "+e.key); - clearTimeout(that.findTimeout); // kill any pending finds whenever a new key is pressed - - ignoreInUp = false; - repeating.ondown++; - - if (e.key == 'DOWN') { - if (isVisible(that)) { - ignoreInUp = true; - nextItem(that); - return false; // on some systems this moves the carat around - } - - } - else if (e.key == 'UP') { - if (isVisible(that)) { - ignoreInUp = true; - prevItem(that); - return false; - } - } - else if (e.key == 'LEFT') { - if (isVisible(that)) { - // user accepts the hilited text - that._value = that.inputElement.val(); - valueChanged(that, true); - that.hide(); - } - } - else if (e.key == 'RIGHT') { - if (isVisible(that)) { - // user accepts the hilited text - that._value = that.inputElement.val(); - valueChanged(that, true); - that.hide(); - } - } - else if (e.key == 'ESC') { // bail - // user accepts the hilited text - that._value = that.inputElement.val(); - valueChanged(that, true); - that.hide(); - return false; - } - else if (e.key == 'DEL' || e.key == 'BACKSPACE') { - that.hide(); - } - else if (e.key == 'ENTER') { // a result item was picked? - if (isVisible(that)) ignoreInUp = true; - else return true; // no: the results aren't visible so just do the default thing - - var selectedOffset = that.getSelectedOffset(); - if (selectedOffset == -1) { // no: there isn't any result item selected - that.hide(); - return true; // do the default thing - } - - // yes: fire the itemSelect event - var e = new events.Event(); - e.source = $(that.overlay.container).get('.active'); - e.selectedItem = that._found[selectedOffset]; - events.fire(that, 'itemSelect', e); - return false; // return false to prevent form submitting? - } - - // if we're still here... - return true; - } - events.addListener(that.inputElement[0], 'keydown', keyDownHandler); - - function keyPressHandler(e) { /*debug*///console.log("keypress "+e.key); - repeating.onpress++; - - // For non-printable characters, like arrow keys... - // Some browsers (like Mac Safari 3.1.1) only ever fire the keydown event - // (even for auto-repeats) and never fire the keypress event. - // Some browsers fire the keydown event only once and then fire the - // keypress event repeatedly until the key is released. - // We need to deal with both possibilities but we must not - // handle the event twice. - - // We do nothing the very first time we get here, because the event must - // have already been handled in previous keydown phase. - // But if we've passed here more than once, oafter a single keydown, - // we must be repeating on keypress, so it's ok to handle it from now on. - if (repeating.ondown == 1 && repeating.onpress > 1) { - if (e.key == 'DOWN') { - if (isVisible(that)) { - nextItem(that); - } - return false; - } - else if (e.key == 'UP') { - if (isVisible(that)) { - prevItem(that); - } - return false; - } - } - - return true; - } - events.addListener(that.inputElement[0], 'keypress', keyPressHandler); - - - function keyUpHandler(e) { /*debug*///console.log("keyUpHandler(e)"); - repeating = {ondown:0, onpress:0}; // not repeating anymore - - if (ignoreInUp) return false; - - that._value = that.inputElement.val(); // stow the new value from the input element - - valueChanged(that); - - return true; - } - events.addListener(that.inputElement[0], 'keyup', keyUpHandler); - } - - /** - @private - @description What to do when the value in the input element changes. - */ - function valueChanged(that, withoutFinding) { /*debug*///console.log("valueChanged(that, "+withoutFinding+")"); - if (typeof that._oldValue == "undefined") that._oldValue = that.inputElement.val(); // initially - var currentValue = that.getValue(); // grab value, so we can send it with the event - - /*debug*///console.log("oldValue is '"+that._oldValue+"'; currentValue is '"+currentValue+"';"); - - var skipFind = false; - if (currentValue.toLowerCase() == that._oldValue.toLowerCase()) { - var skipFind = true; - } - - that._oldValue = currentValue; - - if (withoutFinding || skipFind) return; - - that.findTimeout = setTimeout( - function() { - var e = new glow.events.Event(); - e.value = currentValue; - glow.events.fire(that, 'inputChange', e); - - activateItemOffset(that, 0); - - if (!e.defaultPrevented()) { // user can cancel the find in their handler - if (typeof that.dataSource != 'object') that.loadData(); // urls and functions are always reloaded - that.find(); - } - }, - 500 - ); - } - -/* constructor ****************************************************************/ - - /** - @name glow.widgets.AutoSuggest - @constructor - @description Create an auto-suggest menu for an input element. - - An AutoSuggest widget adds the ability for a text input element to make - suggestions whilst the user types. This appears as a list of selectable - items below the input element which dynamically updates based on what - has been typed so far. - -
      Widgets must be called in a glow.onReady() call.
      - - @param {glow.dom.NodeList | String} inputElement A NodeList or css selector that points to a text input element. - @param {Object[] | String[] | String | Function} dataSource Either an array of objects, an array of strings (referred to as a 'dataSource'), a function that returns a dataSource or a URL that when requested will return a dataSource. - @param {Object} opts Optional configuration settings. - @param {String[] | Function} [opts.index=["name"]] When datasource is an array of objects, the property with this string is used as the index, or this function will be used to generate the index. - @param {Number} [opts.width=inputElement width] Width in pixels of the suggestion container. Default is the width of the inputElement. - @param {Number} [opts.height] Height in pixels of the suggestion container. - @param {Function} [opts.parseData] Transform the response from the server into a dataSource object. - The server may return XML or even plain text, but your parseData function should convert this - data into an array of objects. - - Your function will be passed a {@link glow.net.Response} object from the server. - - @param {Function} [opts.formatItem] Given the matched data item, return HTML or NodeList. - @param {Boolean} [opts.activeOnShow=true] Should the first suggestion automatically be active when the suggestion list appears? - @param {Number} [opts.maxListLength] Limit the size of the result list to this. - @param {Boolean} [opts.caseSensitive=false] Whether case is important when matching suggestions. - @param {Function} [opts.isMatch] Provide a custom function to filter the dataset for results - Your function will be passed an indexed term and the term entered by the user, return - true to confirm a match. - - The default function will check the indexed term begins with the term entered by the user. - @param {Boolean} [opts.complete=false] Append the completed text of the currently active suggestion to the input text. - @param {String} [opts.delim] When defined, the input text will be treated as multiple values, separated by this string (with surrounding spaces ignored). - @param {string} [opts.theme="light"] Either "light" or "dark". - @param {String|Function} [opts.anim] Passed into the Overlay constructor for show and hide transitions. - @param {Function} [opts.onInputChange] Your own handler for the inputChange event. - @param {Function} [opts.onItemSelect] Your own handler for the itemSelect event. - @param {Function} [opts.onDataLoad] Your own handler for the dataLoad event. - @param {Function} [opts.onDataError] Your own handler for the dataError event. - @param {Function} [opts.onDataAbort] Your own handler for the dataAbort event. - - @see AutoSuggest user guide - - @example - - new glow.widgets.AutoSuggest( - "#inputElementId", // HTML input element to bind the AutoSuggest to - ["Apple Flan", "Easy Shortbread", "Apple FlapJack", "Flambe of Brandied Apple Ice"] // Data source - ); - - @example - - myOpts = { - width: 100, - theme: "dark", - maxListLength: "10", - onItemSelect: function(e) { - this.val(e.selectedItem.name); // Updates the binded HTML input element with the selected value - } - } - - myData = [ - { - name: "Apple Flan" - }, - { - name: "Easy Shortbread" - }, - { - name: "Apple FlapJack" - }, - { - name: "Flambe of Brandied Apple Ice" - } - ]; - - myAutoSuggest = new glow.widgets.AutoSuggest( - "#inputElementId", // HTML input element to bind the AutoSuggest to - myData, - myOpts - ); - - @example - - new glow.widgets.AutoSuggest( - myInputElement, // HTML input element to bind the AutoSuggest to - "colornames.js", // URL to data - myOpts - ).loadData(); // load data from URL now - - */ - /** - @name glow.widgets.AutoSuggest#event:inputChange - @event - @description Fired whenever new suggestion appears based on changed input. - @param {glow.events.Event} event Event Object - */ - /** - @name glow.widgets.AutoSuggest#event:itemSelect - @event - @description Fired whenever a suggestion is selected. - - @param {glow.events.Event} event Event Object - @param {Object} event.selectedItem The object in the dataSource that is associated with the selected list item. - */ - /** - @name glow.widgets.AutoSuggest#event:dataLoad - @event - @description Fired whenever raw data is loaded from a request to a URL. - @param {glow.events.Event} event Event Object - */ - /** - @name glow.widgets.AutoSuggest#event:dataError - @event - @description Fired whenever there is an errored request to a URL. - @param {glow.events.Event} event Event Object - */ - /** - @name glow.widgets.AutoSuggest#event:dataAbort - @event - @description Fired whenever there is an aborted request to a URL. - @param {glow.events.Event} event Event Object - */ - glow.widgets.AutoSuggest = function(inputElement, dataSource, opts) { /*debug*///console.log('new glow.widgets.AutoSuggest('+inputElement+', '+dataSource+', '+opts+')'); - this.opts = opts || {}; - - bindTo(this, inputElement); - - this.overlay = new glow.widgets.Overlay( - glow.dom.create('
        '), - { - autoPosition: false, - anim: (this.opts.anim)? this.opts.anim : null - } - ); - - this.configure(this.opts); - buildIndexer(this); // build the function that will be used to create an index from the data - - this.dataSource = dataSource; - this.data = []; - if (typeof dataSource != 'string') this.loadData(); // urls are not loaded on construct, objects and functions are - - addEvents(this); - - if (this.opts.complete) { - - // Fix for trac 175 - "Autosuggest opens overlay onLoad where opts.complete == true" - if (this.inputElement.val() == '') { - this.setData(dataSource); - } - else { - this.setData(dataSource, function() {}); - } - - var that = this; - - events.addListener( - that, - 'itemActive', - function(e) { - var selectedOffset = that.getSelectedOffset(); - if (selectedOffset == -1) return false; - var matchedOn = (that._found[selectedOffset][this.opts.index]||that._found[selectedOffset]['name']||that._found[selectedOffset]); - if (typeof matchedOn.push != "undefined") matchedOn = that._matchedOn; - that.suggest(matchedOn); - - return true; - } - ); - } - } - -/* public fields *************************************************************/ - - /** - @name glow.widgets.AutoSuggest#inputElement - @type glow.dom.NodeList - @description Refers to the input element to which this is attached to. - @example - - var myAutoSuggest = new glow.widgets.AutoSuggest( - "input#preferedColour", - "colornames.js" - ); - alert(myAutoSuggest.inputElement); // returns a nodeList referencing input#preferedColour - - */ - - /** - @name glow.widgets.AutoSuggest#overlay - @type glow.widgets.Overlay - @description Refers to the overlay object that will contain the list of suggestions. - @example - - var myAutoSuggest = new glow.widgets.AutoSuggest( - "input#preferedColour", - "colornames.js" - ); - myAutoSuggest.overlay.show(); - - */ - -/* public methods *************************************************************/ - - /** - @private - @description Used internally to apply configuration options. - */ - glow.widgets.AutoSuggest.prototype.configure = function(opts) { - this.opts = opts || {}; - - if (this.opts.height) { - var listContainer = $(this.overlay.container.get('.glow151-autoSuggest').get('ul')[0]); - listContainer.css('overflow-x', 'hidden'); - listContainer.css('overflow-y', 'auto'); - listContainer.height(this.opts.height); - } - - if (this.opts.theme == 'dark') { - $(this.overlay.container.get('ul')[0]).removeClass('autosuggest-light'); - $(this.overlay.container.get('ul')[0]).addClass('autosuggest-dark'); - } - else { - $(this.overlay.container.get('ul')[0]).removeClass('autosuggest-dark'); - $(this.overlay.container.get('ul')[0]).addClass('autosuggest-light'); - } - - if (this.opts.onDataLoad) events.addListener(this, 'dataLoad', this.opts.onDataLoad); - if (this.opts.onDataError) events.addListener(this, 'dataError', this.opts.onDataError); - if (this.opts.onDataAbort) events.addListener(this, 'dataAbort', this.opts.onDataAbort); - if (this.opts.onInputChange) events.addListener(this, 'inputChange', this.opts.onInputChange); - - this._isMatch = this.opts.isMatch || function(word, lookFor) { return (word.indexOf(lookFor) == 0); } // default - this._formatItem = this.opts.formatItem || function(o) { return (o.name)? o.name : o.toString(); }; // default - this._matchItem = this.opts.formatItem || function(o) { return o.name; }; // default - } - - /** - @name glow.widgets.AutoSuggest#setData - @function - @description Update the data source - - If the dataSource is a URL it will be reloaded asynchronously. - - @param {Object[] | String | Function} dataSource New data source - @type {glow.widgets.AutoSuggest} - @returns The instance of the widget. - @example - myAutoSuggest = new glow.widgets.AutoSuggest( - myInputElement, - "colornames.js", // URL to data - myOpts - ) - myAutoSuggest.setData("newColornames.js"); // Set data to new URL - myAutoSuggest.loadData(); // load data from new URL now - */ - glow.widgets.AutoSuggest.prototype.setData = function(dataSource, callback) { /*debug*///console.log("setData("+((dataSource)?dataSource.toSource():"")+")"); - if (typeof dataSource == 'function') { - dataSource = dataSource.call(this); - } - - if (typeof dataSource == 'string') { // it's a URL but next time through we'll have an actual object, not a string - this.dataURL = dataSource; - this.data = []; // placeholder value until download completes - - // insert the current value of the input element to pass to the server - dataSource = dataSource.replace(/\{input\}/g, escape(this.getValue())); - downloadData(this, dataSource, callback); // calls setData - } - else { - this.data = dataSource; - - // process data to build a results_array and an index like {"keyword": results_array_offsets[]} - this.index = {}; - this.results = []; - - // this._indexer is a function to extract the keywords from each data item - for (var d = 0; d < this.data.length; d++) { - var datum = this.data[d]; - this.results.push(datum); - - // build index keywords - var keywords = this._indexer(datum); - keywords = (typeof keywords == 'string')? [keywords] : keywords; - - // associate data items with keyword - for (var i = 0; i < keywords.length; i++) { - var keyword = "="+(this.opts.caseSensitive? String(keywords[i]) : String(keywords[i]).toLowerCase()); - if (!this.index[keyword]) this.index[keyword] = []; - this.index[keyword].push(this.results.length-1); - } - } - - return this; // chained - } - } - - /** - @name glow.widgets.AutoSuggest#loadData - @function - @description Cause the dataSource passed to the constructor to be set as the current data. - @type {glow.widgets.AutoSuggest} - @returns The instance of the widget. - @example - new glow.widgets.AutoSuggest( - myInputElement, - "colornames.js", // URL to data - myOpts - ).loadData(); // load data from URL now - */ - glow.widgets.AutoSuggest.prototype.loadData = function(callback) { /*debug*///console.log("loadData()"); - this.setData(this.dataSource, callback); - return this; // chained - } - - /** - @private - @description Used to create a sting that combines a completetion with existing text. - */ - function appendTag(currentValue, delim, value) { /*debug*///console.log("called appendTag('"+currentValue+"', '"+delim+"', '"+value+"')"); - var split; - if (delim == '' || currentValue.indexOf(delim) < 0) { - split = new RegExp('^( *)(.*)$'); - } - else { - split = new RegExp('^(.*'+delim+' *)([^'+delim+']*)$'); - } - - var lv = split.exec(currentValue)[1]; - var rv = (split.exec(value)||["", "", value])[2]; - - return lv+rv; - } - - /** - @name glow.widgets.AutoSuggest#val - @function - @param {string} [value] If defined this value is set, otherwise the current value is returned. - @description Sets or gets the value of the input element (minus any unaccepted completions). - @type {String|glow.widgets.AutoSuggest} - @returns The value of the input element when getting, or the instance of the widget when setting. - @example - new glow.widgets.AutoSuggest( - "input#recipeName", // refers to an input element on the page - ["Apple Flan", "Easy Shortbread", "Apple FlapJack", "Flambe of Brandied Apple Ice"], // Data source - { - onItemSelect: function(e) { - this.val(e.selectedItem.name); // Set input#reciptName to the value of the selected item - } - } - ); - */ - glow.widgets.AutoSuggest.prototype.val = function(value) { /*debug*///console.log("called val("+value+")"); - if (typeof value == 'undefined') { //set - return this._value; - } - else { - this._value = value; - this.inputElement.val(value); - return this; // chained - } - } - - /** - @private - @name glow.widgets.AutoSuggest#setValue - @function - @description Sets the current value of the widget. In the case of - the delim option, set the last delimited item. - */ - glow.widgets.AutoSuggest.prototype.setValue = function(value) { /*debug*///console.log("called setValue("+value+")"); - var currentValue = this._value || this.inputElement.val(); - var delim = (this.opts.delim || ''); - value = appendTag(currentValue, delim, value); - - this._value = value; - this.inputElement.val(value); - } - - /** - @private - @name glow.widgets.AutoSuggest#getValue - @function - @description Returns the current value of the widget. In the case of - the delim option, gets the last delimited item. - */ - glow.widgets.AutoSuggest.prototype.getValue = function() { /*debug*///console.log("getValue()"); - var value = this._value || this.inputElement.val(); - - if (typeof this.opts.delim != 'undefined' && this.opts.delim != '') { - value = (value.match(new RegExp('(^|'+this.opts.delim+' *)([^'+this.opts.delim+']*)$')) || ['', '', '']); - value = value[2]; - } - - return value; - } - - /** - @private - @name glow.widgets.AutoSuggest#suggest - @param {string} suggested The text that is being suggested. - @description Display text with the suggested portion selected. - */ - glow.widgets.AutoSuggest.prototype.suggest = function(suggested) { /*debug*///console.log("suggest("+suggested+")"); - this._suggested = suggested; - var currentValue = this.inputElement.val(); - var delim = (this.opts.delim || ''); - var value = appendTag(currentValue, delim, suggested); - this.inputElement.val(value); - - selectRange(this.inputElement[0], {start: (this._value || '').length, end: this.inputElement.val().length}); //currentValue.length+suggested.length}) - } - - /** - @private - @description Make the text in the given range selected. - @param el - @param range - @param range.start - @param range.end - */ - function selectRange(el, range) { /*debug*///console.log("selectRange("+el+", "+range.toSource()+")"); - el.focus(); - - if (!window.opera && el.createTextRange) { // ie, but not opera (faker) - var r = el.createTextRange(); - r.moveEnd('character', range.end); - r.moveStart('character', range.start); - r.select(); - } - else { // moz, saf, opera - el.select(); - el.selectionStart = range.start; - el.selectionEnd = range.end; - } - } - - /** - @private - */ - function setCaretTo(el, pos) { /*debug*///console.log("setCaretTo("+el+", "+pos+")"); - selectRange(el, {start: pos, end: pos}) - } - - /** - @private - @name array_indexOf - @function - @description Find the position of a value in an array. Like Mozilla's - Array#indexOf(). - */ - function array_indexOf(value) { - var index = -1; - - for (var i = 0, l = this.length; i < l; i++) { - if (this[i] === value) { - index = i; - break; - } - } - - return index; - } - - /** - @private - @name glow.widgets.AutoSuggest#find - @function - @description Cause the data to be searched for items that match the value of the inputElement. - @param {String} lookFor For testing purposes you can pass in a lookFor, otherwise it will be drawn from the inputElement. - @returns undefined - */ - glow.widgets.AutoSuggest.prototype.find = function(lookFor) { /*debug*///console.log("find()") - if (typeof lookFor == "undefined") lookFor = this.getValue(); - - // ltrim - while (lookFor.charAt(0) == ' ') lookFor = lookFor.substring(1); - - if (!this.opts.caseSensitive) lookFor = lookFor.toLowerCase(); - - var found = []; - found.indexOf || (found.indexOf = array_indexOf); - - if (lookFor) { - for (var k in this.index) { - var lookAt = k.substring(1); - - if (this._isMatch(lookAt, lookFor)) { - var keys = this.index[k]; - - for (var j = 0; j < keys.length; j++) { - var offset = keys[j]; - - if (found.indexOf(this.results[offset]) == -1) { - found.push(this.results[offset]); - } - } - } - } - } - - this._found = found; // used to get the selected object in event handlers - if (found.length) { - if (this.opts.maxListLength) found.length = Math.min(found.length, this.opts.maxListLength); - - var list = []; - for (var i = 0; i < found.length; i++) { - list.push('
      • '+this._formatItem(found[i])+'
      • '); - } - $(this.overlay.container.get('ul')[0]).html(list.join('')); - - this.show(); - } - else { - this.hide(); - } - - if (this.opts.activeOnShow !== false) nextItem(this); - } - - /** - @private - @description Make the overlay not visible. - */ - glow.widgets.AutoSuggest.prototype.hide = function() { /*debug*///console.log("hide()") - this.overlay.hide(); - } - - /** - @private - @description Make the overlay visible. - */ - glow.widgets.AutoSuggest.prototype.show = function() { /*debug*///console.log("show()") - place(this); - this.overlay.show(); - } - - /** - @private - @description Get the offset of the currently selected item in the results. - */ - glow.widgets.AutoSuggest.prototype.getSelectedOffset = function() { - if (!isVisible(this)) return -1; - - var items = this.overlay.container.get('li'); // TODO: handle embedded list items - - for (var i = 0; i < items.length; i++) { - if ($(items[i]).hasClass('active')) return i; - } - - return -1; - } - } -}); -(window.gloader || glow).module({ - name: 'glow.widgets.AutoComplete', - library: ['glow', '1.5.1'], - depends: [[ - 'glow', '1.5.1', - 'glow.widgets.AutoSuggest' - ]], - - builder: function(glow) { -/* private fields *************************************************************/ - var $ = glow.dom.get, // shortcuts - events = glow.events, - anim = glow.anim; - -/* private functions **********************************************************/ -/* constructor ****************************************************************/ - /** - @constructor - @name glow.widgets.AutoComplete - @see AutoSuggest user guide - @deprecated Since version 1.2.2. You should now use glow.widgets.AutoSuggest with the 'complete' constructor option - @description Adds the ability to complete what has been typed to the AutoSuggest widget. - - This widget acts as a thin wrapper to the {@link glow.widgets.AutoSuggest} - widget and adds the ability to automatically append the missing text - to complete what has been typed by the user. Note that because the - completed item must be text in order to appear in the input element, - this widget can only process data items that are strings. - @param {NodeList | String} inputElement - A NodeList or css selector that points to a text input element. - @param {Object[] | String | Function} dataSource Either a dataSource - object, a function that returns a dataSource object or a URL that - returns a dataSource object. - @param {Object} opts All options are passed directly to the wrapped instance of - {@link glow.widgets.AutoSuggest}. - @param {string} opts.delim Character to delimit multiple entries. - If defined, the user can enter more than one term into the text input - element, delimited by this string (and any spaces surrounding the delimiter). - */ - glow.widgets.AutoComplete = function(inputElement, dataSource, opts) { /*debug*///console.log('new glow.widgets.AutoComplete('+inputElement+', '+dataSource+', '+opts+')'); - opts = opts || {}; - - /** - @object - @name glow.widgets.AutoComplete#autosuggest - @type glow.widgets.AutoSuggest - @description The wrapped instance of glow.widgets.AutoSuggest used to implement this widget. - */ - this.autosuggest = new glow.widgets.AutoSuggest(inputElement, [], opts); - this.autosuggest._indexer = function(dataItem) { return dataItem.toString(); } - this.autosuggest._formatItem = function(dataItem) { return dataItem.toString(); } - this.autosuggest.setData(dataSource); - - var that = this.autosuggest; - - events.addListener( - that, - 'itemActive', - function(e) { - var selectedOffset = that.getSelectedOffset(); - if (selectedOffset == -1) return false; - that.suggest(that._found[selectedOffset]); - - return true; - } - ); - } - -/* public fields **************************************************************/ -/* public methods *************************************************************/ - } -}); -(window.gloader || glow).module({ - name: "glow.widgets.Carousel", - library: ["glow", "1.5.1"], - depends: [[ - "glow", "1.5.1", - "glow.dom", - "glow.events", - "glow.anim", - "glow.widgets" - ]], - builder: function(glow) { - var $ = glow.dom.get, - events = glow.events, - dom = glow.dom; - - /** - @name glow.widgets.Carousel - @class - @description Scroll through a list of items - -
        Widgets must be called in a glow.onReady() call.
        - - @param {glow.dom.NodeList} container The container of items to display as a carousel. - @param {Object} opts Options object - @param {Boolean} [opts.loop=false] True if the carousel should loop when it gets to the end. - When using the loop option, it is good practice to indicate to the user how far they have scrolled through the carousel. We recommend the use of the pageNav parameter when allowing the user the loop to the begining of the carousel. - @param {Number} [opts.animDuration=0.2] Duration of animation in seconds. - @param {Function} [opts.animTween=glow.tweens.easeBoth()] A Glow tween function to animate scrolling. - @param {Number|String} [opts.step=1] Number of items to scroll by. When the string 'page' is passed in the carousel will be set to scroll by the number of viewable items. - @param {Boolean} [opts.vertical=false] Used to create a vertical oriented carousel - @param {Number} [opts.size] Number of items the carousel displays at any one time - By default, the carousel will fill all available horizontal (or vertical) space. - @param {Boolean} [opts.scrollOnHold=true] Continue to scroll while button is held down. - @param {Boolean} [opts.slideOnScroll=false] Use sliding animation when scrolling continuously. - @param {String} [opts.theme="light"] Visual Theme - Only applies when using the default template. Currently supported themes are "dark" and "light". - @param {Boolean} [opts.pageNav=false] Display navigational control next to the carousel. - @param {String} [opts.id] An ID to apply to the container element. - @param {String} [opts.className] List of additional space separated class names to apply to the container. - Space separated values. - - @example - var myCarousel = new glow.widgets.Carousel("#carouselContainer", { - loop: true, - size: 4, - step: 4 - }); - - @see Carousel user guide - */ - /** - @name glow.widgets.Carousel#event:addItem - @event - @description One or more items about to be added to the carousel. - - Canceling this event stops the items being added. - - @param {glow.events.Event} event Event Object - @param {glow.dom.NodeList} event.items NodeList of items being added - */ - /** - @name glow.widgets.Carousel#event:removeItem - @event - @description Item about to be removed. - - Canceling this event results in the item not being removed. - - @param {glow.events.Event} event Event Object - @param {glow.dom.NodeList} event.item Represents the item to be removed - @param {Number} event.itemIndex Index of the item to be removed - */ - /** - @name glow.widgets.Carousel#event:scroll - @event - @description Fired before scrolling. - - @param {glow.events.Event} event Event Object - @param {Number} event.currentPosition Carousel's current position - */ - /** - @name glow.widgets.Carousel#event:afterScroll - @event - @description Fired after scrolling animation is complete. - - @param {glow.events.Event} event Event Object - @param {Number} event.position The carousel's new position - */ - /** - @name glow.widgets.Carousel#event:itemClick - @event - @description Fired when an item within the carousel is clicked. - - The event contains properties 'item' an html element - representing the clicked item, and 'itemIndex', the index of the item - clicked. - - @param {glow.events.Event} event Event Object - @param {glow.dom.NodeList} event.item Represents the item clicked - @param {Number} event.itemIndex Index of the item clicked - - */ - - /* Public Properties */ - - /** - @name glow.widgets.Carousel#element - @type glow.dom.NodeList - @description Carousel HTML Element. - @example - var myCarousel = new glow.widgets.Carousel("#carouselContainer"); - glow.events.addListener(myCarousel, "itemClick", function(e) { - alert(this.element) // returns the HTML element "#carouselContainer" - }); - */ - - /** - @name glow.widgets.Carousel#items - @type glow.dom.NodeList - @description NodeList of the items within the carousel. - @example - var myCarousel = new glow.widgets.Carousel("ul#carouselContainer"); - glow.events.addListener(myCarousel, "itemClick", function(e) { - alert(this.items) // returns an array of <li /> elements in "ul#carouselContainer" - }); - */ - function Carousel(container, opts) { - - opts = opts || {}; // prevent errors when trying to read properties of undefined opts - - // create carousel content - this._content = $(container); - - this._startContentHeight = this._content[0].offsetHeight; - - this._content.addClass("carousel-content") // add a css selector hook - .css("zoom", "1"); // hack: adds "haslayout" on ie - - this.items = this._content.children(); - - // set option defaults - opts = this._opts = glow.lang.apply( - { - animDuration: 0.4, - animTween: glow.tweens.easeBoth(), - loop: false, - step: 1, - vertical: false, - scrollOnHold: true, - slideOnScroll: false, - theme: "light", - pageNav: false - }, - opts - ); - - // cast the integers - opts.animDuration = Number(opts.animDuration); -// opts.step = Number(opts.step); - opts.size = Number(opts.size); - - - // build HTML - this.element = - dom.create("
        " - ); - var themeWrapper = dom.create("
        <\/div>"); - this._viewWindow = dom.create("
        <\/div>"); - - // move the carousel items into the carousel view window, themewrapper, and carousel content - this._content.before(this.element); - themeWrapper.prependTo(this.element); - this._viewWindow.prependTo(themeWrapper); - this._content.prependTo(this._viewWindow); - - // add selector hooks - if (this._opts.vertical) { - this.element.addClass("glow151-vCarousel"); - } - else { - this.element.addClass("glow151-carousel"); - } - - // create the navigational buttons - if (!this._opts.pageNav) { - this._navPrev = dom.create("Previous").insertBefore(this._viewWindow); - this._navNext = dom.create("Next").insertAfter(this._viewWindow); - } - - init.apply(this, [container, opts]); - } - - /** - Runs only once, during construction phase. - @private - */ - function init(container, opts) { - var that = this; // used in callbacks to refer to myself - - // calculate the view size if it isn't given, and size the view window - // we absolutely position the item for a moment so it shrinks to fit - var oldPositionVal = this.items[0].style.position; - this.items[0].style.position = "absolute"; - this._itemWidth = parseInt(this.items[0].offsetWidth) + parseInt($(this.items[0]).css(["margin-left", "margin-right"])); - this._itemHeight = parseInt(this.items[0].offsetHeight) + parseInt($(this.items[0]).css(["margin-top", "margin-bottom"])); - this.items[0].style.position = oldPositionVal; - // Is there a fraction of an item hanging off the end of the carousel? - // This can only happen if opts.size is false - this._itemHangingOffEnd = false; - if (!opts.size) { // you really should just give me the size, don't you think? yes, I know it's optional but is that really too much to ask for considering everything? really? - var itemsInView; - - if (opts.vertical) { - - this._sizeView = this._startContentHeight; - if (!this._opts.pageNav) this._sizeView -= this._navPrev[0].offsetHeight + this._navNext[0].offsetHeight; - - this._viewWindow.css("width", this._itemWidth + "px"); - this._viewWindow.css("height", this._sizeView + "px"); - - itemsInView = this._sizeView / this._itemHeight; - this._opts.size = Math.floor(itemsInView); - // do we have an item hanging off the end? - this._itemHangingOffEnd = (itemsInView != this._opts.size); - - // now fix the size to prevent wrapping if the browser is resized - this.element.css("height", this._sizeView + (this._opts.pageNav? 0 : this._navPrev[0].offsetHeight + this._navNext[0].offsetHeight) + "px"); - } - else { - this._sizeView = this.element[0].offsetWidth; - if (!this._opts.pageNav) this._sizeView -= this._navPrev[0].offsetWidth + this._navNext[0].offsetWidth; - - this._viewWindow.css("width", this._sizeView + "px"); - this._viewWindow.css("height", this._itemHeight + "px"); - - itemsInView = this._sizeView / this._itemWidth; - this._opts.size = Math.floor(itemsInView); - // do we have an item hanging off the end? - this._itemHangingOffEnd = (itemsInView != this._opts.size); - - - this.element.css("width", this._sizeView + (this._opts.pageNav? 0 : this._navPrev[0].offsetWidth + this._navNext[0].offsetWidth) + "px"); - } - } - else { - if (this._opts.vertical) { - this._viewWindow.css("width", this._itemWidth + "px"); - this._viewWindow.css("height", this._opts.size * this._itemHeight + "px"); - } - else { - this._viewWindow.css("width", this._opts.size * this._itemWidth + "px"); - this._viewWindow.css("height", this._itemHeight + "px"); - } - } - - // Change for trac 151 - // If step set to page then set the carousel to step through the items by the number of viewable items - if (this._opts.step == "page") - { - this._opts.step = this._opts.size; - } - - // check that options are sane - if (this._opts.size < this._opts.step) { - throw new Error("Carousel opts.step ("+this._opts.step+") cannot be larger than carousel size ("+this._opts.size+")."); - } - - // install listeners for optinal event handlers - var eventNames = ["addItem","removeItem","scroll","afterScroll","itemClick"], - i = eventNames.length, - onEventName; - while(i--) { - // convert names from event to onEvent - onEventName = "on" + eventNames[i].charAt(0).toUpperCase() + eventNames[i].slice(1); - if (opts[onEventName]) { - events.addListener(that,eventNames[i],opts[onEventName]); - } - } - - // have the nav items already got a width / height set on the style attribute? - this._customButtonDimentions = (this._navPrev && this._navNext) && ( - this._navPrev[0].style.width - || this._navPrev[0].style.height - || this._navNext[0].style.width - || this._navNext[0].style.height - ); - - this._originalOptsLoop = this._opts.loop; // Added for bug fix trac 152 *** - - rebuild.apply(this); - this._ready = true; - } - - /** - Runs during construction phase and whenever items are added or removed. - @private - */ - function rebuild() { /*debug*///console.log("Carousel-rebuild()"); - var that = this; // used in callbacks to refer to myself - - this.items = this._content.children(); - var padCount; - - // Bug fix for trac 152 *** - // Will the content fill the view? - this._notEnoughContent = this.items.length <= this._opts.size; - if (this._notEnoughContent) - { - // If not then stop any looping and add CSS hook - this._opts.loop = false; - this.element.get(".carousel-window").addClass("carousel-notEnoughItems"); - } else { - // - this._opts.loop = this._originalOptsLoop; - this.element.get(".carousel-window").removeClass("carousel-notEnoughItems"); - if (this._navPrev) - { - this._navPrev.removeClass("carousel-prev-disabled"); - this._navNext.removeClass("carousel-next-disabled"); - } - } - - - // the number of pads needed differs if we're looping or not - if (this._opts.loop) { - // pad items to an even step, prevents carousel getting out of step - // if the modulus is zero, we deduct the step to make the padCount zero - padCount = this._opts.step - ((this.items.length % this._opts.step) || this._opts.step); - } else { - // first, how many 'next's does it take to see all the items - var stepsTillEndOfContent = Math.ceil((this.items.length - this._opts.size) / this._opts.step); - padCount = (this._opts.size + (stepsTillEndOfContent * this._opts.step)) - this.items.length; - // we need an extra pad if there's an item hanging off the end - padCount += Number(this._itemHangingOffEnd); - } - // use the first item as a model for our pads - var pad = $(this.items[0]).clone().attr('role', 'presentation'); // hide the padding item from screenreaders - pad.removeAttr("id"); - pad.addClass("carousel-added"); - pad.addClass("carousel-pad"); - /*debug*///pad.html("PAD"); - pad.children().css("visibility", "hidden"); // keep the same dimensions as the model, but don't display anything - - for (var i = 0; i < padCount; i++) { - this._content.append(pad.clone()); - } - this.items = this._content.children(); - var realCount = this.items.length; - // grow items by adding clones, allows for wrapping to work - if (this._opts.loop) { - // We need an extra clone if there's an item hanging off the end - var clonesToAdd = this._opts.size + Number(this._itemHangingOffEnd); - var clone = this.items.slice(0, clonesToAdd).clone(true).attr('role', 'presentation'); - /*debug*///clone.attr("style", "float:left;filter:alpha(opacity=40);opacity:.4;"); - clone.addClass("carousel-added"); - this._content.append(clone); - this.items = this._content.children(); - } - - // add css selector hooks - this.items.addClass("carousel-item"); // add css selector hook - this.items.each(function(i){ this["_index"+glow.UID] = i; }); // items know their index - - // some private variables - this._direction = (this._opts.vertical)? "top" : "left"; - this._countRealItems = realCount - padCount; - this._countReal = realCount; // includes pads, but not clones - this._countAll = this.items.length; - this._countStep = this._opts.step; - this._countView = this._opts.size; - this._sizeEach = (this._opts.vertical? this._itemHeight : this._itemWidth); - this._sizeStep = this._sizeEach * this._opts.step; - this._sizeView = this._sizeEach * this._opts.size; - this._sizeReal = this._sizeEach * this._countReal; - this._sizeAll = this._sizeEach * this._countAll; - this._animationTime = this._opts.animDuration; - // sliding animations take less time - this._slideAnimationTime = this._animationTime / 2; - this._animationTween = this._opts.animTween; - - // size the content - (this._opts.vertical)? this._content.css("height", this._sizeAll+"px") : this._content.css("width", this._sizeAll+"px"); - - // position navigation buttons - if (!this._opts.pageNav && !this._customButtonDimentions) { - if (this._opts.vertical) { - this._navPrev.width(parseInt(this.items[0].offsetWidth)+ parseInt($(this.items[0]).css(["margin-left", "margin-right"]))); - this._navNext.width(parseInt(this.items[0].offsetWidth)+ parseInt($(this.items[0]).css(["margin-left", "margin-right"]))); - } - else { - this._navPrev.height(parseInt(this.items[0].offsetHeight)+ parseInt($(this.items[0]).css(["margin-top", "margin-bottom"]))); - this._navNext.height(parseInt(this.items[0].offsetHeight)+ parseInt($(this.items[0]).css(["margin-top", "margin-bottom"]))); - } - } - - //// build sliding timelines - var channelPrev = []; - var channelNext = []; - var slideMove, slideAnim; - - function animComplete() { - afterScroll.apply(that); - } - - // from the start, how many moves can the carousel make before looping to start or running out of items - if (this._opts.loop) { - this._movesMax = (this._countReal / this._countStep) - 1; - } - else { - // we use _itemHangingOffEnd to ignore a padded item at the end which is only half in view - this._movesMax = Math.ceil( ( this._countReal - this._countView - Number(this._itemHangingOffEnd) ) / this._countStep ); - } - - // we animate for one more step than _movesMax if we're looping, because we need to loop back to the first set of items (ie, the clones) - var len = this._movesMax + Number(this._opts.loop); - for (var i = 0; i < len; i++) { - slideMove = {}; - slideMove["margin-" + this._direction] = { - from: (-i * this._sizeStep)+"px", - to: (-(i+1) * this._sizeStep)+"px" - }; - - slideAnim = glow.anim.css(this._content, this._slideAnimationTime, slideMove, { "tween": glow.tweens.linear() }) - events.addListener(slideAnim, "complete", animComplete); - - channelNext.push(slideAnim); - - slideMove = {}; - slideMove["margin-" + this._direction] = { - from: (-(i+1) * this._sizeStep)+"px", - to: (-i * this._sizeStep)+"px" - }; - - slideAnim = glow.anim.css(this._content, this._slideAnimationTime, slideMove, { "tween": glow.tweens.linear() }) - events.addListener(slideAnim, "complete", animComplete); - - channelPrev.unshift(slideAnim); // note diff in Next, Prev: push versus unshift - } - - this._slidePrev = new glow.anim.Timeline(channelPrev, {loop: this._opts.loop}); - this._slideNext = new glow.anim.Timeline(channelNext, {loop: this._opts.loop}); - //// - - // initialise the "dots", if they are needed - if (this._opts.pageNav) { - this._pageNav = new PageNav( - this._movesMax + 1, - function(newPage) { - that.moveTo(newPage * that._countStep); - } - ); - - // replace the default nav buttons with some from the pageNav - this._navPrev = this._pageNav.leftarrow; - this._navNext = this._pageNav.rightarrow; - - var carouselWindow = this.element.get(".carousel-window"); - // remove any existing pageNav - carouselWindow.parent().get(".pageNav").remove() - this._pageNav.element.insertAfter(carouselWindow); - carouselWindow.addClass("paged"); - - // position pageNav so it is centered with carousel window - if (this._opts.vertical) { - var topmargin = Math.floor(((carouselWindow[0].offsetHeight) - this._pageNav.element[0].offsetHeight) / 2); - this._pageNav.element.css("margin-top", topmargin+"px"); - } - else { - // assumes width of pageNav list == sum of width of all list items - var leftmargin = Math.floor(((carouselWindow[0].offsetWidth) - this._pageNav.leftarrow[0].offsetWidth*(3+this._movesMax)) / 2); - this._pageNav.element.css("margin-left", leftmargin+"px"); - } - this._pageNav.update( (this._visibleIndexFirst() % this._countReal) / this._countStep ); - } - - // set initial disabled-states of the navigation buttons - if (this._notEnoughContent) { - // Added for bug fix trac 152 *** - // If there isn't enough content to require scrolling then disable both buttons - if (this._navPrev) { - this._navPrev.addClass("carousel-prev-disabled"); - this._navNext.addClass("carousel-next-disabled"); - } - } else if (!this._opts.loop) { - if (!canGo.apply(this, ["prev"])) this._navPrev.addClass("carousel-prev-disabled"); - else if (!canGo.apply(this, [])) this._navNext.addClass("carousel-next-disabled"); - } - - // add navigational events - events.addListener(this._navPrev, "click", function(e){ return false; }); - events.addListener(this._navPrev, "mousedown", function(e){ that.prev(); startRepeatOrSlide.call(that, true); return false; }); - events.addListener(this._navPrev, "mouseup", function(e){ stopRepeatOrSlide.call(that); return false; }); - events.addListener(this._navPrev, "mouseout", function(e){ - // in some browsers (firefox 2), relatedTarget can be null if pointer leaves viewport quick enough - if (e.relatedTarget && (e.relatedTarget == that._navPrev[0] || $(e.relatedTarget).isWithin(that._navPrev))) { - return; - } - stopRepeatOrSlide.call(that); - }); - - events.addListener(this._navNext, "click", function(e){ return false; }); - events.addListener(this._navNext, "mousedown", function(e){ that.next(); startRepeatOrSlide.call(that); return false; }); - events.addListener(this._navNext, "mouseup", function(e){ stopRepeatOrSlide.call(that); return false; }); - events.addListener(this._navNext, "mouseout", function(e){ - // in some browsers (firefox 2), relatedTarget can be null if pointer leaves viewport quick enough - if (e.relatedTarget && (e.relatedTarget == that._navNext[0] || $(e.relatedTarget).isWithin(that._navNext))) { - return; - } - stopRepeatOrSlide.call(that); }); - - // keyboard nav - var currentKeyDown; - events.addListener(this.element, "keydown", function(e) { - if (currentKeyDown) { - return false; - } - switch (e.key) { - case "UP": - case "LEFT": - currentKeyDown = e.key - if ( !that._isPlaying() ) { - that.prev(); - startRepeatOrSlide.call(that, true); - } - return false; - case "DOWN": - case "RIGHT": - currentKeyDown = e.key - if ( !that._isPlaying() ) { - that.next(); - startRepeatOrSlide.call(that); - } - return false; - case "ENTER": - currentKeyDown = e.key - if ( e.source == that._navNext[0] || (that._pageNav && e.source.parentNode == that._pageNav.rightarrow[0]) ) { - that.next(); - startRepeatOrSlide.call(that); - return false; - } else if (e.source == that._navPrev[0] || (that._pageNav && e.source.parentNode == that._pageNav.leftarrow[0]) ) { - that.prev(); - startRepeatOrSlide.call(that, true); - return false; - } - } - - }); - events.addListener(this.element, "keyup", function(e) { - switch (e.key) { - case "UP": - case "LEFT": - case "DOWN": - case "RIGHT": - case "ENTER": - currentKeyDown = null; - stopRepeatOrSlide.call(that); - } - }); - // prevent scrolling in opera - events.addListener(this.element, "keypress", function(e) { - switch (e.key) { - case "UP": - case "LEFT": - case "DOWN": - case "RIGHT": - return false; - case "ENTER": - if ( - e.source == that._navNext[0] || (that._pageNav && e.source.parentNode == that._pageNav.rightarrow[0]) || - e.source == that._navPrev[0] || (that._pageNav && e.source.parentNode == that._pageNav.leftarrow[0]) - ) { - return false; - } - } - }); - - // set up item events - glow.events.addListener(this._content, "click", function(e) { /*debug*///console.log("item clicked "+e.source); - for (var el = $(e.source); el[0] != that.element[0]; el = el.parent()) { // climb up the dom tree - if (el.hasClass("carousel-item")) { - if (!el.hasClass("carousel-pad")) { - glow.events.fire(that, "itemClick", { - item: el[0], - itemIndex: el[0]["_index"+glow.UID] % that._countReal - }); - } - break; - } - } - }); - } - - /** - Move the carousel by one step. - @private - @param {Boolean} prev True if moving prevward, otherwise moving nextward. - */ - function step(prev) { /*debug*///console.log("step("+prev+")"); - if ( this._isPlaying() || !canGo.call(this, prev) ) return; - var curMargin = parseInt(this._content.css("margin-" + this._direction)) % this._sizeReal; - - if (prev && curMargin == 0) curMargin -= this._sizeReal; - var newMargin = curMargin - ((prev? -1 : +1 ) * this._sizeStep); - - var move = {}; move["margin-" + this._direction] = { from: curMargin + "px", to: newMargin + "px" }; - this._step = glow.anim.css(this._content, this._animationTime, move, { "tween": this._animationTween }); - this._step.start(); - var that = this; - glow.events.addListener(this._step, "complete", function() { afterScroll.apply(that); }); - } - - /** - Start the carousel repeating / sliding - @private - @param {Boolean} prev True if moving prevward, otherwise moving nextward. - */ - function startRepeatOrSlide(prev) { - // if either of the timelines are currently playing, exit - if ( this._slidePrev.isPlaying() || this._slideNext.isPlaying() ) { - return; - } - - var that = this; - this._repeat = true; - - // either repeat stepping, or start sliding... - function beginRepeatOrSlide() { - if (that._opts.slideOnScroll) { - if (canGo.apply(that, [prev])) { - var timeOffset = getTimeoffset.apply(that); - // transpose timeoffset for previous direction - if (prev) timeOffset = that._slidePrev.duration - timeOffset; - // pause a little then start sliding - var timelineToUse = prev ? that._slidePrev : that._slideNext; - setTimeout( function() { - if ( that._isPlaying() || !that._repeat ) return; - timelineToUse.goTo(timeOffset).resume(); - }, 300); - } - } - else { - if ( !that._repeat ) return; - step.call(that, prev); - if (that._step) { - glow.events.addListener(that._step, "complete", beginRepeatOrSlide); - } - } - } - - if (this._opts.scrollOnHold) { - // if there's currently a step in action (there usually is) we need to wait for it - if ( this._step && this._step.isPlaying() ) { - // we set a property on the step to ensure we only set the listener once - if (!this._step._hasSlidingListener) { - glow.events.addListener(this._step, "complete", beginRepeatOrSlide); - this._step._hasSlidingListener = true; - } - } else { - beginRepeatOrSlide(); - } - } - } - - /** - Stop the carousel repeating / sliding at the next appropiate moment - @private - */ - function stopRepeatOrSlide() { - this._repeat = false; - } - - /** - Is it possible to go one step in the given direction or not? - @private - @param {Boolean} prev True if moving prevward, otherwise moving nextward. - @returns {Boolean} - */ - function canGo(prev) { /*debug*///console.log("canGo("+prev+")"); - if (this._opts.loop) return true; - - // prevent wrapping on non-looping carousels - var firstIndex = this._visibleIndexFirst(); - if (prev) { - return firstIndex != 0; - } - // ok, we're seeing if we can travel forward... - // if there's an item hanging off the end we need to pretend it doesn't exist (it'll always be a padded item, so it's ok) - return (firstIndex + this._countView) < (this._countAll - Number(this._itemHangingOffEnd)); - } - - /** - Runs before the carousel moves. - @private - */ - function beforeScroll() { /*debug*///console.log("Carousel-beforeScroll()"); - this._navPrev.removeClass("carousel-prev-disabled"); - this._navNext.removeClass("carousel-next-disabled"); - - events.fire(this, "scroll", { - currentPosition: this._visibleIndexFirst() % this._countReal - }); - } - - /** - Runs after the carousel moves. - @private - */ - function afterScroll() { /*debug*///console.log("Carousel-afterScroll()"); - - if ( !this._repeat || !this._opts.scrollOnHold ) { - endScroll.apply(this); - } - - var curItem = this._visibleIndexFirst(); - - events.fire(this, "afterScroll", { - position: curItem % this._countReal - }); - - if (this._pageNav) { - this._pageNav.update((curItem % this._countReal) / this._countStep); - } - - if (!this._opts.loop) { - if (!canGo.apply(this, ["prev"])) this._navPrev.addClass("carousel-prev-disabled"); - else if (!canGo.apply(this, [])) this._navNext.addClass("carousel-next-disabled"); - } - } - - /** - Stop any running slides. - @private - */ - function endScroll() { /*debug*///console.log("Carousel-endScroll()"); - this._slideNext.stop(); - this._slidePrev.stop(); - } - - /** - @name glow.widgets.Carousel#prev - @function - @description Scroll backwards by the number of items defined by step in the constructor. - */ - Carousel.prototype.prev = function() { /*debug*///console.log("Carousel#prev()"); - if (!this._isPlaying()) { - if (!canGo.apply(this, ["prev"])) return this; - beforeScroll.apply(this, ["prev"]); - step.apply(this, ["prev"]); - } - return this; - } - - /** - @name glow.widgets.Carousel#next - @function - @description Scroll forward by the number of items definded by step in the constructor. - */ - Carousel.prototype.next = function() { /*debug*///console.log("Carousel#next()"); - if (!this._isPlaying()) { - if (!canGo.apply(this, [])) return this; - beforeScroll.apply(this, []); - step.apply(this, []); - } - return this; - } - - /** - Calculate what the time offset of a slide would be. - - This is necessary because Timelines are indexed by time. - @private - @returns {Number} - */ - function getTimeoffset() { /*debug*///console.log("getTimeoffset()"); - var margin = parseInt(this._content.css("margin-" + this._direction)); - var stepOffset = Math.abs(margin)/this._sizeStep; - var timeOffset = stepOffset * this._slideAnimationTime; - return timeOffset; - } - - /** - Is a step or a slide currently in progress? - @private - @returns {Boolean} - */ - Carousel.prototype._isPlaying = function() { /*debug*///console.log("Carousel#_isPlaying()"); - return ( - (this._step && this._step.isPlaying()) - || - this._slidePrev.isPlaying() || this._slideNext.isPlaying() - ); - } - - /** - Get the 0-based offset of the first currently visible carousel item. - @private - @returns {Number} - */ - Carousel.prototype._visibleIndexFirst = function() { /*debug*///console.log("_visibleIndexFirst()"); - var offset = Math.floor(Math.abs(parseInt(this._content.css("margin-" + this._direction))/this._sizeEach)); - return this.items[offset]["_index"+glow.UID]; - } - - /** - @name glow.widgets.Carousel#visibleIndexes - @function - @description Returns an array of numeric indexes of the currently visable items in the carousel. - @returns {Array} - Array of indexes of the currently visible items. - @example - var myCarousel = new glow.widgets.Carousel("ul#carouselContainer"{ - size: 3 - }); - myCarousel.moveTo(4); - alert(myCarousel.visibleIndexes()); // returns [4, 5, 6] - */ - Carousel.prototype.visibleIndexes = function() { - var leftmost = this._visibleIndexFirst(); - var visibleIndexes = []; - for (var i = 0, l = this._opts.size; (i < l) && (leftmost+i < this._countRealItems); i++) { - visibleIndexes.push((leftmost+i) % this._countReal); - } - return visibleIndexes; - } - - /** - @name glow.widgets.Carousel#visibleItems - @function - @description Returns a NodeList of all items currently visible in the carousel. - @returns {glow.dom.NodeList} - @example - var myCarousel = new glow.widgets.Carousel("ul#carouselContainer"{ - size: 3 - }); - myCarousel.moveTo(4); - alert(myCarousel.visibleItems()); // returns nodeList with 3 items starting from the carousel's 4th item - */ - Carousel.prototype.visibleItems = function() { - var indexes = this.visibleIndexes(); - var visibleItems = new glow.dom.NodeList(); - for (var i = 0; i < indexes.length; i++) { - visibleItems.push(this.items[indexes[i]]); - } - return visibleItems; - } - - /** - @name glow.widgets.Carousel#addItems - @function - @description Used to add one or more new items to the carousel. - @param {glow.dom.NodeList | Element | Selector} itemsToAdd A NodeList of items to add to the carousel. - @param {Number} [position] Index at which to insert the items. By default, items will be added to the end of the carousel. - @example - var myCarousel = new glow.widgets.Carousel("ul#carouselContainer"); // This <ul> has 3 <li> children - alert(myCarousel.items); // returns 3 items - myCarousel.addItems("ul#anotherList li"); // ul#anotherList has 2 <li> children - alert(myCarousel.items); // returns 5 items - */ - Carousel.prototype.addItems = function(itemsToAdd, position) { /*debug*///console.log("Carousel#addItem("+itemsToAdd+", "+position+")"); - itemsToAdd = $(itemsToAdd); - - // fire event and cancel if prevented - var eventProps = { - items: itemsToAdd - } - if ( events.fire(this, "addItem", eventProps).defaultPrevented() ) { - return itemsToAdd; - } - - this._content.get(".carousel-added").remove(); // trim away added pads and clones - if (typeof position != "undefined" && position < this._countReal) { - itemsToAdd.insertBefore(this._content.children().item(position)); - } - else { - this._content.append(itemsToAdd); - } - rebuild.apply(this); - - return itemsToAdd; - } - - /** - @name glow.widgets.Carousel#removeItem - @function - @description Remove an item from the carousel. - @param {Number} indexToRemove A numeric index of the item to remove. - @returns {glow.dom.NodeList} - @example - var myCarousel = new glow.widgets.Carousel("ul#carouselContainer"); - alert(myCarousel.items); //returns array with a length of 5 - myCarousel.removeItem(4); - alert(myCarousel.items); //returns array with a length of 4 - */ - Carousel.prototype.removeItem = function(indexToRemove) { /*debug*///console.log("Carousel#removeItem("+indexToRemove+")"); - var removingItem = this.items.slice(indexToRemove, indexToRemove + 1), - e = { - item: removingItem, - itemIndex: indexToRemove - }; - - if ( events.fire(this, "removeItem", e).defaultPrevented() ) { - return removingItem; - } - - this._content.get(".carousel-added").remove(); // trim away added pads and clones - removingItem.remove(); - rebuild.apply(this); - - return removingItem; - } - - /** - @name glow.widgets.Carousel#moveBy - @function - @description Scrolls the carousel backwards or forwards through the items. - Note: You cannot send a carousel out of sync with its step. It will - scroll to a position where the item you've asked to move to is - visible. - @param {Number} distance The number of items to move by. Positive numbers move forward, negative move backwards. - @param {Boolean} animate Set to false to disable animation. - @example - var myCarousel = new glow.widgets.Carousel("ul#carouselContainer"); - myCarousel.moveNext(); // Move forward to the 2nd item. - myCarousel.moveBy(3,true); // Move forward 3 from the current item to the 5th item - */ - Carousel.prototype.moveBy = function(distance, animate) { /*debug*///console.log("moveBy("+distance+", "+animate+")"); - var currentItem = this._visibleIndexFirst(); - var targetItem = currentItem + distance; - if (this._opts.loop) { // the rolling jake... - if (targetItem < 0) { - this._content.css("margin-"+this._direction, (this._countReal * -this._sizeEach) + "px"); - targetItem = this._countReal + targetItem; - } - if (currentItem >= this._countReal && targetItem > this._countReal) { - this._content.css("margin-"+this._direction, "0px"); - targetItem = targetItem % this._countReal; - } - } - return this.moveTo(targetItem, animate); - } - - /** - @name glow.widgets.Carousel#moveTo - @function - @description Scroll to a specified position in the carousel. - Note: You cannot send a carousel out of sync with its step. It will - scroll to a position where the item you've asked to move to is - visible. - @param {Number} targetItem The index of the item to appear in the leftmost visible position of the carousel. - @param {Boolean} animate Set to false to disable animation. - @example - var myCarousel = new glow.widgets.Carousel("ul#carouselContainer"); - myCarousel.moveTo(3,true); // Move to the 3rd item. - */ - Carousel.prototype.moveTo = function(targetItem, animate) { /*debug*///console.log("moveTo("+targetItem+", "+animate+")"); - var that = this; - if (this._isPlaying()) return this; - - if (!this._opts.loop) targetItem = Math.min(targetItem, this._countReal-1); - targetItem = Math.max(targetItem, 0); - targetItem -= (targetItem % this._countStep); // stay in step - if (!this._opts.loop) { // keep right items close to right-edge in the case of non-looping carousels - targetItem = Math.min(targetItem, this._movesMax * this._countStep); - } - - var currentItem = this._visibleIndexFirst(); - - if (currentItem == targetItem) return this; - - beforeScroll.apply(this, []); - if (animate !== false) { - var move = {}; - move["margin-" + this._direction] = { from: (currentItem * -this._sizeEach) + "px", to: (targetItem * -this._sizeEach) + "px" }; - this._step = glow.anim.css(this._content, this._animationTime, move, { "tween": this._animationTween }); - var that = this; - glow.events.addListener(this._step, "complete", function() { - afterScroll.apply(that, []); - }); - this._step.start(); - } - else { - this._content.css("margin-"+this._direction, (targetItem * -this._sizeEach) + "px"); - afterScroll.apply(this, []); - } - return this; - } - - glow.widgets.Carousel = Carousel; - - /** - @private - @constructor - @description Display the currently visible step. - */ - function PageNav(pagecount, onClick) { - this.leftarrow = dom.create("
      • previous
      • "); - this.rightarrow = dom.create("
      • next
      • "); - - var pageNavHtml = ""; - for (var i = 0; i < pagecount; i++) { - pageNavHtml += "
      • "+(i+1)+"
      • "; - } - - this.element = dom.create(""); - this.leftarrow.insertBefore(this.element.get("li")[0]); - this.rightarrow.insertAfter(this.element.get("li")[this.element.get("li").length-1]); - - var that = this; - glow.events.addListener(this.element, "click", - function(e) { - if ($(e.source).parent().hasClass('dot')) { // clicked a dot? - var newPage = $(e.source).parent()[0].id.replace(/[^0-9]*/, ""); - onClick.apply(that, [newPage]); - } - } - ); - - this.currentPage = 0; - } - - PageNav.prototype.update = function(newPage) { /*debug*///console.log("PageNav.prototype.update("+newPage+")"); - if (typeof newPage == "undefined") newPage = this.currentPage; - this.element.get("#dot"+this.currentPage+"").removeClass("dotActive"); - this.element.get("#dot"+newPage+"").addClass("dotActive"); - this.currentPage = newPage; - } - } -});(window.gloader || glow).module({ - name: 'glow.widgets.Editor', - library: ['glow', '1.5.1'], - depends: [[ - 'glow', '1.5.1', - 'glow.dom', - 'glow.events', - 'glow.widgets' - ]], - - builder: function(glow) { - var $ = glow.dom.get, // shortcuts - events = glow.events; - - /** - @name glow.widgets.Editor - @class - @description A Rich Text Editor to allow text formatting with form inputs. - -
        Widgets must be called in a glow.onReady() call.
        - @constructor - @param {String|glow.dom.NodeList} textarea Textarea HTML element that the Editor replaces on your page - @param {Object} opts - @_param {String} [opts.toolset="basic"] - @param {String} [opts.theme="light"] Visual Theme - Currently supported themes are "dark" and "light". - @_property {glow.dom.NodeList} element - @_property {glow.dom.NodeList} textarea - @_property {glow.widgets.Editor.Toolbar} toolbar - @_property {glow.widgets.Editor.EditArea} editArea - - @example - var editor = new glow.widgets.Editor("#commentEntry"); - - @see Editor user guide - */ - /** - @name glow.widgets.Editor#event:commit - @event - @description Fired whenever the associated textarea is updated by the editor. - @param {glow.events.Event} event Event Object - - */ - glow.widgets.Editor = function(textarea, opts) { - textarea = $(textarea); - - // we need to create our toolset for the first use - if (!TOOLS) { - TOOLS = createTools(); - } - - opts = this._opts = glow.lang.apply( - { - toolset: "basic" - }, - opts - ); - this.element = glow.dom.create('

        You are about to enter a Rich Text Editor, Skip past

        You have left the Rich Text Editor

        '); - this.textarea = textarea; - this.toolbar = new glow.widgets.Editor.Toolbar(this); - - if (this._opts.toolset == "basic") { - this.toolbar._addToolset("italics", "bold", "strike"/*, "blockquote"*/); - } - else throw new Exception("Unknown toolset name."); - - this.editArea = new glow.widgets.Editor.EditArea(this); - this.cleaner = new TagCleaner(); - - // Safari 2 is not enhanced - if (!isSafariTwo()) { - place.apply(this); - bindEditor.apply(this, []); - } - } - -//// - -/** - @ignore - @private - @name endOfEditorCounter - @description We need to give each hidden accessibility hidden message a unique id, so to do this add a number on the end. Work out the number by counting how many of these links are already on the page. - */ - var endOfEditorCounter = function() { - return glow.dom.get('p.endOfEditorCounter').length+1; - } - - -/** - @ignore - @private - @name Idler - @constructor - @param {String|Object} attachTo The object to attach the idler to. - @param {String|String[]} name The event name, or names for multiple events. - @param {Number} wait The number of seconds to wait after no event occurs before the callback should run. - @param {Object} [opts] Options object - @param {Function} [opts.onFire] Function to run when this Idler fires. - @param {Number} [opts.rate] Repeat the fire event every x seconds - */ - -var Idler = function(attachTo, name, wait, opts) { /*debug*///console.info("new Idler()"); - opts = this._opts = glow.lang.apply( - { - onFire: function() {} - }, - opts - ); - - var that = this; - this.attachTo = attachTo; - this.name = name; - this.wait = wait; - this.callback = opts.onFire; - this.rate = opts.rate; - this.running = false; - this.initiated = false; - - if (typeof this.name.pop == "undefined") { // is it an array? - this.name = [this.name]; - } - - for (var i = 0, l = this.name.length; i < l; i++) { - var name = this.name[i]; - glow.events.addListener( - this.attachTo, - name, - function() { - clearInterval(that.intervalId); - clearTimeout(that.timeoutId); - that._tick(); - } - ); - } - - this._start(); -} - -/** - @ignore - @private - @name Idler#disabled - @function - @param {Boolean} [disabledState] - @description Sets/gets the disabled state of the Idler. When disabled, any running - timers are cancelled and no new timers can be started. - */ -Idler.prototype.disabled = function(disabledState) { - if (typeof disabledState == "undefined") { - return !this.running; - } - else { - if (disabledState) this._stop(); - else this._start(); - } -} - -Idler.prototype._tick = function() {/*debug*///console.info("Idler._tick()"); - var that = this; - this.timeoutId = setTimeout( - function() { - if (typeof that.rate != "undefined") { - that.intervalId = setInterval(that.callback, that.rate); - } - else { - that.callback(); - } - }, - that.wait - ); -} - -/** - @ignore - @private - @name Idler#_start - @function - @description Start the idler, if it is not already running. - */ -Idler.prototype._start = function() { - if (this.running) return; - - this._tick(); - - this.running = true; -} - -/** - @ignore - @private - @name Idler#_stop - @function - @description Stop the idler, if it is running. - */ -Idler.prototype._stop = function() { - if (!this.running) return; - - clearInterval(this.intervalId); - clearTimeout(this.timeoutId); - - this.running = false; -} - -//// - - /** - @ignore - @private - @name place - @function - @description Positions the toolbar over the textarea. - @this glow.widgets.Editor.Toolbar - */ - function place() { - var inputOffset = this.textarea.offset(); - var height = (this.textarea[0].offsetHeight > 142) ? this.textarea[0].offsetHeight : 142; - - this.element.css('width', (this.textarea[0].offsetWidth-2) + 'px'); - this.element.css('height', (height-2) + 'px'); - } - - var bindEditor = function() { - // Add the widget into the page - this.textarea.before(this.element); - - // Redo height of the iframe. We need to do it here because we can only get the correct height when the element is in the page. - this.element.get('iframe').css( 'height', (parseInt(this.element.css('height'))-42) ); - - // Set the textarea so the user can't see it (but a screen reader can) - this.textarea.css("display", "block"); - this.textarea.css("position", "absolute"); - this.textarea.css("left", "-9999px"); - this.textarea.css("top", "-9999px"); - - this.bound = true; - } - -//////// - glow.widgets.Editor.prototype.inject = function(html) { - this.editArea._setContent(this.cleaner.dirty(this.cleaner.clean(html))); - } - - glow.widgets.Editor.prototype.commit = function() { - if (this.bound) { - $(this.textarea).val(this.cleaner.clean(this.editArea._getContent())); - } - glow.events.fire(this, "commit", {}); - } - - function TagCleaner(opts) { - // assumes nesting of dirty tags is always valid - // assumes no '>' in any dirty tag attributes - // assumes attributes are quoted with double-quotes, and attribute values contain no escaped double-quotes - - this.opts = opts || {}; - - this.whitelist = ["em", "strong", "strike", "p", "br", "blockquote"]; // TODO: support tags.attributes - } - - // runs before clean - TagCleaner.prototype.pretreat = function(input) { - // remove html comments - input = input.replace(//g, ""); - - // remove style tags and their contents - input = input.replace(//gi, ""); - - // remove script tags and their contents - input = input.replace(//gi, ""); - - return input - } - - TagCleaner.prototype.clean = function(input) { /*debug*///console.log("TagCleaner#clean("+input+")") - var output = "", - stack = []; - - input = this.pretreat(input); - -//var sanity = 99; - while (input) { -//if (sanity-- == 0) throw new Error("stoopid loops"); - var skip = 1; // characters - - if (/^(<[^>]+>)/.test(input)) { // tag encountered - var foundTag = new TagCleaner.Tag(RegExp.$1); - - this.tagClean(foundTag); - - if (foundTag.clean && foundTag.opening) { // there's a clean version - output += foundTag.clean.start; - if (!foundTag.unary) stack.unshift(foundTag); - skip = foundTag.text.length; - } - else if (stack[0] && input.toLowerCase().indexOf(stack[0].end) === 0) { // found tag was closed - output += stack[0].clean.end; - skip = stack[0].end.length; - stack.shift(); - } - else { // unknown tag - output += foundTag; - skip = foundTag.text.length; - } - } - else { // non-tag content - output += input.charAt(0); - } - - // move ahead - input = input.substring(skip); - } - - output = this.spin(output); - - return output; - } - - TagCleaner.prototype.dirty = function(clean) { - var dirty; - - if (glow.env.gecko) { // mozilla? - dirty = clean - .replace(//g, '').replace(/<\/strong>/g, '') - .replace(//g, '').replace(/<\/em>/g, '') - .replace(//g, '') - ; - } - else if (glow.env.ie || glow.env.opera) { - dirty = clean - .replace(//g, '').replace(/<\/strong>/g, '') - .replace(//g, '').replace(/<\/em>/g, '') - .replace(//g, '').replace(/<\/strike>/g, '') - ; - } - else if (glow.env.webkit > 528) { // safari 4? TODO: same as safari 2? - dirty = clean - .replace(//g, '').replace(/<\/strong>/g, '') - .replace(//g, '').replace(/<\/em>/g, '') - .replace(//g, '').replace(/<\/strike>/g, '') - ; - } - else if (glow.env.webkit) { // safari 3? - dirty = clean - .replace(//g, '').replace(/<\/strong>/g, '') - .replace(//g, '').replace(/<\/em>/g, '') - .replace(//g, '').replace(/<\/strike>/g, '') - ; - } - else { throw new Error("Can't be dirty: Unknown browser."); } - - return dirty; - } - - /** - @ignore - @private - @description A single span can become a combination of several semantic tags. - @param {TagCleaner.Tag} tag A span Tag object. - @returns {String[]} An array of [0] opening tag or tags as a string and [1] closing tag or tags as a string. - */ - TagCleaner.prototype.spanClean = function(tag) { - var clean = {start:"", end:""}; - - if (/\bstyle\s*=\s*"(.+)"/.test(tag.attrText.toLowerCase())) { - if (RegExp.$1.indexOf("bold") > -1) { - clean.start += ""; - clean.end = "<\/strong>"+clean.end; - } - // safari needs this - if (RegExp.$1.indexOf("font-weight: normal") > -1) { - clean.start += "<\/strong>"; - clean.end = ""+clean.end; - } - - if (RegExp.$1.indexOf("italic") > -1) { - clean.start += ""; - clean.end = "<\/em>"+clean.end; - } - - // safari needs this - if (RegExp.$1.indexOf("font-style: normal") > -1) { - clean.start += "<\/em>"; - clean.end = ""+clean.end; - } - - if (RegExp.$1.indexOf("line-through") > -1) { - clean.start += ""; - clean.end = "<\/strike>"+clean.end; - } - } - - return clean; - } - - /** - @ignore - @private - @description Given a dirty tag, add a clean tag property, if one can be found. - @param {TagCleaner.Tag} tag A dirty Tag object. - @returns {String[]} An array of [0] opening tag or tags as a string and [1] closing tag or tags as a string. - */ - TagCleaner.prototype.tagClean = function(tag) { - var clean = ["", ""]; - - if (tag.name == "span") clean = this.spanClean(tag); - else if (tag.name == "b") clean = {start:'', end:'<\/strong>'}; - else if (tag.name == "i") clean = {start:'', end:'<\/em>'}; - - if (clean.start) tag.clean = clean; - } - - TagCleaner.Tag = function(tagText) { /*debug*///console.log("new TagCleaner.Tag("+tagText+")"); - /^<(\/?)([a-zA-Z]+)\b(.*)( ?\/)?>$/.exec(tagText); - this.closing = !!RegExp.$1; - this.opening = !this.closing; - this.unary = !!RegExp.$4; - this.name = RegExp.$2.toLowerCase(); - this.attrText = RegExp.$3; - this.text = tagText; - - // normalise case of tag names - this.start = tagText.replace(/^<(\/?)([a-zA-Z]+)\b/, "<$1"+this.name); - - if (this.opening && !this.unary) { - this.end = "<\/"+this.name+">"; - } - } - TagCleaner.Tag.prototype.toString = function() { - return "<" + RegExp.$1 + this.name/* + this.attrText */+ RegExp.$4 +">"; - } - - TagCleaner.prototype.spin = function(input) { - var whitetags = this.whitelist.join("\|"); - // note: Safari 2.0.4 doesn't support unicode in regex, but can use unprintable ASCII characters, like the "Group Separator" - var allowedTags = new RegExp("<(\\/?("+whitetags+")\\b[^>]*)>", "g"); - input = input.replace(allowedTags, "\x1D$1\x1D"); // hide allowed tags - input = input.replace(/<[^>]+>/g, ""); // kill all visible tags - input = input.replace(/\x1D([^\x1D]+)\x1D/g, "<$1>"); // restore allowed tags - - // general final clean up... - input = input.replace(/<>/g, ""); // remove Safari droppings - - return input; - } - -//////// - - /** - @ignore - @private - @name glow.widgets.Editor.Toolbar - @description - @constructor - @_param {glow.widgets.Editor} editor - @_param {Object} opts - */ - glow.widgets.Editor.Toolbar = function(editor, opts) { - opts = opts || {}; - this.editor = editor; - this.element = glow.dom.create('
          '); - this._tools = []; - this.editor.element.get(".editor-state").prepend(this.element); - } - - /** - @ignore - @private - @name glow.widgets.Editor.Toolbar#_addToolset - @description Quickly add several tools at once to the toolbar. - @function - @param {String} ... Either the name of a built-in set of tools, - like "fontFormatting" [TBD] and/or the predefined names of - built-in tools. - @returns {glow.widgets.Editor.Toolbar} this - @example - myToolbar._addToolset("Bold", "Italics") - .addButton("MyCustomButton", opts); // will be chainable - */ - glow.widgets.Editor.Toolbar.prototype._addToolset = function() { - var toolToAdd; - for (var i = 0, l = arguments.length; i < l; i++) { - if ( (toolToAdd = TOOLS[arguments[i]]) ) { - var newTool = new glow.widgets.Editor.Toolbar.Button(toolToAdd.name, toolToAdd.opts); - addTool.call(this, newTool); - } - } - - // Give the toolbar one focusable button - // this.element.get('a').item(0).tabIndex = 0; - addToolbarIntoTabIndex.apply(this); - - return this; - } - - // modifies HTML in place, while preserving the cursor position - glow.widgets.Editor.blackList = { - FORM: true, - TABLE: true, - TBODY: true, - CAPTION: true, - TH: true, - TR: true, - TD: true, - SCRIPT: true, - STYLE: true, - INPUT: true, - BUTTON: true, - OBJECT: true, - EMBED: true, - SELECT: true, - H1: true, - H2: true, - H3: true, - H4: true, - H5: true, - H6: true, - DIV: true, - ADDRESS: true, -// BLOCKQUOTE: true, - CENTER: true, - PRE: true, - CODE: true, - A: true, - UL: true, - OL: true, - LI: true, - DL: true, - DT: true, - DD: true, - ABBR: true, - ACRONYM: true, - DFN: true, - INS: true, - DEL: true, - SAMP: true, - VAR: true, - BIG: true, - SMALL: true, - BLINK: true, - MARQUEE: true, - FONT: true, - Q: true, - U: true, - KBD: true, - SUB: true, - SUP: true, - CITE: true, - HTML: true, - BODY: true, - FIELDSET: true, - LEGEND: true, - LABEL: true, - TEXTAREA: true, - HR: true, - IMG: true, - IFRAME: true, - ILAYER: true, - LAYER: true - }; - glow.widgets.Editor.prototype._rinse = function() { /*debug*///console.log("glow.widgets.Editor#_rinse()"); - if (this._lastRinse == this.editArea._getContent()) return; /*debug*///console.log("rinsing"); - - var doc = this.editArea.contentWindow.document; - var node = doc.body; - - var that = this; // keep track of me, even when recursing - function walkNode(node) { - if (node.childNodes) { - for (var i = 0; i < node.childNodes.length; i++) { - var keepStatus = glow.widgets.Editor.blackList[node.childNodes[i].nodeName]; - - if (node.nodeType == 1) { // an element node - if (keepStatus) { - var replacementNode = doc.createElement("SPAN"); - //replacementNode.setAttribute('class', 'glow-rinsed'); - - replacementNode.innerHTML = that.cleaner.clean(node.childNodes[i].innerHTML+" "); - node.replaceChild(replacementNode, node.childNodes[i]); - } - else { - // it's an allowed node but we should limit external styles as much as possible - if (node.childNodes[i].nodeName == "P") node.childNodes[i].removeAttribute("style"); - if (node.childNodes[i].nodeName == "SPAN") { // webkit may use font-size spans to show headings - if (/font-size/.test(node.childNodes[i].getAttribute("style"))) node.childNodes[i].removeAttribute("style"); - } - walkNode(node.childNodes[i]); - } - } - } - } - else { - if (glow.widgets.Editor.blackList[node.nodeName]) { - node.parentNode.removeChild(node); - } - } - } - - walkNode(node); - - this._lastRinse = this.editArea._getContent(); - } - - /** - @ignore - @name addTool - @private - @function - @param {glow.widgets.Editor.Toolbar.Tool} toolToAdd - */ - function addTool(toolToAdd) { /*debug*///console.log("addTool("+toolToAdd+")") - toolToAdd.editor = this.editor; - this._tools.push(toolToAdd); - this.element.get(".editor-toolbar-tools").append(toolToAdd.element); - } - - /** - @ignore - @private - @name glow.widgets.Editor.Toolbar#addButton - @description - @function - @param {String} name - @param {Object} opts - */ - glow.widgets.Editor.Toolbar.prototype.addButton = function(name, opts) { - var newTool = new glow.widgets.Editor.Toolbar.Button(name, opts, this); - addTool.call(this, newTool); - return this; - } - - /** - @ignore - @private - @name glow.widgets.Editor.Toolbar#getTool - @description - @function - @param {String} name - @returns {glow.widgets.Editor.Toolbar.Tool} - */ - glow.widgets.Editor.Toolbar.prototype.getTool = function(name) { - var i = this._tools.length; - while (--i >= 0) { - if (this._tools[i].name == name) return this._tools[i]; - } - } - - /** - @ignore - @private - @name glow.widgets.Editor.Toolbar#_update - @description - @function - @param {String} name - @returns {glow.widgets.Editor.Toolbar.Tool} - */ - glow.widgets.Editor.Toolbar.prototype._update = function(domPath) { /*debug*///console.log("glow.widgets.Editor.Toolbar.prototype._update("+domPath+")") - var handled = false; - for (var i = 0, l = this._tools.length; i < l; i++) { - if (domPath.indexOf("|"+this._tools[i].tag+"|") > -1) { - this._tools[i].activate(); - handled = true; - } - else { - this._tools[i].deactivate(); - } - } - return handled; - } - - /** - @ignore - @private - @name glow.widgets.Editor.Toolbar#_shortcut - @description - @function - @param {String} name - @returns {glow.widgets.Editor.Toolbar.Tool} - */ - glow.widgets.Editor.Toolbar.prototype._shortcut = function(letter) { - var i = this._tools.length; - var handled = false; - while (--i >= 0) { - if (this._tools[i].shortcut == letter) { - this._tools[i].press(); - return true; - } - } - return false; - } - -//////// - - /** - @ignore - @private - @name glow.widgets.Editor.Toolbar.Tool - @constructor - @description Generic base class for all Tools. - */ - glow.widgets.Editor.Toolbar.Tool = function(name, opts, context) { /*debug*///console.log("glow.widgets.Editor.Toolbar.Tool("+name+", "+opts.toSource()+")") - this.name = name; - this.opts = opts || {}; - this.action = this.opts.action || function(){}; - this.tag = this.opts.tag; - this.command = this.opts.command; - this.shortcut = this.opts.shortcut; - this.isActive = false; - this.isEnabled = true; - - if (this.opts.onDeactivate) glow.events.addListener(this, "deactivate", this.opts.onDeactivate, context); - if (this.opts.onActivate) glow.events.addListener(this, "activate", this.opts.onActivate, context); - if (this.opts.onDisable) glow.events.addListener(this, "disable", this.opts.onDisable, context); - if (this.opts.onEnable) glow.events.addListener(this, "enable", this.opts.onEnable, context); - } - - /** - @ignore - @private - @name glow.widgets.Editor.Toolbar.Tool#activate - @description - @function - */ - glow.widgets.Editor.Toolbar.Tool.prototype.activate = function() { - this.isActive = true; - glow.events.fire(this, 'activate'); - } - - /** - @ignore - @private - @name glow.widgets.Editor.Toolbar.Tool#deactivate - @description - @function - */ - glow.widgets.Editor.Toolbar.Tool.prototype.deactivate = function() { - this.isActive = false; - glow.events.fire(this, 'deactivate'); - } - - /** - @ignore - @private - @name glow.widgets.Editor.Toolbar.Tool#disable - @description - @function - */ - glow.widgets.Editor.Toolbar.Tool.prototype.disable = function() { - this.isEnabled = false; - glow.events.fire(this, 'disable'); - } - - /** - @ignore - @private - @name glow.widgets.Editor.Toolbar.Tool#enable - @description - @function - */ - glow.widgets.Editor.Toolbar.Tool.prototype.enable = function() { - this.isEnabled = true; - glow.events.fire(this, 'enable'); - } - - /** - @ignore - @private - @name glow.widgets.Editor.Toolbar.Tool#press - @description - @function - */ - glow.widgets.Editor.Toolbar.Tool.prototype.press = function() { - if (this.isEnabled) { - this.action.call(this); - if (!this.isActive) this.activate(); - else this.deactivate(); - this.editor._lastDomPath = null; // invalidate the current dom path (this is required on some browsers that "wrap" selections) to force ondompathchange to fire when the user clicks away from the current selection - } - } - -//////// - - /** - @ignore - @private - @name glow.widgets.Editor.Toolbar.Button - @constructor - @extends glow.widgets.Editor.Toolbar.Tool - */ - glow.widgets.Editor.Toolbar.Button = function(name, opts) { /*debug*///console.log("new glow.widgets.Editor.Toolbar.Button("+name+", "+opts.toSource()+")") - this.Base = arguments.callee.base; this.base = this.Base.prototype; - this.Base.apply(this, arguments); - - // a button's CSS classname is defined here - var buttonClass = name.toLowerCase() + "-button"; - this.element = glow.dom.create('
        • '+(opts.label || name)+'<\/span><\/span><\/a><\/span><\/li>'); - - // shortcuts - var toolLink = this.element.get("a"); - this.icon = this.element.get(".editor-toolbar-icon"); - - var key_listener; - - glow.events.addListener(this.icon, "mouseover", function() { if (this.isEnabled && !this.isActive) toolLink.addClass("hover"); }, this); - glow.events.addListener(toolLink, "focus", function() { if (this.isEnabled) toolLink.addClass("hover"); key_listener = enable_key_listener(this); }, this); - glow.events.addListener(this.icon, "mouseout", function() { toolLink.removeClass("hover"); }, this); - glow.events.addListener(toolLink, "blur", function() { toolLink.removeClass("hover"); glow.events.removeListener(key_listener);}, this); - glow.events.addListener(this, "disable", function() { toolLink.addClass("disabled"); }, this); - glow.events.addListener(this, "enable", function() { toolLink.removeClass("disabled"); }, this); - glow.events.addListener(this, "activate", function() { if (this.isEnabled) toolLink.addClass("active"); }, this); - glow.events.addListener(this, "deactivate", function() { toolLink.removeClass("active"); }, this); - - var that = this; - glow.events.addListener(this.element.get("a"), "mousedown", function() { that.press(); return false; }, this); // bind the click handler context to the Tool (not the HTMLElement) - glow.events.addListener(this.element.get("a"), "click", function() { return false; }); - } - - glow.lang.extend(glow.widgets.Editor.Toolbar.Button, glow.widgets.Editor.Toolbar.Tool); - -// TODO: all these would be better handled by onWhatever event handlers passed into the Tool constructor call - glow.widgets.Editor.Toolbar.Button.prototype.activate = function() { - this.base.activate.apply(this, arguments); - } - - glow.widgets.Editor.Toolbar.Button.prototype.deactivate = function() { - this.base.deactivate.apply(this, arguments); - } - - glow.widgets.Editor.Toolbar.Button.prototype.enable = function(name) { - this.base.enable.apply(this, arguments); - } - - glow.widgets.Editor.Toolbar.Button.prototype.disable = function(name) { - this.base.disable.apply(this, arguments); - } - - - - /** - @ignore - @private - @name enable_key_listener - @description Adds listener onto each button when it has focus. Listens for a keyup event with the ENTER key. This allows users to 'press' the button via the keyboard when the button has focus. - */ - function enable_key_listener(button) { - - return glow.events.addListener(glow.dom.get(document), 'keyup', function(event){ - if(event.key == 'ENTER') { - button.press(); - if (event.preventDefault) event.preventDefault(); - return false; - } - }); - - - - } - - /* built-in tools here. */ - var TOOLS; - // this is called when the first instance of editor is created - function createTools() { - return { - bold: new glow.widgets.Editor.Toolbar.Button( - "bold", - { - title: "Bold", - label: "B", - tag: "strong", - command: "bold", - shortcut: "b", - action: function() { tag.call(this.editor.editArea, this.command); return false; } - } - ), - italics: new glow.widgets.Editor.Toolbar.Button( - "italics", - { - title: "Italics", - label: "I", - tag: "em", - command: "italic", - shortcut: "i", - action: function() { tag.call(this.editor.editArea, this.command); return false; } - } - ), - strike: new glow.widgets.Editor.Toolbar.Button( - "strike", - { - title: "Strikethrough", - label: "Strike", - tag: "strike", - command: "strikethrough", - action: function() { tag.call(this.editor.editArea, this.command); return false; } - } - ) - /* tag.call(this.editor.editArea, this.command) - , - blockquote: new glow.widgets.Editor.Toolbar.Button( - "blockquote", - { - title: "Blockquote", - label: "blockquote", - tag: "blockquote", - command: "formatblock", - action: function() { tag.call(this.editor.editArea, this.command, 'blockquote'); return false; } - } - ), - heading1: new glow.widgets.Editor.Toolbar.Button( - "heading1", - { - title: "Heading1", - label: "Heading1", - tag: "Heading1", - command: "formatblock", - action: function() { tag.call(this.editor.editArea, this.command, 'h1'); return false; } - } - ), - toggle: new glow.widgets.Editor.Toolbar.Button( - "toggle", - { - title: "toggle", - label: "toggle", - tag: "toggle", - command: "toggle", - action: function() { this.editor.editArea.toggle_designMode(); return false; } - } - ) */ - }; - } - -//////// - - /** - @ignore - @private - @constructor - @name glow.widgets.Editor.EditArea - */ - glow.widgets.Editor.EditArea = function(editor, opts) { - opts = opts || {}; - this.editor = editor; - this.element = $(document.createElement("iframe")); - this.element.attr('frameBorder', 0); - this.element.src = "javascript:false"; - this.editor.element.get(".editor-state").append(this.element); - var that = this; - setTimeout( - function() { // For FF - that.element[0].contentWindow.document.designMode = "on"; - that.contentWindow = that.element[0].contentWindow; - - if (that.editor.textarea.val()) { - that.contentWindow.document.write(that.editor.textarea.val()); - } - else { - that.contentWindow.document.write("

           

          "); - } - that.contentWindow.document.close(); - that.editor.iframeFocus = false; - addKeyboardListener.call(that); - manageToolbarFocus(that); - if (glow.env.ie || glow.env.opera) { //TODO: /sigh - glow.dom.get(that.element[0].contentWindow.document).item(0).attachEvent('onclick', function() { updateArea.call(that); } ); - glow.dom.get(that.element[0].contentWindow.document).item(0).attachEvent('onkeyup', function() { updateArea.call(that); } ); - //glow.dom.get(that.element[0].contentWindow.document).item(0).attachEvent('onmouseup', function() { updateArea.call(that); }); - //events.addListener(that.element[0], 'focus', function () { updateArea.call(that); } ); - //events.addListener(that.element[0].contentWindow, 'blur', function () { updateArea.call(that); } ); - } - else { - events.addListener(that.contentWindow, 'blur', function () { updateArea.call(that); } ); - //events.addListener(that.contentWindow, 'focus', function () { updateArea.call(that); } ); - events.addListener(that.contentWindow, 'click', function () { updateArea.call(that); } ); - events.addListener(that.contentWindow, 'mouseup', function () { updateArea.call(that); } ); - events.addListener(that.contentWindow, 'keyup', function () { updateArea.call(that); } ); - } - if (glow.env.gecko) { - that.contentWindow.document.execCommand("styleWithCSS", false, false); - } - - // see Webkit bug related to onbeforeunload and iframes - // https://bugs.webkit.org/show_bug.cgi?id=21699 - // http://code.google.com/p/chromium/issues/detail?id=5773 - events.addListener(that.element[0].contentWindow, 'beforeunload', function () { that.editor.commit(); return true; } ); - events.addListener(window, 'beforeunload', function () { that.editor.commit(); return true; } ); - - // Give the toolbar one focusable button - // Boolean that we use to make sure we only do this once - that._toolbarInTabIndex = false; - - // Listener for when the user clicks on the editor - glow.events.addListener( - that.editor.element.get(".editor-state"), - "click", - function() { - addToolbarIntoTabIndex.apply(that); - }, - that - ); - - // Listener for when the user tabs into the iframe - if (!isNaN(glow.env.ie)) { - that.contentWindow.attachEvent( - 'onfocus', - function() { - addToolbarIntoTabIndex.apply(that); - }, - that - ); - } - else { - that.contentWindow.addEventListener( - 'focus', - function() { - addToolbarIntoTabIndex.apply(that); - }, - that - ); - } - - if (that.editor.bound) { - that.idler = new Idler( - that.contentWindow, - ["mousedown", "keypress"], - 350, - { - onFire: function() { - that.editor._rinse(); - }, - rate: 700 - } - ); - } - }, - 0 - ) - } - - /** - @ignore - @name addToolbarIntoTabIndex - @description - */ - function addToolbarIntoTabIndex() { - if (this.editor._toolbarInTabIndex == true) return; - this.editor.toolbar.element.get('a').item(0).tabIndex = 0; - this.editor._toolbarInTabIndex = true; - } - - /** - @ignore - @name addKeyboardListener - @description - @function - */ - function addKeyboardListener() { - // If ie then use attachEvent with onkeydown - if (!isNaN(glow.env.ie)) { - glow.dom.get(this.contentWindow.document).item(0).attachEvent( - 'onkeydown', - (function(that){ - return function(event){ - event = event || window.event; - return checkingKeyCombos.call(that, event); - } - })(this) - ); - } - // If opera then use attachEvent with onkeypress - else if (!isNaN(glow.env.opera)) { - glow.dom.get(this.contentWindow.document).item(0).addEventListener( - 'keypress', - (function(that){ - return function(event){ - event = event || window.event; - return checkingKeyCombos.call(that, event); - } - })(this), - true - ); - } - // Everything else use addEventListener with keydown - else { - - glow.dom.get(this.contentWindow.document).item(0).addEventListener( - 'keydown', - (function(that){ - return function(event){ - event = event || window.event; - return checkingKeyCombos.call(that, event); - } - })(this), - true - ); - } - } - - /** - @ignore - @private - @name checkingKeyCombos - @description Handles keyboard combinations - @function - */ - function checkingKeyCombos(event) { - - // Safari on a mac is too screwy to be trusted with this change to tabbing so I'm filtering it out - if ((navigator.platform.toLowerCase().indexOf('mac') == -1) || isNaN(glow.env.webkit)) - { - // Set [TAB]+[SHIFT] to tab up the page - if ((event.keyCode == 9) && (event.shiftKey == true)) { //console.log('shift up'); - - // If [TAB]+[SHIFT] then we want to set the focus to the tool in the toolbar that has been set to receive focus (see function 'manageToolbarFocus') - var arrIcons = glow.dom.get(this.editor.element).get('ul.editor-toolbar-tools a'); - arrIcons.each(function(i) { - if (arrIcons[i].tabIndex == 0) { - window.focus(); // This forces the iframe to loose focus, otherwise we end up with two elements on the page responding to keyboard events - arrIcons[i].focus(); - } - }); - if (event.preventDefault) event.preventDefault(); - return false; - } - - // Set [TAB] to tab down the page - if ( (event.keyCode == 9) ) { //console.log('shift down'); - window.focus(); // This forces the iframe to loose focus, otherwise we end up with two elements on the page responding to keyboard events - this.element[0].focus(); // This lets gecko loose focus on the iframe - glow.dom.get(this.editor.element).get('p.endOfEditorCounter').item(0).focus(); // Send the focus to the last element in the editor (this is a paragraph that screen readers will see) - if (event.preventDefault) event.preventDefault(); - return false; - } - } - - // [modifier] plus [toolbar shortcut] key - if (appliedModifierKey.call(this, event)) { - if - ( - ( this.editor.toolbar._shortcut(String.fromCharCode(event.keyCode).toLowerCase()) ) || // If toolbar shortcut is related to the key being pressed then it returns true - ( String.fromCharCode(event.keyCode).toLowerCase() == 'u' ) // Don't allow [modifier] + [i] (makes text underlined) - ) - { - if (event.preventDefault) event.preventDefault(); - return false; - } - } - return true; - } - - /** - @ignore - @private - @name appliedModifierKey - @description Returns boolean stating if the modifier key is being pressed - @function - */ - function appliedModifierKey(event) { - if (navigator.platform.toLowerCase().indexOf('mac') != -1) { - if (!isNaN(glow.env.opera)) return event.ctrlKey; - return event.metaKey; - } - else { - return event.ctrlKey; - } - } - - /** - @ignore - @private - @name isSafariTwo - @description Returns boolean if browser is Safari version 2 - @function - */ - function isSafariTwo() { - if ((glow.env.webkit > 400) && (glow.env.webkit < 500)) { - return true; - } - else { - return false; - } - } - - /** - @ignore - @private - @name moveCursorLeft - @description Gives the toolbar a single tabindex, allowing you to tab in and out of the toolbar with cycling through the buttons. When the toolbar does have focus, move between buttons using the LEFT and RIGHT arrow keys. - */ - function manageToolbarFocus(editArea) { - var left_listener, - right_listener, - arrButtons = editArea.editor.toolbar.element.get('a'); - - - // Add key event listeners whenever the 'buttons' the in the toolbar are in focus - // NOTE: We could potential use event delegation but right now I'm not too sure - // how this would work with the browser giving the element focus. - // When the button has focus... - glow.events.addListener( - glow.dom.get(arrButtons), - 'focus', - function() { // console.log('on'); - - // ... swap focus to its siblings if the user presses the LEFT or RIGHT arrow keys - right_listener = glow.events.addKeyListener('RIGHT', 'down', moveFocusRight); - left_listener = glow.events.addKeyListener('LEFT', 'down', moveFocusLeft); - - } - ); - - glow.events.addListener( - glow.dom.get(arrButtons), - 'blur', - function() { // console.log('off'); - - glow.events.removeListener(right_listener); - glow.events.removeListener(left_listener); - - } - ); - } - - /** - @ignore - @private - @name moveCursorLeft - @description Receives left arrow key down event, passes the previous sibling of the element source to moveCursor function. - */ - function moveFocusLeft(event) { - moveFocus( getDistantSibling( glow.dom.get(event.source), -1) ); - } - - /** - @ignore - @private - @name moveCursorRight - @description Receives right arrow key down event, passes the next sibling of the element source to moveCursor function. - */ - function moveFocusRight(event) { - moveFocus( getDistantSibling( glow.dom.get(event.source), 1) ); - } - - /** - @ignore - @private - @name getDistantSibling - @description Builds an array of the hyperlinks in the toolbar, sets all their tabIndexes to -1 and then returns either - the next or previous element in the array based on the element passed in as a param - */ - function getDistantSibling(elm, move) { // console.log('changing the toolbar'); - var itemIndexToFocus = 0, - arrLinks = glow.dom.get(elm).parent().parent().parent().get('a'), - trueArrayLength = (arrLinks.length-1); - - // Loop through array... - arrLinks.each(function(y) { - // If this item is the passed in element item, then set 'itemIndexTofocus' - if (this == elm.item(0)) itemIndexToFocus = (y+move); - // Reset tabIndex - this.tabIndex = -1; - }); - - // Make sure 'itemIndexToFocus' stays within the bounds of the array length - if (itemIndexToFocus < 0) itemIndexToFocus = 0; - if (itemIndexToFocus > trueArrayLength) itemIndexToFocus = trueArrayLength; - - // Return either the next or previous item compared the the element passed in via 'elm' param - return arrLinks.item(itemIndexToFocus); - } - - /** - @ignore - @private - @name moveCursor - @description Moves the focus from the focused element, to one of its siblings. Also manages the tabindex of the toolbar - */ - function moveFocus(item) { - if (typeof item != 'undefined') { - // Set the tab index of the item that is to gain focus to 0 and give it focus. - item.tabIndex = 0; - item.focus(); - } - } - - /** - @ignore - @private - @name tag - @description Applies execCommand - @function - */ - - function tag(tagName, attr) { /*debug*///console && console.log("glow.widgets.Editor.EditArea.prototype.tag("+tagName+", "+attr+")"); - attr = attr || null; - if (this[tagName + "_" + attr]) { - this[tagName + "_" + attr](); - } - else { - this._domPath(); - this.contentWindow.document.execCommand(tagName, false, null); - } - this.contentWindow.focus(); - updateArea.call(this); - } - - /** - @ignore - @private - @name glow.widgets.Editor.EditArea._getSelected - @description gets the selected text - @function - */ - glow.widgets.Editor.EditArea.prototype._getSelected = function() { - if (glow.env.ie) { - // IE doesn't use the DOM2 methods for selection and determining range - return this.contentWindow.document.selection; - } - else { - return this.contentWindow.getSelection(); - } - } - - /** - @ignore - @private - @name updateArea - @description Commits the iframe content to the hidden textarea - @function - */ - function updateArea() { /*debug*///console.log("updateArea()") - this.editor.commit(); - // Update Toolbar - var currentDomPath = this._domPath(); - if (currentDomPath != this.editor._lastDomPath) { - this.editor._lastDomPath = currentDomPath; - var e = glow.events.fire(this, 'domPathChange', { - domPath: currentDomPath - }); - - if (!e.defaultPrevented()) { - this.editor.toolbar._update(currentDomPath); - } - } - } - -// glow.widgets.Editor.EditArea.prototype.formatblock_blockquote = function() { -// elmPath = this._domPath(); -// currentTag = elmPath.split("|"); -// if (currentTag[1] && currentTag[1].toUpperCase() == "BLOCKQUOTE") { -// if (glow.env.webkit) { // Webkit doesn't follow the blockquote spec requiring

          elements -// this.contentWindow.document.execCommand("formatblock", false, "

          "); -// } -// else { -// this.contentWindow.document.execCommand("outdent", false, null); -// // Need to remove from IE generated code? -// // dir=ltr style="MARGIN-RIGHT: 0px" -// // style="MARGIN-RIGHT: 0px" dir=ltr -// } -// } -// else { -// if (glow.env.ie) { // IE incorrect applies a blockquote instead of an indent, which is good because it doesn't support formatblock(blockquote) -// this.contentWindow.document.execCommand("indent", false, null); -// } -// else if (glow.env.gecko <= 2) { // Oddly ff2 will accept almost any string and make it a new tag -// this.contentWindow.document.execCommand("formatblock", false, "blockquote"); -// } -// else { -// this.contentWindow.document.execCommand("formatblock", false, "

          "); -// } -// } -// } - - /* TODO: Use for later release... - glow.widgets.Editor.EditArea.prototype.formatblock_h1 = function() { - this.formatblock_heading("h1"); - } - glow.widgets.Editor.EditArea.prototype.formatblock_h2 = function() { - this.formatblock_heading("h2"); - } - glow.widgets.Editor.EditArea.prototype.formatblock_h3 = function() { - this.formatblock_heading("h3"); - } - glow.widgets.Editor.EditArea.prototype.formatblock_h4 = function() { - this.formatblock_heading("h4"); - } - glow.widgets.Editor.EditArea.prototype.formatblock_h5 = function() { - this.formatblock_heading("h5"); - } - glow.widgets.Editor.EditArea.prototype.formatblock_h6 = function() { - this.formatblock_heading("h6"); - } - glow.widgets.Editor.EditArea.prototype.formatblock_p = function() { - this.formatblock_heading("p"); - } - glow.widgets.Editor.EditArea.prototype.formatblock_heading = function(lvl) { - elmPath = this._domPath(); - currentTag = elmPath.split("|"); - if (currentTag[1].toUpperCase() == lvl.toUpperCase()) { - this.contentWindow.document.execCommand("formatblock", false, "

          "); - } - else { - this.contentWindow.document.execCommand("formatblock", false, "<"+lvl+">"); - } - } - glow.widgets.Editor.EditArea.prototype.toggleDesignMode = function() { - if (this.contentWindow.document.designMode.toLowerCase() == "on") { - if (glow.env.ie) { //IE resets the document, so... - cntWin = glow.dom.get(this.editor.editArea.contentWindow.document).get('body').html(); - this.contentWindow.document.designMode = "off"; - this.editor.editArea.contentWindow.document.write(cntWin); - } - else { - this.contentWindow.document.designMode = "off"; - } - } - else { - this.contentWindow.document.designMode = "on"; - } - } */ - - /** - @ignore - @private - @name glow.widgets.Editor.EditArea#_domPath - @description - @function - @param {String} name - @returns {glow.widgets.Editor.Toolbar.Tool} - */ - glow.widgets.Editor.EditArea.prototype._domPath = function(elm) { - elm = elm || this._getSelectedNode(); - var elmBody = glow.dom.get(this.editor.editArea.contentWindow.document).get('body').item(0); - var trail = ""; - - while (elm.nodeName.toUpperCase() != elmBody.nodeName.toUpperCase()) { - trail = '<' + elm.nodeName.toLowerCase() + ((elm.getAttribute('style'))? ' style="'+elm.getAttribute('style')+'"' : '') + '>' + trail; - elm = elm.parentNode; - } - - var cleanTrail = this.editor.cleaner.clean(trail); - cleanTrail = cleanTrail.replace(/>/g, "|").replace(/= location) { - return node.parentNode; - } - - } - if (node.childNodes) { - for (var i = 0; i < node.childNodes.length; i++) { - var foundNode = walkNode(node.childNodes[i], location); - if (foundNode) return foundNode; - } - } - } - - var foundNode = walkNode(node, location); - return foundNode; - } - - /** - @ignore - @private - @name glow.widgets.Editor.EditArea#_getContent - @description Get the entire contents of the editArea. This will be in the browser's native format. - */ - glow.widgets.Editor.EditArea.prototype._getContent = function() { /*debug*///console.log("glow.widgets.Editor.EditArea#_getContent()"); - return this.contentWindow.document.body.innerHTML; - } - - /** - @ignore - @private - @name glow.widgets.Editor.EditArea#_setContent - @description Set the entire contents of the editArea. This should be in the browser's native format. - */ - glow.widgets.Editor.EditArea.prototype._setContent = function(content) { - this.contentWindow.document.body.innerHTML = content; - } - - /** - @ignore - @private - @name glow.widgets.Editor.EditArea#_select - @description Select the entire contents of the editArea. Useful for unit testing. - */ - glow.widgets.Editor.EditArea.prototype._select = function() { - var el = this.contentWindow; - el.focus(); - - if (glow.env.ie) { // ie, but not opera (faker) - r = el.document.body.createTextRange(); - r.moveEnd('textedit'); - r.select(); - } - else { // moz, saf, opera - var r = el.document.createRange(); - r.selectNodeContents(el.document.body.firstChild.childNodes[0]); - - var s = el.getSelection(); - s.removeAllRanges(); - el.getSelection().addRange(r); - } - } - } -}); - -(window.gloader || glow).module({ - name: "glow.widgets.Timetable", - library: ["glow", "1.5.1"], - depends: [[ - "glow", "1.5.1", - 'glow.dom', - 'glow.events', - 'glow.widgets', - 'glow.widgets.Slider', - 'glow.dragdrop' - ]], - builder: function(glow) { - var $dom = glow.dom, - $ = $dom.get, - $create = $dom.create, - $events = glow.events, - $listen = $events.addListener, - $fire = $events.fire, - $lang = glow.lang, - $apply = $lang.apply, - idIndex = 0, - locale = { - ACCESSIBILITY_MENU_START : "Start", - ACCESSIBILITY_MENU_END : "End", - ACCESSIBILITY_INTRO : "Use this menu to choose what section of the timetable to view.", - SKIPLINK_TO_TRACK : "skip to track data", - SKIPLINK_BACK_TO_HEADERS : "back to track headers" - }, - vocabs = [ - { - length: "width", - breadth: "height", - rootClass: "glow151-Timetable", - dragAxis: "x", - pos: "left", - posOpposite: "right", - otherPos: "top", - otherPosOpposite: "bottom" - - }, - { - length: "height", - breadth: "width", - rootClass: "glow151-vTimetable", - dragAxis: "y", - pos: "top", - posOpposite: "bottom", - otherPos: "left", - otherPosOpposite: "right" - } - ]; - - function getId() { - return glow.UID + "TimetableWidget" + (idIndex++); - } - - /* - This returns either the horizontal or vertical vocab. - */ - function getVocab() { - return vocabs[!!this._opts.vertical * 1]; - } - - // Helper for banding iterations - function _adder(amount) { - return function (prev) { - if(prev instanceof Date) { - return new Date(prev.getTime() + amount); - } else { - return prev + amount; - } - }; - } - - // type is "hour", "day", "week", "month" a function (which will just be returned) or a number - // TODO - stop using this directly and roll it into _getSegments?? () - function _createIncrementer(type) { - switch(type) { - case "am/pm": - return _adder(43200000); // 12 * 60 * 60 * 1000 - case "hour": - return _adder(3600000); // 60 * 60 * 1000 - case "day": - return _adder(86400000); // 24 * 60 * 60 * 1000 - case "week": - return _adder(604800000); // 7 * 24 * 60 * 60 * 1000 - case "month": - return function(prev) - { - var d = new Date(prev); - d.setMonth(d.getMonth() + 1); - return d; - }; - case "year": - return function(prev) - { - var d = new Date(prev); - d.setFullYear(d.getFullYear() + 1); - return d; - }; - default: - if (type instanceof Function) { - return type; - } else if (isNaN(type)) { - throw new Error("Can't create incrementer"); - } else { - return _adder(parseInt(type)); - } - } - } - - function _getSegments(rule, first, start, stop) { - if(rule instanceof Array) { // use it directly - if (!this.numerical) { - // make sure all the values are dates - return glow.lang.map(rule, function(item) { - return new Date(item); - }) - } - return rule; - } - - var iSize, output, sizes, - now, - len = 1, - incrementer = _createIncrementer(rule); - - if(first == "auto") { - sizes = { - "am/pm" : 43200000, // 12 * 60 * 60 * 1000 - "hour" : 3600000, // 60 * 60 * 1000 - "day" : 86400000 // 24 * 60 * 60 * 1000 - }; - - switch (rule) { - case "am/pm": - case "hour": - case "day": - now = new Date(sizes[rule] * Math.floor(start.valueOf() / sizes[rule])); - break; - case "week": - now = new Date(start); - now.setHours(0, 0, 0, 0); - now.setDate(now.getDate() - now.getDay()); - break; - case "month": - now = new Date(start); - now.setHours(0, 0, 0, 0); - now.setDate(1); - break; - case "year": - now = new Date(start); - now.setHours(0, 0, 0, 0); - now.setMonth(0, 1); - break; - default: - now = start; - } - } else { - now = first || start; - } - - output = [now]; - - while(now < stop) - { - now = incrementer(now); - output[len++] = now; - } - - return output; - } - - // for building track header/footer, item, scale segment and scrollbar mark contents from a template - function _buildFromTemplate(template) { - var halfway, content; - - if (template == undefined) { - return null; - } - - if(template instanceof $dom.NodeList) { - halfway = template; - } else if(template instanceof Function) { - halfway = template(this); - } else { - halfway = $lang.interpolate("" + template, this); - } - - if(halfway instanceof $dom.NodeList) { - content = $dom.create("

          ").append(halfway); - } else { - content = $dom.create("
          " + halfway + "
          "); - } - - return content; - } - - /** - @name glow.widgets.Timetable - @class - @description A scrollable list of ordered items displayed against a proportional axis. - Note: you must call draw() on a timetable before it is displayed. - -
          Widgets must be called in a glow.onReady() call.
          - - @param {String|HTMLElement|glow.dom.NodeList} container The Element into which the Timetable will be placed. - If a String is given, it is used as a selector. If a Nodelist is given, the first item is used. - - The contents of the element will be replaced by the Timetable. Ideally, this element should contain - a static version of the timetable for users without JavaScript. - @param {Date | Number} start The start point of the entire Timetable. - The user will not be able to scroll before this point. - - Also controls the unit of the Timetable. If a Number is given, the Timetable is regarded as number based, otherwise it's regarded as Date based - @param {Date | Number} end The end point of the entire Timetable - The user will not be able to scroll beyond this point. - @param {Date | Number} viewStart The start point of the visible area. - This sets the start point of the view, and in conjunction with viewEnd sets the zoom level of the timetable - @param {Date | Number} viewEnd The end point of the visible portion. - In conjunction with viewStart sets the zoom level of the timetable - @param {Object} [opts] An optional options object - @param {String} [opts.theme="light"] Visual theme for the Timetable. - Possible values are "light" and "dark". Both themes can be altered with - CSS to fit the design of your site. - @param {String} [opts.id=auto-generated] An id for the Timetable. - You must set this (or className) if you're going to customise the design - of the timetable, to target it with CSS selectors. - @param {String} [opts.className] A class name for the Timetable's container element - You must set this (or id) if you're going to customise the design - of the timetable, to target it with CSS selectors. - @param {Boolean} [opts.vertical=true] Create a vertical Timetable? - @param {String | Number} [opts.size=container size] The width (if horizontal) or height (if vertical) of the scrollable area of the Timetable. - Note that headers and footers will add to the overall size. - @param {Array} [opts.tracks] An array of Tracks to create the Timetable with. - Each element in the Array is an Array of parameters that are passed to the addTrack method. - - This can be used to pass in timetable data as a single JSON object rather than using the addTrack and - addItem methods. - @param {Boolean} [opts.collapseItemBorders=true] Should item borders collapse into each other? - As in, take up the same space. - @param {Boolean} [opts.collapseTrackBorders=false] Should track borders collapse into each other? - As in, take up the same space. This must be false if you want your tracks to be separated by a margin. - @param {Boolean} [opts.keepItemContentInView=true] Should the content of an item that is partially in view be moved into view? - @param {String | glow.dom.NodeList | Function} [opts.itemTemplate] Template for each Item on a Track in the Timetable -

          A default template is used if this is not provided, which displays just the item title

          -
            -
          • If a String is provided, it is passed through glow.lang.interpolate, with the Item as the data parameter, and the output is used.
          • -
          • If a NodeList is provided it is used directly.
          • -
          • If a function is provided it should take the Item as its only argument, and return the HTML or a NodeList to use.
          • -
          - @param {String | glow.dom.NodeList | Function} [opts.trackHeader] Template for the header section the each Track in the Timetable - Defaults to no header. - See itemTemplate above for a description of how the different values are treated. - @param {String | glow.dom.NodeList | Function} [opts.trackFooter] Template for the footer section the each Track in the Timetable - Defaults to no footer. - See itemTemplate above for a description of how the different values are treated. - @param {Function} [opts.onChange] Event shortcut - @param {Function} [opts.onItemClick] Event shortcut - @param {Function} [opts.onMoveStart] Event shortcut - @param {Function} [opts.onMoveStop] Event shortcut - - @example - // using dates - var myTimetable = new glow.widgets.Timetable('#timetableContainer', - "31 December 2008 23:30", "1 January 2009 14:30", - "1 January 2009 00:30", "1 January 2009 05:30", - { - itemTemplate: "{title} # {id}", - trackHeader: "

          {title}

          ", - } - ) - - // using numbers - var myTimetable = new glow.widgets.Timetable('#timetableContainer', - 0, 100, - 5, 6, - { - itemTemplate: "{title} # {id}", - trackHeader: "

          {title}

          ", - } - ) - - @see
          Timetable user guide - @see Templating within a Timetable - @see Styling a Timetable - @see Loading Timetable Data From A Remote Source - - */ - /** - @name glow.widgets.Timetable#event:change - @event - @description Fires each time the Timetable view start point changes. - - This will fire after dragging, rather than during dragging. - - @param {glow.events.Event} event Event Object - */ - /** - @name glow.widgets.Timetable#event:itemClick - @event - @description Fires when the user clicks an item on the Timetable. - - The Event object will have an 'item' property. - - @param {glow.events.Event} event Event Object - */ - /** - @name glow.widgets.Timetable#event:moveStart - @event - @description Fires when the Timetable starts to move (by whatever UI method). - @param {glow.events.Event} event Event Object - */ - /** - @name glow.widgets.Timetable#event:moveStop - @event - @description Fires when the Timetable stops moving (by whatever UI method). - @param {glow.events.Event} event Event Object - */ - - function Timetable(container, start, end, viewStart, viewEnd, opts) { - - this._opts = opts = $apply({ - vertical: true, - tracks: [], - collapseItemBorders: true, - collapseTrackBorders: false, - keepItemContentInView: true, - className: "", - theme: "light" - }, opts || {}); - - var vocab = getVocab.call(this); - - this._container = $(container); - - if (!this._container[0]) { - throw new Error("Could not find container for Timetable"); - } - - /** - @name glow.widgets.Timetable#id - @type String - @description The Timetable's id - */ - this.id = opts.id || getId(); - - /** - @name glow.widgets.Timetable#size - @type Number - @description The width (if horizontal) or height (if vertical) of the Timetable's scrollable arae - */ - this.size = opts.size || this._container[vocab.length](); - - /** - @name glow.widgets.Timetable#numerical - @type Boolean - @description true if the Timetable is Number based, false if it is Date based - */ - this.numerical = ((typeof start) == "number"); - - /** - @name glow.widgets.Timetable#start - @type Date | Number - @description The start point of the whole Timetable. - The user will not be able to scroll before this point. - */ - this.start = start; - - /** - @name glow.widgets.Timetable#end - @type Date | Number - @description The end point of the whole Timetable - The user will not be able to scroll beyond this point. - */ - this.end = end; - - /** - @name glow.widgets.Timetable#_viewStart - @private - @type Date | Number - @description The start point of the visible portion - */ - this._viewStart = viewStart; - - /** - @name glow.widgets.Timetable#_viewEnd - @private - @type Date | Number - @description The end point of the visible portion - */ - this._viewEnd = viewEnd; - - if(!this.numerical) { - this.start = new Date(start); - this.end = new Date(end); - this._viewStart = new Date(viewStart); - this._viewEnd = new Date(viewEnd); - } - - this._viewWindowSize = this._viewEnd - this._viewStart; - - - - /** - @name glow.widgets.Timetable#tracks - @type glow.widgets.Timetable.Track[] - @description Array of all the Tracks in the Timetable (including disabled ones). - Ordered by the order they were added. - */ - this.tracks = []; - - for(var i = 0, l = opts.tracks.length; i < l; i++) { - this.addTrack.apply(this, opts.tracks[i]); - } - - if(opts.onChange) { - $listen(this, "change", opts.onChange); - } - if(opts.onItemClick) { - $listen(this, "itemClick", opts.onItemClick); - } - if(opts.onMoveStart) { - $listen(this, "moveStart", opts.onMoveStart); - } - if(opts.onMoveStop) { - $listen(this, "moveStop", opts.onMoveStop); - } - - /** - @name glow.widgets.Timetable#element - @type glow.dom.NodeList - @description The root element of the Timetable widget - */ - this.element; - - // create view - this._view = new View(this); - - /** - @name glow.widgets.Timetable#_banding - @private - @type Number[] | Date[] - @description The banding of the Timetable, as an array of banding interval boundary points. An empty array signifies no banding. - */ - this._banding = []; - - /** - @name glow.widgets.Timetable#_primaryScales - @private - @type Object[] - @description The top/left scales of the Timetable, as an array of objects, each containg an array of segment end points ("points" property), the size of the scale ("size" property) and a String/function/Nodelist template ("template" property). An empty array signifies no primary scale. - ie it will end up looking a bit like this [{template: "{start} to {end}", points: [0, 1, 2, 3]}, ...] - */ - this._primaryScales = []; - - /** - @name glow.widgets.Timetable#_secondaryScales - @private - @type Object[] - @description The bottom/right scales of the Timetable, just like _primaryScales. - */ - this._secondaryScales = []; - - /** - @name glow.widgets.Timetable#_primaryScrollbar - @private - @type Object - @description The top/left scrollbar of the Timetable, as an object containg an array of mark points ("points" property), the size of the scrollbar ("size" property) and a String/function/Nodelist template ("template" property). An null object signifies no primary scrollbar. - ie it will end up looking a bit like this [{template: "{start} to {end}", points: [0, 1, 2, 3]}, ...] - */ - this._primaryScrollbar = null; - - /** - @name glow.widgets.Timetable#_secondaryScrollbar - @private - @type Object[] - @description The bottom/right scrollbar of the Timetable, just like _primaryScrollbar. - */ - this._secondaryScrollbar = null; - } - - Timetable.prototype = { - /** - @name glow.widgets.Timetable#addTrack - @function - @description Factory method for creating a standard Track and adding it to the Timetable. - Tracks can only created by using this method. - - @param {String} title The title of the Track - @param {String | Number} size The height (if horizontal) or width (if vertical) of the Track (not including borders and margins) - @param {Object} [opts] Options object - @param {String} [opts.id=auto-generated] An id for the Track - @param {String} [opts.className] A class name for the Track's container element - @param {String | glow.dom.NodeList | Function} [opts.itemTemplate] Template for each Item on this Track. -

          Overrides any template specified at Timetable level.

          -
            -
          1. If a String is provided, it is passed through glow.lang.interpolate, with the Item as the other parameter, and the output is used.
          2. -
          3. If a NodeList is provided it is used directly.
          4. -
          5. If a function is provided it should take the Item as its only argument, and return the HTML or a NodeList to use.
          6. -
          - @param {String | glow.dom.NodeList | Function} [opts.trackHeader] Template for the header section the this Track. -

          Overrides any template specified at Timetable level.

          -

          See itemTemplate above for a description of how the different values are treated.

          - @param {String | glow.dom.NodeList | Function} [opts.trackFooter] Template for the footer section the this Track. -

          Overrides any template specified at Timetable level.

          -

          See itemTemplate above for a description of how the different values are treated.

          - @param {Object} [opts.data] An object of arbitrary data to be attached to the Track. - @param {Boolean} [opts.disabled=false] A disabled track is not rendered in the view - @param {Array} [opts.items] An array of Items to create the Track with - Each element in the Array is an Array of parameters that are passed to the addItem method of the Track - - @returns {glow.widgets.Timetable.Track} The Track that was created - - @example - // no items - var myTrack1 = myTimetable.addTrack("Track 1", 250); - - // add some items at the same time - var myTrack2 = myTimetable.addTrack("Track 2", 250, { - items : [ - ["Item 1", "2009/01/01 00:00", "2009/01/01 01:00"], - ["Item 2", "2009/01/01 01:00", "2009/01/01 01:30"], - ["Item 3", "2009/01/01 01:30", "2009/01/01 01:40"] - ] - }); - - // add additional items - myTrack2.addItem("Item 4", "2009/01/01 01:40", "2009/01/01 02:00"); - - @see Templating within a Timetable - - */ - addTrack : function(title, size, opts) { - return this.tracks[this.tracks.length] = new Track(this, title, size, opts); - }, - - /** - @name glow.widgets.Timetable#currentPosition - @function - @description Gets or sets the start point of the Timetable's visible portion - - @param {Date | Number} value The new start point to use - - @returns this - */ - currentPosition : function(value) { - if(value === undefined) { - var current = (this._view) ? this._view.currentPosition() : this._viewStart; - // convert it to a date if we have to - if (!this.numerical) { - current = new Date(current); - } - return current; - } else { - - if (!this.numerical) { - value = new Date(value); - } - - - - this._view.currentPosition(value); - - return this; - } - }, - - /** - @name glow.widgets.Timetable#viewRange - @function - @description Get or set the visible data range. - Changing this changes the scale of the timetable, you can use - this to 'zoom' the view in or out. - - @param {Object} [newRange] An object with 'start' and / or 'end' properties, in the same format as the viewStart and viewEnd constructor parameters. - If either the 'start' or 'end' property is omitted, the current value is taken. - - @returns this (if setting) or Object with start & end properties representing the view range - - @example - // function to zoom in a timetable - function zoomIn(timetable) { - // get the current range - var range = timetable.viewRange(); - // get the difference between the start and end values - var currentDiff = range.end - range.start; - // half the difference, this is a 2x zoom - var newDiff = currentDiff / 2; - - // set a new end value for the range - timetable.viewRange({ - end: range.start.valueOf() + newDiff - }) - } - */ - viewRange: function(newRange) { - // start by getting the current range - var duration = this._viewEnd - this._viewStart, - currentPos = this.currentPosition(), - range = { - start: currentPos, - end: currentPos.valueOf() + duration - }; - - if (!this.numerical) { - range.end = new Date( range.end ); - } - - if (newRange) { // setting - this._viewStart = newRange.start || range.start; - this._viewEnd = newRange.end || range.end; - - if (!this.numerical) { - this._viewStart = new Date( this._viewStart ); - this._viewEnd = new Date( this._viewEnd ); - } - // make sure the values aren't out of range - if (this._viewStart < this.start) { - this._viewStart = this.start; - } - if (this._viewEnd > this.end) { - this._viewEnd = this.end; - } - if (this._view && this._view._drawn) { - this.draw(true).currentPosition(this._viewStart); - } - return this; - } else { // getting - return range; - } - }, - - /** - @name glow.widgets.Timetable#setItemTemplate - @function - @description Sets the Default Item template for the Timetable - @param {String | glow.dom.NodeList | function} template The template to use - @returns this - - @see Templating within a Timetable - */ - setItemTemplate : function(template) { - this._opts.itemTemplate = template; - - return this; - }, - - /** - @name glow.widgets.Timetable#setTrackHeaderTemplate - @function - @description Sets the default Track header template for the Timetable - @param {String | glow.dom.NodeList | Function} template The template to use - @returns this - - @see Templating within a Timetable - */ - setTrackHeaderTemplate : function(template) { - this._opts.trackHeader = template; - return this; - }, - - /** - @name glow.widgets.Timetable#setTrackFooterTemplate - @function - @description Sets the default Track footer template for the Timetable - @param {String | glow.dom.NodeList | Function} template The template to use - @returns this - - @see Templating within a Timetable - */ - setTrackFooterTemplate : function(template) { - this._opts.trackFooter = template; - return this; - }, - - /** - @name glow.widgets.Timetable#setBanding - @function - @description Sets the background banding for the Timetable. - Note that banding won't appear until the Timetable has been {@link glow.widgets.Timetable#draw redrawn}. - @param {Number | String | function | Array} banding Defines the banding intervals -
            -
          • If a number is passed, banding intervals are defined that far apart. If the Timetable is date based, this should be a number of milliseconds.
          • -
          • If a String is passed, it should be one of "am/pm", "hour", "day", "week", "month" or "year", as shortcuts for common (not necessarily even) time intervals.
          • -
          • If a function is passed, it should take a point on the Timetable as its only argument and return the next point. It will be used to iteratively generate the banding interval points starting from the first.
          • -
          • If an Array is passed, its elements should be points on the Timetable, and these will be used directly as the banding interval end points. The start option below will have no effect.
          • -
          - @param {Object} [opts] Options object - @param {String | Date | Number} [opts.start=auto] The start point of the first banding interval. -

          If set to auto, an appropriate choice is made for the banding.

          -
            -
          • "am/pm" chooses 12am or 12pm.
          • -
          • "hour" gives the beginning of the hour
          • -
          • "day" gives midnight
          • -
          • "week" gives midnight on sunday
          • -
          • "month" gives midnight on the first
          • -
          • "year" gives midnight on January 1st
          • -
          -

          For any other banding definition, auto sets the start to the Timetable start point.

          -

          Has no effect if an Array is passed as the banding.

          - - @returns this - - @example - myTimetable.setBanding("hour"); - */ - setBanding : function(banding, opts) { - var options = opts || {}; - - this._banding = _getSegments.call(this, banding, options.start || "auto", this.start, this.end); - - return this; - }, - - /** - @name glow.widgets.Timetable#addScale - @function - @description Adds a scale to one or both sides of the Timetable. - Several scales can be attached to either side of the Timetable - eg hours and "am/pm". - - Scales will appear in the order in which they were added, working inwards. ie the last to be added will be closest to the Tracks. - - Note that scales won't appear until the Timetable has been {@link glow.widgets.Timetable#draw redrawn}. - - @param {Number | String | function | Array} segmentation Defines the size of the scale's segments -
            -
          • If a number is passed, scale segments are defined that size. If the Timetable is date based, this should be a number of milliseconds.
          • -
          • If a String is passed, it should be one of "am/pm", "hour", "day", "week", "month" or "year", as shortcuts for common (not necessarily even) time intervals.
          • -
          • If a function is passed, it should take a point on the Timetable as its only argument and return another point. It will be used to iteratively generate the scale segment end points starting from the first.
          • -
          • If an Array is passed, its elements should be points on the Timetable, and these will be used directly as the scale segment end points. The start option below will have no effect.
          • -
          - @param {String} [position] "top", "bottom", "left", "right" or "both" to determine where the scale should display. -
            -
          • "top" and "left" are synonymous, and positions the scale above a horizontal timetable or to the left of a vertical one.
          • -
          • "bottom" and "right" are synonymous, and positions the scale below a horizontal timetable or to the right of a vertical one.
          • -
          • "both" positions two identical scales, one in each position.
          • -
          • If more than one scale is added on the same side of the timetable, they will display in the order they were added on the top/left and in the reverse order on the bottom/right.
          • -
          - @param {String | Number} size The height (if horizontal) or width (if vertical) of the scale - @param {Object} [opts] Options object - @param {String} [opts.id] An id for the scale's container element. Do not use this if adding a scale to both sides, or an error will be thrown (use className instead). - @param {String} [opts.className] A class name for the scale's container element - @param {String | glow.dom.NodeList | Function} [opts.template] Specification for templating HTML of each section of the scale -

          The template uses an object giving the segments "start" and "end" points.

          -
            -
          • If a String is provided, it is passed through glow.lang.interpolate, with the above scale segment data object as the other parameter, and the output is used.
          • -
          • If a NodeList is provided it is used directly.
          • -
          • If a function is provided it should take the above scale segment data object as its argument, and return a String or a NodeList to use.
          • -
          • If the final output of the template is a String, it need not contain any HTML (eg just a time of day, like "8am" is OK).
          • -
          • If no template is supplied the scale segments will be empty.
          • -
          - @param {String | Date | Number} [opts.start=auto] The start point of the first scale segments. -

          If set to auto, an appropriate choice is made for the start point.

          -
            -
          • "am/pm" chooses 12am or 12pm
          • -
          • "hour" gives the beginning of the hour
          • -
          • "day" gives midnight
          • -
          • "week" gives midnight on sunday
          • -
          • "month" gives midnight on the first
          • -
          • "year" gives midnight on January 1st
          • -
          -

          For any other segmentation definition, auto sets the start to the Timetable start point.

          -

          Has no effect if an Array is passed as the interval.

          - - @returns this - - @example - myTimetable.addScale("hour", "left", 50, { - template: function (segment) { - return segment.start.getHours(); - } - }); - - @see Templating within a Timetable - - */ - addScale : function(segmentation, position, size, opts) { - var options = opts || {}, - spec = { - template: options.template, - size: size, - points: _getSegments.call(this, segmentation, options.start || "auto", this.start, this.end), - opts: options - }; - - position = position.toLowerCase(); - - if ((position == "both") && options.id) { - throw new Error("Cannot apply an id when adding to both sides of the timetable"); - } - - if((position == "top") || (position == "left") || (position == "both")) { - this._primaryScales[this._primaryScales.length] = spec; - } - - if((position == "bottom") || (position == "right") || (position == "both")) { - this._secondaryScales[this._secondaryScales.length] = spec; - } - - return this; - }, - - /** - @name glow.widgets.Timetable#removeScales - @function - @description Removes all the scales from one or both sides of the Timetable. - Note that scales won't disappear until the Timetable has been {@link glow.widgets.Timetable#draw redrawn}. - - @param {String} [position] "top", "bottom", "left", "right" or "both" to determine which scales to remove. -
            -
          • "top" and "left" are synonymous, and removes the scales above a horizontal timetable or to the left of a vertical one.
          • -
          • "bottom" and "right" are synonymous, and removes the scales below a horizontal timetable or to the right of a vertical one.
          • -
          • "both" all the scales.
          • -
          - - */ - removeScales: function(position) { - if((position == "top") || (position == "left") || (position == "both")) { - this._primaryScales = []; - } - - if((position == "bottom") || (position == "right") || (position == "both")) { - this._secondaryScales = []; - } - - return this; - }, - - /** - @name glow.widgets.Timetable#addScrollbar - @function - @description Adds a scrollbar to one or both sides of the Timetable - Only one scrollbar can be attached to either side of the Timetable, if more than one are added, the last one is used. - - Note that scrollbars won't appear until the Timetable has been {@link glow.widgets.Timetable#draw redrawn}. - @param {Number | String | function | Array} marks Defines the scrollbar's mark points -
            -
          • If a number is passed, mark points are defined that far apart. If the Timetable is date based, this should be a number of milliseconds.
          • -
          • If a String is passed, it should be one of "am/pm", "hour", "day", "week", "month" or "year", as shortcuts for common (not necessarily even) time intervals.
          • -
          • If a function is passed, it should take a point on the Timetable as its only argument and return the next point. It will be used to iteratively generate the mark points starting from the first.
          • -
          • If an Array is passed, its elements should be points on the Timetable, and these will be used directly as the mark points. The start option below will have no effect.
          • -
          - @param {String} [position] "top", "bottom", "left", "right" or "both" to determine where the scale should display -
            -
          • "top" and "left" are synonymous, and positions the scrollbar above a horizontal timetable or to the left of a vertical one.
          • -
          • "bottom" and "right" are synonymous, and positions the scrollbar below a horizontal timetable or to the right of a vertical one.
          • -
          • "both" positions two identical scrollbar, one in each position.
          • -
          - @param {String | Number} size The height (if horizontal) or width (if vertical) of the scrollbar - @param {Object} [opts] Options object - @param {String} [opts.id] An id for the scrollbar container element. Do not use this if adding a scrollbar to both sides, or an errro will be thrown. - @param {String} [opts.className] A class name for the scrollbar's container element - @param {String | glow.dom.NodeList | Function} [opts.template] Specification for templating HTML of each mark point -

          The template uses an object giving the mark's "start" and "end" points.

          -
            -
          • If a String is provided, it is passed through glow.lang.interpolate, with the above data object as the other parameter, and the output is used.
          • -
          • If a NodeList is provided it is used directly.
          • -
          • If a function is provided it should take the above data object as its argument, and return a String or a NodeList to use.
          • -
          • If the final output of the template is a String, it need not contain any HTML (eg just a time of day, like "8am" is OK).
          • -
          • If no template is supplied the scale segments will be empty.
          • -
          - @param {String | Date | Number} [opts.start=auto] The first mark point. -

          If set to auto, an appropriate choice is made for the start point.

          -
            -
          • "am/pm" chooses 12am or 12pm
          • -
          • "hour" gives the beginning of the hour
          • -
          • "day" gives midnight
          • -
          • "week" gives midnight on sunday
          • -
          • "month" gives midnight on the first
          • -
          • "year" gives midnight on January 1st
          • -
          -

          Has no effect if an Array is passed as the marks.

          - - @returns this - - @example - myTimetable.addScrollbar("hour", "left", 50, { - template: function (segment) { - return segment.start.getHours(); - } - }); - - @see Templating within a Timetable - - */ - addScrollbar : function(marks, position, size, opts) { - var options = $apply({ - buttons: true - }, opts || {}), - spec = { - template: options.template, - size: size, - points: _getSegments.call(this, marks, options.start || "auto", this.start, this.end), - opts: options - }; - - position = position.toLowerCase(); - - if ((position == "both") && options.id) { - throw new Error("Cannot apply an id when adding to both sides of the timetable"); - } - - if((position == "top") || (position == "left") || (position == "both")) { - this._primaryScrollbar = spec; - } - - if((position == "bottom") || (position == "right") || (position == "both")) { - this._secondaryScrollbar = spec; - } - - return this; - }, - - /** - @name glow.widgets.Timetable#draw - @function - @description Update the view with any changes you've made. - You need to call this function after adding new tracks or items - to make them visible on the timetable. - - @param {Boolean} [redraw=false] Redraw all items? - Usually, draw will just draw items & tracks that have been added since - last calling draw. Use this option to force the timetable to - completely redraw. - - @returns this - */ - draw: function(redraw) { - this._view.draw(redraw); - return this; - } - }; - - - /** - @name glow.widgets.Timetable.Track - @class - @description A Track is a grouping of Timetable items. - - @glowPrivateConstructor A Track cannot be directly instantiated. Instead the {@link glow.widgets.Timetable#addTrack addTrack} method of glow.widgets.Timetable must be used. - */ - function Track(timetable, title, size, opts) { - this._opts = opts = $apply({ - className: "" - }, opts || {}); - - /** - @name glow.widgets.Timetable.Track#disabled - @type Boolean - @description Should the Track be removed from the view? - If set to true, this Track will not be rendered in the Timetable view the next time it is {@link glow.widgets.Timetable#draw drawn/redrawn} - */ - this.disabled = opts.disabled || false; - - /** - @name glow.widgets.Timetable.Track#data - @type Object - @description The track's arbitrary data store - */ - this.data = opts.data || {}; - - /** - @name glow.widgets.Timetable.Track#title - @type String - @description The Track's title - */ - this.title = title; - - /** - @name glow.widgets.Timetable.Track#size - @type Number - @description The Track's size - */ - this.size = size; - - /** - @name glow.widgets.Timetable.Track#timetable - @type glow.widgets.Timetable - @description The Track's parent Timetable - */ - this.timetable = timetable; - - /** - @name glow.widgets.Timetable.Track#id - @type String - @description The Track's id - */ - this.id = opts.id || getId(); - - /** - @name glow.widgets.Timetable.Track#items - @type Item[] - @description an array of the Track's Items in order of their start time - */ - this.items = []; - - if(opts.items != undefined) { - for(var i = 0, l = opts.items.length; i < l; i++) { - _factoryItem.apply(this, opts.items[i]); - } - - _sortItems.call(this); - } - } - - // create an Item and add it to the Track - function _factoryItem (title, start, end, options) { - return this.items[this.items.length] = new Item(this, title, start, end, options); - } - - function _itemSortOrder(a, b) { - return ((a.start - b.start) || (a._addIndex - b._addIndex)); // _addIndexes will *never* be equal so this function will never return 0, meaning the sort is always stable. - } - - // factorised out because it may become more complex in the future - function _sortItems() { - this.items.sort(_itemSortOrder); - } - - // This is the core function for finding Items on a track given a point or segment of the Timetable. All the public query methods delegate to this - // TODO - investigate quicker (eg binary?) search to find first item quickly. May need an extra sort index. - function _queryItems(start, end, testFunc) { - if (((typeof start) == "number") !== this.timetable.numerical) - { - throw new Error("Cannot get Item(s) - point(s) not in the correct scale type."); - } - - var items = this.items, - results = {items: [], indices: []}, - num = 0; - - if(!this.timetable.numerical) { - start = new Date(start); - end = new Date(end); - } - - for(var i = 0, l = items.length; i < l; i++) { - if(items[i].start > end) { - break; - } - if (testFunc.call(items[i], start, end)) { - results.items[num] = items[i]; - results.indices[num] = i; - num++; - } - } - - return results; - } - - // helper for use in _queryItems calls - function _rangeContainsItem(start, end) { - return ((this.start >= start) && (this.end <= end)); - } - - // helper for use in _queryItems calls and Item.inRange calls - function _itemOverlapsRange(start, end) { - return ((this.start < end) && (this.end > start)); - } - - // helper for use in _queryItems calls - function _itemAtPoint(point) { - return ((this.start <= point) && (this.end > point)); - } - - Track.prototype = { - toString : function() {return this.title;}, - - /** - @name glow.widgets.Timetable.Track#addItem - @function - @description Factory method for creating an Item and adding it to the Track - - @param {String} title The title - @param {Date | Number} start The start point - @param {Date | Number} end The end point - @param {Object} [opts] Options object - @param {String} [opts.id=auto-generated] An id for the Item - @param {String} [opts.className] A class name for the Item's container element - @param {String | glow.dom.NodeList | Function} [opts.itemTemplate] Specification for templating HTML of the this Item -

          Overrides any template specified at Track or Timetable level.

          -
            -
          • If a String is provided, it is passed through glow.lang.interpolate, with the Item as the other parameter, and the output is used.
          • -
          • If a NodeList is provided it is used directly.
          • -
          • If a function is provided it should take the Item as its only argument, and return the HTML or a NodeList to use.
          • -
          - @param {Object} [opts.data] An object of arbitrary data to be attached to the Item - - @returns {glow.widgets.Item} - The created Item. - - @example - // on a numeric Timetable (1 - 100) - var myItem = myTrack.addItem("Some Item", 10, 14); - */ - addItem : function(title, start, end, options) { - var item = _factoryItem.call(this, title, start, end, options); - _sortItems.call(this); - - return item; - }, - - /** - @name glow.widgets.Timetable.Track#itemAt - @function - @description Returns the Item that is on the Track at the given point - - @param {Date | Number} point The point on the Track to inspect - @returns {Item} - - @example - var anItem = myTrack.itemAt("31 December 1999 23:59"); - // anItem now holds a reference to a glow.Timetable.Item - */ - itemAt : function(point) { - return _queryItems.call(this, point, point, _itemAtPoint).items[0]; - }, - - /** - @name glow.widgets.Timetable.Track#indexAt - @function - @description Finds the Item that is on the Track at the given point, and returns it's index in the items[] property - - @param {Date | Number} point The point on the Track to inspect - @returns {Number} - - @example - var nowIndex = myTrack.indexAt(new Date()); - var now = myTrack.items[nowIndex]; - var next = myTrack.items[nowIndex + 1]; - */ - indexAt : function(point) { - return _queryItems.call(this, point, point, _itemAtPoint).indices[0]; - }, - - /** - @name glow.widgets.Timetable.Track#itemsAt - @function - @description Returns an array of any Items that are on the Track at the given point - Items appear in the array in "choronological" order - @param {Date | Number} point The point on the Track to inspect - @returns {Item[]} - - @example - var someItems = myTrack.itemsAt("31 December 1999 23:59"); - // someItems now holds a reference to an Array of glow.Timetable.Items - */ - itemsAt : function(point) { - return _queryItems.call(this, point, point, _itemAtPoint).items; - }, - - /** - @name glow.widgets.Timetable.Track#indicesAt - @function - @description Finds any Items that are on the Track at the given point, and returns an array of their indices in the items[] property - Items appear in the array in "choronological" order - @param {Date | Number} point The point on the Track to inspect - @returns {Number[]} - - @example - var someIndexes = myTrack.indicesAt("31 December 1999 23:59"); - // someIndexes now holds a reference to an Array of integers - */ - indicesAt : function(point) { - return _queryItems.call(this, point, point, _itemAtPoint).indices; - }, - - /** - @name glow.widgets.Timetable.Track#itemsInRange - @function - @description Returns an array of any Items that are on the Track overlapping the given range - Items appear in the array in "choronological" order - @param {Date | Number} start The start point of the portion of the Track to inspect - @param {Date | Number} end The end point of the portion of the Track to inspect - @returns {Item[]} - - @example - var someItems = myTrack.itemsInRange("31 December 1999 23:59", "1 January 2000 00:01"); - // someItems now holds a reference to an Array of glow.Timetable.Items - */ - itemsInRange : function(start, end) { - return _queryItems.call(this, start, end, _itemOverlapsRange).items; - }, - - /** - @name glow.widgets.Timetable.Track#indicesInRange - @function - @description Finds any Items that are on the Track overlapping the given range, and returns an array of their indices in the items[] property - Items appear in the array in "choronological" order - @param {Date | Number} start The start point of the portion of the Track to inspect - @param {Date | Number} end The end point of the portion of the Track to inspect - @returns {Number[]} - - @example - var someIndexes = myTrack.indicesInRange("31 December 1999 23:59", "1 January 2000 00:01"); - // someIndexes now holds a reference to an Array of integers - */ - indicesInRange : function(start, end) { - return _queryItems.call(this, start, end, _itemOverlapsRange).indices; - }, - - /** - @name glow.widgets.Timetable.Track#setItemTemplate - @function - @description Sets the Default Item template for this track - @param {String | glow.dom.NodeList | function} template The template to use - @returns this - - @see Templating within a Timetable - */ - setItemTemplate : function(template) { - this._opts.itemTemplate = template; - - return this; - }, - - /** - @name glow.widgets.Timetable.Track#setTrackHeaderTemplate - @function - @description Sets the header template for this track - @param {String | glow.dom.NodeList | function} template The template to use - @returns this - - @see Templating within a Timetable - */ - setTrackHeaderTemplate : function(template) { - this._opts.trackHeader = template; - - return this; - }, - - /** - @name glow.widgets.Timetable.Track#setTrackFooterTemplate - @function - @description Sets the footer template for this track - @param {String | glow.dom.NodeList | function} template The template to use - @returns this - - @see Templating within a Timetable - */ - setTrackFooterTemplate : function(template) { - this._opts.trackFooter = template; - - return this; - }, - - /** - @name glow.widgets.Timetable.Item#getHeader - @function - @description Gets the actual content of if the Track header (if any) for the Timetable to display - @returns glow.dom.NodeList - */ - getHeader : function() { - return _buildFromTemplate.call(this, this._opts.trackHeader || this.timetable._opts.trackHeader); - }, - - /** - @name glow.widgets.Timetable.Item#getFooter - @function - @description Gets the actual content of if the Track footer (if any) for the Timetable to display - @returns glow.dom.NodeList - */ - getFooter : function() { - return _buildFromTemplate.call(this, this._opts.trackFooter || this.timetable._opts.trackFooter); - } - }; - - /** - @name glow.widgets.Timetable.Item - @class - @description An Item is an "event" on a Track. - - @glowPrivateConstructor An Item cannot be directly instantiated. Instead the {@link glow.widgets.Timetable.Track#addItem addItem} method of glow.widgets.Timetable.Track must be used. - */ - function Item(track, title, start, end, opts) { - /** - @name glow.widgets.Timetable.Item#_addIndex - @private - @type Number - @description An integer that increments by one for each Item added to the track, so that each Item has a different and constant _addIndex. This is then used in Item sorting to force stability on the sort algorithm in JS engines (like V8) that use an unstable sort. - */ - this._addIndex = track.items.length; - - this._opts = opts = $apply({ - className: "" - }, opts || {}); - - if (((typeof start) == "number") !== track.timetable.numerical) - { - throw new Error("Item scale type does not match Timetable."); - } - - /** - @name glow.widgets.Timetable.Item#data - @type Object - @description The Item's arbitrary data store - This can be used to attach any arbitrary data to the Item, without the possibility of conflicting with the Item's designed properties. - @example - myItem.data.moreInfoUrl = "someUrl"; // used in the Item templare to provide a link to a "more info" page. - */ - this.data = opts.data || {}; - - /** - @name glow.widgets.Timetable.Item#title - @type String - @description The Item's title - */ - this.title = title; - - /** - @name glow.widgets.Timetable.Item#start - @type Date | Number - @description The Item's start point - */ - this.start = start; - - /** - @name glow.widgets.Timetable.Item#end - @type Date | Number - @description The Item's end point - */ - this.end = end; - - if(!track.timetable.numerical) { - this.start = new Date(start); - this.end = new Date(end); - } - - /** - @name glow.widgets.Timetable.Item#track - @type glow.widgets.Timetable.Track - @description The Item's parent Track - */ - this.track = track; - - /** - @name glow.widgets.Timetable.Item#id - @type String - @description The Item's id - */ - this.id = opts.id || getId(); - - /** - @name glow.widgets.Timetable.Item#element - @type glow.dom.Nodelist - @description The HTML element that represents the item in the Timetable. - */ - this.element; - } - - Item.prototype = { - toString : function() {return this.title;}, - - /** - @name glow.widgets.Timetable.Item#setItemTemplate - @function - @description Sets the Default Item template for the Timetable - @param {String | glow.dom.NodeList | function} template The template to use - @returns this - - @see Templating within a Timetable - */ - setItemTemplate : function(template) { - this._opts.itemTemplate = template; - - return this; - }, - - /** - @name glow.widgets.Timetable.Item#getContent - @function - @description Gets the actual content of the Item for the Timetable to display - @returns glow.dom.NodeList - */ - getContent : function() { - return _buildFromTemplate.call(this, this._opts.itemTemplate || this.track._opts.itemTemplate || this.track.timetable._opts.itemTemplate); - }, - - - - /** - @name glow.widgets.Timetable.Item#inRange - @function - @description Returns true if the Item overlaps the range with the given start and end points - @returns Boolean - - @example - var item = myTrack.addItem("Item", 1, 2); - - if (item.inRange(0, 1.5)) { - // code here runs - } - - if (item.inRange(3, 4)) { - // code here doesn't run - } - */ - inRange : function (start, end) { - - if(!this.track.timetable.numerical) { - start = new Date(start); - end = new Date(end); - } - - return _itemOverlapsRange.call(this, start, end); - - } - }; - - glow.widgets.Timetable = Timetable; - glow.widgets.Timetable.Track = Track; - glow.widgets.Timetable.Item = Item; - - /** - @name glow.widgets.Timetable.View - @private - @class - @description Deals with the rendering of the timetable - - @param {glow.widgets.Timetable} timetable The timetable instance to render - */ - var View; - - (function() { - // TODO test keyboard navigation of items (can they be focused and clicked?) - var containerTemplate = '' + - '
          ' + - '
          ' + - '
          ' + - '
          ' + - '
          ' + locale.ACCESSIBILITY_INTRO + '
          ' + - '' + - '
          ' + - '
          ' + - '
          ' + - '
          ' + - '
          ' + - '
          ' + - '
          ' + - '
          ' + - '
          ' + - '
          ' + - '' + - '
          ' + - '
          ' + - '
          ' + - '
          ', - headerHolderTemplate = '' + - '
          ', - footerHolderTemplate = '' + - '', - trackTemplate = '' + - '
            ', - itemHolderTemplate = '' + - '
          1. ', - scaleTemplate = '' + - '
            ', - scaleItemTemplate = '' + - '
            ', - emptyDiv = $dom.create('
            '), - // the maximum number of pixels (in any direction) the mouse can move between mousedown and click to count as 'clicking' rather than dragging - mouseMoveTolerance = 10; - - // called when the mouse moves during a timetable drag - function _mouseMoveDuringTimetableDrag(event) { - if (!this._clickStart) { - // this is the first run of this, capture the mouse position - this._clickStart = [event.pageX, event.pageY]; - } - // cancel the click if the mouse has moved outside the tolerance range - else if ( - !this._cancelNextItemClick && // no point recalculating if we've already canceled - (Math.abs(this._clickStart[0] - event.pageX) > mouseMoveTolerance || - Math.abs(this._clickStart[1] - event.pageY) > mouseMoveTolerance) - ) { - this._cancelNextItemClick = true; - } - - // call _moveToPosition with the correct value - _moveToPosition.call( this, this.currentPosition() ); - } - - // here we work out if we should react to the click or not - function _dragAreaClicked(event) { - if (this._cancelNextItemClick) return false; - // TODO work out which item, if any, has been clicked and fire itemClicked event - - var itemElm = $(event.source); - - // bubble up until we find the item the user clicked on - while (itemElm[0] != event.attachedTo) { - if ( itemElm.hasClass("timetable-item") ) { - // found it! - $fire(this._timetable, "itemClick", $apply({item: this.itemInstance[ itemElm[0].id ]}, new $events.Event(event))); - } - itemElm = itemElm.parent(); - } - } - - // called before the timetable begins to move - function _moveStart() { - // TODO does anything need to be included in this event? - // TODO make cancelable - $fire(this._timetable, "moveStart"); - } - - // updates the position of elements for a given val - function _moveToPosition(val) { - var timetable = this._timetable, - vocab = getVocab.call(timetable); - - // update any interface element that need changing - this._dragAreaElm.css( vocab.pos, -(_valToPos.call(this, val)) ); - // TODO add slider position updates here - if (this._scrollbar1) { - this._scrollbar1.moveToPosition(val); - } - if (this._scrollbar2) { - this._scrollbar2.moveToPosition(val); - } - - } - - // called when a moving action ends that triggered a _moveStart (ie, not pragmatic change) - function _moveStop() { - $fire(this._timetable, "moveStop"); - } - - // called when the timetable has completed a move (eg, dragging is complete or position is changed programmatically) - function _moved() { - // move elements that are half in view - _adjustContentPosition.call(this); - // fire the change event if we should - _fireChangeEvent.call(this); - } - - // move content of items into view, and reset others - function _adjustContentPosition() { - - var timetable = this._timetable, - vocab = getVocab.call(timetable), - i = 0, - len = timetable.tracks.length, - item, - itemToAdjust, - itemContentToAdjust, - itemId, - currentPos = timetable.currentPosition(), - itemPixelOffset, - newMargin, - posPixelOffset = parseInt( this._dragAreaElm[0].style[vocab.pos] ); - - // reset position of other items - if (this._timetable._opts.keepItemContentInView) { - this._itemContentHangingOffStart.css("margin-" + vocab.pos, 0); - this._itemsHangingOffStart.removeClass("timetable-itemHangingClipping"); - } - this._itemsHangingOffStart.removeClass("timetable-itemHangingOffStart"); - - // start collecting new items to move - this._itemContentHangingOffStart = new $dom.NodeList(); - this._itemsHangingOffStart = new $dom.NodeList(); - - for (; i < len; i++) { - // get hold of the item element and its inner content - item = timetable.tracks[i].itemAt(currentPos); - // no item found? Also, skip items that start at the current time - if ( !item || item.start.valueOf() == currentPos.valueOf() ) { - continue; - } - itemId = item.id; - itemContentToAdjust = this.itemContent[ itemId ]; - itemToAdjust = this.items[ itemId ]; - - this._itemContentHangingOffStart.push(itemContentToAdjust); - this._itemsHangingOffStart.push(itemToAdjust); - - if (this._timetable._opts.keepItemContentInView) { - // find out what the pixel offset of the item is - itemPixelOffset = parseInt( itemToAdjust[0].style[vocab.pos] ); - newMargin = -posPixelOffset - itemPixelOffset; - itemContentToAdjust.css("margin-" + vocab.pos, - newMargin - ); - // is the content clipping against the bottom of the item box? - if (itemToAdjust[vocab.length]() < (itemContentToAdjust[vocab.length]() + newMargin)) { - itemToAdjust.addClass("timetable-itemHangingClipping"); - } - } - } - this._itemsHangingOffStart.addClass("timetable-itemHangingOffStart"); - } - - // get pixel value for a timetable unit - function _valToPos(val) { - return (val - this._timetable.start) / this.scale; - } - - // get a value for a pixel position - function _posToVal(pos) { - return (pos * this.scale) + this._timetable.start.valueOf(); - } - - // only fire change event if the value has changed - function _fireChangeEvent() { - var timetable = this._timetable, - newPos = timetable.currentPosition(); - - if(newPos.valueOf() != this._posBeforeMove.valueOf()) { - $fire(timetable, "change"); - this._posBeforeMove = newPos; - _updateHiddenNavSelect.call(this); - - } - } - - // create an item element from Item instance - function _createItem(item, border) { - // generate container - var timetable = this._timetable, - vocab = getVocab.call(timetable), - itemPos = _valToPos.call(this, item.start), - itemLength = _valToPos.call(this, item.end) - itemPos, - itemElm = $dom.create(itemHolderTemplate), - itemContent = item.getContent() || _itemDefaultContent(item); - - // add class name & id - itemElm.attr("id", item.id); - itemContent[0].className = "timetable-itemContent " + item._opts.className; - - var self = this; - - item.element = this.items[item.id] = itemElm; - this.itemContent[item.id] = itemContent; - this.itemInstance[item.id] = item; - - // we need to deduct borders from the item length. We deduct 2 borders if we're not collapsing. One otherwise. - itemLength -= border * ((!timetable._opts.collapseItemBorders) + 1); - // size and position - - itemElm.css(vocab.pos, itemPos) - .css(vocab.length, itemLength); - - // add content - itemElm.append(itemContent); - // return container - return itemElm; - } - - function _itemDefaultContent(item) { - return $create("
            " + item.title + "
            "); - } - - function _drawTrack(track) { - var timetable = this._timetable, - vocab = getVocab.call(timetable), - items = track.items, - i = 0, - len = items.length, - trackElm = this.tracks[track.id], - header = this._headers[track.id], - - footer = this._footers[track.id], - - trackListElm, - itemBorder, - itemElmTmp, - item; - - // generate html for the track if we haven't already - if (!trackElm) { - trackElm = this.tracks[track.id] = _createTrack.call(this, track); - // size the track - trackElm.css(vocab.breadth, track.size); - trackElm.appendTo( this._dragAreaElm ); - // draw headers and footers for this track (if needed) - _drawHeaderAndFooter.call(this, track); - if(header) { - trackElm.prepend(header.clone().removeClass("timetable-header-holder").addClass("timetable-accessibility-hidden")); - } - if(footer) { - trackElm.append(footer.clone().removeClass("timetable-footer-holder").addClass("timetable-accessibility-hidden")); - } - } - - - - trackListElm = trackElm.get("> ol"); - - // calculate an item border for this track - itemElmTmp = $dom.create(itemHolderTemplate).appendTo(trackListElm); - itemBorder = parseInt(itemElmTmp.css(["border-" + vocab.pos + "-width", "border-" + vocab.posOpposite + "-width"])) / 2; - itemElmTmp.remove(); - - // loop over items - for (; i < len; i++) { - item = track.items[i]; - // if item isn't already drawn - if (!this.items[item.id]) { - // append item to track and save in register - _createItem.call(this, items[i], itemBorder).appendTo(trackListElm); - } - } - } - - function _drawHeaderAndFooter(track) { - var content, - id = track.id; - - content = track.getHeader(); - - // do we have content? - if (content) { - // get and add the content - this._headers[id] = $dom.create(headerHolderTemplate).append( content.addClass("timetable-header-content") ); - this._headerElm.append(this._headers[id]) - .append('' + locale.SKIPLINK_TO_TRACK + ''); - } - - content = track.getFooter(); - - if (content) { - this._footers[id] = $dom.create(footerHolderTemplate).append( content.addClass("timetable-footer-content") ); - this._footerElm.append(this._footers[id]) - .append('' + locale.SKIPLINK_BACK_TO_HEADERS + ''); - } - } - - function _createTrack(track) { - var r = $dom.create(trackTemplate).attr("id", track.id); - - // TODO add class name & id - return r; - } - - function _positionTracks() { - var timetable = this._timetable, - vocab = getVocab.call(timetable), - i = 0, - tracksLen = timetable.tracks.length, - primaryScalesLen = this._primaryScaleElms.length, - secondaryScalesLen = this._secondaryScaleElms.length, - lenTotal = tracksLen + primaryScalesLen + secondaryScalesLen, - trackElm, - headerElm, - footerElm, - headerMaxLength = 0, - footerMaxLength = 0, - - headerFooterOffset, - headerFooterBreadth, - breadthBorders = ["border-" + vocab.otherPos + "-width", "border-" + vocab.otherPosOpposite + "-width"], - posSum = 0, - collapseBorders = timetable._opts.collapseTrackBorders, - border, - margin, - size, - id, - trackObj; - - - headerFooterOffset = this._scrollbar1Elm[vocab.breadth]() - parseInt( this._headerElm.css("border-" + vocab.otherPos + "-width") ); - - - for (; i < lenTotal; i++) { - // figure out what we're looping through - if (i < primaryScalesLen) { - trackElm = this._primaryScaleElms[i]; - headerElm = footerElm = null; - } else if (i < primaryScalesLen + tracksLen) { - trackObj = timetable.tracks[i-primaryScalesLen]; - id = trackObj.id; - trackElm = this.tracks[id]; - headerElm = this._headers[id]; - footerElm = this._footers[id]; - if (trackObj.disabled) { - // ensure the elements are hidden - $(trackElm, headerElm, footerElm).css("display", "none"); - continue; - } else { - // ensure the elements are shown - $(trackElm, headerElm, footerElm).css("display", ""); - } - } else { - trackElm = this._secondaryScaleElms[i-primaryScalesLen-tracksLen]; - headerElm = footerElm = null; - } - - border = parseInt(trackElm.css(breadthBorders)) / 2; - // || 0 is to catch IE return 'auto' for margin - margin = collapseBorders ? 0 : parseInt(trackElm.css("margin-" + vocab.otherPosOpposite)) || 0; - size = parseInt(trackElm.css(vocab.breadth)) + (border * ((!collapseBorders) + 1)) + margin; - // set breadth and position - trackElm.css(vocab.otherPos, posSum); - if(headerElm) { - headerElm.css(vocab.otherPos, posSum + headerFooterOffset).css(vocab.breadth, trackObj.size + 2 * border); - headerMaxLength = Math.max( - parseInt( headerElm.css(vocab.length) ), - headerMaxLength - ); - - } - - if(footerElm) { - footerElm.css(vocab.otherPos, posSum + headerFooterOffset).css(vocab.breadth, trackObj.size + 2 * border); - footerMaxLength = Math.max( - parseInt( footerElm.css(vocab.length) ), - footerMaxLength - ); - - } - - // deduct border if we're collapsing borders. We deduct 2 borders if we're not collapsing. One otherwise. - posSum += size; - } - - - // set this.element to be width / height of the total, ignoring the final margin - this._innerViewElm.css(vocab.breadth, posSum + (border * collapseBorders) - margin); - // set the width / height of header and footer containers - headerFooterBreadth = posSum + (border * collapseBorders) - margin + headerFooterOffset + this._scrollbar2Elm[vocab.breadth](); - - $(this._headerElm, this._footerElm).css(vocab.breadth, - headerFooterBreadth - - parseInt( this._headerElm.css("border-" + vocab.otherPosOpposite + "-width") ) - ); - - // set container sizes - this._headerElm.css(vocab.length, headerMaxLength); - this._footerElm.css(vocab.length, footerMaxLength); - } - - function _drawBanding() { - var timetable = this._timetable, - vocab = getVocab.call(timetable), - i = 0, - len = timetable._banding.length - 1, - bandStart, - bandStartPos, - bandEnd, - bandEndPos, - band; - - // loop through the banding points - for (; i < len; i++) { - bandStart = timetable._banding[i].valueOf(); - bandEnd = timetable._banding[i+1].valueOf(); - - // get pixel values for band positions - bandStartPos = _valToPos.call(this, bandStart); - bandEndPos = _valToPos.call(this, bandEnd) - bandStartPos; - - // create our band - band = emptyDiv.clone() - // set its starting position - .css(vocab.pos, bandStartPos) - // set its length - .css(vocab.length, bandEndPos) - // add class name - .addClass( "timetable-band" + (i%2 ? 'Odd' : 'Even') ) - // add to document - .appendTo( this._dragAreaElm ); - - } - } - - function _createScale(scaleData) { - var timetable = this._timetable, - vocab = getVocab.call(timetable), - scaleElm = $create(scaleTemplate).css(vocab.breadth, scaleData.size), - i = 0, - points = scaleData.points, - len = points.length - 1, - itemStart, - itemStartPos, - itemEnd, - itemLength, - itemContext; - - scaleElm[0].id = scaleData.opts.id || ""; - scaleElm[0].className += " " + (scaleData.opts.className || ""); - - // loop though points - for (; i < len; i++) { - itemStart = points[i].valueOf(); - itemEnd = points[i+1].valueOf(); - - // get pixel positions - itemStartPos = _valToPos.call(this, itemStart); - itemLength = _valToPos.call(this, itemEnd) - itemStartPos; - - // create template data object - itemContext = { - start: points[i], - end: points[i+1] - } - - // create our item - $create(scaleItemTemplate) - .append( _buildFromTemplate.call(itemContext, scaleData.template).addClass('timetable-itemContent') ) - .css(vocab.pos, itemStartPos) - .css(vocab.length, itemLength) - .appendTo(scaleElm); - } - - return scaleElm; - } - - function _drawScales() { - var timetable = this._timetable, - i = timetable._primaryScales.length, - largestIndex; - - this._primaryScaleElms = []; - this._secondaryScaleElms = []; - - // beginning scales - while (i--) { - this._primaryScaleElms[i] = _createScale.call(this, timetable._primaryScales[i]).addClass("timetable-scalePrimary").appendTo(this._dragAreaElm); - } - // end scales - i = timetable._secondaryScales.length; - largestIndex = i - 1; - while (i--) { - // add them to the elms array in reverse - this._secondaryScaleElms[largestIndex - i] = _createScale.call(this, timetable._secondaryScales[i]).addClass("timetable-scaleSecondary").appendTo(this._dragAreaElm); - } - } - - // add a css rule to the page - function _setStyle(selector, style) { - $create('').appendTo("head"); - } - - function _drawScrollbars() { - // TODO: make these optional and based on API - var timetable = this._timetable, - primary = timetable._primaryScrollbar, - secondary = timetable._secondaryScrollbar; - - // we need to show / hide the scrollbar elm because IE threw its toys out of the pram - if (primary) { - this._scrollbar1Elm.css("display", "block"); - this._scrollbar1 = new TimetableScrollbar(this, this._scrollbar1Elm, primary); - } - this._scrollbar1Elm.css("display", primary ? "block" : ""); - if (secondary) { - this._scrollbar2Elm.css("display", "block"); - this._scrollbar2 = new TimetableScrollbar(this, this._scrollbar2Elm, secondary); - } - this._scrollbar2Elm.css("display", secondary ? "block" : ""); - } - - function _setDraggableSizes() { - var timetable = this._timetable, - vocab = getVocab.call(timetable), - dragAreaSize, - dragRangeSize; - - // size draggable & drag range - dragAreaSize = _valToPos.call(this, timetable.end); - dragRangeSize = (dragAreaSize * 2) - this._viewSize; - // vocab.length is width / height - this._dragAreaElm[vocab.length](dragAreaSize); - this._dragRangeElm[vocab.length](dragRangeSize).css("margin-" + vocab.pos, - dragAreaSize + this._viewSize); - } - - function _calculateScale() { - var timetable = this._timetable, - vocab = getVocab.call(timetable); - - // vocab.length is width / height - this._viewSize = this._innerViewElm[vocab.length](); - /** - @name glow.widgets.Timetable.View#scale - @private - @type Number - @description The units per pixel scale of the Timetable - */ - this.scale = (timetable._viewEnd - timetable._viewStart) / this._viewSize; - } - - function _initDraggable() { - var timetable = this._timetable, - vocab = getVocab.call(timetable), - that = this; - - this._draggable = new glow.dragdrop.Draggable(this._dragAreaElm, { - axis: vocab.dragAxis, - container: this._dragRangeElm, - placeholder: "none", - onDrag: function() { - // we need to distinguish between a drag and a click - that._cancelNextItemClick = false; - that._clickStart = 0; - // listen for mouse moving - that._mouseMoveListener = $listen(document, "mousemove", _mouseMoveDuringTimetableDrag, that); - // TODO, make this cancelable - _moveStart.call(that); - _clearItemHiding.call(that); - - }, - onDrop: function() { - _moveStop.call(that); - _moved.call(that); - // remove mouse move listener - that._mouseMoveListener && glow.events.removeListener(that._mouseMoveListener); - } - }); - } - - function _hideOutOfView () { - - var timetable = this._timetable, - tracks = timetable.tracks, - numTracks = tracks.length, - inCurrentView = this._inCurrentView, - innerViewElm = this._innerViewElm, - newView = null, - viewRange = timetable.viewRange(), - viewStart = viewRange.start, - viewEnd = viewRange.end, - id = "", - i = 0, - l = 0; - - if(inCurrentView == null) { - innerViewElm.addClass("timetable-hideitems"); - this._inCurrentView = inCurrentView = {}; - } - - for(id in inCurrentView) { - if(!inCurrentView[id].inRange(viewStart, viewEnd)) { - delete inCurrentView[id]; - $(id).css("display", ""); - } - } - - for(j = 0; j < numTracks; j++) { - - newView = tracks[j].itemsInRange(viewStart, viewEnd) - - for(i = 0, l = newView.length; i < l; i++) { - id = newView[i].id; - - if(!inCurrentView[id]) { - inCurrentView[id] = newView[i]; - $("#" + id).css("display", "block"); - } - } - } - } - - function _clearItemHiding() { - for(id in this._inCurrentView) { - $("#" + id).css("display", ""); - } - this._inCurrentView = null; - this._innerViewElm.removeClass("timetable-hideitems"); - } - - - - function _createHiddenNavSelect() { - var timetable = this._timetable, - spec = timetable._primaryScales[0] || timetable._secondaryScales[0] || timetable._primaryScrollbar || timetable._secondaryScrollbar; - - if(spec) { - var points = spec.points, - entries = [], - len = points.length - 1, - itemContext, - i = 0, - that = this, - lastViewStart = timetable.end - timetable._viewWindowSize, - startOption = '', - endOption = ''; - - for(; i < len; i++) { - itemContext = { - start: points[i], - end: points[i + 1] - } - if ((itemContext.start >= timetable.start) && (itemContext.start <= lastViewStart)) { - entries[i] = ''; - if (itemContext.start.valueOf() == timetable.start.valueOf()) { - startOption = ''; - } - if (itemContext.start.valueOf() == lastViewStart.valueOf()) { - endOption = ''; - } - } - } - var select = this._accessibiltySelect = $dom.create(''); - - $listen(select, "change", function() { - that._timetable.currentPosition(select.val() * 1); - _hideOutOfView.call(that); - }); - this._accessibiltyElm.append(select); - _updateHiddenNavSelect.call(this); - } - } - - - - function _updateHiddenNavSelect() { - - if(this._accessibiltySelect) { - var currentPos = this.currentPosition(), - selectOptions = this._accessibiltySelect[0].options, - i = 0, - len = selectOptions.length, - val = selectOptions[i].value * 1, - tmp; - - for (; i < len; i++) { - tmp = selectOptions[i].value * 1; - if(tmp <= (currentPos + this.scale)) val = tmp; // add scale to allow for rounding errors - } - this._accessibiltySelect.val(val); - } - } - - - - View = function (timetable) { - var vocab = getVocab.call(timetable), - that = this; - - // storage for determining event firing - this._cancelNextItemClick = false; - this._posBeforeMove = timetable.currentPosition(); - - this._timetable = timetable; - - this._headers = {}; - this._footers = {}; - - this._inCurrentView = null; - - - - /** - @name glow.widgets.Timetable.View#tracks - @private - @type Object - @description A NodeList of a rendered track, indexed by ID - */ - this.tracks = {}; - /** - @name glow.widgets.Timetable.View#items - @private - @type Object - @description A NodeList of a rendered item, indexed by ID - */ - this.items = {}; - /** - @name glow.widgets.Timetable.View#itemContent - @private - @type Object - @description A NodeList of a rendered item's content, indexed by ID - */ - this.itemContent = {}; - /** - @name glow.widgets.Timetable.View#itemInstance - @private - @type Object - @description Instance of an item, indexed by ID - */ - // TODO: should this be outside the view, perhaps a getItemById function on timetable? - this.itemInstance = {}; - - /** - @name glow.widgets.Timetable.View#element - @private - @type glow.dom.NodeList - @description Outer element - */ - // init html interface and apply and class names we need - this.element = $dom.create(containerTemplate).attr("id", timetable.id); - this.element[0].className = timetable._opts.className; - - this.element.addClass(vocab.rootClass); - - - // get hold of the elements we need - this._headerElm = this.element.get("div.timetable-track-headers"); - - this._footerElm = this.element.get("div.timetable-track-footers"); - - this._accessibiltyElm = this.element.get("div.timetable-accessibility-navigation"); - - this._stateElm = this.element.get("div.timetable-state"); - this._themeElm = this.element.get("div.timetable-theme"); - this._innerViewElm = this.element.get("div.timetable-innerView"); - this._dragRangeElm = this.element.get("div.timetable-dragRange"); - this._dragAreaElm = this.element.get("div.timetable-dragArea"); - this._scrollbar1Elm = this.element.get("div.timetable-scrollbar1"); - this._scrollbar2Elm = this.element.get("div.timetable-scrollbar2"); - - // apply theme class - this._themeElm.addClass( - "timetable-" + timetable._opts.theme - ); - - // set a private var for elements that are half in view at the start of the view - // set and used by _adjustContentPosition - this._itemsHangingOffStart = new $dom.NodeList(); - this._itemContentHangingOffStart = new $dom.NodeList(); - - // listen for clicks within the drag area - $listen(this._dragAreaElm, "click", _dragAreaClicked, this); - - }; - - View.prototype = { - /** - @name glow.widgets.Timetable.View#draw - @private - @function - @description Draw the timetable's tracks and items - - @param {Boolean} [redraw=false] Redraw items that have already been drawn - - @returns this - */ - draw: function(redraw) { - var timetable = this._timetable, - vocab = getVocab.call(timetable), - calculatedViewSize = timetable.size, - tracks = timetable.tracks, - len = tracks.length, - startPosition, - i = 0; - - if (!this._drawn) { // first draw - // empty the container, add element to page - this.element.appendTo( timetable._container.empty() ); - // init draggable - _initDraggable.call(this); - } - - if (redraw) { - // capture the current position - startPosition = timetable.currentPosition(); - // if redraw is true, trash our 'drawn' registry and empty our view - this.tracks = {}; - this.items = {}; - this.itemContent = {}; - this.itemInstance = {}; - // clear all items - this._dragAreaElm.empty(); - // destroy scrollbars - this._scrollbar1Elm.empty(); - this._scrollbar2Elm.empty(); - // get rid of headers and footers - this._headerElm.empty(); - this._footerElm.empty(); - // empty hidden a11y nav - this._accessibiltyElm.empty(); - this._headers = {}; - this._footers = {}; - } - - if (redraw || !this._drawn) { - this._innerViewElm[vocab.length]( calculatedViewSize ); - _calculateScale.call(this); - _drawBanding.call(this); - _drawScrollbars.call(this); - _setDraggableSizes.call(this); - _drawScales.call(this); - _createHiddenNavSelect.call(this); - // set start position - _moveToPosition.call(this, startPosition || timetable._viewStart); - - } - - // loop over tracks - for (i = 0; i < len; i++) { - _drawTrack.call(this, tracks[i]); - } - - // set up the width of the tracks (and scales) - _positionTracks.call(this); - _adjustContentPosition.call(this); - - this._drawn = true; - - return this; - }, - /** - @name glow.widgets.Timetable.View#currentPosition - @private - @function - @description Get or sets the - - @param {Number | Date} [val] Value to move to. Omit to get the current value - - @returns Number for getting, 'this' when setting. - */ - currentPosition: function(val) { - var vocab = getVocab.call(this._timetable); - - if (val === undefined) { // getting - return _posToVal.call( this, -parseInt(this._dragAreaElm[0].style[vocab.pos]) ); - } else { // setting - _clearItemHiding.call(this); - - _moveToPosition.call(this, val); - _moved.call(this); - return this; - } - }, - - - - hide : function () { - - _hideOutOfView.call(this); - - }, - - clear : function () { - - _clearItemHiding.call(this); - - } - }; - - var TimetableScrollbar; - - (function() { - var scrollbarNum = 0; - - function _scrollbarChange() { - if (this._ignoreChange) { - return; - } - // update UI - _moveToPosition.call( this._timetable._view, (this._timetable._opts.vertical ? -1 : 1) * this.slider.val() ); - - if (!this._isDraggingChange) { - _moved.call(this._timetable._view); - } - } - - // called when a dragging action start - function _scrollbarMoveStart() { - _clearItemHiding.call(this._timetable._view) - // we use this to tell if a change is a dragging change or set programmatically - this._isDraggingChange = true; - _moveStart.call(this._timetable._view); - } - - // called when a dragging action stops - function _scrollbarMoveStop() { - this._isDraggingChange = false; - _moveStop.call(this._timetable._view); - _moved.call(this._timetable._view); - } - - function _positionHighlight() { - var timetable = this._timetable, - vocab = getVocab.call(timetable), - pos = parseInt( this._sliderHandle[0].style[vocab.pos] ); - - if (this._timetable._opts.vertical) { - this._labelsHighlight[0].style.clip = 'rect(' + pos + 'px, auto, ' + (pos + this._handleLength) + 'px, auto)'; - } else { - this._labelsHighlight[0].style.clip = 'rect(auto, ' + (pos + this._handleLength) + 'px, auto, ' + pos + 'px)'; - } - } - - // TODO, document this properly - TimetableScrollbar = function(view, container, scaleData) { - var timetable = view._timetable, - vocab = getVocab.call(timetable), - i = 0, - points = scaleData.points, - len = points.length - 1, - itemStart, - itemStartPos, - itemEnd, - itemLength, - itemContext, - labels = $create('
            '), - id = glow.UID + "scrollbar" + (scrollbarNum++), - sliderTrack, - viewSize = timetable._viewEnd - timetable._viewStart, - timetableSize = timetable.end - timetable.start, - min, - max, - val, - viewStart = timetable.viewRange().start; - - this._timetable = timetable; - - // set the size of the handle - _setStyle( - "#" + id + " .slider-handle", - vocab.length + ":" + (viewSize/timetableSize)*100 + "%" - ); - - if (timetable._opts.vertical) { - min = -timetable.end + viewSize; - max = -timetable.start; - val = -viewStart; - } else { - min = timetable.start-0; - max = timetable.end - viewSize; - val = viewStart; - } - - // we use negative values for min & max for vertical sliders so the 'min' value is at the top (we flip it round later) - this.slider = new glow.widgets.Slider(container, { - // we use negative min & maxv values if we're vertical, so the larger abs value is at the bottom - min: min, - max: max, - vertical: timetable._opts.vertical, - className: "timetable-scrollbar", - id: id, - val: val, - size: view._innerViewElm[vocab.length](), - step: 0, - changeOnDrag: true - }); - - sliderTrack = this.slider.element.get("div.slider-track"); - // having to set this for IE6, thanks IE. - if (timetable._opts.vertical) { - sliderTrack.css(vocab.length, sliderTrack.get("div.slider-trackOn").css(vocab.length)); - } - - this.slider.element.get("div.slider-btn-bk, div.slider-btn-fwd").push(sliderTrack).css(vocab.breadth, scaleData.size); - // work out the scale to use - this.scale = (timetableSize) / sliderTrack[vocab.length](); - - - // loop though points top create labels - for (; i < len; i++) { - itemStart = points[i].valueOf(); - itemEnd = points[i+1].valueOf(); - - - // get pixel positions - itemStartPos = _valToPos.call(this, itemStart); - itemLength = _valToPos.call(this, itemEnd) - itemStartPos; - - - // create template data object - itemContext = { - start: points[i], - end: points[i+1] - } - - - // create our item - $dom.create('
            ') - .append( _buildFromTemplate.call(itemContext, scaleData.template).addClass('timetable-itemContent') ) - .css(vocab.pos, itemStartPos) - .css(vocab.length, itemLength) - .appendTo(labels); - } - - // create highlighted labels - this._labelsHighlight = labels.clone().addClass("timetable-scrollbarLabelsHighlight"); - - // listen for events - $listen(this.slider, "change", _scrollbarChange, this); - $listen(this.slider, "slideStart", _scrollbarMoveStart, this); - $listen(this.slider, "slideStop", _scrollbarMoveStop, this); - - sliderTrack.prepend(labels).prepend(this._labelsHighlight); - this._sliderHandle = this.slider.element.get("div.slider-handle"); - this._handleLength = this._sliderHandle[vocab.length](); - - _positionHighlight.call(this); - }; - - TimetableScrollbar.prototype = { - // update the interface of the scrollbar for a particular value - moveToPosition: function(val) { - this._ignoreChange = true; - this.slider.val( (this._timetable._opts.vertical ? -1 : 1) * val ); - this._ignoreChange = false; - _positionHighlight.call(this); - } - }; - })(); - })(); - } -}); -/*@end @*/ diff --git a/horde/js/glow/1.5.1/widgets/widgets.js b/horde/js/glow/1.5.1/widgets/widgets.js deleted file mode 100644 index 4da15736e..000000000 --- a/horde/js/glow/1.5.1/widgets/widgets.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - Copyright 2009 British Broadcasting Corporation - - 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. -*/ -/*@cc_on @*/ -/*@if (@_jscript_version > 5.5)@*/ -(window.gloader||glow).module({name:"glow.widgets",library:["glow","1.5.1"],depends:[["glow","1.5.1","glow.dom","glow.events"]],builder:function(d){var c,b,a=d.env;d.ready(function(){c=document;b=c.body;d._addReadyBlock("glow_widgetsCSS");var e=d.dom.create('').appendTo(b);if(e.height()!=20){b.className+=" glow151-basic";}else{(function(){if(e.css("z-index")!="1234"){setTimeout(arguments.callee,10);return;}d._removeReadyBlock("glow_widgetsCSS");if(e.css("background-image").indexOf("ctr.png")==-1){b.className+=" glow151-basic";}})();}a.ie&&(b.className+=" glow151-ie");(a.ie<7||!a.standardsMode)&&(b.className+=" glow151-ielt7");a.gecko&&(b.className+=" glow151-gecko");});d.widgets={_scrollPos:function(){var f=window,e=a.standardsMode?c.documentElement:b;return{x:e.scrollLeft||f.pageXOffset||0,y:e.scrollTop||f.pageYOffset||0};}};}});(window.gloader||glow).module({name:"glow.widgets.Mask",library:["glow","1.5.1"],depends:[["glow","1.5.1","glow.dom","glow.events","glow.widgets"]],builder:function(h){var e=h.dom,f=e.get,k=h.events,g=h.widgets,c,a='
            ';function d(o){this.opts=h.lang.apply({color:"#000",opacity:0.7,zIndex:9900,disableScroll:false},o||{});var m=document.body,l=this.maskElement=e.create(a+"z-index:"+this.opts.zIndex+";background:"+this.opts.color+';visibility:hidden">
            ').appendTo(m),n=this;l.css("opacity",this.opts.opacity);if(h.env.ie<7){this._iframe=e.create(i).css("z-index",this.opts.zIndex-1).appendTo(m);}k.addListener(l,"click",function(){k.fire(n,"click");});if(this.opts.onClick){k.addListener(this,"click",o.onClick);}}d.prototype={add:function(){var v=f(document),o=f(document.body),p=f(window),q=this;if(this.opts.disableScroll&&!b){b=h.dom.create(a+'height:100%;overflow:hidden;">'+a+'">
          2. ');var t=g._scrollPos(),r=o[0].style,n=p.height(),u=p.width(),m=b.get("div"),l=o.children().filter(function(){return(" "+this.className+" ").indexOf("glowNoMask")==-1;});c={margin:[o.css("margin-top"),o.css("margin-right"),o.css("margin-bottom"),o.css("margin-left")],padding:[o.css("padding-top"),o.css("padding-right"),o.css("padding-bottom"),o.css("padding-left")],height:o.css("height")};r.margin=r.padding=0;r.height="100%";m[0].style.zIndex=this.opts.zIndex-1;b.appendTo(o);m.css("margin",c.margin.join(" ")).css("padding",c.padding.join(" ")).css("top",-t.y-parseFloat(c.margin[0])+"px").css("left",-t.x+"px").append(l);}function s(){var y=o.height();for(var w=0;w<2;w++){q.maskElement.css("width","100%").css("height",(q.opts.disableScroll?b.height():Math.max(y,p.height()))+"px");}if(h.env.ie<7){var x=q.maskElement[0].style;q._iframe.css("width",x.width).css("height",x.height);}}this.maskElement.css("visibility","visible").css("display","block");if(h.env.ie<7){this._iframe.css("display","block");}s();this._resizeListener=k.addListener(window,"resize",s);},remove:function(){this.maskElement.css("visibility","hidden").css("display","none");if(h.env.ie<7){this._iframe.css("display","none");}k.removeListener(this._resizeListener);if(this.opts.disableScroll){var l=f(document.body),m=b.children();m.children().appendTo(l);window.scroll(-parseInt(m.css("left")),-parseInt(m.css("top")));b.remove();l.css("margin",c.margin.join(" ")).css("padding",c.padding.join(" ")).css("height",c.height);delete b;b=undefined;}}};h.widgets.Mask=d;}});(window.gloader||glow).module({name:"glow.widgets.Overlay",library:["glow","1.5.1"],depends:[["glow","1.5.1","glow.dom","glow.events","glow.anim","glow.widgets","glow.widgets.Mask"]],builder:function(f){var p=f.dom,d=p.get,a=f.events,n=f.widgets,r=f.env,o=f.anim,m=f.tweens,e='
            ',v=0,u='',k=/.swf($|\?)/i,b=/]*(?:name=["'?]\bwmode["'?][\s\/>]|\bvalue=["'?](?:opaque|transparent)["'?][\s\/>])[^>]*){2}/i,g=(!r.ie&&!(r.webkit<522))||(r.ie>6&&r.standardsMode);function h(y){if(y._hiddenElements[0]){return;}var x=new f.dom.NodeList(),w=y.opts.hideWhileShown,B=y.opts.hideFilter,z=0,A;if(y.opts.hideWindowedFlash){x.push(d("object, embed").filter(function(){return c.call(this,y);}));}if(w){x.push(d(w));}if(B){x=x.filter(B);}y._hiddenElements=x;for(var z=0,A=x.length;zA){if(!w.x){z.css("left",n._scrollPos().x+"px").css("position","absolute");w.x=true;}else{if(this.opts.modal&&d(document).width()C){if(!w.y){z.css("top",n._scrollPos().y+"px").css("position","absolute");w.y=true;}else{if(this.opts.modal&&d(document).height()End of panel. Close Panel
            ');if(m.env.ie){m.ready(function(){var r=function(y){var x=0,v=y.length,w;for(;x";for(var s in u){q=q+p(s,"tr","ctr")+p(s,"tl","ctl")+p(s,"bl","cbl")+p(s,"br","cbr")+p(s,"infoPanel-pointerT","at")+p(s,"infoPanel-pointerR","ar")+p(s,"infoPanel-pointerB","ab")+p(s,"infoPanel-pointerL","al");}q=q+"";m.dom.get("head").append(m.dom.create(q));});}function d(u){var s=u?"
            ":"";for(var q=1,p=arguments.length,t=[];q'+s+"";}return t.join("");}function i(){this.hide();return false;}b=function(){var q=[],p=0;q[p++]='
            ';q[p++]='
            ';q[p++]=d(false,"infoPanel-pointerT","infoPanel-pointerL","infoPanel-pointerR");q[p++]='
            ';q[p++]=d(false,"tr","tl");q[p++]=d(true,"tb");q[p++]='
            ';q[p++]=d(false,"bars");q[p++]='
            ';q[p++]='X';q[p++]=d(false,"panel-hd","panel-bd","panel-ft");q[p++]="
            ";q[p++]="
            ";q[p++]=d(false,"br","bl");q[p++]=d(true,"bb");q[p++]="
            ";q[p++]=d(false,"infoPanel-pointerB");q[p++]="
            ";q[p++]="
            ";return q.join("");}();function l(x,p){x=g(x);p=p||{};if(typeof p.width=="number"){p.width+="px";}if(p.template){var q=true;}p=m.lang.apply({template:b,width:"400px",modal:true,theme:"dark",ariaRole:"dialog",focusOnShow:true},p);var z=f.create(p.template),v=x.get("> .hd"),s=x.get("> .ft"),u=document.body,w=this,y,t,r=e.clone();if(!q){z.addClass("panel-"+p.theme);if(!n[p.theme]&&u.className.indexOf("glow151-basic")==-1){y=z.clone().addClass("glow151-panel-preload").appendTo(u);n[p.theme]=true;}}if(x.length>1){x.each(function(){var A=g(this);if(A.hasClass("hd")){v=A;}else{if(A.hasClass("ft")){s=A;}}});}this.header=z.get(".panel-hd");this.footer=z.get(".panel-ft");this.body=z.get(".panel-bd");if(x.isWithin(u)){z.insertBefore(x);}else{z.appendTo(u);}this.body.append(x);if(v.length){this.header.append(v);}else{if(!q){z.addClass("panel-noHeader");}}if(s.length){this.footer.append(s);}o.addListener(z.get(".panel-close"),"click",i,this);o.addListener(r.get("a"),"click",i,this);a.call(this,z,p);this.container.css("width",p.width).append(r);}c.extend(l,a);m.widgets.Panel=l;}});(window.gloader||glow).module({name:"glow.widgets.Sortable",library:["glow","1.5.1"],depends:[["glow","1.5.1","glow.dom","glow.events","glow.dragdrop","glow.widgets"]],builder:function(i){var f=i.dom.get,k=i.events,b=k.fire,a=k.addListener;function c(n){var o=n[0].offsetParent,m=parseInt(n.css("margin-top"))||0;if(i.env.ie){while(o.currentStyle.position=="static"){o=o.offsetParent;}}return n.offset().top-f(o).offset().top-m;}var d=function(n,m){this._opts=m=i.lang.apply({dropIndicatorClass:"glow-sortable-dropindicator",equaliseColumns:true,draggableOptions:{}},m||{});this.constrainDragTo=m.constrainDragTo;this.axis=m.axis;this.draggables=[];var n=this.containers=f(n),o=this.dropTargets=[];if(m.onSort){a(this,"sort",m.onSort);}n.each(function(p){o[p]=new i.dragdrop.DropTarget(this,{tolerance:"intersect",dropIndicator:"spacer",dropIndicatorClass:m.dropIndicatorClass});});this.addItems(n.children());};function e(){if(this._itemsInMotion){return false;}if(this._opts.equaliseColumns){g.call(this);}this._itemsInMotion=true;}function g(){var p=[],q=0,n,r=this.dropTargets;this.containers.each(function(s){var t=f(this);p[s]=c(t);n=p[s]+t[0].offsetHeight;if(i.env.khtml){n-=t.css("margin-top")+t.css("margin-bottom");}if(n>q){q=n;}});for(var o=0,m=this.dropTargets.length;o
            ';function q(){return false;}function d(){var H=0,G=arguments.length,I,F;while(!(I=arguments[H++])&&(H'),N=o.dom.create('
            '),I=o.dom.create('
            '),K=Number(G.labels),J=d(G.tickMajor,G.tickMinor,K),U,R,S=G.reverse,W=G.max-G.min,P,T,Q,L,O=G.min;this.element=P=o.dom.create('');T=P.get("div.ruler-labels");P[0].id=G.id||"";P[0].className+=" "+(G.className||"");for(;O<=G.max;O+=J){U=((O-G.min)/W)*100;U=S?100-U:U;if(G.tickMajor&&!((O-G.min)%G.tickMajor)){V.clone().css(M.pos,U+"%").appendTo(P);}else{if(G.tickMinor&&!((O-G.min)%G.tickMinor)){N.clone().css(M.pos,U+"%").appendTo(P);}}if(K&&!((O-G.min)%K)){Q=I.clone().css(M.pos,U+"%");Q[0]._labelVal=O;Q.get("span").html(G.labelMapper?G.labelMapper(O):O);T.append(Q);}}if(!K){for(L in G.labels){U=((Number(L)-G.min)/W)*100;U=S?100-U:U;if(U<=100){Q=I.clone().css(M.pos,U+"%");Q[0]._labelVal=Number(L);Q.get("span").html(G.labelMapper?G.labelMapper(G.labels[L]):G.labels[L]);T.append(Q);}}}H.append(P);};})();function v(F){return m[!!F._opts.vertical*1];}function l(F){var G=v(F);F[G.trackToChange][0].style[G.length]=parseInt(F._handleElm[0].style[G.pos])+(F._handleSize/2)+"px";}function D(){l(this);if(this._opts.changeOnDrag){var F=a(this);t(this,F);(this._boundInput[0]||{}).value=F;}}function f(F){var G=v(this);that=this;if(y=="prevented"){return false;}else{if(y!=F.key){if(!y&&C(this).defaultPrevented()){y="prevented";return false;}clearInterval(e);e=setTimeout(function(){e=setInterval(function(){x(that,F.key==G.keyIncrement?1:-1);},40);},500);x(that,F.key==G.keyIncrement?1:-1);y=F.key;}}return false;}function b(F){if(y==F.key){y=null;clearInterval(e);s(this);}}function n(H){if(!this._disabled&&!C(this).defaultPrevented()){var F=H.attachedTo.className.indexOf("-fwd")!=-1?1:-1,G=this;x(this,F);A=setTimeout(function(){A=setInterval(function(){x(G,F);},40);},500);}return false;}function w(F){if(A){clearTimeout(A);clearInterval(A);A=null;s(this);}return false;}function x(G,F){var H=(G._opts.step||(1/G._pixelsPerVal))*F;G._nudgeVal=i(G,G._nudgeVal+H);r(G,G._nudgeVal);if(G._opts.changeOnDrag){t(G,G._nudgeVal);(G._boundInput[0]||{}).value=G._val;}}function r(F,I){var H,G=v(F),I=I===undefined?F._val:I;H=F._opts.vertical?(F._opts.max-I)*F._pixelsPerVal:(I-F._opts.min)*F._pixelsPerVal;F._handleElm[0].style[G.pos]=H+"px";l(F);}function a(H){var I=v(H),G=parseInt(H._handleElm[0].style[I.pos]),F=H._opts.vertical?(H._trackSize-H._handleSize)-G:G;F=(F/H._pixelsPerVal)+H._opts.min;return i(H,F);}function i(I,J){var H=I._opts.step,G=I._opts.min,F=I._opts.max;J=Number(J)||0;if(JF){return F-((F-G)%(H||1));}if(H==0){return J;}return Math.round((J-G)/H)*H+G;}function t(H,G){var F=H._val,G=(G===undefined)?a(H):G;H.element.attr("aria-valuenow",G);H._val=G;if(G!=F){c.fire(H,"change");}}function C(F){F._valBeforeSlide=F._nudgeVal=F._val;return c.fire(F,"slideStart");}function s(G){var F={initialVal:G._valBeforeSlide,currentVal:a(G)};if(c.fire(G,"slideStop",F).defaultPrevented()){t(G,G._valBeforeSlide);G.val(G._valBeforeSlide);return;}t(G,F.currentVal);if(G._opts.snapOnDrop){G.val(F.currentVal);}else{(G._boundInput[0]||{}).value=F.currentVal;}}function h(I,Q){var F=I._opts,J=v(I),K=I.element,R,O,G,M,P,H,L=d((F.step*F.snapOnDrag),F.tickMinor,F.tickMajor),N;if(F.vertical){R=K.height();I._trackOnElm.height(F.size-R);}else{K.width(F.size);}I._trackSize=I._trackElm[J.length]();N=I._handleElm[0].style[J.length];if(o.env.ie<8){I._handleElm[0].style[J.length]=I._handleElm[0].currentStyle[J.length];I._handleElm[0].style[J.length]=I._handleElm[0].style["pixel"+J.lengthUpper];}I._handleSize=I._handleElm[0]["offset"+J.lengthUpper];I._handleElm[0].style[J.length]=N;if(F.val!=undefined){G=F.val;}else{if(I._boundInput[0]&&I._boundInput[0].value!=""){G=I._boundInput[0].value;}else{G=F.min;}}if(L){O=((I._trackSize-I._handleSize)/(F.max-F.min))*L;O=((Math.floor(O)/L)*(F.max-F.min))+I._handleSize;if(F.vertical){I._trackOnElm.height(O);if(Q){Q.element.height(O-I._handleSize);}}else{K.width(F.size-(I._trackSize-O));}I._trackSize=I._trackElm[J.length]();}I._pixelsPerVal=((I._trackSize-I._handleSize)/(F.max-F.min));I.val(G);K.attr({"aria-valuenow":I._val,"aria-valuemin":F.min,"aria-valuemax":F.max});H={axis:J.axis,container:I._trackElm,onDrag:function(){if(I._disabled||C(I).defaultPrevented()){return false;}I._stateElm.addClass("slider-active");M=c.addListener(document,"mousemove",D,I);},onDrop:function(){I._stateElm.removeClass("slider-active");c.removeListener(M);s(I);}};if(F.snapOnDrag){H.step=I._pixelsPerVal*F.step;}P=new o.dragdrop.Draggable(I._handleElm,H);if(F.jumpOnClick){c.addListener(I._trackElm,"mousedown",function(T){if(I._disabled||T.source==I._handleElm[0]){return;}var U=v(I),S=T[U.pagePos];T[U.pagePos]=I._handleElm.offset()[U.pos]+(I._handleSize/2);if(P._startDragMouse.call(P,T)===false){T[U.pagePos]=S;P._dragMouse.call(P,T);l(I);return false;}});}}z=o.widgets.Slider=function(G,F){this._disabled=false;G=k(G);this._opts=F=o.lang.apply({min:0,max:100,step:1,theme:"light",jumpOnClick:1,buttons:1,size:300},F);var J,O,I=v(this),K,R,N,P=[],M=this,H,Q,L=d((F.step*F.snapOnDrag),F.tickMinor,F.tickMajor);J=u.length;while(J--){O="on"+u[J].charAt(0).toUpperCase()+u[J].slice(1);if(F[O]){c.addListener(this,u[J],F[O]);}}this._boundInput=F.bindTo?k(F.bindTo):new o.dom.NodeList();this.element=K=o.dom.create('
            '+g+"
            ");this._trackElm=K.get("div.slider-track");this._trackOnElm=K.get("div.slider-trackOn");this._trackOffElm=K.get("div.slider-trackOff");this._handleElm=this._trackElm.get("div.slider-handle");this._stateElm=K.get("div.slider-state");K.get("div.slider-theme").addClass("slider-"+F.theme);!F.buttons&&this._stateElm.addClass("slider-noButtons");K[0].id=F.id||"";K[0].className+=" "+(F.className||"");if(F.tickMajor||F.tickMinor||F.labels){F.reverse=F.vertical;Q=new B(K.get("div.slider-labels"),F);}this.element.appendTo(G);h(this,Q);if(this._boundInput[0]){c.addListener(this._boundInput,"change",function(){var S=i(M,this.value);t(M,S);M.val(S);});}c.addListener(this.element,"focus",function(){if(!M._disabled){M._stateElm.addClass("slider-active");P[0]=c.addKeyListener(I.keyIncrement,"down",f,M);P[1]=c.addKeyListener(I.keyDecrement,"down",f,M);P[2]=c.addKeyListener(I.keyIncrement,"up",b,M);P[3]=c.addKeyListener(I.keyDecrement,"up",b,M);P[4]=c.addKeyListener(I.keyIncrement,"press",q);P[5]=c.addKeyListener(I.keyDecrement,"press",q);}});c.addListener(this.element,"blur",function(){M._stateElm.removeClass("slider-active");var S=P.length;while(S--){c.removeListener(P[S]);}P=[];});H=this.element.get(".slider-btn-fwd, .slider-btn-bk");c.addListener(H,"mousedown",n,this);c.addListener(H,"mouseup",w,this);c.addListener(H,"mouseout",w,this);if(Q){c.addListener(Q.element,"mousedown",function(S){if(M._disabled){return;}var T=k(S.source),U;while(T[0]!=Q.element[0]){if(T.hasClass("ruler-label")){U=i(M,T[0]._labelVal);t(M,U);M.val(U);return false;}T=T.parent();}});}};z.prototype={disabled:function(F){if(F!==undefined){this._disabled=F=!!F;this.element.attr("aria-disabled",F);this._stateElm[F?"addClass":"removeClass"]("slider-disabled");(this._boundInput[0]||{}).disabled=F;return this;}else{return this._disabled;}},val:function(F){if(F!=undefined){this._val=i(this,F);this.element.attr("aria-valuenow",this._val);(this._boundInput[0]||{}).value=this._val;r(this);return this;}else{return this._val;}},valToLabel:function(K){if(K===undefined){K=this._val;}var L=this._opts.labels,I=Infinity,H=Infinity,F,J,G;if(L===undefined){return null;}if(typeof L=="number"){return Math.round(K/L)*L;}if(L[K]){return L[K];}for(G in L){J=Math.abs(Number(G)-K);if(J=0;i--){result[i]=dataItem[index[i]];}return result;};})(that.opts.index);}else{throw"opts.index must be of type function, string or array, not "+typeof that.opts.index+".";}}}}else{that._indexer=function(dataItem){return(dataItem.name)?dataItem.name:dataItem.toString();};}}function nextItem(that){var currItem=$(that.overlay.container).get(".active");if(currItem.length==0){var items=$(that.overlay.container).get("li");if(items.length){activateItem(that,items[0]);}}else{var nextItem=currItem.next();if(nextItem&&!nextItem.is("ul")){deactivateItem(that,currItem);activateItem(that,nextItem);}}}function prevItem(that){var currItem=$(that.overlay.container).get(".active");if(currItem.length==0){var allItems=$(that.overlay.container).get("li");var lastItem=allItems[allItems.length-1];activateItem(that,lastItem);}else{var prevItem=currItem.prev();if(prevItem&&!prevItem.is("ul")){deactivateItem(that,currItem);activateItem(that,prevItem);}}}function getParentListItem(that,node){var listItem=node;while(listItem.parentNode&&listItem.parentNode.parentNode){if($(listItem.parentNode.parentNode).hasClass("glow151-autoSuggest")){break;}listItem=listItem.parentNode;}return(listItem.nodeName.toLowerCase()=="li")?listItem:null;}function activateItem(that,listItem){deactivateItems(that,listItem);$(listItem).addClass("active");if(that._lastActive!=listItem){that._lastActive=listItem;events.fire(that,"itemActive");}}function activateItemOffset(that,offset){var li=that.overlay.container.get("li")[offset];if(li){$(li).addClass("active");}}function deactivateItem(that,listItem){$(listItem).removeClass("active");}function deactivateItems(that,listItem){var list=(listItem)?$(listItem).parent():that.overlay.container.get("ul");list.get("li").each(function(i){$(this).removeClass("active");});}function addEvents(that){events.addListener(that,"itemActive",function(e){if(!isVisible(that)){return false;}var selectedOffset=that.getSelectedOffset();if(selectedOffset==-1){return false;}if(that.opts.onItemActive){var e=new events.Event();e.activeItem=that._found[selectedOffset];that.opts.onItemActive.apply(that,[e]);}return true;});events.addListener(that.inputElement,"mousedown",function(e){clearTimeout(that.findTimeout);that._value=that.inputElement.val();valueChanged(that,true);that.hide();that.value+=that._selected;that._selected="";return true;});events.addListener(that,"itemSelect",function(e){if(!isVisible(that)){return false;}var selectedOffset=that.getSelectedOffset();if(selectedOffset==-1){return false;}var e=new events.Event();e.source=$(that.overlay.container).get(".active");e.selectedItem=that._found[selectedOffset];if(that.opts.onItemSelect){that.opts.onItemSelect.apply(that,[e]);}setCaretTo(that.inputElement[0],that.inputElement.val().length);valueChanged(that,true);that.hide();return true;});events.addListener(that.overlay.container.get("ul")[0],"mousedown",function(e){events.fire(that,"itemSelect",e);});events.addListener(window,"resize",function(e){place(that);});events.addListener(that.overlay.container,"mousedown",function(){return false;});events.addListener(that.overlay.container,"beforedeactivate",function(event){if($(event.nativeEvent.toElement).isWithin(that.overlay.container)){return false;}return true;});events.addListener(that.inputElement,"blur",function(e){clearTimeout(that.findTimeout);that._value=that.inputElement.val();valueChanged(that,true);that.hide();});events.addListener(that.overlay.container,"mouseover",function(e){var li=getParentListItem(that,e.source);li&&activateItem(that,li);});events.addListener(that.overlay.container,"mouseout",function(e){var li=getParentListItem(that,e.source);if(li&&li!=e.source){deactivateItem(that,li);}});var ignoreInUp=false;var repeating={ondown:0,onpress:0};function keyDownHandler(e){clearTimeout(that.findTimeout);ignoreInUp=false;repeating.ondown++;if(e.key=="DOWN"){if(isVisible(that)){ignoreInUp=true;nextItem(that);return false;}}else{if(e.key=="UP"){if(isVisible(that)){ignoreInUp=true;prevItem(that);return false;}}else{if(e.key=="LEFT"){if(isVisible(that)){that._value=that.inputElement.val();valueChanged(that,true);that.hide();}}else{if(e.key=="RIGHT"){if(isVisible(that)){that._value=that.inputElement.val();valueChanged(that,true);that.hide();}}else{if(e.key=="ESC"){that._value=that.inputElement.val();valueChanged(that,true);that.hide();return false;}else{if(e.key=="DEL"||e.key=="BACKSPACE"){that.hide();}else{if(e.key=="ENTER"){if(isVisible(that)){ignoreInUp=true;}else{return true;}var selectedOffset=that.getSelectedOffset();if(selectedOffset==-1){that.hide();return true;}var e=new events.Event();e.source=$(that.overlay.container).get(".active");e.selectedItem=that._found[selectedOffset];events.fire(that,"itemSelect",e);return false;}}}}}}}return true;}events.addListener(that.inputElement[0],"keydown",keyDownHandler);function keyPressHandler(e){repeating.onpress++;if(repeating.ondown==1&&repeating.onpress>1){if(e.key=="DOWN"){if(isVisible(that)){nextItem(that);}return false;}else{if(e.key=="UP"){if(isVisible(that)){prevItem(that);}return false;}}}return true;}events.addListener(that.inputElement[0],"keypress",keyPressHandler);function keyUpHandler(e){repeating={ondown:0,onpress:0};if(ignoreInUp){return false;}that._value=that.inputElement.val();valueChanged(that);return true;}events.addListener(that.inputElement[0],"keyup",keyUpHandler);}function valueChanged(that,withoutFinding){if(typeof that._oldValue=="undefined"){that._oldValue=that.inputElement.val();}var currentValue=that.getValue();var skipFind=false;if(currentValue.toLowerCase()==that._oldValue.toLowerCase()){var skipFind=true;}that._oldValue=currentValue;if(withoutFinding||skipFind){return;}that.findTimeout=setTimeout(function(){var e=new glow.events.Event();e.value=currentValue;glow.events.fire(that,"inputChange",e);activateItemOffset(that,0);if(!e.defaultPrevented()){if(typeof that.dataSource!="object"){that.loadData();}that.find();}},500);}glow.widgets.AutoSuggest=function(inputElement,dataSource,opts){this.opts=opts||{};bindTo(this,inputElement);this.overlay=new glow.widgets.Overlay(glow.dom.create('
              '),{autoPosition:false,anim:(this.opts.anim)?this.opts.anim:null});this.configure(this.opts);buildIndexer(this);this.dataSource=dataSource;this.data=[];if(typeof dataSource!="string"){this.loadData();}addEvents(this);if(this.opts.complete){if(this.inputElement.val()==""){this.setData(dataSource);}else{this.setData(dataSource,function(){});}var that=this;events.addListener(that,"itemActive",function(e){var selectedOffset=that.getSelectedOffset();if(selectedOffset==-1){return false;}var matchedOn=(that._found[selectedOffset][this.opts.index]||that._found[selectedOffset]["name"]||that._found[selectedOffset]);if(typeof matchedOn.push!="undefined"){matchedOn=that._matchedOn;}that.suggest(matchedOn);return true;});}};glow.widgets.AutoSuggest.prototype.configure=function(opts){this.opts=opts||{};if(this.opts.height){var listContainer=$(this.overlay.container.get(".glow151-autoSuggest").get("ul")[0]);listContainer.css("overflow-x","hidden");listContainer.css("overflow-y","auto");listContainer.height(this.opts.height);}if(this.opts.theme=="dark"){$(this.overlay.container.get("ul")[0]).removeClass("autosuggest-light");$(this.overlay.container.get("ul")[0]).addClass("autosuggest-dark");}else{$(this.overlay.container.get("ul")[0]).removeClass("autosuggest-dark");$(this.overlay.container.get("ul")[0]).addClass("autosuggest-light");}if(this.opts.onDataLoad){events.addListener(this,"dataLoad",this.opts.onDataLoad);}if(this.opts.onDataError){events.addListener(this,"dataError",this.opts.onDataError);}if(this.opts.onDataAbort){events.addListener(this,"dataAbort",this.opts.onDataAbort);}if(this.opts.onInputChange){events.addListener(this,"inputChange",this.opts.onInputChange);}this._isMatch=this.opts.isMatch||function(word,lookFor){return(word.indexOf(lookFor)==0);};this._formatItem=this.opts.formatItem||function(o){return(o.name)?o.name:o.toString();};this._matchItem=this.opts.formatItem||function(o){return o.name;};};glow.widgets.AutoSuggest.prototype.setData=function(dataSource,callback){if(typeof dataSource=="function"){dataSource=dataSource.call(this);}if(typeof dataSource=="string"){this.dataURL=dataSource;this.data=[];dataSource=dataSource.replace(/\{input\}/g,escape(this.getValue()));downloadData(this,dataSource,callback);}else{this.data=dataSource;this.index={};this.results=[];for(var d=0;d'+this._formatItem(found[i])+"");}$(this.overlay.container.get("ul")[0]).html(list.join(""));this.show();}else{this.hide();}if(this.opts.activeOnShow!==false){nextItem(this);}};glow.widgets.AutoSuggest.prototype.hide=function(){this.overlay.hide();};glow.widgets.AutoSuggest.prototype.show=function(){place(this);this.overlay.show();};glow.widgets.AutoSuggest.prototype.getSelectedOffset=function(){if(!isVisible(this)){return -1;}var items=this.overlay.container.get("li");for(var i=0;i');var t=e.create('');this._viewWindow=e.create('');this._content.before(this.element);t.prependTo(this.element);this._viewWindow.prependTo(t);this._content.prependTo(this._viewWindow);if(this._opts.vertical){this.element.addClass("glow151-vCarousel");}else{this.element.addClass("glow151-carousel");}if(!this._opts.pageNav){this._navPrev=e.create('Previous').insertBefore(this._viewWindow);this._navNext=e.create('Next').insertAfter(this._viewWindow);}p.apply(this,[r,s]);}function p(r,v){var u=this;var w=this.items[0].style.position;this.items[0].style.position="absolute";this._itemWidth=parseInt(this.items[0].offsetWidth)+parseInt(f(this.items[0]).css(["margin-left","margin-right"]));this._itemHeight=parseInt(this.items[0].offsetHeight)+parseInt(f(this.items[0]).css(["margin-top","margin-bottom"]));this.items[0].style.position=w;this._itemHangingOffEnd=false;if(!v.size){var s;if(v.vertical){this._sizeView=this._startContentHeight;if(!this._opts.pageNav){this._sizeView-=this._navPrev[0].offsetHeight+this._navNext[0].offsetHeight;}this._viewWindow.css("width",this._itemWidth+"px");this._viewWindow.css("height",this._sizeView+"px");s=this._sizeView/this._itemHeight;this._opts.size=Math.floor(s);this._itemHangingOffEnd=(s!=this._opts.size);this.element.css("height",this._sizeView+(this._opts.pageNav?0:this._navPrev[0].offsetHeight+this._navNext[0].offsetHeight)+"px");}else{this._sizeView=this.element[0].offsetWidth;if(!this._opts.pageNav){this._sizeView-=this._navPrev[0].offsetWidth+this._navNext[0].offsetWidth;}this._viewWindow.css("width",this._sizeView+"px");this._viewWindow.css("height",this._itemHeight+"px");s=this._sizeView/this._itemWidth;this._opts.size=Math.floor(s);this._itemHangingOffEnd=(s!=this._opts.size);this.element.css("width",this._sizeView+(this._opts.pageNav?0:this._navPrev[0].offsetWidth+this._navNext[0].offsetWidth)+"px");}}else{if(this._opts.vertical){this._viewWindow.css("width",this._itemWidth+"px");this._viewWindow.css("height",this._opts.size*this._itemHeight+"px");}else{this._viewWindow.css("width",this._opts.size*this._itemWidth+"px");this._viewWindow.css("height",this._itemHeight+"px");}}if(this._opts.step=="page"){this._opts.step=this._opts.size;}if(this._opts.size=this._countReal&&t>this._countReal){this._content.css("margin-"+this._direction,"0px");t=t%this._countReal;}}return this.moveTo(t,r);};l.prototype.moveTo=function(v,s){var u=this;if(this._isPlaying()){return this;}if(!this._opts.loop){v=Math.min(v,this._countReal-1);}v=Math.max(v,0);v-=(v%this._countStep);if(!this._opts.loop){v=Math.min(v,this._movesMax*this._countStep);}var t=this._visibleIndexFirst();if(t==v){return this;}k.apply(this,[]);if(s!==false){var r={};r["margin-"+this._direction]={from:(t*-this._sizeEach)+"px",to:(v*-this._sizeEach)+"px"};this._step=n.anim.css(this._content,this._animationTime,r,{tween:this._animationTween});var u=this;n.events.addListener(this._step,"complete",function(){g.apply(u,[]);});this._step.start();}else{this._content.css("margin-"+this._direction,(v*-this._sizeEach)+"px");g.apply(this,[]);}return this;};n.widgets.Carousel=l;function o(u,v){this.leftarrow=e.create("
            • previous
            • ");this.rightarrow=e.create("
            • next
            • ");var r="";for(var s=0;s
              "+(s+1)+"
              ";}this.element=e.create("");this.leftarrow.insertBefore(this.element.get("li")[0]);this.rightarrow.insertAfter(this.element.get("li")[this.element.get("li").length-1]);var t=this;n.events.addListener(this.element,"click",function(x){if(f(x.source).parent().hasClass("dot")){var w=f(x.source).parent()[0].id.replace(/[^0-9]*/,"");v.apply(t,[w]);}});this.currentPage=0;}o.prototype.update=function(r){if(typeof r=="undefined"){r=this.currentPage;}this.element.get("#dot"+this.currentPage+"").removeClass("dotActive");this.element.get("#dot"+r+"").addClass("dotActive");this.currentPage=r;};}});(window.gloader||glow).module({name:"glow.widgets.Editor",library:["glow","1.5.1"],depends:[["glow","1.5.1","glow.dom","glow.events","glow.widgets"]],builder:function(l){var h=l.dom.get,c=l.events;l.widgets.Editor=function(z,A){z=h(z);if(!n){n=g();}A=this._opts=l.lang.apply({toolset:"basic"},A);this.element=l.dom.create('

              You are about to enter a Rich Text Editor, Skip past

              You have left the Rich Text Editor

              ');this.textarea=z;this.toolbar=new l.widgets.Editor.Toolbar(this);if(this._opts.toolset=="basic"){this.toolbar._addToolset("italics","bold","strike");}else{throw new Exception("Unknown toolset name.");}this.editArea=new l.widgets.Editor.EditArea(this);this.cleaner=new v();if(!x()){m.apply(this);f.apply(this,[]);}};var u=function(){return l.dom.get("p.endOfEditorCounter").length+1;};var r=function(C,A,F,E){E=this._opts=l.lang.apply({onFire:function(){}},E);var D=this;this.attachTo=C;this.name=A;this.wait=F;this.callback=E.onFire;this.rate=E.rate;this.running=false;this.initiated=false;if(typeof this.name.pop=="undefined"){this.name=[this.name];}for(var B=0,z=this.name.length;B142)?this.textarea[0].offsetHeight:142;this.element.css("width",(this.textarea[0].offsetWidth-2)+"px");this.element.css("height",(z-2)+"px");}var f=function(){this.textarea.before(this.element);this.element.get("iframe").css("height",(parseInt(this.element.css("height"))-42));this.textarea.css("display","block");this.textarea.css("position","absolute");this.textarea.css("left","-9999px");this.textarea.css("top","-9999px");this.bound=true;};l.widgets.Editor.prototype.inject=function(z){this.editArea._setContent(this.cleaner.dirty(this.cleaner.clean(z)));};l.widgets.Editor.prototype.commit=function(){if(this.bound){h(this.textarea).val(this.cleaner.clean(this.editArea._getContent()));}l.events.fire(this,"commit",{});};function v(z){this.opts=z||{};this.whitelist=["em","strong","strike","p","br","blockquote"];}v.prototype.pretreat=function(z){z=z.replace(//g,"");z=z.replace(//gi,"");z=z.replace(//gi,"");return z;};v.prototype.clean=function(C){var B="",A=[];C=this.pretreat(C);while(C){var D=1;if(/^(<[^>]+>)/.test(C)){var z=new v.Tag(RegExp.$1);this.tagClean(z);if(z.clean&&z.opening){B+=z.clean.start;if(!z.unary){A.unshift(z);}D=z.text.length;}else{if(A[0]&&C.toLowerCase().indexOf(A[0].end)===0){B+=A[0].clean.end;D=A[0].end.length;A.shift();}else{B+=z;D=z.text.length;}}}else{B+=C.charAt(0);}C=C.substring(D);}B=this.spin(B);return B;};v.prototype.dirty=function(z){var A;if(l.env.gecko){A=z.replace(//g,'').replace(/<\/strong>/g,"").replace(//g,'').replace(/<\/em>/g,"").replace(//g,'');}else{if(l.env.ie||l.env.opera){A=z.replace(//g,"").replace(/<\/strong>/g,"").replace(//g,"").replace(/<\/em>/g,"").replace(//g,"").replace(/<\/strike>/g,"");}else{if(l.env.webkit>528){A=z.replace(//g,"").replace(/<\/strong>/g,"").replace(//g,"").replace(/<\/em>/g,"").replace(//g,'').replace(/<\/strike>/g,"");}else{if(l.env.webkit){A=z.replace(//g,'').replace(/<\/strong>/g,"").replace(//g,'').replace(/<\/em>/g,"").replace(//g,'').replace(/<\/strike>/g,"");}else{throw new Error("Can't be dirty: Unknown browser.");}}}}return A;};v.prototype.spanClean=function(z){var A={start:"",end:""};if(/\bstyle\s*=\s*"(.+)"/.test(z.attrText.toLowerCase())){if(RegExp.$1.indexOf("bold")>-1){A.start+="";A.end=""+A.end;}if(RegExp.$1.indexOf("font-weight: normal")>-1){A.start+="";A.end=""+A.end;}if(RegExp.$1.indexOf("italic")>-1){A.start+="";A.end=""+A.end;}if(RegExp.$1.indexOf("font-style: normal")>-1){A.start+="";A.end=""+A.end;}if(RegExp.$1.indexOf("line-through")>-1){A.start+="";A.end=""+A.end;}}return A;};v.prototype.tagClean=function(z){var A=["",""];if(z.name=="span"){A=this.spanClean(z);}else{if(z.name=="b"){A={start:"",end:""};}else{if(z.name=="i"){A={start:"",end:""};}}}if(A.start){z.clean=A;}};v.Tag=function(z){/^<(\/?)([a-zA-Z]+)\b(.*)( ?\/)?>$/.exec(z);this.closing=!!RegExp.$1;this.opening=!this.closing;this.unary=!!RegExp.$4;this.name=RegExp.$2.toLowerCase();this.attrText=RegExp.$3;this.text=z;this.start=z.replace(/^<(\/?)([a-zA-Z]+)\b/,"<$1"+this.name);if(this.opening&&!this.unary){this.end="";}};v.Tag.prototype.toString=function(){return"<"+RegExp.$1+this.name+RegExp.$4+">";};v.prototype.spin=function(A){var z=this.whitelist.join("|");var B=new RegExp("<(\\/?("+z+")\\b[^>]*)>","g");A=A.replace(B,"\x1D$1\x1D");A=A.replace(/<[^>]+>/g,"");A=A.replace(/\x1D([^\x1D]+)\x1D/g,"<$1>");A=A.replace(/<>/g,"");return A;};l.widgets.Editor.Toolbar=function(z,A){A=A||{};this.editor=z;this.element=l.dom.create('
                ');this._tools=[];this.editor.element.get(".editor-state").prepend(this.element);};l.widgets.Editor.Toolbar.prototype._addToolset=function(){var C;for(var B=0,A=arguments.length;B=0){if(this._tools[A].name==z){return this._tools[A];}}};l.widgets.Editor.Toolbar.prototype._update=function(B){var C=false;for(var A=0,z=this._tools.length;A-1){this._tools[A].activate();C=true;}else{this._tools[A].deactivate();}}return C;};l.widgets.Editor.Toolbar.prototype._shortcut=function(A){var z=this._tools.length;var B=false;while(--z>=0){if(this._tools[z].shortcut==A){this._tools[z].press();return true;}}return false;};l.widgets.Editor.Toolbar.Tool=function(z,B,A){this.name=z;this.opts=B||{};this.action=this.opts.action||function(){};this.tag=this.opts.tag;this.command=this.opts.command;this.shortcut=this.opts.shortcut;this.isActive=false;this.isEnabled=true;if(this.opts.onDeactivate){l.events.addListener(this,"deactivate",this.opts.onDeactivate,A);}if(this.opts.onActivate){l.events.addListener(this,"activate",this.opts.onActivate,A);}if(this.opts.onDisable){l.events.addListener(this,"disable",this.opts.onDisable,A);}if(this.opts.onEnable){l.events.addListener(this,"enable",this.opts.onEnable,A);}};l.widgets.Editor.Toolbar.Tool.prototype.activate=function(){this.isActive=true;l.events.fire(this,"activate");};l.widgets.Editor.Toolbar.Tool.prototype.deactivate=function(){this.isActive=false;l.events.fire(this,"deactivate");};l.widgets.Editor.Toolbar.Tool.prototype.disable=function(){this.isEnabled=false;l.events.fire(this,"disable");};l.widgets.Editor.Toolbar.Tool.prototype.enable=function(){this.isEnabled=true;l.events.fire(this,"enable");};l.widgets.Editor.Toolbar.Tool.prototype.press=function(){if(this.isEnabled){this.action.call(this);if(!this.isActive){this.activate();}else{this.deactivate();}this.editor._lastDomPath=null;}};l.widgets.Editor.Toolbar.Button=function(A,D){this.Base=arguments.callee.base;this.base=this.Base.prototype;this.Base.apply(this,arguments);var z=A.toLowerCase()+"-button";this.element=l.dom.create('
              • '+(D.label||A)+"
              • ");var E=this.element.get("a");this.icon=this.element.get(".editor-toolbar-icon");var B;l.events.addListener(this.icon,"mouseover",function(){if(this.isEnabled&&!this.isActive){E.addClass("hover");}},this);l.events.addListener(E,"focus",function(){if(this.isEnabled){E.addClass("hover");}B=p(this);},this);l.events.addListener(this.icon,"mouseout",function(){E.removeClass("hover");},this);l.events.addListener(E,"blur",function(){E.removeClass("hover");l.events.removeListener(B);},this);l.events.addListener(this,"disable",function(){E.addClass("disabled");},this);l.events.addListener(this,"enable",function(){E.removeClass("disabled");},this);l.events.addListener(this,"activate",function(){if(this.isEnabled){E.addClass("active");}},this);l.events.addListener(this,"deactivate",function(){E.removeClass("active");},this);var C=this;l.events.addListener(this.element.get("a"),"mousedown",function(){C.press();return false;},this);l.events.addListener(this.element.get("a"),"click",function(){return false;});};l.lang.extend(l.widgets.Editor.Toolbar.Button,l.widgets.Editor.Toolbar.Tool);l.widgets.Editor.Toolbar.Button.prototype.activate=function(){this.base.activate.apply(this,arguments);};l.widgets.Editor.Toolbar.Button.prototype.deactivate=function(){this.base.deactivate.apply(this,arguments);};l.widgets.Editor.Toolbar.Button.prototype.enable=function(z){this.base.enable.apply(this,arguments);};l.widgets.Editor.Toolbar.Button.prototype.disable=function(z){this.base.disable.apply(this,arguments);};function p(z){return l.events.addListener(l.dom.get(document),"keyup",function(A){if(A.key=="ENTER"){z.press();if(A.preventDefault){A.preventDefault();}return false;}});}var n;function g(){return{bold:new l.widgets.Editor.Toolbar.Button("bold",{title:"Bold",label:"B",tag:"strong",command:"bold",shortcut:"b",action:function(){y.call(this.editor.editArea,this.command);return false;}}),italics:new l.widgets.Editor.Toolbar.Button("italics",{title:"Italics",label:"I",tag:"em",command:"italic",shortcut:"i",action:function(){y.call(this.editor.editArea,this.command);return false;}}),strike:new l.widgets.Editor.Toolbar.Button("strike",{title:"Strikethrough",label:"Strike",tag:"strike",command:"strikethrough",action:function(){y.call(this.editor.editArea,this.command);return false;}})};}l.widgets.Editor.EditArea=function(z,B){B=B||{};this.editor=z;this.element=h(document.createElement("iframe"));this.element.attr("frameBorder",0);this.element.src="javascript:false";this.editor.element.get(".editor-state").append(this.element);var A=this;setTimeout(function(){A.element[0].contentWindow.document.designMode="on";A.contentWindow=A.element[0].contentWindow;if(A.editor.textarea.val()){A.contentWindow.document.write(A.editor.textarea.val());}else{A.contentWindow.document.write("

                 

                ");}A.contentWindow.document.close();A.editor.iframeFocus=false;i.call(A);k(A);if(l.env.ie||l.env.opera){l.dom.get(A.element[0].contentWindow.document).item(0).attachEvent("onclick",function(){a.call(A);});l.dom.get(A.element[0].contentWindow.document).item(0).attachEvent("onkeyup",function(){a.call(A);});}else{c.addListener(A.contentWindow,"blur",function(){a.call(A);});c.addListener(A.contentWindow,"click",function(){a.call(A);});c.addListener(A.contentWindow,"mouseup",function(){a.call(A);});c.addListener(A.contentWindow,"keyup",function(){a.call(A);});}if(l.env.gecko){A.contentWindow.document.execCommand("styleWithCSS",false,false);}c.addListener(A.element[0].contentWindow,"beforeunload",function(){A.editor.commit();return true;});c.addListener(window,"beforeunload",function(){A.editor.commit();return true;});A._toolbarInTabIndex=false;l.events.addListener(A.editor.element.get(".editor-state"),"click",function(){q.apply(A);},A);if(!isNaN(l.env.ie)){A.contentWindow.attachEvent("onfocus",function(){q.apply(A);},A);}else{A.contentWindow.addEventListener("focus",function(){q.apply(A);},A);}if(A.editor.bound){A.idler=new r(A.contentWindow,["mousedown","keypress"],350,{onFire:function(){A.editor._rinse();},rate:700});}},0);};function q(){if(this.editor._toolbarInTabIndex==true){return;}this.editor.toolbar.element.get("a").item(0).tabIndex=0;this.editor._toolbarInTabIndex=true;}function i(){if(!isNaN(l.env.ie)){l.dom.get(this.contentWindow.document).item(0).attachEvent("onkeydown",(function(z){return function(A){A=A||window.event;return d.call(z,A);};})(this));}else{if(!isNaN(l.env.opera)){l.dom.get(this.contentWindow.document).item(0).addEventListener("keypress",(function(z){return function(A){A=A||window.event;return d.call(z,A);};})(this),true);}else{l.dom.get(this.contentWindow.document).item(0).addEventListener("keydown",(function(z){return function(A){A=A||window.event;return d.call(z,A);};})(this),true);}}}function d(z){if((navigator.platform.toLowerCase().indexOf("mac")==-1)||isNaN(l.env.webkit)){if((z.keyCode==9)&&(z.shiftKey==true)){var A=l.dom.get(this.editor.element).get("ul.editor-toolbar-tools a");A.each(function(B){if(A[B].tabIndex==0){window.focus();A[B].focus();}});if(z.preventDefault){z.preventDefault();}return false;}if((z.keyCode==9)){window.focus();this.element[0].focus();l.dom.get(this.editor.element).get("p.endOfEditorCounter").item(0).focus();if(z.preventDefault){z.preventDefault();}return false;}}if(o.call(this,z)){if((this.editor.toolbar._shortcut(String.fromCharCode(z.keyCode).toLowerCase()))||(String.fromCharCode(z.keyCode).toLowerCase()=="u")){if(z.preventDefault){z.preventDefault();}return false;}}return true;}function o(z){if(navigator.platform.toLowerCase().indexOf("mac")!=-1){if(!isNaN(l.env.opera)){return z.ctrlKey;}return z.metaKey;}else{return z.ctrlKey;}}function x(){if((l.env.webkit>400)&&(l.env.webkit<500)){return true;}else{return false;}}function k(A){var z,C,B=A.editor.toolbar.element.get("a");l.events.addListener(l.dom.get(B),"focus",function(){C=l.events.addKeyListener("RIGHT","down",s);z=l.events.addKeyListener("LEFT","down",t);});l.events.addListener(l.dom.get(B),"blur",function(){l.events.removeListener(C);l.events.removeListener(z);});}function t(z){w(e(l.dom.get(z.source),-1));}function s(z){w(e(l.dom.get(z.source),1));}function e(D,z){var A=0,B=l.dom.get(D).parent().parent().parent().get("a"),C=(B.length-1);B.each(function(E){if(this==D.item(0)){A=(E+z);}this.tabIndex=-1;});if(A<0){A=0;}if(A>C){A=C;}return B.item(A);}function w(z){if(typeof z!="undefined"){z.tabIndex=0;z.focus();}}function y(A,z){z=z||null;if(this[A+"_"+z]){this[A+"_"+z]();}else{this._domPath();this.contentWindow.document.execCommand(A,false,null);}this.contentWindow.focus();a.call(this);}l.widgets.Editor.EditArea.prototype._getSelected=function(){if(l.env.ie){return this.contentWindow.document.selection;}else{return this.contentWindow.getSelection();}};function a(){this.editor.commit();var A=this._domPath();if(A!=this.editor._lastDomPath){this.editor._lastDomPath=A;var z=l.events.fire(this,"domPathChange",{domPath:A});if(!z.defaultPrevented()){this.editor.toolbar._update(A);}}}l.widgets.Editor.EditArea.prototype._domPath=function(C){C=C||this._getSelectedNode();var A=l.dom.get(this.editor.editArea.contentWindow.document).get("body").item(0);var z="";while(C.nodeName.toUpperCase()!=A.nodeName.toUpperCase()){z="<"+C.nodeName.toLowerCase()+((C.getAttribute("style"))?' style="'+C.getAttribute("style")+'"':"")+">"+z;C=C.parentNode;}var B=this.editor.cleaner.clean(z);B=B.replace(/>/g,"|").replace(/=H){return J.parentNode;}}if(J.childNodes){for(var I=0;I").append(E);}else{G=e.create("
                "+E+"
                ");}return G;}function c(F,G,J,L,M,E){this._opts=E=l({vertical:true,tracks:[],collapseItemBorders:true,collapseTrackBorders:false,keepItemContentInView:true,className:"",theme:"light"},E||{});var I=q.call(this);this._container=h(F);if(!this._container[0]){throw new Error("Could not find container for Timetable");}this.id=E.id||r();this.size=E.size||this._container[I.length]();this.numerical=((typeof G)=="number");this.start=G;this.end=J;this._viewStart=L;this._viewEnd=M;if(!this.numerical){this.start=new Date(G);this.end=new Date(J);this._viewStart=new Date(L);this._viewEnd=new Date(M);}this._viewWindowSize=this._viewEnd-this._viewStart;this.tracks=[];for(var K=0,H=E.tracks.length;Kthis.end){this._viewEnd=this.end;}if(this._view&&this._view._drawn){this.draw(true).currentPosition(this._viewStart);}return this;}else{return E;}},setItemTemplate:function(E){this._opts.itemTemplate=E;return this;},setTrackHeaderTemplate:function(E){this._opts.trackHeader=E;return this;},setTrackFooterTemplate:function(E){this._opts.trackFooter=E;return this;},setBanding:function(G,F){var E=F||{};this._banding=s.call(this,G,E.start||"auto",this.start,this.end);return this;},addScale:function(G,E,I,J){var H=J||{},F={template:H.template,size:I,points:s.call(this,G,H.start||"auto",this.start,this.end),opts:H};E=E.toLowerCase();if((E=="both")&&H.id){throw new Error("Cannot apply an id when adding to both sides of the timetable");}if((E=="top")||(E=="left")||(E=="both")){this._primaryScales[this._primaryScales.length]=F;}if((E=="bottom")||(E=="right")||(E=="both")){this._secondaryScales[this._secondaryScales.length]=F;}return this;},removeScales:function(E){if((E=="top")||(E=="left")||(E=="both")){this._primaryScales=[];}if((E=="bottom")||(E=="right")||(E=="both")){this._secondaryScales=[];}return this;},addScrollbar:function(I,E,H,J){var G=l({buttons:true},J||{}),F={template:G.template,size:H,points:s.call(this,I,G.start||"auto",this.start,this.end),opts:G};E=E.toLowerCase();if((E=="both")&&G.id){throw new Error("Cannot apply an id when adding to both sides of the timetable");}if((E=="top")||(E=="left")||(E=="both")){this._primaryScrollbar=F;}if((E=="bottom")||(E=="right")||(E=="both")){this._secondaryScrollbar=F;}return this;},draw:function(E){this._view.draw(E);return this;}};function C(J,I,G,H){this._opts=H=l({className:""},H||{});this.disabled=H.disabled||false;this.data=H.data||{};this.title=I;this.size=G;this.timetable=J;this.id=H.id||r();this.items=[];if(H.items!=undefined){for(var F=0,E=H.items.length;FF){break;}if(I.call(G[J],L,F)){K.items[H]=G[J];K.indices[H]=J;H++;}}return K;}function k(F,E){return((this.start>=F)&&(this.end<=E));}function b(F,E){return((this.startF));}function a(E){return((this.start<=E)&&(this.end>E));}C.prototype={toString:function(){return this.title;},addItem:function(H,I,E,F){var G=B.call(this,H,I,E,F);n.call(this);return G;},itemAt:function(E){return x.call(this,E,E,a).items[0];},indexAt:function(E){return x.call(this,E,E,a).indices[0];},itemsAt:function(E){return x.call(this,E,E,a).items;},indicesAt:function(E){return x.call(this,E,E,a).indices;},itemsInRange:function(F,E){return x.call(this,F,E,b).items;},indicesInRange:function(F,E){return x.call(this,F,E,b).indices;},setItemTemplate:function(E){this._opts.itemTemplate=E;return this;},setTrackHeaderTemplate:function(E){this._opts.trackHeader=E;return this;},setTrackFooterTemplate:function(E){this._opts.trackFooter=E;return this;},getHeader:function(){return p.call(this,this._opts.trackHeader||this.timetable._opts.trackHeader);},getFooter:function(){return p.call(this,this._opts.trackFooter||this.timetable._opts.trackFooter);}};function d(F,H,I,E,G){this._addIndex=F.items.length;this._opts=G=l({className:""},G||{});if(((typeof I)=="number")!==F.timetable.numerical){throw new Error("Item scale type does not match Timetable.");}this.data=G.data||{};this.title=H;this.start=I;this.end=E;if(!F.timetable.numerical){this.start=new Date(I);this.end=new Date(E);}this.track=F;this.id=G.id||r();this.element;}d.prototype={toString:function(){return this.title;},setItemTemplate:function(E){this._opts.itemTemplate=E;return this;},getContent:function(){return p.call(this,this._opts.itemTemplate||this.track._opts.itemTemplate||this.track.timetable._opts.itemTemplate);},inRange:function(F,E){if(!this.track.timetable.numerical){F=new Date(F);E=new Date(E);}return b.call(this,F,E);}};o.widgets.Timetable=c;o.widgets.Timetable.Track=C;o.widgets.Timetable.Item=d;var z;(function(){var R='
                '+A.ACCESSIBILITY_INTRO+'
                ',I='
                ',an='',am='
                  ',W='
                1. ',S='
                  ',ah='
                  ',ab=e.create("
                  "),Q=10;function al(aq){if(!this._clickStart){this._clickStart=[aq.pageX,aq.pageY];}else{if(!this._cancelNextItemClick&&(Math.abs(this._clickStart[0]-aq.pageX)>Q||Math.abs(this._clickStart[1]-aq.pageY)>Q)){this._cancelNextItemClick=true;}}E.call(this,this.currentPosition());}function J(ar){if(this._cancelNextItemClick){return false;}var aq=h(ar.source);while(aq[0]!=ar.attachedTo){if(aq.hasClass("timetable-item")){f(this._timetable,"itemClick",l({item:this.itemInstance[aq[0].id]},new i.Event(ar)));}aq=aq.parent();}}function Z(){f(this._timetable,"moveStart");}function E(at){var ar=this._timetable,aq=q.call(ar);this._dragAreaElm.css(aq.pos,-(af.call(this,at)));if(this._scrollbar1){this._scrollbar1.moveToPosition(at);}if(this._scrollbar2){this._scrollbar2.moveToPosition(at);}}function ak(){f(this._timetable,"moveStop");}function G(){H.call(this);Y.call(this);}function H(){var ax=this._timetable,ar=q.call(ax),au=0,ay=ax.tracks.length,aC,at,aq,aA,az=ax.currentPosition(),aw,av,aB=parseInt(this._dragAreaElm[0].style[ar.pos]);if(this._timetable._opts.keepItemContentInView){this._itemContentHangingOffStart.css("margin-"+ar.pos,0);this._itemsHangingOffStart.removeClass("timetable-itemHangingClipping");}this._itemsHangingOffStart.removeClass("timetable-itemHangingOffStart");this._itemContentHangingOffStart=new e.NodeList();this._itemsHangingOffStart=new e.NodeList();for(;au"+aq.title+"");}function F(ar){var ax=this._timetable,au=q.call(ax),az=ar.items,av=0,ay=az.length,aC=this.tracks[ar.id],aw=this._headers[ar.id],aB=this._footers[ar.id],at,aA,aq,aD;if(!aC){aC=this.tracks[ar.id]=O.call(this,ar);aC.css(au.breadth,ar.size);aC.appendTo(this._dragAreaElm);X.call(this,ar);if(aw){aC.prepend(aw.clone().removeClass("timetable-header-holder").addClass("timetable-accessibility-hidden"));}if(aB){aC.append(aB.clone().removeClass("timetable-footer-holder").addClass("timetable-accessibility-hidden"));}}at=aC.get("> ol");aq=e.create(W).appendTo(at);aA=parseInt(aq.css(["border-"+au.pos+"-width","border-"+au.posOpposite+"-width"]))/2;aq.remove();for(;av'+A.SKIPLINK_TO_TRACK+"");}ar=aq.getFooter();if(ar){this._footers[at]=e.create(an).append(ar.addClass("timetable-footer-content"));this._footerElm.append(this._footers[at]).append(''+A.SKIPLINK_BACK_TO_HEADERS+"");}}function O(aq){var ar=e.create(am).attr("id",aq.id);return ar;}function ae(){var av=this._timetable,aq=q.call(av),aH=0,au=av.tracks.length,aL=this._primaryScaleElms.length,aE=this._secondaryScaleElms.length,aC=au+aL+aE,ay,aK,aM,ar=0,aF=0,ax,at,aw=["border-"+aq.otherPos+"-width","border-"+aq.otherPosOpposite+"-width"],aI=0,aJ=av._opts.collapseTrackBorders,aD,az,aA,aB,aG;ax=this._scrollbar1Elm[aq.breadth]()-parseInt(this._headerElm.css("border-"+aq.otherPos+"-width"));for(;aH'+aq+" { "+ar+" } ").appendTo("head");}function ad(){var at=this._timetable,ar=at._primaryScrollbar,aq=at._secondaryScrollbar;if(ar){this._scrollbar1Elm.css("display","block");this._scrollbar1=new ao(this,this._scrollbar1Elm,ar);}this._scrollbar1Elm.css("display",ar?"block":"");if(aq){this._scrollbar2Elm.css("display","block");this._scrollbar2=new ao(this,this._scrollbar2Elm,aq);}this._scrollbar2Elm.css("display",aq?"block":"");}function ap(){var au=this._timetable,ar=q.call(au),at,aq;at=af.call(this,au.end);aq=(at*2)-this._viewSize;this._dragAreaElm[ar.length](at);this._dragRangeElm[ar.length](aq).css("margin-"+ar.pos,-at+this._viewSize);}function V(){var ar=this._timetable,aq=q.call(ar);this._viewSize=this._innerViewElm[aq.length]();this.scale=(ar._viewEnd-ar._viewStart)/this._viewSize;}function T(){var at=this._timetable,ar=q.call(at),aq=this;this._draggable=new o.dragdrop.Draggable(this._dragAreaElm,{axis:ar.dragAxis,container:this._dragRangeElm,placeholder:"none",onDrag:function(){aq._cancelNextItemClick=false;aq._clickStart=0;aq._mouseMoveListener=u(document,"mousemove",al,aq);Z.call(aq);ac.call(aq);},onDrop:function(){ak.call(aq);G.call(aq);aq._mouseMoveListener&&o.events.removeListener(aq._mouseMoveListener);}});}function P(){var ax=this._timetable,az=ax.tracks,ay=az.length,aA=this._inCurrentView,ar=this._innerViewElm,au=null,aB=ax.viewRange(),aw=aB.start,aC=aB.end,aq="",av=0,at=0;if(aA==null){ar.addClass("timetable-hideitems");this._inCurrentView=aA={};}for(aq in aA){if(!aA[aq].inRange(aw,aC)){delete aA[aq];h(aq).css("display","");}}for(j=0;j'+A.ACCESSIBILITY_MENU_START+"",az='";for(;ar=au.start)&&(aq.start<=aA)){at[ar]='";if(aq.start.valueOf()==au.start.valueOf()){ax="";}if(aq.start.valueOf()==aA.valueOf()){az="";}}}var ay=this._accessibiltySelect=e.create("");u(ay,"change",function(){av._timetable.currentPosition(ay.val()*1);P.call(av);});this._accessibiltyElm.append(ay);aj.call(this);}}function aj(){if(this._accessibiltySelect){var av=this.currentPosition(),ar=this._accessibiltySelect[0].options,au=0,aq=ar.length,aw=ar[au].value*1,at;for(;au'),aI=o.UID+"scrollbar"+(ar++),ay,aB=aF._viewEnd-aF._viewStart,aJ=aF.end-aF.start,aK,aL,aR,aC=aF.viewRange().start;this._timetable=aF;K("#"+aI+" .slider-handle",az.length+":"+(aB/aJ)*100+"%");if(aF._opts.vertical){aK=-aF.end+aB;aL=-aF.start;aR=-aC;}else{aK=aF.start-0;aL=aF.end-aB;aR=aC;}this.slider=new o.widgets.Slider(aH,{min:aK,max:aL,vertical:aF._opts.vertical,className:"timetable-scrollbar",id:aI,val:aR,size:aG._innerViewElm[az.length](),step:0,changeOnDrag:true});ay=this.slider.element.get("div.slider-track");if(aF._opts.vertical){ay.css(az.length,ay.get("div.slider-trackOn").css(az.length));}this.slider.element.get("div.slider-btn-bk, div.slider-btn-fwd").push(ay).css(az.breadth,aA.size);this.scale=(aJ)/ay[az.length]();for(;aN').append(p.call(aE,aA.template).addClass("timetable-itemContent")).css(az.pos,aw).css(az.length,aD).appendTo(aQ);}this._labelsHighlight=aQ.clone().addClass("timetable-scrollbarLabelsHighlight");u(this.slider,"change",av,this);u(this.slider,"slideStart",au,this);u(this.slider,"slideStop",aq,this);ay.prepend(aQ).prepend(this._labelsHighlight);this._sliderHandle=this.slider.element.get("div.slider-handle");this._handleLength=this._sliderHandle[az.length]();at.call(this);};ao.prototype={moveToPosition:function(aw){this._ignoreChange=true;this.slider.val((this._timetable._opts.vertical?-1:1)*aw);this._ignoreChange=false;at.call(this);}};})();})();}}); -/*@end @*/ diff --git a/horde/js/glow/LICENCE.txt b/horde/js/glow/LICENCE.txt deleted file mode 100644 index 66a27ec5f..000000000 --- a/horde/js/glow/LICENCE.txt +++ /dev/null @@ -1,177 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/horde/js/glow/map.debug.js b/horde/js/glow/map.debug.js deleted file mode 100644 index adafb82af..000000000 --- a/horde/js/glow/map.debug.js +++ /dev/null @@ -1,164 +0,0 @@ -/* - Copyright 2009 British Broadcasting Corporation - - 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() { // begin closure - - // implement glow's magic debug cookie - if (gloader.settings.get("debug")) { - gloader.map.setProperties( - "glow", - { - $debug: ".debug" - } - ); - } - - -// dist file paths -var glow_js = "{$base}gloader/glow/glow{$debug}.js", - effects_js = "{$base}gloader/fx/fx{$debug}.js", - widgets_js = "{$base}gloader/widgets/widgets{$debug}.js", - - core_150_js = "{$base}core/core{$debug}.js", - widgets_150_js = "{$base}widgets/widgets{$debug}.js"; - - -// glow map -gloader.map.add( - "glow", - { - $version: "1.0.0", - "glow": glow_js, - "glow.anim": effects_js, - "glow.data": glow_js, - "glow.debug": glow_js, - "glow.dom": glow_js, - "glow.dragdrop": effects_js, - "glow.events": glow_js, - "glow.net": glow_js, - "glow.tweens": effects_js, - "glow.widgets": [widgets_js, "{$base}widgets/widgets.css"], - "glow.widgets.InfoPanel": widgets_js, - "glow.widgets.Mask": widgets_js, - "glow.widgets.Overlay": widgets_js, - "glow.widgets.Panel": widgets_js, - "glow.widgets.Sortable": widgets_js - }, - { - $version: "1.0.1" - }, - { - $version: "1.0.2" - }, - { - $version: "1.1.0", - "glow.anim": glow_js, - "glow.dragdrop": glow_js, - "glow.tweens": glow_js, - "glow.embed": glow_js, - "glow.forms": glow_js - }, - { - $version: "1.1.1" - }, - { - $version: "1.2.0", - "glow.widgets.AutoComplete": widgets_js, - "glow.widgets.AutoSuggest": widgets_js, - "glow.widgets.Carousel": widgets_js, - "glow.widgets.Slider": widgets_js - }, - { - $version: "1.2.1" - }, - { - $version: "1.2.2" - }, - { - $version: "1.3.0" - }, - { - $version: "1.3.1" - }, - { - $version: "1.3.2" - }, - { - $version: "1.3.3" - }, - { - $version: "1.3.4" - }, - { - $version: "1.3.5" - }, - { - $version: "1.4.0-rc1", - "glow.widgets.Editor": widgets_js, - "glow.widgets.Timetable": widgets_js - }, - { - $version: "1.4.0" - }, - { - $version: "1.4.1" - }, - { - $version: "1.4.2" - }, - { - $version: "1.4.3" - }, - { - $version: "1.5.0-rc1", - "glow": core_150_js, - "glow.anim": core_150_js, - "glow.data": core_150_js, - "glow.debug": core_150_js, - "glow.dom": core_150_js, - "glow.dragdrop": core_150_js, - "glow.embed": core_150_js, - "glow.events": core_150_js, - "glow.forms": core_150_js, - "glow.net": core_150_js, - "glow.tweens": core_150_js, - - "glow.widgets": [widgets_150_js, "{$base}widgets/widgets.css"], - "glow.widgets.AutoComplete": widgets_150_js, - "glow.widgets.AutoSuggest": widgets_150_js, - "glow.widgets.Carousel": widgets_150_js, - "glow.widgets.Editor": widgets_150_js, - "glow.widgets.InfoPanel": widgets_150_js, - "glow.widgets.Mask": widgets_150_js, - "glow.widgets.Overlay": widgets_150_js, - "glow.widgets.Panel": widgets_150_js, - "glow.widgets.Slider": widgets_150_js, - "glow.widgets.Sortable": widgets_150_js, - "glow.widgets.Timetable": widgets_150_js - - }, - { - $version: "1.5.0-rc2" - }, - { - $version: "1.5.0" - }, - { - $version: "1.5.1" - } -); - -})(); // end closure - diff --git a/horde/js/glow/map.js b/horde/js/glow/map.js deleted file mode 100644 index 6c151fc0c..000000000 --- a/horde/js/glow/map.js +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright 2009 British Broadcasting Corporation - - 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(){if(gloader.settings.get("debug")){gloader.map.setProperties("glow",{$debug:".debug"});}var b="{$base}gloader/glow/glow{$debug}.js",e="{$base}gloader/fx/fx{$debug}.js",c="{$base}gloader/widgets/widgets{$debug}.js",d="{$base}core/core{$debug}.js",a="{$base}widgets/widgets{$debug}.js";gloader.map.add("glow",{$version:"1.0.0",glow:b,"glow.anim":e,"glow.data":b,"glow.debug":b,"glow.dom":b,"glow.dragdrop":e,"glow.events":b,"glow.net":b,"glow.tweens":e,"glow.widgets":[c,"{$base}widgets/widgets.css"],"glow.widgets.InfoPanel":c,"glow.widgets.Mask":c,"glow.widgets.Overlay":c,"glow.widgets.Panel":c,"glow.widgets.Sortable":c},{$version:"1.0.1"},{$version:"1.0.2"},{$version:"1.1.0","glow.anim":b,"glow.dragdrop":b,"glow.tweens":b,"glow.embed":b,"glow.forms":b},{$version:"1.1.1"},{$version:"1.2.0","glow.widgets.AutoComplete":c,"glow.widgets.AutoSuggest":c,"glow.widgets.Carousel":c,"glow.widgets.Slider":c},{$version:"1.2.1"},{$version:"1.2.2"},{$version:"1.3.0"},{$version:"1.3.1"},{$version:"1.3.2"},{$version:"1.3.3"},{$version:"1.3.4"},{$version:"1.3.5"},{$version:"1.4.0-rc1","glow.widgets.Editor":c,"glow.widgets.Timetable":c},{$version:"1.4.0"},{$version:"1.4.1"},{$version:"1.4.2"},{$version:"1.4.3"},{$version:"1.5.0-rc1",glow:d,"glow.anim":d,"glow.data":d,"glow.debug":d,"glow.dom":d,"glow.dragdrop":d,"glow.embed":d,"glow.events":d,"glow.forms":d,"glow.net":d,"glow.tweens":d,"glow.widgets":[a,"{$base}widgets/widgets.css"],"glow.widgets.AutoComplete":a,"glow.widgets.AutoSuggest":a,"glow.widgets.Carousel":a,"glow.widgets.Editor":a,"glow.widgets.InfoPanel":a,"glow.widgets.Mask":a,"glow.widgets.Overlay":a,"glow.widgets.Panel":a,"glow.widgets.Slider":a,"glow.widgets.Sortable":a,"glow.widgets.Timetable":a},{$version:"1.5.0-rc2"},{$version:"1.5.0"},{$version:"1.5.1"});})(); \ No newline at end of file -- 2.11.0