Lang file : Update lang french file
[ldapsaisie.git] / trunk / includes / js / mootools-core.js
1 /*
2 Script: Core.js
3         MooTools - My Object Oriented JavaScript Tools.
4
5 License:
6         MIT-style license.
7
8 Copyright:
9         Copyright (c) 2006-2007 [Valerio Proietti](http://mad4milk.net/).
10
11 Code & Documentation:
12         [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
19 var MooTools = {
20         'version': '1.2.0',
21         'build': ''
22 };
23       
24 var Native = function(options){
25         options = options || {};
26
27         var afterImplement = options.afterImplement || function(){};
28         var generics = options.generics;
29         generics = (generics !== false);
30         var legacy = options.legacy;
31         var initialize = options.initialize;
32         var protect = options.protect;
33         var name = options.name;
34
35         var object = initialize || legacy;
36
37         object.constructor = Native;
38         object.$family = {name: 'native'};
39         if (legacy && initialize) object.prototype = legacy.prototype;
40         object.prototype.constructor = object;
41
42         if (name){
43                 var family = name.toLowerCase();
44                 object.prototype.$family = {name: family};
45                 Native.typize(object, family);
46         }
47
48         var add = function(obj, name, method, force){
49                 if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
50                 if (generics) Native.genericize(obj, name, protect);
51                 afterImplement.call(obj, name, method);
52                 return obj;
53         };
54         
55         object.implement = function(a1, a2, a3){
56                 if (typeof a1 == 'string') return add(this, a1, a2, a3);
57                 for (var p in a1) add(this, p, a1[p], a2);
58                 return this;
59         };
60         
61         object.alias = function(a1, a2, a3){
62                 if (typeof a1 == 'string'){
63                         a1 = this.prototype[a1];
64                         if (a1) add(this, a2, a1, a3);
65                 } else {
66                         for (var a in a1) this.alias(a, a1[a], a2);
67                 }
68                 return this;
69         };
70
71         return object;
72 };
73
74 Native.implement = function(objects, properties){
75         for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
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.typize = function(object, family){
86         if (!object.type) object.type = function(item){
87                 return ($type(item) === family);
88         };
89 };
90
91 Native.alias = function(objects, a1, a2, a3){
92         for (var i = 0, j = objects.length; i < j; i++) objects[i].alias(a1, a2, a3);
93 };
94
95 (function(objects){
96         for (var name in objects) Native.typize(objects[name], name);
97 })({'boolean': Boolean, 'native': Native, 'object': Object});
98
99 (function(objects){
100         for (var name in objects) new Native({name: name, initialize: objects[name], protect: true});
101 })({'String': String, 'Function': Function, 'Number': Number, 'Array': Array, 'RegExp': RegExp, 'Date': Date});
102
103 (function(object, methods){
104         for (var i = methods.length; i--; i) Native.genericize(object, methods[i], true);
105         return arguments.callee;
106 })
107 (Array, ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'toString', 'valueOf', 'indexOf', 'lastIndexOf'])
108 (String, ['charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'replace', 'search', 'slice', 'split', 'substr', 'substring', 'toLowerCase', 'toUpperCase', 'valueOf']);
109
110 function $chk(obj){
111         return !!(obj || obj === 0);
112 };
113
114 function $clear(timer){
115         clearTimeout(timer);
116         clearInterval(timer);
117         return null;
118 };
119
120 function $defined(obj){
121         return (obj != undefined);
122 };
123
124 function $empty(){};
125
126 function $arguments(i){
127         return function(){
128                 return arguments[i];
129         };
130 };
131
132 function $lambda(value){
133         return (typeof value == 'function') ? value : function(){
134                 return value;
135         };
136 };
137
138 function $extend(original, extended){
139         for (var key in (extended || {})) original[key] = extended[key];
140         return original;
141 };
142
143 function $unlink(object){
144         var unlinked;
145         
146         switch ($type(object)){
147                 case 'object':
148                         unlinked = {};
149                         for (var p in object) unlinked[p] = $unlink(object[p]);
150                 break;
151                 case 'hash':
152                         unlinked = $unlink(object.getClean());
153                 break;
154                 case 'array':
155                         unlinked = [];
156                         for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
157                 break;
158                 default: return object;
159         }
160         
161         return unlinked;
162 };
163
164 function $merge(){
165         var mix = {};
166         for (var i = 0, l = arguments.length; i < l; i++){
167                 var object = arguments[i];
168                 if ($type(object) != 'object') continue;
169                 for (var key in object){
170                         var op = object[key], mp = mix[key];
171                         mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $merge(mp, op) : $unlink(op);
172                 }
173         }
174         return mix;
175 };
176
177 function $pick(){
178         for (var i = 0, l = arguments.length; i < l; i++){
179                 if (arguments[i] != undefined) return arguments[i];
180         }
181         return null;
182 };
183
184 function $random(min, max){
185         return Math.floor(Math.random() * (max - min + 1) + min);
186 };
187
188 function $splat(obj){
189         var type = $type(obj);
190         return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
191 };
192
193 var $time = Date.now || function(){
194         return new Date().getTime();
195 };
196
197 function $try(){
198         for (var i = 0, l = arguments.length; i < l; i++){
199                 try {
200                         return arguments[i]();
201                 } catch(e){}
202         }
203         return null;
204 };
205
206 function $type(obj){
207         if (obj == undefined) return false;
208         if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
209         if (obj.nodeName){
210                 switch (obj.nodeType){
211                         case 1: return 'element';
212                         case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
213                 }
214         } else if (typeof obj.length == 'number'){
215                 if (obj.callee) return 'arguments';
216                 else if (obj.item) return 'collection';
217         }
218         return typeof obj;
219 };
220
221 var Hash = new Native({
222
223         name: 'Hash',
224
225         initialize: function(object){
226                 if ($type(object) == 'hash') object = $unlink(object.getClean());
227                 for (var key in object) this[key] = object[key];
228                 return this;
229         }
230
231 });
232
233 Hash.implement({
234         
235         getLength: function(){
236                 var length = 0;
237                 for (var key in this){
238                         if (this.hasOwnProperty(key)) length++;
239                 }
240                 return length;
241         },
242
243         forEach: function(fn, bind){
244                 for (var key in this){
245                         if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
246                 }
247         },
248         
249         getClean: function(){
250                 var clean = {};
251                 for (var key in this){
252                         if (this.hasOwnProperty(key)) clean[key] = this[key];
253                 }
254                 return clean;
255         }
256
257 });
258
259 Hash.alias('forEach', 'each');
260
261 function $H(object){
262         return new Hash(object);
263 };
264
265 Array.implement({
266
267         forEach: function(fn, bind){
268                 for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
269         }
270
271 });
272
273 Array.alias('forEach', 'each');
274
275 function $A(iterable){
276         if (iterable.item){
277                 var array = [];
278                 for (var i = 0, l = iterable.length; i < l; i++) array[i] = iterable[i];
279                 return array;
280         }
281         return Array.prototype.slice.call(iterable);
282 };
283
284 function $each(iterable, fn, bind){
285         var type = $type(iterable);
286         ((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
287 };
288
289
290 /*
291 Script: Browser.js
292         The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.
293
294 License:
295         MIT-style license.
296 */
297
298 var Browser = new Hash({
299         Engine: {name: 'unknown', version: ''},
300         Platform: {name: (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},
301         Features: {xpath: !!(document.evaluate), air: !!(window.runtime)},
302         Plugins: {}
303 });
304
305 if (window.opera) Browser.Engine = {name: 'presto', version: (document.getElementsByClassName) ? 950 : 925};
306 else if (window.ActiveXObject) Browser.Engine = {name: 'trident', version: (window.XMLHttpRequest) ? 5 : 4};
307 else if (!navigator.taintEnabled) Browser.Engine = {name: 'webkit', version: (Browser.Features.xpath) ? 420 : 419};
308 else if (document.getBoxObjectFor != null) Browser.Engine = {name: 'gecko', version: (document.getElementsByClassName) ? 19 : 18};
309 Browser.Engine[Browser.Engine.name] = Browser.Engine[Browser.Engine.name + Browser.Engine.version] = true;
310
311 if (window.orientation != undefined) Browser.Platform.name = 'ipod';
312
313 Browser.Platform[Browser.Platform.name] = true;
314
315 Browser.Request = function(){
316         return $try(function(){
317                 return new XMLHttpRequest();
318         }, function(){
319                 return new ActiveXObject('MSXML2.XMLHTTP');
320         });
321 };
322
323 Browser.Features.xhr = !!(Browser.Request());
324
325 Browser.Plugins.Flash = (function(){
326         var version = ($try(function(){
327                 return navigator.plugins['Shockwave Flash'].description;
328         }, function(){
329                 return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
330         }) || '0 r0').match(/\d+/g);
331         return {version: parseInt(version[0] || 0 + '.' + version[1] || 0), build: parseInt(version[2] || 0)};
332 })();
333
334 function $exec(text){
335         if (!text) return text;
336         if (window.execScript){
337                 window.execScript(text);
338         } else {
339                 var script = document.createElement('script');
340                 script.setAttribute('type', 'text/javascript');
341                 script.text = text;
342                 document.head.appendChild(script);
343                 document.head.removeChild(script);
344         }
345         return text;
346 };
347
348 Native.UID = 1;
349
350 var $uid = (Browser.Engine.trident) ? function(item){
351         return (item.uid || (item.uid = [Native.UID++]))[0];
352 } : function(item){
353         return item.uid || (item.uid = Native.UID++);
354 };
355
356 var Window = new Native({
357
358         name: 'Window',
359
360         legacy: (Browser.Engine.trident) ? null: window.Window,
361
362         initialize: function(win){
363                 $uid(win);
364                 if (!win.Element){
365                         win.Element = $empty;
366                         if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2
367                         win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {};
368                 }
369                 return $extend(win, Window.Prototype);
370         },
371
372         afterImplement: function(property, value){
373                 window[property] = Window.Prototype[property] = value;
374         }
375
376 });
377
378 Window.Prototype = {$family: {name: 'window'}};
379
380 new Window(window);
381
382 var Document = new Native({
383
384         name: 'Document',
385
386         legacy: (Browser.Engine.trident) ? null: window.Document,
387
388         initialize: function(doc){
389                 $uid(doc);
390                 doc.head = doc.getElementsByTagName('head')[0];
391                 doc.html = doc.getElementsByTagName('html')[0];
392                 doc.window = doc.defaultView || doc.parentWindow;
393                 if (Browser.Engine.trident4) $try(function(){
394                         doc.execCommand("BackgroundImageCache", false, true);
395                 });
396                 return $extend(doc, Document.Prototype);
397         },
398
399         afterImplement: function(property, value){
400                 document[property] = Document.Prototype[property] = value;
401         }
402
403 });
404
405 Document.Prototype = {$family: {name: 'document'}};
406
407 new Document(document);
408
409 /*
410 Script: Array.js
411         Contains Array Prototypes like copy, each, contains, and remove.
412
413 License:
414         MIT-style license.
415 */
416
417 Array.implement({
418
419         every: function(fn, bind){
420                 for (var i = 0, l = this.length; i < l; i++){
421                         if (!fn.call(bind, this[i], i, this)) return false;
422                 }
423                 return true;
424         },
425
426         filter: function(fn, bind){
427                 var results = [];
428                 for (var i = 0, l = this.length; i < l; i++){
429                         if (fn.call(bind, this[i], i, this)) results.push(this[i]);
430                 }
431                 return results;
432         },
433         
434         clean: function() {
435                 return this.filter($defined);
436         },
437
438         indexOf: function(item, from){
439                 var len = this.length;
440                 for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
441                         if (this[i] === item) return i;
442                 }
443                 return -1;
444         },
445
446         map: function(fn, bind){
447                 var results = [];
448                 for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this);
449                 return results;
450         },
451
452         some: function(fn, bind){
453                 for (var i = 0, l = this.length; i < l; i++){
454                         if (fn.call(bind, this[i], i, this)) return true;
455                 }
456                 return false;
457         },
458
459         associate: function(keys){
460                 var obj = {}, length = Math.min(this.length, keys.length);
461                 for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
462                 return obj;
463         },
464
465         link: function(object){
466                 var result = {};
467                 for (var i = 0, l = this.length; i < l; i++){
468                         for (var key in object){
469                                 if (object[key](this[i])){
470                                         result[key] = this[i];
471                                         delete object[key];
472                                         break;
473                                 }
474                         }
475                 }
476                 return result;
477         },
478
479         contains: function(item, from){
480                 return this.indexOf(item, from) != -1;
481         },
482
483         extend: function(array){
484                 for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
485                 return this;
486         },
487
488         getLast: function(){
489                 return (this.length) ? this[this.length - 1] : null;
490         },
491
492         getRandom: function(){
493                 return (this.length) ? this[$random(0, this.length - 1)] : null;
494         },
495
496         include: function(item){
497                 if (!this.contains(item)) this.push(item);
498                 return this;
499         },
500
501         combine: function(array){
502                 for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
503                 return this;
504         },
505
506         erase: function(item){
507                 for (var i = this.length; i--; i){
508                         if (this[i] === item) this.splice(i, 1);
509                 }
510                 return this;
511         },
512
513         empty: function(){
514                 this.length = 0;
515                 return this;
516         },
517
518         flatten: function(){
519                 var array = [];
520                 for (var i = 0, l = this.length; i < l; i++){
521                         var type = $type(this[i]);
522                         if (!type) continue;
523                         array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]);
524                 }
525                 return array;
526         },
527
528         hexToRgb: function(array){
529                 if (this.length != 3) return null;
530                 var rgb = this.map(function(value){
531                         if (value.length == 1) value += value;
532                         return value.toInt(16);
533                 });
534                 return (array) ? rgb : 'rgb(' + rgb + ')';
535         },
536
537         rgbToHex: function(array){
538                 if (this.length < 3) return null;
539                 if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
540                 var hex = [];
541                 for (var i = 0; i < 3; i++){
542                         var bit = (this[i] - 0).toString(16);
543                         hex.push((bit.length == 1) ? '0' + bit : bit);
544                 }
545                 return (array) ? hex : '#' + hex.join('');
546         }
547
548 });
549
550 /*\r
551 Script: Function.js\r
552         Contains Function Prototypes like create, bind, pass, and delay.\r
553 \r
554 License:\r
555         MIT-style license.\r
556 */\r
557 \r
558 Function.implement({\r
559 \r
560         extend: function(properties){\r
561                 for (var property in properties) this[property] = properties[property];\r
562                 return this;\r
563         },\r
564 \r
565         create: function(options){\r
566                 var self = this;\r
567                 options = options || {};\r
568                 return function(event){\r
569                         var args = options.arguments;\r
570                         args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);\r
571                         if (options.event) args = [event || window.event].extend(args);\r
572                         var returns = function(){\r
573                                 return self.apply(options.bind || null, args);\r
574                         };\r
575                         if (options.delay) return setTimeout(returns, options.delay);\r
576                         if (options.periodical) return setInterval(returns, options.periodical);\r
577                         if (options.attempt) return $try(returns);\r
578                         return returns();\r
579                 };\r
580         },\r
581 \r
582         pass: function(args, bind){\r
583                 return this.create({arguments: args, bind: bind});\r
584         },\r
585 \r
586         attempt: function(args, bind){\r
587                 return this.create({arguments: args, bind: bind, attempt: true})();\r
588         },\r
589 \r
590         bind: function(bind, args){\r
591                 return this.create({bind: bind, arguments: args});\r
592         },\r
593 \r
594         bindWithEvent: function(bind, args){\r
595                 return this.create({bind: bind, event: true, arguments: args});\r
596         },\r
597 \r
598         delay: function(delay, bind, args){\r
599                 return this.create({delay: delay, bind: bind, arguments: args})();\r
600         },\r
601 \r
602         periodical: function(interval, bind, args){\r
603                 return this.create({periodical: interval, bind: bind, arguments: args})();\r
604         },\r
605 \r
606         run: function(args, bind){\r
607                 return this.apply(bind, $splat(args));\r
608         }\r
609 \r
610 });
611
612 /*
613 Script: Number.js
614         Contains Number Prototypes like limit, round, times, and ceil.
615
616 License:
617         MIT-style license.
618 */
619
620 Number.implement({
621
622         limit: function(min, max){
623                 return Math.min(max, Math.max(min, this));
624         },
625
626         round: function(precision){
627                 precision = Math.pow(10, precision || 0);
628                 return Math.round(this * precision) / precision;
629         },
630
631         times: function(fn, bind){
632                 for (var i = 0; i < this; i++) fn.call(bind, i, this);
633         },
634
635         toFloat: function(){
636                 return parseFloat(this);
637         },
638
639         toInt: function(base){
640                 return parseInt(this, base || 10);
641         }
642
643 });
644
645 Number.alias('times', 'each');
646
647 (function(math){
648         var methods = {};
649         math.each(function(name){
650                 if (!Number[name]) methods[name] = function(){
651                         return Math[name].apply(null, [this].concat($A(arguments)));
652                 };
653         });
654         Number.implement(methods);
655 })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
656
657 /*
658 Script: String.js
659         Contains String Prototypes like camelCase, capitalize, test, and toInt.
660
661 License:
662         MIT-style license.
663 */
664
665 String.implement({
666
667         test: function(regex, params){
668                 return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
669         },
670
671         contains: function(string, separator){
672                 return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
673         },
674
675         trim: function(){
676                 return this.replace(/^\s+|\s+$/g, '');
677         },
678
679         clean: function(){
680                 return this.replace(/\s+/g, ' ').trim();
681         },
682
683         camelCase: function(){
684                 return this.replace(/-\D/g, function(match){
685                         return match.charAt(1).toUpperCase();
686                 });
687         },
688
689         hyphenate: function(){
690                 return this.replace(/[A-Z]/g, function(match){
691                         return ('-' + match.charAt(0).toLowerCase());
692                 });
693         },
694
695         capitalize: function(){
696                 return this.replace(/\b[a-z]/g, function(match){
697                         return match.toUpperCase();
698                 });
699         },
700
701         escapeRegExp: function(){
702                 return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
703         },
704
705         toInt: function(base){
706                 return parseInt(this, base || 10);
707         },
708
709         toFloat: function(){
710                 return parseFloat(this);
711         },
712
713         hexToRgb: function(array){
714                 var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
715                 return (hex) ? hex.slice(1).hexToRgb(array) : null;
716         },
717
718         rgbToHex: function(array){
719                 var rgb = this.match(/\d{1,3}/g);
720                 return (rgb) ? rgb.rgbToHex(array) : null;
721         },
722
723         stripScripts: function(option){
724                 var scripts = '';
725                 var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
726                         scripts += arguments[1] + '\n';
727                         return '';
728                 });
729                 if (option === true) $exec(scripts);
730                 else if ($type(option) == 'function') option(scripts, text);
731                 return text;
732         },
733
734         substitute: function(object, regexp){
735                 return this.replace(regexp || (/\\?\{([^}]+)\}/g), function(match, name){
736                         if (match.charAt(0) == '\\') return match.slice(1);
737                         return (object[name] != undefined) ? object[name] : '';
738                 });
739         }
740
741 });
742
743 /*
744 Script: Hash.js
745         Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.
746
747 License:
748         MIT-style license.
749 */
750
751 Hash.implement({
752
753         has: Object.prototype.hasOwnProperty,
754
755         keyOf: function(value){
756                 for (var key in this){
757                         if (this.hasOwnProperty(key) && this[key] === value) return key;
758                 }
759                 return null;
760         },
761
762         hasValue: function(value){
763                 return (Hash.keyOf(this, value) !== null);
764         },
765
766         extend: function(properties){
767                 Hash.each(properties, function(value, key){
768                         Hash.set(this, key, value);
769                 }, this);
770                 return this;
771         },
772
773         combine: function(properties){
774                 Hash.each(properties, function(value, key){
775                         Hash.include(this, key, value);
776                 }, this);
777                 return this;
778         },
779
780         erase: function(key){
781                 if (this.hasOwnProperty(key)) delete this[key];
782                 return this;
783         },
784
785         get: function(key){
786                 return (this.hasOwnProperty(key)) ? this[key] : null;
787         },
788
789         set: function(key, value){
790                 if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
791                 return this;
792         },
793
794         empty: function(){
795                 Hash.each(this, function(value, key){
796                         delete this[key];
797                 }, this);
798                 return this;
799         },
800
801         include: function(key, value){
802                 var k = this[key];
803                 if (k == undefined) this[key] = value;
804                 return this;
805         },
806
807         map: function(fn, bind){
808                 var results = new Hash;
809                 Hash.each(this, function(value, key){
810                         results.set(key, fn.call(bind, value, key, this));
811                 }, this);
812                 return results;
813         },
814
815         filter: function(fn, bind){
816                 var results = new Hash;
817                 Hash.each(this, function(value, key){
818                         if (fn.call(bind, value, key, this)) results.set(key, value);
819                 }, this);
820                 return results;
821         },
822
823         every: function(fn, bind){
824                 for (var key in this){
825                         if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
826                 }
827                 return true;
828         },
829
830         some: function(fn, bind){
831                 for (var key in this){
832                         if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
833                 }
834                 return false;
835         },
836
837         getKeys: function(){
838                 var keys = [];
839                 Hash.each(this, function(value, key){
840                         keys.push(key);
841                 });
842                 return keys;
843         },
844
845         getValues: function(){
846                 var values = [];
847                 Hash.each(this, function(value){
848                         values.push(value);
849                 });
850                 return values;
851         },
852         
853         toQueryString: function(base){
854                 var queryString = [];
855                 Hash.each(this, function(value, key){
856                         if (base) key = base + '[' + key + ']';
857                         var result;
858                         switch ($type(value)){
859                                 case 'object': result = Hash.toQueryString(value, key); break;
860                                 case 'array':
861                                         var qs = {};
862                                         value.each(function(val, i){
863                                                 qs[i] = val;
864                                         });
865                                         result = Hash.toQueryString(qs, key);
866                                 break;
867                                 default: result = key + '=' + encodeURIComponent(value);
868                         }
869                         if (value != undefined) queryString.push(result);
870                 });
871                 
872                 return queryString.join('&');
873         }
874
875 });
876
877 Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});
878
879 /*
880 Script: Event.js
881         Contains the Event Native, to make the event object completely crossbrowser.
882
883 License:
884         MIT-style license.
885 */
886
887 var Event = new Native({
888
889         name: 'Event',
890
891         initialize: function(event, win){
892                 win = win || window;
893                 var doc = win.document;
894                 event = event || win.event;
895                 if (event.$extended) return event;
896                 this.$extended = true;
897                 var type = event.type;
898                 var target = event.target || event.srcElement;
899                 while (target && target.nodeType == 3) target = target.parentNode;
900                 
901                 if (type.test(/key/)){
902                         var code = event.which || event.keyCode;
903                         var key = Event.Keys.keyOf(code);
904                         if (type == 'keydown'){
905                                 var fKey = code - 111;
906                                 if (fKey > 0 && fKey < 13) key = 'f' + fKey;
907                         }
908                         key = key || String.fromCharCode(code).toLowerCase();
909                 } else if (type.match(/(click|mouse|menu)/i)){
910                         doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
911                         var page = {
912                                 x: event.pageX || event.clientX + doc.scrollLeft,
913                                 y: event.pageY || event.clientY + doc.scrollTop
914                         };
915                         var client = {
916                                 x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
917                                 y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
918                         };
919                         if (type.match(/DOMMouseScroll|mousewheel/)){
920                                 var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
921                         }
922                         var rightClick = (event.which == 3) || (event.button == 2);
923                         var related = null;
924                         if (type.match(/over|out/)){
925                                 switch (type){
926                                         case 'mouseover': related = event.relatedTarget || event.fromElement; break;
927                                         case 'mouseout': related = event.relatedTarget || event.toElement;
928                                 }
929                                 if (!(function(){
930                                         while (related && related.nodeType == 3) related = related.parentNode;
931                                         return true;
932                                 }).create({attempt: Browser.Engine.gecko})()) related = false;
933                         }
934                 }
935
936                 return $extend(this, {
937                         event: event,
938                         type: type,
939                         
940                         page: page,
941                         client: client,
942                         rightClick: rightClick,
943                         
944                         wheel: wheel,
945                         
946                         relatedTarget: related,
947                         target: target,
948                         
949                         code: code,
950                         key: key,
951                         
952                         shift: event.shiftKey,
953                         control: event.ctrlKey,
954                         alt: event.altKey,
955                         meta: event.metaKey
956                 });
957         }
958
959 });
960
961 Event.Keys = new Hash({
962         'enter': 13,
963         'up': 38,
964         'down': 40,
965         'left': 37,
966         'right': 39,
967         'esc': 27,
968         'space': 32,
969         'backspace': 8,
970         'tab': 9,
971         'delete': 46
972 });
973
974 Event.implement({
975
976         stop: function(){
977                 return this.stopPropagation().preventDefault();
978         },
979
980         stopPropagation: function(){
981                 if (this.event.stopPropagation) this.event.stopPropagation();
982                 else this.event.cancelBubble = true;
983                 return this;
984         },
985
986         preventDefault: function(){
987                 if (this.event.preventDefault) this.event.preventDefault();
988                 else this.event.returnValue = false;
989                 return this;
990         }
991
992 });
993
994 /*
995 Script: Class.js
996         Contains the Class Function for easily creating, extending, and implementing reusable Classes.
997
998 License:
999         MIT-style license.
1000 */
1001
1002 var Class = new Native({
1003
1004         name: 'Class',
1005
1006         initialize: function(properties){
1007                 properties = properties || {};
1008                 var klass = function(empty){
1009                         for (var key in this) this[key] = $unlink(this[key]);
1010                         for (var mutator in Class.Mutators){
1011                                 if (!this[mutator]) continue;
1012                                 Class.Mutators[mutator](this, this[mutator]);
1013                                 delete this[mutator];
1014                         }
1015
1016                         this.constructor = klass;
1017                         if (empty === $empty) return this;
1018                         
1019                         var self = (this.initialize) ? this.initialize.apply(this, arguments) : this;
1020                         if (this.options && this.options.initialize) this.options.initialize.call(this);
1021                         return self;
1022                 };
1023
1024                 $extend(klass, this);
1025                 klass.constructor = Class;
1026                 klass.prototype = properties;
1027                 return klass;
1028         }
1029
1030 });
1031
1032 Class.implement({
1033
1034         implement: function(){
1035                 Class.Mutators.Implements(this.prototype, Array.slice(arguments));
1036                 return this;
1037         }
1038
1039 });
1040
1041 Class.Mutators = {
1042   
1043   Implements: function(self, klasses){
1044         $splat(klasses).each(function(klass){
1045                 $extend(self, ($type(klass) == 'class') ? new klass($empty) : klass);
1046         });
1047   },
1048   
1049   Extends: function(self, klass){
1050         var instance = new klass($empty);
1051         delete instance.parent;
1052         delete instance.parentOf;
1053
1054         for (var key in instance){
1055                 var current = self[key], previous = instance[key];
1056                 if (current == undefined){
1057                         self[key] = previous;
1058                         continue;
1059                 }
1060
1061                 var ctype = $type(current), ptype = $type(previous);
1062                 if (ctype != ptype) continue;
1063
1064                 switch (ctype){
1065                         case 'function': 
1066                                 // this code will be only executed if the current browser does not support function.caller (currently only opera).
1067                                 // we replace the function code with brute force. Not pretty, but it will only be executed if function.caller is not supported.
1068
1069                                 if (!arguments.callee.caller) self[key] = eval('(' + String(current).replace(/\bthis\.parent\(\s*(\))?/g, function(full, close){
1070                                         return 'arguments.callee._parent_.call(this' + (close || ', ');
1071                                 }) + ')');
1072
1073                                 // end "opera" code
1074                                 self[key]._parent_ = previous;
1075                           break;
1076                         case 'object': self[key] = $merge(previous, current);
1077                 }
1078
1079         }
1080
1081         self.parent = function(){
1082                 return arguments.callee.caller._parent_.apply(this, arguments);
1083         };
1084
1085         self.parentOf = function(descendant){
1086                 return descendant._parent_.apply(this, Array.slice(arguments, 1));
1087         };
1088   }
1089   
1090 };
1091
1092
1093 /*
1094 Script: Class.Extras.js
1095         Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
1096
1097 License:
1098         MIT-style license.
1099 */
1100
1101 var Chain = new Class({
1102
1103         chain: function(){
1104                 this.$chain = (this.$chain || []).extend(arguments);
1105                 return this;
1106         },
1107
1108         callChain: function(){
1109                 return (this.$chain && this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
1110         },
1111
1112         clearChain: function(){
1113                 if (this.$chain) this.$chain.empty();
1114                 return this;
1115         }
1116
1117 });
1118
1119 var Events = new Class({
1120
1121         addEvent: function(type, fn, internal){
1122                 type = Events.removeOn(type);
1123                 if (fn != $empty){
1124                         this.$events = this.$events || {};
1125                         this.$events[type] = this.$events[type] || [];
1126                         this.$events[type].include(fn);
1127                         if (internal) fn.internal = true;
1128                 }
1129                 return this;
1130         },
1131
1132         addEvents: function(events){
1133                 for (var type in events) this.addEvent(type, events[type]);
1134                 return this;
1135         },
1136
1137         fireEvent: function(type, args, delay){
1138                 type = Events.removeOn(type);
1139                 if (!this.$events || !this.$events[type]) return this;
1140                 this.$events[type].each(function(fn){
1141                         fn.create({'bind': this, 'delay': delay, 'arguments': args})();
1142                 }, this);
1143                 return this;
1144         },
1145
1146         removeEvent: function(type, fn){
1147                 type = Events.removeOn(type);
1148                 if (!this.$events || !this.$events[type]) return this;
1149                 if (!fn.internal) this.$events[type].erase(fn);
1150                 return this;
1151         },
1152
1153         removeEvents: function(type){
1154                 for (var e in this.$events){
1155                         if (type && type != e) continue;
1156                         var fns = this.$events[e];
1157                         for (var i = fns.length; i--; i) this.removeEvent(e, fns[i]);
1158                 }
1159                 return this;
1160         }
1161
1162 });
1163
1164 Events.removeOn = function(string){
1165         return string.replace(/^on([A-Z])/, function(full, first) {
1166                 return first.toLowerCase();
1167         });
1168 };
1169
1170 var Options = new Class({
1171
1172         setOptions: function(){
1173                 this.options = $merge.run([this.options].extend(arguments));
1174                 if (!this.addEvent) return this;
1175                 for (var option in this.options){
1176                         if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
1177                         this.addEvent(option, this.options[option]);
1178                         delete this.options[option];
1179                 }
1180                 return this;
1181         }
1182
1183 });
1184
1185 /*
1186 Script: Element.js
1187         One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser,
1188         time-saver methods to let you easily work with HTML Elements.
1189
1190 License:
1191         MIT-style license.
1192 */
1193
1194 Document.implement({
1195
1196         newElement: function(tag, props){
1197                 if (Browser.Engine.trident && props){
1198                         ['name', 'type', 'checked'].each(function(attribute){
1199                                 if (!props[attribute]) return;
1200                                 tag += ' ' + attribute + '="' + props[attribute] + '"';
1201                                 if (attribute != 'checked') delete props[attribute];
1202                         });
1203                         tag = '<' + tag + '>';
1204                 }
1205                 return $.element(this.createElement(tag)).set(props);
1206         },
1207
1208         newTextNode: function(text){
1209                 return this.createTextNode(text);
1210         },
1211
1212         getDocument: function(){
1213                 return this;
1214         },
1215
1216         getWindow: function(){
1217                 return this.defaultView || this.parentWindow;
1218         },
1219
1220         purge: function(){
1221                 var elements = this.getElementsByTagName('*');
1222                 for (var i = 0, l = elements.length; i < l; i++) Browser.freeMem(elements[i]);
1223         }
1224
1225 });
1226
1227 var Element = new Native({
1228
1229         name: 'Element',
1230
1231         legacy: window.Element,
1232
1233         initialize: function(tag, props){
1234                 var konstructor = Element.Constructors.get(tag);
1235                 if (konstructor) return konstructor(props);
1236                 if (typeof tag == 'string') return document.newElement(tag, props);
1237                 return $(tag).set(props);
1238         },
1239
1240         afterImplement: function(key, value){
1241                 if (!Array[key]) Elements.implement(key, Elements.multi(key));
1242                 Element.Prototype[key] = value;
1243         }
1244
1245 });
1246
1247 Element.Prototype = {$family: {name: 'element'}};
1248
1249 Element.Constructors = new Hash;
1250
1251 var IFrame = new Native({
1252
1253         name: 'IFrame',
1254
1255         generics: false,
1256
1257         initialize: function(){
1258                 var params = Array.link(arguments, {properties: Object.type, iframe: $defined});
1259                 var props = params.properties || {};
1260                 var iframe = $(params.iframe) || false;
1261                 var onload = props.onload || $empty;
1262                 delete props.onload;
1263                 props.id = props.name = $pick(props.id, props.name, iframe.id, iframe.name, 'IFrame_' + $time());
1264                 iframe = new Element(iframe || 'iframe', props);
1265                 var onFrameLoad = function(){
1266                         var host = $try(function(){
1267                                 return iframe.contentWindow.location.host;
1268                         });
1269                         if (host && host == window.location.host){
1270                                 var win = new Window(iframe.contentWindow);
1271                                 var doc = new Document(iframe.contentWindow.document);
1272                                 $extend(win.Element.prototype, Element.Prototype);
1273                         }
1274                         onload.call(iframe.contentWindow, iframe.contentWindow.document);
1275                 };
1276                 (!window.frames[props.id]) ? iframe.addListener('load', onFrameLoad) : onFrameLoad();
1277                 return iframe;
1278         }
1279
1280 });
1281
1282 var Elements = new Native({
1283
1284         initialize: function(elements, options){
1285                 options = $extend({ddup: true, cash: true}, options);
1286                 elements = elements || [];
1287                 if (options.ddup || options.cash){
1288                         var uniques = {}, returned = [];
1289                         for (var i = 0, l = elements.length; i < l; i++){
1290                                 var el = $.element(elements[i], !options.cash);
1291                                 if (options.ddup){
1292                                         if (uniques[el.uid]) continue;
1293                                         uniques[el.uid] = true;
1294                                 }
1295                                 returned.push(el);
1296                         }
1297                         elements = returned;
1298                 }
1299                 return (options.cash) ? $extend(elements, this) : elements;
1300         }
1301
1302 });
1303
1304 Elements.implement({
1305
1306         filter: function(filter, bind){
1307                 if (!filter) return this;
1308                 return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){
1309                         return item.match(filter);
1310                 } : filter, bind));
1311         }
1312
1313 });
1314
1315 Elements.multi = function(property){
1316         return function(){
1317                 var items = [];
1318                 var elements = true;
1319                 for (var i = 0, j = this.length; i < j; i++){
1320                         var returns = this[i][property].apply(this[i], arguments);
1321                         items.push(returns);
1322                         if (elements) elements = ($type(returns) == 'element');
1323                 }
1324                 return (elements) ? new Elements(items) : items;
1325         };
1326 };
1327
1328 Window.implement({
1329
1330         $: function(el, nocash){
1331                 if (el && el.$family && el.uid) return el;
1332                 var type = $type(el);
1333                 return ($[type]) ? $[type](el, nocash, this.document) : null;
1334         },
1335
1336         $$: function(selector){
1337                 if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector);
1338                 var elements = [];
1339                 var args = Array.flatten(arguments);
1340                 for (var i = 0, l = args.length; i < l; i++){
1341                         var item = args[i];
1342                         switch ($type(item)){
1343                                 case 'element': item = [item]; break;
1344                                 case 'string': item = this.document.getElements(item, true); break;
1345                                 default: item = false;
1346                         }
1347                         if (item) elements.extend(item);
1348                 }
1349                 return new Elements(elements);
1350         },
1351
1352         getDocument: function(){
1353                 return this.document;
1354         },
1355
1356         getWindow: function(){
1357                 return this;
1358         }
1359
1360 });
1361
1362 $.string = function(id, nocash, doc){
1363         id = doc.getElementById(id);
1364         return (id) ? $.element(id, nocash) : null;
1365 };
1366
1367 $.element = function(el, nocash){
1368         $uid(el);
1369         if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
1370                 var proto = Element.Prototype;
1371                 for (var p in proto) el[p] = proto[p];
1372         };
1373         return el;
1374 };
1375
1376 $.object = function(obj, nocash, doc){
1377         if (obj.toElement) return $.element(obj.toElement(doc), nocash);
1378         return null;
1379 };
1380
1381 $.textnode = $.whitespace = $.window = $.document = $arguments(0);
1382
1383 Native.implement([Element, Document], {
1384
1385         getElement: function(selector, nocash){
1386                 return $(this.getElements(selector, true)[0] || null, nocash);
1387         },
1388
1389         getElements: function(tags, nocash){
1390                 tags = tags.split(',');
1391                 var elements = [];
1392                 var ddup = (tags.length > 1);
1393                 tags.each(function(tag){
1394                         var partial = this.getElementsByTagName(tag.trim());
1395                         (ddup) ? elements.extend(partial) : elements = partial;
1396                 }, this);
1397                 return new Elements(elements, {ddup: ddup, cash: !nocash});
1398         }
1399
1400 });
1401
1402 Element.Storage = {
1403
1404         get: function(uid){
1405                 return (this[uid] || (this[uid] = {}));
1406         }
1407
1408 };
1409
1410 Element.Inserters = new Hash({
1411
1412         before: function(context, element){
1413                 if (element.parentNode) element.parentNode.insertBefore(context, element);
1414         },
1415
1416         after: function(context, element){
1417                 if (!element.parentNode) return;
1418                 var next = element.nextSibling;
1419                 (next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
1420         },
1421
1422         bottom: function(context, element){
1423                 element.appendChild(context);
1424         },
1425
1426         top: function(context, element){
1427                 var first = element.firstChild;
1428                 (first) ? element.insertBefore(context, first) : element.appendChild(context);
1429         }
1430
1431 });
1432
1433 Element.Inserters.inside = Element.Inserters.bottom;
1434
1435 Element.Inserters.each(function(value, key){
1436
1437         var Key = key.capitalize();
1438
1439         Element.implement('inject' + Key, function(el){
1440                 value(this, $(el, true));
1441                 return this;
1442         });
1443
1444         Element.implement('grab' + Key, function(el){
1445                 value($(el, true), this);
1446                 return this;
1447         });
1448
1449 });
1450
1451 Element.implement({
1452
1453         getDocument: function(){
1454                 return this.ownerDocument;
1455         },
1456
1457         getWindow: function(){
1458                 return this.ownerDocument.getWindow();
1459         },
1460
1461         getElementById: function(id, nocash){
1462                 var el = this.ownerDocument.getElementById(id);
1463                 if (!el) return null;
1464                 for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
1465                         if (!parent) return null;
1466                 }
1467                 return $.element(el, nocash);
1468         },
1469
1470         set: function(prop, value){
1471                 switch ($type(prop)){
1472                         case 'object':
1473                                 for (var p in prop) this.set(p, prop[p]);
1474                                 break;
1475                         case 'string':
1476                                 var property = Element.Properties.get(prop);
1477                                 (property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
1478                 }
1479                 return this;
1480         },
1481
1482         get: function(prop){
1483                 var property = Element.Properties.get(prop);
1484                 return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
1485         },
1486
1487         erase: function(prop){
1488                 var property = Element.Properties.get(prop);
1489                 (property && property.erase) ? property.erase.apply(this, Array.slice(arguments, 1)) : this.removeProperty(prop);
1490                 return this;
1491         },
1492
1493         match: function(tag){
1494                 return (!tag || Element.get(this, 'tag') == tag);
1495         },
1496
1497         inject: function(el, where){
1498                 Element.Inserters.get(where || 'bottom')(this, $(el, true));
1499                 return this;
1500         },
1501
1502         wraps: function(el, where){
1503                 el = $(el, true);
1504                 return this.replaces(el).grab(el, where);
1505         },
1506
1507         grab: function(el, where){
1508                 Element.Inserters.get(where || 'bottom')($(el, true), this);
1509                 return this;
1510         },
1511
1512         appendText: function(text, where){
1513                 return this.grab(this.getDocument().newTextNode(text), where);
1514         },
1515
1516         adopt: function(){
1517                 Array.flatten(arguments).each(function(element){
1518                         element = $(element, true);
1519                         if (element) this.appendChild(element);
1520                 }, this);
1521                 return this;
1522         },
1523
1524         dispose: function(){
1525                 return (this.parentNode) ? this.parentNode.removeChild(this) : this;
1526         },
1527
1528         clone: function(contents, keepid){
1529                 switch ($type(this)){
1530                         case 'element':
1531                                 var attributes = {};
1532                                 for (var j = 0, l = this.attributes.length; j < l; j++){
1533                                         var attribute = this.attributes[j], key = attribute.nodeName.toLowerCase();
1534                                         if (Browser.Engine.trident && (/input/i).test(this.tagName) && (/width|height/).test(key)) continue;
1535                                         var value = (key == 'style' && this.style) ? this.style.cssText : attribute.nodeValue;
1536                                         if (!$chk(value) || key == 'uid' || (key == 'id' && !keepid)) continue;
1537                                         if (value != 'inherit' && ['string', 'number'].contains($type(value))) attributes[key] = value;
1538                                 }
1539                                 var element = new Element(this.nodeName.toLowerCase(), attributes);
1540                                 if (contents !== false){
1541                                         for (var i = 0, k = this.childNodes.length; i < k; i++){
1542                                                 var child = Element.clone(this.childNodes[i], true, keepid);
1543                                                 if (child) element.grab(child);
1544                                         }
1545                                 }
1546                                 return element;
1547                         case 'textnode': return document.newTextNode(this.nodeValue);
1548                 }
1549                 return null;
1550         },
1551
1552         replaces: function(el){
1553                 el = $(el, true);
1554                 el.parentNode.replaceChild(this, el);
1555                 return this;
1556         },
1557
1558         hasClass: function(className){
1559                 return this.className.contains(className, ' ');
1560         },
1561
1562         addClass: function(className){
1563                 if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
1564                 return this;
1565         },
1566
1567         removeClass: function(className){
1568                 this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1').clean();
1569                 return this;
1570         },
1571
1572         toggleClass: function(className){
1573                 return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
1574         },
1575
1576         getComputedStyle: function(property){
1577                 if (this.currentStyle) return this.currentStyle[property.camelCase()];
1578                 var computed = this.getWindow().getComputedStyle(this, null);
1579                 return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
1580         },
1581
1582         empty: function(){
1583                 $A(this.childNodes).each(function(node){
1584                         Browser.freeMem(node);
1585                         Element.empty(node);
1586                         Element.dispose(node);
1587                 }, this);
1588                 return this;
1589         },
1590
1591         destroy: function(){
1592                 Browser.freeMem(this.empty().dispose());
1593                 return null;
1594         },
1595
1596         getSelected: function(){
1597                 return new Elements($A(this.options).filter(function(option){
1598                         return option.selected;
1599                 }));
1600         },
1601
1602         toQueryString: function(){
1603                 var queryString = [];
1604                 this.getElements('input, select, textarea').each(function(el){
1605                         if (!el.name || el.disabled) return;
1606                         var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
1607                                 return opt.value;
1608                         }) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
1609                         $splat(value).each(function(val){
1610                                 if (val) queryString.push(el.name + '=' + encodeURIComponent(val));
1611                         });
1612                 });
1613                 return queryString.join('&');
1614         },
1615
1616         getProperty: function(attribute){
1617                 var EA = Element.Attributes, key = EA.Props[attribute];
1618                 var value = (key) ? this[key] : this.getAttribute(attribute, 2);
1619                 return (EA.Bools[attribute]) ? !!value : (key) ? value : value || null;
1620         },
1621
1622         getProperties: function(){
1623                 var args = $A(arguments);
1624                 return args.map(function(attr){
1625                         return this.getProperty(attr);
1626                 }, this).associate(args);
1627         },
1628
1629         setProperty: function(attribute, value){
1630                 var EA = Element.Attributes, key = EA.Props[attribute], hasValue = $defined(value);
1631                 if (key && EA.Bools[attribute]) value = (value || !hasValue) ? true : false;
1632                 else if (!hasValue) return this.removeProperty(attribute);
1633                 (key) ? this[key] = value : this.setAttribute(attribute, value);
1634                 return this;
1635         },
1636
1637         setProperties: function(attributes){
1638                 for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
1639                 return this;
1640         },
1641
1642         removeProperty: function(attribute){
1643                 var EA = Element.Attributes, key = EA.Props[attribute], isBool = (key && EA.Bools[attribute]);
1644                 (key) ? this[key] = (isBool) ? false : '' : this.removeAttribute(attribute);
1645                 return this;
1646         },
1647
1648         removeProperties: function(){
1649                 Array.each(arguments, this.removeProperty, this);
1650                 return this;
1651         }
1652
1653 });
1654
1655 (function(){
1656
1657 var walk = function(element, walk, start, match, all, nocash){
1658         var el = element[start || walk];
1659         var elements = [];
1660         while (el){
1661                 if (el.nodeType == 1 && (!match || Element.match(el, match))){
1662                         elements.push(el);
1663                         if (!all) break;
1664                 }
1665                 el = el[walk];
1666         }
1667         return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : $(elements[0], nocash);
1668 };
1669
1670 Element.implement({
1671
1672         getPrevious: function(match, nocash){
1673                 return walk(this, 'previousSibling', null, match, false, nocash);
1674         },
1675
1676         getAllPrevious: function(match, nocash){
1677                 return walk(this, 'previousSibling', null, match, true, nocash);
1678         },
1679
1680         getNext: function(match, nocash){
1681                 return walk(this, 'nextSibling', null, match, false, nocash);
1682         },
1683
1684         getAllNext: function(match, nocash){
1685                 return walk(this, 'nextSibling', null, match, true, nocash);
1686         },
1687
1688         getFirst: function(match, nocash){
1689                 return walk(this, 'nextSibling', 'firstChild', match, false, nocash);
1690         },
1691
1692         getLast: function(match, nocash){
1693                 return walk(this, 'previousSibling', 'lastChild', match, false, nocash);
1694         },
1695
1696         getParent: function(match, nocash){
1697                 return walk(this, 'parentNode', null, match, false, nocash);
1698         },
1699
1700         getParents: function(match, nocash){
1701                 return walk(this, 'parentNode', null, match, true, nocash);
1702         },
1703
1704         getChildren: function(match, nocash){
1705                 return walk(this, 'nextSibling', 'firstChild', match, true, nocash);
1706         },
1707
1708         hasChild: function(el){
1709                 el = $(el, true);
1710                 return (!!el && $A(this.getElementsByTagName(el.tagName)).contains(el));
1711         }
1712
1713 });
1714
1715 })();
1716
1717 Element.Properties = new Hash;
1718
1719 Element.Properties.style = {
1720
1721         set: function(style){
1722                 this.style.cssText = style;
1723         },
1724
1725         get: function(){
1726                 return this.style.cssText;
1727         },
1728
1729         erase: function(){
1730                 this.style.cssText = '';
1731         }
1732
1733 };
1734
1735 Element.Properties.tag = {get: function(){
1736         return this.tagName.toLowerCase();
1737 }};
1738
1739 Element.Properties.href = {get: function(){
1740         return (!this.href) ? null : this.href.replace(new RegExp('^' + document.location.protocol + '\/\/' + document.location.host), '');
1741 }};
1742
1743 Element.Properties.html = {set: function(){
1744         return this.innerHTML = Array.flatten(arguments).join('');
1745 }};
1746
1747 Native.implement([Element, Window, Document], {
1748
1749         addListener: function(type, fn){
1750                 if (this.addEventListener) this.addEventListener(type, fn, false);
1751                 else this.attachEvent('on' + type, fn);
1752                 return this;
1753         },
1754
1755         removeListener: function(type, fn){
1756                 if (this.removeEventListener) this.removeEventListener(type, fn, false);
1757                 else this.detachEvent('on' + type, fn);
1758                 return this;
1759         },
1760
1761         retrieve: function(property, dflt){
1762                 var storage = Element.Storage.get(this.uid);
1763                 var prop = storage[property];
1764                 if ($defined(dflt) && !$defined(prop)) prop = storage[property] = dflt;
1765                 return $pick(prop);
1766         },
1767
1768         store: function(property, value){
1769                 var storage = Element.Storage.get(this.uid);
1770                 storage[property] = value;
1771                 return this;
1772         },
1773
1774         eliminate: function(property){
1775                 var storage = Element.Storage.get(this.uid);
1776                 delete storage[property];
1777                 return this;
1778         }
1779
1780 });
1781
1782 Element.Attributes = new Hash({
1783         Props: {'html': 'innerHTML', 'class': 'className', 'for': 'htmlFor', 'text': (Browser.Engine.trident) ? 'innerText' : 'textContent'},
1784         Bools: ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'],
1785         Camels: ['value', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap']
1786 });
1787
1788 Browser.freeMem = function(item){
1789         if (!item) return;
1790         if (Browser.Engine.trident && (/object/i).test(item.tagName)){
1791                 for (var p in item){
1792                         if (typeof item[p] == 'function') item[p] = $empty;
1793                 }
1794                 Element.dispose(item);
1795         }
1796         if (item.uid && item.removeEvents) item.removeEvents();
1797 };
1798
1799 (function(EA){
1800
1801         var EAB = EA.Bools, EAC = EA.Camels;
1802         EA.Bools = EAB = EAB.associate(EAB);
1803         Hash.extend(Hash.combine(EA.Props, EAB), EAC.associate(EAC.map(function(v){
1804                 return v.toLowerCase();
1805         })));
1806         EA.erase('Camels');
1807
1808 })(Element.Attributes);
1809
1810 window.addListener('unload', function(){
1811         window.removeListener('unload', arguments.callee);
1812         document.purge();
1813         if (Browser.Engine.trident) CollectGarbage();
1814 });
1815
1816 /*\r
1817 Script: Element.Event.js\r
1818         Contains Element methods for dealing with events, and custom Events.\r
1819 \r
1820 License:\r
1821         MIT-style license.\r
1822 */\r
1823 \r
1824 Element.Properties.events = {set: function(events){\r
1825         this.addEvents(events);\r
1826 }};\r
1827 \r
1828 Native.implement([Element, Window, Document], {\r
1829 \r
1830         addEvent: function(type, fn){\r
1831                 var events = this.retrieve('events', {});\r
1832                 events[type] = events[type] || {'keys': [], 'values': []};\r
1833                 if (events[type].keys.contains(fn)) return this;\r
1834                 events[type].keys.push(fn);\r
1835                 var realType = type, custom = Element.Events.get(type), condition = fn, self = this;\r
1836                 if (custom){\r
1837                         if (custom.onAdd) custom.onAdd.call(this, fn);\r
1838                         if (custom.condition){\r
1839                                 condition = function(event){\r
1840                                         if (custom.condition.call(this, event)) return fn.call(this, event);\r
1841                                         return false;\r
1842                                 };\r
1843                         }\r
1844                         realType = custom.base || realType;\r
1845                 }\r
1846                 var defn = function(){\r
1847                         return fn.call(self);\r
1848                 };\r
1849                 var nativeEvent = Element.NativeEvents[realType] || 0;\r
1850                 if (nativeEvent){\r
1851                         if (nativeEvent == 2){\r
1852                                 defn = function(event){\r
1853                                         event = new Event(event, self.getWindow());\r
1854                                         if (condition.call(self, event) === false) event.stop();\r
1855                                 };\r
1856                         }\r
1857                         this.addListener(realType, defn);\r
1858                 }\r
1859                 events[type].values.push(defn);\r
1860                 return this;\r
1861         },\r
1862 \r
1863         removeEvent: function(type, fn){\r
1864                 var events = this.retrieve('events');\r
1865                 if (!events || !events[type]) return this;\r
1866                 var pos = events[type].keys.indexOf(fn);\r
1867                 if (pos == -1) return this;\r
1868                 var key = events[type].keys.splice(pos, 1)[0];\r
1869                 var value = events[type].values.splice(pos, 1)[0];\r
1870                 var custom = Element.Events.get(type);\r
1871                 if (custom){\r
1872                         if (custom.onRemove) custom.onRemove.call(this, fn);\r
1873                         type = custom.base || type;\r
1874                 }\r
1875                 return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this;\r
1876         },\r
1877 \r
1878         addEvents: function(events){\r
1879                 for (var event in events) this.addEvent(event, events[event]);\r
1880                 return this;\r
1881         },\r
1882 \r
1883         removeEvents: function(type){\r
1884                 var events = this.retrieve('events');\r
1885                 if (!events) return this;\r
1886                 if (!type){\r
1887                         for (var evType in events) this.removeEvents(evType);\r
1888                         events = null;\r
1889                 } else if (events[type]){\r
1890                         while (events[type].keys[0]) this.removeEvent(type, events[type].keys[0]);\r
1891                         events[type] = null;\r
1892                 }\r
1893                 return this;\r
1894         },\r
1895 \r
1896         fireEvent: function(type, args, delay){\r
1897                 var events = this.retrieve('events');\r
1898                 if (!events || !events[type]) return this;\r
1899                 events[type].keys.each(function(fn){\r
1900                         fn.create({'bind': this, 'delay': delay, 'arguments': args})();\r
1901                 }, this);\r
1902                 return this;\r
1903         },\r
1904 \r
1905         cloneEvents: function(from, type){\r
1906                 from = $(from);\r
1907                 var fevents = from.retrieve('events');\r
1908                 if (!fevents) return this;\r
1909                 if (!type){\r
1910                         for (var evType in fevents) this.cloneEvents(from, evType);\r
1911                 } else if (fevents[type]){\r
1912                         fevents[type].keys.each(function(fn){\r
1913                                 this.addEvent(type, fn);\r
1914                         }, this);\r
1915                 }\r
1916                 return this;\r
1917         }\r
1918 \r
1919 });\r
1920 \r
1921 Element.NativeEvents = {\r
1922         click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons\r
1923         mousewheel: 2, DOMMouseScroll: 2, //mouse wheel\r
1924         mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement\r
1925         keydown: 2, keypress: 2, keyup: 2, //keyboard\r
1926         focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements\r
1927         load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window\r
1928         error: 1, abort: 1, scroll: 1 //misc\r
1929 };\r
1930 \r
1931 (function(){\r
1932 \r
1933 var $check = function(event){\r
1934         var related = event.relatedTarget;\r
1935         if (related == undefined) return true;\r
1936         if (related === false) return false;\r
1937         return ($type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related));\r
1938 };\r
1939 \r
1940 Element.Events = new Hash({\r
1941 \r
1942         mouseenter: {\r
1943                 base: 'mouseover',\r
1944                 condition: $check\r
1945         },\r
1946 \r
1947         mouseleave: {\r
1948                 base: 'mouseout',\r
1949                 condition: $check\r
1950         },\r
1951 \r
1952         mousewheel: {\r
1953                 base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel'\r
1954         }\r
1955 \r
1956 });\r
1957 \r
1958 })();
1959
1960 /*
1961 Script: Element.Style.js
1962         Contains methods for interacting with the styles of Elements in a fashionable way.
1963
1964 License:
1965         MIT-style license.
1966 */
1967
1968 Element.Properties.styles = {set: function(styles){
1969         this.setStyles(styles);
1970 }};
1971
1972 Element.Properties.opacity = {
1973
1974         set: function(opacity, novisibility){
1975                 if (!novisibility){
1976                         if (opacity == 0){
1977                                 if (this.style.visibility != 'hidden') this.style.visibility = 'hidden';
1978                         } else {
1979                                 if (this.style.visibility != 'visible') this.style.visibility = 'visible';
1980                         }
1981                 }
1982                 if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
1983                 if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
1984                 this.style.opacity = opacity;
1985                 this.store('opacity', opacity);
1986         },
1987
1988         get: function(){
1989                 return this.retrieve('opacity', 1);
1990         }
1991
1992 };
1993
1994 Element.implement({
1995         
1996         setOpacity: function(value){
1997                 return this.set('opacity', value, true);
1998         },
1999         
2000         getOpacity: function(){
2001                 return this.get('opacity');
2002         },
2003
2004         setStyle: function(property, value){
2005                 switch (property){
2006                         case 'opacity': return this.set('opacity', parseFloat(value));
2007                         case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
2008                 }
2009                 property = property.camelCase();
2010                 if ($type(value) != 'string'){
2011                         var map = (Element.Styles.get(property) || '@').split(' ');
2012                         value = $splat(value).map(function(val, i){
2013                                 if (!map[i]) return '';
2014                                 return ($type(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
2015                         }).join(' ');
2016                 } else if (value == String(Number(value))){
2017                         value = Math.round(value);
2018                 }
2019                 this.style[property] = value;
2020                 return this;
2021         },
2022
2023         getStyle: function(property){
2024                 switch (property){
2025                         case 'opacity': return this.get('opacity');
2026                         case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
2027                 }
2028                 property = property.camelCase();
2029                 var result = this.style[property];
2030                 if (!$chk(result)){
2031                         result = [];
2032                         for (var style in Element.ShortStyles){
2033                                 if (property != style) continue;
2034                                 for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
2035                                 return result.join(' ');
2036                         }
2037                         result = this.getComputedStyle(property);
2038                 }
2039                 if (result){
2040                         result = String(result);
2041                         var color = result.match(/rgba?\([\d\s,]+\)/);
2042                         if (color) result = result.replace(color[0], color[0].rgbToHex());
2043                 }
2044                 if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result)))){
2045                         if (property.test(/^(height|width)$/)){
2046                                 var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
2047                                 values.each(function(value){
2048                                         size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
2049                                 }, this);
2050                                 return this['offset' + property.capitalize()] - size + 'px';
2051                         }
2052                         if (Browser.Engine.presto && String(result).test('px')) return result;
2053                         if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
2054                 }
2055                 return result;
2056         },
2057
2058         setStyles: function(styles){
2059                 for (var style in styles) this.setStyle(style, styles[style]);
2060                 return this;
2061         },
2062
2063         getStyles: function(){
2064                 var result = {};
2065                 Array.each(arguments, function(key){
2066                         result[key] = this.getStyle(key);
2067                 }, this);
2068                 return result;
2069         }
2070
2071 });
2072
2073 Element.Styles = new Hash({
2074         left: '@px', top: '@px', bottom: '@px', right: '@px',
2075         width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
2076         backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
2077         fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
2078         margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
2079         borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
2080         zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
2081 });
2082
2083 Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
2084
2085 ['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
2086         var Short = Element.ShortStyles;
2087         var All = Element.Styles;
2088         ['margin', 'padding'].each(function(style){
2089                 var sd = style + direction;
2090                 Short[style][sd] = All[sd] = '@px';
2091         });
2092         var bd = 'border' + direction;
2093         Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
2094         var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
2095         Short[bd] = {};
2096         Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
2097         Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
2098         Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
2099 });
2100
2101
2102 /*
2103 Script: Element.Dimensions.js
2104         Contains methods to work with size, scroll, or positioning of Elements and the window object.
2105
2106 License:
2107         MIT-style license.
2108
2109 Credits:
2110         - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
2111         - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
2112 */
2113
2114 (function(){
2115
2116 Element.implement({
2117
2118         scrollTo: function(x, y){
2119                 if (isBody(this)){
2120                         this.getWindow().scrollTo(x, y);
2121                 } else {
2122                         this.scrollLeft = x;
2123                         this.scrollTop = y;
2124                 }
2125                 return this;
2126         },
2127
2128         getSize: function(){
2129                 if (isBody(this)) return this.getWindow().getSize();
2130                 return {x: this.offsetWidth, y: this.offsetHeight};
2131         },
2132
2133         getScrollSize: function(){
2134                 if (isBody(this)) return this.getWindow().getScrollSize();
2135                 return {x: this.scrollWidth, y: this.scrollHeight};
2136         },
2137
2138         getScroll: function(){
2139                 if (isBody(this)) return this.getWindow().getScroll();
2140                 return {x: this.scrollLeft, y: this.scrollTop};
2141         },
2142
2143         getScrolls: function(){
2144                 var element = this, position = {x: 0, y: 0};
2145                 while (element && !isBody(element)){
2146                         position.x += element.scrollLeft;
2147                         position.y += element.scrollTop;
2148                         element = element.parentNode;
2149                 }
2150                 return position;
2151         },
2152         
2153         getOffsetParent: function(){
2154                 var element = this;
2155                 if (isBody(element)) return null; 
2156                 if (!Browser.Engine.trident) return element.offsetParent;
2157                 while ((element = element.parentNode) && !isBody(element)){ 
2158                         if (styleString(element, 'position') != 'static') return element;
2159                 } 
2160                 return null;
2161         },
2162
2163         getOffsets: function(){
2164                 var element = this, position = {x: 0, y: 0};
2165                 if (isBody(this)) return position;
2166
2167                 while (element && !isBody(element)){
2168                         position.x += element.offsetLeft;
2169                         position.y += element.offsetTop;
2170
2171                         if (Browser.Engine.gecko){
2172                                 if (!borderBox(element)){
2173                                         position.x += leftBorder(element);
2174                                         position.y += topBorder(element);
2175                                 }
2176                                 var parent = element.parentNode;
2177                                 if (parent && styleString(parent, 'overflow') != 'visible'){
2178                                         position.x += leftBorder(parent);
2179                                         position.y += topBorder(parent);
2180                                 }
2181                         } else if (element != this && (Browser.Engine.trident || Browser.Engine.webkit)){
2182                                 position.x += leftBorder(element);
2183                                 position.y += topBorder(element);
2184                         }
2185
2186                         element = element.offsetParent;
2187                         if (Browser.Engine.trident){
2188                                 while (element && !element.currentStyle.hasLayout) element = element.offsetParent;
2189                         }
2190                 }
2191                 if (Browser.Engine.gecko && !borderBox(this)){
2192                         position.x -= leftBorder(this);
2193                         position.y -= topBorder(this);
2194                 }
2195                 return position;
2196         },
2197
2198         getPosition: function(relative){
2199                 if (isBody(this)) return {x: 0, y: 0};
2200                 var offset = this.getOffsets(), scroll = this.getScrolls();
2201                 var position = {x: offset.x - scroll.x, y: offset.y - scroll.y};
2202                 var relativePosition = (relative && (relative = $(relative))) ? relative.getPosition() : {x: 0, y: 0};
2203                 return {x: position.x - relativePosition.x, y: position.y - relativePosition.y};
2204         },
2205
2206         getCoordinates: function(element){
2207                 if (isBody(this)) return this.getWindow().getCoordinates();
2208                 var position = this.getPosition(element), size = this.getSize();
2209                 var obj = {left: position.x, top: position.y, width: size.x, height: size.y};
2210                 obj.right = obj.left + obj.width;
2211                 obj.bottom = obj.top + obj.height;
2212                 return obj;
2213         },
2214
2215         computePosition: function(obj){
2216                 return {left: obj.x - styleNumber(this, 'margin-left'), top: obj.y - styleNumber(this, 'margin-top')};
2217         },
2218
2219         position: function(obj){
2220                 return this.setStyles(this.computePosition(obj));
2221         }
2222
2223 });
2224
2225 Native.implement([Document, Window], {
2226
2227         getSize: function(){
2228                 var win = this.getWindow();
2229                 if (Browser.Engine.presto || Browser.Engine.webkit) return {x: win.innerWidth, y: win.innerHeight};
2230                 var doc = getCompatElement(this);
2231                 return {x: doc.clientWidth, y: doc.clientHeight};
2232         },
2233
2234         getScroll: function(){
2235                 var win = this.getWindow();
2236                 var doc = getCompatElement(this);
2237                 return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
2238         },
2239
2240         getScrollSize: function(){
2241                 var doc = getCompatElement(this);
2242                 var min = this.getSize();
2243                 return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)};
2244         },
2245
2246         getPosition: function(){
2247                 return {x: 0, y: 0};
2248         },
2249
2250         getCoordinates: function(){
2251                 var size = this.getSize();
2252                 return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
2253         }
2254
2255 });
2256
2257 // private methods
2258
2259 var styleString = Element.getComputedStyle;
2260
2261 function styleNumber(element, style){
2262         return styleString(element, style).toInt() || 0;
2263 };
2264
2265 function borderBox(element){
2266         return styleString(element, '-moz-box-sizing') == 'border-box';
2267 };
2268
2269 function topBorder(element){
2270         return styleNumber(element, 'border-top-width');
2271 };
2272
2273 function leftBorder(element){
2274         return styleNumber(element, 'border-left-width');
2275 };
2276
2277 function isBody(element){
2278         return (/^(?:body|html)$/i).test(element.tagName);
2279 };
2280
2281 function getCompatElement(element){
2282         var doc = element.getDocument();
2283         return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
2284 };
2285
2286 })();
2287
2288 //aliases
2289
2290 Native.implement([Window, Document, Element], {
2291
2292         getHeight: function(){
2293                 return this.getSize().y;
2294         },
2295
2296         getWidth: function(){
2297                 return this.getSize().x;
2298         },
2299
2300         getScrollTop: function(){
2301                 return this.getScroll().y;
2302         },
2303
2304         getScrollLeft: function(){
2305                 return this.getScroll().x;
2306         },
2307
2308         getScrollHeight: function(){
2309                 return this.getScrollSize().y;
2310         },
2311
2312         getScrollWidth: function(){
2313                 return this.getScrollSize().x;
2314         },
2315
2316         getTop: function(){
2317                 return this.getPosition().y;
2318         },
2319
2320         getLeft: function(){
2321                 return this.getPosition().x;
2322         }
2323
2324 });
2325
2326 /*
2327 Script: Selectors.js
2328         Adds advanced CSS Querying capabilities for targeting elements. Also includes pseudoselectors support.
2329
2330 License:
2331         MIT-style license.
2332 */
2333
2334 Native.implement([Document, Element], {
2335         
2336         getElements: function(expression, nocash){
2337                 expression = expression.split(',');
2338                 var items, local = {};
2339                 for (var i = 0, l = expression.length; i < l; i++){
2340                         var selector = expression[i], elements = Selectors.Utils.search(this, selector, local);
2341                         if (i != 0 && elements.item) elements = $A(elements);
2342                         items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements);
2343                 }
2344                 return new Elements(items, {ddup: (expression.length > 1), cash: !nocash});
2345         }
2346         
2347 });
2348
2349 Element.implement({
2350         
2351         match: function(selector){
2352                 if (!selector) return true;
2353                 var tagid = Selectors.Utils.parseTagAndID(selector);
2354                 var tag = tagid[0], id = tagid[1];
2355                 if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false;
2356                 var parsed = Selectors.Utils.parseSelector(selector);
2357                 return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
2358         }
2359         
2360 });
2361
2362 var Selectors = {Cache: {nth: {}, parsed: {}}};
2363
2364 Selectors.RegExps = {
2365         id: (/#([\w-]+)/),
2366         tag: (/^(\w+|\*)/),
2367         quick: (/^(\w+|\*)$/),
2368         splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
2369         combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)["']?(.*?)["']?)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
2370 };
2371
2372 Selectors.Utils = {
2373         
2374         chk: function(item, uniques){
2375                 if (!uniques) return true;
2376                 var uid = $uid(item);
2377                 if (!uniques[uid]) return uniques[uid] = true;
2378                 return false;
2379         },
2380         
2381         parseNthArgument: function(argument){
2382                 if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
2383                 var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
2384                 if (!parsed) return false;
2385                 var inta = parseInt(parsed[1]);
2386                 var a = (inta || inta === 0) ? inta : 1;
2387                 var special = parsed[2] || false;
2388                 var b = parseInt(parsed[3]) || 0;
2389                 if (a != 0){
2390                         b--;
2391                         while (b < 1) b += a;
2392                         while (b >= a) b -= a;
2393                 } else {
2394                         a = b;
2395                         special = 'index';
2396                 }
2397                 switch (special){
2398                         case 'n': parsed = {a: a, b: b, special: 'n'}; break;
2399                         case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
2400                         case 'even': parsed =  {a: 2, b: 1, special: 'n'}; break;
2401                         case 'first': parsed = {a: 0, special: 'index'}; break;
2402                         case 'last': parsed = {special: 'last-child'}; break;
2403                         case 'only': parsed = {special: 'only-child'}; break;
2404                         default: parsed = {a: (a - 1), special: 'index'};
2405                 }
2406                 
2407                 return Selectors.Cache.nth[argument] = parsed;
2408         },
2409         
2410         parseSelector: function(selector){
2411                 if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
2412                 var m, parsed = {classes: [], pseudos: [], attributes: []};
2413                 while ((m = Selectors.RegExps.combined.exec(selector))){
2414                         var cn = m[1], an = m[2], ao = m[3], av = m[4], pn = m[5], pa = m[6];
2415                         if (cn){
2416                                 parsed.classes.push(cn);
2417                         } else if (pn){
2418                                 var parser = Selectors.Pseudo.get(pn);
2419                                 if (parser) parsed.pseudos.push({parser: parser, argument: pa});
2420                                 else parsed.attributes.push({name: pn, operator: '=', value: pa});
2421                         } else if (an){
2422                                 parsed.attributes.push({name: an, operator: ao, value: av});
2423                         }
2424                 }
2425                 if (!parsed.classes.length) delete parsed.classes;
2426                 if (!parsed.attributes.length) delete parsed.attributes;
2427                 if (!parsed.pseudos.length) delete parsed.pseudos;
2428                 if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null;
2429                 return Selectors.Cache.parsed[selector] = parsed;
2430         },
2431         
2432         parseTagAndID: function(selector){
2433                 var tag = selector.match(Selectors.RegExps.tag);
2434                 var id = selector.match(Selectors.RegExps.id);
2435                 return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
2436         },
2437         
2438         filter: function(item, parsed, local){
2439                 var i;
2440                 if (parsed.classes){
2441                         for (i = parsed.classes.length; i--; i){
2442                                 var cn = parsed.classes[i];
2443                                 if (!Selectors.Filters.byClass(item, cn)) return false;
2444                         }
2445                 }
2446                 if (parsed.attributes){
2447                         for (i = parsed.attributes.length; i--; i){
2448                                 var att = parsed.attributes[i];
2449                                 if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false;
2450                         }
2451                 }
2452                 if (parsed.pseudos){
2453                         for (i = parsed.pseudos.length; i--; i){
2454                                 var psd = parsed.pseudos[i];
2455                                 if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false;
2456                         }
2457                 }
2458                 return true;
2459         },
2460         
2461         getByTagAndID: function(ctx, tag, id){
2462                 if (id){
2463                         var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true);
2464                         return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
2465                 } else {
2466                         return ctx.getElementsByTagName(tag);
2467                 }
2468         },
2469         
2470         search: function(self, expression, local){
2471                 var splitters = [];
2472                 
2473                 var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){
2474                         splitters.push(m1);
2475                         return ':)' + m2;
2476                 }).split(':)');
2477                 
2478                 var items, match, filtered, item;
2479                 
2480                 for (var i = 0, l = selectors.length; i < l; i++){
2481                         
2482                         var selector = selectors[i];
2483                         
2484                         if (i == 0 && Selectors.RegExps.quick.test(selector)){
2485                                 items = self.getElementsByTagName(selector);
2486                                 continue;
2487                         }
2488                         
2489                         var splitter = splitters[i - 1];
2490                         
2491                         var tagid = Selectors.Utils.parseTagAndID(selector);
2492                         var tag = tagid[0], id = tagid[1];
2493
2494                         if (i == 0){
2495                                 items = Selectors.Utils.getByTagAndID(self, tag, id);
2496                         } else {
2497                                 var uniques = {}, found = [];
2498                                 for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
2499                                 items = found;
2500                         }
2501                         
2502                         var parsed = Selectors.Utils.parseSelector(selector);
2503                         
2504                         if (parsed){
2505                                 filtered = [];
2506                                 for (var m = 0, n = items.length; m < n; m++){
2507                                         item = items[m];
2508                                         if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
2509                                 }
2510                                 items = filtered;
2511                         }
2512                         
2513                 }
2514                 
2515                 return items;
2516                 
2517         }
2518         
2519 };
2520
2521 Selectors.Getters = {
2522         
2523         ' ': function(found, self, tag, id, uniques){
2524                 var items = Selectors.Utils.getByTagAndID(self, tag, id);
2525                 for (var i = 0, l = items.length; i < l; i++){
2526                         var item = items[i];
2527                         if (Selectors.Utils.chk(item, uniques)) found.push(item);
2528                 }
2529                 return found;
2530         },
2531         
2532         '>': function(found, self, tag, id, uniques){
2533                 var children = Selectors.Utils.getByTagAndID(self, tag, id);
2534                 for (var i = 0, l = children.length; i < l; i++){
2535                         var child = children[i];
2536                         if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child);
2537                 }
2538                 return found;
2539         },
2540         
2541         '+': function(found, self, tag, id, uniques){
2542                 while ((self = self.nextSibling)){
2543                         if (self.nodeType == 1){
2544                                 if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
2545                                 break;
2546                         }
2547                 }
2548                 return found;
2549         },
2550         
2551         '~': function(found, self, tag, id, uniques){
2552                 
2553                 while ((self = self.nextSibling)){
2554                         if (self.nodeType == 1){
2555                                 if (!Selectors.Utils.chk(self, uniques)) break;
2556                                 if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
2557                         } 
2558                 }
2559                 return found;
2560         }
2561         
2562 };
2563
2564 Selectors.Filters = {
2565         
2566         byTag: function(self, tag){
2567                 return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag));
2568         },
2569         
2570         byID: function(self, id){
2571                 return (!id || (self.id && self.id == id));
2572         },
2573         
2574         byClass: function(self, klass){
2575                 return (self.className && self.className.contains(klass, ' '));
2576         },
2577         
2578         byPseudo: function(self, parser, argument, local){
2579                 return parser.call(self, argument, local);
2580         },
2581         
2582         byAttribute: function(self, name, operator, value){
2583                 var result = Element.prototype.getProperty.call(self, name);
2584                 if (!result) return false;
2585                 if (!operator || value == undefined) return true;
2586                 switch (operator){
2587                         case '=': return (result == value);
2588                         case '*=': return (result.contains(value));
2589                         case '^=': return (result.substr(0, value.length) == value);
2590                         case '$=': return (result.substr(result.length - value.length) == value);
2591                         case '!=': return (result != value);
2592                         case '~=': return result.contains(value, ' ');
2593                         case '|=': return result.contains(value, '-');
2594                 }
2595                 return false;
2596         }
2597         
2598 };
2599
2600 Selectors.Pseudo = new Hash({
2601         
2602         // w3c pseudo selectors
2603         
2604         empty: function(){
2605                 return !(this.innerText || this.textContent || '').length;
2606         },
2607         
2608         not: function(selector){
2609                 return !Element.match(this, selector);
2610         },
2611         
2612         contains: function(text){
2613                 return (this.innerText || this.textContent || '').contains(text);
2614         },
2615         
2616         'first-child': function(){
2617                 return Selectors.Pseudo.index.call(this, 0);
2618         },
2619         
2620         'last-child': function(){
2621                 var element = this;
2622                 while ((element = element.nextSibling)){
2623                         if (element.nodeType == 1) return false;
2624                 }
2625                 return true;
2626         },
2627         
2628         'only-child': function(){
2629                 var prev = this;
2630                 while ((prev = prev.previousSibling)){
2631                         if (prev.nodeType == 1) return false;
2632                 }
2633                 var next = this;
2634                 while ((next = next.nextSibling)){
2635                         if (next.nodeType == 1) return false;
2636                 }
2637                 return true;
2638         },
2639         
2640         'nth-child': function(argument, local){
2641                 argument = (argument == undefined) ? 'n' : argument;
2642                 var parsed = Selectors.Utils.parseNthArgument(argument);
2643                 if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local);
2644                 var count = 0;
2645                 local.positions = local.positions || {};
2646                 var uid = $uid(this);
2647                 if (!local.positions[uid]){
2648                         var self = this;
2649                         while ((self = self.previousSibling)){
2650                                 if (self.nodeType != 1) continue;
2651                                 count ++;
2652                                 var position = local.positions[$uid(self)];
2653                                 if (position != undefined){
2654                                         count = position + count;
2655                                         break;
2656                                 }
2657                         }
2658                         local.positions[uid] = count;
2659                 }
2660                 return (local.positions[uid] % parsed.a == parsed.b);
2661         },
2662         
2663         // custom pseudo selectors
2664         
2665         index: function(index){
2666                 var element = this, count = 0;
2667                 while ((element = element.previousSibling)){
2668                         if (element.nodeType == 1 && ++count > index) return false;
2669                 }
2670                 return (count == index);
2671         },
2672         
2673         even: function(argument, local){
2674                 return Selectors.Pseudo['nth-child'].call(this, '2n+1', local);
2675         },
2676
2677         odd: function(argument, local){
2678                 return Selectors.Pseudo['nth-child'].call(this, '2n', local);
2679         }
2680         
2681 });
2682
2683 /*
2684 Script: Domready.js
2685         Contains the domready custom event.
2686
2687 License:
2688         MIT-style license.
2689 */
2690
2691 Element.Events.domready = {
2692
2693         onAdd: function(fn){
2694                 if (Browser.loaded) fn.call(this);
2695         }
2696
2697 };
2698
2699 (function(){
2700         
2701         var domready = function(){
2702                 if (Browser.loaded) return;
2703                 Browser.loaded = true;
2704                 window.fireEvent('domready');
2705                 document.fireEvent('domready');
2706         };
2707         
2708         switch (Browser.Engine.name){
2709
2710                 case 'webkit': (function(){
2711                         (['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50);
2712                 })(); break;
2713
2714                 case 'trident':
2715                         var temp = document.createElement('div');
2716                         (function(){
2717                                 ($try(function(){
2718                                         temp.doScroll('left');
2719                                         return $(temp).inject(document.body).set('html', 'temp').dispose();
2720                                 })) ? domready() : arguments.callee.delay(50);
2721                         })();
2722                 break;
2723                 
2724                 default:
2725                         window.addEvent('load', domready);
2726                         document.addEvent('DOMContentLoaded', domready);
2727
2728         }
2729         
2730 })();
2731
2732 /*
2733 Script: JSON.js
2734         JSON encoder and decoder.
2735
2736 License:
2737         MIT-style license.
2738
2739 See Also:
2740         <http://www.json.org/>
2741 */
2742
2743 var JSON = new Hash({
2744
2745         encode: function(obj){
2746                 switch ($type(obj)){
2747                         case 'string':
2748                                 return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"';
2749                         case 'array':
2750                                 return '[' + String(obj.map(JSON.encode).filter($defined)) + ']';
2751                         case 'object': case 'hash':
2752                                 var string = [];
2753                                 Hash.each(obj, function(value, key){
2754                                         var json = JSON.encode(value);
2755                                         if (json) string.push(JSON.encode(key) + ':' + json);
2756                                 });
2757                                 return '{' + string + '}';
2758                         case 'number': case 'boolean': return String(obj);
2759                         case false: return 'null';
2760                 }
2761                 return null;
2762         },
2763
2764         $specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'},
2765
2766         $replaceChars: function(chr){
2767                 return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
2768         },
2769
2770         decode: function(string, secure){
2771                 if ($type(string) != 'string' || !string.length) return null;
2772                 if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null;
2773                 return eval('(' + string + ')');
2774         }
2775
2776 });
2777
2778 Native.implement([Hash, Array, String, Number], {
2779
2780         toJSON: function(){
2781                 return JSON.encode(this);
2782         }
2783
2784 });
2785
2786
2787 /*
2788 Script: Cookie.js
2789         Class for creating, loading, and saving browser Cookies.
2790
2791 License:
2792         MIT-style license.
2793
2794 Credits:
2795         Based on the functions by Peter-Paul Koch (http://quirksmode.org).
2796 */
2797
2798 var Cookie = new Class({
2799
2800         Implements: Options,
2801
2802         options: {
2803                 path: false,
2804                 domain: false,
2805                 duration: false,
2806                 secure: false,
2807                 document: document
2808         },
2809
2810         initialize: function(key, options){
2811                 this.key = key;
2812                 this.setOptions(options);
2813         },
2814
2815         write: function(value){
2816                 value = encodeURIComponent(value);
2817                 if (this.options.domain) value += '; domain=' + this.options.domain;
2818                 if (this.options.path) value += '; path=' + this.options.path;
2819                 if (this.options.duration){
2820                         var date = new Date();
2821                         date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
2822                         value += '; expires=' + date.toGMTString();
2823                 }
2824                 if (this.options.secure) value += '; secure';
2825                 this.options.document.cookie = this.key + '=' + value;
2826                 return this;
2827         },
2828
2829         read: function(){
2830                 var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
2831                 return (value) ? decodeURIComponent(value[1]) : null;
2832         },
2833
2834         dispose: function(){
2835                 new Cookie(this.key, $merge(this.options, {duration: -1})).write('');
2836                 return this;
2837         }
2838
2839 });
2840
2841 Cookie.write = function(key, value, options){
2842         return new Cookie(key, options).write(value);
2843 };
2844
2845 Cookie.read = function(key){
2846         return new Cookie(key).read();
2847 };
2848
2849 Cookie.dispose = function(key, options){
2850         return new Cookie(key, options).dispose();
2851 };
2852
2853 /*
2854 Script: Swiff.js
2855         Wrapper for embedding SWF movies. Supports (and fixes) External Interface Communication.
2856
2857 License:
2858         MIT-style license.
2859
2860 Credits:
2861         Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
2862 */
2863
2864 var Swiff = new Class({
2865
2866         Implements: [Options],
2867
2868         options: {
2869                 id: null,
2870                 height: 1,
2871                 width: 1,
2872                 container: null,
2873                 properties: {},
2874                 params: {
2875                         quality: 'high',
2876                         allowScriptAccess: 'always',
2877                         wMode: 'transparent',
2878                         swLiveConnect: true
2879                 },
2880                 callBacks: {},
2881                 vars: {}
2882         },
2883
2884         toElement: function(){
2885                 return this.object;
2886         },
2887
2888         initialize: function(path, options){
2889                 this.instance = 'Swiff_' + $time();
2890
2891                 this.setOptions(options);
2892                 options = this.options;
2893                 var id = this.id = options.id || this.instance;
2894                 var container = $(options.container);
2895
2896                 Swiff.CallBacks[this.instance] = {};
2897
2898                 var params = options.params, vars = options.vars, callBacks = options.callBacks;
2899                 var properties = $extend({height: options.height, width: options.width}, options.properties);
2900
2901                 var self = this;
2902
2903                 for (var callBack in callBacks){
2904                         Swiff.CallBacks[this.instance][callBack] = (function(option){
2905                                 return function(){
2906                                         return option.apply(self.object, arguments);
2907                                 };
2908                         })(callBacks[callBack]);
2909                         vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
2910                 }
2911
2912                 params.flashVars = Hash.toQueryString(vars);
2913                 if (Browser.Engine.trident){
2914                         properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
2915                         params.movie = path;
2916                 } else {
2917                         properties.type = 'application/x-shockwave-flash';
2918                         properties.data = path;
2919                 }
2920                 var build = '<object id="' + id + '"';
2921                 for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
2922                 build += '>';
2923                 for (var param in params){
2924                         if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
2925                 }
2926                 build += '</object>';
2927                 this.object =  ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
2928         },
2929
2930         replaces: function(element){
2931                 element = $(element, true);
2932                 element.parentNode.replaceChild(this.toElement(), element);
2933                 return this;
2934         },
2935
2936         inject: function(element){
2937                 $(element, true).appendChild(this.toElement());
2938                 return this;
2939         },
2940
2941         remote: function(){
2942                 return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments));
2943         }
2944
2945 });
2946
2947 Swiff.CallBacks = {};
2948
2949 Swiff.remote = function(obj, fn){
2950         var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
2951         return eval(rs);
2952 };
2953
2954 /*\r
2955 Script: Fx.js\r
2956         Contains the basic animation logic to be extended by all other Fx Classes.\r
2957 \r
2958 License:\r
2959         MIT-style license.\r
2960 */\r
2961 \r
2962 var Fx = new Class({\r
2963 \r
2964         Implements: [Chain, Events, Options],\r
2965 \r
2966         options: {\r
2967                 /*\r
2968                 onStart: $empty,\r
2969                 onCancel: $empty,\r
2970                 onComplete: $empty,\r
2971                 */\r
2972                 fps: 50,\r
2973                 unit: false,\r
2974                 duration: 500,\r
2975                 link: 'ignore',\r
2976                 transition: function(p){\r
2977                         return -(Math.cos(Math.PI * p) - 1) / 2;\r
2978                 }\r
2979         },\r
2980 \r
2981         initialize: function(options){\r
2982                 this.subject = this.subject || this;\r
2983                 this.setOptions(options);\r
2984                 this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt();\r
2985                 var wait = this.options.wait;\r
2986                 if (wait === false) this.options.link = 'cancel';\r
2987         },\r
2988 \r
2989         step: function(){\r
2990                 var time = $time();\r
2991                 if (time < this.time + this.options.duration){\r
2992                         var delta = this.options.transition((time - this.time) / this.options.duration);\r
2993                         this.set(this.compute(this.from, this.to, delta));\r
2994                 } else {\r
2995                         this.set(this.compute(this.from, this.to, 1));\r
2996                         this.complete();\r
2997                 }\r
2998         },\r
2999 \r
3000         set: function(now){\r
3001                 return now;\r
3002         },\r
3003 \r
3004         compute: function(from, to, delta){\r
3005                 return Fx.compute(from, to, delta);\r
3006         },\r
3007 \r
3008         check: function(caller){\r
3009                 if (!this.timer) return true;\r
3010                 switch (this.options.link){\r
3011                         case 'cancel': this.cancel(); return true;\r
3012                         case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return false;\r
3013                 }\r
3014                 return false;\r
3015         },\r
3016 \r
3017         start: function(from, to){\r
3018                 if (!this.check(arguments.callee, from, to)) return this;\r
3019                 this.from = from;\r
3020                 this.to = to;\r
3021                 this.time = 0;\r
3022                 this.startTimer();\r
3023                 this.onStart();\r
3024                 return this;\r
3025         },\r
3026 \r
3027         complete: function(){\r
3028                 if (this.stopTimer()) this.onComplete();\r
3029                 return this;\r
3030         },\r
3031 \r
3032         cancel: function(){\r
3033                 if (this.stopTimer()) this.onCancel();\r
3034                 return this;\r
3035         },\r
3036 \r
3037         onStart: function(){\r
3038                 this.fireEvent('start', this.subject);\r
3039         },\r
3040 \r
3041         onComplete: function(){\r
3042                 this.fireEvent('complete', this.subject);\r
3043                 if (!this.callChain()) this.fireEvent('chainComplete', this.subject);\r
3044         },\r
3045 \r
3046         onCancel: function(){\r
3047                 this.fireEvent('cancel', this.subject).clearChain();\r
3048         },\r
3049 \r
3050         pause: function(){\r
3051                 this.stopTimer();\r
3052                 return this;\r
3053         },\r
3054 \r
3055         resume: function(){\r
3056                 this.startTimer();\r
3057                 return this;\r
3058         },\r
3059 \r
3060         stopTimer: function(){\r
3061                 if (!this.timer) return false;\r
3062                 this.time = $time() - this.time;\r
3063                 this.timer = $clear(this.timer);\r
3064                 return true;\r
3065         },\r
3066 \r
3067         startTimer: function(){\r
3068                 if (this.timer) return false;\r
3069                 this.time = $time() - this.time;\r
3070                 this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);\r
3071                 return true;\r
3072         }\r
3073 \r
3074 });\r
3075 \r
3076 Fx.compute = function(from, to, delta){\r
3077         return (to - from) * delta + from;\r
3078 };\r
3079 \r
3080 Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};\r
3081
3082
3083 /*
3084 Script: Fx.CSS.js
3085         Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
3086
3087 License:
3088         MIT-style license.
3089 */
3090
3091 Fx.CSS = new Class({
3092
3093         Extends: Fx,
3094
3095         //prepares the base from/to object
3096
3097         prepare: function(element, property, values){
3098                 values = $splat(values);
3099                 var values1 = values[1];
3100                 if (!$chk(values1)){
3101                         values[1] = values[0];
3102                         values[0] = element.getStyle(property);
3103                 }
3104                 var parsed = values.map(this.parse);
3105                 return {from: parsed[0], to: parsed[1]};
3106         },
3107
3108         //parses a value into an array
3109
3110         parse: function(value){
3111                 value = $lambda(value)();
3112                 value = (typeof value == 'string') ? value.split(' ') : $splat(value);
3113                 return value.map(function(val){
3114                         val = String(val);
3115                         var found = false;
3116                         Fx.CSS.Parsers.each(function(parser, key){
3117                                 if (found) return;
3118                                 var parsed = parser.parse(val);
3119                                 if ($chk(parsed)) found = {value: parsed, parser: parser};
3120                         });
3121                         found = found || {value: val, parser: Fx.CSS.Parsers.String};
3122                         return found;
3123                 });
3124         },
3125
3126         //computes by a from and to prepared objects, using their parsers.
3127
3128         compute: function(from, to, delta){
3129                 var computed = [];
3130                 (Math.min(from.length, to.length)).times(function(i){
3131                         computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
3132                 });
3133                 computed.$family = {name: 'fx:css:value'};
3134                 return computed;
3135         },
3136
3137         //serves the value as settable
3138
3139         serve: function(value, unit){
3140                 if ($type(value) != 'fx:css:value') value = this.parse(value);
3141                 var returned = [];
3142                 value.each(function(bit){
3143                         returned = returned.concat(bit.parser.serve(bit.value, unit));
3144                 });
3145                 return returned;
3146         },
3147
3148         //renders the change to an element
3149
3150         render: function(element, property, value, unit){
3151                 element.setStyle(property, this.serve(value, unit));
3152         },
3153
3154         //searches inside the page css to find the values for a selector
3155
3156         search: function(selector){
3157                 if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
3158                 var to = {};
3159                 Array.each(document.styleSheets, function(sheet, j){
3160                         var href = sheet.href;
3161                         if (href && href.contains('://') && !href.contains(document.domain)) return;
3162                         var rules = sheet.rules || sheet.cssRules;
3163                         Array.each(rules, function(rule, i){
3164                                 if (!rule.style) return;
3165                                 var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
3166                                         return m.toLowerCase();
3167                                 }) : null;
3168                                 if (!selectorText || !selectorText.test('^' + selector + '$')) return;
3169                                 Element.Styles.each(function(value, style){
3170                                         if (!rule.style[style] || Element.ShortStyles[style]) return;
3171                                         value = String(rule.style[style]);
3172                                         to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value;
3173                                 });
3174                         });
3175                 });
3176                 return Fx.CSS.Cache[selector] = to;
3177         }
3178
3179 });
3180
3181 Fx.CSS.Cache = {};
3182
3183 Fx.CSS.Parsers = new Hash({
3184
3185         Color: {
3186                 parse: function(value){
3187                 &n