add server build to grunt task
[mootools.git] / dist / mootools-core.js
blob41806429c46da80b647d342425f27fadddd685fa
1 /*
2 ---
4 name: Core
6 description: The heart of MooTools.
8 license: MIT-style license.
10 copyright: Copyright (c) 2006-2014 [Valerio Proietti](http://mad4milk.net/).
12 authors: The MooTools production team (http://mootools.net/developers/)
14 inspiration:
15   - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
16   - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
18 provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
20 ...
23 (function(){
25 this.MooTools = {
26         version: '1.5.1',
27         build: '%build%'
30 // typeOf, instanceOf
32 var typeOf = this.typeOf = function(item){
33         if (item == null) return 'null';
34         if (item.$family != null) return item.$family();
36         if (item.nodeName){
37                 if (item.nodeType == 1) return 'element';
38                 if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
39         } else if (typeof item.length == 'number'){
40                 if ('callee' in item) return 'arguments';
41                 if ('item' in item) return 'collection';
42         }
44         return typeof item;
47 var instanceOf = this.instanceOf = function(item, object){
48         if (item == null) return false;
49         var constructor = item.$constructor || item.constructor;
50         while (constructor){
51                 if (constructor === object) return true;
52                 constructor = constructor.parent;
53         }
54         /*<ltIE8>*/
55         if (!item.hasOwnProperty) return false;
56         /*</ltIE8>*/
57         return item instanceof object;
60 // Function overloading
62 var Function = this.Function;
64 var enumerables = true;
65 for (var i in {toString: 1}) enumerables = null;
66 if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
68 Function.prototype.overloadSetter = function(usePlural){
69         var self = this;
70         return function(a, b){
71                 if (a == null) return this;
72                 if (usePlural || typeof a != 'string'){
73                         for (var k in a) self.call(this, k, a[k]);
74                         if (enumerables) for (var i = enumerables.length; i--;){
75                                 k = enumerables[i];
76                                 if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
77                         }
78                 } else {
79                         self.call(this, a, b);
80                 }
81                 return this;
82         };
85 Function.prototype.overloadGetter = function(usePlural){
86         var self = this;
87         return function(a){
88                 var args, result;
89                 if (typeof a != 'string') args = a;
90                 else if (arguments.length > 1) args = arguments;
91                 else if (usePlural) args = [a];
92                 if (args){
93                         result = {};
94                         for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
95                 } else {
96                         result = self.call(this, a);
97                 }
98                 return result;
99         };
102 Function.prototype.extend = function(key, value){
103         this[key] = value;
104 }.overloadSetter();
106 Function.prototype.implement = function(key, value){
107         this.prototype[key] = value;
108 }.overloadSetter();
110 // From
112 var slice = Array.prototype.slice;
114 Function.from = function(item){
115         return (typeOf(item) == 'function') ? item : function(){
116                 return item;
117         };
120 Array.from = function(item){
121         if (item == null) return [];
122         return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
125 Number.from = function(item){
126         var number = parseFloat(item);
127         return isFinite(number) ? number : null;
130 String.from = function(item){
131         return item + '';
134 // hide, protect
136 Function.implement({
138         hide: function(){
139                 this.$hidden = true;
140                 return this;
141         },
143         protect: function(){
144                 this.$protected = true;
145                 return this;
146         }
150 // Type
152 var Type = this.Type = function(name, object){
153         if (name){
154                 var lower = name.toLowerCase();
155                 var typeCheck = function(item){
156                         return (typeOf(item) == lower);
157                 };
159                 Type['is' + name] = typeCheck;
160                 if (object != null){
161                         object.prototype.$family = (function(){
162                                 return lower;
163                         }).hide();
164                         
165                 }
166         }
168         if (object == null) return null;
170         object.extend(this);
171         object.$constructor = Type;
172         object.prototype.$constructor = object;
174         return object;
177 var toString = Object.prototype.toString;
179 Type.isEnumerable = function(item){
180         return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
183 var hooks = {};
185 var hooksOf = function(object){
186         var type = typeOf(object.prototype);
187         return hooks[type] || (hooks[type] = []);
190 var implement = function(name, method){
191         if (method && method.$hidden) return;
193         var hooks = hooksOf(this);
195         for (var i = 0; i < hooks.length; i++){
196                 var hook = hooks[i];
197                 if (typeOf(hook) == 'type') implement.call(hook, name, method);
198                 else hook.call(this, name, method);
199         }
201         var previous = this.prototype[name];
202         if (previous == null || !previous.$protected) this.prototype[name] = method;
204         if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
205                 return method.apply(item, slice.call(arguments, 1));
206         });
209 var extend = function(name, method){
210         if (method && method.$hidden) return;
211         var previous = this[name];
212         if (previous == null || !previous.$protected) this[name] = method;
215 Type.implement({
217         implement: implement.overloadSetter(),
219         extend: extend.overloadSetter(),
221         alias: function(name, existing){
222                 implement.call(this, name, this.prototype[existing]);
223         }.overloadSetter(),
225         mirror: function(hook){
226                 hooksOf(this).push(hook);
227                 return this;
228         }
232 new Type('Type', Type);
234 // Default Types
236 var force = function(name, object, methods){
237         var isType = (object != Object),
238                 prototype = object.prototype;
240         if (isType) object = new Type(name, object);
242         for (var i = 0, l = methods.length; i < l; i++){
243                 var key = methods[i],
244                         generic = object[key],
245                         proto = prototype[key];
247                 if (generic) generic.protect();
248                 if (isType && proto) object.implement(key, proto.protect());
249         }
251         if (isType){
252                 var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]);
253                 object.forEachMethod = function(fn){
254                         if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
255                                 fn.call(prototype, prototype[methods[i]], methods[i]);
256                         }
257                         for (var key in prototype) fn.call(prototype, prototype[key], key);
258                 };
259         }
261         return force;
264 force('String', String, [
265         'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
266         'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
267 ])('Array', Array, [
268         'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
269         'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
270 ])('Number', Number, [
271         'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
272 ])('Function', Function, [
273         'apply', 'call', 'bind'
274 ])('RegExp', RegExp, [
275         'exec', 'test'
276 ])('Object', Object, [
277         'create', 'defineProperty', 'defineProperties', 'keys',
278         'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
279         'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
280 ])('Date', Date, ['now']);
282 Object.extend = extend.overloadSetter();
284 Date.extend('now', function(){
285         return +(new Date);
288 new Type('Boolean', Boolean);
290 // fixes NaN returning as Number
292 Number.prototype.$family = function(){
293         return isFinite(this) ? 'number' : 'null';
294 }.hide();
296 // Number.random
298 Number.extend('random', function(min, max){
299         return Math.floor(Math.random() * (max - min + 1) + min);
302 // forEach, each
304 var hasOwnProperty = Object.prototype.hasOwnProperty;
305 Object.extend('forEach', function(object, fn, bind){
306         for (var key in object){
307                 if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
308         }
311 Object.each = Object.forEach;
313 Array.implement({
315         /*<!ES5>*/
316         forEach: function(fn, bind){
317                 for (var i = 0, l = this.length; i < l; i++){
318                         if (i in this) fn.call(bind, this[i], i, this);
319                 }
320         },
321         /*</!ES5>*/
323         each: function(fn, bind){
324                 Array.forEach(this, fn, bind);
325                 return this;
326         }
330 // Array & Object cloning, Object merging and appending
332 var cloneOf = function(item){
333         switch (typeOf(item)){
334                 case 'array': return item.clone();
335                 case 'object': return Object.clone(item);
336                 default: return item;
337         }
340 Array.implement('clone', function(){
341         var i = this.length, clone = new Array(i);
342         while (i--) clone[i] = cloneOf(this[i]);
343         return clone;
346 var mergeOne = function(source, key, current){
347         switch (typeOf(current)){
348                 case 'object':
349                         if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
350                         else source[key] = Object.clone(current);
351                 break;
352                 case 'array': source[key] = current.clone(); break;
353                 default: source[key] = current;
354         }
355         return source;
358 Object.extend({
360         merge: function(source, k, v){
361                 if (typeOf(k) == 'string') return mergeOne(source, k, v);
362                 for (var i = 1, l = arguments.length; i < l; i++){
363                         var object = arguments[i];
364                         for (var key in object) mergeOne(source, key, object[key]);
365                 }
366                 return source;
367         },
369         clone: function(object){
370                 var clone = {};
371                 for (var key in object) clone[key] = cloneOf(object[key]);
372                 return clone;
373         },
375         append: function(original){
376                 for (var i = 1, l = arguments.length; i < l; i++){
377                         var extended = arguments[i] || {};
378                         for (var key in extended) original[key] = extended[key];
379                 }
380                 return original;
381         }
385 // Object-less types
387 ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
388         new Type(name);
391 // Unique ID
393 var UID = Date.now();
395 String.extend('uniqueID', function(){
396         return (UID++).toString(36);
401 })();
406 name: Array
408 description: Contains Array Prototypes like each, contains, and erase.
410 license: MIT-style license.
412 requires: [Type]
414 provides: Array
419 Array.implement({
421         /*<!ES5>*/
422         every: function(fn, bind){
423                 for (var i = 0, l = this.length >>> 0; i < l; i++){
424                         if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
425                 }
426                 return true;
427         },
429         filter: function(fn, bind){
430                 var results = [];
431                 for (var value, i = 0, l = this.length >>> 0; i < l; i++) if (i in this){
432                         value = this[i];
433                         if (fn.call(bind, value, i, this)) results.push(value);
434                 }
435                 return results;
436         },
438         indexOf: function(item, from){
439                 var length = this.length >>> 0;
440                 for (var i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){
441                         if (this[i] === item) return i;
442                 }
443                 return -1;
444         },
446         map: function(fn, bind){
447                 var length = this.length >>> 0, results = Array(length);
448                 for (var i = 0; i < length; i++){
449                         if (i in this) results[i] = fn.call(bind, this[i], i, this);
450                 }
451                 return results;
452         },
454         some: function(fn, bind){
455                 for (var i = 0, l = this.length >>> 0; i < l; i++){
456                         if ((i in this) && fn.call(bind, this[i], i, this)) return true;
457                 }
458                 return false;
459         },
460         /*</!ES5>*/
462         clean: function(){
463                 return this.filter(function(item){
464                         return item != null;
465                 });
466         },
468         invoke: function(methodName){
469                 var args = Array.slice(arguments, 1);
470                 return this.map(function(item){
471                         return item[methodName].apply(item, args);
472                 });
473         },
475         associate: function(keys){
476                 var obj = {}, length = Math.min(this.length, keys.length);
477                 for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
478                 return obj;
479         },
481         link: function(object){
482                 var result = {};
483                 for (var i = 0, l = this.length; i < l; i++){
484                         for (var key in object){
485                                 if (object[key](this[i])){
486                                         result[key] = this[i];
487                                         delete object[key];
488                                         break;
489                                 }
490                         }
491                 }
492                 return result;
493         },
495         contains: function(item, from){
496                 return this.indexOf(item, from) != -1;
497         },
499         append: function(array){
500                 this.push.apply(this, array);
501                 return this;
502         },
504         getLast: function(){
505                 return (this.length) ? this[this.length - 1] : null;
506         },
508         getRandom: function(){
509                 return (this.length) ? this[Number.random(0, this.length - 1)] : null;
510         },
512         include: function(item){
513                 if (!this.contains(item)) this.push(item);
514                 return this;
515         },
517         combine: function(array){
518                 for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
519                 return this;
520         },
522         erase: function(item){
523                 for (var i = this.length; i--;){
524                         if (this[i] === item) this.splice(i, 1);
525                 }
526                 return this;
527         },
529         empty: function(){
530                 this.length = 0;
531                 return this;
532         },
534         flatten: function(){
535                 var array = [];
536                 for (var i = 0, l = this.length; i < l; i++){
537                         var type = typeOf(this[i]);
538                         if (type == 'null') continue;
539                         array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
540                 }
541                 return array;
542         },
544         pick: function(){
545                 for (var i = 0, l = this.length; i < l; i++){
546                         if (this[i] != null) return this[i];
547                 }
548                 return null;
549         },
551         hexToRgb: function(array){
552                 if (this.length != 3) return null;
553                 var rgb = this.map(function(value){
554                         if (value.length == 1) value += value;
555                         return parseInt(value, 16);
556                 });
557                 return (array) ? rgb : 'rgb(' + rgb + ')';
558         },
560         rgbToHex: function(array){
561                 if (this.length < 3) return null;
562                 if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
563                 var hex = [];
564                 for (var i = 0; i < 3; i++){
565                         var bit = (this[i] - 0).toString(16);
566                         hex.push((bit.length == 1) ? '0' + bit : bit);
567                 }
568                 return (array) ? hex : '#' + hex.join('');
569         }
578 name: Function
580 description: Contains Function Prototypes like create, bind, pass, and delay.
582 license: MIT-style license.
584 requires: Type
586 provides: Function
591 Function.extend({
593         attempt: function(){
594                 for (var i = 0, l = arguments.length; i < l; i++){
595                         try {
596                                 return arguments[i]();
597                         } catch (e){}
598                 }
599                 return null;
600         }
604 Function.implement({
606         attempt: function(args, bind){
607                 try {
608                         return this.apply(bind, Array.from(args));
609                 } catch (e){}
611                 return null;
612         },
614         /*<!ES5-bind>*/
615         bind: function(that){
616                 var self = this,
617                         args = arguments.length > 1 ? Array.slice(arguments, 1) : null,
618                         F = function(){};
620                 var bound = function(){
621                         var context = that, length = arguments.length;
622                         if (this instanceof bound){
623                                 F.prototype = self.prototype;
624                                 context = new F;
625                         }
626                         var result = (!args && !length)
627                                 ? self.call(context)
628                                 : self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments);
629                         return context == that ? result : context;
630                 };
631                 return bound;
632         },
633         /*</!ES5-bind>*/
635         pass: function(args, bind){
636                 var self = this;
637                 if (args != null) args = Array.from(args);
638                 return function(){
639                         return self.apply(bind, args || arguments);
640                 };
641         },
643         delay: function(delay, bind, args){
644                 return setTimeout(this.pass((args == null ? [] : args), bind), delay);
645         },
647         periodical: function(periodical, bind, args){
648                 return setInterval(this.pass((args == null ? [] : args), bind), periodical);
649         }
658 name: Number
660 description: Contains Number Prototypes like limit, round, times, and ceil.
662 license: MIT-style license.
664 requires: Type
666 provides: Number
671 Number.implement({
673         limit: function(min, max){
674                 return Math.min(max, Math.max(min, this));
675         },
677         round: function(precision){
678                 precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
679                 return Math.round(this * precision) / precision;
680         },
682         times: function(fn, bind){
683                 for (var i = 0; i < this; i++) fn.call(bind, i, this);
684         },
686         toFloat: function(){
687                 return parseFloat(this);
688         },
690         toInt: function(base){
691                 return parseInt(this, base || 10);
692         }
696 Number.alias('each', 'times');
698 (function(math){
699         var methods = {};
700         math.each(function(name){
701                 if (!Number[name]) methods[name] = function(){
702                         return Math[name].apply(null, [this].concat(Array.from(arguments)));
703                 };
704         });
705         Number.implement(methods);
706 })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
711 name: String
713 description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
715 license: MIT-style license.
717 requires: [Type, Array]
719 provides: String
724 String.implement({
726         //<!ES6>
727         contains: function(string, index){
728                 return (index ? String(this).slice(index) : String(this)).indexOf(string) > -1;
729         },
730         //</!ES6>
732         test: function(regex, params){
733                 return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
734         },
736         trim: function(){
737                 return String(this).replace(/^\s+|\s+$/g, '');
738         },
740         clean: function(){
741                 return String(this).replace(/\s+/g, ' ').trim();
742         },
744         camelCase: function(){
745                 return String(this).replace(/-\D/g, function(match){
746                         return match.charAt(1).toUpperCase();
747                 });
748         },
750         hyphenate: function(){
751                 return String(this).replace(/[A-Z]/g, function(match){
752                         return ('-' + match.charAt(0).toLowerCase());
753                 });
754         },
756         capitalize: function(){
757                 return String(this).replace(/\b[a-z]/g, function(match){
758                         return match.toUpperCase();
759                 });
760         },
762         escapeRegExp: function(){
763                 return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
764         },
766         toInt: function(base){
767                 return parseInt(this, base || 10);
768         },
770         toFloat: function(){
771                 return parseFloat(this);
772         },
774         hexToRgb: function(array){
775                 var hex = String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
776                 return (hex) ? hex.slice(1).hexToRgb(array) : null;
777         },
779         rgbToHex: function(array){
780                 var rgb = String(this).match(/\d{1,3}/g);
781                 return (rgb) ? rgb.rgbToHex(array) : null;
782         },
784         substitute: function(object, regexp){
785                 return String(this).replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
786                         if (match.charAt(0) == '\\') return match.slice(1);
787                         return (object[name] != null) ? object[name] : '';
788                 });
789         }
798 name: Browser
800 description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.
802 license: MIT-style license.
804 requires: [Array, Function, Number, String]
806 provides: [Browser, Window, Document]
811 (function(){
813 var document = this.document;
814 var window = document.window = this;
816 var parse = function(ua, platform){
817         ua = ua.toLowerCase();
818         platform = (platform ? platform.toLowerCase() : '');
820         var UA = ua.match(/(opera|ie|firefox|chrome|trident|crios|version)[\s\/:]([\w\d\.]+)?.*?(safari|(?:rv[\s\/:]|version[\s\/:])([\w\d\.]+)|$)/) || [null, 'unknown', 0];
822         if (UA[1] == 'trident'){
823                 UA[1] = 'ie';
824                 if (UA[4]) UA[2] = UA[4];
825         } else if (UA[1] == 'crios'){
826                 UA[1] = 'chrome';
827         }
829         platform = ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0];
830         if (platform == 'win') platform = 'windows';
832         return {
833                 extend: Function.prototype.extend,
834                 name: (UA[1] == 'version') ? UA[3] : UA[1],
835                 version: parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
836                 platform: platform
837         };
840 var Browser = this.Browser = parse(navigator.userAgent, navigator.platform);
842 if (Browser.name == 'ie'){
843         Browser.version = document.documentMode;
846 Browser.extend({
847         Features: {
848                 xpath: !!(document.evaluate),
849                 air: !!(window.runtime),
850                 query: !!(document.querySelector),
851                 json: !!(window.JSON)
852         },
853         parseUA: parse
858 // Request
860 Browser.Request = (function(){
862         var XMLHTTP = function(){
863                 return new XMLHttpRequest();
864         };
866         var MSXML2 = function(){
867                 return new ActiveXObject('MSXML2.XMLHTTP');
868         };
870         var MSXML = function(){
871                 return new ActiveXObject('Microsoft.XMLHTTP');
872         };
874         return Function.attempt(function(){
875                 XMLHTTP();
876                 return XMLHTTP;
877         }, function(){
878                 MSXML2();
879                 return MSXML2;
880         }, function(){
881                 MSXML();
882                 return MSXML;
883         });
885 })();
887 Browser.Features.xhr = !!(Browser.Request);
891 // String scripts
893 Browser.exec = function(text){
894         if (!text) return text;
895         if (window.execScript){
896                 window.execScript(text);
897         } else {
898                 var script = document.createElement('script');
899                 script.setAttribute('type', 'text/javascript');
900                 script.text = text;
901                 document.head.appendChild(script);
902                 document.head.removeChild(script);
903         }
904         return text;
907 String.implement('stripScripts', function(exec){
908         var scripts = '';
909         var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
910                 scripts += code + '\n';
911                 return '';
912         });
913         if (exec === true) Browser.exec(scripts);
914         else if (typeOf(exec) == 'function') exec(scripts, text);
915         return text;
918 // Window, Document
920 Browser.extend({
921         Document: this.Document,
922         Window: this.Window,
923         Element: this.Element,
924         Event: this.Event
927 this.Window = this.$constructor = new Type('Window', function(){});
929 this.$family = Function.from('window').hide();
931 Window.mirror(function(name, method){
932         window[name] = method;
935 this.Document = document.$constructor = new Type('Document', function(){});
937 document.$family = Function.from('document').hide();
939 Document.mirror(function(name, method){
940         document[name] = method;
943 document.html = document.documentElement;
944 if (!document.head) document.head = document.getElementsByTagName('head')[0];
946 if (document.execCommand) try {
947         document.execCommand("BackgroundImageCache", false, true);
948 } catch (e){}
950 /*<ltIE9>*/
951 if (this.attachEvent && !this.addEventListener){
952         var unloadEvent = function(){
953                 this.detachEvent('onunload', unloadEvent);
954                 document.head = document.html = document.window = null;
955                 window = this.Window = document = null;
956         };
957         this.attachEvent('onunload', unloadEvent);
960 // IE fails on collections and <select>.options (refers to <select>)
961 var arrayFrom = Array.from;
962 try {
963         arrayFrom(document.html.childNodes);
964 } catch(e){
965         Array.from = function(item){
966                 if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){
967                         var i = item.length, array = new Array(i);
968                         while (i--) array[i] = item[i];
969                         return array;
970                 }
971                 return arrayFrom(item);
972         };
974         var prototype = Array.prototype,
975                 slice = prototype.slice;
976         ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){
977                 var method = prototype[name];
978                 Array[name] = function(item){
979                         return method.apply(Array.from(item), slice.call(arguments, 1));
980                 };
981         });
983 /*</ltIE9>*/
987 })();
992 name: Class
994 description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
996 license: MIT-style license.
998 requires: [Array, String, Function, Number]
1000 provides: Class
1005 (function(){
1007 var Class = this.Class = new Type('Class', function(params){
1008         if (instanceOf(params, Function)) params = {initialize: params};
1010         var newClass = function(){
1011                 reset(this);
1012                 if (newClass.$prototyping) return this;
1013                 this.$caller = null;
1014                 var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
1015                 this.$caller = this.caller = null;
1016                 return value;
1017         }.extend(this).implement(params);
1019         newClass.$constructor = Class;
1020         newClass.prototype.$constructor = newClass;
1021         newClass.prototype.parent = parent;
1023         return newClass;
1026 var parent = function(){
1027         if (!this.$caller) throw new Error('The method "parent" cannot be called.');
1028         var name = this.$caller.$name,
1029                 parent = this.$caller.$owner.parent,
1030                 previous = (parent) ? parent.prototype[name] : null;
1031         if (!previous) throw new Error('The method "' + name + '" has no parent.');
1032         return previous.apply(this, arguments);
1035 var reset = function(object){
1036         for (var key in object){
1037                 var value = object[key];
1038                 switch (typeOf(value)){
1039                         case 'object':
1040                                 var F = function(){};
1041                                 F.prototype = value;
1042                                 object[key] = reset(new F);
1043                         break;
1044                         case 'array': object[key] = value.clone(); break;
1045                 }
1046         }
1047         return object;
1050 var wrap = function(self, key, method){
1051         if (method.$origin) method = method.$origin;
1052         var wrapper = function(){
1053                 if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
1054                 var caller = this.caller, current = this.$caller;
1055                 this.caller = current; this.$caller = wrapper;
1056                 var result = method.apply(this, arguments);
1057                 this.$caller = current; this.caller = caller;
1058                 return result;
1059         }.extend({$owner: self, $origin: method, $name: key});
1060         return wrapper;
1063 var implement = function(key, value, retain){
1064         if (Class.Mutators.hasOwnProperty(key)){
1065                 value = Class.Mutators[key].call(this, value);
1066                 if (value == null) return this;
1067         }
1069         if (typeOf(value) == 'function'){
1070                 if (value.$hidden) return this;
1071                 this.prototype[key] = (retain) ? value : wrap(this, key, value);
1072         } else {
1073                 Object.merge(this.prototype, key, value);
1074         }
1076         return this;
1079 var getInstance = function(klass){
1080         klass.$prototyping = true;
1081         var proto = new klass;
1082         delete klass.$prototyping;
1083         return proto;
1086 Class.implement('implement', implement.overloadSetter());
1088 Class.Mutators = {
1090         Extends: function(parent){
1091                 this.parent = parent;
1092                 this.prototype = getInstance(parent);
1093         },
1095         Implements: function(items){
1096                 Array.from(items).each(function(item){
1097                         var instance = new item;
1098                         for (var key in instance) implement.call(this, key, instance[key], true);
1099                 }, this);
1100         }
1103 })();
1108 name: Class.Extras
1110 description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
1112 license: MIT-style license.
1114 requires: Class
1116 provides: [Class.Extras, Chain, Events, Options]
1121 (function(){
1123 this.Chain = new Class({
1125         $chain: [],
1127         chain: function(){
1128                 this.$chain.append(Array.flatten(arguments));
1129                 return this;
1130         },
1132         callChain: function(){
1133                 return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
1134         },
1136         clearChain: function(){
1137                 this.$chain.empty();
1138                 return this;
1139         }
1143 var removeOn = function(string){
1144         return string.replace(/^on([A-Z])/, function(full, first){
1145                 return first.toLowerCase();
1146         });
1149 this.Events = new Class({
1151         $events: {},
1153         addEvent: function(type, fn, internal){
1154                 type = removeOn(type);
1156                 
1158                 this.$events[type] = (this.$events[type] || []).include(fn);
1159                 if (internal) fn.internal = true;
1160                 return this;
1161         },
1163         addEvents: function(events){
1164                 for (var type in events) this.addEvent(type, events[type]);
1165                 return this;
1166         },
1168         fireEvent: function(type, args, delay){
1169                 type = removeOn(type);
1170                 var events = this.$events[type];
1171                 if (!events) return this;
1172                 args = Array.from(args);
1173                 events.each(function(fn){
1174                         if (delay) fn.delay(delay, this, args);
1175                         else fn.apply(this, args);
1176                 }, this);
1177                 return this;
1178         },
1180         removeEvent: function(type, fn){
1181                 type = removeOn(type);
1182                 var events = this.$events[type];
1183                 if (events && !fn.internal){
1184                         var index = events.indexOf(fn);
1185                         if (index != -1) delete events[index];
1186                 }
1187                 return this;
1188         },
1190         removeEvents: function(events){
1191                 var type;
1192                 if (typeOf(events) == 'object'){
1193                         for (type in events) this.removeEvent(type, events[type]);
1194                         return this;
1195                 }
1196                 if (events) events = removeOn(events);
1197                 for (type in this.$events){
1198                         if (events && events != type) continue;
1199                         var fns = this.$events[type];
1200                         for (var i = fns.length; i--;) if (i in fns){
1201                                 this.removeEvent(type, fns[i]);
1202                         }
1203                 }
1204                 return this;
1205         }
1209 this.Options = new Class({
1211         setOptions: function(){
1212                 var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));
1213                 if (this.addEvent) for (var option in options){
1214                         if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
1215                         this.addEvent(option, options[option]);
1216                         delete options[option];
1217                 }
1218                 return this;
1219         }
1223 })();
1228 name: Object
1230 description: Object generic methods
1232 license: MIT-style license.
1234 requires: Type
1236 provides: [Object, Hash]
1241 (function(){
1243 var hasOwnProperty = Object.prototype.hasOwnProperty;
1245 Object.extend({
1247         subset: function(object, keys){
1248                 var results = {};
1249                 for (var i = 0, l = keys.length; i < l; i++){
1250                         var k = keys[i];
1251                         if (k in object) results[k] = object[k];
1252                 }
1253                 return results;
1254         },
1256         map: function(object, fn, bind){
1257                 var results = {};
1258                 for (var key in object){
1259                         if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
1260                 }
1261                 return results;
1262         },
1264         filter: function(object, fn, bind){
1265                 var results = {};
1266                 for (var key in object){
1267                         var value = object[key];
1268                         if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value;
1269                 }
1270                 return results;
1271         },
1273         every: function(object, fn, bind){
1274                 for (var key in object){
1275                         if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
1276                 }
1277                 return true;
1278         },
1280         some: function(object, fn, bind){
1281                 for (var key in object){
1282                         if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
1283                 }
1284                 return false;
1285         },
1287         keys: function(object){
1288                 var keys = [];
1289                 for (var key in object){
1290                         if (hasOwnProperty.call(object, key)) keys.push(key);
1291                 }
1292                 return keys;
1293         },
1295         values: function(object){
1296                 var values = [];
1297                 for (var key in object){
1298                         if (hasOwnProperty.call(object, key)) values.push(object[key]);
1299                 }
1300                 return values;
1301         },
1303         getLength: function(object){
1304                 return Object.keys(object).length;
1305         },
1307         keyOf: function(object, value){
1308                 for (var key in object){
1309                         if (hasOwnProperty.call(object, key) && object[key] === value) return key;
1310                 }
1311                 return null;
1312         },
1314         contains: function(object, value){
1315                 return Object.keyOf(object, value) != null;
1316         },
1318         toQueryString: function(object, base){
1319                 var queryString = [];
1321                 Object.each(object, function(value, key){
1322                         if (base) key = base + '[' + key + ']';
1323                         var result;
1324                         switch (typeOf(value)){
1325                                 case 'object': result = Object.toQueryString(value, key); break;
1326                                 case 'array':
1327                                         var qs = {};
1328                                         value.each(function(val, i){
1329                                                 qs[i] = val;
1330                                         });
1331                                         result = Object.toQueryString(qs, key);
1332                                 break;
1333                                 default: result = key + '=' + encodeURIComponent(value);
1334                         }
1335                         if (value != null) queryString.push(result);
1336                 });
1338                 return queryString.join('&');
1339         }
1343 })();
1349 name: Slick.Parser
1350 description: Standalone CSS3 Selector parser
1351 provides: Slick.Parser
1355 ;(function(){
1357 var parsed,
1358         separatorIndex,
1359         combinatorIndex,
1360         reversed,
1361         cache = {},
1362         reverseCache = {},
1363         reUnescape = /\\/g;
1365 var parse = function(expression, isReversed){
1366         if (expression == null) return null;
1367         if (expression.Slick === true) return expression;
1368         expression = ('' + expression).replace(/^\s+|\s+$/g, '');
1369         reversed = !!isReversed;
1370         var currentCache = (reversed) ? reverseCache : cache;
1371         if (currentCache[expression]) return currentCache[expression];
1372         parsed = {
1373                 Slick: true,
1374                 expressions: [],
1375                 raw: expression,
1376                 reverse: function(){
1377                         return parse(this.raw, true);
1378                 }
1379         };
1380         separatorIndex = -1;
1381         while (expression != (expression = expression.replace(regexp, parser)));
1382         parsed.length = parsed.expressions.length;
1383         return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
1386 var reverseCombinator = function(combinator){
1387         if (combinator === '!') return ' ';
1388         else if (combinator === ' ') return '!';
1389         else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
1390         else return '!' + combinator;
1393 var reverse = function(expression){
1394         var expressions = expression.expressions;
1395         for (var i = 0; i < expressions.length; i++){
1396                 var exp = expressions[i];
1397                 var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
1399                 for (var j = 0; j < exp.length; j++){
1400                         var cexp = exp[j];
1401                         if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
1402                         cexp.combinator = cexp.reverseCombinator;
1403                         delete cexp.reverseCombinator;
1404                 }
1406                 exp.reverse().push(last);
1407         }
1408         return expression;
1411 var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
1412         return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){
1413                 return '\\' + match;
1414         });
1417 var regexp = new RegExp(
1419 #!/usr/bin/env ruby
1420 puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
1421 __END__
1422         "(?x)^(?:\
1423           \\s* ( , ) \\s*               # Separator          \n\
1424         | \\s* ( <combinator>+ ) \\s*   # Combinator         \n\
1425         |      ( \\s+ )                 # CombinatorChildren \n\
1426         |      ( <unicode>+ | \\* )     # Tag                \n\
1427         | \\#  ( <unicode>+       )     # ID                 \n\
1428         | \\.  ( <unicode>+       )     # ClassName          \n\
1429         |                               # Attribute          \n\
1430         \\[  \
1431                 \\s* (<unicode1>+)  (?:  \
1432                         \\s* ([*^$!~|]?=)  (?:  \
1433                                 \\s* (?:\
1434                                         ([\"']?)(.*?)\\9 \
1435                                 )\
1436                         )  \
1437                 )?  \\s*  \
1438         \\](?!\\]) \n\
1439         |   :+ ( <unicode>+ )(?:\
1440         \\( (?:\
1441                 (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
1442         ) \\)\
1443         )?\
1444         )"
1446         "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
1447         .replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
1448         .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
1449         .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
1452 function parser(
1453         rawMatch,
1455         separator,
1456         combinator,
1457         combinatorChildren,
1459         tagName,
1460         id,
1461         className,
1463         attributeKey,
1464         attributeOperator,
1465         attributeQuote,
1466         attributeValue,
1468         pseudoMarker,
1469         pseudoClass,
1470         pseudoQuote,
1471         pseudoClassQuotedValue,
1472         pseudoClassValue
1474         if (separator || separatorIndex === -1){
1475                 parsed.expressions[++separatorIndex] = [];
1476                 combinatorIndex = -1;
1477                 if (separator) return '';
1478         }
1480         if (combinator || combinatorChildren || combinatorIndex === -1){
1481                 combinator = combinator || ' ';
1482                 var currentSeparator = parsed.expressions[separatorIndex];
1483                 if (reversed && currentSeparator[combinatorIndex])
1484                         currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
1485                 currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
1486         }
1488         var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
1490         if (tagName){
1491                 currentParsed.tag = tagName.replace(reUnescape, '');
1493         } else if (id){
1494                 currentParsed.id = id.replace(reUnescape, '');
1496         } else if (className){
1497                 className = className.replace(reUnescape, '');
1499                 if (!currentParsed.classList) currentParsed.classList = [];
1500                 if (!currentParsed.classes) currentParsed.classes = [];
1501                 currentParsed.classList.push(className);
1502                 currentParsed.classes.push({
1503                         value: className,
1504                         regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
1505                 });
1507         } else if (pseudoClass){
1508                 pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
1509                 pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
1511                 if (!currentParsed.pseudos) currentParsed.pseudos = [];
1512                 currentParsed.pseudos.push({
1513                         key: pseudoClass.replace(reUnescape, ''),
1514                         value: pseudoClassValue,
1515                         type: pseudoMarker.length == 1 ? 'class' : 'element'
1516                 });
1518         } else if (attributeKey){
1519                 attributeKey = attributeKey.replace(reUnescape, '');
1520                 attributeValue = (attributeValue || '').replace(reUnescape, '');
1522                 var test, regexp;
1524                 switch (attributeOperator){
1525                         case '^=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue)            ); break;
1526                         case '$=' : regexp = new RegExp(            escapeRegExp(attributeValue) +'$'       ); break;
1527                         case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
1528                         case '|=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue) +'(-|$)'   ); break;
1529                         case  '=' : test = function(value){
1530                                 return attributeValue == value;
1531                         }; break;
1532                         case '*=' : test = function(value){
1533                                 return value && value.indexOf(attributeValue) > -1;
1534                         }; break;
1535                         case '!=' : test = function(value){
1536                                 return attributeValue != value;
1537                         }; break;
1538                         default   : test = function(value){
1539                                 return !!value;
1540                         };
1541                 }
1543                 if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
1544                         return false;
1545                 };
1547                 if (!test) test = function(value){
1548                         return value && regexp.test(value);
1549                 };
1551                 if (!currentParsed.attributes) currentParsed.attributes = [];
1552                 currentParsed.attributes.push({
1553                         key: attributeKey,
1554                         operator: attributeOperator,
1555                         value: attributeValue,
1556                         test: test
1557                 });
1559         }
1561         return '';
1564 // Slick NS
1566 var Slick = (this.Slick || {});
1568 Slick.parse = function(expression){
1569         return parse(expression);
1572 Slick.escapeRegExp = escapeRegExp;
1574 if (!this.Slick) this.Slick = Slick;
1576 }).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
1580 name: Slick.Finder
1581 description: The new, superfast css selector engine.
1582 provides: Slick.Finder
1583 requires: Slick.Parser
1587 ;(function(){
1589 var local = {},
1590         featuresCache = {},
1591         toString = Object.prototype.toString;
1593 // Feature / Bug detection
1595 local.isNativeCode = function(fn){
1596         return (/\{\s*\[native code\]\s*\}/).test('' + fn);
1599 local.isXML = function(document){
1600         return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') ||
1601         (document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
1604 local.setDocument = function(document){
1606         // convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
1607         var nodeType = document.nodeType;
1608         if (nodeType == 9); // document
1609         else if (nodeType) document = document.ownerDocument; // node
1610         else if (document.navigator) document = document.document; // window
1611         else return;
1613         // check if it's the old document
1615         if (this.document === document) return;
1616         this.document = document;
1618         // check if we have done feature detection on this document before
1620         var root = document.documentElement,
1621                 rootUid = this.getUIDXML(root),
1622                 features = featuresCache[rootUid],
1623                 feature;
1625         if (features){
1626                 for (feature in features){
1627                         this[feature] = features[feature];
1628                 }
1629                 return;
1630         }
1632         features = featuresCache[rootUid] = {};
1634         features.root = root;
1635         features.isXMLDocument = this.isXML(document);
1637         features.brokenStarGEBTN
1638         = features.starSelectsClosedQSA
1639         = features.idGetsName
1640         = features.brokenMixedCaseQSA
1641         = features.brokenGEBCN
1642         = features.brokenCheckedQSA
1643         = features.brokenEmptyAttributeQSA
1644         = features.isHTMLDocument
1645         = features.nativeMatchesSelector
1646         = false;
1648         var starSelectsClosed, starSelectsComments,
1649                 brokenSecondClassNameGEBCN, cachedGetElementsByClassName,
1650                 brokenFormAttributeGetter;
1652         var selected, id = 'slick_uniqueid';
1653         var testNode = document.createElement('div');
1655         var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
1656         testRoot.appendChild(testNode);
1658         // on non-HTML documents innerHTML and getElementsById doesnt work properly
1659         try {
1660                 testNode.innerHTML = '<a id="'+id+'"></a>';
1661                 features.isHTMLDocument = !!document.getElementById(id);
1662         } catch(e){};
1664         if (features.isHTMLDocument){
1666                 testNode.style.display = 'none';
1668                 // IE returns comment nodes for getElementsByTagName('*') for some documents
1669                 testNode.appendChild(document.createComment(''));
1670                 starSelectsComments = (testNode.getElementsByTagName('*').length > 1);
1672                 // IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
1673                 try {
1674                         testNode.innerHTML = 'foo</foo>';
1675                         selected = testNode.getElementsByTagName('*');
1676                         starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
1677                 } catch(e){};
1679                 features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
1681                 // IE returns elements with the name instead of just id for getElementsById for some documents
1682                 try {
1683                         testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
1684                         features.idGetsName = document.getElementById(id) === testNode.firstChild;
1685                 } catch(e){};
1687                 if (testNode.getElementsByClassName){
1689                         // Safari 3.2 getElementsByClassName caches results
1690                         try {
1691                                 testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
1692                                 testNode.getElementsByClassName('b').length;
1693                                 testNode.firstChild.className = 'b';
1694                                 cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
1695                         } catch(e){};
1697                         // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
1698                         try {
1699                                 testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
1700                                 brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
1701                         } catch(e){};
1703                         features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
1704                 }
1706                 if (testNode.querySelectorAll){
1707                         // IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
1708                         try {
1709                                 testNode.innerHTML = 'foo</foo>';
1710                                 selected = testNode.querySelectorAll('*');
1711                                 features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
1712                         } catch(e){};
1714                         // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
1715                         try {
1716                                 testNode.innerHTML = '<a class="MiX"></a>';
1717                                 features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
1718                         } catch(e){};
1720                         // Webkit and Opera dont return selected options on querySelectorAll
1721                         try {
1722                                 testNode.innerHTML = '<select><option selected="selected">a</option></select>';
1723                                 features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
1724                         } catch(e){};
1726                         // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
1727                         try {
1728                                 testNode.innerHTML = '<a class=""></a>';
1729                                 features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
1730                         } catch(e){};
1732                 }
1734                 // IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
1735                 try {
1736                         testNode.innerHTML = '<form action="s"><input id="action"/></form>';
1737                         brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
1738                 } catch(e){};
1740                 // native matchesSelector function
1742                 features.nativeMatchesSelector = root.matches || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
1743                 if (features.nativeMatchesSelector) try {
1744                         // if matchesSelector trows errors on incorrect sintaxes we can use it
1745                         features.nativeMatchesSelector.call(root, ':slick');
1746                         features.nativeMatchesSelector = null;
1747                 } catch(e){};
1749         }
1751         try {
1752                 root.slick_expando = 1;
1753                 delete root.slick_expando;
1754                 features.getUID = this.getUIDHTML;
1755         } catch(e){
1756                 features.getUID = this.getUIDXML;
1757         }
1759         testRoot.removeChild(testNode);
1760         testNode = selected = testRoot = null;
1762         // getAttribute
1764         features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){
1765                 var method = this.attributeGetters[name];
1766                 if (method) return method.call(node);
1767                 var attributeNode = node.getAttributeNode(name);
1768                 return (attributeNode) ? attributeNode.nodeValue : null;
1769         } : function(node, name){
1770                 var method = this.attributeGetters[name];
1771                 return (method) ? method.call(node) : node.getAttribute(name);
1772         };
1774         // hasAttribute
1776         features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute){
1777                 return node.hasAttribute(attribute);
1778         } : function(node, attribute){
1779                 node = node.getAttributeNode(attribute);
1780                 return !!(node && (node.specified || node.nodeValue));
1781         };
1783         // contains
1784         // FIXME: Add specs: local.contains should be different for xml and html documents?
1785         var nativeRootContains = root && this.isNativeCode(root.contains),
1786                 nativeDocumentContains = document && this.isNativeCode(document.contains);
1788         features.contains = (nativeRootContains && nativeDocumentContains) ? function(context, node){
1789                 return context.contains(node);
1790         } : (nativeRootContains && !nativeDocumentContains) ? function(context, node){
1791                 // IE8 does not have .contains on document.
1792                 return context === node || ((context === document) ? document.documentElement : context).contains(node);
1793         } : (root && root.compareDocumentPosition) ? function(context, node){
1794                 return context === node || !!(context.compareDocumentPosition(node) & 16);
1795         } : function(context, node){
1796                 if (node) do {
1797                         if (node === context) return true;
1798                 } while ((node = node.parentNode));
1799                 return false;
1800         };
1802         // document order sorting
1803         // credits to Sizzle (http://sizzlejs.com/)
1805         features.documentSorter = (root.compareDocumentPosition) ? function(a, b){
1806                 if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
1807                 return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
1808         } : ('sourceIndex' in root) ? function(a, b){
1809                 if (!a.sourceIndex || !b.sourceIndex) return 0;
1810                 return a.sourceIndex - b.sourceIndex;
1811         } : (document.createRange) ? function(a, b){
1812                 if (!a.ownerDocument || !b.ownerDocument) return 0;
1813                 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
1814                 aRange.setStart(a, 0);
1815                 aRange.setEnd(a, 0);
1816                 bRange.setStart(b, 0);
1817                 bRange.setEnd(b, 0);
1818                 return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
1819         } : null ;
1821         root = null;
1823         for (feature in features){
1824                 this[feature] = features[feature];
1825         }
1828 // Main Method
1830 var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
1831         reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
1832         qsaFailExpCache = {};
1834 local.search = function(context, expression, append, first){
1836         var found = this.found = (first) ? null : (append || []);
1838         if (!context) return found;
1839         else if (context.navigator) context = context.document; // Convert the node from a window to a document
1840         else if (!context.nodeType) return found;
1842         // setup
1844         var parsed, i,
1845                 uniques = this.uniques = {},
1846                 hasOthers = !!(append && append.length),
1847                 contextIsDocument = (context.nodeType == 9);
1849         if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);
1851         // avoid duplicating items already in the append array
1852         if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;
1854         // expression checks
1856         if (typeof expression == 'string'){ // expression is a string
1858                 /*<simple-selectors-override>*/
1859                 var simpleSelector = expression.match(reSimpleSelector);
1860                 simpleSelectors: if (simpleSelector){
1862                         var symbol = simpleSelector[1],
1863                                 name = simpleSelector[2],
1864                                 node, nodes;
1866                         if (!symbol){
1868                                 if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
1869                                 nodes = context.getElementsByTagName(name);
1870                                 if (first) return nodes[0] || null;
1871                                 for (i = 0; node = nodes[i++];){
1872                                         if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
1873                                 }
1875                         } else if (symbol == '#'){
1877                                 if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
1878                                 node = context.getElementById(name);
1879                                 if (!node) return found;
1880                                 if (this.idGetsName && node.getAttributeNode('id').nodeValue != name) break simpleSelectors;
1881                                 if (first) return node || null;
1882                                 if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
1884                         } else if (symbol == '.'){
1886                                 if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
1887                                 if (context.getElementsByClassName && !this.brokenGEBCN){
1888                                         nodes = context.getElementsByClassName(name);
1889                                         if (first) return nodes[0] || null;
1890                                         for (i = 0; node = nodes[i++];){
1891                                                 if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
1892                                         }
1893                                 } else {
1894                                         var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)');
1895                                         nodes = context.getElementsByTagName('*');
1896                                         for (i = 0; node = nodes[i++];){
1897                                                 className = node.className;
1898                                                 if (!(className && matchClass.test(className))) continue;
1899                                                 if (first) return node;
1900                                                 if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
1901                                         }
1902                                 }
1904                         }
1906                         if (hasOthers) this.sort(found);
1907                         return (first) ? null : found;
1909                 }
1910                 /*</simple-selectors-override>*/
1912                 /*<query-selector-override>*/
1913                 querySelector: if (context.querySelectorAll){
1915                         if (!this.isHTMLDocument
1916                                 || qsaFailExpCache[expression]
1917                                 //TODO: only skip when expression is actually mixed case
1918                                 || this.brokenMixedCaseQSA
1919                                 || (this.brokenCheckedQSA && expression.indexOf(':checked') > -1)
1920                                 || (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression))
1921                                 || (!contextIsDocument //Abort when !contextIsDocument and...
1922                                         //  there are multiple expressions in the selector
1923                                         //  since we currently only fix non-document rooted QSA for single expression selectors
1924                                         && expression.indexOf(',') > -1
1925                                 )
1926                                 || Slick.disableQSA
1927                         ) break querySelector;
1929                         var _expression = expression, _context = context;
1930                         if (!contextIsDocument){
1931                                 // non-document rooted QSA
1932                                 // credits to Andrew Dupont
1933                                 var currentId = _context.getAttribute('id'), slickid = 'slickid__';
1934                                 _context.setAttribute('id', slickid);
1935                                 _expression = '#' + slickid + ' ' + _expression;
1936                                 context = _context.parentNode;
1937                         }
1939                         try {
1940                                 if (first) return context.querySelector(_expression) || null;
1941                                 else nodes = context.querySelectorAll(_expression);
1942                         } catch(e){
1943                                 qsaFailExpCache[expression] = 1;
1944                                 break querySelector;
1945                         } finally {
1946                                 if (!contextIsDocument){
1947                                         if (currentId) _context.setAttribute('id', currentId);
1948                                         else _context.removeAttribute('id');
1949                                         context = _context;
1950                                 }
1951                         }
1953                         if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
1954                                 if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
1955                         } else for (i = 0; node = nodes[i++];){
1956                                 if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
1957                         }
1959                         if (hasOthers) this.sort(found);
1960                         return found;
1962                 }
1963                 /*</query-selector-override>*/
1965                 parsed = this.Slick.parse(expression);
1966                 if (!parsed.length) return found;
1967         } else if (expression == null){ // there is no expression
1968                 return found;
1969         } else if (expression.Slick){ // expression is a parsed Slick object
1970                 parsed = expression;
1971         } else if (this.contains(context.documentElement || context, expression)){ // expression is a node
1972                 (found) ? found.push(expression) : found = expression;
1973                 return found;
1974         } else { // other junk
1975                 return found;
1976         }
1978         /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
1980         // cache elements for the nth selectors
1982         this.posNTH = {};
1983         this.posNTHLast = {};
1984         this.posNTHType = {};
1985         this.posNTHTypeLast = {};
1987         /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
1989         // if append is null and there is only a single selector with one expression use pushArray, else use pushUID
1990         this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
1992         if (found == null) found = [];
1994         // default engine
1996         var j, m, n;
1997         var combinator, tag, id, classList, classes, attributes, pseudos;
1998         var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
2000         search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
2002                 combinator = 'combinator:' + currentBit.combinator;
2003                 if (!this[combinator]) continue search;
2005                 tag        = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
2006                 id         = currentBit.id;
2007                 classList  = currentBit.classList;
2008                 classes    = currentBit.classes;
2009                 attributes = currentBit.attributes;
2010                 pseudos    = currentBit.pseudos;
2011                 lastBit    = (j === (currentExpression.length - 1));
2013                 this.bitUniques = {};
2015                 if (lastBit){
2016                         this.uniques = uniques;
2017                         this.found = found;
2018                 } else {
2019                         this.uniques = {};
2020                         this.found = [];
2021                 }
2023                 if (j === 0){
2024                         this[combinator](context, tag, id, classes, attributes, pseudos, classList);
2025                         if (first && lastBit && found.length) break search;
2026                 } else {
2027                         if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
2028                                 this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
2029                                 if (found.length) break search;
2030                         } else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
2031                 }
2033                 currentItems = this.found;
2034         }
2036         // should sort if there are nodes in append and if you pass multiple expressions.
2037         if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);
2039         return (first) ? (found[0] || null) : found;
2042 // Utils
2044 local.uidx = 1;
2045 local.uidk = 'slick-uniqueid';
2047 local.getUIDXML = function(node){
2048         var uid = node.getAttribute(this.uidk);
2049         if (!uid){
2050                 uid = this.uidx++;
2051                 node.setAttribute(this.uidk, uid);
2052         }
2053         return uid;
2056 local.getUIDHTML = function(node){
2057         return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
2060 // sort based on the setDocument documentSorter method.
2062 local.sort = function(results){
2063         if (!this.documentSorter) return results;
2064         results.sort(this.documentSorter);
2065         return results;
2068 /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
2070 local.cacheNTH = {};
2072 local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
2074 local.parseNTHArgument = function(argument){
2075         var parsed = argument.match(this.matchNTH);
2076         if (!parsed) return false;
2077         var special = parsed[2] || false;
2078         var a = parsed[1] || 1;
2079         if (a == '-') a = -1;
2080         var b = +parsed[3] || 0;
2081         parsed =
2082                 (special == 'n')        ? {a: a, b: b} :
2083                 (special == 'odd')      ? {a: 2, b: 1} :
2084                 (special == 'even')     ? {a: 2, b: 0} : {a: 0, b: a};
2086         return (this.cacheNTH[argument] = parsed);
2089 local.createNTHPseudo = function(child, sibling, positions, ofType){
2090         return function(node, argument){
2091                 var uid = this.getUID(node);
2092                 if (!this[positions][uid]){
2093                         var parent = node.parentNode;
2094                         if (!parent) return false;
2095                         var el = parent[child], count = 1;
2096                         if (ofType){
2097                                 var nodeName = node.nodeName;
2098                                 do {
2099                                         if (el.nodeName != nodeName) continue;
2100                                         this[positions][this.getUID(el)] = count++;
2101                                 } while ((el = el[sibling]));
2102                         } else {
2103                                 do {
2104                                         if (el.nodeType != 1) continue;
2105                                         this[positions][this.getUID(el)] = count++;
2106                                 } while ((el = el[sibling]));
2107                         }
2108                 }
2109                 argument = argument || 'n';
2110                 var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
2111                 if (!parsed) return false;
2112                 var a = parsed.a, b = parsed.b, pos = this[positions][uid];
2113                 if (a == 0) return b == pos;
2114                 if (a > 0){
2115                         if (pos < b) return false;
2116                 } else {
2117                         if (b < pos) return false;
2118                 }
2119                 return ((pos - b) % a) == 0;
2120         };
2123 /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
2125 local.pushArray = function(node, tag, id, classes, attributes, pseudos){
2126         if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
2129 local.pushUID = function(node, tag, id, classes, attributes, pseudos){
2130         var uid = this.getUID(node);
2131         if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
2132                 this.uniques[uid] = true;
2133                 this.found.push(node);
2134         }
2137 local.matchNode = function(node, selector){
2138         if (this.isHTMLDocument && this.nativeMatchesSelector){
2139                 try {
2140                         return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
2141                 } catch(matchError){}
2142         }
2144         var parsed = this.Slick.parse(selector);
2145         if (!parsed) return true;
2147         // simple (single) selectors
2148         var expressions = parsed.expressions, simpleExpCounter = 0, i, currentExpression;
2149         for (i = 0; (currentExpression = expressions[i]); i++){
2150                 if (currentExpression.length == 1){
2151                         var exp = currentExpression[0];
2152                         if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true;
2153                         simpleExpCounter++;
2154                 }
2155         }
2157         if (simpleExpCounter == parsed.length) return false;
2159         var nodes = this.search(this.document, parsed), item;
2160         for (i = 0; item = nodes[i++];){
2161                 if (item === node) return true;
2162         }
2163         return false;
2166 local.matchPseudo = function(node, name, argument){
2167         var pseudoName = 'pseudo:' + name;
2168         if (this[pseudoName]) return this[pseudoName](node, argument);
2169         var attribute = this.getAttribute(node, name);
2170         return (argument) ? argument == attribute : !!attribute;
2173 local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
2174         if (tag){
2175                 var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
2176                 if (tag == '*'){
2177                         if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
2178                 } else {
2179                         if (nodeName != tag) return false;
2180                 }
2181         }
2183         if (id && node.getAttribute('id') != id) return false;
2185         var i, part, cls;
2186         if (classes) for (i = classes.length; i--;){
2187                 cls = this.getAttribute(node, 'class');
2188                 if (!(cls && classes[i].regexp.test(cls))) return false;
2189         }
2190         if (attributes) for (i = attributes.length; i--;){
2191                 part = attributes[i];
2192                 if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
2193         }
2194         if (pseudos) for (i = pseudos.length; i--;){
2195                 part = pseudos[i];
2196                 if (!this.matchPseudo(node, part.key, part.value)) return false;
2197         }
2198         return true;
2201 var combinators = {
2203         ' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
2205                 var i, item, children;
2207                 if (this.isHTMLDocument){
2208                         getById: if (id){
2209                                 item = this.document.getElementById(id);
2210                                 if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){
2211                                         // all[id] returns all the elements with that name or id inside node
2212                                         // if theres just one it will return the element, else it will be a collection
2213                                         children = node.all[id];
2214                                         if (!children) return;
2215                                         if (!children[0]) children = [children];
2216                                         for (i = 0; item = children[i++];){
2217                                                 var idNode = item.getAttributeNode('id');
2218                                                 if (idNode && idNode.nodeValue == id){
2219                                                         this.push(item, tag, null, classes, attributes, pseudos);
2220                                                         break;
2221                                                 }
2222                                         }
2223                                         return;
2224                                 }
2225                                 if (!item){
2226                                         // if the context is in the dom we return, else we will try GEBTN, breaking the getById label
2227                                         if (this.contains(this.root, node)) return;
2228                                         else break getById;
2229                                 } else if (this.document !== node && !this.contains(node, item)) return;
2230                                 this.push(item, tag, null, classes, attributes, pseudos);
2231                                 return;
2232                         }
2233                         getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
2234                                 children = node.getElementsByClassName(classList.join(' '));
2235                                 if (!(children && children.length)) break getByClass;
2236                                 for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
2237                                 return;
2238                         }
2239                 }
2240                 getByTag: {
2241                         children = node.getElementsByTagName(tag);
2242                         if (!(children && children.length)) break getByTag;
2243                         if (!this.brokenStarGEBTN) tag = null;
2244                         for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
2245                 }
2246         },
2248         '>': function(node, tag, id, classes, attributes, pseudos){ // direct children
2249                 if ((node = node.firstChild)) do {
2250                         if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
2251                 } while ((node = node.nextSibling));
2252         },
2254         '+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
2255                 while ((node = node.nextSibling)) if (node.nodeType == 1){
2256                         this.push(node, tag, id, classes, attributes, pseudos);
2257                         break;
2258                 }
2259         },
2261         '^': function(node, tag, id, classes, attributes, pseudos){ // first child
2262                 node = node.firstChild;
2263                 if (node){
2264                         if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
2265                         else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
2266                 }
2267         },
2269         '~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
2270                 while ((node = node.nextSibling)){
2271                         if (node.nodeType != 1) continue;
2272                         var uid = this.getUID(node);
2273                         if (this.bitUniques[uid]) break;
2274                         this.bitUniques[uid] = true;
2275                         this.push(node, tag, id, classes, attributes, pseudos);
2276                 }
2277         },
2279         '++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
2280                 this['combinator:+'](node, tag, id, classes, attributes, pseudos);
2281                 this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
2282         },
2284         '~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
2285                 this['combinator:~'](node, tag, id, classes, attributes, pseudos);
2286                 this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
2287         },
2289         '!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document
2290                 while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
2291         },
2293         '!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
2294                 node = node.parentNode;
2295                 if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
2296         },
2298         '!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
2299                 while ((node = node.previousSibling)) if (node.nodeType == 1){
2300                         this.push(node, tag, id, classes, attributes, pseudos);
2301                         break;
2302                 }
2303         },
2305         '!^': function(node, tag, id, classes, attributes, pseudos){ // last child
2306                 node = node.lastChild;
2307                 if (node){
2308                         if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
2309                         else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
2310                 }
2311         },
2313         '!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
2314                 while ((node = node.previousSibling)){
2315                         if (node.nodeType != 1) continue;
2316                         var uid = this.getUID(node);
2317                         if (this.bitUniques[uid]) break;
2318                         this.bitUniques[uid] = true;
2319                         this.push(node, tag, id, classes, attributes, pseudos);
2320                 }
2321         }
2325 for (var c in combinators) local['combinator:' + c] = combinators[c];
2327 var pseudos = {
2329         /*<pseudo-selectors>*/
2331         'empty': function(node){
2332                 var child = node.firstChild;
2333                 return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
2334         },
2336         'not': function(node, expression){
2337                 return !this.matchNode(node, expression);
2338         },
2340         'contains': function(node, text){
2341                 return (node.innerText || node.textContent || '').indexOf(text) > -1;
2342         },
2344         'first-child': function(node){
2345                 while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
2346                 return true;
2347         },
2349         'last-child': function(node){
2350                 while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
2351                 return true;
2352         },
2354         'only-child': function(node){
2355                 var prev = node;
2356                 while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
2357                 var next = node;
2358                 while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
2359                 return true;
2360         },
2362         /*<nth-pseudo-selectors>*/
2364         'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
2366         'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
2368         'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
2370         'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
2372         'index': function(node, index){
2373                 return this['pseudo:nth-child'](node, '' + (index + 1));
2374         },
2376         'even': function(node){
2377                 return this['pseudo:nth-child'](node, '2n');
2378         },
2380         'odd': function(node){
2381                 return this['pseudo:nth-child'](node, '2n+1');
2382         },
2384         /*</nth-pseudo-selectors>*/
2386         /*<of-type-pseudo-selectors>*/
2388         'first-of-type': function(node){
2389                 var nodeName = node.nodeName;
2390                 while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
2391                 return true;
2392         },
2394         'last-of-type': function(node){
2395                 var nodeName = node.nodeName;
2396                 while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
2397                 return true;
2398         },
2400         'only-of-type': function(node){
2401                 var prev = node, nodeName = node.nodeName;
2402                 while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
2403                 var next = node;
2404                 while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
2405                 return true;
2406         },
2408         /*</of-type-pseudo-selectors>*/
2410         // custom pseudos
2412         'enabled': function(node){
2413                 return !node.disabled;
2414         },
2416         'disabled': function(node){
2417                 return node.disabled;
2418         },
2420         'checked': function(node){
2421                 return node.checked || node.selected;
2422         },
2424         'focus': function(node){
2425                 return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
2426         },
2428         'root': function(node){
2429                 return (node === this.root);
2430         },
2432         'selected': function(node){
2433                 return node.selected;
2434         }
2436         /*</pseudo-selectors>*/
2439 for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
2441 // attributes methods
2443 var attributeGetters = local.attributeGetters = {
2445         'for': function(){
2446                 return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
2447         },
2449         'href': function(){
2450                 return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
2451         },
2453         'style': function(){
2454                 return (this.style) ? this.style.cssText : this.getAttribute('style');
2455         },
2457         'tabindex': function(){
2458                 var attributeNode = this.getAttributeNode('tabindex');
2459                 return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
2460         },
2462         'type': function(){
2463                 return this.getAttribute('type');
2464         },
2466         'maxlength': function(){
2467                 var attributeNode = this.getAttributeNode('maxLength');
2468                 return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
2469         }
2473 attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength;
2475 // Slick
2477 var Slick = local.Slick = (this.Slick || {});
2479 Slick.version = '1.1.7';
2481 // Slick finder
2483 Slick.search = function(context, expression, append){
2484         return local.search(context, expression, append);
2487 Slick.find = function(context, expression){
2488         return local.search(context, expression, null, true);
2491 // Slick containment checker
2493 Slick.contains = function(container, node){
2494         local.setDocument(container);
2495         return local.contains(container, node);
2498 // Slick attribute getter
2500 Slick.getAttribute = function(node, name){
2501         local.setDocument(node);
2502         return local.getAttribute(node, name);
2505 Slick.hasAttribute = function(node, name){
2506         local.setDocument(node);
2507         return local.hasAttribute(node, name);
2510 // Slick matcher
2512 Slick.match = function(node, selector){
2513         if (!(node && selector)) return false;
2514         if (!selector || selector === node) return true;
2515         local.setDocument(node);
2516         return local.matchNode(node, selector);
2519 // Slick attribute accessor
2521 Slick.defineAttributeGetter = function(name, fn){
2522         local.attributeGetters[name] = fn;
2523         return this;
2526 Slick.lookupAttributeGetter = function(name){
2527         return local.attributeGetters[name];
2530 // Slick pseudo accessor
2532 Slick.definePseudo = function(name, fn){
2533         local['pseudo:' + name] = function(node, argument){
2534                 return fn.call(node, argument);
2535         };
2536         return this;
2539 Slick.lookupPseudo = function(name){
2540         var pseudo = local['pseudo:' + name];
2541         if (pseudo) return function(argument){
2542                 return pseudo.call(this, argument);
2543         };
2544         return null;
2547 // Slick overrides accessor
2549 Slick.override = function(regexp, fn){
2550         local.override(regexp, fn);
2551         return this;
2554 Slick.isXML = local.isXML;
2556 Slick.uidOf = function(node){
2557         return local.getUIDHTML(node);
2560 if (!this.Slick) this.Slick = Slick;
2562 }).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
2567 name: Element
2569 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.
2571 license: MIT-style license.
2573 requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder]
2575 provides: [Element, Elements, $, $$, IFrame, Selectors]
2580 var Element = this.Element = function(tag, props){
2581         var konstructor = Element.Constructors[tag];
2582         if (konstructor) return konstructor(props);
2583         if (typeof tag != 'string') return document.id(tag).set(props);
2585         if (!props) props = {};
2587         if (!(/^[\w-]+$/).test(tag)){
2588                 var parsed = Slick.parse(tag).expressions[0][0];
2589                 tag = (parsed.tag == '*') ? 'div' : parsed.tag;
2590                 if (parsed.id && props.id == null) props.id = parsed.id;
2592                 var attributes = parsed.attributes;
2593                 if (attributes) for (var attr, i = 0, l = attributes.length; i < l; i++){
2594                         attr = attributes[i];
2595                         if (props[attr.key] != null) continue;
2597                         if (attr.value != null && attr.operator == '=') props[attr.key] = attr.value;
2598                         else if (!attr.value && !attr.operator) props[attr.key] = true;
2599                 }
2601                 if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' ');
2602         }
2604         return document.newElement(tag, props);
2608 if (Browser.Element){
2609         Element.prototype = Browser.Element.prototype;
2610         // IE8 and IE9 require the wrapping.
2611         Element.prototype._fireEvent = (function(fireEvent){
2612                 return function(type, event){
2613                         return fireEvent.call(this, type, event);
2614                 };
2615         })(Element.prototype.fireEvent);
2618 new Type('Element', Element).mirror(function(name){
2619         if (Array.prototype[name]) return;
2621         var obj = {};
2622         obj[name] = function(){
2623                 var results = [], args = arguments, elements = true;
2624                 for (var i = 0, l = this.length; i < l; i++){
2625                         var element = this[i], result = results[i] = element[name].apply(element, args);
2626                         elements = (elements && typeOf(result) == 'element');
2627                 }
2628                 return (elements) ? new Elements(results) : results;
2629         };
2631         Elements.implement(obj);
2634 if (!Browser.Element){
2635         Element.parent = Object;
2637         Element.Prototype = {
2638                 '$constructor': Element,
2639                 '$family': Function.from('element').hide()
2640         };
2642         Element.mirror(function(name, method){
2643                 Element.Prototype[name] = method;
2644         });
2647 Element.Constructors = {};
2651 var IFrame = new Type('IFrame', function(){
2652         var params = Array.link(arguments, {
2653                 properties: Type.isObject,
2654                 iframe: function(obj){
2655                         return (obj != null);
2656                 }
2657         });
2659         var props = params.properties || {}, iframe;
2660         if (params.iframe) iframe = document.id(params.iframe);
2661         var onload = props.onload || function(){};
2662         delete props.onload;
2663         props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.uniqueID()].pick();
2664         iframe = new Element(iframe || 'iframe', props);
2666         var onLoad = function(){
2667                 onload.call(iframe.contentWindow);
2668         };
2670         if (window.frames[props.id]) onLoad();
2671         else iframe.addListener('load', onLoad);
2672         return iframe;
2675 var Elements = this.Elements = function(nodes){
2676         if (nodes && nodes.length){
2677                 var uniques = {}, node;
2678                 for (var i = 0; node = nodes[i++];){
2679                         var uid = Slick.uidOf(node);
2680                         if (!uniques[uid]){
2681                                 uniques[uid] = true;
2682                                 this.push(node);
2683                         }
2684                 }
2685         }
2688 Elements.prototype = {length: 0};
2689 Elements.parent = Array;
2691 new Type('Elements', Elements).implement({
2693         filter: function(filter, bind){
2694                 if (!filter) return this;
2695                 return new Elements(Array.filter(this, (typeOf(filter) == 'string') ? function(item){
2696                         return item.match(filter);
2697                 } : filter, bind));
2698         }.protect(),
2700         push: function(){
2701                 var length = this.length;
2702                 for (var i = 0, l = arguments.length; i < l; i++){
2703                         var item = document.id(arguments[i]);
2704                         if (item) this[length++] = item;
2705                 }
2706                 return (this.length = length);
2707         }.protect(),
2709         unshift: function(){
2710                 var items = [];
2711                 for (var i = 0, l = arguments.length; i < l; i++){
2712                         var item = document.id(arguments[i]);
2713                         if (item) items.push(item);
2714                 }
2715                 return Array.prototype.unshift.apply(this, items);
2716         }.protect(),
2718         concat: function(){
2719                 var newElements = new Elements(this);
2720                 for (var i = 0, l = arguments.length; i < l; i++){
2721                         var item = arguments[i];
2722                         if (Type.isEnumerable(item)) newElements.append(item);
2723                         else newElements.push(item);
2724                 }
2725                 return newElements;
2726         }.protect(),
2728         append: function(collection){
2729                 for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]);
2730                 return this;
2731         }.protect(),
2733         empty: function(){
2734                 while (this.length) delete this[--this.length];
2735                 return this;
2736         }.protect()
2742 (function(){
2744 // FF, IE
2745 var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2};
2747 splice.call(object, 1, 1);
2748 if (object[1] == 1) Elements.implement('splice', function(){
2749         var length = this.length;
2750         var result = splice.apply(this, arguments);
2751         while (length >= this.length) delete this[length--];
2752         return result;
2753 }.protect());
2755 Array.forEachMethod(function(method, name){
2756         Elements.implement(name, method);
2759 Array.mirror(Elements);
2761 /*<ltIE8>*/
2762 var createElementAcceptsHTML;
2763 try {
2764         createElementAcceptsHTML = (document.createElement('<input name=x>').name == 'x');
2765 } catch (e){}
2767 var escapeQuotes = function(html){
2768         return ('' + html).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
2770 /*</ltIE8>*/
2772 /*<ltIE9>*/
2773 // #2479 - IE8 Cannot set HTML of style element
2774 var canChangeStyleHTML = (function(){
2775     var div = document.createElement('style'),
2776         flag = false;
2777     try {
2778         div.innerHTML = '#justTesing{margin: 0px;}';
2779         flag = !!div.innerHTML;
2780     } catch(e){}
2781     return flag;
2782 })();
2783 /*</ltIE9>*/
2785 Document.implement({
2787         newElement: function(tag, props){
2788                 if (props){
2789                         if (props.checked != null) props.defaultChecked = props.checked;
2790                         if ((props.type == 'checkbox' || props.type == 'radio') && props.value == null) props.value = 'on'; 
2791                         /*<ltIE9>*/ // IE needs the type to be set before changing content of style element
2792                         if (!canChangeStyleHTML && tag == 'style'){
2793                                 var styleElement = document.createElement('style');
2794                                 styleElement.setAttribute('type', 'text/css');
2795                                 if (props.type) delete props.type;
2796                                 return this.id(styleElement).set(props);
2797                         }
2798                         /*</ltIE9>*/
2799                         /*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
2800                         if (createElementAcceptsHTML){
2801                                 tag = '<' + tag;
2802                                 if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
2803                                 if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
2804                                 tag += '>';
2805                                 delete props.name;
2806                                 delete props.type;
2807                         }
2808                         /*</ltIE8>*/
2809                 }
2810                 return this.id(this.createElement(tag)).set(props);
2811         }
2815 })();
2817 (function(){
2819 Slick.uidOf(window);
2820 Slick.uidOf(document);
2822 Document.implement({
2824         newTextNode: function(text){
2825                 return this.createTextNode(text);
2826         },
2828         getDocument: function(){
2829                 return this;
2830         },
2832         getWindow: function(){
2833                 return this.window;
2834         },
2836         id: (function(){
2838                 var types = {
2840                         string: function(id, nocash, doc){
2841                                 id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1'));
2842                                 return (id) ? types.element(id, nocash) : null;
2843                         },
2845                         element: function(el, nocash){
2846                                 Slick.uidOf(el);
2847                                 if (!nocash && !el.$family && !(/^(?:object|embed)$/i).test(el.tagName)){
2848                                         var fireEvent = el.fireEvent;
2849                                         // wrapping needed in IE7, or else crash
2850                                         el._fireEvent = function(type, event){
2851                                                 return fireEvent(type, event);
2852                                         };
2853                                         Object.append(el, Element.Prototype);
2854                                 }
2855                                 return el;
2856                         },
2858                         object: function(obj, nocash, doc){
2859                                 if (obj.toElement) return types.element(obj.toElement(doc), nocash);
2860                                 return null;
2861                         }
2863                 };
2865                 types.textnode = types.whitespace = types.window = types.document = function(zero){
2866                         return zero;
2867                 };
2869                 return function(el, nocash, doc){
2870                         if (el && el.$family && el.uniqueNumber) return el;
2871                         var type = typeOf(el);
2872                         return (types[type]) ? types[type](el, nocash, doc || document) : null;
2873                 };
2875         })()
2879 if (window.$ == null) Window.implement('$', function(el, nc){
2880         return document.id(el, nc, this.document);
2883 Window.implement({
2885         getDocument: function(){
2886                 return this.document;
2887         },
2889         getWindow: function(){
2890                 return this;
2891         }
2895 [Document, Element].invoke('implement', {
2897         getElements: function(expression){
2898                 return Slick.search(this, expression, new Elements);
2899         },
2901         getElement: function(expression){
2902                 return document.id(Slick.find(this, expression));
2903         }
2907 var contains = {contains: function(element){
2908         return Slick.contains(this, element);
2911 if (!document.contains) Document.implement(contains);
2912 if (!document.createElement('div').contains) Element.implement(contains);
2916 // tree walking
2918 var injectCombinator = function(expression, combinator){
2919         if (!expression) return combinator;
2921         expression = Object.clone(Slick.parse(expression));
2923         var expressions = expression.expressions;
2924         for (var i = expressions.length; i--;)
2925                 expressions[i][0].combinator = combinator;
2927         return expression;
2930 Object.forEach({
2931         getNext: '~',
2932         getPrevious: '!~',
2933         getParent: '!'
2934 }, function(combinator, method){
2935         Element.implement(method, function(expression){
2936                 return this.getElement(injectCombinator(expression, combinator));
2937         });
2940 Object.forEach({
2941         getAllNext: '~',
2942         getAllPrevious: '!~',
2943         getSiblings: '~~',
2944         getChildren: '>',
2945         getParents: '!'
2946 }, function(combinator, method){
2947         Element.implement(method, function(expression){
2948                 return this.getElements(injectCombinator(expression, combinator));
2949         });
2952 Element.implement({
2954         getFirst: function(expression){
2955                 return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]);
2956         },
2958         getLast: function(expression){
2959                 return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast());
2960         },
2962         getWindow: function(){
2963                 return this.ownerDocument.window;
2964         },
2966         getDocument: function(){
2967                 return this.ownerDocument;
2968         },
2970         getElementById: function(id){
2971                 return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1')));
2972         },
2974         match: function(expression){
2975                 return !expression || Slick.match(this, expression);
2976         }
2982 if (window.$$ == null) Window.implement('$$', function(selector){
2983         if (arguments.length == 1){
2984                 if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements);
2985                 else if (Type.isEnumerable(selector)) return new Elements(selector);
2986         }
2987         return new Elements(arguments);
2990 // Inserters
2992 var inserters = {
2994         before: function(context, element){
2995                 var parent = element.parentNode;
2996                 if (parent) parent.insertBefore(context, element);
2997         },
2999         after: function(context, element){
3000                 var parent = element.parentNode;
3001                 if (parent) parent.insertBefore(context, element.nextSibling);
3002         },
3004         bottom: function(context, element){
3005                 element.appendChild(context);
3006         },
3008         top: function(context, element){
3009                 element.insertBefore(context, element.firstChild);
3010         }
3014 inserters.inside = inserters.bottom;
3018 // getProperty / setProperty
3020 var propertyGetters = {}, propertySetters = {};
3022 // properties
3024 var properties = {};
3025 Array.forEach([
3026         'type', 'value', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan',
3027         'frameBorder', 'rowSpan', 'tabIndex', 'useMap'
3028 ], function(property){
3029         properties[property.toLowerCase()] = property;
3032 properties.html = 'innerHTML';
3033 properties.text = (document.createElement('div').textContent == null) ? 'innerText': 'textContent';
3035 Object.forEach(properties, function(real, key){
3036         propertySetters[key] = function(node, value){
3037                 node[real] = value;
3038         };
3039         propertyGetters[key] = function(node){
3040                 return node[real];
3041         };
3044 /*<ltIE9>*/
3045 propertySetters.text = (function(setter){
3046         return function(node, value){
3047                 if (node.get('tag') == 'style') node.set('html', value);
3048                 else node[properties.text] = value;
3049         };
3050 })(propertySetters.text);
3052 propertyGetters.text = (function(getter){
3053         return function(node){
3054                 return (node.get('tag') == 'style') ? node.innerHTML : getter(node);
3055         };
3056 })(propertyGetters.text);
3057 /*</ltIE9>*/
3059 // Booleans
3061 var bools = [
3062         'compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked',
3063         'disabled', 'readOnly', 'multiple', 'selected', 'noresize',
3064         'defer', 'defaultChecked', 'autofocus', 'controls', 'autoplay',
3065         'loop'
3068 var booleans = {};
3069 Array.forEach(bools, function(bool){
3070         var lower = bool.toLowerCase();
3071         booleans[lower] = bool;
3072         propertySetters[lower] = function(node, value){
3073                 node[bool] = !!value;
3074         };
3075         propertyGetters[lower] = function(node){
3076                 return !!node[bool];
3077         };
3080 // Special cases
3082 Object.append(propertySetters, {
3084         'class': function(node, value){
3085                 ('className' in node) ? node.className = (value || '') : node.setAttribute('class', value);
3086         },
3088         'for': function(node, value){
3089                 ('htmlFor' in node) ? node.htmlFor = value : node.setAttribute('for', value);
3090         },
3092         'style': function(node, value){
3093                 (node.style) ? node.style.cssText = value : node.setAttribute('style', value);
3094         },
3096         'value': function(node, value){
3097                 node.value = (value != null) ? value : '';
3098         }
3102 propertyGetters['class'] = function(node){
3103         return ('className' in node) ? node.className || null : node.getAttribute('class');
3106 /* <webkit> */
3107 var el = document.createElement('button');
3108 // IE sets type as readonly and throws
3109 try { el.type = 'button'; } catch(e){}
3110 if (el.type != 'button') propertySetters.type = function(node, value){
3111         node.setAttribute('type', value);
3113 el = null;
3114 /* </webkit> */
3116 /*<IE>*/
3118 /*<ltIE9>*/
3119 // #2479 - IE8 Cannot set HTML of style element
3120 var canChangeStyleHTML = (function(){
3121     var div = document.createElement('style'),
3122         flag = false;
3123     try {
3124         div.innerHTML = '#justTesing{margin: 0px;}';
3125         flag = !!div.innerHTML;
3126     } catch(e){}
3127     return flag;
3128 })();
3129 /*</ltIE9>*/
3131 var input = document.createElement('input'), volatileInputValue, html5InputSupport;
3133 // #2178
3134 input.value = 't';
3135 input.type = 'submit';
3136 volatileInputValue = input.value != 't';
3138 // #2443 - IE throws "Invalid Argument" when trying to use html5 input types
3139 try {
3140         input.type = 'email';
3141         html5InputSupport = input.type == 'email';
3142 } catch(e){}
3144 input = null;
3146 if (volatileInputValue || !html5InputSupport) propertySetters.type = function(node, type){
3147         try {
3148                 var value = node.value;
3149                 node.type = type;
3150                 node.value = value;
3151         } catch (e){}
3153 /*</IE>*/
3155 /* getProperty, setProperty */
3157 /* <ltIE9> */
3158 var pollutesGetAttribute = (function(div){
3159         div.random = 'attribute';
3160         return (div.getAttribute('random') == 'attribute');
3161 })(document.createElement('div'));
3163 var hasCloneBug = (function(test){
3164         test.innerHTML = '<object><param name="should_fix" value="the unknown" /></object>';
3165         return test.cloneNode(true).firstChild.childNodes.length != 1;
3166 })(document.createElement('div'));
3167 /* </ltIE9> */
3169 var hasClassList = !!document.createElement('div').classList;
3171 var classes = function(className){
3172         var classNames = (className || '').clean().split(" "), uniques = {};
3173         return classNames.filter(function(className){
3174                 if (className !== "" && !uniques[className]) return uniques[className] = className;
3175         });
3178 var addToClassList = function(name){
3179         this.classList.add(name);
3182 var removeFromClassList = function(name){
3183         this.classList.remove(name);
3186 Element.implement({
3188         setProperty: function(name, value){
3189                 var setter = propertySetters[name.toLowerCase()];
3190                 if (setter){
3191                         setter(this, value);
3192                 } else {
3193                         /* <ltIE9> */
3194                         var attributeWhiteList;
3195                         if (pollutesGetAttribute) attributeWhiteList = this.retrieve('$attributeWhiteList', {});
3196                         /* </ltIE9> */
3198                         if (value == null){
3199                                 this.removeAttribute(name);
3200                                 /* <ltIE9> */
3201                                 if (pollutesGetAttribute) delete attributeWhiteList[name];
3202                                 /* </ltIE9> */
3203                         } else {
3204                                 this.setAttribute(name, '' + value);
3205                                 /* <ltIE9> */
3206                                 if (pollutesGetAttribute) attributeWhiteList[name] = true;
3207                                 /* </ltIE9> */
3208                         }
3209                 }
3210                 return this;
3211         },
3213         setProperties: function(attributes){
3214                 for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
3215                 return this;
3216         },
3218         getProperty: function(name){
3219                 var getter = propertyGetters[name.toLowerCase()];
3220                 if (getter) return getter(this);
3221                 /* <ltIE9> */
3222                 if (pollutesGetAttribute){
3223                         var attr = this.getAttributeNode(name), attributeWhiteList = this.retrieve('$attributeWhiteList', {});
3224                         if (!attr) return null;
3225                         if (attr.expando && !attributeWhiteList[name]){
3226                                 var outer = this.outerHTML;
3227                                 // segment by the opening tag and find mention of attribute name
3228                                 if (outer.substr(0, outer.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(name) < 0) return null;
3229                                 attributeWhiteList[name] = true;
3230                         }
3231                 }
3232                 /* </ltIE9> */
3233                 var result = Slick.getAttribute(this, name);
3234                 return (!result && !Slick.hasAttribute(this, name)) ? null : result;
3235         },
3237         getProperties: function(){
3238                 var args = Array.from(arguments);
3239                 return args.map(this.getProperty, this).associate(args);
3240         },
3242         removeProperty: function(name){
3243                 return this.setProperty(name, null);
3244         },
3246         removeProperties: function(){
3247                 Array.each(arguments, this.removeProperty, this);
3248                 return this;
3249         },
3251         set: function(prop, value){
3252                 var property = Element.Properties[prop];
3253                 (property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value);
3254         }.overloadSetter(),
3256         get: function(prop){
3257                 var property = Element.Properties[prop];
3258                 return (property && property.get) ? property.get.apply(this) : this.getProperty(prop);
3259         }.overloadGetter(),
3261         erase: function(prop){
3262                 var property = Element.Properties[prop];
3263                 (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
3264                 return this;
3265         },
3267         hasClass: hasClassList ? function(className){
3268                 return this.classList.contains(className);
3269         } : function(className){
3270                 return classes(this.className).contains(className);
3271         },
3273         addClass: hasClassList ? function(className){
3274                 classes(className).forEach(addToClassList, this);
3275                 return this;
3276         } : function(className){
3277                 this.className = classes(className + ' ' + this.className).join(' ');
3278                 return this;
3279         },
3281         removeClass: hasClassList ? function(className){
3282                 classes(className).forEach(removeFromClassList, this);
3283                 return this;
3284         } : function(className){
3285                 var classNames = classes(this.className);
3286                 classes(className).forEach(classNames.erase, classNames);
3287                 this.className = classNames.join(' ');
3288                 return this;
3289         },
3291         toggleClass: function(className, force){
3292                 if (force == null) force = !this.hasClass(className);
3293                 return (force) ? this.addClass(className) : this.removeClass(className);
3294         },
3296         adopt: function(){
3297                 var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length;
3298                 if (length > 1) parent = fragment = document.createDocumentFragment();
3300                 for (var i = 0; i < length; i++){
3301                         var element = document.id(elements[i], true);
3302                         if (element) parent.appendChild(element);
3303                 }
3305                 if (fragment) this.appendChild(fragment);
3307                 return this;
3308         },
3310         appendText: function(text, where){
3311                 return this.grab(this.getDocument().newTextNode(text), where);
3312         },
3314         grab: function(el, where){
3315                 inserters[where || 'bottom'](document.id(el, true), this);
3316                 return this;
3317         },
3319         inject: function(el, where){
3320                 inserters[where || 'bottom'](this, document.id(el, true));
3321                 return this;
3322         },
3324         replaces: function(el){
3325                 el = document.id(el, true);
3326                 el.parentNode.replaceChild(this, el);
3327                 return this;
3328         },
3330         wraps: function(el, where){
3331                 el = document.id(el, true);
3332                 return this.replaces(el).grab(el, where);
3333         },
3335         getSelected: function(){
3336                 this.selectedIndex; // Safari 3.2.1
3337                 return new Elements(Array.from(this.options).filter(function(option){
3338                         return option.selected;
3339                 }));
3340         },
3342         toQueryString: function(){
3343                 var queryString = [];
3344                 this.getElements('input, select, textarea').each(function(el){
3345                         var type = el.type;
3346                         if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return;
3348                         var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){
3349                                 // IE
3350                                 return document.id(opt).get('value');
3351                         }) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value');
3353                         Array.from(value).each(function(val){
3354                                 if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val));
3355                         });
3356                 });
3357                 return queryString.join('&');
3358         }
3363 // appendHTML
3365 var appendInserters = {
3366         before: 'beforeBegin',
3367         after: 'afterEnd',
3368         bottom: 'beforeEnd',
3369         top: 'afterBegin',
3370         inside: 'beforeEnd'
3373 Element.implement('appendHTML', ('insertAdjacentHTML' in document.createElement('div')) ? function(html, where){
3374         this.insertAdjacentHTML(appendInserters[where || 'bottom'], html);
3375         return this;
3376 } : function(html, where){
3377         var temp = new Element('div', {html: html}),
3378                 children = temp.childNodes,
3379                 fragment = temp.firstChild;
3381         if (!fragment) return this;
3382         if (children.length > 1){
3383                 fragment = document.createDocumentFragment();
3384                 for (var i = 0, l = children.length; i < l; i++){
3385                         fragment.appendChild(children[i]);
3386                 }
3387         }
3389         inserters[where || 'bottom'](fragment, this);
3390         return this;
3393 var collected = {}, storage = {};
3395 var get = function(uid){
3396         return (storage[uid] || (storage[uid] = {}));
3399 var clean = function(item){
3400         var uid = item.uniqueNumber;
3401         if (item.removeEvents) item.removeEvents();
3402         if (item.clearAttributes) item.clearAttributes();
3403         if (uid != null){
3404                 delete collected[uid];
3405                 delete storage[uid];
3406         }
3407         return item;
3410 var formProps = {input: 'checked', option: 'selected', textarea: 'value'};
3412 Element.implement({
3414         destroy: function(){
3415                 var children = clean(this).getElementsByTagName('*');
3416                 Array.each(children, clean);
3417                 Element.dispose(this);
3418                 return null;
3419         },
3421         empty: function(){
3422                 Array.from(this.childNodes).each(Element.dispose);
3423                 return this;
3424         },
3426         dispose: function(){
3427                 return (this.parentNode) ? this.parentNode.removeChild(this) : this;
3428         },
3430         clone: function(contents, keepid){
3431                 contents = contents !== false;
3432                 var clone = this.cloneNode(contents), ce = [clone], te = [this], i;
3434                 if (contents){
3435                         ce.append(Array.from(clone.getElementsByTagName('*')));
3436                         te.append(Array.from(this.getElementsByTagName('*')));
3437                 }
3439                 for (i = ce.length; i--;){
3440                         var node = ce[i], element = te[i];
3441                         if (!keepid) node.removeAttribute('id');
3442                         /*<ltIE9>*/
3443                         if (node.clearAttributes){
3444                                 node.clearAttributes();
3445                                 node.mergeAttributes(element);
3446                                 node.removeAttribute('uniqueNumber');
3447                                 if (node.options){
3448                                         var no = node.options, eo = element.options;
3449                                         for (var j = no.length; j--;) no[j].selected = eo[j].selected;
3450                                 }
3451                         }
3452                         /*</ltIE9>*/
3453                         var prop = formProps[element.tagName.toLowerCase()];
3454                         if (prop && element[prop]) node[prop] = element[prop];
3455                 }
3457                 /*<ltIE9>*/
3458                 if (hasCloneBug){
3459                         var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object');
3460                         for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML;
3461                 }
3462                 /*</ltIE9>*/
3463                 return document.id(clone);
3464         }
3468 [Element, Window, Document].invoke('implement', {
3470         addListener: function(type, fn){
3471                 if (window.attachEvent && !window.addEventListener){
3472                         collected[Slick.uidOf(this)] = this;
3473                 }
3474                 if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]);
3475                 else this.attachEvent('on' + type, fn);
3476                 return this;
3477         },
3479         removeListener: function(type, fn){
3480                 if (this.removeEventListener) this.removeEventListener(type, fn, !!arguments[2]);
3481                 else this.detachEvent('on' + type, fn);
3482                 return this;
3483         },
3485         retrieve: function(property, dflt){
3486                 var storage = get(Slick.uidOf(this)), prop = storage[property];
3487                 if (dflt != null && prop == null) prop = storage[property] = dflt;
3488                 return prop != null ? prop : null;
3489         },
3491         store: function(property, value){
3492                 var storage = get(Slick.uidOf(this));
3493                 storage[property] = value;
3494                 return this;
3495         },
3497         eliminate: function(property){
3498                 var storage = get(Slick.uidOf(this));
3499                 delete storage[property];
3500                 return this;
3501         }
3505 /*<ltIE9>*/
3506 if (window.attachEvent && !window.addEventListener){
3507         var gc = function(){
3508                 Object.each(collected, clean);
3509                 if (window.CollectGarbage) CollectGarbage();
3510                 window.removeListener('unload', gc);
3511         }
3512         window.addListener('unload', gc);
3514 /*</ltIE9>*/
3516 Element.Properties = {};
3520 Element.Properties.style = {
3522         set: function(style){
3523                 this.style.cssText = style;
3524         },
3526         get: function(){
3527                 return this.style.cssText;
3528         },
3530         erase: function(){
3531                 this.style.cssText = '';
3532         }
3536 Element.Properties.tag = {
3538         get: function(){
3539                 return this.tagName.toLowerCase();
3540         }
3544 Element.Properties.html = {
3546         set: function(html){
3547                 if (html == null) html = '';
3548                 else if (typeOf(html) == 'array') html = html.join('');
3550                 /*<ltIE9>*/
3551                 if (this.styleSheet && !canChangeStyleHTML) this.styleSheet.cssText = html;
3552                 else /*</ltIE9>*/this.innerHTML = html;
3553         },
3554         erase: function(){
3555                 this.set('html', '');
3556         }
3560 var supportsHTML5Elements = true, supportsTableInnerHTML = true, supportsTRInnerHTML = true;
3562 /*<ltIE9>*/
3563 // technique by jdbarlett - http://jdbartlett.com/innershiv/
3564 var div = document.createElement('div');
3565 div.innerHTML = '<nav></nav>';
3566 supportsHTML5Elements = (div.childNodes.length == 1);
3567 if (!supportsHTML5Elements){
3568         var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' '),
3569                 fragment = document.createDocumentFragment(), l = tags.length;
3570         while (l--) fragment.createElement(tags[l]);
3572 div = null;
3573 /*</ltIE9>*/
3575 /*<IE>*/
3576 supportsTableInnerHTML = Function.attempt(function(){
3577         var table = document.createElement('table');
3578         table.innerHTML = '<tr><td></td></tr>';
3579         return true;
3582 /*<ltFF4>*/
3583 var tr = document.createElement('tr'), html = '<td></td>';
3584 tr.innerHTML = html;
3585 supportsTRInnerHTML = (tr.innerHTML == html);
3586 tr = null;
3587 /*</ltFF4>*/
3589 if (!supportsTableInnerHTML || !supportsTRInnerHTML || !supportsHTML5Elements){
3591         Element.Properties.html.set = (function(set){
3593                 var translations = {
3594                         table: [1, '<table>', '</table>'],
3595                         select: [1, '<select>', '</select>'],
3596                         tbody: [2, '<table><tbody>', '</tbody></table>'],
3597                         tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
3598                 };
3600                 translations.thead = translations.tfoot = translations.tbody;
3602                 return function(html){
3604                         /*<ltIE9>*/
3605                         if (this.styleSheet) return set.call(this, html);
3606                         /*</ltIE9>*/
3607                         var wrap = translations[this.get('tag')];
3608                         if (!wrap && !supportsHTML5Elements) wrap = [0, '', ''];
3609                         if (!wrap) return set.call(this, html);
3611                         var level = wrap[0], wrapper = document.createElement('div'), target = wrapper;
3612                         if (!supportsHTML5Elements) fragment.appendChild(wrapper);
3613                         wrapper.innerHTML = [wrap[1], html, wrap[2]].flatten().join('');
3614                         while (level--) target = target.firstChild;
3615                         this.empty().adopt(target.childNodes);
3616                         if (!supportsHTML5Elements) fragment.removeChild(wrapper);
3617                         wrapper = null;
3618                 };
3620         })(Element.Properties.html.set);
3622 /*</IE>*/
3624 /*<ltIE9>*/
3625 var testForm = document.createElement('form');
3626 testForm.innerHTML = '<select><option>s</option></select>';
3628 if (testForm.firstChild.value != 's') Element.Properties.value = {
3630         set: function(value){
3631                 var tag = this.get('tag');
3632                 if (tag != 'select') return this.setProperty('value', value);
3633                 var options = this.getElements('option');
3634                 value = String(value);
3635                 for (var i = 0; i < options.length; i++){
3636                         var option = options[i],
3637                                 attr = option.getAttributeNode('value'),
3638                                 optionValue = (attr && attr.specified) ? option.value : option.get('text');
3639                         if (optionValue === value) return option.selected = true;
3640                 }
3641         },
3643         get: function(){
3644                 var option = this, tag = option.get('tag');
3646                 if (tag != 'select' && tag != 'option') return this.getProperty('value');
3648                 if (tag == 'select' && !(option = option.getSelected()[0])) return '';
3650                 var attr = option.getAttributeNode('value');
3651                 return (attr && attr.specified) ? option.value : option.get('text');
3652         }
3655 testForm = null;
3656 /*</ltIE9>*/
3658 /*<IE>*/
3659 if (document.createElement('div').getAttributeNode('id')) Element.Properties.id = {
3660         set: function(id){
3661                 this.id = this.getAttributeNode('id').value = id;
3662         },
3663         get: function(){
3664                 return this.id || null;
3665         },
3666         erase: function(){
3667                 this.id = this.getAttributeNode('id').value = '';
3668         }
3670 /*</IE>*/
3672 })();
3677 name: Event
3679 description: Contains the Event Type, to make the event object cross-browser.
3681 license: MIT-style license.
3683 requires: [Window, Document, Array, Function, String, Object]
3685 provides: Event
3690 (function(){
3692 var _keys = {};
3693 var normalizeWheelSpeed = function(event){
3694     var normalized;
3695     if (event.wheelDelta){
3696         normalized = event.wheelDelta % 120 == 0 ? event.wheelDelta / 120 : event.wheelDelta / 12;
3697     } else {
3698         var rawAmount = event.deltaY || event.detail || 0;
3699         normalized = -(rawAmount % 3 == 0 ? rawAmount / 3 : rawAmount * 10);
3700     }
3701     return normalized;
3704 var DOMEvent = this.DOMEvent = new Type('DOMEvent', function(event, win){
3705         if (!win) win = window;
3706         event = event || win.event;
3707         if (event.$extended) return event;
3708         this.event = event;
3709         this.$extended = true;
3710         this.shift = event.shiftKey;
3711         this.control = event.ctrlKey;
3712         this.alt = event.altKey;
3713         this.meta = event.metaKey;
3714         var type = this.type = event.type;
3715         var target = event.target || event.srcElement;
3716         while (target && target.nodeType == 3) target = target.parentNode;
3717         this.target = document.id(target);
3719         if (type.indexOf('key') == 0){
3720                 var code = this.code = (event.which || event.keyCode);
3721                 this.key = _keys[code];
3722                 if (type == 'keydown' || type == 'keyup'){
3723                         if (code > 111 && code < 124) this.key = 'f' + (code - 111);
3724                         else if (code > 95 && code < 106) this.key = code - 96;
3725                 }
3726                 if (this.key == null) this.key = String.fromCharCode(code).toLowerCase();
3727         } else if (type == 'click' || type == 'dblclick' || type == 'contextmenu' || type == 'wheel' || type == 'DOMMouseScroll' || type.indexOf('mouse') == 0){
3728                 var doc = win.document;
3729                 doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
3730                 this.page = {
3731                         x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
3732                         y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
3733                 };
3734                 this.client = {
3735                         x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
3736                         y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
3737                 };
3738                 if (type == 'DOMMouseScroll' || type == 'wheel' || type == 'mousewheel') this.wheel = normalizeWheelSpeed(event);
3739                 this.rightClick = (event.which == 3 || event.button == 2);
3740                 if (type == 'mouseover' || type == 'mouseout'){
3741                         var related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element'];
3742                         while (related && related.nodeType == 3) related = related.parentNode;
3743                         this.relatedTarget = document.id(related);
3744                 }
3745         } else if (type.indexOf('touch') == 0 || type.indexOf('gesture') == 0){
3746                 this.rotation = event.rotation;
3747                 this.scale = event.scale;
3748                 this.targetTouches = event.targetTouches;
3749                 this.changedTouches = event.changedTouches;
3750                 var touches = this.touches = event.touches;
3751                 if (touches && touches[0]){
3752                         var touch = touches[0];
3753                         this.page = {x: touch.pageX, y: touch.pageY};
3754                         this.client = {x: touch.clientX, y: touch.clientY};
3755                 }
3756         }
3758         if (!this.client) this.client = {};
3759         if (!this.page) this.page = {};
3762 DOMEvent.implement({
3764         stop: function(){
3765                 return this.preventDefault().stopPropagation();
3766         },
3768         stopPropagation: function(){
3769                 if (this.event.stopPropagation) this.event.stopPropagation();
3770                 else this.event.cancelBubble = true;
3771                 return this;
3772         },
3774         preventDefault: function(){
3775                 if (this.event.preventDefault) this.event.preventDefault();
3776                 else this.event.returnValue = false;
3777                 return this;
3778         }
3782 DOMEvent.defineKey = function(code, key){
3783         _keys[code] = key;
3784         return this;
3787 DOMEvent.defineKeys = DOMEvent.defineKey.overloadSetter(true);
3789 DOMEvent.defineKeys({
3790         '38': 'up', '40': 'down', '37': 'left', '39': 'right',
3791         '27': 'esc', '32': 'space', '8': 'backspace', '9': 'tab',
3792         '46': 'delete', '13': 'enter'
3795 })();
3804 name: Element.Event
3806 description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events, if necessary.
3808 license: MIT-style license.
3810 requires: [Element, Event]
3812 provides: Element.Event
3817 (function(){
3819 Element.Properties.events = {set: function(events){
3820         this.addEvents(events);
3823 [Element, Window, Document].invoke('implement', {
3825         addEvent: function(type, fn){
3826                 var events = this.retrieve('events', {});
3827                 if (!events[type]) events[type] = {keys: [], values: []};
3828                 if (events[type].keys.contains(fn)) return this;
3829                 events[type].keys.push(fn);
3830                 var realType = type,
3831                         custom = Element.Events[type],
3832                         condition = fn,
3833                         self = this;
3834                 if (custom){
3835                         if (custom.onAdd) custom.onAdd.call(this, fn, type);
3836                         if (custom.condition){
3837                                 condition = function(event){
3838                                         if (custom.condition.call(this, event, type)) return fn.call(this, event);
3839                                         return true;
3840                                 };
3841                         }
3842                         if (custom.base) realType = Function.from(custom.base).call(this, type);
3843                 }
3844                 var defn = function(){
3845                         return fn.call(self);
3846                 };
3847                 var nativeEvent = Element.NativeEvents[realType];
3848                 if (nativeEvent){
3849                         if (nativeEvent == 2){
3850                                 defn = function(event){
3851                                         event = new DOMEvent(event, self.getWindow());
3852                                         if (condition.call(self, event) === false) event.stop();
3853                                 };
3854                         }
3855                         this.addListener(realType, defn, arguments[2]);
3856                 }
3857                 events[type].values.push(defn);
3858                 return this;
3859         },
3861         removeEvent: function(type, fn){
3862                 var events = this.retrieve('events');
3863                 if (!events || !events[type]) return this;
3864                 var list = events[type];
3865                 var index = list.keys.indexOf(fn);
3866                 if (index == -1) return this;
3867                 var value = list.values[index];
3868                 delete list.keys[index];
3869                 delete list.values[index];
3870                 var custom = Element.Events[type];
3871                 if (custom){
3872                         if (custom.onRemove) custom.onRemove.call(this, fn, type);
3873                         if (custom.base) type = Function.from(custom.base).call(this, type);
3874                 }
3875                 return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this;
3876         },
3878         addEvents: function(events){
3879                 for (var event in events) this.addEvent(event, events[event]);
3880                 return this;
3881         },
3883         removeEvents: function(events){
3884                 var type;
3885                 if (typeOf(events) == 'object'){
3886                         for (type in events) this.removeEvent(type, events[type]);
3887                         return this;
3888                 }
3889                 var attached = this.retrieve('events');
3890                 if (!attached) return this;
3891                 if (!events){
3892                         for (type in attached) this.removeEvents(type);
3893                         this.eliminate('events');
3894                 } else if (attached[events]){
3895                         attached[events].keys.each(function(fn){
3896                                 this.removeEvent(events, fn);
3897                         }, this);
3898                         delete attached[events];
3899                 }
3900                 return this;
3901         },
3903         fireEvent: function(type, args, delay){
3904                 var events = this.retrieve('events');
3905                 if (!events || !events[type]) return this;
3906                 args = Array.from(args);
3908                 events[type].keys.each(function(fn){
3909                         if (delay) fn.delay(delay, this, args);
3910                         else fn.apply(this, args);
3911                 }, this);
3912                 return this;
3913         },
3915         cloneEvents: function(from, type){
3916                 from = document.id(from);
3917                 var events = from.retrieve('events');
3918                 if (!events) return this;
3919                 if (!type){
3920                         for (var eventType in events) this.cloneEvents(from, eventType);
3921                 } else if (events[type]){
3922                         events[type].keys.each(function(fn){
3923                                 this.addEvent(type, fn);
3924                         }, this);
3925                 }
3926                 return this;
3927         }
3931 Element.NativeEvents = {
3932         click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
3933         wheel: 2, mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
3934         mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
3935         keydown: 2, keypress: 2, keyup: 2, //keyboard
3936         orientationchange: 2, // mobile
3937         touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch
3938         gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture
3939         focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements
3940         load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
3941         hashchange: 1, popstate: 2, // history
3942         error: 1, abort: 1, scroll: 1, message: 2 //misc
3945 Element.Events = {
3946         mousewheel: {
3947                 base: 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll'
3948         }
3951 var check = function(event){
3952         var related = event.relatedTarget;
3953         if (related == null) return true;
3954         if (!related) return false;
3955         return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
3958 if ('onmouseenter' in document.documentElement){
3959         Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2;
3960         Element.MouseenterCheck = check;
3961 } else {
3962         Element.Events.mouseenter = {
3963                 base: 'mouseover',
3964                 condition: check
3965         };
3967         Element.Events.mouseleave = {
3968                 base: 'mouseout',
3969                 condition: check
3970         };
3973 /*<ltIE9>*/
3974 if (!window.addEventListener){
3975         Element.NativeEvents.propertychange = 2;
3976         Element.Events.change = {
3977                 base: function(){
3978                         var type = this.type;
3979                         return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change';
3980                 },
3981                 condition: function(event){
3982                         return event.type != 'propertychange' || event.event.propertyName == 'checked';
3983                 }
3984         };
3986 /*</ltIE9>*/
3990 })();
3995 name: Element.Delegation
3997 description: Extends the Element native object to include the delegate method for more efficient event management.
3999 license: MIT-style license.
4001 requires: [Element.Event]
4003 provides: [Element.Delegation]
4008 (function(){
4010 var eventListenerSupport = !!window.addEventListener;
4012 Element.NativeEvents.focusin = Element.NativeEvents.focusout = 2;
4014 var bubbleUp = function(self, match, fn, event, target){
4015         while (target && target != self){
4016                 if (match(target, event)) return fn.call(target, event, target);
4017                 target = document.id(target.parentNode);
4018         }
4021 var map = {
4022         mouseenter: {
4023                 base: 'mouseover',
4024                 condition: Element.MouseenterCheck
4025         },
4026         mouseleave: {
4027                 base: 'mouseout',
4028                 condition: Element.MouseenterCheck
4029         },
4030         focus: {
4031                 base: 'focus' + (eventListenerSupport ? '' : 'in'),
4032                 capture: true
4033         },
4034         blur: {
4035                 base: eventListenerSupport ? 'blur' : 'focusout',
4036                 capture: true
4037         }
4040 /*<ltIE9>*/
4041 var _key = '$delegation:';
4042 var formObserver = function(type){
4044         return {
4046                 base: 'focusin',
4048                 remove: function(self, uid){
4049                         var list = self.retrieve(_key + type + 'listeners', {})[uid];
4050                         if (list && list.forms) for (var i = list.forms.length; i--;){
4051                                 list.forms[i].removeEvent(type, list.fns[i]);
4052                         }
4053                 },
4055                 listen: function(self, match, fn, event, target, uid){
4056                         var form = (target.get('tag') == 'form') ? target : event.target.getParent('form');
4057                         if (!form) return;
4059                         var listeners = self.retrieve(_key + type + 'listeners', {}),
4060                                 listener = listeners[uid] || {forms: [], fns: []},
4061                                 forms = listener.forms, fns = listener.fns;
4063                         if (forms.indexOf(form) != -1) return;
4064                         forms.push(form);
4066                         var _fn = function(event){
4067                                 bubbleUp(self, match, fn, event, target);
4068                         };
4069                         form.addEvent(type, _fn);
4070                         fns.push(_fn);
4072                         listeners[uid] = listener;
4073                         self.store(_key + type + 'listeners', listeners);
4074                 }
4075         };
4078 var inputObserver = function(type){
4079         return {
4080                 base: 'focusin',
4081                 listen: function(self, match, fn, event, target){
4082                         var events = {blur: function(){
4083                                 this.removeEvents(events);
4084                         }};
4085                         events[type] = function(event){
4086                                 bubbleUp(self, match, fn, event, target);
4087                         };
4088                         event.target.addEvents(events);
4089                 }
4090         };
4093 if (!eventListenerSupport) Object.append(map, {
4094         submit: formObserver('submit'),
4095         reset: formObserver('reset'),
4096         change: inputObserver('change'),
4097         select: inputObserver('select')
4099 /*</ltIE9>*/
4101 var proto = Element.prototype,
4102         addEvent = proto.addEvent,
4103         removeEvent = proto.removeEvent;
4105 var relay = function(old, method){
4106         return function(type, fn, useCapture){
4107                 if (type.indexOf(':relay') == -1) return old.call(this, type, fn, useCapture);
4108                 var parsed = Slick.parse(type).expressions[0][0];
4109                 if (parsed.pseudos[0].key != 'relay') return old.call(this, type, fn, useCapture);
4110                 var newType = parsed.tag;
4111                 parsed.pseudos.slice(1).each(function(pseudo){
4112                         newType += ':' + pseudo.key + (pseudo.value ? '(' + pseudo.value + ')' : '');
4113                 });
4114                 old.call(this, type, fn);
4115                 return method.call(this, newType, parsed.pseudos[0].value, fn);
4116         };
4119 var delegation = {
4121         addEvent: function(type, match, fn){
4122                 var storage = this.retrieve('$delegates', {}), stored = storage[type];
4123                 if (stored) for (var _uid in stored){
4124                         if (stored[_uid].fn == fn && stored[_uid].match == match) return this;
4125                 }
4127                 var _type = type, _match = match, _fn = fn, _map = map[type] || {};
4128                 type = _map.base || _type;
4130                 match = function(target){
4131                         return Slick.match(target, _match);
4132                 };
4134                 var elementEvent = Element.Events[_type];
4135                 if (_map.condition || elementEvent && elementEvent.condition){
4136                         var __match = match, condition = _map.condition || elementEvent.condition;
4137                         match = function(target, event){
4138                                 return __match(target, event) && condition.call(target, event, type);
4139                         };
4140                 }
4142                 var self = this, uid = String.uniqueID();
4143                 var delegator = _map.listen ? function(event, target){
4144                         if (!target && event && event.target) target = event.target;
4145                         if (target) _map.listen(self, match, fn, event, target, uid);
4146                 } : function(event, target){
4147                         if (!target && event && event.target) target = event.target;
4148                         if (target) bubbleUp(self, match, fn, event, target);
4149                 };
4151                 if (!stored) stored = {};
4152                 stored[uid] = {
4153                         match: _match,
4154                         fn: _fn,
4155                         delegator: delegator
4156                 };
4157                 storage[_type] = stored;
4158                 return addEvent.call(this, type, delegator, _map.capture);
4159         },
4161         removeEvent: function(type, match, fn, _uid){
4162                 var storage = this.retrieve('$delegates', {}), stored = storage[type];
4163                 if (!stored) return this;
4165                 if (_uid){
4166                         var _type = type, delegator = stored[_uid].delegator, _map = map[type] || {};
4167                         type = _map.base || _type;
4168                         if (_map.remove) _map.remove(this, _uid);
4169                         delete stored[_uid];
4170                         storage[_type] = stored;
4171                         return removeEvent.call(this, type, delegator, _map.capture);
4172                 }
4174                 var __uid, s;
4175                 if (fn) for (__uid in stored){
4176                         s = stored[__uid];
4177                         if (s.match == match && s.fn == fn) return delegation.removeEvent.call(this, type, match, fn, __uid);
4178                 } else for (__uid in stored){
4179                         s = stored[__uid];
4180                         if (s.match == match) delegation.removeEvent.call(this, type, match, s.fn, __uid);
4181                 }
4182                 return this;
4183         }
4187 [Element, Window, Document].invoke('implement', {
4188         addEvent: relay(addEvent, delegation.addEvent),
4189         removeEvent: relay(removeEvent, delegation.removeEvent)
4192 })();
4197 name: Element.Style
4199 description: Contains methods for interacting with the styles of Elements in a fashionable way.
4201 license: MIT-style license.
4203 requires: Element
4205 provides: Element.Style
4210 (function(){
4212 var html = document.html, el;
4214 //<ltIE9>
4215 // Check for oldIE, which does not remove styles when they're set to null
4216 el = document.createElement('div');
4217 el.style.color = 'red';
4218 el.style.color = null;
4219 var doesNotRemoveStyles = el.style.color == 'red';
4221 // check for oldIE, which returns border* shorthand styles in the wrong order (color-width-style instead of width-style-color)
4222 var border = '1px solid #123abc';
4223 el.style.border = border;
4224 var returnsBordersInWrongOrder = el.style.border != border;
4225 el = null;
4226 //</ltIE9>
4228 var hasGetComputedStyle = !!window.getComputedStyle,
4229         supportBorderRadius = document.createElement('div').style.borderRadius != null;
4231 Element.Properties.styles = {set: function(styles){
4232         this.setStyles(styles);
4235 var hasOpacity = (html.style.opacity != null),
4236         hasFilter = (html.style.filter != null),
4237         reAlpha = /alpha\(opacity=([\d.]+)\)/i;
4239 var setVisibility = function(element, opacity){
4240         element.store('$opacity', opacity);
4241         element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden';
4244 //<ltIE9>
4245 var setFilter = function(element, regexp, value){
4246         var style = element.style,
4247                 filter = style.filter || element.getComputedStyle('filter') || '';
4248         style.filter = (regexp.test(filter) ? filter.replace(regexp, value) : filter + ' ' + value).trim();
4249         if (!style.filter) style.removeAttribute('filter');
4251 //</ltIE9>
4253 var setOpacity = (hasOpacity ? function(element, opacity){
4254         element.style.opacity = opacity;
4255 } : (hasFilter ? function(element, opacity){
4256         if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1;
4257         if (opacity == null || opacity == 1){
4258                 setFilter(element, reAlpha, '');
4259                 if (opacity == 1 && getOpacity(element) != 1) setFilter(element, reAlpha, 'alpha(opacity=100)');
4260         } else {
4261                 setFilter(element, reAlpha, 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')');
4262         }
4263 } : setVisibility));
4265 var getOpacity = (hasOpacity ? function(element){
4266         var opacity = element.style.opacity || element.getComputedStyle('opacity');
4267         return (opacity == '') ? 1 : opacity.toFloat();
4268 } : (hasFilter ? function(element){
4269         var filter = (element.style.filter || element.getComputedStyle('filter')),
4270                 opacity;
4271         if (filter) opacity = filter.match(reAlpha);
4272         return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
4273 } : function(element){
4274         var opacity = element.retrieve('$opacity');
4275         if (opacity == null) opacity = (element.style.visibility == 'hidden' ? 0 : 1);
4276         return opacity;
4277 }));
4279 var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat',
4280         namedPositions = {left: '0%', top: '0%', center: '50%', right: '100%', bottom: '100%'},
4281         hasBackgroundPositionXY = (html.style.backgroundPositionX != null);
4283 //<ltIE9>
4284 var removeStyle = function(style, property){
4285         if (property == 'backgroundPosition'){
4286                 style.removeAttribute(property + 'X');
4287                 property += 'Y';
4288         }
4289         style.removeAttribute(property);
4291 //</ltIE9>
4293 Element.implement({
4295         getComputedStyle: function(property){
4296                 if (!hasGetComputedStyle && this.currentStyle) return this.currentStyle[property.camelCase()];
4297                 var defaultView = Element.getDocument(this).defaultView,
4298                         computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
4299                 return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : '';
4300         },
4302         setStyle: function(property, value){
4303                 if (property == 'opacity'){
4304                         if (value != null) value = parseFloat(value);
4305                         setOpacity(this, value);
4306                         return this;
4307                 }
4308                 property = (property == 'float' ? floatName : property).camelCase();
4309                 if (typeOf(value) != 'string'){
4310                         var map = (Element.Styles[property] || '@').split(' ');
4311                         value = Array.from(value).map(function(val, i){
4312                                 if (!map[i]) return '';
4313                                 return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
4314                         }).join(' ');
4315                 } else if (value == String(Number(value))){
4316                         value = Math.round(value);
4317                 }
4318                 this.style[property] = value;
4319                 //<ltIE9>
4320                 if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){
4321                         removeStyle(this.style, property);
4322                 }
4323                 //</ltIE9>
4324                 return this;
4325         },
4327         getStyle: function(property){
4328                 if (property == 'opacity') return getOpacity(this);
4329                 property = (property == 'float' ? floatName : property).camelCase();
4330                 if (supportBorderRadius && property.indexOf('borderRadius') != -1){
4331                         return ['borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius'].map(function(corner){
4332                                 return this.style[corner] || '0px';
4333                         }, this).join(' ');
4334                 }
4335                 var result = this.style[property];
4336                 if (!result || property == 'zIndex'){
4337                         if (Element.ShortStyles.hasOwnProperty(property)){
4338                                 result = [];
4339                                 for (var s in Element.ShortStyles[property]) result.push(this.getStyle(s));
4340                                 return result.join(' ');
4341                         }
4342                         result = this.getComputedStyle(property);
4343                 }
4344                 if (hasBackgroundPositionXY && /^backgroundPosition[XY]?$/.test(property)){
4345                         return result.replace(/(top|right|bottom|left)/g, function(position){
4346                                 return namedPositions[position];
4347                         }) || '0px';
4348                 }
4349                 if (!result && property == 'backgroundPosition') return '0px 0px';
4350                 if (result){
4351                         result = String(result);
4352                         var color = result.match(/rgba?\([\d\s,]+\)/);
4353                         if (color) result = result.replace(color[0], color[0].rgbToHex());
4354                 }
4355                 if (!hasGetComputedStyle && !this.style[property]){
4356                         if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){
4357                                 var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
4358                                 values.each(function(value){
4359                                         size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
4360                                 }, this);
4361                                 return this['offset' + property.capitalize()] - size + 'px';
4362                         }
4363                         if ((/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){
4364                                 return '0px';
4365                         }
4366                 }
4367                 //<ltIE9>
4368                 if (returnsBordersInWrongOrder && /^border(Top|Right|Bottom|Left)?$/.test(property) && /^#/.test(result)){
4369                         return result.replace(/^(.+)\s(.+)\s(.+)$/, '$2 $3 $1');
4370                 }
4371                 //</ltIE9>
4373                 return result;
4374         },
4376         setStyles: function(styles){
4377                 for (var style in styles) this.setStyle(style, styles[style]);
4378                 return this;
4379         },
4381         getStyles: function(){
4382                 var result = {};
4383                 Array.flatten(arguments).each(function(key){
4384                         result[key] = this.getStyle(key);
4385                 }, this);
4386                 return result;
4387         }
4391 Element.Styles = {
4392         left: '@px', top: '@px', bottom: '@px', right: '@px',
4393         width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
4394         backgroundColor: 'rgb(@, @, @)', backgroundSize: '@px', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
4395         fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
4396         margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
4397         borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
4398         zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@', borderRadius: '@px @px @px @px'
4405 Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
4407 ['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
4408         var Short = Element.ShortStyles;
4409         var All = Element.Styles;
4410         ['margin', 'padding'].each(function(style){
4411                 var sd = style + direction;
4412                 Short[style][sd] = All[sd] = '@px';
4413         });
4414         var bd = 'border' + direction;
4415         Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
4416         var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
4417         Short[bd] = {};
4418         Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
4419         Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
4420         Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
4423 if (hasBackgroundPositionXY) Element.ShortStyles.backgroundPosition = {backgroundPositionX: '@', backgroundPositionY: '@'};
4424 })();
4429 name: Element.Dimensions
4431 description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
4433 license: MIT-style license.
4435 credits:
4436   - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
4437   - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
4439 requires: [Element, Element.Style]
4441 provides: [Element.Dimensions]
4446 (function(){
4448 var element = document.createElement('div'),
4449         child = document.createElement('div');
4450 element.style.height = '0';
4451 element.appendChild(child);
4452 var brokenOffsetParent = (child.offsetParent === element);
4453 element = child = null;
4455 var heightComponents = ['height', 'paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth'],
4456         widthComponents = ['width', 'paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth'];
4458 var svgCalculateSize = function(el){
4460         var gCS = window.getComputedStyle(el),
4461                 bounds = {x: 0, y: 0};
4463         heightComponents.each(function(css){
4464                 bounds.y += parseFloat(gCS[css]);
4465         });
4466         widthComponents.each(function(css){
4467                 bounds.x += parseFloat(gCS[css]);
4468         });
4469         return bounds;
4472 var isOffset = function(el){
4473         return styleString(el, 'position') != 'static' || isBody(el);
4476 var isOffsetStatic = function(el){
4477         return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
4480 Element.implement({
4482         scrollTo: function(x, y){
4483                 if (isBody(this)){
4484                         this.getWindow().scrollTo(x, y);
4485                 } else {
4486                         this.scrollLeft = x;
4487                         this.scrollTop = y;
4488                 }
4489                 return this;
4490         },
4492         getSize: function(){
4493                 if (isBody(this)) return this.getWindow().getSize();
4495                 //<ltIE9>
4496                 // This if clause is because IE8- cannot calculate getBoundingClientRect of elements with visibility hidden.
4497                 if (!window.getComputedStyle) return {x: this.offsetWidth, y: this.offsetHeight};
4498                 //</ltIE9>
4500                 // This svg section under, calling `svgCalculateSize()`, can be removed when FF fixed the svg size bug.
4501                 // Bug info: https://bugzilla.mozilla.org/show_bug.cgi?id=530985
4502                 if (this.get('tag') == 'svg') return svgCalculateSize(this);
4503                 
4504                 var bounds = this.getBoundingClientRect();
4505                 return {x: bounds.width, y: bounds.height};
4506         },
4508         getScrollSize: function(){
4509                 if (isBody(this)) return this.getWindow().getScrollSize();
4510                 return {x: this.scrollWidth, y: this.scrollHeight};
4511         },
4513         getScroll: function(){
4514                 if (isBody(this)) return this.getWindow().getScroll();
4515                 return {x: this.scrollLeft, y: this.scrollTop};
4516         },
4518         getScrolls: function(){
4519                 var element = this.parentNode, position = {x: 0, y: 0};
4520                 while (element && !isBody(element)){
4521                         position.x += element.scrollLeft;
4522                         position.y += element.scrollTop;
4523                         element = element.parentNode;
4524                 }
4525                 return position;
4526         },
4528         getOffsetParent: brokenOffsetParent ? function(){
4529                 var element = this;
4530                 if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
4532                 var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
4533                 while ((element = element.parentNode)){
4534                         if (isOffsetCheck(element)) return element;
4535                 }
4536                 return null;
4537         } : function(){
4538                 var element = this;
4539                 if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
4541                 try {
4542                         return element.offsetParent;
4543                 } catch(e){}
4544                 return null;
4545         },
4547         getOffsets: function(){
4548                 var hasGetBoundingClientRect = this.getBoundingClientRect;
4550                 if (hasGetBoundingClientRect){
4551                         var bound = this.getBoundingClientRect(),
4552                                 html = document.id(this.getDocument().documentElement),
4553                                 htmlScroll = html.getScroll(),
4554                                 elemScrolls = this.getScrolls(),
4555                                 isFixed = (styleString(this, 'position') == 'fixed');
4557                         return {
4558                                 x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
4559                                 y: bound.top.toInt() + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
4560                         };
4561                 }
4563                 var element = this, position = {x: 0, y: 0};
4564                 if (isBody(this)) return position;
4566                 while (element && !isBody(element)){
4567                         position.x += element.offsetLeft;
4568                         position.y += element.offsetTop;
4570                         element = element.offsetParent;
4571                 }
4573                 return position;
4574         },
4576         getPosition: function(relative){
4577                 var offset = this.getOffsets(),
4578                         scroll = this.getScrolls();
4579                 var position = {
4580                         x: offset.x - scroll.x,
4581                         y: offset.y - scroll.y
4582                 };
4584                 if (relative && (relative = document.id(relative))){
4585                         var relativePosition = relative.getPosition();
4586                         return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
4587                 }
4588                 return position;
4589         },
4591         getCoordinates: function(element){
4592                 if (isBody(this)) return this.getWindow().getCoordinates();
4593                 var position = this.getPosition(element),
4594                         size = this.getSize();
4595                 var obj = {
4596                         left: position.x,
4597                         top: position.y,
4598                         width: size.x,
4599                         height: size.y
4600                 };
4601                 obj.right = obj.left + obj.width;
4602                 obj.bottom = obj.top + obj.height;
4603                 return obj;
4604         },
4606         computePosition: function(obj){
4607                 return {
4608                         left: obj.x - styleNumber(this, 'margin-left'),
4609                         top: obj.y - styleNumber(this, 'margin-top')
4610                 };
4611         },
4613         setPosition: function(obj){
4614                 return this.setStyles(this.computePosition(obj));
4615         }
4620 [Document, Window].invoke('implement', {
4622         getSize: function(){
4623                 var doc = getCompatElement(this);
4624                 return {x: doc.clientWidth, y: doc.clientHeight};
4625         },
4627         getScroll: function(){
4628                 var win = this.getWindow(), doc = getCompatElement(this);
4629                 return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
4630         },
4632         getScrollSize: function(){
4633                 var doc = getCompatElement(this),
4634                         min = this.getSize(),
4635                         body = this.getDocument().body;
4637                 return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)};
4638         },
4640         getPosition: function(){
4641                 return {x: 0, y: 0};
4642         },
4644         getCoordinates: function(){
4645                 var size = this.getSize();
4646                 return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
4647         }
4651 // private methods
4653 var styleString = Element.getComputedStyle;
4655 function styleNumber(element, style){
4656         return styleString(element, style).toInt() || 0;
4659 function borderBox(element){
4660         return styleString(element, '-moz-box-sizing') == 'border-box';
4663 function topBorder(element){
4664         return styleNumber(element, 'border-top-width');
4667 function leftBorder(element){
4668         return styleNumber(element, 'border-left-width');
4671 function isBody(element){
4672         return (/^(?:body|html)$/i).test(element.tagName);
4675 function getCompatElement(element){
4676         var doc = element.getDocument();
4677         return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
4680 })();
4682 //aliases
4683 Element.alias({position: 'setPosition'}); //compatability
4685 [Window, Document, Element].invoke('implement', {
4687         getHeight: function(){
4688                 return this.getSize().y;
4689         },
4691         getWidth: function(){
4692                 return this.getSize().x;
4693         },
4695         getScrollTop: function(){
4696                 return this.getScroll().y;
4697         },
4699         getScrollLeft: function(){
4700                 return this.getScroll().x;
4701         },
4703         getScrollHeight: function(){
4704                 return this.getScrollSize().y;
4705         },
4707         getScrollWidth: function(){
4708                 return this.getScrollSize().x;
4709         },
4711         getTop: function(){
4712                 return this.getPosition().y;
4713         },
4715         getLeft: function(){
4716                 return this.getPosition().x;
4717         }
4724 name: Fx
4726 description: Contains the basic animation logic to be extended by all other Fx Classes.
4728 license: MIT-style license.
4730 requires: [Chain, Events, Options]
4732 provides: Fx
4737 (function(){
4739 var Fx = this.Fx = new Class({
4741         Implements: [Chain, Events, Options],
4743         options: {
4744                 /*
4745                 onStart: nil,
4746                 onCancel: nil,
4747                 onComplete: nil,
4748                 */
4749                 fps: 60,
4750                 unit: false,
4751                 duration: 500,
4752                 frames: null,
4753                 frameSkip: true,
4754                 link: 'ignore'
4755         },
4757         initialize: function(options){
4758                 this.subject = this.subject || this;
4759                 this.setOptions(options);
4760         },
4762         getTransition: function(){
4763                 return function(p){
4764                         return -(Math.cos(Math.PI * p) - 1) / 2;
4765                 };
4766         },
4768         step: function(now){
4769                 if (this.options.frameSkip){
4770                         var diff = (this.time != null) ? (now - this.time) : 0, frames = diff / this.frameInterval;
4771                         this.time = now;
4772                         this.frame += frames;
4773                 } else {
4774                         this.frame++;
4775                 }
4777                 if (this.frame < this.frames){
4778                         var delta = this.transition(this.frame / this.frames);
4779                         this.set(this.compute(this.from, this.to, delta));
4780                 } else {
4781                         this.frame = this.frames;
4782                         this.set(this.compute(this.from, this.to, 1));
4783                         this.stop();
4784                 }
4785         },
4787         set: function(now){
4788                 return now;
4789         },
4791         compute: function(from, to, delta){
4792                 return Fx.compute(from, to, delta);
4793         },
4795         check: function(){
4796                 if (!this.isRunning()) return true;
4797                 switch (this.options.link){
4798                         case 'cancel': this.cancel(); return true;
4799                         case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
4800                 }
4801                 return false;
4802         },
4804         start: function(from, to){
4805                 if (!this.check(from, to)) return this;
4806                 this.from = from;
4807                 this.to = to;
4808                 this.frame = (this.options.frameSkip) ? 0 : -1;
4809                 this.time = null;
4810                 this.transition = this.getTransition();
4811                 var frames = this.options.frames, fps = this.options.fps, duration = this.options.duration;
4812                 this.duration = Fx.Durations[duration] || duration.toInt();
4813                 this.frameInterval = 1000 / fps;
4814                 this.frames = frames || Math.round(this.duration / this.frameInterval);
4815                 this.fireEvent('start', this.subject);
4816                 pushInstance.call(this, fps);
4817                 return this;
4818         },
4820         stop: function(){
4821                 if (this.isRunning()){
4822                         this.time = null;
4823                         pullInstance.call(this, this.options.fps);
4824                         if (this.frames == this.frame){
4825                                 this.fireEvent('complete', this.subject);
4826                                 if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
4827                         } else {
4828                                 this.fireEvent('stop', this.subject);
4829                         }
4830                 }
4831                 return this;
4832         },
4834         cancel: function(){
4835                 if (this.isRunning()){
4836                         this.time = null;
4837                         pullInstance.call(this, this.options.fps);
4838                         this.frame = this.frames;
4839                         this.fireEvent('cancel', this.subject).clearChain();
4840                 }
4841                 return this;
4842         },
4844         pause: function(){
4845                 if (this.isRunning()){
4846                         this.time = null;
4847                         pullInstance.call(this, this.options.fps);
4848                 }
4849                 return this;
4850         },
4852         resume: function(){
4853                 if (this.isPaused()) pushInstance.call(this, this.options.fps);
4854                 return this;
4855         },
4857         isRunning: function(){
4858                 var list = instances[this.options.fps];
4859                 return list && list.contains(this);
4860         },
4862         isPaused: function(){
4863                 return (this.frame < this.frames) && !this.isRunning();
4864         }
4868 Fx.compute = function(from, to, delta){
4869         return (to - from) * delta + from;
4872 Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
4874 // global timers
4876 var instances = {}, timers = {};
4878 var loop = function(){
4879         var now = Date.now();
4880         for (var i = this.length; i--;){
4881                 var instance = this[i];
4882                 if (instance) instance.step(now);
4883         }
4886 var pushInstance = function(fps){
4887         var list = instances[fps] || (instances[fps] = []);
4888         list.push(this);
4889         if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list);
4892 var pullInstance = function(fps){
4893         var list = instances[fps];
4894         if (list){
4895                 list.erase(this);
4896                 if (!list.length && timers[fps]){
4897                         delete instances[fps];
4898                         timers[fps] = clearInterval(timers[fps]);
4899                 }
4900         }
4903 })();
4908 name: Fx.CSS
4910 description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
4912 license: MIT-style license.
4914 requires: [Fx, Element.Style]
4916 provides: Fx.CSS
4921 Fx.CSS = new Class({
4923         Extends: Fx,
4925         //prepares the base from/to object
4927         prepare: function(element, property, values){
4928                 values = Array.from(values);
4929                 var from = values[0], to = values[1];
4930                 if (to == null){
4931                         to = from;
4932                         from = element.getStyle(property);
4933                         var unit = this.options.unit;
4934                         // adapted from: https://github.com/ryanmorr/fx/blob/master/fx.js#L299
4935                         if (unit && from && typeof from == 'string' && from.slice(-unit.length) != unit && parseFloat(from) != 0){
4936                                 element.setStyle(property, to + unit);
4937                                 var value = element.getComputedStyle(property);
4938                                 // IE and Opera support pixelLeft or pixelWidth
4939                                 if (!(/px$/.test(value))){
4940                                         value = element.style[('pixel-' + property).camelCase()];
4941                                         if (value == null){
4942                                                 // adapted from Dean Edwards' http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
4943                                                 var left = element.style.left;
4944                                                 element.style.left = to + unit;
4945                                                 value = element.style.pixelLeft;
4946                                                 element.style.left = left;
4947                                         }
4948                                 }
4949                                 from = (to || 1) / (parseFloat(value) || 1) * (parseFloat(from) || 0);
4950                                 element.setStyle(property, from + unit);
4951                         }
4952                 }
4953                 return {from: this.parse(from), to: this.parse(to)};
4954         },
4956         //parses a value into an array
4958         parse: function(value){
4959                 value = Function.from(value)();
4960                 value = (typeof value == 'string') ? value.split(' ') : Array.from(value);
4961                 return value.map(function(val){
4962                         val = String(val);
4963                         var found = false;
4964                         Object.each(Fx.CSS.Parsers, function(parser, key){
4965                                 if (found) return;
4966                                 var parsed = parser.parse(val);
4967                                 if (parsed || parsed === 0) found = {value: parsed, parser: parser};
4968                         });
4969                         found = found || {value: val, parser: Fx.CSS.Parsers.String};
4970                         return found;
4971                 });
4972         },
4974         //computes by a from and to prepared objects, using their parsers.
4976         compute: function(from, to, delta){
4977                 var computed = [];
4978                 (Math.min(from.length, to.length)).times(function(i){
4979                         computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
4980                 });
4981                 computed.$family = Function.from('fx:css:value');
4982                 return computed;
4983         },
4985         //serves the value as settable
4987         serve: function(value, unit){
4988                 if (typeOf(value) != 'fx:css:value') value = this.parse(value);
4989                 var returned = [];
4990                 value.each(function(bit){
4991                         returned = returned.concat(bit.parser.serve(bit.value, unit));
4992                 });
4993                 return returned;
4994         },
4996         //renders the change to an element
4998         render: function(element, property, value, unit){
4999                 element.setStyle(property, this.serve(value, unit));
5000         },
5002         //searches inside the page css to find the values for a selector
5004         search: function(selector){
5005                 if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
5006                 var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
5008                 var searchStyles = function(rules){
5009                         Array.each(rules, function(rule, i){
5010                                 if (rule.media){
5011                                         searchStyles(rule.rules || rule.cssRules);
5012                                         return;
5013                                 }
5014                                 if (!rule.style) return;
5015                                 var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
5016                                         return m.toLowerCase();
5017                                 }) : null;
5018                                 if (!selectorText || !selectorTest.test(selectorText)) return;
5019                                 Object.each(Element.Styles, function(value, style){
5020                                         if (!rule.style[style] || Element.ShortStyles[style]) return;
5021                                         value = String(rule.style[style]);
5022                                         to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
5023                                 });
5024                         });
5025                 };
5027                 Array.each(document.styleSheets, function(sheet, j){
5028                         var href = sheet.href;
5029                         if (href && href.indexOf('://') > -1 && href.indexOf(document.domain) == -1) return;
5030                         var rules = sheet.rules || sheet.cssRules;
5031                         searchStyles(rules);
5032                 });
5033                 return Fx.CSS.Cache[selector] = to;
5034         }
5038 Fx.CSS.Cache = {};
5040 Fx.CSS.Parsers = {
5042         Color: {
5043                 parse: function(value){
5044                         if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
5045                         return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
5046                 },
5047                 compute: function(from, to, delta){
5048                         return from.map(function(value, i){
5049                                 return Math.round(Fx.compute(from[i], to[i], delta));
5050                         });
5051                 },
5052                 serve: function(value){
5053                         return value.map(Number);
5054                 }
5055         },
5057         Number: {
5058                 parse: parseFloat,
5059                 compute: Fx.compute,
5060                 serve: function(value, unit){
5061                         return (unit) ? value + unit : value;
5062                 }
5063         },
5065         String: {
5066                 parse: Function.from(false),
5067                 compute: function(zero, one){
5068                         return one;
5069                 },
5070                 serve: function(zero){
5071                         return zero;
5072                 }
5073         }
5082 name: Fx.Morph
5084 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.
5086 license: MIT-style license.
5088 requires: Fx.CSS
5090 provides: Fx.Morph
5095 Fx.Morph = new Class({
5097         Extends: Fx.CSS,
5099         initialize: function(element, options){
5100                 this.element = this.subject = document.id(element);
5101                 this.parent(options);
5102         },
5104         set: function(now){
5105                 if (typeof now == 'string') now = this.search(now);
5106                 for (var p in now) this.render(this.element, p, now[p], this.options.unit);
5107                 return this;
5108         },
5110         compute: function(from, to, delta){
5111                 var now = {};
5112                 for (var p in from) now[p] = this.parent(from[p], to[p], delta);
5113                 return now;
5114         },
5116         start: function(properties){
5117                 if (!this.check(properties)) return this;
5118                 if (typeof properties == 'string') properties = this.search(properties);
5119                 var from = {}, to = {};
5120                 for (var p in properties){
5121                         var parsed = this.prepare(this.element, p, properties[p]);
5122                         from[p] = parsed.from;
5123                         to[p] = parsed.to;
5124                 }
5125                 return this.parent(from, to);
5126         }
5130 Element.Properties.morph = {
5132         set: function(options){
5133                 this.get('morph').cancel().setOptions(options);
5134                 return this;
5135         },
5137         get: function(){
5138                 var morph = this.retrieve('morph');
5139                 if (!morph){
5140                         morph = new Fx.Morph(this, {link: 'cancel'});
5141                         this.store('morph', morph);
5142                 }
5143                 return morph;
5144         }
5148 Element.implement({
5150         morph: function(props){
5151                 this.get('morph').start(props);
5152                 return this;
5153         }
5160 name: Fx.Transitions
5162 description: Contains a set of advanced transitions to be used with any of the Fx Classes.
5164 license: MIT-style license.
5166 credits:
5167   - Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
5169 requires: Fx
5171 provides: Fx.Transitions
5176 Fx.implement({
5178         getTransition: function(){
5179                 var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
5180                 if (typeof trans == 'string'){
5181                         var data = trans.split(':');
5182                         trans = Fx.Transitions;
5183                         trans = trans[data[0]] || trans[data[0].capitalize()];
5184                         if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
5185                 }
5186                 return trans;
5187         }
5191 Fx.Transition = function(transition, params){
5192         params = Array.from(params);
5193         var easeIn = function(pos){
5194                 return transition(pos, params);
5195         };
5196         return Object.append(easeIn, {
5197                 easeIn: easeIn,
5198                 easeOut: function(pos){
5199                         return 1 - transition(1 - pos, params);
5200                 },
5201                 easeInOut: function(pos){
5202                         return (pos <= 0.5 ? transition(2 * pos, params) : (2 - transition(2 * (1 - pos), params))) / 2;
5203                 }
5204         });
5207 Fx.Transitions = {
5209         linear: function(zero){
5210                 return zero;
5211         }
5217 Fx.Transitions.extend = function(transitions){
5218         for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
5221 Fx.Transitions.extend({
5223         Pow: function(p, x){
5224                 return Math.pow(p, x && x[0] || 6);
5225         },
5227         Expo: function(p){
5228                 return Math.pow(2, 8 * (p - 1));
5229         },
5231         Circ: function(p){
5232                 return 1 - Math.sin(Math.acos(p));
5233         },
5235         Sine: function(p){
5236                 return 1 - Math.cos(p * Math.PI / 2);
5237         },
5239         Back: function(p, x){
5240                 x = x && x[0] || 1.618;
5241                 return Math.pow(p, 2) * ((x + 1) * p - x);
5242         },
5244         Bounce: function(p){
5245                 var value;
5246                 for (var a = 0, b = 1; 1; a += b, b /= 2){
5247                         if (p >= (7 - 4 * a) / 11){
5248                                 value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
5249                                 break;
5250                         }
5251                 }
5252                 return value;
5253         },
5255         Elastic: function(p, x){
5256                 return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && x[0] || 1) / 3);
5257         }
5261 ['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
5262         Fx.Transitions[transition] = new Fx.Transition(function(p){
5263                 return Math.pow(p, i + 2);
5264         });
5270 name: Fx.Tween
5272 description: Formerly Fx.Style, effect to transition any CSS property for an element.
5274 license: MIT-style license.
5276 requires: Fx.CSS
5278 provides: [Fx.Tween, Element.fade, Element.highlight]
5283 Fx.Tween = new Class({
5285         Extends: Fx.CSS,
5287         initialize: function(element, options){
5288                 this.element = this.subject = document.id(element);
5289                 this.parent(options);
5290         },
5292         set: function(property, now){
5293                 if (arguments.length == 1){
5294                         now = property;
5295                         property = this.property || this.options.property;
5296                 }
5297                 this.render(this.element, property, now, this.options.unit);
5298                 return this;
5299         },
5301         start: function(property, from, to){
5302                 if (!this.check(property, from, to)) return this;
5303                 var args = Array.flatten(arguments);
5304                 this.property = this.options.property || args.shift();
5305                 var parsed = this.prepare(this.element, this.property, args);
5306                 return this.parent(parsed.from, parsed.to);
5307         }
5311 Element.Properties.tween = {
5313         set: function(options){
5314                 this.get('tween').cancel().setOptions(options);
5315                 return this;
5316         },
5318         get: function(){
5319                 var tween = this.retrieve('tween');
5320                 if (!tween){
5321                         tween = new Fx.Tween(this, {link: 'cancel'});
5322                         this.store('tween', tween);
5323                 }
5324                 return tween;
5325         }
5329 Element.implement({
5331         tween: function(property, from, to){
5332                 this.get('tween').start(property, from, to);
5333                 return this;
5334         },
5336         fade: function(how){
5337                 var fade = this.get('tween'), method, args = ['opacity'].append(arguments), toggle;
5338                 if (args[1] == null) args[1] = 'toggle';
5339                 switch (args[1]){
5340                         case 'in': method = 'start'; args[1] = 1; break;
5341                         case 'out': method = 'start'; args[1] = 0; break;
5342                         case 'show': method = 'set'; args[1] = 1; break;
5343                         case 'hide': method = 'set'; args[1] = 0; break;
5344                         case 'toggle':
5345                                 var flag = this.retrieve('fade:flag', this.getStyle('opacity') == 1);
5346                                 method = 'start';
5347                                 args[1] = flag ? 0 : 1;
5348                                 this.store('fade:flag', !flag);
5349                                 toggle = true;
5350                         break;
5351                         default: method = 'start';
5352                 }
5353                 if (!toggle) this.eliminate('fade:flag');
5354                 fade[method].apply(fade, args);
5355                 var to = args[args.length - 1];
5356                 if (method == 'set' || to != 0) this.setStyle('visibility', to == 0 ? 'hidden' : 'visible');
5357                 else fade.chain(function(){
5358                         this.element.setStyle('visibility', 'hidden');
5359                         this.callChain();
5360                 });
5361                 return this;
5362         },
5364         highlight: function(start, end){
5365                 if (!end){
5366                         end = this.retrieve('highlight:original', this.getStyle('background-color'));
5367                         end = (end == 'transparent') ? '#fff' : end;
5368                 }
5369                 var tween = this.get('tween');
5370                 tween.start('background-color', start || '#ffff88', end).chain(function(){
5371                         this.setStyle('background-color', this.retrieve('highlight:original'));
5372                         tween.callChain();
5373                 }.bind(this));
5374                 return this;
5375         }
5382 name: Request
5384 description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
5386 license: MIT-style license.
5388 requires: [Object, Element, Chain, Events, Options, Browser]
5390 provides: Request
5395 (function(){
5397 var empty = function(){},
5398         progressSupport = ('onprogress' in new Browser.Request);
5400 var Request = this.Request = new Class({
5402         Implements: [Chain, Events, Options],
5404         options: {/*
5405                 onRequest: function(){},
5406                 onLoadstart: function(event, xhr){},
5407                 onProgress: function(event, xhr){},
5408                 onComplete: function(){},
5409                 onCancel: function(){},
5410                 onSuccess: function(responseText, responseXML){},
5411                 onFailure: function(xhr){},
5412                 onException: function(headerName, value){},
5413                 onTimeout: function(){},
5414                 user: '',
5415                 password: '',
5416                 withCredentials: false,*/
5417                 url: '',
5418                 data: '',
5419                 headers: {
5420                         'X-Requested-With': 'XMLHttpRequest',
5421                         'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
5422                 },
5423                 async: true,
5424                 format: false,
5425                 method: 'post',
5426                 link: 'ignore',
5427                 isSuccess: null,
5428                 emulation: true,
5429                 urlEncoded: true,
5430                 encoding: 'utf-8',
5431                 evalScripts: false,
5432                 evalResponse: false,
5433                 timeout: 0,
5434                 noCache: false
5435         },
5437         initialize: function(options){
5438                 this.xhr = new Browser.Request();
5439                 this.setOptions(options);
5440                 this.headers = this.options.headers;
5441         },
5443         onStateChange: function(){
5444                 var xhr = this.xhr;
5445                 if (xhr.readyState != 4 || !this.running) return;
5446                 this.running = false;
5447                 this.status = 0;
5448                 Function.attempt(function(){
5449                         var status = xhr.status;
5450                         this.status = (status == 1223) ? 204 : status;
5451                 }.bind(this));
5452                 xhr.onreadystatechange = empty;
5453                 if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
5454                 if (this.timer){
5455                         clearTimeout(this.timer);
5456                         delete this.timer;
5457                 }
5459                 this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML};
5460                 if (this.options.isSuccess.call(this, this.status))
5461                         this.success(this.response.text, this.response.xml);
5462                 else
5463                         this.failure();
5464         },
5466         isSuccess: function(){
5467                 var status = this.status;
5468                 return (status >= 200 && status < 300);
5469         },
5471         isRunning: function(){
5472                 return !!this.running;
5473         },
5475         processScripts: function(text){
5476                 if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.exec(text);
5477                 return text.stripScripts(this.options.evalScripts);
5478         },
5480         success: function(text, xml){
5481                 this.onSuccess(this.processScripts(text), xml);
5482         },
5484         onSuccess: function(){
5485                 this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
5486         },
5488         failure: function(){
5489                 this.onFailure();
5490         },
5492         onFailure: function(){
5493                 this.fireEvent('complete').fireEvent('failure', this.xhr);
5494         },
5496         loadstart: function(event){
5497                 this.fireEvent('loadstart', [event, this.xhr]);
5498         },
5500         progress: function(event){
5501                 this.fireEvent('progress', [event, this.xhr]);
5502         },
5504         timeout: function(){
5505                 this.fireEvent('timeout', this.xhr);
5506         },
5508         setHeader: function(name, value){
5509                 this.headers[name] = value;
5510                 return this;
5511         },
5513         getHeader: function(name){
5514                 return Function.attempt(function(){
5515                         return this.xhr.getResponseHeader(name);
5516                 }.bind(this));
5517         },
5519         check: function(){
5520                 if (!this.running) return true;
5521                 switch (this.options.link){
5522                         case 'cancel': this.cancel(); return true;
5523                         case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
5524                 }
5525                 return false;
5526         },
5528         send: function(options){
5529                 if (!this.check(options)) return this;
5531                 this.options.isSuccess = this.options.isSuccess || this.isSuccess;
5532                 this.running = true;
5534                 var type = typeOf(options);
5535                 if (type == 'string' || type == 'element') options = {data: options};
5537                 var old = this.options;
5538                 options = Object.append({data: old.data, url: old.url, method: old.method}, options);
5539                 var data = options.data, url = String(options.url), method = options.method.toLowerCase();
5541                 switch (typeOf(data)){
5542                         case 'element': data = document.id(data).toQueryString(); break;
5543                         case 'object': case 'hash': data = Object.toQueryString(data);
5544                 }
5546                 if (this.options.format){
5547                         var format = 'format=' + this.options.format;
5548                         data = (data) ? format + '&' + data : format;
5549                 }
5551                 if (this.options.emulation && !['get', 'post'].contains(method)){
5552                         var _method = '_method=' + method;
5553                         data = (data) ? _method + '&' + data : _method;
5554                         method = 'post';
5555                 }
5557                 if (this.options.urlEncoded && ['post', 'put'].contains(method)){
5558                         var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
5559                         this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding;
5560                 }
5562                 if (!url) url = document.location.pathname;
5564                 var trimPosition = url.lastIndexOf('/');
5565                 if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
5567                 if (this.options.noCache)
5568                         url += (url.indexOf('?') > -1 ? '&' : '?') + String.uniqueID();
5570                 if (data && (method == 'get' || method == 'delete')){
5571                         url += (url.indexOf('?') > -1 ? '&' : '?') + data;
5572                         data = null;
5573                 }
5575                 var xhr = this.xhr;
5576                 if (progressSupport){
5577                         xhr.onloadstart = this.loadstart.bind(this);
5578                         xhr.onprogress = this.progress.bind(this);
5579                 }
5581                 xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password);
5582                 if ((this.options.withCredentials) && 'withCredentials' in xhr) xhr.withCredentials = true;
5584                 xhr.onreadystatechange = this.onStateChange.bind(this);
5586                 Object.each(this.headers, function(value, key){
5587                         try {
5588                                 xhr.setRequestHeader(key, value);
5589                         } catch (e){
5590                                 this.fireEvent('exception', [key, value]);
5591                         }
5592                 }, this);
5594                 this.fireEvent('request');
5595                 xhr.send(data);
5596                 if (!this.options.async) this.onStateChange();
5597                 else if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
5598                 return this;
5599         },
5601         cancel: function(){
5602                 if (!this.running) return this;
5603                 this.running = false;
5604                 var xhr = this.xhr;
5605                 xhr.abort();
5606                 if (this.timer){
5607                         clearTimeout(this.timer);
5608                         delete this.timer;
5609                 }
5610                 xhr.onreadystatechange = empty;
5611                 if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
5612                 this.xhr = new Browser.Request();
5613                 this.fireEvent('cancel');
5614                 return this;
5615         }
5619 var methods = {};
5620 ['get', 'post', 'put', 'delete', 'patch', 'head', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'].each(function(method){
5621         methods[method] = function(data){
5622                 var object = {
5623                         method: method
5624                 };
5625                 if (data != null) object.data = data;
5626                 return this.send(object);
5627         };
5630 Request.implement(methods);
5632 Element.Properties.send = {
5634         set: function(options){
5635                 var send = this.get('send').cancel();
5636                 send.setOptions(options);
5637                 return this;
5638         },
5640         get: function(){
5641                 var send = this.retrieve('send');
5642                 if (!send){
5643                         send = new Request({
5644                                 data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
5645                         });
5646                         this.store('send', send);
5647                 }
5648                 return send;
5649         }
5653 Element.implement({
5655         send: function(url){
5656                 var sender = this.get('send');
5657                 sender.send({data: this, url: url || sender.options.url});
5658                 return this;
5659         }
5663 })();
5668 name: Request.HTML
5670 description: Extends the basic Request Class with additional methods for interacting with HTML responses.
5672 license: MIT-style license.
5674 requires: [Element, Request]
5676 provides: Request.HTML
5681 Request.HTML = new Class({
5683         Extends: Request,
5685         options: {
5686                 update: false,
5687                 append: false,
5688                 evalScripts: true,
5689                 filter: false,
5690                 headers: {
5691                         Accept: 'text/html, application/xml, text/xml, */*'
5692                 }
5693         },
5695         success: function(text){
5696                 var options = this.options, response = this.response;
5698                 response.html = text.stripScripts(function(script){
5699                         response.javascript = script;
5700                 });
5702                 var match = response.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
5703                 if (match) response.html = match[1];
5704                 var temp = new Element('div').set('html', response.html);
5706                 response.tree = temp.childNodes;
5707                 response.elements = temp.getElements(options.filter || '*');
5709                 if (options.filter) response.tree = response.elements;
5710                 if (options.update){
5711                         var update = document.id(options.update).empty();
5712                         if (options.filter) update.adopt(response.elements);
5713                         else update.set('html', response.html);
5714                 } else if (options.append){
5715                         var append = document.id(options.append);
5716                         if (options.filter) response.elements.reverse().inject(append);
5717                         else append.adopt(temp.getChildren());
5718                 }
5719                 if (options.evalScripts) Browser.exec(response.javascript);
5721                 this.onSuccess(response.tree, response.elements, response.html, response.javascript);
5722         }
5726 Element.Properties.load = {
5728         set: function(options){
5729                 var load = this.get('load').cancel();
5730                 load.setOptions(options);
5731                 return this;
5732         },
5734         get: function(){
5735                 var load = this.retrieve('load');
5736                 if (!load){
5737                         load = new Request.HTML({data: this, link: 'cancel', update: this, method: 'get'});
5738                         this.store('load', load);
5739                 }
5740                 return load;
5741         }
5745 Element.implement({
5747         load: function(){
5748                 this.get('load').send(Array.link(arguments, {data: Type.isObject, url: Type.isString}));
5749                 return this;
5750         }
5757 name: JSON
5759 description: JSON encoder and decoder.
5761 license: MIT-style license.
5763 SeeAlso: <http://www.json.org/>
5765 requires: [Array, String, Number, Function]
5767 provides: JSON
5772 if (typeof JSON == 'undefined') this.JSON = {};
5776 (function(){
5778 var special = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
5780 var escape = function(chr){
5781         return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
5784 JSON.validate = function(string){
5785         string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
5786                                         replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
5787                                         replace(/(?:^|:|,)(?:\s*\[)+/g, '');
5789         return (/^[\],:{}\s]*$/).test(string);
5792 JSON.encode = JSON.stringify ? function(obj){
5793         return JSON.stringify(obj);
5794 } : function(obj){
5795         if (obj && obj.toJSON) obj = obj.toJSON();
5797         switch (typeOf(obj)){
5798                 case 'string':
5799                         return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
5800                 case 'array':
5801                         return '[' + obj.map(JSON.encode).clean() + ']';
5802                 case 'object': case 'hash':
5803                         var string = [];
5804                         Object.each(obj, function(value, key){
5805                                 var json = JSON.encode(value);
5806                                 if (json) string.push(JSON.encode(key) + ':' + json);
5807                         });
5808                         return '{' + string + '}';
5809                 case 'number': case 'boolean': return '' + obj;
5810                 case 'null': return 'null';
5811         }
5813         return null;
5816 JSON.secure = true;
5819 JSON.decode = function(string, secure){
5820         if (!string || typeOf(string) != 'string') return null;
5821     
5822         if (secure == null) secure = JSON.secure; 
5823         if (secure){
5824                 if (JSON.parse) return JSON.parse(string);
5825                 if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
5826         }
5828         return eval('(' + string + ')');
5831 })();
5836 name: Request.JSON
5838 description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
5840 license: MIT-style license.
5842 requires: [Request, JSON]
5844 provides: Request.JSON
5849 Request.JSON = new Class({
5851         Extends: Request,
5853         options: {
5854                 /*onError: function(text, error){},*/
5855                 secure: true
5856         },
5858         initialize: function(options){
5859                 this.parent(options);
5860                 Object.append(this.headers, {
5861                         'Accept': 'application/json',
5862                         'X-Request': 'JSON'
5863                 });
5864         },
5866         success: function(text){
5867                 var json;
5868                 try {
5869                         json = this.response.json = JSON.decode(text, this.options.secure);
5870                 } catch (error){
5871                         this.fireEvent('error', [text, error]);
5872                         return;
5873                 }
5874                 if (json == null) this.onFailure();
5875                 else this.onSuccess(json, text);
5876         }
5883 name: Cookie
5885 description: Class for creating, reading, and deleting browser Cookies.
5887 license: MIT-style license.
5889 credits:
5890   - Based on the functions by Peter-Paul Koch (http://quirksmode.org).
5892 requires: [Options, Browser]
5894 provides: Cookie
5899 var Cookie = new Class({
5901         Implements: Options,
5903         options: {
5904                 path: '/',
5905                 domain: false,
5906                 duration: false,
5907                 secure: false,
5908                 document: document,
5909                 encode: true
5910         },
5912         initialize: function(key, options){
5913                 this.key = key;
5914                 this.setOptions(options);
5915         },
5917         write: function(value){
5918                 if (this.options.encode) value = encodeURIComponent(value);
5919                 if (this.options.domain) value += '; domain=' + this.options.domain;
5920                 if (this.options.path) value += '; path=' + this.options.path;
5921                 if (this.options.duration){
5922                         var date = new Date();
5923                         date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
5924                         value += '; expires=' + date.toGMTString();
5925                 }
5926                 if (this.options.secure) value += '; secure';
5927                 this.options.document.cookie = this.key + '=' + value;
5928                 return this;
5929         },
5931         read: function(){
5932                 var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
5933                 return (value) ? decodeURIComponent(value[1]) : null;
5934         },
5936         dispose: function(){
5937                 new Cookie(this.key, Object.merge({}, this.options, {duration: -1})).write('');
5938                 return this;
5939         }
5943 Cookie.write = function(key, value, options){
5944         return new Cookie(key, options).write(value);
5947 Cookie.read = function(key){
5948         return new Cookie(key).read();
5951 Cookie.dispose = function(key, options){
5952         return new Cookie(key, options).dispose();
5958 name: DOMReady
5960 description: Contains the custom event domready.
5962 license: MIT-style license.
5964 requires: [Browser, Element, Element.Event]
5966 provides: [DOMReady, DomReady]
5971 (function(window, document){
5973 var ready,
5974         loaded,
5975         checks = [],
5976         shouldPoll,
5977         timer,
5978         testElement = document.createElement('div');
5980 var domready = function(){
5981         clearTimeout(timer);
5982         if (!ready) {
5983                 Browser.loaded = ready = true;
5984                 document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check);
5985                 document.fireEvent('domready');
5986                 window.fireEvent('domready');
5987         }
5988         // cleanup scope vars
5989         document = window = testElement = null;
5992 var check = function(){
5993         for (var i = checks.length; i--;) if (checks[i]()){
5994                 domready();
5995                 return true;
5996         }
5997         return false;
6000 var poll = function(){
6001         clearTimeout(timer);
6002         if (!check()) timer = setTimeout(poll, 10);
6005 document.addListener('DOMContentLoaded', domready);
6007 /*<ltIE8>*/
6008 // doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
6009 // testElement.doScroll() throws when the DOM is not ready, only in the top window
6010 var doScrollWorks = function(){
6011         try {
6012                 testElement.doScroll();
6013                 return true;
6014         } catch (e){}
6015         return false;
6017 // If doScroll works already, it can't be used to determine domready
6018 //   e.g. in an iframe
6019 if (testElement.doScroll && !doScrollWorks()){
6020         checks.push(doScrollWorks);
6021         shouldPoll = true;
6023 /*</ltIE8>*/
6025 if (document.readyState) checks.push(function(){
6026         var state = document.readyState;
6027         return (state == 'loaded' || state == 'complete');
6030 if ('onreadystatechange' in document) document.addListener('readystatechange', check);
6031 else shouldPoll = true;
6033 if (shouldPoll) poll();
6035 Element.Events.domready = {
6036         onAdd: function(fn){
6037                 if (ready) fn.call(this);
6038         }
6041 // Make sure that domready fires before load
6042 Element.Events.load = {
6043         base: 'load',
6044         onAdd: function(fn){
6045                 if (loaded && this == window) fn.call(this);
6046         },
6047         condition: function(){
6048                 if (this == window){
6049                         domready();
6050                         delete Element.Events.load;
6051                 }
6052                 return true;
6053         }
6056 // This is based on the custom load event
6057 window.addEvent('load', function(){
6058         loaded = true;
6061 })(window, document);