add server build to grunt task
[mootools.git] / dist / mootools-core-compat.js
blobee9e33c339bdea6755951de09224028d4a42824a
1 /*
2 ---
4 name: Core
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/)
14 inspiration:
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]
20 ...
23 (function(){
25 this.MooTools = {
26         version: '1.5.1',
27         build: '%build%'
30 // typeOf, instanceOf
32 var typeOf = this.typeOf = function(item){
33         if (item == null) return 'null';
34         if (item.$family != null) return item.$family();
36         if (item.nodeName){
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';
42         }
44         return typeof item;
47 var instanceOf = this.instanceOf = function(item, object){
48         if (item == null) return false;
49         var constructor = item.$constructor || item.constructor;
50         while (constructor){
51                 if (constructor === object) return true;
52                 constructor = constructor.parent;
53         }
54         /*<ltIE8>*/
55         if (!item.hasOwnProperty) return false;
56         /*</ltIE8>*/
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){
69         var self = this;
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--;){
75                                 k = enumerables[i];
76                                 if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
77                         }
78                 } else {
79                         self.call(this, a, b);
80                 }
81                 return this;
82         };
85 Function.prototype.overloadGetter = function(usePlural){
86         var self = this;
87         return function(a){
88                 var args, result;
89                 if (typeof a != 'string') args = a;
90                 else if (arguments.length > 1) args = arguments;
91                 else if (usePlural) args = [a];
92                 if (args){
93                         result = {};
94                         for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
95                 } else {
96                         result = self.call(this, a);
97                 }
98                 return result;
99         };
102 Function.prototype.extend = function(key, value){
103         this[key] = value;
104 }.overloadSetter();
106 Function.prototype.implement = function(key, value){
107         this.prototype[key] = value;
108 }.overloadSetter();
110 // From
112 var slice = Array.prototype.slice;
114 Function.from = function(item){
115         return (typeOf(item) == 'function') ? item : function(){
116                 return item;
117         };
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){
131         return item + '';
134 // hide, protect
136 Function.implement({
138         hide: function(){
139                 this.$hidden = true;
140                 return this;
141         },
143         protect: function(){
144                 this.$protected = true;
145                 return this;
146         }
150 // Type
152 var Type = this.Type = function(name, object){
153         if (name){
154                 var lower = name.toLowerCase();
155                 var typeCheck = function(item){
156                         return (typeOf(item) == lower);
157                 };
159                 Type['is' + name] = typeCheck;
160                 if (object != null){
161                         object.prototype.$family = (function(){
162                                 return lower;
163                         }).hide();
164                         //<1.2compat>
165                         object.type = typeCheck;
166                         //</1.2compat>
167                 }
168         }
170         if (object == null) return null;
172         object.extend(this);
173         object.$constructor = Type;
174         object.prototype.$constructor = object;
176         return 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]' );
185 var hooks = {};
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++){
198                 var hook = hooks[i];
199                 if (typeOf(hook) == 'type') implement.call(hook, name, method);
200                 else hook.call(this, name, method);
201         }
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));
208         });
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;
217 Type.implement({
219         implement: implement.overloadSetter(),
221         extend: extend.overloadSetter(),
223         alias: function(name, existing){
224                 implement.call(this, name, this.prototype[existing]);
225         }.overloadSetter(),
227         mirror: function(hook){
228                 hooksOf(this).push(hook);
229                 return this;
230         }
234 new Type('Type', Type);
236 // Default Types
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());
251         }
253         if (isType){
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]);
258                         }
259                         for (var key in prototype) fn.call(prototype, prototype[key], key);
260                 };
261         }
263         return force;
266 force('String', String, [
267         'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
268         'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
269 ])('Array', Array, [
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, [
277         'exec', 'test'
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(){
287         return +(new Date);
290 new Type('Boolean', Boolean);
292 // fixes NaN returning as Number
294 Number.prototype.$family = function(){
295         return isFinite(this) ? 'number' : 'null';
296 }.hide();
298 // Number.random
300 Number.extend('random', function(min, max){
301         return Math.floor(Math.random() * (max - min + 1) + min);
304 // forEach, each
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);
310         }
313 Object.each = Object.forEach;
315 Array.implement({
317         /*<!ES5>*/
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);
321                 }
322         },
323         /*</!ES5>*/
325         each: function(fn, bind){
326                 Array.forEach(this, fn, bind);
327                 return this;
328         }
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;
339         }
342 Array.implement('clone', function(){
343         var i = this.length, clone = new Array(i);
344         while (i--) clone[i] = cloneOf(this[i]);
345         return clone;
348 var mergeOne = function(source, key, current){
349         switch (typeOf(current)){
350                 case 'object':
351                         if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
352                         else source[key] = Object.clone(current);
353                 break;
354                 case 'array': source[key] = current.clone(); break;
355                 default: source[key] = current;
356         }
357         return source;
360 Object.extend({
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]);
367                 }
368                 return source;
369         },
371         clone: function(object){
372                 var clone = {};
373                 for (var key in object) clone[key] = cloneOf(object[key]);
374                 return clone;
375         },
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];
381                 }
382                 return original;
383         }
387 // Object-less types
389 ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
390         new Type(name);
393 // Unique ID
395 var UID = Date.now();
397 String.extend('uniqueID', function(){
398         return (UID++).toString(36);
401 //<1.2compat>
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];
406         return this;
409 Hash.implement({
411         forEach: function(fn, bind){
412                 Object.forEach(this, fn, bind);
413         },
415         getClean: function(){
416                 var clean = {};
417                 for (var key in this){
418                         if (this.hasOwnProperty(key)) clean[key] = this[key];
419                 }
420                 return clean;
421         },
423         getLength: function(){
424                 var length = 0;
425                 for (var key in this){
426                         if (this.hasOwnProperty(key)) length++;
427                 }
428                 return length;
429         }
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);
445         return Native;
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){
458         return function(){
459                 return arguments[i];
460         };
463 this.$chk = function(obj){
464         return !!(obj || obj === 0);
467 this.$clear = function(timer){
468         clearTimeout(timer);
469         clearInterval(timer);
470         return null;
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);
494         args.unshift({});
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;
516         }
519 //</1.2compat>
521 })();
526 name: Array
528 description: Contains Array Prototypes like each, contains, and erase.
530 license: MIT-style license.
532 requires: [Type]
534 provides: Array
539 Array.implement({
541         /*<!ES5>*/
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;
545                 }
546                 return true;
547         },
549         filter: function(fn, bind){
550                 var results = [];
551                 for (var value, i = 0, l = this.length >>> 0; i < l; i++) if (i in this){
552                         value = this[i];
553                         if (fn.call(bind, value, i, this)) results.push(value);
554                 }
555                 return results;
556         },
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;
562                 }
563                 return -1;
564         },
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);
570                 }
571                 return results;
572         },
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;
577                 }
578                 return false;
579         },
580         /*</!ES5>*/
582         clean: function(){
583                 return this.filter(function(item){
584                         return item != null;
585                 });
586         },
588         invoke: function(methodName){
589                 var args = Array.slice(arguments, 1);
590                 return this.map(function(item){
591                         return item[methodName].apply(item, args);
592                 });
593         },
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];
598                 return obj;
599         },
601         link: function(object){
602                 var result = {};
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];
607                                         delete object[key];
608                                         break;
609                                 }
610                         }
611                 }
612                 return result;
613         },
615         contains: function(item, from){
616                 return this.indexOf(item, from) != -1;
617         },
619         append: function(array){
620                 this.push.apply(this, array);
621                 return this;
622         },
624         getLast: function(){
625                 return (this.length) ? this[this.length - 1] : null;
626         },
628         getRandom: function(){
629                 return (this.length) ? this[Number.random(0, this.length - 1)] : null;
630         },
632         include: function(item){
633                 if (!this.contains(item)) this.push(item);
634                 return this;
635         },
637         combine: function(array){
638                 for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
639                 return this;
640         },
642         erase: function(item){
643                 for (var i = this.length; i--;){
644                         if (this[i] === item) this.splice(i, 1);
645                 }
646                 return this;
647         },
649         empty: function(){
650                 this.length = 0;
651                 return this;
652         },
654         flatten: function(){
655                 var array = [];
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]);
660                 }
661                 return array;
662         },
664         pick: function(){
665                 for (var i = 0, l = this.length; i < l; i++){
666                         if (this[i] != null) return this[i];
667                 }
668                 return null;
669         },
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);
676                 });
677                 return (array) ? rgb : 'rgb(' + rgb + ')';
678         },
680         rgbToHex: function(array){
681                 if (this.length < 3) return null;
682                 if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
683                 var hex = [];
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);
687                 }
688                 return (array) ? hex : '#' + hex.join('');
689         }
693 //<1.2compat>
695 Array.alias('extend', 'append');
697 var $pick = function(){
698         return Array.from(arguments).pick();
701 //</1.2compat>
706 name: Function
708 description: Contains Function Prototypes like create, bind, pass, and delay.
710 license: MIT-style license.
712 requires: Type
714 provides: Function
719 Function.extend({
721         attempt: function(){
722                 for (var i = 0, l = arguments.length; i < l; i++){
723                         try {
724                                 return arguments[i]();
725                         } catch (e){}
726                 }
727                 return null;
728         }
732 Function.implement({
734         attempt: function(args, bind){
735                 try {
736                         return this.apply(bind, Array.from(args));
737                 } catch (e){}
739                 return null;
740         },
742         /*<!ES5-bind>*/
743         bind: function(that){
744                 var self = this,
745                         args = arguments.length > 1 ? Array.slice(arguments, 1) : null,
746                         F = function(){};
748                 var bound = function(){
749                         var context = that, length = arguments.length;
750                         if (this instanceof bound){
751                                 F.prototype = self.prototype;
752                                 context = new F;
753                         }
754                         var result = (!args && !length)
755                                 ? self.call(context)
756                                 : self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments);
757                         return context == that ? result : context;
758                 };
759                 return bound;
760         },
761         /*</!ES5-bind>*/
763         pass: function(args, bind){
764                 var self = this;
765                 if (args != null) args = Array.from(args);
766                 return function(){
767                         return self.apply(bind, args || arguments);
768                 };
769         },
771         delay: function(delay, bind, args){
772                 return setTimeout(this.pass((args == null ? [] : args), bind), delay);
773         },
775         periodical: function(periodical, bind, args){
776                 return setInterval(this.pass((args == null ? [] : args), bind), periodical);
777         }
781 //<1.2compat>
783 delete Function.prototype.bind;
785 Function.implement({
787         create: function(options){
788                 var self = this;
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);
796                         };
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);
800                         return returns();
801                 };
802         },
804         bind: function(bind, args){
805                 var self = this;
806                 if (args != null) args = Array.from(args);
807                 return function(){
808                         return self.apply(bind, args || arguments);
809                 };
810         },
812         bindWithEvent: function(bind, args){
813                 var self = this;
814                 if (args != null) args = Array.from(args);
815                 return function(event){
816                         return self.apply(bind, (args == null) ? arguments : [event].concat(args));
817                 };
818         },
820         run: function(args, bind){
821                 return this.apply(bind, Array.from(args));
822         }
826 if (Object.create == Function.prototype.create) Object.create = null;
828 var $try = Function.attempt;
830 //</1.2compat>
835 name: Number
837 description: Contains Number Prototypes like limit, round, times, and ceil.
839 license: MIT-style license.
841 requires: Type
843 provides: Number
848 Number.implement({
850         limit: function(min, max){
851                 return Math.min(max, Math.max(min, this));
852         },
854         round: function(precision){
855                 precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
856                 return Math.round(this * precision) / precision;
857         },
859         times: function(fn, bind){
860                 for (var i = 0; i < this; i++) fn.call(bind, i, this);
861         },
863         toFloat: function(){
864                 return parseFloat(this);
865         },
867         toInt: function(base){
868                 return parseInt(this, base || 10);
869         }
873 Number.alias('each', 'times');
875 (function(math){
876         var methods = {};
877         math.each(function(name){
878                 if (!Number[name]) methods[name] = function(){
879                         return Math[name].apply(null, [this].concat(Array.from(arguments)));
880                 };
881         });
882         Number.implement(methods);
883 })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
888 name: String
890 description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
892 license: MIT-style license.
894 requires: [Type, Array]
896 provides: String
901 String.implement({
903         //<!ES6>
904         contains: function(string, index){
905                 return (index ? String(this).slice(index) : String(this)).indexOf(string) > -1;
906         },
907         //</!ES6>
909         test: function(regex, params){
910                 return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
911         },
913         trim: function(){
914                 return String(this).replace(/^\s+|\s+$/g, '');
915         },
917         clean: function(){
918                 return String(this).replace(/\s+/g, ' ').trim();
919         },
921         camelCase: function(){
922                 return String(this).replace(/-\D/g, function(match){
923                         return match.charAt(1).toUpperCase();
924                 });
925         },
927         hyphenate: function(){
928                 return String(this).replace(/[A-Z]/g, function(match){
929                         return ('-' + match.charAt(0).toLowerCase());
930                 });
931         },
933         capitalize: function(){
934                 return String(this).replace(/\b[a-z]/g, function(match){
935                         return match.toUpperCase();
936                 });
937         },
939         escapeRegExp: function(){
940                 return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
941         },
943         toInt: function(base){
944                 return parseInt(this, base || 10);
945         },
947         toFloat: function(){
948                 return parseFloat(this);
949         },
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;
954         },
956         rgbToHex: function(array){
957                 var rgb = String(this).match(/\d{1,3}/g);
958                 return (rgb) ? rgb.rgbToHex(array) : null;
959         },
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] : '';
965                 });
966         }
970 //<1.4compat>
971 String.prototype.contains = function(string, separator){
972         return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : String(this).indexOf(string) > -1;
974 //</1.4compat>
979 name: Browser
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]
992 (function(){
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'){
1004                 UA[1] = 'ie';
1005                 if (UA[4]) UA[2] = UA[4];
1006         } else if (UA[1] == 'crios'){
1007                 UA[1] = 'chrome';
1008         }
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';
1013         return {
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]),
1017                 platform: platform
1018         };
1021 var Browser = this.Browser = parse(navigator.userAgent, navigator.platform);
1023 if (Browser.name == 'ie'){
1024         Browser.version = document.documentMode;
1027 Browser.extend({
1028         Features: {
1029                 xpath: !!(document.evaluate),
1030                 air: !!(window.runtime),
1031                 query: !!(document.querySelector),
1032                 json: !!(window.JSON)
1033         },
1034         parseUA: parse
1037 //<1.4compat>
1038 Browser[Browser.name] = true;
1039 Browser[Browser.name + parseInt(Browser.version, 10)] = true;
1041 if (Browser.name == 'ie' && Browser.version >= '11'){
1042         delete Browser.ie;
1045 var platform = Browser.platform;
1046 if (platform == 'windows'){
1047         platform = 'win';
1049 Browser.Platform = {
1050         name: platform
1052 Browser.Platform[platform] = true;
1053 //</1.4compat>
1055 // Request
1057 Browser.Request = (function(){
1059         var XMLHTTP = function(){
1060                 return new XMLHttpRequest();
1061         };
1063         var MSXML2 = function(){
1064                 return new ActiveXObject('MSXML2.XMLHTTP');
1065         };
1067         var MSXML = function(){
1068                 return new ActiveXObject('Microsoft.XMLHTTP');
1069         };
1071         return Function.attempt(function(){
1072                 XMLHTTP();
1073                 return XMLHTTP;
1074         }, function(){
1075                 MSXML2();
1076                 return MSXML2;
1077         }, function(){
1078                 MSXML();
1079                 return MSXML;
1080         });
1082 })();
1084 Browser.Features.xhr = !!(Browser.Request);
1086 //<1.4compat>
1088 // Flash detection
1090 var version = (Function.attempt(function(){
1091         return navigator.plugins['Shockwave Flash'].description;
1092 }, function(){
1093         return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
1094 }) || '0 r0').match(/\d+/g);
1096 Browser.Plugins = {
1097         Flash: {
1098                 version: Number(version[0] || '0.' + version[1]) || 0,
1099                 build: Number(version[2]) || 0
1100         }
1103 //</1.4compat>
1105 // String scripts
1107 Browser.exec = function(text){
1108         if (!text) return text;
1109         if (window.execScript){
1110                 window.execScript(text);
1111         } else {
1112                 var script = document.createElement('script');
1113                 script.setAttribute('type', 'text/javascript');
1114                 script.text = text;
1115                 document.head.appendChild(script);
1116                 document.head.removeChild(script);
1117         }
1118         return text;
1121 String.implement('stripScripts', function(exec){
1122         var scripts = '';
1123         var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
1124                 scripts += code + '\n';
1125                 return '';
1126         });
1127         if (exec === true) Browser.exec(scripts);
1128         else if (typeOf(exec) == 'function') exec(scripts, text);
1129         return text;
1132 // Window, Document
1134 Browser.extend({
1135         Document: this.Document,
1136         Window: this.Window,
1137         Element: this.Element,
1138         Event: this.Event
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);
1162 } catch (e){}
1164 /*<ltIE9>*/
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;
1170         };
1171         this.attachEvent('onunload', unloadEvent);
1174 // IE fails on collections and <select>.options (refers to <select>)
1175 var arrayFrom = Array.from;
1176 try {
1177         arrayFrom(document.html.childNodes);
1178 } catch(e){
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];
1183                         return array;
1184                 }
1185                 return arrayFrom(item);
1186         };
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));
1194                 };
1195         });
1197 /*</ltIE9>*/
1199 //<1.2compat>
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;
1211 if (Browser.ie){
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);
1218         }
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);
1235         }
1238 if (Browser.opera){
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]){
1248                 case 'webkit':
1249                 case 'khtml':
1250                         Browser.Engine.webkit = true;
1251                 break;
1252                 case 'gecko':
1253                         Browser.Engine.gecko = true;
1254         }
1257 this.$exec = Browser.exec;
1259 //</1.2compat>
1261 })();
1266 name: Class
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]
1274 provides: Class
1279 (function(){
1281 var Class = this.Class = new Type('Class', function(params){
1282         if (instanceOf(params, Function)) params = {initialize: params};
1284         var newClass = function(){
1285                 reset(this);
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;
1290                 return value;
1291         }.extend(this).implement(params);
1293         newClass.$constructor = Class;
1294         newClass.prototype.$constructor = newClass;
1295         newClass.prototype.parent = parent;
1297         return newClass;
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)){
1313                         case 'object':
1314                                 var F = function(){};
1315                                 F.prototype = value;
1316                                 object[key] = reset(new F);
1317                         break;
1318                         case 'array': object[key] = value.clone(); break;
1319                 }
1320         }
1321         return object;
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;
1332                 return result;
1333         }.extend({$owner: self, $origin: method, $name: key});
1334         return wrapper;
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;
1341         }
1343         if (typeOf(value) == 'function'){
1344                 if (value.$hidden) return this;
1345                 this.prototype[key] = (retain) ? value : wrap(this, key, value);
1346         } else {
1347                 Object.merge(this.prototype, key, value);
1348         }
1350         return this;
1353 var getInstance = function(klass){
1354         klass.$prototyping = true;
1355         var proto = new klass;
1356         delete klass.$prototyping;
1357         return proto;
1360 Class.implement('implement', implement.overloadSetter());
1362 Class.Mutators = {
1364         Extends: function(parent){
1365                 this.parent = parent;
1366                 this.prototype = getInstance(parent);
1367         },
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);
1373                 }, this);
1374         }
1377 })();
1382 name: Class.Extras
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.
1388 requires: Class
1390 provides: [Class.Extras, Chain, Events, Options]
1395 (function(){
1397 this.Chain = new Class({
1399         $chain: [],
1401         chain: function(){
1402                 this.$chain.append(Array.flatten(arguments));
1403                 return this;
1404         },
1406         callChain: function(){
1407                 return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
1408         },
1410         clearChain: function(){
1411                 this.$chain.empty();
1412                 return this;
1413         }
1417 var removeOn = function(string){
1418         return string.replace(/^on([A-Z])/, function(full, first){
1419                 return first.toLowerCase();
1420         });
1423 this.Events = new Class({
1425         $events: {},
1427         addEvent: function(type, fn, internal){
1428                 type = removeOn(type);
1430                 /*<1.2compat>*/
1431                 if (fn == $empty) return this;
1432                 /*</1.2compat>*/
1434                 this.$events[type] = (this.$events[type] || []).include(fn);
1435                 if (internal) fn.internal = true;
1436                 return this;
1437         },
1439         addEvents: function(events){
1440                 for (var type in events) this.addEvent(type, events[type]);
1441                 return this;
1442         },
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);
1452                 }, this);
1453                 return this;
1454         },
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];
1462                 }
1463                 return this;
1464         },
1466         removeEvents: function(events){
1467                 var type;
1468                 if (typeOf(events) == 'object'){
1469                         for (type in events) this.removeEvent(type, events[type]);
1470                         return this;
1471                 }
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]);
1478                         }
1479                 }
1480                 return this;
1481         }
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];
1493                 }
1494                 return this;
1495         }
1499 })();
1504 name: Object
1506 description: Object generic methods
1508 license: MIT-style license.
1510 requires: Type
1512 provides: [Object, Hash]
1517 (function(){
1519 var hasOwnProperty = Object.prototype.hasOwnProperty;
1521 Object.extend({
1523         subset: function(object, keys){
1524                 var results = {};
1525                 for (var i = 0, l = keys.length; i < l; i++){
1526                         var k = keys[i];
1527                         if (k in object) results[k] = object[k];
1528                 }
1529                 return results;
1530         },
1532         map: function(object, fn, bind){
1533                 var results = {};
1534                 for (var key in object){
1535                         if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
1536                 }
1537                 return results;
1538         },
1540         filter: function(object, fn, bind){
1541                 var results = {};
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;
1545                 }
1546                 return results;
1547         },
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;
1552                 }
1553                 return true;
1554         },
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;
1559                 }
1560                 return false;
1561         },
1563         keys: function(object){
1564                 var keys = [];
1565                 for (var key in object){
1566                         if (hasOwnProperty.call(object, key)) keys.push(key);
1567                 }
1568                 return keys;
1569         },
1571         values: function(object){
1572                 var values = [];
1573                 for (var key in object){
1574                         if (hasOwnProperty.call(object, key)) values.push(object[key]);
1575                 }
1576                 return values;
1577         },
1579         getLength: function(object){
1580                 return Object.keys(object).length;
1581         },
1583         keyOf: function(object, value){
1584                 for (var key in object){
1585                         if (hasOwnProperty.call(object, key) && object[key] === value) return key;
1586                 }
1587                 return null;
1588         },
1590         contains: function(object, value){
1591                 return Object.keyOf(object, value) != null;
1592         },
1594         toQueryString: function(object, base){
1595                 var queryString = [];
1597                 Object.each(object, function(value, key){
1598                         if (base) key = base + '[' + key + ']';
1599                         var result;
1600                         switch (typeOf(value)){
1601                                 case 'object': result = Object.toQueryString(value, key); break;
1602                                 case 'array':
1603                                         var qs = {};
1604                                         value.each(function(val, i){
1605                                                 qs[i] = val;
1606                                         });
1607                                         result = Object.toQueryString(qs, key);
1608                                 break;
1609                                 default: result = key + '=' + encodeURIComponent(value);
1610                         }
1611                         if (value != null) queryString.push(result);
1612                 });
1614                 return queryString.join('&');
1615         }
1619 })();
1621 //<1.2compat>
1623 Hash.implement({
1625         has: Object.prototype.hasOwnProperty,
1627         keyOf: function(value){
1628                 return Object.keyOf(this, value);
1629         },
1631         hasValue: function(value){
1632                 return Object.contains(this, value);
1633         },
1635         extend: function(properties){
1636                 Hash.each(properties || {}, function(value, key){
1637                         Hash.set(this, key, value);
1638                 }, this);
1639                 return this;
1640         },
1642         combine: function(properties){
1643                 Hash.each(properties || {}, function(value, key){
1644                         Hash.include(this, key, value);
1645                 }, this);
1646                 return this;
1647         },
1649         erase: function(key){
1650                 if (this.hasOwnProperty(key)) delete this[key];
1651                 return this;
1652         },
1654         get: function(key){
1655                 return (this.hasOwnProperty(key)) ? this[key] : null;
1656         },
1658         set: function(key, value){
1659                 if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
1660                 return this;
1661         },
1663         empty: function(){
1664                 Hash.each(this, function(value, key){
1665                         delete this[key];
1666                 }, this);
1667                 return this;
1668         },
1670         include: function(key, value){
1671                 if (this[key] == null) this[key] = value;
1672                 return this;
1673         },
1675         map: function(fn, bind){
1676                 return new Hash(Object.map(this, fn, bind));
1677         },
1679         filter: function(fn, bind){
1680                 return new Hash(Object.filter(this, fn, bind));
1681         },
1683         every: function(fn, bind){
1684                 return Object.every(this, fn, bind);
1685         },
1687         some: function(fn, bind){
1688                 return Object.some(this, fn, bind);
1689         },
1691         getKeys: function(){
1692                 return Object.keys(this);
1693         },
1695         getValues: function(){
1696                 return Object.values(this);
1697         },
1699         toQueryString: function(base){
1700                 return Object.toQueryString(this, base);
1701         }
1705 Hash.extend = Object.append;
1707 Hash.alias({indexOf: 'keyOf', contains: 'hasValue'});
1709 //</1.2compat>
1713 name: Slick.Parser
1714 description: Standalone CSS3 Selector parser
1715 provides: Slick.Parser
1719 ;(function(){
1721 var parsed,
1722         separatorIndex,
1723         combinatorIndex,
1724         reversed,
1725         cache = {},
1726         reverseCache = {},
1727         reUnescape = /\\/g;
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];
1736         parsed = {
1737                 Slick: true,
1738                 expressions: [],
1739                 raw: expression,
1740                 reverse: function(){
1741                         return parse(this.raw, true);
1742                 }
1743         };
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++){
1764                         var cexp = exp[j];
1765                         if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
1766                         cexp.combinator = cexp.reverseCombinator;
1767                         delete cexp.reverseCombinator;
1768                 }
1770                 exp.reverse().push(last);
1771         }
1772         return expression;
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;
1778         });
1781 var regexp = new RegExp(
1783 #!/usr/bin/env ruby
1784 puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
1785 __END__
1786         "(?x)^(?:\
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\
1793         |                               # Attribute          \n\
1794         \\[  \
1795                 \\s* (<unicode1>+)  (?:  \
1796                         \\s* ([*^$!~|]?=)  (?:  \
1797                                 \\s* (?:\
1798                                         ([\"']?)(.*?)\\9 \
1799                                 )\
1800                         )  \
1801                 )?  \\s*  \
1802         \\](?!\\]) \n\
1803         |   :+ ( <unicode>+ )(?:\
1804         \\( (?:\
1805                 (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
1806         ) \\)\
1807         )?\
1808         )"
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])')
1816 function parser(
1817         rawMatch,
1819         separator,
1820         combinator,
1821         combinatorChildren,
1823         tagName,
1824         id,
1825         className,
1827         attributeKey,
1828         attributeOperator,
1829         attributeQuote,
1830         attributeValue,
1832         pseudoMarker,
1833         pseudoClass,
1834         pseudoQuote,
1835         pseudoClassQuotedValue,
1836         pseudoClassValue
1838         if (separator || separatorIndex === -1){
1839                 parsed.expressions[++separatorIndex] = [];
1840                 combinatorIndex = -1;
1841                 if (separator) return '';
1842         }
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: '*'};
1850         }
1852         var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
1854         if (tagName){
1855                 currentParsed.tag = tagName.replace(reUnescape, '');
1857         } else if (id){
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({
1867                         value: className,
1868                         regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
1869                 });
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'
1880                 });
1882         } else if (attributeKey){
1883                 attributeKey = attributeKey.replace(reUnescape, '');
1884                 attributeValue = (attributeValue || '').replace(reUnescape, '');
1886                 var test, regexp;
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;
1895                         }; break;
1896                         case '*=' : test = function(value){
1897                                 return value && value.indexOf(attributeValue) > -1;
1898                         }; break;
1899                         case '!=' : test = function(value){
1900                                 return attributeValue != value;
1901                         }; break;
1902                         default   : test = function(value){
1903                                 return !!value;
1904                         };
1905                 }
1907                 if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
1908                         return false;
1909                 };
1911                 if (!test) test = function(value){
1912                         return value && regexp.test(value);
1913                 };
1915                 if (!currentParsed.attributes) currentParsed.attributes = [];
1916                 currentParsed.attributes.push({
1917                         key: attributeKey,
1918                         operator: attributeOperator,
1919                         value: attributeValue,
1920                         test: test
1921                 });
1923         }
1925         return '';
1928 // Slick NS
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);
1944 name: Slick.Finder
1945 description: The new, superfast css selector engine.
1946 provides: Slick.Finder
1947 requires: Slick.Parser
1951 ;(function(){
1953 var local = {},
1954         featuresCache = {},
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
1975         else return;
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],
1987                 feature;
1989         if (features){
1990                 for (feature in features){
1991                         this[feature] = features[feature];
1992                 }
1993                 return;
1994         }
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
2010         = false;
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
2023         try {
2024                 testNode.innerHTML = '<a id="'+id+'"></a>';
2025                 features.isHTMLDocument = !!document.getElementById(id);
2026         } catch(e){};
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
2037                 try {
2038                         testNode.innerHTML = 'foo</foo>';
2039                         selected = testNode.getElementsByTagName('*');
2040                         starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
2041                 } catch(e){};
2043                 features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
2045                 // IE returns elements with the name instead of just id for getElementsById for some documents
2046                 try {
2047                         testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
2048                         features.idGetsName = document.getElementById(id) === testNode.firstChild;
2049                 } catch(e){};
2051                 if (testNode.getElementsByClassName){
2053                         // Safari 3.2 getElementsByClassName caches results
2054                         try {
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);
2059                         } catch(e){};
2061                         // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
2062                         try {
2063                                 testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
2064                                 brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
2065                         } catch(e){};
2067                         features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
2068                 }
2070                 if (testNode.querySelectorAll){
2071                         // IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
2072                         try {
2073                                 testNode.innerHTML = 'foo</foo>';
2074                                 selected = testNode.querySelectorAll('*');
2075                                 features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
2076                         } catch(e){};
2078                         // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
2079                         try {
2080                                 testNode.innerHTML = '<a class="MiX"></a>';
2081                                 features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
2082                         } catch(e){};
2084                         // Webkit and Opera dont return selected options on querySelectorAll
2085                         try {
2086                                 testNode.innerHTML = '<select><option selected="selected">a</option></select>';
2087                                 features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
2088                         } catch(e){};
2090                         // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
2091                         try {
2092                                 testNode.innerHTML = '<a class=""></a>';
2093                                 features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
2094                         } catch(e){};
2096                 }
2098                 // IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
2099                 try {
2100                         testNode.innerHTML = '<form action="s"><input id="action"/></form>';
2101                         brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
2102                 } catch(e){};
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;
2111                 } catch(e){};
2113         }
2115         try {
2116                 root.slick_expando = 1;
2117                 delete root.slick_expando;
2118                 features.getUID = this.getUIDHTML;
2119         } catch(e){
2120                 features.getUID = this.getUIDXML;
2121         }
2123         testRoot.removeChild(testNode);
2124         testNode = selected = testRoot = null;
2126         // getAttribute
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);
2136         };
2138         // hasAttribute
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));
2145         };
2147         // contains
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){
2160                 if (node) do {
2161                         if (node === context) return true;
2162                 } while ((node = node.parentNode));
2163                 return false;
2164         };
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);
2183         } : null ;
2185         root = null;
2187         for (feature in features){
2188                 this[feature] = features[feature];
2189         }
2192 // Main Method
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;
2206         // setup
2208         var parsed, i,
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],
2228                                 node, nodes;
2230                         if (!symbol){
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);
2237                                 }
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);
2256                                         }
2257                                 } else {
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);
2265                                         }
2266                                 }
2268                         }
2270                         if (hasOthers) this.sort(found);
2271                         return (first) ? null : found;
2273                 }
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
2289                                 )
2290                                 || Slick.disableQSA
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;
2301                         }
2303                         try {
2304                                 if (first) return context.querySelector(_expression) || null;
2305                                 else nodes = context.querySelectorAll(_expression);
2306                         } catch(e){
2307                                 qsaFailExpCache[expression] = 1;
2308                                 break querySelector;
2309                         } finally {
2310                                 if (!contextIsDocument){
2311                                         if (currentId) _context.setAttribute('id', currentId);
2312                                         else _context.removeAttribute('id');
2313                                         context = _context;
2314                                 }
2315                         }
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);
2321                         }
2323                         if (hasOthers) this.sort(found);
2324                         return found;
2326                 }
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
2332                 return found;
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;
2337                 return found;
2338         } else { // other junk
2339                 return found;
2340         }
2342         /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
2344         // cache elements for the nth selectors
2346         this.posNTH = {};
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 = [];
2358         // default engine
2360         var j, m, n;
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();
2370                 id         = currentBit.id;
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 = {};
2379                 if (lastBit){
2380                         this.uniques = uniques;
2381                         this.found = found;
2382                 } else {
2383                         this.uniques = {};
2384                         this.found = [];
2385                 }
2387                 if (j === 0){
2388                         this[combinator](context, tag, id, classes, attributes, pseudos, classList);
2389                         if (first && lastBit && found.length) break search;
2390                 } else {
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);
2395                 }
2397                 currentItems = this.found;
2398         }
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;
2406 // Utils
2408 local.uidx = 1;
2409 local.uidk = 'slick-uniqueid';
2411 local.getUIDXML = function(node){
2412         var uid = node.getAttribute(this.uidk);
2413         if (!uid){
2414                 uid = this.uidx++;
2415                 node.setAttribute(this.uidk, uid);
2416         }
2417         return 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);
2429         return results;
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;
2445         parsed =
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;
2460                         if (ofType){
2461                                 var nodeName = node.nodeName;
2462                                 do {
2463                                         if (el.nodeName != nodeName) continue;
2464                                         this[positions][this.getUID(el)] = count++;
2465                                 } while ((el = el[sibling]));
2466                         } else {
2467                                 do {
2468                                         if (el.nodeType != 1) continue;
2469                                         this[positions][this.getUID(el)] = count++;
2470                                 } while ((el = el[sibling]));
2471                         }
2472                 }
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;
2478                 if (a > 0){
2479                         if (pos < b) return false;
2480                 } else {
2481                         if (b < pos) return false;
2482                 }
2483                 return ((pos - b) % a) == 0;
2484         };
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);
2498         }
2501 local.matchNode = function(node, selector){
2502         if (this.isHTMLDocument && this.nativeMatchesSelector){
2503                 try {
2504                         return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
2505                 } catch(matchError){}
2506         }
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;
2517                         simpleExpCounter++;
2518                 }
2519         }
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;
2526         }
2527         return false;
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){
2538         if (tag){
2539                 var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
2540                 if (tag == '*'){
2541                         if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
2542                 } else {
2543                         if (nodeName != tag) return false;
2544                 }
2545         }
2547         if (id && node.getAttribute('id') != id) return false;
2549         var i, part, cls;
2550         if (classes) for (i = classes.length; i--;){
2551                 cls = this.getAttribute(node, 'class');
2552                 if (!(cls && classes[i].regexp.test(cls))) return false;
2553         }
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;
2557         }
2558         if (pseudos) for (i = pseudos.length; i--;){
2559                 part = pseudos[i];
2560                 if (!this.matchPseudo(node, part.key, part.value)) return false;
2561         }
2562         return true;
2565 var combinators = {
2567         ' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
2569                 var i, item, children;
2571                 if (this.isHTMLDocument){
2572                         getById: if (id){
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);
2584                                                         break;
2585                                                 }
2586                                         }
2587                                         return;
2588                                 }
2589                                 if (!item){
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;
2592                                         else break getById;
2593                                 } else if (this.document !== node && !this.contains(node, item)) return;
2594                                 this.push(item, tag, null, classes, attributes, pseudos);
2595                                 return;
2596                         }
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);
2601                                 return;
2602                         }
2603                 }
2604                 getByTag: {
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);
2609                 }
2610         },
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));
2616         },
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);
2621                         break;
2622                 }
2623         },
2625         '^': function(node, tag, id, classes, attributes, pseudos){ // first child
2626                 node = node.firstChild;
2627                 if (node){
2628                         if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
2629                         else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
2630                 }
2631         },
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);
2640                 }
2641         },
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);
2646         },
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);
2651         },
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);
2655         },
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);
2660         },
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);
2665                         break;
2666                 }
2667         },
2669         '!^': function(node, tag, id, classes, attributes, pseudos){ // last child
2670                 node = node.lastChild;
2671                 if (node){
2672                         if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
2673                         else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
2674                 }
2675         },
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);
2684                 }
2685         }
2689 for (var c in combinators) local['combinator:' + c] = combinators[c];
2691 var pseudos = {
2693         /*<pseudo-selectors>*/
2695         'empty': function(node){
2696                 var child = node.firstChild;
2697                 return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
2698         },
2700         'not': function(node, expression){
2701                 return !this.matchNode(node, expression);
2702         },
2704         'contains': function(node, text){
2705                 return (node.innerText || node.textContent || '').indexOf(text) > -1;
2706         },
2708         'first-child': function(node){
2709                 while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
2710                 return true;
2711         },
2713         'last-child': function(node){
2714                 while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
2715                 return true;
2716         },
2718         'only-child': function(node){
2719                 var prev = node;
2720                 while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
2721                 var next = node;
2722                 while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
2723                 return true;
2724         },
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));
2738         },
2740         'even': function(node){
2741                 return this['pseudo:nth-child'](node, '2n');
2742         },
2744         'odd': function(node){
2745                 return this['pseudo:nth-child'](node, '2n+1');
2746         },
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;
2755                 return true;
2756         },
2758         'last-of-type': function(node){
2759                 var nodeName = node.nodeName;
2760                 while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
2761                 return true;
2762         },
2764         'only-of-type': function(node){
2765                 var prev = node, nodeName = node.nodeName;
2766                 while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
2767                 var next = node;
2768                 while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
2769                 return true;
2770         },
2772         /*</of-type-pseudo-selectors>*/
2774         // custom pseudos
2776         'enabled': function(node){
2777                 return !node.disabled;
2778         },
2780         'disabled': function(node){
2781                 return node.disabled;
2782         },
2784         'checked': function(node){
2785                 return node.checked || node.selected;
2786         },
2788         'focus': function(node){
2789                 return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
2790         },
2792         'root': function(node){
2793                 return (node === this.root);
2794         },
2796         'selected': function(node){
2797                 return node.selected;
2798         }
2800         /*</pseudo-selectors>*/
2803 for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
2805 // attributes methods
2807 var attributeGetters = local.attributeGetters = {
2809         'for': function(){
2810                 return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
2811         },
2813         'href': function(){
2814                 return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
2815         },
2817         'style': function(){
2818                 return (this.style) ? this.style.cssText : this.getAttribute('style');
2819         },
2821         'tabindex': function(){
2822                 var attributeNode = this.getAttributeNode('tabindex');
2823                 return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
2824         },
2826         'type': function(){
2827                 return this.getAttribute('type');
2828         },
2830         'maxlength': function(){
2831                 var attributeNode = this.getAttributeNode('maxLength');
2832                 return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
2833         }
2837 attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength;
2839 // Slick
2841 var Slick = local.Slick = (this.Slick || {});
2843 Slick.version = '1.1.7';
2845 // Slick finder
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);
2874 // Slick matcher
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;
2887         return this;
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);
2899         };
2900         return this;
2903 Slick.lookupPseudo = function(name){
2904         var pseudo = local['pseudo:' + name];
2905         if (pseudo) return function(argument){
2906                 return pseudo.call(this, argument);
2907         };
2908         return null;
2911 // Slick overrides accessor
2913 Slick.override = function(regexp, fn){
2914         local.override(regexp, fn);
2915         return this;
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);
2931 name: Element
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;
2963                 }
2965                 if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' ');
2966         }
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);
2978                 };
2979         })(Element.prototype.fireEvent);
2982 new Type('Element', Element).mirror(function(name){
2983         if (Array.prototype[name]) return;
2985         var obj = {};
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');
2991                 }
2992                 return (elements) ? new Elements(results) : results;
2993         };
2995         Elements.implement(obj);
2998 if (!Browser.Element){
2999         Element.parent = Object;
3001         Element.Prototype = {
3002                 '$constructor': Element,
3003                 '$family': Function.from('element').hide()
3004         };
3006         Element.mirror(function(name, method){
3007                 Element.Prototype[name] = method;
3008         });
3011 Element.Constructors = {};
3013 //<1.2compat>
3015 Element.Constructors = new Hash;
3017 //</1.2compat>
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);
3024                 }
3025         });
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);
3036         };
3038         if (window.frames[props.id]) onLoad();
3039         else iframe.addListener('load', onLoad);
3040         return iframe;
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);
3048                         if (!uniques[uid]){
3049                                 uniques[uid] = true;
3050                                 this.push(node);
3051                         }
3052                 }
3053         }
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);
3065                 } : filter, bind));
3066         }.protect(),
3068         push: function(){
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;
3073                 }
3074                 return (this.length = length);
3075         }.protect(),
3077         unshift: function(){
3078                 var items = [];
3079                 for (var i = 0, l = arguments.length; i < l; i++){
3080                         var item = document.id(arguments[i]);
3081                         if (item) items.push(item);
3082                 }
3083                 return Array.prototype.unshift.apply(this, items);
3084         }.protect(),
3086         concat: function(){
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);
3092                 }
3093                 return newElements;
3094         }.protect(),
3096         append: function(collection){
3097                 for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]);
3098                 return this;
3099         }.protect(),
3101         empty: function(){
3102                 while (this.length) delete this[--this.length];
3103                 return this;
3104         }.protect()
3108 //<1.2compat>
3110 Elements.alias('extend', 'append');
3112 //</1.2compat>
3114 (function(){
3116 // FF, IE
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--];
3124         return result;
3125 }.protect());
3127 Array.forEachMethod(function(method, name){
3128         Elements.implement(name, method);
3131 Array.mirror(Elements);
3133 /*<ltIE8>*/
3134 var createElementAcceptsHTML;
3135 try {
3136         createElementAcceptsHTML = (document.createElement('<input name=x>').name == 'x');
3137 } catch (e){}
3139 var escapeQuotes = function(html){
3140         return ('' + html).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
3142 /*</ltIE8>*/
3144 /*<ltIE9>*/
3145 // #2479 - IE8 Cannot set HTML of style element
3146 var canChangeStyleHTML = (function(){
3147     var div = document.createElement('style'),
3148         flag = false;
3149     try {
3150         div.innerHTML = '#justTesing{margin: 0px;}';
3151         flag = !!div.innerHTML;
3152     } catch(e){}
3153     return flag;
3154 })();
3155 /*</ltIE9>*/
3157 Document.implement({
3159         newElement: function(tag, props){
3160                 if (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);
3169                         }
3170                         /*</ltIE9>*/
3171                         /*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
3172                         if (createElementAcceptsHTML){
3173                                 tag = '<' + tag;
3174                                 if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
3175                                 if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
3176                                 tag += '>';
3177                                 delete props.name;
3178                                 delete props.type;
3179                         }
3180                         /*</ltIE8>*/
3181                 }
3182                 return this.id(this.createElement(tag)).set(props);
3183         }
3187 })();
3189 (function(){
3191 Slick.uidOf(window);
3192 Slick.uidOf(document);
3194 Document.implement({
3196         newTextNode: function(text){
3197                 return this.createTextNode(text);
3198         },
3200         getDocument: function(){
3201                 return this;
3202         },
3204         getWindow: function(){
3205                 return this.window;
3206         },
3208         id: (function(){
3210                 var types = {
3212                         string: function(id, nocash, doc){
3213                                 id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1'));
3214                                 return (id) ? types.element(id, nocash) : null;
3215                         },
3217                         element: function(el, nocash){
3218                                 Slick.uidOf(el);
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);
3224                                         };
3225                                         Object.append(el, Element.Prototype);
3226                                 }
3227                                 return el;
3228                         },
3230                         object: function(obj, nocash, doc){
3231                                 if (obj.toElement) return types.element(obj.toElement(doc), nocash);
3232                                 return null;
3233                         }
3235                 };
3237                 types.textnode = types.whitespace = types.window = types.document = function(zero){
3238                         return zero;
3239                 };
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;
3245                 };
3247         })()
3251 if (window.$ == null) Window.implement('$', function(el, nc){
3252         return document.id(el, nc, this.document);
3255 Window.implement({
3257         getDocument: function(){
3258                 return this.document;
3259         },
3261         getWindow: function(){
3262                 return this;
3263         }
3267 [Document, Element].invoke('implement', {
3269         getElements: function(expression){
3270                 return Slick.search(this, expression, new Elements);
3271         },
3273         getElement: function(expression){
3274                 return document.id(Slick.find(this, expression));
3275         }
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);
3286 //<1.2compat>
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];
3301                 }
3302         };
3304         Slick.search = function(context, expression, append){
3305                 addSlickPseudos();
3306                 return search.call(this, context, expression, append);
3307         };
3309         Slick.find = function(context, expression){
3310                 addSlickPseudos();
3311                 return find.call(this, context, expression);
3312         };
3314         Slick.match = function(node, selector){
3315                 addSlickPseudos();
3316                 return match.call(this, node, selector);
3317         };
3319 })(Slick.search, Slick.find, Slick.match);
3321 //</1.2compat>
3323 // tree walking
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;
3334         return expression;
3337 Object.forEach({
3338         getNext: '~',
3339         getPrevious: '!~',
3340         getParent: '!'
3341 }, function(combinator, method){
3342         Element.implement(method, function(expression){
3343                 return this.getElement(injectCombinator(expression, combinator));
3344         });
3347 Object.forEach({
3348         getAllNext: '~',
3349         getAllPrevious: '!~',
3350         getSiblings: '~~',
3351         getChildren: '>',
3352         getParents: '!'
3353 }, function(combinator, method){
3354         Element.implement(method, function(expression){
3355                 return this.getElements(injectCombinator(expression, combinator));
3356         });
3359 Element.implement({
3361         getFirst: function(expression){
3362                 return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]);
3363         },
3365         getLast: function(expression){
3366                 return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast());
3367         },
3369         getWindow: function(){
3370                 return this.ownerDocument.window;
3371         },
3373         getDocument: function(){
3374                 return this.ownerDocument;
3375         },
3377         getElementById: function(id){
3378                 return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1')));
3379         },
3381         match: function(expression){
3382                 return !expression || Slick.match(this, expression);
3383         }
3387 //<1.2compat>
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++){
3394                 var item = args[i];
3395                 switch (typeOf(item)){
3396                         case 'element': elements.push(item); break;
3397                         case 'string': Slick.search(this.document, item, elements);
3398                 }
3399         }
3400         return elements;
3403 //</1.2compat>
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);
3409         }
3410         return new Elements(arguments);
3413 // Inserters
3415 var inserters = {
3417         before: function(context, element){
3418                 var parent = element.parentNode;
3419                 if (parent) parent.insertBefore(context, element);
3420         },
3422         after: function(context, element){
3423                 var parent = element.parentNode;
3424                 if (parent) parent.insertBefore(context, element.nextSibling);
3425         },
3427         bottom: function(context, element){
3428                 element.appendChild(context);
3429         },
3431         top: function(context, element){
3432                 element.insertBefore(context, element.firstChild);
3433         }
3437 inserters.inside = inserters.bottom;
3439 //<1.2compat>
3441 Object.each(inserters, function(inserter, where){
3443         where = where.capitalize();
3445         var methods = {};
3447         methods['inject' + where] = function(el){
3448                 inserter(this, document.id(el, true));
3449                 return this;
3450         };
3452         methods['grab' + where] = function(el){
3453                 inserter(document.id(el, true), this);
3454                 return this;
3455         };
3457         Element.implement(methods);
3461 //</1.2compat>
3463 // getProperty / setProperty
3465 var propertyGetters = {}, propertySetters = {};
3467 // properties
3469 var properties = {};
3470 Array.forEach([
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){
3482                 node[real] = value;
3483         };
3484         propertyGetters[key] = function(node){
3485                 return node[real];
3486         };
3489 /*<ltIE9>*/
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;
3494         };
3495 })(propertySetters.text);
3497 propertyGetters.text = (function(getter){
3498         return function(node){
3499                 return (node.get('tag') == 'style') ? node.innerHTML : getter(node);
3500         };
3501 })(propertyGetters.text);
3502 /*</ltIE9>*/
3504 // Booleans
3506 var bools = [
3507         'compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked',
3508         'disabled', 'readOnly', 'multiple', 'selected', 'noresize',
3509         'defer', 'defaultChecked', 'autofocus', 'controls', 'autoplay',
3510         'loop'
3513 var booleans = {};
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;
3519         };
3520         propertyGetters[lower] = function(node){
3521                 return !!node[bool];
3522         };
3525 // Special cases
3527 Object.append(propertySetters, {
3529         'class': function(node, value){
3530                 ('className' in node) ? node.className = (value || '') : node.setAttribute('class', value);
3531         },
3533         'for': function(node, value){
3534                 ('htmlFor' in node) ? node.htmlFor = value : node.setAttribute('for', value);
3535         },
3537         'style': function(node, value){
3538                 (node.style) ? node.style.cssText = value : node.setAttribute('style', value);
3539         },
3541         'value': function(node, value){
3542                 node.value = (value != null) ? value : '';
3543         }
3547 propertyGetters['class'] = function(node){
3548         return ('className' in node) ? node.className || null : node.getAttribute('class');
3551 /* <webkit> */
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);
3558 el = null;
3559 /* </webkit> */
3561 /*<IE>*/
3563 /*<ltIE9>*/
3564 // #2479 - IE8 Cannot set HTML of style element
3565 var canChangeStyleHTML = (function(){
3566     var div = document.createElement('style'),
3567         flag = false;
3568     try {
3569         div.innerHTML = '#justTesing{margin: 0px;}';
3570         flag = !!div.innerHTML;
3571     } catch(e){}
3572     return flag;
3573 })();
3574 /*</ltIE9>*/
3576 var input = document.createElement('input'), volatileInputValue, html5InputSupport;
3578 // #2178
3579 input.value = 't';
3580 input.type = 'submit';
3581 volatileInputValue = input.value != 't';
3583 // #2443 - IE throws "Invalid Argument" when trying to use html5 input types
3584 try {
3585         input.type = 'email';
3586         html5InputSupport = input.type == 'email';
3587 } catch(e){}
3589 input = null;
3591 if (volatileInputValue || !html5InputSupport) propertySetters.type = function(node, type){
3592         try {
3593                 var value = node.value;
3594                 node.type = type;
3595                 node.value = value;
3596         } catch (e){}
3598 /*</IE>*/
3600 /* getProperty, setProperty */
3602 /* <ltIE9> */
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'));
3612 /* </ltIE9> */
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;
3620         });
3623 var addToClassList = function(name){
3624         this.classList.add(name);
3627 var removeFromClassList = function(name){
3628         this.classList.remove(name);
3631 Element.implement({
3633         setProperty: function(name, value){
3634                 var setter = propertySetters[name.toLowerCase()];
3635                 if (setter){
3636                         setter(this, value);
3637                 } else {
3638                         /* <ltIE9> */
3639                         var attributeWhiteList;
3640                         if (pollutesGetAttribute) attributeWhiteList = this.retrieve('$attributeWhiteList', {});
3641                         /* </ltIE9> */
3643                         if (value == null){
3644                                 this.removeAttribute(name);
3645                                 /* <ltIE9> */
3646                                 if (pollutesGetAttribute) delete attributeWhiteList[name];
3647                                 /* </ltIE9> */
3648                         } else {
3649                                 this.setAttribute(name, '' + value);
3650                                 /* <ltIE9> */
3651                                 if (pollutesGetAttribute) attributeWhiteList[name] = true;
3652                                 /* </ltIE9> */
3653                         }
3654                 }
3655                 return this;
3656         },
3658         setProperties: function(attributes){
3659                 for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
3660                 return this;
3661         },
3663         getProperty: function(name){
3664                 var getter = propertyGetters[name.toLowerCase()];
3665                 if (getter) return getter(this);
3666                 /* <ltIE9> */
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;
3675                         }
3676                 }
3677                 /* </ltIE9> */
3678                 var result = Slick.getAttribute(this, name);
3679                 return (!result && !Slick.hasAttribute(this, name)) ? null : result;
3680         },
3682         getProperties: function(){
3683                 var args = Array.from(arguments);
3684                 return args.map(this.getProperty, this).associate(args);
3685         },
3687         removeProperty: function(name){
3688                 return this.setProperty(name, null);
3689         },
3691         removeProperties: function(){
3692                 Array.each(arguments, this.removeProperty, this);
3693                 return this;
3694         },
3696         set: function(prop, value){
3697                 var property = Element.Properties[prop];
3698                 (property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value);
3699         }.overloadSetter(),
3701         get: function(prop){
3702                 var property = Element.Properties[prop];
3703                 return (property && property.get) ? property.get.apply(this) : this.getProperty(prop);
3704         }.overloadGetter(),
3706         erase: function(prop){
3707                 var property = Element.Properties[prop];
3708                 (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
3709                 return this;
3710         },
3712         hasClass: hasClassList ? function(className){
3713                 return this.classList.contains(className);
3714         } : function(className){
3715                 return classes(this.className).contains(className);
3716         },
3718         addClass: hasClassList ? function(className){
3719                 classes(className).forEach(addToClassList, this);
3720                 return this;
3721         } : function(className){
3722                 this.className = classes(className + ' ' + this.className).join(' ');
3723                 return this;
3724         },
3726         removeClass: hasClassList ? function(className){
3727                 classes(className).forEach(removeFromClassList, this);
3728                 return this;
3729         } : function(className){
3730                 var classNames = classes(this.className);
3731                 classes(className).forEach(classNames.erase, classNames);
3732                 this.className = classNames.join(' ');
3733                 return this;
3734         },
3736         toggleClass: function(className, force){
3737                 if (force == null) force = !this.hasClass(className);
3738                 return (force) ? this.addClass(className) : this.removeClass(className);
3739         },
3741         adopt: function(){
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);
3748                 }
3750                 if (fragment) this.appendChild(fragment);
3752                 return this;
3753         },
3755         appendText: function(text, where){
3756                 return this.grab(this.getDocument().newTextNode(text), where);
3757         },
3759         grab: function(el, where){
3760                 inserters[where || 'bottom'](document.id(el, true), this);
3761                 return this;
3762         },
3764         inject: function(el, where){
3765                 inserters[where || 'bottom'](this, document.id(el, true));
3766                 return this;
3767         },
3769         replaces: function(el){
3770                 el = document.id(el, true);
3771                 el.parentNode.replaceChild(this, el);
3772                 return this;
3773         },
3775         wraps: function(el, where){
3776                 el = document.id(el, true);
3777                 return this.replaces(el).grab(el, where);
3778         },
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;
3784                 }));
3785         },
3787         toQueryString: function(){
3788                 var queryString = [];
3789                 this.getElements('input, select, textarea').each(function(el){
3790                         var type = el.type;
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){
3794                                 // IE
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));
3800                         });
3801                 });
3802                 return queryString.join('&');
3803         }
3808 // appendHTML
3810 var appendInserters = {
3811         before: 'beforeBegin',
3812         after: 'afterEnd',
3813         bottom: 'beforeEnd',
3814         top: 'afterBegin',
3815         inside: 'beforeEnd'
3818 Element.implement('appendHTML', ('insertAdjacentHTML' in document.createElement('div')) ? function(html, where){
3819         this.insertAdjacentHTML(appendInserters[where || 'bottom'], html);
3820         return this;
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]);
3831                 }
3832         }
3834         inserters[where || 'bottom'](fragment, this);
3835         return 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();
3848         if (uid != null){
3849                 delete collected[uid];
3850                 delete storage[uid];
3851         }
3852         return item;
3855 var formProps = {input: 'checked', option: 'selected', textarea: 'value'};
3857 Element.implement({
3859         destroy: function(){
3860                 var children = clean(this).getElementsByTagName('*');
3861                 Array.each(children, clean);
3862                 Element.dispose(this);
3863                 return null;
3864         },
3866         empty: function(){
3867                 Array.from(this.childNodes).each(Element.dispose);
3868                 return this;
3869         },
3871         dispose: function(){
3872                 return (this.parentNode) ? this.parentNode.removeChild(this) : this;
3873         },
3875         clone: function(contents, keepid){
3876                 contents = contents !== false;
3877                 var clone = this.cloneNode(contents), ce = [clone], te = [this], i;
3879                 if (contents){
3880                         ce.append(Array.from(clone.getElementsByTagName('*')));
3881                         te.append(Array.from(this.getElementsByTagName('*')));
3882                 }
3884                 for (i = ce.length; i--;){
3885                         var node = ce[i], element = te[i];
3886                         if (!keepid) node.removeAttribute('id');
3887                         /*<ltIE9>*/
3888                         if (node.clearAttributes){
3889                                 node.clearAttributes();
3890                                 node.mergeAttributes(element);
3891                                 node.removeAttribute('uniqueNumber');
3892                                 if (node.options){
3893                                         var no = node.options, eo = element.options;
3894                                         for (var j = no.length; j--;) no[j].selected = eo[j].selected;
3895                                 }
3896                         }
3897                         /*</ltIE9>*/
3898                         var prop = formProps[element.tagName.toLowerCase()];
3899                         if (prop && element[prop]) node[prop] = element[prop];
3900                 }
3902                 /*<ltIE9>*/
3903                 if (hasCloneBug){
3904                         var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object');
3905                         for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML;
3906                 }
3907                 /*</ltIE9>*/
3908                 return document.id(clone);
3909         }
3913 [Element, Window, Document].invoke('implement', {
3915         addListener: function(type, fn){
3916                 if (window.attachEvent && !window.addEventListener){
3917                         collected[Slick.uidOf(this)] = this;
3918                 }
3919                 if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]);
3920                 else this.attachEvent('on' + type, fn);
3921                 return this;
3922         },
3924         removeListener: function(type, fn){
3925                 if (this.removeEventListener) this.removeEventListener(type, fn, !!arguments[2]);
3926                 else this.detachEvent('on' + type, fn);
3927                 return this;
3928         },
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;
3934         },
3936         store: function(property, value){
3937                 var storage = get(Slick.uidOf(this));
3938                 storage[property] = value;
3939                 return this;
3940         },
3942         eliminate: function(property){
3943                 var storage = get(Slick.uidOf(this));
3944                 delete storage[property];
3945                 return this;
3946         }
3950 /*<ltIE9>*/
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);
3956         }
3957         window.addListener('unload', gc);
3959 /*</ltIE9>*/
3961 Element.Properties = {};
3963 //<1.2compat>
3965 Element.Properties = new Hash;
3967 //</1.2compat>
3969 Element.Properties.style = {
3971         set: function(style){
3972                 this.style.cssText = style;
3973         },
3975         get: function(){
3976                 return this.style.cssText;
3977         },
3979         erase: function(){
3980                 this.style.cssText = '';
3981         }
3985 Element.Properties.tag = {
3987         get: function(){
3988                 return this.tagName.toLowerCase();
3989         }
3993 Element.Properties.html = {
3995         set: function(html){
3996                 if (html == null) html = '';
3997                 else if (typeOf(html) == 'array') html = html.join('');
3999                 /*<ltIE9>*/
4000                 if (this.styleSheet && !canChangeStyleHTML) this.styleSheet.cssText = html;
4001                 else /*</ltIE9>*/this.innerHTML = html;
4002         },
4003         erase: function(){
4004                 this.set('html', '');
4005         }
4009 var supportsHTML5Elements = true, supportsTableInnerHTML = true, supportsTRInnerHTML = true;
4011 /*<ltIE9>*/
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]);
4021 div = null;
4022 /*</ltIE9>*/
4024 /*<IE>*/
4025 supportsTableInnerHTML = Function.attempt(function(){
4026         var table = document.createElement('table');
4027         table.innerHTML = '<tr><td></td></tr>';
4028         return true;
4031 /*<ltFF4>*/
4032 var tr = document.createElement('tr'), html = '<td></td>';
4033 tr.innerHTML = html;
4034 supportsTRInnerHTML = (tr.innerHTML == html);
4035 tr = null;
4036 /*</ltFF4>*/
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>']
4047                 };
4049                 translations.thead = translations.tfoot = translations.tbody;
4051                 return function(html){
4053                         /*<ltIE9>*/
4054                         if (this.styleSheet) return set.call(this, html);
4055                         /*</ltIE9>*/
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);
4066                         wrapper = null;
4067                 };
4069         })(Element.Properties.html.set);
4071 /*</IE>*/
4073 /*<ltIE9>*/
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;
4089                 }
4090         },
4092         get: function(){
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');
4101         }
4104 testForm = null;
4105 /*</ltIE9>*/
4107 /*<IE>*/
4108 if (document.createElement('div').getAttributeNode('id')) Element.Properties.id = {
4109         set: function(id){
4110                 this.id = this.getAttributeNode('id').value = id;
4111         },
4112         get: function(){
4113                 return this.id || null;
4114         },
4115         erase: function(){
4116                 this.id = this.getAttributeNode('id').value = '';
4117         }
4119 /*</IE>*/
4121 })();
4126 name: Event
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]
4134 provides: Event
4139 (function(){
4141 var _keys = {};
4142 var normalizeWheelSpeed = function(event){
4143     var normalized;
4144     if (event.wheelDelta){
4145         normalized = event.wheelDelta % 120 == 0 ? event.wheelDelta / 120 : event.wheelDelta / 12;
4146     } else {
4147         var rawAmount = event.deltaY || event.detail || 0;
4148         normalized = -(rawAmount % 3 == 0 ? rawAmount / 3 : rawAmount * 10);
4149     }
4150     return normalized;
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;
4157         this.event = 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;
4174                 }
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;
4179                 this.page = {
4180                         x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
4181                         y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
4182                 };
4183                 this.client = {
4184                         x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
4185                         y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
4186                 };
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);
4193                 }
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};
4204                 }
4205         }
4207         if (!this.client) this.client = {};
4208         if (!this.page) this.page = {};
4211 DOMEvent.implement({
4213         stop: function(){
4214                 return this.preventDefault().stopPropagation();
4215         },
4217         stopPropagation: function(){
4218                 if (this.event.stopPropagation) this.event.stopPropagation();
4219                 else this.event.cancelBubble = true;
4220                 return this;
4221         },
4223         preventDefault: function(){
4224                 if (this.event.preventDefault) this.event.preventDefault();
4225                 else this.event.returnValue = false;
4226                 return this;
4227         }
4231 DOMEvent.defineKey = function(code, key){
4232         _keys[code] = key;
4233         return this;
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'
4244 })();
4246 /*<1.3compat>*/
4247 var Event = DOMEvent;
4248 Event.Keys = {};
4249 /*</1.3compat>*/
4251 /*<1.2compat>*/
4253 Event.Keys = new Hash(Event.Keys);
4255 /*</1.2compat>*/
4260 name: Element.Event
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
4273 (function(){
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],
4288                         condition = fn,
4289                         self = this;
4290                 if (custom){
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);
4295                                         return true;
4296                                 };
4297                         }
4298                         if (custom.base) realType = Function.from(custom.base).call(this, type);
4299                 }
4300                 var defn = function(){
4301                         return fn.call(self);
4302                 };
4303                 var nativeEvent = Element.NativeEvents[realType];
4304                 if (nativeEvent){
4305                         if (nativeEvent == 2){
4306                                 defn = function(event){
4307                                         event = new DOMEvent(event, self.getWindow());
4308                                         if (condition.call(self, event) === false) event.stop();
4309                                 };
4310                         }
4311                         this.addListener(realType, defn, arguments[2]);
4312                 }
4313                 events[type].values.push(defn);
4314                 return this;
4315         },
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];
4327                 if (custom){
4328                         if (custom.onRemove) custom.onRemove.call(this, fn, type);
4329                         if (custom.base) type = Function.from(custom.base).call(this, type);
4330                 }
4331                 return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this;
4332         },
4334         addEvents: function(events){
4335                 for (var event in events) this.addEvent(event, events[event]);
4336                 return this;
4337         },
4339         removeEvents: function(events){
4340                 var type;
4341                 if (typeOf(events) == 'object'){
4342                         for (type in events) this.removeEvent(type, events[type]);
4343                         return this;
4344                 }
4345                 var attached = this.retrieve('events');
4346                 if (!attached) return this;
4347                 if (!events){
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);
4353                         }, this);
4354                         delete attached[events];
4355                 }
4356                 return this;
4357         },
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);
4367                 }, this);
4368                 return this;
4369         },
4371         cloneEvents: function(from, type){
4372                 from = document.id(from);
4373                 var events = from.retrieve('events');
4374                 if (!events) return this;
4375                 if (!type){
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);
4380                         }, this);
4381                 }
4382                 return this;
4383         }
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
4401 Element.Events = {
4402         mousewheel: {
4403                 base: 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll'
4404         }
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;
4417 } else {
4418         Element.Events.mouseenter = {
4419                 base: 'mouseover',
4420                 condition: check
4421         };
4423         Element.Events.mouseleave = {
4424                 base: 'mouseout',
4425                 condition: check
4426         };
4429 /*<ltIE9>*/
4430 if (!window.addEventListener){
4431         Element.NativeEvents.propertychange = 2;
4432         Element.Events.change = {
4433                 base: function(){
4434                         var type = this.type;
4435                         return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change';
4436                 },
4437                 condition: function(event){
4438                         return event.type != 'propertychange' || event.event.propertyName == 'checked';
4439                 }
4440         };
4442 /*</ltIE9>*/
4444 //<1.2compat>
4446 Element.Events = new Hash(Element.Events);
4448 //</1.2compat>
4450 })();
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]
4468 (function(){
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);
4478         }
4481 var map = {
4482         mouseenter: {
4483                 base: 'mouseover',
4484                 condition: Element.MouseenterCheck
4485         },
4486         mouseleave: {
4487                 base: 'mouseout',
4488                 condition: Element.MouseenterCheck
4489         },
4490         focus: {
4491                 base: 'focus' + (eventListenerSupport ? '' : 'in'),
4492                 capture: true
4493         },
4494         blur: {
4495                 base: eventListenerSupport ? 'blur' : 'focusout',
4496                 capture: true
4497         }
4500 /*<ltIE9>*/
4501 var _key = '$delegation:';
4502 var formObserver = function(type){
4504         return {
4506                 base: 'focusin',
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]);
4512                         }
4513                 },
4515                 listen: function(self, match, fn, event, target, uid){
4516                         var form = (target.get('tag') == 'form') ? target : event.target.getParent('form');
4517                         if (!form) return;
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;
4524                         forms.push(form);
4526                         var _fn = function(event){
4527                                 bubbleUp(self, match, fn, event, target);
4528                         };
4529                         form.addEvent(type, _fn);
4530                         fns.push(_fn);
4532                         listeners[uid] = listener;
4533                         self.store(_key + type + 'listeners', listeners);
4534                 }
4535         };
4538 var inputObserver = function(type){
4539         return {
4540                 base: 'focusin',
4541                 listen: function(self, match, fn, event, target){
4542                         var events = {blur: function(){
4543                                 this.removeEvents(events);
4544                         }};
4545                         events[type] = function(event){
4546                                 bubbleUp(self, match, fn, event, target);
4547                         };
4548                         event.target.addEvents(events);
4549                 }
4550         };
4553 if (!eventListenerSupport) Object.append(map, {
4554         submit: formObserver('submit'),
4555         reset: formObserver('reset'),
4556         change: inputObserver('change'),
4557         select: inputObserver('select')
4559 /*</ltIE9>*/
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 + ')' : '');
4573                 });
4574                 old.call(this, type, fn);
4575                 return method.call(this, newType, parsed.pseudos[0].value, fn);
4576         };
4579 var delegation = {
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;
4585                 }
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);
4592                 };
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);
4599                         };
4600                 }
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);
4609                 };
4611                 if (!stored) stored = {};
4612                 stored[uid] = {
4613                         match: _match,
4614                         fn: _fn,
4615                         delegator: delegator
4616                 };
4617                 storage[_type] = stored;
4618                 return addEvent.call(this, type, delegator, _map.capture);
4619         },
4621         removeEvent: function(type, match, fn, _uid){
4622                 var storage = this.retrieve('$delegates', {}), stored = storage[type];
4623                 if (!stored) return this;
4625                 if (_uid){
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);
4632                 }
4634                 var __uid, s;
4635                 if (fn) for (__uid in stored){
4636                         s = stored[__uid];
4637                         if (s.match == match && s.fn == fn) return delegation.removeEvent.call(this, type, match, fn, __uid);
4638                 } else for (__uid in stored){
4639                         s = stored[__uid];
4640                         if (s.match == match) delegation.removeEvent.call(this, type, match, s.fn, __uid);
4641                 }
4642                 return this;
4643         }
4647 [Element, Window, Document].invoke('implement', {
4648         addEvent: relay(addEvent, delegation.addEvent),
4649         removeEvent: relay(removeEvent, delegation.removeEvent)
4652 })();
4657 name: Element.Style
4659 description: Contains methods for interacting with the styles of Elements in a fashionable way.
4661 license: MIT-style license.
4663 requires: Element
4665 provides: Element.Style
4670 (function(){
4672 var html = document.html, el;
4674 //<ltIE9>
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;
4685 el = null;
4686 //</ltIE9>
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';
4704 //<ltIE9>
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');
4711 //</ltIE9>
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)');
4720         } else {
4721                 setFilter(element, reAlpha, 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')');
4722         }
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')),
4730                 opacity;
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);
4736         return opacity;
4737 }));
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);
4743 //<ltIE9>
4744 var removeStyle = function(style, property){
4745         if (property == 'backgroundPosition'){
4746                 style.removeAttribute(property + 'X');
4747                 property += 'Y';
4748         }
4749         style.removeAttribute(property);
4751 //</ltIE9>
4753 Element.implement({
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()) : '';
4760         },
4762         setStyle: function(property, value){
4763                 if (property == 'opacity'){
4764                         if (value != null) value = parseFloat(value);
4765                         setOpacity(this, value);
4766                         return this;
4767                 }
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;
4774                         }).join(' ');
4775                 } else if (value == String(Number(value))){
4776                         value = Math.round(value);
4777                 }
4778                 this.style[property] = value;
4779                 //<ltIE9>
4780                 if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){
4781                         removeStyle(this.style, property);
4782                 }
4783                 //</ltIE9>
4784                 return this;
4785         },
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';
4793                         }, this).join(' ');
4794                 }
4795                 var result = this.style[property];
4796                 if (!result || property == 'zIndex'){
4797                         if (Element.ShortStyles.hasOwnProperty(property)){
4798                                 result = [];
4799                                 for (var s in Element.ShortStyles[property]) result.push(this.getStyle(s));
4800                                 return result.join(' ');
4801                         }
4802                         result = this.getComputedStyle(property);
4803                 }
4804                 if (hasBackgroundPositionXY && /^backgroundPosition[XY]?$/.test(property)){
4805                         return result.replace(/(top|right|bottom|left)/g, function(position){
4806                                 return namedPositions[position];
4807                         }) || '0px';
4808                 }
4809                 if (!result && property == 'backgroundPosition') return '0px 0px';
4810                 if (result){
4811                         result = String(result);
4812                         var color = result.match(/rgba?\([\d\s,]+\)/);
4813                         if (color) result = result.replace(color[0], color[0].rgbToHex());
4814                 }
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();
4820                                 }, this);
4821                                 return this['offset' + property.capitalize()] - size + 'px';
4822                         }
4823                         if ((/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){
4824                                 return '0px';
4825                         }
4826                 }
4827                 //<ltIE9>
4828                 if (returnsBordersInWrongOrder && /^border(Top|Right|Bottom|Left)?$/.test(property) && /^#/.test(result)){
4829                         return result.replace(/^(.+)\s(.+)\s(.+)$/, '$2 $3 $1');
4830                 }
4831                 //</ltIE9>
4833                 return result;
4834         },
4836         setStyles: function(styles){
4837                 for (var style in styles) this.setStyle(style, styles[style]);
4838                 return this;
4839         },
4841         getStyles: function(){
4842                 var result = {};
4843                 Array.flatten(arguments).each(function(key){
4844                         result[key] = this.getStyle(key);
4845                 }, this);
4846                 return result;
4847         }
4851 Element.Styles = {
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'
4861 //<1.3compat>
4863 Element.implement({
4865         setOpacity: function(value){
4866                 setOpacity(this, value);
4867                 return this;
4868         },
4870         getOpacity: function(){
4871                 return getOpacity(this);
4872         }
4876 Element.Properties.opacity = {
4878         set: function(opacity){
4879                 setOpacity(this, opacity);
4880                 setVisibility(this, opacity);
4881         },
4883         get: function(){
4884                 return getOpacity(this);
4885         }
4889 //</1.3compat>
4891 //<1.2compat>
4893 Element.Styles = new Hash(Element.Styles);
4895 //</1.2compat>
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';
4905         });
4906         var bd = 'border' + direction;
4907         Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
4908         var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
4909         Short[bd] = {};
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: '@'};
4916 })();
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.
4927 credits:
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]
4938 (function(){
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]);
4957         });
4958         widthComponents.each(function(css){
4959                 bounds.x += parseFloat(gCS[css]);
4960         });
4961         return bounds;
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);
4972 Element.implement({
4974         scrollTo: function(x, y){
4975                 if (isBody(this)){
4976                         this.getWindow().scrollTo(x, y);
4977                 } else {
4978                         this.scrollLeft = x;
4979                         this.scrollTop = y;
4980                 }
4981                 return this;
4982         },
4984         getSize: function(){
4985                 if (isBody(this)) return this.getWindow().getSize();
4987                 //<ltIE9>
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};
4990                 //</ltIE9>
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);
4995                 
4996                 var bounds = this.getBoundingClientRect();
4997                 return {x: bounds.width, y: bounds.height};
4998         },
5000         getScrollSize: function(){
5001                 if (isBody(this)) return this.getWindow().getScrollSize();
5002                 return {x: this.scrollWidth, y: this.scrollHeight};
5003         },
5005         getScroll: function(){
5006                 if (isBody(this)) return this.getWindow().getScroll();
5007                 return {x: this.scrollLeft, y: this.scrollTop};
5008         },
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;
5016                 }
5017                 return position;
5018         },
5020         getOffsetParent: brokenOffsetParent ? function(){
5021                 var element = this;
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;
5027                 }
5028                 return null;
5029         } : function(){
5030                 var element = this;
5031                 if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
5033                 try {
5034                         return element.offsetParent;
5035                 } catch(e){}
5036                 return null;
5037         },
5039         getOffsets: function(){
5040                 var hasGetBoundingClientRect = this.getBoundingClientRect;
5041 //<1.4compat>
5042                 hasGetBoundingClientRect = hasGetBoundingClientRect && !Browser.Platform.ios
5043 //</1.4compat>
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');
5051                         return {
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
5054                         };
5055                 }
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;
5063 //<1.4compat>
5064                         if (Browser.firefox){
5065                                 if (!borderBox(element)){
5066                                         position.x += leftBorder(element);
5067                                         position.y += topBorder(element);
5068                                 }
5069                                 var parent = element.parentNode;
5070                                 if (parent && styleString(parent, 'overflow') != 'visible'){
5071                                         position.x += leftBorder(parent);
5072                                         position.y += topBorder(parent);
5073                                 }
5074                         } else if (element != this && Browser.safari){
5075                                 position.x += leftBorder(element);
5076                                 position.y += topBorder(element);
5077                         }
5078 //</1.4compat>
5079                         element = element.offsetParent;
5080                 }
5081 //<1.4compat>
5082                 if (Browser.firefox && !borderBox(this)){
5083                         position.x -= leftBorder(this);
5084                         position.y -= topBorder(this);
5085                 }
5086 //</1.4compat>
5087                 return position;
5088         },
5090         getPosition: function(relative){
5091                 var offset = this.getOffsets(),
5092                         scroll = this.getScrolls();
5093                 var position = {
5094                         x: offset.x - scroll.x,
5095                         y: offset.y - scroll.y
5096                 };
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)};
5101                 }
5102                 return position;
5103         },
5105         getCoordinates: function(element){
5106                 if (isBody(this)) return this.getWindow().getCoordinates();
5107                 var position = this.getPosition(element),
5108                         size = this.getSize();
5109                 var obj = {
5110                         left: position.x,
5111                         top: position.y,
5112                         width: size.x,
5113                         height: size.y
5114                 };
5115                 obj.right = obj.left + obj.width;
5116                 obj.bottom = obj.top + obj.height;
5117                 return obj;
5118         },
5120         computePosition: function(obj){
5121                 return {
5122                         left: obj.x - styleNumber(this, 'margin-left'),
5123                         top: obj.y - styleNumber(this, 'margin-top')
5124                 };
5125         },
5127         setPosition: function(obj){
5128                 return this.setStyles(this.computePosition(obj));
5129         }
5134 [Document, Window].invoke('implement', {
5136         getSize: function(){
5137                 var doc = getCompatElement(this);
5138                 return {x: doc.clientWidth, y: doc.clientHeight};
5139         },
5141         getScroll: function(){
5142                 var win = this.getWindow(), doc = getCompatElement(this);
5143                 return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
5144         },
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)};
5152         },
5154         getPosition: function(){
5155                 return {x: 0, y: 0};
5156         },
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};
5161         }
5165 // private methods
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;
5194 })();
5196 //aliases
5197 Element.alias({position: 'setPosition'}); //compatability
5199 [Window, Document, Element].invoke('implement', {
5201         getHeight: function(){
5202                 return this.getSize().y;
5203         },
5205         getWidth: function(){
5206                 return this.getSize().x;
5207         },
5209         getScrollTop: function(){
5210                 return this.getScroll().y;
5211         },
5213         getScrollLeft: function(){
5214                 return this.getScroll().x;
5215         },
5217         getScrollHeight: function(){
5218                 return this.getScrollSize().y;
5219         },
5221         getScrollWidth: function(){
5222                 return this.getScrollSize().x;
5223         },
5225         getTop: function(){
5226                 return this.getPosition().y;
5227         },
5229         getLeft: function(){
5230                 return this.getPosition().x;
5231         }
5238 name: Fx
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]
5246 provides: Fx
5251 (function(){
5253 var Fx = this.Fx = new Class({
5255         Implements: [Chain, Events, Options],
5257         options: {
5258                 /*
5259                 onStart: nil,
5260                 onCancel: nil,
5261                 onComplete: nil,
5262                 */
5263                 fps: 60,
5264                 unit: false,
5265                 duration: 500,
5266                 frames: null,
5267                 frameSkip: true,
5268                 link: 'ignore'
5269         },
5271         initialize: function(options){
5272                 this.subject = this.subject || this;
5273                 this.setOptions(options);
5274         },
5276         getTransition: function(){
5277                 return function(p){
5278                         return -(Math.cos(Math.PI * p) - 1) / 2;
5279                 };
5280         },
5282         step: function(now){
5283                 if (this.options.frameSkip){
5284                         var diff = (this.time != null) ? (now - this.time) : 0, frames = diff / this.frameInterval;
5285                         this.time = now;
5286                         this.frame += frames;
5287                 } else {
5288                         this.frame++;
5289                 }
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));
5294                 } else {
5295                         this.frame = this.frames;
5296                         this.set(this.compute(this.from, this.to, 1));
5297                         this.stop();
5298                 }
5299         },
5301         set: function(now){
5302                 return now;
5303         },
5305         compute: function(from, to, delta){
5306                 return Fx.compute(from, to, delta);
5307         },
5309         check: function(){
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;
5314                 }
5315                 return false;
5316         },
5318         start: function(from, to){
5319                 if (!this.check(from, to)) return this;
5320                 this.from = from;
5321                 this.to = to;
5322                 this.frame = (this.options.frameSkip) ? 0 : -1;
5323                 this.time = null;
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);
5331                 return this;
5332         },
5334         stop: function(){
5335                 if (this.isRunning()){
5336                         this.time = null;
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);
5341                         } else {
5342                                 this.fireEvent('stop', this.subject);
5343                         }
5344                 }
5345                 return this;
5346         },
5348         cancel: function(){
5349                 if (this.isRunning()){
5350                         this.time = null;
5351                         pullInstance.call(this, this.options.fps);
5352                         this.frame = this.frames;
5353                         this.fireEvent('cancel', this.subject).clearChain();
5354                 }
5355                 return this;
5356         },
5358         pause: function(){
5359                 if (this.isRunning()){
5360                         this.time = null;
5361                         pullInstance.call(this, this.options.fps);
5362                 }
5363                 return this;
5364         },
5366         resume: function(){
5367                 if (this.isPaused()) pushInstance.call(this, this.options.fps);
5368                 return this;
5369         },
5371         isRunning: function(){
5372                 var list = instances[this.options.fps];
5373                 return list && list.contains(this);
5374         },
5376         isPaused: function(){
5377                 return (this.frame < this.frames) && !this.isRunning();
5378         }
5382 Fx.compute = function(from, to, delta){
5383         return (to - from) * delta + from;
5386 Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
5388 // global timers
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);
5397         }
5400 var pushInstance = function(fps){
5401         var list = instances[fps] || (instances[fps] = []);
5402         list.push(this);
5403         if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list);
5406 var pullInstance = function(fps){
5407         var list = instances[fps];
5408         if (list){
5409                 list.erase(this);
5410                 if (!list.length && timers[fps]){
5411                         delete instances[fps];
5412                         timers[fps] = clearInterval(timers[fps]);
5413                 }
5414         }
5417 })();
5422 name: Fx.CSS
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]
5430 provides: Fx.CSS
5435 Fx.CSS = new Class({
5437         Extends: Fx,
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];
5444                 if (to == null){
5445                         to = from;
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()];
5455                                         if (value == null){
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;
5461                                         }
5462                                 }
5463                                 from = (to || 1) / (parseFloat(value) || 1) * (parseFloat(from) || 0);
5464                                 element.setStyle(property, from + unit);
5465                         }
5466                 }
5467                 return {from: this.parse(from), to: this.parse(to)};
5468         },
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){
5476                         val = String(val);
5477                         var found = false;
5478                         Object.each(Fx.CSS.Parsers, function(parser, key){
5479                                 if (found) return;
5480                                 var parsed = parser.parse(val);
5481                                 if (parsed || parsed === 0) found = {value: parsed, parser: parser};
5482                         });
5483                         found = found || {value: val, parser: Fx.CSS.Parsers.String};
5484                         return found;
5485                 });
5486         },
5488         //computes by a from and to prepared objects, using their parsers.
5490         compute: function(from, to, delta){
5491                 var computed = [];
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});
5494                 });
5495                 computed.$family = Function.from('fx:css:value');
5496                 return computed;
5497         },
5499         //serves the value as settable
5501         serve: function(value, unit){
5502                 if (typeOf(value) != 'fx:css:value') value = this.parse(value);
5503                 var returned = [];
5504                 value.each(function(bit){
5505                         returned = returned.concat(bit.parser.serve(bit.value, unit));
5506                 });
5507                 return returned;
5508         },
5510         //renders the change to an element
5512         render: function(element, property, value, unit){
5513                 element.setStyle(property, this.serve(value, unit));
5514         },
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){
5524                                 if (rule.media){
5525                                         searchStyles(rule.rules || rule.cssRules);
5526                                         return;
5527                                 }
5528                                 if (!rule.style) return;
5529                                 var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
5530                                         return m.toLowerCase();
5531                                 }) : null;
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;
5537                                 });
5538                         });
5539                 };
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);
5546                 });
5547                 return Fx.CSS.Cache[selector] = to;
5548         }
5552 Fx.CSS.Cache = {};
5554 Fx.CSS.Parsers = {
5556         Color: {
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;
5560                 },
5561                 compute: function(from, to, delta){
5562                         return from.map(function(value, i){
5563                                 return Math.round(Fx.compute(from[i], to[i], delta));
5564                         });
5565                 },
5566                 serve: function(value){
5567                         return value.map(Number);
5568                 }
5569         },
5571         Number: {
5572                 parse: parseFloat,
5573                 compute: Fx.compute,
5574                 serve: function(value, unit){
5575                         return (unit) ? value + unit : value;
5576                 }
5577         },
5579         String: {
5580                 parse: Function.from(false),
5581                 compute: function(zero, one){
5582                         return one;
5583                 },
5584                 serve: function(zero){
5585                         return zero;
5586                 }
5587         }
5591 //<1.2compat>
5593 Fx.CSS.Parsers = new Hash(Fx.CSS.Parsers);
5595 //</1.2compat>
5600 name: Fx.Morph
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.
5606 requires: Fx.CSS
5608 provides: Fx.Morph
5613 Fx.Morph = new Class({
5615         Extends: Fx.CSS,
5617         initialize: function(element, options){
5618                 this.element = this.subject = document.id(element);
5619                 this.parent(options);
5620         },
5622         set: function(now){
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);
5625                 return this;
5626         },
5628         compute: function(from, to, delta){
5629                 var now = {};
5630                 for (var p in from) now[p] = this.parent(from[p], to[p], delta);
5631                 return now;
5632         },
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;
5641                         to[p] = parsed.to;
5642                 }
5643                 return this.parent(from, to);
5644         }
5648 Element.Properties.morph = {
5650         set: function(options){
5651                 this.get('morph').cancel().setOptions(options);
5652                 return this;
5653         },
5655         get: function(){
5656                 var morph = this.retrieve('morph');
5657                 if (!morph){
5658                         morph = new Fx.Morph(this, {link: 'cancel'});
5659                         this.store('morph', morph);
5660                 }
5661                 return morph;
5662         }
5666 Element.implement({
5668         morph: function(props){
5669                 this.get('morph').start(props);
5670                 return this;
5671         }
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.
5684 credits:
5685   - Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
5687 requires: Fx
5689 provides: Fx.Transitions
5694 Fx.implement({
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() : '')];
5703                 }
5704                 return trans;
5705         }
5709 Fx.Transition = function(transition, params){
5710         params = Array.from(params);
5711         var easeIn = function(pos){
5712                 return transition(pos, params);
5713         };
5714         return Object.append(easeIn, {
5715                 easeIn: easeIn,
5716                 easeOut: function(pos){
5717                         return 1 - transition(1 - pos, params);
5718                 },
5719                 easeInOut: function(pos){
5720                         return (pos <= 0.5 ? transition(2 * pos, params) : (2 - transition(2 * (1 - pos), params))) / 2;
5721                 }
5722         });
5725 Fx.Transitions = {
5727         linear: function(zero){
5728                 return zero;
5729         }
5733 //<1.2compat>
5735 Fx.Transitions = new Hash(Fx.Transitions);
5737 //</1.2compat>
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);
5747         },
5749         Expo: function(p){
5750                 return Math.pow(2, 8 * (p - 1));
5751         },
5753         Circ: function(p){
5754                 return 1 - Math.sin(Math.acos(p));
5755         },
5757         Sine: function(p){
5758                 return 1 - Math.cos(p * Math.PI / 2);
5759         },
5761         Back: function(p, x){
5762                 x = x && x[0] || 1.618;
5763                 return Math.pow(p, 2) * ((x + 1) * p - x);
5764         },
5766         Bounce: function(p){
5767                 var value;
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);
5771                                 break;
5772                         }
5773                 }
5774                 return value;
5775         },
5777         Elastic: function(p, x){
5778                 return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && x[0] || 1) / 3);
5779         }
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);
5786         });
5792 name: Fx.Tween
5794 description: Formerly Fx.Style, effect to transition any CSS property for an element.
5796 license: MIT-style license.
5798 requires: Fx.CSS
5800 provides: [Fx.Tween, Element.fade, Element.highlight]
5805 Fx.Tween = new Class({
5807         Extends: Fx.CSS,
5809         initialize: function(element, options){
5810                 this.element = this.subject = document.id(element);
5811                 this.parent(options);
5812         },
5814         set: function(property, now){
5815                 if (arguments.length == 1){
5816                         now = property;
5817                         property = this.property || this.options.property;
5818                 }
5819                 this.render(this.element, property, now, this.options.unit);
5820                 return this;
5821         },
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);
5829         }
5833 Element.Properties.tween = {
5835         set: function(options){
5836                 this.get('tween').cancel().setOptions(options);
5837                 return this;
5838         },
5840         get: function(){
5841                 var tween = this.retrieve('tween');
5842                 if (!tween){
5843                         tween = new Fx.Tween(this, {link: 'cancel'});
5844                         this.store('tween', tween);
5845                 }
5846                 return tween;
5847         }
5851 Element.implement({
5853         tween: function(property, from, to){
5854                 this.get('tween').start(property, from, to);
5855                 return this;
5856         },
5858         fade: function(how){
5859                 var fade = this.get('tween'), method, args = ['opacity'].append(arguments), toggle;
5860                 if (args[1] == null) args[1] = 'toggle';
5861                 switch (args[1]){
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;
5866                         case 'toggle':
5867                                 var flag = this.retrieve('fade:flag', this.getStyle('opacity') == 1);
5868                                 method = 'start';
5869                                 args[1] = flag ? 0 : 1;
5870                                 this.store('fade:flag', !flag);
5871                                 toggle = true;
5872                         break;
5873                         default: method = 'start';
5874                 }
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');
5881                         this.callChain();
5882                 });
5883                 return this;
5884         },
5886         highlight: function(start, end){
5887                 if (!end){
5888                         end = this.retrieve('highlight:original', this.getStyle('background-color'));
5889                         end = (end == 'transparent') ? '#fff' : end;
5890                 }
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'));
5894                         tween.callChain();
5895                 }.bind(this));
5896                 return this;
5897         }
5904 name: Request
5906 description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
5908 license: MIT-style license.
5910 requires: [Object, Element, Chain, Events, Options, Browser]
5912 provides: Request
5917 (function(){
5919 var empty = function(){},
5920         progressSupport = ('onprogress' in new Browser.Request);
5922 var Request = this.Request = new Class({
5924         Implements: [Chain, Events, Options],
5926         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(){},
5936                 user: '',
5937                 password: '',
5938                 withCredentials: false,*/
5939                 url: '',
5940                 data: '',
5941                 headers: {
5942                         'X-Requested-With': 'XMLHttpRequest',
5943                         'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
5944                 },
5945                 async: true,
5946                 format: false,
5947                 method: 'post',
5948                 link: 'ignore',
5949                 isSuccess: null,
5950                 emulation: true,
5951                 urlEncoded: true,
5952                 encoding: 'utf-8',
5953                 evalScripts: false,
5954                 evalResponse: false,
5955                 timeout: 0,
5956                 noCache: false
5957         },
5959         initialize: function(options){
5960                 this.xhr = new Browser.Request();
5961                 this.setOptions(options);
5962                 this.headers = this.options.headers;
5963         },
5965         onStateChange: function(){
5966                 var xhr = this.xhr;
5967                 if (xhr.readyState != 4 || !this.running) return;
5968                 this.running = false;
5969                 this.status = 0;
5970                 Function.attempt(function(){
5971                         var status = xhr.status;
5972                         this.status = (status == 1223) ? 204 : status;
5973                 }.bind(this));
5974                 xhr.onreadystatechange = empty;
5975                 if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
5976                 if (this.timer){
5977                         clearTimeout(this.timer);
5978                         delete this.timer;
5979                 }
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);
5984                 else
5985                         this.failure();
5986         },
5988         isSuccess: function(){
5989                 var status = this.status;
5990                 return (status >= 200 && status < 300);
5991         },
5993         isRunning: function(){
5994                 return !!this.running;
5995         },
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);
6000         },
6002         success: function(text, xml){
6003                 this.onSuccess(this.processScripts(text), xml);
6004         },
6006         onSuccess: function(){
6007                 this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
6008         },
6010         failure: function(){
6011                 this.onFailure();
6012         },
6014         onFailure: function(){
6015                 this.fireEvent('complete').fireEvent('failure', this.xhr);
6016         },
6018         loadstart: function(event){
6019                 this.fireEvent('loadstart', [event, this.xhr]);
6020         },
6022         progress: function(event){
6023                 this.fireEvent('progress', [event, this.xhr]);
6024         },
6026         timeout: function(){
6027                 this.fireEvent('timeout', this.xhr);
6028         },
6030         setHeader: function(name, value){
6031                 this.headers[name] = value;
6032                 return this;
6033         },
6035         getHeader: function(name){
6036                 return Function.attempt(function(){
6037                         return this.xhr.getResponseHeader(name);
6038                 }.bind(this));
6039         },
6041         check: function(){
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;
6046                 }
6047                 return false;
6048         },
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);
6066                 }
6068                 if (this.options.format){
6069                         var format = 'format=' + this.options.format;
6070                         data = (data) ? format + '&' + data : format;
6071                 }
6073                 if (this.options.emulation && !['get', 'post'].contains(method)){
6074                         var _method = '_method=' + method;
6075                         data = (data) ? _method + '&' + data : _method;
6076                         method = 'post';
6077                 }
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;
6082                 }
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;
6094                         data = null;
6095                 }
6097                 var xhr = this.xhr;
6098                 if (progressSupport){
6099                         xhr.onloadstart = this.loadstart.bind(this);
6100                         xhr.onprogress = this.progress.bind(this);
6101                 }
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){
6109                         try {
6110                                 xhr.setRequestHeader(key, value);
6111                         } catch (e){
6112                                 this.fireEvent('exception', [key, value]);
6113                         }
6114                 }, this);
6116                 this.fireEvent('request');
6117                 xhr.send(data);
6118                 if (!this.options.async) this.onStateChange();
6119                 else if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
6120                 return this;
6121         },
6123         cancel: function(){
6124                 if (!this.running) return this;
6125                 this.running = false;
6126                 var xhr = this.xhr;
6127                 xhr.abort();
6128                 if (this.timer){
6129                         clearTimeout(this.timer);
6130                         delete this.timer;
6131                 }
6132                 xhr.onreadystatechange = empty;
6133                 if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
6134                 this.xhr = new Browser.Request();
6135                 this.fireEvent('cancel');
6136                 return this;
6137         }
6141 var methods = {};
6142 ['get', 'post', 'put', 'delete', 'patch', 'head', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'].each(function(method){
6143         methods[method] = function(data){
6144                 var object = {
6145                         method: method
6146                 };
6147                 if (data != null) object.data = data;
6148                 return this.send(object);
6149         };
6152 Request.implement(methods);
6154 Element.Properties.send = {
6156         set: function(options){
6157                 var send = this.get('send').cancel();
6158                 send.setOptions(options);
6159                 return this;
6160         },
6162         get: function(){
6163                 var send = this.retrieve('send');
6164                 if (!send){
6165                         send = new Request({
6166                                 data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
6167                         });
6168                         this.store('send', send);
6169                 }
6170                 return send;
6171         }
6175 Element.implement({
6177         send: function(url){
6178                 var sender = this.get('send');
6179                 sender.send({data: this, url: url || sender.options.url});
6180                 return this;
6181         }
6185 })();
6190 name: Request.HTML
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({
6205         Extends: Request,
6207         options: {
6208                 update: false,
6209                 append: false,
6210                 evalScripts: true,
6211                 filter: false,
6212                 headers: {
6213                         Accept: 'text/html, application/xml, text/xml, */*'
6214                 }
6215         },
6217         success: function(text){
6218                 var options = this.options, response = this.response;
6220                 response.html = text.stripScripts(function(script){
6221                         response.javascript = script;
6222                 });
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());
6240                 }
6241                 if (options.evalScripts) Browser.exec(response.javascript);
6243                 this.onSuccess(response.tree, response.elements, response.html, response.javascript);
6244         }
6248 Element.Properties.load = {
6250         set: function(options){
6251                 var load = this.get('load').cancel();
6252                 load.setOptions(options);
6253                 return this;
6254         },
6256         get: function(){
6257                 var load = this.retrieve('load');
6258                 if (!load){
6259                         load = new Request.HTML({data: this, link: 'cancel', update: this, method: 'get'});
6260                         this.store('load', load);
6261                 }
6262                 return load;
6263         }
6267 Element.implement({
6269         load: function(){
6270                 this.get('load').send(Array.link(arguments, {data: Type.isObject, url: Type.isString}));
6271                 return this;
6272         }
6279 name: JSON
6281 description: JSON encoder and decoder.
6283 license: MIT-style license.
6285 SeeAlso: <http://www.json.org/>
6287 requires: [Array, String, Number, Function]
6289 provides: JSON
6294 if (typeof JSON == 'undefined') this.JSON = {};
6296 //<1.2compat>
6298 JSON = new Hash({
6299         stringify: JSON.stringify,
6300         parse: JSON.parse
6303 //</1.2compat>
6305 (function(){
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);
6323 } : function(obj){
6324         if (obj && obj.toJSON) obj = obj.toJSON();
6326         switch (typeOf(obj)){
6327                 case 'string':
6328                         return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
6329                 case 'array':
6330                         return '[' + obj.map(JSON.encode).clean() + ']';
6331                 case 'object': case 'hash':
6332                         var string = [];
6333                         Object.each(obj, function(value, key){
6334                                 var json = JSON.encode(value);
6335                                 if (json) string.push(JSON.encode(key) + ':' + json);
6336                         });
6337                         return '{' + string + '}';
6338                 case 'number': case 'boolean': return '' + obj;
6339                 case 'null': return 'null';
6340         }
6342         return null;
6345 JSON.secure = true;
6346 //<1.4compat>
6347 JSON.secure = false;
6348 //</1.4compat>
6350 JSON.decode = function(string, secure){
6351         if (!string || typeOf(string) != 'string') return null;
6352     
6353         if (secure == null) secure = JSON.secure; 
6354         if (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.');
6357         }
6359         return eval('(' + string + ')');
6362 })();
6367 name: Request.JSON
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({
6382         Extends: Request,
6384         options: {
6385                 /*onError: function(text, error){},*/
6386                 secure: true
6387         },
6389         initialize: function(options){
6390                 this.parent(options);
6391                 Object.append(this.headers, {
6392                         'Accept': 'application/json',
6393                         'X-Request': 'JSON'
6394                 });
6395         },
6397         success: function(text){
6398                 var json;
6399                 try {
6400                         json = this.response.json = JSON.decode(text, this.options.secure);
6401                 } catch (error){
6402                         this.fireEvent('error', [text, error]);
6403                         return;
6404                 }
6405                 if (json == null) this.onFailure();
6406                 else this.onSuccess(json, text);
6407         }
6414 name: Cookie
6416 description: Class for creating, reading, and deleting browser Cookies.
6418 license: MIT-style license.
6420 credits:
6421   - Based on the functions by Peter-Paul Koch (http://quirksmode.org).
6423 requires: [Options, Browser]
6425 provides: Cookie
6430 var Cookie = new Class({
6432         Implements: Options,
6434         options: {
6435                 path: '/',
6436                 domain: false,
6437                 duration: false,
6438                 secure: false,
6439                 document: document,
6440                 encode: true
6441         },
6443         initialize: function(key, options){
6444                 this.key = key;
6445                 this.setOptions(options);
6446         },
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();
6456                 }
6457                 if (this.options.secure) value += '; secure';
6458                 this.options.document.cookie = this.key + '=' + value;
6459                 return this;
6460         },
6462         read: function(){
6463                 var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
6464                 return (value) ? decodeURIComponent(value[1]) : null;
6465         },
6467         dispose: function(){
6468                 new Cookie(this.key, Object.merge({}, this.options, {duration: -1})).write('');
6469                 return this;
6470         }
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();
6489 name: DOMReady
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){
6504 var ready,
6505         loaded,
6506         checks = [],
6507         shouldPoll,
6508         timer,
6509         testElement = document.createElement('div');
6511 var domready = function(){
6512         clearTimeout(timer);
6513         if (!ready) {
6514                 Browser.loaded = ready = true;
6515                 document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check);
6516                 document.fireEvent('domready');
6517                 window.fireEvent('domready');
6518         }
6519         // cleanup scope vars
6520         document = window = testElement = null;
6523 var check = function(){
6524         for (var i = checks.length; i--;) if (checks[i]()){
6525                 domready();
6526                 return true;
6527         }
6528         return false;
6531 var poll = function(){
6532         clearTimeout(timer);
6533         if (!check()) timer = setTimeout(poll, 10);
6536 document.addListener('DOMContentLoaded', domready);
6538 /*<ltIE8>*/
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(){
6542         try {
6543                 testElement.doScroll();
6544                 return true;
6545         } catch (e){}
6546         return false;
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);
6552         shouldPoll = true;
6554 /*</ltIE8>*/
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);
6569         }
6572 // Make sure that domready fires before load
6573 Element.Events.load = {
6574         base: 'load',
6575         onAdd: function(fn){
6576                 if (loaded && this == window) fn.call(this);
6577         },
6578         condition: function(){
6579                 if (this == window){
6580                         domready();
6581                         delete Element.Events.load;
6582                 }
6583                 return true;
6584         }
6587 // This is based on the custom load event
6588 window.addEvent('load', function(){
6589         loaded = true;
6592 })(window, document);