6 description: The heart of MooTools.
8 license: MIT-style license.
10 copyright: Copyright (c) 2006-2014 [Valerio Proietti](http://mad4milk.net/).
12 authors: The MooTools production team (http://mootools.net/developers/)
15 - 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)
16 - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
18 provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
32 var typeOf = this.typeOf = function(item){
33 if (item == null) return 'null';
34 if (item.$family != null) return item.$family();
37 if (item.nodeType == 1) return 'element';
38 if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
39 } else if (typeof item.length == 'number'){
40 if ('callee' in item) return 'arguments';
41 if ('item' in item) return 'collection';
47 var instanceOf = this.instanceOf = function(item, object){
48 if (item == null) return false;
49 var constructor = item.$constructor || item.constructor;
51 if (constructor === object) return true;
52 constructor = constructor.parent;
55 if (!item.hasOwnProperty) return false;
57 return item instanceof object;
60 // Function overloading
62 var Function = this.Function;
64 var enumerables = true;
65 for (var i in {toString: 1}) enumerables = null;
66 if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
68 Function.prototype.overloadSetter = function(usePlural){
70 return function(a, b){
71 if (a == null) return this;
72 if (usePlural || typeof a != 'string'){
73 for (var k in a) self.call(this, k, a[k]);
74 if (enumerables) for (var i = enumerables.length; i--;){
76 if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
79 self.call(this, a, b);
85 Function.prototype.overloadGetter = function(usePlural){
89 if (typeof a != 'string') args = a;
90 else if (arguments.length > 1) args = arguments;
91 else if (usePlural) args = [a];
94 for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
96 result = self.call(this, a);
102 Function.prototype.extend = function(key, value){
106 Function.prototype.implement = function(key, value){
107 this.prototype[key] = value;
112 var slice = Array.prototype.slice;
114 Function.from = function(item){
115 return (typeOf(item) == 'function') ? item : function(){
120 Array.from = function(item){
121 if (item == null) return [];
122 return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
125 Number.from = function(item){
126 var number = parseFloat(item);
127 return isFinite(number) ? number : null;
130 String.from = function(item){
144 this.$protected = true;
152 var Type = this.Type = function(name, object){
154 var lower = name.toLowerCase();
155 var typeCheck = function(item){
156 return (typeOf(item) == lower);
159 Type['is' + name] = typeCheck;
161 object.prototype.$family = (function(){
165 object.type = typeCheck;
170 if (object == null) return null;
173 object.$constructor = Type;
174 object.prototype.$constructor = object;
179 var toString = Object.prototype.toString;
181 Type.isEnumerable = function(item){
182 return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
187 var hooksOf = function(object){
188 var type = typeOf(object.prototype);
189 return hooks[type] || (hooks[type] = []);
192 var implement = function(name, method){
193 if (method && method.$hidden) return;
195 var hooks = hooksOf(this);
197 for (var i = 0; i < hooks.length; i++){
199 if (typeOf(hook) == 'type') implement.call(hook, name, method);
200 else hook.call(this, name, method);
203 var previous = this.prototype[name];
204 if (previous == null || !previous.$protected) this.prototype[name] = method;
206 if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
207 return method.apply(item, slice.call(arguments, 1));
211 var extend = function(name, method){
212 if (method && method.$hidden) return;
213 var previous = this[name];
214 if (previous == null || !previous.$protected) this[name] = method;
219 implement: implement.overloadSetter(),
221 extend: extend.overloadSetter(),
223 alias: function(name, existing){
224 implement.call(this, name, this.prototype[existing]);
227 mirror: function(hook){
228 hooksOf(this).push(hook);
234 new Type('Type', Type);
238 var force = function(name, object, methods){
239 var isType = (object != Object),
240 prototype = object.prototype;
242 if (isType) object = new Type(name, object);
244 for (var i = 0, l = methods.length; i < l; i++){
245 var key = methods[i],
246 generic = object[key],
247 proto = prototype[key];
249 if (generic) generic.protect();
250 if (isType && proto) object.implement(key, proto.protect());
254 var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]);
255 object.forEachMethod = function(fn){
256 if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
257 fn.call(prototype, prototype[methods[i]], methods[i]);
259 for (var key in prototype) fn.call(prototype, prototype[key], key);
266 force('String', String, [
267 'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
268 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
270 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
271 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
272 ])('Number', Number, [
273 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
274 ])('Function', Function, [
275 'apply', 'call', 'bind'
276 ])('RegExp', RegExp, [
278 ])('Object', Object, [
279 'create', 'defineProperty', 'defineProperties', 'keys',
280 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
281 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
282 ])('Date', Date, ['now']);
284 Object.extend = extend.overloadSetter();
286 Date.extend('now', function(){
290 new Type('Boolean', Boolean);
292 // fixes NaN returning as Number
294 Number.prototype.$family = function(){
295 return isFinite(this) ? 'number' : 'null';
300 Number.extend('random', function(min, max){
301 return Math.floor(Math.random() * (max - min + 1) + min);
306 var hasOwnProperty = Object.prototype.hasOwnProperty;
307 Object.extend('forEach', function(object, fn, bind){
308 for (var key in object){
309 if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
313 Object.each = Object.forEach;
318 forEach: function(fn, bind){
319 for (var i = 0, l = this.length; i < l; i++){
320 if (i in this) fn.call(bind, this[i], i, this);
325 each: function(fn, bind){
326 Array.forEach(this, fn, bind);
332 // Array & Object cloning, Object merging and appending
334 var cloneOf = function(item){
335 switch (typeOf(item)){
336 case 'array': return item.clone();
337 case 'object': return Object.clone(item);
338 default: return item;
342 Array.implement('clone', function(){
343 var i = this.length, clone = new Array(i);
344 while (i--) clone[i] = cloneOf(this[i]);
348 var mergeOne = function(source, key, current){
349 switch (typeOf(current)){
351 if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
352 else source[key] = Object.clone(current);
354 case 'array': source[key] = current.clone(); break;
355 default: source[key] = current;
362 merge: function(source, k, v){
363 if (typeOf(k) == 'string') return mergeOne(source, k, v);
364 for (var i = 1, l = arguments.length; i < l; i++){
365 var object = arguments[i];
366 for (var key in object) mergeOne(source, key, object[key]);
371 clone: function(object){
373 for (var key in object) clone[key] = cloneOf(object[key]);
377 append: function(original){
378 for (var i = 1, l = arguments.length; i < l; i++){
379 var extended = arguments[i] || {};
380 for (var key in extended) original[key] = extended[key];
389 ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
395 var UID = Date.now();
397 String.extend('uniqueID', function(){
398 return (UID++).toString(36);
403 var Hash = this.Hash = new Type('Hash', function(object){
404 if (typeOf(object) == 'hash') object = Object.clone(object.getClean());
405 for (var key in object) this[key] = object[key];
411 forEach: function(fn, bind){
412 Object.forEach(this, fn, bind);
415 getClean: function(){
417 for (var key in this){
418 if (this.hasOwnProperty(key)) clean[key] = this[key];
423 getLength: function(){
425 for (var key in this){
426 if (this.hasOwnProperty(key)) length++;
433 Hash.alias('each', 'forEach');
435 Object.type = Type.isObject;
437 var Native = this.Native = function(properties){
438 return new Type(properties.name, properties.initialize);
441 Native.type = Type.type;
443 Native.implement = function(objects, methods){
444 for (var i = 0; i < objects.length; i++) objects[i].implement(methods);
448 var arrayType = Array.type;
449 Array.type = function(item){
450 return instanceOf(item, Array) || arrayType(item);
453 this.$A = function(item){
454 return Array.from(item).slice();
457 this.$arguments = function(i){
463 this.$chk = function(obj){
464 return !!(obj || obj === 0);
467 this.$clear = function(timer){
469 clearInterval(timer);
473 this.$defined = function(obj){
474 return (obj != null);
477 this.$each = function(iterable, fn, bind){
478 var type = typeOf(iterable);
479 ((type == 'arguments' || type == 'collection' || type == 'array' || type == 'elements') ? Array : Object).each(iterable, fn, bind);
482 this.$empty = function(){};
484 this.$extend = function(original, extended){
485 return Object.append(original, extended);
488 this.$H = function(object){
489 return new Hash(object);
492 this.$merge = function(){
493 var args = Array.slice(arguments);
495 return Object.merge.apply(null, args);
498 this.$lambda = Function.from;
499 this.$mixin = Object.merge;
500 this.$random = Number.random;
501 this.$splat = Array.from;
502 this.$time = Date.now;
504 this.$type = function(object){
505 var type = typeOf(object);
506 if (type == 'elements') return 'array';
507 return (type == 'null') ? false : type;
510 this.$unlink = function(object){
511 switch (typeOf(object)){
512 case 'object': return Object.clone(object);
513 case 'array': return Array.clone(object);
514 case 'hash': return new Hash(object);
515 default: return object;
528 description: Contains Array Prototypes like each, contains, and erase.
530 license: MIT-style license.
542 every: function(fn, bind){
543 for (var i = 0, l = this.length >>> 0; i < l; i++){
544 if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
549 filter: function(fn, bind){
551 for (var value, i = 0, l = this.length >>> 0; i < l; i++) if (i in this){
553 if (fn.call(bind, value, i, this)) results.push(value);
558 indexOf: function(item, from){
559 var length = this.length >>> 0;
560 for (var i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){
561 if (this[i] === item) return i;
566 map: function(fn, bind){
567 var length = this.length >>> 0, results = Array(length);
568 for (var i = 0; i < length; i++){
569 if (i in this) results[i] = fn.call(bind, this[i], i, this);
574 some: function(fn, bind){
575 for (var i = 0, l = this.length >>> 0; i < l; i++){
576 if ((i in this) && fn.call(bind, this[i], i, this)) return true;
583 return this.filter(function(item){
588 invoke: function(methodName){
589 var args = Array.slice(arguments, 1);
590 return this.map(function(item){
591 return item[methodName].apply(item, args);
595 associate: function(keys){
596 var obj = {}, length = Math.min(this.length, keys.length);
597 for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
601 link: function(object){
603 for (var i = 0, l = this.length; i < l; i++){
604 for (var key in object){
605 if (object[key](this[i])){
606 result[key] = this[i];
615 contains: function(item, from){
616 return this.indexOf(item, from) != -1;
619 append: function(array){
620 this.push.apply(this, array);
625 return (this.length) ? this[this.length - 1] : null;
628 getRandom: function(){
629 return (this.length) ? this[Number.random(0, this.length - 1)] : null;
632 include: function(item){
633 if (!this.contains(item)) this.push(item);
637 combine: function(array){
638 for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
642 erase: function(item){
643 for (var i = this.length; i--;){
644 if (this[i] === item) this.splice(i, 1);
656 for (var i = 0, l = this.length; i < l; i++){
657 var type = typeOf(this[i]);
658 if (type == 'null') continue;
659 array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
665 for (var i = 0, l = this.length; i < l; i++){
666 if (this[i] != null) return this[i];
671 hexToRgb: function(array){
672 if (this.length != 3) return null;
673 var rgb = this.map(function(value){
674 if (value.length == 1) value += value;
675 return parseInt(value, 16);
677 return (array) ? rgb : 'rgb(' + rgb + ')';
680 rgbToHex: function(array){
681 if (this.length < 3) return null;
682 if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
684 for (var i = 0; i < 3; i++){
685 var bit = (this[i] - 0).toString(16);
686 hex.push((bit.length == 1) ? '0' + bit : bit);
688 return (array) ? hex : '#' + hex.join('');
695 Array.alias('extend', 'append');
697 var $pick = function(){
698 return Array.from(arguments).pick();
708 description: Contains Function Prototypes like create, bind, pass, and delay.
710 license: MIT-style license.
722 for (var i = 0, l = arguments.length; i < l; i++){
724 return arguments[i]();
734 attempt: function(args, bind){
736 return this.apply(bind, Array.from(args));
743 bind: function(that){
745 args = arguments.length > 1 ? Array.slice(arguments, 1) : null,
748 var bound = function(){
749 var context = that, length = arguments.length;
750 if (this instanceof bound){
751 F.prototype = self.prototype;
754 var result = (!args && !length)
756 : self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments);
757 return context == that ? result : context;
763 pass: function(args, bind){
765 if (args != null) args = Array.from(args);
767 return self.apply(bind, args || arguments);
771 delay: function(delay, bind, args){
772 return setTimeout(this.pass((args == null ? [] : args), bind), delay);
775 periodical: function(periodical, bind, args){
776 return setInterval(this.pass((args == null ? [] : args), bind), periodical);
783 delete Function.prototype.bind;
787 create: function(options){
789 options = options || {};
790 return function(event){
791 var args = options.arguments;
792 args = (args != null) ? Array.from(args) : Array.slice(arguments, (options.event) ? 1 : 0);
793 if (options.event) args = [event || window.event].extend(args);
794 var returns = function(){
795 return self.apply(options.bind || null, args);
797 if (options.delay) return setTimeout(returns, options.delay);
798 if (options.periodical) return setInterval(returns, options.periodical);
799 if (options.attempt) return Function.attempt(returns);
804 bind: function(bind, args){
806 if (args != null) args = Array.from(args);
808 return self.apply(bind, args || arguments);
812 bindWithEvent: function(bind, args){
814 if (args != null) args = Array.from(args);
815 return function(event){
816 return self.apply(bind, (args == null) ? arguments : [event].concat(args));
820 run: function(args, bind){
821 return this.apply(bind, Array.from(args));
826 if (Object.create == Function.prototype.create) Object.create = null;
828 var $try = Function.attempt;
837 description: Contains Number Prototypes like limit, round, times, and ceil.
839 license: MIT-style license.
850 limit: function(min, max){
851 return Math.min(max, Math.max(min, this));
854 round: function(precision){
855 precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
856 return Math.round(this * precision) / precision;
859 times: function(fn, bind){
860 for (var i = 0; i < this; i++) fn.call(bind, i, this);
864 return parseFloat(this);
867 toInt: function(base){
868 return parseInt(this, base || 10);
873 Number.alias('each', 'times');
877 math.each(function(name){
878 if (!Number[name]) methods[name] = function(){
879 return Math[name].apply(null, [this].concat(Array.from(arguments)));
882 Number.implement(methods);
883 })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
890 description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
892 license: MIT-style license.
894 requires: [Type, Array]
904 contains: function(string, index){
905 return (index ? String(this).slice(index) : String(this)).indexOf(string) > -1;
909 test: function(regex, params){
910 return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
914 return String(this).replace(/^\s+|\s+$/g, '');
918 return String(this).replace(/\s+/g, ' ').trim();
921 camelCase: function(){
922 return String(this).replace(/-\D/g, function(match){
923 return match.charAt(1).toUpperCase();
927 hyphenate: function(){
928 return String(this).replace(/[A-Z]/g, function(match){
929 return ('-' + match.charAt(0).toLowerCase());
933 capitalize: function(){
934 return String(this).replace(/\b[a-z]/g, function(match){
935 return match.toUpperCase();
939 escapeRegExp: function(){
940 return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
943 toInt: function(base){
944 return parseInt(this, base || 10);
948 return parseFloat(this);
951 hexToRgb: function(array){
952 var hex = String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
953 return (hex) ? hex.slice(1).hexToRgb(array) : null;
956 rgbToHex: function(array){
957 var rgb = String(this).match(/\d{1,3}/g);
958 return (rgb) ? rgb.rgbToHex(array) : null;
961 substitute: function(object, regexp){
962 return String(this).replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
963 if (match.charAt(0) == '\\') return match.slice(1);
964 return (object[name] != null) ? object[name] : '';
971 String.prototype.contains = function(string, separator){
972 return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : String(this).indexOf(string) > -1;
981 description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.
983 license: MIT-style license.
985 requires: [Array, Function, Number, String]
987 provides: [Browser, Window, Document]
994 var document = this.document;
995 var window = document.window = this;
997 var parse = function(ua, platform){
998 ua = ua.toLowerCase();
999 platform = (platform ? platform.toLowerCase() : '');
1001 var UA = ua.match(/(opera|ie|firefox|chrome|trident|crios|version)[\s\/:]([\w\d\.]+)?.*?(safari|(?:rv[\s\/:]|version[\s\/:])([\w\d\.]+)|$)/) || [null, 'unknown', 0];
1003 if (UA[1] == 'trident'){
1005 if (UA[4]) UA[2] = UA[4];
1006 } else if (UA[1] == 'crios'){
1010 platform = ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0];
1011 if (platform == 'win') platform = 'windows';
1014 extend: Function.prototype.extend,
1015 name: (UA[1] == 'version') ? UA[3] : UA[1],
1016 version: parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
1021 var Browser = this.Browser = parse(navigator.userAgent, navigator.platform);
1023 if (Browser.name == 'ie'){
1024 Browser.version = document.documentMode;
1029 xpath: !!(document.evaluate),
1030 air: !!(window.runtime),
1031 query: !!(document.querySelector),
1032 json: !!(window.JSON)
1038 Browser[Browser.name] = true;
1039 Browser[Browser.name + parseInt(Browser.version, 10)] = true;
1041 if (Browser.name == 'ie' && Browser.version >= '11'){
1045 var platform = Browser.platform;
1046 if (platform == 'windows'){
1049 Browser.Platform = {
1052 Browser.Platform[platform] = true;
1057 Browser.Request = (function(){
1059 var XMLHTTP = function(){
1060 return new XMLHttpRequest();
1063 var MSXML2 = function(){
1064 return new ActiveXObject('MSXML2.XMLHTTP');
1067 var MSXML = function(){
1068 return new ActiveXObject('Microsoft.XMLHTTP');
1071 return Function.attempt(function(){
1084 Browser.Features.xhr = !!(Browser.Request);
1090 var version = (Function.attempt(function(){
1091 return navigator.plugins['Shockwave Flash'].description;
1093 return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
1094 }) || '0 r0').match(/\d+/g);
1098 version: Number(version[0] || '0.' + version[1]) || 0,
1099 build: Number(version[2]) || 0
1107 Browser.exec = function(text){
1108 if (!text) return text;
1109 if (window.execScript){
1110 window.execScript(text);
1112 var script = document.createElement('script');
1113 script.setAttribute('type', 'text/javascript');
1115 document.head.appendChild(script);
1116 document.head.removeChild(script);
1121 String.implement('stripScripts', function(exec){
1123 var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
1124 scripts += code + '\n';
1127 if (exec === true) Browser.exec(scripts);
1128 else if (typeOf(exec) == 'function') exec(scripts, text);
1135 Document: this.Document,
1136 Window: this.Window,
1137 Element: this.Element,
1141 this.Window = this.$constructor = new Type('Window', function(){});
1143 this.$family = Function.from('window').hide();
1145 Window.mirror(function(name, method){
1146 window[name] = method;
1149 this.Document = document.$constructor = new Type('Document', function(){});
1151 document.$family = Function.from('document').hide();
1153 Document.mirror(function(name, method){
1154 document[name] = method;
1157 document.html = document.documentElement;
1158 if (!document.head) document.head = document.getElementsByTagName('head')[0];
1160 if (document.execCommand) try {
1161 document.execCommand("BackgroundImageCache", false, true);
1165 if (this.attachEvent && !this.addEventListener){
1166 var unloadEvent = function(){
1167 this.detachEvent('onunload', unloadEvent);
1168 document.head = document.html = document.window = null;
1169 window = this.Window = document = null;
1171 this.attachEvent('onunload', unloadEvent);
1174 // IE fails on collections and <select>.options (refers to <select>)
1175 var arrayFrom = Array.from;
1177 arrayFrom(document.html.childNodes);
1179 Array.from = function(item){
1180 if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){
1181 var i = item.length, array = new Array(i);
1182 while (i--) array[i] = item[i];
1185 return arrayFrom(item);
1188 var prototype = Array.prototype,
1189 slice = prototype.slice;
1190 ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){
1191 var method = prototype[name];
1192 Array[name] = function(item){
1193 return method.apply(Array.from(item), slice.call(arguments, 1));
1201 if (Browser.Platform.ios) Browser.Platform.ipod = true;
1203 Browser.Engine = {};
1205 var setEngine = function(name, version){
1206 Browser.Engine.name = name;
1207 Browser.Engine[name + version] = true;
1208 Browser.Engine.version = version;
1212 Browser.Engine.trident = true;
1214 switch (Browser.version){
1215 case 6: setEngine('trident', 4); break;
1216 case 7: setEngine('trident', 5); break;
1217 case 8: setEngine('trident', 6);
1221 if (Browser.firefox){
1222 Browser.Engine.gecko = true;
1224 if (Browser.version >= 3) setEngine('gecko', 19);
1225 else setEngine('gecko', 18);
1228 if (Browser.safari || Browser.chrome){
1229 Browser.Engine.webkit = true;
1231 switch (Browser.version){
1232 case 2: setEngine('webkit', 419); break;
1233 case 3: setEngine('webkit', 420); break;
1234 case 4: setEngine('webkit', 525);
1239 Browser.Engine.presto = true;
1241 if (Browser.version >= 9.6) setEngine('presto', 960);
1242 else if (Browser.version >= 9.5) setEngine('presto', 950);
1243 else setEngine('presto', 925);
1246 if (Browser.name == 'unknown'){
1247 switch ((navigator.userAgent.toLowerCase().match(/(?:webkit|khtml|gecko)/) || [])[0]){
1250 Browser.Engine.webkit = true;
1253 Browser.Engine.gecko = true;
1257 this.$exec = Browser.exec;
1268 description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
1270 license: MIT-style license.
1272 requires: [Array, String, Function, Number]
1281 var Class = this.Class = new Type('Class', function(params){
1282 if (instanceOf(params, Function)) params = {initialize: params};
1284 var newClass = function(){
1286 if (newClass.$prototyping) return this;
1287 this.$caller = null;
1288 var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
1289 this.$caller = this.caller = null;
1291 }.extend(this).implement(params);
1293 newClass.$constructor = Class;
1294 newClass.prototype.$constructor = newClass;
1295 newClass.prototype.parent = parent;
1300 var parent = function(){
1301 if (!this.$caller) throw new Error('The method "parent" cannot be called.');
1302 var name = this.$caller.$name,
1303 parent = this.$caller.$owner.parent,
1304 previous = (parent) ? parent.prototype[name] : null;
1305 if (!previous) throw new Error('The method "' + name + '" has no parent.');
1306 return previous.apply(this, arguments);
1309 var reset = function(object){
1310 for (var key in object){
1311 var value = object[key];
1312 switch (typeOf(value)){
1314 var F = function(){};
1315 F.prototype = value;
1316 object[key] = reset(new F);
1318 case 'array': object[key] = value.clone(); break;
1324 var wrap = function(self, key, method){
1325 if (method.$origin) method = method.$origin;
1326 var wrapper = function(){
1327 if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
1328 var caller = this.caller, current = this.$caller;
1329 this.caller = current; this.$caller = wrapper;
1330 var result = method.apply(this, arguments);
1331 this.$caller = current; this.caller = caller;
1333 }.extend({$owner: self, $origin: method, $name: key});
1337 var implement = function(key, value, retain){
1338 if (Class.Mutators.hasOwnProperty(key)){
1339 value = Class.Mutators[key].call(this, value);
1340 if (value == null) return this;
1343 if (typeOf(value) == 'function'){
1344 if (value.$hidden) return this;
1345 this.prototype[key] = (retain) ? value : wrap(this, key, value);
1347 Object.merge(this.prototype, key, value);
1353 var getInstance = function(klass){
1354 klass.$prototyping = true;
1355 var proto = new klass;
1356 delete klass.$prototyping;
1360 Class.implement('implement', implement.overloadSetter());
1364 Extends: function(parent){
1365 this.parent = parent;
1366 this.prototype = getInstance(parent);
1369 Implements: function(items){
1370 Array.from(items).each(function(item){
1371 var instance = new item;
1372 for (var key in instance) implement.call(this, key, instance[key], true);
1384 description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
1386 license: MIT-style license.
1390 provides: [Class.Extras, Chain, Events, Options]
1397 this.Chain = new Class({
1402 this.$chain.append(Array.flatten(arguments));
1406 callChain: function(){
1407 return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
1410 clearChain: function(){
1411 this.$chain.empty();
1417 var removeOn = function(string){
1418 return string.replace(/^on([A-Z])/, function(full, first){
1419 return first.toLowerCase();
1423 this.Events = new Class({
1427 addEvent: function(type, fn, internal){
1428 type = removeOn(type);
1431 if (fn == $empty) return this;
1434 this.$events[type] = (this.$events[type] || []).include(fn);
1435 if (internal) fn.internal = true;
1439 addEvents: function(events){
1440 for (var type in events) this.addEvent(type, events[type]);
1444 fireEvent: function(type, args, delay){
1445 type = removeOn(type);
1446 var events = this.$events[type];
1447 if (!events) return this;
1448 args = Array.from(args);
1449 events.each(function(fn){
1450 if (delay) fn.delay(delay, this, args);
1451 else fn.apply(this, args);
1456 removeEvent: function(type, fn){
1457 type = removeOn(type);
1458 var events = this.$events[type];
1459 if (events && !fn.internal){
1460 var index = events.indexOf(fn);
1461 if (index != -1) delete events[index];
1466 removeEvents: function(events){
1468 if (typeOf(events) == 'object'){
1469 for (type in events) this.removeEvent(type, events[type]);
1472 if (events) events = removeOn(events);
1473 for (type in this.$events){
1474 if (events && events != type) continue;
1475 var fns = this.$events[type];
1476 for (var i = fns.length; i--;) if (i in fns){
1477 this.removeEvent(type, fns[i]);
1485 this.Options = new Class({
1487 setOptions: function(){
1488 var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));
1489 if (this.addEvent) for (var option in options){
1490 if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
1491 this.addEvent(option, options[option]);
1492 delete options[option];
1506 description: Object generic methods
1508 license: MIT-style license.
1512 provides: [Object, Hash]
1519 var hasOwnProperty = Object.prototype.hasOwnProperty;
1523 subset: function(object, keys){
1525 for (var i = 0, l = keys.length; i < l; i++){
1527 if (k in object) results[k] = object[k];
1532 map: function(object, fn, bind){
1534 for (var key in object){
1535 if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
1540 filter: function(object, fn, bind){
1542 for (var key in object){
1543 var value = object[key];
1544 if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value;
1549 every: function(object, fn, bind){
1550 for (var key in object){
1551 if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
1556 some: function(object, fn, bind){
1557 for (var key in object){
1558 if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
1563 keys: function(object){
1565 for (var key in object){
1566 if (hasOwnProperty.call(object, key)) keys.push(key);
1571 values: function(object){
1573 for (var key in object){
1574 if (hasOwnProperty.call(object, key)) values.push(object[key]);
1579 getLength: function(object){
1580 return Object.keys(object).length;
1583 keyOf: function(object, value){
1584 for (var key in object){
1585 if (hasOwnProperty.call(object, key) && object[key] === value) return key;
1590 contains: function(object, value){
1591 return Object.keyOf(object, value) != null;
1594 toQueryString: function(object, base){
1595 var queryString = [];
1597 Object.each(object, function(value, key){
1598 if (base) key = base + '[' + key + ']';
1600 switch (typeOf(value)){
1601 case 'object': result = Object.toQueryString(value, key); break;
1604 value.each(function(val, i){
1607 result = Object.toQueryString(qs, key);
1609 default: result = key + '=' + encodeURIComponent(value);
1611 if (value != null) queryString.push(result);
1614 return queryString.join('&');
1625 has: Object.prototype.hasOwnProperty,
1627 keyOf: function(value){
1628 return Object.keyOf(this, value);
1631 hasValue: function(value){
1632 return Object.contains(this, value);
1635 extend: function(properties){
1636 Hash.each(properties || {}, function(value, key){
1637 Hash.set(this, key, value);
1642 combine: function(properties){
1643 Hash.each(properties || {}, function(value, key){
1644 Hash.include(this, key, value);
1649 erase: function(key){
1650 if (this.hasOwnProperty(key)) delete this[key];
1655 return (this.hasOwnProperty(key)) ? this[key] : null;
1658 set: function(key, value){
1659 if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
1664 Hash.each(this, function(value, key){
1670 include: function(key, value){
1671 if (this[key] == null) this[key] = value;
1675 map: function(fn, bind){
1676 return new Hash(Object.map(this, fn, bind));
1679 filter: function(fn, bind){
1680 return new Hash(Object.filter(this, fn, bind));
1683 every: function(fn, bind){
1684 return Object.every(this, fn, bind);
1687 some: function(fn, bind){
1688 return Object.some(this, fn, bind);
1691 getKeys: function(){
1692 return Object.keys(this);
1695 getValues: function(){
1696 return Object.values(this);
1699 toQueryString: function(base){
1700 return Object.toQueryString(this, base);
1705 Hash.extend = Object.append;
1707 Hash.alias({indexOf: 'keyOf', contains: 'hasValue'});
1714 description: Standalone CSS3 Selector parser
1715 provides: Slick.Parser
1729 var parse = function(expression, isReversed){
1730 if (expression == null) return null;
1731 if (expression.Slick === true) return expression;
1732 expression = ('' + expression).replace(/^\s+|\s+$/g, '');
1733 reversed = !!isReversed;
1734 var currentCache = (reversed) ? reverseCache : cache;
1735 if (currentCache[expression]) return currentCache[expression];
1740 reverse: function(){
1741 return parse(this.raw, true);
1744 separatorIndex = -1;
1745 while (expression != (expression = expression.replace(regexp, parser)));
1746 parsed.length = parsed.expressions.length;
1747 return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
1750 var reverseCombinator = function(combinator){
1751 if (combinator === '!') return ' ';
1752 else if (combinator === ' ') return '!';
1753 else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
1754 else return '!' + combinator;
1757 var reverse = function(expression){
1758 var expressions = expression.expressions;
1759 for (var i = 0; i < expressions.length; i++){
1760 var exp = expressions[i];
1761 var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
1763 for (var j = 0; j < exp.length; j++){
1765 if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
1766 cexp.combinator = cexp.reverseCombinator;
1767 delete cexp.reverseCombinator;
1770 exp.reverse().push(last);
1775 var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
1776 return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){
1777 return '\\' + match;
1781 var regexp = new RegExp(
1784 puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
1787 \\s* ( , ) \\s* # Separator \n\
1788 | \\s* ( <combinator>+ ) \\s* # Combinator \n\
1789 | ( \\s+ ) # CombinatorChildren \n\
1790 | ( <unicode>+ | \\* ) # Tag \n\
1791 | \\# ( <unicode>+ ) # ID \n\
1792 | \\. ( <unicode>+ ) # ClassName \n\
1795 \\s* (<unicode1>+) (?: \
1796 \\s* ([*^$!~|]?=) (?: \
1803 | :+ ( <unicode>+ )(?:\
1805 (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
1810 "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
1811 .replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
1812 .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
1813 .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
1835 pseudoClassQuotedValue,
1838 if (separator || separatorIndex === -1){
1839 parsed.expressions[++separatorIndex] = [];
1840 combinatorIndex = -1;
1841 if (separator) return '';
1844 if (combinator || combinatorChildren || combinatorIndex === -1){
1845 combinator = combinator || ' ';
1846 var currentSeparator = parsed.expressions[separatorIndex];
1847 if (reversed && currentSeparator[combinatorIndex])
1848 currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
1849 currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
1852 var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
1855 currentParsed.tag = tagName.replace(reUnescape, '');
1858 currentParsed.id = id.replace(reUnescape, '');
1860 } else if (className){
1861 className = className.replace(reUnescape, '');
1863 if (!currentParsed.classList) currentParsed.classList = [];
1864 if (!currentParsed.classes) currentParsed.classes = [];
1865 currentParsed.classList.push(className);
1866 currentParsed.classes.push({
1868 regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
1871 } else if (pseudoClass){
1872 pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
1873 pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
1875 if (!currentParsed.pseudos) currentParsed.pseudos = [];
1876 currentParsed.pseudos.push({
1877 key: pseudoClass.replace(reUnescape, ''),
1878 value: pseudoClassValue,
1879 type: pseudoMarker.length == 1 ? 'class' : 'element'
1882 } else if (attributeKey){
1883 attributeKey = attributeKey.replace(reUnescape, '');
1884 attributeValue = (attributeValue || '').replace(reUnescape, '');
1888 switch (attributeOperator){
1889 case '^=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) ); break;
1890 case '$=' : regexp = new RegExp( escapeRegExp(attributeValue) +'$' ); break;
1891 case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
1892 case '|=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) +'(-|$)' ); break;
1893 case '=' : test = function(value){
1894 return attributeValue == value;
1896 case '*=' : test = function(value){
1897 return value && value.indexOf(attributeValue) > -1;
1899 case '!=' : test = function(value){
1900 return attributeValue != value;
1902 default : test = function(value){
1907 if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
1911 if (!test) test = function(value){
1912 return value && regexp.test(value);
1915 if (!currentParsed.attributes) currentParsed.attributes = [];
1916 currentParsed.attributes.push({
1918 operator: attributeOperator,
1919 value: attributeValue,
1930 var Slick = (this.Slick || {});
1932 Slick.parse = function(expression){
1933 return parse(expression);
1936 Slick.escapeRegExp = escapeRegExp;
1938 if (!this.Slick) this.Slick = Slick;
1940 }).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
1945 description: The new, superfast css selector engine.
1946 provides: Slick.Finder
1947 requires: Slick.Parser
1955 toString = Object.prototype.toString;
1957 // Feature / Bug detection
1959 local.isNativeCode = function(fn){
1960 return (/\{\s*\[native code\]\s*\}/).test('' + fn);
1963 local.isXML = function(document){
1964 return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') ||
1965 (document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
1968 local.setDocument = function(document){
1970 // convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
1971 var nodeType = document.nodeType;
1972 if (nodeType == 9); // document
1973 else if (nodeType) document = document.ownerDocument; // node
1974 else if (document.navigator) document = document.document; // window
1977 // check if it's the old document
1979 if (this.document === document) return;
1980 this.document = document;
1982 // check if we have done feature detection on this document before
1984 var root = document.documentElement,
1985 rootUid = this.getUIDXML(root),
1986 features = featuresCache[rootUid],
1990 for (feature in features){
1991 this[feature] = features[feature];
1996 features = featuresCache[rootUid] = {};
1998 features.root = root;
1999 features.isXMLDocument = this.isXML(document);
2001 features.brokenStarGEBTN
2002 = features.starSelectsClosedQSA
2003 = features.idGetsName
2004 = features.brokenMixedCaseQSA
2005 = features.brokenGEBCN
2006 = features.brokenCheckedQSA
2007 = features.brokenEmptyAttributeQSA
2008 = features.isHTMLDocument
2009 = features.nativeMatchesSelector
2012 var starSelectsClosed, starSelectsComments,
2013 brokenSecondClassNameGEBCN, cachedGetElementsByClassName,
2014 brokenFormAttributeGetter;
2016 var selected, id = 'slick_uniqueid';
2017 var testNode = document.createElement('div');
2019 var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
2020 testRoot.appendChild(testNode);
2022 // on non-HTML documents innerHTML and getElementsById doesnt work properly
2024 testNode.innerHTML = '<a id="'+id+'"></a>';
2025 features.isHTMLDocument = !!document.getElementById(id);
2028 if (features.isHTMLDocument){
2030 testNode.style.display = 'none';
2032 // IE returns comment nodes for getElementsByTagName('*') for some documents
2033 testNode.appendChild(document.createComment(''));
2034 starSelectsComments = (testNode.getElementsByTagName('*').length > 1);
2036 // IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
2038 testNode.innerHTML = 'foo</foo>';
2039 selected = testNode.getElementsByTagName('*');
2040 starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
2043 features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
2045 // IE returns elements with the name instead of just id for getElementsById for some documents
2047 testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
2048 features.idGetsName = document.getElementById(id) === testNode.firstChild;
2051 if (testNode.getElementsByClassName){
2053 // Safari 3.2 getElementsByClassName caches results
2055 testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
2056 testNode.getElementsByClassName('b').length;
2057 testNode.firstChild.className = 'b';
2058 cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
2061 // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
2063 testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
2064 brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
2067 features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
2070 if (testNode.querySelectorAll){
2071 // IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
2073 testNode.innerHTML = 'foo</foo>';
2074 selected = testNode.querySelectorAll('*');
2075 features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
2078 // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
2080 testNode.innerHTML = '<a class="MiX"></a>';
2081 features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
2084 // Webkit and Opera dont return selected options on querySelectorAll
2086 testNode.innerHTML = '<select><option selected="selected">a</option></select>';
2087 features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
2090 // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
2092 testNode.innerHTML = '<a class=""></a>';
2093 features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
2098 // IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
2100 testNode.innerHTML = '<form action="s"><input id="action"/></form>';
2101 brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
2104 // native matchesSelector function
2106 features.nativeMatchesSelector = root.matches || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
2107 if (features.nativeMatchesSelector) try {
2108 // if matchesSelector trows errors on incorrect sintaxes we can use it
2109 features.nativeMatchesSelector.call(root, ':slick');
2110 features.nativeMatchesSelector = null;
2116 root.slick_expando = 1;
2117 delete root.slick_expando;
2118 features.getUID = this.getUIDHTML;
2120 features.getUID = this.getUIDXML;
2123 testRoot.removeChild(testNode);
2124 testNode = selected = testRoot = null;
2128 features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){
2129 var method = this.attributeGetters[name];
2130 if (method) return method.call(node);
2131 var attributeNode = node.getAttributeNode(name);
2132 return (attributeNode) ? attributeNode.nodeValue : null;
2133 } : function(node, name){
2134 var method = this.attributeGetters[name];
2135 return (method) ? method.call(node) : node.getAttribute(name);
2140 features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute){
2141 return node.hasAttribute(attribute);
2142 } : function(node, attribute){
2143 node = node.getAttributeNode(attribute);
2144 return !!(node && (node.specified || node.nodeValue));
2148 // FIXME: Add specs: local.contains should be different for xml and html documents?
2149 var nativeRootContains = root && this.isNativeCode(root.contains),
2150 nativeDocumentContains = document && this.isNativeCode(document.contains);
2152 features.contains = (nativeRootContains && nativeDocumentContains) ? function(context, node){
2153 return context.contains(node);
2154 } : (nativeRootContains && !nativeDocumentContains) ? function(context, node){
2155 // IE8 does not have .contains on document.
2156 return context === node || ((context === document) ? document.documentElement : context).contains(node);
2157 } : (root && root.compareDocumentPosition) ? function(context, node){
2158 return context === node || !!(context.compareDocumentPosition(node) & 16);
2159 } : function(context, node){
2161 if (node === context) return true;
2162 } while ((node = node.parentNode));
2166 // document order sorting
2167 // credits to Sizzle (http://sizzlejs.com/)
2169 features.documentSorter = (root.compareDocumentPosition) ? function(a, b){
2170 if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
2171 return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
2172 } : ('sourceIndex' in root) ? function(a, b){
2173 if (!a.sourceIndex || !b.sourceIndex) return 0;
2174 return a.sourceIndex - b.sourceIndex;
2175 } : (document.createRange) ? function(a, b){
2176 if (!a.ownerDocument || !b.ownerDocument) return 0;
2177 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
2178 aRange.setStart(a, 0);
2179 aRange.setEnd(a, 0);
2180 bRange.setStart(b, 0);
2181 bRange.setEnd(b, 0);
2182 return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
2187 for (feature in features){
2188 this[feature] = features[feature];
2194 var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
2195 reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
2196 qsaFailExpCache = {};
2198 local.search = function(context, expression, append, first){
2200 var found = this.found = (first) ? null : (append || []);
2202 if (!context) return found;
2203 else if (context.navigator) context = context.document; // Convert the node from a window to a document
2204 else if (!context.nodeType) return found;
2209 uniques = this.uniques = {},
2210 hasOthers = !!(append && append.length),
2211 contextIsDocument = (context.nodeType == 9);
2213 if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);
2215 // avoid duplicating items already in the append array
2216 if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;
2218 // expression checks
2220 if (typeof expression == 'string'){ // expression is a string
2222 /*<simple-selectors-override>*/
2223 var simpleSelector = expression.match(reSimpleSelector);
2224 simpleSelectors: if (simpleSelector){
2226 var symbol = simpleSelector[1],
2227 name = simpleSelector[2],
2232 if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
2233 nodes = context.getElementsByTagName(name);
2234 if (first) return nodes[0] || null;
2235 for (i = 0; node = nodes[i++];){
2236 if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
2239 } else if (symbol == '#'){
2241 if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
2242 node = context.getElementById(name);
2243 if (!node) return found;
2244 if (this.idGetsName && node.getAttributeNode('id').nodeValue != name) break simpleSelectors;
2245 if (first) return node || null;
2246 if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
2248 } else if (symbol == '.'){
2250 if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
2251 if (context.getElementsByClassName && !this.brokenGEBCN){
2252 nodes = context.getElementsByClassName(name);
2253 if (first) return nodes[0] || null;
2254 for (i = 0; node = nodes[i++];){
2255 if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
2258 var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)');
2259 nodes = context.getElementsByTagName('*');
2260 for (i = 0; node = nodes[i++];){
2261 className = node.className;
2262 if (!(className && matchClass.test(className))) continue;
2263 if (first) return node;
2264 if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
2270 if (hasOthers) this.sort(found);
2271 return (first) ? null : found;
2274 /*</simple-selectors-override>*/
2276 /*<query-selector-override>*/
2277 querySelector: if (context.querySelectorAll){
2279 if (!this.isHTMLDocument
2280 || qsaFailExpCache[expression]
2281 //TODO: only skip when expression is actually mixed case
2282 || this.brokenMixedCaseQSA
2283 || (this.brokenCheckedQSA && expression.indexOf(':checked') > -1)
2284 || (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression))
2285 || (!contextIsDocument //Abort when !contextIsDocument and...
2286 // there are multiple expressions in the selector
2287 // since we currently only fix non-document rooted QSA for single expression selectors
2288 && expression.indexOf(',') > -1
2291 ) break querySelector;
2293 var _expression = expression, _context = context;
2294 if (!contextIsDocument){
2295 // non-document rooted QSA
2296 // credits to Andrew Dupont
2297 var currentId = _context.getAttribute('id'), slickid = 'slickid__';
2298 _context.setAttribute('id', slickid);
2299 _expression = '#' + slickid + ' ' + _expression;
2300 context = _context.parentNode;
2304 if (first) return context.querySelector(_expression) || null;
2305 else nodes = context.querySelectorAll(_expression);
2307 qsaFailExpCache[expression] = 1;
2308 break querySelector;
2310 if (!contextIsDocument){
2311 if (currentId) _context.setAttribute('id', currentId);
2312 else _context.removeAttribute('id');
2317 if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
2318 if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
2319 } else for (i = 0; node = nodes[i++];){
2320 if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
2323 if (hasOthers) this.sort(found);
2327 /*</query-selector-override>*/
2329 parsed = this.Slick.parse(expression);
2330 if (!parsed.length) return found;
2331 } else if (expression == null){ // there is no expression
2333 } else if (expression.Slick){ // expression is a parsed Slick object
2334 parsed = expression;
2335 } else if (this.contains(context.documentElement || context, expression)){ // expression is a node
2336 (found) ? found.push(expression) : found = expression;
2338 } else { // other junk
2342 /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
2344 // cache elements for the nth selectors
2347 this.posNTHLast = {};
2348 this.posNTHType = {};
2349 this.posNTHTypeLast = {};
2351 /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
2353 // if append is null and there is only a single selector with one expression use pushArray, else use pushUID
2354 this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
2356 if (found == null) found = [];
2361 var combinator, tag, id, classList, classes, attributes, pseudos;
2362 var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
2364 search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
2366 combinator = 'combinator:' + currentBit.combinator;
2367 if (!this[combinator]) continue search;
2369 tag = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
2371 classList = currentBit.classList;
2372 classes = currentBit.classes;
2373 attributes = currentBit.attributes;
2374 pseudos = currentBit.pseudos;
2375 lastBit = (j === (currentExpression.length - 1));
2377 this.bitUniques = {};
2380 this.uniques = uniques;
2388 this[combinator](context, tag, id, classes, attributes, pseudos, classList);
2389 if (first && lastBit && found.length) break search;
2391 if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
2392 this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
2393 if (found.length) break search;
2394 } else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
2397 currentItems = this.found;
2400 // should sort if there are nodes in append and if you pass multiple expressions.
2401 if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);
2403 return (first) ? (found[0] || null) : found;
2409 local.uidk = 'slick-uniqueid';
2411 local.getUIDXML = function(node){
2412 var uid = node.getAttribute(this.uidk);
2415 node.setAttribute(this.uidk, uid);
2420 local.getUIDHTML = function(node){
2421 return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
2424 // sort based on the setDocument documentSorter method.
2426 local.sort = function(results){
2427 if (!this.documentSorter) return results;
2428 results.sort(this.documentSorter);
2432 /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
2434 local.cacheNTH = {};
2436 local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
2438 local.parseNTHArgument = function(argument){
2439 var parsed = argument.match(this.matchNTH);
2440 if (!parsed) return false;
2441 var special = parsed[2] || false;
2442 var a = parsed[1] || 1;
2443 if (a == '-') a = -1;
2444 var b = +parsed[3] || 0;
2446 (special == 'n') ? {a: a, b: b} :
2447 (special == 'odd') ? {a: 2, b: 1} :
2448 (special == 'even') ? {a: 2, b: 0} : {a: 0, b: a};
2450 return (this.cacheNTH[argument] = parsed);
2453 local.createNTHPseudo = function(child, sibling, positions, ofType){
2454 return function(node, argument){
2455 var uid = this.getUID(node);
2456 if (!this[positions][uid]){
2457 var parent = node.parentNode;
2458 if (!parent) return false;
2459 var el = parent[child], count = 1;
2461 var nodeName = node.nodeName;
2463 if (el.nodeName != nodeName) continue;
2464 this[positions][this.getUID(el)] = count++;
2465 } while ((el = el[sibling]));
2468 if (el.nodeType != 1) continue;
2469 this[positions][this.getUID(el)] = count++;
2470 } while ((el = el[sibling]));
2473 argument = argument || 'n';
2474 var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
2475 if (!parsed) return false;
2476 var a = parsed.a, b = parsed.b, pos = this[positions][uid];
2477 if (a == 0) return b == pos;
2479 if (pos < b) return false;
2481 if (b < pos) return false;
2483 return ((pos - b) % a) == 0;
2487 /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
2489 local.pushArray = function(node, tag, id, classes, attributes, pseudos){
2490 if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
2493 local.pushUID = function(node, tag, id, classes, attributes, pseudos){
2494 var uid = this.getUID(node);
2495 if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
2496 this.uniques[uid] = true;
2497 this.found.push(node);
2501 local.matchNode = function(node, selector){
2502 if (this.isHTMLDocument && this.nativeMatchesSelector){
2504 return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
2505 } catch(matchError){}
2508 var parsed = this.Slick.parse(selector);
2509 if (!parsed) return true;
2511 // simple (single) selectors
2512 var expressions = parsed.expressions, simpleExpCounter = 0, i, currentExpression;
2513 for (i = 0; (currentExpression = expressions[i]); i++){
2514 if (currentExpression.length == 1){
2515 var exp = currentExpression[0];
2516 if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true;
2521 if (simpleExpCounter == parsed.length) return false;
2523 var nodes = this.search(this.document, parsed), item;
2524 for (i = 0; item = nodes[i++];){
2525 if (item === node) return true;
2530 local.matchPseudo = function(node, name, argument){
2531 var pseudoName = 'pseudo:' + name;
2532 if (this[pseudoName]) return this[pseudoName](node, argument);
2533 var attribute = this.getAttribute(node, name);
2534 return (argument) ? argument == attribute : !!attribute;
2537 local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
2539 var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
2541 if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
2543 if (nodeName != tag) return false;
2547 if (id && node.getAttribute('id') != id) return false;
2550 if (classes) for (i = classes.length; i--;){
2551 cls = this.getAttribute(node, 'class');
2552 if (!(cls && classes[i].regexp.test(cls))) return false;
2554 if (attributes) for (i = attributes.length; i--;){
2555 part = attributes[i];
2556 if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
2558 if (pseudos) for (i = pseudos.length; i--;){
2560 if (!this.matchPseudo(node, part.key, part.value)) return false;
2567 ' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
2569 var i, item, children;
2571 if (this.isHTMLDocument){
2573 item = this.document.getElementById(id);
2574 if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){
2575 // all[id] returns all the elements with that name or id inside node
2576 // if theres just one it will return the element, else it will be a collection
2577 children = node.all[id];
2578 if (!children) return;
2579 if (!children[0]) children = [children];
2580 for (i = 0; item = children[i++];){
2581 var idNode = item.getAttributeNode('id');
2582 if (idNode && idNode.nodeValue == id){
2583 this.push(item, tag, null, classes, attributes, pseudos);
2590 // if the context is in the dom we return, else we will try GEBTN, breaking the getById label
2591 if (this.contains(this.root, node)) return;
2593 } else if (this.document !== node && !this.contains(node, item)) return;
2594 this.push(item, tag, null, classes, attributes, pseudos);
2597 getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
2598 children = node.getElementsByClassName(classList.join(' '));
2599 if (!(children && children.length)) break getByClass;
2600 for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
2605 children = node.getElementsByTagName(tag);
2606 if (!(children && children.length)) break getByTag;
2607 if (!this.brokenStarGEBTN) tag = null;
2608 for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
2612 '>': function(node, tag, id, classes, attributes, pseudos){ // direct children
2613 if ((node = node.firstChild)) do {
2614 if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
2615 } while ((node = node.nextSibling));
2618 '+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
2619 while ((node = node.nextSibling)) if (node.nodeType == 1){
2620 this.push(node, tag, id, classes, attributes, pseudos);
2625 '^': function(node, tag, id, classes, attributes, pseudos){ // first child
2626 node = node.firstChild;
2628 if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
2629 else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
2633 '~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
2634 while ((node = node.nextSibling)){
2635 if (node.nodeType != 1) continue;
2636 var uid = this.getUID(node);
2637 if (this.bitUniques[uid]) break;
2638 this.bitUniques[uid] = true;
2639 this.push(node, tag, id, classes, attributes, pseudos);
2643 '++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
2644 this['combinator:+'](node, tag, id, classes, attributes, pseudos);
2645 this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
2648 '~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
2649 this['combinator:~'](node, tag, id, classes, attributes, pseudos);
2650 this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
2653 '!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document
2654 while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
2657 '!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
2658 node = node.parentNode;
2659 if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
2662 '!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
2663 while ((node = node.previousSibling)) if (node.nodeType == 1){
2664 this.push(node, tag, id, classes, attributes, pseudos);
2669 '!^': function(node, tag, id, classes, attributes, pseudos){ // last child
2670 node = node.lastChild;
2672 if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
2673 else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
2677 '!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
2678 while ((node = node.previousSibling)){
2679 if (node.nodeType != 1) continue;
2680 var uid = this.getUID(node);
2681 if (this.bitUniques[uid]) break;
2682 this.bitUniques[uid] = true;
2683 this.push(node, tag, id, classes, attributes, pseudos);
2689 for (var c in combinators) local['combinator:' + c] = combinators[c];
2693 /*<pseudo-selectors>*/
2695 'empty': function(node){
2696 var child = node.firstChild;
2697 return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
2700 'not': function(node, expression){
2701 return !this.matchNode(node, expression);
2704 'contains': function(node, text){
2705 return (node.innerText || node.textContent || '').indexOf(text) > -1;
2708 'first-child': function(node){
2709 while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
2713 'last-child': function(node){
2714 while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
2718 'only-child': function(node){
2720 while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
2722 while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
2726 /*<nth-pseudo-selectors>*/
2728 'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
2730 'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
2732 'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
2734 'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
2736 'index': function(node, index){
2737 return this['pseudo:nth-child'](node, '' + (index + 1));
2740 'even': function(node){
2741 return this['pseudo:nth-child'](node, '2n');
2744 'odd': function(node){
2745 return this['pseudo:nth-child'](node, '2n+1');
2748 /*</nth-pseudo-selectors>*/
2750 /*<of-type-pseudo-selectors>*/
2752 'first-of-type': function(node){
2753 var nodeName = node.nodeName;
2754 while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
2758 'last-of-type': function(node){
2759 var nodeName = node.nodeName;
2760 while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
2764 'only-of-type': function(node){
2765 var prev = node, nodeName = node.nodeName;
2766 while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
2768 while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
2772 /*</of-type-pseudo-selectors>*/
2776 'enabled': function(node){
2777 return !node.disabled;
2780 'disabled': function(node){
2781 return node.disabled;
2784 'checked': function(node){
2785 return node.checked || node.selected;
2788 'focus': function(node){
2789 return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
2792 'root': function(node){
2793 return (node === this.root);
2796 'selected': function(node){
2797 return node.selected;
2800 /*</pseudo-selectors>*/
2803 for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
2805 // attributes methods
2807 var attributeGetters = local.attributeGetters = {
2810 return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
2814 return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
2817 'style': function(){
2818 return (this.style) ? this.style.cssText : this.getAttribute('style');
2821 'tabindex': function(){
2822 var attributeNode = this.getAttributeNode('tabindex');
2823 return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
2827 return this.getAttribute('type');
2830 'maxlength': function(){
2831 var attributeNode = this.getAttributeNode('maxLength');
2832 return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
2837 attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength;
2841 var Slick = local.Slick = (this.Slick || {});
2843 Slick.version = '1.1.7';
2847 Slick.search = function(context, expression, append){
2848 return local.search(context, expression, append);
2851 Slick.find = function(context, expression){
2852 return local.search(context, expression, null, true);
2855 // Slick containment checker
2857 Slick.contains = function(container, node){
2858 local.setDocument(container);
2859 return local.contains(container, node);
2862 // Slick attribute getter
2864 Slick.getAttribute = function(node, name){
2865 local.setDocument(node);
2866 return local.getAttribute(node, name);
2869 Slick.hasAttribute = function(node, name){
2870 local.setDocument(node);
2871 return local.hasAttribute(node, name);
2876 Slick.match = function(node, selector){
2877 if (!(node && selector)) return false;
2878 if (!selector || selector === node) return true;
2879 local.setDocument(node);
2880 return local.matchNode(node, selector);
2883 // Slick attribute accessor
2885 Slick.defineAttributeGetter = function(name, fn){
2886 local.attributeGetters[name] = fn;
2890 Slick.lookupAttributeGetter = function(name){
2891 return local.attributeGetters[name];
2894 // Slick pseudo accessor
2896 Slick.definePseudo = function(name, fn){
2897 local['pseudo:' + name] = function(node, argument){
2898 return fn.call(node, argument);
2903 Slick.lookupPseudo = function(name){
2904 var pseudo = local['pseudo:' + name];
2905 if (pseudo) return function(argument){
2906 return pseudo.call(this, argument);
2911 // Slick overrides accessor
2913 Slick.override = function(regexp, fn){
2914 local.override(regexp, fn);
2918 Slick.isXML = local.isXML;
2920 Slick.uidOf = function(node){
2921 return local.getUIDHTML(node);
2924 if (!this.Slick) this.Slick = Slick;
2926 }).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
2933 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.
2935 license: MIT-style license.
2937 requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder]
2939 provides: [Element, Elements, $, $$, IFrame, Selectors]
2944 var Element = this.Element = function(tag, props){
2945 var konstructor = Element.Constructors[tag];
2946 if (konstructor) return konstructor(props);
2947 if (typeof tag != 'string') return document.id(tag).set(props);
2949 if (!props) props = {};
2951 if (!(/^[\w-]+$/).test(tag)){
2952 var parsed = Slick.parse(tag).expressions[0][0];
2953 tag = (parsed.tag == '*') ? 'div' : parsed.tag;
2954 if (parsed.id && props.id == null) props.id = parsed.id;
2956 var attributes = parsed.attributes;
2957 if (attributes) for (var attr, i = 0, l = attributes.length; i < l; i++){
2958 attr = attributes[i];
2959 if (props[attr.key] != null) continue;
2961 if (attr.value != null && attr.operator == '=') props[attr.key] = attr.value;
2962 else if (!attr.value && !attr.operator) props[attr.key] = true;
2965 if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' ');
2968 return document.newElement(tag, props);
2972 if (Browser.Element){
2973 Element.prototype = Browser.Element.prototype;
2974 // IE8 and IE9 require the wrapping.
2975 Element.prototype._fireEvent = (function(fireEvent){
2976 return function(type, event){
2977 return fireEvent.call(this, type, event);
2979 })(Element.prototype.fireEvent);
2982 new Type('Element', Element).mirror(function(name){
2983 if (Array.prototype[name]) return;
2986 obj[name] = function(){
2987 var results = [], args = arguments, elements = true;
2988 for (var i = 0, l = this.length; i < l; i++){
2989 var element = this[i], result = results[i] = element[name].apply(element, args);
2990 elements = (elements && typeOf(result) == 'element');
2992 return (elements) ? new Elements(results) : results;
2995 Elements.implement(obj);
2998 if (!Browser.Element){
2999 Element.parent = Object;
3001 Element.Prototype = {
3002 '$constructor': Element,
3003 '$family': Function.from('element').hide()
3006 Element.mirror(function(name, method){
3007 Element.Prototype[name] = method;
3011 Element.Constructors = {};
3015 Element.Constructors = new Hash;
3019 var IFrame = new Type('IFrame', function(){
3020 var params = Array.link(arguments, {
3021 properties: Type.isObject,
3022 iframe: function(obj){
3023 return (obj != null);
3027 var props = params.properties || {}, iframe;
3028 if (params.iframe) iframe = document.id(params.iframe);
3029 var onload = props.onload || function(){};
3030 delete props.onload;
3031 props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.uniqueID()].pick();
3032 iframe = new Element(iframe || 'iframe', props);
3034 var onLoad = function(){
3035 onload.call(iframe.contentWindow);
3038 if (window.frames[props.id]) onLoad();
3039 else iframe.addListener('load', onLoad);
3043 var Elements = this.Elements = function(nodes){
3044 if (nodes && nodes.length){
3045 var uniques = {}, node;
3046 for (var i = 0; node = nodes[i++];){
3047 var uid = Slick.uidOf(node);
3049 uniques[uid] = true;
3056 Elements.prototype = {length: 0};
3057 Elements.parent = Array;
3059 new Type('Elements', Elements).implement({
3061 filter: function(filter, bind){
3062 if (!filter) return this;
3063 return new Elements(Array.filter(this, (typeOf(filter) == 'string') ? function(item){
3064 return item.match(filter);
3069 var length = this.length;
3070 for (var i = 0, l = arguments.length; i < l; i++){
3071 var item = document.id(arguments[i]);
3072 if (item) this[length++] = item;
3074 return (this.length = length);
3077 unshift: function(){
3079 for (var i = 0, l = arguments.length; i < l; i++){
3080 var item = document.id(arguments[i]);
3081 if (item) items.push(item);
3083 return Array.prototype.unshift.apply(this, items);
3087 var newElements = new Elements(this);
3088 for (var i = 0, l = arguments.length; i < l; i++){
3089 var item = arguments[i];
3090 if (Type.isEnumerable(item)) newElements.append(item);
3091 else newElements.push(item);
3096 append: function(collection){
3097 for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]);
3102 while (this.length) delete this[--this.length];
3110 Elements.alias('extend', 'append');
3117 var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2};
3119 splice.call(object, 1, 1);
3120 if (object[1] == 1) Elements.implement('splice', function(){
3121 var length = this.length;
3122 var result = splice.apply(this, arguments);
3123 while (length >= this.length) delete this[length--];
3127 Array.forEachMethod(function(method, name){
3128 Elements.implement(name, method);
3131 Array.mirror(Elements);
3134 var createElementAcceptsHTML;
3136 createElementAcceptsHTML = (document.createElement('<input name=x>').name == 'x');
3139 var escapeQuotes = function(html){
3140 return ('' + html).replace(/&/g, '&').replace(/"/g, '"');
3145 // #2479 - IE8 Cannot set HTML of style element
3146 var canChangeStyleHTML = (function(){
3147 var div = document.createElement('style'),
3150 div.innerHTML = '#justTesing{margin: 0px;}';
3151 flag = !!div.innerHTML;
3157 Document.implement({
3159 newElement: function(tag, props){
3161 if (props.checked != null) props.defaultChecked = props.checked;
3162 if ((props.type == 'checkbox' || props.type == 'radio') && props.value == null) props.value = 'on';
3163 /*<ltIE9>*/ // IE needs the type to be set before changing content of style element
3164 if (!canChangeStyleHTML && tag == 'style'){
3165 var styleElement = document.createElement('style');
3166 styleElement.setAttribute('type', 'text/css');
3167 if (props.type) delete props.type;
3168 return this.id(styleElement).set(props);
3171 /*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
3172 if (createElementAcceptsHTML){
3174 if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
3175 if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
3182 return this.id(this.createElement(tag)).set(props);
3191 Slick.uidOf(window);
3192 Slick.uidOf(document);
3194 Document.implement({
3196 newTextNode: function(text){
3197 return this.createTextNode(text);
3200 getDocument: function(){
3204 getWindow: function(){
3212 string: function(id, nocash, doc){
3213 id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1'));
3214 return (id) ? types.element(id, nocash) : null;
3217 element: function(el, nocash){
3219 if (!nocash && !el.$family && !(/^(?:object|embed)$/i).test(el.tagName)){
3220 var fireEvent = el.fireEvent;
3221 // wrapping needed in IE7, or else crash
3222 el._fireEvent = function(type, event){
3223 return fireEvent(type, event);
3225 Object.append(el, Element.Prototype);
3230 object: function(obj, nocash, doc){
3231 if (obj.toElement) return types.element(obj.toElement(doc), nocash);
3237 types.textnode = types.whitespace = types.window = types.document = function(zero){
3241 return function(el, nocash, doc){
3242 if (el && el.$family && el.uniqueNumber) return el;
3243 var type = typeOf(el);
3244 return (types[type]) ? types[type](el, nocash, doc || document) : null;
3251 if (window.$ == null) Window.implement('$', function(el, nc){
3252 return document.id(el, nc, this.document);
3257 getDocument: function(){
3258 return this.document;
3261 getWindow: function(){
3267 [Document, Element].invoke('implement', {
3269 getElements: function(expression){
3270 return Slick.search(this, expression, new Elements);
3273 getElement: function(expression){
3274 return document.id(Slick.find(this, expression));
3279 var contains = {contains: function(element){
3280 return Slick.contains(this, element);
3283 if (!document.contains) Document.implement(contains);
3284 if (!document.createElement('div').contains) Element.implement(contains);
3288 Element.implement('hasChild', function(element){
3289 return this !== element && this.contains(element);
3292 (function(search, find, match){
3294 this.Selectors = {};
3295 var pseudos = this.Selectors.Pseudo = new Hash();
3297 var addSlickPseudos = function(){
3298 for (var name in pseudos) if (pseudos.hasOwnProperty(name)){
3299 Slick.definePseudo(name, pseudos[name]);
3300 delete pseudos[name];
3304 Slick.search = function(context, expression, append){
3306 return search.call(this, context, expression, append);
3309 Slick.find = function(context, expression){
3311 return find.call(this, context, expression);
3314 Slick.match = function(node, selector){
3316 return match.call(this, node, selector);
3319 })(Slick.search, Slick.find, Slick.match);
3325 var injectCombinator = function(expression, combinator){
3326 if (!expression) return combinator;
3328 expression = Object.clone(Slick.parse(expression));
3330 var expressions = expression.expressions;
3331 for (var i = expressions.length; i--;)
3332 expressions[i][0].combinator = combinator;
3341 }, function(combinator, method){
3342 Element.implement(method, function(expression){
3343 return this.getElement(injectCombinator(expression, combinator));
3349 getAllPrevious: '!~',
3353 }, function(combinator, method){
3354 Element.implement(method, function(expression){
3355 return this.getElements(injectCombinator(expression, combinator));
3361 getFirst: function(expression){
3362 return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]);
3365 getLast: function(expression){
3366 return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast());
3369 getWindow: function(){
3370 return this.ownerDocument.window;
3373 getDocument: function(){
3374 return this.ownerDocument;
3377 getElementById: function(id){
3378 return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1')));
3381 match: function(expression){
3382 return !expression || Slick.match(this, expression);
3389 if (window.$$ == null) Window.implement('$$', function(selector){
3390 var elements = new Elements;
3391 if (arguments.length == 1 && typeof selector == 'string') return Slick.search(this.document, selector, elements);
3392 var args = Array.flatten(arguments);
3393 for (var i = 0, l = args.length; i < l; i++){
3395 switch (typeOf(item)){
3396 case 'element': elements.push(item); break;
3397 case 'string': Slick.search(this.document, item, elements);
3405 if (window.$$ == null) Window.implement('$$', function(selector){
3406 if (arguments.length == 1){
3407 if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements);
3408 else if (Type.isEnumerable(selector)) return new Elements(selector);
3410 return new Elements(arguments);
3417 before: function(context, element){
3418 var parent = element.parentNode;
3419 if (parent) parent.insertBefore(context, element);
3422 after: function(context, element){
3423 var parent = element.parentNode;
3424 if (parent) parent.insertBefore(context, element.nextSibling);
3427 bottom: function(context, element){
3428 element.appendChild(context);
3431 top: function(context, element){
3432 element.insertBefore(context, element.firstChild);
3437 inserters.inside = inserters.bottom;
3441 Object.each(inserters, function(inserter, where){
3443 where = where.capitalize();
3447 methods['inject' + where] = function(el){
3448 inserter(this, document.id(el, true));
3452 methods['grab' + where] = function(el){
3453 inserter(document.id(el, true), this);
3457 Element.implement(methods);
3463 // getProperty / setProperty
3465 var propertyGetters = {}, propertySetters = {};
3469 var properties = {};
3471 'type', 'value', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan',
3472 'frameBorder', 'rowSpan', 'tabIndex', 'useMap'
3473 ], function(property){
3474 properties[property.toLowerCase()] = property;
3477 properties.html = 'innerHTML';
3478 properties.text = (document.createElement('div').textContent == null) ? 'innerText': 'textContent';
3480 Object.forEach(properties, function(real, key){
3481 propertySetters[key] = function(node, value){
3484 propertyGetters[key] = function(node){
3490 propertySetters.text = (function(setter){
3491 return function(node, value){
3492 if (node.get('tag') == 'style') node.set('html', value);
3493 else node[properties.text] = value;
3495 })(propertySetters.text);
3497 propertyGetters.text = (function(getter){
3498 return function(node){
3499 return (node.get('tag') == 'style') ? node.innerHTML : getter(node);
3501 })(propertyGetters.text);
3507 'compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked',
3508 'disabled', 'readOnly', 'multiple', 'selected', 'noresize',
3509 'defer', 'defaultChecked', 'autofocus', 'controls', 'autoplay',
3514 Array.forEach(bools, function(bool){
3515 var lower = bool.toLowerCase();
3516 booleans[lower] = bool;
3517 propertySetters[lower] = function(node, value){
3518 node[bool] = !!value;
3520 propertyGetters[lower] = function(node){
3521 return !!node[bool];
3527 Object.append(propertySetters, {
3529 'class': function(node, value){
3530 ('className' in node) ? node.className = (value || '') : node.setAttribute('class', value);
3533 'for': function(node, value){
3534 ('htmlFor' in node) ? node.htmlFor = value : node.setAttribute('for', value);
3537 'style': function(node, value){
3538 (node.style) ? node.style.cssText = value : node.setAttribute('style', value);
3541 'value': function(node, value){
3542 node.value = (value != null) ? value : '';
3547 propertyGetters['class'] = function(node){
3548 return ('className' in node) ? node.className || null : node.getAttribute('class');
3552 var el = document.createElement('button');
3553 // IE sets type as readonly and throws
3554 try { el.type = 'button'; } catch(e){}
3555 if (el.type != 'button') propertySetters.type = function(node, value){
3556 node.setAttribute('type', value);
3564 // #2479 - IE8 Cannot set HTML of style element
3565 var canChangeStyleHTML = (function(){
3566 var div = document.createElement('style'),
3569 div.innerHTML = '#justTesing{margin: 0px;}';
3570 flag = !!div.innerHTML;
3576 var input = document.createElement('input'), volatileInputValue, html5InputSupport;
3580 input.type = 'submit';
3581 volatileInputValue = input.value != 't';
3583 // #2443 - IE throws "Invalid Argument" when trying to use html5 input types
3585 input.type = 'email';
3586 html5InputSupport = input.type == 'email';
3591 if (volatileInputValue || !html5InputSupport) propertySetters.type = function(node, type){
3593 var value = node.value;
3600 /* getProperty, setProperty */
3603 var pollutesGetAttribute = (function(div){
3604 div.random = 'attribute';
3605 return (div.getAttribute('random') == 'attribute');
3606 })(document.createElement('div'));
3608 var hasCloneBug = (function(test){
3609 test.innerHTML = '<object><param name="should_fix" value="the unknown" /></object>';
3610 return test.cloneNode(true).firstChild.childNodes.length != 1;
3611 })(document.createElement('div'));
3614 var hasClassList = !!document.createElement('div').classList;
3616 var classes = function(className){
3617 var classNames = (className || '').clean().split(" "), uniques = {};
3618 return classNames.filter(function(className){
3619 if (className !== "" && !uniques[className]) return uniques[className] = className;
3623 var addToClassList = function(name){
3624 this.classList.add(name);
3627 var removeFromClassList = function(name){
3628 this.classList.remove(name);
3633 setProperty: function(name, value){
3634 var setter = propertySetters[name.toLowerCase()];
3636 setter(this, value);
3639 var attributeWhiteList;
3640 if (pollutesGetAttribute) attributeWhiteList = this.retrieve('$attributeWhiteList', {});
3644 this.removeAttribute(name);
3646 if (pollutesGetAttribute) delete attributeWhiteList[name];
3649 this.setAttribute(name, '' + value);
3651 if (pollutesGetAttribute) attributeWhiteList[name] = true;
3658 setProperties: function(attributes){
3659 for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
3663 getProperty: function(name){
3664 var getter = propertyGetters[name.toLowerCase()];
3665 if (getter) return getter(this);
3667 if (pollutesGetAttribute){
3668 var attr = this.getAttributeNode(name), attributeWhiteList = this.retrieve('$attributeWhiteList', {});
3669 if (!attr) return null;
3670 if (attr.expando && !attributeWhiteList[name]){
3671 var outer = this.outerHTML;
3672 // segment by the opening tag and find mention of attribute name
3673 if (outer.substr(0, outer.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(name) < 0) return null;
3674 attributeWhiteList[name] = true;
3678 var result = Slick.getAttribute(this, name);
3679 return (!result && !Slick.hasAttribute(this, name)) ? null : result;
3682 getProperties: function(){
3683 var args = Array.from(arguments);
3684 return args.map(this.getProperty, this).associate(args);
3687 removeProperty: function(name){
3688 return this.setProperty(name, null);
3691 removeProperties: function(){
3692 Array.each(arguments, this.removeProperty, this);
3696 set: function(prop, value){
3697 var property = Element.Properties[prop];
3698 (property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value);
3701 get: function(prop){
3702 var property = Element.Properties[prop];
3703 return (property && property.get) ? property.get.apply(this) : this.getProperty(prop);
3706 erase: function(prop){
3707 var property = Element.Properties[prop];
3708 (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
3712 hasClass: hasClassList ? function(className){
3713 return this.classList.contains(className);
3714 } : function(className){
3715 return classes(this.className).contains(className);
3718 addClass: hasClassList ? function(className){
3719 classes(className).forEach(addToClassList, this);
3721 } : function(className){
3722 this.className = classes(className + ' ' + this.className).join(' ');
3726 removeClass: hasClassList ? function(className){
3727 classes(className).forEach(removeFromClassList, this);
3729 } : function(className){
3730 var classNames = classes(this.className);
3731 classes(className).forEach(classNames.erase, classNames);
3732 this.className = classNames.join(' ');
3736 toggleClass: function(className, force){
3737 if (force == null) force = !this.hasClass(className);
3738 return (force) ? this.addClass(className) : this.removeClass(className);
3742 var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length;
3743 if (length > 1) parent = fragment = document.createDocumentFragment();
3745 for (var i = 0; i < length; i++){
3746 var element = document.id(elements[i], true);
3747 if (element) parent.appendChild(element);
3750 if (fragment) this.appendChild(fragment);
3755 appendText: function(text, where){
3756 return this.grab(this.getDocument().newTextNode(text), where);
3759 grab: function(el, where){
3760 inserters[where || 'bottom'](document.id(el, true), this);
3764 inject: function(el, where){
3765 inserters[where || 'bottom'](this, document.id(el, true));
3769 replaces: function(el){
3770 el = document.id(el, true);
3771 el.parentNode.replaceChild(this, el);
3775 wraps: function(el, where){
3776 el = document.id(el, true);
3777 return this.replaces(el).grab(el, where);
3780 getSelected: function(){
3781 this.selectedIndex; // Safari 3.2.1
3782 return new Elements(Array.from(this.options).filter(function(option){
3783 return option.selected;
3787 toQueryString: function(){
3788 var queryString = [];
3789 this.getElements('input, select, textarea').each(function(el){
3791 if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return;
3793 var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){
3795 return document.id(opt).get('value');
3796 }) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value');
3798 Array.from(value).each(function(val){
3799 if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val));
3802 return queryString.join('&');
3810 var appendInserters = {
3811 before: 'beforeBegin',
3813 bottom: 'beforeEnd',
3818 Element.implement('appendHTML', ('insertAdjacentHTML' in document.createElement('div')) ? function(html, where){
3819 this.insertAdjacentHTML(appendInserters[where || 'bottom'], html);
3821 } : function(html, where){
3822 var temp = new Element('div', {html: html}),
3823 children = temp.childNodes,
3824 fragment = temp.firstChild;
3826 if (!fragment) return this;
3827 if (children.length > 1){
3828 fragment = document.createDocumentFragment();
3829 for (var i = 0, l = children.length; i < l; i++){
3830 fragment.appendChild(children[i]);
3834 inserters[where || 'bottom'](fragment, this);
3838 var collected = {}, storage = {};
3840 var get = function(uid){
3841 return (storage[uid] || (storage[uid] = {}));
3844 var clean = function(item){
3845 var uid = item.uniqueNumber;
3846 if (item.removeEvents) item.removeEvents();
3847 if (item.clearAttributes) item.clearAttributes();
3849 delete collected[uid];
3850 delete storage[uid];
3855 var formProps = {input: 'checked', option: 'selected', textarea: 'value'};
3859 destroy: function(){
3860 var children = clean(this).getElementsByTagName('*');
3861 Array.each(children, clean);
3862 Element.dispose(this);
3867 Array.from(this.childNodes).each(Element.dispose);
3871 dispose: function(){
3872 return (this.parentNode) ? this.parentNode.removeChild(this) : this;
3875 clone: function(contents, keepid){
3876 contents = contents !== false;
3877 var clone = this.cloneNode(contents), ce = [clone], te = [this], i;
3880 ce.append(Array.from(clone.getElementsByTagName('*')));
3881 te.append(Array.from(this.getElementsByTagName('*')));
3884 for (i = ce.length; i--;){
3885 var node = ce[i], element = te[i];
3886 if (!keepid) node.removeAttribute('id');
3888 if (node.clearAttributes){
3889 node.clearAttributes();
3890 node.mergeAttributes(element);
3891 node.removeAttribute('uniqueNumber');
3893 var no = node.options, eo = element.options;
3894 for (var j = no.length; j--;) no[j].selected = eo[j].selected;
3898 var prop = formProps[element.tagName.toLowerCase()];
3899 if (prop && element[prop]) node[prop] = element[prop];
3904 var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object');
3905 for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML;
3908 return document.id(clone);
3913 [Element, Window, Document].invoke('implement', {
3915 addListener: function(type, fn){
3916 if (window.attachEvent && !window.addEventListener){
3917 collected[Slick.uidOf(this)] = this;
3919 if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]);
3920 else this.attachEvent('on' + type, fn);
3924 removeListener: function(type, fn){
3925 if (this.removeEventListener) this.removeEventListener(type, fn, !!arguments[2]);
3926 else this.detachEvent('on' + type, fn);
3930 retrieve: function(property, dflt){
3931 var storage = get(Slick.uidOf(this)), prop = storage[property];
3932 if (dflt != null && prop == null) prop = storage[property] = dflt;
3933 return prop != null ? prop : null;
3936 store: function(property, value){
3937 var storage = get(Slick.uidOf(this));
3938 storage[property] = value;
3942 eliminate: function(property){
3943 var storage = get(Slick.uidOf(this));
3944 delete storage[property];
3951 if (window.attachEvent && !window.addEventListener){
3952 var gc = function(){
3953 Object.each(collected, clean);
3954 if (window.CollectGarbage) CollectGarbage();
3955 window.removeListener('unload', gc);
3957 window.addListener('unload', gc);
3961 Element.Properties = {};
3965 Element.Properties = new Hash;
3969 Element.Properties.style = {
3971 set: function(style){
3972 this.style.cssText = style;
3976 return this.style.cssText;
3980 this.style.cssText = '';
3985 Element.Properties.tag = {
3988 return this.tagName.toLowerCase();
3993 Element.Properties.html = {
3995 set: function(html){
3996 if (html == null) html = '';
3997 else if (typeOf(html) == 'array') html = html.join('');
4000 if (this.styleSheet && !canChangeStyleHTML) this.styleSheet.cssText = html;
4001 else /*</ltIE9>*/this.innerHTML = html;
4004 this.set('html', '');
4009 var supportsHTML5Elements = true, supportsTableInnerHTML = true, supportsTRInnerHTML = true;
4012 // technique by jdbarlett - http://jdbartlett.com/innershiv/
4013 var div = document.createElement('div');
4014 div.innerHTML = '<nav></nav>';
4015 supportsHTML5Elements = (div.childNodes.length == 1);
4016 if (!supportsHTML5Elements){
4017 var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' '),
4018 fragment = document.createDocumentFragment(), l = tags.length;
4019 while (l--) fragment.createElement(tags[l]);
4025 supportsTableInnerHTML = Function.attempt(function(){
4026 var table = document.createElement('table');
4027 table.innerHTML = '<tr><td></td></tr>';
4032 var tr = document.createElement('tr'), html = '<td></td>';
4033 tr.innerHTML = html;
4034 supportsTRInnerHTML = (tr.innerHTML == html);
4038 if (!supportsTableInnerHTML || !supportsTRInnerHTML || !supportsHTML5Elements){
4040 Element.Properties.html.set = (function(set){
4042 var translations = {
4043 table: [1, '<table>', '</table>'],
4044 select: [1, '<select>', '</select>'],
4045 tbody: [2, '<table><tbody>', '</tbody></table>'],
4046 tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
4049 translations.thead = translations.tfoot = translations.tbody;
4051 return function(html){
4054 if (this.styleSheet) return set.call(this, html);
4056 var wrap = translations[this.get('tag')];
4057 if (!wrap && !supportsHTML5Elements) wrap = [0, '', ''];
4058 if (!wrap) return set.call(this, html);
4060 var level = wrap[0], wrapper = document.createElement('div'), target = wrapper;
4061 if (!supportsHTML5Elements) fragment.appendChild(wrapper);
4062 wrapper.innerHTML = [wrap[1], html, wrap[2]].flatten().join('');
4063 while (level--) target = target.firstChild;
4064 this.empty().adopt(target.childNodes);
4065 if (!supportsHTML5Elements) fragment.removeChild(wrapper);
4069 })(Element.Properties.html.set);
4074 var testForm = document.createElement('form');
4075 testForm.innerHTML = '<select><option>s</option></select>';
4077 if (testForm.firstChild.value != 's') Element.Properties.value = {
4079 set: function(value){
4080 var tag = this.get('tag');
4081 if (tag != 'select') return this.setProperty('value', value);
4082 var options = this.getElements('option');
4083 value = String(value);
4084 for (var i = 0; i < options.length; i++){
4085 var option = options[i],
4086 attr = option.getAttributeNode('value'),
4087 optionValue = (attr && attr.specified) ? option.value : option.get('text');
4088 if (optionValue === value) return option.selected = true;
4093 var option = this, tag = option.get('tag');
4095 if (tag != 'select' && tag != 'option') return this.getProperty('value');
4097 if (tag == 'select' && !(option = option.getSelected()[0])) return '';
4099 var attr = option.getAttributeNode('value');
4100 return (attr && attr.specified) ? option.value : option.get('text');
4108 if (document.createElement('div').getAttributeNode('id')) Element.Properties.id = {
4110 this.id = this.getAttributeNode('id').value = id;
4113 return this.id || null;
4116 this.id = this.getAttributeNode('id').value = '';
4128 description: Contains the Event Type, to make the event object cross-browser.
4130 license: MIT-style license.
4132 requires: [Window, Document, Array, Function, String, Object]
4142 var normalizeWheelSpeed = function(event){
4144 if (event.wheelDelta){
4145 normalized = event.wheelDelta % 120 == 0 ? event.wheelDelta / 120 : event.wheelDelta / 12;
4147 var rawAmount = event.deltaY || event.detail || 0;
4148 normalized = -(rawAmount % 3 == 0 ? rawAmount / 3 : rawAmount * 10);
4153 var DOMEvent = this.DOMEvent = new Type('DOMEvent', function(event, win){
4154 if (!win) win = window;
4155 event = event || win.event;
4156 if (event.$extended) return event;
4158 this.$extended = true;
4159 this.shift = event.shiftKey;
4160 this.control = event.ctrlKey;
4161 this.alt = event.altKey;
4162 this.meta = event.metaKey;
4163 var type = this.type = event.type;
4164 var target = event.target || event.srcElement;
4165 while (target && target.nodeType == 3) target = target.parentNode;
4166 this.target = document.id(target);
4168 if (type.indexOf('key') == 0){
4169 var code = this.code = (event.which || event.keyCode);
4170 this.key = _keys[code]/*<1.3compat>*/ || Object.keyOf(Event.Keys, code)/*</1.3compat>*/;
4171 if (type == 'keydown' || type == 'keyup'){
4172 if (code > 111 && code < 124) this.key = 'f' + (code - 111);
4173 else if (code > 95 && code < 106) this.key = code - 96;
4175 if (this.key == null) this.key = String.fromCharCode(code).toLowerCase();
4176 } else if (type == 'click' || type == 'dblclick' || type == 'contextmenu' || type == 'wheel' || type == 'DOMMouseScroll' || type.indexOf('mouse') == 0){
4177 var doc = win.document;
4178 doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
4180 x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
4181 y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
4184 x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
4185 y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
4187 if (type == 'DOMMouseScroll' || type == 'wheel' || type == 'mousewheel') this.wheel = normalizeWheelSpeed(event);
4188 this.rightClick = (event.which == 3 || event.button == 2);
4189 if (type == 'mouseover' || type == 'mouseout'){
4190 var related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element'];
4191 while (related && related.nodeType == 3) related = related.parentNode;
4192 this.relatedTarget = document.id(related);
4194 } else if (type.indexOf('touch') == 0 || type.indexOf('gesture') == 0){
4195 this.rotation = event.rotation;
4196 this.scale = event.scale;
4197 this.targetTouches = event.targetTouches;
4198 this.changedTouches = event.changedTouches;
4199 var touches = this.touches = event.touches;
4200 if (touches && touches[0]){
4201 var touch = touches[0];
4202 this.page = {x: touch.pageX, y: touch.pageY};
4203 this.client = {x: touch.clientX, y: touch.clientY};
4207 if (!this.client) this.client = {};
4208 if (!this.page) this.page = {};
4211 DOMEvent.implement({
4214 return this.preventDefault().stopPropagation();
4217 stopPropagation: function(){
4218 if (this.event.stopPropagation) this.event.stopPropagation();
4219 else this.event.cancelBubble = true;
4223 preventDefault: function(){
4224 if (this.event.preventDefault) this.event.preventDefault();
4225 else this.event.returnValue = false;
4231 DOMEvent.defineKey = function(code, key){
4236 DOMEvent.defineKeys = DOMEvent.defineKey.overloadSetter(true);
4238 DOMEvent.defineKeys({
4239 '38': 'up', '40': 'down', '37': 'left', '39': 'right',
4240 '27': 'esc', '32': 'space', '8': 'backspace', '9': 'tab',
4241 '46': 'delete', '13': 'enter'
4247 var Event = DOMEvent;
4253 Event.Keys = new Hash(Event.Keys);
4262 description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events, if necessary.
4264 license: MIT-style license.
4266 requires: [Element, Event]
4268 provides: Element.Event
4275 Element.Properties.events = {set: function(events){
4276 this.addEvents(events);
4279 [Element, Window, Document].invoke('implement', {
4281 addEvent: function(type, fn){
4282 var events = this.retrieve('events', {});
4283 if (!events[type]) events[type] = {keys: [], values: []};
4284 if (events[type].keys.contains(fn)) return this;
4285 events[type].keys.push(fn);
4286 var realType = type,
4287 custom = Element.Events[type],
4291 if (custom.onAdd) custom.onAdd.call(this, fn, type);
4292 if (custom.condition){
4293 condition = function(event){
4294 if (custom.condition.call(this, event, type)) return fn.call(this, event);
4298 if (custom.base) realType = Function.from(custom.base).call(this, type);
4300 var defn = function(){
4301 return fn.call(self);
4303 var nativeEvent = Element.NativeEvents[realType];
4305 if (nativeEvent == 2){
4306 defn = function(event){
4307 event = new DOMEvent(event, self.getWindow());
4308 if (condition.call(self, event) === false) event.stop();
4311 this.addListener(realType, defn, arguments[2]);
4313 events[type].values.push(defn);
4317 removeEvent: function(type, fn){
4318 var events = this.retrieve('events');
4319 if (!events || !events[type]) return this;
4320 var list = events[type];
4321 var index = list.keys.indexOf(fn);
4322 if (index == -1) return this;
4323 var value = list.values[index];
4324 delete list.keys[index];
4325 delete list.values[index];
4326 var custom = Element.Events[type];
4328 if (custom.onRemove) custom.onRemove.call(this, fn, type);
4329 if (custom.base) type = Function.from(custom.base).call(this, type);
4331 return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this;
4334 addEvents: function(events){
4335 for (var event in events) this.addEvent(event, events[event]);
4339 removeEvents: function(events){
4341 if (typeOf(events) == 'object'){
4342 for (type in events) this.removeEvent(type, events[type]);
4345 var attached = this.retrieve('events');
4346 if (!attached) return this;
4348 for (type in attached) this.removeEvents(type);
4349 this.eliminate('events');
4350 } else if (attached[events]){
4351 attached[events].keys.each(function(fn){
4352 this.removeEvent(events, fn);
4354 delete attached[events];
4359 fireEvent: function(type, args, delay){
4360 var events = this.retrieve('events');
4361 if (!events || !events[type]) return this;
4362 args = Array.from(args);
4364 events[type].keys.each(function(fn){
4365 if (delay) fn.delay(delay, this, args);
4366 else fn.apply(this, args);
4371 cloneEvents: function(from, type){
4372 from = document.id(from);
4373 var events = from.retrieve('events');
4374 if (!events) return this;
4376 for (var eventType in events) this.cloneEvents(from, eventType);
4377 } else if (events[type]){
4378 events[type].keys.each(function(fn){
4379 this.addEvent(type, fn);
4387 Element.NativeEvents = {
4388 click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
4389 wheel: 2, mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
4390 mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
4391 keydown: 2, keypress: 2, keyup: 2, //keyboard
4392 orientationchange: 2, // mobile
4393 touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch
4394 gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture
4395 focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements
4396 load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
4397 hashchange: 1, popstate: 2, // history
4398 error: 1, abort: 1, scroll: 1, message: 2 //misc
4403 base: 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll'
4407 var check = function(event){
4408 var related = event.relatedTarget;
4409 if (related == null) return true;
4410 if (!related) return false;
4411 return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
4414 if ('onmouseenter' in document.documentElement){
4415 Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2;
4416 Element.MouseenterCheck = check;
4418 Element.Events.mouseenter = {
4423 Element.Events.mouseleave = {
4430 if (!window.addEventListener){
4431 Element.NativeEvents.propertychange = 2;
4432 Element.Events.change = {
4434 var type = this.type;
4435 return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change';
4437 condition: function(event){
4438 return event.type != 'propertychange' || event.event.propertyName == 'checked';
4446 Element.Events = new Hash(Element.Events);
4455 name: Element.Delegation
4457 description: Extends the Element native object to include the delegate method for more efficient event management.
4459 license: MIT-style license.
4461 requires: [Element.Event]
4463 provides: [Element.Delegation]
4470 var eventListenerSupport = !!window.addEventListener;
4472 Element.NativeEvents.focusin = Element.NativeEvents.focusout = 2;
4474 var bubbleUp = function(self, match, fn, event, target){
4475 while (target && target != self){
4476 if (match(target, event)) return fn.call(target, event, target);
4477 target = document.id(target.parentNode);
4484 condition: Element.MouseenterCheck
4488 condition: Element.MouseenterCheck
4491 base: 'focus' + (eventListenerSupport ? '' : 'in'),
4495 base: eventListenerSupport ? 'blur' : 'focusout',
4501 var _key = '$delegation:';
4502 var formObserver = function(type){
4508 remove: function(self, uid){
4509 var list = self.retrieve(_key + type + 'listeners', {})[uid];
4510 if (list && list.forms) for (var i = list.forms.length; i--;){
4511 list.forms[i].removeEvent(type, list.fns[i]);
4515 listen: function(self, match, fn, event, target, uid){
4516 var form = (target.get('tag') == 'form') ? target : event.target.getParent('form');
4519 var listeners = self.retrieve(_key + type + 'listeners', {}),
4520 listener = listeners[uid] || {forms: [], fns: []},
4521 forms = listener.forms, fns = listener.fns;
4523 if (forms.indexOf(form) != -1) return;
4526 var _fn = function(event){
4527 bubbleUp(self, match, fn, event, target);
4529 form.addEvent(type, _fn);
4532 listeners[uid] = listener;
4533 self.store(_key + type + 'listeners', listeners);
4538 var inputObserver = function(type){
4541 listen: function(self, match, fn, event, target){
4542 var events = {blur: function(){
4543 this.removeEvents(events);
4545 events[type] = function(event){
4546 bubbleUp(self, match, fn, event, target);
4548 event.target.addEvents(events);
4553 if (!eventListenerSupport) Object.append(map, {
4554 submit: formObserver('submit'),
4555 reset: formObserver('reset'),
4556 change: inputObserver('change'),
4557 select: inputObserver('select')
4561 var proto = Element.prototype,
4562 addEvent = proto.addEvent,
4563 removeEvent = proto.removeEvent;
4565 var relay = function(old, method){
4566 return function(type, fn, useCapture){
4567 if (type.indexOf(':relay') == -1) return old.call(this, type, fn, useCapture);
4568 var parsed = Slick.parse(type).expressions[0][0];
4569 if (parsed.pseudos[0].key != 'relay') return old.call(this, type, fn, useCapture);
4570 var newType = parsed.tag;
4571 parsed.pseudos.slice(1).each(function(pseudo){
4572 newType += ':' + pseudo.key + (pseudo.value ? '(' + pseudo.value + ')' : '');
4574 old.call(this, type, fn);
4575 return method.call(this, newType, parsed.pseudos[0].value, fn);
4581 addEvent: function(type, match, fn){
4582 var storage = this.retrieve('$delegates', {}), stored = storage[type];
4583 if (stored) for (var _uid in stored){
4584 if (stored[_uid].fn == fn && stored[_uid].match == match) return this;
4587 var _type = type, _match = match, _fn = fn, _map = map[type] || {};
4588 type = _map.base || _type;
4590 match = function(target){
4591 return Slick.match(target, _match);
4594 var elementEvent = Element.Events[_type];
4595 if (_map.condition || elementEvent && elementEvent.condition){
4596 var __match = match, condition = _map.condition || elementEvent.condition;
4597 match = function(target, event){
4598 return __match(target, event) && condition.call(target, event, type);
4602 var self = this, uid = String.uniqueID();
4603 var delegator = _map.listen ? function(event, target){
4604 if (!target && event && event.target) target = event.target;
4605 if (target) _map.listen(self, match, fn, event, target, uid);
4606 } : function(event, target){
4607 if (!target && event && event.target) target = event.target;
4608 if (target) bubbleUp(self, match, fn, event, target);
4611 if (!stored) stored = {};
4615 delegator: delegator
4617 storage[_type] = stored;
4618 return addEvent.call(this, type, delegator, _map.capture);
4621 removeEvent: function(type, match, fn, _uid){
4622 var storage = this.retrieve('$delegates', {}), stored = storage[type];
4623 if (!stored) return this;
4626 var _type = type, delegator = stored[_uid].delegator, _map = map[type] || {};
4627 type = _map.base || _type;
4628 if (_map.remove) _map.remove(this, _uid);
4629 delete stored[_uid];
4630 storage[_type] = stored;
4631 return removeEvent.call(this, type, delegator, _map.capture);
4635 if (fn) for (__uid in stored){
4637 if (s.match == match && s.fn == fn) return delegation.removeEvent.call(this, type, match, fn, __uid);
4638 } else for (__uid in stored){
4640 if (s.match == match) delegation.removeEvent.call(this, type, match, s.fn, __uid);
4647 [Element, Window, Document].invoke('implement', {
4648 addEvent: relay(addEvent, delegation.addEvent),
4649 removeEvent: relay(removeEvent, delegation.removeEvent)
4659 description: Contains methods for interacting with the styles of Elements in a fashionable way.
4661 license: MIT-style license.
4665 provides: Element.Style
4672 var html = document.html, el;
4675 // Check for oldIE, which does not remove styles when they're set to null
4676 el = document.createElement('div');
4677 el.style.color = 'red';
4678 el.style.color = null;
4679 var doesNotRemoveStyles = el.style.color == 'red';
4681 // check for oldIE, which returns border* shorthand styles in the wrong order (color-width-style instead of width-style-color)
4682 var border = '1px solid #123abc';
4683 el.style.border = border;
4684 var returnsBordersInWrongOrder = el.style.border != border;
4688 var hasGetComputedStyle = !!window.getComputedStyle,
4689 supportBorderRadius = document.createElement('div').style.borderRadius != null;
4691 Element.Properties.styles = {set: function(styles){
4692 this.setStyles(styles);
4695 var hasOpacity = (html.style.opacity != null),
4696 hasFilter = (html.style.filter != null),
4697 reAlpha = /alpha\(opacity=([\d.]+)\)/i;
4699 var setVisibility = function(element, opacity){
4700 element.store('$opacity', opacity);
4701 element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden';
4705 var setFilter = function(element, regexp, value){
4706 var style = element.style,
4707 filter = style.filter || element.getComputedStyle('filter') || '';
4708 style.filter = (regexp.test(filter) ? filter.replace(regexp, value) : filter + ' ' + value).trim();
4709 if (!style.filter) style.removeAttribute('filter');
4713 var setOpacity = (hasOpacity ? function(element, opacity){
4714 element.style.opacity = opacity;
4715 } : (hasFilter ? function(element, opacity){
4716 if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1;
4717 if (opacity == null || opacity == 1){
4718 setFilter(element, reAlpha, '');
4719 if (opacity == 1 && getOpacity(element) != 1) setFilter(element, reAlpha, 'alpha(opacity=100)');
4721 setFilter(element, reAlpha, 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')');
4723 } : setVisibility));
4725 var getOpacity = (hasOpacity ? function(element){
4726 var opacity = element.style.opacity || element.getComputedStyle('opacity');
4727 return (opacity == '') ? 1 : opacity.toFloat();
4728 } : (hasFilter ? function(element){
4729 var filter = (element.style.filter || element.getComputedStyle('filter')),
4731 if (filter) opacity = filter.match(reAlpha);
4732 return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
4733 } : function(element){
4734 var opacity = element.retrieve('$opacity');
4735 if (opacity == null) opacity = (element.style.visibility == 'hidden' ? 0 : 1);
4739 var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat',
4740 namedPositions = {left: '0%', top: '0%', center: '50%', right: '100%', bottom: '100%'},
4741 hasBackgroundPositionXY = (html.style.backgroundPositionX != null);
4744 var removeStyle = function(style, property){
4745 if (property == 'backgroundPosition'){
4746 style.removeAttribute(property + 'X');
4749 style.removeAttribute(property);
4755 getComputedStyle: function(property){
4756 if (!hasGetComputedStyle && this.currentStyle) return this.currentStyle[property.camelCase()];
4757 var defaultView = Element.getDocument(this).defaultView,
4758 computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
4759 return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : '';
4762 setStyle: function(property, value){
4763 if (property == 'opacity'){
4764 if (value != null) value = parseFloat(value);
4765 setOpacity(this, value);
4768 property = (property == 'float' ? floatName : property).camelCase();
4769 if (typeOf(value) != 'string'){
4770 var map = (Element.Styles[property] || '@').split(' ');
4771 value = Array.from(value).map(function(val, i){
4772 if (!map[i]) return '';
4773 return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
4775 } else if (value == String(Number(value))){
4776 value = Math.round(value);
4778 this.style[property] = value;
4780 if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){
4781 removeStyle(this.style, property);
4787 getStyle: function(property){
4788 if (property == 'opacity') return getOpacity(this);
4789 property = (property == 'float' ? floatName : property).camelCase();
4790 if (supportBorderRadius && property.indexOf('borderRadius') != -1){
4791 return ['borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius'].map(function(corner){
4792 return this.style[corner] || '0px';
4795 var result = this.style[property];
4796 if (!result || property == 'zIndex'){
4797 if (Element.ShortStyles.hasOwnProperty(property)){
4799 for (var s in Element.ShortStyles[property]) result.push(this.getStyle(s));
4800 return result.join(' ');
4802 result = this.getComputedStyle(property);
4804 if (hasBackgroundPositionXY && /^backgroundPosition[XY]?$/.test(property)){
4805 return result.replace(/(top|right|bottom|left)/g, function(position){
4806 return namedPositions[position];
4809 if (!result && property == 'backgroundPosition') return '0px 0px';
4811 result = String(result);
4812 var color = result.match(/rgba?\([\d\s,]+\)/);
4813 if (color) result = result.replace(color[0], color[0].rgbToHex());
4815 if (!hasGetComputedStyle && !this.style[property]){
4816 if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){
4817 var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
4818 values.each(function(value){
4819 size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
4821 return this['offset' + property.capitalize()] - size + 'px';
4823 if ((/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){
4828 if (returnsBordersInWrongOrder && /^border(Top|Right|Bottom|Left)?$/.test(property) && /^#/.test(result)){
4829 return result.replace(/^(.+)\s(.+)\s(.+)$/, '$2 $3 $1');
4836 setStyles: function(styles){
4837 for (var style in styles) this.setStyle(style, styles[style]);
4841 getStyles: function(){
4843 Array.flatten(arguments).each(function(key){
4844 result[key] = this.getStyle(key);
4852 left: '@px', top: '@px', bottom: '@px', right: '@px',
4853 width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
4854 backgroundColor: 'rgb(@, @, @)', backgroundSize: '@px', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
4855 fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
4856 margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
4857 borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
4858 zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@', borderRadius: '@px @px @px @px'
4865 setOpacity: function(value){
4866 setOpacity(this, value);
4870 getOpacity: function(){
4871 return getOpacity(this);
4876 Element.Properties.opacity = {
4878 set: function(opacity){
4879 setOpacity(this, opacity);
4880 setVisibility(this, opacity);
4884 return getOpacity(this);
4893 Element.Styles = new Hash(Element.Styles);
4897 Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
4899 ['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
4900 var Short = Element.ShortStyles;
4901 var All = Element.Styles;
4902 ['margin', 'padding'].each(function(style){
4903 var sd = style + direction;
4904 Short[style][sd] = All[sd] = '@px';
4906 var bd = 'border' + direction;
4907 Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
4908 var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
4910 Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
4911 Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
4912 Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
4915 if (hasBackgroundPositionXY) Element.ShortStyles.backgroundPosition = {backgroundPositionX: '@', backgroundPositionY: '@'};
4921 name: Element.Dimensions
4923 description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
4925 license: MIT-style license.
4928 - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
4929 - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
4931 requires: [Element, Element.Style]
4933 provides: [Element.Dimensions]
4940 var element = document.createElement('div'),
4941 child = document.createElement('div');
4942 element.style.height = '0';
4943 element.appendChild(child);
4944 var brokenOffsetParent = (child.offsetParent === element);
4945 element = child = null;
4947 var heightComponents = ['height', 'paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth'],
4948 widthComponents = ['width', 'paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth'];
4950 var svgCalculateSize = function(el){
4952 var gCS = window.getComputedStyle(el),
4953 bounds = {x: 0, y: 0};
4955 heightComponents.each(function(css){
4956 bounds.y += parseFloat(gCS[css]);
4958 widthComponents.each(function(css){
4959 bounds.x += parseFloat(gCS[css]);
4964 var isOffset = function(el){
4965 return styleString(el, 'position') != 'static' || isBody(el);
4968 var isOffsetStatic = function(el){
4969 return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
4974 scrollTo: function(x, y){
4976 this.getWindow().scrollTo(x, y);
4978 this.scrollLeft = x;
4984 getSize: function(){
4985 if (isBody(this)) return this.getWindow().getSize();
4988 // This if clause is because IE8- cannot calculate getBoundingClientRect of elements with visibility hidden.
4989 if (!window.getComputedStyle) return {x: this.offsetWidth, y: this.offsetHeight};
4992 // This svg section under, calling `svgCalculateSize()`, can be removed when FF fixed the svg size bug.
4993 // Bug info: https://bugzilla.mozilla.org/show_bug.cgi?id=530985
4994 if (this.get('tag') == 'svg') return svgCalculateSize(this);
4996 var bounds = this.getBoundingClientRect();
4997 return {x: bounds.width, y: bounds.height};
5000 getScrollSize: function(){
5001 if (isBody(this)) return this.getWindow().getScrollSize();
5002 return {x: this.scrollWidth, y: this.scrollHeight};
5005 getScroll: function(){
5006 if (isBody(this)) return this.getWindow().getScroll();
5007 return {x: this.scrollLeft, y: this.scrollTop};
5010 getScrolls: function(){
5011 var element = this.parentNode, position = {x: 0, y: 0};
5012 while (element && !isBody(element)){
5013 position.x += element.scrollLeft;
5014 position.y += element.scrollTop;
5015 element = element.parentNode;
5020 getOffsetParent: brokenOffsetParent ? function(){
5022 if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
5024 var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
5025 while ((element = element.parentNode)){
5026 if (isOffsetCheck(element)) return element;
5031 if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
5034 return element.offsetParent;
5039 getOffsets: function(){
5040 var hasGetBoundingClientRect = this.getBoundingClientRect;
5042 hasGetBoundingClientRect = hasGetBoundingClientRect && !Browser.Platform.ios
5044 if (hasGetBoundingClientRect){
5045 var bound = this.getBoundingClientRect(),
5046 html = document.id(this.getDocument().documentElement),
5047 htmlScroll = html.getScroll(),
5048 elemScrolls = this.getScrolls(),
5049 isFixed = (styleString(this, 'position') == 'fixed');
5052 x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
5053 y: bound.top.toInt() + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
5057 var element = this, position = {x: 0, y: 0};
5058 if (isBody(this)) return position;
5060 while (element && !isBody(element)){
5061 position.x += element.offsetLeft;
5062 position.y += element.offsetTop;
5064 if (Browser.firefox){
5065 if (!borderBox(element)){
5066 position.x += leftBorder(element);
5067 position.y += topBorder(element);
5069 var parent = element.parentNode;
5070 if (parent && styleString(parent, 'overflow') != 'visible'){
5071 position.x += leftBorder(parent);
5072 position.y += topBorder(parent);
5074 } else if (element != this && Browser.safari){
5075 position.x += leftBorder(element);
5076 position.y += topBorder(element);
5079 element = element.offsetParent;
5082 if (Browser.firefox && !borderBox(this)){
5083 position.x -= leftBorder(this);
5084 position.y -= topBorder(this);
5090 getPosition: function(relative){
5091 var offset = this.getOffsets(),
5092 scroll = this.getScrolls();
5094 x: offset.x - scroll.x,
5095 y: offset.y - scroll.y
5098 if (relative && (relative = document.id(relative))){
5099 var relativePosition = relative.getPosition();
5100 return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
5105 getCoordinates: function(element){
5106 if (isBody(this)) return this.getWindow().getCoordinates();
5107 var position = this.getPosition(element),
5108 size = this.getSize();
5115 obj.right = obj.left + obj.width;
5116 obj.bottom = obj.top + obj.height;
5120 computePosition: function(obj){
5122 left: obj.x - styleNumber(this, 'margin-left'),
5123 top: obj.y - styleNumber(this, 'margin-top')
5127 setPosition: function(obj){
5128 return this.setStyles(this.computePosition(obj));
5134 [Document, Window].invoke('implement', {
5136 getSize: function(){
5137 var doc = getCompatElement(this);
5138 return {x: doc.clientWidth, y: doc.clientHeight};
5141 getScroll: function(){
5142 var win = this.getWindow(), doc = getCompatElement(this);
5143 return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
5146 getScrollSize: function(){
5147 var doc = getCompatElement(this),
5148 min = this.getSize(),
5149 body = this.getDocument().body;
5151 return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)};
5154 getPosition: function(){
5155 return {x: 0, y: 0};
5158 getCoordinates: function(){
5159 var size = this.getSize();
5160 return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
5167 var styleString = Element.getComputedStyle;
5169 function styleNumber(element, style){
5170 return styleString(element, style).toInt() || 0;
5173 function borderBox(element){
5174 return styleString(element, '-moz-box-sizing') == 'border-box';
5177 function topBorder(element){
5178 return styleNumber(element, 'border-top-width');
5181 function leftBorder(element){
5182 return styleNumber(element, 'border-left-width');
5185 function isBody(element){
5186 return (/^(?:body|html)$/i).test(element.tagName);
5189 function getCompatElement(element){
5190 var doc = element.getDocument();
5191 return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
5197 Element.alias({position: 'setPosition'}); //compatability
5199 [Window, Document, Element].invoke('implement', {
5201 getHeight: function(){
5202 return this.getSize().y;
5205 getWidth: function(){
5206 return this.getSize().x;
5209 getScrollTop: function(){
5210 return this.getScroll().y;
5213 getScrollLeft: function(){
5214 return this.getScroll().x;
5217 getScrollHeight: function(){
5218 return this.getScrollSize().y;
5221 getScrollWidth: function(){
5222 return this.getScrollSize().x;
5226 return this.getPosition().y;
5229 getLeft: function(){
5230 return this.getPosition().x;
5240 description: Contains the basic animation logic to be extended by all other Fx Classes.
5242 license: MIT-style license.
5244 requires: [Chain, Events, Options]
5253 var Fx = this.Fx = new Class({
5255 Implements: [Chain, Events, Options],
5271 initialize: function(options){
5272 this.subject = this.subject || this;
5273 this.setOptions(options);
5276 getTransition: function(){
5278 return -(Math.cos(Math.PI * p) - 1) / 2;
5282 step: function(now){
5283 if (this.options.frameSkip){
5284 var diff = (this.time != null) ? (now - this.time) : 0, frames = diff / this.frameInterval;
5286 this.frame += frames;
5291 if (this.frame < this.frames){
5292 var delta = this.transition(this.frame / this.frames);
5293 this.set(this.compute(this.from, this.to, delta));
5295 this.frame = this.frames;
5296 this.set(this.compute(this.from, this.to, 1));
5305 compute: function(from, to, delta){
5306 return Fx.compute(from, to, delta);
5310 if (!this.isRunning()) return true;
5311 switch (this.options.link){
5312 case 'cancel': this.cancel(); return true;
5313 case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
5318 start: function(from, to){
5319 if (!this.check(from, to)) return this;
5322 this.frame = (this.options.frameSkip) ? 0 : -1;
5324 this.transition = this.getTransition();
5325 var frames = this.options.frames, fps = this.options.fps, duration = this.options.duration;
5326 this.duration = Fx.Durations[duration] || duration.toInt();
5327 this.frameInterval = 1000 / fps;
5328 this.frames = frames || Math.round(this.duration / this.frameInterval);
5329 this.fireEvent('start', this.subject);
5330 pushInstance.call(this, fps);
5335 if (this.isRunning()){
5337 pullInstance.call(this, this.options.fps);
5338 if (this.frames == this.frame){
5339 this.fireEvent('complete', this.subject);
5340 if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
5342 this.fireEvent('stop', this.subject);
5349 if (this.isRunning()){
5351 pullInstance.call(this, this.options.fps);
5352 this.frame = this.frames;
5353 this.fireEvent('cancel', this.subject).clearChain();
5359 if (this.isRunning()){
5361 pullInstance.call(this, this.options.fps);
5367 if (this.isPaused()) pushInstance.call(this, this.options.fps);
5371 isRunning: function(){
5372 var list = instances[this.options.fps];
5373 return list && list.contains(this);
5376 isPaused: function(){
5377 return (this.frame < this.frames) && !this.isRunning();
5382 Fx.compute = function(from, to, delta){
5383 return (to - from) * delta + from;
5386 Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
5390 var instances = {}, timers = {};
5392 var loop = function(){
5393 var now = Date.now();
5394 for (var i = this.length; i--;){
5395 var instance = this[i];
5396 if (instance) instance.step(now);
5400 var pushInstance = function(fps){
5401 var list = instances[fps] || (instances[fps] = []);
5403 if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list);
5406 var pullInstance = function(fps){
5407 var list = instances[fps];
5410 if (!list.length && timers[fps]){
5411 delete instances[fps];
5412 timers[fps] = clearInterval(timers[fps]);
5424 description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
5426 license: MIT-style license.
5428 requires: [Fx, Element.Style]
5435 Fx.CSS = new Class({
5439 //prepares the base from/to object
5441 prepare: function(element, property, values){
5442 values = Array.from(values);
5443 var from = values[0], to = values[1];
5446 from = element.getStyle(property);
5447 var unit = this.options.unit;
5448 // adapted from: https://github.com/ryanmorr/fx/blob/master/fx.js#L299
5449 if (unit && from && typeof from == 'string' && from.slice(-unit.length) != unit && parseFloat(from) != 0){
5450 element.setStyle(property, to + unit);
5451 var value = element.getComputedStyle(property);
5452 // IE and Opera support pixelLeft or pixelWidth
5453 if (!(/px$/.test(value))){
5454 value = element.style[('pixel-' + property).camelCase()];
5456 // adapted from Dean Edwards' http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
5457 var left = element.style.left;
5458 element.style.left = to + unit;
5459 value = element.style.pixelLeft;
5460 element.style.left = left;
5463 from = (to || 1) / (parseFloat(value) || 1) * (parseFloat(from) || 0);
5464 element.setStyle(property, from + unit);
5467 return {from: this.parse(from), to: this.parse(to)};
5470 //parses a value into an array
5472 parse: function(value){
5473 value = Function.from(value)();
5474 value = (typeof value == 'string') ? value.split(' ') : Array.from(value);
5475 return value.map(function(val){
5478 Object.each(Fx.CSS.Parsers, function(parser, key){
5480 var parsed = parser.parse(val);
5481 if (parsed || parsed === 0) found = {value: parsed, parser: parser};
5483 found = found || {value: val, parser: Fx.CSS.Parsers.String};
5488 //computes by a from and to prepared objects, using their parsers.
5490 compute: function(from, to, delta){
5492 (Math.min(from.length, to.length)).times(function(i){
5493 computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
5495 computed.$family = Function.from('fx:css:value');
5499 //serves the value as settable
5501 serve: function(value, unit){
5502 if (typeOf(value) != 'fx:css:value') value = this.parse(value);
5504 value.each(function(bit){
5505 returned = returned.concat(bit.parser.serve(bit.value, unit));
5510 //renders the change to an element
5512 render: function(element, property, value, unit){
5513 element.setStyle(property, this.serve(value, unit));
5516 //searches inside the page css to find the values for a selector
5518 search: function(selector){
5519 if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
5520 var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
5522 var searchStyles = function(rules){
5523 Array.each(rules, function(rule, i){
5525 searchStyles(rule.rules || rule.cssRules);
5528 if (!rule.style) return;
5529 var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
5530 return m.toLowerCase();
5532 if (!selectorText || !selectorTest.test(selectorText)) return;
5533 Object.each(Element.Styles, function(value, style){
5534 if (!rule.style[style] || Element.ShortStyles[style]) return;
5535 value = String(rule.style[style]);
5536 to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
5541 Array.each(document.styleSheets, function(sheet, j){
5542 var href = sheet.href;
5543 if (href && href.indexOf('://') > -1 && href.indexOf(document.domain) == -1) return;
5544 var rules = sheet.rules || sheet.cssRules;
5545 searchStyles(rules);
5547 return Fx.CSS.Cache[selector] = to;
5557 parse: function(value){
5558 if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
5559 return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
5561 compute: function(from, to, delta){
5562 return from.map(function(value, i){
5563 return Math.round(Fx.compute(from[i], to[i], delta));
5566 serve: function(value){
5567 return value.map(Number);
5573 compute: Fx.compute,
5574 serve: function(value, unit){
5575 return (unit) ? value + unit : value;
5580 parse: Function.from(false),
5581 compute: function(zero, one){
5584 serve: function(zero){
5593 Fx.CSS.Parsers = new Hash(Fx.CSS.Parsers);
5602 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.
5604 license: MIT-style license.
5613 Fx.Morph = new Class({
5617 initialize: function(element, options){
5618 this.element = this.subject = document.id(element);
5619 this.parent(options);
5623 if (typeof now == 'string') now = this.search(now);
5624 for (var p in now) this.render(this.element, p, now[p], this.options.unit);
5628 compute: function(from, to, delta){
5630 for (var p in from) now[p] = this.parent(from[p], to[p], delta);
5634 start: function(properties){
5635 if (!this.check(properties)) return this;
5636 if (typeof properties == 'string') properties = this.search(properties);
5637 var from = {}, to = {};
5638 for (var p in properties){
5639 var parsed = this.prepare(this.element, p, properties[p]);
5640 from[p] = parsed.from;
5643 return this.parent(from, to);
5648 Element.Properties.morph = {
5650 set: function(options){
5651 this.get('morph').cancel().setOptions(options);
5656 var morph = this.retrieve('morph');
5658 morph = new Fx.Morph(this, {link: 'cancel'});
5659 this.store('morph', morph);
5668 morph: function(props){
5669 this.get('morph').start(props);
5678 name: Fx.Transitions
5680 description: Contains a set of advanced transitions to be used with any of the Fx Classes.
5682 license: MIT-style license.
5685 - Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
5689 provides: Fx.Transitions
5696 getTransition: function(){
5697 var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
5698 if (typeof trans == 'string'){
5699 var data = trans.split(':');
5700 trans = Fx.Transitions;
5701 trans = trans[data[0]] || trans[data[0].capitalize()];
5702 if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
5709 Fx.Transition = function(transition, params){
5710 params = Array.from(params);
5711 var easeIn = function(pos){
5712 return transition(pos, params);
5714 return Object.append(easeIn, {
5716 easeOut: function(pos){
5717 return 1 - transition(1 - pos, params);
5719 easeInOut: function(pos){
5720 return (pos <= 0.5 ? transition(2 * pos, params) : (2 - transition(2 * (1 - pos), params))) / 2;
5727 linear: function(zero){
5735 Fx.Transitions = new Hash(Fx.Transitions);
5739 Fx.Transitions.extend = function(transitions){
5740 for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
5743 Fx.Transitions.extend({
5745 Pow: function(p, x){
5746 return Math.pow(p, x && x[0] || 6);
5750 return Math.pow(2, 8 * (p - 1));
5754 return 1 - Math.sin(Math.acos(p));
5758 return 1 - Math.cos(p * Math.PI / 2);
5761 Back: function(p, x){
5762 x = x && x[0] || 1.618;
5763 return Math.pow(p, 2) * ((x + 1) * p - x);
5766 Bounce: function(p){
5768 for (var a = 0, b = 1; 1; a += b, b /= 2){
5769 if (p >= (7 - 4 * a) / 11){
5770 value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
5777 Elastic: function(p, x){
5778 return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && x[0] || 1) / 3);
5783 ['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
5784 Fx.Transitions[transition] = new Fx.Transition(function(p){
5785 return Math.pow(p, i + 2);
5794 description: Formerly Fx.Style, effect to transition any CSS property for an element.
5796 license: MIT-style license.
5800 provides: [Fx.Tween, Element.fade, Element.highlight]
5805 Fx.Tween = new Class({
5809 initialize: function(element, options){
5810 this.element = this.subject = document.id(element);
5811 this.parent(options);
5814 set: function(property, now){
5815 if (arguments.length == 1){
5817 property = this.property || this.options.property;
5819 this.render(this.element, property, now, this.options.unit);
5823 start: function(property, from, to){
5824 if (!this.check(property, from, to)) return this;
5825 var args = Array.flatten(arguments);
5826 this.property = this.options.property || args.shift();
5827 var parsed = this.prepare(this.element, this.property, args);
5828 return this.parent(parsed.from, parsed.to);
5833 Element.Properties.tween = {
5835 set: function(options){
5836 this.get('tween').cancel().setOptions(options);
5841 var tween = this.retrieve('tween');
5843 tween = new Fx.Tween(this, {link: 'cancel'});
5844 this.store('tween', tween);
5853 tween: function(property, from, to){
5854 this.get('tween').start(property, from, to);
5858 fade: function(how){
5859 var fade = this.get('tween'), method, args = ['opacity'].append(arguments), toggle;
5860 if (args[1] == null) args[1] = 'toggle';
5862 case 'in': method = 'start'; args[1] = 1; break;
5863 case 'out': method = 'start'; args[1] = 0; break;
5864 case 'show': method = 'set'; args[1] = 1; break;
5865 case 'hide': method = 'set'; args[1] = 0; break;
5867 var flag = this.retrieve('fade:flag', this.getStyle('opacity') == 1);
5869 args[1] = flag ? 0 : 1;
5870 this.store('fade:flag', !flag);
5873 default: method = 'start';
5875 if (!toggle) this.eliminate('fade:flag');
5876 fade[method].apply(fade, args);
5877 var to = args[args.length - 1];
5878 if (method == 'set' || to != 0) this.setStyle('visibility', to == 0 ? 'hidden' : 'visible');
5879 else fade.chain(function(){
5880 this.element.setStyle('visibility', 'hidden');
5886 highlight: function(start, end){
5888 end = this.retrieve('highlight:original', this.getStyle('background-color'));
5889 end = (end == 'transparent') ? '#fff' : end;
5891 var tween = this.get('tween');
5892 tween.start('background-color', start || '#ffff88', end).chain(function(){
5893 this.setStyle('background-color', this.retrieve('highlight:original'));
5906 description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
5908 license: MIT-style license.
5910 requires: [Object, Element, Chain, Events, Options, Browser]
5919 var empty = function(){},
5920 progressSupport = ('onprogress' in new Browser.Request);
5922 var Request = this.Request = new Class({
5924 Implements: [Chain, Events, Options],
5927 onRequest: function(){},
5928 onLoadstart: function(event, xhr){},
5929 onProgress: function(event, xhr){},
5930 onComplete: function(){},
5931 onCancel: function(){},
5932 onSuccess: function(responseText, responseXML){},
5933 onFailure: function(xhr){},
5934 onException: function(headerName, value){},
5935 onTimeout: function(){},
5938 withCredentials: false,*/
5942 'X-Requested-With': 'XMLHttpRequest',
5943 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
5954 evalResponse: false,
5959 initialize: function(options){
5960 this.xhr = new Browser.Request();
5961 this.setOptions(options);
5962 this.headers = this.options.headers;
5965 onStateChange: function(){
5967 if (xhr.readyState != 4 || !this.running) return;
5968 this.running = false;
5970 Function.attempt(function(){
5971 var status = xhr.status;
5972 this.status = (status == 1223) ? 204 : status;
5974 xhr.onreadystatechange = empty;
5975 if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
5977 clearTimeout(this.timer);
5981 this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML};
5982 if (this.options.isSuccess.call(this, this.status))
5983 this.success(this.response.text, this.response.xml);
5988 isSuccess: function(){
5989 var status = this.status;
5990 return (status >= 200 && status < 300);
5993 isRunning: function(){
5994 return !!this.running;
5997 processScripts: function(text){
5998 if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.exec(text);
5999 return text.stripScripts(this.options.evalScripts);
6002 success: function(text, xml){
6003 this.onSuccess(this.processScripts(text), xml);
6006 onSuccess: function(){
6007 this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
6010 failure: function(){
6014 onFailure: function(){
6015 this.fireEvent('complete').fireEvent('failure', this.xhr);
6018 loadstart: function(event){
6019 this.fireEvent('loadstart', [event, this.xhr]);
6022 progress: function(event){
6023 this.fireEvent('progress', [event, this.xhr]);
6026 timeout: function(){
6027 this.fireEvent('timeout', this.xhr);
6030 setHeader: function(name, value){
6031 this.headers[name] = value;
6035 getHeader: function(name){
6036 return Function.attempt(function(){
6037 return this.xhr.getResponseHeader(name);
6042 if (!this.running) return true;
6043 switch (this.options.link){
6044 case 'cancel': this.cancel(); return true;
6045 case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
6050 send: function(options){
6051 if (!this.check(options)) return this;
6053 this.options.isSuccess = this.options.isSuccess || this.isSuccess;
6054 this.running = true;
6056 var type = typeOf(options);
6057 if (type == 'string' || type == 'element') options = {data: options};
6059 var old = this.options;
6060 options = Object.append({data: old.data, url: old.url, method: old.method}, options);
6061 var data = options.data, url = String(options.url), method = options.method.toLowerCase();
6063 switch (typeOf(data)){
6064 case 'element': data = document.id(data).toQueryString(); break;
6065 case 'object': case 'hash': data = Object.toQueryString(data);
6068 if (this.options.format){
6069 var format = 'format=' + this.options.format;
6070 data = (data) ? format + '&' + data : format;
6073 if (this.options.emulation && !['get', 'post'].contains(method)){
6074 var _method = '_method=' + method;
6075 data = (data) ? _method + '&' + data : _method;
6079 if (this.options.urlEncoded && ['post', 'put'].contains(method)){
6080 var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
6081 this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding;
6084 if (!url) url = document.location.pathname;
6086 var trimPosition = url.lastIndexOf('/');
6087 if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
6089 if (this.options.noCache)
6090 url += (url.indexOf('?') > -1 ? '&' : '?') + String.uniqueID();
6092 if (data && (method == 'get' || method == 'delete')){
6093 url += (url.indexOf('?') > -1 ? '&' : '?') + data;
6098 if (progressSupport){
6099 xhr.onloadstart = this.loadstart.bind(this);
6100 xhr.onprogress = this.progress.bind(this);
6103 xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password);
6104 if ((/*<1.4compat>*/this.options.user || /*</1.4compat>*/this.options.withCredentials) && 'withCredentials' in xhr) xhr.withCredentials = true;
6106 xhr.onreadystatechange = this.onStateChange.bind(this);
6108 Object.each(this.headers, function(value, key){
6110 xhr.setRequestHeader(key, value);
6112 this.fireEvent('exception', [key, value]);
6116 this.fireEvent('request');
6118 if (!this.options.async) this.onStateChange();
6119 else if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
6124 if (!this.running) return this;
6125 this.running = false;
6129 clearTimeout(this.timer);
6132 xhr.onreadystatechange = empty;
6133 if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
6134 this.xhr = new Browser.Request();
6135 this.fireEvent('cancel');
6142 ['get', 'post', 'put', 'delete', 'patch', 'head', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'].each(function(method){
6143 methods[method] = function(data){
6147 if (data != null) object.data = data;
6148 return this.send(object);
6152 Request.implement(methods);
6154 Element.Properties.send = {
6156 set: function(options){
6157 var send = this.get('send').cancel();
6158 send.setOptions(options);
6163 var send = this.retrieve('send');
6165 send = new Request({
6166 data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
6168 this.store('send', send);
6177 send: function(url){
6178 var sender = this.get('send');
6179 sender.send({data: this, url: url || sender.options.url});
6192 description: Extends the basic Request Class with additional methods for interacting with HTML responses.
6194 license: MIT-style license.
6196 requires: [Element, Request]
6198 provides: Request.HTML
6203 Request.HTML = new Class({
6213 Accept: 'text/html, application/xml, text/xml, */*'
6217 success: function(text){
6218 var options = this.options, response = this.response;
6220 response.html = text.stripScripts(function(script){
6221 response.javascript = script;
6224 var match = response.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
6225 if (match) response.html = match[1];
6226 var temp = new Element('div').set('html', response.html);
6228 response.tree = temp.childNodes;
6229 response.elements = temp.getElements(options.filter || '*');
6231 if (options.filter) response.tree = response.elements;
6232 if (options.update){
6233 var update = document.id(options.update).empty();
6234 if (options.filter) update.adopt(response.elements);
6235 else update.set('html', response.html);
6236 } else if (options.append){
6237 var append = document.id(options.append);
6238 if (options.filter) response.elements.reverse().inject(append);
6239 else append.adopt(temp.getChildren());
6241 if (options.evalScripts) Browser.exec(response.javascript);
6243 this.onSuccess(response.tree, response.elements, response.html, response.javascript);
6248 Element.Properties.load = {
6250 set: function(options){
6251 var load = this.get('load').cancel();
6252 load.setOptions(options);
6257 var load = this.retrieve('load');
6259 load = new Request.HTML({data: this, link: 'cancel', update: this, method: 'get'});
6260 this.store('load', load);
6270 this.get('load').send(Array.link(arguments, {data: Type.isObject, url: Type.isString}));
6281 description: JSON encoder and decoder.
6283 license: MIT-style license.
6285 SeeAlso: <http://www.json.org/>
6287 requires: [Array, String, Number, Function]
6294 if (typeof JSON == 'undefined') this.JSON = {};
6299 stringify: JSON.stringify,
6307 var special = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
6309 var escape = function(chr){
6310 return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
6313 JSON.validate = function(string){
6314 string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
6315 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
6316 replace(/(?:^|:|,)(?:\s*\[)+/g, '');
6318 return (/^[\],:{}\s]*$/).test(string);
6321 JSON.encode = JSON.stringify ? function(obj){
6322 return JSON.stringify(obj);
6324 if (obj && obj.toJSON) obj = obj.toJSON();
6326 switch (typeOf(obj)){
6328 return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
6330 return '[' + obj.map(JSON.encode).clean() + ']';
6331 case 'object': case 'hash':
6333 Object.each(obj, function(value, key){
6334 var json = JSON.encode(value);
6335 if (json) string.push(JSON.encode(key) + ':' + json);
6337 return '{' + string + '}';
6338 case 'number': case 'boolean': return '' + obj;
6339 case 'null': return 'null';
6347 JSON.secure = false;
6350 JSON.decode = function(string, secure){
6351 if (!string || typeOf(string) != 'string') return null;
6353 if (secure == null) secure = JSON.secure;
6355 if (JSON.parse) return JSON.parse(string);
6356 if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
6359 return eval('(' + string + ')');
6369 description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
6371 license: MIT-style license.
6373 requires: [Request, JSON]
6375 provides: Request.JSON
6380 Request.JSON = new Class({
6385 /*onError: function(text, error){},*/
6389 initialize: function(options){
6390 this.parent(options);
6391 Object.append(this.headers, {
6392 'Accept': 'application/json',
6397 success: function(text){
6400 json = this.response.json = JSON.decode(text, this.options.secure);
6402 this.fireEvent('error', [text, error]);
6405 if (json == null) this.onFailure();
6406 else this.onSuccess(json, text);
6416 description: Class for creating, reading, and deleting browser Cookies.
6418 license: MIT-style license.
6421 - Based on the functions by Peter-Paul Koch (http://quirksmode.org).
6423 requires: [Options, Browser]
6430 var Cookie = new Class({
6432 Implements: Options,
6443 initialize: function(key, options){
6445 this.setOptions(options);
6448 write: function(value){
6449 if (this.options.encode) value = encodeURIComponent(value);
6450 if (this.options.domain) value += '; domain=' + this.options.domain;
6451 if (this.options.path) value += '; path=' + this.options.path;
6452 if (this.options.duration){
6453 var date = new Date();
6454 date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
6455 value += '; expires=' + date.toGMTString();
6457 if (this.options.secure) value += '; secure';
6458 this.options.document.cookie = this.key + '=' + value;
6463 var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
6464 return (value) ? decodeURIComponent(value[1]) : null;
6467 dispose: function(){
6468 new Cookie(this.key, Object.merge({}, this.options, {duration: -1})).write('');
6474 Cookie.write = function(key, value, options){
6475 return new Cookie(key, options).write(value);
6478 Cookie.read = function(key){
6479 return new Cookie(key).read();
6482 Cookie.dispose = function(key, options){
6483 return new Cookie(key, options).dispose();
6491 description: Contains the custom event domready.
6493 license: MIT-style license.
6495 requires: [Browser, Element, Element.Event]
6497 provides: [DOMReady, DomReady]
6502 (function(window, document){
6509 testElement = document.createElement('div');
6511 var domready = function(){
6512 clearTimeout(timer);
6514 Browser.loaded = ready = true;
6515 document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check);
6516 document.fireEvent('domready');
6517 window.fireEvent('domready');
6519 // cleanup scope vars
6520 document = window = testElement = null;
6523 var check = function(){
6524 for (var i = checks.length; i--;) if (checks[i]()){
6531 var poll = function(){
6532 clearTimeout(timer);
6533 if (!check()) timer = setTimeout(poll, 10);
6536 document.addListener('DOMContentLoaded', domready);
6539 // doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
6540 // testElement.doScroll() throws when the DOM is not ready, only in the top window
6541 var doScrollWorks = function(){
6543 testElement.doScroll();
6548 // If doScroll works already, it can't be used to determine domready
6549 // e.g. in an iframe
6550 if (testElement.doScroll && !doScrollWorks()){
6551 checks.push(doScrollWorks);
6556 if (document.readyState) checks.push(function(){
6557 var state = document.readyState;
6558 return (state == 'loaded' || state == 'complete');
6561 if ('onreadystatechange' in document) document.addListener('readystatechange', check);
6562 else shouldPoll = true;
6564 if (shouldPoll) poll();
6566 Element.Events.domready = {
6567 onAdd: function(fn){
6568 if (ready) fn.call(this);
6572 // Make sure that domready fires before load
6573 Element.Events.load = {
6575 onAdd: function(fn){
6576 if (loaded && this == window) fn.call(this);
6578 condition: function(){
6579 if (this == window){
6581 delete Element.Events.load;
6587 // This is based on the custom load event
6588 window.addEvent('load', function(){
6592 })(window, document);