/* --- script: Core.js description: The core of MooTools, contains all the base functions and the Native and Hash implementations. Required by all the other scripts. license: MIT-style license. copyright: Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/). authors: The MooTools production team (http://mootools.net/developers/) inspiration: - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php) - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php) provides: [Mootools, Native, Hash.base, Array.each, $util] ... */ var MooTools = { 'version': '1.2.4', 'build': '0d9113241a90b9cd5643b926795852a2026710d4' }; var Native = function(options){ options = options || {}; var name = options.name; var legacy = options.legacy; var protect = options.protect; var methods = options.implement; var generics = options.generics; var initialize = options.initialize; var afterImplement = options.afterImplement || function(){}; var object = initialize || legacy; generics = generics !== false; object.constructor = Native; object.$family = {name: 'native'}; if (legacy && initialize) object.prototype = legacy.prototype; object.prototype.constructor = object; if (name){ var family = name.toLowerCase(); object.prototype.$family = {name: family}; Native.typize(object, family); } var add = function(obj, name, method, force){ if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method; if (generics) Native.genericize(obj, name, protect); afterImplement.call(obj, name, method); return obj; }; object.alias = function(a1, a2, a3){ if (typeof a1 == 'string'){ var pa1 = this.prototype[a1]; if ((a1 = pa1)) return add(this, a2, a1, a3); } for (var a in a1) this.alias(a, a1[a], a2); return this; }; object.implement = function(a1, a2, a3){ if (typeof a1 == 'string') return add(this, a1, a2, a3); for (var p in a1) add(this, p, a1[p], a2); return this; }; if (methods) object.implement(methods); return object; }; Native.genericize = function(object, property, check){ if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){ var args = Array.prototype.slice.call(arguments); return object.prototype[property].apply(args.shift(), args); }; }; Native.implement = function(objects, properties){ for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties); }; Native.typize = function(object, family){ if (!object.type) object.type = function(item){ return ($type(item) === family); }; }; (function(){ var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String}; for (var n in natives) new Native({name: n, initialize: natives[n], protect: true}); var types = {'boolean': Boolean, 'native': Native, 'object': Object}; for (var t in types) Native.typize(types[t], t); var generics = { 'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"], 'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"] }; for (var g in generics){ for (var i = generics[g].length; i--;) Native.genericize(natives[g], generics[g][i], true); } })(); var Hash = new Native({ name: 'Hash', initialize: function(object){ if ($type(object) == 'hash') object = $unlink(object.getClean()); for (var key in object) this[key] = object[key]; return this; } }); Hash.implement({ forEach: function(fn, bind){ for (var key in this){ if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this); } }, getClean: function(){ var clean = {}; for (var key in this){ if (this.hasOwnProperty(key)) clean[key] = this[key]; } return clean; }, getLength: function(){ var length = 0; for (var key in this){ if (this.hasOwnProperty(key)) length++; } return length; } }); Hash.alias('forEach', 'each'); Array.implement({ forEach: function(fn, bind){ for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this); } }); Array.alias('forEach', 'each'); function $A(iterable){ if (iterable.item){ var l = iterable.length, array = new Array(l); while (l--) array[l] = iterable[l]; return array; } return Array.prototype.slice.call(iterable); }; function $arguments(i){ return function(){ return arguments[i]; }; }; function $chk(obj){ return !!(obj || obj === 0); }; function $clear(timer){ clearTimeout(timer); clearInterval(timer); return null; }; function $defined(obj){ return (obj != undefined); }; function $each(iterable, fn, bind){ var type = $type(iterable); ((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind); }; function $empty(){}; function $extend(original, extended){ for (var key in (extended || {})) original[key] = extended[key]; return original; }; function $H(object){ return new Hash(object); }; function $lambda(value){ return ($type(value) == 'function') ? value : function(){ return value; }; }; function $merge(){ var args = Array.slice(arguments); args.unshift({}); return $mixin.apply(null, args); }; function $mixin(mix){ for (var i = 1, l = arguments.length; i < l; i++){ var object = arguments[i]; if ($type(object) != 'object') continue; for (var key in object){ var op = object[key], mp = mix[key]; mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $mixin(mp, op) : $unlink(op); } } return mix; }; function $pick(){ for (var i = 0, l = arguments.length; i < l; i++){ if (arguments[i] != undefined) return arguments[i]; } return null; }; function $random(min, max){ return Math.floor(Math.random() * (max - min + 1) + min); }; function $splat(obj){ var type = $type(obj); return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : []; }; var $time = Date.now || function(){ return +new Date; }; function $try(){ for (var i = 0, l = arguments.length; i < l; i++){ try { return arguments[i](); } catch(e){} } return null; }; function $type(obj){ if (obj == undefined) return false; if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name; if (obj.nodeName){ switch (obj.nodeType){ case 1: return 'element'; case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace'; } } else if (typeof obj.length == 'number'){ if (obj.callee) return 'arguments'; else if (obj.item) return 'collection'; } return typeof obj; }; function $unlink(object){ var unlinked; switch ($type(object)){ case 'object': unlinked = {}; for (var p in object) unlinked[p] = $unlink(object[p]); break; case 'hash': unlinked = new Hash(object); break; case 'array': unlinked = []; for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]); break; default: return object; } return unlinked; }; /* --- script: Browser.js description: The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash. license: MIT-style license. requires: - /Native - /$util provides: [Browser, Window, Document, $exec] ... */ var Browser = $merge({ Engine: {name: 'unknown', version: 0}, Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()}, Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)}, Plugins: {}, Engines: { presto: function(){ return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }, trident: function(){ return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }, webkit: function(){ return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419); }, gecko: function(){ return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); } } }, Browser || {}); Browser.Platform[Browser.Platform.name] = true; Browser.detect = function(){ for (var engine in this.Engines){ var version = this.Engines[engine](); if (version){ this.Engine = {name: engine, version: version}; this.Engine[engine] = this.Engine[engine + version] = true; break; } } return {name: engine, version: version}; }; Browser.detect(); Browser.Request = function(){ return $try(function(){ return new XMLHttpRequest(); }, function(){ return new ActiveXObject('MSXML2.XMLHTTP'); }, function(){ return new ActiveXObject('Microsoft.XMLHTTP'); }); }; Browser.Features.xhr = !!(Browser.Request()); Browser.Plugins.Flash = (function(){ var version = ($try(function(){ return navigator.plugins['Shockwave Flash'].description; }, function(){ return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); }) || '0 r0').match(/\d+/g); return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0}; })(); function $exec(text){ if (!text) return text; if (window.execScript){ window.execScript(text); } else { var script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); script[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : 'text'] = text; document.head.appendChild(script); document.head.removeChild(script); } return text; }; Native.UID = 1; var $uid = (Browser.Engine.trident) ? function(item){ return (item.uid || (item.uid = [Native.UID++]))[0]; } : function(item){ return item.uid || (item.uid = Native.UID++); }; var Window = new Native({ name: 'Window', legacy: (Browser.Engine.trident) ? null: window.Window, initialize: function(win){ $uid(win); if (!win.Element){ win.Element = $empty; if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2 win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {}; } win.document.window = win; return $extend(win, Window.Prototype); }, afterImplement: function(property, value){ window[property] = Window.Prototype[property] = value; } }); Window.Prototype = {$family: {name: 'window'}}; new Window(window); var Document = new Native({ name: 'Document', legacy: (Browser.Engine.trident) ? null: window.Document, initialize: function(doc){ $uid(doc); doc.head = doc.getElementsByTagName('head')[0]; doc.html = doc.getElementsByTagName('html')[0]; if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function(){ doc.execCommand("BackgroundImageCache", false, true); }); if (Browser.Engine.trident) doc.window.attachEvent('onunload', function(){ doc.window.detachEvent('onunload', arguments.callee); doc.head = doc.html = doc.window = null; }); return $extend(doc, Document.Prototype); }, afterImplement: function(property, value){ document[property] = Document.Prototype[property] = value; } }); Document.Prototype = {$family: {name: 'document'}}; new Document(document); /* --- script: Array.js description: Contains Array Prototypes like each, contains, and erase. license: MIT-style license. requires: - /$util - /Array.each provides: [Array] ... */ Array.implement({ every: function(fn, bind){ for (var i = 0, l = this.length; i < l; i++){ if (!fn.call(bind, this[i], i, this)) return false; } return true; }, filter: function(fn, bind){ var results = []; for (var i = 0, l = this.length; i < l; i++){ if (fn.call(bind, this[i], i, this)) results.push(this[i]); } return results; }, clean: function(){ return this.filter($defined); }, indexOf: function(item, from){ var len = this.length; for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){ if (this[i] === item) return i; } return -1; }, map: function(fn, bind){ var results = []; for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this); return results; }, some: function(fn, bind){ for (var i = 0, l = this.length; i < l; i++){ if (fn.call(bind, this[i], i, this)) return true; } return false; }, associate: function(keys){ var obj = {}, length = Math.min(this.length, keys.length); for (var i = 0; i < length; i++) obj[keys[i]] = this[i]; return obj; }, link: function(object){ var result = {}; for (var i = 0, l = this.length; i < l; i++){ for (var key in object){ if (object[key](this[i])){ result[key] = this[i]; delete object[key]; break; } } } return result; }, contains: function(item, from){ return this.indexOf(item, from) != -1; }, extend: function(array){ for (var i = 0, j = array.length; i < j; i++) this.push(array[i]); return this; }, getLast: function(){ return (this.length) ? this[this.length - 1] : null; }, getRandom: function(){ return (this.length) ? this[$random(0, this.length - 1)] : null; }, include: function(item){ if (!this.contains(item)) this.push(item); return this; }, combine: function(array){ for (var i = 0, l = array.length; i < l; i++) this.include(array[i]); return this; }, erase: function(item){ for (var i = this.length; i--; i){ if (this[i] === item) this.splice(i, 1); } return this; }, empty: function(){ this.length = 0; return this; }, flatten: function(){ var array = []; for (var i = 0, l = this.length; i < l; i++){ var type = $type(this[i]); if (!type) continue; array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]); } return array; }, hexToRgb: function(array){ if (this.length != 3) return null; var rgb = this.map(function(value){ if (value.length == 1) value += value; return value.toInt(16); }); return (array) ? rgb : 'rgb(' + rgb + ')'; }, rgbToHex: function(array){ if (this.length < 3) return null; if (this.length == 4 && this[3] == 0 && !array) return 'transparent'; var hex = []; for (var i = 0; i < 3; i++){ var bit = (this[i] - 0).toString(16); hex.push((bit.length == 1) ? '0' + bit : bit); } return (array) ? hex : '#' + hex.join(''); } }); /* --- script: Function.js description: Contains Function Prototypes like create, bind, pass, and delay. license: MIT-style license. requires: - /Native - /$util provides: [Function] ... */ Function.implement({ extend: function(properties){ for (var property in properties) this[property] = properties[property]; return this; }, create: function(options){ var self = this; options = options || {}; return function(event){ var args = options.arguments; args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0); if (options.event) args = [event || window.event].extend(args); var returns = function(){ return self.apply(options.bind || null, args); }; if (options.delay) return setTimeout(returns, options.delay); if (options.periodical) return setInterval(returns, options.periodical); if (options.attempt) return $try(returns); return returns(); }; }, run: function(args, bind){ return this.apply(bind, $splat(args)); }, pass: function(args, bind){ return this.create({bind: bind, arguments: args}); }, bind: function(bind, args){ return this.create({bind: bind, arguments: args}); }, bindWithEvent: function(bind, args){ return this.create({bind: bind, arguments: args, event: true}); }, attempt: function(args, bind){ return this.create({bind: bind, arguments: args, attempt: true})(); }, delay: function(delay, bind, args){ return this.create({bind: bind, arguments: args, delay: delay})(); }, periodical: function(periodical, bind, args){ return this.create({bind: bind, arguments: args, periodical: periodical})(); } }); /* --- script: Number.js description: Contains Number Prototypes like limit, round, times, and ceil. license: MIT-style license. requires: - /Native - /$util provides: [Number] ... */ Number.implement({ limit: function(min, max){ return Math.min(max, Math.max(min, this)); }, round: function(precision){ precision = Math.pow(10, precision || 0); return Math.round(this * precision) / precision; }, times: function(fn, bind){ for (var i = 0; i < this; i++) fn.call(bind, i, this); }, toFloat: function(){ return parseFloat(this); }, toInt: function(base){ return parseInt(this, base || 10); } }); Number.alias('times', 'each'); (function(math){ var methods = {}; math.each(function(name){ if (!Number[name]) methods[name] = function(){ return Math[name].apply(null, [this].concat($A(arguments))); }; }); Number.implement(methods); })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']); /* --- script: String.js description: Contains String Prototypes like camelCase, capitalize, test, and toInt. license: MIT-style license. requires: - /Native provides: [String] ... */ String.implement({ test: function(regex, params){ return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this); }, contains: function(string, separator){ return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1; }, trim: function(){ return this.replace(/^\s+|\s+$/g, ''); }, clean: function(){ return this.replace(/\s+/g, ' ').trim(); }, camelCase: function(){ return this.replace(/-\D/g, function(match){ return match.charAt(1).toUpperCase(); }); }, hyphenate: function(){ return this.replace(/[A-Z]/g, function(match){ return ('-' + match.charAt(0).toLowerCase()); }); }, capitalize: function(){ return this.replace(/\b[a-z]/g, function(match){ return match.toUpperCase(); }); }, escapeRegExp: function(){ return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); }, toInt: function(base){ return parseInt(this, base || 10); }, toFloat: function(){ return parseFloat(this); }, hexToRgb: function(array){ var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); return (hex) ? hex.slice(1).hexToRgb(array) : null; }, rgbToHex: function(array){ var rgb = this.match(/\d{1,3}/g); return (rgb) ? rgb.rgbToHex(array) : null; }, stripScripts: function(option){ var scripts = ''; var text = this.replace(/]*>([\s\S]*?)<\/script>/gi, function(){ scripts += arguments[1] + '\n'; return ''; }); if (option === true) $exec(scripts); else if ($type(option) == 'function') option(scripts, text); return text; }, substitute: function(object, regexp){ return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){ if (match.charAt(0) == '\\') return match.slice(1); return (object[name] != undefined) ? object[name] : ''; }); } }); /* --- script: Hash.js description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects. license: MIT-style license. requires: - /Hash.base provides: [Hash] ... */ Hash.implement({ has: Object.prototype.hasOwnProperty, keyOf: function(value){ for (var key in this){ if (this.hasOwnProperty(key) && this[key] === value) return key; } return null; }, hasValue: function(value){ return (Hash.keyOf(this, value) !== null); }, extend: function(properties){ Hash.each(properties || {}, function(value, key){ Hash.set(this, key, value); }, this); return this; }, combine: function(properties){ Hash.each(properties || {}, function(value, key){ Hash.include(this, key, value); }, this); return this; }, erase: function(key){ if (this.hasOwnProperty(key)) delete this[key]; return this; }, get: function(key){ return (this.hasOwnProperty(key)) ? this[key] : null; }, set: function(key, value){ if (!this[key] || this.hasOwnProperty(key)) this[key] = value; return this; }, empty: function(){ Hash.each(this, function(value, key){ delete this[key]; }, this); return this; }, include: function(key, value){ if (this[key] == undefined) this[key] = value; return this; }, map: function(fn, bind){ var results = new Hash; Hash.each(this, function(value, key){ results.set(key, fn.call(bind, value, key, this)); }, this); return results; }, filter: function(fn, bind){ var results = new Hash; Hash.each(this, function(value, key){ if (fn.call(bind, value, key, this)) results.set(key, value); }, this); return results; }, every: function(fn, bind){ for (var key in this){ if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false; } return true; }, some: function(fn, bind){ for (var key in this){ if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true; } return false; }, getKeys: function(){ var keys = []; Hash.each(this, function(value, key){ keys.push(key); }); return keys; }, getValues: function(){ var values = []; Hash.each(this, function(value){ values.push(value); }); return values; }, toQueryString: function(base){ var queryString = []; Hash.each(this, function(value, key){ if (base) key = base + '[' + key + ']'; var result; switch ($type(value)){ case 'object': result = Hash.toQueryString(value, key); break; case 'array': var qs = {}; value.each(function(val, i){ qs[i] = val; }); result = Hash.toQueryString(qs, key); break; default: result = key + '=' + encodeURIComponent(value); } if (value != undefined) queryString.push(result); }); return queryString.join('&'); } }); Hash.alias({keyOf: 'indexOf', hasValue: 'contains'}); /* --- script: Event.js description: Contains the Event Class, to make the event object cross-browser. license: MIT-style license. requires: - /Window - /Document - /Hash - /Array - /Function - /String provides: [Event] ... */ var Event = new Native({ name: 'Event', initialize: function(event, win){ win = win || window; var doc = win.document; event = event || win.event; if (event.$extended) return event; this.$extended = true; var type = event.type; var target = event.target || event.srcElement; while (target && target.nodeType == 3) target = target.parentNode; if (type.test(/key/)){ var code = event.which || event.keyCode; var key = Event.Keys.keyOf(code); if (type == 'keydown'){ var fKey = code - 111; if (fKey > 0 && fKey < 13) key = 'f' + fKey; } key = key || String.fromCharCode(code).toLowerCase(); } else if (type.match(/(click|mouse|menu)/i)){ doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body; var page = { x: event.pageX || event.clientX + doc.scrollLeft, y: event.pageY || event.clientY + doc.scrollTop }; var client = { x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX, y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY }; if (type.match(/DOMMouseScroll|mousewheel/)){ var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3; } var rightClick = (event.which == 3) || (event.button == 2); var related = null; if (type.match(/over|out/)){ switch (type){ case 'mouseover': related = event.relatedTarget || event.fromElement; break; case 'mouseout': related = event.relatedTarget || event.toElement; } if (!(function(){ while (related && related.nodeType == 3) related = related.parentNode; return true; }).create({attempt: Browser.Engine.gecko})()) related = false; } } return $extend(this, { event: event, type: type, page: page, client: client, rightClick: rightClick, wheel: wheel, relatedTarget: related, target: target, code: code, key: key, shift: event.shiftKey, control: event.ctrlKey, alt: event.altKey, meta: event.metaKey }); } }); Event.Keys = new Hash({ 'enter': 13, 'up': 38, 'down': 40, 'left': 37, 'right': 39, 'esc': 27, 'space': 32, 'backspace': 8, 'tab': 9, 'delete': 46 }); Event.implement({ stop: function(){ return this.stopPropagation().preventDefault(); }, stopPropagation: function(){ if (this.event.stopPropagation) this.event.stopPropagation(); else this.event.cancelBubble = true; return this; }, preventDefault: function(){ if (this.event.preventDefault) this.event.preventDefault(); else this.event.returnValue = false; return this; } }); /* --- script: Class.js description: Contains the Class Function for easily creating, extending, and implementing reusable Classes. license: MIT-style license. requires: - /$util - /Native - /Array - /String - /Function - /Number - /Hash provides: [Class] ... */ function Class(params){ if (params instanceof Function) params = {initialize: params}; var newClass = function(){ Object.reset(this); if (newClass._prototyping) return this; this._current = $empty; var value = (this.initialize) ? this.initialize.apply(this, arguments) : this; delete this._current; delete this.caller; return value; }.extend(this); newClass.implement(params); newClass.constructor = Class; newClass.prototype.constructor = newClass; return newClass; }; Function.prototype.protect = function(){ this._protected = true; return this; }; Object.reset = function(object, key){ if (key == null){ for (var p in object) Object.reset(object, p); return object; } delete object[key]; switch ($type(object[key])){ case 'object': var F = function(){}; F.prototype = object[key]; var i = new F; object[key] = Object.reset(i); break; case 'array': object[key] = $unlink(object[key]); break; } return object; }; new Native({name: 'Class', initialize: Class}).extend({ instantiate: function(F){ F._prototyping = true; var proto = new F; delete F._prototyping; return proto; }, wrap: function(self, key, method){ if (method._origin) method = method._origin; return function(){ if (method._protected && this._current == null) throw new Error('The method "' + key + '" cannot be called.'); var caller = this.caller, current = this._current; this.caller = current; this._current = arguments.callee; var result = method.apply(this, arguments); this._current = current; this.caller = caller; return result; }.extend({_owner: self, _origin: method, _name: key}); } }); Class.implement({ implement: function(key, value){ if ($type(key) == 'object'){ for (var p in key) this.implement(p, key[p]); return this; } var mutator = Class.Mutators[key]; if (mutator){ value = mutator.call(this, value); if (value == null) return this; } var proto = this.prototype; switch ($type(value)){ case 'function': if (value._hidden) return this; proto[key] = Class.wrap(this, key, value); break; case 'object': var previous = proto[key]; if ($type(previous) == 'object') $mixin(previous, value); else proto[key] = $unlink(value); break; case 'array': proto[key] = $unlink(value); break; default: proto[key] = value; } return this; } }); Class.Mutators = { Extends: function(parent){ this.parent = parent; this.prototype = Class.instantiate(parent); this.implement('parent', function(){ var name = this.caller._name, previous = this.caller._owner.parent.prototype[name]; if (!previous) throw new Error('The method "' + name + '" has no parent.'); return previous.apply(this, arguments); }.protect()); }, Implements: function(items){ $splat(items).each(function(item){ if (item instanceof Function) item = Class.instantiate(item); this.implement(item); }, this); } }; /* --- script: Class.Extras.js description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks. license: MIT-style license. requires: - /Class provides: [Chain, Events, Options] ... */ var Chain = new Class({ $chain: [], chain: function(){ this.$chain.extend(Array.flatten(arguments)); return this; }, callChain: function(){ return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false; }, clearChain: function(){ this.$chain.empty(); return this; } }); var Events = new Class({ $events: {}, addEvent: function(type, fn, internal){ type = Events.removeOn(type); if (fn != $empty){ this.$events[type] = this.$events[type] || []; this.$events[type].include(fn); if (internal) fn.internal = true; } return this; }, addEvents: function(events){ for (var type in events) this.addEvent(type, events[type]); return this; }, fireEvent: function(type, args, delay){ type = Events.removeOn(type); if (!this.$events || !this.$events[type]) return this; this.$events[type].each(function(fn){ fn.create({'bind': this, 'delay': delay, 'arguments': args})(); }, this); return this; }, removeEvent: function(type, fn){ type = Events.removeOn(type); if (!this.$events[type]) return this; if (!fn.internal) this.$events[type].erase(fn); return this; }, removeEvents: function(events){ var type; if ($type(events) == 'object'){ for (type in events) this.removeEvent(type, events[type]); return this; } if (events) events = Events.removeOn(events); for (type in this.$events){ if (events && events != type) continue; var fns = this.$events[type]; for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]); } return this; } }); Events.removeOn = function(string){ return string.replace(/^on([A-Z])/, function(full, first){ return first.toLowerCase(); }); }; var Options = new Class({ setOptions: function(){ this.options = $merge.run([this.options].extend(arguments)); if (!this.addEvent) return this; for (var option in this.options){ if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue; this.addEvent(option, this.options[option]); delete this.options[option]; } return this; } }); /* --- script: Element.js description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements. license: MIT-style license. requires: - /Window - /Document - /Array - /String - /Function - /Number - /Hash provides: [Element, Elements, $, $$, Iframe] ... */ var Element = new Native({ name: 'Element', legacy: window.Element, initialize: function(tag, props){ var konstructor = Element.Constructors.get(tag); if (konstructor) return konstructor(props); if (typeof tag == 'string') return document.newElement(tag, props); return document.id(tag).set(props); }, afterImplement: function(key, value){ Element.Prototype[key] = value; if (Array[key]) return; Elements.implement(key, function(){ var items = [], elements = true; for (var i = 0, j = this.length; i < j; i++){ var returns = this[i][key].apply(this[i], arguments); items.push(returns); if (elements) elements = ($type(returns) == 'element'); } return (elements) ? new Elements(items) : items; }); } }); Element.Prototype = {$family: {name: 'element'}}; Element.Constructors = new Hash; var IFrame = new Native({ name: 'IFrame', generics: false, initialize: function(){ var params = Array.link(arguments, {properties: Object.type, iframe: $defined}); var props = params.properties || {}; var iframe = document.id(params.iframe); var onload = props.onload || $empty; delete props.onload; props.id = props.name = $pick(props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + $time()); iframe = new Element(iframe || 'iframe', props); var onFrameLoad = function(){ var host = $try(function(){ return iframe.contentWindow.location.host; }); if (!host || host == window.location.host){ var win = new Window(iframe.contentWindow); new Document(iframe.contentWindow.document); $extend(win.Element.prototype, Element.Prototype); } onload.call(iframe.contentWindow, iframe.contentWindow.document); }; var contentWindow = $try(function(){ return iframe.contentWindow; }); ((contentWindow && contentWindow.document.body) || window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad); return iframe; } }); var Elements = new Native({ initialize: function(elements, options){ options = $extend({ddup: true, cash: true}, options); elements = elements || []; if (options.ddup || options.cash){ var uniques = {}, returned = []; for (var i = 0, l = elements.length; i < l; i++){ var el = document.id(elements[i], !options.cash); if (options.ddup){ if (uniques[el.uid]) continue; uniques[el.uid] = true; } if (el) returned.push(el); } elements = returned; } return (options.cash) ? $extend(elements, this) : elements; } }); Elements.implement({ filter: function(filter, bind){ if (!filter) return this; return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){ return item.match(filter); } : filter, bind)); } }); Document.implement({ newElement: function(tag, props){ if (Browser.Engine.trident && props){ ['name', 'type', 'checked'].each(function(attribute){ if (!props[attribute]) return; tag += ' ' + attribute + '="' + props[attribute] + '"'; if (attribute != 'checked') delete props[attribute]; }); tag = '<' + tag + '>'; } return document.id(this.createElement(tag)).set(props); }, newTextNode: function(text){ return this.createTextNode(text); }, getDocument: function(){ return this; }, getWindow: function(){ return this.window; }, id: (function(){ var types = { string: function(id, nocash, doc){ id = doc.getElementById(id); return (id) ? types.element(id, nocash) : null; }, element: function(el, nocash){ $uid(el); if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){ var proto = Element.Prototype; for (var p in proto) el[p] = proto[p]; }; return el; }, object: function(obj, nocash, doc){ if (obj.toElement) return types.element(obj.toElement(doc), nocash); return null; } }; types.textnode = types.whitespace = types.window = types.document = $arguments(0); return function(el, nocash, doc){ if (el && el.$family && el.uid) return el; var type = $type(el); return (types[type]) ? types[type](el, nocash, doc || document) : null; }; })() }); if (window.$ == null) Window.implement({ $: function(el, nc){ return document.id(el, nc, this.document); } }); Window.implement({ $$: function(selector){ if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector); var elements = []; var args = Array.flatten(arguments); for (var i = 0, l = args.length; i < l; i++){ var item = args[i]; switch ($type(item)){ case 'element': elements.push(item); break; case 'string': elements.extend(this.document.getElements(item, true)); } } return new Elements(elements); }, getDocument: function(){ return this.document; }, getWindow: function(){ return this; } }); Native.implement([Element, Document], { getElement: function(selector, nocash){ return document.id(this.getElements(selector, true)[0] || null, nocash); }, getElements: function(tags, nocash){ tags = tags.split(','); var elements = []; var ddup = (tags.length > 1); tags.each(function(tag){ var partial = this.getElementsByTagName(tag.trim()); (ddup) ? elements.extend(partial) : elements = partial; }, this); return new Elements(elements, {ddup: ddup, cash: !nocash}); } }); (function(){ var collected = {}, storage = {}; var props = {input: 'checked', option: 'selected', textarea: (Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerHTML' : 'value'}; var get = function(uid){ return (storage[uid] || (storage[uid] = {})); }; var clean = function(item, retain){ if (!item) return; var uid = item.uid; if (Browser.Engine.trident){ if (item.clearAttributes){ var clone = retain && item.cloneNode(false); item.clearAttributes(); if (clone) item.mergeAttributes(clone); } else if (item.removeEvents){ item.removeEvents(); } if ((/object/i).test(item.tagName)){ for (var p in item){ if (typeof item[p] == 'function') item[p] = $empty; } Element.dispose(item); } } if (!uid) return; collected[uid] = storage[uid] = null; }; var purge = function(){ Hash.each(collected, clean); if (Browser.Engine.trident) $A(document.getElementsByTagName('object')).each(clean); if (window.CollectGarbage) CollectGarbage(); collected = storage = null; }; var walk = function(element, walk, start, match, all, nocash){ var el = element[start || walk]; var elements = []; while (el){ if (el.nodeType == 1 && (!match || Element.match(el, match))){ if (!all) return document.id(el, nocash); elements.push(el); } el = el[walk]; } return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : null; }; var attributes = { 'html': 'innerHTML', 'class': 'className', 'for': 'htmlFor', 'defaultValue': 'defaultValue', 'text': (Browser.Engine.trident || (Browser.Engine.webkit && Browser.Engine.version < 420)) ? 'innerText' : 'textContent' }; var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer']; var camels = ['value', 'type', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap']; bools = bools.associate(bools); Hash.extend(attributes, bools); Hash.extend(attributes, camels.associate(camels.map(String.toLowerCase))); var inserters = { before: function(context, element){ if (element.parentNode) element.parentNode.insertBefore(context, element); }, after: function(context, element){ if (!element.parentNode) return; var next = element.nextSibling; (next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context); }, bottom: function(context, element){ element.appendChild(context); }, top: function(context, element){ var first = element.firstChild; (first) ? element.insertBefore(context, first) : element.appendChild(context); } }; inserters.inside = inserters.bottom; Hash.each(inserters, function(inserter, where){ where = where.capitalize(); Element.implement('inject' + where, function(el){ inserter(this, document.id(el, true)); return this; }); Element.implement('grab' + where, function(el){ inserter(document.id(el, true), this); return this; }); }); Element.implement({ set: function(prop, value){ switch ($type(prop)){ case 'object': for (var p in prop) this.set(p, prop[p]); break; case 'string': var property = Element.Properties.get(prop); (property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value); } return this; }, get: function(prop){ var property = Element.Properties.get(prop); return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop); }, erase: function(prop){ var property = Element.Properties.get(prop); (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop); return this; }, setProperty: function(attribute, value){ var key = attributes[attribute]; if (value == undefined) return this.removeProperty(attribute); if (key && bools[attribute]) value = !!value; (key) ? this[key] = value : this.setAttribute(attribute, '' + value); return this; }, setProperties: function(attributes){ for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]); return this; }, getProperty: function(attribute){ var key = attributes[attribute]; var value = (key) ? this[key] : this.getAttribute(attribute, 2); return (bools[attribute]) ? !!value : (key) ? value : value || null; }, getProperties: function(){ var args = $A(arguments); return args.map(this.getProperty, this).associate(args); }, removeProperty: function(attribute){ var key = attributes[attribute]; (key) ? this[key] = (key && bools[attribute]) ? false : '' : this.removeAttribute(attribute); return this; }, removeProperties: function(){ Array.each(arguments, this.removeProperty, this); return this; }, hasClass: function(className){ return this.className.contains(className, ' '); }, addClass: function(className){ if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean(); return this; }, removeClass: function(className){ this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1'); return this; }, toggleClass: function(className){ return this.hasClass(className) ? this.removeClass(className) : this.addClass(className); }, adopt: function(){ Array.flatten(arguments).each(function(element){ element = document.id(element, true); if (element) this.appendChild(element); }, this); return this; }, appendText: function(text, where){ return this.grab(this.getDocument().newTextNode(text), where); }, grab: function(el, where){ inserters[where || 'bottom'](document.id(el, true), this); return this; }, inject: function(el, where){ inserters[where || 'bottom'](this, document.id(el, true)); return this; }, replaces: function(el){ el = document.id(el, true); el.parentNode.replaceChild(this, el); return this; }, wraps: function(el, where){ el = document.id(el, true); return this.replaces(el).grab(el, where); }, getPrevious: function(match, nocash){ return walk(this, 'previousSibling', null, match, false, nocash); }, getAllPrevious: function(match, nocash){ return walk(this, 'previousSibling', null, match, true, nocash); }, getNext: function(match, nocash){ return walk(this, 'nextSibling', null, match, false, nocash); }, getAllNext: function(match, nocash){ return walk(this, 'nextSibling', null, match, true, nocash); }, getFirst: function(match, nocash){ return walk(this, 'nextSibling', 'firstChild', match, false, nocash); }, getLast: function(match, nocash){ return walk(this, 'previousSibling', 'lastChild', match, false, nocash); }, getParent: function(match, nocash){ return walk(this, 'parentNode', null, match, false, nocash); }, getParents: function(match, nocash){ return walk(this, 'parentNode', null, match, true, nocash); }, getSiblings: function(match, nocash){ return this.getParent().getChildren(match, nocash).erase(this); }, getChildren: function(match, nocash){ return walk(this, 'nextSibling', 'firstChild', match, true, nocash); }, getWindow: function(){ return this.ownerDocument.window; }, getDocument: function(){ return this.ownerDocument; }, getElementById: function(id, nocash){ var el = this.ownerDocument.getElementById(id); if (!el) return null; for (var parent = el.parentNode; parent != this; parent = parent.parentNode){ if (!parent) return null; } return document.id(el, nocash); }, getSelected: function(){ return new Elements($A(this.options).filter(function(option){ return option.selected; })); }, getComputedStyle: function(property){ if (this.currentStyle) return this.currentStyle[property.camelCase()]; var computed = this.getDocument().defaultView.getComputedStyle(this, null); return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null; }, toQueryString: function(){ var queryString = []; this.getElements('input, select, textarea', true).each(function(el){ if (!el.name || el.disabled || el.type == 'submit' || el.type == 'reset' || el.type == 'file') return; var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){ return opt.value; }) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value; $splat(value).each(function(val){ if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val)); }); }); return queryString.join('&'); }, clone: function(contents, keepid){ contents = contents !== false; var clone = this.cloneNode(contents); var clean = function(node, element){ if (!keepid) node.removeAttribute('id'); if (Browser.Engine.trident){ node.clearAttributes(); node.mergeAttributes(element); node.removeAttribute('uid'); if (node.options){ var no = node.options, eo = element.options; for (var j = no.length; j--;) no[j].selected = eo[j].selected; } } var prop = props[element.tagName.toLowerCase()]; if (prop && element[prop]) node[prop] = element[prop]; }; if (contents){ var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*'); for (var i = ce.length; i--;) clean(ce[i], te[i]); } clean(clone, this); return document.id(clone); }, destroy: function(){ Element.empty(this); Element.dispose(this); clean(this, true); return null; }, empty: function(){ $A(this.childNodes).each(function(node){ Element.destroy(node); }); return this; }, dispose: function(){ return (this.parentNode) ? this.parentNode.removeChild(this) : this; }, hasChild: function(el){ el = document.id(el, true); if (!el) return false; if (Browser.Engine.webkit && Browser.Engine.version < 420) return $A(this.getElementsByTagName(el.tagName)).contains(el); return (this.contains) ? (this != el && this.contains(el)) : !!(this.compareDocumentPosition(el) & 16); }, match: function(tag){ return (!tag || (tag == this) || (Element.get(this, 'tag') == tag)); } }); Native.implement([Element, Window, Document], { addListener: function(type, fn){ if (type == 'unload'){ var old = fn, self = this; fn = function(){ self.removeListener('unload', fn); old(); }; } else { collected[this.uid] = this; } if (this.addEventListener) this.addEventListener(type, fn, false); else this.attachEvent('on' + type, fn); return this; }, removeListener: function(type, fn){ if (this.removeEventListener) this.removeEventListener(type, fn, false); else this.detachEvent('on' + type, fn); return this; }, retrieve: function(property, dflt){ var storage = get(this.uid), prop = storage[property]; if (dflt != undefined && prop == undefined) prop = storage[property] = dflt; return $pick(prop); }, store: function(property, value){ var storage = get(this.uid); storage[property] = value; return this; }, eliminate: function(property){ var storage = get(this.uid); delete storage[property]; return this; } }); window.addListener('unload', purge); })(); Element.Properties = new Hash; Element.Properties.style = { set: function(style){ this.style.cssText = style; }, get: function(){ return this.style.cssText; }, erase: function(){ this.style.cssText = ''; } }; Element.Properties.tag = { get: function(){ return this.tagName.toLowerCase(); } }; Element.Properties.html = (function(){ var wrapper = document.createElement('div'); var translations = { table: [1, '', '
'], select: [1, ''], tbody: [2, '', '
'], tr: [3, '', '
'] }; translations.thead = translations.tfoot = translations.tbody; var html = { set: function(){ var html = Array.flatten(arguments).join(''); var wrap = Browser.Engine.trident && translations[this.get('tag')]; if (wrap){ var first = wrapper; first.innerHTML = wrap[1] + html + wrap[2]; for (var i = wrap[0]; i--;) first = first.firstChild; this.empty().adopt(first.childNodes); } else { this.innerHTML = html; } } }; html.erase = html.set; return html; })(); if (Browser.Engine.webkit && Browser.Engine.version < 420) Element.Properties.text = { get: function(){ if (this.innerText) return this.innerText; var temp = this.ownerDocument.newElement('div', {html: this.innerHTML}).inject(this.ownerDocument.body); var text = temp.innerText; temp.destroy(); return text; } }; /* --- script: Element.Event.js description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events. license: MIT-style license. requires: - /Element - /Event provides: [Element.Event] ... */ Element.Properties.events = {set: function(events){ this.addEvents(events); }}; Native.implement([Element, Window, Document], { addEvent: function(type, fn){ var events = this.retrieve('events', {}); events[type] = events[type] || {'keys': [], 'values': []}; if (events[type].keys.contains(fn)) return this; events[type].keys.push(fn); var realType = type, custom = Element.Events.get(type), condition = fn, self = this; if (custom){ if (custom.onAdd) custom.onAdd.call(this, fn); if (custom.condition){ condition = function(event){ if (custom.condition.call(this, event)) return fn.call(this, event); return true; }; } realType = custom.base || realType; } var defn = function(){ return fn.call(self); }; var nativeEvent = Element.NativeEvents[realType]; if (nativeEvent){ if (nativeEvent == 2){ defn = function(event){ event = new Event(event, self.getWindow()); if (condition.call(self, event) === false) event.stop(); }; } this.addListener(realType, defn); } events[type].values.push(defn); return this; }, removeEvent: function(type, fn){ var events = this.retrieve('events'); if (!events || !events[type]) return this; var pos = events[type].keys.indexOf(fn); if (pos == -1) return this; events[type].keys.splice(pos, 1); var value = events[type].values.splice(pos, 1)[0]; var custom = Element.Events.get(type); if (custom){ if (custom.onRemove) custom.onRemove.call(this, fn); type = custom.base || type; } return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this; }, addEvents: function(events){ for (var event in events) this.addEvent(event, events[event]); return this; }, removeEvents: function(events){ var type; if ($type(events) == 'object'){ for (type in events) this.removeEvent(type, events[type]); return this; } var attached = this.retrieve('events'); if (!attached) return this; if (!events){ for (type in attached) this.removeEvents(type); this.eliminate('events'); } else if (attached[events]){ while (attached[events].keys[0]) this.removeEvent(events, attached[events].keys[0]); attached[events] = null; } return this; }, fireEvent: function(type, args, delay){ var events = this.retrieve('events'); if (!events || !events[type]) return this; events[type].keys.each(function(fn){ fn.create({'bind': this, 'delay': delay, 'arguments': args})(); }, this); return this; }, cloneEvents: function(from, type){ from = document.id(from); var fevents = from.retrieve('events'); if (!fevents) return this; if (!type){ for (var evType in fevents) this.cloneEvents(from, evType); } else if (fevents[type]){ fevents[type].keys.each(function(fn){ this.addEvent(type, fn); }, this); } return this; } }); Element.NativeEvents = { click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons mousewheel: 2, DOMMouseScroll: 2, //mouse wheel mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement keydown: 2, keypress: 2, keyup: 2, //keyboard focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window error: 1, abort: 1, scroll: 1 //misc }; (function(){ var $check = function(event){ var related = event.relatedTarget; if (related == undefined) return true; if (related === false) return false; return ($type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related)); }; Element.Events = new Hash({ mouseenter: { base: 'mouseover', condition: $check }, mouseleave: { base: 'mouseout', condition: $check }, mousewheel: { base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel' } }); })(); /* --- script: Element.Style.js description: Contains methods for interacting with the styles of Elements in a fashionable way. license: MIT-style license. requires: - /Element provides: [Element.Style] ... */ Element.Properties.styles = {set: function(styles){ this.setStyles(styles); }}; Element.Properties.opacity = { set: function(opacity, novisibility){ if (!novisibility){ if (opacity == 0){ if (this.style.visibility != 'hidden') this.style.visibility = 'hidden'; } else { if (this.style.visibility != 'visible') this.style.visibility = 'visible'; } } if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1; if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')'; this.style.opacity = opacity; this.store('opacity', opacity); }, get: function(){ return this.retrieve('opacity', 1); } }; Element.implement({ setOpacity: function(value){ return this.set('opacity', value, true); }, getOpacity: function(){ return this.get('opacity'); }, setStyle: function(property, value){ switch (property){ case 'opacity': return this.set('opacity', parseFloat(value)); case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat'; } property = property.camelCase(); if ($type(value) != 'string'){ var map = (Element.Styles.get(property) || '@').split(' '); value = $splat(value).map(function(val, i){ if (!map[i]) return ''; return ($type(val) == 'number') ? map[i].replace('@', Math.round(val)) : val; }).join(' '); } else if (value == String(Number(value))){ value = Math.round(value); } this.style[property] = value; return this; }, getStyle: function(property){ switch (property){ case 'opacity': return this.get('opacity'); case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat'; } property = property.camelCase(); var result = this.style[property]; if (!$chk(result)){ result = []; for (var style in Element.ShortStyles){ if (property != style) continue; for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s)); return result.join(' '); } result = this.getComputedStyle(property); } if (result){ result = String(result); var color = result.match(/rgba?\([\d\s,]+\)/); if (color) result = result.replace(color[0], color[0].rgbToHex()); } if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result, 10)))){ if (property.test(/^(height|width)$/)){ var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0; values.each(function(value){ size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt(); }, this); return this['offset' + property.capitalize()] - size + 'px'; } if ((Browser.Engine.presto) && String(result).test('px')) return result; if (property.test(/(border(.+)Width|margin|padding)/)) return '0px'; } return result; }, setStyles: function(styles){ for (var style in styles) this.setStyle(style, styles[style]); return this; }, getStyles: function(){ var result = {}; Array.flatten(arguments).each(function(key){ result[key] = this.getStyle(key); }, this); return result; } }); Element.Styles = new Hash({ left: '@px', top: '@px', bottom: '@px', right: '@px', width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px', backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)', fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)', margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)', borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)', zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@' }); Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}}; ['Top', 'Right', 'Bottom', 'Left'].each(function(direction){ var Short = Element.ShortStyles; var All = Element.Styles; ['margin', 'padding'].each(function(style){ var sd = style + direction; Short[style][sd] = All[sd] = '@px'; }); var bd = 'border' + direction; Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)'; var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color'; Short[bd] = {}; Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px'; Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@'; Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)'; }); /* --- script: Element.Dimensions.js description: Contains methods to work with size, scroll, or positioning of Elements and the window object. license: MIT-style license. credits: - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html). - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html). requires: - /Element provides: [Element.Dimensions] ... */ (function(){ Element.implement({ scrollTo: function(x, y){ if (isBody(this)){ this.getWindow().scrollTo(x, y); } else { this.scrollLeft = x; this.scrollTop = y; } return this; }, getSize: function(){ if (isBody(this)) return this.getWindow().getSize(); return {x: this.offsetWidth, y: this.offsetHeight}; }, getScrollSize: function(){ if (isBody(this)) return this.getWindow().getScrollSize(); return {x: this.scrollWidth, y: this.scrollHeight}; }, getScroll: function(){ if (isBody(this)) return this.getWindow().getScroll(); return {x: this.scrollLeft, y: this.scrollTop}; }, getScrolls: function(){ var element = this, position = {x: 0, y: 0}; while (element && !isBody(element)){ position.x += element.scrollLeft; position.y += element.scrollTop; element = element.parentNode; } return position; }, getOffsetParent: function(){ var element = this; if (isBody(element)) return null; if (!Browser.Engine.trident) return element.offsetParent; while ((element = element.parentNode) && !isBody(element)){ if (styleString(element, 'position') != 'static') return element; } return null; }, getOffsets: function(){ if (this.getBoundingClientRect){ var bound = this.getBoundingClientRect(), html = document.id(this.getDocument().documentElement), htmlScroll = html.getScroll(), elemScrolls = this.getScrolls(), elemScroll = this.getScroll(), isFixed = (styleString(this, 'position') == 'fixed'); return { x: bound.left.toInt() + elemScrolls.x - elemScroll.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft, y: bound.top.toInt() + elemScrolls.y - elemScroll.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop }; } var element = this, position = {x: 0, y: 0}; if (isBody(this)) return position; while (element && !isBody(element)){ position.x += element.offsetLeft; position.y += element.offsetTop; if (Browser.Engine.gecko){ if (!borderBox(element)){ position.x += leftBorder(element); position.y += topBorder(element); } var parent = element.parentNode; if (parent && styleString(parent, 'overflow') != 'visible'){ position.x += leftBorder(parent); position.y += topBorder(parent); } } else if (element != this && Browser.Engine.webkit){ position.x += leftBorder(element); position.y += topBorder(element); } element = element.offsetParent; } if (Browser.Engine.gecko && !borderBox(this)){ position.x -= leftBorder(this); position.y -= topBorder(this); } return position; }, getPosition: function(relative){ if (isBody(this)) return {x: 0, y: 0}; var offset = this.getOffsets(), scroll = this.getScrolls(); var position = { x: offset.x - scroll.x, y: offset.y - scroll.y }; var relativePosition = (relative && (relative = document.id(relative))) ? relative.getPosition() : {x: 0, y: 0}; return {x: position.x - relativePosition.x, y: position.y - relativePosition.y}; }, getCoordinates: function(element){ if (isBody(this)) return this.getWindow().getCoordinates(); var position = this.getPosition(element), size = this.getSize(); var obj = { left: position.x, top: position.y, width: size.x, height: size.y }; obj.right = obj.left + obj.width; obj.bottom = obj.top + obj.height; return obj; }, computePosition: function(obj){ return { left: obj.x - styleNumber(this, 'margin-left'), top: obj.y - styleNumber(this, 'margin-top') }; }, setPosition: function(obj){ return this.setStyles(this.computePosition(obj)); } }); Native.implement([Document, Window], { getSize: function(){ if (Browser.Engine.presto || Browser.Engine.webkit){ var win = this.getWindow(); return {x: win.innerWidth, y: win.innerHeight}; } var doc = getCompatElement(this); return {x: doc.clientWidth, y: doc.clientHeight}; }, getScroll: function(){ var win = this.getWindow(), doc = getCompatElement(this); return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop}; }, getScrollSize: function(){ var doc = getCompatElement(this), min = this.getSize(); return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)}; }, getPosition: function(){ return {x: 0, y: 0}; }, getCoordinates: function(){ var size = this.getSize(); return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x}; } }); // private methods var styleString = Element.getComputedStyle; function styleNumber(element, style){ return styleString(element, style).toInt() || 0; }; function borderBox(element){ return styleString(element, '-moz-box-sizing') == 'border-box'; }; function topBorder(element){ return styleNumber(element, 'border-top-width'); }; function leftBorder(element){ return styleNumber(element, 'border-left-width'); }; function isBody(element){ return (/^(?:body|html)$/i).test(element.tagName); }; function getCompatElement(element){ var doc = element.getDocument(); return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body; }; })(); //aliases Element.alias('setPosition', 'position'); //compatability Native.implement([Window, Document, Element], { getHeight: function(){ return this.getSize().y; }, getWidth: function(){ return this.getSize().x; }, getScrollTop: function(){ return this.getScroll().y; }, getScrollLeft: function(){ return this.getScroll().x; }, getScrollHeight: function(){ return this.getScrollSize().y; }, getScrollWidth: function(){ return this.getScrollSize().x; }, getTop: function(){ return this.getPosition().y; }, getLeft: function(){ return this.getPosition().x; } }); /* --- script: Selectors.js description: Adds advanced CSS-style querying capabilities for targeting HTML Elements. Includes pseudo selectors. license: MIT-style license. requires: - /Element provides: [Selectors] ... */ Native.implement([Document, Element], { getElements: function(expression, nocash){ expression = expression.split(','); var items, local = {}; for (var i = 0, l = expression.length; i < l; i++){ var selector = expression[i], elements = Selectors.Utils.search(this, selector, local); if (i != 0 && elements.item) elements = $A(elements); items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements); } return new Elements(items, {ddup: (expression.length > 1), cash: !nocash}); } }); Element.implement({ match: function(selector){ if (!selector || (selector == this)) return true; var tagid = Selectors.Utils.parseTagAndID(selector); var tag = tagid[0], id = tagid[1]; if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false; var parsed = Selectors.Utils.parseSelector(selector); return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true; } }); var Selectors = {Cache: {nth: {}, parsed: {}}}; Selectors.RegExps = { id: (/#([\w-]+)/), tag: (/^(\w+|\*)/), quick: (/^(\w+|\*)$/), splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g), combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)(["']?)([^\4]*?)\4)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g) }; Selectors.Utils = { chk: function(item, uniques){ if (!uniques) return true; var uid = $uid(item); if (!uniques[uid]) return uniques[uid] = true; return false; }, parseNthArgument: function(argument){ if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument]; var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/); if (!parsed) return false; var inta = parseInt(parsed[1], 10); var a = (inta || inta === 0) ? inta : 1; var special = parsed[2] || false; var b = parseInt(parsed[3], 10) || 0; if (a != 0){ b--; while (b < 1) b += a; while (b >= a) b -= a; } else { a = b; special = 'index'; } switch (special){ case 'n': parsed = {a: a, b: b, special: 'n'}; break; case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break; case 'even': parsed = {a: 2, b: 1, special: 'n'}; break; case 'first': parsed = {a: 0, special: 'index'}; break; case 'last': parsed = {special: 'last-child'}; break; case 'only': parsed = {special: 'only-child'}; break; default: parsed = {a: (a - 1), special: 'index'}; } return Selectors.Cache.nth[argument] = parsed; }, parseSelector: function(selector){ if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector]; var m, parsed = {classes: [], pseudos: [], attributes: []}; while ((m = Selectors.RegExps.combined.exec(selector))){ var cn = m[1], an = m[2], ao = m[3], av = m[5], pn = m[6], pa = m[7]; if (cn){ parsed.classes.push(cn); } else if (pn){ var parser = Selectors.Pseudo.get(pn); if (parser) parsed.pseudos.push({parser: parser, argument: pa}); else parsed.attributes.push({name: pn, operator: '=', value: pa}); } else if (an){ parsed.attributes.push({name: an, operator: ao, value: av}); } } if (!parsed.classes.length) delete parsed.classes; if (!parsed.attributes.length) delete parsed.attributes; if (!parsed.pseudos.length) delete parsed.pseudos; if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null; return Selectors.Cache.parsed[selector] = parsed; }, parseTagAndID: function(selector){ var tag = selector.match(Selectors.RegExps.tag); var id = selector.match(Selectors.RegExps.id); return [(tag) ? tag[1] : '*', (id) ? id[1] : false]; }, filter: function(item, parsed, local){ var i; if (parsed.classes){ for (i = parsed.classes.length; i--; i){ var cn = parsed.classes[i]; if (!Selectors.Filters.byClass(item, cn)) return false; } } if (parsed.attributes){ for (i = parsed.attributes.length; i--; i){ var att = parsed.attributes[i]; if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false; } } if (parsed.pseudos){ for (i = parsed.pseudos.length; i--; i){ var psd = parsed.pseudos[i]; if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false; } } return true; }, getByTagAndID: function(ctx, tag, id){ if (id){ var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true); return (item && Selectors.Filters.byTag(item, tag)) ? [item] : []; } else { return ctx.getElementsByTagName(tag); } }, search: function(self, expression, local){ var splitters = []; var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){ splitters.push(m1); return ':)' + m2; }).split(':)'); var items, filtered, item; for (var i = 0, l = selectors.length; i < l; i++){ var selector = selectors[i]; if (i == 0 && Selectors.RegExps.quick.test(selector)){ items = self.getElementsByTagName(selector); continue; } var splitter = splitters[i - 1]; var tagid = Selectors.Utils.parseTagAndID(selector); var tag = tagid[0], id = tagid[1]; if (i == 0){ items = Selectors.Utils.getByTagAndID(self, tag, id); } else { var uniques = {}, found = []; for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques); items = found; } var parsed = Selectors.Utils.parseSelector(selector); if (parsed){ filtered = []; for (var m = 0, n = items.length; m < n; m++){ item = items[m]; if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item); } items = filtered; } } return items; } }; Selectors.Getters = { ' ': function(found, self, tag, id, uniques){ var items = Selectors.Utils.getByTagAndID(self, tag, id); for (var i = 0, l = items.length; i < l; i++){ var item = items[i]; if (Selectors.Utils.chk(item, uniques)) found.push(item); } return found; }, '>': function(found, self, tag, id, uniques){ var children = Selectors.Utils.getByTagAndID(self, tag, id); for (var i = 0, l = children.length; i < l; i++){ var child = children[i]; if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child); } return found; }, '+': function(found, self, tag, id, uniques){ while ((self = self.nextSibling)){ if (self.nodeType == 1){ if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self); break; } } return found; }, '~': function(found, self, tag, id, uniques){ while ((self = self.nextSibling)){ if (self.nodeType == 1){ if (!Selectors.Utils.chk(self, uniques)) break; if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self); } } return found; } }; Selectors.Filters = { byTag: function(self, tag){ return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag)); }, byID: function(self, id){ return (!id || (self.id && self.id == id)); }, byClass: function(self, klass){ return (self.className && self.className.contains && self.className.contains(klass, ' ')); }, byPseudo: function(self, parser, argument, local){ return parser.call(self, argument, local); }, byAttribute: function(self, name, operator, value){ var result = Element.prototype.getProperty.call(self, name); if (!result) return (operator == '!='); if (!operator || value == undefined) return true; switch (operator){ case '=': return (result == value); case '*=': return (result.contains(value)); case '^=': return (result.substr(0, value.length) == value); case '$=': return (result.substr(result.length - value.length) == value); case '!=': return (result != value); case '~=': return result.contains(value, ' '); case '|=': return result.contains(value, '-'); } return false; } }; Selectors.Pseudo = new Hash({ // w3c pseudo selectors checked: function(){ return this.checked; }, empty: function(){ return !(this.innerText || this.textContent || '').length; }, not: function(selector){ return !Element.match(this, selector); }, contains: function(text){ return (this.innerText || this.textContent || '').contains(text); }, 'first-child': function(){ return Selectors.Pseudo.index.call(this, 0); }, 'last-child': function(){ var element = this; while ((element = element.nextSibling)){ if (element.nodeType == 1) return false; } return true; }, 'only-child': function(){ var prev = this; while ((prev = prev.previousSibling)){ if (prev.nodeType == 1) return false; } var next = this; while ((next = next.nextSibling)){ if (next.nodeType == 1) return false; } return true; }, 'nth-child': function(argument, local){ argument = (argument == undefined) ? 'n' : argument; var parsed = Selectors.Utils.parseNthArgument(argument); if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local); var count = 0; local.positions = local.positions || {}; var uid = $uid(this); if (!local.positions[uid]){ var self = this; while ((self = self.previousSibling)){ if (self.nodeType != 1) continue; count ++; var position = local.positions[$uid(self)]; if (position != undefined){ count = position + count; break; } } local.positions[uid] = count; } return (local.positions[uid] % parsed.a == parsed.b); }, // custom pseudo selectors index: function(index){ var element = this, count = 0; while ((element = element.previousSibling)){ if (element.nodeType == 1 && ++count > index) return false; } return (count == index); }, even: function(argument, local){ return Selectors.Pseudo['nth-child'].call(this, '2n+1', local); }, odd: function(argument, local){ return Selectors.Pseudo['nth-child'].call(this, '2n', local); }, selected: function(){ return this.selected; }, enabled: function(){ return (this.disabled === false); } }); /* --- script: DomReady.js description: Contains the custom event domready. license: MIT-style license. requires: - /Element.Event provides: [DomReady] ... */ Element.Events.domready = { onAdd: function(fn){ if (Browser.loaded) fn.call(this); } }; (function(){ var domready = function(){ if (Browser.loaded) return; Browser.loaded = true; window.fireEvent('domready'); document.fireEvent('domready'); }; window.addEvent('load', domready); if (Browser.Engine.trident){ var temp = document.createElement('div'); (function(){ ($try(function(){ temp.doScroll(); // Technique by Diego Perini return document.id(temp).inject(document.body).set('html', 'temp').dispose(); })) ? domready() : arguments.callee.delay(50); })(); } else if (Browser.Engine.webkit && Browser.Engine.version < 525){ (function(){ (['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50); })(); } else { document.addEvent('DOMContentLoaded', domready); } })(); /* --- script: JSON.js description: JSON encoder and decoder. license: MIT-style license. See Also: requires: - /Array - /String - /Number - /Function - /Hash provides: [JSON] ... */ var JSON = new Hash(this.JSON && { stringify: JSON.stringify, parse: JSON.parse }).extend({ $specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'}, $replaceChars: function(chr){ return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16); }, encode: function(obj){ switch ($type(obj)){ case 'string': return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"'; case 'array': return '[' + String(obj.map(JSON.encode).clean()) + ']'; case 'object': case 'hash': var string = []; Hash.each(obj, function(value, key){ var json = JSON.encode(value); if (json) string.push(JSON.encode(key) + ':' + json); }); return '{' + string + '}'; case 'number': case 'boolean': return String(obj); case false: return 'null'; } return null; }, decode: function(string, secure){ if ($type(string) != 'string' || !string.length) return null; if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null; return eval('(' + string + ')'); } }); Native.implement([Hash, Array, String, Number], { toJSON: function(){ return JSON.encode(this); } }); /* --- script: Cookie.js description: Class for creating, reading, and deleting browser Cookies. license: MIT-style license. credits: - Based on the functions by Peter-Paul Koch (http://quirksmode.org). requires: - /Options provides: [Cookie] ... */ var Cookie = new Class({ Implements: Options, options: { path: false, domain: false, duration: false, secure: false, document: document }, initialize: function(key, options){ this.key = key; this.setOptions(options); }, write: function(value){ value = encodeURIComponent(value); if (this.options.domain) value += '; domain=' + this.options.domain; if (this.options.path) value += '; path=' + this.options.path; if (this.options.duration){ var date = new Date(); date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000); value += '; expires=' + date.toGMTString(); } if (this.options.secure) value += '; secure'; this.options.document.cookie = this.key + '=' + value; return this; }, read: function(){ var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)'); return (value) ? decodeURIComponent(value[1]) : null; }, dispose: function(){ new Cookie(this.key, $merge(this.options, {duration: -1})).write(''); return this; } }); Cookie.write = function(key, value, options){ return new Cookie(key, options).write(value); }; Cookie.read = function(key){ return new Cookie(key).read(); }; Cookie.dispose = function(key, options){ return new Cookie(key, options).dispose(); }; /* --- script: Swiff.js description: Wrapper for embedding SWF movies. Supports External Interface Communication. license: MIT-style license. credits: - Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject. requires: - /Options - /$util provides: [Swiff] ... */ var Swiff = new Class({ Implements: [Options], options: { id: null, height: 1, width: 1, container: null, properties: {}, params: { quality: 'high', allowScriptAccess: 'always', wMode: 'transparent', swLiveConnect: true }, callBacks: {}, vars: {} }, toElement: function(){ return this.object; }, initialize: function(path, options){ this.instance = 'Swiff_' + $time(); this.setOptions(options); options = this.options; var id = this.id = options.id || this.instance; var container = document.id(options.container); Swiff.CallBacks[this.instance] = {}; var params = options.params, vars = options.vars, callBacks = options.callBacks; var properties = $extend({height: options.height, width: options.width}, options.properties); var self = this; for (var callBack in callBacks){ Swiff.CallBacks[this.instance][callBack] = (function(option){ return function(){ return option.apply(self.object, arguments); }; })(callBacks[callBack]); vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack; } params.flashVars = Hash.toQueryString(vars); if (Browser.Engine.trident){ properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'; params.movie = path; } else { properties.type = 'application/x-shockwave-flash'; properties.data = path; } var build = ''; } build += ''; this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild; }, replaces: function(element){ element = document.id(element, true); element.parentNode.replaceChild(this.toElement(), element); return this; }, inject: function(element){ document.id(element, true).appendChild(this.toElement()); return this; }, remote: function(){ return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments)); } }); Swiff.CallBacks = {}; Swiff.remote = function(obj, fn){ var rs = obj.CallFunction('' + __flash__argumentsToXML(arguments, 2) + ''); return eval(rs); }; /* --- script: Fx.js description: Contains the basic animation logic to be extended by all other Fx Classes. license: MIT-style license. requires: - /Chain - /Events - /Options provides: [Fx] ... */ var Fx = new Class({ Implements: [Chain, Events, Options], options: { /* onStart: $empty, onCancel: $empty, onComplete: $empty, */ fps: 50, unit: false, duration: 500, link: 'ignore' }, initialize: function(options){ this.subject = this.subject || this; this.setOptions(options); this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt(); var wait = this.options.wait; if (wait === false) this.options.link = 'cancel'; }, getTransition: function(){ return function(p){ return -(Math.cos(Math.PI * p) - 1) / 2; }; }, step: function(){ var time = $time(); if (time < this.time + this.options.duration){ var delta = this.transition((time - this.time) / this.options.duration); this.set(this.compute(this.from, this.to, delta)); } else { this.set(this.compute(this.from, this.to, 1)); this.complete(); } }, set: function(now){ return now; }, compute: function(from, to, delta){ return Fx.compute(from, to, delta); }, check: function(){ if (!this.timer) return true; switch (this.options.link){ case 'cancel': this.cancel(); return true; case 'chain': this.chain(this.caller.bind(this, arguments)); return false; } return false; }, start: function(from, to){ if (!this.check(from, to)) return this; this.from = from; this.to = to; this.time = 0; this.transition = this.getTransition(); this.startTimer(); this.onStart(); return this; }, complete: function(){ if (this.stopTimer()) this.onComplete(); return this; }, cancel: function(){ if (this.stopTimer()) this.onCancel(); return this; }, onStart: function(){ this.fireEvent('start', this.subject); }, onComplete: function(){ this.fireEvent('complete', this.subject); if (!this.callChain()) this.fireEvent('chainComplete', this.subject); }, onCancel: function(){ this.fireEvent('cancel', this.subject).clearChain(); }, pause: function(){ this.stopTimer(); return this; }, resume: function(){ this.startTimer(); return this; }, stopTimer: function(){ if (!this.timer) return false; this.time = $time() - this.time; this.timer = $clear(this.timer); return true; }, startTimer: function(){ if (this.timer) return false; this.time = $time() - this.time; this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this); return true; } }); Fx.compute = function(from, to, delta){ return (to - from) * delta + from; }; Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000}; /* --- script: Fx.CSS.js description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements. license: MIT-style license. requires: - /Fx - /Element.Style provides: [Fx.CSS] ... */ Fx.CSS = new Class({ Extends: Fx, //prepares the base from/to object prepare: function(element, property, values){ values = $splat(values); var values1 = values[1]; if (!$chk(values1)){ values[1] = values[0]; values[0] = element.getStyle(property); } var parsed = values.map(this.parse); return {from: parsed[0], to: parsed[1]}; }, //parses a value into an array parse: function(value){ value = $lambda(value)(); value = (typeof value == 'string') ? value.split(' ') : $splat(value); return value.map(function(val){ val = String(val); var found = false; Fx.CSS.Parsers.each(function(parser, key){ if (found) return; var parsed = parser.parse(val); if ($chk(parsed)) found = {value: parsed, parser: parser}; }); found = found || {value: val, parser: Fx.CSS.Parsers.String}; return found; }); }, //computes by a from and to prepared objects, using their parsers. compute: function(from, to, delta){ var computed = []; (Math.min(from.length, to.length)).times(function(i){ computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser}); }); computed.$family = {name: 'fx:css:value'}; return computed; }, //serves the value as settable serve: function(value, unit){ if ($type(value) != 'fx:css:value') value = this.parse(value); var returned = []; value.each(function(bit){ returned = returned.concat(bit.parser.serve(bit.value, unit)); }); return returned; }, //renders the change to an element render: function(element, property, value, unit){ element.setStyle(property, this.serve(value, unit)); }, //searches inside the page css to find the values for a selector search: function(selector){ if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector]; var to = {}; Array.each(document.styleSheets, function(sheet, j){ var href = sheet.href; if (href && href.contains('://') && !href.contains(document.domain)) return; var rules = sheet.rules || sheet.cssRules; Array.each(rules, function(rule, i){ if (!rule.style) return; var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){ return m.toLowerCase(); }) : null; if (!selectorText || !selectorText.test('^' + selector + '$')) return; Element.Styles.each(function(value, style){ if (!rule.style[style] || Element.ShortStyles[style]) return; value = String(rule.style[style]); to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value; }); }); }); return Fx.CSS.Cache[selector] = to; } }); Fx.CSS.Cache = {}; Fx.CSS.Parsers = new Hash({ Color: { parse: function(value){ if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true); return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false; }, compute: function(from, to, delta){ return from.map(function(value, i){ return Math.round(Fx.compute(from[i], to[i], delta)); }); }, serve: function(value){ return value.map(Number); } }, Number: { parse: parseFloat, compute: Fx.compute, serve: function(value, unit){ return (unit) ? value + unit : value; } }, String: { parse: $lambda(false), compute: $arguments(1), serve: $arguments(0) } }); /* --- script: Fx.Tween.js description: Formerly Fx.Style, effect to transition any CSS property for an element. license: MIT-style license. requires: - /Fx.CSS provides: [Fx.Tween, Element.fade, Element.highlight] ... */ Fx.Tween = new Class({ Extends: Fx.CSS, initialize: function(element, options){ this.element = this.subject = document.id(element); this.parent(options); }, set: function(property, now){ if (arguments.length == 1){ now = property; property = this.property || this.options.property; } this.render(this.element, property, now, this.options.unit); return this; }, start: function(property, from, to){ if (!this.check(property, from, to)) return this; var args = Array.flatten(arguments); this.property = this.options.property || args.shift(); var parsed = this.prepare(this.element, this.property, args); return this.parent(parsed.from, parsed.to); } }); Element.Properties.tween = { set: function(options){ var tween = this.retrieve('tween'); if (tween) tween.cancel(); return this.eliminate('tween').store('tween:options', $extend({link: 'cancel'}, options)); }, get: function(options){ if (options || !this.retrieve('tween')){ if (options || !this.retrieve('tween:options')) this.set('tween', options); this.store('tween', new Fx.Tween(this, this.retrieve('tween:options'))); } return this.retrieve('tween'); } }; Element.implement({ tween: function(property, from, to){ this.get('tween').start(arguments); return this; }, fade: function(how){ var fade = this.get('tween'), o = 'opacity', toggle; how = $pick(how, 'toggle'); switch (how){ case 'in': fade.start(o, 1); break; case 'out': fade.start(o, 0); break; case 'show': fade.set(o, 1); break; case 'hide': fade.set(o, 0); break; case 'toggle': var flag = this.retrieve('fade:flag', this.get('opacity') == 1); fade.start(o, (flag) ? 0 : 1); this.store('fade:flag', !flag); toggle = true; break; default: fade.start(o, arguments); } if (!toggle) this.eliminate('fade:flag'); return this; }, highlight: function(start, end){ if (!end){ end = this.retrieve('highlight:original', this.getStyle('background-color')); end = (end == 'transparent') ? '#fff' : end; } var tween = this.get('tween'); tween.start('background-color', start || '#ffff88', end).chain(function(){ this.setStyle('background-color', this.retrieve('highlight:original')); tween.callChain(); }.bind(this)); return this; } }); /* --- script: Fx.Morph.js description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules. license: MIT-style license. requires: - /Fx.CSS provides: [Fx.Morph] ... */ Fx.Morph = new Class({ Extends: Fx.CSS, initialize: function(element, options){ this.element = this.subject = document.id(element); this.parent(options); }, set: function(now){ if (typeof now == 'string') now = this.search(now); for (var p in now) this.render(this.element, p, now[p], this.options.unit); return this; }, compute: function(from, to, delta){ var now = {}; for (var p in from) now[p] = this.parent(from[p], to[p], delta); return now; }, start: function(properties){ if (!this.check(properties)) return this; if (typeof properties == 'string') properties = this.search(properties); var from = {}, to = {}; for (var p in properties){ var parsed = this.prepare(this.element, p, properties[p]); from[p] = parsed.from; to[p] = parsed.to; } return this.parent(from, to); } }); Element.Properties.morph = { set: function(options){ var morph = this.retrieve('morph'); if (morph) morph.cancel(); return this.eliminate('morph').store('morph:options', $extend({link: 'cancel'}, options)); }, get: function(options){ if (options || !this.retrieve('morph')){ if (options || !this.retrieve('morph:options')) this.set('morph', options); this.store('morph', new Fx.Morph(this, this.retrieve('morph:options'))); } return this.retrieve('morph'); } }; Element.implement({ morph: function(props){ this.get('morph').start(props); return this; } }); /* --- script: Fx.Transitions.js description: Contains a set of advanced transitions to be used with any of the Fx Classes. license: MIT-style license. credits: - Easing Equations by Robert Penner, , modified and optimized to be used with MooTools. requires: - /Fx provides: [Fx.Transitions] ... */ Fx.implement({ getTransition: function(){ var trans = this.options.transition || Fx.Transitions.Sine.easeInOut; if (typeof trans == 'string'){ var data = trans.split(':'); trans = Fx.Transitions; trans = trans[data[0]] || trans[data[0].capitalize()]; if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')]; } return trans; } }); Fx.Transition = function(transition, params){ params = $splat(params); return $extend(transition, { easeIn: function(pos){ return transition(pos, params); }, easeOut: function(pos){ return 1 - transition(1 - pos, params); }, easeInOut: function(pos){ return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2; } }); }; Fx.Transitions = new Hash({ linear: $arguments(0) }); Fx.Transitions.extend = function(transitions){ for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]); }; Fx.Transitions.extend({ Pow: function(p, x){ return Math.pow(p, x[0] || 6); }, Expo: function(p){ return Math.pow(2, 8 * (p - 1)); }, Circ: function(p){ return 1 - Math.sin(Math.acos(p)); }, Sine: function(p){ return 1 - Math.sin((1 - p) * Math.PI / 2); }, Back: function(p, x){ x = x[0] || 1.618; return Math.pow(p, 2) * ((x + 1) * p - x); }, Bounce: function(p){ var value; for (var a = 0, b = 1; 1; a += b, b /= 2){ if (p >= (7 - 4 * a) / 11){ value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2); break; } } return value; }, Elastic: function(p, x){ return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3); } }); ['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){ Fx.Transitions[transition] = new Fx.Transition(function(p){ return Math.pow(p, [i + 2]); }); }); /* --- script: Request.js description: Powerful all purpose Request Class. Uses XMLHTTPRequest. license: MIT-style license. requires: - /Element - /Chain - /Events - /Options - /Browser provides: [Request] ... */ var Request = new Class({ Implements: [Chain, Events, Options], options: {/* onRequest: $empty, onComplete: $empty, onCancel: $empty, onSuccess: $empty, onFailure: $empty, onException: $empty,*/ url: '', data: '', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' }, async: true, format: false, method: 'post', link: 'ignore', isSuccess: null, emulation: true, urlEncoded: true, encoding: 'utf-8', evalScripts: false, evalResponse: false, noCache: false }, initialize: function(options){ this.xhr = new Browser.Request(); this.setOptions(options); this.options.isSuccess = this.options.isSuccess || this.isSuccess; this.headers = new Hash(this.options.headers); }, onStateChange: function(){ if (this.xhr.readyState != 4 || !this.running) return; this.running = false; this.status = 0; $try(function(){ this.status = this.xhr.status; }.bind(this)); this.xhr.onreadystatechange = $empty; if (this.options.isSuccess.call(this, this.status)){ this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML}; this.success(this.response.text, this.response.xml); } else { this.response = {text: null, xml: null}; this.failure(); } }, isSuccess: function(){ return ((this.status >= 200) && (this.status < 300)); }, processScripts: function(text){ if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text); return text.stripScripts(this.options.evalScripts); }, success: function(text, xml){ this.onSuccess(this.processScripts(text), xml); }, onSuccess: function(){ this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain(); }, failure: function(){ this.onFailure(); }, onFailure: function(){ this.fireEvent('complete').fireEvent('failure', this.xhr); }, setHeader: function(name, value){ this.headers.set(name, value); return this; }, getHeader: function(name){ return $try(function(){ return this.xhr.getResponseHeader(name); }.bind(this)); }, check: function(){ if (!this.running) return true; switch (this.options.link){ case 'cancel': this.cancel(); return true; case 'chain': this.chain(this.caller.bind(this, arguments)); return false; } return false; }, send: function(options){ if (!this.check(options)) return this; this.running = true; var type = $type(options); if (type == 'string' || type == 'element') options = {data: options}; var old = this.options; options = $extend({data: old.data, url: old.url, method: old.method}, options); var data = options.data, url = String(options.url), method = options.method.toLowerCase(); switch ($type(data)){ case 'element': data = document.id(data).toQueryString(); break; case 'object': case 'hash': data = Hash.toQueryString(data); } if (this.options.format){ var format = 'format=' + this.options.format; data = (data) ? format + '&' + data : format; } if (this.options.emulation && !['get', 'post'].contains(method)){ var _method = '_method=' + method; data = (data) ? _method + '&' + data : _method; method = 'post'; } if (this.options.urlEncoded && method == 'post'){ var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : ''; this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding); } if (this.options.noCache){ var noCache = 'noCache=' + new Date().getTime(); data = (data) ? noCache + '&' + data : noCache; } var trimPosition = url.lastIndexOf('/'); if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition); if (data && method == 'get'){ url = url + (url.contains('?') ? '&' : '?') + data; data = null; } this.xhr.open(method.toUpperCase(), url, this.options.async); this.xhr.onreadystatechange = this.onStateChange.bind(this); this.headers.each(function(value, key){ try { this.xhr.setRequestHeader(key, value); } catch (e){ this.fireEvent('exception', [key, value]); } }, this); this.fireEvent('request'); this.xhr.send(data); if (!this.options.async) this.onStateChange(); return this; }, cancel: function(){ if (!this.running) return this; this.running = false; this.xhr.abort(); this.xhr.onreadystatechange = $empty; this.xhr = new Browser.Request(); this.fireEvent('cancel'); return this; } }); (function(){ var methods = {}; ['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){ methods[method] = function(){ var params = Array.link(arguments, {url: String.type, data: $defined}); return this.send($extend(params, {method: method})); }; }); Request.implement(methods); })(); Element.Properties.send = { set: function(options){ var send = this.retrieve('send'); if (send) send.cancel(); return this.eliminate('send').store('send:options', $extend({ data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action') }, options)); }, get: function(options){ if (options || !this.retrieve('send')){ if (options || !this.retrieve('send:options')) this.set('send', options); this.store('send', new Request(this.retrieve('send:options'))); } return this.retrieve('send'); } }; Element.implement({ send: function(url){ var sender = this.get('send'); sender.send({data: this, url: url || sender.options.url}); return this; } }); /* --- script: Request.HTML.js description: Extends the basic Request Class with additional methods for interacting with HTML responses. license: MIT-style license. requires: - /Request - /Element provides: [Request.HTML] ... */ Request.HTML = new Class({ Extends: Request, options: { update: false, append: false, evalScripts: true, filter: false }, processHTML: function(text){ var match = text.match(/]*>([\s\S]*?)<\/body>/i); text = (match) ? match[1] : text; var container = new Element('div'); return $try(function(){ var root = '' + text + '', doc; if (Browser.Engine.trident){ doc = new ActiveXObject('Microsoft.XMLDOM'); doc.async = false; doc.loadXML(root); } else { doc = new DOMParser().parseFromString(root, 'text/xml'); } root = doc.getElementsByTagName('root')[0]; if (!root) return null; for (var i = 0, k = root.childNodes.length; i < k; i++){ var child = Element.clone(root.childNodes[i], true, true); if (child) container.grab(child); } return container; }) || container.set('html', text); }, success: function(text){ var options = this.options, response = this.response; response.html = text.stripScripts(function(script){ response.javascript = script; }); var temp = this.processHTML(response.html); response.tree = temp.childNodes; response.elements = temp.getElements('*'); if (options.filter) response.tree = response.elements.filter(options.filter); if (options.update) document.id(options.update).empty().set('html', response.html); else if (options.append) document.id(options.append).adopt(temp.getChildren()); if (options.evalScripts) $exec(response.javascript); this.onSuccess(response.tree, response.elements, response.html, response.javascript); } }); Element.Properties.load = { set: function(options){ var load = this.retrieve('load'); if (load) load.cancel(); return this.eliminate('load').store('load:options', $extend({data: this, link: 'cancel', update: this, method: 'get'}, options)); }, get: function(options){ if (options || ! this.retrieve('load')){ if (options || !this.retrieve('load:options')) this.set('load', options); this.store('load', new Request.HTML(this.retrieve('load:options'))); } return this.retrieve('load'); } }; Element.implement({ load: function(){ this.get('load').send(Array.link(arguments, {data: Object.type, url: String.type})); return this; } }); /* --- script: Request.JSON.js description: Extends the basic Request Class with additional methods for sending and receiving JSON data. license: MIT-style license. requires: - /Request JSON provides: [Request.HTML] ... */ Request.JSON = new Class({ Extends: Request, options: { secure: true }, initialize: function(options){ this.parent(options); this.headers.extend({'Accept': 'application/json', 'X-Request': 'JSON'}); }, success: function(text){ this.response.json = JSON.decode(text, this.options.secure); this.onSuccess(this.response.json, text); } }); /* --- script: More.js description: MooTools More license: MIT-style license authors: - Guillermo Rauch - Thomas Aylott - Scott Kyle requires: - core:1.2.4/MooTools provides: [MooTools.More] ... *//* --- script: More.js description: MooTools More license: MIT-style license authors: - Guillermo Rauch - Thomas Aylott - Scott Kyle requires: - core:1.2.4/MooTools provides: [MooTools.More] ... */ MooTools.More = { 'version': '1.2.4.4', 'build': '6f6057dc645fdb7547689183b2311063bd653ddf' }; /* --- script: MooTools.Lang.js description: Provides methods for localization. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Events - /MooTools.More provides: [MooTools.Lang] ... */ (function(){ var data = { language: 'en-US', languages: { 'en-US': {} }, cascades: ['en-US'] }; var cascaded; MooTools.lang = new Events(); $extend(MooTools.lang, { setLanguage: function(lang){ if (!data.languages[lang]) return this; data.language = lang; this.load(); this.fireEvent('langChange', lang); return this; }, load: function() { var langs = this.cascade(this.getCurrentLanguage()); cascaded = {}; $each(langs, function(set, setName){ cascaded[setName] = this.lambda(set); }, this); }, getCurrentLanguage: function(){ return data.language; }, addLanguage: function(lang){ data.languages[lang] = data.languages[lang] || {}; return this; }, cascade: function(lang){ var cascades = (data.languages[lang] || {}).cascades || []; cascades.combine(data.cascades); cascades.erase(lang).push(lang); var langs = cascades.map(function(lng){ return data.languages[lng]; }, this); return $merge.apply(this, langs); }, lambda: function(set) { (set || {}).get = function(key, args){ return $lambda(set[key]).apply(this, $splat(args)); }; return set; }, get: function(set, key, args){ if (cascaded && cascaded[set]) return (key ? cascaded[set].get(key, args) : cascaded[set]); }, set: function(lang, set, members){ this.addLanguage(lang); langData = data.languages[lang]; if (!langData[set]) langData[set] = {}; $extend(langData[set], members); if (lang == this.getCurrentLanguage()){ this.load(); this.fireEvent('langChange', lang); } return this; }, list: function(){ return Hash.getKeys(data.languages); } }); })(); /* --- script: Log.js description: Provides basic logging functionality for plugins to implement. license: MIT-style license authors: - Guillermo Rauch - Thomas Aylott - Scott Kyle requires: - core:1.2.4/Class - /MooTools.More provides: [Log] ... */ (function(){ var global = this; var log = function(){ if (global.console && console.log){ try { console.log.apply(console, arguments); } catch(e) { console.log(Array.slice(arguments)); } } else { Log.logged.push(arguments); } return this; }; var disabled = function(){ this.logged.push(arguments); return this; }; this.Log = new Class({ logged: [], log: disabled, resetLog: function(){ this.logged.empty(); return this; }, enableLog: function(){ this.log = log; this.logged.each(function(args){ this.log.apply(this, args); }, this); return this.resetLog(); }, disableLog: function(){ this.log = disabled; return this; } }); Log.extend(new Log).enableLog(); // legacy Log.logger = function(){ return this.log.apply(this, arguments); }; })(); /* --- script: Depender.js description: A stand alone dependency loader for the MooTools library. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Element.Events - core:1.2.4/Request.JSON - /MooTools.More - /Log provides: Depender ... */ var Depender = { options: { /* onRequire: $empty(options), onRequirementLoaded: $empty([scripts, options]), onScriptLoaded: $empty({ script: script, totalLoaded: percentOfTotalLoaded, loaded: scriptsState }), serial: false, target: null, noCache: false, log: false,*/ loadedSources: [], loadedScripts: ['Core', 'Browser', 'Array', 'String', 'Function', 'Number', 'Hash', 'Element', 'Event', 'Element.Event', 'Class', 'DomReady', 'Class.Extras', 'Request', 'JSON', 'Request.JSON', 'More', 'Depender', 'Log'], useScriptInjection: true }, loaded: [], sources: {}, libs: {}, include: function(libs){ this.log('include: ', libs); this.mapLoaded = false; var loader = function(data){ this.libs = $merge(this.libs, data); $each(this.libs, function(data, lib){ if (data.scripts) this.loadSource(lib, data.scripts); }, this); }.bind(this); if ($type(libs) == 'string'){ this.log('fetching libs ', libs); this.request(libs, loader); } else { loader(libs); } return this; }, required: [], require: function(options){ var loaded = function(){ var scripts = this.calculateDependencies(options.scripts); if (options.sources){ options.sources.each(function(source){ scripts.combine(this.libs[source].files); }, this); } if (options.serial) scripts.combine(this.getLoadedScripts()); options.scripts = scripts; this.required.push(options); this.fireEvent('require', options); this.loadScripts(options.scripts); }; if (this.mapLoaded) loaded.call(this); else this.addEvent('mapLoaded', loaded.bind(this)); return this; }, cleanDoubleSlash: function(str){ if (!str) return str; var prefix = ''; if (str.test(/^http:\/\//)){ prefix = 'http://'; str = str.substring(7, str.length); } str = str.replace(/\/\//g, '/'); return prefix + str; }, request: function(url, callback){ new Request.JSON({ url: url, secure: false, onSuccess: callback }).send(); }, loadSource: function(lib, source){ if (this.libs[lib].files){ this.dataLoaded(); return; } this.log('loading source: ', source); this.request(this.cleanDoubleSlash(source + '/scripts.json'), function(result){ this.log('loaded source: ', source); this.libs[lib].files = result; this.dataLoaded(); }.bind(this)); }, dataLoaded: function(){ var loaded = true; $each(this.libs, function(v, k){ if (!this.libs[k].files) loaded = false; }, this); if (loaded){ this.mapTree(); this.mapLoaded = true; this.calculateLoaded(); this.lastLoaded = this.getLoadedScripts().getLength(); this.fireEvent('mapLoaded'); this.removeEvents('mapLoaded'); } }, calculateLoaded: function(){ var set = function(script){ this.scriptsState[script] = true; }.bind(this); if (this.options.loadedScripts) this.options.loadedScripts.each(set); if (this.options.loadedSources){ this.options.loadedSources.each(function(lib){ $each(this.libs[lib].files, function(dir){ $each(dir, function(data, file){ set(file); }, this); }, this); }, this); } }, deps: {}, pathMap: {}, mapTree: function(){ $each(this.libs, function(data, source){ $each(data.files, function(scripts, folder){ $each(scripts, function(details, script){ var path = source + ':' + folder + ':' + script; if (this.deps[path]) return; this.deps[path] = details.deps; this.pathMap[script] = path; }, this); }, this); }, this); }, getDepsForScript: function(script){ return this.deps[this.pathMap[script]] || []; }, calculateDependencies: function(scripts){ var reqs = []; $splat(scripts).each(function(script){ if (script == 'None' || !script) return; var deps = this.getDepsForScript(script); if (!deps){ if (window.console && console.warn) console.warn('dependencies not mapped: script: %o, map: %o, :deps: %o', script, this.pathMap, this.deps); } else { deps.each(function(scr){ if (scr == script || scr == 'None' || !scr) return; if (!reqs.contains(scr)) reqs.combine(this.calculateDependencies(scr)); reqs.include(scr); }, this); } reqs.include(script); }, this); return reqs; }, getPath: function(script){ try { var chunks = this.pathMap[script].split(':'); var lib = this.libs[chunks[0]]; var dir = (lib.path || lib.scripts) + '/'; chunks.shift(); return this.cleanDoubleSlash(dir + chunks.join('/') + '.js'); } catch(e){ return script; } }, loadScripts: function(scripts){ scripts = scripts.filter(function(s){ if (!this.scriptsState[s] && s != 'None'){ this.scriptsState[s] = false; return true; } }, this); if (scripts.length){ scripts.each(function(scr){ this.loadScript(scr); }, this); } else { this.check(); } }, toLoad: [], loadScript: function(script){ if (this.scriptsState[script] && this.toLoad.length){ this.loadScript(this.toLoad.shift()); return; } else if (this.loading){ this.toLoad.push(script); return; } var finish = function(){ this.loading = false; this.scriptLoaded(script); if (this.toLoad.length) this.loadScript(this.toLoad.shift()); }.bind(this); var error = function(){ this.log('could not load: ', scriptPath); }.bind(this); this.loading = true; var scriptPath = this.getPath(script); if (this.options.useScriptInjection){ this.log('injecting script: ', scriptPath); var loaded = function(){ this.log('loaded script: ', scriptPath); finish(); }.bind(this); new Element('script', { src: scriptPath + (this.options.noCache ? '?noCache=' + new Date().getTime() : ''), events: { load: loaded, readystatechange: function(){ if (['loaded', 'complete'].contains(this.readyState)) loaded(); }, error: error } }).inject(this.options.target || document.head); } else { this.log('requesting script: ', scriptPath); new Request({ url: scriptPath, noCache: this.options.noCache, onComplete: function(js){ this.log('loaded script: ', scriptPath); $exec(js); finish(); }.bind(this), onFailure: error, onException: error }).send(); } }, scriptsState: $H(), getLoadedScripts: function(){ return this.scriptsState.filter(function(state){ return state; }); }, scriptLoaded: function(script){ this.log('loaded script: ', script); this.scriptsState[script] = true; this.check(); var loaded = this.getLoadedScripts(); var loadedLength = loaded.getLength(); var toLoad = this.scriptsState.getLength(); this.fireEvent('scriptLoaded', { script: script, totalLoaded: (loadedLength / toLoad * 100).round(), currentLoaded: ((loadedLength - this.lastLoaded) / (toLoad - this.lastLoaded) * 100).round(), loaded: loaded }); if (loadedLength == toLoad) this.lastLoaded = loadedLength; }, lastLoaded: 0, check: function(){ var incomplete = []; this.required.each(function(required){ var loaded = []; required.scripts.each(function(script){ if (this.scriptsState[script]) loaded.push(script); }, this); if (required.onStep){ required.onStep({ percent: loaded.length / required.scripts.length * 100, scripts: loaded }); }; if (required.scripts.length != loaded.length) return; required.callback(); this.required.erase(required); this.fireEvent('requirementLoaded', [loaded, required]); }, this); } }; $extend(Depender, new Events); $extend(Depender, new Options); $extend(Depender, new Log); Depender._setOptions = Depender.setOptions; Depender.setOptions = function(){ Depender._setOptions.apply(Depender, arguments); if (this.options.log) Depender.enableLog(); return this; }; /* --- script: Class.Refactor.js description: Extends a class onto itself with new property, preserving any items attached to the class's namespace. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Class - /MooTools.More provides: [Class.refactor] ... */ Class.refactor = function(original, refactors){ $each(refactors, function(item, name){ var origin = original.prototype[name]; if (origin && (origin = origin._origin) && typeof item == 'function') original.implement(name, function(){ var old = this.previous; this.previous = origin; var value = item.apply(this, arguments); this.previous = old; return value; }); else original.implement(name, item); }); return original; }; /* --- script: Class.Binds.js description: Automagically binds specified methods in a class to the instance of the class. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Class - /MooTools.More provides: [Class.Binds] ... */ Class.Mutators.Binds = function(binds){ return binds; }; Class.Mutators.initialize = function(initialize){ return function(){ $splat(this.Binds).each(function(name){ var original = this[name]; if (original) this[name] = original.bind(this); }, this); return initialize.apply(this, arguments); }; }; /* --- script: Class.Occlude.js description: Prevents a class from being applied to a DOM element twice. license: MIT-style license. authors: - Aaron Newton requires: - core/1.2.4/Class - core:1.2.4/Element - /MooTools.More provides: [Class.Occlude] ... */ Class.Occlude = new Class({ occlude: function(property, element){ element = document.id(element || this.element); var instance = element.retrieve(property || this.property); if (instance && !$defined(this.occluded)) return this.occluded = instance; this.occluded = false; element.store(property || this.property, this); return this.occluded; } }); /* --- script: Chain.Wait.js description: value, Adds a method to inject pauses between chained events. license: MIT-style license. authors: - Aaron Newton requires: - core:1.2.4/Chain - core:1.2.4/Element - core:1.2.4/Fx - /MooTools.More provides: [Chain.Wait] ... */ (function(){ var wait = { wait: function(duration){ return this.chain(function(){ this.callChain.delay($pick(duration, 500), this); }.bind(this)); } }; Chain.implement(wait); if (window.Fx){ Fx.implement(wait); ['Css', 'Tween', 'Elements'].each(function(cls){ if (Fx[cls]) Fx[cls].implement(wait); }); } Element.implement({ chains: function(effects){ $splat($pick(effects, ['tween', 'morph', 'reveal'])).each(function(effect){ effect = this.get(effect); if (!effect) return; effect.setOptions({ link:'chain' }); }, this); return this; }, pauseFx: function(duration, effect){ this.chains(effect).get($pick(effect, 'tween')).wait(duration); return this; } }); })(); /* --- script: Array.Extras.js description: Extends the Array native object to include useful methods to work with arrays. license: MIT-style license authors: - Christoph Pojer requires: - core:1.2.4/Array provides: [Array.Extras] ... */ Array.implement({ min: function(){ return Math.min.apply(null, this); }, max: function(){ return Math.max.apply(null, this); }, average: function(){ return this.length ? this.sum() / this.length : 0; }, sum: function(){ var result = 0, l = this.length; if (l){ do { result += this[--l]; } while (l); } return result; }, unique: function(){ return [].combine(this); }, shuffle: function(){ for (var i = this.length; i && --i;){ var temp = this[i], r = Math.floor(Math.random() * ( i + 1 )); this[i] = this[r]; this[r] = temp; } return this; } }); /* --- script: Date.js description: Extends the Date native object to include methods useful in managing dates. license: MIT-style license authors: - Aaron Newton - Nicholas Barthelemy - https://svn.nbarthelemy.com/date-js/ - Harald Kirshner - mail [at] digitarald.de; http://digitarald.de - Scott Kyle - scott [at] appden.com; http://appden.com requires: - core:1.2.4/Array - core:1.2.4/String - core:1.2.4/Number - core:1.2.4/Lang - core:1.2.4/Date.English.US - /MooTools.More provides: [Date] ... */ (function(){ var Date = this.Date; if (!Date.now) Date.now = $time; Date.Methods = { ms: 'Milliseconds', year: 'FullYear', min: 'Minutes', mo: 'Month', sec: 'Seconds', hr: 'Hours' }; ['Date', 'Day', 'FullYear', 'Hours', 'Milliseconds', 'Minutes', 'Month', 'Seconds', 'Time', 'TimezoneOffset', 'Week', 'Timezone', 'GMTOffset', 'DayOfYear', 'LastMonth', 'LastDayOfMonth', 'UTCDate', 'UTCDay', 'UTCFullYear', 'AMPM', 'Ordinal', 'UTCHours', 'UTCMilliseconds', 'UTCMinutes', 'UTCMonth', 'UTCSeconds'].each(function(method){ Date.Methods[method.toLowerCase()] = method; }); var pad = function(what, length){ return new Array(length - String(what).length + 1).join('0') + what; }; Date.implement({ set: function(prop, value){ switch ($type(prop)){ case 'object': for (var p in prop) this.set(p, prop[p]); break; case 'string': prop = prop.toLowerCase(); var m = Date.Methods; if (m[prop]) this['set' + m[prop]](value); } return this; }, get: function(prop){ prop = prop.toLowerCase(); var m = Date.Methods; if (m[prop]) return this['get' + m[prop]](); return null; }, clone: function(){ return new Date(this.get('time')); }, increment: function(interval, times){ interval = interval || 'day'; times = $pick(times, 1); switch (interval){ case 'year': return this.increment('month', times * 12); case 'month': var d = this.get('date'); this.set('date', 1).set('mo', this.get('mo') + times); return this.set('date', d.min(this.get('lastdayofmonth'))); case 'week': return this.increment('day', times * 7); case 'day': return this.set('date', this.get('date') + times); } if (!Date.units[interval]) throw new Error(interval + ' is not a supported interval'); return this.set('time', this.get('time') + times * Date.units[interval]()); }, decrement: function(interval, times){ return this.increment(interval, -1 * $pick(times, 1)); }, isLeapYear: function(){ return Date.isLeapYear(this.get('year')); }, clearTime: function(){ return this.set({hr: 0, min: 0, sec: 0, ms: 0}); }, diff: function(date, resolution){ if ($type(date) == 'string') date = Date.parse(date); return ((date - this) / Date.units[resolution || 'day'](3, 3)).toInt(); // non-leap year, 30-day month }, getLastDayOfMonth: function(){ return Date.daysInMonth(this.get('mo'), this.get('year')); }, getDayOfYear: function(){ return (Date.UTC(this.get('year'), this.get('mo'), this.get('date') + 1) - Date.UTC(this.get('year'), 0, 1)) / Date.units.day(); }, getWeek: function(){ return (this.get('dayofyear') / 7).ceil(); }, getOrdinal: function(day){ return Date.getMsg('ordinal', day || this.get('date')); }, getTimezone: function(){ return this.toString() .replace(/^.*? ([A-Z]{3}).[0-9]{4}.*$/, '$1') .replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, '$1$2$3'); }, getGMTOffset: function(){ var off = this.get('timezoneOffset'); return ((off > 0) ? '-' : '+') + pad((off.abs() / 60).floor(), 2) + pad(off % 60, 2); }, setAMPM: function(ampm){ ampm = ampm.toUpperCase(); var hr = this.get('hr'); if (hr > 11 && ampm == 'AM') return this.decrement('hour', 12); else if (hr < 12 && ampm == 'PM') return this.increment('hour', 12); return this; }, getAMPM: function(){ return (this.get('hr') < 12) ? 'AM' : 'PM'; }, parse: function(str){ this.set('time', Date.parse(str)); return this; }, isValid: function(date) { return !!(date || this).valueOf(); }, format: function(f){ if (!this.isValid()) return 'invalid date'; f = f || '%x %X'; f = formats[f.toLowerCase()] || f; // replace short-hand with actual format var d = this; return f.replace(/%([a-z%])/gi, function($0, $1){ switch ($1){ case 'a': return Date.getMsg('days')[d.get('day')].substr(0, 3); case 'A': return Date.getMsg('days')[d.get('day')]; case 'b': return Date.getMsg('months')[d.get('month')].substr(0, 3); case 'B': return Date.getMsg('months')[d.get('month')]; case 'c': return d.toString(); case 'd': return pad(d.get('date'), 2); case 'H': return pad(d.get('hr'), 2); case 'I': return ((d.get('hr') % 12) || 12); case 'j': return pad(d.get('dayofyear'), 3); case 'm': return pad((d.get('mo') + 1), 2); case 'M': return pad(d.get('min'), 2); case 'o': return d.get('ordinal'); case 'p': return Date.getMsg(d.get('ampm')); case 'S': return pad(d.get('seconds'), 2); case 'U': return pad(d.get('week'), 2); case 'w': return d.get('day'); case 'x': return d.format(Date.getMsg('shortDate')); case 'X': return d.format(Date.getMsg('shortTime')); case 'y': return d.get('year').toString().substr(2); case 'Y': return d.get('year'); case 'T': return d.get('GMTOffset'); case 'Z': return d.get('Timezone'); } return $1; } ); }, toISOString: function(){ return this.format('iso8601'); } }); Date.alias('toISOString', 'toJSON'); Date.alias('diff', 'compare'); Date.alias('format', 'strftime'); var formats = { db: '%Y-%m-%d %H:%M:%S', compact: '%Y%m%dT%H%M%S', iso8601: '%Y-%m-%dT%H:%M:%S%T', rfc822: '%a, %d %b %Y %H:%M:%S %Z', 'short': '%d %b %H:%M', 'long': '%B %d, %Y %H:%M' }; var parsePatterns = []; var nativeParse = Date.parse; var parseWord = function(type, word, num){ var ret = -1; var translated = Date.getMsg(type + 's'); switch ($type(word)){ case 'object': ret = translated[word.get(type)]; break; case 'number': ret = translated[month - 1]; if (!ret) throw new Error('Invalid ' + type + ' index: ' + index); break; case 'string': var match = translated.filter(function(name){ return this.test(name); }, new RegExp('^' + word, 'i')); if (!match.length) throw new Error('Invalid ' + type + ' string'); if (match.length > 1) throw new Error('Ambiguous ' + type); ret = match[0]; } return (num) ? translated.indexOf(ret) : ret; }; Date.extend({ getMsg: function(key, args) { return MooTools.lang.get('Date', key, args); }, units: { ms: $lambda(1), second: $lambda(1000), minute: $lambda(60000), hour: $lambda(3600000), day: $lambda(86400000), week: $lambda(608400000), month: function(month, year){ var d = new Date; return Date.daysInMonth($pick(month, d.get('mo')), $pick(year, d.get('year'))) * 86400000; }, year: function(year){ year = year || new Date().get('year'); return Date.isLeapYear(year) ? 31622400000 : 31536000000; } }, daysInMonth: function(month, year){ return [31, Date.isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; }, isLeapYear: function(year){ return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0); }, parse: function(from){ var t = $type(from); if (t == 'number') return new Date(from); if (t != 'string') return from; from = from.clean(); if (!from.length) return null; var parsed; parsePatterns.some(function(pattern){ var bits = pattern.re.exec(from); return (bits) ? (parsed = pattern.handler(bits)) : false; }); return parsed || new Date(nativeParse(from)); }, parseDay: function(day, num){ return parseWord('day', day, num); }, parseMonth: function(month, num){ return parseWord('month', month, num); }, parseUTC: function(value){ var localDate = new Date(value); var utcSeconds = Date.UTC( localDate.get('year'), localDate.get('mo'), localDate.get('date'), localDate.get('hr'), localDate.get('min'), localDate.get('sec') ); return new Date(utcSeconds); }, orderIndex: function(unit){ return Date.getMsg('dateOrder').indexOf(unit) + 1; }, defineFormat: function(name, format){ formats[name] = format; }, defineFormats: function(formats){ for (var name in formats) Date.defineFormat(name, formats[name]); }, parsePatterns: parsePatterns, // this is deprecated defineParser: function(pattern){ parsePatterns.push((pattern.re && pattern.handler) ? pattern : build(pattern)); }, defineParsers: function(){ Array.flatten(arguments).each(Date.defineParser); }, define2DigitYearStart: function(year){ startYear = year % 100; startCentury = year - startYear; } }); var startCentury = 1900; var startYear = 70; var regexOf = function(type){ return new RegExp('(?:' + Date.getMsg(type).map(function(name){ return name.substr(0, 3); }).join('|') + ')[a-z]*'); }; var replacers = function(key){ switch(key){ case 'x': // iso8601 covers yyyy-mm-dd, so just check if month is first return ((Date.orderIndex('month') == 1) ? '%m[.-/]%d' : '%d[.-/]%m') + '([.-/]%y)?'; case 'X': return '%H([.:]%M)?([.:]%S([.:]%s)?)? ?%p? ?%T?'; } return null; }; var keys = { d: /[0-2]?[0-9]|3[01]/, H: /[01]?[0-9]|2[0-3]/, I: /0?[1-9]|1[0-2]/, M: /[0-5]?\d/, s: /\d+/, o: /[a-z]*/, p: /[ap]\.?m\.?/, y: /\d{2}|\d{4}/, Y: /\d{4}/, T: /Z|[+-]\d{2}(?::?\d{2})?/ }; keys.m = keys.I; keys.S = keys.M; var currentLanguage; var recompile = function(language){ currentLanguage = language; keys.a = keys.A = regexOf('days'); keys.b = keys.B = regexOf('months'); parsePatterns.each(function(pattern, i){ if (pattern.format) parsePatterns[i] = build(pattern.format); }); }; var build = function(format){ if (!currentLanguage) return {format: format}; var parsed = []; var re = (format.source || format) // allow format to be regex .replace(/%([a-z])/gi, function($0, $1){ return replacers($1) || $0; } ).replace(/\((?!\?)/g, '(?:') // make all groups non-capturing .replace(/ (?!\?|\*)/g, ',? ') // be forgiving with spaces and commas .replace(/%([a-z%])/gi, function($0, $1){ var p = keys[$1]; if (!p) return $1; parsed.push($1); return '(' + p.source + ')'; } ).replace(/\[a-z\]/gi, '[a-z\\u00c0-\\uffff]'); // handle unicode words return { format: format, re: new RegExp('^' + re + '$', 'i'), handler: function(bits){ bits = bits.slice(1).associate(parsed); var date = new Date().clearTime(); if ('d' in bits) handle.call(date, 'd', 1); if ('m' in bits || 'b' in bits || 'B' in bits) handle.call(date, 'm', 1); for (var key in bits) handle.call(date, key, bits[key]); return date; } }; }; var handle = function(key, value){ if (!value) return this; switch(key){ case 'a': case 'A': return this.set('day', Date.parseDay(value, true)); case 'b': case 'B': return this.set('mo', Date.parseMonth(value, true)); case 'd': return this.set('date', value); case 'H': case 'I': return this.set('hr', value); case 'm': return this.set('mo', value - 1); case 'M': return this.set('min', value); case 'p': return this.set('ampm', value.replace(/\./g, '')); case 'S': return this.set('sec', value); case 's': return this.set('ms', ('0.' + value) * 1000); case 'w': return this.set('day', value); case 'Y': return this.set('year', value); case 'y': value = +value; if (value < 100) value += startCentury + (value < startYear ? 100 : 0); return this.set('year', value); case 'T': if (value == 'Z') value = '+00'; var offset = value.match(/([+-])(\d{2}):?(\d{2})?/); offset = (offset[1] + '1') * (offset[2] * 60 + (+offset[3] || 0)) + this.getTimezoneOffset(); return this.set('time', this - offset * 60000); } return this; }; Date.defineParsers( '%Y([-./]%m([-./]%d((T| )%X)?)?)?', // "1999-12-31", "1999-12-31 11:59pm", "1999-12-31 23:59:59", ISO8601 '%Y%m%d(T%H(%M%S?)?)?', // "19991231", "19991231T1159", compact '%x( %X)?', // "12/31", "12.31.99", "12-31-1999", "12/31/2008 11:59 PM" '%d%o( %b( %Y)?)?( %X)?', // "31st", "31st December", "31 Dec 1999", "31 Dec 1999 11:59pm" '%b( %d%o)?( %Y)?( %X)?', // Same as above with month and day switched '%Y %b( %d%o( %X)?)?', // Same as above with year coming first '%o %b %d %X %T %Y' // "Thu Oct 22 08:11:23 +0000 2009" ); MooTools.lang.addEvent('langChange', function(language){ if (MooTools.lang.get('Date')) recompile(language); }).fireEvent('langChange', MooTools.lang.getCurrentLanguage()); })(); /* --- script: Date.Extras.js description: Extends the Date native object to include extra methods (on top of those in Date.js). license: MIT-style license authors: - Aaron Newton - Scott Kyle requires: - /Date provides: [Date.Extras] ... */ Date.implement({ timeDiffInWords: function(relative_to){ return Date.distanceOfTimeInWords(this, relative_to || new Date); }, timeDiff: function(to, joiner){ if (to == null) to = new Date; var delta = ((to - this) / 1000).toInt(); if (!delta) return '0s'; var durations = {s: 60, m: 60, h: 24, d: 365, y: 0}; var duration, vals = []; for (var step in durations){ if (!delta) break; if ((duration = durations[step])){ vals.unshift((delta % duration) + step); delta = (delta / duration).toInt(); } else { vals.unshift(delta + step); } } return vals.join(joiner || ':'); } }); Date.alias('timeDiffInWords', 'timeAgoInWords'); Date.extend({ distanceOfTimeInWords: function(from, to){ return Date.getTimePhrase(((to - from) / 1000).toInt()); }, getTimePhrase: function(delta){ var suffix = (delta < 0) ? 'Until' : 'Ago'; if (delta < 0) delta *= -1; var units = { minute: 60, hour: 60, day: 24, week: 7, month: 52 / 12, year: 12, eon: Infinity }; var msg = 'lessThanMinute'; for (var unit in units){ var interval = units[unit]; if (delta < 1.5 * interval){ if (delta > 0.75 * interval) msg = unit; break; } delta /= interval; msg = unit + 's'; } return Date.getMsg(msg + suffix).substitute({delta: delta.round()}); } }); Date.defineParsers( { // "today", "tomorrow", "yesterday" re: /^(?:tod|tom|yes)/i, handler: function(bits){ var d = new Date().clearTime(); switch(bits[0]){ case 'tom': return d.increment(); case 'yes': return d.decrement(); default: return d; } } }, { // "next Wednesday", "last Thursday" re: /^(next|last) ([a-z]+)$/i, handler: function(bits){ var d = new Date().clearTime(); var day = d.getDay(); var newDay = Date.parseDay(bits[2], true); var addDays = newDay - day; if (newDay <= day) addDays += 7; if (bits[1] == 'last') addDays -= 7; return d.set('date', d.getDate() + addDays); } } ); /* --- script: Hash.Extras.js description: Extends the Hash native object to include getFromPath which allows a path notation to child elements. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Hash.base - /MooTools.More provides: [Hash.Extras] ... */ Hash.implement({ getFromPath: function(notation){ var source = this.getClean(); notation.replace(/\[([^\]]+)\]|\.([^.[]+)|[^[.]+/g, function(match){ if (!source) return null; var prop = arguments[2] || arguments[1] || arguments[0]; source = (prop in source) ? source[prop] : null; return match; }); return source; }, cleanValues: function(method){ method = method || $defined; this.each(function(v, k){ if (!method(v)) this.erase(k); }, this); return this; }, run: function(){ var args = arguments; this.each(function(v, k){ if ($type(v) == 'function') v.run(args); }); } }); /* --- script: String.Extras.js description: Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc). license: MIT-style license authors: - Aaron Newton - Guillermo Rauch requires: - core:1.2.4/String - core:1.2.4/$util - core:1.2.4/Array provides: [String.Extras] ... */ (function(){ var special = ['À','à','Á','á','Â','â','Ã','ã','Ä','ä','Å','å','Ă','ă','Ą','ą','Ć','ć','Č','č','Ç','ç', 'Ď','ď','Đ','đ', 'È','è','É','é','Ê','ê','Ë','ë','Ě','ě','Ę','ę', 'Ğ','ğ','Ì','ì','Í','í','Î','î','Ï','ï', 'Ĺ','ĺ','Ľ','ľ','Ł','ł', 'Ñ','ñ','Ň','ň','Ń','ń','Ò','ò','Ó','ó','Ô','ô','Õ','õ','Ö','ö','Ø','ø','ő','Ř','ř','Ŕ','ŕ','Š','š','Ş','ş','Ś','ś', 'Ť','ť','Ť','ť','Ţ','ţ','Ù','ù','Ú','ú','Û','û','Ü','ü','Ů','ů', 'Ÿ','ÿ','ý','Ý','Ž','ž','Ź','ź','Ż','ż', 'Þ','þ','Ð','ð','ß','Œ','œ','Æ','æ','µ']; var standard = ['A','a','A','a','A','a','A','a','Ae','ae','A','a','A','a','A','a','C','c','C','c','C','c','D','d','D','d', 'E','e','E','e','E','e','E','e','E','e','E','e','G','g','I','i','I','i','I','i','I','i','L','l','L','l','L','l', 'N','n','N','n','N','n', 'O','o','O','o','O','o','O','o','Oe','oe','O','o','o', 'R','r','R','r', 'S','s','S','s','S','s','T','t','T','t','T','t', 'U','u','U','u','U','u','Ue','ue','U','u','Y','y','Y','y','Z','z','Z','z','Z','z','TH','th','DH','dh','ss','OE','oe','AE','ae','u']; var tidymap = { "[\xa0\u2002\u2003\u2009]": " ", "\xb7": "*", "[\u2018\u2019]": "'", "[\u201c\u201d]": '"', "\u2026": "...", "\u2013": "-", "\u2014": "--", "\uFFFD": "»" }; var getRegForTag = function(tag, contents) { tag = tag || ''; var regstr = contents ? "<" + tag + "[^>]*>([\\s\\S]*?)<\/" + tag + ">" : "<\/?" + tag + "([^>]+)?>"; reg = new RegExp(regstr, "gi"); return reg; }; String.implement({ standardize: function(){ var text = this; special.each(function(ch, i){ text = text.replace(new RegExp(ch, 'g'), standard[i]); }); return text; }, repeat: function(times){ return new Array(times + 1).join(this); }, pad: function(length, str, dir){ if (this.length >= length) return this; var pad = (str == null ? ' ' : '' + str).repeat(length - this.length).substr(0, length - this.length); if (!dir || dir == 'right') return this + pad; if (dir == 'left') return pad + this; return pad.substr(0, (pad.length / 2).floor()) + this + pad.substr(0, (pad.length / 2).ceil()); }, getTags: function(tag, contents){ return this.match(getRegForTag(tag, contents)) || []; }, stripTags: function(tag, contents){ return this.replace(getRegForTag(tag, contents), ''); }, tidy: function(){ var txt = this.toString(); $each(tidymap, function(value, key){ txt = txt.replace(new RegExp(key, 'g'), value); }); return txt; } }); })(); /* --- script: String.QueryString.js description: Methods for dealing with URI query strings. license: MIT-style license authors: - Sebastian Markbåge, Aaron Newton, Lennart Pilon, Valerio Proietti requires: - core:1.2.4/Array - core:1.2.4/String - /MooTools.More provides: [String.QueryString] ... */ String.implement({ parseQueryString: function(){ var vars = this.split(/[&;]/), res = {}; if (vars.length) vars.each(function(val){ var index = val.indexOf('='), keys = index < 0 ? [''] : val.substr(0, index).match(/[^\]\[]+/g), value = decodeURIComponent(val.substr(index + 1)), obj = res; keys.each(function(key, i){ var current = obj[key]; if(i < keys.length - 1) obj = obj[key] = current || {}; else if($type(current) == 'array') current.push(value); else obj[key] = $defined(current) ? [current, value] : value; }); }); return res; }, cleanQueryString: function(method){ return this.split('&').filter(function(val){ var index = val.indexOf('='), key = index < 0 ? '' : val.substr(0, index), value = val.substr(index + 1); return method ? method.run([key, value]) : $chk(value); }).join('&'); } }); /* --- script: URI.js description: Provides methods useful in managing the window location and uris. license: MIT-style license authors: - Sebastian Markb樥 - Aaron Newton requires: - core:1.2.4/Selectors - /String.QueryString provides: URI ... */ var URI = new Class({ Implements: Options, options: { /*base: false*/ }, regex: /^(?:(\w+):)?(?:\/\/(?:(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)?(\.\.?$|(?:[^?#\/]*\/)*)([^?#]*)(?:\?([^#]*))?(?:#(.*))?/, parts: ['scheme', 'user', 'password', 'host', 'port', 'directory', 'file', 'query', 'fragment'], schemes: {http: 80, https: 443, ftp: 21, rtsp: 554, mms: 1755, file: 0}, initialize: function(uri, options){ this.setOptions(options); var base = this.options.base || URI.base; if(!uri) uri = base; if (uri && uri.parsed) this.parsed = $unlink(uri.parsed); else this.set('value', uri.href || uri.toString(), base ? new URI(base) : false); }, parse: function(value, base){ var bits = value.match(this.regex); if (!bits) return false; bits.shift(); return this.merge(bits.associate(this.parts), base); }, merge: function(bits, base){ if ((!bits || !bits.scheme) && (!base || !base.scheme)) return false; if (base){ this.parts.every(function(part){ if (bits[part]) return false; bits[part] = base[part] || ''; return true; }); } bits.port = bits.port || this.schemes[bits.scheme.toLowerCase()]; bits.directory = bits.directory ? this.parseDirectory(bits.directory, base ? base.directory : '') : '/'; return bits; }, parseDirectory: function(directory, baseDirectory) { directory = (directory.substr(0, 1) == '/' ? '' : (baseDirectory || '/')) + directory; if (!directory.test(URI.regs.directoryDot)) return directory; var result = []; directory.replace(URI.regs.endSlash, '').split('/').each(function(dir){ if (dir == '..' && result.length > 0) result.pop(); else if (dir != '.') result.push(dir); }); return result.join('/') + '/'; }, combine: function(bits){ return bits.value || bits.scheme + '://' + (bits.user ? bits.user + (bits.password ? ':' + bits.password : '') + '@' : '') + (bits.host || '') + (bits.port && bits.port != this.schemes[bits.scheme] ? ':' + bits.port : '') + (bits.directory || '/') + (bits.file || '') + (bits.query ? '?' + bits.query : '') + (bits.fragment ? '#' + bits.fragment : ''); }, set: function(part, value, base){ if (part == 'value'){ var scheme = value.match(URI.regs.scheme); if (scheme) scheme = scheme[1]; if (scheme && !$defined(this.schemes[scheme.toLowerCase()])) this.parsed = { scheme: scheme, value: value }; else this.parsed = this.parse(value, (base || this).parsed) || (scheme ? { scheme: scheme, value: value } : { value: value }); } else if (part == 'data') { this.setData(value); } else { this.parsed[part] = value; } return this; }, get: function(part, base){ switch(part){ case 'value': return this.combine(this.parsed, base ? base.parsed : false); case 'data' : return this.getData(); } return this.parsed[part] || ''; }, go: function(){ document.location.href = this.toString(); }, toURI: function(){ return this; }, getData: function(key, part){ var qs = this.get(part || 'query'); if (!$chk(qs)) return key ? null : {}; var obj = qs.parseQueryString(); return key ? obj[key] : obj; }, setData: function(values, merge, part){ if (typeof values == 'string'){ data = this.getData(); data[arguments[0]] = arguments[1]; values = data; } else if (merge) { values = $merge(this.getData(), values); } return this.set(part || 'query', Hash.toQueryString(values)); }, clearData: function(part){ return this.set(part || 'query', ''); } }); URI.prototype.toString = URI.prototype.valueOf = function(){ return this.get('value'); }; URI.regs = { endSlash: /\/$/, scheme: /^(\w+):/, directoryDot: /\.\/|\.$/ }; URI.base = new URI(document.getElements('base[href]', true).getLast(), {base: document.location}); String.implement({ toURI: function(options){ return new URI(this, options); } }); /* --- script: URI.Relative.js description: Extends the URI class to add methods for computing relative and absolute urls. license: MIT-style license authors: - Sebastian Markbåge requires: - /Class.refactor - /URI provides: [URI.Relative] ... */ URI = Class.refactor(URI, { combine: function(bits, base){ if (!base || bits.scheme != base.scheme || bits.host != base.host || bits.port != base.port) return this.previous.apply(this, arguments); var end = bits.file + (bits.query ? '?' + bits.query : '') + (bits.fragment ? '#' + bits.fragment : ''); if (!base.directory) return (bits.directory || (bits.file ? '' : './')) + end; var baseDir = base.directory.split('/'), relDir = bits.directory.split('/'), path = '', offset; var i = 0; for(offset = 0; offset < baseDir.length && offset < relDir.length && baseDir[offset] == relDir[offset]; offset++); for(i = 0; i < baseDir.length - offset - 1; i++) path += '../'; for(i = offset; i < relDir.length - 1; i++) path += relDir[i] + '/'; return (path || (bits.file ? '' : './')) + end; }, toAbsolute: function(base){ base = new URI(base); if (base) base.set('directory', '').set('file', ''); return this.toRelative(base); }, toRelative: function(base){ return this.get('value', new URI(base)); } }); /* --- script: Element.Forms.js description: Extends the Element native object to include methods useful in managing inputs. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Element - /MooTools.More provides: [Element.Forms] ... */ Element.implement({ tidy: function(){ this.set('value', this.get('value').tidy()); }, getTextInRange: function(start, end){ return this.get('value').substring(start, end); }, getSelectedText: function(){ if (this.setSelectionRange) return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd()); return document.selection.createRange().text; }, getSelectedRange: function() { if ($defined(this.selectionStart)) return {start: this.selectionStart, end: this.selectionEnd}; var pos = {start: 0, end: 0}; var range = this.getDocument().selection.createRange(); if (!range || range.parentElement() != this) return pos; var dup = range.duplicate(); if (this.type == 'text') { pos.start = 0 - dup.moveStart('character', -100000); pos.end = pos.start + range.text.length; } else { var value = this.get('value'); var offset = value.length; dup.moveToElementText(this); dup.setEndPoint('StartToEnd', range); if(dup.text.length) offset -= value.match(/[\n\r]*$/)[0].length; pos.end = offset - dup.text.length; dup.setEndPoint('StartToStart', range); pos.start = offset - dup.text.length; } return pos; }, getSelectionStart: function(){ return this.getSelectedRange().start; }, getSelectionEnd: function(){ return this.getSelectedRange().end; }, setCaretPosition: function(pos){ if (pos == 'end') pos = this.get('value').length; this.selectRange(pos, pos); return this; }, getCaretPosition: function(){ return this.getSelectedRange().start; }, selectRange: function(start, end){ if (this.setSelectionRange) { this.focus(); this.setSelectionRange(start, end); } else { var value = this.get('value'); var diff = value.substr(start, end - start).replace(/\r/g, '').length; start = value.substr(0, start).replace(/\r/g, '').length; var range = this.createTextRange(); range.collapse(true); range.moveEnd('character', start + diff); range.moveStart('character', start); range.select(); } return this; }, insertAtCursor: function(value, select){ var pos = this.getSelectedRange(); var text = this.get('value'); this.set('value', text.substring(0, pos.start) + value + text.substring(pos.end, text.length)); if ($pick(select, true)) this.selectRange(pos.start, pos.start + value.length); else this.setCaretPosition(pos.start + value.length); return this; }, insertAroundCursor: function(options, select){ options = $extend({ before: '', defaultMiddle: '', after: '' }, options); var value = this.getSelectedText() || options.defaultMiddle; var pos = this.getSelectedRange(); var text = this.get('value'); if (pos.start == pos.end){ this.set('value', text.substring(0, pos.start) + options.before + value + options.after + text.substring(pos.end, text.length)); this.selectRange(pos.start + options.before.length, pos.end + options.before.length + value.length); } else { var current = text.substring(pos.start, pos.end); this.set('value', text.substring(0, pos.start) + options.before + current + options.after + text.substring(pos.end, text.length)); var selStart = pos.start + options.before.length; if ($pick(select, true)) this.selectRange(selStart, selStart + current.length); else this.setCaretPosition(selStart + text.length); } return this; } }); /* --- script: Elements.From.js description: Returns a collection of elements from a string of html. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Element - /MooTools.More provides: [Elements.from] ... */ Elements.from = function(text, excludeScripts){ if ($pick(excludeScripts, true)) text = text.stripScripts(); var container, match = text.match(/^\s*<(t[dhr]|tbody|tfoot|thead)/i); if (match){ container = new Element('table'); var tag = match[1].toLowerCase(); if (['td', 'th', 'tr'].contains(tag)){ container = new Element('tbody').inject(container); if (tag != 'tr') container = new Element('tr').inject(container); } } return (container || new Element('div')).set('html', text).getChildren(); }; /* --- script: Element.Delegation.js description: Extends the Element native object to include the delegate method for more efficient event management. credits: - "Event checking based on the work of Daniel Steigerwald. License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz" license: MIT-style license authors: - Aaron Newton - Daniel Steigerwald requires: - core:1.2.4/Element.Event - core:1.2.4/Selectors - /MooTools.More provides: [Element.Delegation] ... */ (function(addEvent, removeEvent){ var match = /(.*?):relay\(([^)]+)\)$/, combinators = /[+>~\s]/, splitType = function(type){ var bits = type.match(match); return !bits ? {event: type} : { event: bits[1], selector: bits[2] }; }, check = function(e, selector){ var t = e.target; if (combinators.test(selector = selector.trim())){ var els = this.getElements(selector); for (var i = els.length; i--; ){ var el = els[i]; if (t == el || el.hasChild(t)) return el; } } else { for ( ; t && t != this; t = t.parentNode){ if (Element.match(t, selector)) return document.id(t); } } return null; }; Element.implement({ addEvent: function(type, fn){ var splitted = splitType(type); if (splitted.selector){ var monitors = this.retrieve('$moo:delegateMonitors', {}); if (!monitors[type]){ var monitor = function(e){ var el = check.call(this, e, splitted.selector); if (el) this.fireEvent(type, [e, el], 0, el); }.bind(this); monitors[type] = monitor; addEvent.call(this, splitted.event, monitor); } } return addEvent.apply(this, arguments); }, removeEvent: function(type, fn){ var splitted = splitType(type); if (splitted.selector){ var events = this.retrieve('events'); if (!events || !events[type] || (fn && !events[type].keys.contains(fn))) return this; if (fn) removeEvent.apply(this, [type, fn]); else removeEvent.apply(this, type); events = this.retrieve('events'); if (events && events[type] && events[type].keys.length == 0){ var monitors = this.retrieve('$moo:delegateMonitors', {}); removeEvent.apply(this, [splitted.event, monitors[type]]); delete monitors[type]; } return this; } return removeEvent.apply(this, arguments); }, fireEvent: function(type, args, delay, bind){ var events = this.retrieve('events'); if (!events || !events[type]) return this; events[type].keys.each(function(fn){ fn.create({bind: bind || this, delay: delay, arguments: args})(); }, this); return this; } }); })(Element.prototype.addEvent, Element.prototype.removeEvent); /* --- script: Element.Measure.js description: Extends the Element native object to include methods useful in measuring dimensions. credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz" license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Element.Style - core:1.2.4/Element.Dimensions - /MooTools.More provides: [Element.Measure] ... */ Element.implement({ measure: function(fn){ var vis = function(el) { return !!(!el || el.offsetHeight || el.offsetWidth); }; if (vis(this)) return fn.apply(this); var parent = this.getParent(), restorers = [], toMeasure = []; while (!vis(parent) && parent != document.body) { toMeasure.push(parent.expose()); parent = parent.getParent(); } var restore = this.expose(); var result = fn.apply(this); restore(); toMeasure.each(function(restore){ restore(); }); return result; }, expose: function(){ if (this.getStyle('display') != 'none') return $empty; var before = this.style.cssText; this.setStyles({ display: 'block', position: 'absolute', visibility: 'hidden' }); return function(){ this.style.cssText = before; }.bind(this); }, getDimensions: function(options){ options = $merge({computeSize: false},options); var dim = {}; var getSize = function(el, options){ return (options.computeSize)?el.getComputedSize(options):el.getSize(); }; var parent = this.getParent('body'); if (parent && this.getStyle('display') == 'none'){ dim = this.measure(function(){ return getSize(this, options); }); } else if (parent){ try { //safari sometimes crashes here, so catch it dim = getSize(this, options); }catch(e){} } else { dim = {x: 0, y: 0}; } return $chk(dim.x) ? $extend(dim, {width: dim.x, height: dim.y}) : $extend(dim, {x: dim.width, y: dim.height}); }, getComputedSize: function(options){ options = $merge({ styles: ['padding','border'], plains: { height: ['top','bottom'], width: ['left','right'] }, mode: 'both' }, options); var size = {width: 0,height: 0}; switch (options.mode){ case 'vertical': delete size.width; delete options.plains.width; break; case 'horizontal': delete size.height; delete options.plains.height; break; } var getStyles = []; //this function might be useful in other places; perhaps it should be outside this function? $each(options.plains, function(plain, key){ plain.each(function(edge){ options.styles.each(function(style){ getStyles.push((style == 'border') ? style + '-' + edge + '-' + 'width' : style + '-' + edge); }); }); }); var styles = {}; getStyles.each(function(style){ styles[style] = this.getComputedStyle(style); }, this); var subtracted = []; $each(options.plains, function(plain, key){ //keys: width, height, plains: ['left', 'right'], ['top','bottom'] var capitalized = key.capitalize(); size['total' + capitalized] = size['computed' + capitalized] = 0; plain.each(function(edge){ //top, left, right, bottom size['computed' + edge.capitalize()] = 0; getStyles.each(function(style, i){ //padding, border, etc. //'padding-left'.test('left') size['totalWidth'] = size['width'] + [padding-left] if (style.test(edge)){ styles[style] = styles[style].toInt() || 0; //styles['padding-left'] = 5; size['total' + capitalized] = size['total' + capitalized] + styles[style]; size['computed' + edge.capitalize()] = size['computed' + edge.capitalize()] + styles[style]; } //if width != width (so, padding-left, for instance), then subtract that from the total if (style.test(edge) && key != style && (style.test('border') || style.test('padding')) && !subtracted.contains(style)){ subtracted.push(style); size['computed' + capitalized] = size['computed' + capitalized]-styles[style]; } }); }); }); ['Width', 'Height'].each(function(value){ var lower = value.toLowerCase(); if(!$chk(size[lower])) return; size[lower] = size[lower] + this['offset' + value] + size['computed' + value]; size['total' + value] = size[lower] + size['total' + value]; delete size['computed' + value]; }, this); return $extend(styles, size); } }); /* --- script: Element.Pin.js description: Extends the Element native object to include the pin method useful for fixed positioning for elements. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Element.Event - core:1.2.4/Element.Dimensions - core:1.2.4/Element.Style - /MooTools.More provides: [Element.Pin] ... */ (function(){ var supportsPositionFixed = false; window.addEvent('domready', function(){ var test = new Element('div').setStyles({ position: 'fixed', top: 0, right: 0 }).inject(document.body); supportsPositionFixed = (test.offsetTop === 0); test.dispose(); }); Element.implement({ pin: function(enable){ if (this.getStyle('display') == 'none') return null; var p, scroll = window.getScroll(); if (enable !== false){ p = this.getPosition(); if (!this.retrieve('pinned')){ var pos = { top: p.y - scroll.y, left: p.x - scroll.x }; if (supportsPositionFixed){ this.setStyle('position', 'fixed').setStyles(pos); } else { this.store('pinnedByJS', true); this.setStyles({ position: 'absolute', top: p.y, left: p.x }).addClass('isPinned'); this.store('scrollFixer', (function(){ if (this.retrieve('pinned')) var scroll = window.getScroll(); this.setStyles({ top: pos.top.toInt() + scroll.y, left: pos.left.toInt() + scroll.x }); }).bind(this)); window.addEvent('scroll', this.retrieve('scrollFixer')); } this.store('pinned', true); } } else { var op; if (!Browser.Engine.trident){ var parent = this.getParent(); op = (parent.getComputedStyle('position') != 'static' ? parent : parent.getOffsetParent()); } p = this.getPosition(op); this.store('pinned', false); var reposition; if (supportsPositionFixed && !this.retrieve('pinnedByJS')){ reposition = { top: p.y + scroll.y, left: p.x + scroll.x }; } else { this.store('pinnedByJS', false); window.removeEvent('scroll', this.retrieve('scrollFixer')); reposition = { top: p.y, left: p.x }; } this.setStyles($merge(reposition, {position: 'absolute'})).removeClass('isPinned'); } return this; }, unpin: function(){ return this.pin(false); }, togglepin: function(){ this.pin(!this.retrieve('pinned')); } }); })(); /* --- script: Element.Position.js description: Extends the Element native object to include methods useful positioning elements relative to others. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Element.Dimensions - /Element.Measure provides: [Elements.Position] ... */ (function(){ var original = Element.prototype.position; Element.implement({ position: function(options){ //call original position if the options are x/y values if (options && ($defined(options.x) || $defined(options.y))) return original ? original.apply(this, arguments) : this; $each(options||{}, function(v, k){ if (!$defined(v)) delete options[k]; }); options = $merge({ // minimum: { x: 0, y: 0 }, // maximum: { x: 0, y: 0}, relativeTo: document.body, position: { x: 'center', //left, center, right y: 'center' //top, center, bottom }, edge: false, offset: {x: 0, y: 0}, returnPos: false, relFixedPosition: false, ignoreMargins: false, ignoreScroll: false, allowNegative: false }, options); //compute the offset of the parent positioned element if this element is in one var parentOffset = {x: 0, y: 0}, parentPositioned = false; /* dollar around getOffsetParent should not be necessary, but as it does not return * a mootools extended element in IE, an error occurs on the call to expose. See: * http://mootools.lighthouseapp.com/projects/2706/tickets/333-element-getoffsetparent-inconsistency-between-ie-and-other-browsers */ var offsetParent = this.measure(function(){ return document.id(this.getOffsetParent()); }); if (offsetParent && offsetParent != this.getDocument().body){ parentOffset = offsetParent.measure(function(){ return this.getPosition(); }); parentPositioned = offsetParent != document.id(options.relativeTo); options.offset.x = options.offset.x - parentOffset.x; options.offset.y = options.offset.y - parentOffset.y; } //upperRight, bottomRight, centerRight, upperLeft, bottomLeft, centerLeft //topRight, topLeft, centerTop, centerBottom, center var fixValue = function(option){ if ($type(option) != 'string') return option; option = option.toLowerCase(); var val = {}; if (option.test('left')) val.x = 'left'; else if (option.test('right')) val.x = 'right'; else val.x = 'center'; if (option.test('upper') || option.test('top')) val.y = 'top'; else if (option.test('bottom')) val.y = 'bottom'; else val.y = 'center'; return val; }; options.edge = fixValue(options.edge); options.position = fixValue(options.position); if (!options.edge){ if (options.position.x == 'center' && options.position.y == 'center') options.edge = {x:'center', y:'center'}; else options.edge = {x:'left', y:'top'}; } this.setStyle('position', 'absolute'); var rel = document.id(options.relativeTo) || document.body, calc = rel == document.body ? window.getScroll() : rel.getPosition(), top = calc.y, left = calc.x; var dim = this.getDimensions({computeSize: true, styles:['padding', 'border','margin']}); var pos = {}, prefY = options.offset.y, prefX = options.offset.x, winSize = window.getSize(); switch(options.position.x){ case 'left': pos.x = left + prefX; break; case 'right': pos.x = left + prefX + rel.offsetWidth; break; default: //center pos.x = left + ((rel == document.body ? winSize.x : rel.offsetWidth)/2) + prefX; break; } switch(options.position.y){ case 'top': pos.y = top + prefY; break; case 'bottom': pos.y = top + prefY + rel.offsetHeight; break; default: //center pos.y = top + ((rel == document.body ? winSize.y : rel.offsetHeight)/2) + prefY; break; } if (options.edge){ var edgeOffset = {}; switch(options.edge.x){ case 'left': edgeOffset.x = 0; break; case 'right': edgeOffset.x = -dim.x-dim.computedRight-dim.computedLeft; break; default: //center edgeOffset.x = -(dim.totalWidth/2); break; } switch(options.edge.y){ case 'top': edgeOffset.y = 0; break; case 'bottom': edgeOffset.y = -dim.y-dim.computedTop-dim.computedBottom; break; default: //center edgeOffset.y = -(dim.totalHeight/2); break; } pos.x += edgeOffset.x; pos.y += edgeOffset.y; } pos = { left: ((pos.x >= 0 || parentPositioned || options.allowNegative) ? pos.x : 0).toInt(), top: ((pos.y >= 0 || parentPositioned || options.allowNegative) ? pos.y : 0).toInt() }; var xy = {left: 'x', top: 'y'}; ['minimum', 'maximum'].each(function(minmax) { ['left', 'top'].each(function(lr) { var val = options[minmax] ? options[minmax][xy[lr]] : null; if (val != null && pos[lr] < val) pos[lr] = val; }); }); if (rel.getStyle('position') == 'fixed' || options.relFixedPosition){ var winScroll = window.getScroll(); pos.top+= winScroll.y; pos.left+= winScroll.x; } if (options.ignoreScroll) { var relScroll = rel.getScroll(); pos.top-= relScroll.y; pos.left-= relScroll.x; } if (options.ignoreMargins) { pos.left += ( options.edge.x == 'right' ? dim['margin-right'] : options.edge.x == 'center' ? -dim['margin-left'] + ((dim['margin-right'] + dim['margin-left'])/2) : - dim['margin-left'] ); pos.top += ( options.edge.y == 'bottom' ? dim['margin-bottom'] : options.edge.y == 'center' ? -dim['margin-top'] + ((dim['margin-bottom'] + dim['margin-top'])/2) : - dim['margin-top'] ); } pos.left = Math.ceil(pos.left); pos.top = Math.ceil(pos.top); if (options.returnPos) return pos; else this.setStyles(pos); return this; } }); })(); /* --- script: Element.Shortcuts.js description: Extends the Element native object to include some shortcut methods. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Element.Style - /MooTools.More provides: [Element.Shortcuts] ... */ Element.implement({ isDisplayed: function(){ return this.getStyle('display') != 'none'; }, isVisible: function(){ var w = this.offsetWidth, h = this.offsetHeight; return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.isDisplayed(); }, toggle: function(){ return this[this.isDisplayed() ? 'hide' : 'show'](); }, hide: function(){ var d; try { //IE fails here if the element is not in the dom d = this.getStyle('display'); } catch(e){} return this.store('originalDisplay', d || '').setStyle('display', 'none'); }, show: function(display){ display = display || this.retrieve('originalDisplay') || 'block'; return this.setStyle('display', (display == 'none') ? 'block' : display); }, swapClass: function(remove, add){ return this.removeClass(remove).addClass(add); } }); /* --- script: Form.Request.js description: Handles the basic functionality of submitting a form and updating a dom element with the result. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Element.Event - core:1.2.4/Request.HTML - /Class.Binds - /Class.Occlude - /Spinner - /String.QueryString provides: [Form.Request] ... */ if (!window.Form) window.Form = {}; (function(){ Form.Request = new Class({ Binds: ['onSubmit', 'onFormValidate'], Implements: [Options, Events, Class.Occlude], options: { //onFailure: $empty, //onSuccess: #empty, //aliased to onComplete, //onSend: $empty requestOptions: { evalScripts: true, useSpinner: true, emulation: false, link: 'ignore' }, extraData: {}, resetForm: true }, property: 'form.request', initialize: function(form, update, options) { this.element = document.id(form); if (this.occlude()) return this.occluded; this.update = document.id(update); this.setOptions(options); this.makeRequest(); if (this.options.resetForm) { this.request.addEvent('success', function(){ $try(function(){ this.element.reset(); }.bind(this)); if (window.OverText) OverText.update(); }.bind(this)); } this.attach(); }, toElement: function() { return this.element; }, makeRequest: function(){ this.request = new Request.HTML($merge({ update: this.update, emulation: false, spinnerTarget: this.element, method: this.element.get('method') || 'post' }, this.options.requestOptions)).addEvents({ success: function(text, xml){ ['complete', 'success'].each(function(evt){ this.fireEvent(evt, [this.update, text, xml]); }, this); }.bind(this), failure: function(xhr){ this.fireEvent('complete').fireEvent('failure', xhr); }.bind(this), exception: function(){ this.fireEvent('failure', xhr); }.bind(this) }); }, attach: function(attach){ attach = $pick(attach, true); method = attach ? 'addEvent' : 'removeEvent'; var fv = this.element.retrieve('validator'); if (fv) fv[method]('onFormValidate', this.onFormValidate); if (!fv || !attach) this.element[method]('submit', this.onSubmit); }, detach: function(){ this.attach(false); }, //public method enable: function(){ this.attach(); }, //public method disable: function(){ this.detach(); }, onFormValidate: function(valid, form, e) { var fv = this.element.retrieve('validator'); if (valid || (fv && !fv.options.stopOnFailure)) { if (e && e.stop) e.stop(); this.send(); } }, onSubmit: function(e){ if (this.element.retrieve('validator')) { //form validator was created after Form.Request this.detach(); return; } e.stop(); this.send(); }, send: function(){ var str = this.element.toQueryString().trim(); var data = $H(this.options.extraData).toQueryString(); if (str) str += "&" + data; else str = data; this.fireEvent('send', [this.element, str.parseQueryString()]); this.request.send({data: str, url: this.element.get("action")}); return this; } }); Element.Properties.formRequest = { set: function(){ var opt = Array.link(arguments, {options: Object.type, update: Element.type, updateId: String.type}); var update = opt.update || opt.updateId; var updater = this.retrieve('form.request'); if (update) { if (updater) updater.update = document.id(update); this.store('form.request:update', update); } if (opt.options) { if (updater) updater.setOptions(opt.options); this.store('form.request:options', opt.options); } return this; }, get: function(){ var opt = Array.link(arguments, {options: Object.type, update: Element.type, updateId: String.type}); var update = opt.update || opt.updateId; if (opt.options || update || !this.retrieve('form.request')){ if (opt.options || !this.retrieve('form.request:options')) this.set('form.request', opt.options); if (update) this.set('form.request', update); this.store('form.request', new Form.Request(this, this.retrieve('form.request:update'), this.retrieve('form.request:options'))); } return this.retrieve('form.request'); } }; Element.implement({ formUpdate: function(update, options){ this.get('form.request', update, options).send(); return this; } }); })(); /* --- script: Form.Request.Append.js description: Handles the basic functionality of submitting a form and updating a dom element with the result. The result is appended to the DOM element instead of replacing its contents. license: MIT-style license authors: - Aaron Newton requires: - /Form.Request - /Fx.Reveal - /Elements.from provides: [Form.Request.Append] ... */ Form.Request.Append = new Class({ Extends: Form.Request, options: { //onBeforeEffect: $empty, useReveal: true, revealOptions: {}, inject: 'bottom' }, makeRequest: function(){ this.request = new Request.HTML($merge({ url: this.element.get('action'), method: this.element.get('method') || 'post', spinnerTarget: this.element }, this.options.requestOptions, { evalScripts: false }) ).addEvents({ success: function(tree, elements, html, javascript){ var container; var kids = Elements.from(html); if (kids.length == 1) { container = kids[0]; } else { container = new Element('div', { styles: { display: 'none' } }).adopt(kids); } container.inject(this.update, this.options.inject); if (this.options.requestOptions.evalScripts) $exec(javascript); this.fireEvent('beforeEffect', container); var finish = function(){ this.fireEvent('success', [container, this.update, tree, elements, html, javascript]); }.bind(this); if (this.options.useReveal) { container.get('reveal', this.options.revealOptions).chain(finish); container.reveal(); } else { finish(); } }.bind(this), failure: function(xhr){ this.fireEvent('failure', xhr); }.bind(this) }); } }); /* --- script: Form.Validator.js description: A css-class based form validation system. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Options - core:1.2.4/Events - core:1.2.4/Selectors - core:1.2.4/Element.Event - core:1.2.4/Element.Style - core:1.2.4/JSON - /Lang- /Class.Binds - /Date Element.Forms - /Form.Validator.English - /Element.Shortcuts provides: [Form.Validator, InputValidator, FormValidator.BaseValidators] ... */ if (!window.Form) window.Form = {}; var InputValidator = new Class({ Implements: [Options], options: { errorMsg: 'Validation failed.', test: function(field){return true;} }, initialize: function(className, options){ this.setOptions(options); this.className = className; }, test: function(field, props){ if (document.id(field)) return this.options.test(document.id(field), props||this.getProps(field)); else return false; }, getError: function(field, props){ var err = this.options.errorMsg; if ($type(err) == 'function') err = err(document.id(field), props||this.getProps(field)); return err; }, getProps: function(field){ if (!document.id(field)) return {}; return field.get('validatorProps'); } }); Element.Properties.validatorProps = { set: function(props){ return this.eliminate('validatorProps').store('validatorProps', props); }, get: function(props){ if (props) this.set(props); if (this.retrieve('validatorProps')) return this.retrieve('validatorProps'); if (this.getProperty('validatorProps')){ try { this.store('validatorProps', JSON.decode(this.getProperty('validatorProps'))); }catch(e){ return {}; } } else { var vals = this.get('class').split(' ').filter(function(cls){ return cls.test(':'); }); if (!vals.length){ this.store('validatorProps', {}); } else { props = {}; vals.each(function(cls){ var split = cls.split(':'); if (split[1]) { try { props[split[0]] = JSON.decode(split[1]); } catch(e) {} } }); this.store('validatorProps', props); } } return this.retrieve('validatorProps'); } }; Form.Validator = new Class({ Implements:[Options, Events], Binds: ['onSubmit'], options: {/* onFormValidate: $empty(isValid, form, event), onElementValidate: $empty(isValid, field, className, warn), onElementPass: $empty(field), onElementFail: $empty(field, validatorsFailed) */ fieldSelectors: 'input, select, textarea', ignoreHidden: true, ignoreDisabled: true, useTitles: false, evaluateOnSubmit: true, evaluateFieldsOnBlur: true, evaluateFieldsOnChange: true, serial: true, stopOnFailure: true, warningPrefix: function(){ return Form.Validator.getMsg('warningPrefix') || 'Warning: '; }, errorPrefix: function(){ return Form.Validator.getMsg('errorPrefix') || 'Error: '; } }, initialize: function(form, options){ this.setOptions(options); this.element = document.id(form); this.element.store('validator', this); this.warningPrefix = $lambda(this.options.warningPrefix)(); this.errorPrefix = $lambda(this.options.errorPrefix)(); if (this.options.evaluateOnSubmit) this.element.addEvent('submit', this.onSubmit); if (this.options.evaluateFieldsOnBlur || this.options.evaluateFieldsOnChange) this.watchFields(this.getFields()); }, toElement: function(){ return this.element; }, getFields: function(){ return (this.fields = this.element.getElements(this.options.fieldSelectors)); }, watchFields: function(fields){ fields.each(function(el){ if (this.options.evaluateFieldsOnBlur) el.addEvent('blur', this.validationMonitor.pass([el, false], this)); if (this.options.evaluateFieldsOnChange) el.addEvent('change', this.validationMonitor.pass([el, true], this)); }, this); }, validationMonitor: function(){ $clear(this.timer); this.timer = this.validateField.delay(50, this, arguments); }, onSubmit: function(event){ if (!this.validate(event) && event) event.preventDefault(); else this.reset(); }, reset: function(){ this.getFields().each(this.resetField, this); return this; }, validate: function(event){ var result = this.getFields().map(function(field){ return this.validateField(field, true); }, this).every(function(v){ return v;}); this.fireEvent('formValidate', [result, this.element, event]); if (this.options.stopOnFailure && !result && event) event.preventDefault(); return result; }, validateField: function(field, force){ if (this.paused) return true; field = document.id(field); var passed = !field.hasClass('validation-failed'); var failed, warned; if (this.options.serial && !force){ failed = this.element.getElement('.validation-failed'); warned = this.element.getElement('.warning'); } if (field && (!failed || force || field.hasClass('validation-failed') || (failed && !this.options.serial))){ var validators = field.className.split(' ').some(function(cn){ return this.getValidator(cn); }, this); var validatorsFailed = []; field.className.split(' ').each(function(className){ if (className && !this.test(className, field)) validatorsFailed.include(className); }, this); passed = validatorsFailed.length === 0; if (validators && !field.hasClass('warnOnly')){ if (passed){ field.addClass('validation-passed').removeClass('validation-failed'); this.fireEvent('elementPass', field); } else { field.addClass('validation-failed').removeClass('validation-passed'); this.fireEvent('elementFail', [field, validatorsFailed]); } } if (!warned){ var warnings = field.className.split(' ').some(function(cn){ if (cn.test('^warn-') || field.hasClass('warnOnly')) return this.getValidator(cn.replace(/^warn-/,'')); else return null; }, this); field.removeClass('warning'); var warnResult = field.className.split(' ').map(function(cn){ if (cn.test('^warn-') || field.hasClass('warnOnly')) return this.test(cn.replace(/^warn-/,''), field, true); else return null; }, this); } } return passed; }, test: function(className, field, warn){ field = document.id(field); if((this.options.ignoreHidden && !field.isVisible()) || (this.options.ignoreDisabled && field.get('disabled'))) return true; var validator = this.getValidator(className); if (field.hasClass('ignoreValidation')) return true; warn = $pick(warn, false); if (field.hasClass('warnOnly')) warn = true; var isValid = validator ? validator.test(field) : true; if (validator && field.isVisible()) this.fireEvent('elementValidate', [isValid, field, className, warn]); if (warn) return true; return isValid; }, resetField: function(field){ field = document.id(field); if (field){ field.className.split(' ').each(function(className){ if (className.test('^warn-')) className = className.replace(/^warn-/, ''); field.removeClass('validation-failed'); field.removeClass('warning'); field.removeClass('validation-passed'); }, this); } return this; }, stop: function(){ this.paused = true; return this; }, start: function(){ this.paused = false; return this; }, ignoreField: function(field, warn){ field = document.id(field); if (field){ this.enforceField(field); if (warn) field.addClass('warnOnly'); else field.addClass('ignoreValidation'); } return this; }, enforceField: function(field){ field = document.id(field); if (field) field.removeClass('warnOnly').removeClass('ignoreValidation'); return this; } }); Form.Validator.getMsg = function(key){ return MooTools.lang.get('Form.Validator', key); }; Form.Validator.adders = { validators:{}, add : function(className, options){ this.validators[className] = new InputValidator(className, options); //if this is a class (this method is used by instances of Form.Validator and the Form.Validator namespace) //extend these validators into it //this allows validators to be global and/or per instance if (!this.initialize){ this.implement({ validators: this.validators }); } }, addAllThese : function(validators){ $A(validators).each(function(validator){ this.add(validator[0], validator[1]); }, this); }, getValidator: function(className){ return this.validators[className.split(':')[0]]; } }; $extend(Form.Validator, Form.Validator.adders); Form.Validator.implement(Form.Validator.adders); Form.Validator.add('IsEmpty', { errorMsg: false, test: function(element){ if (element.type == 'select-one' || element.type == 'select') return !(element.selectedIndex >= 0 && element.options[element.selectedIndex].value != ''); else return ((element.get('value') == null) || (element.get('value').length == 0)); } }); Form.Validator.addAllThese([ ['required', { errorMsg: function(){ return Form.Validator.getMsg('required'); }, test: function(element){ return !Form.Validator.getValidator('IsEmpty').test(element); } }], ['minLength', { errorMsg: function(element, props){ if ($type(props.minLength)) return Form.Validator.getMsg('minLength').substitute({minLength:props.minLength,length:element.get('value').length }); else return ''; }, test: function(element, props){ if ($type(props.minLength)) return (element.get('value').length >= $pick(props.minLength, 0)); else return true; } }], ['maxLength', { errorMsg: function(element, props){ //props is {maxLength:10} if ($type(props.maxLength)) return Form.Validator.getMsg('maxLength').substitute({maxLength:props.maxLength,length:element.get('value').length }); else return ''; }, test: function(element, props){ //if the value is <= than the maxLength value, element passes test return (element.get('value').length <= $pick(props.maxLength, 10000)); } }], ['validate-integer', { errorMsg: Form.Validator.getMsg.pass('integer'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || (/^(-?[1-9]\d*|0)$/).test(element.get('value')); } }], ['validate-numeric', { errorMsg: Form.Validator.getMsg.pass('numeric'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || (/^-?(?:0$0(?=\d*\.)|[1-9]|0)\d*(\.\d+)?$/).test(element.get('value')); } }], ['validate-digits', { errorMsg: Form.Validator.getMsg.pass('digits'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || (/^[\d() .:\-\+#]+$/.test(element.get('value'))); } }], ['validate-alpha', { errorMsg: Form.Validator.getMsg.pass('alpha'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || (/^[a-zA-Z]+$/).test(element.get('value')); } }], ['validate-alphanum', { errorMsg: Form.Validator.getMsg.pass('alphanum'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || !(/\W/).test(element.get('value')); } }], ['validate-date', { errorMsg: function(element, props){ if (Date.parse){ var format = props.dateFormat || '%x'; return Form.Validator.getMsg('dateSuchAs').substitute({date: new Date().format(format)}); } else { return Form.Validator.getMsg('dateInFormatMDY'); } }, test: function(element, props){ if (Form.Validator.getValidator('IsEmpty').test(element)) return true; var d; if (Date.parse){ var format = props.dateFormat || '%x'; d = Date.parse(element.get('value')); var formatted = d.format(format); if (formatted != 'invalid date') element.set('value', formatted); return !isNaN(d); } else { var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/; if (!regex.test(element.get('value'))) return false; d = new Date(element.get('value').replace(regex, '$1/$2/$3')); return (parseInt(RegExp.$1, 10) == (1 + d.getMonth())) && (parseInt(RegExp.$2, 10) == d.getDate()) && (parseInt(RegExp.$3, 10) == d.getFullYear()); } } }], ['validate-email', { errorMsg: Form.Validator.getMsg.pass('email'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || (/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i).test(element.get('value')); } }], ['validate-url', { errorMsg: Form.Validator.getMsg.pass('url'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || (/^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i).test(element.get('value')); } }], ['validate-currency-dollar', { errorMsg: Form.Validator.getMsg.pass('currencyDollar'), test: function(element){ // [$]1[##][,###]+[.##] // [$]1###+[.##] // [$]0.## // [$].## return Form.Validator.getValidator('IsEmpty').test(element) || (/^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(element.get('value')); } }], ['validate-one-required', { errorMsg: Form.Validator.getMsg.pass('oneRequired'), test: function(element, props){ var p = document.id(props['validate-one-required']) || element.getParent(); return p.getElements('input').some(function(el){ if (['checkbox', 'radio'].contains(el.get('type'))) return el.get('checked'); return el.get('value'); }); } }] ]); Element.Properties.validator = { set: function(options){ var validator = this.retrieve('validator'); if (validator) validator.setOptions(options); return this.store('validator:options'); }, get: function(options){ if (options || !this.retrieve('validator')){ if (options || !this.retrieve('validator:options')) this.set('validator', options); this.store('validator', new Form.Validator(this, this.retrieve('validator:options'))); } return this.retrieve('validator'); } }; Element.implement({ validate: function(options){ this.set('validator', options); return this.get('validator', options).validate(); } }); //legacy var FormValidator = Form.Validator; /* --- script: Form.Validator.Inline.js description: Extends Form.Validator to add inline messages. license: MIT-style license authors: - Aaron Newton requires: - /Form.Validator provides: [Form.Validator.Inline] ... */ Form.Validator.Inline = new Class({ Extends: Form.Validator, options: { scrollToErrorsOnSubmit: true, scrollFxOptions: { transition: 'quad:out', offset: { y: -20 } } }, initialize: function(form, options){ this.parent(form, options); this.addEvent('onElementValidate', function(isValid, field, className, warn){ var validator = this.getValidator(className); if (!isValid && validator.getError(field)){ if (warn) field.addClass('warning'); var advice = this.makeAdvice(className, field, validator.getError(field), warn); this.insertAdvice(advice, field); this.showAdvice(className, field); } else { this.hideAdvice(className, field); } }); }, makeAdvice: function(className, field, error, warn){ var errorMsg = (warn)?this.warningPrefix:this.errorPrefix; errorMsg += (this.options.useTitles) ? field.title || error:error; var cssClass = (warn) ? 'warning-advice' : 'validation-advice'; var advice = this.getAdvice(className, field); if(advice) { advice = advice.set('html', errorMsg); } else { advice = new Element('div', { html: errorMsg, styles: { display: 'none' }, id: 'advice-' + className + '-' + this.getFieldId(field) }).addClass(cssClass); } field.store('advice-' + className, advice); return advice; }, getFieldId : function(field){ return field.id ? field.id : field.id = 'input_' + field.name; }, showAdvice: function(className, field){ var advice = this.getAdvice(className, field); if (advice && !field.retrieve(this.getPropName(className)) && (advice.getStyle('display') == 'none' || advice.getStyle('visiblity') == 'hidden' || advice.getStyle('opacity') == 0)){ field.store(this.getPropName(className), true); if (advice.reveal) advice.reveal(); else advice.setStyle('display', 'block'); } }, hideAdvice: function(className, field){ var advice = this.getAdvice(className, field); if (advice && field.retrieve(this.getPropName(className))){ field.store(this.getPropName(className), false); //if Fx.Reveal.js is present, transition the advice out if (advice.dissolve) advice.dissolve(); else advice.setStyle('display', 'none'); } }, getPropName: function(className){ return 'advice' + className; }, resetField: function(field){ field = document.id(field); if (!field) return this; this.parent(field); field.className.split(' ').each(function(className){ this.hideAdvice(className, field); }, this); return this; }, getAllAdviceMessages: function(field, force){ var advice = []; if (field.hasClass('ignoreValidation') && !force) return advice; var validators = field.className.split(' ').some(function(cn){ var warner = cn.test('^warn-') || field.hasClass('warnOnly'); if (warner) cn = cn.replace(/^warn-/, ''); var validator = this.getValidator(cn); if (!validator) return; advice.push({ message: validator.getError(field), warnOnly: warner, passed: validator.test(), validator: validator }); }, this); return advice; }, getAdvice: function(className, field){ return field.retrieve('advice-' + className); }, insertAdvice: function(advice, field){ //Check for error position prop var props = field.get('validatorProps'); //Build advice if (!props.msgPos || !document.id(props.msgPos)){ if(field.type.toLowerCase() == 'radio') field.getParent().adopt(advice); else advice.inject(document.id(field), 'after'); } else { document.id(props.msgPos).grab(advice); } }, validateField: function(field, force){ var result = this.parent(field, force); if (this.options.scrollToErrorsOnSubmit && !result){ var failed = document.id(this).getElement('.validation-failed'); var par = document.id(this).getParent(); while (par != document.body && par.getScrollSize().y == par.getSize().y){ par = par.getParent(); } var fx = par.retrieve('fvScroller'); if (!fx && window.Fx && Fx.Scroll){ fx = new Fx.Scroll(par, this.options.scrollFxOptions); par.store('fvScroller', fx); } if (failed){ if (fx) fx.toElement(failed); else par.scrollTo(par.getScroll().x, failed.getPosition(par).y - 20); } } return result; } }); /* --- script: Form.Validator.Extras.js description: Additional validators for the Form.Validator class. license: MIT-style license authors: - Aaron Newton requires: - /Form.Validator provides: [Form.Validator.Extras] ... */ Form.Validator.addAllThese([ ['validate-enforce-oncheck', { test: function(element, props){ if (element.checked){ var fv = element.getParent('form').retrieve('validator'); if (!fv) return true; (props.toEnforce || document.id(props.enforceChildrenOf).getElements('input, select, textarea')).map(function(item){ fv.enforceField(item); }); } return true; } }], ['validate-ignore-oncheck', { test: function(element, props){ if (element.checked){ var fv = element.getParent('form').retrieve('validator'); if (!fv) return true; (props.toIgnore || document.id(props.ignoreChildrenOf).getElements('input, select, textarea')).each(function(item){ fv.ignoreField(item); fv.resetField(item); }); } return true; } }], ['validate-nospace', { errorMsg: function(){ return Form.Validator.getMsg('noSpace'); }, test: function(element, props){ return !element.get('value').test(/\s/); } }], ['validate-toggle-oncheck', { test: function(element, props){ var fv = element.getParent('form').retrieve('validator'); if (!fv) return true; var eleArr = props.toToggle || document.id(props.toToggleChildrenOf).getElements('input, select, textarea'); if (!element.checked){ eleArr.each(function(item){ fv.ignoreField(item); fv.resetField(item); }); } else { eleArr.each(function(item){ fv.enforceField(item); }); } return true; } }], ['validate-reqchk-bynode', { errorMsg: function(){ return Form.Validator.getMsg('reqChkByNode'); }, test: function(element, props){ return (document.id(props.nodeId).getElements(props.selector || 'input[type=checkbox], input[type=radio]')).some(function(item){ return item.checked; }); } }], ['validate-required-check', { errorMsg: function(element, props){ return props.useTitle ? element.get('title') : Form.Validator.getMsg('requiredChk'); }, test: function(element, props){ return !!element.checked; } }], ['validate-reqchk-byname', { errorMsg: function(element, props){ return Form.Validator.getMsg('reqChkByName').substitute({label: props.label || element.get('type')}); }, test: function(element, props){ var grpName = props.groupName || element.get('name'); var oneCheckedItem = $$(document.getElementsByName(grpName)).some(function(item, index){ return item.checked; }); var fv = element.getParent('form').retrieve('validator'); if (oneCheckedItem && fv) fv.resetField(element); return oneCheckedItem; } }], ['validate-match', { errorMsg: function(element, props){ return Form.Validator.getMsg('match').substitute({matchName: props.matchName || document.id(props.matchInput).get('name')}); }, test: function(element, props){ var eleVal = element.get('value'); var matchVal = document.id(props.matchInput) && document.id(props.matchInput).get('value'); return eleVal && matchVal ? eleVal == matchVal : true; } }], ['validate-after-date', { errorMsg: function(element, props){ return Form.Validator.getMsg('afterDate').substitute({ label: props.afterLabel || (props.afterElement ? Form.Validator.getMsg('startDate') : Form.Validator.getMsg('currentDate')) }); }, test: function(element, props){ var start = document.id(props.afterElement) ? Date.parse(document.id(props.afterElement).get('value')) : new Date(); var end = Date.parse(element.get('value')); return end && start ? end >= start : true; } }], ['validate-before-date', { errorMsg: function(element, props){ return Form.Validator.getMsg('beforeDate').substitute({ label: props.beforeLabel || (props.beforeElement ? Form.Validator.getMsg('endDate') : Form.Validator.getMsg('currentDate')) }); }, test: function(element, props){ var start = Date.parse(element.get('value')); var end = document.id(props.beforeElement) ? Date.parse(document.id(props.beforeElement).get('value')) : new Date(); return end && start ? end >= start : true; } }], ['validate-custom-required', { errorMsg: function(){ return Form.Validator.getMsg('required'); }, test: function(element, props){ return element.get('value') != props.emptyValue; } }], ['validate-same-month', { errorMsg: function(element, props){ var startMo = document.id(props.sameMonthAs) && document.id(props.sameMonthAs).get('value'); var eleVal = element.get('value'); if (eleVal != '') return Form.Validator.getMsg(startMo ? 'sameMonth' : 'startMonth'); }, test: function(element, props){ var d1 = Date.parse(element.get('value')); var d2 = Date.parse(document.id(props.sameMonthAs) && document.id(props.sameMonthAs).get('value')); return d1 && d2 ? d1.format('%B') == d2.format('%B') : true; } }], ['validate-cc-num', { errorMsg: function(element){ var ccNum = element.get('value').replace(/[^0-9]/g, ''); return Form.Validator.getMsg('creditcard').substitute({length: ccNum.length}); }, test: function(element){ // required is a different test if (Form.Validator.getValidator('IsEmpty').test(element)) { return true; } // Clean number value var ccNum = element.get('value'); ccNum = ccNum.replace(/[^0-9]/g, ''); var valid_type = false; if (ccNum.test(/^4[0-9]{12}([0-9]{3})?$/)) valid_type = 'Visa'; else if (ccNum.test(/^5[1-5]([0-9]{14})$/)) valid_type = 'Master Card'; else if (ccNum.test(/^3[47][0-9]{13}$/)) valid_type = 'American Express'; else if (ccNum.test(/^6011[0-9]{12}$/)) valid_type = 'Discover'; if (valid_type) { var sum = 0; var cur = 0; for(var i=ccNum.length-1; i>=0; --i) { cur = ccNum.charAt(i).toInt(); if (cur == 0) { continue; } if ((ccNum.length-i) % 2 == 0) { cur += cur; } if (cur > 9) { cur = cur.toString().charAt(0).toInt() + cur.toString().charAt(1).toInt(); } sum += cur; } if ((sum % 10) == 0) { return true; } } var chunks = ''; while (ccNum != '') { chunks += ' ' + ccNum.substr(0,4); ccNum = ccNum.substr(4); } element.getParent('form').retrieve('validator').ignoreField(element); element.set('value', chunks.clean()); element.getParent('form').retrieve('validator').enforceField(element); return false; } }] ]); /* --- script: OverText.js description: Shows text over an input that disappears when the user clicks into it. The text remains hidden if the user adds a value. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Options - core:1.2.4/Events - core:1.2.4/Element.Event - /Class.Binds - /Class.Occlude - /Element.Position - /Element.Shortcuts provides: [OverText] ... */ var OverText = new Class({ Implements: [Options, Events, Class.Occlude], Binds: ['reposition', 'assert', 'focus', 'hide'], options: {/* textOverride: null, onFocus: $empty() onTextHide: $empty(textEl, inputEl), onTextShow: $empty(textEl, inputEl), */ element: 'label', positionOptions: { position: 'upperLeft', edge: 'upperLeft', offset: { x: 4, y: 2 } }, poll: false, pollInterval: 250, wrap: false }, property: 'OverText', initialize: function(element, options){ this.element = document.id(element); if (this.occlude()) return this.occluded; this.setOptions(options); this.attach(this.element); OverText.instances.push(this); if (this.options.poll) this.poll(); return this; }, toElement: function(){ return this.element; }, attach: function(){ var val = this.options.textOverride || this.element.get('alt') || this.element.get('title'); if (!val) return; this.text = new Element(this.options.element, { 'class': 'overTxtLabel', styles: { lineHeight: 'normal', position: 'absolute', cursor: 'text' }, html: val, events: { click: this.hide.pass(this.options.element == 'label', this) } }).inject(this.element, 'after'); if (this.options.element == 'label') { if (!this.element.get('id')) this.element.set('id', 'input_' + new Date().getTime()); this.text.set('for', this.element.get('id')); } if (this.options.wrap) { this.textHolder = new Element('div', { styles: { lineHeight: 'normal', position: 'relative' }, 'class':'overTxtWrapper' }).adopt(this.text).inject(this.element, 'before'); } this.element.addEvents({ focus: this.focus, blur: this.assert, change: this.assert }).store('OverTextDiv', this.text); window.addEvent('resize', this.reposition.bind(this)); this.assert(true); this.reposition(); }, wrap: function(){ if (this.options.element == 'label') { if (!this.element.get('id')) this.element.set('id', 'input_' + new Date().getTime()); this.text.set('for', this.element.get('id')); } }, startPolling: function(){ this.pollingPaused = false; return this.poll(); }, poll: function(stop){ //start immediately //pause on focus //resumeon blur if (this.poller && !stop) return this; var test = function(){ if (!this.pollingPaused) this.assert(true); }.bind(this); if (stop) $clear(this.poller); else this.poller = test.periodical(this.options.pollInterval, this); return this; }, stopPolling: function(){ this.pollingPaused = true; return this.poll(true); }, focus: function(){ if (this.text && (!this.text.isDisplayed() || this.element.get('disabled'))) return; this.hide(); }, hide: function(suppressFocus, force){ if (this.text && (this.text.isDisplayed() && (!this.element.get('disabled') || force))){ this.text.hide(); this.fireEvent('textHide', [this.text, this.element]); this.pollingPaused = true; if (!suppressFocus){ try { this.element.fireEvent('focus'); this.element.focus(); } catch(e){} //IE barfs if you call focus on hidden elements } } return this; }, show: function(){ if (this.text && !this.text.isDisplayed()){ this.text.show(); this.reposition(); this.fireEvent('textShow', [this.text, this.element]); this.pollingPaused = false; } return this; }, assert: function(suppressFocus){ this[this.test() ? 'show' : 'hide'](suppressFocus); }, test: function(){ var v = this.element.get('value'); return !v; }, reposition: function(){ this.assert(true); if (!this.element.isVisible()) return this.stopPolling().hide(); if (this.text && this.test()) this.text.position($merge(this.options.positionOptions, {relativeTo: this.element})); return this; } }); OverText.instances = []; $extend(OverText, { each: function(fn) { return OverText.instances.map(function(ot, i){ if (ot.element && ot.text) return fn.apply(OverText, [ot, i]); return null; //the input or the text was destroyed }); }, update: function(){ return OverText.each(function(ot){ return ot.reposition(); }); }, hideAll: function(){ return OverText.each(function(ot){ return ot.hide(true, true); }); }, showAll: function(){ return OverText.each(function(ot) { return ot.show(); }); } }); if (window.Fx && Fx.Reveal) { Fx.Reveal.implement({ hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed, .overTxtLabel' : false }); } /* --- script: Fx.Elements.js description: Effect to change any number of CSS properties of any number of Elements. license: MIT-style license authors: - Valerio Proietti requires: - core:1.2.4/Fx.CSS - /MooTools.More provides: [Fx.Elements] ... */ Fx.Elements = new Class({ Extends: Fx.CSS, initialize: function(elements, options){ this.elements = this.subject = $$(elements); this.parent(options); }, compute: function(from, to, delta){ var now = {}; for (var i in from){ var iFrom = from[i], iTo = to[i], iNow = now[i] = {}; for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta); } return now; }, set: function(now){ for (var i in now){ var iNow = now[i]; for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit); } return this; }, start: function(obj){ if (!this.check(obj)) return this; var from = {}, to = {}; for (var i in obj){ var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {}; for (var p in iProps){ var parsed = this.prepare(this.elements[i], p, iProps[p]); iFrom[p] = parsed.from; iTo[p] = parsed.to; } } return this.parent(from, to); } }); /* --- script: Fx.Accordion.js description: An Fx.Elements extension which allows you to easily create accordion type controls. license: MIT-style license authors: - Valerio Proietti requires: - core:1.2.4/Element.Event - /Fx.Elements provides: [Fx.Accordion] ... */ Fx.Accordion = new Class({ Extends: Fx.Elements, options: {/* onActive: $empty(toggler, section), onBackground: $empty(toggler, section), fixedHeight: false, fixedWidth: false, */ display: 0, show: false, height: true, width: false, opacity: true, alwaysHide: false, trigger: 'click', initialDisplayFx: true, returnHeightToAuto: true }, initialize: function(){ var params = Array.link(arguments, { 'container': Element.type, //deprecated 'options': Object.type, 'togglers': $defined, 'elements': $defined }); this.parent(params.elements, params.options); this.togglers = $$(params.togglers); this.previous = -1; this.internalChain = new Chain(); if (this.options.alwaysHide) this.options.wait = true; if ($chk(this.options.show)){ this.options.display = false; this.previous = this.options.show; } if (this.options.start){ this.options.display = false; this.options.show = false; } this.effects = {}; if (this.options.opacity) this.effects.opacity = 'fullOpacity'; if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth'; if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight'; for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]); this.elements.each(function(el, i){ if (this.options.show === i){ this.fireEvent('active', [this.togglers[i], el]); } else { for (var fx in this.effects) el.setStyle(fx, 0); } }, this); if ($chk(this.options.display) || this.options.initialDisplayFx === false) this.display(this.options.display, this.options.initialDisplayFx); if (this.options.fixedHeight !== false) this.options.returnHeightToAuto = false; this.addEvent('complete', this.internalChain.callChain.bind(this.internalChain)); }, addSection: function(toggler, element){ toggler = document.id(toggler); element = document.id(element); var test = this.togglers.contains(toggler); this.togglers.include(toggler); this.elements.include(element); var idx = this.togglers.indexOf(toggler); var displayer = this.display.bind(this, idx); toggler.store('accordion:display', displayer); toggler.addEvent(this.options.trigger, displayer); if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'}); if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'}); element.fullOpacity = 1; if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth; if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight; element.setStyle('overflow', 'hidden'); if (!test){ for (var fx in this.effects) element.setStyle(fx, 0); } return this; }, detach: function(){ this.togglers.each(function(toggler) { toggler.removeEvent(this.options.trigger, toggler.retrieve('accordion:display')); }, this); }, display: function(index, useFx){ if (!this.check(index, useFx)) return this; useFx = $pick(useFx, true); if (this.options.returnHeightToAuto){ var prev = this.elements[this.previous]; if (prev && !this.selfHidden){ for (var fx in this.effects){ prev.setStyle(fx, prev[this.effects[fx]]); } } } index = ($type(index) == 'element') ? this.elements.indexOf(index) : index; if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this; this.previous = index; var obj = {}; this.elements.each(function(el, i){ obj[i] = {}; var hide; if (i != index){ hide = true; } else if (this.options.alwaysHide && ((el.offsetHeight > 0 && this.options.height) || el.offsetWidth > 0 && this.options.width)){ hide = true; this.selfHidden = true; } this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]); for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]]; }, this); this.internalChain.chain(function(){ if (this.options.returnHeightToAuto && !this.selfHidden){ var el = this.elements[index]; if (el) el.setStyle('height', 'auto'); }; }.bind(this)); return useFx ? this.start(obj) : this.set(obj); } }); /* Compatibility with 1.2.0 */ var Accordion = new Class({ Extends: Fx.Accordion, initialize: function(){ this.parent.apply(this, arguments); var params = Array.link(arguments, {'container': Element.type}); this.container = params.container; }, addSection: function(toggler, element, pos){ toggler = document.id(toggler); element = document.id(element); var test = this.togglers.contains(toggler); var len = this.togglers.length; if (len && (!test || pos)){ pos = $pick(pos, len - 1); toggler.inject(this.togglers[pos], 'before'); element.inject(toggler, 'after'); } else if (this.container && !test){ toggler.inject(this.container); element.inject(this.container); } return this.parent.apply(this, arguments); } }); /* --- script: Fx.Move.js description: Defines Fx.Move, a class that works with Element.Position.js to transition an element from one location to another. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Fx.Morph - /Element.Position provides: [Fx.Move] ... */ Fx.Move = new Class({ Extends: Fx.Morph, options: { relativeTo: document.body, position: 'center', edge: false, offset: {x: 0, y: 0} }, start: function(destination){ return this.parent(this.element.position($merge(this.options, destination, {returnPos: true}))); } }); Element.Properties.move = { set: function(options){ var morph = this.retrieve('move'); if (morph) morph.cancel(); return this.eliminate('move').store('move:options', $extend({link: 'cancel'}, options)); }, get: function(options){ if (options || !this.retrieve('move')){ if (options || !this.retrieve('move:options')) this.set('move', options); this.store('move', new Fx.Move(this, this.retrieve('move:options'))); } return this.retrieve('move'); } }; Element.implement({ move: function(options){ this.get('move').start(options); return this; } }); /* --- script: Fx.Reveal.js description: Defines Fx.Reveal, a class that shows and hides elements with a transition. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Fx.Morph - /Element.Shortcuts - /Element.Measure provides: [Fx.Reveal] ... */ Fx.Reveal = new Class({ Extends: Fx.Morph, options: {/* onShow: $empty(thisElement), onHide: $empty(thisElement), onComplete: $empty(thisElement), heightOverride: null, widthOverride: null, */ link: 'cancel', styles: ['padding', 'border', 'margin'], transitionOpacity: !Browser.Engine.trident4, mode: 'vertical', display: 'block', hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed' : false }, dissolve: function(){ try { if (!this.hiding && !this.showing){ if (this.element.getStyle('display') != 'none'){ this.hiding = true; this.showing = false; this.hidden = true; this.cssText = this.element.style.cssText; var startStyles = this.element.getComputedSize({ styles: this.options.styles, mode: this.options.mode }); this.element.setStyle('display', this.options.display); if (this.options.transitionOpacity) startStyles.opacity = 1; var zero = {}; $each(startStyles, function(style, name){ zero[name] = [style, 0]; }, this); this.element.setStyle('overflow', 'hidden'); var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null; this.$chain.unshift(function(){ if (this.hidden){ this.hiding = false; $each(startStyles, function(style, name){ startStyles[name] = style; }, this); this.element.style.cssText = this.cssText; this.element.setStyle('display', 'none'); if (hideThese) hideThese.setStyle('visibility', 'visible'); } this.fireEvent('hide', this.element); this.callChain(); }.bind(this)); if (hideThese) hideThese.setStyle('visibility', 'hidden'); this.start(zero); } else { this.callChain.delay(10, this); this.fireEvent('complete', this.element); this.fireEvent('hide', this.element); } } else if (this.options.link == 'chain'){ this.chain(this.dissolve.bind(this)); } else if (this.options.link == 'cancel' && !this.hiding){ this.cancel(); this.dissolve(); } } catch(e){ this.hiding = false; this.element.setStyle('display', 'none'); this.callChain.delay(10, this); this.fireEvent('complete', this.element); this.fireEvent('hide', this.element); } return this; }, reveal: function(){ try { if (!this.showing && !this.hiding){ if (this.element.getStyle('display') == 'none' || this.element.getStyle('visiblity') == 'hidden' || this.element.getStyle('opacity') == 0){ this.showing = true; this.hiding = this.hidden = false; var startStyles; this.cssText = this.element.style.cssText; //toggle display, but hide it this.element.measure(function(){ //create the styles for the opened/visible state startStyles = this.element.getComputedSize({ styles: this.options.styles, mode: this.options.mode }); }.bind(this)); $each(startStyles, function(style, name){ startStyles[name] = style; }); //if we're overridding height/width if ($chk(this.options.heightOverride)) startStyles.height = this.options.heightOverride.toInt(); if ($chk(this.options.widthOverride)) startStyles.width = this.options.widthOverride.toInt(); if (this.options.transitionOpacity) { this.element.setStyle('opacity', 0); startStyles.opacity = 1; } //create the zero state for the beginning of the transition var zero = { height: 0, display: this.options.display }; $each(startStyles, function(style, name){ zero[name] = 0; }); //set to zero this.element.setStyles($merge(zero, {overflow: 'hidden'})); //hide inputs var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null; if (hideThese) hideThese.setStyle('visibility', 'hidden'); //start the effect this.start(startStyles); this.$chain.unshift(function(){ this.element.style.cssText = this.cssText; this.element.setStyle('display', this.options.display); if (!this.hidden) this.showing = false; if (hideThese) hideThese.setStyle('visibility', 'visible'); this.callChain(); this.fireEvent('show', this.element); }.bind(this)); } else { this.callChain(); this.fireEvent('complete', this.element); this.fireEvent('show', this.element); } } else if (this.options.link == 'chain'){ this.chain(this.reveal.bind(this)); } else if (this.options.link == 'cancel' && !this.showing){ this.cancel(); this.reveal(); } } catch(e){ this.element.setStyles({ display: this.options.display, visiblity: 'visible', opacity: 1 }); this.showing = false; this.callChain.delay(10, this); this.fireEvent('complete', this.element); this.fireEvent('show', this.element); } return this; }, toggle: function(){ if (this.element.getStyle('display') == 'none' || this.element.getStyle('visiblity') == 'hidden' || this.element.getStyle('opacity') == 0){ this.reveal(); } else { this.dissolve(); } return this; }, cancel: function(){ this.parent.apply(this, arguments); this.element.style.cssText = this.cssText; this.hidding = false; this.showing = false; } }); Element.Properties.reveal = { set: function(options){ var reveal = this.retrieve('reveal'); if (reveal) reveal.cancel(); return this.eliminate('reveal').store('reveal:options', options); }, get: function(options){ if (options || !this.retrieve('reveal')){ if (options || !this.retrieve('reveal:options')) this.set('reveal', options); this.store('reveal', new Fx.Reveal(this, this.retrieve('reveal:options'))); } return this.retrieve('reveal'); } }; Element.Properties.dissolve = Element.Properties.reveal; Element.implement({ reveal: function(options){ this.get('reveal', options).reveal(); return this; }, dissolve: function(options){ this.get('reveal', options).dissolve(); return this; }, nix: function(){ var params = Array.link(arguments, {destroy: Boolean.type, options: Object.type}); this.get('reveal', params.options).dissolve().chain(function(){ this[params.destroy ? 'destroy' : 'dispose'](); }.bind(this)); return this; }, wink: function(){ var params = Array.link(arguments, {duration: Number.type, options: Object.type}); var reveal = this.get('reveal', params.options); reveal.reveal().chain(function(){ (function(){ reveal.dissolve(); }).delay(params.duration || 2000); }); } }); /* --- script: Fx.Scroll.js description: Effect to smoothly scroll any element, including the window. license: MIT-style license authors: - Valerio Proietti requires: - core:1.2.4/Fx - core:1.2.4/Element.Event - core:1.2.4/Element.Dimensions - /MooTools.More provides: [Fx.Scroll] ... */ Fx.Scroll = new Class({ Extends: Fx, options: { offset: {x: 0, y: 0}, wheelStops: true }, initialize: function(element, options){ this.element = this.subject = document.id(element); this.parent(options); var cancel = this.cancel.bind(this, false); if ($type(this.element) != 'element') this.element = document.id(this.element.getDocument().body); var stopper = this.element; if (this.options.wheelStops){ this.addEvent('start', function(){ stopper.addEvent('mousewheel', cancel); }, true); this.addEvent('complete', function(){ stopper.removeEvent('mousewheel', cancel); }, true); } }, set: function(){ var now = Array.flatten(arguments); if (Browser.Engine.gecko) now = [Math.round(now[0]), Math.round(now[1])]; this.element.scrollTo(now[0], now[1]); }, compute: function(from, to, delta){ return [0, 1].map(function(i){ return Fx.compute(from[i], to[i], delta); }); }, start: function(x, y){ if (!this.check(x, y)) return this; var scrollSize = this.element.getScrollSize(), scroll = this.element.getScroll(), values = {x: x, y: y}; for (var z in values){ var max = scrollSize[z]; if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z] : max; else values[z] = scroll[z]; values[z] += this.options.offset[z]; } return this.parent([scroll.x, scroll.y], [values.x, values.y]); }, toTop: function(){ return this.start(false, 0); }, toLeft: function(){ return this.start(0, false); }, toRight: function(){ return this.start('right', false); }, toBottom: function(){ return this.start(false, 'bottom'); }, toElement: function(el){ var position = document.id(el).getPosition(this.element); return this.start(position.x, position.y); }, scrollIntoView: function(el, axes, offset){ axes = axes ? $splat(axes) : ['x','y']; var to = {}; el = document.id(el); var pos = el.getPosition(this.element); var size = el.getSize(); var scroll = this.element.getScroll(); var containerSize = this.element.getSize(); var edge = { x: pos.x + size.x, y: pos.y + size.y }; ['x','y'].each(function(axis) { if (axes.contains(axis)) { if (edge[axis] > scroll[axis] + containerSize[axis]) to[axis] = edge[axis] - containerSize[axis]; if (pos[axis] < scroll[axis]) to[axis] = pos[axis]; } if (to[axis] == null) to[axis] = scroll[axis]; if (offset && offset[axis]) to[axis] = to[axis] + offset[axis]; }, this); if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y); return this; }, scrollToCenter: function(el, axes, offset){ axes = axes ? $splat(axes) : ['x', 'y']; el = $(el); var to = {}, pos = el.getPosition(this.element), size = el.getSize(), scroll = this.element.getScroll(), containerSize = this.element.getSize(), edge = { x: pos.x + size.x, y: pos.y + size.y }; ['x','y'].each(function(axis){ if(axes.contains(axis)){ to[axis] = pos[axis] - (containerSize[axis] - size[axis])/2; } if(to[axis] == null) to[axis] = scroll[axis]; if(offset && offset[axis]) to[axis] = to[axis] + offset[axis]; }, this); if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y); return this; } }); /* --- script: Fx.Slide.js description: Effect to slide an element in and out of view. license: MIT-style license authors: - Valerio Proietti requires: - core:1.2.4/Fx Element.Style - /MooTools.More provides: [Fx.Slide] ... */ Fx.Slide = new Class({ Extends: Fx, options: { mode: 'vertical', wrapper: false, hideOverflow: true }, initialize: function(element, options){ this.addEvent('complete', function(){ this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0); if (this.open) this.wrapper.setStyle('height', ''); if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper); }, true); this.element = this.subject = document.id(element); this.parent(options); var wrapper = this.element.retrieve('wrapper'); var styles = this.element.getStyles('margin', 'position', 'overflow'); if (this.options.hideOverflow) styles = $extend(styles, {overflow: 'hidden'}); if (this.options.wrapper) wrapper = document.id(this.options.wrapper).setStyles(styles); this.wrapper = wrapper || new Element('div', { styles: styles }).wraps(this.element); this.element.store('wrapper', this.wrapper).setStyle('margin', 0); this.now = []; this.open = true; }, vertical: function(){ this.margin = 'margin-top'; this.layout = 'height'; this.offset = this.element.offsetHeight; }, horizontal: function(){ this.margin = 'margin-left'; this.layout = 'width'; this.offset = this.element.offsetWidth; }, set: function(now){ this.element.setStyle(this.margin, now[0]); this.wrapper.setStyle(this.layout, now[1]); return this; }, compute: function(from, to, delta){ return [0, 1].map(function(i){ return Fx.compute(from[i], to[i], delta); }); }, start: function(how, mode){ if (!this.check(how, mode)) return this; this[mode || this.options.mode](); var margin = this.element.getStyle(this.margin).toInt(); var layout = this.wrapper.getStyle(this.layout).toInt(); var caseIn = [[margin, layout], [0, this.offset]]; var caseOut = [[margin, layout], [-this.offset, 0]]; var start; switch (how){ case 'in': start = caseIn; break; case 'out': start = caseOut; break; case 'toggle': start = (layout == 0) ? caseIn : caseOut; } return this.parent(start[0], start[1]); }, slideIn: function(mode){ return this.start('in', mode); }, slideOut: function(mode){ return this.start('out', mode); }, hide: function(mode){ this[mode || this.options.mode](); this.open = false; return this.set([-this.offset, 0]); }, show: function(mode){ this[mode || this.options.mode](); this.open = true; return this.set([0, this.offset]); }, toggle: function(mode){ return this.start('toggle', mode); } }); Element.Properties.slide = { set: function(options){ var slide = this.retrieve('slide'); if (slide) slide.cancel(); return this.eliminate('slide').store('slide:options', $extend({link: 'cancel'}, options)); }, get: function(options){ if (options || !this.retrieve('slide')){ if (options || !this.retrieve('slide:options')) this.set('slide', options); this.store('slide', new Fx.Slide(this, this.retrieve('slide:options'))); } return this.retrieve('slide'); } }; Element.implement({ slide: function(how, mode){ how = how || 'toggle'; var slide = this.get('slide'), toggle; switch (how){ case 'hide': slide.hide(mode); break; case 'show': slide.show(mode); break; case 'toggle': var flag = this.retrieve('slide:flag', slide.open); slide[flag ? 'slideOut' : 'slideIn'](mode); this.store('slide:flag', !flag); toggle = true; break; default: slide.start(how, mode); } if (!toggle) this.eliminate('slide:flag'); return this; } }); /* --- script: Fx.SmoothScroll.js description: Class for creating a smooth scrolling effect to all internal links on the page. license: MIT-style license authors: - Valerio Proietti requires: - core:1.2.4/Selectors - /Fx.Scroll provides: [Fx.SmoothScroll] ... */ var SmoothScroll = Fx.SmoothScroll = new Class({ Extends: Fx.Scroll, initialize: function(options, context){ context = context || document; this.doc = context.getDocument(); var win = context.getWindow(); this.parent(this.doc, options); this.links = $$(this.options.links || this.doc.links); var location = win.location.href.match(/^[^#]*/)[0] + '#'; this.links.each(function(link){ if (link.href.indexOf(location) != 0) {return;} var anchor = link.href.substr(location.length); if (anchor) this.useLink(link, anchor); }, this); if (!Browser.Engine.webkit419) { this.addEvent('complete', function(){ win.location.hash = this.anchor; }, true); } }, useLink: function(link, anchor){ var el; link.addEvent('click', function(event){ if (el !== false && !el) el = document.id(anchor) || this.doc.getElement('a[name=' + anchor + ']'); if (el) { event.preventDefault(); this.anchor = anchor; this.toElement(el).chain(function(){ this.fireEvent('scrolledTo', [link, el]); }.bind(this)); link.blur(); } }.bind(this)); } }); /* --- script: Fx.Sort.js description: Defines Fx.Sort, a class that reorders lists with a transition. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Element.Dimensions - /Fx.Elements - /Element.Measure provides: [Fx.Sort] ... */ Fx.Sort = new Class({ Extends: Fx.Elements, options: { mode: 'vertical' }, initialize: function(elements, options){ this.parent(elements, options); this.elements.each(function(el){ if (el.getStyle('position') == 'static') el.setStyle('position', 'relative'); }); this.setDefaultOrder(); }, setDefaultOrder: function(){ this.currentOrder = this.elements.map(function(el, index){ return index; }); }, sort: function(newOrder){ if ($type(newOrder) != 'array') return false; var top = 0, left = 0, next = {}, zero = {}, vert = this.options.mode == 'vertical'; var current = this.elements.map(function(el, index){ var size = el.getComputedSize({styles: ['border', 'padding', 'margin']}); var val; if (vert){ val = { top: top, margin: size['margin-top'], height: size.totalHeight }; top += val.height - size['margin-top']; } else { val = { left: left, margin: size['margin-left'], width: size.totalWidth }; left += val.width; } var plain = vert ? 'top' : 'left'; zero[index] = {}; var start = el.getStyle(plain).toInt(); zero[index][plain] = start || 0; return val; }, this); this.set(zero); newOrder = newOrder.map(function(i){ return i.toInt(); }); if (newOrder.length != this.elements.length){ this.currentOrder.each(function(index){ if (!newOrder.contains(index)) newOrder.push(index); }); if (newOrder.length > this.elements.length) newOrder.splice(this.elements.length-1, newOrder.length - this.elements.length); } var margin = top = left = 0; newOrder.each(function(item, index){ var newPos = {}; if (vert){ newPos.top = top - current[item].top - margin; top += current[item].height; } else { newPos.left = left - current[item].left; left += current[item].width; } margin = margin + current[item].margin; next[item]=newPos; }, this); var mapped = {}; $A(newOrder).sort().each(function(index){ mapped[index] = next[index]; }); this.start(mapped); this.currentOrder = newOrder; return this; }, rearrangeDOM: function(newOrder){ newOrder = newOrder || this.currentOrder; var parent = this.elements[0].getParent(); var rearranged = []; this.elements.setStyle('opacity', 0); //move each element and store the new default order newOrder.each(function(index){ rearranged.push(this.elements[index].inject(parent).setStyles({ top: 0, left: 0 })); }, this); this.elements.setStyle('opacity', 1); this.elements = $$(rearranged); this.setDefaultOrder(); return this; }, getDefaultOrder: function(){ return this.elements.map(function(el, index){ return index; }); }, forward: function(){ return this.sort(this.getDefaultOrder()); }, backward: function(){ return this.sort(this.getDefaultOrder().reverse()); }, reverse: function(){ return this.sort(this.currentOrder.reverse()); }, sortByElements: function(elements){ return this.sort(elements.map(function(el){ return this.elements.indexOf(el); }, this)); }, swap: function(one, two){ if ($type(one) == 'element') one = this.elements.indexOf(one); if ($type(two) == 'element') two = this.elements.indexOf(two); var newOrder = $A(this.currentOrder); newOrder[this.currentOrder.indexOf(one)] = two; newOrder[this.currentOrder.indexOf(two)] = one; return this.sort(newOrder); } }); /* --- script: Drag.js description: The base Drag Class. Can be used to drag and resize Elements using mouse events. license: MIT-style license authors: - Valerio Proietti - Tom Occhinno - Jan Kassens requires: - core:1.2.4/Events - core:1.2.4/Options - core:1.2.4/Element.Event - core:1.2.4/Element.Style - /MooTools.More provides: [Drag] */ var Drag = new Class({ Implements: [Events, Options], options: {/* onBeforeStart: $empty(thisElement), onStart: $empty(thisElement, event), onSnap: $empty(thisElement) onDrag: $empty(thisElement, event), onCancel: $empty(thisElement), onComplete: $empty(thisElement, event),*/ snap: 6, unit: 'px', grid: false, style: true, limit: false, handle: false, invert: false, preventDefault: false, stopPropagation: false, modifiers: {x: 'left', y: 'top'} }, initialize: function(){ var params = Array.link(arguments, {'options': Object.type, 'element': $defined}); this.element = document.id(params.element); this.document = this.element.getDocument(); this.setOptions(params.options || {}); var htype = $type(this.options.handle); this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element; this.mouse = {'now': {}, 'pos': {}}; this.value = {'start': {}, 'now': {}}; this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown'; this.bound = { start: this.start.bind(this), check: this.check.bind(this), drag: this.drag.bind(this), stop: this.stop.bind(this), cancel: this.cancel.bind(this), eventStop: $lambda(false) }; this.attach(); }, attach: function(){ this.handles.addEvent('mousedown', this.bound.start); return this; }, detach: function(){ this.handles.removeEvent('mousedown', this.bound.start); return this; }, start: function(event){ if (event.rightClick) return; if (this.options.preventDefault) event.preventDefault(); if (this.options.stopPropagation) event.stopPropagation(); this.mouse.start = event.page; this.fireEvent('beforeStart', this.element); var limit = this.options.limit; this.limit = {x: [], y: []}; for (var z in this.options.modifiers){ if (!this.options.modifiers[z]) continue; if (this.options.style) this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt(); else this.value.now[z] = this.element[this.options.modifiers[z]]; if (this.options.invert) this.value.now[z] *= -1; this.mouse.pos[z] = event.page[z] - this.value.now[z]; if (limit && limit[z]){ for (var i = 2; i--; i){ if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])(); } } } if ($type(this.options.grid) == 'number') this.options.grid = {x: this.options.grid, y: this.options.grid}; this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel}); this.document.addEvent(this.selection, this.bound.eventStop); }, check: function(event){ if (this.options.preventDefault) event.preventDefault(); var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2))); if (distance > this.options.snap){ this.cancel(); this.document.addEvents({ mousemove: this.bound.drag, mouseup: this.bound.stop }); this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element); } }, drag: function(event){ if (this.options.preventDefault) event.preventDefault(); this.mouse.now = event.page; for (var z in this.options.modifiers){ if (!this.options.modifiers[z]) continue; this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z]; if (this.options.invert) this.value.now[z] *= -1; if (this.options.limit && this.limit[z]){ if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){ this.value.now[z] = this.limit[z][1]; } else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){ this.value.now[z] = this.limit[z][0]; } } if (this.options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % this.options.grid[z]); if (this.options.style) { this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit); } else { this.element[this.options.modifiers[z]] = this.value.now[z]; } } this.fireEvent('drag', [this.element, event]); }, cancel: function(event){ this.document.removeEvent('mousemove', this.bound.check); this.document.removeEvent('mouseup', this.bound.cancel); if (event){ this.document.removeEvent(this.selection, this.bound.eventStop); this.fireEvent('cancel', this.element); } }, stop: function(event){ this.document.removeEvent(this.selection, this.bound.eventStop); this.document.removeEvent('mousemove', this.bound.drag); this.document.removeEvent('mouseup', this.bound.stop); if (event) this.fireEvent('complete', [this.element, event]); } }); Element.implement({ makeResizable: function(options){ var drag = new Drag(this, $merge({modifiers: {x: 'width', y: 'height'}}, options)); this.store('resizer', drag); return drag.addEvent('drag', function(){ this.fireEvent('resize', drag); }.bind(this)); } }); /* --- script: Drag.Move.js description: A Drag extension that provides support for the constraining of draggables to containers and droppables. license: MIT-style license authors: - Valerio Proietti - Tom Occhinno - Jan Kassens - Aaron Newton - Scott Kyle requires: - core:1.2.4/Element.Dimensions - /Drag provides: [Drag.Move] ... */ Drag.Move = new Class({ Extends: Drag, options: {/* onEnter: $empty(thisElement, overed), onLeave: $empty(thisElement, overed), onDrop: $empty(thisElement, overed, event),*/ droppables: [], container: false, precalculate: false, includeMargins: true, checkDroppables: true }, initialize: function(element, options){ this.parent(element, options); element = this.element; this.droppables = $$(this.options.droppables); this.container = document.id(this.options.container); if (this.container && $type(this.container) != 'element') this.container = document.id(this.container.getDocument().body); var styles = element.getStyles('left', 'top', 'position'); if (styles.left == 'auto' || styles.top == 'auto') element.setPosition(element.getPosition(element.getOffsetParent())); if (styles.position == 'static') element.setStyle('position', 'absolute'); this.addEvent('start', this.checkDroppables, true); this.overed = null; }, start: function(event){ if (this.container) this.options.limit = this.calculateLimit(); if (this.options.precalculate){ this.positions = this.droppables.map(function(el){ return el.getCoordinates(); }); } this.parent(event); }, calculateLimit: function(){ var offsetParent = this.element.getOffsetParent(), containerCoordinates = this.container.getCoordinates(offsetParent), containerBorder = {}, elementMargin = {}, elementBorder = {}, containerMargin = {}, offsetParentPadding = {}; ['top', 'right', 'bottom', 'left'].each(function(pad){ containerBorder[pad] = this.container.getStyle('border-' + pad).toInt(); elementBorder[pad] = this.element.getStyle('border-' + pad).toInt(); elementMargin[pad] = this.element.getStyle('margin-' + pad).toInt(); containerMargin[pad] = this.container.getStyle('margin-' + pad).toInt(); offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt(); }, this); var width = this.element.offsetWidth + elementMargin.left + elementMargin.right, height = this.element.offsetHeight + elementMargin.top + elementMargin.bottom, left = 0, top = 0, right = containerCoordinates.right - containerBorder.right - width, bottom = containerCoordinates.bottom - containerBorder.bottom - height; if (this.options.includeMargins){ left += elementMargin.left; top += elementMargin.top; } else { right += elementMargin.right; bottom += elementMargin.bottom; } if (this.element.getStyle('position') == 'relative'){ var coords = this.element.getCoordinates(offsetParent); coords.left -= this.element.getStyle('left').toInt(); coords.top -= this.element.getStyle('top').toInt(); left += containerBorder.left - coords.left; top += containerBorder.top - coords.top; right += elementMargin.left - coords.left; bottom += elementMargin.top - coords.top; if (this.container != offsetParent){ left += containerMargin.left + offsetParentPadding.left; top += (Browser.Engine.trident4 ? 0 : containerMargin.top) + offsetParentPadding.top; } } else { left -= elementMargin.left; top -= elementMargin.top; if (this.container == offsetParent){ right -= containerBorder.left; bottom -= containerBorder.top; } else { left += containerCoordinates.left + containerBorder.left; top += containerCoordinates.top + containerBorder.top; } } return { x: [left, right], y: [top, bottom] }; }, checkAgainst: function(el, i){ el = (this.positions) ? this.positions[i] : el.getCoordinates(); var now = this.mouse.now; return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top); }, checkDroppables: function(){ var overed = this.droppables.filter(this.checkAgainst, this).getLast(); if (this.overed != overed){ if (this.overed) this.fireEvent('leave', [this.element, this.overed]); if (overed) this.fireEvent('enter', [this.element, overed]); this.overed = overed; } }, drag: function(event){ this.parent(event); if (this.options.checkDroppables && this.droppables.length) this.checkDroppables(); }, stop: function(event){ this.checkDroppables(); this.fireEvent('drop', [this.element, this.overed, event]); this.overed = null; return this.parent(event); } }); Element.implement({ makeDraggable: function(options){ var drag = new Drag.Move(this, options); this.store('dragger', drag); return drag; } }); /* --- script: Slider.js description: Class for creating horizontal and vertical slider controls. license: MIT-style license authors: - Valerio Proietti requires: - core:1.2.4/Element.Dimensions - /Class.Binds - /Drag - /Element.Dimensions - /Element.Measure provides: [Slider] ... */ var Slider = new Class({ Implements: [Events, Options], Binds: ['clickedElement', 'draggedKnob', 'scrolledElement'], options: {/* onTick: $empty(intPosition), onChange: $empty(intStep), onComplete: $empty(strStep),*/ onTick: function(position){ if (this.options.snap) position = this.toPosition(this.step); this.knob.setStyle(this.property, position); }, initialStep: 0, snap: false, offset: 0, range: false, wheel: false, steps: 100, mode: 'horizontal' }, initialize: function(element, knob, options){ this.setOptions(options); this.element = document.id(element); this.knob = document.id(knob); this.previousChange = this.previousEnd = this.step = -1; var offset, limit = {}, modifiers = {'x': false, 'y': false}; switch (this.options.mode){ case 'vertical': this.axis = 'y'; this.property = 'top'; offset = 'offsetHeight'; break; case 'horizontal': this.axis = 'x'; this.property = 'left'; offset = 'offsetWidth'; } this.full = this.element.measure(function(){ this.half = this.knob[offset] / 2; return this.element[offset] - this.knob[offset] + (this.options.offset * 2); }.bind(this)); this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0; this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps; this.range = this.max - this.min; this.steps = this.options.steps || this.full; this.stepSize = Math.abs(this.range) / this.steps; this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ; this.knob.setStyle('position', 'relative').setStyle(this.property, this.options.initialStep ? this.toPosition(this.options.initialStep) : - this.options.offset); modifiers[this.axis] = this.property; limit[this.axis] = [- this.options.offset, this.full - this.options.offset]; var dragOptions = { snap: 0, limit: limit, modifiers: modifiers, onDrag: this.draggedKnob, onStart: this.draggedKnob, onBeforeStart: (function(){ this.isDragging = true; }).bind(this), onCancel: function() { this.isDragging = false; }.bind(this), onComplete: function(){ this.isDragging = false; this.draggedKnob(); this.end(); }.bind(this) }; if (this.options.snap){ dragOptions.grid = Math.ceil(this.stepWidth); dragOptions.limit[this.axis][1] = this.full; } this.drag = new Drag(this.knob, dragOptions); this.attach(); }, attach: function(){ this.element.addEvent('mousedown', this.clickedElement); if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement); this.drag.attach(); return this; }, detach: function(){ this.element.removeEvent('mousedown', this.clickedElement); this.element.removeEvent('mousewheel', this.scrolledElement); this.drag.detach(); return this; }, set: function(step){ if (!((this.range > 0) ^ (step < this.min))) step = this.min; if (!((this.range > 0) ^ (step > this.max))) step = this.max; this.step = Math.round(step); this.checkStep(); this.fireEvent('tick', this.toPosition(this.step)); this.end(); return this; }, clickedElement: function(event){ if (this.isDragging || event.target == this.knob) return; var dir = this.range < 0 ? -1 : 1; var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half; position = position.limit(-this.options.offset, this.full -this.options.offset); this.step = Math.round(this.min + dir * this.toStep(position)); this.checkStep(); this.fireEvent('tick', position); this.end(); }, scrolledElement: function(event){ var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0); this.set(mode ? this.step - this.stepSize : this.step + this.stepSize); event.stop(); }, draggedKnob: function(){ var dir = this.range < 0 ? -1 : 1; var position = this.drag.value.now[this.axis]; position = position.limit(-this.options.offset, this.full -this.options.offset); this.step = Math.round(this.min + dir * this.toStep(position)); this.checkStep(); }, checkStep: function(){ if (this.previousChange != this.step){ this.previousChange = this.step; this.fireEvent('change', this.step); } }, end: function(){ if (this.previousEnd !== this.step){ this.previousEnd = this.step; this.fireEvent('complete', this.step + ''); } }, toStep: function(position){ var step = (position + this.options.offset) * this.stepSize / this.full * this.steps; return this.options.steps ? Math.round(step -= step % this.stepSize) : step; }, toPosition: function(step){ return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset; } }); /* --- script: Sortables.js description: Class for creating a drag and drop sorting interface for lists of items. license: MIT-style license authors: - Tom Occhino requires: - /Drag.Move provides: [Slider] ... */ var Sortables = new Class({ Implements: [Events, Options], options: {/* onSort: $empty(element, clone), onStart: $empty(element, clone), onComplete: $empty(element),*/ snap: 4, opacity: 1, clone: false, revert: false, handle: false, constrain: false }, initialize: function(lists, options){ this.setOptions(options); this.elements = []; this.lists = []; this.idle = true; this.addLists($$(document.id(lists) || lists)); if (!this.options.clone) this.options.revert = false; if (this.options.revert) this.effect = new Fx.Morph(null, $merge({duration: 250, link: 'cancel'}, this.options.revert)); }, attach: function(){ this.addLists(this.lists); return this; }, detach: function(){ this.lists = this.removeLists(this.lists); return this; }, addItems: function(){ Array.flatten(arguments).each(function(element){ this.elements.push(element); var start = element.retrieve('sortables:start', this.start.bindWithEvent(this, element)); (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start); }, this); return this; }, addLists: function(){ Array.flatten(arguments).each(function(list){ this.lists.push(list); this.addItems(list.getChildren()); }, this); return this; }, removeItems: function(){ return $$(Array.flatten(arguments).map(function(element){ this.elements.erase(element); var start = element.retrieve('sortables:start'); (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start); return element; }, this)); }, removeLists: function(){ return $$(Array.flatten(arguments).map(function(list){ this.lists.erase(list); this.removeItems(list.getChildren()); return list; }, this)); }, getClone: function(event, element){ if (!this.options.clone) return new Element('div').inject(document.body); if ($type(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list); var clone = element.clone(true).setStyles({ margin: '0px', position: 'absolute', visibility: 'hidden', 'width': element.getStyle('width') }); //prevent the duplicated radio inputs from unchecking the real one if (clone.get('html').test('radio')) { clone.getElements('input[type=radio]').each(function(input, i) { input.set('name', 'clone_' + i); }); } return clone.inject(this.list).setPosition(element.getPosition(element.getOffsetParent())); }, getDroppables: function(){ var droppables = this.list.getChildren(); if (!this.options.constrain) droppables = this.lists.concat(droppables).erase(this.list); return droppables.erase(this.clone).erase(this.element); }, insert: function(dragging, element){ var where = 'inside'; if (this.lists.contains(element)){ this.list = element; this.drag.droppables = this.getDroppables(); } else { where = this.element.getAllPrevious().contains(element) ? 'before' : 'after'; } this.element.inject(element, where); this.fireEvent('sort', [this.element, this.clone]); }, start: function(event, element){ if (!this.idle) return; this.idle = false; this.element = element; this.opacity = element.get('opacity'); this.list = element.getParent(); this.clone = this.getClone(event, element); this.drag = new Drag.Move(this.clone, { snap: this.options.snap, container: this.options.constrain && this.element.getParent(), droppables: this.getDroppables(), onSnap: function(){ event.stop(); this.clone.setStyle('visibility', 'visible'); this.element.set('opacity', this.options.opacity || 0); this.fireEvent('start', [this.element, this.clone]); }.bind(this), onEnter: this.insert.bind(this), onCancel: this.reset.bind(this), onComplete: this.end.bind(this) }); this.clone.inject(this.element, 'before'); this.drag.start(event); }, end: function(){ this.drag.detach(); this.element.set('opacity', this.opacity); if (this.effect){ var dim = this.element.getStyles('width', 'height'); var pos = this.clone.computePosition(this.element.getPosition(this.clone.offsetParent)); this.effect.element = this.clone; this.effect.start({ top: pos.top, left: pos.left, width: dim.width, height: dim.height, opacity: 0.25 }).chain(this.reset.bind(this)); } else { this.reset(); } }, reset: function(){ this.idle = true; this.clone.destroy(); this.fireEvent('complete', this.element); }, serialize: function(){ var params = Array.link(arguments, {modifier: Function.type, index: $defined}); var serial = this.lists.map(function(list){ return list.getChildren().map(params.modifier || function(element){ return element.get('id'); }, this); }, this); var index = params.index; if (this.lists.length == 1) index = 0; return $chk(index) && index >= 0 && index < this.lists.length ? serial[index] : serial; } }); /* --- script: Request.JSONP.js description: Defines Request.JSONP, a class for cross domain javascript via script injection. license: MIT-style license authors: - Aaron Newton - Guillermo Rauch requires: - core:1.2.4/Element - core:1.2.4/Request - /Log provides: [Request.JSONP] ... */ Request.JSONP = new Class({ Implements: [Chain, Events, Options, Log], options: {/* onRetry: $empty(intRetries), onRequest: $empty(scriptElement), onComplete: $empty(data), onSuccess: $empty(data), onCancel: $empty(), log: false, */ url: '', data: {}, retries: 0, timeout: 0, link: 'ignore', callbackKey: 'callback', injectScript: document.head }, initialize: function(options){ this.setOptions(options); if (this.options.log) this.enableLog(); this.running = false; this.requests = 0; this.triesRemaining = []; }, check: function(){ if (!this.running) return true; switch (this.options.link){ case 'cancel': this.cancel(); return true; case 'chain': this.chain(this.caller.bind(this, arguments)); return false; } return false; }, send: function(options){ if (!$chk(arguments[1]) && !this.check(options)) return this; var type = $type(options), old = this.options, index = $chk(arguments[1]) ? arguments[1] : this.requests++; if (type == 'string' || type == 'element') options = {data: options}; options = $extend({data: old.data, url: old.url}, options); if (!$chk(this.triesRemaining[index])) this.triesRemaining[index] = this.options.retries; var remaining = this.triesRemaining[index]; (function(){ var script = this.getScript(options); this.log('JSONP retrieving script with url: ' + script.get('src')); this.fireEvent('request', script); this.running = true; (function(){ if (remaining){ this.triesRemaining[index] = remaining - 1; if (script){ script.destroy(); this.send(options, index).fireEvent('retry', this.triesRemaining[index]); } } else if(script && this.options.timeout){ script.destroy(); this.cancel().fireEvent('failure'); } }).delay(this.options.timeout, this); }).delay(Browser.Engine.trident ? 50 : 0, this); return this; }, cancel: function(){ if (!this.running) return this; this.running = false; this.fireEvent('cancel'); return this; }, getScript: function(options){ var index = Request.JSONP.counter, data; Request.JSONP.counter++; switch ($type(options.data)){ case 'element': data = document.id(options.data).toQueryString(); break; case 'object': case 'hash': data = Hash.toQueryString(options.data); } var src = options.url + (options.url.test('\\?') ? '&' :'?') + (options.callbackKey || this.options.callbackKey) + '=Request.JSONP.request_map.request_'+ index + (data ? '&' + data : ''); if (src.length > 2083) this.log('JSONP '+ src +' will fail in Internet Explorer, which enforces a 2083 bytes length limit on URIs'); var script = new Element('script', {type: 'text/javascript', src: src}); Request.JSONP.request_map['request_' + index] = function(){ this.success(arguments, script); }.bind(this); return script.inject(this.options.injectScript); }, success: function(args, script){ if (script) script.destroy(); this.running = false; this.log('JSONP successfully retrieved: ', args); this.fireEvent('complete', args).fireEvent('success', args).callChain(); } }); Request.JSONP.counter = 0; Request.JSONP.request_map = {}; /* --- script: Request.Queue.js description: Controls several instances of Request and its variants to run only one request at a time. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Element - core:1.2.4/Request - /Log provides: [Request.Queue] ... */ Request.Queue = new Class({ Implements: [Options, Events], Binds: ['attach', 'request', 'complete', 'cancel', 'success', 'failure', 'exception'], options: {/* onRequest: $empty(argsPassedToOnRequest), onSuccess: $empty(argsPassedToOnSuccess), onComplete: $empty(argsPassedToOnComplete), onCancel: $empty(argsPassedToOnCancel), onException: $empty(argsPassedToOnException), onFailure: $empty(argsPassedToOnFailure), onEnd: $empty, */ stopOnFailure: true, autoAdvance: true, concurrent: 1, requests: {} }, initialize: function(options){ if(options){ var requests = options.requests; delete options.requests; } this.setOptions(options); this.requests = new Hash; this.queue = []; this.reqBinders = {}; if(requests) this.addRequests(requests); }, addRequest: function(name, request){ this.requests.set(name, request); this.attach(name, request); return this; }, addRequests: function(obj){ $each(obj, function(req, name){ this.addRequest(name, req); }, this); return this; }, getName: function(req){ return this.requests.keyOf(req); }, attach: function(name, req){ if (req._groupSend) return this; ['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){ if(!this.reqBinders[name]) this.reqBinders[name] = {}; this.reqBinders[name][evt] = function(){ this['on' + evt.capitalize()].apply(this, [name, req].extend(arguments)); }.bind(this); req.addEvent(evt, this.reqBinders[name][evt]); }, this); req._groupSend = req.send; req.send = function(options){ this.send(name, options); return req; }.bind(this); return this; }, removeRequest: function(req){ var name = $type(req) == 'object' ? this.getName(req) : req; if (!name && $type(name) != 'string') return this; req = this.requests.get(name); if (!req) return this; ['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){ req.removeEvent(evt, this.reqBinders[name][evt]); }, this); req.send = req._groupSend; delete req._groupSend; return this; }, getRunning: function(){ return this.requests.filter(function(r){ return r.running; }); }, isRunning: function(){ return !!(this.getRunning().getKeys().length); }, send: function(name, options){ var q = function(){ this.requests.get(name)._groupSend(options); this.queue.erase(q); }.bind(this); q.name = name; if (this.getRunning().getKeys().length >= this.options.concurrent || (this.error && this.options.stopOnFailure)) this.queue.push(q); else q(); return this; }, hasNext: function(name){ return (!name) ? !!this.queue.length : !!this.queue.filter(function(q){ return q.name == name; }).length; }, resume: function(){ this.error = false; (this.options.concurrent - this.getRunning().getKeys().length).times(this.runNext, this); return this; }, runNext: function(name){ if (!this.queue.length) return this; if (!name){ this.queue[0](); } else { var found; this.queue.each(function(q){ if (!found && q.name == name){ found = true; q(); } }); } return this; }, runAll: function() { this.queue.each(function(q) { q(); }); return this; }, clear: function(name){ if (!name){ this.queue.empty(); } else { this.queue = this.queue.map(function(q){ if (q.name != name) return q; else return false; }).filter(function(q){ return q; }); } return this; }, cancel: function(name){ this.requests.get(name).cancel(); return this; }, onRequest: function(){ this.fireEvent('request', arguments); }, onComplete: function(){ this.fireEvent('complete', arguments); if (!this.queue.length) this.fireEvent('end'); }, onCancel: function(){ if (this.options.autoAdvance && !this.error) this.runNext(); this.fireEvent('cancel', arguments); }, onSuccess: function(){ if (this.options.autoAdvance && !this.error) this.runNext(); this.fireEvent('success', arguments); }, onFailure: function(){ this.error = true; if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext(); this.fireEvent('failure', arguments); }, onException: function(){ this.error = true; if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext(); this.fireEvent('exception', arguments); } }); /* --- script: Request.Periodical.js description: Requests the same URL to pull data from a server but increases the intervals if no data is returned to reduce the load license: MIT-style license authors: - Christoph Pojer requires: - core:1.2.4/Request - /MooTools.More provides: [Request.Periodical] ... */ Request.implement({ options: { initialDelay: 5000, delay: 5000, limit: 60000 }, startTimer: function(data){ var fn = function(){ if (!this.running) this.send({data: data}); }; this.timer = fn.delay(this.options.initialDelay, this); this.lastDelay = this.options.initialDelay; this.completeCheck = function(response){ $clear(this.timer); this.lastDelay = (response) ? this.options.delay : (this.lastDelay + this.options.delay).min(this.options.limit); this.timer = fn.delay(this.lastDelay, this); }; return this.addEvent('complete', this.completeCheck); }, stopTimer: function(){ $clear(this.timer); return this.removeEvent('complete', this.completeCheck); } }); /* --- script: Assets.js description: Provides methods to dynamically load JavaScript, CSS, and Image files into the document. license: MIT-style license authors: - Valerio Proietti requires: - core:1.2.4/Element.Event - /MooTools.More provides: [Assets] ... */ var Asset = { javascript: function(source, properties){ properties = $extend({ onload: $empty, document: document, check: $lambda(true) }, properties); if (properties.onLoad) properties.onload = properties.onLoad; var script = new Element('script', {src: source, type: 'text/javascript'}); var load = properties.onload.bind(script), check = properties.check, doc = properties.document; delete properties.onload; delete properties.check; delete properties.document; script.addEvents({ load: load, readystatechange: function(){ if (['loaded', 'complete'].contains(this.readyState)) load(); } }).set(properties); if (Browser.Engine.webkit419) var checker = (function(){ if (!$try(check)) return; $clear(checker); load(); }).periodical(50); return script.inject(doc.head); }, css: function(source, properties){ return new Element('link', $merge({ rel: 'stylesheet', media: 'screen', type: 'text/css', href: source }, properties)).inject(document.head); }, image: function(source, properties){ properties = $merge({ onload: $empty, onabort: $empty, onerror: $empty }, properties); var image = new Image(); var element = document.id(image) || new Element('img'); ['load', 'abort', 'error'].each(function(name){ var type = 'on' + name; var cap = name.capitalize(); if (properties['on' + cap]) properties[type] = properties['on' + cap]; var event = properties[type]; delete properties[type]; image[type] = function(){ if (!image) return; if (!element.parentNode){ element.width = image.width; element.height = image.height; } image = image.onload = image.onabort = image.onerror = null; event.delay(1, element, element); element.fireEvent(name, element, 1); }; }); image.src = element.src = source; if (image && image.complete) image.onload.delay(1); return element.set(properties); }, images: function(sources, options){ options = $merge({ onComplete: $empty, onProgress: $empty, onError: $empty, properties: {} }, options); sources = $splat(sources); var images = []; var counter = 0; return new Elements(sources.map(function(source){ return Asset.image(source, $extend(options.properties, { onload: function(){ options.onProgress.call(this, counter, sources.indexOf(source)); counter++; if (counter == sources.length) options.onComplete(); }, onerror: function(){ options.onError.call(this, counter, sources.indexOf(source)); counter++; if (counter == sources.length) options.onComplete(); } })); })); } }; /* --- script: Color.js description: Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa. license: MIT-style license authors: - Valerio Proietti requires: - core:1.2.4/Array - core:1.2.4/String - core:1.2.4/Number - core:1.2.4/Hash - core:1.2.4/Function - core:1.2.4/$util provides: [Color] ... */ var Color = new Native({ initialize: function(color, type){ if (arguments.length >= 3){ type = 'rgb'; color = Array.slice(arguments, 0, 3); } else if (typeof color == 'string'){ if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true); else if (color.match(/hsb/)) color = color.hsbToRgb(); else color = color.hexToRgb(true); } type = type || 'rgb'; switch (type){ case 'hsb': var old = color; color = color.hsbToRgb(); color.hsb = old; break; case 'hex': color = color.hexToRgb(true); break; } color.rgb = color.slice(0, 3); color.hsb = color.hsb || color.rgbToHsb(); color.hex = color.rgbToHex(); return $extend(color, this); } }); Color.implement({ mix: function(){ var colors = Array.slice(arguments); var alpha = ($type(colors.getLast()) == 'number') ? colors.pop() : 50; var rgb = this.slice(); colors.each(function(color){ color = new Color(color); for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha)); }); return new Color(rgb, 'rgb'); }, invert: function(){ return new Color(this.map(function(value){ return 255 - value; })); }, setHue: function(value){ return new Color([value, this.hsb[1], this.hsb[2]], 'hsb'); }, setSaturation: function(percent){ return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb'); }, setBrightness: function(percent){ return new Color([this.hsb[0], this.hsb[1], percent], 'hsb'); } }); var $RGB = function(r, g, b){ return new Color([r, g, b], 'rgb'); }; var $HSB = function(h, s, b){ return new Color([h, s, b], 'hsb'); }; var $HEX = function(hex){ return new Color(hex, 'hex'); }; Array.implement({ rgbToHsb: function(){ var red = this[0], green = this[1], blue = this[2], hue = 0; var max = Math.max(red, green, blue), min = Math.min(red, green, blue); var delta = max - min; var brightness = max / 255, saturation = (max != 0) ? delta / max : 0; if(saturation != 0) { var rr = (max - red) / delta; var gr = (max - green) / delta; var br = (max - blue) / delta; if (red == max) hue = br - gr; else if (green == max) hue = 2 + rr - br; else hue = 4 + gr - rr; hue /= 6; if (hue < 0) hue++; } return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)]; }, hsbToRgb: function(){ var br = Math.round(this[2] / 100 * 255); if (this[1] == 0){ return [br, br, br]; } else { var hue = this[0] % 360; var f = hue % 60; var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255); var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255); var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255); switch (Math.floor(hue / 60)){ case 0: return [br, t, p]; case 1: return [q, br, p]; case 2: return [p, br, t]; case 3: return [p, q, br]; case 4: return [t, p, br]; case 5: return [br, p, q]; } } return false; } }); String.implement({ rgbToHsb: function(){ var rgb = this.match(/\d{1,3}/g); return (rgb) ? rgb.rgbToHsb() : null; }, hsbToRgb: function(){ var hsb = this.match(/\d{1,3}/g); return (hsb) ? hsb.hsbToRgb() : null; } }); /* --- script: Group.js description: Class for monitoring collections of events license: MIT-style license authors: - Valerio Proietti requires: - core:1.2.4/Events - /MooTools.More provides: [Group] ... */ var Group = new Class({ initialize: function(){ this.instances = Array.flatten(arguments); this.events = {}; this.checker = {}; }, addEvent: function(type, fn){ this.checker[type] = this.checker[type] || {}; this.events[type] = this.events[type] || []; if (this.events[type].contains(fn)) return false; else this.events[type].push(fn); this.instances.each(function(instance, i){ instance.addEvent(type, this.check.bind(this, [type, instance, i])); }, this); return this; }, check: function(type, instance, i){ this.checker[type][i] = true; var every = this.instances.every(function(current, j){ return this.checker[type][j] || false; }, this); if (!every) return; this.checker[type] = {}; this.events[type].each(function(event){ event.call(this, this.instances, instance); }, this); } }); /* --- script: Hash.Cookie.js description: Class for creating, reading, and deleting Cookies in JSON format. license: MIT-style license authors: - Valerio Proietti - Aaron Newton requires: - core:1.2.4/Cookie - core:1.2.4/JSON - /MooTools.More provides: [Hash.Cookie] ... */ Hash.Cookie = new Class({ Extends: Cookie, options: { autoSave: true }, initialize: function(name, options){ this.parent(name, options); this.load(); }, save: function(){ var value = JSON.encode(this.hash); if (!value || value.length > 4096) return false; //cookie would be truncated! if (value == '{}') this.dispose(); else this.write(value); return true; }, load: function(){ this.hash = new Hash(JSON.decode(this.read(), true)); return this; } }); Hash.each(Hash.prototype, function(method, name){ if (typeof method == 'function') Hash.Cookie.implement(name, function(){ var value = method.apply(this.hash, arguments); if (this.options.autoSave) this.save(); return value; }); }); /* --- script: IframeShim.js description: Defines IframeShim, a class for obscuring select lists and flash objects in IE. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Element.Event - core:1.2.4/Element.Style - core:1.2.4/Options Events - /Element.Position - /Class.Occlude provides: [IframeShim] ... */ var IframeShim = new Class({ Implements: [Options, Events, Class.Occlude], options: { className: 'iframeShim', src: 'javascript:false;document.write("");', display: false, zIndex: null, margin: 0, offset: {x: 0, y: 0}, browsers: (Browser.Engine.trident4 || (Browser.Engine.gecko && !Browser.Engine.gecko19 && Browser.Platform.mac)) }, property: 'IframeShim', initialize: function(element, options){ this.element = document.id(element); if (this.occlude()) return this.occluded; this.setOptions(options); this.makeShim(); return this; }, makeShim: function(){ if(this.options.browsers){ var zIndex = this.element.getStyle('zIndex').toInt(); if (!zIndex){ zIndex = 1; var pos = this.element.getStyle('position'); if (pos == 'static' || !pos) this.element.setStyle('position', 'relative'); this.element.setStyle('zIndex', zIndex); } zIndex = ($chk(this.options.zIndex) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1; if (zIndex < 0) zIndex = 1; this.shim = new Element('iframe', { src: this.options.src, scrolling: 'no', frameborder: 0, styles: { zIndex: zIndex, position: 'absolute', border: 'none', filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)' }, 'class': this.options.className }).store('IframeShim', this); var inject = (function(){ this.shim.inject(this.element, 'after'); this[this.options.display ? 'show' : 'hide'](); this.fireEvent('inject'); }).bind(this); if (!IframeShim.ready) window.addEvent('load', inject); else inject(); } else { this.position = this.hide = this.show = this.dispose = $lambda(this); } }, position: function(){ if (!IframeShim.ready || !this.shim) return this; var size = this.element.measure(function(){ return this.getSize(); }); if (this.options.margin != undefined){ size.x = size.x - (this.options.margin * 2); size.y = size.y - (this.options.margin * 2); this.options.offset.x += this.options.margin; this.options.offset.y += this.options.margin; } this.shim.set({width: size.x, height: size.y}).position({ relativeTo: this.element, offset: this.options.offset }); return this; }, hide: function(){ if (this.shim) this.shim.setStyle('display', 'none'); return this; }, show: function(){ if (this.shim) this.shim.setStyle('display', 'block'); return this.position(); }, dispose: function(){ if (this.shim) this.shim.dispose(); return this; }, destroy: function(){ if (this.shim) this.shim.destroy(); return this; } }); window.addEvent('load', function(){ IframeShim.ready = true; }); /* --- script: HtmlTable.js description: Builds table elements with methods to add rows. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Options - core:1.2.4/Events - /Class.Occlude provides: [HtmlTable] ... */ var HtmlTable = new Class({ Implements: [Options, Events, Class.Occlude], options: { properties: { cellpadding: 0, cellspacing: 0, border: 0 }, rows: [], headers: [], footers: [] }, property: 'HtmlTable', initialize: function(){ var params = Array.link(arguments, {options: Object.type, table: Element.type}); this.setOptions(params.options); this.element = params.table || new Element('table', this.options.properties); if (this.occlude()) return this.occluded; this.build(); }, build: function(){ this.element.store('HtmlTable', this); this.body = document.id(this.element.tBodies[0]) || new Element('tbody').inject(this.element); $$(this.body.rows); if (this.options.headers.length) this.setHeaders(this.options.headers); else this.thead = document.id(this.element.tHead); if (this.thead) this.head = document.id(this.thead.rows[0]); if (this.options.footers.length) this.setFooters(this.options.footers); this.tfoot = document.id(this.element.tFoot); if (this.tfoot) this.foot = document.id(this.thead.rows[0]); this.options.rows.each(function(row){ this.push(row); }, this); ['adopt', 'inject', 'wraps', 'grab', 'replaces', 'dispose'].each(function(method){ this[method] = this.element[method].bind(this.element); }, this); }, toElement: function(){ return this.element; }, empty: function(){ this.body.empty(); return this; }, set: function(what, items) { var target = (what == 'headers') ? 'tHead' : 'tFoot'; this[target.toLowerCase()] = (document.id(this.element[target]) || new Element(target.toLowerCase()).inject(this.element, 'top')).empty(); var data = this.push(items, {}, this[target.toLowerCase()], what == 'headers' ? 'th' : 'td'); if (what == 'headers') this.head = document.id(this.thead.rows[0]); else this.foot = document.id(this.thead.rows[0]); return data; }, setHeaders: function(headers){ this.set('headers', headers); return this; }, setFooters: function(footers){ this.set('footers', footers); return this; }, push: function(row, rowProperties, target, tag){ var tds = row.map(function(data){ var td = new Element(tag || 'td', data.properties), type = data.content || data || '', element = document.id(type); if($type(type) != 'string' && element) td.adopt(element); else td.set('html', type); return td; }); return { tr: new Element('tr', rowProperties).inject(target || this.body).adopt(tds), tds: tds }; } }); /* --- script: HtmlTable.Zebra.js description: Builds a stripy table with methods to add rows. license: MIT-style license authors: - Harald Kirschner - Aaron Newton requires: - /HtmlTable - /Class.refactor provides: [HtmlTable.Zebra] ... */ HtmlTable = Class.refactor(HtmlTable, { options: { classZebra: 'table-tr-odd', zebra: true }, initialize: function(){ this.previous.apply(this, arguments); if (this.occluded) return this.occluded; if (this.options.zebra) this.updateZebras(); }, updateZebras: function(){ Array.each(this.body.rows, this.zebra, this); }, zebra: function(row, i){ return row[((i % 2) ? 'remove' : 'add')+'Class'](this.options.classZebra); }, push: function(){ var pushed = this.previous.apply(this, arguments); if (this.options.zebra) this.updateZebras(); return pushed; } }); /* --- script: HtmlTable.Sort.js description: Builds a stripy, sortable table with methods to add rows. license: MIT-style license authors: - Harald Kirschner - Aaron Newton requires: - core:1.2.4/Hash - /HtmlTable - /Class.refactor - /Element.Delegation - /Date provides: [HtmlTable.Sort] ... */ HtmlTable = Class.refactor(HtmlTable, { options: {/* onSort: $empty, */ sortIndex: 0, sortReverse: false, parsers: [], defaultParser: 'string', classSortable: 'table-sortable', classHeadSort: 'table-th-sort', classHeadSortRev: 'table-th-sort-rev', classNoSort: 'table-th-nosort', classGroupHead: 'table-tr-group-head', classGroup: 'table-tr-group', classCellSort: 'table-td-sort', classSortSpan: 'table-th-sort-span', sortable: false }, initialize: function () { this.previous.apply(this, arguments); if (this.occluded) return this.occluded; this.sorted = {index: null, dir: 1}; this.bound = { headClick: this.headClick.bind(this) }; this.sortSpans = new Elements(); if (this.options.sortable) { this.enableSort(); if (this.options.sortIndex != null) this.sort(this.options.sortIndex, this.options.sortReverse); } }, attachSorts: function(attach){ this.element.removeEvents('click:relay(th)'); this.element[$pick(attach, true) ? 'addEvent' : 'removeEvent']('click:relay(th)', this.bound.headClick); }, setHeaders: function(){ this.previous.apply(this, arguments); if (this.sortEnabled) this.detectParsers(); }, detectParsers: function(force){ if (!this.head) return; var parsers = this.options.parsers, rows = this.body.rows; // auto-detect this.parsers = $$(this.head.cells).map(function(cell, index) { if (!force && (cell.hasClass(this.options.classNoSort) || cell.retrieve('htmltable-parser'))) return cell.retrieve('htmltable-parser'); var thDiv = new Element('div'); $each(cell.childNodes, function(node) { thDiv.adopt(node); }); thDiv.inject(cell); var sortSpan = new Element('span', {'html': ' ', 'class': this.options.classSortSpan}).inject(thDiv, 'top'); this.sortSpans.push(sortSpan); var parser = parsers[index], cancel; switch ($type(parser)) { case 'function': parser = {convert: parser}; cancel = true; break; case 'string': parser = parser; cancel = true; break; } if (!cancel) { HtmlTable.Parsers.some(function(current) { var match = current.match; if (!match) return false; for (var i = 0, j = rows.length; i < j; i++) { var text = $(rows[i].cells[index]).get('html').clean(); if (text && match.test(text)) { parser = current; return true; } } }); } if (!parser) parser = this.options.defaultParser; cell.store('htmltable-parser', parser); return parser; }, this); }, headClick: function(event, el) { if (!this.head || el.hasClass(this.options.classNoSort)) return; var index = Array.indexOf(this.head.cells, el); this.sort(index); return false; }, sort: function(index, reverse, pre) { if (!this.head) return; pre = !!(pre); var classCellSort = this.options.classCellSort; var classGroup = this.options.classGroup, classGroupHead = this.options.classGroupHead; if (!pre) { if (index != null) { if (this.sorted.index == index) { this.sorted.reverse = !(this.sorted.reverse); } else { if (this.sorted.index != null) { this.sorted.reverse = false; this.head.cells[this.sorted.index].removeClass(this.options.classHeadSort).removeClass(this.options.classHeadSortRev); } else { this.sorted.reverse = true; } this.sorted.index = index; } } else { index = this.sorted.index; } if (reverse != null) this.sorted.reverse = reverse; var head = document.id(this.head.cells[index]); if (head) { head.addClass(this.options.classHeadSort); if (this.sorted.reverse) head.addClass(this.options.classHeadSortRev); else head.removeClass(this.options.classHeadSortRev); } this.body.getElements('td').removeClass(this.options.classCellSort); } var parser = this.parsers[index]; if ($type(parser) == 'string') parser = HtmlTable.Parsers.get(parser); if (!parser) return; if (!Browser.Engine.trident) { var rel = this.body.getParent(); this.body.dispose(); } var data = Array.map(this.body.rows, function(row, i) { var value = parser.convert.call(document.id(row.cells[index])); return { position: i, value: value, toString: function() { return value.toString(); } }; }, this); data.reverse(true); data.sort(function(a, b){ if (a.value === b.value) return 0; return a.value > b.value ? 1 : -1; }); if (!this.sorted.reverse) data.reverse(true); var i = data.length, body = this.body; var j, position, entry, group; while (i) { var item = data[--i]; position = item.position; var row = body.rows[position]; if (row.disabled) continue; if (!pre) { if (group === item.value) { row.removeClass(classGroupHead).addClass(classGroup); } else { group = item.value; row.removeClass(classGroup).addClass(classGroupHead); } if (this.zebra) this.zebra(row, i); row.cells[index].addClass(classCellSort); } body.appendChild(row); for (j = 0; j < i; j++) { if (data[j].position > position) data[j].position--; } }; data = null; if (rel) rel.grab(body); return this.fireEvent('sort', [body, index]); }, reSort: function(){ if (this.sortEnabled) this.sort.call(this, this.sorted.index, this.sorted.reverse); return this; }, enableSort: function(){ this.element.addClass(this.options.classSortable); this.attachSorts(true); this.detectParsers(); this.sortEnabled = true; return this; }, disableSort: function(){ this.element.removeClass(this.options.classSortable); this.attachSorts(false); this.sortSpans.each(function(span) { span.destroy(); }); this.sortSpans.empty(); this.sortEnabled = false; return this; } }); HtmlTable.Parsers = new Hash({ 'date': { match: /^\d{2}[-\/ ]\d{2}[-\/ ]\d{2,4}$/, convert: function() { return Date.parse(this.get('text')).format('db'); }, type: 'date' }, 'input-checked': { match: / type="(radio|checkbox)" /, convert: function() { return this.getElement('input').checked; } }, 'input-value': { match: /= this.body.rows.length) to = this.body.rows.length - 1; if (this.hover == this.body.rows[to]) return this; this.enterRow(this.body.rows[to]); }, leaveRow: function(row){ row.removeClass(this.options.classRowHovered); }, focusRow: function(){ var row = arguments[1] || arguments[0]; //delegation passes the event first if (!this.body.getChildren().contains(row)) return; var unfocus = function(row){ this.selectedRows.erase(row); row.removeClass(this.options.classRowSelected); this.fireEvent('rowUnfocus', [row, this.selectedRows]); }.bind(this); if (!this.options.allowMultiSelect) this.selectedRows.each(unfocus); if (!this.selectedRows.contains(row)) { this.selectedRows.push(row); row.addClass(this.options.classRowSelected); this.fireEvent('rowFocus', [row, this.selectedRows]); } else { unfocus(row); } return false; }, selectAll: function(status){ status = $pick(status, true); if (!this.options.allowMultiSelect && status) return; if (!status) this.selectedRows.removeClass(this.options.classRowSelected).empty(); else this.selectedRows.combine(this.body.rows).addClass(this.options.classRowSelected); return this; }, selectNone: function(){ return this.selectAll(false); } }); /* --- script: Keyboard.js description: KeyboardEvents used to intercept events on a class for keyboard and format modifiers in a specific order so as to make alt+shift+c the same as shift+alt+c. license: MIT-style license authors: - Perrin Westrich - Aaron Newton - Scott Kyle requires: - core:1.2.4/Events - core:1.2.4/Options - core:1.2.4/Element.Event - /Log provides: [Keyboard] ... */ (function(){ var Keyboard = this.Keyboard = new Class({ Extends: Events, Implements: [Options, Log], options: { /* onActivate: $empty, onDeactivate: $empty, */ defaultEventType: 'keydown', active: false, events: {}, nonParsedEvents: ['activate', 'deactivate', 'onactivate', 'ondeactivate', 'changed', 'onchanged'] }, initialize: function(options){ this.setOptions(options); this.setup(); }, setup: function(){ this.addEvents(this.options.events); //if this is the root manager, nothing manages it if (Keyboard.manager && !this.manager) Keyboard.manager.manage(this); if (this.options.active) this.activate(); }, handle: function(event, type){ //Keyboard.stop(event) prevents key propagation if (event.preventKeyboardPropagation) return; var bubbles = !!this.manager; if (bubbles && this.activeKB){ this.activeKB.handle(event, type); if (event.preventKeyboardPropagation) return; } this.fireEvent(type, event); if (!bubbles && this.activeKB) this.activeKB.handle(event, type); }, addEvent: function(type, fn, internal){ return this.parent(Keyboard.parse(type, this.options.defaultEventType, this.options.nonParsedEvents), fn, internal); }, removeEvent: function(type, fn){ return this.parent(Keyboard.parse(type, this.options.defaultEventType, this.options.nonParsedEvents), fn); }, toggleActive: function(){ return this[this.active ? 'deactivate' : 'activate'](); }, activate: function(instance){ if (instance) { //if we're stealing focus, store the last keyboard to have it so the relenquish command works if (instance != this.activeKB) this.previous = this.activeKB; //if we're enabling a child, assign it so that events are now passed to it this.activeKB = instance.fireEvent('activate'); Keyboard.manager.fireEvent('changed'); } else if (this.manager) { //else we're enabling ourselves, we must ask our parent to do it for us this.manager.activate(this); } return this; }, deactivate: function(instance){ if (instance) { if(instance === this.activeKB) { this.activeKB = null; instance.fireEvent('deactivate'); Keyboard.manager.fireEvent('changed'); } } else if (this.manager) { this.manager.deactivate(this); } return this; }, relenquish: function(){ if (this.previous) this.activate(this.previous); }, //management logic manage: function(instance){ if (instance.manager) instance.manager.drop(instance); this.instances.push(instance); instance.manager = this; if (!this.activeKB) this.activate(instance); else this._disable(instance); }, _disable: function(instance){ if (this.activeKB == instance) this.activeKB = null; }, drop: function(instance){ this._disable(instance); this.instances.erase(instance); }, instances: [], trace: function(){ Keyboard.trace(this); }, each: function(fn){ Keyboard.each(this, fn); } }); var parsed = {}; var modifiers = ['shift', 'control', 'alt', 'meta']; var regex = /^(?:shift|control|ctrl|alt|meta)$/; Keyboard.parse = function(type, eventType, ignore){ if (ignore && ignore.contains(type.toLowerCase())) return type; type = type.toLowerCase().replace(/^(keyup|keydown):/, function($0, $1){ eventType = $1; return ''; }); if (!parsed[type]){ var key, mods = {}; type.split('+').each(function(part){ if (regex.test(part)) mods[part] = true; else key = part; }); mods.control = mods.control || mods.ctrl; // allow both control and ctrl var keys = []; modifiers.each(function(mod){ if (mods[mod]) keys.push(mod); }); if (key) keys.push(key); parsed[type] = keys.join('+'); } return eventType + ':' + parsed[type]; }; Keyboard.each = function(keyboard, fn){ var current = keyboard || Keyboard.manager; while (current){ fn.run(current); current = current.activeKB; } }; Keyboard.stop = function(event){ event.preventKeyboardPropagation = true; }; Keyboard.manager = new Keyboard({ active: true }); Keyboard.trace = function(keyboard){ keyboard = keyboard || Keyboard.manager; keyboard.enableLog(); keyboard.log('the following items have focus: '); Keyboard.each(keyboard, function(current){ keyboard.log(document.id(current.widget) || current.wiget || current); }); }; var handler = function(event){ var keys = []; modifiers.each(function(mod){ if (event[mod]) keys.push(mod); }); if (!regex.test(event.key)) keys.push(event.key); Keyboard.manager.handle(event, event.type + ':' + keys.join('+')); }; document.addEvents({ 'keyup': handler, 'keydown': handler }); Event.Keys.extend({ 'shift': 16, 'control': 17, 'alt': 18, 'capslock': 20, 'pageup': 33, 'pagedown': 34, 'end': 35, 'home': 36, 'numlock': 144, 'scrolllock': 145, ';': 186, '=': 187, ',': 188, '-': Browser.Engine.Gecko ? 109 : 189, '.': 190, '/': 191, '`': 192, '[': 219, '\\': 220, ']': 221, "'": 222 }); })(); /* --- script: Keyboard.js description: Enhances Keyboard by adding the ability to name and describe keyboard shortcuts, and the ability to grab shortcuts by name and bind the shortcut to different keys. license: MIT-style license authors: - Perrin Westrich requires: - core:1.2.4/Function - /Keyboard.Extras provides: [Keyboard.Extras] ... */ Keyboard.prototype.options.nonParsedEvents.combine(['rebound', 'onrebound']); Keyboard.implement({ /* shortcut should be in the format of: { 'keys': 'shift+s', // the default to add as an event. 'description': 'blah blah blah', // a brief description of the functionality. 'handler': function(){} // the event handler to run when keys are pressed. } */ addShortcut: function(name, shortcut) { this.shortcuts = this.shortcuts || []; this.shortcutIndex = this.shortcutIndex || {}; shortcut.getKeyboard = $lambda(this); shortcut.name = name; this.shortcutIndex[name] = shortcut; this.shortcuts.push(shortcut); if(shortcut.keys) this.addEvent(shortcut.keys, shortcut.handler); return this; }, addShortcuts: function(obj){ for(var name in obj) this.addShortcut(name, obj[name]); return this; }, getShortcuts: function(){ return this.shortcuts || []; }, getShortcut: function(name){ return (this.shortcutIndex || {})[name]; } }); Keyboard.rebind = function(newKeys, shortcuts){ $splat(shortcuts).each(function(shortcut){ shortcut.getKeyboard().removeEvent(shortcut.keys, shortcut.handler); shortcut.getKeyboard().addEvent(newKeys, shortcut.handler); shortcut.keys = newKeys; shortcut.getKeyboard().fireEvent('rebound'); }); }; Keyboard.getActiveShortcuts = function(keyboard) { var activeKBS = [], activeSCS = []; Keyboard.each(keyboard, [].push.bind(activeKBS)); activeKBS.each(function(kb){ activeSCS.extend(kb.getShortcuts()); }); return activeSCS; }; Keyboard.getShortcut = function(name, keyboard, opts){ opts = opts || {}; var shortcuts = opts.many ? [] : null, set = opts.many ? function(kb){ var shortcut = kb.getShortcut(name); if(shortcut) shortcuts.push(shortcut); } : function(kb) { if(!shortcuts) shortcuts = kb.getShortcut(name); }; Keyboard.each(keyboard, set); return shortcuts; }; Keyboard.getShortcuts = function(name, keyboard) { return Keyboard.getShortcut(name, keyboard, { many: true }); }; /* --- script: Mask.js description: Creates a mask element to cover another. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Options - core:1.2.4/Events - core:1.2.4/Element.Event - /Class.Binds - /Element.Position - /IframeShim provides: [Mask] ... */ var Mask = new Class({ Implements: [Options, Events], Binds: ['position'], options: { // onShow: $empty, // onHide: $empty, // onDestroy: $empty, // onClick: $empty, //inject: { // where: 'after', // target: null, //}, // hideOnClick: false, // id: null, // destroyOnHide: false, style: {}, 'class': 'mask', maskMargins: false, useIframeShim: true, iframeShimOptions: {} }, initialize: function(target, options){ this.target = document.id(target) || document.id(document.body); this.target.store('Mask', this); this.setOptions(options); this.render(); this.inject(); }, render: function() { this.element = new Element('div', { 'class': this.options['class'], id: this.options.id || 'mask-' + $time(), styles: $merge(this.options.style, { display: 'none' }), events: { click: function(){ this.fireEvent('click'); if (this.options.hideOnClick) this.hide(); }.bind(this) } }); this.hidden = true; }, toElement: function(){ return this.element; }, inject: function(target, where){ where = where || this.options.inject ? this.options.inject.where : '' || this.target == document.body ? 'inside' : 'after'; target = target || this.options.inject ? this.options.inject.target : '' || this.target; this.element.inject(target, where); if (this.options.useIframeShim) { this.shim = new IframeShim(this.element, this.options.iframeShimOptions); this.addEvents({ show: this.shim.show.bind(this.shim), hide: this.shim.hide.bind(this.shim), destroy: this.shim.destroy.bind(this.shim) }); } }, position: function(){ this.resize(this.options.width, this.options.height); this.element.position({ relativeTo: this.target, position: 'topLeft', ignoreMargins: !this.options.maskMargins, ignoreScroll: this.target == document.body }); return this; }, resize: function(x, y){ var opt = { styles: ['padding', 'border'] }; if (this.options.maskMargins) opt.styles.push('margin'); var dim = this.target.getComputedSize(opt); if (this.target == document.body) { var win = window.getSize(); if (dim.totalHeight < win.y) dim.totalHeight = win.y; if (dim.totalWidth < win.x) dim.totalWidth = win.x; } this.element.setStyles({ width: $pick(x, dim.totalWidth, dim.x), height: $pick(y, dim.totalHeight, dim.y) }); return this; }, show: function(){ if (!this.hidden) return this; window.addEvent('resize', this.position); this.position(); this.showMask.apply(this, arguments); return this; }, showMask: function(){ this.element.setStyle('display', 'block'); this.hidden = false; this.fireEvent('show'); }, hide: function(){ if (this.hidden) return this; window.removeEvent('resize', this.position); this.hideMask.apply(this, arguments); if (this.options.destroyOnHide) return this.destroy(); return this; }, hideMask: function(){ this.element.setStyle('display', 'none'); this.hidden = true; this.fireEvent('hide'); }, toggle: function(){ this[this.hidden ? 'show' : 'hide'](); }, destroy: function(){ this.hide(); this.element.destroy(); this.fireEvent('destroy'); this.target.eliminate('mask'); } }); Element.Properties.mask = { set: function(options){ var mask = this.retrieve('mask'); return this.eliminate('mask').store('mask:options', options); }, get: function(options){ if (options || !this.retrieve('mask')){ if (this.retrieve('mask')) this.retrieve('mask').destroy(); if (options || !this.retrieve('mask:options')) this.set('mask', options); this.store('mask', new Mask(this, this.retrieve('mask:options'))); } return this.retrieve('mask'); } }; Element.implement({ mask: function(options){ this.get('mask', options).show(); return this; }, unmask: function(){ this.get('mask').hide(); return this; } }); /* --- script: Scroller.js description: Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries. license: MIT-style license authors: - Valerio Proietti requires: - core:1.2.4/Events - core:1.2.4/Options - core:1.2.4/Element.Event - core:1.2.4/Element.Dimensions provides: [Scroller] ... */ var Scroller = new Class({ Implements: [Events, Options], options: { area: 20, velocity: 1, onChange: function(x, y){ this.element.scrollTo(x, y); }, fps: 50 }, initialize: function(element, options){ this.setOptions(options); this.element = document.id(element); this.docBody = document.id(this.element.getDocument().body); this.listener = ($type(this.element) != 'element') ? this.docBody : this.element; this.timer = null; this.bound = { attach: this.attach.bind(this), detach: this.detach.bind(this), getCoords: this.getCoords.bind(this) }; }, start: function(){ this.listener.addEvents({ mouseover: this.bound.attach, mouseout: this.bound.detach }); }, stop: function(){ this.listener.removeEvents({ mouseover: this.bound.attach, mouseout: this.bound.detach }); this.detach(); this.timer = $clear(this.timer); }, attach: function(){ this.listener.addEvent('mousemove', this.bound.getCoords); }, detach: function(){ this.listener.removeEvent('mousemove', this.bound.getCoords); this.timer = $clear(this.timer); }, getCoords: function(event){ this.page = (this.listener.get('tag') == 'body') ? event.client : event.page; if (!this.timer) this.timer = this.scroll.periodical(Math.round(1000 / this.options.fps), this); }, scroll: function(){ var size = this.element.getSize(), scroll = this.element.getScroll(), pos = this.element != this.docBody ? this.element.getOffsets() : {x: 0, y:0}, scrollSize = this.element.getScrollSize(), change = {x: 0, y: 0}; for (var z in this.page){ if (this.page[z] < (this.options.area + pos[z]) && scroll[z] != 0) { change[z] = (this.page[z] - this.options.area - pos[z]) * this.options.velocity; } else if (this.page[z] + this.options.area > (size[z] + pos[z]) && scroll[z] + size[z] != scrollSize[z]) { change[z] = (this.page[z] - size[z] + this.options.area - pos[z]) * this.options.velocity; } } if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x, scroll.y + change.y]); } }); /* --- script: Tips.js description: Class for creating nice tips that follow the mouse cursor when hovering an element. license: MIT-style license authors: - Valerio Proietti - Christoph Pojer requires: - core:1.2.4/Options - core:1.2.4/Events - core:1.2.4/Element.Event - core:1.2.4/Element.Style - core:1.2.4/Element.Dimensions - /MooTools.More provides: [Tips] ... */ (function(){ var read = function(option, element){ return (option) ? ($type(option) == 'function' ? option(element) : element.get(option)) : ''; }; this.Tips = new Class({ Implements: [Events, Options], options: { /* onAttach: $empty(element), onDetach: $empty(element), */ onShow: function(){ this.tip.setStyle('display', 'block'); }, onHide: function(){ this.tip.setStyle('display', 'none'); }, title: 'title', text: function(element){ return element.get('rel') || element.get('href'); }, showDelay: 100, hideDelay: 100, className: 'tip-wrap', offset: {x: 16, y: 16}, windowPadding: {x:0, y:0}, fixed: false }, initialize: function(){ var params = Array.link(arguments, {options: Object.type, elements: $defined}); this.setOptions(params.options); if (params.elements) this.attach(params.elements); this.container = new Element('div', {'class': 'tip'}); }, toElement: function(){ if (this.tip) return this.tip; return this.tip = new Element('div', { 'class': this.options.className, styles: { position: 'absolute', top: 0, left: 0 } }).adopt( new Element('div', {'class': 'tip-top'}), this.container, new Element('div', {'class': 'tip-bottom'}) ).inject(document.body); }, attach: function(elements){ $$(elements).each(function(element){ var title = read(this.options.title, element), text = read(this.options.text, element); element.erase('title').store('tip:native', title).retrieve('tip:title', title); element.retrieve('tip:text', text); this.fireEvent('attach', [element]); var events = ['enter', 'leave']; if (!this.options.fixed) events.push('move'); events.each(function(value){ var event = element.retrieve('tip:' + value); if (!event) event = this['element' + value.capitalize()].bindWithEvent(this, element); element.store('tip:' + value, event).addEvent('mouse' + value, event); }, this); }, this); return this; }, detach: function(elements){ $$(elements).each(function(element){ ['enter', 'leave', 'move'].each(function(value){ element.removeEvent('mouse' + value, element.retrieve('tip:' + value)).eliminate('tip:' + value); }); this.fireEvent('detach', [element]); if (this.options.title == 'title'){ // This is necessary to check if we can revert the title var original = element.retrieve('tip:native'); if (original) element.set('title', original); } }, this); return this; }, elementEnter: function(event, element){ this.container.empty(); ['title', 'text'].each(function(value){ var content = element.retrieve('tip:' + value); if (content) this.fill(new Element('div', {'class': 'tip-' + value}).inject(this.container), content); }, this); $clear(this.timer); this.timer = (function(){ this.show(this, element); this.position((this.options.fixed) ? {page: element.getPosition()} : event); }).delay(this.options.showDelay, this); }, elementLeave: function(event, element){ $clear(this.timer); this.timer = this.hide.delay(this.options.hideDelay, this, element); this.fireForParent(event, element); }, fireForParent: function(event, element){ element = element.getParent(); if (!element || element == document.body) return; if (element.retrieve('tip:enter')) element.fireEvent('mouseenter', event); else this.fireForParent(event, element); }, elementMove: function(event, element){ this.position(event); }, position: function(event){ if (!this.tip) document.id(this); var size = window.getSize(), scroll = window.getScroll(), tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight}, props = {x: 'left', y: 'top'}, obj = {}; for (var z in props){ obj[props[z]] = event.page[z] + this.options.offset[z]; if ((obj[props[z]] + tip[z] - scroll[z]) > size[z] - this.options.windowPadding[z]) obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z]; } this.tip.setStyles(obj); }, fill: function(element, contents){ if(typeof contents == 'string') element.set('html', contents); else element.adopt(contents); }, show: function(element){ if (!this.tip) document.id(this); this.fireEvent('show', [this.tip, element]); }, hide: function(element){ if (!this.tip) document.id(this); this.fireEvent('hide', [this.tip, element]); } }); })(); /* --- script: Spinner.js description: Adds a semi-transparent overlay over a dom element with a spinnin ajax icon. license: MIT-style license authors: - Aaron Newton requires: - core:1.2.4/Fx.Tween - /Class.refactor - /Mask provides: [Spinner] ... */ var Spinner = new Class({ Extends: Mask, options: { /*message: false,*/ 'class':'spinner', containerPosition: {}, content: { 'class':'spinner-content' }, messageContainer: { 'class':'spinner-msg' }, img: { 'class':'spinner-img' }, fxOptions: { link: 'chain' } }, initialize: function(){ this.parent.apply(this, arguments); this.target.store('spinner', this); //add this to events for when noFx is true; parent methods handle hide/show var deactivate = function(){ this.active = false; }.bind(this); this.addEvents({ hide: deactivate, show: deactivate }); }, render: function(){ this.parent(); this.element.set('id', this.options.id || 'spinner-'+$time()); this.content = document.id(this.options.content) || new Element('div', this.options.content); this.content.inject(this.element); if (this.options.message) { this.msg = document.id(this.options.message) || new Element('p', this.options.messageContainer).appendText(this.options.message); this.msg.inject(this.content); } if (this.options.img) { this.img = document.id(this.options.img) || new Element('div', this.options.img); this.img.inject(this.content); } this.element.set('tween', this.options.fxOptions); }, show: function(noFx){ if (this.active) return this.chain(this.show.bind(this)); if (!this.hidden) { this.callChain.delay(20, this); return this; } this.active = true; return this.parent(noFx); }, showMask: function(noFx){ var pos = function(){ this.content.position($merge({ relativeTo: this.element }, this.options.containerPosition)); }.bind(this); if (noFx) { this.parent(); pos(); } else { this.element.setStyles({ display: 'block', opacity: 0 }).tween('opacity', this.options.style.opacity || 0.9); pos(); this.hidden = false; this.fireEvent('show'); this.callChain(); } }, hide: function(noFx){ if (this.active) return this.chain(this.hide.bind(this)); if (this.hidden) { this.callChain.delay(20, this); return this; } this.active = true; return this.parent(noFx); }, hideMask: function(noFx){ if (noFx) return this.parent(); this.element.tween('opacity', 0).get('tween').chain(function(){ this.element.setStyle('display', 'none'); this.hidden = true; this.fireEvent('hide'); this.callChain(); }.bind(this)); }, destroy: function(){ this.content.destroy(); this.parent(); this.target.eliminate('spinner'); } }); Spinner.implement(new Chain); if (window.Request) { Request = Class.refactor(Request, { options: { useSpinner: false, spinnerOptions: {}, spinnerTarget: false }, initialize: function(options){ this._send = this.send; this.send = function(options){ if (this.spinner) this.spinner.chain(this._send.bind(this, options)).show(); else this._send(options); return this; }; this.previous(options); var update = document.id(this.options.spinnerTarget) || document.id(this.options.update); if (this.options.useSpinner && update) { this.spinner = update.get('spinner', this.options.spinnerOptions); ['onComplete', 'onException', 'onCancel'].each(function(event){ this.addEvent(event, this.spinner.hide.bind(this.spinner)); }, this); } }, getSpinner: function(){ return this.spinner; } }); } Element.Properties.spinner = { set: function(options){ var spinner = this.retrieve('spinner'); return this.eliminate('spinner').store('spinner:options', options); }, get: function(options){ if (options || !this.retrieve('spinner')){ if (this.retrieve('spinner')) this.retrieve('spinner').destroy(); if (options || !this.retrieve('spinner:options')) this.set('spinner', options); new Spinner(this, this.retrieve('spinner:options')); } return this.retrieve('spinner'); } }; Element.implement({ spin: function(options){ this.get('spinner', options).show(); return this; }, unspin: function(){ var opt = Array.link(arguments, {options: Object.type, callback: Function.type}); this.get('spinner', opt.options).hide(opt.callback); return this; } }); /* --- script: Date.English.US.js description: Date messages for US English. license: MIT-style license authors: - Aaron Newton requires: - /Lang - /Date provides: [Date.English.US] ... */ MooTools.lang.set('en-US', 'Date', { months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], //culture's date order: MM/DD/YYYY dateOrder: ['month', 'date', 'year'], shortDate: '%m/%d/%Y', shortTime: '%I:%M%p', AM: 'AM', PM: 'PM', /* Date.Extras */ ordinal: function(dayOfMonth){ //1st, 2nd, 3rd, etc. return (dayOfMonth > 3 && dayOfMonth < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][Math.min(dayOfMonth % 10, 4)]; }, lessThanMinuteAgo: 'less than a minute ago', minuteAgo: 'about a minute ago', minutesAgo: '{delta} minutes ago', hourAgo: 'about an hour ago', hoursAgo: 'about {delta} hours ago', dayAgo: '1 day ago', daysAgo: '{delta} days ago', weekAgo: '1 week ago', weeksAgo: '{delta} weeks ago', monthAgo: '1 month ago', monthsAgo: '{delta} months ago', yearAgo: '1 year ago', yearsAgo: '{delta} years ago', lessThanMinuteUntil: 'less than a minute from now', minuteUntil: 'about a minute from now', minutesUntil: '{delta} minutes from now', hourUntil: 'about an hour from now', hoursUntil: 'about {delta} hours from now', dayUntil: '1 day from now', daysUntil: '{delta} days from now', weekUntil: '1 week from now', weeksUntil: '{delta} weeks from now', monthUntil: '1 month from now', monthsUntil: '{delta} months from now', yearUntil: '1 year from now', yearsUntil: '{delta} years from now' }); /* --- script: Form.Validator.English.js description: Form Validator messages for English. license: MIT-style license authors: - Aaron Newton requires: - /Lang - /Form.Validator provides: [Form.Validator.English] ... */ MooTools.lang.set('en-US', 'Form.Validator', { required:'This field is required.', minLength:'Please enter at least {minLength} characters (you entered {length} characters).', maxLength:'Please enter no more than {maxLength} characters (you entered {length} characters).', integer:'Please enter an integer in this field. Numbers with decimals (e.g. 1.25) are not permitted.', numeric:'Please enter only numeric values in this field (i.e. "1" or "1.1" or "-1" or "-1.1").', digits:'Please use numbers and punctuation only in this field (for example, a phone number with dashes or dots is permitted).', alpha:'Please use letters only (a-z) with in this field. No spaces or other characters are allowed.', alphanum:'Please use only letters (a-z) or numbers (0-9) only in this field. No spaces or other characters are allowed.', dateSuchAs:'Please enter a valid date such as {date}', dateInFormatMDY:'Please enter a valid date such as MM/DD/YYYY (i.e. "12/31/1999")', email:'Please enter a valid email address. For example "fred@domain.com".', url:'Please enter a valid URL such as http://www.google.com.', currencyDollar:'Please enter a valid $ amount. For example $100.00 .', oneRequired:'Please enter something for at least one of these inputs.', errorPrefix: 'Error: ', warningPrefix: 'Warning: ', //Form.Validator.Extras noSpace: 'There can be no spaces in this input.', reqChkByNode: 'No items are selected.', requiredChk: 'This field is required.', reqChkByName: 'Please select a {label}.', match: 'This field needs to match the {matchName} field', startDate: 'the start date', endDate: 'the end date', currendDate: 'the current date', afterDate: 'The date should be the same or after {label}.', beforeDate: 'The date should be the same or before {label}.', startMonth: 'Please select a start month', sameMonth: 'These two dates must be in the same month - you must change one or the other.', creditcard: 'The credit card number entered is invalid. Please check the number and try again. {length} digits entered.' }); /* --- script: Date.German.js description: Date messages for German. license: MIT-style license authors: - Christoph Pojer - Frank Rossi - Ulrich Petri - Fabian Beiner requires: - /Lang - /Date provides: [Date.German] ... */ MooTools.lang.set('de-DE', 'Date', { months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'], //culture's date order: MM/DD/YYYY dateOrder: [ 'date', 'month', 'year', '.'], AM: 'vormittags', PM: 'nachmittags', shortDate: '%d.%m.%Y', shortTime: '%H:%M', /* Date.Extras */ ordinal: '.', lessThanMinuteAgo: 'Vor weniger als einer Minute', minuteAgo: 'Vor einer Minute', minutesAgo: 'Vor {delta} Minuten', hourAgo: 'Vor einer Stunde', hoursAgo: 'Vor {delta} Stunden', dayAgo: 'Vor einem Tag', daysAgo: 'Vor {delta} Tagen', weekAgo: 'Vor einer Woche', weeksAgo: 'Vor {delta} Wochen', monthAgo: 'Vor einem Monat', monthsAgo: 'Vor {delta} Monaten', yearAgo: 'Vor einem Jahr', yearsAgo: 'Vor {delta} Jahren', lessThanMinuteUntil: 'In weniger als einer Minute', minuteUntil: 'In einer Minute', minutesUntil: 'In {delta} Minuten', hourUntil: 'In ca. einer Stunde', hoursUntil: 'In ca. {delta} Stunden', dayUntil: 'In einem Tag', daysUntil: 'In {delta} Tagen', weekUntil: 'In einer Woche', weeksUntil: 'In {delta} Wochen', monthUntil: 'In einem Monat', monthsUntil: 'In {delta} Monaten', yearUntil: 'In einem Jahr', yearsUntil: 'In {delta} Jahren' }); /* --- script: Date.German.CH.js description: Date messages for German (Switzerland). license: MIT-style license authors: - Michael van der Weg requires: - /Lang - /Date.German provides: [Date.German.CH] ... */ MooTools.lang.set('de-CH', 'cascade', ['de-DE']); /* --- script: Form.Validator.German.js description: Date messages for German. license: MIT-style license authors: - Frank Rossi - Ulrich Petri - Fabian Beiner requires: - /Lang - /Form.Validator provides: [Form.Validator.German] ... */ MooTools.lang.set('de-DE', 'Form.Validator', { required: 'Dieses Eingabefeld muss ausgefüllt werden.', minLength: 'Geben Sie bitte mindestens {minLength} Zeichen ein (Sie haben nur {length} Zeichen eingegeben).', maxLength: 'Geben Sie bitte nicht mehr als {maxLength} Zeichen ein (Sie haben {length} Zeichen eingegeben).', integer: 'Geben Sie in diesem Eingabefeld bitte eine ganze Zahl ein. Dezimalzahlen (z.B. "1.25") sind nicht erlaubt.', numeric: 'Geben Sie in diesem Eingabefeld bitte nur Zahlenwerte (z.B. "1", "1.1", "-1" oder "-1.1") ein.', digits: 'Geben Sie in diesem Eingabefeld bitte nur Zahlen und Satzzeichen ein (z.B. eine Telefonnummer mit Bindestrichen und Punkten ist erlaubt).', alpha: 'Geben Sie in diesem Eingabefeld bitte nur Buchstaben (a-z) ein. Leerzeichen und andere Zeichen sind nicht erlaubt.', alphanum: 'Geben Sie in diesem Eingabefeld bitte nur Buchstaben (a-z) und Zahlen (0-9) ein. Leerzeichen oder andere Zeichen sind nicht erlaubt.', dateSuchAs: 'Geben Sie bitte ein gültiges Datum ein (z.B. "{date}").', dateInFormatMDY: 'Geben Sie bitte ein gültiges Datum im Format TT.MM.JJJJ ein (z.B. "31.12.1999").', email: 'Geben Sie bitte eine gültige E-Mail-Adresse ein (z.B. "max@mustermann.de").', url: 'Geben Sie bitte eine gültige URL ein (z.B. "http://www.google.de").', currencyDollar: 'Geben Sie bitte einen gültigen Betrag in EURO ein (z.B. 100.00€).', oneRequired: 'Bitte füllen Sie mindestens ein Eingabefeld aus.', errorPrefix: 'Fehler: ', warningPrefix: 'Warnung: ', //Form.Validator.Extras noSpace: 'Es darf kein Leerzeichen in diesem Eingabefeld sein.', reqChkByNode: 'Es wurden keine Elemente gewählt.', requiredChk: 'Dieses Feld muss ausgefüllt werden.', reqChkByName: 'Bitte wählen Sie ein {label}.', match: 'Dieses Eingabefeld muss mit dem {matchName} Eingabefeld übereinstimmen.', startDate: 'Das Anfangsdatum', endDate: 'Das Enddatum', currendDate: 'Das aktuelle Datum', afterDate: 'Das Datum sollte zur gleichen Zeit oder später sein als {label}.', beforeDate: 'Das Datum sollte zur gleichen Zeit oder früher sein als {label}.', startMonth: 'Wählen Sie bitte einen Anfangsmonat', sameMonth: 'Diese zwei Datumsangaben müssen im selben Monat sein - Sie müssen eines von beiden verändern.', creditcard: 'Die eingegebene Kreditkartennummer ist ungültig. Bitte überprüfen Sie diese und versuchen Sie es erneut. {length} Zahlen eingegeben.' }); /* --- script: Form.Validator.German.CH.js description: Date messages for German (Switzerland). license: MIT-style license authors: - Michael van der Weg requires: - /Lang - /Form.Validator.German provides: [Form.Validator.German.CH] ... */ MooTools.lang.set('de-CH', 'Form.Validator', { required:'Dieses Feld ist obligatorisch.', minLength:'Geben Sie bitte mindestens {minLength} Zeichen ein (Sie haben {length} Zeichen eingegeben).', maxLength:'Bitte geben Sie nicht mehr als {maxLength} Zeichen ein (Sie haben {length} Zeichen eingegeben).', integer:'Geben Sie bitte eine ganze Zahl ein. Dezimalzahlen (z.B. 1.25) sind nicht erlaubt.', numeric:'Geben Sie bitte nur Zahlenwerte in dieses Eingabefeld ein (z.B. "1", "1.1", "-1" oder "-1.1").', digits:'Benutzen Sie bitte nur Zahlen und Satzzeichen in diesem Eingabefeld (erlaubt ist z.B. eine Telefonnummer mit Bindestrichen und Punkten).', alpha:'Benutzen Sie bitte nur Buchstaben (a-z) in diesem Feld. Leerzeichen und andere Zeichen sind nicht erlaubt.', alphanum:'Benutzen Sie bitte nur Buchstaben (a-z) und Zahlen (0-9) in diesem Eingabefeld. Leerzeichen und andere Zeichen sind nicht erlaubt.', dateSuchAs:'Geben Sie bitte ein gültiges Datum ein. Wie zum Beispiel {date}', dateInFormatMDY:'Geben Sie bitte ein gültiges Datum ein. Wie zum Beispiel TT.MM.JJJJ (z.B. "31.12.1999")', email:'Geben Sie bitte eine gültige E-Mail Adresse ein. Wie zum Beispiel "maria@bernasconi.ch".', url:'Geben Sie bitte eine gültige URL ein. Wie zum Beispiel http://www.google.ch.', currencyDollar:'Geben Sie bitte einen gültigen Betrag in Schweizer Franken ein. Wie zum Beispiel 100.00 CHF .', oneRequired:'Machen Sie für mindestens eines der Eingabefelder einen Eintrag.', errorPrefix: 'Fehler: ', warningPrefix: 'Warnung: ', //Form.Validator.Extras noSpace: 'In diesem Eingabefeld darf kein Leerzeichen sein.', reqChkByNode: 'Es wurden keine Elemente gewählt.', requiredChk: 'Dieses Feld ist obligatorisch.', reqChkByName: 'Bitte wählen Sie ein {label}.', match: 'Dieses Eingabefeld muss mit dem Feld {matchName} übereinstimmen.', startDate: 'Das Anfangsdatum', endDate: 'Das Enddatum', currendDate: 'Das aktuelle Datum', afterDate: 'Das Datum sollte zur gleichen Zeit oder später sein {label}.', beforeDate: 'Das Datum sollte zur gleichen Zeit oder früher sein {label}.', startMonth: 'Wählen Sie bitte einen Anfangsmonat', sameMonth: 'Diese zwei Datumsangaben müssen im selben Monat sein - Sie müssen eine von beiden verändern.' });// Calendar: a Javascript class for Mootools that adds accessible and unobtrusive date pickers to your form elements // Calendar RC4, Copyright (c) 2007 Aeron Glemann , MIT Style License. // Mootools 1.2 compatibility by Davorin Šego var Calendar = new Class({ Implements: Options, options: { blocked: [], // blocked dates classes: [], // ['calendar', 'prev', 'next', 'month', 'year', 'today', 'invalid', 'valid', 'inactive', 'active', 'hover', 'hilite'] days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'], // days of the week starting at sunday direction: 0, // -1 past, 0 past + future, 1 future draggable: true, months: ['Januar', 'Februar', 'Maerz', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], navigation: 1, // 0 = no nav; 1 = single nav for month; 2 = dual nav for month and year offset: 0, // first day of the week: 0 = sunday, 1 = monday, etc.. onHideStart: Class.empty, onHideComplete: Class.empty, onShowStart: Class.empty, onShowComplete: Class.empty, pad: 1, // padding between multiple calendars tweak: {x: 0, y: 0} // tweak calendar positioning }, // initialize: calendar constructor // @param obj (obj) a js object containing the form elements and format strings { id: 'format', id: 'format' etc } // @param props (obj) optional properties initialize: function(obj, options) { // basic error checking if (!obj) { return false; } this.setOptions(options); // create our classes array var keys = ['calendar', 'prev', 'next', 'month', 'year', 'today', 'invalid', 'valid', 'inactive', 'active', 'hover', 'hilite']; var values = keys.map(function(key, i) { if (this.options.classes[i]) { if (this.options.classes[i].length) { key = this.options.classes[i]; } } return key; }, this); this.classes = values.associate(keys); // create cal element with css styles required for proper cal functioning this.calendar = new Element('div', { 'styles': { left: '-1000px', opacity: 0, position: 'absolute', top: '-1000px', zIndex: 1000 } }).addClass(this.classes.calendar).injectInside(document.body); // iex 6 needs a transparent iframe underneath the calendar in order to not allow select elements to render through if (window.ie6) { this.iframe = new Element('iframe', { 'styles': { left: '-1000px', position: 'absolute', top: '-1000px', zIndex: 999 } }).injectInside(document.body); this.iframe.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'; } // initialize fade method this.fx = new Fx.Tween(this.calendar, { onStart: function() { if (this.calendar.getStyle('opacity') == 0) { // show if (window.ie6) { this.iframe.setStyle('display', 'block'); } this.calendar.setStyle('display', 'block'); this.fireEvent('onShowStart', this.element); } else { // hide this.fireEvent('onHideStart', this.element); } }.bind(this), onComplete: function() { if (this.calendar.getStyle('opacity') == 0) { // hidden this.calendar.setStyle('display', 'none'); if (window.ie6) { this.iframe.setStyle('display', 'none'); } this.fireEvent('onHideComplete', this.element); } else { // shown this.fireEvent('onShowComplete', this.element); } }.bind(this) }); // initialize drag method if (window.Drag && this.options.draggable) { this.drag = new Drag.Move(this.calendar, { onDrag: function() { if (window.ie6) { this.iframe.setStyles({ left: this.calendar.style.left, top: this.calendar.style.top }); } }.bind(this) }); } // create calendars array this.calendars = []; var id = 0; var d = new Date(); // today d.setDate(d.getDate() + this.options.direction.toInt()); // correct today for directional offset for (var i in obj) { var cal = { button: new Element('button', { 'type': 'button' }), el: $(i), els: [], id: id++, month: d.getMonth(), visible: false, year: d.getFullYear() }; // fix for bad element (naughty, naughty element!) if (!this.element(i, obj[i], cal)) { continue; } cal.el.addClass(this.classes.calendar); // create cal button cal.button.addClass(this.classes.calendar).addEvent('click', function(cal) { this.toggle(cal); }.pass(cal, this)).injectAfter(cal.el); // read in default value cal.val = this.read(cal); $extend(cal, this.bounds(cal)); // abs bounds of calendar $extend(cal, this.values(cal)); // valid days, months, years this.rebuild(cal); this.calendars.push(cal); // add to cals array } }, // blocked: returns an array of blocked days for the month / year // @param cal (obj) // @returns blocked days (array) blocked: function(cal) { var blocked = []; var offset = new Date(cal.year, cal.month, 1).getDay(); // day of the week (offset) var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of this month this.options.blocked.each(function(date){ var values = date.split(' '); // preparation for (var i = 0; i <= 3; i++){ if (!values[i]){ values[i] = (i == 3) ? '' : '*'; } // make sure blocked date contains values for at least d, m and y values[i] = values[i].contains(',') ? values[i].split(',') : new Array(values[i]); // split multiple values var count = values[i].length - 1; for (var j = count; j >= 0; j--){ if (values[i][j].contains('-')){ // a range var val = values[i][j].split('-'); for (var k = val[0]; k <= val[1]; k++){ if (!values[i].contains(k)){ values[i].push(k + ''); } } values[i].splice(j, 1); } } } // execution if (values[2].contains(cal.year + '') || values[2].contains('*')){ if (values[1].contains(cal.month + 1 + '') || values[1].contains('*')){ values[0].each(function(val){ // if blocked value indicates this month / year if (val > 0){ blocked.push(val.toInt()); } // add date to blocked array }); if (values[3]){ // optional value for day of week for (var i = 0; i < last; i++){ var day = (i + offset) % 7; if (values[3].contains(day + '')){ blocked.push(i + 1); // add every date that corresponds to the blocked day of the week to the blocked array } } } } } }, this); return blocked; }, // bounds: returns the start / end bounds of the calendar // @param cal (obj) // @returns obj bounds: function(cal) { // 1. first we assume the calendar has no bounds (or a thousand years in either direction) // by default the calendar will accept a millennium in either direction var start = new Date(1000, 0, 1); // jan 1, 1000 var end = new Date(2999, 11, 31); // dec 31, 2999 // 2. but if the cal is one directional we adjust accordingly var date = new Date().getDate() + this.options.direction.toInt(); if (this.options.direction > 0) { start = new Date(); start.setDate(date + this.options.pad * cal.id); } if (this.options.direction < 0) { end = new Date(); end.setDate(date - this.options.pad * (this.calendars.length - cal.id - 1)); } // 3. then we can further filter the limits by using the pre-existing values in the selects cal.els.each(function(el) { if (el.get('tag') == 'select') { if (el.format.test('(y|Y)')) { // search for a year select var years = []; el.getChildren().each(function(option) { // get options var values = this.unformat(option.value, el.format); if (!years.contains(values[0])) { years.push(values[0]); } // add to years array }, this); years.sort(this.sort); if (years[0] > start.getFullYear()) { d = new Date(years[0], start.getMonth() + 1, 0); // last day of new month if (start.getDate() > d.getDate()) { start.setDate(d.getDate()); } start.setYear(years[0]); } if (years.getLast() < end.getFullYear()) { d = new Date(years.getLast(), end.getMonth() + 1, 0); // last day of new month if (end.getDate() > d.getDate()) { end.setDate(d.getDate()); } end.setYear(years.getLast()); } } if (el.format.test('(F|m|M|n)')) { // search for a month select var months_start = []; var months_end = []; el.getChildren().each(function(option) { // get options var values = this.unformat(option.value, el.format); if ($type(values[0]) != 'number' || values[0] == years[0]) { // if it's a year / month combo for curr year, or simply a month select if (!months_start.contains(values[1])) { months_start.push(values[1]); } // add to months array } if ($type(values[0]) != 'number' || values[0] == years.getLast()) { // if it's a year / month combo for curr year, or simply a month select if (!months_end.contains(values[1])) { months_end.push(values[1]); } // add to months array } }, this); months_start.sort(this.sort); months_end.sort(this.sort); if (months_start[0] > start.getMonth()) { d = new Date(start.getFullYear(), months_start[0] + 1, 0); // last day of new month if (start.getDate() > d.getDate()) { start.setDate(d.getDate()); } start.setMonth(months_start[0]); } if (months_end.getLast() < end.getMonth()) { d = new Date(start.getFullYear(), months_end.getLast() + 1, 0); // last day of new month if (end.getDate() > d.getDate()) { end.setDate(d.getDate()); } end.setMonth(months_end.getLast()); } } } }, this); return { 'start': start, 'end': end }; }, // caption: returns the caption element with header and navigation // @param cal (obj) // @returns caption (element) caption: function(cal) { // start by assuming navigation is allowed var navigation = { prev: { 'month': true, 'year': true }, next: { 'month': true, 'year': true } }; // if we're in an out of bounds year if (cal.year == cal.start.getFullYear()) { navigation.prev.year = false; if (cal.month == cal.start.getMonth() && this.options.navigation == 1) { navigation.prev.month = false; } } if (cal.year == cal.end.getFullYear()) { navigation.next.year = false; if (cal.month == cal.end.getMonth() && this.options.navigation == 1) { navigation.next.month = false; } } // special case of improved navigation but months array with only 1 month we can disable all month navigation if ($type(cal.months) == 'array') { if (cal.months.length == 1 && this.options.navigation == 2) { navigation.prev.month = navigation.next.month = false; } } var caption = new Element('caption'); var prev = new Element('a').addClass(this.classes.prev).appendText('\x3c'); // < var next = new Element('a').addClass(this.classes.next).appendText('\x3e'); // > if (this.options.navigation == 2) { var month = new Element('span').addClass(this.classes.month).injectInside(caption); if (navigation.prev.month) { prev.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', -1); }.pass(cal, this)).injectInside(month); } month.adopt(new Element('span').appendText(this.options.months[cal.month])); if (navigation.next.month) { next.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', 1); }.pass(cal, this)).injectInside(month); } var year = new Element('span').addClass(this.classes.year).injectInside(caption); if (navigation.prev.year) { prev.clone().addEvent('click', function(cal) { this.navigate(cal, 'y', -1); }.pass(cal, this)).injectInside(year); } year.adopt(new Element('span').appendText(cal.year)); if (navigation.next.year) { next.clone().addEvent('click', function(cal) { this.navigate(cal, 'y', 1); }.pass(cal, this)).injectInside(year); } } else { // 1 or 0 if (navigation.prev.month && this.options.navigation) { prev.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', -1); }.pass(cal, this)).injectInside(caption); } caption.adopt(new Element('span').addClass(this.classes.month).appendText(this.options.months[cal.month])); caption.adopt(new Element('span').addClass(this.classes.year).appendText(cal.year)); if (navigation.next.month && this.options.navigation) { next.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', 1); }.pass(cal, this)).injectInside(caption); } } return caption; }, // changed: run when a select value is changed // @param cal (obj) changed: function(cal) { cal.val = this.read(cal); // update calendar val from inputs $extend(cal, this.values(cal)); // update bounds - based on curr month this.rebuild(cal); // rebuild days select if (!cal.val) { return; } // in case the same date was clicked the cal has no set date we should exit if (cal.val.getDate() < cal.days[0]) { cal.val.setDate(cal.days[0]); } if (cal.val.getDate() > cal.days.getLast()) { cal.val.setDate(cal.days.getLast()); } cal.els.each(function(el) { // then we can set the value to the field el.value = this.format(cal.val, el.format); }, this); this.check(cal); // checks other cals this.calendars.each(function(kal) { // update cal graphic if visible if (kal.visible) { this.display(kal); } }, this); }, // check: checks other calendars to make sure no overlapping values // @param cal (obj) check: function(cal) { this.calendars.each(function(kal, i) { if (kal.val) { // if calendar has value set var change = false; if (i < cal.id) { // preceding calendar var bound = new Date(Date.parse(cal.val)); bound.setDate(bound.getDate() - (this.options.pad * (cal.id - i))); if (bound < kal.val) { change = true; } } if (i > cal.id) { // following calendar var bound = new Date(Date.parse(cal.val)); bound.setDate(bound.getDate() + (this.options.pad * (i - cal.id))); if (bound > kal.val) { change = true; } } if (change) { if (kal.start > bound) { bound = kal.start; } if (kal.end < bound) { bound = kal.end; } kal.month = bound.getMonth(); kal.year = bound.getFullYear(); $extend(kal, this.values(kal)); // TODO - IN THE CASE OF SELECT MOVE TO NEAREST VALID VALUE // IN THE CASE OF INPUT DISABLE // if new date is not valid better unset cal value // otherwise it would mean incrementally checking to find the nearest valid date which could be months / years away kal.val = kal.days.contains(bound.getDate()) ? bound : null; this.write(kal); if (kal.visible) { this.display(kal); } // update cal graphic if visible } } else { kal.month = cal.month; kal.year = cal.year; } }, this); }, // clicked: run when a valid day is clicked in the calendar // @param cal (obj) clicked: function(td, day, cal) { cal.val = (this.value(cal) == day) ? null : new Date(cal.year, cal.month, day); // set new value - if same then disable this.write(cal); // ok - in the special case that it's all selects and there's always a date no matter what (at least as far as the form is concerned) // we can't let the calendar undo a date selection - it's just not possible!! if (!cal.val) { cal.val = this.read(cal); } if (cal.val) { this.check(cal); // checks other cals this.toggle(cal); // hide cal } else { // remove active class and replace with valid td.addClass(this.classes.valid); td.removeClass(this.classes.active); } }, // display: create calendar element // @param cal (obj) display: function(cal) { // 1. header and navigation this.calendar.empty(); // init div this.calendar.className = this.classes.calendar + ' ' + this.options.months[cal.month].toLowerCase(); var div = new Element('div').injectInside(this.calendar); // a wrapper div to help correct browser css problems with the caption element var table = new Element('table').injectInside(div).adopt(this.caption(cal)); // 2. day names var thead = new Element('thead').injectInside(table); var tr = new Element('tr').injectInside(thead); for (var i = 0; i <= 6; i++) { var th = this.options.days[(i + this.options.offset) % 7]; tr.adopt(new Element('th', { 'title': th }).appendText(th.substr(0, 1))); } // 3. day numbers var tbody = new Element('tbody').injectInside(table); var tr = new Element('tr').injectInside(tbody); var d = new Date(cal.year, cal.month, 1); var offset = ((d.getDay() - this.options.offset) + 7) % 7; // day of the week (offset) var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of this month var prev = new Date(cal.year, cal.month, 0).getDate(); // last day of previous month var active = this.value(cal); // active date (if set and within curr month) var valid = cal.days; // valid days for curr month var inactive = []; // active dates set by other calendars var hilited = []; this.calendars.each(function(kal, i) { if (kal != cal && kal.val) { if (cal.year == kal.val.getFullYear() && cal.month == kal.val.getMonth()) { inactive.push(kal.val.getDate()); } if (cal.val) { for (var day = 1; day <= last; day++) { d.setDate(day); if ((i < cal.id && d > kal.val && d < cal.val) || (i > cal.id && d > cal.val && d < kal.val)) { if (!hilited.contains(day)) { hilited.push(day); } } } } } }, this); var d = new Date(); var today = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); // today obv for (var i = 1; i < 43; i++) { // 1 to 42 (6 x 7 or 6 weeks) if ((i - 1) % 7 == 0) { tr = new Element('tr').injectInside(tbody); } // each week is it's own table row var td = new Element('td').injectInside(tr); var day = i - offset; var date = new Date(cal.year, cal.month, day); var cls = ''; if (day === active) { cls = this.classes.active; } // active else if (inactive.contains(day)) { cls = this.classes.inactive; } // inactive else if (valid.contains(day)) { cls = this.classes.valid; } // valid else if (day >= 1 && day <= last) { cls = this.classes.invalid; } // invalid if (date.getTime() == today) { cls = cls + ' ' + this.classes.today; } // adds class for today if (hilited.contains(day)) { cls = cls + ' ' + this.classes.hilite; } // adds class if hilited td.addClass(cls); if (valid.contains(day)) { // if it's a valid - clickable - day we add interaction td.setProperty('title', this.format(date, 'D M jS Y')); td.addEvents({ 'click': function(td, day, cal) { this.clicked(td, day, cal); }.pass([td, day, cal], this), 'mouseover': function(td, cls) { td.addClass(cls); }.pass([td, this.classes.hover]), 'mouseout': function(td, cls) { td.removeClass(cls); }.pass([td, this.classes.hover]) }); } // pad calendar with last days of prev month and first days of next month if (day < 1) { day = prev + day; } else if (day > last) { day = day - last; } td.appendText(day); } }, // element: helper function // @param el (string) element id // @param f (string) format string // @param cal (obj) element: function(el, f, cal) { if ($type(f) == 'object') { // in the case of multiple inputs per calendar for (var i in f) { if (!this.element(i, f[i], cal)) { return false; } } return true; } el = $(el); if (!el) { return false; } el.format = f; if (el.get('tag') == 'select') { // select elements allow the user to manually set the date via select option el.addEvent('change', function(cal) { this.changed(cal); }.pass(cal, this)); } else { // input (type text) elements restrict the user to only setting the date via the calendar el.readOnly = true; el.addEvent('focus', function(cal) { this.toggle(cal); }.pass(cal, this)); } cal.els.push(el); return true; }, // format: formats a date object according to passed in instructions // @param date (obj) // @param f (string) any combination of punctuation / separators and d, j, D, l, S, m, n, F, M, y, Y // @returns string format: function(date, format) { var str = ''; if (date) { var j = date.getDate(); // 1 - 31 var w = date.getDay(); // 0 - 6 var l = this.options.days[w]; // Sunday - Saturday var n = date.getMonth() + 1; // 1 - 12 var f = this.options.months[n - 1]; // January - December var y = date.getFullYear() + ''; // 19xx - 20xx for (var i = 0, len = format.length; i < len; i++) { var cha = format.charAt(i); // format char switch(cha) { // year cases case 'y': // xx - xx y = y.substr(2); case 'Y': // 19xx - 20xx str += y; break; // month cases case 'm': // 01 - 12 if (n < 10) { n = '0' + n; } case 'n': // 1 - 12 str += n; break; case 'M': // Jan - Dec f = f.substr(0, 3); case 'F': // January - December str += f; break; // day cases case 'd': // 01 - 31 if (j < 10) { j = '0' + j; } case 'j': // 1 - 31 str += j; break; case 'D': // Sun - Sat l = l.substr(0, 3); case 'l': // Sunday - Saturday str += l; break; case 'N': // 1 - 7 w += 1; case 'w': // 0 - 6 str += w; break; case 'S': // st, nd, rd or th (works well with j) if (j % 10 == 1 && j != '11') { str += 'st'; } else if (j % 10 == 2 && j != '12') { str += 'nd'; } else if (j % 10 == 3 && j != '13') { str += 'rd'; } else { str += 'th'; } break; default: str += cha; } } } return str; // return format with values replaced }, // navigate: calendar navigation // @param cal (obj) // @param type (str) m or y for month or year // @param n (int) + or - for next or prev navigate: function(cal, type, n) { switch (type) { case 'm': // month if ($type(cal.months) == 'array') { var i = cal.months.indexOf(cal.month) + n; // index of current month if (i < 0 || i == cal.months.length) { // out of range if (this.options.navigation == 1) { // if type 1 nav we'll need to increment the year this.navigate(cal, 'y', n); } i = (i < 0) ? cal.months.length - 1 : 0; } cal.month = cal.months[i]; } else { var i = cal.month + n; if (i < 0 || i == 12) { if (this.options.navigation == 1) { this.navigate(cal, 'y', n); } i = (i < 0) ? 11 : 0; } cal.month = i; } break; case 'y': // year if ($type(cal.years) == 'array') { var i = cal.years.indexOf(cal.year) + n; cal.year = cal.years[i]; } else { cal.year += n; } break; } $extend(cal, this.values(cal)); if ($type(cal.months) == 'array') { // if the calendar has a months select var i = cal.months.indexOf(cal.month); // and make sure the curr months exists for the new year if (i < 0) { cal.month = cal.months[0]; } // otherwise we'll reset the month } this.display(cal); }, // read: compiles cal value based on array of inputs passed in // @param cal (obj) // @returns date (obj) or (null) read: function(cal) { var arr = [null, null, null]; cal.els.each(function(el) { // returns an array which may contain empty values var values = this.unformat(el.value, el.format); values.each(function(val, i) { if ($type(val) == 'number') { arr[i] = val; } }); }, this); // we can update the cals month and year values if ($type(arr[0]) == 'number') { cal.year = arr[0]; } if ($type(arr[1]) == 'number') { cal.month = arr[1]; } var val = null; if (arr.every(function(i) { return $type(i) == 'number'; })) { // if valid date var last = new Date(arr[0], arr[1] + 1, 0).getDate(); // last day of month if (arr[2] > last) { arr[2] = last; } // make sure we stay within the month (ex in case default day of select is 31 and month is feb) val = new Date(arr[0], arr[1], arr[2]); } return (cal.val == val) ? null : val; // if new date matches old return null (same date clicked twice = disable) }, // rebuild: rebuilds days + months selects // @param cal (obj) rebuild: function(cal) { cal.els.each(function(el) { /* if (el.get('tag') == 'select' && el.format.test('^(F|m|M|n)$')) { // special case for months-only select if (!cal.options) { cal.options = el.clone(); } // clone a copy of months select var val = (cal.val) ? cal.val.getMonth() : el.value.toInt(); el.empty(); // initialize select cal.months.each(function(month) { // create an option element var option = new Element('option', { 'selected': (val == month), 'value': this.format(new Date(1, month, 1), el.format); }).appendText(day).injectInside(el); }, this); } */ if (el.get('tag') == 'select' && el.format.test('^(d|j)$')) { // special case for days-only select var d = this.value(cal); if (!d) { d = el.value.toInt(); } // if the calendar doesn't have a set value, try to use value from select el.empty(); // initialize select cal.days.each(function(day) { // create an option element var option = new Element('option', { 'selected': (d == day), 'value': ((el.format == 'd' && day < 10) ? '0' + day : day) }).appendText(day).injectInside(el); }, this); } }, this); }, // sort: helper function for numerical sorting sort: function(a, b) { return a - b; }, // toggle: show / hide calendar // @param cal (obj) toggle: function(cal) { document.removeEvent('mousedown', this.fn); // always remove the current mousedown script first if (cal.visible) { // simply hide curr cal cal.visible = false; cal.button.removeClass(this.classes.active); // active this.fx.start('opacity', 1, 0); } else { // otherwise show (may have to hide others) // hide cal on out-of-bounds click this.fn = function(e, cal) { var e = new Event(e); var el = e.target; var stop = false; while (el != document.body && el.nodeType == 1) { if (el == this.calendar) { stop = true; } this.calendars.each(function(kal) { if (kal.button == el || kal.els.contains(el)) { stop = true; } }); if (stop) { e.stop(); return false; } else { el = el.parentNode; } } this.toggle(cal); }.create({ 'arguments': cal, 'bind': this, 'event': true }); document.addEvent('mousedown', this.fn); this.calendars.each(function(kal) { if (kal == cal) { kal.visible = true; kal.button.addClass(this.classes.active); // css c-icon-active } else { kal.visible = false; kal.button.removeClass(this.classes.active); // css c-icon-active } }, this); var size = window.getScrollSize(); var coord = cal.button.getCoordinates(); var x = coord.right + this.options.tweak.x; var y = coord.top + this.options.tweak.y; // make sure the calendar doesn't open off screen if (!this.calendar.coord) { this.calendar.coord = this.calendar.getCoordinates(); } if (x + this.calendar.coord.width > size.x) { x -= (x + this.calendar.coord.width - size.x); } if (y + this.calendar.coord.height > size.y) { y -= (y + this.calendar.coord.height - size.y); } this.calendar.setStyles({ left: x + 'px', top: y + 'px' }); if (window.ie6) { this.iframe.setStyles({ height: this.calendar.coord.height + 'px', left: x + 'px', top: y + 'px', width: this.calendar.coord.width + 'px' }); } this.display(cal); this.fx.start('opacity', 0, 1); } }, // unformat: takes a value from an input and parses the d, m and y elements // @param val (string) // @param f (string) any combination of punctuation / separators and d, j, D, l, S, m, n, F, M, y, Y // @returns array unformat: function(val, f) { f = f.escapeRegExp(); var re = { d: '([0-9]{2})', j: '([0-9]{1,2})', D: '(' + this.options.days.map(function(day) { return day.substr(0, 3); }).join('|') + ')', l: '(' + this.options.days.join('|') + ')', S: '(st|nd|rd|th)', F: '(' + this.options.months.join('|') + ')', m: '([0-9]{2})', M: '(' + this.options.months.map(function(month) { return month.substr(0, 3); }).join('|') + ')', n: '([0-9]{1,2})', Y: '([0-9]{4})', y: '([0-9]{2})' } var arr = []; // array of indexes var g = ''; // convert our format string to regexp for (var i = 0; i < f.length; i++) { var c = f.charAt(i); if (re[c]) { arr.push(c); g += re[c]; } else { g += c; } } // match against date var matches = val.match('^' + g + '$'); var dates = new Array(3); if (matches) { matches = matches.slice(1); // remove first match which is the date arr.each(function(c, i) { i = matches[i]; switch(c) { // year cases case 'y': i = '19' + i; // 2 digit year assumes 19th century (same as JS) case 'Y': dates[0] = i.toInt(); break; // month cases case 'F': i = i.substr(0, 3); case 'M': i = this.options.months.map(function(month) { return month.substr(0, 3); }).indexOf(i) + 1; case 'm': case 'n': dates[1] = i.toInt() - 1; break; // day cases case 'd': case 'j': dates[2] = i.toInt(); break; } }, this); } return dates; }, // value: returns day value of calendar if set // @param cal (obj) // @returns day (int) or null value: function(cal) { var day = null; if (cal.val) { if (cal.year == cal.val.getFullYear() && cal.month == cal.val.getMonth()) { day = cal.val.getDate(); } } return day; }, // values: returns the years, months (for curr year) and days (for curr month and year) for the calendar // @param cal (obj) // @returns obj values: function(cal) { var years, months, days; cal.els.each(function(el) { if (el.get('tag') == 'select') { if (el.format.test('(y|Y)')) { // search for a year select years = []; el.getChildren().each(function(option) { // get options var values = this.unformat(option.value, el.format); if (!years.contains(values[0])) { years.push(values[0]); } // add to years array }, this); years.sort(this.sort); } if (el.format.test('(F|m|M|n)')) { // search for a month select months = []; // 0 - 11 should be el.getChildren().each(function(option) { // get options var values = this.unformat(option.value, el.format); if ($type(values[0]) != 'number' || values[0] == cal.year) { // if it's a year / month combo for curr year, or simply a month select if (!months.contains(values[1])) { months.push(values[1]); } // add to months array } }, this); months.sort(this.sort); } if (el.format.test('(d|j)') && !el.format.test('^(d|j)$')) { // search for a day select, but NOT a days only select days = []; // 1 - 31 el.getChildren().each(function(option) { // get options var values = this.unformat(option.value, el.format); // in the special case of days we dont want the value if its a days only select // otherwise that will screw up the options rebuilding // we will take the values if they are exact dates though if (values[0] == cal.year && values[1] == cal.month) { if (!days.contains(values[2])) { days.push(values[2]); } // add to days array } }, this); } } }, this); // we start with what would be the first and last days were there no restrictions var first = 1; var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of the month // if we're in an out of bounds year if (cal.year == cal.start.getFullYear()) { // in the special case of improved navigation but no months array, we'll need to construct one if (months == null && this.options.navigation == 2) { months = []; for (var i = 0; i < 12; i ++) { if (i >= cal.start.getMonth()) { months.push(i); } } } // if we're in an out of bounds month if (cal.month == cal.start.getMonth()) { first = cal.start.getDate(); // first day equals day of bound } } if (cal.year == cal.end.getFullYear()) { // in the special case of improved navigation but no months array, we'll need to construct one if (months == null && this.options.navigation == 2) { months = []; for (var i = 0; i < 12; i ++) { if (i <= cal.end.getMonth()) { months.push(i); } } } if (cal.month == cal.end.getMonth()) { last = cal.end.getDate(); // last day equals day of bound } } // let's get our invalid days var blocked = this.blocked(cal); // finally we can prepare all the valid days in a neat little array if ($type(days) == 'array') { // somewhere there was a days select days = days.filter(function(day) { if (day >= first && day <= last && !blocked.contains(day)) { return day; } }); } else { // no days select we'll need to construct a valid days array days = []; for (var i = first; i <= last; i++) { if (!blocked.contains(i)) { days.push(i); } } } days.sort(this.sort); // sorting our days will give us first and last of month return { 'days': days, 'months': months, 'years': years }; }, // write: sets calendars value to form elements // @param cal (obj) write: function(cal) { this.rebuild(cal); // in the case of options, we'll need to make sure we have the correct number of days available cal.els.each(function(el) { // then we can set the value to the field el.value = this.format(cal.val, el.format); }, this); } }); Calendar.implement(new Events, new Options);/*** * MooRainbow * * @version 1.2b2 * @license MIT-style license * @author Djamil Legato - < djamil [at] djamil.it > * @infos http://moorainbow.woolly-sheep.net * @copyright Author * * */ var Rainbows = []; var MooRainbow = new Class({ options: { id: 'mooRainbow', prefix: 'moor-', imgPath: 'images/', startColor: [255, 0, 0], wheel: false, onComplete: $empty, onChange: $empty }, initialize: function(el, options) { this.element = $(el); if (!this.element) return; this.setOptions(options); this.sliderPos = 0; this.pickerPos = {x: 0, y: 0}; this.backupColor = this.options.startColor; this.currentColor = this.options.startColor; this.sets = { rgb: [], hsb: [], hex: [] }; this.pickerClick = this.sliderClick = false; if (!this.layout) this.doLayout(); this.OverlayEvents(); this.sliderEvents(); this.backupEvent(); if (this.options.wheel) this.wheelEvents(); this.element.addEvent('click', function(e) { this.closeAll().toggle(e); }.bind(this)); this.layout.overlay.setStyle('background-color', this.options.startColor.rgbToHex()); this.layout.backup.setStyle('background-color', this.backupColor.rgbToHex()); this.pickerPos.x = this.snippet('curPos').l + this.snippet('curSize', 'int').w; this.pickerPos.y = this.snippet('curPos').t + this.snippet('curSize', 'int').h; this.manualSet(this.options.startColor); this.pickerPos.x = this.snippet('curPos').l + this.snippet('curSize', 'int').w; this.pickerPos.y = this.snippet('curPos').t + this.snippet('curSize', 'int').h; this.sliderPos = this.snippet('arrPos') - this.snippet('arrSize', 'int'); if (window.khtml) this.hide(); }, toggle: function() { this[this.visible ? 'hide' : 'show'](); }, show: function() { this.rePosition(); this.layout.setStyle('display', 'block'); this.visible = true; }, hide: function() { this.layout.setStyles({'display': 'none'}); this.visible = false; }, closeAll: function() { Rainbows.each(function(obj) { obj.hide(); }); return this; }, manualSet: function(color, type) { if (!type || (type != 'hsb' && type != 'hex')) type = 'rgb'; var rgb, hsb, hex; if (type == 'rgb') { rgb = color; hsb = color.rgbToHsb(); hex = color.rgbToHex(); } else if (type == 'hsb') { hsb = color; rgb = color.hsbToRgb(); hex = rgb.rgbToHex(); } else { hex = color; rgb = color.hexToRgb(true); hsb = rgb.rgbToHsb(); } this.setMooRainbow(rgb); this.autoSet(hsb); }, autoSet: function(hsb) { var curH = this.snippet('curSize', 'int').h; var curW = this.snippet('curSize', 'int').w; var oveH = this.layout.overlay.height; var oveW = this.layout.overlay.width; var sliH = this.layout.slider.height; var arwH = this.snippet('arrSize', 'int'); var hue; var posx = Math.round(((oveW * hsb[1]) / 100) - curW); var posy = Math.round(- ((oveH * hsb[2]) / 100) + oveH - curH); var c = Math.round(((sliH * hsb[0]) / 360)); c = (c == 360) ? 0 : c; var position = sliH - c + this.snippet('slider') - arwH; hue = [this.sets.hsb[0], 100, 100].hsbToRgb().rgbToHex(); this.layout.cursor.setStyles({'top': posy, 'left': posx}); this.layout.arrows.setStyle('top', position); this.layout.overlay.setStyle('background-color', hue); this.sliderPos = this.snippet('arrPos') - arwH; this.pickerPos.x = this.snippet('curPos').l + curW; this.pickerPos.y = this.snippet('curPos').t + curH; }, setMooRainbow: function(color, type) { if (!type || (type != 'hsb' && type != 'hex')) type = 'rgb'; var rgb, hsb, hex; if (type == 'rgb') { rgb = color; hsb = color.rgbToHsb(); hex = color.rgbToHex(); } else if (type == 'hsb') { hsb = color; rgb = color.hsbToRgb(); hex = rgb.rgbToHex(); } else { hex = color; rgb = color.hexToRgb(); hsb = rgb.rgbToHsb(); } this.sets = { rgb: rgb, hsb: hsb, hex: hex }; if (!$chk(this.pickerPos.x)) this.autoSet(hsb); this.RedInput.value = rgb[0]; this.GreenInput.value = rgb[1]; this.BlueInput.value = rgb[2]; this.HueInput.value = hsb[0]; this.SatuInput.value = hsb[1]; this.BrighInput.value = hsb[2]; this.hexInput.value = hex; this.currentColor = rgb; this.chooseColor.setStyle('background-color', rgb.rgbToHex()); }, parseColors: function(x, y, z) { var s = Math.round((x * 100) / this.layout.overlay.width); var b = 100 - Math.round((y * 100) / this.layout.overlay.height); var h = 360 - Math.round((z * 360) / this.layout.slider.height) + this.snippet('slider') - this.snippet('arrSize', 'int'); h -= this.snippet('arrSize', 'int'); h = (h >= 360) ? 0 : (h < 0) ? 0 : h; s = (s > 100) ? 100 : (s < 0) ? 0 : s; b = (b > 100) ? 100 : (b < 0) ? 0 : b; return [h, s, b]; }, OverlayEvents: function() { var lim, curH, curW, inputs; curH = this.snippet('curSize', 'int').h; curW = this.snippet('curSize', 'int').w; inputs = $A(this.arrRGB).concat(this.arrHSB, this.hexInput); document.addEvent('click', function() { if(this.visible) this.hide(this.layout); }.bind(this)); inputs.each(function(el) { el.addEvent('keydown', this.eventKeydown.bindWithEvent(this, el)); el.addEvent('keyup', this.eventKeyup.bindWithEvent(this, el)); }, this); [this.element, this.layout].each(function(el) { el.addEvents({ 'click': function(e) { new Event(e).stop(); }, 'keyup': function(e) { e = new Event(e); if(e.key == 'esc' && this.visible) this.hide(this.layout); }.bind(this) }, this); }, this); lim = { x: [0 - curW, (this.layout.overlay.width - curW)], y: [0 - curH, (this.layout.overlay.height - curH)] }; this.layout.drag = new Drag(this.layout.cursor, { limit: lim, onBeforeStart: this.overlayDrag.bind(this), onStart: this.overlayDrag.bind(this), onDrag: this.overlayDrag.bind(this), snap: 0 }); this.layout.overlay2.addEvent('mousedown', function(e){ e = new Event(e); this.layout.cursor.setStyles({ 'top': e.page.y - this.layout.overlay.getTop() - curH, 'left': e.page.x - this.layout.overlay.getLeft() - curW }); this.layout.drag.start(e); }.bind(this)); this.okButton.addEvent('click', function() { if (this.currentColor == this.options.startColor) { this.hide(); this.fireEvent('onComplete', [this.sets, this]); } else { this.backupColor = this.currentColor; this.layout.backup.setStyle('background-color', this.backupColor.rgbToHex()); this.hide(); this.fireEvent('onComplete', [this.sets, this]); } }.bind(this)); this.transp.addEvent('click', function () { this.hide(); this.fireEvent('onComplete', ['transparent', this]); }.bind(this)); }, overlayDrag: function() { var curH = this.snippet('curSize', 'int').h; var curW = this.snippet('curSize', 'int').w; this.pickerPos.x = this.snippet('curPos').l + curW; this.pickerPos.y = this.snippet('curPos').t + curH; this.setMooRainbow(this.parseColors(this.pickerPos.x, this.pickerPos.y, this.sliderPos), 'hsb'); this.fireEvent('onChange', [this.sets, this]); }, sliderEvents: function() { var arwH = this.snippet('arrSize', 'int'), lim; lim = [0 + this.snippet('slider') - arwH, this.layout.slider.height - arwH + this.snippet('slider')]; this.layout.sliderDrag = new Drag(this.layout.arrows, { limit: {y: lim}, modifiers: {x: false}, onBeforeStart: this.sliderDrag.bind(this), onStart: this.sliderDrag.bind(this), onDrag: this.sliderDrag.bind(this), snap: 0 }); this.layout.slider.addEvent('mousedown', function(e){ e = new Event(e); this.layout.arrows.setStyle( 'top', e.page.y - this.layout.slider.getTop() + this.snippet('slider') - arwH ); this.layout.sliderDrag.start(e); }.bind(this)); }, sliderDrag: function() { var arwH = this.snippet('arrSize', 'int'), hue; this.sliderPos = this.snippet('arrPos') - arwH; this.setMooRainbow(this.parseColors(this.pickerPos.x, this.pickerPos.y, this.sliderPos), 'hsb'); hue = [this.sets.hsb[0], 100, 100].hsbToRgb().rgbToHex(); this.layout.overlay.setStyle('background-color', hue); this.fireEvent('onChange', [this.sets, this]); }, backupEvent: function() { this.layout.backup.addEvent('click', function() { this.manualSet(this.backupColor); this.fireEvent('onChange', [this.sets, this]); }.bind(this)); }, wheelEvents: function() { var arrColors = $A(this.arrRGB).extend(this.arrHSB); arrColors.each(function(el) { el.addEvents({ 'mousewheel': this.eventKeys.bindWithEvent(this, el), 'keydown': this.eventKeys.bindWithEvent(this, el) }); }, this); [this.layout.arrows, this.layout.slider].each(function(el) { el.addEvents({ 'mousewheel': this.eventKeys.bindWithEvent(this, [this.arrHSB[0], 'slider']), 'keydown': this.eventKeys.bindWithEvent(this, [this.arrHSB[0], 'slider']) }); }, this); }, eventKeys: function(e, el, id) { var wheel, type; id = (!id) ? el.id : this.arrHSB[0]; if (e.type == 'keydown') { if (e.key == 'up') wheel = 1; else if (e.key == 'down') wheel = -1; else return; } else if (e.type == Element.Events.mousewheel.base) wheel = (e.wheel > 0) ? 1 : -1; if (this.arrRGB.contains(el)) type = 'rgb'; else if (this.arrHSB.contains(el)) type = 'hsb'; else type = 'hsb'; if (type == 'rgb') { var rgb = this.sets.rgb, hsb = this.sets.hsb, prefix = this.options.prefix, pass; var value = (el.value.toInt() || 0) + wheel; value = (value > 255) ? 255 : (value < 0) ? 0 : value; switch(el.className) { case prefix + 'rInput': pass = [value, rgb[1], rgb[2]]; break; case prefix + 'gInput': pass = [rgb[0], value, rgb[2]]; break; case prefix + 'bInput': pass = [rgb[0], rgb[1], value]; break; default : pass = rgb; } this.manualSet(pass); this.fireEvent('onChange', [this.sets, this]); } else { var rgb = this.sets.rgb, hsb = this.sets.hsb, prefix = this.options.prefix, pass; var value = (el.value.toInt() || 0) + wheel; if (el.className.test(/(HueInput)/)) value = (value > 359) ? 0 : (value < 0) ? 0 : value; else value = (value > 100) ? 100 : (value < 0) ? 0 : value; switch(el.className) { case prefix + 'HueInput': pass = [value, hsb[1], hsb[2]]; break; case prefix + 'SatuInput': pass = [hsb[0], value, hsb[2]]; break; case prefix + 'BrighInput': pass = [hsb[0], hsb[1], value]; break; default : pass = hsb; } this.manualSet(pass, 'hsb'); this.fireEvent('onChange', [this.sets, this]); } e.stop(); }, eventKeydown: function(e, el) { var n = e.code, k = e.key; if ((!el.className.test(/hexInput/) && !(n >= 48 && n <= 57)) && (k!='backspace' && k!='tab' && k !='delete' && k!='left' && k!='right')) e.stop(); }, eventKeyup: function(e, el) { var n = e.code, k = e.key, pass, prefix, chr = el.value.charAt(0); if (!$chk(el.value)) return; if (el.className.test(/hexInput/)) { if (chr != "#" && el.value.length != 6) return; if (chr == '#' && el.value.length != 7) return; } else { if (!(n >= 48 && n <= 57) && (!['backspace', 'tab', 'delete', 'left', 'right'].contains(k)) && el.value.length > 3) return; } prefix = this.options.prefix; if (el.className.test(/(rInput|gInput|bInput)/)) { if (el.value < 0 || el.value > 255) return; switch(el.className){ case prefix + 'rInput': pass = [el.value, this.sets.rgb[1], this.sets.rgb[2]]; break; case prefix + 'gInput': pass = [this.sets.rgb[0], el.value, this.sets.rgb[2]]; break; case prefix + 'bInput': pass = [this.sets.rgb[0], this.sets.rgb[1], el.value]; break; default : pass = this.sets.rgb; } this.manualSet(pass); this.fireEvent('onChange', [this.sets, this]); } else if (!el.className.test(/hexInput/)) { if (el.className.test(/HueInput/) && el.value < 0 || el.value > 360) return; else if (el.className.test(/HueInput/) && el.value == 360) el.value = 0; else if (el.className.test(/(SatuInput|BrighInput)/) && el.value < 0 || el.value > 100) return; switch(el.className){ case prefix + 'HueInput': pass = [el.value, this.sets.hsb[1], this.sets.hsb[2]]; break; case prefix + 'SatuInput': pass = [this.sets.hsb[0], el.value, this.sets.hsb[2]]; break; case prefix + 'BrighInput': pass = [this.sets.hsb[0], this.sets.hsb[1], el.value]; break; default : pass = this.sets.hsb; } this.manualSet(pass, 'hsb'); this.fireEvent('onChange', [this.sets, this]); } else { pass = el.value.hexToRgb(true); if (isNaN(pass[0])||isNaN(pass[1])||isNaN(pass[2])) return; if ($chk(pass)) { this.manualSet(pass); this.fireEvent('onChange', [this.sets, this]); } } }, doLayout: function() { var id = this.options.id, prefix = this.options.prefix; var idPrefix = id + ' .' + prefix; this.layout = new Element('div', { 'styles': {'display': 'block', 'position': 'absolute'}, 'id': id }).inject(document.body); Rainbows.push(this); var box = new Element('div', { 'styles': {'position': 'relative'}, 'class': prefix + 'box' }).inject(this.layout); var div = new Element('div', { 'styles': {'position': 'absolute', 'overflow': 'hidden'}, 'class': prefix + 'overlayBox' }).inject(box); var ar = new Element('div', { 'styles': {'position': 'absolute', 'zIndex': 1}, 'class': prefix + 'arrows' }).inject(box); ar.width = ar.getStyle('width').toInt(); ar.height = ar.getStyle('height').toInt(); var ov = new Element('img', { 'styles': {'background-color': '#fff', 'position': 'relative', 'zIndex': 2}, 'src': this.options.imgPath + 'moor_woverlay.png', 'class': prefix + 'overlay' }).inject(div); var ov2 = new Element('img', { 'styles': {'position': 'absolute', 'top': 0, 'left': 0, 'zIndex': 2}, 'src': this.options.imgPath + 'moor_boverlay.png', 'class': prefix + 'overlay' }).inject(div); if (window.ie6) { div.setStyle('overflow', ''); var src = ov.src; ov.src = this.options.imgPath + 'blank.gif'; ov.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod='scale')"; src = ov2.src; ov2.src = this.options.imgPath + 'blank.gif'; ov2.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod='scale')"; } ov.width = ov2.width = div.getStyle('width').toInt(); ov.height = ov2.height = div.getStyle('height').toInt(); var cr = new Element('div', { 'styles': {'overflow': 'hidden', 'position': 'absolute', 'zIndex': 2}, 'class': prefix + 'cursor' }).inject(div); cr.width = cr.getStyle('width').toInt(); cr.height = cr.getStyle('height').toInt(); var sl = new Element('img', { 'styles': {'position': 'absolute', 'z-index': 2}, 'src': this.options.imgPath + 'moor_slider.png', 'class': prefix + 'slider' }).inject(box); this.layout.slider = document.getElement('#' + idPrefix + 'slider'); sl.width = sl.getStyle('width').toInt(); sl.height = sl.getStyle('height').toInt(); new Element('div', { 'styles': {'position': 'absolute'}, 'class': prefix + 'colorBox' }).inject(box); new Element('div', { 'styles': {'zIndex': 2, 'position': 'absolute'}, 'class': prefix + 'chooseColor' }).inject(box); this.layout.backup = new Element('div', { 'styles': {'zIndex': 2, 'position': 'absolute', 'cursor': 'pointer'}, 'class': prefix + 'currentColor' }).inject(box); var R = new Element('label').inject(box).setStyle('position', 'absolute'); var G = R.clone().inject(box).addClass(prefix + 'gLabel').appendText('G: '); var B = R.clone().inject(box).addClass(prefix + 'bLabel').appendText('B: '); R.appendText('R: ').addClass(prefix + 'rLabel'); var inputR = new Element('input'); var inputG = inputR.clone().inject(G).addClass(prefix + 'gInput'); var inputB = inputR.clone().inject(B).addClass(prefix + 'bInput'); inputR.inject(R).addClass(prefix + 'rInput'); var HU = new Element('label').inject(box).setStyle('position', 'absolute'); var SA = HU.clone().inject(box).addClass(prefix + 'SatuLabel').appendText('S: '); var BR = HU.clone().inject(box).addClass(prefix + 'BrighLabel').appendText('B: '); HU.appendText('H: ').addClass(prefix + 'HueLabel'); var inputHU = new Element('input'); var inputSA = inputHU.clone().inject(SA).addClass(prefix + 'SatuInput'); var inputBR = inputHU.clone().inject(BR).addClass(prefix + 'BrighInput'); inputHU.inject(HU).addClass(prefix + 'HueInput'); SA.appendText(' %'); BR.appendText(' %'); new Element('span', {'styles': {'position': 'absolute'}, 'class': prefix + 'ballino'}).set('html', " °").injectAfter(HU); var hex = new Element('label').inject(box).setStyle('position', 'absolute').addClass(prefix + 'hexLabel').appendText('#hex: ').adopt(new Element('input').addClass(prefix + 'hexInput')); var ok = new Element('input', { 'styles': {'position': 'absolute'}, 'type': 'button', 'value': 'Auswaehlen', 'class': prefix + 'okButton' }).inject(box); var transp = new Element('a', {'style': {'position': 'absolute'}, 'href': '#', 'class': prefix + 'transp'}).inject(box); this.rePosition(); var overlays = $$('#' + idPrefix + 'overlay'); this.layout.overlay = overlays[0]; this.layout.overlay2 = overlays[1]; this.layout.cursor = document.getElement('#' + idPrefix + 'cursor'); this.layout.arrows = document.getElement('#' + idPrefix + 'arrows'); this.chooseColor = document.getElement('#' + idPrefix + 'chooseColor'); this.layout.backup = document.getElement('#' + idPrefix + 'currentColor'); this.RedInput = document.getElement('#' + idPrefix + 'rInput'); this.GreenInput = document.getElement('#' + idPrefix + 'gInput'); this.BlueInput = document.getElement('#' + idPrefix + 'bInput'); this.HueInput = document.getElement('#' + idPrefix + 'HueInput'); this.SatuInput = document.getElement('#' + idPrefix + 'SatuInput'); this.BrighInput = document.getElement('#' + idPrefix + 'BrighInput'); this.hexInput = document.getElement('#' + idPrefix + 'hexInput'); this.arrRGB = [this.RedInput, this.GreenInput, this.BlueInput]; this.arrHSB = [this.HueInput, this.SatuInput, this.BrighInput]; this.okButton = document.getElement('#' + idPrefix + 'okButton'); this.transp = box.getElement('.' + prefix + 'transp'); if (!window.khtml) this.hide(); }, rePosition: function() { var coords = this.element.getCoordinates(); this.layout.setStyles({ 'left': coords.left, 'top': coords.top + coords.height + 1 }); }, snippet: function(mode, type) { var size; type = (type) ? type : 'none'; switch(mode) { case 'arrPos': var t = this.layout.arrows.getStyle('top').toInt(); size = t; break; case 'arrSize': var h = this.layout.arrows.height; h = (type == 'int') ? (h/2).toInt() : h; size = h; break; case 'curPos': var l = this.layout.cursor.getStyle('left').toInt(); var t = this.layout.cursor.getStyle('top').toInt(); size = {'l': l, 't': t}; break; case 'slider': var t = this.layout.slider.getStyle('marginTop').toInt(); size = t; break; default : var h = this.layout.cursor.height; var w = this.layout.cursor.width; h = (type == 'int') ? (h/2).toInt() : h; w = (type == 'int') ? (w/2).toInt() : w; size = {w: w, h: h}; }; return size; } }); MooRainbow.implement(new Options); MooRainbow.implement(new Events); var OwnWindoo = new Class({ Implements: [Events, Options], options: { fxDuration: 700, maxOpacity: 1, timeIn: 1000, timeOut: 5000, width:'300px', height:'400px', ownPosition:false, fixed:true, offsets: {'x': 16, 'y': 16}, modal:false, draggMe:false, resizeMe:false, resizeLimit:{y: [300, 600],x:[300,500]}, effecting:false, stayalive:true, transitionEffect: Fx.Transitions.Elastic.easeOut, startFrom: {'x': 0, 'y': 0}, moveTo: {'x': 100, 'y': 100}, title:'', text:'', desktopUse:true, lilWindowShow:'FooterWindows', StyleInaktiv:{'background-color':'#ff0000'}, StyleAktiv:{'background-color':'#fff'}, imageClose:'images/windoo/close.gif', imageResize:'images/windoo/resize.gif', ajaxfile:'tooltext.php', ajaxoption:'', limitContainer:false, className: 'myHtmlAreaWin', onAjaxComplete: Class.empty }, initialize: function(element, options){ this.setOptions(options); this.element = $(element); this.document = (element.length) ? element[0].ownerDocument : document; this.WindooMain = new Element('div').setProperties({'id':this.element.id+'_main','class':this.options.className+'-WinMain'}).setStyles({'position': 'absolute','top': '0px','left': '0px','width': this.options.width +'px','height': this.options.height+'px','visibility':'hidden'}); if(Browser.Engine.trident) { try { this.document.execCommand("BackgroundImageCache",false, true); } catch (e) { } } var WindooCloseImg=''; if(this.options.stayalive){ WindooCloseImg=''; // WindooCloseImg='schliessen'; } if(this.options.resizeMe){ var WindooResizeImg='
'; //this.WindooResizeImg=new Element('div').setProperties({'id':this.element.id+'_resize','class':this.options.className+'-WinResize'}) }else{ WindooResizeImg=''; } var WindooLilWindow=''; if(this.options.desktopUse){ WindooLilWindow='
'; this.lilWindowShow = $(this.options.lilWindowShow); } // var WindooHead ='
'+this.options.title+'
'+WindooCloseImg+'
'; var WindooHead ='
'+this.options.title+'
'+WindooLilWindow+'
'+WindooCloseImg+'
'; var WindooBody='
'; var WindooFooter ='
'+WindooResizeImg+'
'; this.WindooMain.set('html',WindooHead+WindooBody+WindooFooter); this.WindooMain.inject(document.body); if(this.options.modal==true){ if(Browser.Engine.trident) { this.iframeBG= new Element("iframe").setProperties({'id':this.element.id+'_iframeBg','class':this.options.className+'-DivBg'}).setStyles({'height':Window.getScrollHeight()+'px','opacity':0.1}).inject(this.WindooMain,'before'); } this.divBG= new Element('div').setProperties({'id':this.element.id+'_divBg','class':this.options.className+'-DivBg'}).setStyles({'height':Window.getScrollHeight()+'px','opacity':0.7}).inject(this.WindooMain,'before'); } //DEFINE Aktuelles Objekt this.BodyInstance=$(this.element.id+'_toolBody'); this.AjaxInstance=$(this.element.id+'_ajaxcontent'); this.FooterInstance=$(this.element.id+'_bot'); this.DragerInstance=$(this.element.id+'_dragToolArea'); if(this.options.desktopUse){ this.LilHandlerInstance=$(this.element.id+'_lilknob'); this.LilHandlerInstance.addEvent('click',function(){ this.WindooMain.setStyle('opacity','0'); this.LilWindow.setStyles(this.options.StyleInaktiv); }.bind(this)); } if(this.options.resizeMe){ this.ResizerInstance=$(this.element.id+'_resize'); this.ResizerInstance.addEvent('click',function(){ this.cloneInstance=new Element('div').setProperties({'id':this.element.id+'_CloneResize','class':this.options.className+'-resizeDiv'}).setStyles($(this.element.id+'_main').getCoordinates()) .setStyles({'opacity':'0.5'}) .inject(document.body); this.cloneInstance.set('html','
Resize Me'); this.WindooMain.setStyles({'opacity':'0.8'}); this.cloneInstance.makeResizable({ limit: this.options.resizeLimit, snap: 2, onStart:function(){ }.bind(this), onComplete:function(){ saveHeight=this.cloneInstance.getCoordinates().height.toInt(); saveWidth=this.cloneInstance.getCoordinates().width.toInt(); saveBotHeight=this.FooterInstance.getCoordinates().height.toInt(); saveTopHeight=this.DragerInstance.getCoordinates().height.toInt(); this.WindooMain.setStyles({'width':saveWidth,'height':saveHeight}).setStyles({'position':'absolute','opacity':'1'}); this.BodyInstance.setStyles({ 'height':(saveHeight-saveTopHeight)+'px' }); this.AjaxInstance.setStyles({ 'height':(saveHeight-(saveBotHeight+saveTopHeight+10))+'px', 'width':(saveWidth-16)+'px' }); this.cloneInstance.destroy(); }.bind(this) }); }.bind(this)); } if(this.options.draggMe){ this.DraggerHandlerInstance=$(this.element.id+'_dragTool'); this.WindooMain.makeDraggable({ handle: this.DraggerHandlerInstance, container:this.options.limitContainer, onBeforeStart:function(){ this.lilWindowInit(); }.bind(this), onComplete:function(){ // this.WindooMain.setStyle('zIndex',100); } }); } if(this.options.stayalive){ this.CloserHandlerInstance=$(this.element.id+'_close'); this.CloserHandlerInstance.addEvent('click',function(){ this.end(); if(this.options.desktopUse){ this.LilWindow.destroy(); } }.bind(this)); } if(!this.options.stayalive && this.options.effecting==false){ var end = this.endAlive.bind(this); this.element.addEvent('mouseleave', end); } this.myEffect = new Fx.Morph(this.WindooMain, {duration: 1000, transition: this.options.transitionEffect}); this.BodyInstance.setStyles({'height':(this.options.height-(this.FooterInstance.getCoordinates().height*2))+'px','width':'auto'}); if (this.options.initialize) this.options.initialize.call(this); }, end: function(event){ this.WindooMain.set('html',''); $clear(this.timer); this.timer = this.disappear.delay(500, this); }, endAlive: function(event){ $clear(this.timer); this.timer = this.disappear.delay(500, this); }, lilWindowInit:function(){ // myZindex=this.WindooMain.getStyle('zIndex'); if($$('div.'+this.options.className+'-WinMain').length>1){ $$('div.'+this.options.className+'-WinMain').each(function(el){ el.setStyles({'zIndex':'100'}); }); } if(this.options.desktopUse){ if($$('div.'+this.options.className+'-WinLil').length>1){ $$('div.'+this.options.className+'-WinLil').each(function(el){ el.setStyles(this.options.StyleInaktiv); }.bind(this)); } this.LilWindow.setStyles(this.options.StyleAktiv); } this.WindooMain.setStyles({'zIndex':'101','opacity':'1'}); }, createLilWindow:function(){ this.LilWindow=new Element('div').setProperties({'id':this.element.id+'_lil','class':this.options.className+'-WinLil'}).set('html',this.options.title); this.LilWindow.inject(this.lilWindowShow); this.lilWindowInit(); this.LilWindow.addEvent('click',function(){ this.lilWindowInit(); }.bind(this)); }, position: function(element){ var newleft=''; var pos = element.getPosition(); if((this.options.width.toInt()+pos.x.toInt()) > Window.getWidth()){ newleft=(Window.getWidth()-this.options.width-20); }else{ newleft=pos.x; } this.WindooMain.setStyles({ 'left': newleft + this.options.offsets.x+'px', 'top': pos.y + this.options.offsets.y+'px' }); }, ownposition: function(){ var newleft=''; if((this.options.width.toInt()+this.options.offsets.x.toInt()) > Window.getWidth()){ newleft=(Window.getWidth()-this.options.width-20); }else{ newleft=this.options.offsets.x; } this.WindooMain.setStyles({ 'position':'absolute', 'left':newleft+'px', 'top':this.options.offsets.y+'px' }); }, getEffect: function(){ var newleft=''; if((this.options.width.toInt()+this.options.moveTo.x.toInt()) > Window.getWidth()){ newleft=(Window.getWidth()-this.options.width-20); }else{ newleft=this.options.moveTo.x; } //mitte 200px breite 400px= 200=(400/2)+2 var tempLeft=newleft+(this.options.width/2); var tempTop=this.options.moveTo.y+(this.options.height/2); this.myEffect.start({ 'height':[0,this.options.height], 'width':[0,this.options.width], 'top':[tempTop,this.options.moveTo.y], 'left':[tempLeft,newleft], 'opacity':1 }).chain(function(){ saveBotHeight=this.FooterInstance.getCoordinates().height.toInt(); saveTopHeight=this.DragerInstance.getCoordinates().height.toInt(); this.BodyInstance.setStyles({ 'height':(this.options.height-saveTopHeight)+'px' }); this.AjaxInstance.setStyles({ 'height':(this.options.height-(saveBotHeight+saveTopHeight+10))+'px', 'width':(this.options.width-16)+'px' }); // this.Schatten = new Element('div').setProperties({'id':this.element.id+'_schatten','class':this.options.className+'-Schatten'}).setStyles({'position': 'absolute','top': (this.options.moveTo.y+10)+'px','left': (newleft+5)+'px','width':this.options.width.toInt() +'px','height': this.options.height.toInt()+'px',}).inject(this.WindooMain,'after'); this.show(); }.bind(this)); if(!this.options.stayalive){ this.endAlive(this); } }, show: function(){ if(this.options.takeajax){ this.ajaxload(); }else{ this.AjaxInstance.set('html', this.options.text); } if(!this.options.effecting){ this.appear(); }else{ if(this.options.desktopUse){this.createLilWindow();} } }, appear: function(){ this.WindooMain.setStyle('visibility','visible'); saveHeight=this.WindooMain.getCoordinates().height.toInt(); saveWidth=this.WindooMain.getCoordinates().width.toInt(); saveBotHeight=this.FooterInstance.getCoordinates().height.toInt(); saveTopHeight=this.DragerInstance.getCoordinates().height.toInt(); this.BodyInstance.setStyles({ 'height':(saveHeight-saveTopHeight-10)+'px' }); this.AjaxInstance.setStyles({ 'height':(saveHeight-(saveBotHeight+saveTopHeight+10))+'px', 'width':(saveWidth-16)+'px' }); if(this.options.desktopUse){this.createLilWindow();} }, ajaxload: function(){ new Request.HTML({url:this.options.ajaxfile, method:'post',data:this.options.ajaxoption,update:this.AjaxInstance,evalScripts:true, onRequest:function(){ this.AjaxInstance.set('html','
'); }.bind(this), onFailure:function(){ this.AjaxInstance.set('html','Content konnte nicht geladen werden'); }.bind(this), onComplete:function(){ this.fireEvent('onAjaxComplete',this.options.onAjaxComplete); }.bind(this)}).send(); return this; }, showMe:function(){ if(!this.options.effecting){ if (!this.options.fixed && !this.options.ownPosition) { this.position(this.element); } if(!this.options.fixed && this.options.ownPosition){ this.ownposition(); } if(this.options.fixed && !this.options.ownPosition){ this.position(this.element); } this.show(); } if(this.options.effecting){ this.timer = $clear(this.timer); this.timer = this.getEffect.delay(this.options.timeIn, this); } }, disappear: function(){ if(this.options.modal==true){ this.divBG.destroy(); if(Browser.Engine.trident) { this.iframeBG.destroy(); } } this.WindooMain.destroy(); } });/** * Autocompleter * * http://digitarald.de/project/autocompleter/ * * @version 1.1.2 * * @license MIT-style license * @author Harald Kirschner * @copyright Author */ var Autocompleter = new Class({ Implements: [Options, Events], options: {/* onOver: $empty, onSelect: $empty, onSelection: $empty, onShow: $empty, onHide: $empty, onBlur: $empty, onFocus: $empty,*/ minLength: 1, markQuery: true, width: 'inherit', maxChoices: 10, injectChoice: null, customChoices: null, emptyChoices: null, visibleChoices: true, className: 'autocompleter-choices', zIndex: 42, delay: 400, observerOptions: {}, fxOptions: {}, autoSubmit: false, overflow: false, overflowMargin: 25, selectFirst: false, filter: null, filterCase: false, filterSubset: false, forceSelect: false, selectMode: true, choicesMatch: null, multiple: false, separator: ', ', separatorSplit: /\s*[,;]\s*/, autoTrim: false, allowDupes: false, cache: true, relative: false }, initialize: function(element, options) { this.element = $(element); this.setOptions(options); this.build(); this.observer = new Observer(this.element, this.prefetch.bind(this), $merge({ 'delay': this.options.delay }, this.options.observerOptions)); this.queryValue = null; if (this.options.filter) this.filter = this.options.filter.bind(this); var mode = this.options.selectMode; this.typeAhead = (mode == 'type-ahead'); this.selectMode = (mode === true) ? 'selection' : mode; this.cached = []; }, /** * build - Initialize DOM * * Builds the html structure for choices and appends the events to the element. * Override this function to modify the html generation. */ build: function() { if ($(this.options.customChoices)) { this.choices = this.options.customChoices; } else { this.choices = new Element('ul', { 'class': this.options.className, 'styles': { 'zIndex': this.options.zIndex } }).inject(document.body); this.relative = false; if (this.options.relative) { this.choices.inject(this.element, 'after'); this.relative = this.element.getOffsetParent(); } this.fix = new OverlayFix(this.choices); } if (!this.options.separator.test(this.options.separatorSplit)) { this.options.separatorSplit = this.options.separator; } this.fx = (!this.options.fxOptions) ? null : new Fx.Tween(this.choices, $merge({ 'property': 'opacity', 'link': 'cancel', 'duration': 200 }, this.options.fxOptions)).addEvent('onStart', Chain.prototype.clearChain).set(0); this.element.setProperty('autocomplete', 'off') .addEvent((Browser.Engine.trident || Browser.Engine.webkit) ? 'keydown' : 'keypress', this.onCommand.bind(this)) .addEvent('click', this.onCommand.bind(this, [false])) .addEvent('focus', this.toggleFocus.create({bind: this, arguments: true, delay: 100})) .addEvent('blur', this.toggleFocus.create({bind: this, arguments: false, delay: 100})); }, destroy: function() { if (this.fix) this.fix.destroy(); this.choices = this.selected = this.choices.destroy(); }, toggleFocus: function(state) { this.focussed = state; if (!state) this.hideChoices(true); this.fireEvent((state) ? 'onFocus' : 'onBlur', [this.element]); }, onCommand: function(e) { if (!e && this.focussed) return this.prefetch(); if (e && e.key && !e.shift) { switch (e.key) { case 'enter': if (this.element.value != this.opted) return true; if (this.selected && this.visible) { this.choiceSelect(this.selected); return !!(this.options.autoSubmit); } break; case 'up': case 'down': if (!this.prefetch() && this.queryValue !== null) { var up = (e.key == 'up'); this.choiceOver((this.selected || this.choices)[ (this.selected) ? ((up) ? 'getPrevious' : 'getNext') : ((up) ? 'getLast' : 'getFirst') ](this.options.choicesMatch), true); } return false; case 'esc': case 'tab': this.hideChoices(true); break; } } return true; }, setSelection: function(finish) { var input = this.selected.inputValue, value = input; var start = this.queryValue.length, end = input.length; if (input.substr(0, start).toLowerCase() != this.queryValue.toLowerCase()) start = 0; if (this.options.multiple) { var split = this.options.separatorSplit; value = this.element.value; start += this.queryIndex; end += this.queryIndex; var old = value.substr(this.queryIndex).split(split, 1)[0]; value = value.substr(0, this.queryIndex) + input + value.substr(this.queryIndex + old.length); if (finish) { var tokens = value.split(this.options.separatorSplit).filter(function(entry) { return this.test(entry); }, /[^\s,]+/); if (!this.options.allowDupes) tokens = [].combine(tokens); var sep = this.options.separator; value = tokens.join(sep) + sep; end = value.length; } } this.observer.setValue(value); this.opted = value; if (finish || this.selectMode == 'pick') start = end; this.element.selectRange(start, end); this.fireEvent('onSelection', [this.element, this.selected, value, input]); }, showChoices: function() { var match = this.options.choicesMatch, first = this.choices.getFirst(match); this.selected = this.selectedValue = null; if (this.fix) { var pos = this.element.getCoordinates(this.relative), width = this.options.width || 'auto'; this.choices.setStyles({ 'left': pos.left, 'top': pos.bottom, 'width': (width === true || width == 'inherit') ? pos.width : width }); } if (!first) return; if (!this.visible) { this.visible = true; this.choices.setStyle('display', ''); if (this.fx) this.fx.start(1); this.fireEvent('onShow', [this.element, this.choices]); } if (this.options.selectFirst || this.typeAhead || first.inputValue == this.queryValue) this.choiceOver(first, this.typeAhead); var items = this.choices.getChildren(match), max = this.options.maxChoices; var styles = {'overflowY': 'hidden', 'height': ''}; this.overflown = false; if (items.length > max) { var item = items[max - 1]; styles.overflowY = 'scroll'; styles.height = item.getCoordinates(this.choices).bottom; this.overflown = true; }; this.choices.setStyles(styles); this.fix.show(); if (this.options.visibleChoices) { var scroll = document.getScroll(), size = document.getSize(), coords = this.choices.getCoordinates(); if (coords.right > scroll.x + size.x) scroll.x = coords.right - size.x; if (coords.bottom > scroll.y + size.y) scroll.y = coords.bottom - size.y; window.scrollTo(Math.min(scroll.x, coords.left), Math.min(scroll.y, coords.top)); } }, hideChoices: function(clear) { if (clear) { var value = this.element.value; if (this.options.forceSelect) value = this.opted; if (this.options.autoTrim) { value = value.split(this.options.separatorSplit).filter($arguments(0)).join(this.options.separator); } this.observer.setValue(value); } if (!this.visible) return; this.visible = false; if (this.selected) this.selected.removeClass('autocompleter-selected'); this.observer.clear(); var hide = function(){ this.choices.setStyle('display', 'none'); this.fix.hide(); }.bind(this); if (this.fx) this.fx.start(0).chain(hide); else hide(); this.fireEvent('onHide', [this.element, this.choices]); }, prefetch: function() { var value = this.element.value, query = value; if (this.options.multiple) { var split = this.options.separatorSplit; var values = value.split(split); var index = this.element.getSelectedRange().start; var toIndex = value.substr(0, index).split(split); var last = toIndex.length - 1; index -= toIndex[last].length; query = values[last]; } if (query.length < this.options.minLength) { this.hideChoices(); } else { if (query === this.queryValue || (this.visible && query == this.selectedValue)) { if (this.visible) return false; this.showChoices(); } else { this.queryValue = query; this.queryIndex = index; if (!this.fetchCached()) this.query(); } } return true; }, fetchCached: function() { return false; if (!this.options.cache || !this.cached || !this.cached.length || this.cached.length >= this.options.maxChoices || this.queryValue) return false; this.update(this.filter(this.cached)); return true; }, update: function(tokens) { this.choices.empty(); this.cached = tokens; var type = tokens && $type(tokens); if (!type || (type == 'array' && !tokens.length) || (type == 'hash' && !tokens.getLength())) { (this.options.emptyChoices || this.hideChoices).call(this); } else { if (this.options.maxChoices < tokens.length && !this.options.overflow) tokens.length = this.options.maxChoices; tokens.each(this.options.injectChoice || function(token){ var choice = new Element('li', {'html': this.markQueryValue(token)}); choice.inputValue = token; this.addChoiceEvents(choice).inject(this.choices); }, this); this.showChoices(); } }, choiceOver: function(choice, selection) { if (!choice || choice == this.selected) return; if (this.selected) this.selected.removeClass('autocompleter-selected'); this.selected = choice.addClass('autocompleter-selected'); this.fireEvent('onSelect', [this.element, this.selected, selection]); if (!this.selectMode) this.opted = this.element.value; if (!selection) return; this.selectedValue = this.selected.inputValue; if (this.overflown) { var coords = this.selected.getCoordinates(this.choices), margin = this.options.overflowMargin, top = this.choices.scrollTop, height = this.choices.offsetHeight, bottom = top + height; if (coords.top - margin < top && top) this.choices.scrollTop = Math.max(coords.top - margin, 0); else if (coords.bottom + margin > bottom) this.choices.scrollTop = Math.min(coords.bottom - height + margin, bottom); } if (this.selectMode) this.setSelection(); }, choiceSelect: function(choice) { if (choice) this.choiceOver(choice); this.setSelection(true); this.queryValue = false; this.hideChoices(); }, filter: function(tokens) { return (tokens || this.tokens).filter(function(token) { return this.test(token); }, new RegExp(((this.options.filterSubset) ? '' : '^') + this.queryValue.escapeRegExp(), (this.options.filterCase) ? '' : 'i')); }, /** * markQueryValue * * Marks the queried word in the given string with * * Call this i.e. from your custom parseChoices, same for addChoiceEvents * * @param {String} Text * @return {String} Text */ markQueryValue: function(str) { return (!this.options.markQuery || !this.queryValue) ? str : str.replace(new RegExp('(' + ((this.options.filterSubset) ? '' : '^') + this.queryValue.escapeRegExp() + ')', (this.options.filterCase) ? '' : 'i'), '$1'); }, /** * addChoiceEvents * * Appends the needed event handlers for a choice-entry to the given element. * * @param {Element} Choice entry * @return {Element} Choice entry */ addChoiceEvents: function(el) { return el.addEvents({ 'mouseover': this.choiceOver.bind(this, [el]), 'click': this.choiceSelect.bind(this, [el]) }); } }); var OverlayFix = new Class({ initialize: function(el) { if (Browser.Engine.trident) { this.element = $(el); this.relative = this.element.getOffsetParent(); this.fix = new Element('iframe', { 'frameborder': '0', 'scrolling': 'no', 'src': 'javascript:false;', 'styles': { 'position': 'absolute', 'border': 'none', 'display': 'none', 'filter': 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)' } }).inject(this.element, 'after'); } }, show: function() { if (this.fix) { var coords = this.element.getCoordinates(this.relative); delete coords.right; delete coords.bottom; this.fix.setStyles($extend(coords, { 'display': '', 'zIndex': (this.element.getStyle('zIndex') || 1) - 1 })); } return this; }, hide: function() { if (this.fix) this.fix.setStyle('display', 'none'); return this; }, destroy: function() { if (this.fix) this.fix = this.fix.destroy(); } }); Element.implement({ getSelectedRange: function() { if (!Browser.Engine.trident) return {start: this.selectionStart, end: this.selectionEnd}; var pos = {start: 0, end: 0}; var range = this.getDocument().selection.createRange(); if (!range || range.parentElement() != this) return pos; var dup = range.duplicate(); if (this.type == 'text') { pos.start = 0 - dup.moveStart('character', -100000); pos.end = pos.start + range.text.length; } else { var value = this.value; var offset = value.length - value.match(/[\n\r]*$/)[0].length; dup.moveToElementText(this); dup.setEndPoint('StartToEnd', range); pos.end = offset - dup.text.length; dup.setEndPoint('StartToStart', range); pos.start = offset - dup.text.length; } return pos; }, selectRange: function(start, end) { if (Browser.Engine.trident) { var diff = this.value.substr(start, end - start).replace(/\r/g, '').length; start = this.value.substr(0, start).replace(/\r/g, '').length; var range = this.createTextRange(); range.collapse(true); range.moveEnd('character', start + diff); range.moveStart('character', start); range.select(); } else { this.focus(); this.setSelectionRange(start, end); } return this; } }); /* compatibility */ Autocompleter.Base = Autocompleter;/** * Autocompleter.Request * * http://digitarald.de/project/autocompleter/ * * @version 1.1.2 * * @license MIT-style license * @author Harald Kirschner * @copyright Author */ Autocompleter.Request = new Class({ Extends: Autocompleter, options: {/* indicator: null, indicatorClass: null, onRequest: $empty, onComplete: $empty,*/ postData: {}, ajaxOptions: {}, postVar: 'value' }, query: function(){ var data = $unlink(this.options.postData) || {}; data[this.options.postVar] = this.queryValue; var indicator = $(this.options.indicator); if (indicator) indicator.setStyle('display', ''); var cls = this.options.indicatorClass; if (cls) this.element.addClass(cls); this.fireEvent('onRequest', [this.element, this.request, data, this.queryValue]); this.request.send({'data': data}); }, /** * queryResponse - abstract * * Inherated classes have to extend this function and use this.parent() */ queryResponse: function() { var indicator = $(this.options.indicator); if (indicator) indicator.setStyle('display', 'none'); var cls = this.options.indicatorClass; if (cls) this.element.removeClass(cls); return this.fireEvent('onComplete', [this.element, this.request]); } }); Autocompleter.Request.JSON = new Class({ Extends: Autocompleter.Request, initialize: function(el, url, options) { this.parent(el, options); this.request = new Request.JSON($merge({ 'url': url, 'link': 'cancel' }, this.options.ajaxOptions)).addEvent('onComplete', this.queryResponse.bind(this)); }, queryResponse: function(response) { this.parent(); this.update(response); } }); Autocompleter.Request.HTML = new Class({ Extends: Autocompleter.Request, initialize: function(el, url, options) { this.parent(el, options); this.request = new Request.HTML($merge({ 'url': url, 'link': 'cancel', 'update': this.choices }, this.options.ajaxOptions)).addEvent('onComplete', this.queryResponse.bind(this)); }, queryResponse: function(tree, elements) { this.parent(); if (!elements || !elements.length) { this.hideChoices(); } else { this.choices.getChildren(this.options.choicesMatch).each(this.options.injectChoice || function(choice) { var value = choice.innerHTML; choice.inputValue = value; this.addChoiceEvents(choice.set('html', this.markQueryValue(value))); }, this); this.showChoices(); } } }); /* compatibility */ Autocompleter.Ajax = { Base: Autocompleter.Request, Json: Autocompleter.Request.JSON, Xhtml: Autocompleter.Request.HTML }; /** * Observer - Observe formelements for changes * * - Additional code from clientside.cnet.com * * @version 1.1 * * @license MIT-style license * @author Harald Kirschner * @copyright Author */ var Observer = new Class({ Implements: [Options, Events], options: { periodical: false, delay: 1000 }, initialize: function(el, onFired, options){ this.element = $(el) || $$(el); this.addEvent('onFired', onFired); this.setOptions(options); this.bound = this.changed.bind(this); this.resume(); }, changed: function() { var value = this.element.get('value'); if ($equals(this.value, value)) return; this.clear(); this.value = value; this.timeout = this.onFired.delay(this.options.delay, this); }, setValue: function(value) { this.value = value; this.element.set('value', value); return this.clear(); }, onFired: function() { this.fireEvent('onFired', [this.value, this.element]); }, clear: function() { $clear(this.timeout || null); return this; }, pause: function(){ if (this.timer) $clear(this.timer); else this.element.removeEvent('keyup', this.bound); return this.clear(); }, resume: function(){ this.value = this.element.get('value'); if (this.options.periodical) this.timer = this.changed.periodical(this.options.periodical, this); else this.element.addEvent('keyup', this.bound); return this; } }); var $equals = function(obj1, obj2) { return (obj1 == obj2 || JSON.encode(obj1) == JSON.encode(obj2)); };var UI = {}; /* Script: UI.Menu.js Class: UI.Menu Creates a menu Syntax: >var myMenu = new UI.Menu( [options] ); Arguments: options - (object, optional) An object with options for the menu. See below. options (continued): event - (string: defaults to 'leftClick') The mouse event : leftClick or rightClick or mouseOver container - (string: defaults to false (equal to document.body)) The container for the menu element clsPrefix - (string: defaults to 'ui-') The class prefix for CSS modifications position - (string or array: defaults to 'mouse') The position of the menu blankImg - (string) Path to a blank img (like s.gif) autoHideDelay - (number: defaults to 2000) Time in ms before the menu hides when the mouse leaves the menu. subMenu - (boolean: defaults to false) True if the menu is a submenu parentMenu - (string: defaults to false) ID of the parent menu. id - (string: defaults to Native.UID++) The id of the menu element. Returns: (class) A new UI.Menu class instance. Example: [javascript] var myMenu = new UI.Menu(); [/javascript] */ UI.Menu = new Class ( { Implements : [ Options ], options : { event : 'leftClick' // leftClick || rightClick || mouseOver , id : null , container : null , clsPrefix : 'ui-' , position : 'mouse' // mouse || [ xPos( 'right' || 'left' ) , yPos( 'top' || 'bottom' ) ] , blankImg : 'menu/s.gif' , autoHideDelay : 2000 , subMenu : false , parentMenu : false }, initialize : function ( trigger, options ) { this.setOptions( options ); this.items = []; this.separators = []; this.submenus = []; this.trigger = $( trigger ); this.container = $pick( this.options.container, document.body ); this.id = $pick( this.options.id, 'menu-' + (Native.UID++) ); this.menu = new Element( 'ul', { id : this.id, 'class' : this.options.clsPrefix + 'menu '+ this.options.clsPrefix+'menu-invisible' } ).inject( this.container, 'inside' ); $(this.id).store( 'menu-obj', this ); this.onClickHideMenu = this.hideMenu.bind( this ); if ( !this.options.subMenu ) { document.addEvent( 'mousedown', function( ev ) { ev = new Event(ev); if (ev.target.id != this.id && !ev.target.getParents().contains( this.menu ) ) this.hideMenu(); }.bind( this ) ); }; if ( this.options.subMenu ) { this.menu.addEvents( { 'mouseenter' : function() { var parentMenuObj = $( this.options.parentMenu ).retrieve( 'menu-obj' ); if ( parentMenuObj.getItemStatus( this.trigger.get( 'id' ) ) ) { this.trigger.addClass( 'over'); if ( parentMenuObj.timeOut ) $clear( parentMenuObj.timeOut ); }; }.bind(this) } ); }; if ( this.options.autoHideDelay ) { this.menu.addEvents( { 'mouseleave' : function() { this.timeOut = this.hideMenu.delay( this.options.autoHideDelay, this ); }.bind(this) , 'mouseenter' : function() { $clear( this.timeOut ); }.bind(this) } ); }; if ( [ 'rightClick', 'leftClick' ].contains( this.options.event ) ) { this.trigger.addEvent( 'mouseup', function(event) { if ( ( this.options.event == 'rightClick' && event.rightClick ) || ( this.options.event == 'leftClick' && !event.rightClick ) ) this.showMenu( event ); }.bind( this ).bind( this.trigger ) ); }; if ( this.options.event == 'mouseOver' ) { this.trigger.addEvent( 'mouseover', function(event) { if ( !this.options.subMenu || ( this.options.subMenu && $( this.options.parentMenu ).retrieve( 'menu-obj' ).getItemStatus( this.trigger.get( 'id' ) ) ) ) this.showMenu.delay( 15, this, event ); }.bind( this ) ); }; this.hideMenu(); }, /* Method: addItem Adds an item to the menu Syntax: >myMenu.addItem( options ); Arguments: options - (object, optional) An object with options for the item. See below. options (continued): id - (string: defaults to Native.UID++) The id of the item element. label - (string: defaults to false) The label of the item. icon - (string: defaults to false) The path to the icon of the item. onclick - (function: false) The function to execute when user clicks on the item styles - (object: defaults to false) An object containing all the styles to apply. status - (boolean: defaults to true) True if the item is enable. separator - (boolean: defaults to false) If true, adds a separator to the menu and ignores others options. Examples: [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addItem( { id : 'button1', label : 'Demo Button', icon : 'images/demo.png', onclick : function() { alert('demo button clicked') } } ); [/javascript] [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addItem( { separator : true } ); // Adds a separator [/javascript] */ addItem: function( item ) { item = $merge( { separator : false , status : true , onclick : false , id : null }, item ); if ( item.separator) { this.addSeparator(); } else { item.id = $pick( item.id, 'ui-menu-item-' + Native.UID++ ); this.items.push( item.id ); this.items[ item.id ] = item; this.element = new Element( 'li', { id : item.id, 'class' : this.options.clsPrefix + 'menu-item' } ); this.element.adopt( new Element( 'span', { 'class' : this.options.clsPrefix + 'menu-label' } ).adopt( new Element( 'img' ) ) ); this.element.getElement( 'span' ).appendText( item.label ); this.element.getElement( 'img' ).set( 'src', ( item.icon ? item.icon : this.options.blankImg ) ); this.element.addEvents( { 'mouseenter' : function( el, menu ) { if ( menu.getItemStatus( el.get( 'id' ) ) ) { menu.hideSubMenus(); menu.unselectItems(); el.addClass( 'over'); }; }.pass( [this.element, this] ) , 'mouseleave' : function( el, menu ) { if ( menu.getItemStatus( el.get( 'id' ) ) ) { el.removeClass( 'over'); } }.pass( [this.element, this] ) , 'click' : function( el, menu ) { if ( menu.getItemStatus( el.get( 'id' ) ) ) { menu.onClickHideMenu(); } }.pass( [this.element, this] ) } ); this.element.addEvent( 'click', function( el, menu ) { if ( menu.getItemStatus( el.get( 'id' ) ) && menu.items[ el.get( 'id' ) ].onclick ) { menu.items[ el.get( 'id' ) ].onclick.bind(menu)(); } }.pass( [this.element, this] ) ); if ( item.styles ) this.element.setStyles( item.styles ); this.element.inject( this.menu, 'inside' ); this.items[ item.id ].element = this.element; if ( Browser.Engine.trident ) { // Resizes items and separators width if browser = IE var maxWidth = 0; this.items.forEach( function( item ) { maxWidth = ( maxWidth < this.items[ item ].element.getSize().x ) ? this.items[ item ].element.getSize().x : maxWidth; }.bind(this) ); this.items.forEach( function( item ) { this.items[ item ].element.setStyle( 'width', maxWidth ); }.bind(this) ); this.separators.forEach( function( item ) { item.element.setStyle( 'width', maxWidth ); }.bind(this) ); }; delete this.element; }; }, /* Method: updateItemOnclick Updates an item onclick function Syntax: >myMenu.updateItemOnclick( el, onclick ); Arguments: el - (string or number) ID or index of the item in the menu onclick - (function) Function to execute when the user clicks on the item Example: [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addItem( { id : 'button1', label : 'Demo Button', icon : 'images/demo.png', onclick : function() { alert('demo button clicked') } } ); myMenu.updateItemOnclick( 'button1', function() { alert( 'new function' ); } ); [/javascript] */ updateItemOnclick: function( ref, onclick ) { var id = ( $type( ref ) == 'string' ) ? ref : this.menu.getElements( '.' + this.options.clsPrefix + 'menu-item' )[ref].get( 'id' ); this.items[ id ].onclick = onclick; }, /* Method: addItems Adds items to the menu Syntax: >myMenu.addItems( [ options[, options, ...] ] ); Arguments: options - (object, optional) An object with options for the item. See addItem options. Example: [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addItems( [ { id : 'button1', label : 'Demo Button 1', icon : 'images/demo1.png', onclick : function() { alert('demo button 1 clicked') } } , { id : 'button2', label : 'Demo Button 2', icon : 'images/demo2.png', onclick : function() { alert('demo button 2 clicked') } } ] ); [/javascript] */ addItems: function( items ) { if ( items.length > 0 ) { $each( items, function( item ) { this.addItem( item ); }, this ); }; }, /* Method: getItem Returns an item element Syntax: >myMenu.getItem(0); Example: [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addItem( { id : 'button1', label : 'Demo Button 1', icon : 'images/demo1.png', onclick : function() { alert('demo button 1 clicked') } ); myMenu.getItem(0); [/javascript] */ getItem: function( index ) { var id = ( $type( ref ) == 'string' ) ? ref : this.menu.getElements( '.' + this.options.clsPrefix + 'menu-item' )[ref].get( 'id' ); return $( id ); }, /* Method: getItemStatus Returns item status Syntax: >myMenu.getItemStatus( ref ); Arguments: ref - (string or number) ID or index of the item in the menu Returns: (boolean) True if the item is enable. Example: [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addItems( [ { id : 'button1', label : 'Demo Button 1', icon : 'images/demo1.png', onclick : function() { alert('demo button 1 clicked') } } , { id : 'button2', label : 'Demo Button 2', icon : 'images/demo2.png', onclick : function() { alert('demo button 2 clicked') } } ] ); alert( myMenu.getItemStatus( 'button2' ) ); // Returns true [/javascript] */ getItemStatus: function( ref ) { var id = ( $type( ref ) == 'string' ) ? ref : this.menu.getElements( '.' + this.options.clsPrefix + 'menu-item' )[ref].get( 'id' ); return this.items[ id ].status; }, /* Method: removeItem Removes an item from the menu Syntax: >myMenu.removeItem( ref ); Arguments: ref - (string or number) ID or index of the item in the menu Example: [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addItem( { id : 'button1', label : 'Demo Button', icon : 'images/demo.png', onclick : function() { alert('demo button clicked') } } ); myMenu.removeItem( 'button1' ); // or myMenu.removeItem( 0 ); [/javascript] */ removeItem: function( ref ) { var id = ( $type( ref ) == 'string' ) ? ref : this.menu.getElements( '.' + this.options.clsPrefix + 'menu-item' )[ref].get( 'id' ); this.items[ id ].element.destroy(); this.items.remove( id ); }, /* Method: disableItem Disables an item Syntax: >myMenu.disableItem( ref ); Arguments: ref - (string or number) ID or index of the item in the menu Example: [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addItem( { id : 'button1', label : 'Demo Button', icon : 'images/demo.png', onclick : function() { alert('demo button clicked') } } ); myMenu.disableItem( 'button1' ); // or myMenu.disableItem( 0 ); [/javascript] */ disableItem: function( ref ) { var id = ( $type( ref ) == 'string' ) ? ref : this.menu.getElements( '.' + this.options.clsPrefix + 'menu-item' )[ref].get( 'id' ); if ( this.getItemStatus( id ) ) { this.items[ id ].element.addClass( 'disable' ).set( 'opacity', 0.4 ); this.items[ id ].status = false; }; }, /* Method: enableItem Enables an item Syntax: >myMenu.enableItem( ref ); Arguments: ref - (string or number) ID or index of the item in the menu Example: [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addItem( { id : 'button1', label : 'Demo Button', icon : 'images/demo.png', onclick : function() { alert('demo button clicked') } } ); myMenu.enableItem( 'button1' ); // or myMenu.enableItem( 0 ); [/javascript] */ enableItem: function( ref ) { var id = ( $type( ref ) == 'string' ) ? ref : this.menu.getElements( '.' + this.options.clsPrefix + 'menu-item' )[ref].get( 'id' ); if ( !this.getItemStatus( id ) ) { this.items[ id ].element.removeClass( 'disable' ).set( 'opacity', 1 ); this.items[ id ].status = true; }; }, /* Method: toggleItem Toggle an item status (enable/disable) Syntax: >myMenu.toggleItem( ref ); Arguments: ref - (string or number) ID or index of the item in the menu Example: [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addItem( { id : 'button1', label : 'Demo Button', icon : 'images/demo.png', onclick : function() { alert('demo button clicked') } } ); myMenu.toggleItem( 'button1' ); // or myMenu.toggleItem( 0 ); [/javascript] */ toggleItem: function( ref ) { var id = ( $type( ref ) == 'string' ) ? ref : this.menu.getElements( '.' + this.options.clsPrefix + 'menu-item' )[ref].get( 'id' ); if ( this.items[ id ].element.hasClass( 'disable' ) ) this.enableItem( id ); else this.disableItem( id ); }, /* Method: addSeparator Adds a separator line to the menu Syntax: >myMenu.addSeparator(); Example: [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addSeparator(); [/javascript] */ addSeparator: function() { this.element = new Element( 'li', { 'class' : this.options.clsPrefix + 'menu-sep' } ); this.element.adopt( new Element( 'span' ) ); this.element.inject( this.menu, 'inside' ); var index = this.separators.length == 0 ? 0 : this.separators.length + 1; this.separators.push( this.separators.length ); this.separators[ index ] = {}; this.separators[ index ].element = this.element; delete this.element; }, /* Method: removeSeparator Removes a separator line to the menu Syntax: >myMenu.removeSeparator(); Example: [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addSeparator(); myMenu.removeSeparator(); [/javascript] */ removeSeparator: function( index ) { this.separators[ index ].element.destroy(); this.separators.remove( index ); }, /* Method: getSeparator Returns a separator line to the menu Syntax: >myMenu.getSeparator(); Example: [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addSeparator(); myMenu.getSeparator(0); [/javascript] */ getSeparator: function( index ) { return this.menu.getElements( '.' + this.options.clsPrefix + 'menu-sep' )[index]; }, /* Method: addSubMenu Adds a sub menu to an item Syntax: >myMenu.addSubMenu( ref[, options ] ); Arguments: ref - (string or number) ID or index of the item in the menu options - (object, optional) Options for the sub menu. See the class options. Returns: (class) A new UI.Menu class instance. Example: [javascript] var myMenu = new UI.Menu( document.body ); myMenu.addItems( [ { id : 'button1', label : 'Demo Button 1', icon : 'images/demo1.png' } , { id : 'button2', label : 'Demo Button 2', icon : 'images/demo2.png', onclick : function() { alert('demo button 2 clicked') } } ] ); var mySubMenu = myMenu.addSubMenu( 'button1' ); mySubMenu.addItem( { id : 'button11', label : 'Demo Sub Button', icon : 'images/demo.png', onclick : function() { alert('sub menu button clicked') } } ); [/javascript] */ addSubMenu: function( ref, options ) { var id = ( $type( ref ) == 'string' ) ? ref : this.menu.getElements( '.' + this.options.clsPrefix + 'menu-item' )[ref].get( 'id' ); this.items[ id ].element.getElement( 'span' ).addClass( 'arrow' ); this.items[ id ].element.removeEvent( 'click', this.onClickHideMenu ); this.submenus.push( id ); this.submenus[ id ] = {}; this.submenus[ id ].obj = new UI.Menu( this.items[ id ].element, $merge( { position : ['top', 'right'], subMenu : true, parentMenu : this.id, event : 'mouseOver' }, options ) ); return this.submenus[ id ].obj; }, showMenu: function ( event ) { this.hideMenu(); if ( $type( this.options.position ) == 'string' && this.options.position == 'mouse' ) { var styleX = window.getWidth() - event.client.x < this.menu.getSize().x ? { 'left' : event.client.x - this.menu.getSize().x } : { 'left' : event.client.x }; var styleY = window.getHeight() - event.client.y < this.menu.getSize().y ? { 'top' : event.client.y - this.menu.getSize().y } : { 'top' : event.client.y }; } else { if ( this.options.position.contains( 'right' ) ) { var styleX = window.getWidth() - this.trigger.getSize().x < this.menu.getSize().x ? { 'left' : this.trigger.getCoordinates().left - this.menu.getSize().x } : { 'left' : this.trigger.getCoordinates().right }; }; if ( this.options.position.contains( 'left' ) ) { var styleX = window.getWidth() - this.trigger.getSize().x < this.menu.getSize().x ? { 'left' : this.trigger.getCoordinates().left - this.menu.getSize().x } : { 'left' : this.trigger.getCoordinates().left }; }; if ( this.options.position.contains( 'top' ) ) { var styleY = window.getHeight() - this.trigger.getSize().y < this.menu.getSize().y ? { 'top' : this.trigger.getCoordinates().top - this.menu.getSize().y } : { 'top' : this.trigger.getCoordinates().top }; }; if ( this.options.position.contains( 'bottom' ) ) { var styleY = window.getHeight() - this.trigger.getSize().y < this.menu.getSize().y ? { 'top' : this.trigger.getCoordinates().top + this.menu.getSize().y } : { 'top' : this.trigger.getCoordinates().bottom }; }; }; this.menu.addClass( this.options.clsPrefix + 'menu-visible' ).setStyles( $merge( styleX, styleY ) ); this.menu.removeClass( this.options.clsPrefix + 'menu-invisible' ); }, hideMenu: function () { this.hideSubMenus(); this.menu.removeClass( this.options.clsPrefix + 'menu-visible' ).setStyle( 'left', '-600px' ); this.menu.addClass( this.options.clsPrefix + 'menu-invisible' ); }, hideSubMenus: function () { this.submenus.forEach( function( submenu ) { this.submenus[ submenu ].obj.hideMenu(); this.submenus[ submenu ].obj.unselectItems(); }.bind(this) ); }, unselectItems: function() { this.items.forEach( function( item ) { this.items[ item ].element.removeClass( 'over' ); }.bind(this) ); } } );