]> www.average.org Git - mkgallery.git/blob - include/mootools.js
give path to header.pl
[mkgallery.git] / include / mootools.js
1 /*
2 ---
3
4 script: Core.js
5
6 description: The core of MooTools, contains all the base functions and the Native and Hash implementations. Required by all the other scripts.
7
8 license: MIT-style license.
9
10 copyright: Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/).
11
12 authors: The MooTools production team (http://mootools.net/developers/)
13
14 inspiration:
15 - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
16 - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
17
18 provides: [Mootools, Native, Hash.base, Array.each, $util]
19
20 ...
21 */
22
23 var MooTools = {
24         'version': '1.2.4',
25         'build': '0d9113241a90b9cd5643b926795852a2026710d4'
26 };
27
28 var Native = function(options){
29         options = options || {};
30         var name = options.name;
31         var legacy = options.legacy;
32         var protect = options.protect;
33         var methods = options.implement;
34         var generics = options.generics;
35         var initialize = options.initialize;
36         var afterImplement = options.afterImplement || function(){};
37         var object = initialize || legacy;
38         generics = generics !== false;
39
40         object.constructor = Native;
41         object.$family = {name: 'native'};
42         if (legacy && initialize) object.prototype = legacy.prototype;
43         object.prototype.constructor = object;
44
45         if (name){
46                 var family = name.toLowerCase();
47                 object.prototype.$family = {name: family};
48                 Native.typize(object, family);
49         }
50
51         var add = function(obj, name, method, force){
52                 if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
53                 if (generics) Native.genericize(obj, name, protect);
54                 afterImplement.call(obj, name, method);
55                 return obj;
56         };
57
58         object.alias = function(a1, a2, a3){
59                 if (typeof a1 == 'string'){
60                         var pa1 = this.prototype[a1];
61                         if ((a1 = pa1)) return add(this, a2, a1, a3);
62                 }
63                 for (var a in a1) this.alias(a, a1[a], a2);
64                 return this;
65         };
66
67         object.implement = function(a1, a2, a3){
68                 if (typeof a1 == 'string') return add(this, a1, a2, a3);
69                 for (var p in a1) add(this, p, a1[p], a2);
70                 return this;
71         };
72
73         if (methods) object.implement(methods);
74
75         return object;
76 };
77
78 Native.genericize = function(object, property, check){
79         if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){
80                 var args = Array.prototype.slice.call(arguments);
81                 return object.prototype[property].apply(args.shift(), args);
82         };
83 };
84
85 Native.implement = function(objects, properties){
86         for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
87 };
88
89 Native.typize = function(object, family){
90         if (!object.type) object.type = function(item){
91                 return ($type(item) === family);
92         };
93 };
94
95 (function(){
96         var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String};
97         for (var n in natives) new Native({name: n, initialize: natives[n], protect: true});
98
99         var types = {'boolean': Boolean, 'native': Native, 'object': Object};
100         for (var t in types) Native.typize(types[t], t);
101
102         var generics = {
103                 'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"],
104                 'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
105         };
106         for (var g in generics){
107                 for (var i = generics[g].length; i--;) Native.genericize(natives[g], generics[g][i], true);
108         }
109 })();
110
111 var Hash = new Native({
112
113         name: 'Hash',
114
115         initialize: function(object){
116                 if ($type(object) == 'hash') object = $unlink(object.getClean());
117                 for (var key in object) this[key] = object[key];
118                 return this;
119         }
120
121 });
122
123 Hash.implement({
124
125         forEach: function(fn, bind){
126                 for (var key in this){
127                         if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
128                 }
129         },
130
131         getClean: function(){
132                 var clean = {};
133                 for (var key in this){
134                         if (this.hasOwnProperty(key)) clean[key] = this[key];
135                 }
136                 return clean;
137         },
138
139         getLength: function(){
140                 var length = 0;
141                 for (var key in this){
142                         if (this.hasOwnProperty(key)) length++;
143                 }
144                 return length;
145         }
146
147 });
148
149 Hash.alias('forEach', 'each');
150
151 Array.implement({
152
153         forEach: function(fn, bind){
154                 for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
155         }
156
157 });
158
159 Array.alias('forEach', 'each');
160
161 function $A(iterable){
162         if (iterable.item){
163                 var l = iterable.length, array = new Array(l);
164                 while (l--) array[l] = iterable[l];
165                 return array;
166         }
167         return Array.prototype.slice.call(iterable);
168 };
169
170 function $arguments(i){
171         return function(){
172                 return arguments[i];
173         };
174 };
175
176 function $chk(obj){
177         return !!(obj || obj === 0);
178 };
179
180 function $clear(timer){
181         clearTimeout(timer);
182         clearInterval(timer);
183         return null;
184 };
185
186 function $defined(obj){
187         return (obj != undefined);
188 };
189
190 function $each(iterable, fn, bind){
191         var type = $type(iterable);
192         ((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
193 };
194
195 function $empty(){};
196
197 function $extend(original, extended){
198         for (var key in (extended || {})) original[key] = extended[key];
199         return original;
200 };
201
202 function $H(object){
203         return new Hash(object);
204 };
205
206 function $lambda(value){
207         return ($type(value) == 'function') ? value : function(){
208                 return value;
209         };
210 };
211
212 function $merge(){
213         var args = Array.slice(arguments);
214         args.unshift({});
215         return $mixin.apply(null, args);
216 };
217
218 function $mixin(mix){
219         for (var i = 1, l = arguments.length; i < l; i++){
220                 var object = arguments[i];
221                 if ($type(object) != 'object') continue;
222                 for (var key in object){
223                         var op = object[key], mp = mix[key];
224                         mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $mixin(mp, op) : $unlink(op);
225                 }
226         }
227         return mix;
228 };
229
230 function $pick(){
231         for (var i = 0, l = arguments.length; i < l; i++){
232                 if (arguments[i] != undefined) return arguments[i];
233         }
234         return null;
235 };
236
237 function $random(min, max){
238         return Math.floor(Math.random() * (max - min + 1) + min);
239 };
240
241 function $splat(obj){
242         var type = $type(obj);
243         return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
244 };
245
246 var $time = Date.now || function(){
247         return +new Date;
248 };
249
250 function $try(){
251         for (var i = 0, l = arguments.length; i < l; i++){
252                 try {
253                         return arguments[i]();
254                 } catch(e){}
255         }
256         return null;
257 };
258
259 function $type(obj){
260         if (obj == undefined) return false;
261         if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
262         if (obj.nodeName){
263                 switch (obj.nodeType){
264                         case 1: return 'element';
265                         case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
266                 }
267         } else if (typeof obj.length == 'number'){
268                 if (obj.callee) return 'arguments';
269                 else if (obj.item) return 'collection';
270         }
271         return typeof obj;
272 };
273
274 function $unlink(object){
275         var unlinked;
276         switch ($type(object)){
277                 case 'object':
278                         unlinked = {};
279                         for (var p in object) unlinked[p] = $unlink(object[p]);
280                 break;
281                 case 'hash':
282                         unlinked = new Hash(object);
283                 break;
284                 case 'array':
285                         unlinked = [];
286                         for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
287                 break;
288                 default: return object;
289         }
290         return unlinked;
291 };
292
293
294 /*
295 ---
296
297 script: Browser.js
298
299 description: The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.
300
301 license: MIT-style license.
302
303 requires: 
304 - /Native
305 - /$util
306
307 provides: [Browser, Window, Document, $exec]
308
309 ...
310 */
311
312 var Browser = $merge({
313
314         Engine: {name: 'unknown', version: 0},
315
316         Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},
317
318         Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},
319
320         Plugins: {},
321
322         Engines: {
323
324                 presto: function(){
325                         return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
326                 },
327
328                 trident: function(){
329                         return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
330                 },
331
332                 webkit: function(){
333                         return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
334                 },
335
336                 gecko: function(){
337                         return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18);
338                 }
339
340         }
341
342 }, Browser || {});
343
344 Browser.Platform[Browser.Platform.name] = true;
345
346 Browser.detect = function(){
347
348         for (var engine in this.Engines){
349                 var version = this.Engines[engine]();
350                 if (version){
351                         this.Engine = {name: engine, version: version};
352                         this.Engine[engine] = this.Engine[engine + version] = true;
353                         break;
354                 }
355         }
356
357         return {name: engine, version: version};
358
359 };
360
361 Browser.detect();
362
363 Browser.Request = function(){
364         return $try(function(){
365                 return new XMLHttpRequest();
366         }, function(){
367                 return new ActiveXObject('MSXML2.XMLHTTP');
368         }, function(){
369                 return new ActiveXObject('Microsoft.XMLHTTP');
370         });
371 };
372
373 Browser.Features.xhr = !!(Browser.Request());
374
375 Browser.Plugins.Flash = (function(){
376         var version = ($try(function(){
377                 return navigator.plugins['Shockwave Flash'].description;
378         }, function(){
379                 return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
380         }) || '0 r0').match(/\d+/g);
381         return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
382 })();
383
384 function $exec(text){
385         if (!text) return text;
386         if (window.execScript){
387                 window.execScript(text);
388         } else {
389                 var script = document.createElement('script');
390                 script.setAttribute('type', 'text/javascript');
391                 script[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : 'text'] = text;
392                 document.head.appendChild(script);
393                 document.head.removeChild(script);
394         }
395         return text;
396 };
397
398 Native.UID = 1;
399
400 var $uid = (Browser.Engine.trident) ? function(item){
401         return (item.uid || (item.uid = [Native.UID++]))[0];
402 } : function(item){
403         return item.uid || (item.uid = Native.UID++);
404 };
405
406 var Window = new Native({
407
408         name: 'Window',
409
410         legacy: (Browser.Engine.trident) ? null: window.Window,
411
412         initialize: function(win){
413                 $uid(win);
414                 if (!win.Element){
415                         win.Element = $empty;
416                         if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2
417                         win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {};
418                 }
419                 win.document.window = win;
420                 return $extend(win, Window.Prototype);
421         },
422
423         afterImplement: function(property, value){
424                 window[property] = Window.Prototype[property] = value;
425         }
426
427 });
428
429 Window.Prototype = {$family: {name: 'window'}};
430
431 new Window(window);
432
433 var Document = new Native({
434
435         name: 'Document',
436
437         legacy: (Browser.Engine.trident) ? null: window.Document,
438
439         initialize: function(doc){
440                 $uid(doc);
441                 doc.head = doc.getElementsByTagName('head')[0];
442                 doc.html = doc.getElementsByTagName('html')[0];
443                 if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function(){
444                         doc.execCommand("BackgroundImageCache", false, true);
445                 });
446                 if (Browser.Engine.trident) doc.window.attachEvent('onunload', function(){
447                         doc.window.detachEvent('onunload', arguments.callee);
448                         doc.head = doc.html = doc.window = null;
449                 });
450                 return $extend(doc, Document.Prototype);
451         },
452
453         afterImplement: function(property, value){
454                 document[property] = Document.Prototype[property] = value;
455         }
456
457 });
458
459 Document.Prototype = {$family: {name: 'document'}};
460
461 new Document(document);
462
463
464 /*
465 ---
466
467 script: Array.js
468
469 description: Contains Array Prototypes like each, contains, and erase.
470
471 license: MIT-style license.
472
473 requires:
474 - /$util
475 - /Array.each
476
477 provides: [Array]
478
479 ...
480 */
481
482 Array.implement({
483
484         every: function(fn, bind){
485                 for (var i = 0, l = this.length; i < l; i++){
486                         if (!fn.call(bind, this[i], i, this)) return false;
487                 }
488                 return true;
489         },
490
491         filter: function(fn, bind){
492                 var results = [];
493                 for (var i = 0, l = this.length; i < l; i++){
494                         if (fn.call(bind, this[i], i, this)) results.push(this[i]);
495                 }
496                 return results;
497         },
498
499         clean: function(){
500                 return this.filter($defined);
501         },
502
503         indexOf: function(item, from){
504                 var len = this.length;
505                 for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
506                         if (this[i] === item) return i;
507                 }
508                 return -1;
509         },
510
511         map: function(fn, bind){
512                 var results = [];
513                 for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this);
514                 return results;
515         },
516
517         some: function(fn, bind){
518                 for (var i = 0, l = this.length; i < l; i++){
519                         if (fn.call(bind, this[i], i, this)) return true;
520                 }
521                 return false;
522         },
523
524         associate: function(keys){
525                 var obj = {}, length = Math.min(this.length, keys.length);
526                 for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
527                 return obj;
528         },
529
530         link: function(object){
531                 var result = {};
532                 for (var i = 0, l = this.length; i < l; i++){
533                         for (var key in object){
534                                 if (object[key](this[i])){
535                                         result[key] = this[i];
536                                         delete object[key];
537                                         break;
538                                 }
539                         }
540                 }
541                 return result;
542         },
543
544         contains: function(item, from){
545                 return this.indexOf(item, from) != -1;
546         },
547
548         extend: function(array){
549                 for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
550                 return this;
551         },
552         
553         getLast: function(){
554                 return (this.length) ? this[this.length - 1] : null;
555         },
556
557         getRandom: function(){
558                 return (this.length) ? this[$random(0, this.length - 1)] : null;
559         },
560
561         include: function(item){
562                 if (!this.contains(item)) this.push(item);
563                 return this;
564         },
565
566         combine: function(array){
567                 for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
568                 return this;
569         },
570
571         erase: function(item){
572                 for (var i = this.length; i--; i){
573                         if (this[i] === item) this.splice(i, 1);
574                 }
575                 return this;
576         },
577
578         empty: function(){
579                 this.length = 0;
580                 return this;
581         },
582
583         flatten: function(){
584                 var array = [];
585                 for (var i = 0, l = this.length; i < l; i++){
586                         var type = $type(this[i]);
587                         if (!type) continue;
588                         array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]);
589                 }
590                 return array;
591         },
592
593         hexToRgb: function(array){
594                 if (this.length != 3) return null;
595                 var rgb = this.map(function(value){
596                         if (value.length == 1) value += value;
597                         return value.toInt(16);
598                 });
599                 return (array) ? rgb : 'rgb(' + rgb + ')';
600         },
601
602         rgbToHex: function(array){
603                 if (this.length < 3) return null;
604                 if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
605                 var hex = [];
606                 for (var i = 0; i < 3; i++){
607                         var bit = (this[i] - 0).toString(16);
608                         hex.push((bit.length == 1) ? '0' + bit : bit);
609                 }
610                 return (array) ? hex : '#' + hex.join('');
611         }
612
613 });
614
615
616 /*
617 ---
618
619 script: Function.js
620
621 description: Contains Function Prototypes like create, bind, pass, and delay.
622
623 license: MIT-style license.
624
625 requires:
626 - /Native
627 - /$util
628
629 provides: [Function]
630
631 ...
632 */
633
634 Function.implement({
635
636         extend: function(properties){
637                 for (var property in properties) this[property] = properties[property];
638                 return this;
639         },
640
641         create: function(options){
642                 var self = this;
643                 options = options || {};
644                 return function(event){
645                         var args = options.arguments;
646                         args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
647                         if (options.event) args = [event || window.event].extend(args);
648                         var returns = function(){
649                                 return self.apply(options.bind || null, args);
650                         };
651                         if (options.delay) return setTimeout(returns, options.delay);
652                         if (options.periodical) return setInterval(returns, options.periodical);
653                         if (options.attempt) return $try(returns);
654                         return returns();
655                 };
656         },
657
658         run: function(args, bind){
659                 return this.apply(bind, $splat(args));
660         },
661
662         pass: function(args, bind){
663                 return this.create({bind: bind, arguments: args});
664         },
665
666         bind: function(bind, args){
667                 return this.create({bind: bind, arguments: args});
668         },
669
670         bindWithEvent: function(bind, args){
671                 return this.create({bind: bind, arguments: args, event: true});
672         },
673
674         attempt: function(args, bind){
675                 return this.create({bind: bind, arguments: args, attempt: true})();
676         },
677
678         delay: function(delay, bind, args){
679                 return this.create({bind: bind, arguments: args, delay: delay})();
680         },
681
682         periodical: function(periodical, bind, args){
683                 return this.create({bind: bind, arguments: args, periodical: periodical})();
684         }
685
686 });
687
688
689 /*
690 ---
691
692 script: Number.js
693
694 description: Contains Number Prototypes like limit, round, times, and ceil.
695
696 license: MIT-style license.
697
698 requires:
699 - /Native
700 - /$util
701
702 provides: [Number]
703
704 ...
705 */
706
707 Number.implement({
708
709         limit: function(min, max){
710                 return Math.min(max, Math.max(min, this));
711         },
712
713         round: function(precision){
714                 precision = Math.pow(10, precision || 0);
715                 return Math.round(this * precision) / precision;
716         },
717
718         times: function(fn, bind){
719                 for (var i = 0; i < this; i++) fn.call(bind, i, this);
720         },
721
722         toFloat: function(){
723                 return parseFloat(this);
724         },
725
726         toInt: function(base){
727                 return parseInt(this, base || 10);
728         }
729
730 });
731
732 Number.alias('times', 'each');
733
734 (function(math){
735         var methods = {};
736         math.each(function(name){
737                 if (!Number[name]) methods[name] = function(){
738                         return Math[name].apply(null, [this].concat($A(arguments)));
739                 };
740         });
741         Number.implement(methods);
742 })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
743
744
745 /*
746 ---
747
748 script: String.js
749
750 description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
751
752 license: MIT-style license.
753
754 requires:
755 - /Native
756
757 provides: [String]
758
759 ...
760 */
761
762 String.implement({
763
764         test: function(regex, params){
765                 return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
766         },
767
768         contains: function(string, separator){
769                 return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
770         },
771
772         trim: function(){
773                 return this.replace(/^\s+|\s+$/g, '');
774         },
775
776         clean: function(){
777                 return this.replace(/\s+/g, ' ').trim();
778         },
779
780         camelCase: function(){
781                 return this.replace(/-\D/g, function(match){
782                         return match.charAt(1).toUpperCase();
783                 });
784         },
785
786         hyphenate: function(){
787                 return this.replace(/[A-Z]/g, function(match){
788                         return ('-' + match.charAt(0).toLowerCase());
789                 });
790         },
791
792         capitalize: function(){
793                 return this.replace(/\b[a-z]/g, function(match){
794                         return match.toUpperCase();
795                 });
796         },
797
798         escapeRegExp: function(){
799                 return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
800         },
801
802         toInt: function(base){
803                 return parseInt(this, base || 10);
804         },
805
806         toFloat: function(){
807                 return parseFloat(this);
808         },
809
810         hexToRgb: function(array){
811                 var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
812                 return (hex) ? hex.slice(1).hexToRgb(array) : null;
813         },
814
815         rgbToHex: function(array){
816                 var rgb = this.match(/\d{1,3}/g);
817                 return (rgb) ? rgb.rgbToHex(array) : null;
818         },
819
820         stripScripts: function(option){
821                 var scripts = '';
822                 var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
823                         scripts += arguments[1] + '\n';
824                         return '';
825                 });
826                 if (option === true) $exec(scripts);
827                 else if ($type(option) == 'function') option(scripts, text);
828                 return text;
829         },
830
831         substitute: function(object, regexp){
832                 return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
833                         if (match.charAt(0) == '\\') return match.slice(1);
834                         return (object[name] != undefined) ? object[name] : '';
835                 });
836         }
837
838 });
839
840
841 /*
842 ---
843
844 script: Hash.js
845
846 description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.
847
848 license: MIT-style license.
849
850 requires:
851 - /Hash.base
852
853 provides: [Hash]
854
855 ...
856 */
857
858 Hash.implement({
859
860         has: Object.prototype.hasOwnProperty,
861
862         keyOf: function(value){
863                 for (var key in this){
864                         if (this.hasOwnProperty(key) && this[key] === value) return key;
865                 }
866                 return null;
867         },
868
869         hasValue: function(value){
870                 return (Hash.keyOf(this, value) !== null);
871         },
872
873         extend: function(properties){
874                 Hash.each(properties || {}, function(value, key){
875                         Hash.set(this, key, value);
876                 }, this);
877                 return this;
878         },
879
880         combine: function(properties){
881                 Hash.each(properties || {}, function(value, key){
882                         Hash.include(this, key, value);
883                 }, this);
884                 return this;
885         },
886
887         erase: function(key){
888                 if (this.hasOwnProperty(key)) delete this[key];
889                 return this;
890         },
891
892         get: function(key){
893                 return (this.hasOwnProperty(key)) ? this[key] : null;
894         },
895
896         set: function(key, value){
897                 if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
898                 return this;
899         },
900
901         empty: function(){
902                 Hash.each(this, function(value, key){
903                         delete this[key];
904                 }, this);
905                 return this;
906         },
907
908         include: function(key, value){
909                 if (this[key] == undefined) this[key] = value;
910                 return this;
911         },
912
913         map: function(fn, bind){
914                 var results = new Hash;
915                 Hash.each(this, function(value, key){
916                         results.set(key, fn.call(bind, value, key, this));
917                 }, this);
918                 return results;
919         },
920
921         filter: function(fn, bind){
922                 var results = new Hash;
923                 Hash.each(this, function(value, key){
924                         if (fn.call(bind, value, key, this)) results.set(key, value);
925                 }, this);
926                 return results;
927         },
928
929         every: function(fn, bind){
930                 for (var key in this){
931                         if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
932                 }
933                 return true;
934         },
935
936         some: function(fn, bind){
937                 for (var key in this){
938                         if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
939                 }
940                 return false;
941         },
942
943         getKeys: function(){
944                 var keys = [];
945                 Hash.each(this, function(value, key){
946                         keys.push(key);
947                 });
948                 return keys;
949         },
950
951         getValues: function(){
952                 var values = [];
953                 Hash.each(this, function(value){
954                         values.push(value);
955                 });
956                 return values;
957         },
958
959         toQueryString: function(base){
960                 var queryString = [];
961                 Hash.each(this, function(value, key){
962                         if (base) key = base + '[' + key + ']';
963                         var result;
964                         switch ($type(value)){
965                                 case 'object': result = Hash.toQueryString(value, key); break;
966                                 case 'array':
967                                         var qs = {};
968                                         value.each(function(val, i){
969                                                 qs[i] = val;
970                                         });
971                                         result = Hash.toQueryString(qs, key);
972                                 break;
973                                 default: result = key + '=' + encodeURIComponent(value);
974                         }
975                         if (value != undefined) queryString.push(result);
976                 });
977
978                 return queryString.join('&');
979         }
980
981 });
982
983 Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});
984
985
986 /*
987 ---
988
989 script: Event.js
990
991 description: Contains the Event Class, to make the event object cross-browser.
992
993 license: MIT-style license.
994
995 requires:
996 - /Window
997 - /Document
998 - /Hash
999 - /Array
1000 - /Function
1001 - /String
1002
1003 provides: [Event]
1004
1005 ...
1006 */
1007
1008 var Event = new Native({
1009
1010         name: 'Event',
1011
1012         initialize: function(event, win){
1013                 win = win || window;
1014                 var doc = win.document;
1015                 event = event || win.event;
1016                 if (event.$extended) return event;
1017                 this.$extended = true;
1018                 var type = event.type;
1019                 var target = event.target || event.srcElement;
1020                 while (target && target.nodeType == 3) target = target.parentNode;
1021
1022                 if (type.test(/key/)){
1023                         var code = event.which || event.keyCode;
1024                         var key = Event.Keys.keyOf(code);
1025                         if (type == 'keydown'){
1026                                 var fKey = code - 111;
1027                                 if (fKey > 0 && fKey < 13) key = 'f' + fKey;
1028                         }
1029                         key = key || String.fromCharCode(code).toLowerCase();
1030                 } else if (type.match(/(click|mouse|menu)/i)){
1031                         doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
1032                         var page = {
1033                                 x: event.pageX || event.clientX + doc.scrollLeft,
1034                                 y: event.pageY || event.clientY + doc.scrollTop
1035                         };
1036                         var client = {
1037                                 x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
1038                                 y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
1039                         };
1040                         if (type.match(/DOMMouseScroll|mousewheel/)){
1041                                 var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
1042                         }
1043                         var rightClick = (event.which == 3) || (event.button == 2);
1044                         var related = null;
1045                         if (type.match(/over|out/)){
1046                                 switch (type){
1047                                         case 'mouseover': related = event.relatedTarget || event.fromElement; break;
1048                                         case 'mouseout': related = event.relatedTarget || event.toElement;
1049                                 }
1050                                 if (!(function(){
1051                                         while (related && related.nodeType == 3) related = related.parentNode;
1052                                         return true;
1053                                 }).create({attempt: Browser.Engine.gecko})()) related = false;
1054                         }
1055                 }
1056
1057                 return $extend(this, {
1058                         event: event,
1059                         type: type,
1060
1061                         page: page,
1062                         client: client,
1063                         rightClick: rightClick,
1064
1065                         wheel: wheel,
1066
1067                         relatedTarget: related,
1068                         target: target,
1069
1070                         code: code,
1071                         key: key,
1072
1073                         shift: event.shiftKey,
1074                         control: event.ctrlKey,
1075                         alt: event.altKey,
1076                         meta: event.metaKey
1077                 });
1078         }
1079
1080 });
1081
1082 Event.Keys = new Hash({
1083         'enter': 13,
1084         'up': 38,
1085         'down': 40,
1086         'left': 37,
1087         'right': 39,
1088         'esc': 27,
1089         'space': 32,
1090         'backspace': 8,
1091         'tab': 9,
1092         'delete': 46
1093 });
1094
1095 Event.implement({
1096
1097         stop: function(){
1098                 return this.stopPropagation().preventDefault();
1099         },
1100
1101         stopPropagation: function(){
1102                 if (this.event.stopPropagation) this.event.stopPropagation();
1103                 else this.event.cancelBubble = true;
1104                 return this;
1105         },
1106
1107         preventDefault: function(){
1108                 if (this.event.preventDefault) this.event.preventDefault();
1109                 else this.event.returnValue = false;
1110                 return this;
1111         }
1112
1113 });
1114
1115
1116 /*
1117 ---
1118
1119 script: Class.js
1120
1121 description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
1122
1123 license: MIT-style license.
1124
1125 requires:
1126 - /$util
1127 - /Native
1128 - /Array
1129 - /String
1130 - /Function
1131 - /Number
1132 - /Hash
1133
1134 provides: [Class]
1135
1136 ...
1137 */
1138
1139 function Class(params){
1140         
1141         if (params instanceof Function) params = {initialize: params};
1142         
1143         var newClass = function(){
1144                 Object.reset(this);
1145                 if (newClass._prototyping) return this;
1146                 this._current = $empty;
1147                 var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
1148                 delete this._current; delete this.caller;
1149                 return value;
1150         }.extend(this);
1151         
1152         newClass.implement(params);
1153         
1154         newClass.constructor = Class;
1155         newClass.prototype.constructor = newClass;
1156
1157         return newClass;
1158
1159 };
1160
1161 Function.prototype.protect = function(){
1162         this._protected = true;
1163         return this;
1164 };
1165
1166 Object.reset = function(object, key){
1167                 
1168         if (key == null){
1169                 for (var p in object) Object.reset(object, p);
1170                 return object;
1171         }
1172         
1173         delete object[key];
1174         
1175         switch ($type(object[key])){
1176                 case 'object':
1177                         var F = function(){};
1178                         F.prototype = object[key];
1179                         var i = new F;
1180                         object[key] = Object.reset(i);
1181                 break;
1182                 case 'array': object[key] = $unlink(object[key]); break;
1183         }
1184         
1185         return object;
1186         
1187 };
1188
1189 new Native({name: 'Class', initialize: Class}).extend({
1190
1191         instantiate: function(F){
1192                 F._prototyping = true;
1193                 var proto = new F;
1194                 delete F._prototyping;
1195                 return proto;
1196         },
1197         
1198         wrap: function(self, key, method){
1199                 if (method._origin) method = method._origin;
1200                 
1201                 return function(){
1202                         if (method._protected && this._current == null) throw new Error('The method "' + key + '" cannot be called.');
1203                         var caller = this.caller, current = this._current;
1204                         this.caller = current; this._current = arguments.callee;
1205                         var result = method.apply(this, arguments);
1206                         this._current = current; this.caller = caller;
1207                         return result;
1208                 }.extend({_owner: self, _origin: method, _name: key});
1209
1210         }
1211         
1212 });
1213
1214 Class.implement({
1215         
1216         implement: function(key, value){
1217                 
1218                 if ($type(key) == 'object'){
1219                         for (var p in key) this.implement(p, key[p]);
1220                         return this;
1221                 }
1222                 
1223                 var mutator = Class.Mutators[key];
1224                 
1225                 if (mutator){
1226                         value = mutator.call(this, value);
1227                         if (value == null) return this;
1228                 }
1229                 
1230                 var proto = this.prototype;
1231
1232                 switch ($type(value)){
1233                         
1234                         case 'function':
1235                                 if (value._hidden) return this;
1236                                 proto[key] = Class.wrap(this, key, value);
1237                         break;
1238                         
1239                         case 'object':
1240                                 var previous = proto[key];
1241                                 if ($type(previous) == 'object') $mixin(previous, value);
1242                                 else proto[key] = $unlink(value);
1243                         break;
1244                         
1245                         case 'array':
1246                                 proto[key] = $unlink(value);
1247                         break;
1248                         
1249                         default: proto[key] = value;
1250
1251                 }
1252                 
1253                 return this;
1254
1255         }
1256         
1257 });
1258
1259 Class.Mutators = {
1260         
1261         Extends: function(parent){
1262
1263                 this.parent = parent;
1264                 this.prototype = Class.instantiate(parent);
1265
1266                 this.implement('parent', function(){
1267                         var name = this.caller._name, previous = this.caller._owner.parent.prototype[name];
1268                         if (!previous) throw new Error('The method "' + name + '" has no parent.');
1269                         return previous.apply(this, arguments);
1270                 }.protect());
1271
1272         },
1273
1274         Implements: function(items){
1275                 $splat(items).each(function(item){
1276                         if (item instanceof Function) item = Class.instantiate(item);
1277                         this.implement(item);
1278                 }, this);
1279
1280         }
1281         
1282 };
1283
1284
1285 /*
1286 ---
1287
1288 script: Class.Extras.js
1289
1290 description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
1291
1292 license: MIT-style license.
1293
1294 requires:
1295 - /Class
1296
1297 provides: [Chain, Events, Options]
1298
1299 ...
1300 */
1301
1302 var Chain = new Class({
1303
1304         $chain: [],
1305
1306         chain: function(){
1307                 this.$chain.extend(Array.flatten(arguments));
1308                 return this;
1309         },
1310
1311         callChain: function(){
1312                 return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
1313         },
1314
1315         clearChain: function(){
1316                 this.$chain.empty();
1317                 return this;
1318         }
1319
1320 });
1321
1322 var Events = new Class({
1323
1324         $events: {},
1325
1326         addEvent: function(type, fn, internal){
1327                 type = Events.removeOn(type);
1328                 if (fn != $empty){
1329                         this.$events[type] = this.$events[type] || [];
1330                         this.$events[type].include(fn);
1331                         if (internal) fn.internal = true;
1332                 }
1333                 return this;
1334         },
1335
1336         addEvents: function(events){
1337                 for (var type in events) this.addEvent(type, events[type]);
1338                 return this;
1339         },
1340
1341         fireEvent: function(type, args, delay){
1342                 type = Events.removeOn(type);
1343                 if (!this.$events || !this.$events[type]) return this;
1344                 this.$events[type].each(function(fn){
1345                         fn.create({'bind': this, 'delay': delay, 'arguments': args})();
1346                 }, this);
1347                 return this;
1348         },
1349
1350         removeEvent: function(type, fn){
1351                 type = Events.removeOn(type);
1352                 if (!this.$events[type]) return this;
1353                 if (!fn.internal) this.$events[type].erase(fn);
1354                 return this;
1355         },
1356
1357         removeEvents: function(events){
1358                 var type;
1359                 if ($type(events) == 'object'){
1360                         for (type in events) this.removeEvent(type, events[type]);
1361                         return this;
1362                 }
1363                 if (events) events = Events.removeOn(events);
1364                 for (type in this.$events){
1365                         if (events && events != type) continue;
1366                         var fns = this.$events[type];
1367                         for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]);
1368                 }
1369                 return this;
1370         }
1371
1372 });
1373
1374 Events.removeOn = function(string){
1375         return string.replace(/^on([A-Z])/, function(full, first){
1376                 return first.toLowerCase();
1377         });
1378 };
1379
1380 var Options = new Class({
1381
1382         setOptions: function(){
1383                 this.options = $merge.run([this.options].extend(arguments));
1384                 if (!this.addEvent) return this;
1385                 for (var option in this.options){
1386                         if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
1387                         this.addEvent(option, this.options[option]);
1388                         delete this.options[option];
1389                 }
1390                 return this;
1391         }
1392
1393 });
1394
1395
1396 /*
1397 ---
1398
1399 script: Element.js
1400
1401 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.
1402
1403 license: MIT-style license.
1404
1405 requires:
1406 - /Window
1407 - /Document
1408 - /Array
1409 - /String
1410 - /Function
1411 - /Number
1412 - /Hash
1413
1414 provides: [Element, Elements, $, $$, Iframe]
1415
1416 ...
1417 */
1418
1419 var Element = new Native({
1420
1421         name: 'Element',
1422
1423         legacy: window.Element,
1424
1425         initialize: function(tag, props){
1426                 var konstructor = Element.Constructors.get(tag);
1427                 if (konstructor) return konstructor(props);
1428                 if (typeof tag == 'string') return document.newElement(tag, props);
1429                 return document.id(tag).set(props);
1430         },
1431
1432         afterImplement: function(key, value){
1433                 Element.Prototype[key] = value;
1434                 if (Array[key]) return;
1435                 Elements.implement(key, function(){
1436                         var items = [], elements = true;
1437                         for (var i = 0, j = this.length; i < j; i++){
1438                                 var returns = this[i][key].apply(this[i], arguments);
1439                                 items.push(returns);
1440                                 if (elements) elements = ($type(returns) == 'element');
1441                         }
1442                         return (elements) ? new Elements(items) : items;
1443                 });
1444         }
1445
1446 });
1447
1448 Element.Prototype = {$family: {name: 'element'}};
1449
1450 Element.Constructors = new Hash;
1451
1452 var IFrame = new Native({
1453
1454         name: 'IFrame',
1455
1456         generics: false,
1457
1458         initialize: function(){
1459                 var params = Array.link(arguments, {properties: Object.type, iframe: $defined});
1460                 var props = params.properties || {};
1461                 var iframe = document.id(params.iframe);
1462                 var onload = props.onload || $empty;
1463                 delete props.onload;
1464                 props.id = props.name = $pick(props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + $time());
1465                 iframe = new Element(iframe || 'iframe', props);
1466                 var onFrameLoad = function(){
1467                         var host = $try(function(){
1468                                 return iframe.contentWindow.location.host;
1469                         });
1470                         if (!host || host == window.location.host){
1471                                 var win = new Window(iframe.contentWindow);
1472                                 new Document(iframe.contentWindow.document);
1473                                 $extend(win.Element.prototype, Element.Prototype);
1474                         }
1475                         onload.call(iframe.contentWindow, iframe.contentWindow.document);
1476                 };
1477                 var contentWindow = $try(function(){
1478                         return iframe.contentWindow;
1479                 });
1480                 ((contentWindow && contentWindow.document.body) || window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad);
1481                 return iframe;
1482         }
1483
1484 });
1485
1486 var Elements = new Native({
1487
1488         initialize: function(elements, options){
1489                 options = $extend({ddup: true, cash: true}, options);
1490                 elements = elements || [];
1491                 if (options.ddup || options.cash){
1492                         var uniques = {}, returned = [];
1493                         for (var i = 0, l = elements.length; i < l; i++){
1494                                 var el = document.id(elements[i], !options.cash);
1495                                 if (options.ddup){
1496                                         if (uniques[el.uid]) continue;
1497                                         uniques[el.uid] = true;
1498                                 }
1499                                 if (el) returned.push(el);
1500                         }
1501                         elements = returned;
1502                 }
1503                 return (options.cash) ? $extend(elements, this) : elements;
1504         }
1505
1506 });
1507
1508 Elements.implement({
1509
1510         filter: function(filter, bind){
1511                 if (!filter) return this;
1512                 return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){
1513                         return item.match(filter);
1514                 } : filter, bind));
1515         }
1516
1517 });
1518
1519 Document.implement({
1520
1521         newElement: function(tag, props){
1522                 if (Browser.Engine.trident && props){
1523                         ['name', 'type', 'checked'].each(function(attribute){
1524                                 if (!props[attribute]) return;
1525                                 tag += ' ' + attribute + '="' + props[attribute] + '"';
1526                                 if (attribute != 'checked') delete props[attribute];
1527                         });
1528                         tag = '<' + tag + '>';
1529                 }
1530                 return document.id(this.createElement(tag)).set(props);
1531         },
1532
1533         newTextNode: function(text){
1534                 return this.createTextNode(text);
1535         },
1536
1537         getDocument: function(){
1538                 return this;
1539         },
1540
1541         getWindow: function(){
1542                 return this.window;
1543         },
1544         
1545         id: (function(){
1546                 
1547                 var types = {
1548
1549                         string: function(id, nocash, doc){
1550                                 id = doc.getElementById(id);
1551                                 return (id) ? types.element(id, nocash) : null;
1552                         },
1553                         
1554                         element: function(el, nocash){
1555                                 $uid(el);
1556                                 if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
1557                                         var proto = Element.Prototype;
1558                                         for (var p in proto) el[p] = proto[p];
1559                                 };
1560                                 return el;
1561                         },
1562                         
1563                         object: function(obj, nocash, doc){
1564                                 if (obj.toElement) return types.element(obj.toElement(doc), nocash);
1565                                 return null;
1566                         }
1567                         
1568                 };
1569
1570                 types.textnode = types.whitespace = types.window = types.document = $arguments(0);
1571                 
1572                 return function(el, nocash, doc){
1573                         if (el && el.$family && el.uid) return el;
1574                         var type = $type(el);
1575                         return (types[type]) ? types[type](el, nocash, doc || document) : null;
1576                 };
1577
1578         })()
1579
1580 });
1581
1582 if (window.$ == null) Window.implement({
1583         $: function(el, nc){
1584                 return document.id(el, nc, this.document);
1585         }
1586 });
1587
1588 Window.implement({
1589
1590         $$: function(selector){
1591                 if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector);
1592                 var elements = [];
1593                 var args = Array.flatten(arguments);
1594                 for (var i = 0, l = args.length; i < l; i++){
1595                         var item = args[i];
1596                         switch ($type(item)){
1597                                 case 'element': elements.push(item); break;
1598                                 case 'string': elements.extend(this.document.getElements(item, true));
1599                         }
1600                 }
1601                 return new Elements(elements);
1602         },
1603
1604         getDocument: function(){
1605                 return this.document;
1606         },
1607
1608         getWindow: function(){
1609                 return this;
1610         }
1611
1612 });
1613
1614 Native.implement([Element, Document], {
1615
1616         getElement: function(selector, nocash){
1617                 return document.id(this.getElements(selector, true)[0] || null, nocash);
1618         },
1619
1620         getElements: function(tags, nocash){
1621                 tags = tags.split(',');
1622                 var elements = [];
1623                 var ddup = (tags.length > 1);
1624                 tags.each(function(tag){
1625                         var partial = this.getElementsByTagName(tag.trim());
1626                         (ddup) ? elements.extend(partial) : elements = partial;
1627                 }, this);
1628                 return new Elements(elements, {ddup: ddup, cash: !nocash});
1629         }
1630
1631 });
1632
1633 (function(){
1634
1635 var collected = {}, storage = {};
1636 var props = {input: 'checked', option: 'selected', textarea: (Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerHTML' : 'value'};
1637
1638 var get = function(uid){
1639         return (storage[uid] || (storage[uid] = {}));
1640 };
1641
1642 var clean = function(item, retain){
1643         if (!item) return;
1644         var uid = item.uid;
1645         if (Browser.Engine.trident){
1646                 if (item.clearAttributes){
1647                         var clone = retain && item.cloneNode(false);
1648                         item.clearAttributes();
1649                         if (clone) item.mergeAttributes(clone);
1650                 } else if (item.removeEvents){
1651                         item.removeEvents();
1652                 }
1653                 if ((/object/i).test(item.tagName)){
1654                         for (var p in item){
1655                                 if (typeof item[p] == 'function') item[p] = $empty;
1656                         }
1657                         Element.dispose(item);
1658                 }
1659         }       
1660         if (!uid) return;
1661         collected[uid] = storage[uid] = null;
1662 };
1663
1664 var purge = function(){
1665         Hash.each(collected, clean);
1666         if (Browser.Engine.trident) $A(document.getElementsByTagName('object')).each(clean);
1667         if (window.CollectGarbage) CollectGarbage();
1668         collected = storage = null;
1669 };
1670
1671 var walk = function(element, walk, start, match, all, nocash){
1672         var el = element[start || walk];
1673         var elements = [];
1674         while (el){
1675                 if (el.nodeType == 1 && (!match || Element.match(el, match))){
1676                         if (!all) return document.id(el, nocash);
1677                         elements.push(el);
1678                 }
1679                 el = el[walk];
1680         }
1681         return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : null;
1682 };
1683
1684 var attributes = {
1685         'html': 'innerHTML',
1686         'class': 'className',
1687         'for': 'htmlFor',
1688         'defaultValue': 'defaultValue',
1689         'text': (Browser.Engine.trident || (Browser.Engine.webkit && Browser.Engine.version < 420)) ? 'innerText' : 'textContent'
1690 };
1691 var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'];
1692 var camels = ['value', 'type', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'];
1693
1694 bools = bools.associate(bools);
1695
1696 Hash.extend(attributes, bools);
1697 Hash.extend(attributes, camels.associate(camels.map(String.toLowerCase)));
1698
1699 var inserters = {
1700
1701         before: function(context, element){
1702                 if (element.parentNode) element.parentNode.insertBefore(context, element);
1703         },
1704
1705         after: function(context, element){
1706                 if (!element.parentNode) return;
1707                 var next = element.nextSibling;
1708                 (next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
1709         },
1710
1711         bottom: function(context, element){
1712                 element.appendChild(context);
1713         },
1714
1715         top: function(context, element){
1716                 var first = element.firstChild;
1717                 (first) ? element.insertBefore(context, first) : element.appendChild(context);
1718         }
1719
1720 };
1721
1722 inserters.inside = inserters.bottom;
1723
1724 Hash.each(inserters, function(inserter, where){
1725
1726         where = where.capitalize();
1727
1728         Element.implement('inject' + where, function(el){
1729                 inserter(this, document.id(el, true));
1730                 return this;
1731         });
1732
1733         Element.implement('grab' + where, function(el){
1734                 inserter(document.id(el, true), this);
1735                 return this;
1736         });
1737
1738 });
1739
1740 Element.implement({
1741
1742         set: function(prop, value){
1743                 switch ($type(prop)){
1744                         case 'object':
1745                                 for (var p in prop) this.set(p, prop[p]);
1746                                 break;
1747                         case 'string':
1748                                 var property = Element.Properties.get(prop);
1749                                 (property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
1750                 }
1751                 return this;
1752         },
1753
1754         get: function(prop){
1755                 var property = Element.Properties.get(prop);
1756                 return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
1757         },
1758
1759         erase: function(prop){
1760                 var property = Element.Properties.get(prop);
1761                 (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
1762                 return this;
1763         },
1764
1765         setProperty: function(attribute, value){
1766                 var key = attributes[attribute];
1767                 if (value == undefined) return this.removeProperty(attribute);
1768                 if (key && bools[attribute]) value = !!value;
1769                 (key) ? this[key] = value : this.setAttribute(attribute, '' + value);
1770                 return this;
1771         },
1772
1773         setProperties: function(attributes){
1774                 for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
1775                 return this;
1776         },
1777
1778         getProperty: function(attribute){
1779                 var key = attributes[attribute];
1780                 var value = (key) ? this[key] : this.getAttribute(attribute, 2);
1781                 return (bools[attribute]) ? !!value : (key) ? value : value || null;
1782         },
1783
1784         getProperties: function(){
1785                 var args = $A(arguments);
1786                 return args.map(this.getProperty, this).associate(args);
1787         },
1788
1789         removeProperty: function(attribute){
1790                 var key = attributes[attribute];
1791                 (key) ? this[key] = (key && bools[attribute]) ? false : '' : this.removeAttribute(attribute);
1792                 return this;
1793         },
1794
1795         removeProperties: function(){
1796                 Array.each(arguments, this.removeProperty, this);
1797                 return this;
1798         },
1799
1800         hasClass: function(className){
1801                 return this.className.contains(className, ' ');
1802         },
1803
1804         addClass: function(className){
1805                 if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
1806                 return this;
1807         },
1808
1809         removeClass: function(className){
1810                 this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
1811                 return this;
1812         },
1813
1814         toggleClass: function(className){
1815                 return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
1816         },
1817
1818         adopt: function(){
1819                 Array.flatten(arguments).each(function(element){
1820                         element = document.id(element, true);
1821                         if (element) this.appendChild(element);
1822                 }, this);
1823                 return this;
1824         },
1825
1826         appendText: function(text, where){
1827                 return this.grab(this.getDocument().newTextNode(text), where);
1828         },
1829
1830         grab: function(el, where){
1831                 inserters[where || 'bottom'](document.id(el, true), this);
1832                 return this;
1833         },
1834
1835         inject: function(el, where){
1836                 inserters[where || 'bottom'](this, document.id(el, true));
1837                 return this;
1838         },
1839
1840         replaces: function(el){
1841                 el = document.id(el, true);
1842                 el.parentNode.replaceChild(this, el);
1843                 return this;
1844         },
1845
1846         wraps: function(el, where){
1847                 el = document.id(el, true);
1848                 return this.replaces(el).grab(el, where);
1849         },
1850
1851         getPrevious: function(match, nocash){
1852                 return walk(this, 'previousSibling', null, match, false, nocash);
1853         },
1854
1855         getAllPrevious: function(match, nocash){
1856                 return walk(this, 'previousSibling', null, match, true, nocash);
1857         },
1858
1859         getNext: function(match, nocash){
1860                 return walk(this, 'nextSibling', null, match, false, nocash);
1861         },
1862
1863         getAllNext: function(match, nocash){
1864                 return walk(this, 'nextSibling', null, match, true, nocash);
1865         },
1866
1867         getFirst: function(match, nocash){
1868                 return walk(this, 'nextSibling', 'firstChild', match, false, nocash);
1869         },
1870
1871         getLast: function(match, nocash){
1872                 return walk(this, 'previousSibling', 'lastChild', match, false, nocash);
1873         },
1874
1875         getParent: function(match, nocash){
1876                 return walk(this, 'parentNode', null, match, false, nocash);
1877         },
1878
1879         getParents: function(match, nocash){
1880                 return walk(this, 'parentNode', null, match, true, nocash);
1881         },
1882         
1883         getSiblings: function(match, nocash){
1884                 return this.getParent().getChildren(match, nocash).erase(this);
1885         },
1886
1887         getChildren: function(match, nocash){
1888                 return walk(this, 'nextSibling', 'firstChild', match, true, nocash);
1889         },
1890
1891         getWindow: function(){
1892                 return this.ownerDocument.window;
1893         },
1894
1895         getDocument: function(){
1896                 return this.ownerDocument;
1897         },
1898
1899         getElementById: function(id, nocash){
1900                 var el = this.ownerDocument.getElementById(id);
1901                 if (!el) return null;
1902                 for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
1903                         if (!parent) return null;
1904                 }
1905                 return document.id(el, nocash);
1906         },
1907
1908         getSelected: function(){
1909                 return new Elements($A(this.options).filter(function(option){
1910                         return option.selected;
1911                 }));
1912         },
1913
1914         getComputedStyle: function(property){
1915                 if (this.currentStyle) return this.currentStyle[property.camelCase()];
1916                 var computed = this.getDocument().defaultView.getComputedStyle(this, null);
1917                 return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
1918         },
1919
1920         toQueryString: function(){
1921                 var queryString = [];
1922                 this.getElements('input, select, textarea', true).each(function(el){
1923                         if (!el.name || el.disabled || el.type == 'submit' || el.type == 'reset' || el.type == 'file') return;
1924                         var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
1925                                 return opt.value;
1926                         }) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
1927                         $splat(value).each(function(val){
1928                                 if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val));
1929                         });
1930                 });
1931                 return queryString.join('&');
1932         },
1933
1934         clone: function(contents, keepid){
1935                 contents = contents !== false;
1936                 var clone = this.cloneNode(contents);
1937                 var clean = function(node, element){
1938                         if (!keepid) node.removeAttribute('id');
1939                         if (Browser.Engine.trident){
1940                                 node.clearAttributes();
1941                                 node.mergeAttributes(element);
1942                                 node.removeAttribute('uid');
1943                                 if (node.options){
1944                                         var no = node.options, eo = element.options;
1945                                         for (var j = no.length; j--;) no[j].selected = eo[j].selected;
1946                                 }
1947                         }
1948                         var prop = props[element.tagName.toLowerCase()];
1949                         if (prop && element[prop]) node[prop] = element[prop];
1950                 };
1951
1952                 if (contents){
1953                         var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
1954                         for (var i = ce.length; i--;) clean(ce[i], te[i]);
1955                 }
1956
1957                 clean(clone, this);
1958                 return document.id(clone);
1959         },
1960
1961         destroy: function(){
1962                 Element.empty(this);
1963                 Element.dispose(this);
1964                 clean(this, true);
1965                 return null;
1966         },
1967
1968         empty: function(){
1969                 $A(this.childNodes).each(function(node){
1970                         Element.destroy(node);
1971                 });
1972                 return this;
1973         },
1974
1975         dispose: function(){
1976                 return (this.parentNode) ? this.parentNode.removeChild(this) : this;
1977         },
1978
1979         hasChild: function(el){
1980                 el = document.id(el, true);
1981                 if (!el) return false;
1982                 if (Browser.Engine.webkit && Browser.Engine.version < 420) return $A(this.getElementsByTagName(el.tagName)).contains(el);
1983                 return (this.contains) ? (this != el && this.contains(el)) : !!(this.compareDocumentPosition(el) & 16);
1984         },
1985
1986         match: function(tag){
1987                 return (!tag || (tag == this) || (Element.get(this, 'tag') == tag));
1988         }
1989
1990 });
1991
1992 Native.implement([Element, Window, Document], {
1993
1994         addListener: function(type, fn){
1995                 if (type == 'unload'){
1996                         var old = fn, self = this;
1997                         fn = function(){
1998                                 self.removeListener('unload', fn);
1999                                 old();
2000                         };
2001                 } else {
2002                         collected[this.uid] = this;
2003                 }
2004                 if (this.addEventListener) this.addEventListener(type, fn, false);
2005                 else this.attachEvent('on' + type, fn);
2006                 return this;
2007         },
2008
2009         removeListener: function(type, fn){
2010                 if (this.removeEventListener) this.removeEventListener(type, fn, false);
2011                 else this.detachEvent('on' + type, fn);
2012                 return this;
2013         },
2014
2015         retrieve: function(property, dflt){
2016                 var storage = get(this.uid), prop = storage[property];
2017                 if (dflt != undefined && prop == undefined) prop = storage[property] = dflt;
2018                 return $pick(prop);
2019         },
2020
2021         store: function(property, value){
2022                 var storage = get(this.uid);
2023                 storage[property] = value;
2024                 return this;
2025         },
2026
2027         eliminate: function(property){
2028                 var storage = get(this.uid);
2029                 delete storage[property];
2030                 return this;
2031         }
2032
2033 });
2034
2035 window.addListener('unload', purge);
2036
2037 })();
2038
2039 Element.Properties = new Hash;
2040
2041 Element.Properties.style = {
2042
2043         set: function(style){
2044                 this.style.cssText = style;
2045         },
2046
2047         get: function(){
2048                 return this.style.cssText;
2049         },
2050
2051         erase: function(){
2052                 this.style.cssText = '';
2053         }
2054
2055 };
2056
2057 Element.Properties.tag = {
2058
2059         get: function(){
2060                 return this.tagName.toLowerCase();
2061         }
2062
2063 };
2064
2065 Element.Properties.html = (function(){
2066         var wrapper = document.createElement('div');
2067
2068         var translations = {
2069                 table: [1, '<table>', '</table>'],
2070                 select: [1, '<select>', '</select>'],
2071                 tbody: [2, '<table><tbody>', '</tbody></table>'],
2072                 tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
2073         };
2074         translations.thead = translations.tfoot = translations.tbody;
2075
2076         var html = {
2077                 set: function(){
2078                         var html = Array.flatten(arguments).join('');
2079                         var wrap = Browser.Engine.trident && translations[this.get('tag')];
2080                         if (wrap){
2081                                 var first = wrapper;
2082                                 first.innerHTML = wrap[1] + html + wrap[2];
2083                                 for (var i = wrap[0]; i--;) first = first.firstChild;
2084                                 this.empty().adopt(first.childNodes);
2085                         } else {
2086                                 this.innerHTML = html;
2087                         }
2088                 }
2089         };
2090
2091         html.erase = html.set;
2092
2093         return html;
2094 })();
2095
2096 if (Browser.Engine.webkit && Browser.Engine.version < 420) Element.Properties.text = {
2097         get: function(){
2098                 if (this.innerText) return this.innerText;
2099                 var temp = this.ownerDocument.newElement('div', {html: this.innerHTML}).inject(this.ownerDocument.body);
2100                 var text = temp.innerText;
2101                 temp.destroy();
2102                 return text;
2103         }
2104 };
2105
2106
2107 /*
2108 ---
2109
2110 script: Element.Event.js
2111
2112 description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events.
2113
2114 license: MIT-style license.
2115
2116 requires: 
2117 - /Element
2118 - /Event
2119
2120 provides: [Element.Event]
2121
2122 ...
2123 */
2124
2125 Element.Properties.events = {set: function(events){
2126         this.addEvents(events);
2127 }};
2128
2129 Native.implement([Element, Window, Document], {
2130
2131         addEvent: function(type, fn){
2132                 var events = this.retrieve('events', {});
2133                 events[type] = events[type] || {'keys': [], 'values': []};
2134                 if (events[type].keys.contains(fn)) return this;
2135                 events[type].keys.push(fn);
2136                 var realType = type, custom = Element.Events.get(type), condition = fn, self = this;
2137                 if (custom){
2138                         if (custom.onAdd) custom.onAdd.call(this, fn);
2139                         if (custom.condition){
2140                                 condition = function(event){
2141                                         if (custom.condition.call(this, event)) return fn.call(this, event);
2142                                         return true;
2143                                 };
2144                         }
2145                         realType = custom.base || realType;
2146                 }
2147                 var defn = function(){
2148                         return fn.call(self);
2149                 };
2150                 var nativeEvent = Element.NativeEvents[realType];
2151                 if (nativeEvent){
2152                         if (nativeEvent == 2){
2153                                 defn = function(event){
2154                                         event = new Event(event, self.getWindow());
2155                                         if (condition.call(self, event) === false) event.stop();
2156                                 };
2157                         }
2158                         this.addListener(realType, defn);
2159                 }
2160                 events[type].values.push(defn);
2161                 return this;
2162         },
2163
2164         removeEvent: function(type, fn){
2165                 var events = this.retrieve('events');
2166                 if (!events || !events[type]) return this;
2167                 var pos = events[type].keys.indexOf(fn);
2168                 if (pos == -1) return this;
2169                 events[type].keys.splice(pos, 1);
2170                 var value = events[type].values.splice(pos, 1)[0];
2171                 var custom = Element.Events.get(type);
2172                 if (custom){
2173                         if (custom.onRemove) custom.onRemove.call(this, fn);
2174                         type = custom.base || type;
2175                 }
2176                 return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this;
2177         },
2178
2179         addEvents: function(events){
2180                 for (var event in events) this.addEvent(event, events[event]);
2181                 return this;
2182         },
2183
2184         removeEvents: function(events){
2185                 var type;
2186                 if ($type(events) == 'object'){
2187                         for (type in events) this.removeEvent(type, events[type]);
2188                         return this;
2189                 }
2190                 var attached = this.retrieve('events');
2191                 if (!attached) return this;
2192                 if (!events){
2193                         for (type in attached) this.removeEvents(type);
2194                         this.eliminate('events');
2195                 } else if (attached[events]){
2196                         while (attached[events].keys[0]) this.removeEvent(events, attached[events].keys[0]);
2197                         attached[events] = null;
2198                 }
2199                 return this;
2200         },
2201
2202         fireEvent: function(type, args, delay){
2203                 var events = this.retrieve('events');
2204                 if (!events || !events[type]) return this;
2205                 events[type].keys.each(function(fn){
2206                         fn.create({'bind': this, 'delay': delay, 'arguments': args})();
2207                 }, this);
2208                 return this;
2209         },
2210
2211         cloneEvents: function(from, type){
2212                 from = document.id(from);
2213                 var fevents = from.retrieve('events');
2214                 if (!fevents) return this;
2215                 if (!type){
2216                         for (var evType in fevents) this.cloneEvents(from, evType);
2217                 } else if (fevents[type]){
2218                         fevents[type].keys.each(function(fn){
2219                                 this.addEvent(type, fn);
2220                         }, this);
2221                 }
2222                 return this;
2223         }
2224
2225 });
2226
2227 Element.NativeEvents = {
2228         click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
2229         mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
2230         mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
2231         keydown: 2, keypress: 2, keyup: 2, //keyboard
2232         focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements
2233         load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
2234         error: 1, abort: 1, scroll: 1 //misc
2235 };
2236
2237 (function(){
2238
2239 var $check = function(event){
2240         var related = event.relatedTarget;
2241         if (related == undefined) return true;
2242         if (related === false) return false;
2243         return ($type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related));
2244 };
2245
2246 Element.Events = new Hash({
2247
2248         mouseenter: {
2249                 base: 'mouseover',
2250                 condition: $check
2251         },
2252
2253         mouseleave: {
2254                 base: 'mouseout',
2255                 condition: $check
2256         },
2257
2258         mousewheel: {
2259                 base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel'
2260         }
2261
2262 });
2263
2264 })();
2265
2266
2267 /*
2268 ---
2269
2270 script: Element.Style.js
2271
2272 description: Contains methods for interacting with the styles of Elements in a fashionable way.
2273
2274 license: MIT-style license.
2275
2276 requires:
2277 - /Element
2278
2279 provides: [Element.Style]
2280
2281 ...
2282 */
2283
2284 Element.Properties.styles = {set: function(styles){
2285         this.setStyles(styles);
2286 }};
2287
2288 Element.Properties.opacity = {
2289
2290         set: function(opacity, novisibility){
2291                 if (!novisibility){
2292                         if (opacity == 0){
2293                                 if (this.style.visibility != 'hidden') this.style.visibility = 'hidden';
2294                         } else {
2295                                 if (this.style.visibility != 'visible') this.style.visibility = 'visible';
2296                         }
2297                 }
2298                 if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
2299                 if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
2300                 this.style.opacity = opacity;
2301                 this.store('opacity', opacity);
2302         },
2303
2304         get: function(){
2305                 return this.retrieve('opacity', 1);
2306         }
2307
2308 };
2309
2310 Element.implement({
2311
2312         setOpacity: function(value){
2313                 return this.set('opacity', value, true);
2314         },
2315
2316         getOpacity: function(){
2317                 return this.get('opacity');
2318         },
2319
2320         setStyle: function(property, value){
2321                 switch (property){
2322                         case 'opacity': return this.set('opacity', parseFloat(value));
2323                         case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
2324                 }
2325                 property = property.camelCase();
2326                 if ($type(value) != 'string'){
2327                         var map = (Element.Styles.get(property) || '@').split(' ');
2328                         value = $splat(value).map(function(val, i){
2329                                 if (!map[i]) return '';
2330                                 return ($type(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
2331                         }).join(' ');
2332                 } else if (value == String(Number(value))){
2333                         value = Math.round(value);
2334                 }
2335                 this.style[property] = value;
2336                 return this;
2337         },
2338
2339         getStyle: function(property){
2340                 switch (property){
2341                         case 'opacity': return this.get('opacity');
2342                         case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
2343                 }
2344                 property = property.camelCase();
2345                 var result = this.style[property];
2346                 if (!$chk(result)){
2347                         result = [];
2348                         for (var style in Element.ShortStyles){
2349                                 if (property != style) continue;
2350                                 for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
2351                                 return result.join(' ');
2352                         }
2353                         result = this.getComputedStyle(property);
2354                 }
2355                 if (result){
2356                         result = String(result);
2357                         var color = result.match(/rgba?\([\d\s,]+\)/);
2358                         if (color) result = result.replace(color[0], color[0].rgbToHex());
2359                 }
2360                 if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result, 10)))){
2361                         if (property.test(/^(height|width)$/)){
2362                                 var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
2363                                 values.each(function(value){
2364                                         size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
2365                                 }, this);
2366                                 return this['offset' + property.capitalize()] - size + 'px';
2367                         }
2368                         if ((Browser.Engine.presto) && String(result).test('px')) return result;
2369                         if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
2370                 }
2371                 return result;
2372         },
2373
2374         setStyles: function(styles){
2375                 for (var style in styles) this.setStyle(style, styles[style]);
2376                 return this;
2377         },
2378
2379         getStyles: function(){
2380                 var result = {};
2381                 Array.flatten(arguments).each(function(key){
2382                         result[key] = this.getStyle(key);
2383                 }, this);
2384                 return result;
2385         }
2386
2387 });
2388
2389 Element.Styles = new Hash({
2390         left: '@px', top: '@px', bottom: '@px', right: '@px',
2391         width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
2392         backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
2393         fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
2394         margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
2395         borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
2396         zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
2397 });
2398
2399 Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
2400
2401 ['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
2402         var Short = Element.ShortStyles;
2403         var All = Element.Styles;
2404         ['margin', 'padding'].each(function(style){
2405                 var sd = style + direction;
2406                 Short[style][sd] = All[sd] = '@px';
2407         });
2408         var bd = 'border' + direction;
2409         Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
2410         var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
2411         Short[bd] = {};
2412         Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
2413         Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
2414         Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
2415 });
2416
2417
2418 /*
2419 ---
2420
2421 script: Element.Dimensions.js
2422
2423 description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
2424
2425 license: MIT-style license.
2426
2427 credits:
2428 - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
2429 - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
2430
2431 requires:
2432 - /Element
2433
2434 provides: [Element.Dimensions]
2435
2436 ...
2437 */
2438
2439 (function(){
2440
2441 Element.implement({
2442
2443         scrollTo: function(x, y){
2444                 if (isBody(this)){
2445                         this.getWindow().scrollTo(x, y);
2446                 } else {
2447                         this.scrollLeft = x;
2448                         this.scrollTop = y;
2449                 }
2450                 return this;
2451         },
2452
2453         getSize: function(){
2454                 if (isBody(this)) return this.getWindow().getSize();
2455                 return {x: this.offsetWidth, y: this.offsetHeight};
2456         },
2457
2458         getScrollSize: function(){
2459                 if (isBody(this)) return this.getWindow().getScrollSize();
2460                 return {x: this.scrollWidth, y: this.scrollHeight};
2461         },
2462
2463         getScroll: function(){
2464                 if (isBody(this)) return this.getWindow().getScroll();
2465                 return {x: this.scrollLeft, y: this.scrollTop};
2466         },
2467
2468         getScrolls: function(){
2469                 var element = this, position = {x: 0, y: 0};
2470                 while (element && !isBody(element)){
2471                         position.x += element.scrollLeft;
2472                         position.y += element.scrollTop;
2473                         element = element.parentNode;
2474                 }
2475                 return position;
2476         },
2477
2478         getOffsetParent: function(){
2479                 var element = this;
2480                 if (isBody(element)) return null;
2481                 if (!Browser.Engine.trident) return element.offsetParent;
2482                 while ((element = element.parentNode) && !isBody(element)){
2483                         if (styleString(element, 'position') != 'static') return element;
2484                 }
2485                 return null;
2486         },
2487
2488         getOffsets: function(){
2489                 if (this.getBoundingClientRect){
2490                         var bound = this.getBoundingClientRect(),
2491                                 html = document.id(this.getDocument().documentElement),
2492                                 htmlScroll = html.getScroll(),
2493                                 elemScrolls = this.getScrolls(),
2494                                 elemScroll = this.getScroll(),
2495                                 isFixed = (styleString(this, 'position') == 'fixed');
2496
2497                         return {
2498                                 x: bound.left.toInt() + elemScrolls.x - elemScroll.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
2499                                 y: bound.top.toInt()  + elemScrolls.y - elemScroll.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
2500                         };
2501                 }
2502
2503                 var element = this, position = {x: 0, y: 0};
2504                 if (isBody(this)) return position;
2505
2506                 while (element && !isBody(element)){
2507                         position.x += element.offsetLeft;
2508                         position.y += element.offsetTop;
2509
2510                         if (Browser.Engine.gecko){
2511                                 if (!borderBox(element)){
2512                                         position.x += leftBorder(element);
2513                                         position.y += topBorder(element);
2514                                 }
2515                                 var parent = element.parentNode;
2516                                 if (parent && styleString(parent, 'overflow') != 'visible'){
2517                                         position.x += leftBorder(parent);
2518                                         position.y += topBorder(parent);
2519                                 }
2520                         } else if (element != this && Browser.Engine.webkit){
2521                                 position.x += leftBorder(element);
2522                                 position.y += topBorder(element);
2523                         }
2524
2525                         element = element.offsetParent;
2526                 }
2527                 if (Browser.Engine.gecko && !borderBox(this)){
2528                         position.x -= leftBorder(this);
2529                         position.y -= topBorder(this);
2530                 }
2531                 return position;
2532         },
2533
2534         getPosition: function(relative){
2535                 if (isBody(this)) return {x: 0, y: 0};
2536                 var offset = this.getOffsets(),
2537                                 scroll = this.getScrolls();
2538                 var position = {
2539                         x: offset.x - scroll.x,
2540                         y: offset.y - scroll.y
2541                 };
2542                 var relativePosition = (relative && (relative = document.id(relative))) ? relative.getPosition() : {x: 0, y: 0};
2543                 return {x: position.x - relativePosition.x, y: position.y - relativePosition.y};
2544         },
2545
2546         getCoordinates: function(element){
2547                 if (isBody(this)) return this.getWindow().getCoordinates();
2548                 var position = this.getPosition(element),
2549                                 size = this.getSize();
2550                 var obj = {
2551                         left: position.x,
2552                         top: position.y,
2553                         width: size.x,
2554                         height: size.y
2555                 };
2556                 obj.right = obj.left + obj.width;
2557                 obj.bottom = obj.top + obj.height;
2558                 return obj;
2559         },
2560
2561         computePosition: function(obj){
2562                 return {
2563                         left: obj.x - styleNumber(this, 'margin-left'),
2564                         top: obj.y - styleNumber(this, 'margin-top')
2565                 };
2566         },
2567
2568         setPosition: function(obj){
2569                 return this.setStyles(this.computePosition(obj));
2570         }
2571
2572 });
2573
2574
2575 Native.implement([Document, Window], {
2576
2577         getSize: function(){
2578                 if (Browser.Engine.presto || Browser.Engine.webkit){
2579                         var win = this.getWindow();
2580                         return {x: win.innerWidth, y: win.innerHeight};
2581                 }
2582                 var doc = getCompatElement(this);
2583                 return {x: doc.clientWidth, y: doc.clientHeight};
2584         },
2585
2586         getScroll: function(){
2587                 var win = this.getWindow(), doc = getCompatElement(this);
2588                 return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
2589         },
2590
2591         getScrollSize: function(){
2592                 var doc = getCompatElement(this), min = this.getSize();
2593                 return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)};
2594         },
2595
2596         getPosition: function(){
2597                 return {x: 0, y: 0};
2598         },
2599
2600         getCoordinates: function(){
2601                 var size = this.getSize();
2602                 return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
2603         }
2604
2605 });
2606
2607 // private methods
2608
2609 var styleString = Element.getComputedStyle;
2610
2611 function styleNumber(element, style){
2612         return styleString(element, style).toInt() || 0;
2613 };
2614
2615 function borderBox(element){
2616         return styleString(element, '-moz-box-sizing') == 'border-box';
2617 };
2618
2619 function topBorder(element){
2620         return styleNumber(element, 'border-top-width');
2621 };
2622
2623 function leftBorder(element){
2624         return styleNumber(element, 'border-left-width');
2625 };
2626
2627 function isBody(element){
2628         return (/^(?:body|html)$/i).test(element.tagName);
2629 };
2630
2631 function getCompatElement(element){
2632         var doc = element.getDocument();
2633         return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
2634 };
2635
2636 })();
2637
2638 //aliases
2639 Element.alias('setPosition', 'position'); //compatability
2640
2641 Native.implement([Window, Document, Element], {
2642
2643         getHeight: function(){
2644                 return this.getSize().y;
2645         },
2646
2647         getWidth: function(){
2648                 return this.getSize().x;
2649         },
2650
2651         getScrollTop: function(){
2652                 return this.getScroll().y;
2653         },
2654
2655         getScrollLeft: function(){
2656                 return this.getScroll().x;
2657         },
2658
2659         getScrollHeight: function(){
2660                 return this.getScrollSize().y;
2661         },
2662
2663         getScrollWidth: function(){
2664                 return this.getScrollSize().x;
2665         },
2666
2667         getTop: function(){
2668                 return this.getPosition().y;
2669         },
2670
2671         getLeft: function(){
2672                 return this.getPosition().x;
2673         }
2674
2675 });
2676
2677
2678 /*
2679 ---
2680
2681 script: Selectors.js
2682
2683 description: Adds advanced CSS-style querying capabilities for targeting HTML Elements. Includes pseudo selectors.
2684
2685 license: MIT-style license.
2686
2687 requires:
2688 - /Element
2689
2690 provides: [Selectors]
2691
2692 ...
2693 */
2694
2695 Native.implement([Document, Element], {
2696
2697         getElements: function(expression, nocash){
2698                 expression = expression.split(',');
2699                 var items, local = {};
2700                 for (var i = 0, l = expression.length; i < l; i++){
2701                         var selector = expression[i], elements = Selectors.Utils.search(this, selector, local);
2702                         if (i != 0 && elements.item) elements = $A(elements);
2703                         items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements);
2704                 }
2705                 return new Elements(items, {ddup: (expression.length > 1), cash: !nocash});
2706         }
2707
2708 });
2709
2710 Element.implement({
2711
2712         match: function(selector){
2713                 if (!selector || (selector == this)) return true;
2714                 var tagid = Selectors.Utils.parseTagAndID(selector);
2715                 var tag = tagid[0], id = tagid[1];
2716                 if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false;
2717                 var parsed = Selectors.Utils.parseSelector(selector);
2718                 return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
2719         }
2720
2721 });
2722
2723 var Selectors = {Cache: {nth: {}, parsed: {}}};
2724
2725 Selectors.RegExps = {
2726         id: (/#([\w-]+)/),
2727         tag: (/^(\w+|\*)/),
2728         quick: (/^(\w+|\*)$/),
2729         splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
2730         combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)(["']?)([^\4]*?)\4)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
2731 };
2732
2733 Selectors.Utils = {
2734
2735         chk: function(item, uniques){
2736                 if (!uniques) return true;
2737                 var uid = $uid(item);
2738                 if (!uniques[uid]) return uniques[uid] = true;
2739                 return false;
2740         },
2741
2742         parseNthArgument: function(argument){
2743                 if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
2744                 var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
2745                 if (!parsed) return false;
2746                 var inta = parseInt(parsed[1], 10);
2747                 var a = (inta || inta === 0) ? inta : 1;
2748                 var special = parsed[2] || false;
2749                 var b = parseInt(parsed[3], 10) || 0;
2750                 if (a != 0){
2751                         b--;
2752                         while (b < 1) b += a;
2753                         while (b >= a) b -= a;
2754                 } else {
2755                         a = b;
2756                         special = 'index';
2757                 }
2758                 switch (special){
2759                         case 'n': parsed = {a: a, b: b, special: 'n'}; break;
2760                         case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
2761                         case 'even': parsed = {a: 2, b: 1, special: 'n'}; break;
2762                         case 'first': parsed = {a: 0, special: 'index'}; break;
2763                         case 'last': parsed = {special: 'last-child'}; break;
2764                         case 'only': parsed = {special: 'only-child'}; break;
2765                         default: parsed = {a: (a - 1), special: 'index'};
2766                 }
2767
2768                 return Selectors.Cache.nth[argument] = parsed;
2769         },
2770
2771         parseSelector: function(selector){
2772                 if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
2773                 var m, parsed = {classes: [], pseudos: [], attributes: []};
2774                 while ((m = Selectors.RegExps.combined.exec(selector))){
2775                         var cn = m[1], an = m[2], ao = m[3], av = m[5], pn = m[6], pa = m[7];
2776                         if (cn){
2777                                 parsed.classes.push(cn);
2778                         } else if (pn){
2779                                 var parser = Selectors.Pseudo.get(pn);
2780                                 if (parser) parsed.pseudos.push({parser: parser, argument: pa});
2781                                 else parsed.attributes.push({name: pn, operator: '=', value: pa});
2782                         } else if (an){
2783                                 parsed.attributes.push({name: an, operator: ao, value: av});
2784                         }
2785                 }
2786                 if (!parsed.classes.length) delete parsed.classes;
2787                 if (!parsed.attributes.length) delete parsed.attributes;
2788                 if (!parsed.pseudos.length) delete parsed.pseudos;
2789                 if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null;
2790                 return Selectors.Cache.parsed[selector] = parsed;
2791         },
2792
2793         parseTagAndID: function(selector){
2794                 var tag = selector.match(Selectors.RegExps.tag);
2795                 var id = selector.match(Selectors.RegExps.id);
2796                 return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
2797         },
2798
2799         filter: function(item, parsed, local){
2800                 var i;
2801                 if (parsed.classes){
2802                         for (i = parsed.classes.length; i--; i){
2803                                 var cn = parsed.classes[i];
2804                                 if (!Selectors.Filters.byClass(item, cn)) return false;
2805                         }
2806                 }
2807                 if (parsed.attributes){
2808                         for (i = parsed.attributes.length; i--; i){
2809                                 var att = parsed.attributes[i];
2810                                 if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false;
2811                         }
2812                 }
2813                 if (parsed.pseudos){
2814                         for (i = parsed.pseudos.length; i--; i){
2815                                 var psd = parsed.pseudos[i];
2816                                 if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false;
2817                         }
2818                 }
2819                 return true;
2820         },
2821
2822         getByTagAndID: function(ctx, tag, id){
2823                 if (id){
2824                         var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true);
2825                         return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
2826                 } else {
2827                         return ctx.getElementsByTagName(tag);
2828                 }
2829         },
2830
2831         search: function(self, expression, local){
2832                 var splitters = [];
2833
2834                 var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){
2835                         splitters.push(m1);
2836                         return ':)' + m2;
2837                 }).split(':)');
2838
2839                 var items, filtered, item;
2840
2841                 for (var i = 0, l = selectors.length; i < l; i++){
2842
2843                         var selector = selectors[i];
2844
2845                         if (i == 0 && Selectors.RegExps.quick.test(selector)){
2846                                 items = self.getElementsByTagName(selector);
2847                                 continue;
2848                         }
2849
2850                         var splitter = splitters[i - 1];
2851
2852                         var tagid = Selectors.Utils.parseTagAndID(selector);
2853                         var tag = tagid[0], id = tagid[1];
2854
2855                         if (i == 0){
2856                                 items = Selectors.Utils.getByTagAndID(self, tag, id);
2857                         } else {
2858                                 var uniques = {}, found = [];
2859                                 for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
2860                                 items = found;
2861                         }
2862
2863                         var parsed = Selectors.Utils.parseSelector(selector);
2864
2865                         if (parsed){
2866                                 filtered = [];
2867                                 for (var m = 0, n = items.length; m < n; m++){
2868                                         item = items[m];
2869                                         if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
2870                                 }
2871                                 items = filtered;
2872                         }
2873
2874                 }
2875
2876                 return items;
2877
2878         }
2879
2880 };
2881
2882 Selectors.Getters = {
2883
2884         ' ': function(found, self, tag, id, uniques){
2885                 var items = Selectors.Utils.getByTagAndID(self, tag, id);
2886                 for (var i = 0, l = items.length; i < l; i++){
2887                         var item = items[i];
2888                         if (Selectors.Utils.chk(item, uniques)) found.push(item);
2889                 }
2890                 return found;
2891         },
2892
2893         '>': function(found, self, tag, id, uniques){
2894                 var children = Selectors.Utils.getByTagAndID(self, tag, id);
2895                 for (var i = 0, l = children.length; i < l; i++){
2896                         var child = children[i];
2897                         if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child);
2898                 }
2899                 return found;
2900         },
2901
2902         '+': function(found, self, tag, id, uniques){
2903                 while ((self = self.nextSibling)){
2904                         if (self.nodeType == 1){
2905                                 if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
2906                                 break;
2907                         }
2908                 }
2909                 return found;
2910         },
2911
2912         '~': function(found, self, tag, id, uniques){
2913                 while ((self = self.nextSibling)){
2914                         if (self.nodeType == 1){
2915                                 if (!Selectors.Utils.chk(self, uniques)) break;
2916                                 if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
2917                         }
2918                 }
2919                 return found;
2920         }
2921
2922 };
2923
2924 Selectors.Filters = {
2925
2926         byTag: function(self, tag){
2927                 return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag));
2928         },
2929
2930         byID: function(self, id){
2931                 return (!id || (self.id && self.id == id));
2932         },
2933
2934         byClass: function(self, klass){
2935                 return (self.className && self.className.contains && self.className.contains(klass, ' '));
2936         },
2937
2938         byPseudo: function(self, parser, argument, local){
2939                 return parser.call(self, argument, local);
2940         },
2941
2942         byAttribute: function(self, name, operator, value){
2943                 var result = Element.prototype.getProperty.call(self, name);
2944                 if (!result) return (operator == '!=');
2945                 if (!operator || value == undefined) return true;
2946                 switch (operator){
2947                         case '=': return (result == value);
2948                         case '*=': return (result.contains(value));
2949                         case '^=': return (result.substr(0, value.length) == value);
2950                         case '$=': return (result.substr(result.length - value.length) == value);
2951                         case '!=': return (result != value);
2952                         case '~=': return result.contains(value, ' ');
2953                         case '|=': return result.contains(value, '-');
2954                 }
2955                 return false;
2956         }
2957
2958 };
2959
2960 Selectors.Pseudo = new Hash({
2961
2962         // w3c pseudo selectors
2963
2964         checked: function(){
2965                 return this.checked;
2966         },
2967         
2968         empty: function(){
2969                 return !(this.innerText || this.textContent || '').length;
2970         },
2971
2972         not: function(selector){
2973                 return !Element.match(this, selector);
2974         },
2975
2976         contains: function(text){
2977                 return (this.innerText || this.textContent || '').contains(text);
2978         },
2979
2980         'first-child': function(){
2981                 return Selectors.Pseudo.index.call(this, 0);
2982         },
2983
2984         'last-child': function(){
2985                 var element = this;
2986                 while ((element = element.nextSibling)){
2987                         if (element.nodeType == 1) return false;
2988                 }
2989                 return true;
2990         },
2991
2992         'only-child': function(){
2993                 var prev = this;
2994                 while ((prev = prev.previousSibling)){
2995                         if (prev.nodeType == 1) return false;
2996                 }
2997                 var next = this;
2998                 while ((next = next.nextSibling)){
2999                         if (next.nodeType == 1) return false;
3000                 }
3001                 return true;
3002         },
3003
3004         'nth-child': function(argument, local){
3005                 argument = (argument == undefined) ? 'n' : argument;
3006                 var parsed = Selectors.Utils.parseNthArgument(argument);
3007                 if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local);
3008                 var count = 0;
3009                 local.positions = local.positions || {};
3010                 var uid = $uid(this);
3011                 if (!local.positions[uid]){
3012                         var self = this;
3013                         while ((self = self.previousSibling)){
3014                                 if (self.nodeType != 1) continue;
3015                                 count ++;
3016                                 var position = local.positions[$uid(self)];
3017                                 if (position != undefined){
3018                                         count = position + count;
3019                                         break;
3020                                 }
3021                         }
3022                         local.positions[uid] = count;
3023                 }
3024                 return (local.positions[uid] % parsed.a == parsed.b);
3025         },
3026
3027         // custom pseudo selectors
3028
3029         index: function(index){
3030                 var element = this, count = 0;
3031                 while ((element = element.previousSibling)){
3032                         if (element.nodeType == 1 && ++count > index) return false;
3033                 }
3034                 return (count == index);
3035         },
3036
3037         even: function(argument, local){
3038                 return Selectors.Pseudo['nth-child'].call(this, '2n+1', local);
3039         },
3040
3041         odd: function(argument, local){
3042                 return Selectors.Pseudo['nth-child'].call(this, '2n', local);
3043         },
3044         
3045         selected: function(){
3046                 return this.selected;
3047         },
3048         
3049         enabled: function(){
3050                 return (this.disabled === false);
3051         }
3052
3053 });
3054
3055
3056 /*
3057 ---
3058
3059 script: DomReady.js
3060
3061 description: Contains the custom event domready.
3062
3063 license: MIT-style license.
3064
3065 requires:
3066 - /Element.Event
3067
3068 provides: [DomReady]
3069
3070 ...
3071 */
3072
3073 Element.Events.domready = {
3074
3075         onAdd: function(fn){
3076                 if (Browser.loaded) fn.call(this);
3077         }
3078
3079 };
3080
3081 (function(){
3082
3083         var domready = function(){
3084                 if (Browser.loaded) return;
3085                 Browser.loaded = true;
3086                 window.fireEvent('domready');
3087                 document.fireEvent('domready');
3088         };
3089         
3090         window.addEvent('load', domready);
3091
3092         if (Browser.Engine.trident){
3093                 var temp = document.createElement('div');
3094                 (function(){
3095                         ($try(function(){
3096                                 temp.doScroll(); // Technique by Diego Perini
3097                                 return document.id(temp).inject(document.body).set('html', 'temp').dispose();
3098                         })) ? domready() : arguments.callee.delay(50);
3099                 })();
3100         } else if (Browser.Engine.webkit && Browser.Engine.version < 525){
3101                 (function(){
3102                         (['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50);
3103                 })();
3104         } else {
3105                 document.addEvent('DOMContentLoaded', domready);
3106         }
3107
3108 })();
3109
3110
3111 /*
3112 ---
3113
3114 script: JSON.js
3115
3116 description: JSON encoder and decoder.
3117
3118 license: MIT-style license.
3119
3120 See Also: <http://www.json.org/>
3121
3122 requires:
3123 - /Array
3124 - /String
3125 - /Number
3126 - /Function
3127 - /Hash
3128
3129 provides: [JSON]
3130
3131 ...
3132 */
3133
3134 var JSON = new Hash(this.JSON && {
3135         stringify: JSON.stringify,
3136         parse: JSON.parse
3137 }).extend({
3138         
3139         $specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'},
3140
3141         $replaceChars: function(chr){
3142                 return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
3143         },
3144
3145         encode: function(obj){
3146                 switch ($type(obj)){
3147                         case 'string':
3148                                 return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"';
3149                         case 'array':
3150                                 return '[' + String(obj.map(JSON.encode).clean()) + ']';
3151                         case 'object': case 'hash':
3152                                 var string = [];
3153                                 Hash.each(obj, function(value, key){
3154                                         var json = JSON.encode(value);
3155                                         if (json) string.push(JSON.encode(key) + ':' + json);
3156                                 });
3157                                 return '{' + string + '}';
3158                         case 'number': case 'boolean': return String(obj);
3159                         case false: return 'null';
3160                 }
3161                 return null;
3162         },
3163
3164         decode: function(string, secure){
3165                 if ($type(string) != 'string' || !string.length) return null;
3166                 if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null;
3167                 return eval('(' + string + ')');
3168         }
3169
3170 });
3171
3172 Native.implement([Hash, Array, String, Number], {
3173
3174         toJSON: function(){
3175                 return JSON.encode(this);
3176         }
3177
3178 });
3179
3180
3181 /*
3182 ---
3183
3184 script: Cookie.js
3185
3186 description: Class for creating, reading, and deleting browser Cookies.
3187
3188 license: MIT-style license.
3189
3190 credits:
3191 - Based on the functions by Peter-Paul Koch (http://quirksmode.org).
3192
3193 requires:
3194 - /Options
3195
3196 provides: [Cookie]
3197
3198 ...
3199 */
3200
3201 var Cookie = new Class({
3202
3203         Implements: Options,
3204
3205         options: {
3206                 path: false,
3207                 domain: false,
3208                 duration: false,
3209                 secure: false,
3210                 document: document
3211         },
3212
3213         initialize: function(key, options){
3214                 this.key = key;
3215                 this.setOptions(options);
3216         },
3217
3218         write: function(value){
3219                 value = encodeURIComponent(value);
3220                 if (this.options.domain) value += '; domain=' + this.options.domain;
3221                 if (this.options.path) value += '; path=' + this.options.path;
3222                 if (this.options.duration){
3223                         var date = new Date();
3224                         date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
3225                         value += '; expires=' + date.toGMTString();
3226                 }
3227                 if (this.options.secure) value += '; secure';
3228                 this.options.document.cookie = this.key + '=' + value;
3229                 return this;
3230         },
3231
3232         read: function(){
3233                 var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
3234                 return (value) ? decodeURIComponent(value[1]) : null;
3235         },
3236
3237         dispose: function(){
3238                 new Cookie(this.key, $merge(this.options, {duration: -1})).write('');
3239                 return this;
3240         }
3241
3242 });
3243
3244 Cookie.write = function(key, value, options){
3245         return new Cookie(key, options).write(value);
3246 };
3247
3248 Cookie.read = function(key){
3249         return new Cookie(key).read();
3250 };
3251
3252 Cookie.dispose = function(key, options){
3253         return new Cookie(key, options).dispose();
3254 };
3255
3256
3257 /*
3258 ---
3259
3260 script: Swiff.js
3261
3262 description: Wrapper for embedding SWF movies. Supports External Interface Communication.
3263
3264 license: MIT-style license.
3265
3266 credits: 
3267 - Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
3268
3269 requires:
3270 - /Options
3271 - /$util
3272
3273 provides: [Swiff]
3274
3275 ...
3276 */
3277
3278 var Swiff = new Class({
3279
3280         Implements: [Options],
3281
3282         options: {
3283                 id: null,
3284                 height: 1,
3285                 width: 1,
3286                 container: null,
3287                 properties: {},
3288                 params: {
3289                         quality: 'high',
3290                         allowScriptAccess: 'always',
3291                         wMode: 'transparent',
3292                         swLiveConnect: true
3293                 },
3294                 callBacks: {},
3295                 vars: {}
3296         },
3297
3298         toElement: function(){
3299                 return this.object;
3300         },
3301
3302         initialize: function(path, options){
3303                 this.instance = 'Swiff_' + $time();
3304
3305                 this.setOptions(options);
3306                 options = this.options;
3307                 var id = this.id = options.id || this.instance;
3308                 var container = document.id(options.container);
3309
3310                 Swiff.CallBacks[this.instance] = {};
3311
3312                 var params = options.params, vars = options.vars, callBacks = options.callBacks;
3313                 var properties = $extend({height: options.height, width: options.width}, options.properties);
3314
3315                 var self = this;
3316
3317                 for (var callBack in callBacks){
3318                         Swiff.CallBacks[this.instance][callBack] = (function(option){
3319                                 return function(){
3320                                         return option.apply(self.object, arguments);
3321                                 };
3322                         })(callBacks[callBack]);
3323                         vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
3324                 }
3325
3326                 params.flashVars = Hash.toQueryString(vars);
3327                 if (Browser.Engine.trident){
3328                         properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
3329                         params.movie = path;
3330                 } else {
3331                         properties.type = 'application/x-shockwave-flash';
3332                         properties.data = path;
3333                 }
3334                 var build = '<object id="' + id + '"';
3335                 for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
3336                 build += '>';
3337                 for (var param in params){
3338                         if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
3339                 }
3340                 build += '</object>';
3341                 this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
3342         },
3343
3344         replaces: function(element){
3345                 element = document.id(element, true);
3346                 element.parentNode.replaceChild(this.toElement(), element);
3347                 return this;
3348         },
3349
3350         inject: function(element){
3351                 document.id(element, true).appendChild(this.toElement());
3352                 return this;
3353         },
3354
3355         remote: function(){
3356                 return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments));
3357         }
3358
3359 });
3360
3361 Swiff.CallBacks = {};
3362
3363 Swiff.remote = function(obj, fn){
3364         var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
3365         return eval(rs);
3366 };
3367
3368
3369 /*
3370 ---
3371
3372 script: Fx.js
3373
3374 description: Contains the basic animation logic to be extended by all other Fx Classes.
3375
3376 license: MIT-style license.
3377
3378 requires:
3379 - /Chain
3380 - /Events
3381 - /Options
3382
3383 provides: [Fx]
3384
3385 ...
3386 */
3387
3388 var Fx = new Class({
3389
3390         Implements: [Chain, Events, Options],
3391
3392         options: {
3393                 /*
3394                 onStart: $empty,
3395                 onCancel: $empty,
3396                 onComplete: $empty,
3397                 */
3398                 fps: 50,
3399                 unit: false,
3400                 duration: 500,
3401                 link: 'ignore'
3402         },
3403
3404         initialize: function(options){
3405                 this.subject = this.subject || this;
3406                 this.setOptions(options);
3407                 this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt();
3408                 var wait = this.options.wait;
3409                 if (wait === false) this.options.link = 'cancel';
3410         },
3411
3412         getTransition: function(){
3413                 return function(p){
3414                         return -(Math.cos(Math.PI * p) - 1) / 2;
3415                 };
3416         },
3417
3418         step: function(){
3419                 var time = $time();
3420                 if (time < this.time + this.options.duration){
3421                         var delta = this.transition((time - this.time) / this.options.duration);
3422                         this.set(this.compute(this.from, this.to, delta));
3423                 } else {
3424                         this.set(this.compute(this.from, this.to, 1));
3425                         this.complete();
3426                 }
3427         },
3428
3429         set: function(now){
3430                 return now;
3431         },
3432
3433         compute: function(from, to, delta){
3434                 return Fx.compute(from, to, delta);
3435         },
3436
3437         check: function(){
3438                 if (!this.timer) return true;
3439                 switch (this.options.link){
3440                         case 'cancel': this.cancel(); return true;
3441                         case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
3442                 }
3443                 return false;
3444         },
3445
3446         start: function(from, to){
3447                 if (!this.check(from, to)) return this;
3448                 this.from = from;
3449                 this.to = to;
3450                 this.time = 0;
3451                 this.transition = this.getTransition();
3452                 this.startTimer();
3453                 this.onStart();
3454                 return this;
3455         },
3456
3457         complete: function(){
3458                 if (this.stopTimer()) this.onComplete();
3459                 return this;
3460         },
3461
3462         cancel: function(){
3463                 if (this.stopTimer()) this.onCancel();
3464                 return this;
3465         },
3466
3467         onStart: function(){
3468                 this.fireEvent('start', this.subject);
3469         },
3470
3471         onComplete: function(){
3472                 this.fireEvent('complete', this.subject);
3473                 if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
3474         },
3475
3476         onCancel: function(){
3477                 this.fireEvent('cancel', this.subject).clearChain();
3478         },
3479
3480         pause: function(){
3481                 this.stopTimer();
3482                 return this;
3483         },
3484
3485         resume: function(){
3486                 this.startTimer();
3487                 return this;
3488         },
3489
3490         stopTimer: function(){
3491                 if (!this.timer) return false;
3492                 this.time = $time() - this.time;
3493                 this.timer = $clear(this.timer);
3494                 return true;
3495         },
3496
3497         startTimer: function(){
3498                 if (this.timer) return false;
3499                 this.time = $time() - this.time;
3500                 this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
3501                 return true;
3502         }
3503
3504 });
3505
3506 Fx.compute = function(from, to, delta){
3507         return (to - from) * delta + from;
3508 };
3509
3510 Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
3511
3512
3513 /*
3514 ---
3515
3516 script: Fx.CSS.js
3517
3518 description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
3519
3520 license: MIT-style license.
3521
3522 requires:
3523 - /Fx
3524 - /Element.Style
3525
3526 provides: [Fx.CSS]
3527
3528 ...
3529 */
3530
3531 Fx.CSS = new Class({
3532
3533         Extends: Fx,
3534
3535         //prepares the base from/to object
3536
3537         prepare: function(element, property, values){
3538                 values = $splat(values);
3539                 var values1 = values[1];
3540                 if (!$chk(values1)){
3541                         values[1] = values[0];
3542                         values[0] = element.getStyle(property);
3543                 }
3544                 var parsed = values.map(this.parse);
3545                 return {from: parsed[0], to: parsed[1]};
3546         },
3547
3548         //parses a value into an array
3549
3550         parse: function(value){
3551                 value = $lambda(value)();
3552                 value = (typeof value == 'string') ? value.split(' ') : $splat(value);
3553                 return value.map(function(val){
3554                         val = String(val);
3555                         var found = false;
3556                         Fx.CSS.Parsers.each(function(parser, key){
3557                                 if (found) return;
3558                                 var parsed = parser.parse(val);
3559                                 if ($chk(parsed)) found = {value: parsed, parser: parser};
3560                         });
3561                         found = found || {value: val, parser: Fx.CSS.Parsers.String};
3562                         return found;
3563                 });
3564         },
3565
3566         //computes by a from and to prepared objects, using their parsers.
3567
3568         compute: function(from, to, delta){
3569                 var computed = [];
3570                 (Math.min(from.length, to.length)).times(function(i){
3571                         computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
3572                 });
3573                 computed.$family = {name: 'fx:css:value'};
3574                 return computed;
3575         },
3576
3577         //serves the value as settable
3578
3579         serve: function(value, unit){
3580                 if ($type(value) != 'fx:css:value') value = this.parse(value);
3581                 var returned = [];
3582                 value.each(function(bit){
3583                         returned = returned.concat(bit.parser.serve(bit.value, unit));
3584                 });
3585                 return returned;
3586         },
3587
3588         //renders the change to an element
3589
3590         render: function(element, property, value, unit){
3591                 element.setStyle(property, this.serve(value, unit));
3592         },
3593
3594         //searches inside the page css to find the values for a selector
3595
3596         search: function(selector){
3597                 if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
3598                 var to = {};
3599                 Array.each(document.styleSheets, function(sheet, j){
3600                         var href = sheet.href;
3601                         if (href && href.contains('://') && !href.contains(document.domain)) return;
3602                         var rules = sheet.rules || sheet.cssRules;
3603                         Array.each(rules, function(rule, i){
3604                                 if (!rule.style) return;
3605                                 var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
3606                                         return m.toLowerCase();
3607                                 }) : null;
3608                                 if (!selectorText || !selectorText.test('^' + selector + '$')) return;
3609                                 Element.Styles.each(function(value, style){
3610                                         if (!rule.style[style] || Element.ShortStyles[style]) return;
3611                                         value = String(rule.style[style]);
3612                                         to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value;
3613                                 });
3614                         });
3615                 });
3616                 return Fx.CSS.Cache[selector] = to;
3617         }
3618
3619 });
3620
3621 Fx.CSS.Cache = {};
3622
3623 Fx.CSS.Parsers = new Hash({
3624
3625         Color: {
3626                 parse: function(value){
3627                         if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
3628                         return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
3629                 },
3630                 compute: function(from, to, delta){
3631                         return from.map(function(value, i){
3632                                 return Math.round(Fx.compute(from[i], to[i], delta));
3633                         });
3634                 },
3635                 serve: function(value){
3636                         return value.map(Number);
3637                 }
3638         },
3639
3640         Number: {
3641                 parse: parseFloat,
3642                 compute: Fx.compute,
3643                 serve: function(value, unit){
3644                         return (unit) ? value + unit : value;
3645                 }
3646         },
3647
3648         String: {
3649                 parse: $lambda(false),
3650                 compute: $arguments(1),
3651                 serve: $arguments(0)
3652         }
3653
3654 });
3655
3656
3657 /*
3658 ---
3659
3660 script: Fx.Tween.js
3661
3662 description: Formerly Fx.Style, effect to transition any CSS property for an element.
3663
3664 license: MIT-style license.
3665
3666 requires: 
3667 - /Fx.CSS
3668
3669 provides: [Fx.Tween, Element.fade, Element.highlight]
3670
3671 ...
3672 */
3673
3674 Fx.Tween = new Class({
3675
3676         Extends: Fx.CSS,
3677
3678         initialize: function(element, options){
3679                 this.element = this.subject = document.id(element);
3680                 this.parent(options);
3681         },
3682
3683         set: function(property, now){
3684                 if (arguments.length == 1){
3685                         now = property;
3686                         property = this.property || this.options.property;
3687                 }
3688                 this.render(this.element, property, now, this.options.unit);
3689                 return this;
3690         },
3691
3692         start: function(property, from, to){
3693                 if (!this.check(property, from, to)) return this;
3694                 var args = Array.flatten(arguments);
3695                 this.property = this.options.property || args.shift();
3696                 var parsed = this.prepare(this.element, this.property, args);
3697                 return this.parent(parsed.from, parsed.to);
3698         }
3699
3700 });
3701
3702 Element.Properties.tween = {
3703
3704         set: function(options){
3705                 var tween = this.retrieve('tween');
3706                 if (tween) tween.cancel();
3707                 return this.eliminate('tween').store('tween:options', $extend({link: 'cancel'}, options));
3708         },
3709
3710         get: function(options){
3711                 if (options || !this.retrieve('tween')){
3712                         if (options || !this.retrieve('tween:options')) this.set('tween', options);
3713                         this.store('tween', new Fx.Tween(this, this.retrieve('tween:options')));
3714                 }
3715                 return this.retrieve('tween');
3716         }
3717
3718 };
3719
3720 Element.implement({
3721
3722         tween: function(property, from, to){
3723                 this.get('tween').start(arguments);
3724                 return this;
3725         },
3726
3727         fade: function(how){
3728                 var fade = this.get('tween'), o = 'opacity', toggle;
3729                 how = $pick(how, 'toggle');
3730                 switch (how){
3731                         case 'in': fade.start(o, 1); break;
3732                         case 'out': fade.start(o, 0); break;
3733                         case 'show': fade.set(o, 1); break;
3734                         case 'hide': fade.set(o, 0); break;
3735                         case 'toggle':
3736                                 var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
3737                                 fade.start(o, (flag) ? 0 : 1);
3738                                 this.store('fade:flag', !flag);
3739                                 toggle = true;
3740                         break;
3741                         default: fade.start(o, arguments);
3742                 }
3743                 if (!toggle) this.eliminate('fade:flag');
3744                 return this;
3745         },
3746
3747         highlight: function(start, end){
3748                 if (!end){
3749                         end = this.retrieve('highlight:original', this.getStyle('background-color'));
3750                         end = (end == 'transparent') ? '#fff' : end;
3751                 }
3752                 var tween = this.get('tween');
3753                 tween.start('background-color', start || '#ffff88', end).chain(function(){
3754                         this.setStyle('background-color', this.retrieve('highlight:original'));
3755                         tween.callChain();
3756                 }.bind(this));
3757                 return this;
3758         }
3759
3760 });
3761
3762
3763 /*
3764 ---
3765
3766 script: Fx.Morph.js
3767
3768 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.
3769
3770 license: MIT-style license.
3771
3772 requires:
3773 - /Fx.CSS
3774
3775 provides: [Fx.Morph]
3776
3777 ...
3778 */
3779
3780 Fx.Morph = new Class({
3781
3782         Extends: Fx.CSS,
3783
3784         initialize: function(element, options){
3785                 this.element = this.subject = document.id(element);
3786                 this.parent(options);
3787         },
3788
3789         set: function(now){
3790                 if (typeof now == 'string') now = this.search(now);
3791                 for (var p in now) this.render(this.element, p, now[p], this.options.unit);
3792                 return this;
3793         },
3794
3795         compute: function(from, to, delta){
3796                 var now = {};
3797                 for (var p in from) now[p] = this.parent(from[p], to[p], delta);
3798                 return now;
3799         },
3800
3801         start: function(properties){
3802                 if (!this.check(properties)) return this;
3803                 if (typeof properties == 'string') properties = this.search(properties);
3804                 var from = {}, to = {};
3805                 for (var p in properties){
3806                         var parsed = this.prepare(this.element, p, properties[p]);
3807                         from[p] = parsed.from;
3808                         to[p] = parsed.to;
3809                 }
3810                 return this.parent(from, to);
3811         }
3812
3813 });
3814
3815 Element.Properties.morph = {
3816
3817         set: function(options){
3818                 var morph = this.retrieve('morph');
3819                 if (morph) morph.cancel();
3820                 return this.eliminate('morph').store('morph:options', $extend({link: 'cancel'}, options));
3821         },
3822
3823         get: function(options){
3824                 if (options || !this.retrieve('morph')){
3825                         if (options || !this.retrieve('morph:options')) this.set('morph', options);
3826                         this.store('morph', new Fx.Morph(this, this.retrieve('morph:options')));
3827                 }
3828                 return this.retrieve('morph');
3829         }
3830
3831 };
3832
3833 Element.implement({
3834
3835         morph: function(props){
3836                 this.get('morph').start(props);
3837                 return this;
3838         }
3839
3840 });
3841
3842
3843 /*
3844 ---
3845
3846 script: Fx.Transitions.js
3847
3848 description: Contains a set of advanced transitions to be used with any of the Fx Classes.
3849
3850 license: MIT-style license.
3851
3852 credits:
3853 - Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
3854
3855 requires:
3856 - /Fx
3857
3858 provides: [Fx.Transitions]
3859
3860 ...
3861 */
3862
3863 Fx.implement({
3864
3865         getTransition: function(){
3866                 var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
3867                 if (typeof trans == 'string'){
3868                         var data = trans.split(':');
3869                         trans = Fx.Transitions;
3870                         trans = trans[data[0]] || trans[data[0].capitalize()];
3871                         if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
3872                 }
3873                 return trans;
3874         }
3875
3876 });
3877
3878 Fx.Transition = function(transition, params){
3879         params = $splat(params);
3880         return $extend(transition, {
3881                 easeIn: function(pos){
3882                         return transition(pos, params);
3883                 },
3884                 easeOut: function(pos){
3885                         return 1 - transition(1 - pos, params);
3886                 },
3887                 easeInOut: function(pos){
3888                         return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
3889                 }
3890         });
3891 };
3892
3893 Fx.Transitions = new Hash({
3894
3895         linear: $arguments(0)
3896
3897 });
3898
3899 Fx.Transitions.extend = function(transitions){
3900         for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
3901 };
3902
3903 Fx.Transitions.extend({
3904
3905         Pow: function(p, x){
3906                 return Math.pow(p, x[0] || 6);
3907         },
3908
3909         Expo: function(p){
3910                 return Math.pow(2, 8 * (p - 1));
3911         },
3912
3913         Circ: function(p){
3914                 return 1 - Math.sin(Math.acos(p));
3915         },
3916
3917         Sine: function(p){
3918                 return 1 - Math.sin((1 - p) * Math.PI / 2);
3919         },
3920
3921         Back: function(p, x){
3922                 x = x[0] || 1.618;
3923                 return Math.pow(p, 2) * ((x + 1) * p - x);
3924         },
3925
3926         Bounce: function(p){
3927                 var value;
3928                 for (var a = 0, b = 1; 1; a += b, b /= 2){
3929                         if (p >= (7 - 4 * a) / 11){
3930                                 value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
3931                                 break;
3932                         }
3933                 }
3934                 return value;
3935         },
3936
3937         Elastic: function(p, x){
3938                 return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
3939         }
3940
3941 });
3942
3943 ['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
3944         Fx.Transitions[transition] = new Fx.Transition(function(p){
3945                 return Math.pow(p, [i + 2]);
3946         });
3947 });
3948
3949
3950 /*
3951 ---
3952
3953 script: Request.js
3954
3955 description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
3956
3957 license: MIT-style license.
3958
3959 requires:
3960 - /Element
3961 - /Chain
3962 - /Events
3963 - /Options
3964 - /Browser
3965
3966 provides: [Request]
3967
3968 ...
3969 */
3970
3971 var Request = new Class({
3972
3973         Implements: [Chain, Events, Options],
3974
3975         options: {/*
3976                 onRequest: $empty,
3977                 onComplete: $empty,
3978                 onCancel: $empty,
3979                 onSuccess: $empty,
3980                 onFailure: $empty,
3981                 onException: $empty,*/
3982                 url: '',
3983                 data: '',
3984                 headers: {
3985                         'X-Requested-With': 'XMLHttpRequest',
3986                         'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
3987                 },
3988                 async: true,
3989                 format: false,
3990                 method: 'post',
3991                 link: 'ignore',
3992                 isSuccess: null,
3993                 emulation: true,
3994                 urlEncoded: true,
3995                 encoding: 'utf-8',
3996                 evalScripts: false,
3997                 evalResponse: false,
3998                 noCache: false
3999         },
4000
4001         initialize: function(options){
4002                 this.xhr = new Browser.Request();
4003                 this.setOptions(options);
4004                 this.options.isSuccess = this.options.isSuccess || this.isSuccess;
4005                 this.headers = new Hash(this.options.headers);
4006         },
4007
4008         onStateChange: function(){
4009                 if (this.xhr.readyState != 4 || !this.running) return;
4010                 this.running = false;
4011                 this.status = 0;
4012                 $try(function(){
4013                         this.status = this.xhr.status;
4014                 }.bind(this));
4015                 this.xhr.onreadystatechange = $empty;
4016                 if (this.options.isSuccess.call(this, this.status)){
4017                         this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
4018                         this.success(this.response.text, this.response.xml);
4019                 } else {
4020                         this.response = {text: null, xml: null};
4021                         this.failure();
4022                 }
4023         },
4024
4025         isSuccess: function(){
4026                 return ((this.status >= 200) && (this.status < 300));
4027         },
4028
4029         processScripts: function(text){
4030                 if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
4031                 return text.stripScripts(this.options.evalScripts);
4032         },
4033
4034         success: function(text, xml){
4035                 this.onSuccess(this.processScripts(text), xml);
4036         },
4037
4038         onSuccess: function(){
4039                 this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
4040         },
4041
4042         failure: function(){
4043                 this.onFailure();
4044         },
4045
4046         onFailure: function(){
4047                 this.fireEvent('complete').fireEvent('failure', this.xhr);
4048         },
4049
4050         setHeader: function(name, value){
4051                 this.headers.set(name, value);
4052                 return this;
4053         },
4054
4055         getHeader: function(name){
4056                 return $try(function(){
4057                         return this.xhr.getResponseHeader(name);
4058                 }.bind(this));
4059         },
4060
4061         check: function(){
4062                 if (!this.running) return true;
4063                 switch (this.options.link){
4064                         case 'cancel': this.cancel(); return true;
4065                         case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
4066                 }
4067                 return false;
4068         },
4069
4070         send: function(options){
4071                 if (!this.check(options)) return this;
4072                 this.running = true;
4073
4074                 var type = $type(options);
4075                 if (type == 'string' || type == 'element') options = {data: options};
4076
4077                 var old = this.options;
4078                 options = $extend({data: old.data, url: old.url, method: old.method}, options);
4079                 var data = options.data, url = String(options.url), method = options.method.toLowerCase();
4080
4081                 switch ($type(data)){
4082                         case 'element': data = document.id(data).toQueryString(); break;
4083                         case 'object': case 'hash': data = Hash.toQueryString(data);
4084                 }
4085
4086                 if (this.options.format){
4087                         var format = 'format=' + this.options.format;
4088                         data = (data) ? format + '&' + data : format;
4089                 }
4090
4091                 if (this.options.emulation && !['get', 'post'].contains(method)){
4092                         var _method = '_method=' + method;
4093                         data = (data) ? _method + '&' + data : _method;
4094                         method = 'post';
4095                 }
4096
4097                 if (this.options.urlEncoded && method == 'post'){
4098                         var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
4099                         this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
4100                 }
4101
4102                 if (this.options.noCache){
4103                         var noCache = 'noCache=' + new Date().getTime();
4104                         data = (data) ? noCache + '&' + data : noCache;
4105                 }
4106
4107                 var trimPosition = url.lastIndexOf('/');
4108                 if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
4109
4110                 if (data && method == 'get'){
4111                         url = url + (url.contains('?') ? '&' : '?') + data;
4112                         data = null;
4113                 }
4114
4115                 this.xhr.open(method.toUpperCase(), url, this.options.async);
4116
4117                 this.xhr.onreadystatechange = this.onStateChange.bind(this);
4118
4119                 this.headers.each(function(value, key){
4120                         try {
4121                                 this.xhr.setRequestHeader(key, value);
4122                         } catch (e){
4123                                 this.fireEvent('exception', [key, value]);
4124                         }
4125                 }, this);
4126
4127                 this.fireEvent('request');
4128                 this.xhr.send(data);
4129                 if (!this.options.async) this.onStateChange();
4130                 return this;
4131         },
4132
4133         cancel: function(){
4134                 if (!this.running) return this;
4135                 this.running = false;
4136                 this.xhr.abort();
4137                 this.xhr.onreadystatechange = $empty;
4138                 this.xhr = new Browser.Request();
4139                 this.fireEvent('cancel');
4140                 return this;
4141         }
4142
4143 });
4144
4145 (function(){
4146
4147 var methods = {};
4148 ['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
4149         methods[method] = function(){
4150                 var params = Array.link(arguments, {url: String.type, data: $defined});
4151                 return this.send($extend(params, {method: method}));
4152         };
4153 });
4154
4155 Request.implement(methods);
4156
4157 })();
4158
4159 Element.Properties.send = {
4160
4161         set: function(options){
4162                 var send = this.retrieve('send');
4163                 if (send) send.cancel();
4164                 return this.eliminate('send').store('send:options', $extend({
4165                         data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
4166                 }, options));
4167         },
4168
4169         get: function(options){
4170                 if (options || !this.retrieve('send')){
4171                         if (options || !this.retrieve('send:options')) this.set('send', options);
4172                         this.store('send', new Request(this.retrieve('send:options')));
4173                 }
4174                 return this.retrieve('send');
4175         }
4176
4177 };
4178
4179 Element.implement({
4180
4181         send: function(url){
4182                 var sender = this.get('send');
4183                 sender.send({data: this, url: url || sender.options.url});
4184                 return this;
4185         }
4186
4187 });
4188
4189
4190 /*
4191 ---
4192
4193 script: Request.HTML.js
4194
4195 description: Extends the basic Request Class with additional methods for interacting with HTML responses.
4196
4197 license: MIT-style license.
4198
4199 requires:
4200 - /Request
4201 - /Element
4202
4203 provides: [Request.HTML]
4204
4205 ...
4206 */
4207
4208 Request.HTML = new Class({
4209
4210         Extends: Request,
4211
4212         options: {
4213                 update: false,
4214                 append: false,
4215                 evalScripts: true,
4216                 filter: false
4217         },
4218
4219         processHTML: function(text){
4220                 var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
4221                 text = (match) ? match[1] : text;
4222
4223                 var container = new Element('div');
4224
4225                 return $try(function(){
4226                         var root = '<root>' + text + '</root>', doc;
4227                         if (Browser.Engine.trident){
4228                                 doc = new ActiveXObject('Microsoft.XMLDOM');
4229                                 doc.async = false;
4230                                 doc.loadXML(root);
4231                         } else {
4232                                 doc = new DOMParser().parseFromString(root, 'text/xml');
4233                         }
4234                         root = doc.getElementsByTagName('root')[0];
4235                         if (!root) return null;
4236                         for (var i = 0, k = root.childNodes.length; i < k; i++){
4237                                 var child = Element.clone(root.childNodes[i], true, true);
4238                                 if (child) container.grab(child);
4239                         }
4240                         return container;
4241                 }) || container.set('html', text);
4242         },
4243
4244         success: function(text){
4245                 var options = this.options, response = this.response;
4246
4247                 response.html = text.stripScripts(function(script){
4248                         response.javascript = script;
4249                 });
4250
4251                 var temp = this.processHTML(response.html);
4252
4253                 response.tree = temp.childNodes;
4254                 response.elements = temp.getElements('*');
4255
4256                 if (options.filter) response.tree = response.elements.filter(options.filter);
4257                 if (options.update) document.id(options.update).empty().set('html', response.html);
4258                 else if (options.append) document.id(options.append).adopt(temp.getChildren());
4259                 if (options.evalScripts) $exec(response.javascript);
4260
4261                 this.onSuccess(response.tree, response.elements, response.html, response.javascript);
4262         }
4263
4264 });
4265
4266 Element.Properties.load = {
4267
4268         set: function(options){
4269                 var load = this.retrieve('load');
4270                 if (load) load.cancel();
4271                 return this.eliminate('load').store('load:options', $extend({data: this, link: 'cancel', update: this, method: 'get'}, options));
4272         },
4273
4274         get: function(options){
4275                 if (options || ! this.retrieve('load')){
4276                         if (options || !this.retrieve('load:options')) this.set('load', options);
4277                         this.store('load', new Request.HTML(this.retrieve('load:options')));
4278                 }
4279                 return this.retrieve('load');
4280         }
4281
4282 };
4283
4284 Element.implement({
4285
4286         load: function(){
4287                 this.get('load').send(Array.link(arguments, {data: Object.type, url: String.type}));
4288                 return this;
4289         }
4290
4291 });
4292
4293
4294 /*
4295 ---
4296
4297 script: Request.JSON.js
4298
4299 description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
4300
4301 license: MIT-style license.
4302
4303 requires:
4304 - /Request JSON
4305
4306 provides: [Request.HTML]
4307
4308 ...
4309 */
4310
4311 Request.JSON = new Class({
4312
4313         Extends: Request,
4314
4315         options: {
4316                 secure: true
4317         },
4318
4319         initialize: function(options){
4320                 this.parent(options);
4321                 this.headers.extend({'Accept': 'application/json', 'X-Request': 'JSON'});
4322         },
4323
4324         success: function(text){
4325                 this.response.json = JSON.decode(text, this.options.secure);
4326                 this.onSuccess(this.response.json, text);
4327         }
4328
4329 });
4330 //MooTools More, <http://mootools.net/more>. Copyright (c) 2006-2009 Aaron Newton <http://clientcide.com/>, Valerio Proietti <http://mad4milk.net> & the MooTools team <http://mootools.net/developers>, MIT Style License.
4331
4332 /*
4333 ---
4334
4335 script: More.js
4336
4337 description: MooTools More
4338
4339 license: MIT-style license
4340
4341 authors:
4342 - Guillermo Rauch
4343 - Thomas Aylott
4344 - Scott Kyle
4345
4346 requires:
4347 - core:1.2.4/MooTools
4348
4349 provides: [MooTools.More]
4350
4351 ...
4352 */
4353
4354 MooTools.More = {
4355         'version': '1.2.4.2',
4356         'build': 'bd5a93c0913cce25917c48cbdacde568e15e02ef'
4357 };
4358
4359 /*
4360 ---
4361
4362 script: Fx.Elements.js
4363
4364 description: Effect to change any number of CSS properties of any number of Elements.
4365
4366 license: MIT-style license
4367
4368 authors:
4369 - Valerio Proietti
4370
4371 requires:
4372 - core:1.2.4/Fx.CSS
4373 - /MooTools.More
4374
4375 provides: [Fx.Elements]
4376
4377 ...
4378 */
4379
4380 Fx.Elements = new Class({
4381
4382         Extends: Fx.CSS,
4383
4384         initialize: function(elements, options){
4385                 this.elements = this.subject = $$(elements);
4386                 this.parent(options);
4387         },
4388
4389         compute: function(from, to, delta){
4390                 var now = {};
4391                 for (var i in from){
4392                         var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
4393                         for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
4394                 }
4395                 return now;
4396         },
4397
4398         set: function(now){
4399                 for (var i in now){
4400                         var iNow = now[i];
4401                         for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
4402                 }
4403                 return this;
4404         },
4405
4406         start: function(obj){
4407                 if (!this.check(obj)) return this;
4408                 var from = {}, to = {};
4409                 for (var i in obj){
4410                         var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
4411                         for (var p in iProps){
4412                                 var parsed = this.prepare(this.elements[i], p, iProps[p]);
4413                                 iFrom[p] = parsed.from;
4414                                 iTo[p] = parsed.to;
4415                         }
4416                 }
4417                 return this.parent(from, to);
4418         }
4419
4420 });
4421
4422 /*
4423 ---
4424
4425 script: Fx.Slide.js
4426
4427 description: Effect to slide an element in and out of view.
4428
4429 license: MIT-style license
4430
4431 authors:
4432 - Valerio Proietti
4433
4434 requires:
4435 - core:1.2.4/Fx Element.Style
4436 - /MooTools.More
4437
4438 provides: [Fx.Slide]
4439
4440 ...
4441 */
4442
4443 Fx.Slide = new Class({
4444
4445         Extends: Fx,
4446
4447         options: {
4448                 mode: 'vertical',
4449                 hideOverflow: true
4450         },
4451
4452         initialize: function(element, options){
4453                 this.addEvent('complete', function(){
4454                         this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);
4455                         if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper);
4456                 }, true);
4457                 this.element = this.subject = document.id(element);
4458                 this.parent(options);
4459                 var wrapper = this.element.retrieve('wrapper');
4460                 var styles = this.element.getStyles('margin', 'position', 'overflow');
4461                 if (this.options.hideOverflow) styles = $extend(styles, {overflow: 'hidden'});
4462                 this.wrapper = wrapper || new Element('div', {
4463                         styles: styles
4464                 }).wraps(this.element);
4465                 this.element.store('wrapper', this.wrapper).setStyle('margin', 0);
4466                 this.now = [];
4467                 this.open = true;
4468         },
4469
4470         vertical: function(){
4471                 this.margin = 'margin-top';
4472                 this.layout = 'height';
4473                 this.offset = this.element.offsetHeight;
4474         },
4475
4476         horizontal: function(){
4477                 this.margin = 'margin-left';
4478                 this.layout = 'width';
4479                 this.offset = this.element.offsetWidth;
4480         },
4481
4482         set: function(now){
4483                 this.element.setStyle(this.margin, now[0]);
4484                 this.wrapper.setStyle(this.layout, now[1]);
4485                 return this;
4486         },
4487
4488         compute: function(from, to, delta){
4489                 return [0, 1].map(function(i){
4490                         return Fx.compute(from[i], to[i], delta);
4491                 });
4492         },
4493
4494         start: function(how, mode){
4495                 if (!this.check(how, mode)) return this;
4496                 this[mode || this.options.mode]();
4497                 var margin = this.element.getStyle(this.margin).toInt();
4498                 var layout = this.wrapper.getStyle(this.layout).toInt();
4499                 var caseIn = [[margin, layout], [0, this.offset]];
4500                 var caseOut = [[margin, layout], [-this.offset, 0]];
4501                 var start;
4502                 switch (how){
4503                         case 'in': start = caseIn; break;
4504                         case 'out': start = caseOut; break;
4505                         case 'toggle': start = (layout == 0) ? caseIn : caseOut;
4506                 }
4507                 return this.parent(start[0], start[1]);
4508         },
4509
4510         slideIn: function(mode){
4511                 return this.start('in', mode);
4512         },
4513
4514         slideOut: function(mode){
4515                 return this.start('out', mode);
4516         },
4517
4518         hide: function(mode){
4519                 this[mode || this.options.mode]();
4520                 this.open = false;
4521                 return this.set([-this.offset, 0]);
4522         },
4523
4524         show: function(mode){
4525                 this[mode || this.options.mode]();
4526                 this.open = true;
4527                 return this.set([0, this.offset]);
4528         },
4529
4530         toggle: function(mode){
4531                 return this.start('toggle', mode);
4532         }
4533
4534 });
4535
4536 Element.Properties.slide = {
4537
4538         set: function(options){
4539                 var slide = this.retrieve('slide');
4540                 if (slide) slide.cancel();
4541                 return this.eliminate('slide').store('slide:options', $extend({link: 'cancel'}, options));
4542         },
4543
4544         get: function(options){
4545                 if (options || !this.retrieve('slide')){
4546                         if (options || !this.retrieve('slide:options')) this.set('slide', options);
4547                         this.store('slide', new Fx.Slide(this, this.retrieve('slide:options')));
4548                 }
4549                 return this.retrieve('slide');
4550         }
4551
4552 };
4553
4554 Element.implement({
4555
4556         slide: function(how, mode){
4557                 how = how || 'toggle';
4558                 var slide = this.get('slide'), toggle;
4559                 switch (how){
4560                         case 'hide': slide.hide(mode); break;
4561                         case 'show': slide.show(mode); break;
4562                         case 'toggle':
4563                                 var flag = this.retrieve('slide:flag', slide.open);
4564                                 slide[flag ? 'slideOut' : 'slideIn'](mode);
4565                                 this.store('slide:flag', !flag);
4566                                 toggle = true;
4567                         break;
4568                         default: slide.start(how, mode);
4569                 }
4570                 if (!toggle) this.eliminate('slide:flag');
4571                 return this;
4572         }
4573
4574 });
4575
4576
4577 /*
4578 ---
4579
4580 script: Assets.js
4581
4582 description: Provides methods to dynamically load JavaScript, CSS, and Image files into the document.
4583
4584 license: MIT-style license
4585
4586 authors:
4587 - Valerio Proietti
4588
4589 requires:
4590 - core:1.2.4/Element.Event
4591 - /MooTools.More
4592
4593 provides: [Assets]
4594
4595 ...
4596 */
4597
4598 var Asset = {
4599
4600         javascript: function(source, properties){
4601                 properties = $extend({
4602                         onload: $empty,
4603                         document: document,
4604                         check: $lambda(true)
4605                 }, properties);
4606
4607                 var script = new Element('script', {src: source, type: 'text/javascript'});
4608
4609                 var load = properties.onload.bind(script), 
4610                         check = properties.check, 
4611                         doc = properties.document;
4612                 delete properties.onload;
4613                 delete properties.check;
4614                 delete properties.document;
4615
4616                 script.addEvents({
4617                         load: load,
4618                         readystatechange: function(){
4619                                 if (['loaded', 'complete'].contains(this.readyState)) load();
4620                         }
4621                 }).set(properties);
4622
4623                 if (Browser.Engine.webkit419) var checker = (function(){
4624                         if (!$try(check)) return;
4625                         $clear(checker);
4626                         load();
4627                 }).periodical(50);
4628
4629                 return script.inject(doc.head);
4630         },
4631
4632         css: function(source, properties){
4633                 return new Element('link', $merge({
4634                         rel: 'stylesheet',
4635                         media: 'screen',
4636                         type: 'text/css',
4637                         href: source
4638                 }, properties)).inject(document.head);
4639         },
4640
4641         image: function(source, properties){
4642                 properties = $merge({
4643                         onload: $empty,
4644                         onabort: $empty,
4645                         onerror: $empty
4646                 }, properties);
4647                 var image = new Image();
4648                 var element = document.id(image) || new Element('img');
4649                 ['load', 'abort', 'error'].each(function(name){
4650                         var type = 'on' + name;
4651                         var event = properties[type];
4652                         delete properties[type];
4653                         image[type] = function(){
4654                                 if (!image) return;
4655                                 if (!element.parentNode){
4656                                         element.width = image.width;
4657                                         element.height = image.height;
4658                                 }
4659                                 image = image.onload = image.onabort = image.onerror = null;
4660                                 event.delay(1, element, element);
4661                                 element.fireEvent(name, element, 1);
4662                         };
4663                 });
4664                 image.src = element.src = source;
4665                 if (image && image.complete) image.onload.delay(1);
4666                 return element.set(properties);
4667         },
4668
4669         images: function(sources, options){
4670                 options = $merge({
4671                         onComplete: $empty,
4672                         onProgress: $empty,
4673                         onError: $empty,
4674                         properties: {}
4675                 }, options);
4676                 sources = $splat(sources);
4677                 var images = [];
4678                 var counter = 0;
4679                 return new Elements(sources.map(function(source){
4680                         return Asset.image(source, $extend(options.properties, {
4681                                 onload: function(){
4682                                         options.onProgress.call(this, counter, sources.indexOf(source));
4683                                         counter++;
4684                                         if (counter == sources.length) options.onComplete();
4685                                 },
4686                                 onerror: function(){
4687                                         options.onError.call(this, counter, sources.indexOf(source));
4688                                         counter++;
4689                                         if (counter == sources.length) options.onComplete();
4690                                 }
4691                         }));
4692                 }));
4693         }
4694
4695 };