]> www.average.org Git - mkgallery.git/blob - include/mootools.js
IE compatibility
[mkgallery.git] / include / mootools.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                         if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
3188                         return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
3189                 },
3190                 compute: function(from, to, delta){
3191                         return from.map(function(value, i){
3192                                 return Math.round(Fx.compute(from[i], to[i], delta));
3193                         });
3194                 },
3195                 serve: function(value){
3196                         return value.map(Number);
3197                 }
3198         },
3199
3200         Number: {
3201                 parse: parseFloat,
3202                 compute: Fx.compute,
3203                 serve: function(value, unit){
3204                         return (unit) ? value + unit : value;
3205                 }
3206         },
3207
3208         String: {
3209                 parse: $lambda(false),
3210                 compute: $arguments(1),
3211                 serve: $arguments(0)
3212         }
3213
3214 });
3215
3216
3217 /*
3218 Script: Fx.Tween.js
3219         Formerly Fx.Style, effect to transition any CSS property for an element.
3220
3221 License:
3222         MIT-style license.
3223 */
3224
3225 Fx.Tween = new Class({
3226
3227         Extends: Fx.CSS,
3228
3229         initialize: function(element, options){
3230                 this.element = this.subject = $(element);
3231                 this.parent(options);
3232         },
3233
3234         set: function(property, now){
3235                 if (arguments.length == 1){
3236                         now = property;
3237                         property = this.property || this.options.property;
3238                 }
3239                 this.render(this.element, property, now, this.options.unit);
3240                 return this;
3241         },
3242
3243         start: function(property, from, to){
3244                 if (!this.check(arguments.callee, property, from, to)) return this;
3245                 var args = Array.flatten(arguments);
3246                 this.property = this.options.property || args.shift();
3247                 var parsed = this.prepare(this.element, this.property, args);
3248                 return this.parent(parsed.from, parsed.to);
3249         }
3250
3251 });
3252
3253 Element.Properties.tween = {
3254
3255         set: function(options){
3256                 var tween = this.retrieve('tween');
3257                 if (tween) tween.cancel();
3258                 return this.eliminate('tween').store('tween:options', $extend({link: 'cancel'}, options));
3259         },
3260
3261         get: function(options){
3262                 if (options || !this.retrieve('tween')){
3263                         if (options || !this.retrieve('tween:options')) this.set('tween', options);
3264                         this.store('tween', new Fx.Tween(this, this.retrieve('tween:options')));
3265                 }
3266                 return this.retrieve('tween');
3267         }
3268
3269 };
3270
3271 Element.implement({
3272
3273         tween: function(property, from, to){
3274                 this.get('tween').start(arguments);
3275                 return this;
3276         },
3277
3278         fade: function(how){
3279                 var fade = this.get('tween'), o = 'opacity', toggle;
3280                 how = $pick(how, 'toggle');
3281                 switch (how){
3282                         case 'in': fade.start(o, 1); break;
3283                         case 'out': fade.start(o, 0); break;
3284                         case 'show': fade.set(o, 1); break;
3285                         case 'hide': fade.set(o, 0); break;
3286                         case 'toggle':
3287                                 var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
3288                                 fade.start(o, (flag) ? 0 : 1);
3289                                 this.store('fade:flag', !flag);
3290                                 toggle = true;
3291                         break;
3292                         default: fade.start(o, arguments);
3293                 }
3294                 if (!toggle) this.eliminate('fade:flag');
3295                 return this;
3296         },
3297
3298         highlight: function(start, end){
3299                 if (!end){
3300                         end = this.retrieve('highlight:original', this.getStyle('background-color'));
3301                         end = (end == 'transparent') ? '#fff' : end;
3302                 }
3303                 var tween = this.get('tween');
3304                 tween.start('background-color', start || '#ffff88', end).chain(function(){
3305                         this.setStyle('background-color', this.retrieve('highlight:original'));
3306                         tween.callChain();
3307                 }.bind(this));
3308                 return this;
3309         }
3310
3311 });
3312
3313
3314 /*
3315 Script: Fx.Morph.js
3316         Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
3317
3318 License:
3319         MIT-style license.
3320 */
3321
3322 Fx.Morph = new Class({
3323
3324         Extends: Fx.CSS,
3325
3326         initialize: function(element, options){
3327                 this.element = this.subject = $(element);
3328                 this.parent(options);
3329         },
3330
3331         set: function(now){
3332                 if (typeof now == 'string') now = this.search(now);
3333                 for (var p in now) this.render(this.element, p, now[p], this.options.unit);
3334                 return this;
3335         },
3336
3337         compute: function(from, to, delta){
3338                 var now = {};
3339                 for (var p in from) now[p] = this.parent(from[p], to[p], delta);
3340                 return now;
3341         },
3342
3343         start: function(properties){
3344                 if (!this.check(arguments.callee, properties)) return this;
3345                 if (typeof properties == 'string') properties = this.search(properties);
3346                 var from = {}, to = {};
3347                 for (var p in properties){
3348                         var parsed = this.prepare(this.element, p, properties[p]);
3349                         from[p] = parsed.from;
3350                         to[p] = parsed.to;
3351                 }
3352                 return this.parent(from, to);
3353         }
3354
3355 });
3356
3357 Element.Properties.morph = {
3358
3359         set: function(options){
3360                 var morph = this.retrieve('morph');
3361                 if (morph) morph.cancel();
3362                 return this.eliminate('morph').store('morph:options', $extend({link: 'cancel'}, options));
3363         },
3364
3365         get: function(options){
3366                 if (options || !this.retrieve('morph')){
3367                         if (options || !this.retrieve('morph:options')) this.set('morph', options);
3368                         this.store('morph', new Fx.Morph(this, this.retrieve('morph:options')));
3369                 }
3370                 return this.retrieve('morph');
3371         }
3372
3373 };
3374
3375 Element.implement({
3376
3377         morph: function(props){
3378                 this.get('morph').start(props);
3379                 return this;
3380         }
3381
3382 });
3383
3384 /*
3385 Script: Fx.Transitions.js
3386         Contains a set of advanced transitions to be used with any of the Fx Classes.
3387
3388 License:
3389         MIT-style license.
3390
3391 Credits:
3392         Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
3393 */
3394
3395 (function(){
3396
3397         var old = Fx.prototype.initialize;
3398
3399         Fx.prototype.initialize = function(options){
3400                 old.call(this, options);
3401                 var trans = this.options.transition;
3402                 if (typeof trans == 'string' && (trans = trans.split(':'))){
3403                         var base = Fx.Transitions;
3404                         base = base[trans[0]] || base[trans[0].capitalize()];
3405                         if (trans[1]) base = base['ease' + trans[1].capitalize() + (trans[2] ? trans[2].capitalize() : '')];
3406                         this.options.transition = base;
3407                 }
3408         };
3409
3410 })();
3411
3412 Fx.Transition = function(transition, params){
3413         params = $splat(params);
3414         return $extend(transition, {
3415                 easeIn: function(pos){
3416                         return transition(pos, params);
3417                 },
3418                 easeOut: function(pos){
3419                         return 1 - transition(1 - pos, params);
3420                 },
3421                 easeInOut: function(pos){
3422                         return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
3423                 }
3424         });
3425 };
3426
3427 Fx.Transitions = new Hash({
3428
3429         linear: $arguments(0)
3430
3431 });
3432
3433 Fx.Transitions.extend = function(transitions){
3434         for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
3435 };
3436
3437 Fx.Transitions.extend({
3438
3439         Pow: function(p, x){
3440                 return Math.pow(p, x[0] || 6);
3441         },
3442
3443         Expo: function(p){
3444                 return Math.pow(2, 8 * (p - 1));
3445         },
3446
3447         Circ: function(p){
3448                 return 1 - Math.sin(Math.acos(p));
3449         },
3450
3451         Sine: function(p){
3452                 return 1 - Math.sin((1 - p) * Math.PI / 2);
3453         },
3454
3455         Back: function(p, x){
3456                 x = x[0] || 1.618;
3457                 return Math.pow(p, 2) * ((x + 1) * p - x);
3458         },
3459
3460         Bounce: function(p){
3461                 var value;
3462                 for (var a = 0, b = 1; 1; a += b, b /= 2){
3463                         if (p >= (7 - 4 * a) / 11){
3464                                 value = - Math.pow((11 - 6 * a - 11 * p) / 4, 2) + b * b;
3465                                 break;
3466                         }
3467                 }
3468                 return value;
3469         },
3470
3471         Elastic: function(p, x){
3472                 return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
3473         }
3474
3475 });
3476
3477 ['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
3478         Fx.Transitions[transition] = new Fx.Transition(function(p){
3479                 return Math.pow(p, [i + 2]);
3480         });
3481 });
3482
3483
3484 /*
3485 Script: Request.js
3486         Powerful all purpose Request Class. Uses XMLHTTPRequest.
3487
3488 License:
3489         MIT-style license.
3490 */
3491
3492 var Request = new Class({
3493
3494         Implements: [Chain, Events, Options],
3495
3496         options: {
3497                 /*onRequest: $empty,
3498                 onSuccess: $empty,
3499                 onFailure: $empty,
3500                 onException: $empty,*/
3501                 url: '',
3502                 data: '',
3503                 headers: {
3504                         'X-Requested-With': 'XMLHttpRequest',
3505                         'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
3506                 },
3507                 async: true,
3508                 format: false,
3509                 method: 'post',
3510                 link: 'ignore',
3511                 isSuccess: null,
3512                 emulation: true,
3513                 urlEncoded: true,
3514                 encoding: 'utf-8',
3515                 evalScripts: false,
3516                 evalResponse: false
3517         },
3518
3519         initialize: function(options){
3520                 this.xhr = new Browser.Request();
3521                 this.setOptions(options);
3522                 this.options.isSuccess = this.options.isSuccess || this.isSuccess;
3523                 this.headers = new Hash(this.options.headers);
3524         },
3525
3526         onStateChange: function(){
3527                 if (this.xhr.readyState != 4 || !this.running) return;
3528                 this.running = false;
3529                 this.status = 0;
3530                 $try(function(){
3531                         this.status = this.xhr.status;
3532                 }.bind(this));
3533                 if (this.options.isSuccess.call(this, this.status)){
3534                         this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
3535                         this.success(this.response.text, this.response.xml);
3536                 } else {
3537                         this.response = {text: null, xml: null};
3538                         this.failure();
3539                 }
3540                 this.xhr.onreadystatechange = $empty;
3541         },
3542
3543         isSuccess: function(){
3544                 return ((this.status >= 200) && (this.status < 300));
3545         },
3546
3547         processScripts: function(text){
3548                 if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
3549                 return text.stripScripts(this.options.evalScripts);
3550         },
3551
3552         success: function(text, xml){
3553                 this.onSuccess(this.processScripts(text), xml);
3554         },
3555         
3556         onSuccess: function(){
3557                 this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
3558         },
3559         
3560         failure: function(){
3561                 this.onFailure();
3562         },
3563
3564         onFailure: function(){
3565                 this.fireEvent('complete').fireEvent('failure', this.xhr);
3566         },
3567
3568         setHeader: function(name, value){
3569                 this.headers.set(name, value);
3570                 return this;
3571         },
3572
3573         getHeader: function(name){
3574                 return $try(function(){
3575                         return this.xhr.getResponseHeader(name);
3576                 }.bind(this));
3577         },
3578
3579         check: function(caller){
3580                 if (!this.running) return true;
3581                 switch (this.options.link){
3582                         case 'cancel': this.cancel(); return true;
3583                         case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return false;
3584                 }
3585                 return false;
3586         },
3587
3588         send: function(options){
3589                 if (!this.check(arguments.callee, options)) return this;
3590                 this.running = true;
3591
3592                 var type = $type(options);
3593                 if (type == 'string' || type == 'element') options = {data: options};
3594
3595                 var old = this.options;
3596                 options = $extend({data: old.data, url: old.url, method: old.method}, options);
3597                 var data = options.data, url = options.url, method = options.method;
3598
3599                 switch ($type(data)){
3600                         case 'element': data = $(data).toQueryString(); break;
3601                         case 'object': case 'hash': data = Hash.toQueryString(data);
3602                 }
3603
3604                 if (this.options.format){
3605                         var format = 'format=' + this.options.format;
3606                         data = (data) ? format + '&' + data : format;
3607                 }
3608
3609                 if (this.options.emulation && ['put', 'delete'].contains(method)){
3610                         var _method = '_method=' + method;
3611                         data = (data) ? _method + '&' + data : _method;
3612                         method = 'post';
3613                 }
3614
3615                 if (this.options.urlEncoded && method == 'post'){
3616                         var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
3617                         this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
3618                 }
3619
3620                 if (data && method == 'get'){
3621                         url = url + (url.contains('?') ? '&' : '?') + data;
3622                         data = null;
3623                 }
3624
3625                 this.xhr.open(method.toUpperCase(), url, this.options.async);
3626
3627                 this.xhr.onreadystatechange = this.onStateChange.bind(this);
3628
3629                 this.headers.each(function(value, key){
3630                         if (!$try(function(){
3631                                 this.xhr.setRequestHeader(key, value);
3632                                 return true;
3633                         }.bind(this))) this.fireEvent('exception', [key, value]);
3634                 }, this);
3635
3636                 this.fireEvent('request');
3637                 this.xhr.send(data);
3638                 if (!this.options.async) this.onStateChange();
3639                 return this;
3640         },
3641
3642         cancel: function(){
3643                 if (!this.running) return this;
3644                 this.running = false;
3645                 this.xhr.abort();
3646                 this.xhr.onreadystatechange = $empty;
3647                 this.xhr = new Browser.Request();
3648                 this.fireEvent('cancel');
3649                 return this;
3650         }
3651
3652 });
3653
3654 (function(){
3655
3656 var methods = {};
3657 ['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
3658         methods[method] = function(){
3659                 var params = Array.link(arguments, {url: String.type, data: $defined});
3660                 return this.send($extend(params, {method: method.toLowerCase()}));
3661         };
3662 });
3663
3664 Request.implement(methods);
3665
3666 })();
3667
3668 Element.Properties.send = {
3669         
3670         set: function(options){
3671                 var send = this.retrieve('send');
3672                 if (send) send.cancel();
3673                 return this.eliminate('send').store('send:options', $extend({
3674                         data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
3675                 }, options));
3676         },
3677
3678         get: function(options){
3679                 if (options || !this.retrieve('send')){
3680                         if (options || !this.retrieve('send:options')) this.set('send', options);
3681                         this.store('send', new Request(this.retrieve('send:options')));
3682                 }
3683                 return this.retrieve('send');
3684         }
3685
3686 };
3687
3688 Element.implement({
3689
3690         send: function(url){
3691                 var sender = this.get('send');
3692                 sender.send({data: this, url: url || sender.options.url});
3693                 return this;
3694         }
3695
3696 });
3697
3698
3699 /*
3700 Script: Request.HTML.js
3701         Extends the basic Request Class with additional methods for interacting with HTML responses.
3702
3703 License:
3704         MIT-style license.
3705 */
3706
3707 Request.HTML = new Class({
3708
3709         Extends: Request,
3710
3711         options: {
3712                 update: false,
3713                 evalScripts: true,
3714                 filter: false
3715         },
3716
3717         processHTML: function(text){
3718                 var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
3719                 text = (match) ? match[1] : text;
3720                 
3721                 var container = new Element('div');
3722                 
3723                 return $try(function(){
3724                         var root = '<root>' + text + '</root>', doc;
3725                         if (Browser.Engine.trident){
3726                                 doc = new ActiveXObject('Microsoft.XMLDOM');
3727                                 doc.async = false;
3728                                 doc.loadXML(root);
3729                         } else {
3730                                 doc = new DOMParser().parseFromString(root, 'text/xml');
3731                         }
3732                         root = doc.getElementsByTagName('root')[0];
3733                         for (var i = 0, k = root.childNodes.length; i < k; i++){
3734                                 var child = Element.clone(root.childNodes[i], true, true);
3735                                 if (child) container.grab(child);
3736                         }
3737                         return container;
3738                 }) || container.set('html', text);
3739         },
3740
3741         success: function(text){
3742                 var options = this.options, response = this.response;
3743                 
3744                 response.html = text.stripScripts(function(script){
3745                         response.javascript = script;
3746                 });
3747                 
3748                 var temp = this.processHTML(response.html);
3749                 
3750                 response.tree = temp.childNodes;
3751                 response.elements = temp.getElements('*');
3752                 
3753                 if (options.filter) response.tree = response.elements.filter(options.filter);
3754                 if (options.update) $(options.update).empty().adopt(response.tree);
3755                 if (options.evalScripts) $exec(response.javascript);
3756                 
3757                 this.onSuccess(response.tree, response.elements, response.html, response.javascript);
3758         }
3759
3760 });
3761
3762 Element.Properties.load = {
3763         
3764         set: function(options){
3765                 var load = this.retrieve('load');
3766                 if (load) send.cancel();
3767                 return this.eliminate('load').store('load:options', $extend({data: this, link: 'cancel', update: this, method: 'get'}, options));
3768         },
3769
3770         get: function(options){
3771                 if (options || ! this.retrieve('load')){
3772                         if (options || !this.retrieve('load:options')) this.set('load', options);
3773                         this.store('load', new Request.HTML(this.retrieve('load:options')));
3774                 }
3775                 return this.retrieve('load');
3776         }
3777
3778 };
3779
3780 Element.implement({
3781         
3782         load: function(){
3783                 this.get('load').send(Array.link(arguments, {data: Object.type, url: String.type}));
3784                 return this;
3785         }
3786
3787 });
3788
3789
3790 /*
3791 Script: Request.JSON.js
3792         Extends the basic Request Class with additional methods for sending and receiving JSON data.
3793
3794 License:
3795         MIT-style license.
3796 */
3797
3798 Request.JSON = new Class({
3799
3800         Extends: Request,
3801
3802         options: {
3803                 secure: true
3804         },
3805
3806         initialize: function(options){
3807                 this.parent(options);
3808                 this.headers.extend({'Accept': 'application/json', 'X-Request': 'JSON'});
3809         },
3810
3811         success: function(text){
3812                 this.response.json = JSON.decode(text, this.options.secure);
3813                 this.onSuccess(this.response.json, text);
3814         }
3815
3816 });
3817 //MooTools More, <http://mootools.net/more>. Copyright (c) 2006-2008 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
3818
3819 /*\r
3820 Script: Fx.Slide.js\r
3821         Effect to slide an element in and out of view.\r
3822 \r
3823 License:\r
3824         MIT-style license.\r
3825 */\r
3826 \r
3827 Fx.Slide = new Class({\r
3828 \r
3829         Extends: Fx,\r
3830 \r
3831         options: {\r
3832                 mode: 'vertical'\r
3833         },\r
3834 \r
3835         initialize: function(element, options){\r
3836                 this.addEvent('complete', function(){\r
3837                         this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);\r
3838                         if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper);\r
3839                 }, true);\r
3840                 this.element = this.subject = $(element);\r
3841                 this.parent(options);\r
3842                 var wrapper = this.element.retrieve('wrapper');\r
3843                 this.wrapper = wrapper || new Element('div', {\r
3844                         styles: $extend(this.element.getStyles('margin', 'position'), {'overflow': 'hidden'})\r
3845                 }).wraps(this.element);\r
3846                 this.element.store('wrapper', this.wrapper).setStyle('margin', 0);\r
3847                 this.now = [];\r
3848                 this.open = true;\r
3849         },\r
3850 \r
3851         vertical: function(){\r
3852                 this.margin = 'margin-top';\r
3853                 this.layout = 'height';\r
3854                 this.offset = this.element.offsetHeight;\r
3855         },\r
3856 \r
3857         horizontal: function(){\r
3858                 this.margin = 'margin-left';\r
3859                 this.layout = 'width';\r
3860                 this.offset = this.element.offsetWidth;\r
3861         },\r
3862 \r
3863         set: function(now){\r
3864                 this.element.setStyle(this.margin, now[0]);\r
3865                 this.wrapper.setStyle(this.layout, now[1]);\r
3866                 return this;\r
3867         },\r
3868 \r
3869         compute: function(from, to, delta){\r
3870                 var now = [];\r
3871                 var x = 2;\r
3872                 x.times(function(i){\r
3873                         now[i] = Fx.compute(from[i], to[i], delta);\r
3874                 });\r
3875                 return now;\r
3876         },\r
3877 \r
3878         start: function(how, mode){\r
3879                 if (!this.check(arguments.callee, how, mode)) return this;\r
3880                 this[mode || this.options.mode]();\r
3881                 var margin = this.element.getStyle(this.margin).toInt();\r
3882                 var layout = this.wrapper.getStyle(this.layout).toInt();\r
3883                 var caseIn = [[margin, layout], [0, this.offset]];\r
3884                 var caseOut = [[margin, layout], [-this.offset, 0]];\r
3885                 var start;\r
3886                 switch (how){\r
3887                         case 'in': start = caseIn; break;\r
3888                         case 'out': start = caseOut; break;\r
3889                         case 'toggle': start = (this.wrapper['offset' + this.layout.capitalize()] == 0) ? caseIn : caseOut;\r
3890                 }\r
3891                 return this.parent(start[0], start[1]);\r
3892         },\r
3893 \r
3894         slideIn: function(mode){\r
3895                 return this.start('in', mode);\r
3896         },\r
3897 \r
3898         slideOut: function(mode){\r
3899                 return this.start('out', mode);\r
3900         },\r
3901 \r
3902         hide: function(mode){\r
3903                 this[mode || this.options.mode]();\r
3904                 this.open = false;\r
3905                 return this.set([-this.offset, 0]);\r
3906         },\r
3907 \r
3908         show: function(mode){\r
3909                 this[mode || this.options.mode]();\r
3910                 this.open = true;\r
3911                 return this.set([0, this.offset]);\r
3912         },\r
3913 \r
3914         toggle: function(mode){\r
3915                 return this.start('toggle', mode);\r
3916         }\r
3917 \r
3918 });\r
3919 \r
3920 Element.Properties.slide = {\r
3921 \r
3922         set: function(options){\r
3923                 var slide = this.retrieve('slide');\r
3924                 if (slide) slide.cancel();\r
3925                 return this.eliminate('slide').store('slide:options', $extend({link: 'cancel'}, options));\r
3926         },\r
3927         \r
3928         get: function(options){\r
3929                 if (options || !this.retrieve('slide')){\r
3930                         if (options || !this.retrieve('slide:options')) this.set('slide', options);\r
3931                         this.store('slide', new Fx.Slide(this, this.retrieve('slide:options')));\r
3932                 }\r
3933                 return this.retrieve('slide');\r
3934         }\r
3935 \r
3936 };\r
3937 \r
3938 Element.implement({\r
3939 \r
3940         slide: function(how, mode){\r
3941                 how = how || 'toggle';\r
3942                 var slide = this.get('slide'), toggle;\r
3943                 switch (how){\r
3944                         case 'hide': slide.hide(mode); break;\r
3945                         case 'show': slide.show(mode); break;\r
3946                         case 'toggle':\r
3947                                 var flag = this.retrieve('slide:flag', slide.open);\r
3948                                 slide[(flag) ? 'slideOut' : 'slideIn'](mode);\r
3949                                 this.store('slide:flag', !flag);\r
3950                                 toggle = true;\r
3951                         break;\r
3952                         default: slide.start(how, mode);\r
3953                 }\r
3954                 if (!toggle) this.eliminate('slide:flag');\r
3955                 return this;\r
3956         }\r
3957 \r
3958 });\r
3959
3960
3961 /*
3962 Script: Fx.Scroll.js
3963         Effect to smoothly scroll any element, including the window.
3964
3965 License:
3966         MIT-style license.
3967 */
3968
3969 Fx.Scroll = new Class({
3970
3971         Extends: Fx,
3972
3973         options: {
3974                 offset: {'x': 0, 'y': 0},
3975                 wheelStops: true
3976         },
3977
3978         initialize: function(element, options){
3979                 this.element = this.subject = $(element);
3980                 this.parent(options);
3981                 var cancel = this.cancel.bind(this, false);
3982
3983                 if ($type(this.element) != 'element') this.element = $(this.element.getDocument().body);
3984
3985                 var stopper = this.element;
3986
3987                 if (this.options.wheelStops){
3988                         this.addEvent('start', function(){
3989                                 stopper.addEvent('mousewheel', cancel);
3990                         }, true);
3991                         this.addEvent('complete', function(){
3992                                 stopper.removeEvent('mousewheel', cancel);
3993                         }, true);
3994                 }
3995         },
3996
3997         set: function(){
3998                 var now = Array.flatten(arguments);
3999                 this.element.scrollTo(now[0], now[1]);
4000         },
4001
4002         compute: function(from, to, delta){
4003                 var now = [];
4004                 var x = 2;
4005                 x.times(function(i){
4006                         now.push(Fx.compute(from[i], to[i], delta));
4007                 });
4008                 return now;
4009         },
4010
4011         start: function(x, y){
4012                 if (!this.check(arguments.callee, x, y)) return this;
4013                 var offsetSize = this.element.getSize(), scrollSize = this.element.getScrollSize();
4014                 var scroll = this.element.getScroll(), values = {x: x, y: y};
4015                 for (var z in values){
4016                         var max = scrollSize[z] - offsetSize[z];
4017                         if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z].limit(0, max) : max;
4018                         else values[z] = scroll[z];
4019                         values[z] += this.options.offset[z];
4020                 }
4021                 return this.parent([scroll.x, scroll.y], [values.x, values.y]);
4022         },
4023
4024         toTop: function(){
4025                 return this.start(false, 0);
4026         },
4027
4028         toLeft: function(){
4029                 return this.start(0, false);
4030         },
4031
4032         toRight: function(){
4033                 return this.start('right', false);
4034         },
4035
4036         toBottom: function(){
4037                 return this.start(false, 'bottom');
4038         },
4039
4040         toElement: function(el){
4041                 var position = $(el).getPosition(this.element);
4042                 return this.start(position.x, position.y);
4043         }
4044
4045 });
4046
4047
4048 /*
4049 Script: Fx.Elements.js
4050         Effect to change any number of CSS properties of any number of Elements.
4051
4052 License:
4053         MIT-style license.
4054 */
4055
4056 Fx.Elements = new Class({
4057
4058         Extends: Fx.CSS,
4059
4060         initialize: function(elements, options){
4061                 this.elements = this.subject = $$(elements);
4062                 this.parent(options);
4063         },
4064
4065         compute: function(from, to, delta){
4066                 var now = {};
4067                 for (var i in from){
4068                         var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
4069                         for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
4070                 }
4071                 return now;
4072         },
4073
4074         set: function(now){
4075                 for (var i in now){
4076                         var iNow = now[i];
4077                         for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
4078                 }
4079                 return this;
4080         },
4081
4082         start: function(obj){
4083                 if (!this.check(arguments.callee, obj)) return this;
4084                 var from = {}, to = {};
4085                 for (var i in obj){
4086                         var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
4087                         for (var p in iProps){
4088                                 var parsed = this.prepare(this.elements[i], p, iProps[p]);
4089                                 iFrom[p] = parsed.from;
4090                                 iTo[p] = parsed.to;
4091                         }
4092                 }
4093                 return this.parent(from, to);
4094         }
4095
4096 });
4097
4098 /*
4099 Script: Drag.js
4100         The base Drag Class. Can be used to drag and resize Elements using mouse events.
4101
4102 License:
4103         MIT-style license.
4104 */
4105
4106 var Drag = new Class({
4107
4108         Implements: [Events, Options],
4109
4110         options: {/*
4111                 onBeforeStart: $empty,
4112                 onStart: $empty,
4113                 onDrag: $empty,
4114                 onCancel: $empty,
4115                 onComplete: $empty,*/
4116                 snap: 6,
4117                 unit: 'px',
4118                 grid: false,
4119                 style: true,
4120                 limit: false,
4121                 handle: false,
4122                 invert: false,
4123                 preventDefault: false,
4124                 modifiers: {x: 'left', y: 'top'}
4125         },
4126
4127         initialize: function(){
4128                 var params = Array.link(arguments, {'options': Object.type, 'element': $defined});
4129                 this.element = $(params.element);
4130                 this.document = this.element.getDocument();
4131                 this.setOptions(params.options || {});
4132                 var htype = $type(this.options.handle);
4133                 this.handles = (htype == 'array' || htype == 'collection') ? $$(this.options.handle) : $(this.options.handle) || this.element;
4134                 this.mouse = {'now': {}, 'pos': {}};
4135                 this.value = {'start': {}, 'now': {}};
4136                 
4137                 this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';
4138                 
4139                 this.bound = {
4140                         start: this.start.bind(this),
4141                         check: this.check.bind(this),
4142                         drag: this.drag.bind(this),
4143                         stop: this.stop.bind(this),
4144                         cancel: this.cancel.bind(this),
4145                         eventStop: $lambda(false)
4146                 };
4147                 this.attach();
4148         },
4149
4150         attach: function(){
4151                 this.handles.addEvent('mousedown', this.bound.start);
4152                 return this;
4153         },
4154
4155         detach: function(){
4156                 this.handles.removeEvent('mousedown', this.bound.start);
4157                 return this;
4158         },
4159
4160         start: function(event){
4161                 if (this.options.preventDefault) event.preventDefault();
4162                 this.fireEvent('beforeStart', this.element);
4163                 this.mouse.start = event.page;
4164                 var limit = this.options.limit;
4165                 this.limit = {'x': [], 'y': []};
4166                 for (var z in this.options.modifiers){
4167                         if (!this.options.modifiers[z]) continue;
4168                         if (this.options.style) this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt();
4169                         else this.value.now[z] = this.element[this.options.modifiers[z]];
4170                         if (this.options.invert) this.value.now[z] *= -1;
4171                         this.mouse.pos[z] = event.page[z] - this.value.now[z];
4172                         if (limit && limit[z]){
4173                                 for (var i = 2; i--; i){
4174                                         if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
4175                                 }
4176                         }
4177                 }
4178                 if ($type(this.options.grid) == 'number') this.options.grid = {'x': this.options.grid, 'y': this.options.grid};
4179                 this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
4180                 this.document.addEvent(this.selection, this.bound.eventStop);
4181         },
4182
4183         check: function(event){
4184                 if (this.options.preventDefault) event.preventDefault();
4185                 var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
4186                 if (distance > this.options.snap){
4187                         this.cancel();
4188                         this.document.addEvents({
4189                                 mousemove: this.bound.drag,
4190                                 mouseup: this.bound.stop
4191                         });
4192                         this.fireEvent('start', this.element).fireEvent('snap', this.element);
4193                 }
4194         },
4195
4196         drag: function(event){
4197                 if (this.options.preventDefault) event.preventDefault();
4198                 this.mouse.now = event.page;
4199                 for (var z in this.options.modifiers){
4200                         if (!this.options.modifiers[z]) continue;
4201                         this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
4202                         if (this.options.invert) this.value.now[z] *= -1;
4203                         if (this.options.limit && this.limit[z]){
4204                                 if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){
4205                                         this.value.now[z] = this.limit[z][1];
4206                                 } else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){
4207                                         this.value.now[z] = this.limit[z][0];
4208                                 }
4209                         }
4210                         if (this.options.grid[z]) this.value.now[z] -= (this.value.now[z] % this.options.grid[z]);
4211                         if (this.options.style) this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
4212                         else this.element[this.options.modifiers[z]] = this.value.now[z];
4213                 }
4214                 this.fireEvent('drag', this.element);
4215         },
4216
4217         cancel: function(event){
4218                 this.document.removeEvent('mousemove', this.bound.check);
4219                 this.document.removeEvent('mouseup', this.bound.cancel);
4220                 if (event){
4221                         this.document.removeEvent(this.selection, this.bound.eventStop);
4222                         this.fireEvent('cancel', this.element);
4223                 }
4224         },
4225
4226         stop: function(event){
4227                 this.document.removeEvent(this.selection, this.bound.eventStop);
4228                 this.document.removeEvent('mousemove', this.bound.drag);
4229                 this.document.removeEvent('mouseup', this.bound.stop);
4230                 if (event) this.fireEvent('complete', this.element);
4231         }
4232
4233 });
4234
4235 Element.implement({
4236         
4237         makeResizable: function(options){
4238                 return new Drag(this, $merge({modifiers: {'x': 'width', 'y': 'height'}}, options));
4239         }
4240
4241 });
4242
4243 /*
4244 Script: Drag.Move.js
4245         A Drag extension that provides support for the constraining of draggables to containers and droppables.
4246
4247 License:
4248         MIT-style license.
4249 */
4250
4251 Drag.Move = new Class({
4252
4253         Extends: Drag,
4254
4255         options: {
4256                 droppables: [],
4257                 container: false
4258         },
4259
4260         initialize: function(element, options){
4261                 this.parent(element, options);
4262                 this.droppables = $$(this.options.droppables);
4263                 this.container = $(this.options.container);
4264                 if (this.container && $type(this.container) != 'element') this.container = $(this.container.getDocument().body);
4265                 element = this.element;
4266                 
4267                 var current = element.getStyle('position');
4268                 var position = (current != 'static') ? current : 'absolute';
4269                 if (element.getStyle('left') == 'auto' || element.getStyle('top') == 'auto') element.position(element.getPosition(element.offsetParent));
4270                 
4271                 element.setStyle('position', position);
4272                 
4273                 this.addEvent('start', function(){
4274                         this.checkDroppables();
4275                 }, true);
4276         },
4277
4278         start: function(event){
4279                 if (this.container){
4280                         var el = this.element, cont = this.container, ccoo = cont.getCoordinates(el.offsetParent), cps = {}, ems = {};
4281
4282                         ['top', 'right', 'bottom', 'left'].each(function(pad){
4283                                 cps[pad] = cont.getStyle('padding-' + pad).toInt();
4284                                 ems[pad] = el.getStyle('margin-' + pad).toInt();
4285                         }, this);
4286
4287                         var width = el.offsetWidth + ems.left + ems.right, height = el.offsetHeight + ems.top + ems.bottom;
4288                         var x = [ccoo.left + cps.left, ccoo.right - cps.right - width];
4289                         var y = [ccoo.top + cps.top, ccoo.bottom - cps.bottom - height];
4290
4291                         this.options.limit = {x: x, y: y};
4292                 }
4293                 this.parent(event);
4294         },
4295
4296         checkAgainst: function(el){
4297                 el = el.getCoordinates();
4298                 var now = this.mouse.now;
4299                 return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
4300         },
4301
4302         checkDroppables: function(){
4303                 var overed = this.droppables.filter(this.checkAgainst, this).getLast();
4304                 if (this.overed != overed){
4305                         if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
4306                         if (overed){
4307                                 this.overed = overed;
4308                                 this.fireEvent('enter', [this.element, overed]);
4309                         } else {
4310                                 this.overed = null;
4311                         }
4312                 }
4313         },
4314
4315         drag: function(event){
4316                 this.parent(event);
4317                 if (this.droppables.length) this.checkDroppables();
4318         },
4319
4320         stop: function(event){
4321                 this.checkDroppables();
4322                 this.fireEvent('drop', [this.element, this.overed]);
4323                 this.overed = null;
4324                 return this.parent(event);
4325         }
4326
4327 });
4328
4329 Element.implement({
4330
4331         makeDraggable: function(options){
4332                 return new Drag.Move(this, options);
4333         }
4334
4335 });
4336
4337
4338 /*\r
4339 Script: Hash.Cookie.js\r
4340         Class for creating, reading, and deleting Cookies in JSON format.\r
4341 \r
4342 License:\r
4343         MIT-style license.\r
4344 */\r
4345 \r
4346 Hash.Cookie = new Class({\r
4347 \r
4348         Extends: Cookie,\r
4349 \r
4350         options: {\r
4351                 autoSave: true\r
4352         },\r
4353 \r
4354         initialize: function(name, options){\r
4355                 this.parent(name, options);\r
4356                 this.load();\r
4357         },\r
4358 \r
4359         save: function(){\r
4360                 var value = JSON.encode(this.hash);\r
4361                 if (!value || value.length > 4096) return false; //cookie would be truncated!\r
4362                 if (value == '{}') this.dispose();\r
4363                 else this.write(value);\r
4364                 return true;\r
4365         },\r
4366 \r
4367         load: function(){\r
4368                 this.hash = new Hash(JSON.decode(this.read(), true));\r
4369                 return this;\r
4370         }\r
4371 \r
4372 });\r
4373 \r
4374 Hash.Cookie.implement((function(){\r
4375         \r
4376         var methods = {};\r
4377         \r
4378         Hash.each(Hash.prototype, function(method, name){\r
4379                 methods[name] = function(){\r
4380                         var value = method.apply(this.hash, arguments);\r
4381                         if (this.options.autoSave) this.save();\r
4382                         return value;\r
4383                 };\r
4384         });\r
4385         \r
4386         return methods;\r
4387         \r
4388 })());
4389
4390 /*
4391 Script: Color.js
4392         Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.
4393
4394 License:
4395         MIT-style license.
4396 */
4397
4398 var Color = new Native({
4399   
4400         initialize: function(color, type){
4401                 if (arguments.length >= 3){
4402                         type = "rgb"; color = Array.slice(arguments, 0, 3);
4403                 } else if (typeof color == 'string'){
4404                         if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true);
4405                         else if (color.match(/hsb/)) color = color.hsbToRgb();
4406                         else color = color.hexToRgb(true);
4407                 }
4408                 type = type || 'rgb';
4409                 switch (type){
4410                         case 'hsb':
4411                                 var old = color;
4412                                 color = color.hsbToRgb();
4413                                 color.hsb = old;
4414                         break;
4415                         case 'hex': color = color.hexToRgb(true); break;
4416                 }
4417                 color.rgb = color.slice(0, 3);
4418                 color.hsb = color.hsb || color.rgbToHsb();
4419                 color.hex = color.rgbToHex();
4420                 return $extend(color, this);
4421         }
4422
4423 });
4424
4425 Color.implement({
4426
4427         mix: function(){
4428                 var colors = Array.slice(arguments);
4429                 var alpha = ($type(colors.getLast()) == 'number') ? colors.pop() : 50;
4430                 var rgb = this.slice();
4431                 colors.each(function(color){
4432                         color = new Color(color);
4433                         for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha));
4434                 });
4435                 return new Color(rgb, 'rgb');
4436         },
4437
4438         invert: function(){
4439                 return new Color(this.map(function(value){
4440                         return 255 - value;
4441                 }));
4442         },
4443
4444         setHue: function(value){
4445                 return new Color([value, this.hsb[1], this.hsb[2]], 'hsb');
4446         },
4447
4448         setSaturation: function(percent){
4449                 return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb');
4450         },
4451
4452         setBrightness: function(percent){
4453                 return new Color([this.hsb[0], this.hsb[1], percent], 'hsb');
4454         }
4455
4456 });
4457
4458 function $RGB(r, g, b){
4459         return new Color([r, g, b], 'rgb');
4460 };
4461
4462 function $HSB(h, s, b){
4463         return new Color([h, s, b], 'hsb');
4464 };
4465
4466 function $HEX(hex){
4467         return new Color(hex, 'hex');
4468 };
4469
4470 Array.implement({
4471
4472         rgbToHsb: function(){
4473                 var red = this[0], green = this[1], blue = this[2];
4474                 var hue, saturation, brightness;
4475                 var max = Math.max(red, green, blue), min = Math.min(red, green, blue);
4476                 var delta = max - min;
4477                 brightness = max / 255;
4478                 saturation = (max != 0) ? delta / max : 0;
4479                 if (saturation == 0){
4480                         hue = 0;
4481                 } else {
4482                         var rr = (max - red) / delta;
4483                         var gr = (max - green) / delta;
4484                         var br = (max - blue) / delta;
4485                         if (red == max) hue = br - gr;
4486                         else if (green == max) hue = 2 + rr - br;
4487                         else hue = 4 + gr - rr;
4488                         hue /= 6;
4489                         if (hue < 0) hue++;
4490                 }
4491                 return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)];
4492         },
4493
4494         hsbToRgb: function(){
4495                 var br = Math.round(this[2] / 100 * 255);
4496                 if (this[1] == 0){
4497                         return [br, br, br];
4498                 } else {
4499                         var hue = this[0] % 360;
4500                         var f = hue % 60;
4501                         var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
4502                         var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255);
4503                         var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255);
4504                         switch (Math.floor(hue / 60)){
4505                                 case 0: return [br, t, p];
4506                                 case 1: return [q, br, p];
4507                                 case 2: return [p, br, t];
4508                                 case 3: return [p, q, br];
4509                                 case 4: return [t, p, br];
4510                                 case 5: return [br, p, q];
4511                         }
4512                 }
4513                 return false;
4514         }
4515
4516 });
4517
4518 String.implement({
4519
4520         rgbToHsb: function(){
4521                 var rgb = this.match(/\d{1,3}/g);
4522                 return (rgb) ? hsb.rgbToHsb() : null;
4523         },
4524         
4525         hsbToRgb: function(){
4526                 var hsb = this.match(/\d{1,3}/g);
4527                 return (hsb) ? hsb.hsbToRgb() : null;
4528         }
4529
4530 });
4531
4532
4533 /*
4534 Script: Group.js
4535         Class for monitoring collections of events
4536
4537 License:
4538         MIT-style license.
4539 */
4540
4541 var Group = new Class({
4542
4543         initialize: function(){
4544                 this.instances = Array.flatten(arguments);
4545                 this.events = {};
4546                 this.checker = {};
4547         },
4548
4549         addEvent: function(type, fn){
4550                 this.checker[type] = this.checker[type] || {};
4551                 this.events[type] = this.events[type] || [];
4552                 if (this.events[type].contains(fn)) return false;
4553                 else this.events[type].push(fn);
4554                 this.instances.each(function(instance, i){
4555                         instance.addEvent(type, this.check.bind(this, [type, instance, i]));
4556                 }, this);
4557                 return this;
4558         },
4559
4560         check: function(type, instance, i){
4561                 this.checker[type][i] = true;
4562                 var every = this.instances.every(function(current, j){
4563                         return this.checker[type][j] || false;
4564                 }, this);
4565                 if (!every) return;
4566                 this.checker[type] = {};
4567                 this.events[type].each(function(event){
4568                         event.call(this, this.instances, instance);
4569                 }, this);
4570         }
4571
4572 });
4573
4574
4575 /*
4576 Script: Assets.js
4577         Provides methods to dynamically load JavaScript, CSS, and Image files into the document.
4578
4579 License:
4580         MIT-style license.
4581 */
4582
4583 var Asset = new Hash({
4584
4585         javascript: function(source, properties){
4586                 properties = $extend({
4587                         onload: $empty,
4588                         document: document,
4589                         check: $lambda(true)
4590                 }, properties);
4591                 
4592                 var script = new Element('script', {'src': source, 'type': 'text/javascript'});
4593                 
4594                 var load = properties.onload.bind(script), check = properties.check, doc = properties.document;
4595                 delete properties.onload; delete properties.check; delete properties.document;
4596                 
4597                 script.addEvents({
4598                         load: load,
4599                         readystatechange: function(){
4600                                 if (['loaded', 'complete'].contains(this.readyState)) load();
4601                         }
4602                 }).setProperties(properties);
4603                 
4604                 
4605                 if (Browser.Engine.webkit419) var checker = (function(){
4606                         if (!$try(check)) return;
4607                         $clear(checker);
4608                         load();
4609                 }).periodical(50);
4610                 
4611                 return script.inject(doc.head);
4612         },
4613
4614         css: function(source, properties){
4615                 return new Element('link', $merge({
4616                         'rel': 'stylesheet', 'media': 'screen', 'type': 'text/css', 'href': source
4617                 }, properties)).inject(document.head);
4618         },
4619
4620         image: function(source, properties){
4621                 properties = $merge({
4622                         'onload': $empty,
4623                         'onabort': $empty,
4624                         'onerror': $empty
4625                 }, properties);
4626                 var image = new Image();
4627                 var element = $(image) || new Element('img');
4628                 ['load', 'abort', 'error'].each(function(name){
4629                         var type = 'on' + name;
4630                         var event = properties[type];
4631                         delete properties[type];
4632                         image[type] = function(){
4633                                 if (!image) return;
4634                                 if (!element.parentNode){
4635                                         element.width = image.width;
4636                                         element.height = image.height;
4637                                 }
4638                                 image = image.onload = image.onabort = image.onerror = null;
4639                                 event.delay(1, element, element);
4640                                 element.fireEvent(name, element, 1);
4641                         };
4642                 });
4643                 image.src = element.src = source;
4644                 if (image && image.complete) image.onload.delay(1);
4645                 return element.setProperties(properties);
4646         },
4647
4648         images: function(sources, options){
4649                 options = $merge({
4650                         onComplete: $empty,
4651                         onProgress: $empty
4652                 }, options);
4653                 if (!sources.push) sources = [sources];
4654                 var images = [];
4655                 var counter = 0;
4656                 sources.each(function(source){
4657                         var img = new Asset.image(source, {
4658                                 'onload': function(){
4659                                         options.onProgress.call(this, counter, sources.indexOf(source));
4660                                         counter++;
4661                                         if (counter == sources.length) options.onComplete();
4662                                 }
4663                         });
4664                         images.push(img);
4665                 });
4666                 return new Elements(images);
4667         }
4668
4669 });
4670
4671 /*
4672 Script: Sortables.js
4673         Class for creating a drag and drop sorting interface for lists of items.
4674
4675 License:
4676         MIT-style license.
4677 */
4678
4679 var Sortables = new Class({
4680
4681         Implements: [Events, Options],
4682
4683         options: {/*
4684                 onSort: $empty,
4685                 onStart: $empty,
4686                 onComplete: $empty,*/
4687                 snap: 4,
4688                 opacity: 1,
4689                 clone: false,
4690                 revert: false,
4691                 handle: false,
4692                 constrain: false
4693         },
4694
4695         initialize: function(lists, options){
4696                 this.setOptions(options);
4697                 this.elements = [];
4698                 this.lists = [];
4699                 this.idle = true;
4700                 
4701                 this.addLists($$($(lists) || lists));
4702                 if (!this.options.clone) this.options.revert = false;
4703                 if (this.options.revert) this.effect = new Fx.Morph(null, $merge({duration: 250, link: 'cancel'}, this.options.revert));
4704         },
4705
4706         attach: function(){
4707                 this.addLists(this.lists);
4708                 return this;
4709         },
4710
4711         detach: function(){
4712                 this.lists = this.removeLists(this.lists);
4713                 return this;
4714         },
4715
4716         addItems: function(){
4717                 Array.flatten(arguments).each(function(element){
4718                         this.elements.push(element);
4719                         var start = element.retrieve('sortables:start', this.start.bindWithEvent(this, element));
4720                         (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
4721                 }, this);
4722                 return this;
4723         },
4724
4725         addLists: function(){
4726                 Array.flatten(arguments).each(function(list){
4727                         this.lists.push(list);
4728                         this.addItems(list.getChildren());
4729                 }, this);
4730                 return this;
4731         },
4732
4733         removeItems: function(){
4734                 var elements = [];
4735                 Array.flatten(arguments).each(function(element){
4736                         elements.push(element);
4737                         this.elements.erase(element);
4738                         var start = element.retrieve('sortables:start');
4739                         (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
4740                 }, this);
4741                 return $$(elements);
4742         },
4743
4744         removeLists: function(){
4745                 var lists = [];
4746                 Array.flatten(arguments).each(function(list){
4747                         lists.push(list);
4748                         this.lists.erase(list);
4749                         this.removeItems(list.getChildren());
4750                 }, this);
4751                 return $$(lists);
4752         },
4753
4754         getClone: function(event, element){
4755                 if (!this.options.clone) return new Element('div').inject(document.body);
4756                 if ($type(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
4757                 return element.clone(true).setStyles({
4758                         'margin': '0px',
4759                         'position': 'absolute',
4760                         'visibility': 'hidden',
4761                         'width': element.getStyle('width')
4762                 }).inject(this.list).position(element.getPosition(element.getOffsetParent()));
4763         },
4764
4765         getDroppables: function(){
4766                 var droppables = this.list.getChildren();
4767                 if (!this.options.constrain) droppables = this.lists.concat(droppables).erase(this.list);
4768                 return droppables.erase(this.clone).erase(this.element);
4769         },
4770
4771         insert: function(dragging, element){
4772                 var where = 'inside';
4773                 if (this.lists.contains(element)){
4774                         this.list = element;
4775                         this.drag.droppables = this.getDroppables();
4776                 } else {
4777                         where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
4778                 }
4779                 this.element.inject(element, where);
4780                 this.fireEvent('sort', [this.element, this.clone]);
4781         },
4782
4783         start: function(event, element){
4784                 if (!this.idle) return;
4785                 this.idle = false;
4786                 this.element = element;
4787                 this.opacity = element.get('opacity');
4788                 this.list = element.getParent();
4789                 this.clone = this.getClone(event, element);
4790                 
4791                 this.drag = new Drag.Move(this.clone, {
4792                         snap: this.options.snap,
4793                         container: this.options.constrain && this.element.getParent(),
4794                         droppables: this.getDroppables(),
4795                         onSnap: function(){
4796                                 event.stop();
4797                                 this.clone.setStyle('visibility', 'visible');
4798                                 this.element.set('opacity', this.options.opacity || 0);
4799                                 this.fireEvent('start', [this.element, this.clone]);
4800                         }.bind(this),
4801                         onEnter: this.insert.bind(this),
4802                         onCancel: this.reset.bind(this),
4803                         onComplete: this.end.bind(this)
4804                 });
4805                 
4806                 this.clone.inject(this.element, 'before');
4807                 this.drag.start(event);
4808         },
4809
4810         end: function(){
4811                 this.drag.detach();
4812                 this.element.set('opacity', this.opacity);
4813                 if (this.effect){
4814                         var dim = this.element.getStyles('width', 'height');
4815                         var pos = this.clone.computePosition(this.element.getPosition(this.clone.offsetParent));
4816                         this.effect.element = this.clone;
4817                         this.effect.start({
4818                                 top: pos.top,
4819                                 left: pos.left,
4820                                 width: dim.width,
4821                                 height: dim.height,
4822                                 opacity: 0.25
4823                         }).chain(this.reset.bind(this));
4824                 } else {
4825                         this.reset();
4826                 }
4827         },
4828
4829         reset: function(){
4830                 this.idle = true;
4831                 this.clone.destroy();
4832                 this.fireEvent('complete', this.element);
4833         },
4834
4835         serialize: function(){
4836                 var params = Array.link(arguments, {modifier: Function.type, index: $defined});
4837                 var serial = this.lists.map(function(list){
4838                         return list.getChildren().map(params.modifier || function(element){
4839                                 return element.get('id');
4840                         }, this);
4841                 }, this);
4842                 
4843                 var index = params.index;
4844                 if (this.lists.length == 1) index = 0;
4845                 return $chk(index) && index >= 0 && index < this.lists.length ? serial[index] : serial;
4846         }
4847
4848 });
4849
4850 /*\r
4851 Script: Tips.js\r
4852         Class for creating nice tips that follow the mouse cursor when hovering an element.\r
4853 \r
4854 License:\r
4855         MIT-style license.\r
4856 */\r
4857 \r
4858 var Tips = new Class({\r
4859 \r
4860         Implements: [Events, Options],\r
4861 \r
4862         options: {\r
4863                 onShow: function(tip){\r
4864                         tip.setStyle('visibility', 'visible');\r
4865                 },\r
4866                 onHide: function(tip){\r
4867                         tip.setStyle('visibility', 'hidden');\r
4868                 },\r
4869                 showDelay: 100,\r
4870                 hideDelay: 100,\r
4871                 className: null,\r
4872                 offsets: {x: 16, y: 16},\r
4873                 fixed: false\r
4874         },\r
4875 \r
4876         initialize: function(){\r
4877                 var params = Array.link(arguments, {options: Object.type, elements: $defined});\r
4878                 this.setOptions(params.options || null);\r
4879                 \r
4880                 this.tip = new Element('div').inject(document.body);\r
4881                 \r
4882                 if (this.options.className) this.tip.addClass(this.options.className);\r
4883                 \r
4884                 var top = new Element('div', {'class': 'tip-top'}).inject(this.tip);\r
4885                 this.container = new Element('div', {'class': 'tip'}).inject(this.tip);\r
4886                 var bottom = new Element('div', {'class': 'tip-bottom'}).inject(this.tip);\r
4887 \r
4888                 this.tip.setStyles({position: 'absolute', top: 0, left: 0, visibility: 'hidden'});\r
4889                 \r
4890                 if (params.elements) this.attach(params.elements);\r
4891         },\r
4892         \r
4893         attach: function(elements){\r
4894                 $$(elements).each(function(element){\r
4895                         var title = element.retrieve('tip:title', element.get('title'));\r
4896                         var text = element.retrieve('tip:text', element.get('rel') || element.get('href'));\r
4897                         var enter = element.retrieve('tip:enter', this.elementEnter.bindWithEvent(this, element));\r
4898                         var leave = element.retrieve('tip:leave', this.elementLeave.bindWithEvent(this, element));\r
4899                         element.addEvents({mouseenter: enter, mouseleave: leave});\r
4900                         if (!this.options.fixed){\r
4901                                 var move = element.retrieve('tip:move', this.elementMove.bindWithEvent(this, element));\r
4902                                 element.addEvent('mousemove', move);\r
4903                         }\r
4904                         element.store('tip:native', element.get('title'));\r
4905                         element.erase('title');\r
4906                 }, this);\r
4907                 return this;\r
4908         },\r
4909         \r
4910         detach: function(elements){\r
4911                 $$(elements).each(function(element){\r
4912                         element.removeEvent('mouseenter', element.retrieve('tip:enter') || $empty);\r
4913                         element.removeEvent('mouseleave', element.retrieve('tip:leave') || $empty);\r
4914                         element.removeEvent('mousemove', element.retrieve('tip:move') || $empty);\r
4915                         element.eliminate('tip:enter').eliminate('tip:leave').eliminate('tip:move');\r
4916                         var original = element.retrieve('tip:native');\r
4917                         if (original) element.set('title', original);\r
4918                 });\r
4919                 return this;\r
4920         },\r
4921         \r
4922         elementEnter: function(event, element){\r
4923                 \r
4924                 $A(this.container.childNodes).each(Element.dispose);\r
4925                 \r
4926                 var title = element.retrieve('tip:title');\r
4927                 \r
4928                 if (title){\r
4929                         this.titleElement = new Element('div', {'class': 'tip-title'}).inject(this.container);\r
4930                         this.fill(this.titleElement, title);\r
4931                 }\r
4932                 \r
4933                 var text = element.retrieve('tip:text');\r
4934                 if (text){\r
4935                         this.textElement = new Element('div', {'class': 'tip-text'}).inject(this.container);\r
4936                         this.fill(this.textElement, text);\r
4937                 }\r
4938                 \r
4939                 this.timer = $clear(this.timer);\r
4940                 this.timer = this.show.delay(this.options.showDelay, this);\r
4941 \r
4942                 this.position((!this.options.fixed) ? event : {page: element.getPosition()});\r
4943         },\r
4944         \r
4945         elementLeave: function(event){\r
4946                 $clear(this.timer);\r
4947                 this.timer = this.hide.delay(this.options.hideDelay, this);\r
4948         },\r
4949         \r
4950         elementMove: function(event){\r
4951                 this.position(event);\r
4952         },\r
4953         \r
4954         position: function(event){\r
4955                 var size = window.getSize(), scroll = window.getScroll();\r
4956                 var tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight};\r
4957                 var props = {x: 'left', y: 'top'};\r
4958                 for (var z in props){\r
4959                         var pos = event.page[z] + this.options.offsets[z];\r
4960                         if ((pos + tip[z] - scroll[z]) > size[z]) pos = event.page[z] - this.options.offsets[z] - tip[z];\r
4961                         this.tip.setStyle(props[z], pos);\r
4962                 }\r
4963         },\r
4964         \r
4965         fill: function(element, contents){\r
4966                 (typeof contents == 'string') ? element.set('html', contents) : element.adopt(contents);\r
4967         },\r
4968 \r
4969         show: function(){\r
4970                 this.fireEvent('show', this.tip);\r
4971         },\r
4972 \r
4973         hide: function(){\r
4974                 this.fireEvent('hide', this.tip);\r
4975         }\r
4976 \r
4977 });
4978
4979 /*\r
4980 Script: SmoothScroll.js\r
4981         Class for creating a smooth scrolling effect to all internal links on the page.\r
4982 \r
4983 License:\r
4984         MIT-style license.\r
4985 */\r
4986 \r
4987 var SmoothScroll = new Class({\r
4988 \r
4989         Extends: Fx.Scroll,\r
4990 \r
4991         initialize: function(options, context){\r
4992                 context = context || document;\r
4993                 var doc = context.getDocument(), win = context.getWindow();\r
4994                 this.parent(doc, options);\r
4995                 this.links = (this.options.links) ? $$(this.options.links) : $$(doc.links);\r
4996                 var location = win.location.href.match(/^[^#]*/)[0] + '#';\r
4997                 this.links.each(function(link){\r
4998                         if (link.href.indexOf(location) != 0) return;\r
4999                         var anchor = link.href.substr(location.length);\r
5000                         if (anchor && $(anchor)) this.useLink(link, anchor);\r
5001                 }, this);\r
5002                 if (!Browser.Engine.webkit419) this.addEvent('complete', function(){\r
5003                         win.location.hash = this.anchor;\r
5004                 }, true);\r
5005         },\r
5006 \r
5007         useLink: function(link, anchor){\r
5008                 link.addEvent('click', function(event){\r
5009                         this.anchor = anchor;\r
5010                         this.toElement(anchor);\r
5011                         event.stop();\r
5012                 }.bind(this));\r
5013         }\r
5014 \r
5015 });
5016
5017 /*
5018 Script: Slider.js
5019         Class for creating horizontal and vertical slider controls.
5020
5021 License:
5022         MIT-style license.
5023 */
5024
5025 var Slider = new Class({
5026
5027         Implements: [Events, Options],
5028
5029         options: {/*
5030                 onChange: $empty,
5031                 onComplete: $empty,*/
5032                 onTick: function(position){
5033                         if(this.options.snap) position = this.toPosition(this.step);
5034                         this.knob.setStyle(this.property, position);
5035                 },
5036                 snap: false,
5037                 offset: 0,
5038                 range: false,
5039                 wheel: false,
5040                 steps: 100,
5041                 mode: 'horizontal'
5042         },
5043
5044         initialize: function(element, knob, options){
5045                 this.setOptions(options);
5046                 this.element = $(element);
5047                 this.knob = $(knob);
5048                 this.previousChange = this.previousEnd = this.step = -1;
5049                 this.element.addEvent('mousedown', this.clickedElement.bind(this));
5050                 if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement.bindWithEvent(this));
5051                 var offset, limit = {}, modifiers = {'x': false, 'y': false};
5052                 switch (this.options.mode){
5053                         case 'vertical':
5054                                 this.axis = 'y';
5055                                 this.property = 'top';
5056                                 offset = 'offsetHeight';
5057                                 break;
5058                         case 'horizontal':
5059                                 this.axis = 'x';
5060                                 this.property = 'left';
5061                                 offset = 'offsetWidth';
5062                 }
5063                 this.half = this.knob[offset] / 2;
5064                 this.full = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
5065                 this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
5066                 this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
5067                 this.range = this.max - this.min;
5068                 this.steps = this.options.steps || this.full;
5069                 this.stepSize = Math.abs(this.range) / this.steps;
5070                 this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;
5071                 
5072                 this.knob.setStyle('position', 'relative').setStyle(this.property, - this.options.offset);
5073                 modifiers[this.axis] = this.property;
5074                 limit[this.axis] = [- this.options.offset, this.full - this.options.offset];
5075                 this.drag = new Drag(this.knob, {
5076                         snap: 0,
5077                         limit: limit,
5078                         modifiers: modifiers,
5079                         onDrag: this.draggedKnob.bind(this),
5080                         onStart: this.draggedKnob.bind(this),
5081                         onComplete: function(){
5082                                 this.draggedKnob();
5083                                 this.end();
5084                         }.bind(this)
5085                 });
5086                 if (this.options.snap) {
5087                         this.drag.options.grid = Math.ceil(this.stepWidth);
5088                         this.drag.options.limit[this.axis][1] = this.full;
5089                 }
5090         },
5091
5092         set: function(step){
5093                 if (!((this.range > 0) ^ (step < this.min))) step = this.min;
5094                 if (!((this.range > 0) ^ (step > this.max))) step = this.max;
5095                 
5096                 this.step = Math.round(step);
5097                 this.checkStep();
5098                 this.end();
5099                 this.fireEvent('tick', this.toPosition(this.step));
5100                 return this;
5101         },
5102
5103         clickedElement: function(event){
5104                 var dir = this.range < 0 ? -1 : 1;
5105                 var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
5106                 position = position.limit(-this.options.offset, this.full -this.options.offset);
5107                 
5108                 this.step = Math.round(this.min + dir * this.toStep(position));
5109                 this.checkStep();
5110                 this.end();
5111                 this.fireEvent('tick', position);
5112         },
5113         
5114         scrolledElement: function(event){
5115                 var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
5116                 this.set(mode ? this.step - this.stepSize : this.step + this.stepSize);
5117                 event.stop();
5118         },
5119
5120         draggedKnob: function(){
5121                 var dir = this.range < 0 ? -1 : 1;
5122                 var position = this.drag.value.now[this.axis];
5123                 position = position.limit(-this.options.offset, this.full -this.options.offset);
5124                 this.step = Math.round(this.min + dir * this.toStep(position));
5125                 this.checkStep();
5126         },
5127
5128         checkStep: function(){
5129                 if (this.previousChange != this.step){
5130                         this.previousChange = this.step;
5131                         this.fireEvent('change', this.step);
5132                 }
5133         },
5134
5135         end: function(){
5136                 if (this.previousEnd !== this.step){
5137                         this.previousEnd = this.step;
5138                         this.fireEvent('complete', this.step + '');
5139                 }
5140         },
5141
5142         toStep: function(position){
5143                 var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
5144                 return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
5145         },
5146
5147         toPosition: function(step){
5148                 return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
5149         }
5150
5151 });
5152
5153 /*
5154 Script: Scroller.js
5155         Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries.
5156
5157 License:
5158         MIT-style license.
5159 */
5160
5161 var Scroller = new Class({
5162
5163         Implements: [Events, Options],
5164
5165         options: {
5166                 area: 20,
5167                 velocity: 1,
5168                 onChange: function(x, y){
5169                         this.element.scrollTo(x, y);
5170                 }
5171         },
5172
5173         initialize: function(element, options){
5174                 this.setOptions(options);
5175                 this.element = $(element);
5176                 this.listener = ($type(this.element) != 'element') ? $(this.element.getDocument().body) : this.element;
5177                 this.timer = null;
5178                 this.coord = this.getCoords.bind(this);
5179         },
5180
5181         start: function(){
5182                 this.listener.addEvent('mousemove', this.coord);
5183         },
5184
5185         stop: function(){
5186                 this.listener.removeEvent('mousemove', this.coord);
5187                 this.timer = $clear(this.timer);
5188         },
5189
5190         getCoords: function(event){
5191                 this.page = (this.listener.get('tag') == 'body') ? event.client : event.page;
5192                 if (!this.timer) this.timer = this.scroll.periodical(50, this);
5193         },
5194
5195         scroll: function(){
5196                 var size = this.element.getSize(), scroll = this.element.getScroll(), pos = this.element.getPosition(), change = {'x': 0, 'y': 0};
5197                 for (var z in this.page){
5198                         if (this.page[z] < (this.options.area + pos[z]) && scroll[z] != 0)
5199                                 change[z] = (this.page[z] - this.options.area - pos[z]) * this.options.velocity;
5200                         else if (this.page[z] + this.options.area > (size[z] + pos[z]) && size[z] + size[z] != scroll[z])
5201                                 change[z] = (this.page[z] - size[z] + this.options.area - pos[z]) * this.options.velocity;
5202                 }
5203                 if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x, scroll.y + change.y]);
5204         }
5205
5206 });
5207
5208 /*\r
5209 Script: Accordion.js\r
5210         An Fx.Elements extension which allows you to easily create accordion type controls.\r
5211 \r
5212 License:\r
5213         MIT-style license.\r
5214 */\r
5215 \r
5216 var Accordion = new Class({\r
5217 \r
5218         Extends: Fx.Elements,\r
5219 \r
5220         options: {/*\r
5221                 onActive: $empty,\r
5222                 onBackground: $empty,*/\r
5223                 display: 0,\r
5224                 show: false,\r
5225                 height: true,\r
5226                 width: false,\r
5227                 opacity: true,\r
5228                 fixedHeight: false,\r
5229                 fixedWidth: false,\r
5230                 wait: false,\r
5231                 alwaysHide: false\r
5232         },\r
5233 \r
5234         initialize: function(){\r
5235                 var params = Array.link(arguments, {'container': Element.type, 'options': Object.type, 'togglers': $defined, 'elements': $defined});\r
5236                 this.parent(params.elements, params.options);\r
5237                 this.togglers = $$(params.togglers);\r
5238                 this.container = $(params.container);\r
5239                 this.previous = -1;\r
5240                 if (this.options.alwaysHide) this.options.wait = true;\r
5241                 if ($chk(this.options.show)){\r
5242                         this.options.display = false;\r
5243                         this.previous = this.options.show;\r
5244                 }\r
5245                 if (this.options.start){\r
5246                         this.options.display = false;\r
5247                         this.options.show = false;\r
5248                 }\r
5249                 this.effects = {};\r
5250                 if (this.options.opacity) this.effects.opacity = 'fullOpacity';\r
5251                 if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';\r
5252                 if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';\r
5253                 for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);\r
5254                 this.elements.each(function(el, i){\r
5255                         if (this.options.show === i){\r
5256                                 this.fireEvent('active', [this.togglers[i], el]);\r
5257                         } else {\r
5258                                 for (var fx in this.effects) el.setStyle(fx, 0);\r
5259                         }\r
5260                 }, this);\r
5261                 if ($chk(this.options.display)) this.display(this.options.display);\r
5262         },\r
5263 \r
5264         addSection: function(toggler, element, pos){\r
5265                 toggler = $(toggler);\r
5266                 element = $(element);\r
5267                 var test = this.togglers.contains(toggler);\r
5268                 var len = this.togglers.length;\r
5269                 this.togglers.include(toggler);\r
5270                 this.elements.include(element);\r
5271                 if (len && (!test || pos)){\r
5272                         pos = $pick(pos, len - 1);\r
5273                         toggler.inject(this.togglers[pos], 'before');\r
5274                         element.inject(toggler, 'after');\r
5275                 } else if (this.container && !test){\r
5276                         toggler.inject(this.container);\r
5277                         element.inject(this.container);\r
5278                 }\r
5279                 var idx = this.togglers.indexOf(toggler);\r
5280                 toggler.addEvent('click', this.display.bind(this, idx));\r
5281                 if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});\r
5282                 if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});\r
5283                 element.fullOpacity = 1;\r
5284                 if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;\r
5285                 if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;\r
5286                 element.setStyle('overflow', 'hidden');\r
5287                 if (!test){\r
5288                         for (var fx in this.effects) element.setStyle(fx, 0);\r
5289                 }\r
5290                 return this;\r
5291         },\r
5292 \r
5293         display: function(index){\r
5294                 index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;\r
5295                 if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;\r
5296                 this.previous = index;\r
5297                 var obj = {};\r
5298                 this.elements.each(function(el, i){\r
5299                         obj[i] = {};\r
5300                         var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight > 0));\r
5301                         this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);\r
5302                         for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];\r
5303                 }, this);\r
5304                 return this.start(obj);\r
5305         }\r
5306 \r
5307 });