]> www.average.org Git - mkgallery.git/blob - include/mootools.js
upgrade mootools to work with Firefox 4
[mkgallery.git] / include / mootools.js
1 /*
2 ---
3 MooTools: the javascript framework
4
5 web build:
6  - http://mootools.net/core/f62dd4514b59c41f42c7579664bca750
7
8 packager build:
9  - packager build Core/Core Core/Array Core/Event Core/Browser Core/Class.Extras Core/Element.Event Core/Element.Dimensions Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/DOMReady
10
11 /*
12 ---
13
14 name: Core
15
16 description: The heart of MooTools.
17
18 license: MIT-style license.
19
20 copyright: Copyright (c) 2006-2010 [Valerio Proietti](http://mad4milk.net/).
21
22 authors: The MooTools production team (http://mootools.net/developers/)
23
24 inspiration:
25   - 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)
26   - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
27
28 provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
29
30 ...
31 */
32
33 (function(){
34
35 this.MooTools = {
36         version: '1.3',
37         build: 'a3eed692dd85050d80168ec2c708efe901bb7db3'
38 };
39
40 // typeOf, instanceOf
41
42 var typeOf = this.typeOf = function(item){
43         if (item == null) return 'null';
44         if (item.$family) return item.$family();
45
46         if (item.nodeName){
47                 if (item.nodeType == 1) return 'element';
48                 if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
49         } else if (typeof item.length == 'number'){
50                 if (item.callee) return 'arguments';
51                 if ('item' in item) return 'collection';
52         }
53
54         return typeof item;
55 };
56
57 var instanceOf = this.instanceOf = function(item, object){
58         if (item == null) return false;
59         var constructor = item.$constructor || item.constructor;
60         while (constructor){
61                 if (constructor === object) return true;
62                 constructor = constructor.parent;
63         }
64         return item instanceof object;
65 };
66
67 // Function overloading
68
69 var Function = this.Function;
70
71 var enumerables = true;
72 for (var i in {toString: 1}) enumerables = null;
73 if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
74
75 Function.prototype.overloadSetter = function(usePlural){
76         var self = this;
77         return function(a, b){
78                 if (a == null) return this;
79                 if (usePlural || typeof a != 'string'){
80                         for (var k in a) self.call(this, k, a[k]);
81                         if (enumerables) for (var i = enumerables.length; i--;){
82                                 k = enumerables[i];
83                                 if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
84                         }
85                 } else {
86                         self.call(this, a, b);
87                 }
88                 return this;
89         };
90 };
91
92 Function.prototype.overloadGetter = function(usePlural){
93         var self = this;
94         return function(a){
95                 var args, result;
96                 if (usePlural || typeof a != 'string') args = a;
97                 else if (arguments.length > 1) args = arguments;
98                 if (args){
99                         result = {};
100                         for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
101                 } else {
102                         result = self.call(this, a);
103                 }
104                 return result;
105         };
106 };
107
108 Function.prototype.extend = function(key, value){
109         this[key] = value;
110 }.overloadSetter();
111
112 Function.prototype.implement = function(key, value){
113         this.prototype[key] = value;
114 }.overloadSetter();
115
116 // From
117
118 var slice = Array.prototype.slice;
119
120 Function.from = function(item){
121         return (typeOf(item) == 'function') ? item : function(){
122                 return item;
123         };
124 };
125
126 Array.from = function(item){
127         if (item == null) return [];
128         return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
129 };
130
131 Number.from = function(item){
132         var number = parseFloat(item);
133         return isFinite(number) ? number : null;
134 };
135
136 String.from = function(item){
137         return item + '';
138 };
139
140 // hide, protect
141
142 Function.implement({
143
144         hide: function(){
145                 this.$hidden = true;
146                 return this;
147         },
148
149         protect: function(){
150                 this.$protected = true;
151                 return this;
152         }
153
154 });
155
156 // Type
157
158 var Type = this.Type = function(name, object){
159         if (name){
160                 var lower = name.toLowerCase();
161                 var typeCheck = function(item){
162                         return (typeOf(item) == lower);
163                 };
164
165                 Type['is' + name] = typeCheck;
166                 if (object != null){
167                         object.prototype.$family = (function(){
168                                 return lower;
169                         }).hide();
170                         //<1.2compat>
171                         object.type = typeCheck;
172                         //</1.2compat>
173                 }
174         }
175
176         if (object == null) return null;
177
178         object.extend(this);
179         object.$constructor = Type;
180         object.prototype.$constructor = object;
181
182         return object;
183 };
184
185 var toString = Object.prototype.toString;
186
187 Type.isEnumerable = function(item){
188         return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
189 };
190
191 var hooks = {};
192
193 var hooksOf = function(object){
194         var type = typeOf(object.prototype);
195         return hooks[type] || (hooks[type] = []);
196 };
197
198 var implement = function(name, method){
199         if (method && method.$hidden) return this;
200
201         var hooks = hooksOf(this);
202
203         for (var i = 0; i < hooks.length; i++){
204                 var hook = hooks[i];
205                 if (typeOf(hook) == 'type') implement.call(hook, name, method);
206                 else hook.call(this, name, method);
207         }
208         
209         var previous = this.prototype[name];
210         if (previous == null || !previous.$protected) this.prototype[name] = method;
211
212         if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
213                 return method.apply(item, slice.call(arguments, 1));
214         });
215
216         return this;
217 };
218
219 var extend = function(name, method){
220         if (method && method.$hidden) return this;
221         var previous = this[name];
222         if (previous == null || !previous.$protected) this[name] = method;
223         return this;
224 };
225
226 Type.implement({
227
228         implement: implement.overloadSetter(),
229
230         extend: extend.overloadSetter(),
231
232         alias: function(name, existing){
233                 implement.call(this, name, this.prototype[existing]);
234         }.overloadSetter(),
235
236         mirror: function(hook){
237                 hooksOf(this).push(hook);
238                 return this;
239         }
240
241 });
242
243 new Type('Type', Type);
244
245 // Default Types
246
247 var force = function(name, object, methods){
248         var isType = (object != Object),
249                 prototype = object.prototype;
250
251         if (isType) object = new Type(name, object);
252
253         for (var i = 0, l = methods.length; i < l; i++){
254                 var key = methods[i],
255                         generic = object[key],
256                         proto = prototype[key];
257
258                 if (generic) generic.protect();
259
260                 if (isType && proto){
261                         delete prototype[key];
262                         prototype[key] = proto.protect();
263                 }
264         }
265
266         if (isType) object.implement(prototype);
267
268         return force;
269 };
270
271 force('String', String, [
272         'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
273         'slice', 'split', 'substr', 'substring', 'toLowerCase', 'toUpperCase'
274 ])('Array', Array, [
275         'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
276         'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
277 ])('Number', Number, [
278         'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
279 ])('Function', Function, [
280         'apply', 'call', 'bind'
281 ])('RegExp', RegExp, [
282         'exec', 'test'
283 ])('Object', Object, [
284         'create', 'defineProperty', 'defineProperties', 'keys',
285         'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
286         'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
287 ])('Date', Date, ['now']);
288
289 Object.extend = extend.overloadSetter();
290
291 Date.extend('now', function(){
292         return +(new Date);
293 });
294
295 new Type('Boolean', Boolean);
296
297 // fixes NaN returning as Number
298
299 Number.prototype.$family = function(){
300         return isFinite(this) ? 'number' : 'null';
301 }.hide();
302
303 // Number.random
304
305 Number.extend('random', function(min, max){
306         return Math.floor(Math.random() * (max - min + 1) + min);
307 });
308
309 // forEach, each
310
311 Object.extend('forEach', function(object, fn, bind){
312         for (var key in object){
313                 if (object.hasOwnProperty(key)) fn.call(bind, object[key], key, object);
314         }
315 });
316
317 Object.each = Object.forEach;
318
319 Array.implement({
320
321         forEach: function(fn, bind){
322                 for (var i = 0, l = this.length; i < l; i++){
323                         if (i in this) fn.call(bind, this[i], i, this);
324                 }
325         },
326
327         each: function(fn, bind){
328                 Array.forEach(this, fn, bind);
329                 return this;
330         }
331
332 });
333
334 // Array & Object cloning, Object merging and appending
335
336 var cloneOf = function(item){
337         switch (typeOf(item)){
338                 case 'array': return item.clone();
339                 case 'object': return Object.clone(item);
340                 default: return item;
341         }
342 };
343
344 Array.implement('clone', function(){
345         var i = this.length, clone = new Array(i);
346         while (i--) clone[i] = cloneOf(this[i]);
347         return clone;
348 });
349
350 var mergeOne = function(source, key, current){
351         switch (typeOf(current)){
352                 case 'object':
353                         if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
354                         else source[key] = Object.clone(current);
355                 break;
356                 case 'array': source[key] = current.clone(); break;
357                 default: source[key] = current;
358         }
359         return source;
360 };
361
362 Object.extend({
363
364         merge: function(source, k, v){
365                 if (typeOf(k) == 'string') return mergeOne(source, k, v);
366                 for (var i = 1, l = arguments.length; i < l; i++){
367                         var object = arguments[i];
368                         for (var key in object) mergeOne(source, key, object[key]);
369                 }
370                 return source;
371         },
372
373         clone: function(object){
374                 var clone = {};
375                 for (var key in object) clone[key] = cloneOf(object[key]);
376                 return clone;
377         },
378
379         append: function(original){
380                 for (var i = 1, l = arguments.length; i < l; i++){
381                         var extended = arguments[i] || {};
382                         for (var key in extended) original[key] = extended[key];
383                 }
384                 return original;
385         }
386
387 });
388
389 // Object-less types
390
391 ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
392         new Type(name);
393 });
394
395 // Unique ID
396
397 var UID = Date.now();
398
399 String.extend('uniqueID', function(){
400         return (UID++).toString(36);
401 });
402
403 //<1.2compat>
404
405 var Hash = this.Hash = new Type('Hash', function(object){
406         if (typeOf(object) == 'hash') object = Object.clone(object.getClean());
407         for (var key in object) this[key] = object[key];
408         return this;
409 });
410
411 Hash.implement({
412
413         forEach: function(fn, bind){
414                 Object.forEach(this, fn, bind);
415         },
416
417         getClean: function(){
418                 var clean = {};
419                 for (var key in this){
420                         if (this.hasOwnProperty(key)) clean[key] = this[key];
421                 }
422                 return clean;
423         },
424
425         getLength: function(){
426                 var length = 0;
427                 for (var key in this){
428                         if (this.hasOwnProperty(key)) length++;
429                 }
430                 return length;
431         }
432
433 });
434
435 Hash.alias('each', 'forEach');
436
437 Object.type = Type.isObject;
438
439 var Native = this.Native = function(properties){
440         return new Type(properties.name, properties.initialize);
441 };
442
443 Native.type = Type.type;
444
445 Native.implement = function(objects, methods){
446         for (var i = 0; i < objects.length; i++) objects[i].implement(methods);
447         return Native;
448 };
449
450 var arrayType = Array.type;
451 Array.type = function(item){
452         return instanceOf(item, Array) || arrayType(item);
453 };
454
455 this.$A = function(item){
456         return Array.from(item).slice();
457 };
458
459 this.$arguments = function(i){
460         return function(){
461                 return arguments[i];
462         };
463 };
464
465 this.$chk = function(obj){
466         return !!(obj || obj === 0);
467 };
468
469 this.$clear = function(timer){
470         clearTimeout(timer);
471         clearInterval(timer);
472         return null;
473 };
474
475 this.$defined = function(obj){
476         return (obj != null);
477 };
478
479 this.$each = function(iterable, fn, bind){
480         var type = typeOf(iterable);
481         ((type == 'arguments' || type == 'collection' || type == 'array' || type == 'elements') ? Array : Object).each(iterable, fn, bind);
482 };
483
484 this.$empty = function(){};
485
486 this.$extend = function(original, extended){
487         return Object.append(original, extended);
488 };
489
490 this.$H = function(object){
491         return new Hash(object);
492 };
493
494 this.$merge = function(){
495         var args = Array.slice(arguments);
496         args.unshift({});
497         return Object.merge.apply(null, args);
498 };
499
500 this.$lambda = Function.from;
501 this.$mixin = Object.merge;
502 this.$random = Number.random;
503 this.$splat = Array.from;
504 this.$time = Date.now;
505
506 this.$type = function(object){
507         var type = typeOf(object);
508         if (type == 'elements') return 'array';
509         return (type == 'null') ? false : type;
510 };
511
512 this.$unlink = function(object){
513         switch (typeOf(object)){
514                 case 'object': return Object.clone(object);
515                 case 'array': return Array.clone(object);
516                 case 'hash': return new Hash(object);
517                 default: return object;
518         }
519 };
520
521 //</1.2compat>
522
523 })();
524
525
526 /*
527 ---
528
529 name: Array
530
531 description: Contains Array Prototypes like each, contains, and erase.
532
533 license: MIT-style license.
534
535 requires: Type
536
537 provides: Array
538
539 ...
540 */
541
542 Array.implement({
543
544         invoke: function(methodName){
545                 var args = Array.slice(arguments, 1);
546                 return this.map(function(item){
547                         return item[methodName].apply(item, args);
548                 });
549         },
550
551         every: function(fn, bind){
552                 for (var i = 0, l = this.length; i < l; i++){
553                         if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
554                 }
555                 return true;
556         },
557
558         filter: function(fn, bind){
559                 var results = [];
560                 for (var i = 0, l = this.length; i < l; i++){
561                         if ((i in this) && fn.call(bind, this[i], i, this)) results.push(this[i]);
562                 }
563                 return results;
564         },
565
566         clean: function(){
567                 return this.filter(function(item){
568                         return item != null;
569                 });
570         },
571
572         indexOf: function(item, from){
573                 var len = this.length;
574                 for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
575                         if (this[i] === item) return i;
576                 }
577                 return -1;
578         },
579
580         map: function(fn, bind){
581                 var results = [];
582                 for (var i = 0, l = this.length; i < l; i++){
583                         if (i in this) results[i] = fn.call(bind, this[i], i, this);
584                 }
585                 return results;
586         },
587
588         some: function(fn, bind){
589                 for (var i = 0, l = this.length; i < l; i++){
590                         if ((i in this) && fn.call(bind, this[i], i, this)) return true;
591                 }
592                 return false;
593         },
594
595         associate: function(keys){
596                 var obj = {}, length = Math.min(this.length, keys.length);
597                 for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
598                 return obj;
599         },
600
601         link: function(object){
602                 var result = {};
603                 for (var i = 0, l = this.length; i < l; i++){
604                         for (var key in object){
605                                 if (object[key](this[i])){
606                                         result[key] = this[i];
607                                         delete object[key];
608                                         break;
609                                 }
610                         }
611                 }
612                 return result;
613         },
614
615         contains: function(item, from){
616                 return this.indexOf(item, from) != -1;
617         },
618
619         append: function(array){
620                 this.push.apply(this, array);
621                 return this;
622         },
623
624         getLast: function(){
625                 return (this.length) ? this[this.length - 1] : null;
626         },
627
628         getRandom: function(){
629                 return (this.length) ? this[Number.random(0, this.length - 1)] : null;
630         },
631
632         include: function(item){
633                 if (!this.contains(item)) this.push(item);
634                 return this;
635         },
636
637         combine: function(array){
638                 for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
639                 return this;
640         },
641
642         erase: function(item){
643                 for (var i = this.length; i--;){
644                         if (this[i] === item) this.splice(i, 1);
645                 }
646                 return this;
647         },
648
649         empty: function(){
650                 this.length = 0;
651                 return this;
652         },
653
654         flatten: function(){
655                 var array = [];
656                 for (var i = 0, l = this.length; i < l; i++){
657                         var type = typeOf(this[i]);
658                         if (type == 'null') continue;
659                         array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
660                 }
661                 return array;
662         },
663
664         pick: function(){
665                 for (var i = 0, l = this.length; i < l; i++){
666                         if (this[i] != null) return this[i];
667                 }
668                 return null;
669         },
670
671         hexToRgb: function(array){
672                 if (this.length != 3) return null;
673                 var rgb = this.map(function(value){
674                         if (value.length == 1) value += value;
675                         return value.toInt(16);
676                 });
677                 return (array) ? rgb : 'rgb(' + rgb + ')';
678         },
679
680         rgbToHex: function(array){
681                 if (this.length < 3) return null;
682                 if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
683                 var hex = [];
684                 for (var i = 0; i < 3; i++){
685                         var bit = (this[i] - 0).toString(16);
686                         hex.push((bit.length == 1) ? '0' + bit : bit);
687                 }
688                 return (array) ? hex : '#' + hex.join('');
689         }
690
691 });
692
693 //<1.2compat>
694
695 Array.alias('extend', 'append');
696
697 var $pick = function(){
698         return Array.from(arguments).pick();
699 };
700
701 //</1.2compat>
702
703
704 /*
705 ---
706
707 name: Function
708
709 description: Contains Function Prototypes like create, bind, pass, and delay.
710
711 license: MIT-style license.
712
713 requires: Type
714
715 provides: Function
716
717 ...
718 */
719
720 Function.extend({
721
722         attempt: function(){
723                 for (var i = 0, l = arguments.length; i < l; i++){
724                         try {
725                                 return arguments[i]();
726                         } catch (e){}
727                 }
728                 return null;
729         }
730
731 });
732
733 Function.implement({
734
735         attempt: function(args, bind){
736                 try {
737                         return this.apply(bind, Array.from(args));
738                 } catch (e){}
739                 
740                 return null;
741         },
742
743         bind: function(bind){
744                 var self = this,
745                         args = (arguments.length > 1) ? Array.slice(arguments, 1) : null;
746                 
747                 return function(){
748                         if (!args && !arguments.length) return self.call(bind);
749                         if (args && arguments.length) return self.apply(bind, args.concat(Array.from(arguments)));
750                         return self.apply(bind, args || arguments);
751                 };
752         },
753
754         pass: function(args, bind){
755                 var self = this;
756                 if (args != null) args = Array.from(args);
757                 return function(){
758                         return self.apply(bind, args || arguments);
759                 };
760         },
761
762         delay: function(delay, bind, args){
763                 return setTimeout(this.pass(args, bind), delay);
764         },
765
766         periodical: function(periodical, bind, args){
767                 return setInterval(this.pass(args, bind), periodical);
768         }
769
770 });
771
772 //<1.2compat>
773
774 delete Function.prototype.bind;
775
776 Function.implement({
777
778         create: function(options){
779                 var self = this;
780                 options = options || {};
781                 return function(event){
782                         var args = options.arguments;
783                         args = (args != null) ? Array.from(args) : Array.slice(arguments, (options.event) ? 1 : 0);
784                         if (options.event) args = [event || window.event].extend(args);
785                         var returns = function(){
786                                 return self.apply(options.bind || null, args);
787                         };
788                         if (options.delay) return setTimeout(returns, options.delay);
789                         if (options.periodical) return setInterval(returns, options.periodical);
790                         if (options.attempt) return Function.attempt(returns);
791                         return returns();
792                 };
793         },
794
795         bind: function(bind, args){
796                 var self = this;
797                 if (args != null) args = Array.from(args);
798                 return function(){
799                         return self.apply(bind, args || arguments);
800                 };
801         },
802
803         bindWithEvent: function(bind, args){
804                 var self = this;
805                 if (args != null) args = Array.from(args);
806                 return function(event){
807                         return self.apply(bind, (args == null) ? arguments : [event].concat(args));
808                 };
809         },
810
811         run: function(args, bind){
812                 return this.apply(bind, Array.from(args));
813         }
814
815 });
816
817 var $try = Function.attempt;
818
819 //</1.2compat>
820
821
822 /*
823 ---
824
825 name: Number
826
827 description: Contains Number Prototypes like limit, round, times, and ceil.
828
829 license: MIT-style license.
830
831 requires: Type
832
833 provides: Number
834
835 ...
836 */
837
838 Number.implement({
839
840         limit: function(min, max){
841                 return Math.min(max, Math.max(min, this));
842         },
843
844         round: function(precision){
845                 precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
846                 return Math.round(this * precision) / precision;
847         },
848
849         times: function(fn, bind){
850                 for (var i = 0; i < this; i++) fn.call(bind, i, this);
851         },
852
853         toFloat: function(){
854                 return parseFloat(this);
855         },
856
857         toInt: function(base){
858                 return parseInt(this, base || 10);
859         }
860
861 });
862
863 Number.alias('each', 'times');
864
865 (function(math){
866         var methods = {};
867         math.each(function(name){
868                 if (!Number[name]) methods[name] = function(){
869                         return Math[name].apply(null, [this].concat(Array.from(arguments)));
870                 };
871         });
872         Number.implement(methods);
873 })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
874
875
876 /*
877 ---
878
879 name: String
880
881 description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
882
883 license: MIT-style license.
884
885 requires: Type
886
887 provides: String
888
889 ...
890 */
891
892 String.implement({
893
894         test: function(regex, params){
895                 return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
896         },
897
898         contains: function(string, separator){
899                 return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
900         },
901
902         trim: function(){
903                 return this.replace(/^\s+|\s+$/g, '');
904         },
905
906         clean: function(){
907                 return this.replace(/\s+/g, ' ').trim();
908         },
909
910         camelCase: function(){
911                 return this.replace(/-\D/g, function(match){
912                         return match.charAt(1).toUpperCase();
913                 });
914         },
915
916         hyphenate: function(){
917                 return this.replace(/[A-Z]/g, function(match){
918                         return ('-' + match.charAt(0).toLowerCase());
919                 });
920         },
921
922         capitalize: function(){
923                 return this.replace(/\b[a-z]/g, function(match){
924                         return match.toUpperCase();
925                 });
926         },
927
928         escapeRegExp: function(){
929                 return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
930         },
931
932         toInt: function(base){
933                 return parseInt(this, base || 10);
934         },
935
936         toFloat: function(){
937                 return parseFloat(this);
938         },
939
940         hexToRgb: function(array){
941                 var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
942                 return (hex) ? hex.slice(1).hexToRgb(array) : null;
943         },
944
945         rgbToHex: function(array){
946                 var rgb = this.match(/\d{1,3}/g);
947                 return (rgb) ? rgb.rgbToHex(array) : null;
948         },
949
950         substitute: function(object, regexp){
951                 return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
952                         if (match.charAt(0) == '\\') return match.slice(1);
953                         return (object[name] != null) ? object[name] : '';
954                 });
955         }
956
957 });
958
959
960 /*
961 ---
962
963 name: Browser
964
965 description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.
966
967 license: MIT-style license.
968
969 requires: [Array, Function, Number, String]
970
971 provides: [Browser, Window, Document]
972
973 ...
974 */
975
976 (function(){
977
978 var document = this.document;
979 var window = document.window = this;
980
981 var UID = 1;
982
983 this.$uid = (window.ActiveXObject) ? function(item){
984         return (item.uid || (item.uid = [UID++]))[0];
985 } : function(item){
986         return item.uid || (item.uid = UID++);
987 };
988
989 $uid(window);
990 $uid(document);
991
992 var ua = navigator.userAgent.toLowerCase(),
993         platform = navigator.platform.toLowerCase(),
994         UA = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0],
995         mode = UA[1] == 'ie' && document.documentMode;
996
997 var Browser = this.Browser = {
998
999         extend: Function.prototype.extend,
1000
1001         name: (UA[1] == 'version') ? UA[3] : UA[1],
1002
1003         version: mode || parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
1004
1005         Platform: {
1006                 name: ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0]
1007         },
1008
1009         Features: {
1010                 xpath: !!(document.evaluate),
1011                 air: !!(window.runtime),
1012                 query: !!(document.querySelector),
1013                 json: !!(window.JSON)
1014         },
1015
1016         Plugins: {}
1017
1018 };
1019
1020 Browser[Browser.name] = true;
1021 Browser[Browser.name + parseInt(Browser.version, 10)] = true;
1022 Browser.Platform[Browser.Platform.name] = true;
1023
1024 // Request
1025
1026 Browser.Request = (function(){
1027
1028         var XMLHTTP = function(){
1029                 return new XMLHttpRequest();
1030         };
1031
1032         var MSXML2 = function(){
1033                 return new ActiveXObject('MSXML2.XMLHTTP');
1034         };
1035
1036         var MSXML = function(){
1037                 return new ActiveXObject('Microsoft.XMLHTTP');
1038         };
1039
1040         return Function.attempt(function(){
1041                 XMLHTTP();
1042                 return XMLHTTP;
1043         }, function(){
1044                 MSXML2();
1045                 return MSXML2;
1046         }, function(){
1047                 MSXML();
1048                 return MSXML;
1049         });
1050
1051 })();
1052
1053 Browser.Features.xhr = !!(Browser.Request);
1054
1055 // Flash detection
1056
1057 var version = (Function.attempt(function(){
1058         return navigator.plugins['Shockwave Flash'].description;
1059 }, function(){
1060         return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
1061 }) || '0 r0').match(/\d+/g);
1062
1063 Browser.Plugins.Flash = {
1064         version: Number(version[0] || '0.' + version[1]) || 0,
1065         build: Number(version[2]) || 0
1066 };
1067
1068 // String scripts
1069
1070 Browser.exec = function(text){
1071         if (!text) return text;
1072         if (window.execScript){
1073                 window.execScript(text);
1074         } else {
1075                 var script = document.createElement('script');
1076                 script.setAttribute('type', 'text/javascript');
1077                 script.text = text;
1078                 document.head.appendChild(script);
1079                 document.head.removeChild(script);
1080         }
1081         return text;
1082 };
1083
1084 String.implement('stripScripts', function(exec){
1085         var scripts = '';
1086         var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
1087                 scripts += code + '\n';
1088                 return '';
1089         });
1090         if (exec === true) Browser.exec(scripts);
1091         else if (typeOf(exec) == 'function') exec(scripts, text);
1092         return text;
1093 });
1094
1095 // Window, Document
1096
1097 Browser.extend({
1098         Document: this.Document,
1099         Window: this.Window,
1100         Element: this.Element,
1101         Event: this.Event
1102 });
1103
1104 this.Window = this.$constructor = new Type('Window', function(){});
1105
1106 this.$family = Function.from('window').hide();
1107
1108 Window.mirror(function(name, method){
1109         window[name] = method;
1110 });
1111
1112 this.Document = document.$constructor = new Type('Document', function(){});
1113
1114 document.$family = Function.from('document').hide();
1115
1116 Document.mirror(function(name, method){
1117         document[name] = method;
1118 });
1119
1120 document.html = document.documentElement;
1121 document.head = document.getElementsByTagName('head')[0];
1122
1123 if (document.execCommand) try {
1124         document.execCommand("BackgroundImageCache", false, true);
1125 } catch (e){}
1126
1127 if (this.attachEvent && !this.addEventListener){
1128         var unloadEvent = function(){
1129                 this.detachEvent('onunload', unloadEvent);
1130                 document.head = document.html = document.window = null;
1131         };
1132         this.attachEvent('onunload', unloadEvent);
1133 }
1134
1135 // IE fails on collections and <select>.options (refers to <select>)
1136 var arrayFrom = Array.from;
1137 try {
1138         arrayFrom(document.html.childNodes);
1139 } catch(e){
1140         Array.from = function(item){
1141                 if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){
1142                         var i = item.length, array = new Array(i);
1143                         while (i--) array[i] = item[i];
1144                         return array;
1145                 }
1146                 return arrayFrom(item);
1147         };
1148
1149         var prototype = Array.prototype,
1150                 slice = prototype.slice;
1151         ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){
1152                 var method = prototype[name];
1153                 Array[name] = function(item){
1154                         return method.apply(Array.from(item), slice.call(arguments, 1));
1155                 };
1156         });
1157 }
1158
1159 //<1.2compat>
1160
1161 if (Browser.Platform.ios) Browser.Platform.ipod = true;
1162
1163 Browser.Engine = {};
1164
1165 var setEngine = function(name, version){
1166         Browser.Engine.name = name;
1167         Browser.Engine[name + version] = true;
1168         Browser.Engine.version = version;
1169 };
1170
1171 if (Browser.ie){
1172         Browser.Engine.trident = true;
1173
1174         switch (Browser.version){
1175                 case 6: setEngine('trident', 4); break;
1176                 case 7: setEngine('trident', 5); break;
1177                 case 8: setEngine('trident', 6);
1178         }
1179 }
1180
1181 if (Browser.firefox){
1182         Browser.Engine.gecko = true;
1183
1184         if (Browser.version >= 3) setEngine('gecko', 19);
1185         else setEngine('gecko', 18);
1186 }
1187
1188 if (Browser.safari || Browser.chrome){
1189         Browser.Engine.webkit = true;
1190
1191         switch (Browser.version){
1192                 case 2: setEngine('webkit', 419); break;
1193                 case 3: setEngine('webkit', 420); break;
1194                 case 4: setEngine('webkit', 525);
1195         }
1196 }
1197
1198 if (Browser.opera){
1199         Browser.Engine.presto = true;
1200
1201         if (Browser.version >= 9.6) setEngine('presto', 960);
1202         else if (Browser.version >= 9.5) setEngine('presto', 950);
1203         else setEngine('presto', 925);
1204 }
1205
1206 if (Browser.name == 'unknown'){
1207         switch ((ua.match(/(?:webkit|khtml|gecko)/) || [])[0]){
1208                 case 'webkit':
1209                 case 'khtml':
1210                         Browser.Engine.webkit = true;
1211                 break;
1212                 case 'gecko':
1213                         Browser.Engine.gecko = true;
1214         }
1215 }
1216
1217 this.$exec = Browser.exec;
1218
1219 //</1.2compat>
1220
1221 })();
1222
1223
1224 /*
1225 ---
1226
1227 name: Object
1228
1229 description: Object generic methods
1230
1231 license: MIT-style license.
1232
1233 requires: Type
1234
1235 provides: [Object, Hash]
1236
1237 ...
1238 */
1239
1240
1241 Object.extend({
1242
1243         subset: function(object, keys){
1244                 var results = {};
1245                 for (var i = 0, l = keys.length; i < l; i++){
1246                         var k = keys[i];
1247                         results[k] = object[k];
1248                 }
1249                 return results;
1250         },
1251
1252         map: function(object, fn, bind){
1253                 var results = {};
1254                 for (var key in object){
1255                         if (object.hasOwnProperty(key)) results[key] = fn.call(bind, object[key], key, object);
1256                 }
1257                 return results;
1258         },
1259
1260         filter: function(object, fn, bind){
1261                 var results = {};
1262                 Object.each(object, function(value, key){
1263                         if (fn.call(bind, value, key, object)) results[key] = value;
1264                 });
1265                 return results;
1266         },
1267
1268         every: function(object, fn, bind){
1269                 for (var key in object){
1270                         if (object.hasOwnProperty(key) && !fn.call(bind, object[key], key)) return false;
1271                 }
1272                 return true;
1273         },
1274
1275         some: function(object, fn, bind){
1276                 for (var key in object){
1277                         if (object.hasOwnProperty(key) && fn.call(bind, object[key], key)) return true;
1278                 }
1279                 return false;
1280         },
1281
1282         keys: function(object){
1283                 var keys = [];
1284                 for (var key in object){
1285                         if (object.hasOwnProperty(key)) keys.push(key);
1286                 }
1287                 return keys;
1288         },
1289
1290         values: function(object){
1291                 var values = [];
1292                 for (var key in object){
1293                         if (object.hasOwnProperty(key)) values.push(object[key]);
1294                 }
1295                 return values;
1296         },
1297
1298         getLength: function(object){
1299                 return Object.keys(object).length;
1300         },
1301
1302         keyOf: function(object, value){
1303                 for (var key in object){
1304                         if (object.hasOwnProperty(key) && object[key] === value) return key;
1305                 }
1306                 return null;
1307         },
1308
1309         contains: function(object, value){
1310                 return Object.keyOf(object, value) != null;
1311         },
1312
1313         toQueryString: function(object, base){
1314                 var queryString = [];
1315
1316                 Object.each(object, function(value, key){
1317                         if (base) key = base + '[' + key + ']';
1318                         var result;
1319                         switch (typeOf(value)){
1320                                 case 'object': result = Object.toQueryString(value, key); break;
1321                                 case 'array':
1322                                         var qs = {};
1323                                         value.each(function(val, i){
1324                                                 qs[i] = val;
1325                                         });
1326                                         result = Object.toQueryString(qs, key);
1327                                 break;
1328                                 default: result = key + '=' + encodeURIComponent(value);
1329                         }
1330                         if (value != null) queryString.push(result);
1331                 });
1332
1333                 return queryString.join('&');
1334         }
1335
1336 });
1337
1338
1339 //<1.2compat>
1340
1341 Hash.implement({
1342
1343         has: Object.prototype.hasOwnProperty,
1344
1345         keyOf: function(value){
1346                 return Object.keyOf(this, value);
1347         },
1348
1349         hasValue: function(value){
1350                 return Object.contains(this, value);
1351         },
1352
1353         extend: function(properties){
1354                 Hash.each(properties || {}, function(value, key){
1355                         Hash.set(this, key, value);
1356                 }, this);
1357                 return this;
1358         },
1359
1360         combine: function(properties){
1361                 Hash.each(properties || {}, function(value, key){
1362                         Hash.include(this, key, value);
1363                 }, this);
1364                 return this;
1365         },
1366
1367         erase: function(key){
1368                 if (this.hasOwnProperty(key)) delete this[key];
1369                 return this;
1370         },
1371
1372         get: function(key){
1373                 return (this.hasOwnProperty(key)) ? this[key] : null;
1374         },
1375
1376         set: function(key, value){
1377                 if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
1378                 return this;
1379         },
1380
1381         empty: function(){
1382                 Hash.each(this, function(value, key){
1383                         delete this[key];
1384                 }, this);
1385                 return this;
1386         },
1387
1388         include: function(key, value){
1389                 if (this[key] == null) this[key] = value;
1390                 return this;
1391         },
1392
1393         map: function(fn, bind){
1394                 return new Hash(Object.map(this, fn, bind));
1395         },
1396
1397         filter: function(fn, bind){
1398                 return new Hash(Object.filter(this, fn, bind));
1399         },
1400
1401         every: function(fn, bind){
1402                 return Object.every(this, fn, bind);
1403         },
1404
1405         some: function(fn, bind){
1406                 return Object.some(this, fn, bind);
1407         },
1408
1409         getKeys: function(){
1410                 return Object.keys(this);
1411         },
1412
1413         getValues: function(){
1414                 return Object.values(this);
1415         },
1416
1417         toQueryString: function(base){
1418                 return Object.toQueryString(this, base);
1419         }
1420
1421 });
1422
1423 Hash.extend = Object.append;
1424
1425 Hash.alias({indexOf: 'keyOf', contains: 'hasValue'});
1426
1427 //</1.2compat>
1428
1429
1430 /*
1431 ---
1432
1433 name: Event
1434
1435 description: Contains the Event Class, to make the event object cross-browser.
1436
1437 license: MIT-style license.
1438
1439 requires: [Window, Document, Array, Function, String, Object]
1440
1441 provides: Event
1442
1443 ...
1444 */
1445
1446 var Event = new Type('Event', function(event, win){
1447         if (!win) win = window;
1448         var doc = win.document;
1449         event = event || win.event;
1450         if (event.$extended) return event;
1451         this.$extended = true;
1452         var type = event.type,
1453                 target = event.target || event.srcElement,
1454                 page = {},
1455                 client = {};
1456         while (target && target.nodeType == 3) target = target.parentNode;
1457
1458         if (type.indexOf('key') != -1){
1459                 var code = event.which || event.keyCode;
1460                 var key = Object.keyOf(Event.Keys, code);
1461                 if (type == 'keydown'){
1462                         var fKey = code - 111;
1463                         if (fKey > 0 && fKey < 13) key = 'f' + fKey;
1464                 }
1465                 if (!key) key = String.fromCharCode(code).toLowerCase();
1466         } else if (type.test(/click|mouse|menu/i)){
1467                 doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
1468                 page = {
1469                         x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
1470                         y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
1471                 };
1472                 client = {
1473                         x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
1474                         y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
1475                 };
1476                 if (type.test(/DOMMouseScroll|mousewheel/)){
1477                         var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
1478                 }
1479                 var rightClick = (event.which == 3) || (event.button == 2),
1480                         related = null;
1481                 if (type.test(/over|out/)){
1482                         related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element'];
1483                         var testRelated = function(){
1484                                 while (related && related.nodeType == 3) related = related.parentNode;
1485                                 return true;
1486                         };
1487                         var hasRelated = (Browser.firefox2) ? testRelated.attempt() : testRelated();
1488                         related = (hasRelated) ? related : null;
1489                 }
1490         } else if (type.test(/gesture|touch/i)){
1491                 this.rotation = event.rotation;
1492                 this.scale = event.scale;
1493                 this.targetTouches = event.targetTouches;
1494                 this.changedTouches = event.changedTouches;
1495                 var touches = this.touches = event.touches;
1496                 if (touches && touches[0]){
1497                         var touch = touches[0];
1498                         page = {x: touch.pageX, y: touch.pageY};
1499                         client = {x: touch.clientX, y: touch.clientY};
1500                 }
1501         }
1502
1503         return Object.append(this, {
1504                 event: event,
1505                 type: type,
1506
1507                 page: page,
1508                 client: client,
1509                 rightClick: rightClick,
1510
1511                 wheel: wheel,
1512
1513                 relatedTarget: document.id(related),
1514                 target: document.id(target),
1515
1516                 code: code,
1517                 key: key,
1518
1519                 shift: event.shiftKey,
1520                 control: event.ctrlKey,
1521                 alt: event.altKey,
1522                 meta: event.metaKey
1523         });
1524 });
1525
1526 Event.Keys = {
1527         'enter': 13,
1528         'up': 38,
1529         'down': 40,
1530         'left': 37,
1531         'right': 39,
1532         'esc': 27,
1533         'space': 32,
1534         'backspace': 8,
1535         'tab': 9,
1536         'delete': 46
1537 };
1538
1539 //<1.2compat>
1540
1541 Event.Keys = new Hash(Event.Keys);
1542
1543 //</1.2compat>
1544
1545 Event.implement({
1546
1547         stop: function(){
1548                 return this.stopPropagation().preventDefault();
1549         },
1550
1551         stopPropagation: function(){
1552                 if (this.event.stopPropagation) this.event.stopPropagation();
1553                 else this.event.cancelBubble = true;
1554                 return this;
1555         },
1556
1557         preventDefault: function(){
1558                 if (this.event.preventDefault) this.event.preventDefault();
1559                 else this.event.returnValue = false;
1560                 return this;
1561         }
1562
1563 });
1564
1565
1566 /*
1567 ---
1568
1569 name: Class
1570
1571 description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
1572
1573 license: MIT-style license.
1574
1575 requires: [Array, String, Function, Number]
1576
1577 provides: Class
1578
1579 ...
1580 */
1581
1582 (function(){
1583
1584 var Class = this.Class = new Type('Class', function(params){
1585         if (instanceOf(params, Function)) params = {initialize: params};
1586
1587         var newClass = function(){
1588                 reset(this);
1589                 if (newClass.$prototyping) return this;
1590                 this.$caller = null;
1591                 var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
1592                 this.$caller = this.caller = null;
1593                 return value;
1594         }.extend(this).implement(params);
1595
1596         newClass.$constructor = Class;
1597         newClass.prototype.$constructor = newClass;
1598         newClass.prototype.parent = parent;
1599
1600         return newClass;
1601 });
1602
1603 var parent = function(){
1604         if (!this.$caller) throw new Error('The method "parent" cannot be called.');
1605         var name = this.$caller.$name,
1606                 parent = this.$caller.$owner.parent,
1607                 previous = (parent) ? parent.prototype[name] : null;
1608         if (!previous) throw new Error('The method "' + name + '" has no parent.');
1609         return previous.apply(this, arguments);
1610 };
1611
1612 var reset = function(object){
1613         for (var key in object){
1614                 var value = object[key];
1615                 switch (typeOf(value)){
1616                         case 'object':
1617                                 var F = function(){};
1618                                 F.prototype = value;
1619                                 object[key] = reset(new F);
1620                         break;
1621                         case 'array': object[key] = value.clone(); break;
1622                 }
1623         }
1624         return object;
1625 };
1626
1627 var wrap = function(self, key, method){
1628         if (method.$origin) method = method.$origin;
1629         var wrapper = function(){
1630                 if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
1631                 var caller = this.caller, current = this.$caller;
1632                 this.caller = current; this.$caller = wrapper;
1633                 var result = method.apply(this, arguments);
1634                 this.$caller = current; this.caller = caller;
1635                 return result;
1636         }.extend({$owner: self, $origin: method, $name: key});
1637         return wrapper;
1638 };
1639
1640 var implement = function(key, value, retain){
1641         if (Class.Mutators.hasOwnProperty(key)){
1642                 value = Class.Mutators[key].call(this, value);
1643                 if (value == null) return this;
1644         }
1645
1646         if (typeOf(value) == 'function'){
1647                 if (value.$hidden) return this;
1648                 this.prototype[key] = (retain) ? value : wrap(this, key, value);
1649         } else {
1650                 Object.merge(this.prototype, key, value);
1651         }
1652
1653         return this;
1654 };
1655
1656 var getInstance = function(klass){
1657         klass.$prototyping = true;
1658         var proto = new klass;
1659         delete klass.$prototyping;
1660         return proto;
1661 };
1662
1663 Class.implement('implement', implement.overloadSetter());
1664
1665 Class.Mutators = {
1666
1667         Extends: function(parent){
1668                 this.parent = parent;
1669                 this.prototype = getInstance(parent);
1670         },
1671
1672         Implements: function(items){
1673                 Array.from(items).each(function(item){
1674                         var instance = new item;
1675                         for (var key in instance) implement.call(this, key, instance[key], true);
1676                 }, this);
1677         }
1678 };
1679
1680 })();
1681
1682
1683 /*
1684 ---
1685
1686 name: Class.Extras
1687
1688 description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
1689
1690 license: MIT-style license.
1691
1692 requires: Class
1693
1694 provides: [Class.Extras, Chain, Events, Options]
1695
1696 ...
1697 */
1698
1699 (function(){
1700
1701 this.Chain = new Class({
1702
1703         $chain: [],
1704
1705         chain: function(){
1706                 this.$chain.append(Array.flatten(arguments));
1707                 return this;
1708         },
1709
1710         callChain: function(){
1711                 return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
1712         },
1713
1714         clearChain: function(){
1715                 this.$chain.empty();
1716                 return this;
1717         }
1718
1719 });
1720
1721 var removeOn = function(string){
1722         return string.replace(/^on([A-Z])/, function(full, first){
1723                 return first.toLowerCase();
1724         });
1725 };
1726
1727 this.Events = new Class({
1728
1729         $events: {},
1730
1731         addEvent: function(type, fn, internal){
1732                 type = removeOn(type);
1733
1734                 /*<1.2compat>*/
1735                 if (fn == $empty) return this;
1736                 /*</1.2compat>*/
1737
1738                 this.$events[type] = (this.$events[type] || []).include(fn);
1739                 if (internal) fn.internal = true;
1740                 return this;
1741         },
1742
1743         addEvents: function(events){
1744                 for (var type in events) this.addEvent(type, events[type]);
1745                 return this;
1746         },
1747
1748         fireEvent: function(type, args, delay){
1749                 type = removeOn(type);
1750                 var events = this.$events[type];
1751                 if (!events) return this;
1752                 args = Array.from(args);
1753                 events.each(function(fn){
1754                         if (delay) fn.delay(delay, this, args);
1755                         else fn.apply(this, args);
1756                 }, this);
1757                 return this;
1758         },
1759         
1760         removeEvent: function(type, fn){
1761                 type = removeOn(type);
1762                 var events = this.$events[type];
1763                 if (events && !fn.internal){
1764                         var index =  events.indexOf(fn);
1765                         if (index != -1) delete events[index];
1766                 }
1767                 return this;
1768         },
1769
1770         removeEvents: function(events){
1771                 var type;
1772                 if (typeOf(events) == 'object'){
1773                         for (type in events) this.removeEvent(type, events[type]);
1774                         return this;
1775                 }
1776                 if (events) events = removeOn(events);
1777                 for (type in this.$events){
1778                         if (events && events != type) continue;
1779                         var fns = this.$events[type];
1780                         for (var i = fns.length; i--;) this.removeEvent(type, fns[i]);
1781                 }
1782                 return this;
1783         }
1784
1785 });
1786
1787 this.Options = new Class({
1788
1789         setOptions: function(){
1790                 var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));
1791                 if (!this.addEvent) return this;
1792                 for (var option in options){
1793                         if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
1794                         this.addEvent(option, options[option]);
1795                         delete options[option];
1796                 }
1797                 return this;
1798         }
1799
1800 });
1801
1802 })();
1803
1804
1805 /*
1806 ---
1807 name: Slick.Parser
1808 description: Standalone CSS3 Selector parser
1809 provides: Slick.Parser
1810 ...
1811 */
1812
1813 (function(){
1814
1815 var parsed,
1816         separatorIndex,
1817         combinatorIndex,
1818         reversed,
1819         cache = {},
1820         reverseCache = {},
1821         reUnescape = /\\/g;
1822
1823 var parse = function(expression, isReversed){
1824         if (expression == null) return null;
1825         if (expression.Slick === true) return expression;
1826         expression = ('' + expression).replace(/^\s+|\s+$/g, '');
1827         reversed = !!isReversed;
1828         var currentCache = (reversed) ? reverseCache : cache;
1829         if (currentCache[expression]) return currentCache[expression];
1830         parsed = {Slick: true, expressions: [], raw: expression, reverse: function(){
1831                 return parse(this.raw, true);
1832         }};
1833         separatorIndex = -1;
1834         while (expression != (expression = expression.replace(regexp, parser)));
1835         parsed.length = parsed.expressions.length;
1836         return currentCache[expression] = (reversed) ? reverse(parsed) : parsed;
1837 };
1838
1839 var reverseCombinator = function(combinator){
1840         if (combinator === '!') return ' ';
1841         else if (combinator === ' ') return '!';
1842         else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
1843         else return '!' + combinator;
1844 };
1845
1846 var reverse = function(expression){
1847         var expressions = expression.expressions;
1848         for (var i = 0; i < expressions.length; i++){
1849                 var exp = expressions[i];
1850                 var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
1851
1852                 for (var j = 0; j < exp.length; j++){
1853                         var cexp = exp[j];
1854                         if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
1855                         cexp.combinator = cexp.reverseCombinator;
1856                         delete cexp.reverseCombinator;
1857                 }
1858
1859                 exp.reverse().push(last);
1860         }
1861         return expression;
1862 };
1863
1864 var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
1865         return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, "\\$&");
1866 };
1867
1868 var regexp = new RegExp(
1869 /*
1870 #!/usr/bin/env ruby
1871 puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
1872 __END__
1873         "(?x)^(?:\
1874           \\s* ( , ) \\s*               # Separator          \n\
1875         | \\s* ( <combinator>+ ) \\s*   # Combinator         \n\
1876         |      ( \\s+ )                 # CombinatorChildren \n\
1877         |      ( <unicode>+ | \\* )     # Tag                \n\
1878         | \\#  ( <unicode>+       )     # ID                 \n\
1879         | \\.  ( <unicode>+       )     # ClassName          \n\
1880         |                               # Attribute          \n\
1881         \\[  \
1882                 \\s* (<unicode1>+)  (?:  \
1883                         \\s* ([*^$!~|]?=)  (?:  \
1884                                 \\s* (?:\
1885                                         ([\"']?)(.*?)\\9 \
1886                                 )\
1887                         )  \
1888                 )?  \\s*  \
1889         \\](?!\\]) \n\
1890         |   :+ ( <unicode>+ )(?:\
1891         \\( (?:\
1892                 (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
1893         ) \\)\
1894         )?\
1895         )"
1896 */
1897         "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|:+(<unicode>+)(?:\\((?:(?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
1898         .replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
1899         .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
1900         .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
1901 );
1902
1903 function parser(
1904         rawMatch,
1905
1906         separator,
1907         combinator,
1908         combinatorChildren,
1909
1910         tagName,
1911         id,
1912         className,
1913
1914         attributeKey,
1915         attributeOperator,
1916         attributeQuote,
1917         attributeValue,
1918
1919         pseudoClass,
1920         pseudoQuote,
1921         pseudoClassQuotedValue,
1922         pseudoClassValue
1923 ){
1924         if (separator || separatorIndex === -1){
1925                 parsed.expressions[++separatorIndex] = [];
1926                 combinatorIndex = -1;
1927                 if (separator) return '';
1928         }
1929
1930         if (combinator || combinatorChildren || combinatorIndex === -1){
1931                 combinator = combinator || ' ';
1932                 var currentSeparator = parsed.expressions[separatorIndex];
1933                 if (reversed && currentSeparator[combinatorIndex])
1934                         currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
1935                 currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
1936         }
1937
1938         var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
1939
1940         if (tagName){
1941                 currentParsed.tag = tagName.replace(reUnescape, '');
1942
1943         } else if (id){
1944                 currentParsed.id = id.replace(reUnescape, '');
1945
1946         } else if (className){
1947                 className = className.replace(reUnescape, '');
1948
1949                 if (!currentParsed.classList) currentParsed.classList = [];
1950                 if (!currentParsed.classes) currentParsed.classes = [];
1951                 currentParsed.classList.push(className);
1952                 currentParsed.classes.push({
1953                         value: className,
1954                         regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
1955                 });
1956
1957         } else if (pseudoClass){
1958                 pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
1959                 pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
1960
1961                 if (!currentParsed.pseudos) currentParsed.pseudos = [];
1962                 currentParsed.pseudos.push({
1963                         key: pseudoClass.replace(reUnescape, ''),
1964                         value: pseudoClassValue
1965                 });
1966
1967         } else if (attributeKey){
1968                 attributeKey = attributeKey.replace(reUnescape, '');
1969                 attributeValue = (attributeValue || '').replace(reUnescape, '');
1970
1971                 var test, regexp;
1972
1973                 switch (attributeOperator){
1974                         case '^=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue)            ); break;
1975                         case '$=' : regexp = new RegExp(            escapeRegExp(attributeValue) +'$'       ); break;
1976                         case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
1977                         case '|=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue) +'(-|$)'   ); break;
1978                         case  '=' : test = function(value){
1979                                 return attributeValue == value;
1980                         }; break;
1981                         case '*=' : test = function(value){
1982                                 return value && value.indexOf(attributeValue) > -1;
1983                         }; break;
1984                         case '!=' : test = function(value){
1985                                 return attributeValue != value;
1986                         }; break;
1987                         default   : test = function(value){
1988                                 return !!value;
1989                         };
1990                 }
1991
1992                 if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
1993                         return false;
1994                 };
1995
1996                 if (!test) test = function(value){
1997                         return value && regexp.test(value);
1998                 };
1999
2000                 if (!currentParsed.attributes) currentParsed.attributes = [];
2001                 currentParsed.attributes.push({
2002                         key: attributeKey,
2003                         operator: attributeOperator,
2004                         value: attributeValue,
2005                         test: test
2006                 });
2007
2008         }
2009
2010         return '';
2011 };
2012
2013 // Slick NS
2014
2015 var Slick = (this.Slick || {});
2016
2017 Slick.parse = function(expression){
2018         return parse(expression);
2019 };
2020
2021 Slick.escapeRegExp = escapeRegExp;
2022
2023 if (!this.Slick) this.Slick = Slick;
2024
2025 }).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
2026
2027
2028 /*
2029 ---
2030 name: Slick.Finder
2031 description: The new, superfast css selector engine.
2032 provides: Slick.Finder
2033 requires: Slick.Parser
2034 ...
2035 */
2036
2037 (function(){
2038
2039 var local = {};
2040
2041 // Feature / Bug detection
2042
2043 local.isNativeCode = function(fn){
2044         return (/\{\s*\[native code\]\s*\}/).test('' + fn);
2045 };
2046
2047 local.isXML = function(document){
2048         return (!!document.xmlVersion) || (!!document.xml) || (Object.prototype.toString.call(document) === '[object XMLDocument]') ||
2049         (document.nodeType === 9 && document.documentElement.nodeName !== 'HTML');
2050 };
2051
2052 local.setDocument = function(document){
2053
2054         // convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
2055
2056         if (document.nodeType === 9); // document
2057         else if (document.ownerDocument) document = document.ownerDocument; // node
2058         else if (document.navigator) document = document.document; // window
2059         else return;
2060
2061         // check if it's the old document
2062
2063         if (this.document === document) return;
2064         this.document = document;
2065         var root = this.root = document.documentElement;
2066
2067         this.isXMLDocument = this.isXML(document);
2068
2069         this.brokenStarGEBTN
2070         = this.starSelectsClosedQSA
2071         = this.idGetsName
2072         = this.brokenMixedCaseQSA
2073         = this.brokenGEBCN
2074         = this.brokenCheckedQSA
2075         = this.brokenEmptyAttributeQSA
2076         = this.isHTMLDocument
2077         = false;
2078
2079         var starSelectsClosed, starSelectsComments,
2080                 brokenSecondClassNameGEBCN, cachedGetElementsByClassName;
2081
2082         var selected, id;
2083         var testNode = document.createElement('div');
2084         root.appendChild(testNode);
2085
2086         // on non-HTML documents innerHTML and getElementsById doesnt work properly
2087         try {
2088                 id = 'slick_getbyid_test';
2089                 testNode.innerHTML = '<a id="'+id+'"></a>';
2090                 this.isHTMLDocument = !!document.getElementById(id);
2091         } catch(e){};
2092
2093         if (this.isHTMLDocument){
2094                 
2095                 testNode.style.display = 'none';
2096                 
2097                 // IE returns comment nodes for getElementsByTagName('*') for some documents
2098                 testNode.appendChild(document.createComment(''));
2099                 starSelectsComments = (testNode.getElementsByTagName('*').length > 0);
2100
2101                 // IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
2102                 try {
2103                         testNode.innerHTML = 'foo</foo>';
2104                         selected = testNode.getElementsByTagName('*');
2105                         starSelectsClosed = (selected && selected.length && selected[0].nodeName.charAt(0) == '/');
2106                 } catch(e){};
2107
2108                 this.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
2109
2110                 // IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
2111                 if (testNode.querySelectorAll) try {
2112                         testNode.innerHTML = 'foo</foo>';
2113                         selected = testNode.querySelectorAll('*');
2114                         this.starSelectsClosedQSA = (selected && selected.length && selected[0].nodeName.charAt(0) == '/');
2115                 } catch(e){};
2116
2117                 // IE returns elements with the name instead of just id for getElementsById for some documents
2118                 try {
2119                         id = 'slick_id_gets_name';
2120                         testNode.innerHTML = '<a name="'+id+'"></a><b id="'+id+'"></b>';
2121                         this.idGetsName = document.getElementById(id) === testNode.firstChild;
2122                 } catch(e){};
2123
2124                 // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
2125                 try {
2126                         testNode.innerHTML = '<a class="MiXedCaSe"></a>';
2127                         this.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiXedCaSe').length;
2128                 } catch(e){};
2129
2130                 try {
2131                         testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
2132                         testNode.getElementsByClassName('b').length;
2133                         testNode.firstChild.className = 'b';
2134                         cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
2135                 } catch(e){};
2136
2137                 // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
2138                 try {
2139                         testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
2140                         brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
2141                 } catch(e){};
2142
2143                 this.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
2144                 
2145                 // Webkit dont return selected options on querySelectorAll
2146                 try {
2147                         testNode.innerHTML = '<select><option selected="selected">a</option></select>';
2148                         this.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
2149                 } catch(e){};
2150                 
2151                 // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
2152                 try {
2153                         testNode.innerHTML = '<a class=""></a>';
2154                         this.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
2155                 } catch(e){};
2156                 
2157         }
2158
2159         root.removeChild(testNode);
2160         testNode = null;
2161
2162         // hasAttribute
2163
2164         this.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute) {
2165                 return node.hasAttribute(attribute);
2166         } : function(node, attribute) {
2167                 node = node.getAttributeNode(attribute);
2168                 return !!(node && (node.specified || node.nodeValue));
2169         };
2170
2171         // contains
2172         // FIXME: Add specs: local.contains should be different for xml and html documents?
2173         this.contains = (root && this.isNativeCode(root.contains)) ? function(context, node){
2174                 return context.contains(node);
2175         } : (root && root.compareDocumentPosition) ? function(context, node){
2176                 return context === node || !!(context.compareDocumentPosition(node) & 16);
2177         } : function(context, node){
2178                 if (node) do {
2179                         if (node === context) return true;
2180                 } while ((node = node.parentNode));
2181                 return false;
2182         };
2183
2184         // document order sorting
2185         // credits to Sizzle (http://sizzlejs.com/)
2186
2187         this.documentSorter = (root.compareDocumentPosition) ? function(a, b){
2188                 if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
2189                 return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
2190         } : ('sourceIndex' in root) ? function(a, b){
2191                 if (!a.sourceIndex || !b.sourceIndex) return 0;
2192                 return a.sourceIndex - b.sourceIndex;
2193         } : (document.createRange) ? function(a, b){
2194                 if (!a.ownerDocument || !b.ownerDocument) return 0;
2195                 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
2196                 aRange.setStart(a, 0);
2197                 aRange.setEnd(a, 0);
2198                 bRange.setStart(b, 0);
2199                 bRange.setEnd(b, 0);
2200                 return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
2201         } : null ;
2202
2203         this.getUID = (this.isHTMLDocument) ? this.getUIDHTML : this.getUIDXML;
2204
2205 };
2206
2207 // Main Method
2208
2209 local.search = function(context, expression, append, first){
2210
2211         var found = this.found = (first) ? null : (append || []);
2212
2213         // context checks
2214
2215         if (!context) return found; // No context
2216         if (context.navigator) context = context.document; // Convert the node from a window to a document
2217         else if (!context.nodeType) return found; // Reject misc junk input
2218
2219         // setup
2220
2221         var parsed, i;
2222
2223         var uniques = this.uniques = {};
2224
2225         if (this.document !== (context.ownerDocument || context)) this.setDocument(context);
2226
2227         // should sort if there are nodes in append and if you pass multiple expressions.
2228         // should remove duplicates if append already has items
2229         var shouldUniques = !!(append && append.length);
2230
2231         // avoid duplicating items already in the append array
2232         if (shouldUniques) for (i = found.length; i--;) this.uniques[this.getUID(found[i])] = true;
2233
2234         // expression checks
2235
2236         if (typeof expression == 'string'){ // expression is a string
2237
2238                 // Overrides
2239
2240                 for (i = this.overrides.length; i--;){
2241                         var override = this.overrides[i];
2242                         if (override.regexp.test(expression)){
2243                                 var result = override.method.call(context, expression, found, first);
2244                                 if (result === false) continue;
2245                                 if (result === true) return found;
2246                                 return result;
2247                         }
2248                 }
2249
2250                 parsed = this.Slick.parse(expression);
2251                 if (!parsed.length) return found;
2252         } else if (expression == null){ // there is no expression
2253                 return found;
2254         } else if (expression.Slick){ // expression is a parsed Slick object
2255                 parsed = expression;
2256         } else if (this.contains(context.documentElement || context, expression)){ // expression is a node
2257                 (found) ? found.push(expression) : found = expression;
2258                 return found;
2259         } else { // other junk
2260                 return found;
2261         }
2262
2263         // cache elements for the nth selectors
2264
2265         /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
2266
2267         this.posNTH = {};
2268         this.posNTHLast = {};
2269         this.posNTHType = {};
2270         this.posNTHTypeLast = {};
2271
2272         /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
2273
2274         // if append is null and there is only a single selector with one expression use pushArray, else use pushUID
2275         this.push = (!shouldUniques && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
2276
2277         if (found == null) found = [];
2278
2279         // default engine
2280
2281         var j, m, n;
2282         var combinator, tag, id, classList, classes, attributes, pseudos;
2283         var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
2284
2285         search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
2286
2287                 combinator = 'combinator:' + currentBit.combinator;
2288                 if (!this[combinator]) continue search;
2289
2290                 tag        = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
2291                 id         = currentBit.id;
2292                 classList  = currentBit.classList;
2293                 classes    = currentBit.classes;
2294                 attributes = currentBit.attributes;
2295                 pseudos    = currentBit.pseudos;
2296                 lastBit    = (j === (currentExpression.length - 1));
2297
2298                 this.bitUniques = {};
2299
2300                 if (lastBit){
2301                         this.uniques = uniques;
2302                         this.found = found;
2303                 } else {
2304                         this.uniques = {};
2305                         this.found = [];
2306                 }
2307
2308                 if (j === 0){
2309                         this[combinator](context, tag, id, classes, attributes, pseudos, classList);
2310                         if (first && lastBit && found.length) break search;
2311                 } else {
2312                         if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
2313                                 this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
2314                                 if (found.length) break search;
2315                         } else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
2316                 }
2317
2318                 currentItems = this.found;
2319         }
2320
2321         if (shouldUniques || (parsed.expressions.length > 1)) this.sort(found);
2322
2323         return (first) ? (found[0] || null) : found;
2324 };
2325
2326 // Utils
2327
2328 local.uidx = 1;
2329 local.uidk = 'slick:uniqueid';
2330
2331 local.getUIDXML = function(node){
2332         var uid = node.getAttribute(this.uidk);
2333         if (!uid){
2334                 uid = this.uidx++;
2335                 node.setAttribute(this.uidk, uid);
2336         }
2337         return uid;
2338 };
2339
2340 local.getUIDHTML = function(node){
2341         return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
2342 };
2343
2344 // sort based on the setDocument documentSorter method.
2345
2346 local.sort = function(results){
2347         if (!this.documentSorter) return results;
2348         results.sort(this.documentSorter);
2349         return results;
2350 };
2351
2352 /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
2353
2354 local.cacheNTH = {};
2355
2356 local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
2357
2358 local.parseNTHArgument = function(argument){
2359         var parsed = argument.match(this.matchNTH);
2360         if (!parsed) return false;
2361         var special = parsed[2] || false;
2362         var a = parsed[1] || 1;
2363         if (a == '-') a = -1;
2364         var b = +parsed[3] || 0;
2365         parsed =
2366                 (special == 'n')        ? {a: a, b: b} :
2367                 (special == 'odd')      ? {a: 2, b: 1} :
2368                 (special == 'even')     ? {a: 2, b: 0} : {a: 0, b: a};
2369
2370         return (this.cacheNTH[argument] = parsed);
2371 };
2372
2373 local.createNTHPseudo = function(child, sibling, positions, ofType){
2374         return function(node, argument){
2375                 var uid = this.getUID(node);
2376                 if (!this[positions][uid]){
2377                         var parent = node.parentNode;
2378                         if (!parent) return false;
2379                         var el = parent[child], count = 1;
2380                         if (ofType){
2381                                 var nodeName = node.nodeName;
2382                                 do {
2383                                         if (el.nodeName !== nodeName) continue;
2384                                         this[positions][this.getUID(el)] = count++;
2385                                 } while ((el = el[sibling]));
2386                         } else {
2387                                 do {
2388                                         if (el.nodeType !== 1) continue;
2389                                         this[positions][this.getUID(el)] = count++;
2390                                 } while ((el = el[sibling]));
2391                         }
2392                 }
2393                 argument = argument || 'n';
2394                 var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
2395                 if (!parsed) return false;
2396                 var a = parsed.a, b = parsed.b, pos = this[positions][uid];
2397                 if (a == 0) return b == pos;
2398                 if (a > 0){
2399                         if (pos < b) return false;
2400                 } else {
2401                         if (b < pos) return false;
2402                 }
2403                 return ((pos - b) % a) == 0;
2404         };
2405 };
2406
2407 /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
2408
2409 local.pushArray = function(node, tag, id, classes, attributes, pseudos){
2410         if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
2411 };
2412
2413 local.pushUID = function(node, tag, id, classes, attributes, pseudos){
2414         var uid = this.getUID(node);
2415         if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
2416                 this.uniques[uid] = true;
2417                 this.found.push(node);
2418         }
2419 };
2420
2421 local.matchNode = function(node, selector){
2422         var parsed = this.Slick.parse(selector);
2423         if (!parsed) return true;
2424
2425         // simple (single) selectors
2426         if(parsed.length == 1 && parsed.expressions[0].length == 1){
2427                 var exp = parsed.expressions[0][0];
2428                 return this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos);
2429         }
2430
2431         var nodes = this.search(this.document, parsed);
2432         for (var i = 0, item; item = nodes[i++];){
2433                 if (item === node) return true;
2434         }
2435         return false;
2436 };
2437
2438 local.matchPseudo = function(node, name, argument){
2439         var pseudoName = 'pseudo:' + name;
2440         if (this[pseudoName]) return this[pseudoName](node, argument);
2441         var attribute = this.getAttribute(node, name);
2442         return (argument) ? argument == attribute : !!attribute;
2443 };
2444
2445 local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
2446         if (tag){
2447                 if (tag == '*'){
2448                         if (node.nodeName < '@') return false; // Fix for comment nodes and closed nodes
2449                 } else {
2450                         if (node.nodeName != tag) return false;
2451                 }
2452         }
2453
2454         if (id && node.getAttribute('id') != id) return false;
2455
2456         var i, part, cls;
2457         if (classes) for (i = classes.length; i--;){
2458                 cls = ('className' in node) ? node.className : node.getAttribute('class');
2459                 if (!(cls && classes[i].regexp.test(cls))) return false;
2460         }
2461         if (attributes) for (i = attributes.length; i--;){
2462                 part = attributes[i];
2463                 if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
2464         }
2465         if (pseudos) for (i = pseudos.length; i--;){
2466                 part = pseudos[i];
2467                 if (!this.matchPseudo(node, part.key, part.value)) return false;
2468         }
2469         return true;
2470 };
2471
2472 var combinators = {
2473
2474         ' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
2475
2476                 var i, item, children;
2477
2478                 if (this.isHTMLDocument){
2479                         getById: if (id){
2480                                 item = this.document.getElementById(id);
2481                                 if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){
2482                                         // all[id] returns all the elements with that name or id inside node
2483                                         // if theres just one it will return the element, else it will be a collection
2484                                         children = node.all[id];
2485                                         if (!children) return;
2486                                         if (!children[0]) children = [children];
2487                                         for (i = 0; item = children[i++];) if (item.getAttributeNode('id').nodeValue == id){
2488                                                 this.push(item, tag, null, classes, attributes, pseudos);
2489                                                 break;
2490                                         } 
2491                                         return;
2492                                 }
2493                                 if (!item){
2494                                         // if the context is in the dom we return, else we will try GEBTN, breaking the getById label
2495                                         if (this.contains(this.document.documentElement, node)) return;
2496                                         else break getById;
2497                                 } else if (this.document !== node && !this.contains(node, item)) return;
2498                                 this.push(item, tag, null, classes, attributes, pseudos);
2499                                 return;
2500                         }
2501                         getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
2502                                 children = node.getElementsByClassName(classList.join(' '));
2503                                 if (!(children && children.length)) break getByClass;
2504                                 for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
2505                                 return;
2506                         }
2507                 }
2508                 getByTag: {
2509                         children = node.getElementsByTagName(tag);
2510                         if (!(children && children.length)) break getByTag;
2511                         if (!this.brokenStarGEBTN) tag = null;
2512                         for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
2513                 }
2514         },
2515
2516         '>': function(node, tag, id, classes, attributes, pseudos){ // direct children
2517                 if ((node = node.firstChild)) do {
2518                         if (node.nodeType === 1) this.push(node, tag, id, classes, attributes, pseudos);
2519                 } while ((node = node.nextSibling));
2520         },
2521
2522         '+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
2523                 while ((node = node.nextSibling)) if (node.nodeType === 1){
2524                         this.push(node, tag, id, classes, attributes, pseudos);
2525                         break;
2526                 }
2527         },
2528
2529         '^': function(node, tag, id, classes, attributes, pseudos){ // first child
2530                 node = node.firstChild;
2531                 if (node){
2532                         if (node.nodeType === 1) this.push(node, tag, id, classes, attributes, pseudos);
2533                         else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
2534                 }
2535         },
2536
2537         '~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
2538                 while ((node = node.nextSibling)){
2539                         if (node.nodeType !== 1) continue;
2540                         var uid = this.getUID(node);
2541                         if (this.bitUniques[uid]) break;
2542                         this.bitUniques[uid] = true;
2543                         this.push(node, tag, id, classes, attributes, pseudos);
2544                 }
2545         },
2546
2547         '++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
2548                 this['combinator:+'](node, tag, id, classes, attributes, pseudos);
2549                 this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
2550         },
2551
2552         '~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
2553                 this['combinator:~'](node, tag, id, classes, attributes, pseudos);
2554                 this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
2555         },
2556
2557         '!': function(node, tag, id, classes, attributes, pseudos){  // all parent nodes up to document
2558                 while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
2559         },
2560
2561         '!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
2562                 node = node.parentNode;
2563                 if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
2564         },
2565
2566         '!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
2567                 while ((node = node.previousSibling)) if (node.nodeType === 1){
2568                         this.push(node, tag, id, classes, attributes, pseudos);
2569                         break;
2570                 }
2571         },
2572
2573         '!^': function(node, tag, id, classes, attributes, pseudos){ // last child
2574                 node = node.lastChild;
2575                 if (node){
2576                         if (node.nodeType === 1) this.push(node, tag, id, classes, attributes, pseudos);
2577                         else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
2578                 }
2579         },
2580
2581         '!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
2582                 while ((node = node.previousSibling)){
2583                         if (node.nodeType !== 1) continue;
2584                         var uid = this.getUID(node);
2585                         if (this.bitUniques[uid]) break;
2586                         this.bitUniques[uid] = true;
2587                         this.push(node, tag, id, classes, attributes, pseudos);
2588                 }
2589         }
2590
2591 };
2592
2593 for (var c in combinators) local['combinator:' + c] = combinators[c];
2594
2595 var pseudos = {
2596
2597         /*<pseudo-selectors>*/
2598
2599         'empty': function(node){
2600                 var child = node.firstChild;
2601                 return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
2602         },
2603
2604         'not': function(node, expression){
2605                 return !this.matchNode(node, expression);
2606         },
2607
2608         'contains': function(node, text){
2609                 return (node.innerText || node.textContent || '').indexOf(text) > -1;
2610         },
2611
2612         'first-child': function(node){
2613                 while ((node = node.previousSibling)) if (node.nodeType === 1) return false;
2614                 return true;
2615         },
2616
2617         'last-child': function(node){
2618                 while ((node = node.nextSibling)) if (node.nodeType === 1) return false;
2619                 return true;
2620         },
2621
2622         'only-child': function(node){
2623                 var prev = node;
2624                 while ((prev = prev.previousSibling)) if (prev.nodeType === 1) return false;
2625                 var next = node;
2626                 while ((next = next.nextSibling)) if (next.nodeType === 1) return false;
2627                 return true;
2628         },
2629
2630         /*<nth-pseudo-selectors>*/
2631
2632         'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
2633
2634         'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
2635
2636         'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
2637
2638         'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
2639
2640         'index': function(node, index){
2641                 return this['pseudo:nth-child'](node, '' + index + 1);
2642         },
2643
2644         'even': function(node, argument){
2645                 return this['pseudo:nth-child'](node, '2n');
2646         },
2647
2648         'odd': function(node, argument){
2649                 return this['pseudo:nth-child'](node, '2n+1');
2650         },
2651
2652         /*</nth-pseudo-selectors>*/
2653
2654         /*<of-type-pseudo-selectors>*/
2655
2656         'first-of-type': function(node){
2657                 var nodeName = node.nodeName;
2658                 while ((node = node.previousSibling)) if (node.nodeName === nodeName) return false;
2659                 return true;
2660         },
2661
2662         'last-of-type': function(node){
2663                 var nodeName = node.nodeName;
2664                 while ((node = node.nextSibling)) if (node.nodeName === nodeName) return false;
2665                 return true;
2666         },
2667
2668         'only-of-type': function(node){
2669                 var prev = node, nodeName = node.nodeName;
2670                 while ((prev = prev.previousSibling)) if (prev.nodeName === nodeName) return false;
2671                 var next = node;
2672                 while ((next = next.nextSibling)) if (next.nodeName === nodeName) return false;
2673                 return true;
2674         },
2675
2676         /*</of-type-pseudo-selectors>*/
2677
2678         // custom pseudos
2679
2680         'enabled': function(node){
2681                 return (node.disabled === false);
2682         },
2683
2684         'disabled': function(node){
2685                 return (node.disabled === true);
2686         },
2687
2688         'checked': function(node){
2689                 return node.checked || node.selected;
2690         },
2691
2692         'focus': function(node){
2693                 return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
2694         },
2695
2696         'root': function(node){
2697                 return (node === this.root);
2698         },
2699         
2700         'selected': function(node){
2701                 return node.selected;
2702         }
2703
2704         /*</pseudo-selectors>*/
2705 };
2706
2707 for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
2708
2709 // attributes methods
2710
2711 local.attributeGetters = {
2712
2713         'class': function(){
2714                 return ('className' in this) ? this.className : this.getAttribute('class');
2715         },
2716
2717         'for': function(){
2718                 return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
2719         },
2720
2721         'href': function(){
2722                 return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
2723         },
2724
2725         'style': function(){
2726                 return (this.style) ? this.style.cssText : this.getAttribute('style');
2727         }
2728
2729 };
2730
2731 local.getAttribute = function(node, name){
2732         // FIXME: check if getAttribute() will get input elements on a form on this browser
2733         // getAttribute is faster than getAttributeNode().nodeValue
2734         var method = this.attributeGetters[name];
2735         if (method) return method.call(node);
2736         var attributeNode = node.getAttributeNode(name);
2737         return attributeNode ? attributeNode.nodeValue : null;
2738 };
2739
2740 // overrides
2741
2742 local.overrides = [];
2743
2744 local.override = function(regexp, method){
2745         this.overrides.push({regexp: regexp, method: method});
2746 };
2747
2748 /*<overrides>*/
2749
2750 /*<query-selector-override>*/
2751
2752 var reEmptyAttribute = /\[.*[*$^]=(?:["']{2})?\]/;
2753
2754 local.override(/./, function(expression, found, first){ //querySelectorAll override
2755
2756         if (!this.querySelectorAll || this.nodeType != 9 || !local.isHTMLDocument || local.brokenMixedCaseQSA ||
2757         (local.brokenCheckedQSA && expression.indexOf(':checked') > -1) ||
2758         (local.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression)) || Slick.disableQSA) return false;
2759
2760         var nodes, node;
2761         try {
2762                 if (first) return this.querySelector(expression) || null;
2763                 else nodes = this.querySelectorAll(expression);
2764         } catch(error){
2765                 return false;
2766         }
2767
2768         var i, hasOthers = !!(found.length);
2769
2770         if (local.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
2771                 if (node.nodeName > '@' && (!hasOthers || !local.uniques[local.getUIDHTML(node)])) found.push(node);
2772         } else for (i = 0; node = nodes[i++];){
2773                 if (!hasOthers || !local.uniques[local.getUIDHTML(node)]) found.push(node);
2774         }
2775
2776         if (hasOthers) local.sort(found);
2777
2778         return true;
2779
2780 });
2781
2782 /*</query-selector-override>*/
2783
2784 /*<tag-override>*/
2785
2786 local.override(/^[\w-]+$|^\*$/, function(expression, found, first){ // tag override
2787         var tag = expression;
2788         if (tag == '*' && local.brokenStarGEBTN) return false;
2789
2790         var nodes = this.getElementsByTagName(tag);
2791
2792         if (first) return nodes[0] || null;
2793         var i, node, hasOthers = !!(found.length);
2794
2795         for (i = 0; node = nodes[i++];){
2796                 if (!hasOthers || !local.uniques[local.getUID(node)]) found.push(node);
2797         }
2798
2799         if (hasOthers) local.sort(found);
2800
2801         return true;
2802 });
2803
2804 /*</tag-override>*/
2805
2806 /*<class-override>*/
2807
2808 local.override(/^\.[\w-]+$/, function(expression, found, first){ // class override
2809         if (!local.isHTMLDocument || (!this.getElementsByClassName && this.querySelectorAll)) return false;
2810
2811         var nodes, node, i, hasOthers = !!(found && found.length), className = expression.substring(1);
2812         if (this.getElementsByClassName && !local.brokenGEBCN){
2813                 nodes = this.getElementsByClassName(className);
2814                 if (first) return nodes[0] || null;
2815                 for (i = 0; node = nodes[i++];){
2816                         if (!hasOthers || !local.uniques[local.getUIDHTML(node)]) found.push(node);
2817                 }
2818         } else {
2819                 var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(className) +'(\\s|$)');
2820                 nodes = this.getElementsByTagName('*');
2821                 for (i = 0; node = nodes[i++];){
2822                         className = node.className;
2823                         if (!className || !matchClass.test(className)) continue;
2824                         if (first) return node;
2825                         if (!hasOthers || !local.uniques[local.getUIDHTML(node)]) found.push(node);
2826                 }
2827         }
2828         if (hasOthers) local.sort(found);
2829         return (first) ? null : true;
2830 });
2831
2832 /*</class-override>*/
2833
2834 /*<id-override>*/
2835
2836 local.override(/^#[\w-]+$/, function(expression, found, first){ // ID override
2837         if (!local.isHTMLDocument || this.nodeType != 9) return false;
2838
2839         var id = expression.substring(1), el = this.getElementById(id);
2840         if (!el) return found;
2841         if (local.idGetsName && el.getAttributeNode('id').nodeValue != id) return false;
2842         if (first) return el || null;
2843         var hasOthers = !!(found.length);
2844         if (!hasOthers || !local.uniques[local.getUIDHTML(el)]) found.push(el);
2845         if (hasOthers) local.sort(found);
2846         return true;
2847 });
2848
2849 /*</id-override>*/
2850
2851 /*</overrides>*/
2852
2853 if (typeof document != 'undefined') local.setDocument(document);
2854
2855 // Slick
2856
2857 var Slick = local.Slick = (this.Slick || {});
2858
2859 Slick.version = '0.9dev';
2860
2861 // Slick finder
2862
2863 Slick.search = function(context, expression, append){
2864         return local.search(context, expression, append);
2865 };
2866
2867 Slick.find = function(context, expression){
2868         return local.search(context, expression, null, true);
2869 };
2870
2871 // Slick containment checker
2872
2873 Slick.contains = function(container, node){
2874         local.setDocument(container);
2875         return local.contains(container, node);
2876 };
2877
2878 // Slick attribute getter
2879
2880 Slick.getAttribute = function(node, name){
2881         return local.getAttribute(node, name);
2882 };
2883
2884 // Slick matcher
2885
2886 Slick.match = function(node, selector){
2887         if (!(node && selector)) return false;
2888         if (!selector || selector === node) return true;
2889         if (typeof selector != 'string') return false;
2890         local.setDocument(node);
2891         return local.matchNode(node, selector);
2892 };
2893
2894 // Slick attribute accessor
2895
2896 Slick.defineAttributeGetter = function(name, fn){
2897         local.attributeGetters[name] = fn;
2898         return this;
2899 };
2900
2901 Slick.lookupAttributeGetter = function(name){
2902         return local.attributeGetters[name];
2903 };
2904
2905 // Slick pseudo accessor
2906
2907 Slick.definePseudo = function(name, fn){
2908         local['pseudo:' + name] = function(node, argument){
2909                 return fn.call(node, argument);
2910         };
2911         return this;
2912 };
2913
2914 Slick.lookupPseudo = function(name){
2915         var pseudo = local['pseudo:' + name];
2916         if (pseudo) return function(argument){
2917                 return pseudo.call(this, argument);
2918         };
2919         return null;
2920 };
2921
2922 // Slick overrides accessor
2923
2924 Slick.override = function(regexp, fn){
2925         local.override(regexp, fn);
2926         return this;
2927 };
2928
2929 Slick.isXML = local.isXML;
2930
2931 Slick.uidOf = function(node){
2932         return local.getUIDHTML(node);
2933 };
2934
2935 if (!this.Slick) this.Slick = Slick;
2936
2937 }).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
2938
2939
2940 /*
2941 ---
2942
2943 name: Element
2944
2945 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.
2946
2947 license: MIT-style license.
2948
2949 requires: [Window, Document, Array, String, Function, Number, Slick.Parser, Slick.Finder]
2950
2951 provides: [Element, Elements, $, $$, Iframe, Selectors]
2952
2953 ...
2954 */
2955
2956 var Element = function(tag, props){
2957         var konstructor = Element.Constructors[tag];
2958         if (konstructor) return konstructor(props);
2959         if (typeof tag != 'string') return document.id(tag).set(props);
2960
2961         if (!props) props = {};
2962
2963         if (!tag.test(/^[\w-]+$/)){
2964                 var parsed = Slick.parse(tag).expressions[0][0];
2965                 tag = (parsed.tag == '*') ? 'div' : parsed.tag;
2966                 if (parsed.id && props.id == null) props.id = parsed.id;
2967
2968                 var attributes = parsed.attributes;
2969                 if (attributes) for (var i = 0, l = attributes.length; i < l; i++){
2970                         var attr = attributes[i];
2971                         if (attr.value != null && attr.operator == '=' && props[attr.key] == null)
2972                                 props[attr.key] = attr.value;
2973                 }
2974
2975                 if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' ');
2976         }
2977
2978         return document.newElement(tag, props);
2979 };
2980
2981 if (Browser.Element) Element.prototype = Browser.Element.prototype;
2982
2983 new Type('Element', Element).mirror(function(name){
2984         if (Array.prototype[name]) return;
2985
2986         var obj = {};
2987         obj[name] = function(){
2988                 var results = [], args = arguments, elements = true;
2989                 for (var i = 0, l = this.length; i < l; i++){
2990                         var element = this[i], result = results[i] = element[name].apply(element, args);
2991                         elements = (elements && typeOf(result) == 'element');
2992                 }
2993                 return (elements) ? new Elements(results) : results;
2994         };
2995
2996         Elements.implement(obj);
2997 });
2998
2999 if (!Browser.Element){
3000         Element.parent = Object;
3001
3002         Element.Prototype = {'$family': Function.from('element').hide()};
3003
3004         Element.mirror(function(name, method){
3005                 Element.Prototype[name] = method;
3006         });
3007 }
3008
3009 Element.Constructors = {};
3010
3011 //<1.2compat>
3012
3013 Element.Constructors = new Hash;
3014
3015 //</1.2compat>
3016
3017 var IFrame = new Type('IFrame', function(){
3018         var params = Array.link(arguments, {
3019                 properties: Type.isObject,
3020                 iframe: function(obj){
3021                         return (obj != null);
3022                 }
3023         });
3024
3025         var props = params.properties || {}, iframe;
3026         if (params.iframe) iframe = document.id(params.iframe);
3027         var onload = props.onload || function(){};
3028         delete props.onload;
3029         props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.uniqueID()].pick();
3030         iframe = new Element(iframe || 'iframe', props);
3031
3032         var onLoad = function(){
3033                 onload.call(iframe.contentWindow);
3034         };
3035         
3036         if (window.frames[props.id]) onLoad();
3037         else iframe.addListener('load', onLoad);
3038         return iframe;
3039 });
3040
3041 var Elements = this.Elements = function(nodes){
3042         if (nodes && nodes.length){
3043                 var uniques = {}, node;
3044                 for (var i = 0; node = nodes[i++];){
3045                         var uid = Slick.uidOf(node);
3046                         if (!uniques[uid]){
3047                                 uniques[uid] = true;
3048                                 this.push(node);
3049                         }
3050                 }
3051         }
3052 };
3053
3054 Elements.prototype = {length: 0};
3055 Elements.parent = Array;
3056
3057 new Type('Elements', Elements).implement({
3058
3059         filter: function(filter, bind){
3060                 if (!filter) return this;
3061                 return new Elements(Array.filter(this, (typeOf(filter) == 'string') ? function(item){
3062                         return item.match(filter);
3063                 } : filter, bind));
3064         }.protect(),
3065
3066         push: function(){
3067                 var length = this.length;
3068                 for (var i = 0, l = arguments.length; i < l; i++){
3069                         var item = document.id(arguments[i]);
3070                         if (item) this[length++] = item;
3071                 }
3072                 return (this.length = length);
3073         }.protect(),
3074
3075         concat: function(){
3076                 var newElements = new Elements(this);
3077                 for (var i = 0, l = arguments.length; i < l; i++){
3078                         var item = arguments[i];
3079                         if (Type.isEnumerable(item)) newElements.append(item);
3080                         else newElements.push(item);
3081                 }
3082                 return newElements;
3083         }.protect(),
3084
3085         append: function(collection){
3086                 for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]);
3087                 return this;
3088         }.protect(),
3089
3090         empty: function(){
3091                 while (this.length) delete this[--this.length];
3092                 return this;
3093         }.protect()
3094
3095 });
3096
3097 (function(){
3098
3099 // FF, IE
3100 var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2};
3101
3102 splice.call(object, 1, 1);
3103 if (object[1] == 1) Elements.implement('splice', function(){
3104         var length = this.length;
3105         splice.apply(this, arguments);
3106         while (length >= this.length) delete this[length--];
3107         return this;
3108 }.protect());
3109
3110 Elements.implement(Array.prototype);
3111
3112 Array.mirror(Elements);
3113
3114 /*<ltIE8>*/
3115 var createElementAcceptsHTML;
3116 try {
3117         var x = document.createElement('<input name=x>');
3118         createElementAcceptsHTML = (x.name == 'x');
3119 } catch(e){}
3120
3121 var escapeQuotes = function(html){
3122         return ('' + html).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
3123 };
3124 /*</ltIE8>*/
3125
3126 Document.implement({
3127
3128         newElement: function(tag, props){
3129                 if (props && props.checked != null) props.defaultChecked = props.checked;
3130                 /*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
3131                 if (createElementAcceptsHTML && props){
3132                         tag = '<' + tag;
3133                         if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
3134                         if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
3135                         tag += '>';
3136                         delete props.name;
3137                         delete props.type;
3138                 }
3139                 /*</ltIE8>*/
3140                 return this.id(this.createElement(tag)).set(props);
3141         }
3142
3143 });
3144
3145 })();
3146
3147 Document.implement({
3148
3149         newTextNode: function(text){
3150                 return this.createTextNode(text);
3151         },
3152
3153         getDocument: function(){
3154                 return this;
3155         },
3156
3157         getWindow: function(){
3158                 return this.window;
3159         },
3160
3161         id: (function(){
3162
3163                 var types = {
3164
3165                         string: function(id, nocash, doc){
3166                                 id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1'));
3167                                 return (id) ? types.element(id, nocash) : null;
3168                         },
3169
3170                         element: function(el, nocash){
3171                                 $uid(el);
3172                                 if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
3173                                         Object.append(el, Element.Prototype);
3174                                 }
3175                                 return el;
3176                         },
3177
3178                         object: function(obj, nocash, doc){
3179                                 if (obj.toElement) return types.element(obj.toElement(doc), nocash);
3180                                 return null;
3181                         }
3182
3183                 };
3184
3185                 types.textnode = types.whitespace = types.window = types.document = function(zero){
3186                         return zero;
3187                 };
3188
3189                 return function(el, nocash, doc){
3190                         if (el && el.$family && el.uid) return el;
3191                         var type = typeOf(el);
3192                         return (types[type]) ? types[type](el, nocash, doc || document) : null;
3193                 };
3194
3195         })()
3196
3197 });
3198
3199 if (window.$ == null) Window.implement('$', function(el, nc){
3200         return document.id(el, nc, this.document);
3201 });
3202
3203 Window.implement({
3204
3205         getDocument: function(){
3206                 return this.document;
3207         },
3208
3209         getWindow: function(){
3210                 return this;
3211         }
3212
3213 });
3214
3215 [Document, Element].invoke('implement', {
3216
3217         getElements: function(expression){
3218                 return Slick.search(this, expression, new Elements);
3219         },
3220
3221         getElement: function(expression){
3222                 return document.id(Slick.find(this, expression));
3223         }
3224
3225 });
3226
3227 //<1.2compat>
3228
3229 (function(search, find, match){
3230
3231         this.Selectors = {};
3232         var pseudos = this.Selectors.Pseudo = new Hash();
3233
3234         var addSlickPseudos = function(){
3235                 for (var name in pseudos) if (pseudos.hasOwnProperty(name)){
3236                         Slick.definePseudo(name, pseudos[name]);
3237                         delete pseudos[name];
3238                 }
3239         };
3240
3241         Slick.search = function(context, expression, append){
3242                 addSlickPseudos();
3243                 return search.call(this, context, expression, append);
3244         };
3245
3246         Slick.find = function(context, expression){
3247                 addSlickPseudos();
3248                 return find.call(this, context, expression);
3249         };
3250
3251         Slick.match = function(node, selector){
3252                 addSlickPseudos();
3253                 return match.call(this, node, selector);
3254         };
3255
3256 })(Slick.search, Slick.find, Slick.match);
3257
3258 if (window.$$ == null) Window.implement('$$', function(selector){
3259         var elements = new Elements;
3260         if (arguments.length == 1 && typeof selector == 'string') return Slick.search(this.document, selector, elements);
3261         var args = Array.flatten(arguments);
3262         for (var i = 0, l = args.length; i < l; i++){
3263                 var item = args[i];
3264                 switch (typeOf(item)){
3265                         case 'element': elements.push(item); break;
3266                         case 'string': Slick.search(this.document, item, elements);
3267                 }
3268         }
3269         return elements;
3270 });
3271
3272 //</1.2compat>
3273
3274 if (window.$$ == null) Window.implement('$$', function(selector){
3275         if (arguments.length == 1){
3276                 if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements);
3277                 else if (Type.isEnumerable(selector)) return new Elements(selector);
3278         }
3279         return new Elements(arguments);
3280 });
3281
3282 (function(){
3283
3284 var collected = {}, storage = {};
3285 var props = {input: 'checked', option: 'selected', textarea: 'value'};
3286
3287 var get = function(uid){
3288         return (storage[uid] || (storage[uid] = {}));
3289 };
3290
3291 var clean = function(item){
3292         if (item.removeEvents) item.removeEvents();
3293         if (item.clearAttributes) item.clearAttributes();
3294         var uid = item.uid;
3295         if (uid != null){
3296                 delete collected[uid];
3297                 delete storage[uid];
3298         }
3299         return item;
3300 };
3301
3302 var camels = ['defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly',
3303         'rowSpan', 'tabIndex', 'useMap'
3304 ];
3305 var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readOnly', 'multiple', 'selected',
3306         'noresize', 'defer'
3307 ];
3308  var attributes = {
3309         'html': 'innerHTML',
3310         'class': 'className',
3311         'for': 'htmlFor',
3312         'text': (function(){
3313                 var temp = document.createElement('div');
3314                 return (temp.innerText == null) ? 'textContent' : 'innerText';
3315         })()
3316 };
3317 var readOnly = ['type'];
3318 var expandos = ['value', 'defaultValue'];
3319 var uriAttrs = /^(?:href|src|usemap)$/i;
3320
3321 bools = bools.associate(bools);
3322 camels = camels.associate(camels.map(String.toLowerCase));
3323 readOnly = readOnly.associate(readOnly);
3324
3325 Object.append(attributes, expandos.associate(expandos));
3326
3327 var inserters = {
3328
3329         before: function(context, element){
3330                 var parent = element.parentNode;
3331                 if (parent) parent.insertBefore(context, element);
3332         },
3333
3334         after: function(context, element){
3335                 var parent = element.parentNode;
3336                 if (parent) parent.insertBefore(context, element.nextSibling);
3337         },
3338
3339         bottom: function(context, element){
3340                 element.appendChild(context);
3341         },
3342
3343         top: function(context, element){
3344                 element.insertBefore(context, element.firstChild);
3345         }
3346
3347 };
3348
3349 inserters.inside = inserters.bottom;
3350
3351 //<1.2compat>
3352
3353 Object.each(inserters, function(inserter, where){
3354
3355         where = where.capitalize();
3356
3357         var methods = {};
3358
3359         methods['inject' + where] = function(el){
3360                 inserter(this, document.id(el, true));
3361                 return this;
3362         };
3363
3364         methods['grab' + where] = function(el){
3365                 inserter(document.id(el, true), this);
3366                 return this;
3367         };
3368
3369         Element.implement(methods);
3370
3371 });
3372
3373 //</1.2compat>
3374
3375 var injectCombinator = function(expression, combinator){
3376         if (!expression) return combinator;
3377
3378         expression = Slick.parse(expression);
3379
3380         var expressions = expression.expressions;
3381         for (var i = expressions.length; i--;)
3382                 expressions[i][0].combinator = combinator;
3383
3384         return expression;
3385 };
3386
3387 Element.implement({
3388
3389         set: function(prop, value){
3390                 var property = Element.Properties[prop];
3391                 (property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value);
3392         }.overloadSetter(),
3393
3394         get: function(prop){
3395                 var property = Element.Properties[prop];
3396                 return (property && property.get) ? property.get.apply(this) : this.getProperty(prop);
3397         }.overloadGetter(),
3398
3399         erase: function(prop){
3400                 var property = Element.Properties[prop];
3401                 (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
3402                 return this;
3403         },
3404
3405         setProperty: function(attribute, value){
3406                 attribute = camels[attribute] || attribute;
3407                 if (value == null) return this.removeProperty(attribute);
3408                 var key = attributes[attribute];
3409                 (key) ? this[key] = value :
3410                         (bools[attribute]) ? this[attribute] = !!value : this.setAttribute(attribute, '' + value);
3411                 return this;
3412         },
3413
3414         setProperties: function(attributes){
3415                 for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
3416                 return this;
3417         },
3418
3419         getProperty: function(attribute){
3420                 attribute = camels[attribute] || attribute;
3421                 var key = attributes[attribute] || readOnly[attribute];
3422                 return (key) ? this[key] :
3423                         (bools[attribute]) ? !!this[attribute] :
3424                         (uriAttrs.test(attribute) ? this.getAttribute(attribute, 2) :
3425                         (key = this.getAttributeNode(attribute)) ? key.nodeValue : null) || null;
3426         },
3427
3428         getProperties: function(){
3429                 var args = Array.from(arguments);
3430                 return args.map(this.getProperty, this).associate(args);
3431         },
3432
3433         removeProperty: function(attribute){
3434                 attribute = camels[attribute] || attribute;
3435                 var key = attributes[attribute];
3436                 (key) ? this[key] = '' :
3437                         (bools[attribute]) ? this[attribute] = false : this.removeAttribute(attribute);
3438                 return this;
3439         },
3440
3441         removeProperties: function(){
3442                 Array.each(arguments, this.removeProperty, this);
3443                 return this;
3444         },
3445
3446         hasClass: function(className){
3447                 return this.className.clean().contains(className, ' ');
3448         },
3449
3450         addClass: function(className){
3451                 if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
3452                 return this;
3453         },
3454
3455         removeClass: function(className){
3456                 this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
3457                 return this;
3458         },
3459
3460         toggleClass: function(className, force){
3461                 if (force == null) force = !this.hasClass(className);
3462                 return (force) ? this.addClass(className) : this.removeClass(className);
3463         },
3464
3465         adopt: function(){
3466                 var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length;
3467                 if (length > 1) parent = fragment = document.createDocumentFragment();
3468
3469                 for (var i = 0; i < length; i++){
3470                         var element = document.id(elements[i], true);
3471                         if (element) parent.appendChild(element);
3472                 }
3473
3474                 if (fragment) this.appendChild(fragment);
3475
3476                 return this;
3477         },
3478
3479         appendText: function(text, where){
3480                 return this.grab(this.getDocument().newTextNode(text), where);
3481         },
3482
3483         grab: function(el, where){
3484                 inserters[where || 'bottom'](document.id(el, true), this);
3485                 return this;
3486         },
3487
3488         inject: function(el, where){
3489                 inserters[where || 'bottom'](this, document.id(el, true));
3490                 return this;
3491         },
3492
3493         replaces: function(el){
3494                 el = document.id(el, true);
3495                 el.parentNode.replaceChild(this, el);
3496                 return this;
3497         },
3498
3499         wraps: function(el, where){
3500                 el = document.id(el, true);
3501                 return this.replaces(el).grab(el, where);
3502         },
3503
3504         getPrevious: function(expression){
3505                 return document.id(Slick.find(this, injectCombinator(expression, '!~')));
3506         },
3507
3508         getAllPrevious: function(expression){
3509                 return Slick.search(this, injectCombinator(expression, '!~'), new Elements);
3510         },
3511
3512         getNext: function(expression){
3513                 return document.id(Slick.find(this, injectCombinator(expression, '~')));
3514         },
3515
3516         getAllNext: function(expression){
3517                 return Slick.search(this, injectCombinator(expression, '~'), new Elements);
3518         },
3519
3520         getFirst: function(expression){
3521                 return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]);
3522         },
3523
3524         getLast: function(expression){
3525                 return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast());
3526         },
3527
3528         getParent: function(expression){
3529                 return document.id(Slick.find(this, injectCombinator(expression, '!')));
3530         },
3531
3532         getParents: function(expression){
3533                 return Slick.search(this, injectCombinator(expression, '!'), new Elements);
3534         },
3535
3536         getSiblings: function(expression){
3537                 return Slick.search(this, injectCombinator(expression, '~~'), new Elements);
3538         },
3539
3540         getChildren: function(expression){
3541                 return Slick.search(this, injectCombinator(expression, '>'), new Elements);
3542         },
3543
3544         getWindow: function(){
3545                 return this.ownerDocument.window;
3546         },
3547
3548         getDocument: function(){
3549                 return this.ownerDocument;
3550         },
3551
3552         getElementById: function(id){
3553                 return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1')));
3554         },
3555
3556         getSelected: function(){
3557                 this.selectedIndex; // Safari 3.2.1
3558                 return new Elements(Array.from(this.options).filter(function(option){
3559                         return option.selected;
3560                 }));
3561         },
3562
3563         toQueryString: function(){
3564                 var queryString = [];
3565                 this.getElements('input, select, textarea').each(function(el){
3566                         var type = el.type;
3567                         if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return;
3568
3569                         var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){
3570                                 // IE
3571                                 return document.id(opt).get('value');
3572                         }) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value');
3573
3574                         Array.from(value).each(function(val){
3575                                 if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val));
3576                         });
3577                 });
3578                 return queryString.join('&');
3579         },
3580
3581         clone: function(contents, keepid){
3582                 contents = contents !== false;
3583                 var clone = this.cloneNode(contents);
3584                 var clean = function(node, element){
3585                         if (!keepid) node.removeAttribute('id');
3586                         if (Browser.ie){
3587                                 node.clearAttributes();
3588                                 node.mergeAttributes(element);
3589                                 node.removeAttribute('uid');
3590                                 if (node.options){
3591                                         var no = node.options, eo = element.options;
3592                                         for (var j = no.length; j--;) no[j].selected = eo[j].selected;
3593                                 }
3594                         }
3595                         var prop = props[element.tagName.toLowerCase()];
3596                         if (prop && element[prop]) node[prop] = element[prop];
3597                 };
3598
3599                 var i;
3600                 if (contents){
3601                         var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
3602                         for (i = ce.length; i--;) clean(ce[i], te[i]);
3603                 }
3604
3605                 clean(clone, this);
3606                 if (Browser.ie){
3607                         var ts = this.getElementsByTagName('object'),
3608                                 cs = clone.getElementsByTagName('object'),
3609                                 tl = ts.length, cl = cs.length;
3610                         for (i = 0; i < tl && i < cl; i++)
3611                                 cs[i].outerHTML = ts[i].outerHTML;
3612                 }
3613                 return document.id(clone);
3614         },
3615
3616         destroy: function(){
3617                 var children = clean(this).getElementsByTagName('*');
3618                 Array.each(children, clean);
3619                 Element.dispose(this);
3620                 return null;
3621         },
3622
3623         empty: function(){
3624                 Array.from(this.childNodes).each(Element.dispose);
3625                 return this;
3626         },
3627
3628         dispose: function(){
3629                 return (this.parentNode) ? this.parentNode.removeChild(this) : this;
3630         },
3631
3632         match: function(expression){
3633                 return !expression || Slick.match(this, expression);
3634         }
3635
3636 });
3637
3638 var contains = {contains: function(element){
3639         return Slick.contains(this, element);
3640 }};
3641
3642 if (!document.contains) Document.implement(contains);
3643 if (!document.createElement('div').contains) Element.implement(contains);
3644
3645 //<1.2compat>
3646
3647 Element.implement('hasChild', function(element){
3648         return this !== element && this.contains(element);
3649 });
3650
3651 //</1.2compat>
3652
3653 [Element, Window, Document].invoke('implement', {
3654
3655         addListener: function(type, fn){
3656                 if (type == 'unload'){
3657                         var old = fn, self = this;
3658                         fn = function(){
3659                                 self.removeListener('unload', fn);
3660                                 old();
3661                         };
3662                 } else {
3663                         collected[this.uid] = this;
3664                 }
3665                 if (this.addEventListener) this.addEventListener(type, fn, false);
3666                 else this.attachEvent('on' + type, fn);
3667                 return this;
3668         },
3669
3670         removeListener: function(type, fn){
3671                 if (this.removeEventListener) this.removeEventListener(type, fn, false);
3672                 else this.detachEvent('on' + type, fn);
3673                 return this;
3674         },
3675
3676         retrieve: function(property, dflt){
3677                 var storage = get(this.uid), prop = storage[property];
3678                 if (dflt != null && prop == null) prop = storage[property] = dflt;
3679                 return prop != null ? prop : null;
3680         },
3681
3682         store: function(property, value){
3683                 var storage = get(this.uid);
3684                 storage[property] = value;
3685                 return this;
3686         },
3687
3688         eliminate: function(property){
3689                 var storage = get(this.uid);
3690                 delete storage[property];
3691                 return this;
3692         }
3693
3694 });
3695
3696 // IE purge
3697 if (window.attachEvent && !window.addEventListener) window.addListener('unload', function(){
3698         Object.each(collected, clean);
3699         if (window.CollectGarbage) CollectGarbage();
3700 });
3701
3702 })();
3703
3704 Element.Properties = {};
3705
3706 //<1.2compat>
3707
3708 Element.Properties = new Hash;
3709
3710 //</1.2compat>
3711
3712 Element.Properties.style = {
3713
3714         set: function(style){
3715                 this.style.cssText = style;
3716         },
3717
3718         get: function(){
3719                 return this.style.cssText;
3720         },
3721
3722         erase: function(){
3723                 this.style.cssText = '';
3724         }
3725
3726 };
3727
3728 Element.Properties.tag = {
3729
3730         get: function(){
3731                 return this.tagName.toLowerCase();
3732         }
3733
3734 };
3735
3736 (function(maxLength){
3737         if (maxLength != null) Element.Properties.maxlength = Element.Properties.maxLength = {
3738                 get: function(){
3739                         var maxlength = this.getAttribute('maxLength');
3740                         return maxlength == maxLength ? null : maxlength;
3741                 }
3742         };
3743 })(document.createElement('input').getAttribute('maxLength'));
3744
3745 Element.Properties.html = (function(){
3746
3747         var tableTest = Function.attempt(function(){
3748                 var table = document.createElement('table');
3749                 table.innerHTML = '<tr><td></td></tr>';
3750         });
3751
3752         var wrapper = document.createElement('div');
3753
3754         var translations = {
3755                 table: [1, '<table>', '</table>'],
3756                 select: [1, '<select>', '</select>'],
3757                 tbody: [2, '<table><tbody>', '</tbody></table>'],
3758                 tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
3759         };
3760         translations.thead = translations.tfoot = translations.tbody;
3761
3762         var html = {
3763                 set: function(){
3764                         var html = Array.flatten(arguments).join('');
3765                         var wrap = (!tableTest && translations[this.get('tag')]);
3766                         if (wrap){
3767                                 var first = wrapper;
3768                                 first.innerHTML = wrap[1] + html + wrap[2];
3769                                 for (var i = wrap[0]; i--;) first = first.firstChild;
3770                                 this.empty().adopt(first.childNodes);
3771                         } else {
3772                                 this.innerHTML = html;
3773                         }
3774                 }
3775         };
3776
3777         html.erase = html.set;
3778
3779         return html;
3780 })();
3781
3782
3783 /*
3784 ---
3785
3786 name: Element.Event
3787
3788 description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events.
3789
3790 license: MIT-style license.
3791
3792 requires: [Element, Event]
3793
3794 provides: Element.Event
3795
3796 ...
3797 */
3798
3799 (function(){
3800
3801 Element.Properties.events = {set: function(events){
3802         this.addEvents(events);
3803 }};
3804
3805 [Element, Window, Document].invoke('implement', {
3806
3807         addEvent: function(type, fn){
3808                 var events = this.retrieve('events', {});
3809                 if (!events[type]) events[type] = {keys: [], values: []};
3810                 if (events[type].keys.contains(fn)) return this;
3811                 events[type].keys.push(fn);
3812                 var realType = type,
3813                         custom = Element.Events[type],
3814                         condition = fn,
3815                         self = this;
3816                 if (custom){
3817                         if (custom.onAdd) custom.onAdd.call(this, fn);
3818                         if (custom.condition){
3819                                 condition = function(event){
3820                                         if (custom.condition.call(this, event)) return fn.call(this, event);
3821                                         return true;
3822                                 };
3823                         }
3824                         realType = custom.base || realType;
3825                 }
3826                 var defn = function(){
3827                         return fn.call(self);
3828                 };
3829                 var nativeEvent = Element.NativeEvents[realType];
3830                 if (nativeEvent){
3831                         if (nativeEvent == 2){
3832                                 defn = function(event){
3833                                         event = new Event(event, self.getWindow());
3834                                         if (condition.call(self, event) === false) event.stop();
3835                                 };
3836                         }
3837                         this.addListener(realType, defn);
3838                 }
3839                 events[type].values.push(defn);
3840                 return this;
3841         },
3842
3843         removeEvent: function(type, fn){
3844                 var events = this.retrieve('events');
3845                 if (!events || !events[type]) return this;
3846                 var list = events[type];
3847                 var index = list.keys.indexOf(fn);
3848                 if (index == -1) return this;
3849                 var value = list.values[index];
3850                 delete list.keys[index];
3851                 delete list.values[index];
3852                 var custom = Element.Events[type];
3853                 if (custom){
3854                         if (custom.onRemove) custom.onRemove.call(this, fn);
3855                         type = custom.base || type;
3856                 }
3857                 return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this;
3858         },
3859
3860         addEvents: function(events){
3861                 for (var event in events) this.addEvent(event, events[event]);
3862                 return this;
3863         },
3864
3865         removeEvents: function(events){
3866                 var type;
3867                 if (typeOf(events) == 'object'){
3868                         for (type in events) this.removeEvent(type, events[type]);
3869                         return this;
3870                 }
3871                 var attached = this.retrieve('events');
3872                 if (!attached) return this;
3873                 if (!events){
3874                         for (type in attached) this.removeEvents(type);
3875                         this.eliminate('events');
3876                 } else if (attached[events]){
3877                         attached[events].keys.each(function(fn){
3878                                 this.removeEvent(events, fn);
3879                         }, this);
3880                         delete attached[events];
3881                 }
3882                 return this;
3883         },
3884
3885         fireEvent: function(type, args, delay){
3886                 var events = this.retrieve('events');
3887                 if (!events || !events[type]) return this;
3888                 args = Array.from(args);
3889
3890                 events[type].keys.each(function(fn){
3891                         if (delay) fn.delay(delay, this, args);
3892                         else fn.apply(this, args);
3893                 }, this);
3894                 return this;
3895         },
3896
3897         cloneEvents: function(from, type){
3898                 from = document.id(from);
3899                 var events = from.retrieve('events');
3900                 if (!events) return this;
3901                 if (!type){
3902                         for (var eventType in events) this.cloneEvents(from, eventType);
3903                 } else if (events[type]){
3904                         events[type].keys.each(function(fn){
3905                                 this.addEvent(type, fn);
3906                         }, this);
3907                 }
3908                 return this;
3909         }
3910
3911 });
3912
3913 // IE9
3914 try {
3915         if (typeof HTMLElement != 'undefined')
3916                 HTMLElement.prototype.fireEvent = Element.prototype.fireEvent;
3917 } catch(e){}
3918
3919 Element.NativeEvents = {
3920         click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
3921         mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
3922         mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
3923         keydown: 2, keypress: 2, keyup: 2, //keyboard
3924         orientationchange: 2, // mobile
3925         touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch
3926         gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture
3927         focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements
3928         load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
3929         error: 1, abort: 1, scroll: 1 //misc
3930 };
3931
3932 var check = function(event){
3933         var related = event.relatedTarget;
3934         if (related == null) return true;
3935         if (!related) return false;
3936         return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
3937 };
3938
3939 Element.Events = {
3940
3941         mouseenter: {
3942                 base: 'mouseover',
3943                 condition: check
3944         },
3945
3946         mouseleave: {
3947                 base: 'mouseout',
3948                 condition: check
3949         },
3950
3951         mousewheel: {
3952                 base: (Browser.firefox) ? 'DOMMouseScroll' : 'mousewheel'
3953         }
3954
3955 };
3956
3957 //<1.2compat>
3958
3959 Element.Events = new Hash(Element.Events);
3960
3961 //</1.2compat>
3962
3963 })();
3964
3965
3966 /*
3967 ---
3968
3969 name: Element.Style
3970
3971 description: Contains methods for interacting with the styles of Elements in a fashionable way.
3972
3973 license: MIT-style license.
3974
3975 requires: Element
3976
3977 provides: Element.Style
3978
3979 ...
3980 */
3981
3982 (function(){
3983
3984 var html = document.html;
3985
3986 Element.Properties.styles = {set: function(styles){
3987         this.setStyles(styles);
3988 }};
3989
3990 var hasOpacity = (html.style.opacity != null);
3991 var reAlpha = /alpha\(opacity=([\d.]+)\)/i;
3992
3993 var setOpacity = function(element, opacity){
3994         if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1;
3995         if (hasOpacity){
3996                 element.style.opacity = opacity;
3997         } else {
3998                 opacity = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
3999                 var filter = element.style.filter || element.getComputedStyle('filter') || '';
4000                 element.style.filter = filter.test(reAlpha) ? filter.replace(reAlpha, opacity) : filter + opacity;
4001         }
4002 };
4003
4004 Element.Properties.opacity = {
4005
4006         set: function(opacity){
4007                 var visibility = this.style.visibility;
4008                 if (opacity == 0 && visibility != 'hidden') this.style.visibility = 'hidden';
4009                 else if (opacity != 0 && visibility != 'visible') this.style.visibility = 'visible';
4010
4011                 setOpacity(this, opacity);
4012         },
4013
4014         get: (hasOpacity) ? function(){
4015                 var opacity = this.style.opacity || this.getComputedStyle('opacity');
4016                 return (opacity == '') ? 1 : opacity;
4017         } : function(){
4018                 var opacity, filter = (this.style.filter || this.getComputedStyle('filter'));
4019                 if (filter) opacity = filter.match(reAlpha);
4020                 return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
4021         }
4022
4023 };
4024
4025 var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat';
4026
4027 Element.implement({
4028
4029         getComputedStyle: function(property){
4030                 if (this.currentStyle) return this.currentStyle[property.camelCase()];
4031                 var defaultView = Element.getDocument(this).defaultView,
4032                         computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
4033                 return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : null;
4034         },
4035
4036         setOpacity: function(value){
4037                 setOpacity(this, value);
4038                 return this;
4039         },
4040
4041         getOpacity: function(){
4042                 return this.get('opacity');
4043         },
4044
4045         setStyle: function(property, value){
4046                 switch (property){
4047                         case 'opacity': return this.set('opacity', parseFloat(value));
4048                         case 'float': property = floatName;
4049                 }
4050                 property = property.camelCase();
4051                 if (typeOf(value) != 'string'){
4052                         var map = (Element.Styles[property] || '@').split(' ');
4053                         value = Array.from(value).map(function(val, i){
4054                                 if (!map[i]) return '';
4055                                 return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
4056                         }).join(' ');
4057                 } else if (value == String(Number(value))){
4058                         value = Math.round(value);
4059                 }
4060                 this.style[property] = value;
4061                 return this;
4062         },
4063
4064         getStyle: function(property){
4065                 switch (property){
4066                         case 'opacity': return this.get('opacity');
4067                         case 'float': property = floatName;
4068                 }
4069                 property = property.camelCase();
4070                 var result = this.style[property];
4071                 if (!result || property == 'zIndex'){
4072                         result = [];
4073                         for (var style in Element.ShortStyles){
4074                                 if (property != style) continue;
4075                                 for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
4076                                 return result.join(' ');
4077                         }
4078                         result = this.getComputedStyle(property);
4079                 }
4080                 if (result){
4081                         result = String(result);
4082                         var color = result.match(/rgba?\([\d\s,]+\)/);
4083                         if (color) result = result.replace(color[0], color[0].rgbToHex());
4084                 }
4085                 if (Browser.opera || (Browser.ie && isNaN(parseFloat(result)))){
4086                         if (property.test(/^(height|width)$/)){
4087                                 var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
4088                                 values.each(function(value){
4089                                         size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
4090                                 }, this);
4091                                 return this['offset' + property.capitalize()] - size + 'px';
4092                         }
4093                         if (Browser.opera && String(result).indexOf('px') != -1) return result;
4094                         if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
4095                 }
4096                 return result;
4097         },
4098
4099         setStyles: function(styles){
4100                 for (var style in styles) this.setStyle(style, styles[style]);
4101                 return this;
4102         },
4103
4104         getStyles: function(){
4105                 var result = {};
4106                 Array.flatten(arguments).each(function(key){
4107                         result[key] = this.getStyle(key);
4108                 }, this);
4109                 return result;
4110         }
4111
4112 });
4113
4114 Element.Styles = {
4115         left: '@px', top: '@px', bottom: '@px', right: '@px',
4116         width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
4117         backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
4118         fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
4119         margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
4120         borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
4121         zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
4122 };
4123
4124 //<1.2compat>
4125
4126 Element.Styles = new Hash(Element.Styles);
4127
4128 //</1.2compat>
4129
4130 Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
4131
4132 ['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
4133         var Short = Element.ShortStyles;
4134         var All = Element.Styles;
4135         ['margin', 'padding'].each(function(style){
4136                 var sd = style + direction;
4137                 Short[style][sd] = All[sd] = '@px';
4138         });
4139         var bd = 'border' + direction;
4140         Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
4141         var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
4142         Short[bd] = {};
4143         Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
4144         Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
4145         Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
4146 });
4147
4148 })();
4149
4150
4151 /*
4152 ---
4153
4154 name: Element.Dimensions
4155
4156 description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
4157
4158 license: MIT-style license.
4159
4160 credits:
4161   - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
4162   - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
4163
4164 requires: [Element, Element.Style]
4165
4166 provides: [Element.Dimensions]
4167
4168 ...
4169 */
4170
4171 (function(){
4172
4173 Element.implement({
4174
4175         scrollTo: function(x, y){
4176                 if (isBody(this)){
4177                         this.getWindow().scrollTo(x, y);
4178                 } else {
4179                         this.scrollLeft = x;
4180                         this.scrollTop = y;
4181                 }
4182                 return this;
4183         },
4184
4185         getSize: function(){
4186                 if (isBody(this)) return this.getWindow().getSize();
4187                 return {x: this.offsetWidth, y: this.offsetHeight};
4188         },
4189
4190         getScrollSize: function(){
4191                 if (isBody(this)) return this.getWindow().getScrollSize();
4192                 return {x: this.scrollWidth, y: this.scrollHeight};
4193         },
4194
4195         getScroll: function(){
4196                 if (isBody(this)) return this.getWindow().getScroll();
4197                 return {x: this.scrollLeft, y: this.scrollTop};
4198         },
4199
4200         getScrolls: function(){
4201                 var element = this.parentNode, position = {x: 0, y: 0};
4202                 while (element && !isBody(element)){
4203                         position.x += element.scrollLeft;
4204                         position.y += element.scrollTop;
4205                         element = element.parentNode;
4206                 }
4207                 return position;
4208         },
4209
4210         getOffsetParent: function(){
4211                 var element = this;
4212                 if (isBody(element)) return null;
4213                 if (!Browser.ie) return element.offsetParent;
4214                 while ((element = element.parentNode)){
4215                         if (styleString(element, 'position') != 'static' || isBody(element)) return element;
4216                 }
4217                 return null;
4218         },
4219
4220         getOffsets: function(){
4221                 if (this.getBoundingClientRect && !Browser.Platform.ios){
4222                         var bound = this.getBoundingClientRect(),
4223                                 html = document.id(this.getDocument().documentElement),
4224                                 htmlScroll = html.getScroll(),
4225                                 elemScrolls = this.getScrolls(),
4226                                 isFixed = (styleString(this, 'position') == 'fixed');
4227
4228                         return {
4229                                 x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
4230                                 y: bound.top.toInt()  + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
4231                         };
4232                 }
4233
4234                 var element = this, position = {x: 0, y: 0};
4235                 if (isBody(this)) return position;
4236
4237                 while (element && !isBody(element)){
4238                         position.x += element.offsetLeft;
4239                         position.y += element.offsetTop;
4240
4241                         if (Browser.firefox){
4242                                 if (!borderBox(element)){
4243                                         position.x += leftBorder(element);
4244                                         position.y += topBorder(element);
4245                                 }
4246                                 var parent = element.parentNode;
4247                                 if (parent && styleString(parent, 'overflow') != 'visible'){
4248                                         position.x += leftBorder(parent);
4249                                         position.y += topBorder(parent);
4250                                 }
4251                         } else if (element != this && Browser.safari){
4252                                 position.x += leftBorder(element);
4253                                 position.y += topBorder(element);
4254                         }
4255
4256                         element = element.offsetParent;
4257                 }
4258                 if (Browser.firefox && !borderBox(this)){
4259                         position.x -= leftBorder(this);
4260                         position.y -= topBorder(this);
4261                 }
4262                 return position;
4263         },
4264
4265         getPosition: function(relative){
4266                 if (isBody(this)) return {x: 0, y: 0};
4267                 var offset = this.getOffsets(),
4268                         scroll = this.getScrolls();
4269                 var position = {
4270                         x: offset.x - scroll.x,
4271                         y: offset.y - scroll.y
4272                 };
4273                 
4274                 if (relative && (relative = document.id(relative))){
4275                         var relativePosition = relative.getPosition();
4276                         return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
4277                 }
4278                 return position;
4279         },
4280
4281         getCoordinates: function(element){
4282                 if (isBody(this)) return this.getWindow().getCoordinates();
4283                 var position = this.getPosition(element),
4284                         size = this.getSize();
4285                 var obj = {
4286                         left: position.x,
4287                         top: position.y,
4288                         width: size.x,
4289                         height: size.y
4290                 };
4291                 obj.right = obj.left + obj.width;
4292                 obj.bottom = obj.top + obj.height;
4293                 return obj;
4294         },
4295
4296         computePosition: function(obj){
4297                 return {
4298                         left: obj.x - styleNumber(this, 'margin-left'),
4299                         top: obj.y - styleNumber(this, 'margin-top')
4300                 };
4301         },
4302
4303         setPosition: function(obj){
4304                 return this.setStyles(this.computePosition(obj));
4305         }
4306
4307 });
4308
4309
4310 [Document, Window].invoke('implement', {
4311
4312         getSize: function(){
4313                 var doc = getCompatElement(this);
4314                 return {x: doc.clientWidth, y: doc.clientHeight};
4315         },
4316
4317         getScroll: function(){
4318                 var win = this.getWindow(), doc = getCompatElement(this);
4319                 return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
4320         },
4321
4322         getScrollSize: function(){
4323                 var doc = getCompatElement(this),
4324                         min = this.getSize(),
4325                         body = this.getDocument().body;
4326
4327                 return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)};
4328         },
4329
4330         getPosition: function(){
4331                 return {x: 0, y: 0};
4332         },
4333
4334         getCoordinates: function(){
4335                 var size = this.getSize();
4336                 return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
4337         }
4338
4339 });
4340
4341 // private methods
4342
4343 var styleString = Element.getComputedStyle;
4344
4345 function styleNumber(element, style){
4346         return styleString(element, style).toInt() || 0;
4347 };
4348
4349 function borderBox(element){
4350         return styleString(element, '-moz-box-sizing') == 'border-box';
4351 };
4352
4353 function topBorder(element){
4354         return styleNumber(element, 'border-top-width');
4355 };
4356
4357 function leftBorder(element){
4358         return styleNumber(element, 'border-left-width');
4359 };
4360
4361 function isBody(element){
4362         return (/^(?:body|html)$/i).test(element.tagName);
4363 };
4364
4365 function getCompatElement(element){
4366         var doc = element.getDocument();
4367         return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
4368 };
4369
4370 })();
4371
4372 //aliases
4373 Element.alias({position: 'setPosition'}); //compatability
4374
4375 [Window, Document, Element].invoke('implement', {
4376
4377         getHeight: function(){
4378                 return this.getSize().y;
4379         },
4380
4381         getWidth: function(){
4382                 return this.getSize().x;
4383         },
4384
4385         getScrollTop: function(){
4386                 return this.getScroll().y;
4387         },
4388
4389         getScrollLeft: function(){
4390                 return this.getScroll().x;
4391         },
4392
4393         getScrollHeight: function(){
4394                 return this.getScrollSize().y;
4395         },
4396
4397         getScrollWidth: function(){
4398                 return this.getScrollSize().x;
4399         },
4400
4401         getTop: function(){
4402                 return this.getPosition().y;
4403         },
4404
4405         getLeft: function(){
4406                 return this.getPosition().x;
4407         }
4408
4409 });
4410
4411
4412 /*
4413 ---
4414
4415 name: Fx
4416
4417 description: Contains the basic animation logic to be extended by all other Fx Classes.
4418
4419 license: MIT-style license.
4420
4421 requires: [Chain, Events, Options]
4422
4423 provides: Fx
4424
4425 ...
4426 */
4427
4428 (function(){
4429
4430 var Fx = this.Fx = new Class({
4431
4432         Implements: [Chain, Events, Options],
4433
4434         options: {
4435                 /*
4436                 onStart: nil,
4437                 onCancel: nil,
4438                 onComplete: nil,
4439                 */
4440                 fps: 50,
4441                 unit: false,
4442                 duration: 500,
4443                 link: 'ignore'
4444         },
4445
4446         initialize: function(options){
4447                 this.subject = this.subject || this;
4448                 this.setOptions(options);
4449         },
4450
4451         getTransition: function(){
4452                 return function(p){
4453                         return -(Math.cos(Math.PI * p) - 1) / 2;
4454                 };
4455         },
4456
4457         step: function(){
4458                 var time = Date.now();
4459                 if (time < this.time + this.options.duration){
4460                         var delta = this.transition((time - this.time) / this.options.duration);
4461                         this.set(this.compute(this.from, this.to, delta));
4462                 } else {
4463                         this.set(this.compute(this.from, this.to, 1));
4464                         this.complete();
4465                 }
4466         },
4467
4468         set: function(now){
4469                 return now;
4470         },
4471
4472         compute: function(from, to, delta){
4473                 return Fx.compute(from, to, delta);
4474         },
4475
4476         check: function(){
4477                 if (!this.timer) return true;
4478                 switch (this.options.link){
4479                         case 'cancel': this.cancel(); return true;
4480                         case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
4481                 }
4482                 return false;
4483         },
4484
4485         start: function(from, to){
4486                 if (!this.check(from, to)) return this;
4487                 var duration = this.options.duration;
4488                 this.options.duration = Fx.Durations[duration] || duration.toInt();
4489                 this.from = from;
4490                 this.to = to;
4491                 this.time = 0;
4492                 this.transition = this.getTransition();
4493                 this.startTimer();
4494                 this.onStart();
4495                 return this;
4496         },
4497
4498         complete: function(){
4499                 if (this.stopTimer()) this.onComplete();
4500                 return this;
4501         },
4502
4503         cancel: function(){
4504                 if (this.stopTimer()) this.onCancel();
4505                 return this;
4506         },
4507
4508         onStart: function(){
4509                 this.fireEvent('start', this.subject);
4510         },
4511
4512         onComplete: function(){
4513                 this.fireEvent('complete', this.subject);
4514                 if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
4515         },
4516
4517         onCancel: function(){
4518                 this.fireEvent('cancel', this.subject).clearChain();
4519         },
4520
4521         pause: function(){
4522                 this.stopTimer();
4523                 return this;
4524         },
4525
4526         resume: function(){
4527                 this.startTimer();
4528                 return this;
4529         },
4530
4531         stopTimer: function(){
4532                 if (!this.timer) return false;
4533                 this.time = Date.now() - this.time;
4534                 this.timer = removeInstance(this);
4535                 return true;
4536         },
4537
4538         startTimer: function(){
4539                 if (this.timer) return false;
4540                 this.time = Date.now() - this.time;
4541                 this.timer = addInstance(this);
4542                 return true;
4543         }
4544
4545 });
4546
4547 Fx.compute = function(from, to, delta){
4548         return (to - from) * delta + from;
4549 };
4550
4551 Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
4552
4553 // global timers
4554
4555 var instances = {}, timers = {};
4556
4557 var loop = function(){
4558         for (var i = this.length; i--;){
4559                 if (this[i]) this[i].step();
4560         }
4561 };
4562
4563 var addInstance = function(instance){
4564         var fps = instance.options.fps,
4565                 list = instances[fps] || (instances[fps] = []);
4566         list.push(instance);
4567         if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list);
4568         return true;
4569 };
4570
4571 var removeInstance = function(instance){
4572         var fps = instance.options.fps,
4573                 list = instances[fps] || [];
4574         list.erase(instance);
4575         if (!list.length && timers[fps]) timers[fps] = clearInterval(timers[fps]);
4576         return false;
4577 };
4578
4579 })();
4580
4581
4582 /*
4583 ---
4584
4585 name: Fx.CSS
4586
4587 description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
4588
4589 license: MIT-style license.
4590
4591 requires: [Fx, Element.Style]
4592
4593 provides: Fx.CSS
4594
4595 ...
4596 */
4597
4598 Fx.CSS = new Class({
4599
4600         Extends: Fx,
4601
4602         //prepares the base from/to object
4603
4604         prepare: function(element, property, values){
4605                 values = Array.from(values);
4606                 if (values[1] == null){
4607                         values[1] = values[0];
4608                         values[0] = element.getStyle(property);
4609                 }
4610                 var parsed = values.map(this.parse);
4611                 return {from: parsed[0], to: parsed[1]};
4612         },
4613
4614         //parses a value into an array
4615
4616         parse: function(value){
4617                 value = Function.from(value)();
4618                 value = (typeof value == 'string') ? value.split(' ') : Array.from(value);
4619                 return value.map(function(val){
4620                         val = String(val);
4621                         var found = false;
4622                         Object.each(Fx.CSS.Parsers, function(parser, key){
4623                                 if (found) return;
4624                                 var parsed = parser.parse(val);
4625                                 if (parsed || parsed === 0) found = {value: parsed, parser: parser};
4626                         });
4627                         found = found || {value: val, parser: Fx.CSS.Parsers.String};
4628                         return found;
4629                 });
4630         },
4631
4632         //computes by a from and to prepared objects, using their parsers.
4633
4634         compute: function(from, to, delta){
4635                 var computed = [];
4636                 (Math.min(from.length, to.length)).times(function(i){
4637                         computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
4638                 });
4639                 computed.$family = Function.from('fx:css:value');
4640                 return computed;
4641         },
4642
4643         //serves the value as settable
4644
4645         serve: function(value, unit){
4646                 if (typeOf(value) != 'fx:css:value') value = this.parse(value);
4647                 var returned = [];
4648                 value.each(function(bit){
4649                         returned = returned.concat(bit.parser.serve(bit.value, unit));
4650                 });
4651                 return returned;
4652         },
4653
4654         //renders the change to an element
4655
4656         render: function(element, property, value, unit){
4657                 element.setStyle(property, this.serve(value, unit));
4658         },
4659
4660         //searches inside the page css to find the values for a selector
4661
4662         search: function(selector){
4663                 if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
4664                 var to = {};
4665                 Array.each(document.styleSheets, function(sheet, j){
4666                         var href = sheet.href;
4667                         if (href && href.contains('://') && !href.contains(document.domain)) return;
4668                         var rules = sheet.rules || sheet.cssRules;
4669                         Array.each(rules, function(rule, i){
4670                                 if (!rule.style) return;
4671                                 var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
4672                                         return m.toLowerCase();
4673                                 }) : null;
4674                                 if (!selectorText || !selectorText.test('^' + selector + '$')) return;
4675                                 Element.Styles.each(function(value, style){
4676                                         if (!rule.style[style] || Element.ShortStyles[style]) return;
4677                                         value = String(rule.style[style]);
4678                                         to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value;
4679                                 });
4680                         });
4681                 });
4682                 return Fx.CSS.Cache[selector] = to;
4683         }
4684
4685 });
4686
4687 Fx.CSS.Cache = {};
4688
4689 Fx.CSS.Parsers = {
4690
4691         Color: {
4692                 parse: function(value){
4693                         if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
4694                         return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
4695                 },
4696                 compute: function(from, to, delta){
4697                         return from.map(function(value, i){
4698                                 return Math.round(Fx.compute(from[i], to[i], delta));
4699                         });
4700                 },
4701                 serve: function(value){
4702                         return value.map(Number);
4703                 }
4704         },
4705
4706         Number: {
4707                 parse: parseFloat,
4708                 compute: Fx.compute,
4709                 serve: function(value, unit){
4710                         return (unit) ? value + unit : value;
4711                 }
4712         },
4713
4714         String: {
4715                 parse: Function.from(false),
4716                 compute: function(zero, one){
4717                         return one;
4718                 },
4719                 serve: function(zero){
4720                         return zero;
4721                 }
4722         }
4723
4724 };
4725
4726 //<1.2compat>
4727
4728 Fx.CSS.Parsers = new Hash(Fx.CSS.Parsers);
4729
4730 //</1.2compat>
4731
4732
4733 /*
4734 ---
4735
4736 name: Fx.Tween
4737
4738 description: Formerly Fx.Style, effect to transition any CSS property for an element.
4739
4740 license: MIT-style license.
4741
4742 requires: Fx.CSS
4743
4744 provides: [Fx.Tween, Element.fade, Element.highlight]
4745
4746 ...
4747 */
4748
4749 Fx.Tween = new Class({
4750
4751         Extends: Fx.CSS,
4752
4753         initialize: function(element, options){
4754                 this.element = this.subject = document.id(element);
4755                 this.parent(options);
4756         },
4757
4758         set: function(property, now){
4759                 if (arguments.length == 1){
4760                         now = property;
4761                         property = this.property || this.options.property;
4762                 }
4763                 this.render(this.element, property, now, this.options.unit);
4764                 return this;
4765         },
4766
4767         start: function(property, from, to){
4768                 if (!this.check(property, from, to)) return this;
4769                 var args = Array.flatten(arguments);
4770                 this.property = this.options.property || args.shift();
4771                 var parsed = this.prepare(this.element, this.property, args);
4772                 return this.parent(parsed.from, parsed.to);
4773         }
4774
4775 });
4776
4777 Element.Properties.tween = {
4778
4779         set: function(options){
4780                 this.get('tween').cancel().setOptions(options);
4781                 return this;
4782         },
4783
4784         get: function(){
4785                 var tween = this.retrieve('tween');
4786                 if (!tween){
4787                         tween = new Fx.Tween(this, {link: 'cancel'});
4788                         this.store('tween', tween);
4789                 }
4790                 return tween;
4791         }
4792
4793 };
4794
4795 Element.implement({
4796
4797         tween: function(property, from, to){
4798                 this.get('tween').start(arguments);
4799                 return this;
4800         },
4801
4802         fade: function(how){
4803                 var fade = this.get('tween'), o = 'opacity', toggle;
4804                 how = [how, 'toggle'].pick();
4805                 switch (how){
4806                         case 'in': fade.start(o, 1); break;
4807                         case 'out': fade.start(o, 0); break;
4808                         case 'show': fade.set(o, 1); break;
4809                         case 'hide': fade.set(o, 0); break;
4810                         case 'toggle':
4811                                 var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
4812                                 fade.start(o, (flag) ? 0 : 1);
4813                                 this.store('fade:flag', !flag);
4814                                 toggle = true;
4815                         break;
4816                         default: fade.start(o, arguments);
4817                 }
4818                 if (!toggle) this.eliminate('fade:flag');
4819                 return this;
4820         },
4821
4822         highlight: function(start, end){
4823                 if (!end){
4824                         end = this.retrieve('highlight:original', this.getStyle('background-color'));
4825                         end = (end == 'transparent') ? '#fff' : end;
4826                 }
4827                 var tween = this.get('tween');
4828                 tween.start('background-color', start || '#ffff88', end).chain(function(){
4829                         this.setStyle('background-color', this.retrieve('highlight:original'));
4830                         tween.callChain();
4831                 }.bind(this));
4832                 return this;
4833         }
4834
4835 });
4836
4837
4838 /*
4839 ---
4840
4841 name: Fx.Morph
4842
4843 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.
4844
4845 license: MIT-style license.
4846
4847 requires: Fx.CSS
4848
4849 provides: Fx.Morph
4850
4851 ...
4852 */
4853
4854 Fx.Morph = new Class({
4855
4856         Extends: Fx.CSS,
4857
4858         initialize: function(element, options){
4859                 this.element = this.subject = document.id(element);
4860                 this.parent(options);
4861         },
4862
4863         set: function(now){
4864                 if (typeof now == 'string') now = this.search(now);
4865                 for (var p in now) this.render(this.element, p, now[p], this.options.unit);
4866                 return this;
4867         },
4868
4869         compute: function(from, to, delta){
4870                 var now = {};
4871                 for (var p in from) now[p] = this.parent(from[p], to[p], delta);
4872                 return now;
4873         },
4874
4875         start: function(properties){
4876                 if (!this.check(properties)) return this;
4877                 if (typeof properties == 'string') properties = this.search(properties);
4878                 var from = {}, to = {};
4879                 for (var p in properties){
4880                         var parsed = this.prepare(this.element, p, properties[p]);
4881                         from[p] = parsed.from;
4882                         to[p] = parsed.to;
4883                 }
4884                 return this.parent(from, to);
4885         }
4886
4887 });
4888
4889 Element.Properties.morph = {
4890
4891         set: function(options){
4892                 this.get('morph').cancel().setOptions(options);
4893                 return this;
4894         },
4895
4896         get: function(){
4897                 var morph = this.retrieve('morph');
4898                 if (!morph){
4899                         morph = new Fx.Morph(this, {link: 'cancel'});
4900                         this.store('morph', morph);
4901                 }
4902                 return morph;
4903         }
4904
4905 };
4906
4907 Element.implement({
4908
4909         morph: function(props){
4910                 this.get('morph').start(props);
4911                 return this;
4912         }
4913
4914 });
4915
4916
4917 /*
4918 ---
4919
4920 name: Fx.Transitions
4921
4922 description: Contains a set of advanced transitions to be used with any of the Fx Classes.
4923
4924 license: MIT-style license.
4925
4926 credits:
4927   - Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
4928
4929 requires: Fx
4930
4931 provides: Fx.Transitions
4932
4933 ...
4934 */
4935
4936 Fx.implement({
4937
4938         getTransition: function(){
4939                 var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
4940                 if (typeof trans == 'string'){
4941                         var data = trans.split(':');
4942                         trans = Fx.Transitions;
4943                         trans = trans[data[0]] || trans[data[0].capitalize()];
4944                         if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
4945                 }
4946                 return trans;
4947         }
4948
4949 });
4950
4951 Fx.Transition = function(transition, params){
4952         params = Array.from(params);
4953         return Object.append(transition, {
4954                 easeIn: function(pos){
4955                         return transition(pos, params);
4956                 },
4957                 easeOut: function(pos){
4958                         return 1 - transition(1 - pos, params);
4959                 },
4960                 easeInOut: function(pos){
4961                         return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
4962                 }
4963         });
4964 };
4965
4966 Fx.Transitions = {
4967
4968         linear: function(zero){
4969                 return zero;
4970         }
4971
4972 };
4973
4974 //<1.2compat>
4975
4976 Fx.Transitions = new Hash(Fx.Transitions);
4977
4978 //</1.2compat>
4979
4980 Fx.Transitions.extend = function(transitions){
4981         for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
4982 };
4983
4984 Fx.Transitions.extend({
4985
4986         Pow: function(p, x){
4987                 return Math.pow(p, x && x[0] || 6);
4988         },
4989
4990         Expo: function(p){
4991                 return Math.pow(2, 8 * (p - 1));
4992         },
4993
4994         Circ: function(p){
4995                 return 1 - Math.sin(Math.acos(p));
4996         },
4997
4998         Sine: function(p){
4999                 return 1 - Math.sin((1 - p) * Math.PI / 2);
5000         },
5001
5002         Back: function(p, x){
5003                 x = x && x[0] || 1.618;
5004                 return Math.pow(p, 2) * ((x + 1) * p - x);
5005         },
5006
5007         Bounce: function(p){
5008                 var value;
5009                 for (var a = 0, b = 1; 1; a += b, b /= 2){
5010                         if (p >= (7 - 4 * a) / 11){
5011                                 value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
5012                                 break;
5013                         }
5014                 }
5015                 return value;
5016         },
5017
5018         Elastic: function(p, x){
5019                 return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && x[0] || 1) / 3);
5020         }
5021
5022 });
5023
5024 ['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
5025         Fx.Transitions[transition] = new Fx.Transition(function(p){
5026                 return Math.pow(p, [i + 2]);
5027         });
5028 });
5029
5030
5031 /*
5032 ---
5033
5034 name: DOMReady
5035
5036 description: Contains the custom event domready.
5037
5038 license: MIT-style license.
5039
5040 requires: [Browser, Element, Element.Event]
5041
5042 provides: [DOMReady, DomReady]
5043
5044 ...
5045 */
5046
5047 (function(window, document){
5048
5049 var ready,
5050         loaded,
5051         checks = [],
5052         shouldPoll,
5053         timer,
5054         isFramed = true;
5055
5056 // Thanks to Rich Dougherty <http://www.richdougherty.com/>
5057 try {
5058         isFramed = window.frameElement != null;
5059 } catch(e){}
5060
5061 var domready = function(){
5062         clearTimeout(timer);
5063         if (ready) return;
5064         Browser.loaded = ready = true;
5065         document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check);
5066         
5067         document.fireEvent('domready');
5068         window.fireEvent('domready');
5069 };
5070
5071 var check = function(){
5072         for (var i = checks.length; i--;) if (checks[i]()){
5073                 domready();
5074                 return true;
5075         }
5076
5077         return false;
5078 };
5079
5080 var poll = function(){
5081         clearTimeout(timer);
5082         if (!check()) timer = setTimeout(poll, 10);
5083 };
5084
5085 document.addListener('DOMContentLoaded', domready);
5086
5087 // doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
5088 var testElement = document.createElement('div');
5089 if (testElement.doScroll && !isFramed){
5090         checks.push(function(){
5091                 try {
5092                         testElement.doScroll();
5093                         return true;
5094                 } catch (e){}
5095
5096                 return false;
5097         });
5098         shouldPoll = true;
5099 }
5100
5101 if (document.readyState) checks.push(function(){
5102         var state = document.readyState;
5103         return (state == 'loaded' || state == 'complete');
5104 });
5105
5106 if ('onreadystatechange' in document) document.addListener('readystatechange', check);
5107 else shouldPoll = true;
5108
5109 if (shouldPoll) poll();
5110
5111 Element.Events.domready = {
5112         onAdd: function(fn){
5113                 if (ready) fn.call(this);
5114         }
5115 };
5116
5117 // Make sure that domready fires before load
5118 Element.Events.load = {
5119         base: 'load',
5120         onAdd: function(fn){
5121                 if (loaded && this == window) fn.call(this);
5122         },
5123         condition: function(){
5124                 if (this == window){
5125                         domready();
5126                         delete Element.Events.load;
5127                 }
5128                 
5129                 return true;
5130         }
5131 };
5132
5133 // This is based on the custom load event
5134 window.addEvent('load', function(){
5135         loaded = true;
5136 });
5137
5138 })(window, document);
5139
5140 // MooTools: the javascript framework.
5141 // Load this file's selection again by visiting: http://mootools.net/more/4e3c76ec202edfd6dce2f7a94df9b03d 
5142 // Or build this file again with packager using: packager build More/More More/Fx.Elements More/Fx.Slide More/Assets
5143 /*
5144 ---
5145
5146 script: More.js
5147
5148 name: More
5149
5150 description: MooTools More
5151
5152 license: MIT-style license
5153
5154 authors:
5155   - Guillermo Rauch
5156   - Thomas Aylott
5157   - Scott Kyle
5158   - Arian Stolwijk
5159   - Tim Wienk
5160   - Christoph Pojer
5161   - Aaron Newton
5162
5163 requires:
5164   - Core/MooTools
5165
5166 provides: [MooTools.More]
5167
5168 ...
5169 */
5170
5171 MooTools.More = {
5172         'version': '1.3.0.1',
5173         'build': '6dce99bed2792dffcbbbb4ddc15a1fb9a41994b5'
5174 };
5175
5176
5177 /*
5178 ---
5179
5180 script: Fx.Elements.js
5181
5182 name: Fx.Elements
5183
5184 description: Effect to change any number of CSS properties of any number of Elements.
5185
5186 license: MIT-style license
5187
5188 authors:
5189   - Valerio Proietti
5190
5191 requires:
5192   - Core/Fx.CSS
5193   - /MooTools.More
5194
5195 provides: [Fx.Elements]
5196
5197 ...
5198 */
5199
5200 Fx.Elements = new Class({
5201
5202         Extends: Fx.CSS,
5203
5204         initialize: function(elements, options){
5205                 this.elements = this.subject = $$(elements);
5206                 this.parent(options);
5207         },
5208
5209         compute: function(from, to, delta){
5210                 var now = {};
5211
5212                 for (var i in from){
5213                         var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
5214                         for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
5215                 }
5216
5217                 return now;
5218         },
5219
5220         set: function(now){
5221                 for (var i in now){
5222                         if (!this.elements[i]) continue;
5223
5224                         var iNow = now[i];
5225                         for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
5226                 }
5227
5228                 return this;
5229         },
5230
5231         start: function(obj){
5232                 if (!this.check(obj)) return this;
5233                 var from = {}, to = {};
5234
5235                 for (var i in obj){
5236                         if (!this.elements[i]) continue;
5237
5238                         var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
5239
5240                         for (var p in iProps){
5241                                 var parsed = this.prepare(this.elements[i], p, iProps[p]);
5242                                 iFrom[p] = parsed.from;
5243                                 iTo[p] = parsed.to;
5244                         }
5245                 }
5246
5247                 return this.parent(from, to);
5248         }
5249
5250 });
5251
5252
5253 /*
5254 ---
5255
5256 script: Fx.Slide.js
5257
5258 name: Fx.Slide
5259
5260 description: Effect to slide an element in and out of view.
5261
5262 license: MIT-style license
5263
5264 authors:
5265   - Valerio Proietti
5266
5267 requires:
5268   - Core/Fx
5269   - Core/Element.Style
5270   - /MooTools.More
5271
5272 provides: [Fx.Slide]
5273
5274 ...
5275 */
5276
5277 Fx.Slide = new Class({
5278
5279         Extends: Fx,
5280
5281         options: {
5282                 mode: 'vertical',
5283                 wrapper: false,
5284                 hideOverflow: true,
5285                 resetHeight: false
5286         },
5287
5288         initialize: function(element, options){
5289                 this.addEvent('complete', function(){
5290                         this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);
5291                         if (this.open && this.options.resetHeight) this.wrapper.setStyle('height', '');
5292                 }, true);
5293
5294                 this.element = this.subject = document.id(element);
5295                 this.parent(options);
5296                 var wrapper = this.element.retrieve('wrapper');
5297                 var styles = this.element.getStyles('margin', 'position', 'overflow');
5298
5299                 if (this.options.hideOverflow) styles = Object.append(styles, {overflow: 'hidden'});
5300                 if (this.options.wrapper) wrapper = document.id(this.options.wrapper).setStyles(styles);
5301
5302                 this.wrapper = wrapper || new Element('div', {
5303                         styles: styles
5304                 }).wraps(this.element);
5305
5306                 this.element.store('wrapper', this.wrapper).setStyle('margin', 0);
5307                 this.now = [];
5308                 this.open = true;
5309         },
5310
5311         vertical: function(){
5312                 this.margin = 'margin-top';
5313                 this.layout = 'height';
5314                 this.offset = this.element.offsetHeight;
5315         },
5316
5317         horizontal: function(){
5318                 this.margin = 'margin-left';
5319                 this.layout = 'width';
5320                 this.offset = this.element.offsetWidth;
5321         },
5322
5323         set: function(now){
5324                 this.element.setStyle(this.margin, now[0]);
5325                 this.wrapper.setStyle(this.layout, now[1]);
5326                 return this;
5327         },
5328
5329         compute: function(from, to, delta){
5330                 return [0, 1].map(function(i){
5331                         return Fx.compute(from[i], to[i], delta);
5332                 });
5333         },
5334
5335         start: function(how, mode){
5336                 if (!this.check(how, mode)) return this;
5337                 this[mode || this.options.mode]();
5338                 var margin = this.element.getStyle(this.margin).toInt();
5339                 var layout = this.wrapper.getStyle(this.layout).toInt();
5340                 var caseIn = [[margin, layout], [0, this.offset]];
5341                 var caseOut = [[margin, layout], [-this.offset, 0]];
5342                 var start;
5343                 switch (how){
5344                         case 'in': start = caseIn; break;
5345                         case 'out': start = caseOut; break;
5346                         case 'toggle': start = (layout == 0) ? caseIn : caseOut;
5347                 }
5348                 return this.parent(start[0], start[1]);
5349         },
5350
5351         slideIn: function(mode){
5352                 return this.start('in', mode);
5353         },
5354
5355         slideOut: function(mode){
5356                 return this.start('out', mode);
5357         },
5358
5359         hide: function(mode){
5360                 this[mode || this.options.mode]();
5361                 this.open = false;
5362                 return this.set([-this.offset, 0]);
5363         },
5364
5365         show: function(mode){
5366                 this[mode || this.options.mode]();
5367                 this.open = true;
5368                 return this.set([0, this.offset]);
5369         },
5370
5371         toggle: function(mode){
5372                 return this.start('toggle', mode);
5373         }
5374
5375 });
5376
5377 Element.Properties.slide = {
5378
5379         set: function(options){
5380                 this.get('slide').cancel().setOptions(options);
5381                 return this;
5382         },
5383
5384         get: function(){
5385                 var slide = this.retrieve('slide');
5386                 if (!slide){
5387                         slide = new Fx.Slide(this, {link: 'cancel'});
5388                         this.store('slide', slide);
5389                 }
5390                 return slide;
5391         }
5392
5393 };
5394
5395 Element.implement({
5396
5397         slide: function(how, mode){
5398                 how = how || 'toggle';
5399                 var slide = this.get('slide'), toggle;
5400                 switch (how){
5401                         case 'hide': slide.hide(mode); break;
5402                         case 'show': slide.show(mode); break;
5403                         case 'toggle':
5404                                 var flag = this.retrieve('slide:flag', slide.open);
5405                                 slide[flag ? 'slideOut' : 'slideIn'](mode);
5406                                 this.store('slide:flag', !flag);
5407                                 toggle = true;
5408                         break;
5409                         default: slide.start(how, mode);
5410                 }
5411                 if (!toggle) this.eliminate('slide:flag');
5412                 return this;
5413         }
5414
5415 });
5416
5417
5418 /*
5419 ---
5420
5421 script: Assets.js
5422
5423 name: Assets
5424
5425 description: Provides methods to dynamically load JavaScript, CSS, and Image files into the document.
5426
5427 license: MIT-style license
5428
5429 authors:
5430   - Valerio Proietti
5431
5432 requires:
5433   - Core/Element.Event
5434   - /MooTools.More
5435
5436 provides: [Assets]
5437
5438 ...
5439 */
5440
5441 var Asset = {
5442
5443         javascript: function(source, properties){
5444                 properties = Object.append({
5445                         document: document
5446                 }, properties);
5447
5448                 if (properties.onLoad){
5449                         properties.onload = properties.onLoad;
5450                         delete properties.onLoad;
5451                 }
5452
5453                 var script = new Element('script', {src: source, type: 'text/javascript'});
5454                 var load = properties.onload || function(){},
5455                         doc = properties.document;
5456                 delete properties.onload;
5457                 delete properties.document;
5458
5459                 return script.addEvents({
5460                         load: load,
5461                         readystatechange: function(){
5462                                 if (['loaded', 'complete'].contains(this.readyState)) load.call(this);
5463                         }
5464                 }).set(properties).inject(doc.head);
5465         },
5466
5467         css: function(source, properties){
5468                 properties = properties || {};
5469                 var onload = properties.onload || properties.onLoad;
5470                 if (onload){
5471                         properties.events = properties.events || {};
5472                         properties.events.load = onload;
5473                         delete properties.onload;
5474                         delete properties.onLoad;
5475                 }
5476                 return new Element('link', Object.merge({
5477                         rel: 'stylesheet',
5478                         media: 'screen',
5479                         type: 'text/css',
5480                         href: source
5481                 }, properties)).inject(document.head);
5482         },
5483
5484         image: function(source, properties){
5485                 properties = Object.merge({
5486                         onload: function(){},
5487                         onabort: function(){},
5488                         onerror: function(){}
5489                 }, properties);
5490                 var image = new Image();
5491                 var element = document.id(image) || new Element('img');
5492                 ['load', 'abort', 'error'].each(function(name){
5493                         var type = 'on' + name;
5494                         var cap = name.capitalize();
5495                         if (properties['on' + cap]){
5496                                 properties[type] = properties['on' + cap];
5497                                 delete properties['on' + cap];
5498                         }
5499                         var event = properties[type];
5500                         delete properties[type];
5501                         image[type] = function(){
5502                                 if (!image) return;
5503                                 if (!element.parentNode){
5504                                         element.width = image.width;
5505                                         element.height = image.height;
5506                                 }
5507                                 image = image.onload = image.onabort = image.onerror = null;
5508                                 event.delay(1, element, element);
5509                                 element.fireEvent(name, element, 1);
5510                         };
5511                 });
5512                 image.src = element.src = source;
5513                 if (image && image.complete) image.onload.delay(1);
5514                 return element.set(properties);
5515         },
5516
5517         images: function(sources, options){
5518                 options = Object.merge({
5519                         onComplete: function(){},
5520                         onProgress: function(){},
5521                         onError: function(){},
5522                         properties: {}
5523                 }, options);
5524                 sources = Array.from(sources);
5525                 var counter = 0;
5526                 return new Elements(sources.map(function(source, index){
5527                         return Asset.image(source, Object.append(options.properties, {
5528                                 onload: function(){
5529                                         counter++;
5530                                         options.onProgress.call(this, counter, index, source);
5531                                         if (counter == sources.length) options.onComplete();
5532                                 },
5533                                 onerror: function(){
5534                                         counter++;
5535                                         options.onError.call(this, counter, index, source);
5536                                         if (counter == sources.length) options.onComplete();
5537                                 }
5538                         }));
5539                 }));
5540         }
5541
5542 };
5543