borderRadius getter, specs and consistency
[mootools.git] / dist / mootools-core-compat.js
blob32472d4be1b039d8bc48ff1964f9800b9b736225
1 /*
2 ---
4 name: Core
6 description: The heart of MooTools.
8 license: MIT-style license.
10 copyright: Copyright (c) 2006-2012 [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.0',
27         build: '0f7b690afee9349b15909f33016a25d2e4d9f4e3'
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: String
708 description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
710 license: MIT-style license.
712 requires: [Type, Array]
714 provides: String
719 String.implement({
721         //<!ES6>
722         contains: function(string, index){
723                 return (index ? String(this).slice(index) : String(this)).indexOf(string) > -1;
724         },
725         //</!ES6>
727         test: function(regex, params){
728                 return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
729         },
731         trim: function(){
732                 return String(this).replace(/^\s+|\s+$/g, '');
733         },
735         clean: function(){
736                 return String(this).replace(/\s+/g, ' ').trim();
737         },
739         camelCase: function(){
740                 return String(this).replace(/-\D/g, function(match){
741                         return match.charAt(1).toUpperCase();
742                 });
743         },
745         hyphenate: function(){
746                 return String(this).replace(/[A-Z]/g, function(match){
747                         return ('-' + match.charAt(0).toLowerCase());
748                 });
749         },
751         capitalize: function(){
752                 return String(this).replace(/\b[a-z]/g, function(match){
753                         return match.toUpperCase();
754                 });
755         },
757         escapeRegExp: function(){
758                 return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
759         },
761         toInt: function(base){
762                 return parseInt(this, base || 10);
763         },
765         toFloat: function(){
766                 return parseFloat(this);
767         },
769         hexToRgb: function(array){
770                 var hex = String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
771                 return (hex) ? hex.slice(1).hexToRgb(array) : null;
772         },
774         rgbToHex: function(array){
775                 var rgb = String(this).match(/\d{1,3}/g);
776                 return (rgb) ? rgb.rgbToHex(array) : null;
777         },
779         substitute: function(object, regexp){
780                 return String(this).replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
781                         if (match.charAt(0) == '\\') return match.slice(1);
782                         return (object[name] != null) ? object[name] : '';
783                 });
784         }
788 //<1.4compat>
789 String.prototype.contains = function(string, separator){
790         return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : String(this).indexOf(string) > -1;
792 //</1.4compat>
797 name: Number
799 description: Contains Number Prototypes like limit, round, times, and ceil.
801 license: MIT-style license.
803 requires: Type
805 provides: Number
810 Number.implement({
812         limit: function(min, max){
813                 return Math.min(max, Math.max(min, this));
814         },
816         round: function(precision){
817                 precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
818                 return Math.round(this * precision) / precision;
819         },
821         times: function(fn, bind){
822                 for (var i = 0; i < this; i++) fn.call(bind, i, this);
823         },
825         toFloat: function(){
826                 return parseFloat(this);
827         },
829         toInt: function(base){
830                 return parseInt(this, base || 10);
831         }
835 Number.alias('each', 'times');
837 (function(math){
838         var methods = {};
839         math.each(function(name){
840                 if (!Number[name]) methods[name] = function(){
841                         return Math[name].apply(null, [this].concat(Array.from(arguments)));
842                 };
843         });
844         Number.implement(methods);
845 })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
850 name: Function
852 description: Contains Function Prototypes like create, bind, pass, and delay.
854 license: MIT-style license.
856 requires: Type
858 provides: Function
863 Function.extend({
865         attempt: function(){
866                 for (var i = 0, l = arguments.length; i < l; i++){
867                         try {
868                                 return arguments[i]();
869                         } catch (e){}
870                 }
871                 return null;
872         }
876 Function.implement({
878         attempt: function(args, bind){
879                 try {
880                         return this.apply(bind, Array.from(args));
881                 } catch (e){}
883                 return null;
884         },
886         /*<!ES5-bind>*/
887         bind: function(that){
888                 var self = this,
889                         args = arguments.length > 1 ? Array.slice(arguments, 1) : null,
890                         F = function(){};
892                 var bound = function(){
893                         var context = that, length = arguments.length;
894                         if (this instanceof bound){
895                                 F.prototype = self.prototype;
896                                 context = new F;
897                         }
898                         var result = (!args && !length)
899                                 ? self.call(context)
900                                 : self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments);
901                         return context == that ? result : context;
902                 };
903                 return bound;
904         },
905         /*</!ES5-bind>*/
907         pass: function(args, bind){
908                 var self = this;
909                 if (args != null) args = Array.from(args);
910                 return function(){
911                         return self.apply(bind, args || arguments);
912                 };
913         },
915         delay: function(delay, bind, args){
916                 return setTimeout(this.pass((args == null ? [] : args), bind), delay);
917         },
919         periodical: function(periodical, bind, args){
920                 return setInterval(this.pass((args == null ? [] : args), bind), periodical);
921         }
925 //<1.2compat>
927 delete Function.prototype.bind;
929 Function.implement({
931         create: function(options){
932                 var self = this;
933                 options = options || {};
934                 return function(event){
935                         var args = options.arguments;
936                         args = (args != null) ? Array.from(args) : Array.slice(arguments, (options.event) ? 1 : 0);
937                         if (options.event) args = [event || window.event].extend(args);
938                         var returns = function(){
939                                 return self.apply(options.bind || null, args);
940                         };
941                         if (options.delay) return setTimeout(returns, options.delay);
942                         if (options.periodical) return setInterval(returns, options.periodical);
943                         if (options.attempt) return Function.attempt(returns);
944                         return returns();
945                 };
946         },
948         bind: function(bind, args){
949                 var self = this;
950                 if (args != null) args = Array.from(args);
951                 return function(){
952                         return self.apply(bind, args || arguments);
953                 };
954         },
956         bindWithEvent: function(bind, args){
957                 var self = this;
958                 if (args != null) args = Array.from(args);
959                 return function(event){
960                         return self.apply(bind, (args == null) ? arguments : [event].concat(args));
961                 };
962         },
964         run: function(args, bind){
965                 return this.apply(bind, Array.from(args));
966         }
970 if (Object.create == Function.prototype.create) Object.create = null;
972 var $try = Function.attempt;
974 //</1.2compat>
979 name: Object
981 description: Object generic methods
983 license: MIT-style license.
985 requires: Type
987 provides: [Object, Hash]
992 (function(){
994 var hasOwnProperty = Object.prototype.hasOwnProperty;
996 Object.extend({
998         subset: function(object, keys){
999                 var results = {};
1000                 for (var i = 0, l = keys.length; i < l; i++){
1001                         var k = keys[i];
1002                         if (k in object) results[k] = object[k];
1003                 }
1004                 return results;
1005         },
1007         map: function(object, fn, bind){
1008                 var results = {};
1009                 for (var key in object){
1010                         if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
1011                 }
1012                 return results;
1013         },
1015         filter: function(object, fn, bind){
1016                 var results = {};
1017                 for (var key in object){
1018                         var value = object[key];
1019                         if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value;
1020                 }
1021                 return results;
1022         },
1024         every: function(object, fn, bind){
1025                 for (var key in object){
1026                         if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
1027                 }
1028                 return true;
1029         },
1031         some: function(object, fn, bind){
1032                 for (var key in object){
1033                         if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
1034                 }
1035                 return false;
1036         },
1038         keys: function(object){
1039                 var keys = [];
1040                 for (var key in object){
1041                         if (hasOwnProperty.call(object, key)) keys.push(key);
1042                 }
1043                 return keys;
1044         },
1046         values: function(object){
1047                 var values = [];
1048                 for (var key in object){
1049                         if (hasOwnProperty.call(object, key)) values.push(object[key]);
1050                 }
1051                 return values;
1052         },
1054         getLength: function(object){
1055                 return Object.keys(object).length;
1056         },
1058         keyOf: function(object, value){
1059                 for (var key in object){
1060                         if (hasOwnProperty.call(object, key) && object[key] === value) return key;
1061                 }
1062                 return null;
1063         },
1065         contains: function(object, value){
1066                 return Object.keyOf(object, value) != null;
1067         },
1069         toQueryString: function(object, base){
1070                 var queryString = [];
1072                 Object.each(object, function(value, key){
1073                         if (base) key = base + '[' + key + ']';
1074                         var result;
1075                         switch (typeOf(value)){
1076                                 case 'object': result = Object.toQueryString(value, key); break;
1077                                 case 'array':
1078                                         var qs = {};
1079                                         value.each(function(val, i){
1080                                                 qs[i] = val;
1081                                         });
1082                                         result = Object.toQueryString(qs, key);
1083                                 break;
1084                                 default: result = key + '=' + encodeURIComponent(value);
1085                         }
1086                         if (value != null) queryString.push(result);
1087                 });
1089                 return queryString.join('&');
1090         }
1094 })();
1096 //<1.2compat>
1098 Hash.implement({
1100         has: Object.prototype.hasOwnProperty,
1102         keyOf: function(value){
1103                 return Object.keyOf(this, value);
1104         },
1106         hasValue: function(value){
1107                 return Object.contains(this, value);
1108         },
1110         extend: function(properties){
1111                 Hash.each(properties || {}, function(value, key){
1112                         Hash.set(this, key, value);
1113                 }, this);
1114                 return this;
1115         },
1117         combine: function(properties){
1118                 Hash.each(properties || {}, function(value, key){
1119                         Hash.include(this, key, value);
1120                 }, this);
1121                 return this;
1122         },
1124         erase: function(key){
1125                 if (this.hasOwnProperty(key)) delete this[key];
1126                 return this;
1127         },
1129         get: function(key){
1130                 return (this.hasOwnProperty(key)) ? this[key] : null;
1131         },
1133         set: function(key, value){
1134                 if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
1135                 return this;
1136         },
1138         empty: function(){
1139                 Hash.each(this, function(value, key){
1140                         delete this[key];
1141                 }, this);
1142                 return this;
1143         },
1145         include: function(key, value){
1146                 if (this[key] == null) this[key] = value;
1147                 return this;
1148         },
1150         map: function(fn, bind){
1151                 return new Hash(Object.map(this, fn, bind));
1152         },
1154         filter: function(fn, bind){
1155                 return new Hash(Object.filter(this, fn, bind));
1156         },
1158         every: function(fn, bind){
1159                 return Object.every(this, fn, bind);
1160         },
1162         some: function(fn, bind){
1163                 return Object.some(this, fn, bind);
1164         },
1166         getKeys: function(){
1167                 return Object.keys(this);
1168         },
1170         getValues: function(){
1171                 return Object.values(this);
1172         },
1174         toQueryString: function(base){
1175                 return Object.toQueryString(this, base);
1176         }
1180 Hash.extend = Object.append;
1182 Hash.alias({indexOf: 'keyOf', contains: 'hasValue'});
1184 //</1.2compat>
1189 name: Browser
1191 description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.
1193 license: MIT-style license.
1195 requires: [Array, Function, Number, String]
1197 provides: [Browser, Window, Document]
1202 (function(){
1204 var document = this.document;
1205 var window = document.window = this;
1207 var parse = function(ua, platform){
1208         ua = ua.toLowerCase();
1209         platform = (platform ? platform.toLowerCase() : '');
1211         var UA = ua.match(/(opera|ie|firefox|chrome|trident|crios|version)[\s\/:]([\w\d\.]+)?.*?(safari|(?:rv[\s\/:]|version[\s\/:])([\w\d\.]+)|$)/) || [null, 'unknown', 0];
1213         if (UA[1] == 'trident'){
1214                 UA[1] = 'ie';
1215                 if (UA[4]) UA[2] = UA[4];
1216         } else if (UA[1] == 'crios') {
1217                 UA[1] = 'chrome';
1218         }
1220         var platform = ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0];
1221         if (platform == 'win') platform = 'windows';
1223         return {
1224                 extend: Function.prototype.extend,
1225                 name: (UA[1] == 'version') ? UA[3] : UA[1],
1226                 version: parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
1227                 platform: platform
1228         };
1231 var Browser = this.Browser = parse(navigator.userAgent, navigator.platform);
1233 if (Browser.ie){
1234         Browser.version = document.documentMode;
1237 Browser.extend({
1238         Features: {
1239                 xpath: !!(document.evaluate),
1240                 air: !!(window.runtime),
1241                 query: !!(document.querySelector),
1242                 json: !!(window.JSON)
1243         },
1244         parseUA: parse
1247 //<1.4compat>
1248 Browser[Browser.name] = true;
1249 Browser[Browser.name + parseInt(Browser.version, 10)] = true;
1251 if (Browser.name == 'ie' && Browser.version >= '11') {
1252         delete Browser.ie;
1255 var platform = Browser.platform;
1256 if (platform == 'windows'){
1257         platform = 'win';
1259 Browser.Platform = {
1260         name: platform
1262 Browser.Platform[platform] = true;
1263 //</1.4compat>
1265 // Request
1267 Browser.Request = (function(){
1269         var XMLHTTP = function(){
1270                 return new XMLHttpRequest();
1271         };
1273         var MSXML2 = function(){
1274                 return new ActiveXObject('MSXML2.XMLHTTP');
1275         };
1277         var MSXML = function(){
1278                 return new ActiveXObject('Microsoft.XMLHTTP');
1279         };
1281         return Function.attempt(function(){
1282                 XMLHTTP();
1283                 return XMLHTTP;
1284         }, function(){
1285                 MSXML2();
1286                 return MSXML2;
1287         }, function(){
1288                 MSXML();
1289                 return MSXML;
1290         });
1292 })();
1294 Browser.Features.xhr = !!(Browser.Request);
1296 //<1.4compat>
1298 // Flash detection
1300 var version = (Function.attempt(function(){
1301         return navigator.plugins['Shockwave Flash'].description;
1302 }, function(){
1303         return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
1304 }) || '0 r0').match(/\d+/g);
1306 Browser.Plugins = {
1307         Flash: {
1308                 version: Number(version[0] || '0.' + version[1]) || 0,
1309                 build: Number(version[2]) || 0
1310         }
1313 //</1.4compat>
1315 // String scripts
1317 Browser.exec = function(text){
1318         if (!text) return text;
1319         if (window.execScript){
1320                 window.execScript(text);
1321         } else {
1322                 var script = document.createElement('script');
1323                 script.setAttribute('type', 'text/javascript');
1324                 script.text = text;
1325                 document.head.appendChild(script);
1326                 document.head.removeChild(script);
1327         }
1328         return text;
1331 String.implement('stripScripts', function(exec){
1332         var scripts = '';
1333         var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
1334                 scripts += code + '\n';
1335                 return '';
1336         });
1337         if (exec === true) Browser.exec(scripts);
1338         else if (typeOf(exec) == 'function') exec(scripts, text);
1339         return text;
1342 // Window, Document
1344 Browser.extend({
1345         Document: this.Document,
1346         Window: this.Window,
1347         Element: this.Element,
1348         Event: this.Event
1351 this.Window = this.$constructor = new Type('Window', function(){});
1353 this.$family = Function.from('window').hide();
1355 Window.mirror(function(name, method){
1356         window[name] = method;
1359 this.Document = document.$constructor = new Type('Document', function(){});
1361 document.$family = Function.from('document').hide();
1363 Document.mirror(function(name, method){
1364         document[name] = method;
1367 document.html = document.documentElement;
1368 if (!document.head) document.head = document.getElementsByTagName('head')[0];
1370 if (document.execCommand) try {
1371         document.execCommand("BackgroundImageCache", false, true);
1372 } catch (e){}
1374 /*<ltIE9>*/
1375 if (this.attachEvent && !this.addEventListener){
1376         var unloadEvent = function(){
1377                 this.detachEvent('onunload', unloadEvent);
1378                 document.head = document.html = document.window = null;
1379         };
1380         this.attachEvent('onunload', unloadEvent);
1383 // IE fails on collections and <select>.options (refers to <select>)
1384 var arrayFrom = Array.from;
1385 try {
1386         arrayFrom(document.html.childNodes);
1387 } catch(e){
1388         Array.from = function(item){
1389                 if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){
1390                         var i = item.length, array = new Array(i);
1391                         while (i--) array[i] = item[i];
1392                         return array;
1393                 }
1394                 return arrayFrom(item);
1395         };
1397         var prototype = Array.prototype,
1398                 slice = prototype.slice;
1399         ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){
1400                 var method = prototype[name];
1401                 Array[name] = function(item){
1402                         return method.apply(Array.from(item), slice.call(arguments, 1));
1403                 };
1404         });
1406 /*</ltIE9>*/
1408 //<1.2compat>
1410 if (Browser.Platform.ios) Browser.Platform.ipod = true;
1412 Browser.Engine = {};
1414 var setEngine = function(name, version){
1415         Browser.Engine.name = name;
1416         Browser.Engine[name + version] = true;
1417         Browser.Engine.version = version;
1420 if (Browser.ie){
1421         Browser.Engine.trident = true;
1423         switch (Browser.version){
1424                 case 6: setEngine('trident', 4); break;
1425                 case 7: setEngine('trident', 5); break;
1426                 case 8: setEngine('trident', 6);
1427         }
1430 if (Browser.firefox){
1431         Browser.Engine.gecko = true;
1433         if (Browser.version >= 3) setEngine('gecko', 19);
1434         else setEngine('gecko', 18);
1437 if (Browser.safari || Browser.chrome){
1438         Browser.Engine.webkit = true;
1440         switch (Browser.version){
1441                 case 2: setEngine('webkit', 419); break;
1442                 case 3: setEngine('webkit', 420); break;
1443                 case 4: setEngine('webkit', 525);
1444         }
1447 if (Browser.opera){
1448         Browser.Engine.presto = true;
1450         if (Browser.version >= 9.6) setEngine('presto', 960);
1451         else if (Browser.version >= 9.5) setEngine('presto', 950);
1452         else setEngine('presto', 925);
1455 if (Browser.name == 'unknown'){
1456         switch ((navigator.userAgent.toLowerCase().match(/(?:webkit|khtml|gecko)/) || [])[0]){
1457                 case 'webkit':
1458                 case 'khtml':
1459                         Browser.Engine.webkit = true;
1460                 break;
1461                 case 'gecko':
1462                         Browser.Engine.gecko = true;
1463         }
1466 this.$exec = Browser.exec;
1468 //</1.2compat>
1470 })();
1475 name: Event
1477 description: Contains the Event Type, to make the event object cross-browser.
1479 license: MIT-style license.
1481 requires: [Window, Document, Array, Function, String, Object]
1483 provides: Event
1488 (function() {
1490 var _keys = {};
1492 var DOMEvent = this.DOMEvent = new Type('DOMEvent', function(event, win){
1493         if (!win) win = window;
1494         event = event || win.event;
1495         if (event.$extended) return event;
1496         this.event = event;
1497         this.$extended = true;
1498         this.shift = event.shiftKey;
1499         this.control = event.ctrlKey;
1500         this.alt = event.altKey;
1501         this.meta = event.metaKey;
1502         var type = this.type = event.type;
1503         var target = event.target || event.srcElement;
1504         while (target && target.nodeType == 3) target = target.parentNode;
1505         this.target = document.id(target);
1507         if (type.indexOf('key') == 0){
1508                 var code = this.code = (event.which || event.keyCode);
1509                 this.key = _keys[code]/*<1.3compat>*/ || Object.keyOf(Event.Keys, code)/*</1.3compat>*/;
1510                 if (type == 'keydown' || type == 'keyup'){
1511                         if (code > 111 && code < 124) this.key = 'f' + (code - 111);
1512                         else if (code > 95 && code < 106) this.key = code - 96;
1513                 }
1514                 if (this.key == null) this.key = String.fromCharCode(code).toLowerCase();
1515         } else if (type == 'click' || type == 'dblclick' || type == 'contextmenu' || type == 'DOMMouseScroll' || type.indexOf('mouse') == 0){
1516                 var doc = win.document;
1517                 doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
1518                 this.page = {
1519                         x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
1520                         y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
1521                 };
1522                 this.client = {
1523                         x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
1524                         y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
1525                 };
1526                 if (type == 'DOMMouseScroll' || type == 'mousewheel')
1527                         this.wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
1529                 this.rightClick = (event.which == 3 || event.button == 2);
1530                 if (type == 'mouseover' || type == 'mouseout'){
1531                         var related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element'];
1532                         while (related && related.nodeType == 3) related = related.parentNode;
1533                         this.relatedTarget = document.id(related);
1534                 }
1535         } else if (type.indexOf('touch') == 0 || type.indexOf('gesture') == 0){
1536                 this.rotation = event.rotation;
1537                 this.scale = event.scale;
1538                 this.targetTouches = event.targetTouches;
1539                 this.changedTouches = event.changedTouches;
1540                 var touches = this.touches = event.touches;
1541                 if (touches && touches[0]){
1542                         var touch = touches[0];
1543                         this.page = {x: touch.pageX, y: touch.pageY};
1544                         this.client = {x: touch.clientX, y: touch.clientY};
1545                 }
1546         }
1548         if (!this.client) this.client = {};
1549         if (!this.page) this.page = {};
1552 DOMEvent.implement({
1554         stop: function(){
1555                 return this.preventDefault().stopPropagation();
1556         },
1558         stopPropagation: function(){
1559                 if (this.event.stopPropagation) this.event.stopPropagation();
1560                 else this.event.cancelBubble = true;
1561                 return this;
1562         },
1564         preventDefault: function(){
1565                 if (this.event.preventDefault) this.event.preventDefault();
1566                 else this.event.returnValue = false;
1567                 return this;
1568         }
1572 DOMEvent.defineKey = function(code, key){
1573         _keys[code] = key;
1574         return this;
1577 DOMEvent.defineKeys = DOMEvent.defineKey.overloadSetter(true);
1579 DOMEvent.defineKeys({
1580         '38': 'up', '40': 'down', '37': 'left', '39': 'right',
1581         '27': 'esc', '32': 'space', '8': 'backspace', '9': 'tab',
1582         '46': 'delete', '13': 'enter'
1585 })();
1587 /*<1.3compat>*/
1588 var Event = DOMEvent;
1589 Event.Keys = {};
1590 /*</1.3compat>*/
1592 /*<1.2compat>*/
1594 Event.Keys = new Hash(Event.Keys);
1596 /*</1.2compat>*/
1601 name: Class
1603 description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
1605 license: MIT-style license.
1607 requires: [Array, String, Function, Number]
1609 provides: Class
1614 (function(){
1616 var Class = this.Class = new Type('Class', function(params){
1617         if (instanceOf(params, Function)) params = {initialize: params};
1619         var newClass = function(){
1620                 reset(this);
1621                 if (newClass.$prototyping) return this;
1622                 this.$caller = null;
1623                 var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
1624                 this.$caller = this.caller = null;
1625                 return value;
1626         }.extend(this).implement(params);
1628         newClass.$constructor = Class;
1629         newClass.prototype.$constructor = newClass;
1630         newClass.prototype.parent = parent;
1632         return newClass;
1635 var parent = function(){
1636         if (!this.$caller) throw new Error('The method "parent" cannot be called.');
1637         var name = this.$caller.$name,
1638                 parent = this.$caller.$owner.parent,
1639                 previous = (parent) ? parent.prototype[name] : null;
1640         if (!previous) throw new Error('The method "' + name + '" has no parent.');
1641         return previous.apply(this, arguments);
1644 var reset = function(object){
1645         for (var key in object){
1646                 var value = object[key];
1647                 switch (typeOf(value)){
1648                         case 'object':
1649                                 var F = function(){};
1650                                 F.prototype = value;
1651                                 object[key] = reset(new F);
1652                         break;
1653                         case 'array': object[key] = value.clone(); break;
1654                 }
1655         }
1656         return object;
1659 var wrap = function(self, key, method){
1660         if (method.$origin) method = method.$origin;
1661         var wrapper = function(){
1662                 if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
1663                 var caller = this.caller, current = this.$caller;
1664                 this.caller = current; this.$caller = wrapper;
1665                 var result = method.apply(this, arguments);
1666                 this.$caller = current; this.caller = caller;
1667                 return result;
1668         }.extend({$owner: self, $origin: method, $name: key});
1669         return wrapper;
1672 var implement = function(key, value, retain){
1673         if (Class.Mutators.hasOwnProperty(key)){
1674                 value = Class.Mutators[key].call(this, value);
1675                 if (value == null) return this;
1676         }
1678         if (typeOf(value) == 'function'){
1679                 if (value.$hidden) return this;
1680                 this.prototype[key] = (retain) ? value : wrap(this, key, value);
1681         } else {
1682                 Object.merge(this.prototype, key, value);
1683         }
1685         return this;
1688 var getInstance = function(klass){
1689         klass.$prototyping = true;
1690         var proto = new klass;
1691         delete klass.$prototyping;
1692         return proto;
1695 Class.implement('implement', implement.overloadSetter());
1697 Class.Mutators = {
1699         Extends: function(parent){
1700                 this.parent = parent;
1701                 this.prototype = getInstance(parent);
1702         },
1704         Implements: function(items){
1705                 Array.from(items).each(function(item){
1706                         var instance = new item;
1707                         for (var key in instance) implement.call(this, key, instance[key], true);
1708                 }, this);
1709         }
1712 })();
1717 name: Class.Extras
1719 description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
1721 license: MIT-style license.
1723 requires: Class
1725 provides: [Class.Extras, Chain, Events, Options]
1730 (function(){
1732 this.Chain = new Class({
1734         $chain: [],
1736         chain: function(){
1737                 this.$chain.append(Array.flatten(arguments));
1738                 return this;
1739         },
1741         callChain: function(){
1742                 return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
1743         },
1745         clearChain: function(){
1746                 this.$chain.empty();
1747                 return this;
1748         }
1752 var removeOn = function(string){
1753         return string.replace(/^on([A-Z])/, function(full, first){
1754                 return first.toLowerCase();
1755         });
1758 this.Events = new Class({
1760         $events: {},
1762         addEvent: function(type, fn, internal){
1763                 type = removeOn(type);
1765                 /*<1.2compat>*/
1766                 if (fn == $empty) return this;
1767                 /*</1.2compat>*/
1769                 this.$events[type] = (this.$events[type] || []).include(fn);
1770                 if (internal) fn.internal = true;
1771                 return this;
1772         },
1774         addEvents: function(events){
1775                 for (var type in events) this.addEvent(type, events[type]);
1776                 return this;
1777         },
1779         fireEvent: function(type, args, delay){
1780                 type = removeOn(type);
1781                 var events = this.$events[type];
1782                 if (!events) return this;
1783                 args = Array.from(args);
1784                 events.each(function(fn){
1785                         if (delay) fn.delay(delay, this, args);
1786                         else fn.apply(this, args);
1787                 }, this);
1788                 return this;
1789         },
1791         removeEvent: function(type, fn){
1792                 type = removeOn(type);
1793                 var events = this.$events[type];
1794                 if (events && !fn.internal){
1795                         var index =  events.indexOf(fn);
1796                         if (index != -1) delete events[index];
1797                 }
1798                 return this;
1799         },
1801         removeEvents: function(events){
1802                 var type;
1803                 if (typeOf(events) == 'object'){
1804                         for (type in events) this.removeEvent(type, events[type]);
1805                         return this;
1806                 }
1807                 if (events) events = removeOn(events);
1808                 for (type in this.$events){
1809                         if (events && events != type) continue;
1810                         var fns = this.$events[type];
1811                         for (var i = fns.length; i--;) if (i in fns){
1812                                 this.removeEvent(type, fns[i]);
1813                         }
1814                 }
1815                 return this;
1816         }
1820 this.Options = new Class({
1822         setOptions: function(){
1823                 var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));
1824                 if (this.addEvent) for (var option in options){
1825                         if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
1826                         this.addEvent(option, options[option]);
1827                         delete options[option];
1828                 }
1829                 return this;
1830         }
1834 })();
1838 name: Slick.Parser
1839 description: Standalone CSS3 Selector parser
1840 provides: Slick.Parser
1844 ;(function(){
1846 var parsed,
1847         separatorIndex,
1848         combinatorIndex,
1849         reversed,
1850         cache = {},
1851         reverseCache = {},
1852         reUnescape = /\\/g;
1854 var parse = function(expression, isReversed){
1855         if (expression == null) return null;
1856         if (expression.Slick === true) return expression;
1857         expression = ('' + expression).replace(/^\s+|\s+$/g, '');
1858         reversed = !!isReversed;
1859         var currentCache = (reversed) ? reverseCache : cache;
1860         if (currentCache[expression]) return currentCache[expression];
1861         parsed = {
1862                 Slick: true,
1863                 expressions: [],
1864                 raw: expression,
1865                 reverse: function(){
1866                         return parse(this.raw, true);
1867                 }
1868         };
1869         separatorIndex = -1;
1870         while (expression != (expression = expression.replace(regexp, parser)));
1871         parsed.length = parsed.expressions.length;
1872         return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
1875 var reverseCombinator = function(combinator){
1876         if (combinator === '!') return ' ';
1877         else if (combinator === ' ') return '!';
1878         else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
1879         else return '!' + combinator;
1882 var reverse = function(expression){
1883         var expressions = expression.expressions;
1884         for (var i = 0; i < expressions.length; i++){
1885                 var exp = expressions[i];
1886                 var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
1888                 for (var j = 0; j < exp.length; j++){
1889                         var cexp = exp[j];
1890                         if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
1891                         cexp.combinator = cexp.reverseCombinator;
1892                         delete cexp.reverseCombinator;
1893                 }
1895                 exp.reverse().push(last);
1896         }
1897         return expression;
1900 var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
1901         return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){
1902                 return '\\' + match;
1903         });
1906 var regexp = new RegExp(
1908 #!/usr/bin/env ruby
1909 puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
1910 __END__
1911         "(?x)^(?:\
1912           \\s* ( , ) \\s*               # Separator          \n\
1913         | \\s* ( <combinator>+ ) \\s*   # Combinator         \n\
1914         |      ( \\s+ )                 # CombinatorChildren \n\
1915         |      ( <unicode>+ | \\* )     # Tag                \n\
1916         | \\#  ( <unicode>+       )     # ID                 \n\
1917         | \\.  ( <unicode>+       )     # ClassName          \n\
1918         |                               # Attribute          \n\
1919         \\[  \
1920                 \\s* (<unicode1>+)  (?:  \
1921                         \\s* ([*^$!~|]?=)  (?:  \
1922                                 \\s* (?:\
1923                                         ([\"']?)(.*?)\\9 \
1924                                 )\
1925                         )  \
1926                 )?  \\s*  \
1927         \\](?!\\]) \n\
1928         |   :+ ( <unicode>+ )(?:\
1929         \\( (?:\
1930                 (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
1931         ) \\)\
1932         )?\
1933         )"
1935         "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
1936         .replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
1937         .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
1938         .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
1941 function parser(
1942         rawMatch,
1944         separator,
1945         combinator,
1946         combinatorChildren,
1948         tagName,
1949         id,
1950         className,
1952         attributeKey,
1953         attributeOperator,
1954         attributeQuote,
1955         attributeValue,
1957         pseudoMarker,
1958         pseudoClass,
1959         pseudoQuote,
1960         pseudoClassQuotedValue,
1961         pseudoClassValue
1963         if (separator || separatorIndex === -1){
1964                 parsed.expressions[++separatorIndex] = [];
1965                 combinatorIndex = -1;
1966                 if (separator) return '';
1967         }
1969         if (combinator || combinatorChildren || combinatorIndex === -1){
1970                 combinator = combinator || ' ';
1971                 var currentSeparator = parsed.expressions[separatorIndex];
1972                 if (reversed && currentSeparator[combinatorIndex])
1973                         currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
1974                 currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
1975         }
1977         var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
1979         if (tagName){
1980                 currentParsed.tag = tagName.replace(reUnescape, '');
1982         } else if (id){
1983                 currentParsed.id = id.replace(reUnescape, '');
1985         } else if (className){
1986                 className = className.replace(reUnescape, '');
1988                 if (!currentParsed.classList) currentParsed.classList = [];
1989                 if (!currentParsed.classes) currentParsed.classes = [];
1990                 currentParsed.classList.push(className);
1991                 currentParsed.classes.push({
1992                         value: className,
1993                         regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
1994                 });
1996         } else if (pseudoClass){
1997                 pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
1998                 pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
2000                 if (!currentParsed.pseudos) currentParsed.pseudos = [];
2001                 currentParsed.pseudos.push({
2002                         key: pseudoClass.replace(reUnescape, ''),
2003                         value: pseudoClassValue,
2004                         type: pseudoMarker.length == 1 ? 'class' : 'element'
2005                 });
2007         } else if (attributeKey){
2008                 attributeKey = attributeKey.replace(reUnescape, '');
2009                 attributeValue = (attributeValue || '').replace(reUnescape, '');
2011                 var test, regexp;
2013                 switch (attributeOperator){
2014                         case '^=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue)            ); break;
2015                         case '$=' : regexp = new RegExp(            escapeRegExp(attributeValue) +'$'       ); break;
2016                         case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
2017                         case '|=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue) +'(-|$)'   ); break;
2018                         case  '=' : test = function(value){
2019                                 return attributeValue == value;
2020                         }; break;
2021                         case '*=' : test = function(value){
2022                                 return value && value.indexOf(attributeValue) > -1;
2023                         }; break;
2024                         case '!=' : test = function(value){
2025                                 return attributeValue != value;
2026                         }; break;
2027                         default   : test = function(value){
2028                                 return !!value;
2029                         };
2030                 }
2032                 if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
2033                         return false;
2034                 };
2036                 if (!test) test = function(value){
2037                         return value && regexp.test(value);
2038                 };
2040                 if (!currentParsed.attributes) currentParsed.attributes = [];
2041                 currentParsed.attributes.push({
2042                         key: attributeKey,
2043                         operator: attributeOperator,
2044                         value: attributeValue,
2045                         test: test
2046                 });
2048         }
2050         return '';
2053 // Slick NS
2055 var Slick = (this.Slick || {});
2057 Slick.parse = function(expression){
2058         return parse(expression);
2061 Slick.escapeRegExp = escapeRegExp;
2063 if (!this.Slick) this.Slick = Slick;
2065 }).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
2069 name: Slick.Finder
2070 description: The new, superfast css selector engine.
2071 provides: Slick.Finder
2072 requires: Slick.Parser
2076 ;(function(){
2078 var local = {},
2079         featuresCache = {},
2080         toString = Object.prototype.toString;
2082 // Feature / Bug detection
2084 local.isNativeCode = function(fn){
2085         return (/\{\s*\[native code\]\s*\}/).test('' + fn);
2088 local.isXML = function(document){
2089         return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') ||
2090         (document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
2093 local.setDocument = function(document){
2095         // convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
2096         var nodeType = document.nodeType;
2097         if (nodeType == 9); // document
2098         else if (nodeType) document = document.ownerDocument; // node
2099         else if (document.navigator) document = document.document; // window
2100         else return;
2102         // check if it's the old document
2104         if (this.document === document) return;
2105         this.document = document;
2107         // check if we have done feature detection on this document before
2109         var root = document.documentElement,
2110                 rootUid = this.getUIDXML(root),
2111                 features = featuresCache[rootUid],
2112                 feature;
2114         if (features){
2115                 for (feature in features){
2116                         this[feature] = features[feature];
2117                 }
2118                 return;
2119         }
2121         features = featuresCache[rootUid] = {};
2123         features.root = root;
2124         features.isXMLDocument = this.isXML(document);
2126         features.brokenStarGEBTN
2127         = features.starSelectsClosedQSA
2128         = features.idGetsName
2129         = features.brokenMixedCaseQSA
2130         = features.brokenGEBCN
2131         = features.brokenCheckedQSA
2132         = features.brokenEmptyAttributeQSA
2133         = features.isHTMLDocument
2134         = features.nativeMatchesSelector
2135         = false;
2137         var starSelectsClosed, starSelectsComments,
2138                 brokenSecondClassNameGEBCN, cachedGetElementsByClassName,
2139                 brokenFormAttributeGetter;
2141         var selected, id = 'slick_uniqueid';
2142         var testNode = document.createElement('div');
2144         var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
2145         testRoot.appendChild(testNode);
2147         // on non-HTML documents innerHTML and getElementsById doesnt work properly
2148         try {
2149                 testNode.innerHTML = '<a id="'+id+'"></a>';
2150                 features.isHTMLDocument = !!document.getElementById(id);
2151         } catch(e){};
2153         if (features.isHTMLDocument){
2155                 testNode.style.display = 'none';
2157                 // IE returns comment nodes for getElementsByTagName('*') for some documents
2158                 testNode.appendChild(document.createComment(''));
2159                 starSelectsComments = (testNode.getElementsByTagName('*').length > 1);
2161                 // IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
2162                 try {
2163                         testNode.innerHTML = 'foo</foo>';
2164                         selected = testNode.getElementsByTagName('*');
2165                         starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
2166                 } catch(e){};
2168                 features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
2170                 // IE returns elements with the name instead of just id for getElementsById for some documents
2171                 try {
2172                         testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
2173                         features.idGetsName = document.getElementById(id) === testNode.firstChild;
2174                 } catch(e){};
2176                 if (testNode.getElementsByClassName){
2178                         // Safari 3.2 getElementsByClassName caches results
2179                         try {
2180                                 testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
2181                                 testNode.getElementsByClassName('b').length;
2182                                 testNode.firstChild.className = 'b';
2183                                 cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
2184                         } catch(e){};
2186                         // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
2187                         try {
2188                                 testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
2189                                 brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
2190                         } catch(e){};
2192                         features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
2193                 }
2195                 if (testNode.querySelectorAll){
2196                         // IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
2197                         try {
2198                                 testNode.innerHTML = 'foo</foo>';
2199                                 selected = testNode.querySelectorAll('*');
2200                                 features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
2201                         } catch(e){};
2203                         // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
2204                         try {
2205                                 testNode.innerHTML = '<a class="MiX"></a>';
2206                                 features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
2207                         } catch(e){};
2209                         // Webkit and Opera dont return selected options on querySelectorAll
2210                         try {
2211                                 testNode.innerHTML = '<select><option selected="selected">a</option></select>';
2212                                 features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
2213                         } catch(e){};
2215                         // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
2216                         try {
2217                                 testNode.innerHTML = '<a class=""></a>';
2218                                 features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
2219                         } catch(e){};
2221                 }
2223                 // IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
2224                 try {
2225                         testNode.innerHTML = '<form action="s"><input id="action"/></form>';
2226                         brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
2227                 } catch(e){};
2229                 // native matchesSelector function
2231                 features.nativeMatchesSelector = root.matches || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
2232                 if (features.nativeMatchesSelector) try {
2233                         // if matchesSelector trows errors on incorrect sintaxes we can use it
2234                         features.nativeMatchesSelector.call(root, ':slick');
2235                         features.nativeMatchesSelector = null;
2236                 } catch(e){};
2238         }
2240         try {
2241                 root.slick_expando = 1;
2242                 delete root.slick_expando;
2243                 features.getUID = this.getUIDHTML;
2244         } catch(e) {
2245                 features.getUID = this.getUIDXML;
2246         }
2248         testRoot.removeChild(testNode);
2249         testNode = selected = testRoot = null;
2251         // getAttribute
2253         features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){
2254                 var method = this.attributeGetters[name];
2255                 if (method) return method.call(node);
2256                 var attributeNode = node.getAttributeNode(name);
2257                 return (attributeNode) ? attributeNode.nodeValue : null;
2258         } : function(node, name){
2259                 var method = this.attributeGetters[name];
2260                 return (method) ? method.call(node) : node.getAttribute(name);
2261         };
2263         // hasAttribute
2265         features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute) {
2266                 return node.hasAttribute(attribute);
2267         } : function(node, attribute) {
2268                 node = node.getAttributeNode(attribute);
2269                 return !!(node && (node.specified || node.nodeValue));
2270         };
2272         // contains
2273         // FIXME: Add specs: local.contains should be different for xml and html documents?
2274         var nativeRootContains = root && this.isNativeCode(root.contains),
2275                 nativeDocumentContains = document && this.isNativeCode(document.contains);
2277         features.contains = (nativeRootContains && nativeDocumentContains) ? function(context, node){
2278                 return context.contains(node);
2279         } : (nativeRootContains && !nativeDocumentContains) ? function(context, node){
2280                 // IE8 does not have .contains on document.
2281                 return context === node || ((context === document) ? document.documentElement : context).contains(node);
2282         } : (root && root.compareDocumentPosition) ? function(context, node){
2283                 return context === node || !!(context.compareDocumentPosition(node) & 16);
2284         } : function(context, node){
2285                 if (node) do {
2286                         if (node === context) return true;
2287                 } while ((node = node.parentNode));
2288                 return false;
2289         };
2291         // document order sorting
2292         // credits to Sizzle (http://sizzlejs.com/)
2294         features.documentSorter = (root.compareDocumentPosition) ? function(a, b){
2295                 if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
2296                 return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
2297         } : ('sourceIndex' in root) ? function(a, b){
2298                 if (!a.sourceIndex || !b.sourceIndex) return 0;
2299                 return a.sourceIndex - b.sourceIndex;
2300         } : (document.createRange) ? function(a, b){
2301                 if (!a.ownerDocument || !b.ownerDocument) return 0;
2302                 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
2303                 aRange.setStart(a, 0);
2304                 aRange.setEnd(a, 0);
2305                 bRange.setStart(b, 0);
2306                 bRange.setEnd(b, 0);
2307                 return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
2308         } : null ;
2310         root = null;
2312         for (feature in features){
2313                 this[feature] = features[feature];
2314         }
2317 // Main Method
2319 var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
2320         reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
2321         qsaFailExpCache = {};
2323 local.search = function(context, expression, append, first){
2325         var found = this.found = (first) ? null : (append || []);
2327         if (!context) return found;
2328         else if (context.navigator) context = context.document; // Convert the node from a window to a document
2329         else if (!context.nodeType) return found;
2331         // setup
2333         var parsed, i,
2334                 uniques = this.uniques = {},
2335                 hasOthers = !!(append && append.length),
2336                 contextIsDocument = (context.nodeType == 9);
2338         if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);
2340         // avoid duplicating items already in the append array
2341         if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;
2343         // expression checks
2345         if (typeof expression == 'string'){ // expression is a string
2347                 /*<simple-selectors-override>*/
2348                 var simpleSelector = expression.match(reSimpleSelector);
2349                 simpleSelectors: if (simpleSelector) {
2351                         var symbol = simpleSelector[1],
2352                                 name = simpleSelector[2],
2353                                 node, nodes;
2355                         if (!symbol){
2357                                 if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
2358                                 nodes = context.getElementsByTagName(name);
2359                                 if (first) return nodes[0] || null;
2360                                 for (i = 0; node = nodes[i++];){
2361                                         if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
2362                                 }
2364                         } else if (symbol == '#'){
2366                                 if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
2367                                 node = context.getElementById(name);
2368                                 if (!node) return found;
2369                                 if (this.idGetsName && node.getAttributeNode('id').nodeValue != name) break simpleSelectors;
2370                                 if (first) return node || null;
2371                                 if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
2373                         } else if (symbol == '.'){
2375                                 if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
2376                                 if (context.getElementsByClassName && !this.brokenGEBCN){
2377                                         nodes = context.getElementsByClassName(name);
2378                                         if (first) return nodes[0] || null;
2379                                         for (i = 0; node = nodes[i++];){
2380                                                 if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
2381                                         }
2382                                 } else {
2383                                         var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)');
2384                                         nodes = context.getElementsByTagName('*');
2385                                         for (i = 0; node = nodes[i++];){
2386                                                 className = node.className;
2387                                                 if (!(className && matchClass.test(className))) continue;
2388                                                 if (first) return node;
2389                                                 if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
2390                                         }
2391                                 }
2393                         }
2395                         if (hasOthers) this.sort(found);
2396                         return (first) ? null : found;
2398                 }
2399                 /*</simple-selectors-override>*/
2401                 /*<query-selector-override>*/
2402                 querySelector: if (context.querySelectorAll) {
2404                         if (!this.isHTMLDocument
2405                                 || qsaFailExpCache[expression]
2406                                 //TODO: only skip when expression is actually mixed case
2407                                 || this.brokenMixedCaseQSA
2408                                 || (this.brokenCheckedQSA && expression.indexOf(':checked') > -1)
2409                                 || (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression))
2410                                 || (!contextIsDocument //Abort when !contextIsDocument and...
2411                                         //  there are multiple expressions in the selector
2412                                         //  since we currently only fix non-document rooted QSA for single expression selectors
2413                                         && expression.indexOf(',') > -1
2414                                 )
2415                                 || Slick.disableQSA
2416                         ) break querySelector;
2418                         var _expression = expression, _context = context;
2419                         if (!contextIsDocument){
2420                                 // non-document rooted QSA
2421                                 // credits to Andrew Dupont
2422                                 var currentId = _context.getAttribute('id'), slickid = 'slickid__';
2423                                 _context.setAttribute('id', slickid);
2424                                 _expression = '#' + slickid + ' ' + _expression;
2425                                 context = _context.parentNode;
2426                         }
2428                         try {
2429                                 if (first) return context.querySelector(_expression) || null;
2430                                 else nodes = context.querySelectorAll(_expression);
2431                         } catch(e) {
2432                                 qsaFailExpCache[expression] = 1;
2433                                 break querySelector;
2434                         } finally {
2435                                 if (!contextIsDocument){
2436                                         if (currentId) _context.setAttribute('id', currentId);
2437                                         else _context.removeAttribute('id');
2438                                         context = _context;
2439                                 }
2440                         }
2442                         if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
2443                                 if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
2444                         } else for (i = 0; node = nodes[i++];){
2445                                 if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
2446                         }
2448                         if (hasOthers) this.sort(found);
2449                         return found;
2451                 }
2452                 /*</query-selector-override>*/
2454                 parsed = this.Slick.parse(expression);
2455                 if (!parsed.length) return found;
2456         } else if (expression == null){ // there is no expression
2457                 return found;
2458         } else if (expression.Slick){ // expression is a parsed Slick object
2459                 parsed = expression;
2460         } else if (this.contains(context.documentElement || context, expression)){ // expression is a node
2461                 (found) ? found.push(expression) : found = expression;
2462                 return found;
2463         } else { // other junk
2464                 return found;
2465         }
2467         /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
2469         // cache elements for the nth selectors
2471         this.posNTH = {};
2472         this.posNTHLast = {};
2473         this.posNTHType = {};
2474         this.posNTHTypeLast = {};
2476         /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
2478         // if append is null and there is only a single selector with one expression use pushArray, else use pushUID
2479         this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
2481         if (found == null) found = [];
2483         // default engine
2485         var j, m, n;
2486         var combinator, tag, id, classList, classes, attributes, pseudos;
2487         var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
2489         search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
2491                 combinator = 'combinator:' + currentBit.combinator;
2492                 if (!this[combinator]) continue search;
2494                 tag        = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
2495                 id         = currentBit.id;
2496                 classList  = currentBit.classList;
2497                 classes    = currentBit.classes;
2498                 attributes = currentBit.attributes;
2499                 pseudos    = currentBit.pseudos;
2500                 lastBit    = (j === (currentExpression.length - 1));
2502                 this.bitUniques = {};
2504                 if (lastBit){
2505                         this.uniques = uniques;
2506                         this.found = found;
2507                 } else {
2508                         this.uniques = {};
2509                         this.found = [];
2510                 }
2512                 if (j === 0){
2513                         this[combinator](context, tag, id, classes, attributes, pseudos, classList);
2514                         if (first && lastBit && found.length) break search;
2515                 } else {
2516                         if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
2517                                 this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
2518                                 if (found.length) break search;
2519                         } else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
2520                 }
2522                 currentItems = this.found;
2523         }
2525         // should sort if there are nodes in append and if you pass multiple expressions.
2526         if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);
2528         return (first) ? (found[0] || null) : found;
2531 // Utils
2533 local.uidx = 1;
2534 local.uidk = 'slick-uniqueid';
2536 local.getUIDXML = function(node){
2537         var uid = node.getAttribute(this.uidk);
2538         if (!uid){
2539                 uid = this.uidx++;
2540                 node.setAttribute(this.uidk, uid);
2541         }
2542         return uid;
2545 local.getUIDHTML = function(node){
2546         return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
2549 // sort based on the setDocument documentSorter method.
2551 local.sort = function(results){
2552         if (!this.documentSorter) return results;
2553         results.sort(this.documentSorter);
2554         return results;
2557 /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
2559 local.cacheNTH = {};
2561 local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
2563 local.parseNTHArgument = function(argument){
2564         var parsed = argument.match(this.matchNTH);
2565         if (!parsed) return false;
2566         var special = parsed[2] || false;
2567         var a = parsed[1] || 1;
2568         if (a == '-') a = -1;
2569         var b = +parsed[3] || 0;
2570         parsed =
2571                 (special == 'n')        ? {a: a, b: b} :
2572                 (special == 'odd')      ? {a: 2, b: 1} :
2573                 (special == 'even')     ? {a: 2, b: 0} : {a: 0, b: a};
2575         return (this.cacheNTH[argument] = parsed);
2578 local.createNTHPseudo = function(child, sibling, positions, ofType){
2579         return function(node, argument){
2580                 var uid = this.getUID(node);
2581                 if (!this[positions][uid]){
2582                         var parent = node.parentNode;
2583                         if (!parent) return false;
2584                         var el = parent[child], count = 1;
2585                         if (ofType){
2586                                 var nodeName = node.nodeName;
2587                                 do {
2588                                         if (el.nodeName != nodeName) continue;
2589                                         this[positions][this.getUID(el)] = count++;
2590                                 } while ((el = el[sibling]));
2591                         } else {
2592                                 do {
2593                                         if (el.nodeType != 1) continue;
2594                                         this[positions][this.getUID(el)] = count++;
2595                                 } while ((el = el[sibling]));
2596                         }
2597                 }
2598                 argument = argument || 'n';
2599                 var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
2600                 if (!parsed) return false;
2601                 var a = parsed.a, b = parsed.b, pos = this[positions][uid];
2602                 if (a == 0) return b == pos;
2603                 if (a > 0){
2604                         if (pos < b) return false;
2605                 } else {
2606                         if (b < pos) return false;
2607                 }
2608                 return ((pos - b) % a) == 0;
2609         };
2612 /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
2614 local.pushArray = function(node, tag, id, classes, attributes, pseudos){
2615         if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
2618 local.pushUID = function(node, tag, id, classes, attributes, pseudos){
2619         var uid = this.getUID(node);
2620         if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
2621                 this.uniques[uid] = true;
2622                 this.found.push(node);
2623         }
2626 local.matchNode = function(node, selector){
2627         if (this.isHTMLDocument && this.nativeMatchesSelector){
2628                 try {
2629                         return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
2630                 } catch(matchError) {}
2631         }
2633         var parsed = this.Slick.parse(selector);
2634         if (!parsed) return true;
2636         // simple (single) selectors
2637         var expressions = parsed.expressions, simpleExpCounter = 0, i;
2638         for (i = 0; (currentExpression = expressions[i]); i++){
2639                 if (currentExpression.length == 1){
2640                         var exp = currentExpression[0];
2641                         if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true;
2642                         simpleExpCounter++;
2643                 }
2644         }
2646         if (simpleExpCounter == parsed.length) return false;
2648         var nodes = this.search(this.document, parsed), item;
2649         for (i = 0; item = nodes[i++];){
2650                 if (item === node) return true;
2651         }
2652         return false;
2655 local.matchPseudo = function(node, name, argument){
2656         var pseudoName = 'pseudo:' + name;
2657         if (this[pseudoName]) return this[pseudoName](node, argument);
2658         var attribute = this.getAttribute(node, name);
2659         return (argument) ? argument == attribute : !!attribute;
2662 local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
2663         if (tag){
2664                 var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
2665                 if (tag == '*'){
2666                         if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
2667                 } else {
2668                         if (nodeName != tag) return false;
2669                 }
2670         }
2672         if (id && node.getAttribute('id') != id) return false;
2674         var i, part, cls;
2675         if (classes) for (i = classes.length; i--;){
2676                 cls = this.getAttribute(node, 'class');
2677                 if (!(cls && classes[i].regexp.test(cls))) return false;
2678         }
2679         if (attributes) for (i = attributes.length; i--;){
2680                 part = attributes[i];
2681                 if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
2682         }
2683         if (pseudos) for (i = pseudos.length; i--;){
2684                 part = pseudos[i];
2685                 if (!this.matchPseudo(node, part.key, part.value)) return false;
2686         }
2687         return true;
2690 var combinators = {
2692         ' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
2694                 var i, item, children;
2696                 if (this.isHTMLDocument){
2697                         getById: if (id){
2698                                 item = this.document.getElementById(id);
2699                                 if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){
2700                                         // all[id] returns all the elements with that name or id inside node
2701                                         // if theres just one it will return the element, else it will be a collection
2702                                         children = node.all[id];
2703                                         if (!children) return;
2704                                         if (!children[0]) children = [children];
2705                                         for (i = 0; item = children[i++];){
2706                                                 var idNode = item.getAttributeNode('id');
2707                                                 if (idNode && idNode.nodeValue == id){
2708                                                         this.push(item, tag, null, classes, attributes, pseudos);
2709                                                         break;
2710                                                 }
2711                                         }
2712                                         return;
2713                                 }
2714                                 if (!item){
2715                                         // if the context is in the dom we return, else we will try GEBTN, breaking the getById label
2716                                         if (this.contains(this.root, node)) return;
2717                                         else break getById;
2718                                 } else if (this.document !== node && !this.contains(node, item)) return;
2719                                 this.push(item, tag, null, classes, attributes, pseudos);
2720                                 return;
2721                         }
2722                         getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
2723                                 children = node.getElementsByClassName(classList.join(' '));
2724                                 if (!(children && children.length)) break getByClass;
2725                                 for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
2726                                 return;
2727                         }
2728                 }
2729                 getByTag: {
2730                         children = node.getElementsByTagName(tag);
2731                         if (!(children && children.length)) break getByTag;
2732                         if (!this.brokenStarGEBTN) tag = null;
2733                         for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
2734                 }
2735         },
2737         '>': function(node, tag, id, classes, attributes, pseudos){ // direct children
2738                 if ((node = node.firstChild)) do {
2739                         if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
2740                 } while ((node = node.nextSibling));
2741         },
2743         '+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
2744                 while ((node = node.nextSibling)) if (node.nodeType == 1){
2745                         this.push(node, tag, id, classes, attributes, pseudos);
2746                         break;
2747                 }
2748         },
2750         '^': function(node, tag, id, classes, attributes, pseudos){ // first child
2751                 node = node.firstChild;
2752                 if (node){
2753                         if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
2754                         else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
2755                 }
2756         },
2758         '~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
2759                 while ((node = node.nextSibling)){
2760                         if (node.nodeType != 1) continue;
2761                         var uid = this.getUID(node);
2762                         if (this.bitUniques[uid]) break;
2763                         this.bitUniques[uid] = true;
2764                         this.push(node, tag, id, classes, attributes, pseudos);
2765                 }
2766         },
2768         '++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
2769                 this['combinator:+'](node, tag, id, classes, attributes, pseudos);
2770                 this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
2771         },
2773         '~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
2774                 this['combinator:~'](node, tag, id, classes, attributes, pseudos);
2775                 this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
2776         },
2778         '!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document
2779                 while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
2780         },
2782         '!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
2783                 node = node.parentNode;
2784                 if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
2785         },
2787         '!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
2788                 while ((node = node.previousSibling)) if (node.nodeType == 1){
2789                         this.push(node, tag, id, classes, attributes, pseudos);
2790                         break;
2791                 }
2792         },
2794         '!^': function(node, tag, id, classes, attributes, pseudos){ // last child
2795                 node = node.lastChild;
2796                 if (node){
2797                         if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
2798                         else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
2799                 }
2800         },
2802         '!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
2803                 while ((node = node.previousSibling)){
2804                         if (node.nodeType != 1) continue;
2805                         var uid = this.getUID(node);
2806                         if (this.bitUniques[uid]) break;
2807                         this.bitUniques[uid] = true;
2808                         this.push(node, tag, id, classes, attributes, pseudos);
2809                 }
2810         }
2814 for (var c in combinators) local['combinator:' + c] = combinators[c];
2816 var pseudos = {
2818         /*<pseudo-selectors>*/
2820         'empty': function(node){
2821                 var child = node.firstChild;
2822                 return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
2823         },
2825         'not': function(node, expression){
2826                 return !this.matchNode(node, expression);
2827         },
2829         'contains': function(node, text){
2830                 return (node.innerText || node.textContent || '').indexOf(text) > -1;
2831         },
2833         'first-child': function(node){
2834                 while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
2835                 return true;
2836         },
2838         'last-child': function(node){
2839                 while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
2840                 return true;
2841         },
2843         'only-child': function(node){
2844                 var prev = node;
2845                 while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
2846                 var next = node;
2847                 while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
2848                 return true;
2849         },
2851         /*<nth-pseudo-selectors>*/
2853         'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
2855         'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
2857         'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
2859         'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
2861         'index': function(node, index){
2862                 return this['pseudo:nth-child'](node, '' + (index + 1));
2863         },
2865         'even': function(node){
2866                 return this['pseudo:nth-child'](node, '2n');
2867         },
2869         'odd': function(node){
2870                 return this['pseudo:nth-child'](node, '2n+1');
2871         },
2873         /*</nth-pseudo-selectors>*/
2875         /*<of-type-pseudo-selectors>*/
2877         'first-of-type': function(node){
2878                 var nodeName = node.nodeName;
2879                 while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
2880                 return true;
2881         },
2883         'last-of-type': function(node){
2884                 var nodeName = node.nodeName;
2885                 while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
2886                 return true;
2887         },
2889         'only-of-type': function(node){
2890                 var prev = node, nodeName = node.nodeName;
2891                 while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
2892                 var next = node;
2893                 while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
2894                 return true;
2895         },
2897         /*</of-type-pseudo-selectors>*/
2899         // custom pseudos
2901         'enabled': function(node){
2902                 return !node.disabled;
2903         },
2905         'disabled': function(node){
2906                 return node.disabled;
2907         },
2909         'checked': function(node){
2910                 return node.checked || node.selected;
2911         },
2913         'focus': function(node){
2914                 return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
2915         },
2917         'root': function(node){
2918                 return (node === this.root);
2919         },
2921         'selected': function(node){
2922                 return node.selected;
2923         }
2925         /*</pseudo-selectors>*/
2928 for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
2930 // attributes methods
2932 var attributeGetters = local.attributeGetters = {
2934         'for': function(){
2935                 return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
2936         },
2938         'href': function(){
2939                 return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
2940         },
2942         'style': function(){
2943                 return (this.style) ? this.style.cssText : this.getAttribute('style');
2944         },
2946         'tabindex': function(){
2947                 var attributeNode = this.getAttributeNode('tabindex');
2948                 return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
2949         },
2951         'type': function(){
2952                 return this.getAttribute('type');
2953         },
2955         'maxlength': function(){
2956                 var attributeNode = this.getAttributeNode('maxLength');
2957                 return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
2958         }
2962 attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength;
2964 // Slick
2966 var Slick = local.Slick = (this.Slick || {});
2968 Slick.version = '1.1.7';
2970 // Slick finder
2972 Slick.search = function(context, expression, append){
2973         return local.search(context, expression, append);
2976 Slick.find = function(context, expression){
2977         return local.search(context, expression, null, true);
2980 // Slick containment checker
2982 Slick.contains = function(container, node){
2983         local.setDocument(container);
2984         return local.contains(container, node);
2987 // Slick attribute getter
2989 Slick.getAttribute = function(node, name){
2990         local.setDocument(node);
2991         return local.getAttribute(node, name);
2994 Slick.hasAttribute = function(node, name){
2995         local.setDocument(node);
2996         return local.hasAttribute(node, name);
2999 // Slick matcher
3001 Slick.match = function(node, selector){
3002         if (!(node && selector)) return false;
3003         if (!selector || selector === node) return true;
3004         local.setDocument(node);
3005         return local.matchNode(node, selector);
3008 // Slick attribute accessor
3010 Slick.defineAttributeGetter = function(name, fn){
3011         local.attributeGetters[name] = fn;
3012         return this;
3015 Slick.lookupAttributeGetter = function(name){
3016         return local.attributeGetters[name];
3019 // Slick pseudo accessor
3021 Slick.definePseudo = function(name, fn){
3022         local['pseudo:' + name] = function(node, argument){
3023                 return fn.call(node, argument);
3024         };
3025         return this;
3028 Slick.lookupPseudo = function(name){
3029         var pseudo = local['pseudo:' + name];
3030         if (pseudo) return function(argument){
3031                 return pseudo.call(this, argument);
3032         };
3033         return null;
3036 // Slick overrides accessor
3038 Slick.override = function(regexp, fn){
3039         local.override(regexp, fn);
3040         return this;
3043 Slick.isXML = local.isXML;
3045 Slick.uidOf = function(node){
3046         return local.getUIDHTML(node);
3049 if (!this.Slick) this.Slick = Slick;
3051 }).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
3056 name: Element
3058 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.
3060 license: MIT-style license.
3062 requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder]
3064 provides: [Element, Elements, $, $$, IFrame, Selectors]
3069 var Element = this.Element = function(tag, props){
3070         var konstructor = Element.Constructors[tag];
3071         if (konstructor) return konstructor(props);
3072         if (typeof tag != 'string') return document.id(tag).set(props);
3074         if (!props) props = {};
3076         if (!(/^[\w-]+$/).test(tag)){
3077                 var parsed = Slick.parse(tag).expressions[0][0];
3078                 tag = (parsed.tag == '*') ? 'div' : parsed.tag;
3079                 if (parsed.id && props.id == null) props.id = parsed.id;
3081                 var attributes = parsed.attributes;
3082                 if (attributes) for (var attr, i = 0, l = attributes.length; i < l; i++){
3083                         attr = attributes[i];
3084                         if (props[attr.key] != null) continue;
3086                         if (attr.value != null && attr.operator == '=') props[attr.key] = attr.value;
3087                         else if (!attr.value && !attr.operator) props[attr.key] = true;
3088                 }
3090                 if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' ');
3091         }
3093         return document.newElement(tag, props);
3097 if (Browser.Element){
3098         Element.prototype = Browser.Element.prototype;
3099         // IE8 and IE9 require the wrapping.
3100         Element.prototype._fireEvent = (function(fireEvent){
3101                 return function(type, event){
3102                         return fireEvent.call(this, type, event);
3103                 };
3104         })(Element.prototype.fireEvent);
3107 new Type('Element', Element).mirror(function(name){
3108         if (Array.prototype[name]) return;
3110         var obj = {};
3111         obj[name] = function(){
3112                 var results = [], args = arguments, elements = true;
3113                 for (var i = 0, l = this.length; i < l; i++){
3114                         var element = this[i], result = results[i] = element[name].apply(element, args);
3115                         elements = (elements && typeOf(result) == 'element');
3116                 }
3117                 return (elements) ? new Elements(results) : results;
3118         };
3120         Elements.implement(obj);
3123 if (!Browser.Element){
3124         Element.parent = Object;
3126         Element.Prototype = {
3127                 '$constructor': Element,
3128                 '$family': Function.from('element').hide()
3129         };
3131         Element.mirror(function(name, method){
3132                 Element.Prototype[name] = method;
3133         });
3136 Element.Constructors = {};
3138 //<1.2compat>
3140 Element.Constructors = new Hash;
3142 //</1.2compat>
3144 var IFrame = new Type('IFrame', function(){
3145         var params = Array.link(arguments, {
3146                 properties: Type.isObject,
3147                 iframe: function(obj){
3148                         return (obj != null);
3149                 }
3150         });
3152         var props = params.properties || {}, iframe;
3153         if (params.iframe) iframe = document.id(params.iframe);
3154         var onload = props.onload || function(){};
3155         delete props.onload;
3156         props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.uniqueID()].pick();
3157         iframe = new Element(iframe || 'iframe', props);
3159         var onLoad = function(){
3160                 onload.call(iframe.contentWindow);
3161         };
3163         if (window.frames[props.id]) onLoad();
3164         else iframe.addListener('load', onLoad);
3165         return iframe;
3168 var Elements = this.Elements = function(nodes){
3169         if (nodes && nodes.length){
3170                 var uniques = {}, node;
3171                 for (var i = 0; node = nodes[i++];){
3172                         var uid = Slick.uidOf(node);
3173                         if (!uniques[uid]){
3174                                 uniques[uid] = true;
3175                                 this.push(node);
3176                         }
3177                 }
3178         }
3181 Elements.prototype = {length: 0};
3182 Elements.parent = Array;
3184 new Type('Elements', Elements).implement({
3186         filter: function(filter, bind){
3187                 if (!filter) return this;
3188                 return new Elements(Array.filter(this, (typeOf(filter) == 'string') ? function(item){
3189                         return item.match(filter);
3190                 } : filter, bind));
3191         }.protect(),
3193         push: function(){
3194                 var length = this.length;
3195                 for (var i = 0, l = arguments.length; i < l; i++){
3196                         var item = document.id(arguments[i]);
3197                         if (item) this[length++] = item;
3198                 }
3199                 return (this.length = length);
3200         }.protect(),
3202         unshift: function(){
3203                 var items = [];
3204                 for (var i = 0, l = arguments.length; i < l; i++){
3205                         var item = document.id(arguments[i]);
3206                         if (item) items.push(item);
3207                 }
3208                 return Array.prototype.unshift.apply(this, items);
3209         }.protect(),
3211         concat: function(){
3212                 var newElements = new Elements(this);
3213                 for (var i = 0, l = arguments.length; i < l; i++){
3214                         var item = arguments[i];
3215                         if (Type.isEnumerable(item)) newElements.append(item);
3216                         else newElements.push(item);
3217                 }
3218                 return newElements;
3219         }.protect(),
3221         append: function(collection){
3222                 for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]);
3223                 return this;
3224         }.protect(),
3226         empty: function(){
3227                 while (this.length) delete this[--this.length];
3228                 return this;
3229         }.protect()
3233 //<1.2compat>
3235 Elements.alias('extend', 'append');
3237 //</1.2compat>
3239 (function(){
3241 // FF, IE
3242 var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2};
3244 splice.call(object, 1, 1);
3245 if (object[1] == 1) Elements.implement('splice', function(){
3246         var length = this.length;
3247         var result = splice.apply(this, arguments);
3248         while (length >= this.length) delete this[length--];
3249         return result;
3250 }.protect());
3252 Array.forEachMethod(function(method, name){
3253         Elements.implement(name, method);
3256 Array.mirror(Elements);
3258 /*<ltIE8>*/
3259 var createElementAcceptsHTML;
3260 try {
3261         createElementAcceptsHTML = (document.createElement('<input name=x>').name == 'x');
3262 } catch (e){}
3264 var escapeQuotes = function(html){
3265         return ('' + html).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
3267 /*</ltIE8>*/
3269 Document.implement({
3271         newElement: function(tag, props){
3272                 if (props && props.checked != null) props.defaultChecked = props.checked;
3273                 /*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
3274                 if (createElementAcceptsHTML && props){
3275                         tag = '<' + tag;
3276                         if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
3277                         if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
3278                         tag += '>';
3279                         delete props.name;
3280                         delete props.type;
3281                 }
3282                 /*</ltIE8>*/
3283                 return this.id(this.createElement(tag)).set(props);
3284         }
3288 })();
3290 (function(){
3292 Slick.uidOf(window);
3293 Slick.uidOf(document);
3295 Document.implement({
3297         newTextNode: function(text){
3298                 return this.createTextNode(text);
3299         },
3301         getDocument: function(){
3302                 return this;
3303         },
3305         getWindow: function(){
3306                 return this.window;
3307         },
3309         id: (function(){
3311                 var types = {
3313                         string: function(id, nocash, doc){
3314                                 id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1'));
3315                                 return (id) ? types.element(id, nocash) : null;
3316                         },
3318                         element: function(el, nocash){
3319                                 Slick.uidOf(el);
3320                                 if (!nocash && !el.$family && !(/^(?:object|embed)$/i).test(el.tagName)){
3321                                         var fireEvent = el.fireEvent;
3322                                         // wrapping needed in IE7, or else crash
3323                                         el._fireEvent = function(type, event){
3324                                                 return fireEvent(type, event);
3325                                         };
3326                                         Object.append(el, Element.Prototype);
3327                                 }
3328                                 return el;
3329                         },
3331                         object: function(obj, nocash, doc){
3332                                 if (obj.toElement) return types.element(obj.toElement(doc), nocash);
3333                                 return null;
3334                         }
3336                 };
3338                 types.textnode = types.whitespace = types.window = types.document = function(zero){
3339                         return zero;
3340                 };
3342                 return function(el, nocash, doc){
3343                         if (el && el.$family && el.uniqueNumber) return el;
3344                         var type = typeOf(el);
3345                         return (types[type]) ? types[type](el, nocash, doc || document) : null;
3346                 };
3348         })()
3352 if (window.$ == null) Window.implement('$', function(el, nc){
3353         return document.id(el, nc, this.document);
3356 Window.implement({
3358         getDocument: function(){
3359                 return this.document;
3360         },
3362         getWindow: function(){
3363                 return this;
3364         }
3368 [Document, Element].invoke('implement', {
3370         getElements: function(expression){
3371                 return Slick.search(this, expression, new Elements);
3372         },
3374         getElement: function(expression){
3375                 return document.id(Slick.find(this, expression));
3376         }
3380 var contains = {contains: function(element){
3381         return Slick.contains(this, element);
3384 if (!document.contains) Document.implement(contains);
3385 if (!document.createElement('div').contains) Element.implement(contains);
3387 //<1.2compat>
3389 Element.implement('hasChild', function(element){
3390         return this !== element && this.contains(element);
3393 (function(search, find, match){
3395         this.Selectors = {};
3396         var pseudos = this.Selectors.Pseudo = new Hash();
3398         var addSlickPseudos = function(){
3399                 for (var name in pseudos) if (pseudos.hasOwnProperty(name)){
3400                         Slick.definePseudo(name, pseudos[name]);
3401                         delete pseudos[name];
3402                 }
3403         };
3405         Slick.search = function(context, expression, append){
3406                 addSlickPseudos();
3407                 return search.call(this, context, expression, append);
3408         };
3410         Slick.find = function(context, expression){
3411                 addSlickPseudos();
3412                 return find.call(this, context, expression);
3413         };
3415         Slick.match = function(node, selector){
3416                 addSlickPseudos();
3417                 return match.call(this, node, selector);
3418         };
3420 })(Slick.search, Slick.find, Slick.match);
3422 //</1.2compat>
3424 // tree walking
3426 var injectCombinator = function(expression, combinator){
3427         if (!expression) return combinator;
3429         expression = Object.clone(Slick.parse(expression));
3431         var expressions = expression.expressions;
3432         for (var i = expressions.length; i--;)
3433                 expressions[i][0].combinator = combinator;
3435         return expression;
3438 Object.forEach({
3439         getNext: '~',
3440         getPrevious: '!~',
3441         getParent: '!'
3442 }, function(combinator, method){
3443         Element.implement(method, function(expression){
3444                 return this.getElement(injectCombinator(expression, combinator));
3445         });
3448 Object.forEach({
3449         getAllNext: '~',
3450         getAllPrevious: '!~',
3451         getSiblings: '~~',
3452         getChildren: '>',
3453         getParents: '!'
3454 }, function(combinator, method){
3455         Element.implement(method, function(expression){
3456                 return this.getElements(injectCombinator(expression, combinator));
3457         });
3460 Element.implement({
3462         getFirst: function(expression){
3463                 return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]);
3464         },
3466         getLast: function(expression){
3467                 return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast());
3468         },
3470         getWindow: function(){
3471                 return this.ownerDocument.window;
3472         },
3474         getDocument: function(){
3475                 return this.ownerDocument;
3476         },
3478         getElementById: function(id){
3479                 return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1')));
3480         },
3482         match: function(expression){
3483                 return !expression || Slick.match(this, expression);
3484         }
3488 //<1.2compat>
3490 if (window.$$ == null) Window.implement('$$', function(selector){
3491         var elements = new Elements;
3492         if (arguments.length == 1 && typeof selector == 'string') return Slick.search(this.document, selector, elements);
3493         var args = Array.flatten(arguments);
3494         for (var i = 0, l = args.length; i < l; i++){
3495                 var item = args[i];
3496                 switch (typeOf(item)){
3497                         case 'element': elements.push(item); break;
3498                         case 'string': Slick.search(this.document, item, elements);
3499                 }
3500         }
3501         return elements;
3504 //</1.2compat>
3506 if (window.$$ == null) Window.implement('$$', function(selector){
3507         if (arguments.length == 1){
3508                 if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements);
3509                 else if (Type.isEnumerable(selector)) return new Elements(selector);
3510         }
3511         return new Elements(arguments);
3514 // Inserters
3516 var inserters = {
3518         before: function(context, element){
3519                 var parent = element.parentNode;
3520                 if (parent) parent.insertBefore(context, element);
3521         },
3523         after: function(context, element){
3524                 var parent = element.parentNode;
3525                 if (parent) parent.insertBefore(context, element.nextSibling);
3526         },
3528         bottom: function(context, element){
3529                 element.appendChild(context);
3530         },
3532         top: function(context, element){
3533                 element.insertBefore(context, element.firstChild);
3534         }
3538 inserters.inside = inserters.bottom;
3540 //<1.2compat>
3542 Object.each(inserters, function(inserter, where){
3544         where = where.capitalize();
3546         var methods = {};
3548         methods['inject' + where] = function(el){
3549                 inserter(this, document.id(el, true));
3550                 return this;
3551         };
3553         methods['grab' + where] = function(el){
3554                 inserter(document.id(el, true), this);
3555                 return this;
3556         };
3558         Element.implement(methods);
3562 //</1.2compat>
3564 // getProperty / setProperty
3566 var propertyGetters = {}, propertySetters = {};
3568 // properties
3570 var properties = {};
3571 Array.forEach([
3572         'type', 'value', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan',
3573         'frameBorder', 'rowSpan', 'tabIndex', 'useMap'
3574 ], function(property){
3575         properties[property.toLowerCase()] = property;
3578 properties.html = 'innerHTML';
3579 properties.text = (document.createElement('div').textContent == null) ? 'innerText': 'textContent';
3581 Object.forEach(properties, function(real, key){
3582         propertySetters[key] = function(node, value){
3583                 node[real] = value;
3584         };
3585         propertyGetters[key] = function(node){
3586                 return node[real];
3587         };
3590 // Booleans
3592 var bools = [
3593         'compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked',
3594         'disabled', 'readOnly', 'multiple', 'selected', 'noresize',
3595         'defer', 'defaultChecked', 'autofocus', 'controls', 'autoplay',
3596         'loop'
3599 var booleans = {};
3600 Array.forEach(bools, function(bool){
3601         var lower = bool.toLowerCase();
3602         booleans[lower] = bool;
3603         propertySetters[lower] = function(node, value){
3604                 node[bool] = !!value;
3605         };
3606         propertyGetters[lower] = function(node){
3607                 return !!node[bool];
3608         };
3611 // Special cases
3613 Object.append(propertySetters, {
3615         'class': function(node, value){
3616                 ('className' in node) ? node.className = (value || '') : node.setAttribute('class', value);
3617         },
3619         'for': function(node, value){
3620                 ('htmlFor' in node) ? node.htmlFor = value : node.setAttribute('for', value);
3621         },
3623         'style': function(node, value){
3624                 (node.style) ? node.style.cssText = value : node.setAttribute('style', value);
3625         },
3627         'value': function(node, value){
3628                 node.value = (value != null) ? value : '';
3629         }
3633 propertyGetters['class'] = function(node){
3634         return ('className' in node) ? node.className || null : node.getAttribute('class');
3637 /* <webkit> */
3638 var el = document.createElement('button');
3639 // IE sets type as readonly and throws
3640 try { el.type = 'button'; } catch(e){}
3641 if (el.type != 'button') propertySetters.type = function(node, value){
3642         node.setAttribute('type', value);
3644 el = null;
3645 /* </webkit> */
3647 /*<IE>*/
3648 var input = document.createElement('input');
3649 input.value = 't';
3650 input.type = 'submit';
3651 if (input.value != 't') propertySetters.type = function(node, type){
3652         var value = node.value;
3653         node.type = type;
3654         node.value = value;
3656 input = null;
3657 /*</IE>*/
3659 /* getProperty, setProperty */
3661 /* <ltIE9> */
3662 var pollutesGetAttribute = (function(div){
3663         div.random = 'attribute';
3664         return (div.getAttribute('random') == 'attribute');
3665 })(document.createElement('div'));
3667 var hasCloneBug = (function(test){
3668         test.innerHTML = '<object><param name="should_fix" value="the unknown"></object>';
3669         return test.cloneNode(true).firstChild.childNodes.length != 1;
3670 })(document.createElement('div'));
3671 /* </ltIE9> */
3673 var hasClassList = !!document.createElement('div').classList;
3675 var classes = function(className){
3676         var classNames = (className || '').clean().split(" "), uniques = {};
3677         return classNames.filter(function(className){
3678                 if (className !== "" && !uniques[className]) return uniques[className] = className;
3679         });
3682 var addToClassList = function(name){
3683         this.classList.add(name);
3686 var removeFromClassList = function(name){
3687         this.classList.remove(name);
3690 Element.implement({
3692         setProperty: function(name, value){
3693                 var setter = propertySetters[name.toLowerCase()];
3694                 if (setter){
3695                         setter(this, value);
3696                 } else {
3697                         /* <ltIE9> */
3698                         var attributeWhiteList;
3699                         if (pollutesGetAttribute) attributeWhiteList = this.retrieve('$attributeWhiteList', {});
3700                         /* </ltIE9> */
3702                         if (value == null){
3703                                 this.removeAttribute(name);
3704                                 /* <ltIE9> */
3705                                 if (pollutesGetAttribute) delete attributeWhiteList[name];
3706                                 /* </ltIE9> */
3707                         } else {
3708                                 this.setAttribute(name, '' + value);
3709                                 /* <ltIE9> */
3710                                 if (pollutesGetAttribute) attributeWhiteList[name] = true;
3711                                 /* </ltIE9> */
3712                         }
3713                 }
3714                 return this;
3715         },
3717         setProperties: function(attributes){
3718                 for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
3719                 return this;
3720         },
3722         getProperty: function(name){
3723                 var getter = propertyGetters[name.toLowerCase()];
3724                 if (getter) return getter(this);
3725                 /* <ltIE9> */
3726                 if (pollutesGetAttribute){
3727                         var attr = this.getAttributeNode(name), attributeWhiteList = this.retrieve('$attributeWhiteList', {});
3728                         if (!attr) return null;
3729                         if (attr.expando && !attributeWhiteList[name]){
3730                                 var outer = this.outerHTML;
3731                                 // segment by the opening tag and find mention of attribute name
3732                                 if (outer.substr(0, outer.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(name) < 0) return null;
3733                                 attributeWhiteList[name] = true;
3734                         }
3735                 }
3736                 /* </ltIE9> */
3737                 var result = Slick.getAttribute(this, name);
3738                 return (!result && !Slick.hasAttribute(this, name)) ? null : result;
3739         },
3741         getProperties: function(){
3742                 var args = Array.from(arguments);
3743                 return args.map(this.getProperty, this).associate(args);
3744         },
3746         removeProperty: function(name){
3747                 return this.setProperty(name, null);
3748         },
3750         removeProperties: function(){
3751                 Array.each(arguments, this.removeProperty, this);
3752                 return this;
3753         },
3755         set: function(prop, value){
3756                 var property = Element.Properties[prop];
3757                 (property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value);
3758         }.overloadSetter(),
3760         get: function(prop){
3761                 var property = Element.Properties[prop];
3762                 return (property && property.get) ? property.get.apply(this) : this.getProperty(prop);
3763         }.overloadGetter(),
3765         erase: function(prop){
3766                 var property = Element.Properties[prop];
3767                 (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
3768                 return this;
3769         },
3771         hasClass: hasClassList ? function(className){
3772                 return this.classList.contains(className);
3773         } : function(className){
3774                 return this.className.clean().contains(className, ' ');
3775         },
3777         addClass: hasClassList ? function(className){
3778                 classes(className).forEach(addToClassList, this);
3779                 return this;
3780         } : function(className){
3781                 this.className = classes(className + ' ' + this.className).join(' ');
3782                 return this;
3783         },
3785         removeClass: hasClassList ? function(className){
3786                 classes(className).forEach(removeFromClassList, this);
3787                 return this;
3788         } : function(className){
3789                 var classNames = classes(this.className);
3790                 classes(className).forEach(classNames.erase, classNames);
3791                 this.className = classNames.join(' ');
3792                 return this;
3793         },
3795         toggleClass: function(className, force){
3796                 if (force == null) force = !this.hasClass(className);
3797                 return (force) ? this.addClass(className) : this.removeClass(className);
3798         },
3800         adopt: function(){
3801                 var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length;
3802                 if (length > 1) parent = fragment = document.createDocumentFragment();
3804                 for (var i = 0; i < length; i++){
3805                         var element = document.id(elements[i], true);
3806                         if (element) parent.appendChild(element);
3807                 }
3809                 if (fragment) this.appendChild(fragment);
3811                 return this;
3812         },
3814         appendText: function(text, where){
3815                 return this.grab(this.getDocument().newTextNode(text), where);
3816         },
3818         grab: function(el, where){
3819                 inserters[where || 'bottom'](document.id(el, true), this);
3820                 return this;
3821         },
3823         inject: function(el, where){
3824                 inserters[where || 'bottom'](this, document.id(el, true));
3825                 return this;
3826         },
3828         replaces: function(el){
3829                 el = document.id(el, true);
3830                 el.parentNode.replaceChild(this, el);
3831                 return this;
3832         },
3834         wraps: function(el, where){
3835                 el = document.id(el, true);
3836                 return this.replaces(el).grab(el, where);
3837         },
3839         getSelected: function(){
3840                 this.selectedIndex; // Safari 3.2.1
3841                 return new Elements(Array.from(this.options).filter(function(option){
3842                         return option.selected;
3843                 }));
3844         },
3846         toQueryString: function(){
3847                 var queryString = [];
3848                 this.getElements('input, select, textarea').each(function(el){
3849                         var type = el.type;
3850                         if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return;
3852                         var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){
3853                                 // IE
3854                                 return document.id(opt).get('value');
3855                         }) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value');
3857                         Array.from(value).each(function(val){
3858                                 if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val));
3859                         });
3860                 });
3861                 return queryString.join('&');
3862         }
3867 // appendHTML
3869 var appendInserters = {
3870         before: 'beforeBegin',
3871         after: 'afterEnd',
3872         bottom: 'beforeEnd',
3873         top: 'afterBegin',
3874         inside: 'beforeEnd'
3877 Element.implement('appendHTML', ('insertAdjacentHTML' in document.createElement('div')) ? function(html, where){
3878         this.insertAdjacentHTML(appendInserters[where || 'bottom'], html);
3879         return this;
3880 } : function(html, where){
3881         var temp = new Element('div', {html: html}),
3882                 children = temp.childNodes,
3883                 fragment = temp.firstChild;
3885         if (!fragment) return this;
3886         if (children.length > 1){
3887                 fragment = document.createDocumentFragment();
3888                 for (var i = 0, l = children.length; i < l; i++){
3889                         fragment.appendChild(children[i]);
3890                 }
3891         }
3893         inserters[where || 'bottom'](fragment, this);
3894         return this;
3897 var collected = {}, storage = {};
3899 var get = function(uid){
3900         return (storage[uid] || (storage[uid] = {}));
3903 var clean = function(item){
3904         var uid = item.uniqueNumber;
3905         if (item.removeEvents) item.removeEvents();
3906         if (item.clearAttributes) item.clearAttributes();
3907         if (uid != null){
3908                 delete collected[uid];
3909                 delete storage[uid];
3910         }
3911         return item;
3914 var formProps = {input: 'checked', option: 'selected', textarea: 'value'};
3916 Element.implement({
3918         destroy: function(){
3919                 var children = clean(this).getElementsByTagName('*');
3920                 Array.each(children, clean);
3921                 Element.dispose(this);
3922                 return null;
3923         },
3925         empty: function(){
3926                 Array.from(this.childNodes).each(Element.dispose);
3927                 return this;
3928         },
3930         dispose: function(){
3931                 return (this.parentNode) ? this.parentNode.removeChild(this) : this;
3932         },
3934         clone: function(contents, keepid){
3935                 contents = contents !== false;
3936                 var clone = this.cloneNode(contents), ce = [clone], te = [this], i;
3938                 if (contents){
3939                         ce.append(Array.from(clone.getElementsByTagName('*')));
3940                         te.append(Array.from(this.getElementsByTagName('*')));
3941                 }
3943                 for (i = ce.length; i--;){
3944                         var node = ce[i], element = te[i];
3945                         if (!keepid) node.removeAttribute('id');
3946                         /*<ltIE9>*/
3947                         if (node.clearAttributes){
3948                                 node.clearAttributes();
3949                                 node.mergeAttributes(element);
3950                                 node.removeAttribute('uniqueNumber');
3951                                 if (node.options){
3952                                         var no = node.options, eo = element.options;
3953                                         for (var j = no.length; j--;) no[j].selected = eo[j].selected;
3954                                 }
3955                         }
3956                         /*</ltIE9>*/
3957                         var prop = formProps[element.tagName.toLowerCase()];
3958                         if (prop && element[prop]) node[prop] = element[prop];
3959                 }
3961                 /*<ltIE9>*/
3962                 if (hasCloneBug){
3963                         var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object');
3964                         for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML;
3965                 }
3966                 /*</ltIE9>*/
3967                 return document.id(clone);
3968         }
3972 [Element, Window, Document].invoke('implement', {
3974         addListener: function(type, fn){
3975                 if (window.attachEvent && !window.addEventListener){
3976                         collected[Slick.uidOf(this)] = this;
3977                 }
3978                 if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]);
3979                 else this.attachEvent('on' + type, fn);
3980                 return this;
3981         },
3983         removeListener: function(type, fn){
3984                 if (this.removeEventListener) this.removeEventListener(type, fn, !!arguments[2]);
3985                 else this.detachEvent('on' + type, fn);
3986                 return this;
3987         },
3989         retrieve: function(property, dflt){
3990                 var storage = get(Slick.uidOf(this)), prop = storage[property];
3991                 if (dflt != null && prop == null) prop = storage[property] = dflt;
3992                 return prop != null ? prop : null;
3993         },
3995         store: function(property, value){
3996                 var storage = get(Slick.uidOf(this));
3997                 storage[property] = value;
3998                 return this;
3999         },
4001         eliminate: function(property){
4002                 var storage = get(Slick.uidOf(this));
4003                 delete storage[property];
4004                 return this;
4005         }
4009 /*<ltIE9>*/
4010 if (window.attachEvent && !window.addEventListener){
4011         var gc = function(){
4012                 Object.each(collected, clean);
4013                 if (window.CollectGarbage) CollectGarbage();
4014                 window.removeListener('unload', gc);
4015         }
4016         window.addListener('unload', gc);
4018 /*</ltIE9>*/
4020 Element.Properties = {};
4022 //<1.2compat>
4024 Element.Properties = new Hash;
4026 //</1.2compat>
4028 Element.Properties.style = {
4030         set: function(style){
4031                 this.style.cssText = style;
4032         },
4034         get: function(){
4035                 return this.style.cssText;
4036         },
4038         erase: function(){
4039                 this.style.cssText = '';
4040         }
4044 Element.Properties.tag = {
4046         get: function(){
4047                 return this.tagName.toLowerCase();
4048         }
4052 Element.Properties.html = {
4054         set: function(html){
4055                 if (html == null) html = '';
4056                 else if (typeOf(html) == 'array') html = html.join('');
4057                 this.innerHTML = html;
4058         },
4060         erase: function(){
4061                 this.innerHTML = '';
4062         }
4066 var supportsHTML5Elements = true, supportsTableInnerHTML = true, supportsTRInnerHTML = true;
4068 /*<ltIE9>*/
4069 // technique by jdbarlett - http://jdbartlett.com/innershiv/
4070 var div = document.createElement('div');
4071 div.innerHTML = '<nav></nav>';
4072 supportsHTML5Elements = (div.childNodes.length == 1);
4073 if (!supportsHTML5Elements){
4074         var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' '),
4075                 fragment = document.createDocumentFragment(), l = tags.length;
4076         while (l--) fragment.createElement(tags[l]);
4078 div = null;
4079 /*</ltIE9>*/
4081 /*<IE>*/
4082 supportsTableInnerHTML = Function.attempt(function(){
4083         var table = document.createElement('table');
4084         table.innerHTML = '<tr><td></td></tr>';
4085         return true;
4088 /*<ltFF4>*/
4089 var tr = document.createElement('tr'), html = '<td></td>';
4090 tr.innerHTML = html;
4091 supportsTRInnerHTML = (tr.innerHTML == html);
4092 tr = null;
4093 /*</ltFF4>*/
4095 if (!supportsTableInnerHTML || !supportsTRInnerHTML || !supportsHTML5Elements){
4097         Element.Properties.html.set = (function(set){
4099                 var translations = {
4100                         table: [1, '<table>', '</table>'],
4101                         select: [1, '<select>', '</select>'],
4102                         tbody: [2, '<table><tbody>', '</tbody></table>'],
4103                         tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
4104                 };
4106                 translations.thead = translations.tfoot = translations.tbody;
4108                 return function(html){
4109                         var wrap = translations[this.get('tag')];
4110                         if (!wrap && !supportsHTML5Elements) wrap = [0, '', ''];
4111                         if (!wrap) return set.call(this, html);
4113                         var level = wrap[0], wrapper = document.createElement('div'), target = wrapper;
4114                         if (!supportsHTML5Elements) fragment.appendChild(wrapper);
4115                         wrapper.innerHTML = [wrap[1], html, wrap[2]].flatten().join('');
4116                         while (level--) target = target.firstChild;
4117                         this.empty().adopt(target.childNodes);
4118                         if (!supportsHTML5Elements) fragment.removeChild(wrapper);
4119                         wrapper = null;
4120                 };
4122         })(Element.Properties.html.set);
4124 /*</IE>*/
4126 /*<ltIE9>*/
4127 var testForm = document.createElement('form');
4128 testForm.innerHTML = '<select><option>s</option></select>';
4130 if (testForm.firstChild.value != 's') Element.Properties.value = {
4132         set: function(value){
4133                 var tag = this.get('tag');
4134                 if (tag != 'select') return this.setProperty('value', value);
4135                 var options = this.getElements('option');
4136                 value = String(value);
4137                 for (var i = 0; i < options.length; i++){
4138                         var option = options[i],
4139                                 attr = option.getAttributeNode('value'),
4140                                 optionValue = (attr && attr.specified) ? option.value : option.get('text');
4141                         if (optionValue === value) return option.selected = true;
4142                 }
4143         },
4145         get: function(){
4146                 var option = this, tag = option.get('tag');
4148                 if (tag != 'select' && tag != 'option') return this.getProperty('value');
4150                 if (tag == 'select' && !(option = option.getSelected()[0])) return '';
4152                 var attr = option.getAttributeNode('value');
4153                 return (attr && attr.specified) ? option.value : option.get('text');
4154         }
4157 testForm = null;
4158 /*</ltIE9>*/
4160 /*<IE>*/
4161 if (document.createElement('div').getAttributeNode('id')) Element.Properties.id = {
4162         set: function(id){
4163                 this.id = this.getAttributeNode('id').value = id;
4164         },
4165         get: function(){
4166                 return this.id || null;
4167         },
4168         erase: function(){
4169                 this.id = this.getAttributeNode('id').value = '';
4170         }
4172 /*</IE>*/
4174 })();
4179 name: Element.Style
4181 description: Contains methods for interacting with the styles of Elements in a fashionable way.
4183 license: MIT-style license.
4185 requires: Element
4187 provides: Element.Style
4192 (function(){
4194 var html = document.html, el;
4196 //<ltIE9>
4197 // Check for oldIE, which does not remove styles when they're set to null
4198 el = document.createElement('div');
4199 el.style.color = 'red';
4200 el.style.color = null;
4201 var doesNotRemoveStyles = el.style.color == 'red';
4203 // check for oldIE, which returns border* shorthand styles in the wrong order (color-width-style instead of width-style-color)
4204 var border = '1px solid #123abc';
4205 el.style.border = border;
4206 var returnsBordersInWrongOrder = el.style.border != border;
4207 el = null;
4208 //</ltIE9>
4210 var hasGetComputedStyle = !!window.getComputedStyle;
4212 Element.Properties.styles = {set: function(styles){
4213         this.setStyles(styles);
4216 var hasOpacity = (html.style.opacity != null),
4217         hasFilter = (html.style.filter != null),
4218         reAlpha = /alpha\(opacity=([\d.]+)\)/i;
4220 var setVisibility = function(element, opacity){
4221         element.store('$opacity', opacity);
4222         element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden';
4225 //<ltIE9>
4226 var setFilter = function(element, regexp, value){
4227         var style = element.style,
4228                 filter = style.filter || element.getComputedStyle('filter') || '';
4229         style.filter = (regexp.test(filter) ? filter.replace(regexp, value) : filter + ' ' + value).trim();
4230         if (!style.filter) style.removeAttribute('filter');
4232 //</ltIE9>
4234 var setOpacity = (hasOpacity ? function(element, opacity){
4235         element.style.opacity = opacity;
4236 } : (hasFilter ? function(element, opacity){
4237         if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1;
4238         if (opacity == null || opacity == 1){
4239                 setFilter(element, reAlpha, '');
4240                 if (opacity == 1 && getOpacity(element) != 1) setFilter(element, reAlpha, 'alpha(opacity=100)');
4241         } else {
4242                 setFilter(element, reAlpha, 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')');
4243         }
4244 } : setVisibility));
4246 var getOpacity = (hasOpacity ? function(element){
4247         var opacity = element.style.opacity || element.getComputedStyle('opacity');
4248         return (opacity == '') ? 1 : opacity.toFloat();
4249 } : (hasFilter ? function(element){
4250         var filter = (element.style.filter || element.getComputedStyle('filter')),
4251                 opacity;
4252         if (filter) opacity = filter.match(reAlpha);
4253         return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
4254 } : function(element){
4255         var opacity = element.retrieve('$opacity');
4256         if (opacity == null) opacity = (element.style.visibility == 'hidden' ? 0 : 1);
4257         return opacity;
4258 }));
4260 var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat',
4261         namedPositions = {left: '0%', top: '0%', center: '50%', right: '100%', bottom: '100%'},
4262         hasBackgroundPositionXY = (html.style.backgroundPositionX != null);
4264 //<ltIE9>
4265 var removeStyle = function(style, property){
4266         if (property == 'backgroundPosition'){
4267                 style.removeAttribute(property + 'X');
4268                 property += 'Y';
4269         }
4270         style.removeAttribute(property);
4272 //</ltIE9>
4274 Element.implement({
4276         getComputedStyle: function(property){
4277                 if (!hasGetComputedStyle && this.currentStyle) return this.currentStyle[property.camelCase()];
4278                 var defaultView = Element.getDocument(this).defaultView,
4279                         computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
4280                 return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : '';
4281         },
4283         setStyle: function(property, value){
4284                 if (property == 'opacity'){
4285                         if (value != null) value = parseFloat(value);
4286                         setOpacity(this, value);
4287                         return this;
4288                 }
4289                 property = (property == 'float' ? floatName : property).camelCase();
4290                 if (typeOf(value) != 'string'){
4291                         var map = (Element.Styles[property] || '@').split(' ');
4292                         value = Array.from(value).map(function(val, i){
4293                                 if (!map[i]) return '';
4294                                 return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
4295                         }).join(' ');
4296                 } else if (value == String(Number(value))){
4297                         value = Math.round(value);
4298                 }
4299                 this.style[property] = value;
4300                 //<ltIE9>
4301                 if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){
4302                         removeStyle(this.style, property);
4303                 }
4304                 //</ltIE9>
4305                 return this;
4306         },
4308         getStyle: function(property){
4309                 if (property == 'opacity') return getOpacity(this);
4310                 property = (property == 'float' ? floatName : property).camelCase();
4311                 var result = this.style[property];
4312                 if (!result || property == 'zIndex'){
4313                         if (Element.ShortStyles.hasOwnProperty(property)){
4314                                 result = [];
4315                                 for (var s in Element.ShortStyles[property]) result.push(this.getStyle(s));
4316                                 return result.join(' ');
4317                         }
4318                         result = this.getComputedStyle(property);
4319                 }
4320                 if (hasBackgroundPositionXY && /^backgroundPosition[XY]?$/.test(property)){
4321                         return result.replace(/(top|right|bottom|left)/g, function(position){
4322                                 return namedPositions[position];
4323                         }) || '0px';
4324                 }
4325                 if (!result && property == 'backgroundPosition') return '0px 0px';
4326                 if (result){
4327                         result = String(result);
4328                         var color = result.match(/rgba?\([\d\s,]+\)/);
4329                         if (color) result = result.replace(color[0], color[0].rgbToHex());
4330                 }
4331                 if (!hasGetComputedStyle && !this.style[property]){
4332                         if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){
4333                                 var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
4334                                 values.each(function(value){
4335                                         size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
4336                                 }, this);
4337                                 return this['offset' + property.capitalize()] - size + 'px';
4338                         }
4339                         if ((/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){
4340                                 return '0px';
4341                         }
4342                 }
4343                 //<ltIE9>
4344                 if (returnsBordersInWrongOrder && /^border(Top|Right|Bottom|Left)?$/.test(property) && /^#/.test(result)){
4345                         return result.replace(/^(.+)\s(.+)\s(.+)$/, '$2 $3 $1');
4346                 }
4347                 //</ltIE9>
4348                 return result;
4349         },
4351         setStyles: function(styles){
4352                 for (var style in styles) this.setStyle(style, styles[style]);
4353                 return this;
4354         },
4356         getStyles: function(){
4357                 var result = {};
4358                 Array.flatten(arguments).each(function(key){
4359                         result[key] = this.getStyle(key);
4360                 }, this);
4361                 return result;
4362         }
4366 Element.Styles = {
4367         left: '@px', top: '@px', bottom: '@px', right: '@px',
4368         width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
4369         backgroundColor: 'rgb(@, @, @)', backgroundSize: '@px', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
4370         fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
4371         margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
4372         borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
4373         zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
4376 //<1.3compat>
4378 Element.implement({
4380         setOpacity: function(value){
4381                 setOpacity(this, value);
4382                 return this;
4383         },
4385         getOpacity: function(){
4386                 return getOpacity(this);
4387         }
4391 Element.Properties.opacity = {
4393         set: function(opacity){
4394                 setOpacity(this, opacity);
4395                 setVisibility(this, opacity);
4396         },
4398         get: function(){
4399                 return getOpacity(this);
4400         }
4404 //</1.3compat>
4406 //<1.2compat>
4408 Element.Styles = new Hash(Element.Styles);
4410 //</1.2compat>
4412 Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
4414 ['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
4415         var Short = Element.ShortStyles;
4416         var All = Element.Styles;
4417         ['margin', 'padding'].each(function(style){
4418                 var sd = style + direction;
4419                 Short[style][sd] = All[sd] = '@px';
4420         });
4421         var bd = 'border' + direction;
4422         Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
4423         var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
4424         Short[bd] = {};
4425         Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
4426         Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
4427         Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
4430 if (hasBackgroundPositionXY) Element.ShortStyles.backgroundPosition = {backgroundPositionX: '@', backgroundPositionY: '@'};
4431 })();
4436 name: Element.Event
4438 description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events, if necessary.
4440 license: MIT-style license.
4442 requires: [Element, Event]
4444 provides: Element.Event
4449 (function(){
4451 Element.Properties.events = {set: function(events){
4452         this.addEvents(events);
4455 [Element, Window, Document].invoke('implement', {
4457         addEvent: function(type, fn){
4458                 var events = this.retrieve('events', {});
4459                 if (!events[type]) events[type] = {keys: [], values: []};
4460                 if (events[type].keys.contains(fn)) return this;
4461                 events[type].keys.push(fn);
4462                 var realType = type,
4463                         custom = Element.Events[type],
4464                         condition = fn,
4465                         self = this;
4466                 if (custom){
4467                         if (custom.onAdd) custom.onAdd.call(this, fn, type);
4468                         if (custom.condition){
4469                                 condition = function(event){
4470                                         if (custom.condition.call(this, event, type)) return fn.call(this, event);
4471                                         return true;
4472                                 };
4473                         }
4474                         if (custom.base) realType = Function.from(custom.base).call(this, type);
4475                 }
4476                 var defn = function(){
4477                         return fn.call(self);
4478                 };
4479                 var nativeEvent = Element.NativeEvents[realType];
4480                 if (nativeEvent){
4481                         if (nativeEvent == 2){
4482                                 defn = function(event){
4483                                         event = new DOMEvent(event, self.getWindow());
4484                                         if (condition.call(self, event) === false) event.stop();
4485                                 };
4486                         }
4487                         this.addListener(realType, defn, arguments[2]);
4488                 }
4489                 events[type].values.push(defn);
4490                 return this;
4491         },
4493         removeEvent: function(type, fn){
4494                 var events = this.retrieve('events');
4495                 if (!events || !events[type]) return this;
4496                 var list = events[type];
4497                 var index = list.keys.indexOf(fn);
4498                 if (index == -1) return this;
4499                 var value = list.values[index];
4500                 delete list.keys[index];
4501                 delete list.values[index];
4502                 var custom = Element.Events[type];
4503                 if (custom){
4504                         if (custom.onRemove) custom.onRemove.call(this, fn, type);
4505                         if (custom.base) type = Function.from(custom.base).call(this, type);
4506                 }
4507                 return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this;
4508         },
4510         addEvents: function(events){
4511                 for (var event in events) this.addEvent(event, events[event]);
4512                 return this;
4513         },
4515         removeEvents: function(events){
4516                 var type;
4517                 if (typeOf(events) == 'object'){
4518                         for (type in events) this.removeEvent(type, events[type]);
4519                         return this;
4520                 }
4521                 var attached = this.retrieve('events');
4522                 if (!attached) return this;
4523                 if (!events){
4524                         for (type in attached) this.removeEvents(type);
4525                         this.eliminate('events');
4526                 } else if (attached[events]){
4527                         attached[events].keys.each(function(fn){
4528                                 this.removeEvent(events, fn);
4529                         }, this);
4530                         delete attached[events];
4531                 }
4532                 return this;
4533         },
4535         fireEvent: function(type, args, delay){
4536                 var events = this.retrieve('events');
4537                 if (!events || !events[type]) return this;
4538                 args = Array.from(args);
4540                 events[type].keys.each(function(fn){
4541                         if (delay) fn.delay(delay, this, args);
4542                         else fn.apply(this, args);
4543                 }, this);
4544                 return this;
4545         },
4547         cloneEvents: function(from, type){
4548                 from = document.id(from);
4549                 var events = from.retrieve('events');
4550                 if (!events) return this;
4551                 if (!type){
4552                         for (var eventType in events) this.cloneEvents(from, eventType);
4553                 } else if (events[type]){
4554                         events[type].keys.each(function(fn){
4555                                 this.addEvent(type, fn);
4556                         }, this);
4557                 }
4558                 return this;
4559         }
4563 Element.NativeEvents = {
4564         click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
4565         mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
4566         mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
4567         keydown: 2, keypress: 2, keyup: 2, //keyboard
4568         orientationchange: 2, // mobile
4569         touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch
4570         gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture
4571         focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements
4572         load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
4573         hashchange: 1, popstate: 2, // history
4574         error: 1, abort: 1, scroll: 1 //misc
4577 Element.Events = {
4578         mousewheel: {
4579                 base: 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll'
4580         }
4583 var check = function(event){
4584         var related = event.relatedTarget;
4585         if (related == null) return true;
4586         if (!related) return false;
4587         return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
4590 if ('onmouseenter' in document.documentElement){
4591         Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2;
4592         Element.MouseenterCheck = check;
4593 } else {
4594         Element.Events.mouseenter = {
4595                 base: 'mouseover',
4596                 condition: check
4597         };
4599         Element.Events.mouseleave = {
4600                 base: 'mouseout',
4601                 condition: check
4602         };
4605 /*<ltIE9>*/
4606 if (!window.addEventListener){
4607         Element.NativeEvents.propertychange = 2;
4608         Element.Events.change = {
4609                 base: function(){
4610                         var type = this.type;
4611                         return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change';
4612                 },
4613                 condition: function(event){
4614                         return event.type != 'propertychange' || event.event.propertyName == 'checked';
4615                 }
4616         };
4618 /*</ltIE9>*/
4620 //<1.2compat>
4622 Element.Events = new Hash(Element.Events);
4624 //</1.2compat>
4626 })();
4631 name: Element.Delegation
4633 description: Extends the Element native object to include the delegate method for more efficient event management.
4635 license: MIT-style license.
4637 requires: [Element.Event]
4639 provides: [Element.Delegation]
4644 (function(){
4646 var eventListenerSupport = !!window.addEventListener;
4648 Element.NativeEvents.focusin = Element.NativeEvents.focusout = 2;
4650 var bubbleUp = function(self, match, fn, event, target){
4651         while (target && target != self){
4652                 if (match(target, event)) return fn.call(target, event, target);
4653                 target = document.id(target.parentNode);
4654         }
4657 var map = {
4658         mouseenter: {
4659                 base: 'mouseover',
4660                 condition: Element.MouseenterCheck
4661         },
4662         mouseleave: {
4663                 base: 'mouseout',
4664                 condition: Element.MouseenterCheck
4665         },
4666         focus: {
4667                 base: 'focus' + (eventListenerSupport ? '' : 'in'),
4668                 capture: true
4669         },
4670         blur: {
4671                 base: eventListenerSupport ? 'blur' : 'focusout',
4672                 capture: true
4673         }
4676 /*<ltIE9>*/
4677 var _key = '$delegation:';
4678 var formObserver = function(type){
4680         return {
4682                 base: 'focusin',
4684                 remove: function(self, uid){
4685                         var list = self.retrieve(_key + type + 'listeners', {})[uid];
4686                         if (list && list.forms) for (var i = list.forms.length; i--;){
4687                                 list.forms[i].removeEvent(type, list.fns[i]);
4688                         }
4689                 },
4691                 listen: function(self, match, fn, event, target, uid){
4692                         var form = (target.get('tag') == 'form') ? target : event.target.getParent('form');
4693                         if (!form) return;
4695                         var listeners = self.retrieve(_key + type + 'listeners', {}),
4696                                 listener = listeners[uid] || {forms: [], fns: []},
4697                                 forms = listener.forms, fns = listener.fns;
4699                         if (forms.indexOf(form) != -1) return;
4700                         forms.push(form);
4702                         var _fn = function(event){
4703                                 bubbleUp(self, match, fn, event, target);
4704                         };
4705                         form.addEvent(type, _fn);
4706                         fns.push(_fn);
4708                         listeners[uid] = listener;
4709                         self.store(_key + type + 'listeners', listeners);
4710                 }
4711         };
4714 var inputObserver = function(type){
4715         return {
4716                 base: 'focusin',
4717                 listen: function(self, match, fn, event, target){
4718                         var events = {blur: function(){
4719                                 this.removeEvents(events);
4720                         }};
4721                         events[type] = function(event){
4722                                 bubbleUp(self, match, fn, event, target);
4723                         };
4724                         event.target.addEvents(events);
4725                 }
4726         };
4729 if (!eventListenerSupport) Object.append(map, {
4730         submit: formObserver('submit'),
4731         reset: formObserver('reset'),
4732         change: inputObserver('change'),
4733         select: inputObserver('select')
4735 /*</ltIE9>*/
4737 var proto = Element.prototype,
4738         addEvent = proto.addEvent,
4739         removeEvent = proto.removeEvent;
4741 var relay = function(old, method){
4742         return function(type, fn, useCapture){
4743                 if (type.indexOf(':relay') == -1) return old.call(this, type, fn, useCapture);
4744                 var parsed = Slick.parse(type).expressions[0][0];
4745                 if (parsed.pseudos[0].key != 'relay') return old.call(this, type, fn, useCapture);
4746                 var newType = parsed.tag;
4747                 parsed.pseudos.slice(1).each(function(pseudo){
4748                         newType += ':' + pseudo.key + (pseudo.value ? '(' + pseudo.value + ')' : '');
4749                 });
4750                 old.call(this, type, fn);
4751                 return method.call(this, newType, parsed.pseudos[0].value, fn);
4752         };
4755 var delegation = {
4757         addEvent: function(type, match, fn){
4758                 var storage = this.retrieve('$delegates', {}), stored = storage[type];
4759                 if (stored) for (var _uid in stored){
4760                         if (stored[_uid].fn == fn && stored[_uid].match == match) return this;
4761                 }
4763                 var _type = type, _match = match, _fn = fn, _map = map[type] || {};
4764                 type = _map.base || _type;
4766                 match = function(target){
4767                         return Slick.match(target, _match);
4768                 };
4770                 var elementEvent = Element.Events[_type];
4771                 if (_map.condition || elementEvent && elementEvent.condition){
4772                         var __match = match, condition = _map.condition || elementEvent.condition;
4773                         match = function(target, event){
4774                                 return __match(target, event) && condition.call(target, event, type);
4775                         };
4776                 }
4778                 var self = this, uid = String.uniqueID();
4779                 var delegator = _map.listen ? function(event, target){
4780                         if (!target && event && event.target) target = event.target;
4781                         if (target) _map.listen(self, match, fn, event, target, uid);
4782                 } : function(event, target){
4783                         if (!target && event && event.target) target = event.target;
4784                         if (target) bubbleUp(self, match, fn, event, target);
4785                 };
4787                 if (!stored) stored = {};
4788                 stored[uid] = {
4789                         match: _match,
4790                         fn: _fn,
4791                         delegator: delegator
4792                 };
4793                 storage[_type] = stored;
4794                 return addEvent.call(this, type, delegator, _map.capture);
4795         },
4797         removeEvent: function(type, match, fn, _uid){
4798                 var storage = this.retrieve('$delegates', {}), stored = storage[type];
4799                 if (!stored) return this;
4801                 if (_uid){
4802                         var _type = type, delegator = stored[_uid].delegator, _map = map[type] || {};
4803                         type = _map.base || _type;
4804                         if (_map.remove) _map.remove(this, _uid);
4805                         delete stored[_uid];
4806                         storage[_type] = stored;
4807                         return removeEvent.call(this, type, delegator, _map.capture);
4808                 }
4810                 var __uid, s;
4811                 if (fn) for (__uid in stored){
4812                         s = stored[__uid];
4813                         if (s.match == match && s.fn == fn) return delegation.removeEvent.call(this, type, match, fn, __uid);
4814                 } else for (__uid in stored){
4815                         s = stored[__uid];
4816                         if (s.match == match) delegation.removeEvent.call(this, type, match, s.fn, __uid);
4817                 }
4818                 return this;
4819         }
4823 [Element, Window, Document].invoke('implement', {
4824         addEvent: relay(addEvent, delegation.addEvent),
4825         removeEvent: relay(removeEvent, delegation.removeEvent)
4828 })();
4833 name: Element.Dimensions
4835 description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
4837 license: MIT-style license.
4839 credits:
4840   - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
4841   - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
4843 requires: [Element, Element.Style]
4845 provides: [Element.Dimensions]
4850 (function(){
4852 var element = document.createElement('div'),
4853         child = document.createElement('div');
4854 element.style.height = '0';
4855 element.appendChild(child);
4856 var brokenOffsetParent = (child.offsetParent === element);
4857 element = child = null;
4859 var isOffset = function(el){
4860         return styleString(el, 'position') != 'static' || isBody(el);
4863 var isOffsetStatic = function(el){
4864         return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
4867 Element.implement({
4869         scrollTo: function(x, y){
4870                 if (isBody(this)){
4871                         this.getWindow().scrollTo(x, y);
4872                 } else {
4873                         this.scrollLeft = x;
4874                         this.scrollTop = y;
4875                 }
4876                 return this;
4877         },
4879         getSize: function(){
4880                 if (isBody(this)) return this.getWindow().getSize();
4881                 return {x: this.offsetWidth, y: this.offsetHeight};
4882         },
4884         getScrollSize: function(){
4885                 if (isBody(this)) return this.getWindow().getScrollSize();
4886                 return {x: this.scrollWidth, y: this.scrollHeight};
4887         },
4889         getScroll: function(){
4890                 if (isBody(this)) return this.getWindow().getScroll();
4891                 return {x: this.scrollLeft, y: this.scrollTop};
4892         },
4894         getScrolls: function(){
4895                 var element = this.parentNode, position = {x: 0, y: 0};
4896                 while (element && !isBody(element)){
4897                         position.x += element.scrollLeft;
4898                         position.y += element.scrollTop;
4899                         element = element.parentNode;
4900                 }
4901                 return position;
4902         },
4904         getOffsetParent: brokenOffsetParent ? function(){
4905                 var element = this;
4906                 if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
4908                 var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
4909                 while ((element = element.parentNode)){
4910                         if (isOffsetCheck(element)) return element;
4911                 }
4912                 return null;
4913         } : function(){
4914                 var element = this;
4915                 if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
4917                 try {
4918                         return element.offsetParent;
4919                 } catch(e) {}
4920                 return null;
4921         },
4923         getOffsets: function(){
4924                 var hasGetBoundingClientRect = this.getBoundingClientRect;
4925 //<1.4compat>
4926                 hasGetBoundingClientRect = hasGetBoundingClientRect && !Browser.Platform.ios
4927 //</1.4compat>
4928                 if (hasGetBoundingClientRect){
4929                         var bound = this.getBoundingClientRect(),
4930                                 html = document.id(this.getDocument().documentElement),
4931                                 htmlScroll = html.getScroll(),
4932                                 elemScrolls = this.getScrolls(),
4933                                 isFixed = (styleString(this, 'position') == 'fixed');
4935                         return {
4936                                 x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
4937                                 y: bound.top.toInt()  + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
4938                         };
4939                 }
4941                 var element = this, position = {x: 0, y: 0};
4942                 if (isBody(this)) return position;
4944                 while (element && !isBody(element)){
4945                         position.x += element.offsetLeft;
4946                         position.y += element.offsetTop;
4947 //<1.4compat>
4948                         if (Browser.firefox){
4949                                 if (!borderBox(element)){
4950                                         position.x += leftBorder(element);
4951                                         position.y += topBorder(element);
4952                                 }
4953                                 var parent = element.parentNode;
4954                                 if (parent && styleString(parent, 'overflow') != 'visible'){
4955                                         position.x += leftBorder(parent);
4956                                         position.y += topBorder(parent);
4957                                 }
4958                         } else if (element != this && Browser.safari){
4959                                 position.x += leftBorder(element);
4960                                 position.y += topBorder(element);
4961                         }
4962 //</1.4compat>
4963                         element = element.offsetParent;
4964                 }
4965 //<1.4compat>
4966                 if (Browser.firefox && !borderBox(this)){
4967                         position.x -= leftBorder(this);
4968                         position.y -= topBorder(this);
4969                 }
4970 //</1.4compat>
4971                 return position;
4972         },
4974         getPosition: function(relative){
4975                 var offset = this.getOffsets(),
4976                         scroll = this.getScrolls();
4977                 var position = {
4978                         x: offset.x - scroll.x,
4979                         y: offset.y - scroll.y
4980                 };
4982                 if (relative && (relative = document.id(relative))){
4983                         var relativePosition = relative.getPosition();
4984                         return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
4985                 }
4986                 return position;
4987         },
4989         getCoordinates: function(element){
4990                 if (isBody(this)) return this.getWindow().getCoordinates();
4991                 var position = this.getPosition(element),
4992                         size = this.getSize();
4993                 var obj = {
4994                         left: position.x,
4995                         top: position.y,
4996                         width: size.x,
4997                         height: size.y
4998                 };
4999                 obj.right = obj.left + obj.width;
5000                 obj.bottom = obj.top + obj.height;
5001                 return obj;
5002         },
5004         computePosition: function(obj){
5005                 return {
5006                         left: obj.x - styleNumber(this, 'margin-left'),
5007                         top: obj.y - styleNumber(this, 'margin-top')
5008                 };
5009         },
5011         setPosition: function(obj){
5012                 return this.setStyles(this.computePosition(obj));
5013         }
5018 [Document, Window].invoke('implement', {
5020         getSize: function(){
5021                 var doc = getCompatElement(this);
5022                 return {x: doc.clientWidth, y: doc.clientHeight};
5023         },
5025         getScroll: function(){
5026                 var win = this.getWindow(), doc = getCompatElement(this);
5027                 return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
5028         },
5030         getScrollSize: function(){
5031                 var doc = getCompatElement(this),
5032                         min = this.getSize(),
5033                         body = this.getDocument().body;
5035                 return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)};
5036         },
5038         getPosition: function(){
5039                 return {x: 0, y: 0};
5040         },
5042         getCoordinates: function(){
5043                 var size = this.getSize();
5044                 return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
5045         }
5049 // private methods
5051 var styleString = Element.getComputedStyle;
5053 function styleNumber(element, style){
5054         return styleString(element, style).toInt() || 0;
5057 function borderBox(element){
5058         return styleString(element, '-moz-box-sizing') == 'border-box';
5061 function topBorder(element){
5062         return styleNumber(element, 'border-top-width');
5065 function leftBorder(element){
5066         return styleNumber(element, 'border-left-width');
5069 function isBody(element){
5070         return (/^(?:body|html)$/i).test(element.tagName);
5073 function getCompatElement(element){
5074         var doc = element.getDocument();
5075         return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
5078 })();
5080 //aliases
5081 Element.alias({position: 'setPosition'}); //compatability
5083 [Window, Document, Element].invoke('implement', {
5085         getHeight: function(){
5086                 return this.getSize().y;
5087         },
5089         getWidth: function(){
5090                 return this.getSize().x;
5091         },
5093         getScrollTop: function(){
5094                 return this.getScroll().y;
5095         },
5097         getScrollLeft: function(){
5098                 return this.getScroll().x;
5099         },
5101         getScrollHeight: function(){
5102                 return this.getScrollSize().y;
5103         },
5105         getScrollWidth: function(){
5106                 return this.getScrollSize().x;
5107         },
5109         getTop: function(){
5110                 return this.getPosition().y;
5111         },
5113         getLeft: function(){
5114                 return this.getPosition().x;
5115         }
5122 name: Fx
5124 description: Contains the basic animation logic to be extended by all other Fx Classes.
5126 license: MIT-style license.
5128 requires: [Chain, Events, Options]
5130 provides: Fx
5135 (function(){
5137 var Fx = this.Fx = new Class({
5139         Implements: [Chain, Events, Options],
5141         options: {
5142                 /*
5143                 onStart: nil,
5144                 onCancel: nil,
5145                 onComplete: nil,
5146                 */
5147                 fps: 60,
5148                 unit: false,
5149                 duration: 500,
5150                 frames: null,
5151                 frameSkip: true,
5152                 link: 'ignore'
5153         },
5155         initialize: function(options){
5156                 this.subject = this.subject || this;
5157                 this.setOptions(options);
5158         },
5160         getTransition: function(){
5161                 return function(p){
5162                         return -(Math.cos(Math.PI * p) - 1) / 2;
5163                 };
5164         },
5166         step: function(now){
5167                 if (this.options.frameSkip){
5168                         var diff = (this.time != null) ? (now - this.time) : 0, frames = diff / this.frameInterval;
5169                         this.time = now;
5170                         this.frame += frames;
5171                 } else {
5172                         this.frame++;
5173                 }
5175                 if (this.frame < this.frames){
5176                         var delta = this.transition(this.frame / this.frames);
5177                         this.set(this.compute(this.from, this.to, delta));
5178                 } else {
5179                         this.frame = this.frames;
5180                         this.set(this.compute(this.from, this.to, 1));
5181                         this.stop();
5182                 }
5183         },
5185         set: function(now){
5186                 return now;
5187         },
5189         compute: function(from, to, delta){
5190                 return Fx.compute(from, to, delta);
5191         },
5193         check: function(){
5194                 if (!this.isRunning()) return true;
5195                 switch (this.options.link){
5196                         case 'cancel': this.cancel(); return true;
5197                         case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
5198                 }
5199                 return false;
5200         },
5202         start: function(from, to){
5203                 if (!this.check(from, to)) return this;
5204                 this.from = from;
5205                 this.to = to;
5206                 this.frame = (this.options.frameSkip) ? 0 : -1;
5207                 this.time = null;
5208                 this.transition = this.getTransition();
5209                 var frames = this.options.frames, fps = this.options.fps, duration = this.options.duration;
5210                 this.duration = Fx.Durations[duration] || duration.toInt();
5211                 this.frameInterval = 1000 / fps;
5212                 this.frames = frames || Math.round(this.duration / this.frameInterval);
5213                 this.fireEvent('start', this.subject);
5214                 pushInstance.call(this, fps);
5215                 return this;
5216         },
5218         stop: function(){
5219                 if (this.isRunning()){
5220                         this.time = null;
5221                         pullInstance.call(this, this.options.fps);
5222                         if (this.frames == this.frame){
5223                                 this.fireEvent('complete', this.subject);
5224                                 if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
5225                         } else {
5226                                 this.fireEvent('stop', this.subject);
5227                         }
5228                 }
5229                 return this;
5230         },
5232         cancel: function(){
5233                 if (this.isRunning()){
5234                         this.time = null;
5235                         pullInstance.call(this, this.options.fps);
5236                         this.frame = this.frames;
5237                         this.fireEvent('cancel', this.subject).clearChain();
5238                 }
5239                 return this;
5240         },
5242         pause: function(){
5243                 if (this.isRunning()){
5244                         this.time = null;
5245                         pullInstance.call(this, this.options.fps);
5246                 }
5247                 return this;
5248         },
5250         resume: function(){
5251                 if (this.isPaused()) pushInstance.call(this, this.options.fps);
5252                 return this;
5253         },
5255         isRunning: function(){
5256                 var list = instances[this.options.fps];
5257                 return list && list.contains(this);
5258         },
5260         isPaused: function(){
5261                 return (this.frame < this.frames) && !this.isRunning();
5262         }
5266 Fx.compute = function(from, to, delta){
5267         return (to - from) * delta + from;
5270 Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
5272 // global timers
5274 var instances = {}, timers = {};
5276 var loop = function(){
5277         var now = Date.now();
5278         for (var i = this.length; i--;){
5279                 var instance = this[i];
5280                 if (instance) instance.step(now);
5281         }
5284 var pushInstance = function(fps){
5285         var list = instances[fps] || (instances[fps] = []);
5286         list.push(this);
5287         if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list);
5290 var pullInstance = function(fps){
5291         var list = instances[fps];
5292         if (list){
5293                 list.erase(this);
5294                 if (!list.length && timers[fps]){
5295                         delete instances[fps];
5296                         timers[fps] = clearInterval(timers[fps]);
5297                 }
5298         }
5301 })();
5306 name: Fx.CSS
5308 description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
5310 license: MIT-style license.
5312 requires: [Fx, Element.Style]
5314 provides: Fx.CSS
5319 Fx.CSS = new Class({
5321         Extends: Fx,
5323         //prepares the base from/to object
5325         prepare: function(element, property, values){
5326                 values = Array.from(values);
5327                 var from = values[0], to = values[1];
5328                 if (to == null){
5329                         to = from;
5330                         from = element.getStyle(property);
5331                         var unit = this.options.unit;
5332                         // adapted from: https://github.com/ryanmorr/fx/blob/master/fx.js#L299
5333                         if (unit && from && typeof from == 'string' && from.slice(-unit.length) != unit && parseFloat(from) != 0){
5334                                 element.setStyle(property, to + unit);
5335                                 var value = element.getComputedStyle(property);
5336                                 // IE and Opera support pixelLeft or pixelWidth
5337                                 if (!(/px$/.test(value))){
5338                                         value = element.style[('pixel-' + property).camelCase()];
5339                                         if (value == null){
5340                                                 // adapted from Dean Edwards' http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
5341                                                 var left = element.style.left;
5342                                                 element.style.left = to + unit;
5343                                                 value = element.style.pixelLeft;
5344                                                 element.style.left = left;
5345                                         }
5346                                 }
5347                                 from = (to || 1) / (parseFloat(value) || 1) * (parseFloat(from) || 0);
5348                                 element.setStyle(property, from + unit);
5349                         }
5350                 }
5351                 return {from: this.parse(from), to: this.parse(to)};
5352         },
5354         //parses a value into an array
5356         parse: function(value){
5357                 value = Function.from(value)();
5358                 value = (typeof value == 'string') ? value.split(' ') : Array.from(value);
5359                 return value.map(function(val){
5360                         val = String(val);
5361                         var found = false;
5362                         Object.each(Fx.CSS.Parsers, function(parser, key){
5363                                 if (found) return;
5364                                 var parsed = parser.parse(val);
5365                                 if (parsed || parsed === 0) found = {value: parsed, parser: parser};
5366                         });
5367                         found = found || {value: val, parser: Fx.CSS.Parsers.String};
5368                         return found;
5369                 });
5370         },
5372         //computes by a from and to prepared objects, using their parsers.
5374         compute: function(from, to, delta){
5375                 var computed = [];
5376                 (Math.min(from.length, to.length)).times(function(i){
5377                         computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
5378                 });
5379                 computed.$family = Function.from('fx:css:value');
5380                 return computed;
5381         },
5383         //serves the value as settable
5385         serve: function(value, unit){
5386                 if (typeOf(value) != 'fx:css:value') value = this.parse(value);
5387                 var returned = [];
5388                 value.each(function(bit){
5389                         returned = returned.concat(bit.parser.serve(bit.value, unit));
5390                 });
5391                 return returned;
5392         },
5394         //renders the change to an element
5396         render: function(element, property, value, unit){
5397                 element.setStyle(property, this.serve(value, unit));
5398         },
5400         //searches inside the page css to find the values for a selector
5402         search: function(selector){
5403                 if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
5404                 var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
5406                 var searchStyles = function(rules){
5407                         Array.each(rules, function(rule, i){
5408                                 if (rule.media){
5409                                         searchStyles(rule.rules || rule.cssRules);
5410                                         return;
5411                                 }
5412                                 if (!rule.style) return;
5413                                 var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
5414                                         return m.toLowerCase();
5415                                 }) : null;
5416                                 if (!selectorText || !selectorTest.test(selectorText)) return;
5417                                 Object.each(Element.Styles, function(value, style){
5418                                         if (!rule.style[style] || Element.ShortStyles[style]) return;
5419                                         value = String(rule.style[style]);
5420                                         to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
5421                                 });
5422                         });
5423                 };
5425                 Array.each(document.styleSheets, function(sheet, j){
5426                         var href = sheet.href;
5427                         if (href && href.indexOf('://') > -1 && href.indexOf(document.domain) == -1) return;
5428                         var rules = sheet.rules || sheet.cssRules;
5429                         searchStyles(rules);
5430                 });
5431                 return Fx.CSS.Cache[selector] = to;
5432         }
5436 Fx.CSS.Cache = {};
5438 Fx.CSS.Parsers = {
5440         Color: {
5441                 parse: function(value){
5442                         if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
5443                         return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
5444                 },
5445                 compute: function(from, to, delta){
5446                         return from.map(function(value, i){
5447                                 return Math.round(Fx.compute(from[i], to[i], delta));
5448                         });
5449                 },
5450                 serve: function(value){
5451                         return value.map(Number);
5452                 }
5453         },
5455         Number: {
5456                 parse: parseFloat,
5457                 compute: Fx.compute,
5458                 serve: function(value, unit){
5459                         return (unit) ? value + unit : value;
5460                 }
5461         },
5463         String: {
5464                 parse: Function.from(false),
5465                 compute: function(zero, one){
5466                         return one;
5467                 },
5468                 serve: function(zero){
5469                         return zero;
5470                 }
5471         }
5475 //<1.2compat>
5477 Fx.CSS.Parsers = new Hash(Fx.CSS.Parsers);
5479 //</1.2compat>
5484 name: Fx.Tween
5486 description: Formerly Fx.Style, effect to transition any CSS property for an element.
5488 license: MIT-style license.
5490 requires: Fx.CSS
5492 provides: [Fx.Tween, Element.fade, Element.highlight]
5497 Fx.Tween = new Class({
5499         Extends: Fx.CSS,
5501         initialize: function(element, options){
5502                 this.element = this.subject = document.id(element);
5503                 this.parent(options);
5504         },
5506         set: function(property, now){
5507                 if (arguments.length == 1){
5508                         now = property;
5509                         property = this.property || this.options.property;
5510                 }
5511                 this.render(this.element, property, now, this.options.unit);
5512                 return this;
5513         },
5515         start: function(property, from, to){
5516                 if (!this.check(property, from, to)) return this;
5517                 var args = Array.flatten(arguments);
5518                 this.property = this.options.property || args.shift();
5519                 var parsed = this.prepare(this.element, this.property, args);
5520                 return this.parent(parsed.from, parsed.to);
5521         }
5525 Element.Properties.tween = {
5527         set: function(options){
5528                 this.get('tween').cancel().setOptions(options);
5529                 return this;
5530         },
5532         get: function(){
5533                 var tween = this.retrieve('tween');
5534                 if (!tween){
5535                         tween = new Fx.Tween(this, {link: 'cancel'});
5536                         this.store('tween', tween);
5537                 }
5538                 return tween;
5539         }
5543 Element.implement({
5545         tween: function(property, from, to){
5546                 this.get('tween').start(property, from, to);
5547                 return this;
5548         },
5550         fade: function(how){
5551                 var fade = this.get('tween'), method, args = ['opacity'].append(arguments), toggle;
5552                 if (args[1] == null) args[1] = 'toggle';
5553                 switch (args[1]){
5554                         case 'in': method = 'start'; args[1] = 1; break;
5555                         case 'out': method = 'start'; args[1] = 0; break;
5556                         case 'show': method = 'set'; args[1] = 1; break;
5557                         case 'hide': method = 'set'; args[1] = 0; break;
5558                         case 'toggle':
5559                                 var flag = this.retrieve('fade:flag', this.getStyle('opacity') == 1);
5560                                 method = 'start';
5561                                 args[1] = flag ? 0 : 1;
5562                                 this.store('fade:flag', !flag);
5563                                 toggle = true;
5564                         break;
5565                         default: method = 'start';
5566                 }
5567                 if (!toggle) this.eliminate('fade:flag');
5568                 fade[method].apply(fade, args);
5569                 var to = args[args.length - 1];
5570                 if (method == 'set' || to != 0) this.setStyle('visibility', to == 0 ? 'hidden' : 'visible');
5571                 else fade.chain(function(){
5572                         this.element.setStyle('visibility', 'hidden');
5573                         this.callChain();
5574                 });
5575                 return this;
5576         },
5578         highlight: function(start, end){
5579                 if (!end){
5580                         end = this.retrieve('highlight:original', this.getStyle('background-color'));
5581                         end = (end == 'transparent') ? '#fff' : end;
5582                 }
5583                 var tween = this.get('tween');
5584                 tween.start('background-color', start || '#ffff88', end).chain(function(){
5585                         this.setStyle('background-color', this.retrieve('highlight:original'));
5586                         tween.callChain();
5587                 }.bind(this));
5588                 return this;
5589         }
5596 name: Fx.Morph
5598 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.
5600 license: MIT-style license.
5602 requires: Fx.CSS
5604 provides: Fx.Morph
5609 Fx.Morph = new Class({
5611         Extends: Fx.CSS,
5613         initialize: function(element, options){
5614                 this.element = this.subject = document.id(element);
5615                 this.parent(options);
5616         },
5618         set: function(now){
5619                 if (typeof now == 'string') now = this.search(now);
5620                 for (var p in now) this.render(this.element, p, now[p], this.options.unit);
5621                 return this;
5622         },
5624         compute: function(from, to, delta){
5625                 var now = {};
5626                 for (var p in from) now[p] = this.parent(from[p], to[p], delta);
5627                 return now;
5628         },
5630         start: function(properties){
5631                 if (!this.check(properties)) return this;
5632                 if (typeof properties == 'string') properties = this.search(properties);
5633                 var from = {}, to = {};
5634                 for (var p in properties){
5635                         var parsed = this.prepare(this.element, p, properties[p]);
5636                         from[p] = parsed.from;
5637                         to[p] = parsed.to;
5638                 }
5639                 return this.parent(from, to);
5640         }
5644 Element.Properties.morph = {
5646         set: function(options){
5647                 this.get('morph').cancel().setOptions(options);
5648                 return this;
5649         },
5651         get: function(){
5652                 var morph = this.retrieve('morph');
5653                 if (!morph){
5654                         morph = new Fx.Morph(this, {link: 'cancel'});
5655                         this.store('morph', morph);
5656                 }
5657                 return morph;
5658         }
5662 Element.implement({
5664         morph: function(props){
5665                 this.get('morph').start(props);
5666                 return this;
5667         }
5674 name: Fx.Transitions
5676 description: Contains a set of advanced transitions to be used with any of the Fx Classes.
5678 license: MIT-style license.
5680 credits:
5681   - Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
5683 requires: Fx
5685 provides: Fx.Transitions
5690 Fx.implement({
5692         getTransition: function(){
5693                 var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
5694                 if (typeof trans == 'string'){
5695                         var data = trans.split(':');
5696                         trans = Fx.Transitions;
5697                         trans = trans[data[0]] || trans[data[0].capitalize()];
5698                         if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
5699                 }
5700                 return trans;
5701         }
5705 Fx.Transition = function(transition, params){
5706         params = Array.from(params);
5707         var easeIn = function(pos){
5708                 return transition(pos, params);
5709         };
5710         return Object.append(easeIn, {
5711                 easeIn: easeIn,
5712                 easeOut: function(pos){
5713                         return 1 - transition(1 - pos, params);
5714                 },
5715                 easeInOut: function(pos){
5716                         return (pos <= 0.5 ? transition(2 * pos, params) : (2 - transition(2 * (1 - pos), params))) / 2;
5717                 }
5718         });
5721 Fx.Transitions = {
5723         linear: function(zero){
5724                 return zero;
5725         }
5729 //<1.2compat>
5731 Fx.Transitions = new Hash(Fx.Transitions);
5733 //</1.2compat>
5735 Fx.Transitions.extend = function(transitions){
5736         for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
5739 Fx.Transitions.extend({
5741         Pow: function(p, x){
5742                 return Math.pow(p, x && x[0] || 6);
5743         },
5745         Expo: function(p){
5746                 return Math.pow(2, 8 * (p - 1));
5747         },
5749         Circ: function(p){
5750                 return 1 - Math.sin(Math.acos(p));
5751         },
5753         Sine: function(p){
5754                 return 1 - Math.cos(p * Math.PI / 2);
5755         },
5757         Back: function(p, x){
5758                 x = x && x[0] || 1.618;
5759                 return Math.pow(p, 2) * ((x + 1) * p - x);
5760         },
5762         Bounce: function(p){
5763                 var value;
5764                 for (var a = 0, b = 1; 1; a += b, b /= 2){
5765                         if (p >= (7 - 4 * a) / 11){
5766                                 value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
5767                                 break;
5768                         }
5769                 }
5770                 return value;
5771         },
5773         Elastic: function(p, x){
5774                 return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && x[0] || 1) / 3);
5775         }
5779 ['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
5780         Fx.Transitions[transition] = new Fx.Transition(function(p){
5781                 return Math.pow(p, i + 2);
5782         });
5788 name: Request
5790 description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
5792 license: MIT-style license.
5794 requires: [Object, Element, Chain, Events, Options, Browser]
5796 provides: Request
5801 (function(){
5803 var empty = function(){},
5804         progressSupport = ('onprogress' in new Browser.Request);
5806 var Request = this.Request = new Class({
5808         Implements: [Chain, Events, Options],
5810         options: {/*
5811                 onRequest: function(){},
5812                 onLoadstart: function(event, xhr){},
5813                 onProgress: function(event, xhr){},
5814                 onComplete: function(){},
5815                 onCancel: function(){},
5816                 onSuccess: function(responseText, responseXML){},
5817                 onFailure: function(xhr){},
5818                 onException: function(headerName, value){},
5819                 onTimeout: function(){},
5820                 user: '',
5821                 password: '',*/
5822                 url: '',
5823                 data: '',
5824                 headers: {
5825                         'X-Requested-With': 'XMLHttpRequest',
5826                         'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
5827                 },
5828                 async: true,
5829                 format: false,
5830                 method: 'post',
5831                 link: 'ignore',
5832                 isSuccess: null,
5833                 emulation: true,
5834                 urlEncoded: true,
5835                 encoding: 'utf-8',
5836                 evalScripts: false,
5837                 evalResponse: false,
5838                 timeout: 0,
5839                 noCache: false
5840         },
5842         initialize: function(options){
5843                 this.xhr = new Browser.Request();
5844                 this.setOptions(options);
5845                 this.headers = this.options.headers;
5846         },
5848         onStateChange: function(){
5849                 var xhr = this.xhr;
5850                 if (xhr.readyState != 4 || !this.running) return;
5851                 this.running = false;
5852                 this.status = 0;
5853                 Function.attempt(function(){
5854                         var status = xhr.status;
5855                         this.status = (status == 1223) ? 204 : status;
5856                 }.bind(this));
5857                 xhr.onreadystatechange = empty;
5858                 if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
5859                 clearTimeout(this.timer);
5861                 this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML};
5862                 if (this.options.isSuccess.call(this, this.status))
5863                         this.success(this.response.text, this.response.xml);
5864                 else
5865                         this.failure();
5866         },
5868         isSuccess: function(){
5869                 var status = this.status;
5870                 return (status >= 200 && status < 300);
5871         },
5873         isRunning: function(){
5874                 return !!this.running;
5875         },
5877         processScripts: function(text){
5878                 if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.exec(text);
5879                 return text.stripScripts(this.options.evalScripts);
5880         },
5882         success: function(text, xml){
5883                 this.onSuccess(this.processScripts(text), xml);
5884         },
5886         onSuccess: function(){
5887                 this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
5888         },
5890         failure: function(){
5891                 this.onFailure();
5892         },
5894         onFailure: function(){
5895                 this.fireEvent('complete').fireEvent('failure', this.xhr);
5896         },
5898         loadstart: function(event){
5899                 this.fireEvent('loadstart', [event, this.xhr]);
5900         },
5902         progress: function(event){
5903                 this.fireEvent('progress', [event, this.xhr]);
5904         },
5906         timeout: function(){
5907                 this.fireEvent('timeout', this.xhr);
5908         },
5910         setHeader: function(name, value){
5911                 this.headers[name] = value;
5912                 return this;
5913         },
5915         getHeader: function(name){
5916                 return Function.attempt(function(){
5917                         return this.xhr.getResponseHeader(name);
5918                 }.bind(this));
5919         },
5921         check: function(){
5922                 if (!this.running) return true;
5923                 switch (this.options.link){
5924                         case 'cancel': this.cancel(); return true;
5925                         case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
5926                 }
5927                 return false;
5928         },
5930         send: function(options){
5931                 if (!this.check(options)) return this;
5933                 this.options.isSuccess = this.options.isSuccess || this.isSuccess;
5934                 this.running = true;
5936                 var type = typeOf(options);
5937                 if (type == 'string' || type == 'element') options = {data: options};
5939                 var old = this.options;
5940                 options = Object.append({data: old.data, url: old.url, method: old.method}, options);
5941                 var data = options.data, url = String(options.url), method = options.method.toLowerCase();
5943                 switch (typeOf(data)){
5944                         case 'element': data = document.id(data).toQueryString(); break;
5945                         case 'object': case 'hash': data = Object.toQueryString(data);
5946                 }
5948                 if (this.options.format){
5949                         var format = 'format=' + this.options.format;
5950                         data = (data) ? format + '&' + data : format;
5951                 }
5953                 if (this.options.emulation && !['get', 'post'].contains(method)){
5954                         var _method = '_method=' + method;
5955                         data = (data) ? _method + '&' + data : _method;
5956                         method = 'post';
5957                 }
5959                 if (this.options.urlEncoded && ['post', 'put'].contains(method)){
5960                         var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
5961                         this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding;
5962                 }
5964                 if (!url) url = document.location.pathname;
5966                 var trimPosition = url.lastIndexOf('/');
5967                 if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
5969                 if (this.options.noCache)
5970                         url += (url.indexOf('?') > -1 ? '&' : '?') + String.uniqueID();
5972                 if (data && (method == 'get' || method == 'delete')){
5973                         url += (url.indexOf('?') > -1 ? '&' : '?') + data;
5974                         data = null;
5975                 }
5977                 var xhr = this.xhr;
5978                 if (progressSupport){
5979                         xhr.onloadstart = this.loadstart.bind(this);
5980                         xhr.onprogress = this.progress.bind(this);
5981                 }
5983                 xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password);
5984                 if (this.options.user && 'withCredentials' in xhr) xhr.withCredentials = true;
5986                 xhr.onreadystatechange = this.onStateChange.bind(this);
5988                 Object.each(this.headers, function(value, key){
5989                         try {
5990                                 xhr.setRequestHeader(key, value);
5991                         } catch (e){
5992                                 this.fireEvent('exception', [key, value]);
5993                         }
5994                 }, this);
5996                 this.fireEvent('request');
5997                 xhr.send(data);
5998                 if (!this.options.async) this.onStateChange();
5999                 else if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
6000                 return this;
6001         },
6003         cancel: function(){
6004                 if (!this.running) return this;
6005                 this.running = false;
6006                 var xhr = this.xhr;
6007                 xhr.abort();
6008                 clearTimeout(this.timer);
6009                 xhr.onreadystatechange = empty;
6010                 if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
6011                 this.xhr = new Browser.Request();
6012                 this.fireEvent('cancel');
6013                 return this;
6014         }
6018 var methods = {};
6019 ['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
6020         methods[method] = function(data){
6021                 var object = {
6022                         method: method
6023                 };
6024                 if (data != null) object.data = data;
6025                 return this.send(object);
6026         };
6029 Request.implement(methods);
6031 Element.Properties.send = {
6033         set: function(options){
6034                 var send = this.get('send').cancel();
6035                 send.setOptions(options);
6036                 return this;
6037         },
6039         get: function(){
6040                 var send = this.retrieve('send');
6041                 if (!send){
6042                         send = new Request({
6043                                 data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
6044                         });
6045                         this.store('send', send);
6046                 }
6047                 return send;
6048         }
6052 Element.implement({
6054         send: function(url){
6055                 var sender = this.get('send');
6056                 sender.send({data: this, url: url || sender.options.url});
6057                 return this;
6058         }
6062 })();
6067 name: Request.HTML
6069 description: Extends the basic Request Class with additional methods for interacting with HTML responses.
6071 license: MIT-style license.
6073 requires: [Element, Request]
6075 provides: Request.HTML
6080 Request.HTML = new Class({
6082         Extends: Request,
6084         options: {
6085                 update: false,
6086                 append: false,
6087                 evalScripts: true,
6088                 filter: false,
6089                 headers: {
6090                         Accept: 'text/html, application/xml, text/xml, */*'
6091                 }
6092         },
6094         success: function(text){
6095                 var options = this.options, response = this.response;
6097                 response.html = text.stripScripts(function(script){
6098                         response.javascript = script;
6099                 });
6101                 var match = response.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
6102                 if (match) response.html = match[1];
6103                 var temp = new Element('div').set('html', response.html);
6105                 response.tree = temp.childNodes;
6106                 response.elements = temp.getElements(options.filter || '*');
6108                 if (options.filter) response.tree = response.elements;
6109                 if (options.update){
6110                         var update = document.id(options.update).empty();
6111                         if (options.filter) update.adopt(response.elements);
6112                         else update.set('html', response.html);
6113                 } else if (options.append){
6114                         var append = document.id(options.append);
6115                         if (options.filter) response.elements.reverse().inject(append);
6116                         else append.adopt(temp.getChildren());
6117                 }
6118                 if (options.evalScripts) Browser.exec(response.javascript);
6120                 this.onSuccess(response.tree, response.elements, response.html, response.javascript);
6121         }
6125 Element.Properties.load = {
6127         set: function(options){
6128                 var load = this.get('load').cancel();
6129                 load.setOptions(options);
6130                 return this;
6131         },
6133         get: function(){
6134                 var load = this.retrieve('load');
6135                 if (!load){
6136                         load = new Request.HTML({data: this, link: 'cancel', update: this, method: 'get'});
6137                         this.store('load', load);
6138                 }
6139                 return load;
6140         }
6144 Element.implement({
6146         load: function(){
6147                 this.get('load').send(Array.link(arguments, {data: Type.isObject, url: Type.isString}));
6148                 return this;
6149         }
6156 name: JSON
6158 description: JSON encoder and decoder.
6160 license: MIT-style license.
6162 SeeAlso: <http://www.json.org/>
6164 requires: [Array, String, Number, Function]
6166 provides: JSON
6171 if (typeof JSON == 'undefined') this.JSON = {};
6173 //<1.2compat>
6175 JSON = new Hash({
6176         stringify: JSON.stringify,
6177         parse: JSON.parse
6180 //</1.2compat>
6182 (function(){
6184 var special = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
6186 var escape = function(chr){
6187         return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
6190 JSON.validate = function(string){
6191         string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
6192                                         replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
6193                                         replace(/(?:^|:|,)(?:\s*\[)+/g, '');
6195         return (/^[\],:{}\s]*$/).test(string);
6198 JSON.encode = JSON.stringify ? function(obj){
6199         return JSON.stringify(obj);
6200 } : function(obj){
6201         if (obj && obj.toJSON) obj = obj.toJSON();
6203         switch (typeOf(obj)){
6204                 case 'string':
6205                         return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
6206                 case 'array':
6207                         return '[' + obj.map(JSON.encode).clean() + ']';
6208                 case 'object': case 'hash':
6209                         var string = [];
6210                         Object.each(obj, function(value, key){
6211                                 var json = JSON.encode(value);
6212                                 if (json) string.push(JSON.encode(key) + ':' + json);
6213                         });
6214                         return '{' + string + '}';
6215                 case 'number': case 'boolean': return '' + obj;
6216                 case 'null': return 'null';
6217         }
6219         return null;
6222 JSON.secure = true;
6223 //<1.4compat>
6224 JSON.secure = false;
6225 //</1.4compat>
6227 JSON.decode = function(string, secure){
6228         if (!string || typeOf(string) != 'string') return null;
6229     
6230         if (secure == null) secure = JSON.secure; 
6231         if (secure){
6232                 if (JSON.parse) return JSON.parse(string);
6233                 if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
6234         }
6236         return eval('(' + string + ')');
6239 })();
6244 name: Request.JSON
6246 description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
6248 license: MIT-style license.
6250 requires: [Request, JSON]
6252 provides: Request.JSON
6257 Request.JSON = new Class({
6259         Extends: Request,
6261         options: {
6262                 /*onError: function(text, error){},*/
6263                 secure: true
6264         },
6266         initialize: function(options){
6267                 this.parent(options);
6268                 Object.append(this.headers, {
6269                         'Accept': 'application/json',
6270                         'X-Request': 'JSON'
6271                 });
6272         },
6274         success: function(text){
6275                 var json;
6276                 try {
6277                         json = this.response.json = JSON.decode(text, this.options.secure);
6278                 } catch (error){
6279                         this.fireEvent('error', [text, error]);
6280                         return;
6281                 }
6282                 if (json == null) this.onFailure();
6283                 else this.onSuccess(json, text);
6284         }
6291 name: Cookie
6293 description: Class for creating, reading, and deleting browser Cookies.
6295 license: MIT-style license.
6297 credits:
6298   - Based on the functions by Peter-Paul Koch (http://quirksmode.org).
6300 requires: [Options, Browser]
6302 provides: Cookie
6307 var Cookie = new Class({
6309         Implements: Options,
6311         options: {
6312                 path: '/',
6313                 domain: false,
6314                 duration: false,
6315                 secure: false,
6316                 document: document,
6317                 encode: true
6318         },
6320         initialize: function(key, options){
6321                 this.key = key;
6322                 this.setOptions(options);
6323         },
6325         write: function(value){
6326                 if (this.options.encode) value = encodeURIComponent(value);
6327                 if (this.options.domain) value += '; domain=' + this.options.domain;
6328                 if (this.options.path) value += '; path=' + this.options.path;
6329                 if (this.options.duration){
6330                         var date = new Date();
6331                         date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
6332                         value += '; expires=' + date.toGMTString();
6333                 }
6334                 if (this.options.secure) value += '; secure';
6335                 this.options.document.cookie = this.key + '=' + value;
6336                 return this;
6337         },
6339         read: function(){
6340                 var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
6341                 return (value) ? decodeURIComponent(value[1]) : null;
6342         },
6344         dispose: function(){
6345                 new Cookie(this.key, Object.merge({}, this.options, {duration: -1})).write('');
6346                 return this;
6347         }
6351 Cookie.write = function(key, value, options){
6352         return new Cookie(key, options).write(value);
6355 Cookie.read = function(key){
6356         return new Cookie(key).read();
6359 Cookie.dispose = function(key, options){
6360         return new Cookie(key, options).dispose();
6366 name: DOMReady
6368 description: Contains the custom event domready.
6370 license: MIT-style license.
6372 requires: [Browser, Element, Element.Event]
6374 provides: [DOMReady, DomReady]
6379 (function(window, document){
6381 var ready,
6382         loaded,
6383         checks = [],
6384         shouldPoll,
6385         timer,
6386         testElement = document.createElement('div');
6388 var domready = function(){
6389         clearTimeout(timer);
6390         if (ready) return;
6391         Browser.loaded = ready = true;
6392         document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check);
6394         document.fireEvent('domready');
6395         window.fireEvent('domready');
6398 var check = function(){
6399         for (var i = checks.length; i--;) if (checks[i]()){
6400                 domready();
6401                 return true;
6402         }
6403         return false;
6406 var poll = function(){
6407         clearTimeout(timer);
6408         if (!check()) timer = setTimeout(poll, 10);
6411 document.addListener('DOMContentLoaded', domready);
6413 /*<ltIE8>*/
6414 // doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
6415 // testElement.doScroll() throws when the DOM is not ready, only in the top window
6416 var doScrollWorks = function(){
6417         try {
6418                 testElement.doScroll();
6419                 return true;
6420         } catch (e){}
6421         return false;
6423 // If doScroll works already, it can't be used to determine domready
6424 //   e.g. in an iframe
6425 if (testElement.doScroll && !doScrollWorks()){
6426         checks.push(doScrollWorks);
6427         shouldPoll = true;
6429 /*</ltIE8>*/
6431 if (document.readyState) checks.push(function(){
6432         var state = document.readyState;
6433         return (state == 'loaded' || state == 'complete');
6436 if ('onreadystatechange' in document) document.addListener('readystatechange', check);
6437 else shouldPoll = true;
6439 if (shouldPoll) poll();
6441 Element.Events.domready = {
6442         onAdd: function(fn){
6443                 if (ready) fn.call(this);
6444         }
6447 // Make sure that domready fires before load
6448 Element.Events.load = {
6449         base: 'load',
6450         onAdd: function(fn){
6451                 if (loaded && this == window) fn.call(this);
6452         },
6453         condition: function(){
6454                 if (this == window){
6455                         domready();
6456                         delete Element.Events.load;
6457                 }
6458                 return true;
6459         }
6462 // This is based on the custom load event
6463 window.addEvent('load', function(){
6464         loaded = true;
6467 })(window, document);