PageSourceSearch

https://www.taaonline.net/plugins/system/mtupgrade/mootools.js

js taaonline.net collected 2026-10-02 05:05:32 UTC 181,556 bytes, 7,188 lines download raw bytes

1/*
2---
3
4name: Core
5
6description: The core of MooTools, contains all the base functions and the Native and Hash implementations. Required by all the other scripts.
7
8license: MIT-style license.
9
10copyright: Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/).
11
12authors: The MooTools production team (http://mootools.net/developers/)
13
14inspiration:
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
18provides: [MooTools, Native, Hash.base, Array.each, $util]
19
20...
21*/
22
23var MooTools = {
24	'version': '1.2.5',
25	'build': '008d8f0f2fcc2044e54fdd3635341aaab274e757'
26};
27
28var Native = function(options){
29	options = options || {};
30	var name = options.name;
31	var legacy = options.legacy;
32	var protect = options.protect;
33	var methods = options.implement;
34	var generics = options.generics;
35	var initialize = options.initialize;
36	var afterImplement = options.afterImplement || function(){};
37	var object = initialize || legacy;
38	generics = generics !== false;
39
40	object.constructor = Native;
41	object.$family = {name: 'native'};
42	if (legacy && initialize) object.prototype = legacy.prototype;
43	object.prototype.constructor = object;
44
45	if (name){
46		var family = name.toLowerCase();
47		object.prototype.$family = {name: family};
48		Native.typize(object, family);
49	}
50
51	var add = function(obj, name, method, force){
52		if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
53		if (generics) Native.genericize(obj, name, protect);
54		afterImplement.call(obj, name, method);
55		return obj;
56	};
57
58	object.alias = function(a1, a2, a3){
59		if (typeof a1 == 'string'){
60			var pa1 = this.prototype[a1];
61			if ((a1 = pa1)) return add(this, a2, a1, a3);
62		}
63		for (var a in a1) this.alias(a, a1[a], a2);
64		return this;
65	};
66
67	object.implement = function(a1, a2, a3){
68		if (typeof a1 == 'string') return add(this, a1, a2, a3);
69		for (var p in a1) add(this, p, a1[p], a2);
70		return this;
71	};
72
73	if (methods) object.implement(methods);
74
75	return object;
76};
77
78Native.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
85Native.implement = function(objects, properties){
86	for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
87};
88
89Native.typize = function(object, family){
90	if (!object.type) object.type = function(item){
91		return ($type(item) === family);
92	};
93};
94
95(function(){
96	var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String};
97	for (var n in natives) new Native({name: n, initialize: natives[n], protect: true});
98
99	var types = {'boolean': Boolean, 'native': Native, 'object': Object};
100	for (var t in types) Native.typize(types[t], t);
101
102	var generics = {
103		'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"],
104		'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
105	};
106	for (var g in generics){
107		for (var i = generics[g].length; i--;) Native.genericize(natives[g], generics[g][i], true);
108	}
109})();
110
111var Hash = new Native({
112
113	name: 'Hash',
114
115	initialize: function(object){
116		if ($type(object) == 'hash') object = $unlink(object.getClean());
117		for (var key in object) this[key] = object[key];
118		return this;
119	}
120
121});
122
123Hash.implement({
124
125	forEach: function(fn, bind){
126		for (var key in this){
127			if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
128		}
129	},
130
131	getClean: function(){
132		var clean = {};
133		for (var key in this){
134			if (this.hasOwnProperty(key)) clean[key] = this[key];
135		}
136		return clean;
137	},
138
139	getLength: function(){
140		var length = 0;
141		for (var key in this){
142			if (this.hasOwnProperty(key)) length++;
143		}
144		return length;
145	}
146
147});
148
149Hash.alias('forEach', 'each');
150
151//For support in IE 8 for Object.keys
152// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
153if (!Object.keys) {
154    Object.keys = (function () {
155        'use strict';
156        var hasOwnProperty = Object.prototype.hasOwnProperty,
157            hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
158            dontEnums = [
159                'toString',
160                'toLocaleString',
161                'valueOf',
162                'hasOwnProperty',
163                'isPrototypeOf',
164                'propertyIsEnumerable',
165                'constructor'
166            ],
167            dontEnumsLength = dontEnums.length;
168
169        return function (obj) {
170            if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
171                throw new TypeError('Object.keys called on non-object');
172            }
173
174            var result = [], prop, i;
175
176            for (prop in obj) {
177                if (hasOwnProperty.call(obj, prop)) {
178                    result.push(prop);
179                }
180            }
181
182            if (hasDontEnumBug) {
183                for (i = 0; i < dontEnumsLength; i++) {
184                    if (hasOwnProperty.call(obj, dontEnums[i])) {
185                        result.push(dontEnums[i]);
186                    }
187                }
188            }
189            return result;
190        };
191    }());
192}
193
194
195Array.implement({
196
197	forEach: function(fn, bind){
198		for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
199	}
200
201});
202
203Array.alias('forEach', 'each');
204
205function $A(iterable){
206	if (iterable.item){
207		var l = iterable.length, array = new Array(l);
208		while (l--) array[l] = iterable[l];
209		return array;
210	}
211	return Array.prototype.slice.call(iterable);
212};
213
214function $arguments(i){
215	return function(){
216		return arguments[i];
217	};
218};
219
220function $chk(obj){
221	return !!(obj || obj === 0);
222};
223
224function $clear(timer){
225	clearTimeout(timer);
226	clearInterval(timer);
227	return null;
228};
229
230function $defined(obj){
231	return (obj != undefined);
232};
233
234function $each(iterable, fn, bind){
235	var type = $type(iterable);
236	((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
237};
238
239function $empty(){};
240
241function $extend(original, extended){
242	for (var key in (extended || {})) original[key] = extended[key];
243	return original;
244};
245
246function $H(object){
247	return new Hash(object);
248};
249
250function $lambda(value){
251	return ($type(value) == 'function') ? value : function(){
252		return value;
253	};
254};
255
256function $merge(){
257	var args = Array.slice(arguments);
258	args.unshift({});
259	return $mixin.apply(null, args);
260};
261
262function $mixin(mix){
263	for (var i = 1, l = arguments.length; i < l; i++){
264		var object = arguments[i];
265		if ($type(object) != 'object') continue;
266		for (var key in object){
267			var op = object[key], mp = mix[key];
268			mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $mixin(mp, op) : $unlink(op);
269		}
270	}
271	return mix;
272};
273
274function $pick(){
275	for (var i = 0, l = arguments.length; i < l; i++){
276		if (arguments[i] != undefined) return arguments[i];
277	}
278	return null;
279};
280
281function $random(min, max){
282	return Math.floor(Math.random() * (max - min + 1) + min);
283};
284
285function $splat(obj){
286	var type = $type(obj);
287	return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
288};
289
290var $time = Date.now || function(){
291	return +new Date;
292};
293
294function $try(){
295	for (var i = 0, l = arguments.length; i < l; i++){
296		try {
297			return arguments[i]();
298		} catch(e){}
299	}
300	return null;
301};
302
303function $type(obj){
304	if (obj == undefined) return false;
305	if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
306	if (obj.nodeName){
307		switch (obj.nodeType){
308			case 1: return 'element';
309			case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
310		}
311	} else if (typeof obj.length == 'number'){
312		if (obj.callee) return 'arguments';
313		else if (obj.item) return 'collection';
314	}
315	return typeof obj;
316};
317
318function $unlink(object){
319	var unlinked;
320	switch ($type(object)){
321		case 'object':
322			unlinked = {};
323			for (var p in object) unlinked[p] = $unlink(object[p]);
324		break;
325		case 'hash':
326			unlinked = new Hash(object);
327		break;
328		case 'array':
329			unlinked = [];
330			for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
331		break;
332		default: return object;
333	}
334	return unlinked;
335};
336
337
338/*
339---
340
341name: Browser
342
343description: The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.
344
345license: MIT-style license.
346
347requires: [Native, $util]
348
349provides: [Browser, Window, Document, $exec]
350
351...
352*/
353
354var Browser = $merge({
355
356	Engine: {name: 'unknown', version: 0},
357
358	Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},
359
360	Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},
361
362	Plugins: {},
363
364	Engines: {
365
366		presto: function(){
367			return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
368		},
369
370		trident: function(){
371			return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
372		},
373
374		webkit: function(){
375			return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
376		},
377
378		gecko: function(){
379			return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18);
380		}
381
382	}
383
384}, Browser || {});
385
386Browser.Platform[Browser.Platform.name] = true;
387
388Browser.detect = function(){
389
390	for (var engine in this.Engines){
391		var version = this.Engines[engine]();
392		if (version){
393			this.Engine = {name: engine, version: version};
394			this.Engine[engine] = this.Engine[engine + version] = true;
395			break;
396		}
397	}
398
399	return {name: engine, version: version};
400
401};
402
403Browser.detect();
404
405Browser.Request = function(){
406	return $try(function(){
407		return new XMLHttpRequest();
408	}, function(){
409		return new ActiveXObject('MSXML2.XMLHTTP');
410	}, function(){
411		return new ActiveXObject('Microsoft.XMLHTTP');
412	});
413};
414
415Browser.Features.xhr = !!(Browser.Request());
416
417Browser.Plugins.Flash = (function(){
418	var version = ($try(function(){
419		return navigator.plugins['Shockwave Flash'].description;
420	}, function(){
421		return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
422	}) || '0 r0').match(/\d+/g);
423	return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
424})();
425
426function $exec(text){
427	if (!text) return text;
428	if (window.execScript){
429		window.execScript(text);
430	} else {
431		var script = document.createElement('script');
432		script.setAttribute('type', 'text/javascript');
433		script[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : 'text'] = text;
434		document.head.appendChild(script);
435		document.head.removeChild(script);
436	}
437	return text;
438};
439
440Native.UID = 1;
441
442var $uid = (Browser.Engine.trident) ? function(item){
443	return (item.uid || (item.uid = [Native.UID++]))[0];
444} : function(item){
445	return item.uid || (item.uid = Native.UID++);
446};
447
448var Window = new Native({
449
450	name: 'Window',
451
452	legacy: (Browser.Engine.trident) ? null: window.Window,
453
454	initialize: function(win){
455		$uid(win);
456		if (!win.Element){
457			win.Element = $empty;
458			if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2
459			win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {};
460		}
461		win.document.window = win;
462		return $extend(win, Window.Prototype);
463	},
464
465	afterImplement: function(property, value){
466		window[property] = Window.Prototype[property] = value;
467	}
468
469});
470
471Window.Prototype = {$family: {name: 'window'}};
472
473new Window(window);
474
475var Document = new Native({
476
477	name: 'Document',
478
479	legacy: (Browser.Engine.trident) ? null: window.Document,
480
481	initialize: function(doc){
482		$uid(doc);
483		doc.head = doc.getElementsByTagName('head')[0];
484		doc.html = doc.getElementsByTagName('html')[0];
485		if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function(){
486			doc.execCommand("BackgroundImageCache", false, true);
487		});
488		if (Browser.Engine.trident) doc.window.attachEvent('onunload', function(){
489			doc.window.detachEvent('onunload', arguments.callee);
490			doc.head = doc.html = doc.window = null;
491		});
492		return $extend(doc, Document.Prototype);
493	},
494
495	afterImplement: function(property, value){
496		document[property] = Document.Prototype[property] = value;
497	}
498
499});
500
501Document.Prototype = {$family: {name: 'document'}};
502
503new Document(document);
504
505
506/*
507---
508
509name: Array
510
511description: Contains Array Prototypes like each, contains, and erase.
512
513license: MIT-style license.
514
515requires: [$util, Array.each]
516
517provides: Array
518
519...
520*/
521
522Array.implement({
523
524	every: function(fn, bind){
525		for (var i = 0, l = this.length; i < l; i++){
526			if (!fn.call(bind, this[i], i, this)) return false;
527		}
528		return true;
529	},
530
531	filter: function(fn, bind){
532		var results = [];
533		for (var i = 0, l = this.length; i < l; i++){
534			if (fn.call(bind, this[i], i, this)) results.push(this[i]);
535		}
536		return results;
537	},
538
539	clean: function(){
540		return this.filter($defined);
541	},
542
543	indexOf: function(item, from){
544		var len = this.length;
545		for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
546			if (this[i] === item) return i;
547		}
548		return -1;
549	},
550
551	map: function(fn, bind){
552		var results = [];
553		for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this);
554		return results;
555	},
556
557	some: function(fn, bind){
558		for (var i = 0, l = this.length; i < l; i++){
559			if (fn.call(bind, this[i], i, this)) return true;
560		}
561		return false;
562	},
563
564	associate: function(keys){
565		var obj = {}, length = Math.min(this.length, keys.length);
566		for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
567		return obj;
568	},
569
570	link: function(object){
571		var result = {};
572		for (var i = 0, l = this.length; i < l; i++){
573			for (var key in object){
574				if (object[key](this[i])){
575					result[key] = this[i];
576					delete object[key];
577					break;
578				}
579			}
580		}
581		return result;
582	},
583
584	contains: function(item, from){
585		return this.indexOf(item, from) != -1;
586	},
587
588	extend: function(array){
589		for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
590		return this;
591	},
592	
593	getLast: function(){
594		return (this.length) ? this[this.length - 1] : null;
595	},
596
597	getRandom: function(){
598		return (this.length) ? this[$random(0, this.length - 1)] : null;
599	},
600
601	include: function(item){
602		if (!this.contains(item)) this.push(item);
603		return this;
604	},
605
606	combine: function(array){
607		for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
608		return this;
609	},
610
611	erase: function(item){
612		for (var i = this.length; i--; i){
613			if (this[i] === item) this.splice(i, 1);
614		}
615		return this;
616	},
617
618	empty: function(){
619		this.length = 0;
620		return this;
621	},
622
623	flatten: function(){
624		var array = [];
625		for (var i = 0, l = this.length; i < l; i++){
626			var type = $type(this[i]);
627			if (!type) continue;
628			array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]);
629		}
630		return array;
631	},
632
633	hexToRgb: function(array){
634		if (this.length != 3) return null;
635		var rgb = this.map(function(value){
636			if (value.length == 1) value += value;
637			return value.toInt(16);
638		});
639		return (array) ? rgb : 'rgb(' + rgb + ')';
640	},
641
642	rgbToHex: function(array){
643		if (this.length < 3) return null;
644		if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
645		var hex = [];
646		for (var i = 0; i < 3; i++){
647			var bit = (this[i] - 0).toString(16);
648			hex.push((bit.length == 1) ? '0' + bit : bit);
649		}
650		return (array) ? hex : '#' + hex.join('');
651	}
652
653});
654
655
656/*
657---
658
659name: Function
660
661description: Contains Function Prototypes like create, bind, pass, and delay.
662
663license: MIT-style license.
664
665requires: [Native, $util]
666
667provides: Function
668
669...
670*/
671
672try {
673	//delete Function.prototype.bind;
674} catch(e){}
675
676Function.implement({
677
678	extend: function(properties){
679		for (var property in properties) this[property] = properties[property];
680		return this;
681	},
682
683	create: function(options){
684		var self = this;
685		options = options || {};
686		return function(event){
687			var args = options.arguments;
688			args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
689			if (options.event) args = [event || window.event].extend(args);
690			var returns = function(){
691				return self.apply(options.bind || null, args);
692			};
693			if (options.delay) return setTimeout(returns, options.delay);
694			if (options.periodical) return setInterval(returns, options.periodical);
695			if (options.attempt) return $try(returns);
696			return returns();
697		};
698	},
699
700	run: function(args, bind){
701		return this.apply(bind, $splat(args));
702	},
703
704	pass: function(args, bind){
705		return this.create({bind: bind, arguments: args});
706	},
707
708	bind: function(bind, args){
709		return this.create({bind: bind, arguments: args});
710	},
711
712	bindWithEvent: function(bind, args){
713		return this.create({bind: bind, arguments: args, event: true});
714	},
715
716	attempt: function(args, bind){
717		return this.create({bind: bind, arguments: args, attempt: true})();
718	},
719
720	delay: function(delay, bind, args){
721		return this.create({bind: bind, arguments: args, delay: delay})();
722	},
723
724	periodical: function(periodical, bind, args){
725		return this.create({bind: bind, arguments: args, periodical: periodical})();
726	}
727
728});
729
730
731/*
732---
733
734name: Number
735
736description: Contains Number Prototypes like limit, round, times, and ceil.
737
738license: MIT-style license.
739
740requires: [Native, $util]
741
742provides: Number
743
744...
745*/
746
747Number.implement({
748
749	limit: function(min, max){
750		return Math.min(max, Math.max(min, this));
751	},
752
753	round: function(precision){
754		precision = Math.pow(10, precision || 0);
755		return Math.round(this * precision) / precision;
756	},
757
758	times: function(fn, bind){
759		for (var i = 0; i < this; i++) fn.call(bind, i, this);
760	},
761
762	toFloat: function(){
763		return parseFloat(this);
764	},
765
766	toInt: function(base){
767		return parseInt(this, base || 10);
768	}
769
770});
771
772Number.alias('times', 'each');
773
774(function(math){
775	var methods = {};
776	math.each(function(name){
777		if (!Number[name]) methods[name] = function(){
778			return Math[name].apply(null, [this].concat($A(arguments)));
779		};
780	});
781	Number.implement(methods);
782})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
783
784
785/*
786---
787
788name: String
789
790description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
791
792license: MIT-style license.
793
794requires: Native
795
796provides: String
797
798...
799*/
800
801String.implement({
802
803	test: function(regex, params){
804		return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
805	},
806
807	contains: function(string, separator){
808		return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
809	},
810
811	trim: function(){
812		return this.replace(/^\s+|\s+$/g, '');
813	},
814
815	clean: function(){
816		return this.replace(/\s+/g, ' ').trim();
817	},
818
819	camelCase: function(){
820		return this.replace(/-\D/g, function(match){
821			return match.charAt(1).toUpperCase();
822		});
823	},
824
825	hyphenate: function(){
826		return this.replace(/[A-Z]/g, function(match){
827			return ('-' + match.charAt(0).toLowerCase());
828		});
829	},
830
831	capitalize: function(){
832		return this.replace(/\b[a-z]/g, function(match){
833			return match.toUpperCase();
834		});
835	},
836
837	escapeRegExp: function(){
838		return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
839	},
840
841	toInt: function(base){
842		return parseInt(this, base || 10);
843	},
844
845	toFloat: function(){
846		return parseFloat(this);
847	},
848
849	hexToRgb: function(array){
850		var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
851		return (hex) ? hex.slice(1).hexToRgb(array) : null;
852	},
853
854	rgbToHex: function(array){
855		var rgb = this.match(/\d{1,3}/g);
856		return (rgb) ? rgb.rgbToHex(array) : null;
857	},
858
859	stripScripts: function(option){
860		var scripts = '';
861		var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
862			scripts += arguments[1] + '\n';
863			return '';
864		});
865		if (option === true) $exec(scripts);
866		else if ($type(option) == 'function') option(scripts, text);
867		return text;
868	},
869
870	substitute: function(object, regexp){
871		return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
872			if (match.charAt(0) == '\\') return match.slice(1);
873			return (object[name] != undefined) ? object[name] : '';
874		});
875	}
876
877});
878
879
880/*
881---
882
883name: Hash
884
885description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.
886
887license: MIT-style license.
888
889requires: Hash.base
890
891provides: Hash
892
893...
894*/
895
896Hash.implement({
897
898	has: Object.prototype.hasOwnProperty,
899
900	keyOf: function(value){
901		for (var key in this){
902			if (this.hasOwnProperty(key) && this[key] === value) return key;
903		}
904		return null;
905	},
906
907	hasValue: function(value){
908		return (Hash.keyOf(this, value) !== null);
909	},
910
911	extend: function(properties){
912		Hash.each(properties || {}, function(value, key){
913			Hash.set(this, key, value);
914		}, this);
915		return this;
916	},
917
918	combine: function(properties){
919		Hash.each(properties || {}, function(value, key){
920			Hash.include(this, key, value);
921		}, this);
922		return this;
923	},
924
925	erase: function(key){
926		if (this.hasOwnProperty(key)) delete this[key];
927		return this;
928	},
929
930	get: function(key){
931		return (this.hasOwnProperty(key)) ? this[key] : null;
932	},
933
934	set: function(key, value){
935		if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
936		return this;
937	},
938
939	empty: function(){
940		Hash.each(this, function(value, key){
941			delete this[key];
942		}, this);
943		return this;
944	},
945
946	include: function(key, value){
947		if (this[key] == undefined) this[key] = value;
948		return this;
949	},
950
951	map: function(fn, bind){
952		var results = new Hash;
953		Hash.each(this, function(value, key){
954			results.set(key, fn.call(bind, value, key, this));
955		}, this);
956		return results;
957	},
958
959	filter: function(fn, bind){
960		var results = new Hash;
961		Hash.each(this, function(value, key){
962			if (fn.call(bind, value, key, this)) results.set(key, value);
963		}, this);
964		return results;
965	},
966
967	every: function(fn, bind){
968		for (var key in this){
969			if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
970		}
971		return true;
972	},
973
974	some: function(fn, bind){
975		for (var key in this){
976			if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
977		}
978		return false;
979	},
980
981	getKeys: function(){
982		var keys = [];
983		Hash.each(this, function(value, key){
984			keys.push(key);
985		});
986		return keys;
987	},
988
989	getValues: function(){
990		var values = [];
991		Hash.each(this, function(value){
992			values.push(value);
993		});
994		return values;
995	},
996
997	toQueryString: function(base){
998		var queryString = [];
999		Hash.each(this, function(value, key){
1000			if (base) key = base + '[' + key + ']';
1001			var result;
1002			switch ($type(value)){
1003				case 'object': result = Hash.toQueryString(value, key); break;
1004				case 'array':
1005					var qs = {};
1006					value.each(function(val, i){
1007						qs[i] = val;
1008					});
1009					result = Hash.toQueryString(qs, key);
1010				break;
1011				default: result = key + '=' + encodeURIComponent(value);
1012			}
1013			if (value != undefined) queryString.push(result);
1014		});
1015
1016		return queryString.join('&');
1017	}
1018
1019});
1020
1021Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});
1022
1023
1024/*
1025---
1026
1027name: Event
1028
1029description: Contains the Event Class, to make the event object cross-browser.
1030
1031license: MIT-style license.
1032
1033requires: [Window, Document, Hash, Array, Function, String]
1034
1035provides: Event
1036
1037...
1038*/
1039
1040var Event = new Native({
1041
1042	name: 'Event',
1043
1044	initialize: function(event, win){
1045		win = win || window;
1046		var doc = win.document;
1047		event = event || win.event;
1048		if (event.$extended) return event;
1049		this.$extended = true;
1050		var type = event.type;
1051		var target = event.target || event.srcElement;
1052		while (target && target.nodeType == 3) target = target.parentNode;
1053
1054		if (type.test(/key/)){
1055			var code = event.which || event.keyCode;
1056			var key = Event.Keys.keyOf(code);
1057			if (type == 'keydown'){
1058				var fKey = code - 111;
1059				if (fKey > 0 && fKey < 13) key = 'f' + fKey;
1060			}
1061			key = key || String.fromCharCode(code).toLowerCase();
1062		} else if (type.match(/(click|mouse|menu)/i)){
1063			doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
1064			var page = {
1065				x: event.pageX || event.clientX + doc.scrollLeft,
1066				y: event.pageY || event.clientY + doc.scrollTop
1067			};
1068			var client = {
1069				x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
1070				y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
1071			};
1072			if (type.match(/DOMMouseScroll|mousewheel/)){
1073				var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
1074			}
1075			var rightClick = (event.which == 3) || (event.button == 2);
1076			var related = null;
1077			if (type.match(/over|out/)){
1078				switch (type){
1079					case 'mouseover': related = event.relatedTarget || event.fromElement; break;
1080					case 'mouseout': related = event.relatedTarget || event.toElement;
1081				}
1082				if (!(function(){
1083					while (related && related.nodeType == 3) related = related.parentNode;
1084					return true;
1085				}).create({attempt: Browser.Engine.gecko})()) related = false;
1086			}
1087		}
1088
1089		return $extend(this, {
1090			event: event,
1091			type: type,
1092
1093			page: page,
1094			client: client,
1095			rightClick: rightClick,
1096
1097			wheel: wheel,
1098
1099			relatedTarget: related,
1100			target: target,
1101
1102			code: code,
1103			key: key,
1104
1105			shift: event.shiftKey,
1106			control: event.ctrlKey,
1107			alt: event.altKey,
1108			meta: event.metaKey
1109		});
1110	}
1111
1112});
1113
1114Event.Keys = new Hash({
1115	'enter': 13,
1116	'up': 38,
1117	'down': 40,
1118	'left': 37,
1119	'right': 39,
1120	'esc': 27,
1121	'space': 32,
1122	'backspace': 8,
1123	'tab': 9,
1124	'delete': 46
1125});
1126
1127Event.implement({
1128
1129	stop: function(){
1130		return this.stopPropagation().preventDefault();
1131	},
1132
1133	stopPropagation: function(){
1134		if (this.event.stopPropagation) this.event.stopPropagation();
1135		else this.event.cancelBubble = true;
1136		return this;
1137	},
1138
1139	preventDefault: function(){
1140		if (this.event.preventDefault) this.event.preventDefault();
1141		else this.event.returnValue = false;
1142		return this;
1143	}
1144
1145});
1146
1147
1148/*
1149---
1150
1151name: Class
1152
1153description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
1154
1155license: MIT-style license.
1156
1157requires: [$util, Native, Array, String, Function, Number, Hash]
1158
1159provides: Class
1160
1161...
1162*/
1163
1164function Class(params){
1165	
1166	if (params instanceof Function) params = {initialize: params};
1167	
1168	var newClass = function(){
1169		Object.reset(this);
1170		if (newClass._prototyping) return this;
1171		this._current = $empty;
1172		var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
1173		delete this._current; delete this.caller;
1174		return value;
1175	}.extend(this);
1176	
1177	newClass.implement(params);
1178	
1179	newClass.constructor = Class;
1180	newClass.prototype.constructor = newClass;
1181
1182	return newClass;
1183
1184};
1185
1186Function.prototype.protect = function(){
1187	this._protected = true;
1188	return this;
1189};
1190
1191Object.reset = function(object, key){
1192		
1193	if (key == null){
1194		for (var p in object) Object.reset(object, p);
1195		return object;
1196	}
1197	
1198	delete object[key];
1199	
1200	switch ($type(object[key])){
1201		case 'object':
1202			var F = function(){};
1203			F.prototype = object[key];
1204			var i = new F;
1205			object[key] = Object.reset(i);
1206		break;
1207		case 'array': object[key] = $unlink(object[key]); break;
1208	}
1209	
1210	return object;
1211	
1212};
1213
1214new Native({name: 'Class', initialize: Class}).extend({
1215
1216	instantiate: function(F){
1217		F._prototyping = true;
1218		var proto = new F;
1219		delete F._prototyping;
1220		return proto;
1221	},
1222	
1223	wrap: function(self, key, method){
1224		if (method._origin) method = method._origin;
1225		
1226		return function(){
1227			if (method._protected && this._current == null) throw new Error('The method "' + key + '" cannot be called.');
1228			var caller = this.caller, current = this._current;
1229			this.caller = current; this._current = arguments.callee;
1230			var result = method.apply(this, arguments);
1231			this._current = current; this.caller = caller;
1232			return result;
1233		}.extend({_owner: self, _origin: method, _name: key});
1234
1235	}
1236	
1237});
1238
1239Class.implement({
1240	
1241	implement: function(key, value){
1242		
1243		if ($type(key) == 'object'){
1244			for (var p in key) this.implement(p, key[p]);
1245			return this;
1246		}
1247		
1248		var mutator = Class.Mutators[key];
1249		
1250		if (mutator){
1251			value = mutator.call(this, value);
1252			if (value == null) return this;
1253		}
1254		
1255		var proto = this.prototype;
1256
1257		switch ($type(value)){
1258			
1259			case 'function':
1260				if (value._hidden) return this;
1261				proto[key] = Class.wrap(this, key, value);
1262			break;
1263			
1264			case 'object':
1265				var previous = proto[key];
1266				if ($type(previous) == 'object') $mixin(previous, value);
1267				else proto[key] = $unlink(value);
1268			break;
1269			
1270			case 'array':
1271				proto[key] = $unlink(value);
1272			break;
1273			
1274			default: proto[key] = value;
1275
1276		}
1277		
1278		return this;
1279
1280	}
1281	
1282});
1283
1284Class.Mutators = {
1285	
1286	Extends: function(parent){
1287
1288		this.parent = parent;
1289		this.prototype = Class.instantiate(parent);
1290
1291		this.implement('parent', function(){
1292			var name = this.caller._name, previous = this.caller._owner.parent.prototype[name];
1293			if (!previous) throw new Error('The method "' + name + '" has no parent.');
1294			return previous.apply(this, arguments);
1295		}.protect());
1296
1297	},
1298
1299	Implements: function(items){
1300		$splat(items).each(function(item){
1301			if (item instanceof Function) item = Class.instantiate(item);
1302			this.implement(item);
1303		}, this);
1304
1305	}
1306	
1307};
1308
1309
1310/*
1311---
1312
1313name: Class.Extras
1314
1315description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
1316
1317license: MIT-style license.
1318
1319requires: Class
1320
1321provides: [Chain, Events, Options, Class.Extras]
1322
1323...
1324*/
1325
1326var Chain = new Class({
1327
1328	$chain: [],
1329
1330	chain: function(){
1331		this.$chain.extend(Array.flatten(arguments));
1332		return this;
1333	},
1334
1335	callChain: function(){
1336		return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
1337	},
1338
1339	clearChain: function(){
1340		this.$chain.empty();
1341		return this;
1342	}
1343
1344});
1345
1346var Events = new Class({
1347
1348	$events: {},
1349
1350	addEvent: function(type, fn, internal){
1351		type = Events.removeOn(type);
1352		if (fn != $empty){
1353			this.$events[type] = this.$events[type] || [];
1354			this.$events[type].include(fn);
1355			if (internal) fn.internal = true;
1356		}
1357		return this;
1358	},
1359
1360	addEvents: function(events){
1361		for (var type in events) this.addEvent(type, events[type]);
1362		return this;
1363	},
1364
1365	fireEvent: function(type, args, delay){
1366		type = Events.removeOn(type);
1367		if (!this.$events || !this.$events[type]) return this;
1368		this.$events[type].each(function(fn){
1369			fn.create({'bind': this, 'delay': delay, 'arguments': args})();
1370		}, this);
1371		return this;
1372	},
1373
1374	removeEvent: function(type, fn){
1375		type = Events.removeOn(type);
1376		if (!this.$events[type]) return this;
1377		if (!fn.internal) this.$events[type].erase(fn);
1378		return this;
1379	},
1380
1381	removeEvents: function(events){
1382		var type;
1383		if ($type(events) == 'object'){
1384			for (type in events) this.removeEvent(type, events[type]);
1385			return this;
1386		}
1387		if (events) events = Events.removeOn(events);
1388		for (type in this.$events){
1389			if (events && events != type) continue;
1390			var fns = this.$events[type];
1391			for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]);
1392		}
1393		return this;
1394	}
1395
1396});
1397
1398Events.removeOn = function(string){
1399	return string.replace(/^on([A-Z])/, function(full, first){
1400		return first.toLowerCase();
1401	});
1402};
1403
1404var Options = new Class({
1405
1406	setOptions: function(){
1407		this.options = $merge.run([this.options].extend(arguments));
1408		if (!this.addEvent) return this;
1409		for (var option in this.options){
1410			if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
1411			this.addEvent(option, this.options[option]);
1412			delete this.options[option];
1413		}
1414		return this;
1415	}
1416
1417});
1418
1419
1420/*
1421---
1422
1423name: Element
1424
1425description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements.
1426
1427license: MIT-style license.
1428
1429requires: [Window, Document, Array, String, Function, Number, Hash]
1430
1431provides: [Element, Elements, $, $$, Iframe]
1432
1433...
1434*/
1435
1436var Element = new Native({
1437
1438	name: 'Element',
1439
1440	legacy: window.Element,
1441
1442	initialize: function(tag, props){
1443		var konstructor = Element.Constructors.get(tag);
1444		if (konstructor) return konstructor(props);
1445		if (typeof tag == 'string') return document.newElement(tag, props);
1446		return document.id(tag).set(props);
1447	},
1448
1449	afterImplement: function(key, value){
1450		Element.Prototype[key] = value;
1451		if (Array[key]) return;
1452		Elements.implement(key, function(){
1453			var items = [], elements = true;
1454			for (var i = 0, j = this.length; i < j; i++){
1455				var returns = this[i][key].apply(this[i], arguments);
1456				items.push(returns);
1457				if (elements) elements = ($type(returns) == 'element');
1458			}
1459			return (elements) ? new Elements(items) : items;
1460		});
1461	}
1462
1463});
1464
1465Element.Prototype = {$family: {name: 'element'}};
1466
1467Element.Constructors = new Hash;
1468
1469var IFrame = new Native({
1470
1471	name: 'IFrame',
1472
1473	generics: false,
1474
1475	initialize: function(){
1476		var params = Array.link(arguments, {properties: Object.type, iframe: $defined});
1477		var props = params.properties || {};
1478		var iframe = document.id(params.iframe);
1479		var onload = props.onload || $empty;
1480		delete props.onload;
1481		props.id = props.name = $pick(props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + $time());
1482		iframe = new Element(iframe || 'iframe', props);
1483		var onFrameLoad = function(){
1484			var host = $try(function(){
1485				return iframe.contentWindow.location.host;
1486			});
1487			if (!host || host == window.location.host){
1488				var win = new Window(iframe.contentWindow);
1489				new Document(iframe.contentWindow.document);
1490				$extend(win.Element.prototype, Element.Prototype);
1491			}
1492			onload.call(iframe.contentWindow, iframe.contentWindow.document);
1493		};
1494		var contentWindow = $try(function(){
1495			return iframe.contentWindow;
1496		});
1497		((contentWindow && contentWindow.document.body) || window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad);
1498		return iframe;
1499	}
1500
1501});
1502
1503var Elements = new Native({
1504
1505	initialize: function(elements, options){
1506		options = $extend({ddup: true, cash: true}, options);
1507		elements = elements || [];
1508		if (options.ddup || options.cash){
1509			var uniques = {}, returned = [];
1510			for (var i = 0, l = elements.length; i < l; i++){
1511				var el = document.id(elements[i], !options.cash);
1512				if (options.ddup){
1513					if (uniques[el.uid]) continue;
1514					uniques[el.uid] = true;
1515				}
1516				if (el) returned.push(el);
1517			}
1518			elements = returned;
1519		}
1520		return (options.cash) ? $extend(elements, this) : elements;
1521	}
1522
1523});
1524
1525Elements.implement({
1526
1527	filter: function(filter, bind){
1528		if (!filter) return this;
1529		return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){
1530			return item.match(filter);
1531		} : filter, bind));
1532	}
1533
1534});
1535
1536(function(){
1537
1538/*<ltIE8>*/
1539var createElementAcceptsHTML;
1540try {
1541	var x = document.createElement('<input name=x>');
1542	createElementAcceptsHTML = (x.name == 'x');
1543} catch(e){}
1544
1545var escapeQuotes = function(html){
1546	return ('' + html).replace(/&/g,'&amp;').replace(/"/g,'&quot;');
1547};
1548/*</ltIE8>*/
1549
1550Document.implement({
1551
1552	newElement: function(tag, props){
1553		if (props && props.checked != null) props.defaultChecked = props.checked;
1554		/*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
1555		if (createElementAcceptsHTML && props){
1556			tag = '<' + tag;
1557			if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
1558			if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
1559			tag += '>';
1560			delete props.name;
1561			delete props.type;
1562		}
1563		/*</ltIE8>*/
1564		return this.id(this.createElement(tag)).set(props);
1565	},
1566
1567	newTextNode: function(text){
1568		return this.createTextNode(text);
1569	},
1570
1571	getDocument: function(){
1572		return this;
1573	},
1574
1575	getWindow: function(){
1576		return this.window;
1577	},
1578	
1579	id: (function(){
1580		
1581		var types = {
1582
1583			string: function(id, nocash, doc){
1584				id = doc.getElementById(id);
1585				return (id) ? types.element(id, nocash) : null;
1586			},
1587			
1588			element: function(el, nocash){
1589				$uid(el);
1590				if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
1591					var proto = Element.Prototype;
1592					for (var p in proto) el[p] = proto[p];
1593				};
1594				return el;
1595			},
1596			
1597			object: function(obj, nocash, doc){
1598				if (obj.toElement) return types.element(obj.toElement(doc), nocash);
1599				return null;
1600			}
1601			
1602		};
1603
1604		types.textnode = types.whitespace = types.window = types.document = $arguments(0);
1605		
1606		return function(el, nocash, doc){
1607			if (el && el.$family && el.uid) return el;
1608			var type = $type(el);
1609			return (types[type]) ? types[type](el, nocash, doc || document) : null;
1610		};
1611
1612	})()
1613
1614});
1615
1616})();
1617
1618if (window.$ == null) Window.implement({
1619	$: function(el, nc){
1620		return document.id(el, nc, this.document);
1621	}
1622});
1623
1624Window.implement({
1625
1626	$$: function(selector){
1627		if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector);
1628		var elements = [];
1629		var args = Array.flatten(arguments);
1630		for (var i = 0, l = args.length; i < l; i++){
1631			var item = args[i];
1632			switch ($type(item)){
1633				case 'element': elements.push(item); break;
1634				case 'string': elements.extend(this.document.getElements(item, true));
1635			}
1636		}
1637		return new Elements(elements);
1638	},
1639
1640	getDocument: function(){
1641		return this.document;
1642	},
1643
1644	getWindow: function(){
1645		return this;
1646	}
1647
1648});
1649
1650Native.implement([Element, Document], {
1651
1652	getElement: function(selector, nocash){
1653		return document.id(this.getElements(selector, true)[0] || null, nocash);
1654	},
1655
1656	getElements: function(tags, nocash){
1657		tags = tags.split(',');
1658		var elements = [];
1659		var ddup = (tags.length > 1);
1660		tags.each(function(tag){
1661			var partial = this.getElementsByTagName(tag.trim());
1662			(ddup) ? elements.extend(partial) : elements = partial;
1663		}, this);
1664		return new Elements(elements, {ddup: ddup, cash: !nocash});
1665	}
1666
1667});
1668
1669(function(){
1670
1671var collected = {}, storage = {};
1672var props = {input: 'checked', option: 'selected', textarea: (Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerHTML' : 'value'};
1673
1674var get = function(uid){
1675	return (storage[uid] || (storage[uid] = {}));
1676};
1677
1678var clean = function(item, retain){
1679	if (!item) return;
1680	var uid = item.uid;
1681	if (retain !== true) retain = false;
1682	if (Browser.Engine.trident){
1683		if (item.clearAttributes){
1684			var clone = retain && item.cloneNode(false);
1685			item.clearAttributes();
1686			if (clone) item.mergeAttributes(clone);
1687		} else if (item.removeEvents){
1688			item.removeEvents();
1689		}
1690		if ((/object/i).test(item.tagName)){
1691			for (var p in item){
1692				if (typeof item[p] == 'function') item[p] = $empty;
1693			}
1694			Element.dispose(item);
1695		}
1696	}	
1697	if (!uid) return;
1698	collected[uid] = storage[uid] = null;
1699};
1700
1701var purge = function(){
1702	Hash.each(collected, clean);
1703	if (Browser.Engine.trident) $A(document.getElementsByTagName('object')).each(clean);
1704	if (window.CollectGarbage) CollectGarbage();
1705	collected = storage = null;
1706};
1707
1708var walk = function(element, walk, start, match, all, nocash){
1709	var el = element[start || walk];
1710	var elements = [];
1711	while (el){
1712		if (el.nodeType == 1 && (!match || Element.match(el, match))){
1713			if (!all) return document.id(el, nocash);
1714			elements.push(el);
1715		}
1716		el = el[walk];
1717	}
1718	return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : null;
1719};
1720
1721var attributes = {
1722	'html': 'innerHTML',
1723	'class': 'className',
1724	'for': 'htmlFor',
1725	'defaultValue': 'defaultValue',
1726	'text': (Browser.Engine.trident || (Browser.Engine.webkit && Browser.Engine.version < 420)) ? 'innerText' : 'textContent'
1727};
1728var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'];
1729var camels = ['value', 'type', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'];
1730
1731bools = bools.associate(bools);
1732
1733Hash.extend(attributes, bools);
1734Hash.extend(attributes, camels.associate(camels.map(String.toLowerCase)));
1735
1736var inserters = {
1737
1738	before: function(context, element){
1739		if (element.parentNode) element.parentNode.insertBefore(context, element);
1740	},
1741
1742	after: function(context, element){
1743		if (!element.parentNode) return;
1744		var next = element.nextSibling;
1745		(next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
1746	},
1747
1748	bottom: function(context, element){
1749		element.appendChild(context);
1750	},
1751
1752	top: function(context, element){
1753		var first = element.firstChild;
1754		(first) ? element.insertBefore(context, first) : element.appendChild(context);
1755	}
1756
1757};
1758
1759inserters.inside = inserters.bottom;
1760
1761Hash.each(inserters, function(inserter, where){
1762
1763	where = where.capitalize();
1764
1765	Element.implement('inject' + where, function(el){
1766		inserter(this, document.id(el, true));
1767		return this;
1768	});
1769
1770	Element.implement('grab' + where, function(el){
1771		inserter(document.id(el, true), this);
1772		return this;
1773	});
1774
1775});
1776
1777Element.implement({
1778
1779	set: function(prop, value){
1780		switch ($type(prop)){
1781			case 'object':
1782				for (var p in prop) this.set(p, prop[p]);
1783				break;
1784			case 'string':
1785				var property = Element.Properties.get(prop);
1786				(property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
1787		}
1788		return this;
1789	},
1790
1791	get: function(prop){
1792		var property = Element.Properties.get(prop);
1793		return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
1794	},
1795
1796	erase: function(prop){
1797		var property = Element.Properties.get(prop);
1798		(property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
1799		return this;
1800	},
1801
1802	setProperty: function(attribute, value){
1803		var key = attributes[attribute];
1804		if (value == undefined) return this.removeProperty(attribute);
1805		if (key && bools[attribute]) value = !!value;
1806		(key) ? this[key] = value : this.setAttribute(attribute, '' + value);
1807		return this;
1808	},
1809
1810	setProperties: function(attributes){
1811		for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
1812		return this;
1813	},
1814
1815	getProperty: function(attribute){
1816		var key = attributes[attribute];
1817		var value = (key) ? this[key] : this.getAttribute(attribute, 2);
1818		return (bools[attribute]) ? !!value : (key) ? value : value || null;
1819	},
1820
1821	getProperties: function(){
1822		var args = $A(arguments);
1823		return args.map(this.getProperty, this).associate(args);
1824	},
1825
1826	removeProperty: function(attribute){
1827		var key = attributes[attribute];
1828		(key) ? this[key] = (key && bools[attribute]) ? false : '' : this.removeAttribute(attribute);
1829		return this;
1830	},
1831
1832	removeProperties: function(){
1833		Array.each(arguments, this.removeProperty, this);
1834		return this;
1835	},
1836
1837	hasClass: function(className){
1838		return this.className.contains(className, ' ');
1839	},
1840
1841	addClass: function(className){
1842		if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
1843		return this;
1844	},
1845
1846	removeClass: function(className){
1847		this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
1848		return this;
1849	},
1850
1851	toggleClass: function(className){
1852		return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
1853	},
1854
1855	adopt: function(){
1856		Array.flatten(arguments).each(function(element){
1857			element = document.id(element, true);
1858			if (element) this.appendChild(element);
1859		}, this);
1860		return this;
1861	},
1862
1863	appendText: function(text, where){
1864		return this.grab(this.getDocument().newTextNode(text), where);
1865	},
1866
1867	grab: function(el, where){
1868		inserters[where || 'bottom'](document.id(el, true), this);
1869		return this;
1870	},
1871
1872	inject: function(el, where){
1873		inserters[where || 'bottom'](this, document.id(el, true));
1874		return this;
1875	},
1876
1877	replaces: function(el){
1878		el = document.id(el, true);
1879		el.parentNode.replaceChild(this, el);
1880		return this;
1881	},
1882
1883	wraps: function(el, where){
1884		el = document.id(el, true);
1885		return this.replaces(el).grab(el, where);
1886	},
1887
1888	getPrevious: function(match, nocash){
1889		return walk(this, 'previousSibling', null, match, false, nocash);
1890	},
1891
1892	getAllPrevious: function(match, nocash){
1893		return walk(this, 'previousSibling', null, match, true, nocash);
1894	},
1895
1896	getNext: function(match, nocash){
1897		return walk(this, 'nextSibling', null, match, false, nocash);
1898	},
1899
1900	getAllNext: function(match, nocash){
1901		return walk(this, 'nextSibling', null, match, true, nocash);
1902	},
1903
1904	getFirst: function(match, nocash){
1905		return walk(this, 'nextSibling', 'firstChild', match, false, nocash);
1906	},
1907
1908	getLast: function(match, nocash){
1909		return walk(this, 'previousSibling', 'lastChild', match, false, nocash);
1910	},
1911
1912	getParent: function(match, nocash){
1913		return walk(this, 'parentNode', null, match, false, nocash);
1914	},
1915
1916	getParents: function(match, nocash){
1917		return walk(this, 'parentNode', null, match, true, nocash);
1918	},
1919	
1920	getSiblings: function(match, nocash){
1921		return this.getParent().getChildren(match, nocash).erase(this);
1922	},
1923
1924	getChildren: function(match, nocash){
1925		return walk(this, 'nextSibling', 'firstChild', match, true, nocash);
1926	},
1927
1928	getWindow: function(){
1929		return this.ownerDocument.window;
1930	},
1931
1932	getDocument: function(){
1933		return this.ownerDocument;
1934	},
1935
1936	getElementById: function(id, nocash){
1937		var el = this.ownerDocument.getElementById(id);
1938		if (!el) return null;
1939		for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
1940			if (!parent) return null;
1941		}
1942		return document.id(el, nocash);
1943	},
1944
1945	getSelected: function(){
1946		return new Elements($A(this.options).filter(function(option){
1947			return option.selected;
1948		}));
1949	},
1950
1951	getComputedStyle: function(property){
1952		if (this.currentStyle) return this.currentStyle[property.camelCase()];
1953		var computed = this.getDocument().defaultView.getComputedStyle(this, null);
1954		return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
1955	},
1956
1957	toQueryString: function(){
1958		var queryString = [];
1959		this.getElements('input, select, textarea', true).each(function(el){
1960			if (!el.name || el.disabled || el.type == 'submit' || el.type == 'reset' || el.type == 'file') return;
1961			var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
1962				return opt.value;
1963			}) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
1964			$splat(value).each(function(val){
1965				if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val));
1966			});
1967		});
1968		return queryString.join('&');
1969	},
1970
1971	clone: function(contents, keepid){
1972		contents = contents !== false;
1973		var clone = this.cloneNode(contents);
1974		var clean = function(node, element){
1975			if (!keepid) node.removeAttribute('id');
1976			if (Browser.Engine.trident){
1977				node.clearAttributes();
1978				node.mergeAttributes(element);
1979				node.removeAttribute('uid');
1980				if (node.options){
1981					var no = node.options, eo = element.options;
1982					for (var j = no.length; j--;) no[j].selected = eo[j].selected;
1983				}
1984			}
1985			var prop = props[element.tagName.toLowerCase()];
1986			if (prop && element[prop]) node[prop] = element[prop];
1987		};
1988
1989		if (contents){
1990			var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
1991			for (var i = ce.length; i--;) clean(ce[i], te[i]);
1992		}
1993
1994		clean(clone, this);
1995		return document.id(clone);
1996	},
1997
1998	destroy: function(){
1999		Element.empty(this);
2000		Element.dispose(this);
2001		clean(this, true);
2002		return null;
2003	},
2004
2005	empty: function(){
2006		$A(this.childNodes).each(function(node){
2007			Element.destroy(node);
2008		});
2009		return this;
2010	},
2011
2012	dispose: function(){
2013		return (this.parentNode) ? this.parentNode.removeChild(this) : this;
2014	},
2015
2016	hasChild: function(el){
2017		el = document.id(el, true);
2018		if (!el) return false;
2019		if (Browser.Engine.webkit && Browser.Engine.version < 420) return $A(this.getElementsByTagName(el.tagName)).contains(el);
2020		return (this.contains) ? (this != el && this.contains(el)) : !!(this.compareDocumentPosition(el) & 16);
2021	},
2022
2023	match: function(tag){
2024		return (!tag || (tag == this) || (Element.get(this, 'tag') == tag));
2025	}
2026
2027});
2028
2029Native.implement([Element, Window, Document], {
2030
2031	addListener: function(type, fn){
2032		if (type == 'unload'){
2033			var old = fn, self = this;
2034			fn = function(){
2035				self.removeListener('unload', fn);
2036				old();
2037			};
2038		} else {
2039			collected[this.uid] = this;
2040		}
2041		if (this.addEventListener) this.addEventListener(type, fn, false);
2042		else this.attachEvent('on' + type, fn);
2043		return this;
2044	},
2045
2046	removeListener: function(type, fn){
2047		if (this.removeEventListener) this.removeEventListener(type, fn, false);
2048		else this.detachEvent('on' + type, fn);
2049		return this;
2050	},
2051
2052	retrieve: function(property, dflt){
2053		var storage = get(this.uid), prop = storage[property];
2054		if (dflt != undefined && prop == undefined) prop = storage[property] = dflt;
2055		return $pick(prop);
2056	},
2057
2058	store: function(property, value){
2059		var storage = get(this.uid);
2060		storage[property] = value;
2061		return this;
2062	},
2063
2064	eliminate: function(property){
2065		var storage = get(this.uid);
2066		delete storage[property];
2067		return this;
2068	}
2069
2070});
2071
2072window.addListener('unload', purge);
2073
2074})();
2075
2076Element.Properties = new Hash;
2077
2078Element.Properties.style = {
2079
2080	set: function(style){
2081		this.style.cssText = style;
2082	},
2083
2084	get: function(){
2085		return this.style.cssText;
2086	},
2087
2088	erase: function(){
2089		this.style.cssText = '';
2090	}
2091
2092};
2093
2094Element.Properties.tag = {
2095
2096	get: function(){
2097		return this.tagName.toLowerCase();
2098	}
2099
2100};
2101
2102Element.Properties.html = (function(){
2103	var wrapper = document.createElement('div');
2104
2105	var translations = {
2106		table: [1, '<table>', '</table>'],
2107		select: [1, '<select>', '</select>'],
2108		tbody: [2, '<table><tbody>', '</tbody></table>'],
2109		tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
2110	};
2111	translations.thead = translations.tfoot = translations.tbody;
2112
2113	var html = {
2114		set: function(){
2115			var html = Array.flatten(arguments).join('');
2116			var wrap = Browser.Engine.trident && translations[this.get('tag')];
2117			if (wrap){
2118				var first = wrapper;
2119				first.innerHTML = wrap[1] + html + wrap[2];
2120				for (var i = wrap[0]; i--;) first = first.firstChild;
2121				this.empty().adopt(first.childNodes);
2122			} else {
2123				this.innerHTML = html;
2124			}
2125		}
2126	};
2127
2128	html.erase = html.set;
2129
2130	return html;
2131})();
2132
2133if (Browser.Engine.webkit && Browser.Engine.version < 420) Element.Properties.text = {
2134	get: function(){
2135		if (this.innerText) return this.innerText;
2136		var temp = this.ownerDocument.newElement('div', {html: this.innerHTML}).inject(this.ownerDocument.body);
2137		var text = temp.innerText;
2138		temp.destroy();
2139		return text;
2140	}
2141};
2142
2143
2144/*
2145---
2146
2147name: Element.Event
2148
2149description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events.
2150
2151license: MIT-style license.
2152
2153requires: [Element, Event]
2154
2155provides: Element.Event
2156
2157...
2158*/
2159
2160Element.Properties.events = {set: function(events){
2161	this.addEvents(events);
2162}};
2163
2164Native.implement([Element, Window, Document], {
2165
2166	addEvent: function(type, fn){
2167		var events = this.retrieve('events', {});
2168		events[type] = events[type] || {'keys': [], 'values': []};
2169		if (events[type].keys.contains(fn)) return this;
2170		events[type].keys.push(fn);
2171		var realType = type, custom = Element.Events.get(type), condition = fn, self = this;
2172		if (custom){
2173			if (custom.onAdd) custom.onAdd.call(this, fn);
2174			if (custom.condition){
2175				condition = function(event){
2176					if (custom.condition.call(this, event)) return fn.call(this, event);
2177					return true;
2178				};
2179			}
2180			realType = custom.base || realType;
2181		}
2182		var defn = function(){
2183			return fn.call(self);
2184		};
2185		var nativeEvent = Element.NativeEvents[realType];
2186		if (nativeEvent){
2187			if (nativeEvent == 2){
2188				defn = function(event){
2189					event = new Event(event, self.getWindow());
2190					if (condition.call(self, event) === false) event.stop();
2191				};
2192			}
2193			this.addListener(realType, defn);
2194		}
2195		events[type].values.push(defn);
2196		return this;
2197	},
2198
2199	removeEvent: function(type, fn){
2200		var events = this.retrieve('events');
2201		if (!events || !events[type]) return this;
2202		var pos = events[type].keys.indexOf(fn);
2203		if (pos == -1) return this;
2204		events[type].keys.splice(pos, 1);
2205		var value = events[type].values.splice(pos, 1)[0];
2206		var custom = Element.Events.get(type);
2207		if (custom){
2208			if (custom.onRemove) custom.onRemove.call(this, fn);
2209			type = custom.base || type;
2210		}
2211		return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this;
2212	},
2213
2214	addEvents: function(events){
2215		for (var event in events) this.addEvent(event, events[event]);
2216		return this;
2217	},
2218
2219	removeEvents: function(events){
2220		var type;
2221		if ($type(events) == 'object'){
2222			for (type in events) this.removeEvent(type, events[type]);
2223			return this;
2224		}
2225		var attached = this.retrieve('events');
2226		if (!attached) return this;
2227		if (!events){
2228			for (type in attached) this.removeEvents(type);
2229			this.eliminate('events');
2230		} else if (attached[events]){
2231			while (attached[events].keys[0]) this.removeEvent(events, attached[events].keys[0]);
2232			attached[events] = null;
2233		}
2234		return this;
2235	},
2236
2237	fireEvent: function(type, args, delay){
2238		var events = this.retrieve('events');
2239		if (!events || !events[type]) return this;
2240		events[type].keys.each(function(fn){
2241			fn.create({'bind': this, 'delay': delay, 'arguments': args})();
2242		}, this);
2243		return this;
2244	},
2245
2246	cloneEvents: function(from, type){
2247		from = document.id(from);
2248		var fevents = from.retrieve('events');
2249		if (!fevents) return this;
2250		if (!type){
2251			for (var evType in fevents) this.cloneEvents(from, evType);
2252		} else if (fevents[type]){
2253			fevents[type].keys.each(function(fn){
2254				this.addEvent(type, fn);
2255			}, this);
2256		}
2257		return this;
2258	}
2259
2260});
2261
2262// IE9
2263try {
2264	if (typeof HTMLElement != 'undefined')
2265		HTMLElement.prototype.fireEvent = Element.prototype.fireEvent;
2266} catch(e){}
2267
2268Element.NativeEvents = {
2269	click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
2270	mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
2271	mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
2272	keydown: 2, keypress: 2, keyup: 2, //keyboard
2273	focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements
2274	load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
2275	error: 1, abort: 1, scroll: 1 //misc
2276};
2277
2278(function(){
2279
2280var $check = function(event){
2281	var related = event.relatedTarget;
2282	if (related == undefined) return true;
2283	if (related === false) return false;
2284	return ($type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related));
2285};
2286
2287Element.Events = new Hash({
2288
2289	mouseenter: {
2290		base: 'mouseover',
2291		condition: $check
2292	},
2293
2294	mouseleave: {
2295		base: 'mouseout',
2296		condition: $check
2297	},
2298
2299	mousewheel: {
2300		base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel'
2301	}
2302
2303});
2304
2305})();
2306
2307
2308/*
2309---
2310
2311name: Element.Style
2312
2313description: Contains methods for interacting with the styles of Elements in a fas
2313hionable way.
2314
2315license: MIT-style license.
2316
2317requires: Element
2318
2319provides: Element.Style
2320
2321...
2322*/
2323
2324Element.Properties.styles = {set: function(styles){
2325	this.setStyles(styles);
2326}};
2327
2328Element.Properties.opacity = {
2329
2330	set: function(opacity, novisibility){
2331		if (!novisibility){
2332			if (opacity == 0){
2333				if (this.style.visibility != 'hidden') this.style.visibility = 'hidden';
2334			} else {
2335				if (this.style.visibility != 'visible') this.style.visibility = 'visible';
2336			}
2337		}
2338		if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
2339		if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
2340		this.style.opacity = opacity;
2341		this.store('opacity', opacity);
2342	},
2343
2344	get: function(){
2345		return this.retrieve('opacity', 1);
2346	}
2347
2348};
2349
2350Element.implement({
2351
2352	setOpacity: function(value){
2353		return this.set('opacity', value, true);
2354	},
2355
2356	getOpacity: function(){
2357		return this.get('opacity');
2358	},
2359
2360	setStyle: function(property, value){
2361		switch (property){
2362			case 'opacity': return this.set('opacity', parseFloat(value));
2363			case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
2364		}
2365		property = property.camelCase();
2366		if ($type(value) != 'string'){
2367			var map = (Element.Styles.get(property) || '@').split(' ');
2368			value = $splat(value).map(function(val, i){
2369				if (!map[i]) return '';
2370				return ($type(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
2371			}).join(' ');
2372		} else if (value == String(Number(value))){
2373			value = Math.round(value);
2374		}
2375		this.style[property] = value;
2376		return this;
2377	},
2378
2379	getStyle: function(property){
2380		switch (property){
2381			case 'opacity': return this.get('opacity');
2382			case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
2383		}
2384		property = property.camelCase();
2385		var result = this.style[property];
2386		if (!$chk(result)){
2387			result = [];
2388			for (var style in Element.ShortStyles){
2389				if (property != style) continue;
2390				for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
2391				return result.join(' ');
2392			}
2393			result = this.getComputedStyle(property);
2394		}
2395		if (result){
2396			result = String(result);
2397			var color = result.match(/rgba?\([\d\s,]+\)/);
2398			if (color) result = result.replace(color[0], color[0].rgbToHex());
2399		}
2400		if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result, 10)))){
2401			if (property.test(/^(height|width)$/)){
2402				var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
2403				values.each(function(value){
2404					size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
2405				}, this);
2406				return this['offset' + property.capitalize()] - size + 'px';
2407			}
2408			if ((Browser.Engine.presto) && String(result).test('px')) return result;
2409			if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
2410		}
2411		return result;
2412	},
2413
2414	setStyles: function(styles){
2415		for (var style in styles) this.setStyle(style, styles[style]);
2416		return this;
2417	},
2418
2419	getStyles: function(){
2420		var result = {};
2421		Array.flatten(arguments).each(function(key){
2422			result[key] = this.getStyle(key);
2423		}, this);
2424		return result;
2425	}
2426
2427});
2428
2429Element.Styles = new Hash({
2430	left: '@px', top: '@px', bottom: '@px', right: '@px',
2431	width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
2432	backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
2433	fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
2434	margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
2435	borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
2436	zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
2437});
2438
2439Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
2440
2441['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
2442	var Short = Element.ShortStyles;
2443	var All = Element.Styles;
2444	['margin', 'padding'].each(function(style){
2445		var sd = style + direction;
2446		Short[style][sd] = All[sd] = '@px';
2447	});
2448	var bd = 'border' + direction;
2449	Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
2450	var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
2451	Short[bd] = {};
2452	Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
2453	Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
2454	Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
2455});
2456
2457
2458/*
2459---
2460
2461name: Element.Dimensions
2462
2463description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
2464
2465license: MIT-style license.
2466
2467credits:
2468  - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
2469  - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui
2469/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
2470
2471requires: Element
2472
2473provides: Element.Dimensions
2474
2475...
2476*/
2477
2478(function(){
2479
2480Element.implement({
2481
2482	scrollTo: function(x, y){
2483		if (isBody(this)){
2484			this.getWindow().scrollTo(x, y);
2485		} else {
2486			this.scrollLeft = x;
2487			this.scrollTop = y;
2488		}
2489		return this;
2490	},
2491
2492	getSize: function(){
2493		if (isBody(this)) return this.getWindow().getSize();
2494		return {x: this.offsetWidth, y: this.offsetHeight};
2495	},
2496
2497	getScrollSize: function(){
2498		if (isBody(this)) return this.getWindow().getScrollSize();
2499		return {x: this.scrollWidth, y: this.scrollHeight};
2500	},
2501
2502	getScroll: function(){
2503		if (isBody(this)) return this.getWindow().getScroll();
2504		return {x: this.scrollLeft, y: this.scrollTop};
2505	},
2506
2507	getScrolls: function(){
2508		var element = this, position = {x: 0, y: 0};
2509		while (element && !isBody(element)){
2510			position.x += element.scrollLeft;
2511			position.y += element.scrollTop;
2512			element = element.parentNode;
2513		}
2514		return position;
2515	},
2516
2517	getOffsetParent: function(){
2518		var element = this;
2519		if (isBody(element)) return null;
2520		if (!Browser.Engine.trident) return element.offsetParent;
2521		while ((element = element.parentNode) && !isBody(element)){
2522			if (styleString(element, 'position') != 'static') return element;
2523		}
2524		return null;
2525	},
2526
2527	getOffsets: function(){
2528		if (this.getBoundingClientRect){
2529			var bound = this.getBoundingClientRect(),
2530				html = document.id(this.getDocument().documentElement),
2531				htmlScroll = html.getScroll(),
2532				elemScrolls = this.getScrolls(),
2533				elemScroll = this.getScroll(),
2534				isFixed = (styleString(this, 'position') == 'fixed');
2535
2536			return {
2537				x: bound.left.toInt() + elemScrolls.x - elemScroll.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
2538				y: bound.top.toInt()  + elemScrolls.y - elemScroll.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
2539			};
2540		}
2541
2542		var element = this, position = {x: 0, y: 0};
2543		if (isBody(this)) return position;
2544
2545		while (element && !isBody(element)){
2546			position.x += element.offsetLeft;
2547			position.y += element.offsetTop;
2548
2549			if (Browser.Engine.gecko){
2550				if (!borderBox(element)){
2551					position.x += leftBorder(element);
2552					position.y += topBorder(element);
2553				}
2554				var parent = element.parentNode;
2555				if (parent && styleString(parent, 'overflow') != 'visible'){
2556					position.x += leftBorder(parent);
2557					position.y += topBorder(parent);
2558				}
2559			} else if (element != this && Browser.Engine.webkit){
2560				position.x += leftBorder(element);
2561				position.y += topBorder(element);
2562			}
2563
2564			element = element.offsetParent;
2565		}
2566		if (Browser.Engine.gecko && !borderBox(this)){
2567			position.x -= leftBorder(this);
2568			position.y -= topBorder(this);
2569		}
2570		return position;
2571	},
2572
2573	getPosition: function(relative){
2574		if (isBody(this)) return {x: 0, y: 0};
2575		var offset = this.getOffsets(),
2576				scroll = this.getScrolls();
2577		var position = {
2578			x: offset.x - scroll.x,
2579			y: offset.y - scroll.y
2580		};
2581		var relativePosition = (relative && (relative = document.id(relative))) ? relative.getPosition() : {x: 0, y: 0};
2582		return {x: position.x - relativePosition.x, y: position.y - relativePosition.y};
2583	},
2584
2585	getCoordinates: function(element){
2586		if (isBody(this)) return this.getWindow().getCoordinates();
2587		var position = this.getPosition(element),
2588				size = this.getSize();
2589		var obj = {
2590			left: position.x,
2591			top: position.y,
2592			width: size.x,
2593			height: size.y
2594		};
2595		obj.right = obj.left + obj.width;
2596		obj.bottom = obj.top + obj.height;
2597		return obj;
2598	},
2599
2600	computePosition: function(obj){
2601		return {
2602			left: obj.x - styleNumber(this, 'margin-left'),
2603			top: obj.y - styleNumber(this, 'margin-top')
2604		};
2605	},
2606
2607	setPosition: function(obj){
2608		return this.setStyles(this.computePosition(obj));
2609	}
2610
2611});
2612
2613
2614Native.implement([Document, Window], {
2615
2616	getSize: function(){
2617		if (Browser.Engine.presto || Browser.Engine.webkit){
2618			var win = this.getWindow();
2619			return {x: win.innerWidth, y: win.innerHeight};
2620		}
2621		var doc = getCompatElement(this);
2622		return {x: doc.clientWidth, y: doc.clientHeight};
2623	},
2624
2625	getScroll: function(){
2626		var win = this.getWindow(), doc = getCompatElement(this);
2627		return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
2628	},
2629
2630	getScrollSize: function(){
2631		var doc = getCompatElement(this), min = this.getSize();
2632		return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)};
2633	},
2634
2635	getPosition: function(){
2636		return {x: 0, y: 0};
2637	},
2638
2639	getCoordinates: function(){
2640		var size = this.getSize();
2641		return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
2642	}
2643
2644});
2645
2646// private methods
2647
2648var styleString = Element.getComputedStyle;
2649
2650function styleNumber(element, style){
2651	return styleString(element, style).toInt() || 0;
2652};
2653
2654function borderBox(element){
2655	return styleString(element, '-moz-box-sizing') == 'border-box';
2656};
2657
2658function topBorder(element){
2659	return styleNumber(element, 'border-top-width');
2660};
2661
2662function leftBorder(element){
2663	return styleNumber(element, 'border-left-width');
2664};
2665
2666function isBody(element){
2667	return (/^(?:body|html)$/i).test(element.tagName);
2668};
2669
2670function getCompatElement(element){
2671	var doc = element.getDocument();
2672	return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
2673};
2674
2675})();
2676
2677//aliases
2678Element.alias('setPosition', 'position'); //compatability
2679
2680Native.implement([Window, Document, Element], {
2681
2682	getHeight: function(){
2683		return this.getSize().y;
2684	},
2685
2686	getWidth: function(){
2687		return this.getSize().x;
2688	},
2689
2690	getScrollTop: function(){
2691		return this.getScroll().y;
2692	},
2693
2694	getScrollLeft: function(){
2695		return this.getScroll().x;
2696	},
2697
2698	getScrollHeight: function(){
2699		return this.getScrollSize().y;
2700	},
2701
2702	getScrollWidth: function(){
2703		return this.getScrollSize().x;
2704	},
2705
2706	getTop: function(){
2707		return this.getPosition().y;
2708	},
2709
2710	getLeft: function(){
2711		return this.getPosition().x;
2712	}
2713
2714});
2715
2716
2717/*
2718---
2719
2720name: Selectors
2721
2722description: Adds advanced CSS-style querying capabilities for targeting HTML Elements. Includes pseudo selectors.
2723
2724license: MIT-style license.
2725
2726requires: Element
2727
2728provides: Selectors
2729
2730...
2731*/
2732
2733Native.implement([Document, Element], {
2734
2735	getElements: function(expression, nocash){
2736		expression = expression.split(',');
2737		var items, local = {};
2738		for (var i = 0, l = expression.length; i < l; i++){
2739			var selector = expression[i], elements = Selectors.Utils.search(this, selector, local);
2740			if (i != 0 && elements.item) elements = $A(elements);
2741			items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements);
2742		}
2743		return new Elements(items, {ddup: (expression.length > 1), cash: !nocash});
2744	}
2745
2746});
2747
2748Element.implement({
2749
2750	match: function(selector){
2751		if (!selector || (selector == this)) return true;
2752		var tagid = Selectors.Utils.parseTagAndID(selector);
2753		var tag = tagid[0], id = tagid[1];
2754		if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false;
2755		var parsed = Selectors.Utils.parseSelector(selector);
2756		return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
2757	}
2758
2759});
2760
2761var Selectors = {Cache: {nth: {}, parsed: {}}};
2762
2763Selectors.RegExps = {
2764	id: (/#([\w-]+)/),
2765	tag: (/^(\w+|\*)/),
2766	quick: (/^(\w+|\*)$/),
2767	splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
2768	combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)(["']?)([^\4]*?)\4)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
2769};
2770
2771Selectors.Utils = {
2772
2773	chk: function(item, uniques){
2774		if (!uniques) return true;
2775		var uid = $uid(item);
2776		if (!uniques[uid]) return uniques[uid] = true;
2777		return false;
2778	},
2779
2780	parseNthArgument: function(argument){
2781		if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
2782		var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
2783		if (!parsed) return false;
2784		var inta = parseInt(parsed[1], 10);
2785		var a = (inta || inta === 0) ? inta : 1;
2786		var special = parsed[2] || false;
2787		var b = parseInt(parsed[3], 10) || 0;
2788		if (a != 0){
2789			b--;
2790			while (b < 1) b += a;
2791			while (b >= a) b -= a;
2792		} else {
2793			a = b;
2794			special = 'index';
2795		}
2796		switch (special){
2797			case 'n': parsed = {a: a, b: b, special: 'n'}; break;
2798			case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
2799			case 'even': parsed = {a: 2, b: 1, special: 'n'}; break;
2800			case 'first': parsed = {a: 0, special: 'index'}; break;
2801			case 'last': parsed = {special: 'last-child'}; break;
2802			case 'only': parsed = {special: 'only-child'}; break;
2803			default: parsed = {a: (a - 1), special: 'index'};
2804		}
2805
2806		return Selectors.Cache.nth[argument] = parsed;
2807	},
2808
2809	parseSelector: function(selector){
2810		if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
2811		var m, parsed = {classes: [], pseudos: [], attributes: []};
2812		while ((m = Selectors.RegExps.combined.exec(selector))){
2813			var cn = m[1], an = m[2], ao = m[3], av = m[5], pn = m[6], pa = m[7];
2814			if (cn){
2815				parsed.classes.push(cn);
2816			} else if (pn){
2817				var parser = Selectors.Pseudo.get(pn);
2818				if (parser) parsed.pseudos.push({parser: parser, argument: pa});
2819				else parsed.attributes.push({name: pn, operator: '=', value: pa});
2820			} else if (an){
2821				parsed.attributes.push({name: an, operator: ao, value: av});
2822			}
2823		}
2824		if (!parsed.classes.length) delete parsed.classes;
2825		if (!parsed.attributes.length) delete parsed.attributes;
2826		if (!parsed.pseudos.length) delete parsed.pseudos;
2827		if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null;
2828		return Selectors.Cache.parsed[selector] = parsed;
2829	},
2830
2831	parseTagAndID: function(selector){
2832		var tag = selector.match(Selectors.RegExps.tag);
2833		var id = selector.match(Selectors.RegExps.id);
2834		return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
2835	},
2836
2837	filter: function(item, parsed, local){
2838		var i;
2839		if (parsed.classes){
2840			for (i = parsed.classes.length; i--; i){
2841				var cn = parsed.classes[i];
2842				if (!Selectors.Filters.byClass(item, cn)) return false;
2843			}
2844		}
2845		if (parsed.attributes){
2846			for (i = parsed.attributes.length; i--; i){
2847				var att = parsed.attributes[i];
2848				if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false;
2849			}
2850		}
2851		if (parsed.pseudos){
2852			for (i = parsed.pseudos.length; i--; i){
2853				var psd = parsed.pseudos[i];
2854				if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false;
2855			}
2856		}
2857		return true;
2858	},
2859
2860	getByTagAndID: function(ctx, tag, id){
2861		if (id){
2862			var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true);
2863			return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
2864		} else {
2865			return ctx.getElementsByTagName(tag);
2866		}
2867	},
2868
2869	search: function(self, expression, local){
2870		var splitters = [];
2871
2872		var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){
2873			splitters.push(m1);
2874			return ':)' + m2;
2875		}).split(':)');
2876
2877		var items, filtered, item;
2878
2879		for (var i = 0, l = selectors.length; i < l; i++){
2880
2881			var selector = selectors[i];
2882
2883			if (i == 0 && Selectors.RegExps.quick.test(selector)){
2884				items = self.getElementsByTagName(selector);
2885				continue;
2886			}
2887
2888			var splitter = splitters[i - 1];
2889
2890			var tagid = Selectors.Utils.parseTagAndID(selector);
2891			var tag = tagid[0], id = tagid[1];
2892
2893			if (i == 0){
2894				items = Selectors.Utils.getByTagAndID(self, tag, id);
2895			} else {
2896				var uniques = {}, found = [];
2897				for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
2898				items = found;
2899			}
2900
2901			var parsed = Selectors.Utils.parseSelector(selector);
2902
2903			if (parsed){
2904				filtered = [];
2905				for (var m = 0, n = items.length; m < n; m++){
2906					item = items[m];
2907					if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
2908				}
2909				items = filtered;
2910			}
2911
2912		}
2913
2914		return items;
2915
2916	}
2917
2918};
2919
2920Selectors.Getters = {
2921
2922	' ': function(found, self, tag, id, uniques){
2923		var items = Selectors.Utils.getByTagAndID(self, tag, id);
2924		for (var i = 0, l = items.length; i < l; i++){
2925			var item = items[i];
2926			if (Selectors.Utils.chk(item, uniques)) found.push(item);
2927		}
2928		return found;
2929	},
2930
2931	'>': function(found, self, tag, id, uniques){
2932		var children = Selectors.Utils.getByTagAndID(self, tag, id);
2933		for (var i = 0, l = children.length; i < l; i++){
2934			var child = children[i];
2935			if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child);
2936		}
2937		return found;
2938	},
2939
2940	'+': function(found, self, tag, id, uniques){
2941		while ((self = self.nextSibling)){
2942			if (self.nodeType == 1){
2943				if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
2944				break;
2945			}
2946		}
2947		return found;
2948	},
2949
2950	'~': function(found, self, tag, id, uniques){
2951		while ((self = self.nextSibling)){
2952			if (self.nodeType == 1){
2953				if (!Selectors.Utils.chk(self, uniques)) break;
2954				if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
2955			}
2956		}
2957		return found;
2958	}
2959
2960};
2961
2962Selectors.Filters = {
2963
2964	byTag: function(self, tag){
2965		return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag));
2966	},
2967
2968	byID: function(self, id){
2969		return (!id || (self.id && self.id == id));
2970	},
2971
2972	byClass: function(self, klass){
2973		return (self.className && self.className.contains && self.className.contains(klass, ' '));
2974	},
2975
2976	byPseudo: function(self, parser, argument, local){
2977		return parser.call(self, argument, local);
2978	},
2979
2980	byAttribute: function(self, name, operator, value){
2981		var result = Element.prototype.getProperty.call(self, name);
2982		if (!result) return (operator == '!=');
2983		if (!operator || value == undefined) return true;
2984		switch (operator){
2985			case '=': return (result == value);
2986			case '*=': return (result.contains(value));
2987			case '^=': return (result.substr(0, value.length) == value);
2988			case '$=': return (result.substr(result.length - value.length) == value);
2989			case '!=': return (result != value);
2990			case '~=': return result.contains(value, ' ');
2991			case '|=': return result.contains(value, '-');
2992		}
2993		return false;
2994	}
2995
2996};
2997
2998Selectors.Pseudo = new Hash({
2999
3000	// w3c pseudo selectors
3001
3002	checked: function(){
3003		return this.checked;
3004	},
3005	
3006	empty: function(){
3007		return !(this.innerText || this.textContent || '').length;
3008	},
3009
3010	not: function(selector){
3011		return !Element.match(this, selector);
3012	},
3013
3014	contains: function(text){
3015		return (this.innerText || this.textContent || '').contains(text);
3016	},
3017
3018	'first-child': function(){
3019		return Selectors.Pseudo.index.call(this, 0);
3020	},
3021
3022	'last-child': function(){
3023		var element = this;
3024		while ((element = element.nextSibling)){
3025			if (element.nodeType == 1) return false;
3026		}
3027		return true;
3028	},
3029
3030	'only-child': function(){
3031		var prev = this;
3032		while ((prev = prev.previousSibling)){
3033			if (prev.nodeType == 1) return false;
3034		}
3035		var next = this;
3036		while ((next = next.nextSibling)){
3037			if (next.nodeType == 1) return false;
3038		}
3039		return true;
3040	},
3041
3042	'nth-child': function(argument, local){
3043		argument = (argument == undefined) ? 'n' : argument;
3044		var parsed = Selectors.Utils.parseNthArgument(argument);
3045		if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local);
3046		var count = 0;
3047		local.positions = local.positions || {};
3048		var uid = $uid(this);
3049		if (!local.positions[uid]){
3050			var self = this;
3051			while ((self = self.previousSibling)){
3052				if (self.nodeType != 1) continue;
3053				count ++;
3054				var position = local.positions[$uid(self)];
3055				if (position != undefined){
3056					count = position + count;
3057					break;
3058				}
3059			}
3060			local.positions[uid] = count;
3061		}
3062		return (local.positions[uid] % parsed.a == parsed.b);
3063	},
3064
3065	// custom pseudo selectors
3066
3067	index: function(index){
3068		var element = this, count = 0;
3069		while ((element = element.previousSibling)){
3070			if (element.nodeType == 1 && ++count > index) return false;
3071		}
3072		return (count == index);
3073	},
3074
3075	even: function(argument, local){
3076		return Selectors.Pseudo['nth-child'].call(this, '2n+1', local);
3077	},
3078
3079	odd: function(argument, local){
3080		return Selectors.Pseudo['nth-child'].call(this, '2n', local);
3081	},
3082	
3083	selected: function(){
3084		return this.selected;
3085	},
3086	
3087	enabled: function(){
3088		return (this.disabled === false);
3089	}
3090
3091});
3092
3093
3094/*
3095---
3096
3097name: DomReady
3098
3099description: Contains the custom event domready.
3100
3101license: MIT-style license.
3102
3103requires: Element.Event
3104
3105provides: DomReady
3106
3107...
3108*/
3109
3110Element.Events.domready = {
3111
3112	onAdd: function(fn){
3113		if (Browser.loaded) fn.call(this);
3114	}
3115
3116};
3117
3118(function(){
3119
3120	var domready = function(){
3121		if (Browser.loaded) return;
3122		Browser.loaded = true;
3123		window.fireEvent('domready');
3124		document.fireEvent('domready');
3125	};
3126	
3127	window.addEvent('load', domready);
3128
3129	if (Browser.Engine.trident){
3130		var temp = document.createElement('div');
3131		(function(){
3132			($try(function(){
3133				temp.doScroll(); // Technique by Diego Perini
3134				return document.id(temp).inject(document.body).set('html', 'temp').dispose();
3135			})) ? domready() : arguments.callee.delay(50);
3136		})();
3137	} else if (Browser.Engine.webkit && Browser.Engine.version < 525){
3138		(function(){
3139			(['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50);
3140		})();
3141	} else {
3142		document.addEvent('DOMContentLoaded', domready);
3143	}
3144
3145})();
3146
3147
3148/*
3149---
3150
3151name: JSON
3152
3153description: JSON encoder and decoder.
3154
3155license: MIT-style license.
3156
3157see: <http://www.json.org/>
3158
3159requires: [Array, String, Number, Function, Hash]
3160
3161provides: JSON
3162
3163...
3164*/
3165
3166var JSON = new Hash(this.JSON && {
3167	stringify: JSON.stringify,
3168	parse: JSON.parse
3169}).extend({
3170	
3171	$specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'},
3172
3173	$replaceChars: function(chr){
3174		return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
3175	},
3176
3177	encode: function(obj){
3178		switch ($type(obj)){
3179			case 'string':
3180				return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"';
3181			case 'array':
3182				return '[' + String(obj.map(JSON.encode).clean()) + ']';
3183			case 'object': case 'hash':
3184				var string = [];
3185				Hash.each(obj, function(value, key){
3186					var json = JSON.encode(value);
3187					if (json) string.push(JSON.encode(key) + ':' + json);
3188				});
3189				return '{' + string + '}';
3190			case 'number': case 'boolean': return String(obj);
3191			case false: return 'null';
3192		}
3193		return null;
3194	},
3195
3196	decode: function(string, secure){
3197		if ($type(string) != 'string' || !string.length) return null;
3198		if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null;
3199		return eval('(' + string + ')');
3200	}
3201
3202});
3203
3204
3205/*
3206---
3207
3208name: Cookie
3209
3210description: Class for creating, reading, and deleting browser Cookies.
3211
3212license: MIT-style license.
3213
3214credits: Based on the functions by Peter-Paul Koch (http://quirksmode.org).
3215
3216requires: Options
3217
3218provides: Cookie
3219
3220...
3221*/
3222
3223var Cookie = new Class({
3224
3225	Implements: Options,
3226
3227	options: {
3228		path: false,
3229		domain: false,
3230		duration: false,
3231		secure: false,
3232		document: document
3233	},
3234
3235	initialize: function(key, options){
3236		this.key = key;
3237		this.setOptions(options);
3238	},
3239
3240	write: function(value){
3241		value = encodeURIComponent(value);
3242		if (this.options.domain) value += '; domain=' + this.options.domain;
3243		if (this.options.path) value += '; path=' + this.options.path;
3244		if (this.options.duration){
3245			var date = new Date();
3246			date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
3247			value += '; expires=' + date.toGMTString();
3248		}
3249		if (this.options.secure) value += '; secure';
3250		this.options.document.cookie = this.key + '=' + value;
3251		return this;
3252	},
3253
3254	read: function(){
3255		var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
3256		return (value) ? decodeURIComponent(value[1]) : null;
3257	},
3258
3259	dispose: function(){
3260		new Cookie(this.key, $merge(this.options, {duration: -1})).write('');
3261		return this;
3262	}
3263
3264});
3265
3266Cookie.write = function(key, value, options){
3267	return new Cookie(key, options).write(value);
3268};
3269
3270Cookie.read = function(key){
3271	return new Cookie(key).read();
3272};
3273
3274Cookie.dispose = function(key, options){
3275	return new Cookie(key, options).dispose();
3276};
3277
3278
3279/*
3280---
3281
3282name: Swiff
3283
3284description: Wrapper for embedding SWF movies. Supports External Interface Communication.
3285
3286license: MIT-style license.
3287
3288credits: Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
3289
3290requires: [Options, $util]
3291
3292provides: Swiff
3293
3294...
3295*/
3296
3297var Swiff = new Class({
3298
3299	Implements: [Options],
3300
3301	options: {
3302		id: null,
3303		height: 1,
3304		width: 1,
3305		container: null,
3306		properties: {},
3307		params: {
3308			quality: 'high',
3309			allowScriptAccess: 'always',
3310			wMode: 'transparent',
3311			swLiveConnect: true
3312		},
3313		callBacks: {},
3314		vars: {}
3315	},
3316
3317	toElement: function(){
3318		return this.object;
3319	},
3320
3321	initialize: function(path, options){
3322		this.instance = 'Swiff_' + $time();
3323
3324		this.setOptions(options);
3325		options = this.options;
3326		var id = this.id = options.id || this.instance;
3327		var container = document.id(options.container);
3328
3329		Swiff.CallBacks[this.instance] = {};
3330
3331		var params = options.params, vars = options.vars, callBacks = options.callBacks;
3332		var properties = $extend({height: options.height, width: options.width}, options.properties);
3333
3334		var self = this;
3335
3336		for (var callBack in callBacks){
3337			Swiff.CallBacks[this.instance][callBack] = (function(option){
3338				return function(){
3339					return option.apply(self.object, arguments);
3340				};
3341			})(callBacks[callBack]);
3342			vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
3343		}
3344
3345		params.flashVars = Hash.toQueryString(vars);
3346		if (Browser.Engine.trident){
3347			properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
3348			params.movie = path;
3349		} else {
3350			properties.type = 'application/x-shockwave-flash';
3351			properties.data = path;
3352		}
3353		var build = '<object id="' + id + '"';
3354		for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
3355		build += '>';
3356		for (var param in params){
3357			if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
3358		}
3359		build += '</object>';
3360		this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
3361	},
3362
3363	replaces: function(element){
3364		element = document.id(element, true);
3365		element.parentNode.replaceChild(this.toElement(), element);
3366		return this;
3367	},
3368
3369	inject: function(element){
3370		document.id(element, true).appendChild(this.toElement());
3371		return this;
3372	},
3373
3374	remote: function(){
3375		return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments));
3376	}
3377
3378});
3379
3380Swiff.CallBacks = {};
3381
3382Swiff.remote = function(obj, fn){
3383	var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
3384	return eval(rs);
3385};
3386
3387
3388/*
3389---
3390
3391name: Fx
3392
3393description: Contains the basic animation logic to be extended by all other Fx Classes.
3394
3395license: MIT-style license.
3396
3397requires: [Chain, Events, Options]
3398
3399provides: Fx
3400
3401...
3402*/
3403
3404var Fx = new Class({
3405
3406	Implements: [Chain, Events, Options],
3407
3408	options: {
3409		/*
3410		onStart: $empty,
3411		onCancel: $empty,
3412		onComplete: $empty,
3413		*/
3414		fps: 50,
3415		unit: false,
3416		duration: 500,
3417		link: 'ignore'
3418	},
3419
3420	initialize: function(options){
3421		this.subject = this.subject || this;
3422		this.setOptions(options);
3423		this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt();
3424		var wait = this.options.wait;
3425		if (wait === false) this.options.link = 'cancel';
3426	},
3427
3428	getTransition: function(){
3429		return function(p){
3430			return -(Math.cos(Math.PI * p) - 1) / 2;
3431		};
3432	},
3433
3434	step: function(){
3435		var time = $time();
3436		if (time < this.time + this.options.duration){
3437			var delta = this.transition((time - this.time) / this.options.duration);
3438			this.set(this.compute(this.from, this.to, delta));
3439		} else {
3440			this.set(this.compute(this.from, this.to, 1));
3441			this.complete();
3442		}
3443	},
3444
3445	set: function(now){
3446		return now;
3447	},
3448
3449	compute: function(from, to, delta){
3450		return Fx.compute(from, to, delta);
3451	},
3452
3453	check: function(){
3454		if (!this.timer) return true;
3455		switch (this.options.link){
3456			case 'cancel': this.cancel(); return true;
3457			case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
3458		}
3459		return false;
3460	},
3461
3462	start: function(from, to){
3463		if (!this.check(from, to)) return this;
3464		this.from = from;
3465		this.to = to;
3466		this.time = 0;
3467		this.transition = this.getTransition();
3468		this.startTimer();
3469		this.onStart();
3470		return this;
3471	},
3472
3473	complete: function(){
3474		if (this.stopTimer()) this.onComplete();
3475		return this;
3476	},
3477
3478	cancel: function(){
3479		if (this.stopTimer()) this.onCancel();
3480		return this;
3481	},
3482
3483	onStart: function(){
3484		this.fireEvent('start', this.subject);
3485	},
3486
3487	onComplete: function(){
3488		this.fireEvent('complete', this.subject);
3489		if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
3490	},
3491
3492	onCancel: function(){
3493		this.fireEvent('cancel', this.subject).clearChain();
3494	},
3495
3496	pause: function(){
3497		this.stopTimer();
3498		return this;
3499	},
3500
3501	resume: function(){
3502		this.startTimer();
3503		return this;
3504	},
3505
3506	stopTimer: function(){
3507		if (!this.timer) return false;
3508		this.time = $time() - this.time;
3509		this.timer = $clear(this.timer);
3510		return true;
3511	},
3512
3513	startTimer: function(){
3514		if (this.timer) return false;
3515		this.time = $time() - this.time;
3516		this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
3517		return true;
3518	}
3519
3520});
3521
3522Fx.compute = function(from, to, delta){
3523	return (to - from) * delta + from;
3524};
3525
3526Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
3527
3528
3529/*
3530---
3531
3532name: Fx.CSS
3533
3534description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
3535
3536license: MIT-style license.
3537
3538requires: [Fx, Element.Style]
3539
3540provides: Fx.CSS
3541
3542...
3543*/
3544
3545Fx.CSS = new Class({
3546
3547	Extends: Fx,
3548
3549	//prepares the base from/to object
3550
3551	prepare: function(element, property, values){
3552		values = $splat(values);
3553		var values1 = values[1];
3554		if (!$chk(values1)){
3555			values[1] = values[0];
3556			values[0] = element.getStyle(property);
3557		}
3558		var parsed = values.map(this.parse);
3559		return {from: parsed[0], to: parsed[1]};
3560	},
3561
3562	//parses a value into an array
3563
3564	parse: function(value){
3565		value = $lambda(value)();
3566		value = (typeof value == 'string') ? value.split(' ') : $splat(value);
3567		return value.map(function(val){
3568			val = String(val);
3569			var found = false;
3570			Fx.CSS.Parsers.each(function(parser, key){
3571				if (found) return;
3572				var parsed = parser.parse(val);
3573				if ($chk(parsed)) found = {value: parsed, parser: parser};
3574			});
3575			found = found || {value: val, parser: Fx.CSS.Parsers.String};
3576			return found;
3577		});
3578	},
3579
3580	//computes by a from and to prepared objects, using their parsers.
3581
3582	compute: function(from, to, delta){
3583		var computed = [];
3584		(Math.min(from.length, to.length)).times(function(i){
3585			computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
3586		});
3587		computed.$family = {name: 'fx:css:value'};
3588		return computed;
3589	},
3590
3591	//serves the value as settable
3592
3593	serve: function(value, unit){
3594		if ($type(value) != 'fx:css:value') value = this.parse(value);
3595		var returned = [];
3596		value.each(function(bit){
3597			returned = returned.concat(bit.parser.serve(bit.value, unit));
3598		});
3599		return returned;
3600	},
3601
3602	//renders the change to an element
3603
3604	render: function(element, property, value, unit){
3605		element.setStyle(property, this.serve(value, unit));
3606	},
3607
3608	//searches inside the page css to find the values for a selector
3609
3610	search: function(selector){
3611		if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
3612		var to = {};
3613		Array.each(document.styleSheets, function(sheet, j){
3614			var href = sheet.href;
3615			if (href && href.contains('://') && !href.contains(document.domain)) return;
3616			var rules = sheet.rules || sheet.cssRules;
3617			Array.each(rules, function(rule, i){
3618				if (!rule.style) return;
3619				var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
3620					return m.toLowerCase();
3621				}) : null;
3622				if (!selectorText || !selectorText.test('^' + selector + '$')) return;
3623				Element.Styles.each(function(value, style){
3624					if (!rule.style[style] || Element.ShortStyles[style]) return;
3625					value = String(rule.style[style]);
3626					to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value;
3627				});
3628			});
3629		});
3630		return Fx.CSS.Cache[selector] = to;
3631	}
3632
3633});
3634
3635Fx.CSS.Cache = {};
3636
3637Fx.CSS.Parsers = new Hash({
3638
3639	Color: {
3640		parse: function(value){
3641			if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
3642			return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
3643		},
3644		compute: function(from, to, delta){
3645			return from.map(function(value, i){
3646				return Math.round(Fx.compute(from[i], to[i], delta));
3647			});
3648		},
3649		serve: function(value){
3650			return value.map(Number);
3651		}
3652	},
3653
3654	Number: {
3655		parse: parseFloat,
3656		compute: Fx.compute,
3657		serve: function(value, unit){
3658			return (unit) ? value + unit : value;
3659		}
3660	},
3661
3662	String: {
3663		parse: $lambda(false),
3664		compute: $arguments(1),
3665		serve: $arguments(0)
3666	}
3667
3668});
3669
3670
3671/*
3672---
3673
3674name: Fx.Tween
3675
3676description: Formerly Fx.Style, effect to transition any CSS property for an element.
3677
3678license: MIT-style license.
3679
3680requires: Fx.CSS
3681
3682provides: [Fx.Tween, Element.fade, Element.highlight]
3683
3684...
3685*/
3686
3687Fx.Tween = new Class({
3688
3689	Extends: Fx.CSS,
3690
3691	initialize: function(element, options){
3692		this.element = this.subject = document.id(element);
3693		this.parent(options);
3694	},
3695
3696	set: function(property, now){
3697		if (arguments.length == 1){
3698			now = property;
3699			property = this.property || this.options.property;
3700		}
3701		this.render(this.element, property, now, this.options.unit);
3702		return this;
3703	},
3704
3705	start: function(property, from, to){
3706		if (!this.check(property, from, to)) return this;
3707		var args = Array.flatten(arguments);
3708		this.property = this.options.property || args.shift();
3709		var parsed = this.prepare(this.element, this.property, args);
3710		return this.parent(parsed.from, parsed.to);
3711	}
3712
3713});
3714
3715Element.Properties.tween = {
3716
3717	set: function(options){
3718		var tween = this.retrieve('tween');
3719		if (tween) tween.cancel();
3720		return this.eliminate('tween').store('tween:options', $extend({link: 'cancel'}, options));
3721	},
3722
3723	get: function(options){
3724		if (options || !this.retrieve('tween')){
3725			if (options || !this.retrieve('tween:options')) this.set('tween', options);
3726			this.store('tween', new Fx.Tween(this, this.retrieve('tween:options')));
3727		}
3728		return this.retrieve('tween');
3729	}
3730
3731};
3732
3733Element.implement({
3734
3735	tween: function(property, from, to){
3736		this.get('tween').start(arguments);
3737		return this;
3738	},
3739
3740	fade: function(how){
3741		var fade = this.get('tween'), o = 'opacity', toggle;
3742		how = $pick(how, 'toggle');
3743		switch (how){
3744			case 'in': fade.start(o, 1); break;
3745			case 'out': fade.start(o, 0); break;
3746			case 'show': fade.set(o, 1); break;
3747			case 'hide': fade.set(o, 0); break;
3748			case 'toggle':
3749				var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
3750				fade.start(o, (flag) ? 0 : 1);
3751				this.store('fade:flag', !flag);
3752				toggle = true;
3753			break;
3754			default: fade.start(o, arguments);
3755		}
3756		if (!toggle) this.eliminate('fade:flag');
3757		return this;
3758	},
3759
3760	highlight: function(start, end){
3761		if (!end){
3762			end = this.retrieve('highlight:original', this.getStyle('background-color'));
3763			end = (end == 'transparent') ? '#fff' : end;
3764		}
3765		var tween = this.get('tween');
3766		tween.start('background-color', start || '#ffff88', end).chain(function(){
3767			this.setStyle('background-color', this.retrieve('highlight:original'));
3768			tween.callChain();
3769		}.bind(this));
3770		return this;
3771	}
3772
3773});
3774
3775
3776/*
3777---
3778
3779name: Fx.Morph
3780
3781description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
3782
3783license: MIT-style license.
3784
3785requires: Fx.CSS
3786
3787provides: Fx.Morph
3788
3789...
3790*/
3791
3792Fx.Morph = new Class({
3793
3794	Extends: Fx.CSS,
3795
3796	initialize: function(element, options){
3797		this.element = this.subject = document.id(element);
3798		this.parent(options);
3799	},
3800
3801	set: function(now){
3802		if (typeof now == 'string') now = this.search(now);
3803		for (var p in now) this.render(this.element, p, now[p], this.options.unit);
3804		return this;
3805	},
3806
3807	compute: function(from, to, delta){
3808		var now = {};
3809		for (var p in from) now[p] = this.parent(from[p], to[p], delta);
3810		return now;
3811	},
3812
3813	start: function(properties){
3814		if (!this.check(properties)) return this;
3815		if (typeof properties == 'string') properties = this.search(properties);
3816		var from = {}, to = {};
3817		for (var p in properties){
3818			var parsed = this.prepare(this.element, p, properties[p]);
3819			from[p] = parsed.from;
3820			to[p] = parsed.to;
3821		}
3822		return this.parent(from, to);
3823	}
3824
3825});
3826
3827Element.Properties.morph = {
3828
3829	set: function(options){
3830		var morph = this.retrieve('morph');
3831		if (morph) morph.cancel();
3832		return this.eliminate('morph').store('morph:options', $extend({link: 'cancel'}, options));
3833	},
3834
3835	get: function(options){
3836		if (options || !this.retrieve('morph')){
3837			if (options || !this.retrieve('morph:options')) this.set('morph', options);
3838			this.store('morph', new Fx.Morph(this, this.retrieve('morph:options')));
3839		}
3840		return this.retrieve('morph');
3841	}
3842
3843};
3844
3845Element.implement({
3846
3847	morph: function(props){
3848		this.get('morph').start(props);
3849		return this;
3850	}
3851
3852});
3853
3854
3855/*
3856---
3857
3858name: Fx.Transitions
3859
3860description: Contains a set of advanced transitions to be used with any of the Fx Classes.
3861
3862license: MIT-style license.
3863
3864credits: Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
3865
3866requires: Fx
3867
3868provides: Fx.Transitions
3869
3870...
3871*/
3872
3873Fx.implement({
3874
3875	getTransition: function(){
3876		var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
3877		if (typeof trans == 'string'){
3878			var data = trans.split(':');
3879			trans = Fx.Transitions;
3880			trans = trans[data[0]] || trans[data[0].capitalize()];
3881			if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
3882		}
3883		return trans;
3884	}
3885
3886});
3887
3888Fx.Transition = function(transition, params){
3889	params = $splat(params);
3890	return $extend(transition, {
3891		easeIn: function(pos){
3892			return transition(pos, params);
3893		},
3894		easeOut: function(pos){
3895			return 1 - transition(1 - pos, params);
3896		},
3897		easeInOut: function(pos){
3898			return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
3899		}
3900	});
3901};
3902
3903Fx.Transitions = new Hash({
3904
3905	linear: $arguments(0)
3906
3907});
3908
3909Fx.Transitions.extend = function(transitions){
3910	for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
3911};
3912
3913Fx.Transitions.extend({
3914
3915	Pow: function(p, x){
3916		return Math.pow(p, x[0] || 6);
3917	},
3918
3919	Expo: function(p){
3920		return Math.pow(2, 8 * (p - 1));
3921	},
3922
3923	Circ: function(p){
3924		return 1 - Math.sin(Math.acos(p));
3925	},
3926
3927	Sine: function(p){
3928		return 1 - Math.sin((1 - p) * Math.PI / 2);
3929	},
3930
3931	Back: function(p, x){
3932		x = x[0] || 1.618;
3933		return Math.pow(p, 2) * ((x + 1) * p - x);
3934	},
3935
3936	Bounce: function(p){
3937		var value;
3938		for (var a = 0, b = 1; 1; a += b, b /= 2){
3939			if (p >= (7 - 4 * a) / 11){
3940				value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
3941				break;
3942			}
3943		}
3944		return value;
3945	},
3946
3947	Elastic: function(p, x){
3948		return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
3949	}
3950
3951});
3952
3953['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
3954	Fx.Transitions[transition] = new Fx.Transition(function(p){
3955		return Math.pow(p, [i + 2]);
3956	});
3957});
3958
3959
3960/*
3961---
3962
3963name: Request
3964
3965description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
3966
3967license: MIT-style license.
3968
3969requires: [Element, Chain, Events, Options, Browser]
3970
3971provides: Request
3972
3973...
3974*/
3975
3976var Request = new Class({
3977
3978	Implements: [Chain, Events, Options],
3979
3980	options: {/*
3981		onRequest: $empty,
3982		onComplete: $empty,
3983		onCancel: $empty,
3984		onSuccess: $empty,
3985		onFailure: $empty,
3986		onException: $empty,*/
3987		url: '',
3988		data: '',
3989		headers: {
3990			'X-Requested-With': 'XMLHttpRequest',
3991			'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
3992		},
3993		async: true,
3994		format: false,
3995		method: 'post',
3996		link: 'ignore',
3997		isSuccess: null,
3998		emulation: true,
3999		urlEncoded: true,
4000		encoding: 'utf-8',
4001		evalScripts: false,
4002		evalResponse: false,
4003		noCache: false
4004	},
4005
4006	initialize: function(options){
4007		this.xhr = new Browser.Request();
4008		this.setOptions(options);
4009		this.options.isSuccess = this.options.isSuccess || this.isSuccess;
4010		this.headers = new Hash(this.options.headers);
4011	},
4012
4013	onStateChange: function(){
4014		if (this.xhr.readyState != 4 || !this.running) return;
4015		this.running = false;
4016		this.status = 0;
4017		$try(function(){
4018			this.status = this.xhr.status;
4019		}.bind(this));
4020		this.xhr.onreadystatechange = $empty;
4021		if (this.options.isSuccess.call(this, this.status)){
4022			this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
4023			this.success(this.response.text, this.response.xml);
4024		} else {
4025			this.response = {text: null, xml: null};
4026			this.failure();
4027		}
4028	},
4029
4030	isSuccess: function(){
4031		return ((this.status >= 200) && (this.status < 300));
4032	},
4033
4034	processScripts: function(text){
4035		if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
4036		return text.stripScripts(this.options.evalScripts);
4037	},
4038
4039	success: function(text, xml){
4040		this.onSuccess(this.processScripts(text), xml);
4041	},
4042
4043	onSuccess: function(){
4044		this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
4045	},
4046
4047	failure: function(){
4048		this.onFailure();
4049	},
4050
4051	onFailure: function(){
4052		this.fireEvent('complete').fireEvent('failure', this.xhr);
4053	},
4054
4055	setHeader: function(name, value){
4056		this.headers.set(name, value);
4057		return this;
4058	},
4059
4060	getHeader: function(name){
4061		return $try(function(){
4062			return this.xhr.getResponseHeader(name);
4063		}.bind(this));
4064	},
4065
4066	check: function(){
4067		if (!this.running) return true;
4068		switch (this.options.link){
4069			case 'cancel': this.cancel(); return true;
4070			case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
4071		}
4072		return false;
4073	},
4074
4075	send: function(options){
4076		if (!this.check(options)) return this;
4077		this.running = true;
4078
4079		var type = $type(options);
4080		if (type == 'string' || type == 'element') options = {data: options};
4081
4082		var old = this.options;
4083		options = $extend({data: old.data, url: old.url, method: old.method}, options);
4084		var data = options.data, url = String(options.url), method = options.method.toLowerCase();
4085
4086		switch ($type(data)){
4087			case 'element': data = document.id(data).toQueryString(); break;
4088			case 'object': case 'hash': data = Hash.toQueryString(data);
4089		}
4090
4091		if (this.options.format){
4092			var format = 'format=' + this.options.format;
4093			data = (data) ? format + '&' + data : format;
4094		}
4095
4096		if (this.options.emulation && !['get', 'post'].contains(method)){
4097			var _method = '_method=' + method;
4098			data = (data) ? _method + '&' + data : _method;
4099			method = 'post';
4100		}
4101
4102		if (this.options.urlEncoded && method == 'post'){
4103			var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
4104			this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
4105		}
4106
4107		if (this.options.noCache){
4108			var noCache = 'noCache=' + new Date().getTime();
4109			data = (data) ? noCache + '&' + data : noCache;
4110		}
4111
4112		var trimPosition = url.lastIndexOf('/');
4113		if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
4114
4115		if (data && method == 'get'){
4116			url = url + (url.contains('?') ? '&' : '?') + data;
4117			data = null;
4118		}
4119
4120		this.xhr.open(method.toUpperCase(), url, this.options.async);
4121
4122		this.xhr.onreadystatechange = this.onStateChange.bind(this);
4123
4124		this.headers.each(function(value, key){
4125			try {
4126				this.xhr.setRequestHeader(key, value);
4127			} catch (e){
4128				this.fireEvent('exception', [key, value]);
4129			}
4130		}, this);
4131
4132		this.fireEvent('request');
4133		this.xhr.send(data);
4134		if (!this.options.async) this.onStateChange();
4135		return this;
4136	},
4137
4138	cancel: function(){
4139		if (!this.running) return this;
4140		this.running = false;
4141		this.xhr.abort();
4142		this.xhr.onreadystatechange = $empty;
4143		this.xhr = new Browser.Request();
4144		this.fireEvent('cancel');
4145		return this;
4146	}
4147
4148});
4149
4150(function(){
4151
4152var methods = {};
4153['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
4154	methods[method] = function(){
4155		var params = Array.link(arguments, {url: String.type, data: $defined});
4156		return this.send($extend(params, {method: method}));
4157	};
4158});
4159
4160Request.implement(methods);
4161
4162})();
4163
4164Element.Properties.send = {
4165
4166	set: function(options){
4167		var send = this.retrieve('send');
4168		if (send) send.cancel();
4169		return this.eliminate('send').store('send:options', $extend({
4170			data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
4171		}, options));
4172	},
4173
4174	get: function(options){
4175		if (options || !this.retrieve('send')){
4176			if (options || !this.retrieve('send:options')) this.set('send', options);
4177			this.store('send', new Request(this.retrieve('send:options')));
4178		}
4179		return this.retrieve('send');
4180	}
4181
4182};
4183
4184Element.implement({
4185
4186	send: function(url){
4187		var sender = this.get('send');
4188		sender.send({data: this, url: url || sender.options.url});
4189		return this;
4190	}
4191
4192});
4193
4194
4195/*
4196---
4197
4198name: Request.HTML
4199
4200description: Extends the basic Request Class with additional methods for interacting with HTML responses.
4201
4202license: MIT-style license.
4203
4204requires: [Request, Element]
4205
4206provides: Request.HTML
4207
4208...
4209*/
4210
4211Request.HTML = new Class({
4212
4213	Extends: Request,
4214
4215	options: {
4216		update: false,
4217		append: false,
4218		evalScripts: true,
4219		filter: false
4220	},
4221
4222	processHTML: function(text){
4223		var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
4224		text = (match) ? match[1] : text;
4225
4226		var container = new Element('div');
4227
4228		return $try(function(){
4229			var root = '<root>' + text + '</root>', doc;
4230			if (Browser.Engine.trident){
4231				doc = new ActiveXObject('Microsoft.XMLDOM');
4232				doc.async = false;
4233				doc.loadXML(root);
4234			} else {
4235				doc = new DOMParser().parseFromString(root, 'text/xml');
4236			}
4237			root = doc.getElementsByTagName('root')[0];
4238			if (!root) return null;
4239			for (var i = 0, k = root.childNodes.length; i < k; i++){
4240				var child = Element.clone(root.childNodes[i], true, true);
4241				if (child) container.grab(child);
4242			}
4243			return container;
4244		}) || container.set('html', text);
4245	},
4246
4247	success: function(text){
4248		var options = this.options, response = this.response;
4249
4250		response.html = text.stripScripts(function(script){
4251			response.javascript = script;
4252		});
4253
4254		var temp = this.processHTML(response.html);
4255
4256		response.tree = temp.childNodes;
4257		response.elements = temp.getElements('*');
4258
4259		if (options.filter) response.tree = response.elements.filter(options.filter);
4260		if (options.update) document.id(options.update).empty().set('html', response.html);
4261		else if (options.append) document.id(options.append).adopt(temp.getChildren());
4262		if (options.evalScripts) $exec(response.javascript);
4263
4264		this.onSuccess(response.tree, response.elements, response.html, response.javascript);
4265	}
4266
4267});
4268
4269Element.Properties.load = {
4270
4271	set: function(options){
4272		var load = this.retrieve('load');
4273		if (load) load.cancel();
4274		return this.eliminate('load').store('load:options', $extend({data: this, link: 'cancel', update: this, method: 'get'}, options));
4275	},
4276
4277	get: function(options){
4278		if (options || ! this.retrieve('load')){
4279			if (options || !this.retrieve('load:options')) this.set('load', options);
4280			this.store('load', new Request.HTML(this.retrieve('load:options')));
4281		}
4282		return this.retrieve('load');
4283	}
4284
4285};
4286
4287Element.implement({
4288
4289	load: function(){
4290		this.get('load').send(Array.link(arguments, {data: Object.type, url: String.type}));
4291		return this;
4292	}
4293
4294});
4295
4296
4297/*
4298---
4299
4300name: Request.JSON
4301
4302description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
4303
4304license: MIT-style license.
4305
4306requires: [Request, JSON]
4307
4308provides: [Request.JSON]
4309
4310...
4311*/
4312
4313Request.JSON = new Class({
4314
4315	Extends: Request,
4316
4317	options: {
4318		secure: true
4319	},
4320
4321	initialize: function(options){
4322		this.parent(options);
4323		this.headers.extend({'Accept': 'application/json', 'X-Request': 'JSON'});
4324	},
4325
4326	success: function(text){
4327		this.response.json = JSON.decode(text, this.options.secure);
4328		this.onSuccess(this.response.json, text);
4329	}
4330
4331});
4332
4333/*
4334---
4335
4336script: More.js
4337
4338description: MooTools More
4339
4340license: MIT-style license
4341
4342authors:
4343- Guillermo Rauch
4344- Thomas Aylott
4345- Scott Kyle
4346
4347requires:
4348- core:1.2.4/MooTools
4349
4350provides: [MooTools.More]
4351
4352...
4353*/
4354
4355MooTools.More = {
4356	'version': '1.2.4.2',
4357	'build': 'bd5a93c0913cce25917c48cbdacde568e15e02ef'
4358};/*
4359---
4360
4361script: Fx.Elements.js
4362
4363description: Effect to change any number of CSS properties of any number of Elements.
4364
4365license: MIT-style license
4366
4367authors:
4368- Valerio Proietti
4369
4370requires:
4371- core:1.2.4/Fx.CSS
4372- /MooTools.More
4373
4374provides: [Fx.Elements]
4375
4376...
4377*/
4378
4379Fx.Elements = new Class({
4380
4381	Extends: Fx.CSS,
4382
4383	initialize: function(elements, options){
4384		this.elements = this.subject = $$(elements);
4385		this.parent(options);
4386	},
4387
4388	compute: function(from, to, delta){
4389		var now = {};
4390		for (var i in from){
4391			var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
4392			for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
4393		}
4394		return now;
4395	},
4396
4397	set: function(now){
4398		for (var i in now){
4399			var iNow = now[i];
4400			for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
4401		}
4402		return this;
4403	},
4404
4405	start: function(obj){
4406		if (!this.check(obj)) return this;
4407		var from = {}, to = {};
4408		for (var i in obj){
4409			var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
4410			for (var p in iProps){
4411				var parsed = this.prepare(this.elements[i], p, iProps[p]);
4412				iFrom[p] = parsed.from;
4413				iTo[p] = parsed.to;
4414			}
4415		}
4416		return this.parent(from, to);
4417	}
4418
4419});/*
4420---
4421
4422script: Fx.Accordion.js
4423
4424description: An Fx.Elements extension which allows you to easily create accordion type controls.
4425
4426license: MIT-style license
4427
4428authors:
4429- Valerio Proietti
4430
4431requires:
4432- core:1.2.4/Element.Event
4433- /Fx.Elements
4434
4435provides: [Fx.Accordion]
4436
4437...
4438*/
4439
4440var Accordion = Fx.Accordion = new Class({
4441
4442	Extends: Fx.Elements,
4443
4444	options: {/*
4445		onActive: $empty(toggler, section),
4446		onBackground: $empty(toggler, section),
4447		fixedHeight: false,
4448		fixedWidth: false,
4449		*/
4450		display: 0,
4451		show: false,
4452		height: true,
4453		width: false,
4454		opacity: true,
4455		alwaysHide: false,
4456		trigger: 'click',
4457		initialDisplayFx: true,
4458		returnHeightToAuto: true
4459	},
4460
4461	initialize: function(){
4462		var params = Array.link(arguments, {'container': Element.type, 'options': Object.type, 'togglers': $defined, 'elements': $defined});
4463		this.parent(params.elements, params.options);
4464		this.togglers = $$(params.togglers);
4465		this.container = document.id(params.container);
4466		this.previous = -1;
4467		this.internalChain = new Chain();
4468		if (this.options.alwaysHide) this.options.wait = true;
4469		if ($chk(this.options.show)){
4470			this.options.display = false;
4471			this.previous = this.options.show;
4472		}
4473		if (this.options.start){
4474			this.options.display = false;
4475			this.options.show = false;
4476		}
4477		this.effects = {};
4478		if (this.options.opacity) this.effects.opacity = 'fullOpacity';
4479		if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';
4480		if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';
4481		for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);
4482		this.elements.each(function(el, i){
4483			if (this.options.show === i){
4484				this.fireEvent('active', [this.togglers[i], el]);
4485			} else {
4486				for (var fx in this.effects) el.setStyle(fx, 0);
4487			}
4488		}, this);
4489		if ($chk(this.options.display)) this.display(this.options.display, this.options.initialDisplayFx);
4490		this.addEvent('complete', this.internalChain.callChain.bind(this.internalChain));
4491	},
4492
4493	addSection: function(toggler, element){
4494		toggler = document.id(toggler);
4495		element = document.id(element);
4496		var test = this.togglers.contains(toggler);
4497		this.togglers.include(toggler);
4498		this.elements.include(element);
4499		var idx = this.togglers.indexOf(toggler);
4500		var displayer = this.display.bind(this, idx);
4501		toggler.store('accordion:display', displayer);
4502		toggler.addEvent(this.options.trigger, displayer);
4503		if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
4504		if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
4505		element.fullOpacity = 1;
4506		if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;
4507		if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;
4508		element.setStyle('overflow', 'hidden');
4509		if (!test){
4510			for (var fx in this.effects) element.setStyle(fx, 0);
4511		}
4512		return this;
4513	},
4514
4515	detach: function(){
4516		this.togglers.each(function(toggler) {
4517			toggler.removeEvent(this.options.trigger, toggler.retrieve('accordion:display'));
4518		}, this);
4519	},
4520
4521	display: function(index, useFx){
4522		if (!this.check(index, useFx)) return this;
4523		useFx = $pick(useFx, true);
4524		if (this.options.returnHeightToAuto){
4525			var prev = this.elements[this.previous];
4526			if (prev && !this.selfHidden){
4527				for (var fx in this.effects){
4528					prev.setStyle(fx, prev[this.effects[fx]]);
4529				}
4530			}
4531		}
4532		index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
4533		if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
4534		this.previous = index;
4535		var obj = {};
4536		this.elements.each(function(el, i){
4537			obj[i] = {};
4538			var hide;
4539			if (i != index){
4540				hide = true;
4541			} else if (this.options.alwaysHide && ((el.offsetHeight > 0 && this.options.height) || el.offsetWidth > 0 && this.options.width)){
4542				hide = true;
4543				this.selfHidden = true;
4544			}
4545			this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
4546			for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
4547		}, this);
4548		this.internalChain.chain(function(){
4549			if (this.options.returnHeightToAuto && !this.selfHidden){
4550				var el = this.elements[index];
4551				if (el) el.setStyle('height', 'auto');
4552			};
4553		}.bind(this));
4554		return useFx ? this.start(obj) : this.set(obj);
4555	}
4556
4557});/*
4558---
4559
4560script: Fx.Scroll.js
4561
4562description: Effect to smoothly scroll any element, including the window.
4563
4564license: MIT-style license
4565
4566authors:
4567- Valerio Proietti
4568
4569requires:
4570- core:1.2.4/Fx
4571- core:1.2.4/Element.Event
4572- core:1.2.4/Element.Dimensions
4573- /MooTools.More
4574
4575provides: [Fx.Scroll]
4576
4577...
4578*/
4579
4580Fx.Scroll = new Class({
4581
4582	Extends: Fx,
4583
4584	options: {
4585		offset: {x: 0, y: 0},
4586		wheelStops: true
4587	},
4588
4589	initialize: function(element, options){
4590		this.element = this.subject = document.id(element);
4591		this.parent(options);
4592		var cancel = this.cancel.bind(this, false);
4593
4594		if ($type(this.element) != 'element') this.element = document.id(this.element.getDocument().body);
4595
4596		var stopper = this.element;
4597
4598		if (this.options.wheelStops){
4599			this.addEvent('start', function(){
4600				stopper.addEvent('mousewheel', cancel);
4601			}, true);
4602			this.addEvent('complete', function(){
4603				stopper.removeEvent('mousewheel', cancel);
4604			}, true);
4605		}
4606	},
4607
4608	set: function(){
4609		var now = Array.flatten(arguments);
4610		if (Browser.Engine.gecko) now = [Math.round(now[0]), Math.round(now[1])];
4611		this.element.scrollTo(now[0], now[1]);
4612	},
4613
4614	compute: function(from, to, delta){
4615		return [0, 1].map(function(i){
4616			return Fx.compute(from[i], to[i], delta);
4617		});
4618	},
4619
4620	start: function(x, y){
4621		if (!this.check(x, y)) return this;
4622		var scrollSize = this.element.getScrollSize(),
4623			scroll = this.element.getScroll(), 
4624			values = {x: x, y: y};
4625		for (var z in values){
4626			var max = scrollSize[z];
4627			if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z] : max;
4628			else values[z] = scroll[z];
4629			values[z] += this.options.offset[z];
4630		}
4631		return this.parent([scroll.x, scroll.y], [values.x, values.y]);
4632	},
4633
4634	toTop: function(){
4635		return this.start(false, 0);
4636	},
4637
4638	toLeft: function(){
4639		return this.start(0, false);
4640	},
4641
4642	toRight: function(){
4643		return this.start('right', false);
4644	},
4645
4646	toBottom: function(){
4647		return this.start(false, 'bottom');
4648	},
4649
4650	toElement: function(el){
4651		var position = document.id(el).getPosition(this.element);
4652		return this.start(position.x, position.y);
4653	},
4654
4655	scrollIntoView: function(el, axes, offset){
4656		axes = axes ? $splat(axes) : ['x','y'];
4657		var to = {};
4658		el = document.id(el);
4659		var pos = el.getPosition(this.element);
4660		var size = el.getSize();
4661		var scroll = this.element.getScroll();
4662		var containerSize = this.element.getSize();
4663		var edge = {
4664			x: pos.x + size.x,
4665			y: pos.y + size.y
4666		};
4667		['x','y'].each(function(axis) {
4668			if (axes.contains(axis)) {
4669				if (edge[axis] > scroll[axis] + containerSize[axis]) to[axis] = edge[axis] - containerSize[axis];
4670				if (pos[axis] < scroll[axis]) to[axis] = pos[axis];
4671			}
4672			if (to[axis] == null) to[axis] = scroll[axis];
4673			if (offset && offset[axis]) to[axis] = to[axis] + offset[axis];
4674		}, this);
4675		if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y);
4676		return this;
4677	},
4678
4679	scrollToCenter: function(el, axes, offset){
4680		axes = axes ? $splat(axes) : ['x', 'y'];
4681		el = $(el);
4682		var to = {},
4683			pos = el.getPosition(this.element),
4684			size = el.getSize(),
4685			scroll = this.element.getScroll(),
4686			containerSize = this.element.getSize(),
4687			edge = {
4688				x: pos.x + size.x,
4689				y: pos.y + size.y
4690			};
4691
4692		['x','y'].each(function(axis){
4693			if(axes.contains(axis)){
4694				to[axis] = pos[axis] - (containerSize[axis] - size[axis])/2;
4695			}
4696			if(to[axis] == null) to[axis] = scroll[axis];
4697			if(offset && offset[axis]) to[axis] = to[axis] + offset[axis];
4698		}, this);
4699		if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y);
4700		return this;
4701	}
4702
4703});
4704/*
4705---
4706
4707script: Fx.Slide.js
4708
4709description: Effect to slide an element in and out of view.
4710
4711license: MIT-style license
4712
4713authors:
4714- Valerio Proietti
4715
4716requires:
4717- core:1.2.4/Fx Element.Style
4718- /MooTools.More
4719
4720provides: [Fx.Slide]
4721
4722...
4723*/
4724
4725Fx.Slide = new Class({
4726
4727	Extends: Fx,
4728
4729	options: {
4730		mode: 'vertical',
4731		hideOverflow: true
4732	},
4733
4734	initialize: function(element, options){
4735		this.addEvent('complete', function(){
4736			this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);
4737			if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper);
4738		}, true);
4739		this.element = this.subject = document.id(element);
4740		this.parent(options);
4741		var wrapper = this.element.retrieve('wrapper');
4742		var styles = this.element.getStyles('margin', 'position', 'overflow');
4743		if (this.options.hideOverflow) styles = $extend(styles, {overflow: 'hidden'});
4744		this.wrapper = wrapper || new Element('div', {
4745			styles: styles
4746		}).wraps(this.element);
4747		this.element.store('wrapper', this.wrapper).setStyle('margin', 0);
4748		this.now = [];
4749		this.open = true;
4750	},
4751
4752	vertical: function(){
4753		this.margin = 'margin-top';
4754		this.layout = 'height';
4755		this.offset = this.element.offsetHeight;
4756	},
4757
4758	horizontal: function(){
4759		this.margin = 'margin-left';
4760		this.layout = 'width';
4761		this.offset = this.element.offsetWidth;
4762	},
4763
4764	set: function(now){
4765		this.element.setStyle(this.margin, now[0]);
4766		this.wrapper.setStyle(this.layout, now[1]);
4767		return this;
4768	},
4769
4770	compute: function(from, to, delta){
4771		return [0, 1].map(function(i){
4772			return Fx.compute(from[i], to[i], delta);
4773		});
4774	},
4775
4776	start: function(how, mode){
4777		if (!this.check(how, mode)) return this;
4778		this[mode || this.options.mode]();
4779		var margin = this.element.getStyle(this.margin).toInt();
4780		var layout = this.wrapper.getStyle(this.layout).toInt();
4781		var caseIn = [[margin, layout], [0, this.offset]];
4782		var caseOut = [[margin, layout], [-this.offset, 0]];
4783		var start;
4784		switch (how){
4785			case 'in': start = caseIn; break;
4786			case 'out': start = caseOut; break;
4787			case 'toggle': start = (layout == 0) ? caseIn : caseOut;
4788		}
4789		return this.parent(start[0], start[1]);
4790	},
4791
4792	slideIn: function(mode){
4793		return this.start('in', mode);
4794	},
4795
4796	slideOut: function(mode){
4797		return this.start('out', mode);
4798	},
4799
4800	hide: function(mode){
4801		this[mode || this.options.mode]();
4802		this.open = false;
4803		return this.set([-this.offset, 0]);
4804	},
4805
4806	show: function(mode){
4807		this[mode || this.options.mode]();
4808		this.open = true;
4809		return this.set([0, this.offset]);
4810	},
4811
4812	toggle: function(mode){
4813		return this.start('toggle', mode);
4814	}
4815
4816});
4817
4818Element.Properties.slide = {
4819
4820	set: function(options){
4821		var slide = this.retrieve('slide');
4822		if (slide) slide.cancel();
4823		return this.eliminate('slide').store('slide:options', $extend({link: 'cancel'}, options));
4824	},
4825
4826	get: function(options){
4827		if (options || !this.retrieve('slide')){
4828			if (options || !this.retrieve('slide:options')) this.set('slide', options);
4829			this.store('slide', new Fx.Slide(this, this.retrieve('slide:options')));
4830		}
4831		return this.retrieve('slide');
4832	}
4833
4834};
4835
4836Element.implement({
4837
4838	slide: function(how, mode){
4839		how = how || 'toggle';
4840		var slide = this.get('slide'), toggle;
4841		switch (how){
4842			case 'hide': slide.hide(mode); break;
4843			case 'show': slide.show(mode); break;
4844			case 'toggle':
4845				var flag = this.retrieve('slide:flag', slide.open);
4846				slide[flag ? 'slideOut' : 'slideIn'](mode);
4847				this.store('slide:flag', !flag);
4848				toggle = true;
4849			break;
4850			default: slide.start(how, mode);
4851		}
4852		if (!toggle) this.eliminate('slide:flag');
4853		return this;
4854	}
4855
4856});
4857/*
4858---
4859
4860script: Fx.SmoothScroll.js
4861
4862description: Class for creating a smooth scrolling effect to all internal links on the page.
4863
4864license: MIT-style license
4865
4866authors:
4867- Valerio Proietti
4868
4869requires:
4870- core:1.2.4/Selectors
4871- /Fx.Scroll
4872
4873provides: [Fx.SmoothScroll]
4874
4875...
4876*/
4877
4878var SmoothScroll = Fx.SmoothScroll = new Class({
4879
4880	Extends: Fx.Scroll,
4881
4882	initialize: function(options, context){
4883		context = context || document;
4884		this.doc = context.getDocument();
4885		var win = context.getWindow();
4886		this.parent(this.doc, options);
4887		this.links = $$(this.options.links || this.doc.links);
4888		var location = win.location.href.match(/^[^#]*/)[0] + '#';
4889		this.links.each(function(link){
4890			if (link.href.indexOf(location) != 0) {return;}
4891			var anchor = link.href.substr(location.length);
4892			if (anchor) this.useLink(link, anchor);
4893		}, this);
4894		if (!Browser.Engine.webkit419) {
4895			this.addEvent('complete', function(){
4896				win.location.hash = this.anchor;
4897			}, true);
4898		}
4899	},
4900
4901	useLink: function(link, anchor){
4902		var el;
4903		link.addEvent('click', function(event){
4904			if (el !== false && !el) el = document.id(anchor) || this.doc.getElement('a[name=' + anchor + ']');
4905			if (el) {
4906				event.preventDefault();
4907				this.anchor = anchor;
4908				this.toElement(el).chain(function(){
4909					this.fireEvent('scrolledTo', [link, el]);
4910				}.bind(this));
4911				link.blur();
4912			}
4913		}.bind(this));
4914	}
4915});/*
4916---
4917
4918script: Drag.js
4919
4920description: The base Drag Class. Can be used to drag and resize Elements using mouse events.
4921
4922license: MIT-style license
4923
4924authors:
4925- Valerio Proietti
4926- Tom Occhinno
4927- Jan Kassens
4928
4929requires:
4930- core:1.2.4/Events
4931- core:1.2.4/Options
4932- core:1.2.4/Element.Event
4933- core:1.2.4/Element.Style
4934- /MooTools.More
4935
4936provides: [Drag]
4937
4938*/
4939
4940var Drag = new Class({
4941
4942	Implements: [Events, Options],
4943
4944	options: {/*
4945		onBeforeStart: $empty(thisElement),
4946		onStart: $empty(thisElement, event),
4947		onSnap: $empty(thisElement)
4948		onDrag: $empty(thisElement, event),
4949		onCancel: $empty(thisElement),
4950		onComplete: $empty(thisElement, event),*/
4951		snap: 6,
4952		unit: 'px',
4953		grid: false,
4954		style: true,
4955		limit: false,
4956		handle: false,
4957		invert: false,
4958		preventDefault: false,
4959		stopPropagation: false,
4960		modifiers: {x: 'left', y: 'top'}
4961	},
4962
4963	initialize: function(){
4964		var params = Array.link(arguments, {'options': Object.type, 'element': $defined});
4965		this.element = document.id(params.element);
4966		this.document = this.element.getDocument();
4967		this.setOptions(params.options || {});
4968		var htype = $type(this.options.handle);
4969		this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element;
4970		this.mouse = {'now': {}, 'pos': {}};
4971		this.value = {'start': {}, 'now': {}};
4972
4973		this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';
4974
4975		this.bound = {
4976			start: this.start.bind(this),
4977			check: this.check.bind(this),
4978			drag: this.drag.bind(this),
4979			stop: this.stop.bind(this),
4980			cancel: this.cancel.bind(this),
4981			eventStop: $lambda(false)
4982		};
4983		this.attach();
4984	},
4985
4986	attach: function(){
4987		this.handles.addEvent('mousedown', this.bound.start);
4988		return this;
4989	},
4990
4991	detach: function(){
4992		this.handles.removeEvent('mousedown', this.bound.start);
4993		return this;
4994	},
4995
4996	start: function(event){
4997		if (event.rightClick) return;
4998		if (this.options.preventDefault) event.preventDefault();
4999		if (this.options.stopPropagation) event.stopPropagation();
5000		this.mouse.start = event.page;
5001		this.fireEvent('beforeStart', this.element);
5002		var limit = this.options.limit;
5003		this.limit = {x: [], y: []};
5004		for (var z in this.options.modifiers){
5005			if (!this.options.modifiers[z]) continue;
5006			if (this.options.style) this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt();
5007			else this.value.now[z] = this.element[this.options.modifiers[z]];
5008			if (this.options.invert) this.value.now[z] *= -1;
5009			this.mouse.pos[z] = event.page[z] - this.value.now[z];
5010			if (limit && limit[z]){
5011				for (var i = 2; i--; i){
5012					if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
5013				}
5014			}
5015		}
5016		if ($type(this.options.grid) == 'number') this.options.grid = {x: this.options.grid, y: this.options.grid};
5017		this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
5018		this.document.addEvent(this.selection, this.bound.eventStop);
5019	},
5020
5021	check: function(event){
5022		if (this.options.preventDefault) event.preventDefault();
5023		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)));
5024		if (distance > this.options.snap){
5025			this.cancel();
5026			this.document.addEvents({
5027				mousemove: this.bound.drag,
5028				mouseup: this.bound.stop
5029			});
5030			this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element);
5031		}
5032	},
5033
5034	drag: function(event){
5035		if (this.options.preventDefault) event.preventDefault();
5036		this.mouse.now = event.page;
5037		for (var z in this.options.modifiers){
5038			if (!this.options.modifiers[z]) continue;
5039			this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
5040			if (this.options.invert) this.value.now[z] *= -1;
5041			if (this.options.limit && this.limit[z]){
5042				if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){
5043					this.value.now[z] = this.limit[z][1];
5044				} else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){
5045					this.value.now[z] = this.limit[z][0];
5046				}
5047			}
5048			if (this.options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % this.options.grid[z]);
5049			if (this.options.style) {
5050				this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
5051			} else {
5052				this.element[this.options.modifiers[z]] = this.value.now[z];
5053			}
5054		}
5055		this.fireEvent('drag', [this.element, event]);
5056	},
5057
5058	cancel: function(event){
5059		this.document.removeEvent('mousemove', this.bound.check);
5060		this.document.removeEvent('mouseup', this.bound.cancel);
5061		if (event){
5062			this.document.removeEvent(this.selection, this.bound.eventStop);
5063			this.fireEvent('cancel', this.element);
5064		}
5065	},
5066
5067	stop: function(event){
5068		this.document.removeEvent(this.selection, this.bound.eventStop);
5069		this.document.removeEvent('mousemove', this.bound.drag);
5070		this.document.removeEvent('mouseup', this.bound.stop);
5071		if (event) this.fireEvent('complete', [this.element, event]);
5072	}
5073
5074});
5075
5076Element.implement({
5077
5078	makeResizable: function(options){
5079		var drag = new Drag(this, $merge({modifiers: {x: 'width', y: 'height'}}, options));
5080		this.store('resizer', drag);
5081		return drag.addEvent('drag', function(){
5082			this.fireEvent('resize', drag);
5083		}.bind(this));
5084	}
5085
5086});
5087/*
5088---
5089
5090script: Drag.Move.js
5091
5092description: A Drag extension that provides support for the constraining of draggables to containers and droppables.
5093
5094license: MIT-style license
5095
5096authors:
5097- Valerio Proietti
5098- Tom Occhinno
5099- Jan Kassens
5100- Aaron Newton
5101- Scott Kyle
5102
5103requires:
5104- core:1.2.4/Element.Dimensions
5105- /Drag
5106
5107provides: [Drag.Move]
5108
5109...
5110*/
5111
5112Drag.Move = new Class({
5113
5114	Extends: Drag,
5115
5116	options: {/*
5117		onEnter: $empty(thisElement, overed),
5118		onLeave: $empty(thisElement, overed),
5119		onDrop: $empty(thisElement, overed, event),*/
5120		droppables: [],
5121		container: false,
5122		precalculate: false,
5123		includeMargins: true,
5124		checkDroppables: true
5125	},
5126
5127	initialize: function(element, options){
5128		this.parent(element, options);
5129		element = this.element;
5130		
5131		this.droppables = $$(this.options.droppables);
5132		this.container = document.id(this.options.container);
5133		
5134		if (this.container && $type(this.container) != 'element')
5135			this.container = document.id(this.container.getDocument().body);
5136		
5137		var styles = element.getStyles('left', 'right', 'position');
5138		if (styles.left == 'auto' || styles.top == 'auto')
5139			element.setPosition(element.getPosition(element.getOffsetParent()));
5140		
5141		if (styles.position == 'static')
5142			element.setStyle('position', 'absolute');
5143
5144		this.addEvent('start', this.checkDroppables, true);
5145
5146		this.overed = null;
5147	},
5148
5149	start: function(event){
5150		if (this.container) this.options.limit = this.calculateLimit();
5151		
5152		if (this.options.precalculate){
5153			this.positions = this.droppables.map(function(el){
5154				return el.getCoordinates();
5155			});
5156		}
5157		
5158		this.parent(event);
5159	},
5160	
5161	calculateLimit: function(){
5162		var offsetParent = this.element.getOffsetParent(),
5163			containerCoordinates = this.container.getCoordinates(offsetParent),
5164			containerBorder = {},
5165			elementMargin = {},
5166			elementBorder = {},
5167			containerMargin = {},
5168			offsetParentPadding = {};
5169
5170		['top', 'right', 'bottom', 'left'].each(function(pad){
5171			containerBorder[pad] = this.container.getStyle('border-' + pad).toInt();
5172			elementBorder[pad] = this.element.getStyle('border-' + pad).toInt();
5173			elementMargin[pad] = this.element.getStyle('margin-' + pad).toInt();
5174			containerMargin[pad] = this.container.getStyle('margin-' + pad).toInt();
5175			offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt();
5176		}, this);
5177
5178		var width = this.element.offsetWidth + elementMargin.left + elementMargin.right,
5179			height = this.element.offsetHeight + elementMargin.top + elementMargin.bottom,
5180			left = 0,
5181			top = 0,
5182			right = containerCoordinates.right - containerBorder.right - width,
5183			bottom = containerCoordinates.bottom - containerBorder.bottom - height;
5184
5185		if (this.options.includeMargins){
5186			left += elementMargin.left;
5187			top += elementMargin.top;
5188		} else {
5189			right += elementMargin.right;
5190			bottom += elementMargin.bottom;
5191		}
5192		
5193		if (this.element.getStyle('position') == 'relative'){
5194			var coords = this.element.getCoordinates(offsetParent);
5195			coords.left -= this.element.getStyle('left').toInt();
5196			coords.top -= this.element.getStyle('top').toInt();
5197			
5198			left += containerBorder.left - coords.left;
5199			top += containerBorder.top - coords.top;
5200			right += elementMargin.left - coords.left;
5201			bottom += elementMargin.top - coords.top;
5202			
5203			if (this.container != offsetParent){
5204				left += containerMargin.left + offsetParentPadding.left;
5205				top += (Browser.Engine.trident4 ? 0 : containerMargin.top) + offsetParentPadding.top;
5206			}
5207		} else {
5208			left -= elementMargin.left;
5209			top -= elementMargin.top;
5210			
5211			if (this.container == offsetParent){
5212				right -= containerBorder.left;
5213				bottom -= containerBorder.top;
5214			} else {
5215				left += containerCoordinates.left + containerBorder.left;
5216				top += containerCoordinates.top + containerBorder.top;
5217			}
5218		}
5219		
5220		return {
5221			x: [left, right],
5222			y: [top, bottom]
5223		};
5224	},
5225
5226	checkAgainst: function(el, i){
5227		el = (this.positions) ? this.positions[i] : el.getCoordinates();
5228		var now = this.mouse.now;
5229		return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
5230	},
5231
5232	checkDroppables: function(){
5233		var overed = this.droppables.filter(this.checkAgainst, this).getLast();
5234		if (this.overed != overed){
5235			if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
5236			if (overed) this.fireEvent('enter', [this.element, overed]);
5237			this.overed = overed;
5238		}
5239	},
5240
5241	drag: function(event){
5242		this.parent(event);
5243		if (this.options.checkDroppables && this.droppables.length) this.checkDroppables();
5244	},
5245
5246	stop: function(event){
5247		this.checkDroppables();
5248		this.fireEvent('drop', [this.element, this.overed, event]);
5249		this.overed = null;
5250		return this.parent(event);
5251	}
5252
5253});
5254
5255Element.implement({
5256
5257	makeDraggable: function(options){
5258		var drag = new Drag.Move(this, options);
5259		this.store('dragger', drag);
5260		return drag;
5261	}
5262
5263});
5264/*
5265---
5266
5267script: Class.Binds.js
5268
5269description: Automagically binds specified methods in a class to the instance of the class.
5270
5271license: MIT-style license
5272
5273authors:
5274- Aaron Newton
5275
5276requires:
5277- core:1.2.4/Class
5278- /MooTools.More
5279
5280provides: [Class.Binds]
5281
5282...
5283*/
5284
5285Class.Mutators.Binds = function(binds){
5286    return binds;
5287};
5288
5289Class.Mutators.initialize = function(initialize){
5290	return function(){
5291		$splat(this.Binds).each(function(name){
5292			var original = this[name];
5293			if (original) this[name] = original.bind(this);
5294		}, this);
5295		return initialize.apply(this, arguments);
5296	};
5297};
5298/*
5299---
5300
5301script: Element.Measure.js
5302
5303description: Extends the Element native object to include methods useful in measuring dimensions.
5304
5305credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"
5306
5307license: MIT-style license
5308
5309authors:
5310- Aaron Newton
5311
5312requires:
5313- core:1.2.4/Element.Style
5314- core:1.2.4/Element.Dimensions
5315- /MooTools.More
5316
5317provides: [Element.Measure]
5318
5319...
5320*/
5321
5322Element.implement({
5323
5324	measure: function(fn){
5325		var vis = function(el) {
5326			return !!(!el || el.offsetHeight || el.offsetWidth);
5327		};
5328		if (vis(this)) return fn.apply(this);
5329		var parent = this.getParent(),
5330			restorers = [],
5331			toMeasure = []; 
5332		while (!vis(parent) && parent != document.body) {
5333			toMeasure.push(parent.expose());
5334			parent = parent.getParent();
5335		}
5336		var restore = this.expose();
5337		var result = fn.apply(this);
5338		restore();
5339		toMeasure.each(function(restore){
5340			restore();
5341		});
5342		return result;
5343	},
5344
5345	expose: function(){
5346		if (this.getStyle('display') != 'none') return $empty;
5347		var before = this.style.cssText;
5348		this.setStyles({
5349			display: 'block',
5350			position: 'absolute',
5351			visibility: 'hidden'
5352		});
5353		return function(){
5354			this.style.cssText = before;
5355		}.bind(this);
5356	},
5357
5358	getDimensions: function(options){
5359		options = $merge({computeSize: false},options);
5360		var dim = {};
5361		var getSize = function(el, options){
5362			return (options.computeSize)?el.getComputedSize(options):el.getSize();
5363		};
5364		var parent = this.getParent('body');
5365		if (parent && this.getStyle('display') == 'none'){
5366			dim = this.measure(function(){
5367				return getSize(this, options);
5368			});
5369		} else if (parent){
5370			try { //safari sometimes crashes here, so catch it
5371				dim = getSize(this, options);
5372			}catch(e){}
5373		} else {
5374			dim = {x: 0, y: 0};
5375		}
5376		return $chk(dim.x) ? $extend(dim, {width: dim.x, height: dim.y}) : $extend(dim, {x: dim.width, y: dim.height});
5377	},
5378
5379	getComputedSize: function(options){
5380		options = $merge({
5381			styles: ['padding','border'],
5382			plains: {
5383				height: ['top','bottom'],
5384				width: ['left','right']
5385			},
5386			mode: 'both'
5387		}, options);
5388		var size = {width: 0,height: 0};
5389		switch (options.mode){
5390			case 'vertical':
5391				delete size.width;
5392				delete options.plains.width;
5393				break;
5394			case 'horizontal':
5395				delete size.height;
5396				delete options.plains.height;
5397				break;
5398		}
5399		var getStyles = [];
5400		//this function might be useful in other places; perhaps it should be outside this function?
5401		$each(options.plains, function(plain, key){
5402			plain.each(function(edge){
5403				options.styles.each(function(style){
5404					getStyles.push((style == 'border') ? style + '-' + edge + '-' + 'width' : style + '-' + edge);
5405				});
5406			});
5407		});
5408		var styles = {};
5409		getStyles.each(function(style){ styles[style] = this.getComputedStyle(style); }, this);
5410		var subtracted = [];
5411		$each(options.plains, function(plain, key){ //keys: width, height, plains: ['left', 'right'], ['top','bottom']
5412			var capitalized = key.capitalize();
5413			size['total' + capitalized] = size['computed' + capitalized] = 0;
5414			plain.each(function(edge){ //top, left, right, bottom
5415				size['computed' + edge.capitalize()] = 0;
5416				getStyles.each(function(style, i){ //padding, border, etc.
5417					//'padding-left'.test('left') size['totalWidth'] = size['width'] + [padding-left]
5418					if (style.test(edge)){
5419						styles[style] = styles[style].toInt() || 0; //styles['padding-left'] = 5;
5420						size['total' + capitalized] = size['total' + capitalized] + styles[style];
5421						size['computed' + edge.capitalize()] = size['computed' + edge.capitalize()] + styles[style];
5422					}
5423					//if width != width (so, padding-left, for instance), then subtract that from the total
5424					if (style.test(edge) && key != style &&
5425						(style.test('border') || style.test('padding')) && !subtracted.contains(style)){
5426						subtracted.push(style);
5427						size['computed' + capitalized] = size['computed' + capitalized]-styles[style];
5428					}
5429				});
5430			});
5431		});
5432
5433		['Width', 'Height'].each(function(value){
5434			var lower = value.toLowerCase();
5435			if(!$chk(size[lower])) return;
5436
5437			size[lower] = size[lower] + this['offset' + value] + size['computed' + value];
5438			size['total' + value] = size[lower] + size['total' + value];
5439			delete size['computed' + value];
5440		}, this);
5441
5442		return $extend(styles, size);
5443	}
5444
5445});/*
5446---
5447
5448script: Slider.js
5449
5450description: Class for creating horizontal and vertical slider controls.
5451
5452license: MIT-style license
5453
5454authors:
5455- Valerio Proietti
5456
5457requires:
5458- core:1.2.4/Element.Dimensions
5459- /Class.Binds
5460- /Drag
5461- /Element.Dimensions
5462- /Element.Measure
5463
5464provides: [Slider]
5465
5466...
5467*/
5468
5469var Slider = new Class({
5470
5471	Implements: [Events, Options],
5472
5473	Binds: ['clickedElement', 'draggedKnob', 'scrolledElement'],
5474
5475	options: {/*
5476		onTick: $empty(intPosition),
5477		onChange: $empty(intStep),
5478		onComplete: $empty(strStep),*/
5479		onTick: function(position){
5480			if (this.options.snap) position = this.toPosition(this.step);
5481			this.knob.setStyle(this.property, position);
5482		},
5483		initialStep: 0,
5484		snap: false,
5485		offset: 0,
5486		range: false,
5487		wheel: false,
5488		steps: 100,
5489		mode: 'horizontal'
5490	},
5491
5492	initialize: function(element, knob, options){
5493		this.setOptions(options);
5494		this.element = document.id(element);
5495		this.knob = document.id(knob);
5496		this.previousChange = this.previousEnd = this.step = -1;
5497		var offset, limit = {}, modifiers = {'x': false, 'y': false};
5498		switch (this.options.mode){
5499			case 'vertical':
5500				this.axis = 'y';
5501				this.property = 'top';
5502				offset = 'offsetHeight';
5503				break;
5504			case 'horizontal':
5505				this.axis = 'x';
5506				this.property = 'left';
5507				offset = 'offsetWidth';
5508		}
5509		
5510		this.full = this.element.measure(function(){ 
5511			this.half = this.knob[offset] / 2; 
5512			return this.element[offset] - this.knob[offset] + (this.options.offset * 2); 
5513		}.bind(this));
5514		
5515		this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
5516		this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
5517		this.range = this.max - this.min;
5518		this.steps = this.options.steps || this.full;
5519		this.stepSize = Math.abs(this.range) / this.steps;
5520		this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;
5521
5522		this.knob.setStyle('position', 'relative').setStyle(this.property, this.options.initialStep ? this.toPosition(this.options.initialStep) : - this.options.offset);
5523		modifiers[this.axis] = this.property;
5524		limit[this.axis] = [- this.options.offset, this.full - this.options.offset];
5525
5526		var dragOptions = {
5527			snap: 0,
5528			limit: limit,
5529			modifiers: modifiers,
5530			onDrag: this.draggedKnob,
5531			onStart: this.draggedKnob,
5532			onBeforeStart: (function(){
5533				this.isDragging = true;
5534			}).bind(this),
5535			onCancel: function() {
5536				this.isDragging = false;
5537			}.bind(this),
5538			onComplete: function(){
5539				this.isDragging = false;
5540				this.draggedKnob();
5541				this.end();
5542			}.bind(this)
5543		};
5544		if (this.options.snap){
5545			dragOptions.grid = Math.ceil(this.stepWidth);
5546			dragOptions.limit[this.axis][1] = this.full;
5547		}
5548
5549		this.drag = new Drag(this.knob, dragOptions);
5550		this.attach();
5551	},
5552
5553	attach: function(){
5554		this.element.addEvent('mousedown', this.clickedElement);
5555		if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement);
5556		this.drag.attach();
5557		return this;
5558	},
5559
5560	detach: function(){
5561		this.element.removeEvent('mousedown', this.clickedElement);
5562		this.element.removeEvent('mousewheel', this.scrolledElement);
5563		this.drag.detach();
5564		return this;
5565	},
5566
5567	set: function(step){
5568		if (!((this.range > 0) ^ (step < this.min))) step = this.min;
5569		if (!((this.range > 0) ^ (step > this.max))) step = this.max;
5570
5571		this.step = Math.round(step);
5572		this.checkStep();
5573		this.fireEvent('tick', this.toPosition(this.step));
5574		this.end();
5575		return this;
5576	},
5577
5578	clickedElement: function(event){
5579		if (this.isDragging || event.target == this.knob) return;
5580
5581		var dir = this.range < 0 ? -1 : 1;
5582		var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
5583		position = position.limit(-this.options.offset, this.full -this.options.offset);
5584
5585		this.step = Math.round(this.min + dir * this.toStep(position));
5586		this.checkStep();
5587		this.fireEvent('tick', position);
5588		this.end();
5589	},
5590
5591	scrolledElement: function(event){
5592		var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
5593		this.set(mode ? this.step - this.stepSize : this.step + this.stepSize);
5594		event.stop();
5595	},
5596
5597	draggedKnob: function(){
5598		var dir = this.range < 0 ? -1 : 1;
5599		var position = this.drag.value.now[this.axis];
5600		position = position.limit(-this.options.offset, this.full -this.options.offset);
5601		this.step = Math.round(this.min + dir * this.toStep(position));
5602		this.checkStep();
5603	},
5604
5605	checkStep: function(){
5606		if (this.previousChange != this.step){
5607			this.previousChange = this.step;
5608			this.fireEvent('change', this.step);
5609		}
5610	},
5611
5612	end: function(){
5613		if (this.previousEnd !== this.step){
5614			this.previousEnd = this.step;
5615			this.fireEvent('complete', this.step + '');
5616		}
5617	},
5618
5619	toStep: function(position){
5620		var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
5621		return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
5622	},
5623
5624	toPosition: function(step){
5625		return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
5626	}
5627
5628});/*
5629---
5630
5631script: Sortables.js
5632
5633description: Class for creating a drag and drop sorting interface for lists of items.
5634
5635license: MIT-style license
5636
5637authors:
5638- Tom Occhino
5639
5640requires:
5641- /Drag.Move
5642
5643provides: [Slider]
5644
5645...
5646*/
5647
5648var Sortables = new Class({
5649
5650	Implements: [Events, Options],
5651
5652	options: {/*
5653		onSort: $empty(element, clone),
5654		onStart: $empty(element, clone),
5655		onComplete: $empty(element),*/
5656		snap: 4,
5657		opacity: 1,
5658		clone: false,
5659		revert: false,
5660		handle: false,
5661		constrain: false
5662	},
5663
5664	initialize: function(lists, options){
5665		this.setOptions(options);
5666		this.elements = [];
5667		this.lists = [];
5668		this.idle = true;
5669
5670		this.addLists($$(document.id(lists) || lists));
5671		if (!this.options.clone) this.options.revert = false;
5672		if (this.options.revert) this.effect = new Fx.Morph(null, $merge({duration: 250, link: 'cancel'}, this.options.revert));
5673	},
5674
5675	attach: function(){
5676		this.addLists(this.lists);
5677		return this;
5678	},
5679
5680	detach: function(){
5681		this.lists = this.removeLists(this.lists);
5682		return this;
5683	},
5684
5685	addItems: function(){
5686		Array.flatten(arguments).each(function(element){
5687			this.elements.push(element);
5688			var start = element.retrieve('sortables:start', this.start.bindWithEvent(this, element));
5689			(this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
5690		}, this);
5691		return this;
5692	},
5693
5694	addLists: function(){
5695		Array.flatten(arguments).each(function(list){
5696			this.lists.push(list);
5697			this.addItems(list.getChildren());
5698		}, this);
5699		return this;
5700	},
5701
5702	removeItems: function(){
5703		return $$(Array.flatten(arguments).map(function(element){
5704			this.elements.erase(element);
5705			var start = element.retrieve('sortables:start');
5706			(this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
5707			
5708			return element;
5709		}, this));
5710	},
5711
5712	removeLists: function(){
5713		return $$(Array.flatten(arguments).map(function(list){
5714			this.lists.erase(list);
5715			this.removeItems(list.getChildren());
5716			
5717			return list;
5718		}, this));
5719	},
5720
5721	getClone: function(event, element){
5722		if (!this.options.clone) return new Element('div').inject(document.body);
5723		if ($type(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
5724		return element.clone(true).setStyles({
5725			margin: '0px',
5726			position: 'absolute',
5727			visibility: 'hidden',
5728			'width': element.getStyle('width')
5729		}).inject(this.list).setPosition(element.getPosition(element.getOffsetParent()));
5730	},
5731
5732	getDroppables: function(){
5733		var droppables = this.list.getChildren();
5734		if (!this.options.constrain) droppables = this.lists.concat(droppables).erase(this.list);
5735		return droppables.erase(this.clone).erase(this.element);
5736	},
5737
5738	insert: function(dragging, element){
5739		var where = 'inside';
5740		if (this.lists.contains(element)){
5741			this.list = element;
5742			this.drag.droppables = this.getDroppables();
5743		} else {
5744			where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
5745		}
5746		this.element.inject(element, where);
5747		this.fireEvent('sort', [this.element, this.clone]);
5748	},
5749
5750	start: function(event, element){
5751		if (!this.idle) return;
5752		this.idle = false;
5753		this.element = element;
5754		this.opacity = element.get('opacity');
5755		this.list = element.getParent();
5756		this.clone = this.getClone(event, element);
5757
5758		this.drag = new Drag.Move(this.clone, {
5759			snap: this.options.snap,
5760			container: this.options.constrain && this.element.getParent(),
5761			droppables: this.getDroppables(),
5762			onSnap: function(){
5763				event.stop();
5764				this.clone.setStyle('visibility', 'visible');
5765				this.element.set('opacity', this.options.opacity || 0);
5766				this.fireEvent('start', [this.element, this.clone]);
5767			}.bind(this),
5768			onEnter: this.insert.bind(this),
5769			onCancel: this.reset.bind(this),
5770			onComplete: this.end.bind(this)
5771		});
5772
5773		this.clone.inject(this.element, 'before');
5774		this.drag.start(event);
5775	},
5776
5777	end: function(){
5778		this.drag.detach();
5779		this.element.set('opacity', this.opacity);
5780		if (this.effect){
5781			var dim = this.element.getStyles('width', 'height');
5782			var pos = this.clone.computePosition(this.element.getPosition(this.clone.offsetParent));
5783			this.effect.element = this.clone;
5784			this.effect.start({
5785				top: pos.top,
5786				left: pos.left,
5787				width: dim.width,
5788				height: dim.height,
5789				opacity: 0.25
5790			}).chain(this.reset.bind(this));
5791		} else {
5792			this.reset();
5793		}
5794	},
5795
5796	reset: function(){
5797		this.idle = true;
5798		this.clone.destroy();
5799		this.fireEvent('complete', this.element);
5800	},
5801
5802	serialize: function(){
5803		var params = Array.link(arguments, {modifier: Function.type, index: $defined});
5804		var serial = this.lists.map(function(list){
5805			return list.getChildren().map(params.modifier || function(element){
5806				return element.get('id');
5807			}, this);
5808		}, this);
5809
5810		var index = params.index;
5811		if (this.lists.length == 1) index = 0;
5812		return $chk(index) && index >= 0 && index < this.lists.length ? serial[index] : serial;
5813	}
5814
5815});
5816/*
5817---
5818
5819script: Color.js
5820
5821description: Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.
5822
5823license: MIT-style license
5824
5825authors:
5826- Valerio Proietti
5827
5828requires:
5829- core:1.2.4/Array
5830- core:1.2.4/String
5831- core:1.2.4/Number
5832- core:1.2.4/Hash
5833- core:1.2.4/Function
5834- core:1.2.4/$util
5835
5836provides: [Color]
5837
5838...
5839*/
5840
5841var Color = new Native({
5842
5843	initialize: function(color, type){
5844		if (arguments.length >= 3){
5845			type = 'rgb'; color = Array.slice(arguments, 0, 3);
5846		} else if (typeof color == 'string'){
5847			if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true);
5848			else if (color.match(/hsb/)) color = color.hsbToRgb();
5849			else color = color.hexToRgb(true);
5850		}
5851		type = type || 'rgb';
5852		switch (type){
5853			case 'hsb':
5854				var old = color;
5855				color = color.hsbToRgb();
5856				color.hsb = old;
5857			break;
5858			case 'hex': color = color.hexToRgb(true); break;
5859		}
5860		color.rgb = color.slice(0, 3);
5861		color.hsb = color.hsb || color.rgbToHsb();
5862		color.hex = color.rgbToHex();
5863		return $extend(color, this);
5864	}
5865
5866});
5867
5868Color.implement({
5869
5870	mix: function(){
5871		var colors = Array.slice(arguments);
5872		var alpha = ($type(colors.getLast()) == 'number') ? colors.pop() : 50;
5873		var rgb = this.slice();
5874		colors.each(function(color){
5875			color = new Color(color);
5876			for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha));
5877		});
5878		return new Color(rgb, 'rgb');
5879	},
5880
5881	invert: function(){
5882		return new Color(this.map(function(value){
5883			return 255 - value;
5884		}));
5885	},
5886
5887	setHue: function(value){
5888		return new Color([value, this.hsb[1], this.hsb[2]], 'hsb');
5889	},
5890
5891	setSaturation: function(percent){
5892		return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb');
5893	},
5894
5895	setBrightness: function(percent){
5896		return new Color([this.hsb[0], this.hsb[1], percent], 'hsb');
5897	}
5898
5899});
5900
5901var $RGB = function(r, g, b){
5902	return new Color([r, g, b], 'rgb');
5903};
5904
5905var $HSB = function(h, s, b){
5906	return new Color([h, s, b], 'hsb');
5907};
5908
5909var $HEX = function(hex){
5910	return new Color(hex, 'hex');
5911};
5912
5913Array.implement({
5914
5915	rgbToHsb: function(){
5916		var red = this[0],
5917				green = this[1],
5918				blue = this[2],
5919				hue = 0;
5920		var max = Math.max(red, green, blue),
5921				min = Math.min(red, green, blue);
5922		var delta = max - min;
5923		var brightness = max / 255,
5924				saturation = (max != 0) ? delta / max : 0;
5925		if(saturation != 0) {
5926			var rr = (max - red) / delta;
5927			var gr = (max - green) / delta;
5928			var br = (max - blue) / delta;
5929			if (red == max) hue = br - gr;
5930			else if (green == max) hue = 2 + rr - br;
5931			else hue = 4 + gr - rr;
5932			hue /= 6;
5933			if (hue < 0) hue++;
5934		}
5935		return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)];
5936	},
5937
5938	hsbToRgb: function(){
5939		var br = Math.round(this[2] / 100 * 255);
5940		if (this[1] == 0){
5941			return [br, br, br];
5942		} else {
5943			var hue = this[0] % 360;
5944			var f = hue % 60;
5945			var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
5946			var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255);
5947			var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255);
5948			switch (Math.floor(hue / 60)){
5949				case 0: return [br, t, p];
5950				case 1: return [q, br, p];
5951				case 2: return [p, br, t];
5952				case 3: return [p, q, br];
5953				case 4: return [t, p, br];
5954				case 5: return [br, p, q];
5955			}
5956		}
5957		return false;
5958	}
5959
5960});
5961
5962String.implement({
5963
5964	rgbToHsb: function(){
5965		var rgb = this.match(/\d{1,3}/g);
5966		return (rgb) ? rgb.rgbToHsb() : null;
5967	},
5968
5969	hsbToRgb: function(){
5970		var hsb = this.match(/\d{1,3}/g);
5971		return (hsb) ? hsb.hsbToRgb() : null;
5972	}
5973
5974});
5975/*
5976---
5977
5978script: Group.js
5979
5980description: Class for monitoring collections of events
5981
5982license: MIT-style license
5983
5984authors:
5985- Valerio Proietti
5986
5987requires:
5988- core:1.2.4/Events
5989- /MooTools.More
5990
5991provides: [Group]
5992
5993...
5994*/
5995
5996var Group = new Class({
5997
5998	initialize: function(){
5999		this.instances = Array.flatten(arguments);
6000		this.events = {};
6001		this.checker = {};
6002	},
6003
6004	addEvent: function(type, fn){
6005		this.checker[type] = this.checker[type] || {};
6006		this.events[type] = this.events[type] || [];
6007		if (this.events[type].contains(fn)) return false;
6008		else this.events[type].push(fn);
6009		this.instances.each(function(instance, i){
6010			instance.addEvent(type, this.check.bind(this, [type, instance, i]));
6011		}, this);
6012		return this;
6013	},
6014
6015	check: function(type, instance, i){
6016		this.checker[type][i] = true;
6017		var every = this.instances.every(function(current, j){
6018			return this.checker[type][j] || false;
6019		}, this);
6020		if (!every) return;
6021		this.checker[type] = {};
6022		this.events[type].each(function(event){
6023			event.call(this, this.instances, instance);
6024		}, this);
6025	}
6026
6027});
6028/*
6029---
6030
6031script: Hash.Cookie.js
6032
6033description: Class for creating, reading, and deleting Cookies in JSON format.
6034
6035license: MIT-style license
6036
6037authors:
6038- Valerio Proietti
6039- Aaron Newton
6040
6041requires:
6042- core:1.2.4/Cookie
6043- core:1.2.4/JSON
6044- /MooTools.More
6045
6046provides: [Hash.Cookie]
6047
6048...
6049*/
6050
6051Hash.Cookie = new Class({
6052
6053	Extends: Cookie,
6054
6055	options: {
6056		autoSave: true
6057	},
6058
6059	initialize: function(name, options){
6060		this.parent(name, options);
6061		this.load();
6062	},
6063
6064	save: function(){
6065		var value = JSON.encode(this.hash);
6066		if (!value || value.length > 4096) return false; //cookie would be truncated!
6067		if (value == '{}') this.dispose();
6068		else this.write(value);
6069		return true;
6070	},
6071
6072	load: function(){
6073		this.hash = new Hash(JSON.decode(this.read(), true));
6074		return this;
6075	}
6076
6077});
6078
6079Hash.each(Hash.prototype, function(method, name){
6080	if (typeof method == 'function') Hash.Cookie.implement(name, function(){
6081		var value = method.apply(this.hash, arguments);
6082		if (this.options.autoSave) this.save();
6083		return value;
6084	});
6085});/*
6086---
6087
6088script: Scroller.js
6089
6090description: Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries.
6091
6092license: MIT-style license
6093
6094authors:
6095- Valerio Proietti
6096
6097requires:
6098- core:1.2.4/Events
6099- core:1.2.4/Options
6100- core:1.2.4/Element.Event
6101- core:1.2.4/Element.Dimensions
6102
6103provides: [Scroller]
6104
6105...
6106*/
6107
6108var Scroller = new Class({
6109
6110	Implements: [Events, Options],
6111
6112	options: {
6113		area: 20,
6114		velocity: 1,
6115		onChange: function(x, y){
6116			this.element.scrollTo(x, y);
6117		},
6118		fps: 50
6119	},
6120
6121	initialize: function(element, options){
6122		this.setOptions(options);
6123		this.element = document.id(element);
6124		this.listener = ($type(this.element) != 'element') ? document.id(this.element.getDocument().body) : this.element;
6125		this.timer = null;
6126		this.bound = {
6127			attach: this.attach.bind(this),
6128			detach: this.detach.bind(this),
6129			getCoords: this.getCoords.bind(this)
6130		};
6131	},
6132
6133	start: function(){
6134		this.listener.addEvents({
6135			mouseover: this.bound.attach,
6136			mouseout: this.bound.detach
6137		});
6138	},
6139
6140	stop: function(){
6141		this.listener.removeEvents({
6142			mouseover: this.bound.attach,
6143			mouseout: this.bound.detach
6144		});
6145		this.detach();
6146		this.timer = $clear(this.timer);
6147	},
6148
6149	attach: function(){
6150		this.listener.addEvent('mousemove', this.bound.getCoords);
6151	},
6152
6153	detach: function(){
6154		this.listener.removeEvent('mousemove', this.bound.getCoords);
6155		this.timer = $clear(this.timer);
6156	},
6157
6158	getCoords: function(event){
6159		this.page = (this.listener.get('tag') == 'body') ? event.client : event.page;
6160		if (!this.timer) this.timer = this.scroll.periodical(Math.round(1000 / this.options.fps), this);
6161	},
6162
6163	scroll: function(){
6164		var size = this.element.getSize(), 
6165			scroll = this.element.getScroll(), 
6166			pos = this.element.getOffsets(), 
6167			scrollSize = this.element.getScrollSize(), 
6168			change = {x: 0, y: 0};
6169		for (var z in this.page){
6170			if (this.page[z] < (this.options.area + pos[z]) && scroll[z] != 0)
6171				change[z] = (this.page[z] - this.options.area - pos[z]) * this.options.velocity;
6172			else if (this.page[z] + this.options.area > (size[z] + pos[z]) && scroll[z] + size[z] != scrollSize[z])
6173				change[z] = (this.page[z] - size[z] + this.options.area - pos[z]) * this.options.velocity;
6174		}
6175		if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x, scroll.y + change.y]);
6176	}
6177
6178});/*
6179---
6180
6181script: Tips.js
6182
6183name: Tips
6184
6185description: Class for creating nice tips that follow the mouse 
6185cursor when hovering an element.
6186
6187license: MIT-style license
6188
6189authors:
6190  - Valerio Proietti
6191  - Christoph Pojer
6192
6193requires:
6194  - Core:1.2.4/Options
6195  - Core:1.2.4/Events
6196  - Core:1.2.4/Element.Event
6197  - Core:1.2.4/Element.Style
6198  - Core:1.2.4/Element.Dimensions
6199  - /MooTools.More
6200
6201provides: [Tips]
6202
6203...
6204*/
6205
6206(function(){
6207
6208var read = function(option, element){
6209	return (option) ? ($type(option) == 'function' ? option(element) : element.get(option)) : '';
6210};
6211
6212this.Tips = new Class({
6213
6214	Implements: [Events, Options],
6215
6216	options: {
6217		/*
6218		onAttach: $empty(element),
6219		onDetach: $empty(element),
6220		*/
6221		onShow: function(){
6222			this.tip.setStyle('display', 'block');
6223		},
6224		onHide: function(){
6225			this.tip.setStyle('display', 'none');
6226		},
6227		title: 'title',
6228		text: function(element){
6229			return element.get('rel') || element.get('href');
6230		},
6231		showDelay: 100,
6232		hideDelay: 100,
6233		className: 'tip-wrap',
6234		offset: {x: 16, y: 16},
6235		windowPadding: {x:0, y:0},
6236		fixed: false
6237	},
6238
6239	initialize: function(){
6240		var params = Array.link(arguments, {options: Object.type, elements: $defined});
6241		this.setOptions(params.options);
6242		if (params.elements) this.attach(params.elements);
6243		this.container = new Element('div', {'class': 'tip'});
6244	},
6245
6246	toElement: function(){
6247		if (this.tip) return this.tip;
6248		
6249		this.container = new Element('div', {'class': 'tip'});
6250		return this.tip = new Element('div', {
6251			'class': this.options.className,
6252			styles: {
6253				position: 'absolute',
6254				top: 0,
6255				left: 0
6256			}
6257		}).adopt(
6258			new Element('div', {'class': 'tip-top'}),
6259			this.container,
6260			new Element('div', {'class': 'tip-bottom'})
6261		).inject(document.body);
6262	},
6263
6264	attach: function(elements){
6265		$$(elements).each(function(element){
6266			var title = read(this.options.title, element),
6267				text = read(this.options.text, element);
6268			
6269			element.erase('title').store('tip:native', title).retrieve('tip:title', title);
6270			element.retrieve('tip:text', text);
6271			this.fireEvent('attach', [element]);
6272			
6273			var events = ['enter', 'leave'];
6274			if (!this.options.fixed) events.push('move');
6275			
6276			events.each(function(value){
6277				var event = element.retrieve('tip:' + value);
6278				if (!event) event = this['element' + value.capitalize()].bindWithEvent(this, element);
6279				
6280				element.store('tip:' + value, event).addEvent('mouse' + value, event);
6281			}, this);
6282		}, this);
6283		
6284		return this;
6285	},
6286
6287	detach: function(elements){
6288		$$(elements).each(function(element){
6289			['enter', 'leave', 'move'].each(function(value){
6290				element.removeEvent('mouse' + value, element.retrieve('tip:' + value)).eliminate('tip:' + value);
6291			});
6292			
6293			this.fireEvent('detach', [element]);
6294			
6295			if (this.options.title == 'title'){ // This is necessary to check if we can revert the title
6296				var original = element.retrieve('tip:native');
6297				if (original) element.set('title', original);
6298			}
6299		}, this);
6300		
6301		return this;
6302	},
6303
6304	elementEnter: function(event, element){
6305		this.container.empty();
6306		
6307		['title', 'text'].each(function(value){
6308			var content = element.retrieve('tip:' + value);
6309			if (content) this.fill(new Element('div', {'class': 'tip-' + value}).inject(this.container), content);
6310		}, this);
6311		
6312		$clear(this.timer);
6313		this.timer = (function(){
6314			this.show(element);
6315			this.position((this.options.fixed) ? {page: element.getPosition()} : event);
6316		}).delay(this.options.showDelay, this);
6317	},
6318
6319	elementLeave: function(event, element){
6320		$clear(this.timer);
6321		this.timer = this.hide.delay(this.options.hideDelay, this, element);
6322		this.fireForParent(event, element);
6323	},
6324
6325	fireForParent: function(event, element){
6326		element = element.getParent();
6327		if (!element || element == document.body) return;
6328		if (element.retrieve('tip:enter')) element.fireEvent('mouseenter', event);
6329		else this.fireForParent(event, element);
6330	},
6331
6332	elementMove: function(event, element){
6333		this.position(event);
6334	},
6335
6336	position: function(event){
6337		if (!this.tip) document.id(this);
6338
6339		var size = window.getSize(), scroll = window.getScroll(),
6340			tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight},
6341			props = {x: 'left', y: 'top'},
6342			obj = {};
6343		
6344		for (var z in props){
6345			obj[props[z]] = event.page[z] + this.options.offset[z];
6346			if ((obj[props[z]] + tip[z] - scroll[z]) > size[z] - this.options.windowPadding[z]) obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z];
6347		}
6348		
6349		this.tip.setStyles(obj);
6350	},
6351
6352	fill: function(element, contents){
6353		if(typeof contents == 'string') element.set('html', contents);
6354		else element.adopt(contents);
6355	},
6356
6357	show: function(element){
6358		if (!this.tip) document.id(this);
6359		this.fireEvent('show', [this.tip, element]);
6360	},
6361
6362	hide: function(element){
6363		if (!this.tip) document.id(this);
6364		this.fireEvent('hide', [this.tip, element]);
6365	}
6366
6367});
6368
6369})();
6370
6371/*
6372---
6373
6374script: Assets.js
6375
6376description: Provides methods to dynamically load JavaScript, CSS, and Image files into the document.
6377
6378license: MIT-style license
6379
6380authors:
6381- Valerio Proietti
6382
6383requires:
6384- core:1.2.4/Element.Event
6385- /MooTools.More
6386
6387provides: [Assets]
6388
6389...
6390*/
6391
6392var Asset = {
6393
6394	javascript: function(source, properties){
6395		properties = $extend({
6396			onload: $empty,
6397			document: document,
6398			check: $lambda(true)
6399		}, properties);
6400
6401		var script = new Element('script', {src: source, type: 'text/javascript'});
6402
6403		var load = properties.onload.bind(script), 
6404			check = properties.check, 
6405			doc = properties.document;
6406		delete properties.onload;
6407		delete properties.check;
6408		delete properties.document;
6409
6410		script.addEvents({
6411			load: load,
6412			readystatechange: function(){
6413				if (['loaded', 'complete'].contains(this.readyState)) load();
6414			}
6415		}).set(properties);
6416
6417		if (Browser.Engine.webkit419) var checker = (function(){
6418			if (!$try(check)) return;
6419			$clear(checker);
6420			load();
6421		}).periodical(50);
6422
6423		return script.inject(doc.head);
6424	},
6425
6426	css: function(source, properties){
6427		return new Element('link', $merge({
6428			rel: 'stylesheet',
6429			media: 'screen',
6430			type: 'text/css',
6431			href: source
6432		}, properties)).inject(document.head);
6433	},
6434
6435	image: function(source, properties){
6436		properties = $merge({
6437			onload: $empty,
6438			onabort: $empty,
6439			onerror: $empty
6440		}, properties);
6441		var image = new Image();
6442		var element = document.id(image) || new Element('img');
6443		['load', 'abort', 'error'].each(function(name){
6444			var type = 'on' + name;
6445			var event = properties[type];
6446			delete properties[type];
6447			image[type] = function(){
6448				if (!image) return;
6449				if (!element.parentNode){
6450					element.width = image.width;
6451					element.height = image.height;
6452				}
6453				image = image.onload = image.onabort = image.onerror = null;
6454				event.delay(1, element, element);
6455				element.fireEvent(name, element, 1);
6456			};
6457		});
6458		image.src = element.src = source;
6459		if (image && image.complete) image.onload.delay(1);
6460		return element.set(properties);
6461	},
6462
6463	images: function(sources, options){
6464		options = $merge({
6465			onComplete: $empty,
6466			onProgress: $empty,
6467			onError: $empty,
6468			properties: {}
6469		}, options);
6470		sources = $splat(sources);
6471		var images = [];
6472		var counter = 0;
6473		return new Elements(sources.map(function(source){
6474			return Asset.image(source, $extend(options.properties, {
6475				onload: function(){
6476					options.onProgress.call(this, counter, sources.indexOf(source));
6477					counter++;
6478					if (counter == sources.length) options.onComplete();
6479				},
6480				onerror: function(){
6481					options.onError.call(this, counter, sources.indexOf(source));
6482					counter++;
6483					if (counter == sources.length) options.onComplete();
6484				}
6485			}));
6486		}));
6487	}
6488
6489};
6490
6491/*
6492---
6493
6494script: mootools-1.1-to-1.2-upgrade-helper.js
6495
6496description: Provides legacy API for Mootools 1.2
6497
6498license: MIT-style license
6499
6500authors:
6501- Aaron Newton
6502
6503...
6504*/
6505
6506if(!window.console) var console = {};
6507if(!console.log) console.log = function(){};
6508if(!console.warn) console.warn = console.log;
6509if(!console.error) console.error = console.warn;
6510
6511MooTools.upgradeLog = function() {
6512	if (console[this.upgradeLogLevel]) console[this.upgradeLogLevel].apply(console, arguments);
6513};
6514
6515(function(){
6516	oldA = $A;
6517	window.$A = function(iterable, start, length){
6518		if (start != undefined && length != undefined) {
6519			MooTools.upgradeLog('1.1 > 1.2: $A no longer takes start and length arguments.');
6520			if (Browser.Engine.trident && $type(iterable) == 'collection'){
6521				start = start || 0;
6522				if (start < 0) start = iterable.length + start;
6523				length = length || (iterable.length - start);
6524				var array = [];
6525				for (var i = 0; i < length; i++) array[i] = iterable[start++];
6526				return array;
6527			}
6528			start = (start || 0) + ((start < 0) ? iterable.length : 0);
6529			var end = ((!$chk(length)) ? iterable.length : length) + start;
6530			return Array.prototype.slice.call(iterable, start, end);
6531		}
6532		return oldA(iterable);
6533	};
6534
6535
6536	var strs = ['Array', 'Function', 'String', 'RegExp', 'Number', 'Window', 'Document', 'Element', 'Elements'];
6537	for (var i = 0, l = strs.length; i < l; i++) {
6538		var type = strs[i];
6539		var natv = window[type];
6540		if (natv) {
6541			var extend = natv.extend;
6542			natv.extend = function(props){
6543				MooTools.upgradeLog('1.1 > 1.2: native types no longer use .extend to add methods to prototypes but instead use .implement. NOTE: YOUR METHODS WERE NOT IMPLEMENTED ON THE NATIVE ' + type.toUpperCase() + ' PROTOTYPE.');
6544				return extend.apply(this, arguments);
6545			};
6546		}
6547	}
6548})();
6549
6550window.onDomReady = Window.onDomReady = function(fn){
6551	MooTools.upgradeLog('1.1 > 1.2: window.onDomReady is no longer supported. Use window.addEvent("domready") instead');
6552	return window.addEvent('domready', fn);
6553};if (Browser.__defineGetter__) {
6554	Browser.__defineGetter__('hasGetter',function(){
6555		return true;
6556	});
6557}
6558
6559if(Browser.hasGetter){ // webkit, gecko, opera support
6560	
6561	window.__defineGetter__('ie',function(){
6562		MooTools.upgradeLog('1.1 > 1.2: window.ie is deprecated. Use Browser.Engine.trident');
6563		return (Browser.Engine.name == 'trident') ? true : false;
6564	});
6565	window.__defineGetter__('ie6',function(){
6566		MooTools.upgradeLog('1.1 > 1.2: window.ie6 is deprecated. Use Browser.Engine.trident and Browser.Engine.version');
6567		return (Browser.Engine.name == 'trident' && Browser.Engine.version == 4) ? true : false;
6568	});
6569	window.__defineGetter__('ie7',function(){
6570		MooTools.upgradeLog('1.1 > 1.2: window.ie7 is deprecated. Use Browser.Engine.trident and Browser.Engine.version');
6571		return (Browser.Engine.name == 'trident' && Browser.Engine.version == 5) ? true : false;
6572	});
6573	window.__defineGetter__('gecko',function(){
6574		MooTools.upgradeLog('1.1 > 1.2: window.gecko is deprecated. Use Browser.Engine.gecko');
6575		return (Browser.Engine.name == 'gecko') ? true : false;
6576	});
6577	window.__defineGetter__('webkit',function(){
6578		MooTools.upgradeLog('1.1 > 1.2: window.webkit is deprecated. Use Browser.Engine.webkit');
6579		return (Browser.Engine.name == 'webkit') ? true : false;
6580	});
6581	window.__defineGetter__('webkit419',function(){
6582		MooTools.upgradeLog('1.1 > 1.2: window.webkit is deprecated. Use Browser.Engine.webkit and Browser.Engine.version');
6583		return (Browser.Engine.name == 'webkit' && Browser.Engine.version == 419) ? true : false;
6584	});
6585	window.__defineGetter__('webkit420',function(){
6586		MooTools.upgradeLog('1.1 > 1.2: window.webkit is deprecated. Use Browser.Engine.webkit and Browser.Engine.version');
6587		return (Browser.Engine.name == 'webkit' && Browser.Engine.version == 420) ? true : false;
6588	});
6589	window.__defineGetter__('opera',function(){
6590		MooTools.upgradeLog('1.1 > 1.2: window.opera is deprecated. Use Browser.Engine.presto');
6591		return (Browser.Engine.name == 'presto') ? true : false;
6592	});
6593} else {
6594	window[Browser.Engine.name] = window[Browser.Engine.name + Browser.Engine.version] = true;
6595	window.ie = window.trident;
6596	window.ie6 = window.trident4;
6597	window.ie7 = window.trident5;	
6598}
6599Array.implement({
6600
6601	copy: function(start, length){
6602		MooTools.upgradeLog('1.1 > 1.2: Array.copy is deprecated. Use Array.splice');
6603		return $A(this, start, length);
6604	},
6605
6606	remove : function(item){
6607		MooTools.upgradeLog('1.1 > 1.2: Array.remove is deprecated. Use Array.erase');
6608		return this.erase(item);
6609	},
6610
6611	merge : function(array){
6612		MooTools.upgradeLog('1.1 > 1.2: Array.merge is deprecated. Use Array.combine');
6613		return this.combine(array);
6614	}
6615
6616});
6617Function.implement({
6618
6619	bindAsEventListener: function(bind, args){
6620		MooTools.upgradeLog('1.1 > 1.2: Function.bindAsEventListener is deprecated. Use bindWithEvent.');
6621		return this.bindWithEvent.call(this, bind, args);
6622	}
6623
6624});
6625
6626Function.empty = function(){
6627	MooTools.upgradeLog('1.1 > 1.2: Function.empty is now just $empty.');
6628};Hash.implement({
6629
6630	keys : function(){
6631		MooTools.upgradeLog('1.1 > 1.2: Hash.keys is deprecated. Use Hash.getKeys');
6632		return this.getKeys();
6633	},
6634
6635	values : function(){
6636		MooTools.upgradeLog('1.1 > 1.2: Hash.values is deprecated. Use Hash.getValues');
6637		return this.getValues();
6638	},
6639
6640	hasKey : function(item){
6641		MooTools.upgradeLog('1.1 > 1.2: Hash.hasKey is deprecated. Use Hash.has');
6642		return this.has(item);
6643	},
6644
6645	merge : function(properties){
6646		MooTools.upgradeLog('1.1 > 1.2: Hash.merge is deprecated. Use Hash.combine');
6647		return this.extend(properties);
6648	},
6649
6650	remove: function(key){
6651		MooTools.upgradeLog('1.1 > 1.2: Hash.remove is deprecated. use Hash.erase');
6652		return this.erase(key);
6653	}
6654
6655});
6656
6657Object.toQueryString = function(obj){
6658	MooTools.upgradeLog('1.1 > 1.2: Object.toQueryString() is deprecated. use Hash.toQueryString() instead');
6659	$H(obj).each(function(item, key){
6660		if ($type(item) == 'object' || $type(item) == 'array'){
6661			obj[key] = item.toString();
6662		}
6663	});
6664	return Hash.toQueryString(obj);
6665};
6666
6667var Abstract = function(obj){
6668	MooTools.upgradeLog('1.1 > 1.2: Abstract is deprecated. Use Hash');
6669	return new Hash(obj);
6670};Class.empty = function(){ 
6671	MooTools.upgradeLog('1.1 > 1.2: replace Class.empty with $empty');
6672	return $empty;
6673};
6674
6675//legacy .extend support
6676
6677(function(){
6678	var proto = function(obj) {
6679		var f = function(){
6680			return this;
6681		};
6682		f.prototype = obj;
6683		return f;
6684	};
6685
6686	Class.prototype.extend = function(properties){
6687		MooTools.upgradeLog('1.1 > 1.2: Class.extend is deprecated. See the class Extend mutator.');
6688		var maker = proto(properties);
6689		var made = new maker();
6690		made.Extends = this;
6691		return new Class(made);
6692	};
6693
6694	var __implement = Class.prototype.implement;
6695	Class.prototype.implement = function(){
6696		if (arguments.length > 1 && Array.every(arguments, Object.type)){
6697			MooTools.upgradeLog('1.1 > 1.2: Class.implement no longer takes more than one thing at a time, either MyClass.implement(key, value) or MyClass.implement(object) but NOT MyClass.implement(new Foo, new Bar, new Baz). See also: the class Implements mutator.');
6698			Array.each(arguments, function(argument){
6699				__implement.call(this, argument);
6700			}, this);
6701			return this;
6702		}
6703		return __implement.apply(this, arguments);
6704	};
6705})();(function(){
6706
6707	var getPosition = Element.prototype.getPosition;
6708	var getCoordinates = Element.prototype.getCoordinates;
6709
6710	function isBody(element){
6711		return (/^(?:body|html)$/i).test(element.tagName);
6712	};
6713
6714	var getSize = Element.prototype.getSize;
6715
6716	Element.implement({
6717	
6718		getSize: function(){
6719			MooTools.upgradeLog('1.1 > 1.2: NOTE: getSize is different in 1.2; it no longer returns values for size, scroll, and scrollSize, but instead just returns x/y values for the dimensions of the element.');
6720			var size = getSize.apply(this, arguments);
6721			return $merge(size, {
6722				size: size,
6723				scroll: this.getScroll(),
6724				scrollSize: this.getScrollSize()
6725			});
6726		},
6727
6728		getPosition: function(relative){
6729			if (relative && $type(relative) == "array") {
6730				MooTools.upgradeLog('1.1 > 1.2: Element.getPosition no longer accepts an array of overflown elements but rather, optionally, a single element to get relative coordinates.');
6731				relative = null;
6732			}
6733			return getPosition.apply(this, [relative]);
6734		},
6735
6736		getCoordinates: function(relative){
6737			if (relative && $type(relative) == "array") {
6738				MooTools.upgradeLog('1.1 > 1.2: Element.getCoordinates no longer accepts an array of overflown elements but rather, optionally, a single element to get relative coordinates.');
6739				relative = null;
6740			}
6741			return getCoordinates.apply(this, [relative]);
6742		}
6743	
6744	});
6745
6746	Native.implement([Document, Window], {
6747
6748		getSize: function(){
6749			MooTools.upgradeLog('1.1 > 1.2: NOTE: getSize is different in 1.2; it no longer returns values for size, scroll, and scrollSize, but instead just returns x/y values for the dimensions of the element.');
6750			var size;
6751			var win = this.getWindow();
6752			var doc = this.getDocument();
6753			doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
6754			if (Browser.Engine.presto || Browser.Engine.webkit){
6755				size =  {x: win.innerWidth, y: win.innerHeight};
6756			} else {
6757				size = {x: doc.clientWidth, y: doc.clientHeight};
6758			}
6759			return $extend(size, {
6760				size: size,
6761				scroll: {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop},
6762				scrollSize: {x: Math.max(doc.scrollWidth, size.x), y: Math.max(doc.scrollHeight, size.y)}
6763			});
6764		}
6765
6766	});
6767
6768})();Event.keys = Event.Keys; // TODO
6769(function(){
6770
6771	var toQueryString = Element.prototype.toQueryString;
6772
6773	Element.implement({
6774
6775		getFormElements: function(){
6776			MooTools.upgradeLog('1.1 > 1.2: Element.getFormElements is deprecated, use Element.getElements("input, textarea, select");'); 
6777			return this.getElements('input, textarea, select');
6778		},
6779
6780		replaceWith: function(el){
6781			MooTools.upgradeLog('1.1 > 1.2: Element.replaceWith is deprecated, use Element.replaces instead.'); 
6782			el = $(el);
6783			this.parentNode.replaceChild(el, this);
6784			return el;
6785		},
6786
6787		remove: function() {
6788			MooTools.upgradeLog('1.1 > 1.2: Element.remove is deprecated - use Element.dispose.');
6789			return this.dispose.apply(this, arguments);
6790		},
6791
6792		getText: function(){
6793			MooTools.upgradeLog('1.1 > 1.2: Element.getText is deprecated - use Element.get("text").'); 
6794			return this.get('text');
6795		},
6796
6797		setText: function(text){
6798			MooTools.upgradeLog('1.1 > 1.2: Element.setText is deprecated - use Element.set("text", text).'); 
6799			return this.set('text', text);
6800		},
6801
6802		setHTML: function(){
6803			MooTools.upgradeLog('1.1 > 1.2: Element.setHTML is deprecated - use Element.set("html", HTML).'); 
6804			return this.set('html', arguments);
6805		},
6806
6807		getHTML: function(){
6808			MooTools.upgradeLog('1.1 > 1.2: Element.getHTML is deprecated - use Element.get("html").'); 
6809			return this.get('html');
6810		},
6811
6812		getTag: function(){
6813			MooTools.upgradeLog('1.1 > 1.2: Element.getTag is deprecated - use Element.get("tag").'); 
6814			return this.get('tag');
6815		},
6816	
6817		getValue: function(){
6818			MooTools.upgradeLog('1.1 > 1.2: Element.getValue is deprecated - use Element.get("value").');
6819			switch(this.getTag()){
6820				case 'select':
6821					var values = [];
6822					$each(this.options, function(option){
6823						if (option.selected) values.push($pick(option.value, option.text));
6824					});
6825					return (this.multiple) ? values : values[0];
6826				case 'input':
6827                    if (!(this.checked && ['checkbox', 'radio'].contains(this.type)) && !['hidden', 'text', 'password'].contains(this.type)) break;
6828				case 'textarea': return this.value;
6829			}
6830			return false;
6831		},
6832
6833		toQueryString: function(){
6834			MooTools.upgradeLog('1.1 > 1.2: warning Element.toQueryString is slightly different; inputs without names are excluded, inputs with type == submit, reset, and file are excluded, and inputs with undefined values are excluded.');
6835			return toQueryString.apply(this, arguments);
6836		}
6837	});
6838})();
6839
6840Element.Properties.properties = {
6841	
6842	set: function(props){
6843		MooTools.upgradeLog('1.1 > 1.2: Element.set({properties: {}}
6843) is deprecated; instead of properties, just name the values at the root of the object (Element.set({src: url})).');
6844		$H(props).each(function(value, property){
6845			this.set(property, value);
6846		}, this);
6847	}
6848	
6849};
6850Element.implement({
6851
6852	setOpacity: function(op){
6853		MooTools.upgradeLog('1.1 > 1.2: Element.setOpacity is deprecated; use Element.setStyle("opacity", value).');
6854		return this.setStyle('opacity', op);
6855	}
6856
6857});
6858
6859Element.Properties.styles = {
6860	
6861	set: function(styles){
6862		MooTools.upgradeLog('1.1 > 1.2: Element.set("styles") no longer accepts a string as an argument. Pass an object instead.');
6863		if ($type(styles) == 'string'){
6864			styles.split(";").each(function(style){
6865				this.setStyle(style.split(":")[0], style.split(":")[1]);
6866			}, this);
6867		} else {
6868			this.setStyles(styles);
6869		}
6870	}
6871	
6872};Fx.implement({
6873
6874	custom: function(from, to){
6875		MooTools.upgradeLog('1.1 > 1.2: Fx.custom is deprecated. use Fx.start.');
6876		return this.start(from, to);
6877	},
6878
6879	clearTimer: function(){
6880		MooTools.upgradeLog('1.1 > 1.2: Fx.clearTimer is deprecated. use Fx.cancel.');
6881		return this.cancel();
6882	},
6883
6884	stop: function(){
6885		MooTools.upgradeLog('1.1 > 1.2: Fx.stop is deprecated. use Fx.cancel.');
6886		return this.cancel();
6887	}
6888
6889});
6890
6891Fx.Base = new Class({
6892	Extends: Fx,
6893	initialize: function(){
6894		MooTools.upgradeLog('1.1 > 1.2: Fx.Base is deprecated. use Fx.');
6895		this.parent.apply(this, arguments);
6896	}
6897});
6898Fx.Style = new Class({
6899	Extends: Fx.Tween,
6900	initialize: function(element, property, options){
6901		MooTools.upgradeLog('1.1 > 1.2: Fx.Style is deprecated. use Fx.Tween.');
6902		this.property = property;
6903		this.parent(element, options);
6904	},
6905	
6906	start: function(from, to) {
6907		return this.parent(this.property, from, to);
6908	},
6909	
6910	set: function(to) {
6911		return this.parent(this.property, to);
6912	},
6913	
6914	hide: function(){
6915		MooTools.upgradeLog('1.1 > 1.2: Fx.Style .hide() is deprecated; use Fx.Tween .set(0) instead');
6916		return this.set(0);
6917	}
6918
6919});
6920
6921Element.implement({
6922
6923	effect: function(property, options){
6924		MooTools.upgradeLog('1.1 > 1.2: Element.effect is deprecated; use Fx.Tween or Element.tween.');
6925		return new Fx.Style(this, property, options);
6926	}
6927
6928});
6929Fx.Styles = new Class({
6930	Extends: Fx.Morph,
6931	initialize: function(){
6932		MooTools.upgradeLog('1.1 > 1.2: Fx.Styles is deprecated. use Fx.Morph.');
6933		this.parent.apply(this, arguments);
6934	}
6935});
6936
6937Element.implement({
6938
6939	effects: function(options){
6940		MooTools.upgradeLog('1.1 > 1.2: Element.effects is deprecated; use Fx.Morph or Element.morph.');
6941		return new Fx.Morph(this, options);
6942	}
6943
6944});Fx.Scroll.implement({
6945
6946	scrollTo: function(y, x){
6947		MooTools.upgradeLog('1.1 > 1.2: Fx.Scroll\'s .scrollTo is deprecated; use .start.');
6948		return this.start(y, x);
6949	}
6950
6951});Request.implement({
6952	//1.11 passed along the response text and xml to onComplete
6953	onStateChange: function(){
6954		if (this.xhr.readyState != 4 || !this.running) return;
6955		this.running = false;
6956		this.status = 0;
6957		$try(function(){
6958			this.status = this.xhr.status;
6959		}.bind(this));
6960		this.xhr.onreadystatechange = $empty;
6961		this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
6962		if (this.options.isSuccess.call(this, this.status)) this.success(this.response.text, this.response.xml);
6963		else this.failure(this.response.text, this.response.xml);
6964	},
6965	
6966	failure: function(){
6967		this.onFailure.apply(this, arguments);
6968	},
6969
6970	onFailure: function(){
6971		MooTools.upgradeLog('1.1 > 1.2: Note that onComplete does not receive arguments in 1.2. Also note that onComplete is invoked on BOTH success and failure (while in 1.1 it was only invoked on success). Use the onSuccess event instead if you wish to limit this invocation to success.');
6972		this.fireEvent('complete', arguments).fireEvent('failure', this.xhr);
6973	}
6974
6975});
6976
6977var XHR = new Class({
6978
6979	Extends: Request,
6980
6981	options: {
6982		update: false
6983	},
6984
6985	initialize: function(options){
6986		MooTools.upgradeLog('1.1 > 1.2: XHR is deprecated. Use Request.');
6987		this.parent(options);
6988		this.transport = this.xhr;
6989	},
6990
6991	request: function(data){
6992		MooTools.upgradeLog('1.1 > 1.2: XHR.request() is deprecated. Use Request.send() instead.');
6993		return this.send(this.url, data || this.options.data);
6994	},
6995
6996	send: function(url, data){
6997		if (!this.check(arguments.callee, url, data)) return this;
6998		return this.parent({url: url, data: data});
6999	},
7000
7001	success: function(text, xml){
7002		text = this.processScripts(text);
7003		if (this.options.update) $(this.options.update).empty().set('html', text);
7004		this.onSuccess(text, xml);
7005	},
7006
7007	failure: function(){
7008		this.fireEvent('failure', this.xhr);
7009	}
7010
7011});
7012
7013
7014var Ajax = new Class({
7015
7016	Extends: XHR,
7017
7018	initialize: function(url, options){
7019		MooTools.upgradeLog('1.1 > 1.2: Ajax is deprecated. Use Request.');
7020		this.url = url;
7021		this.parent(options);
7022	},
7023
7024	success: function(text, xml){
7025		// This version processes scripts *after* the update element is updated, like Mootools 1.1's Ajax class
7026		// Partially from Remote.Ajax.success
7027		this.processScripts(text);
7028		response = this.response;
7029		response.html = text.stripScripts(function(script){
7030				response.javascript = script;
7031		});
7032		if (this.options.update) $(this.options.update).empty().set('html', response.html);
7033		if (this.options.evalScripts) $exec(response.javascript);
7034		this.onSuccess(text, xml);
7035	}
7036
7037});
7038
7039(function(){
7040	var send = Element.prototype.send;
7041	Element.implement({
7042		send: function(url) {
7043			if ($type(url) == "string") return send.apply(this, arguments);
7044			if ($type(url) == "object") {
7045				MooTools.upgradeLog('1.1 > 1.2: Element.send no longer takes an options argument as its object but rather a url. See docs.');
7046				this.set('send', url);
7047				send.call(this);
7048			}
7049			return this;
7050		}
7051	});
7052})();JSON.Remote = new Class({
7053
7054	options: {
7055		key: 'json'
7056	},
7057
7058	Extends: Request.JSON,
7059
7060	initialize: function(url, options){
7061		MooTools.upgradeLog('JSON.Remote is deprecated. Use Request.JSON');
7062		this.parent(options);
7063		this.onComplete = $empty;
7064		this.url = url;
7065	},
7066
7067	send: function(data){
7068		if (!this.check(arguments.callee, data)) return this;
7069		return this.parent({url: this.url, data: {json: Json.encode(data)}});
7070	},
7071
7072	failure: function(){
7073		this.fireEvent('failure', this.xhr);
7074	}
7075
7076});
7077
7078Cookie.set = function(key, value, options){
7079	MooTools.upgradeLog('1.1 > 1.2: Cookie.set is deprecated. Use Cookie.write');
7080	return new Cookie(key, options).write(value);
7081};
7082
7083Cookie.get = function(key){
7084	MooTools.upgradeLog('1.1 > 1.2: Cookie.get is deprecated. Use Cookie.read');
7085	return new Cookie(key).read();
7086};
7087
7088Cookie.remove = function(key, options){
7089	MooTools.upgradeLog('1.1 > 1.2: Cookie.remove is deprecated. Use Cookie.dispose');
7090	return new Cookie(key, options).dispose();
7091};
7092JSON.toString = function(obj){ 
7093	MooTools.upgradeLog('1.1 > 1.2: JSON.toString is deprecated. Use JSON.encode');
7094	return JSON.encode(obj); 
7095};
7096JSON.evaluate = function(str){
7097	MooTools.upgradeLog('1.1 > 1.2: JSON.evaluate is deprecated. Use JSON.decode');
7098	return JSON.decode(str); 
7099};
7100var Json = JSON;
7101
7102Native.implement([Element, Document], {
7103
7104	getElementsByClassName: function(className){
7105		MooTools.upgradeLog('1.1 > 1.2: Element.filterByTag is deprecated.');
7106		
7107		return this.getElements('.' + className);
7108	},
7109
7110	getElementsBySelector: function(selector){
7111		MooTools.upgradeLog('1.1 > 1.2: Element.getElementsBySelector is deprecated. Use getElements()');
7112		return this.getElements(selector);
7113	}
7114
7115});
7116
7117Elements.implement({
7118
7119	filterByTag: function(tag){
7120		MooTools.upgradeLog('1.1 > 1.2: Elements.filterByTag is deprecated. Use Elements.filter.');
7121		return this.filter(tag);
7122	},
7123
7124	filterByClass: function(className){
7125		MooTools.upgradeLog('1.1 > 1.2: Elements.filterByClass is deprecated. Use Elements.filter.');
7126		return this.filter('.' + className);
7127	},
7128
7129	filterById: function(id){
7130		MooTools.upgradeLog('1.1 > 1.2: Elements.filterById is deprecated. Use Elements.filter.');
7131		return this.filter('#' + id);
7132	},
7133
7134	filterByAttribute: function(name, operator, value){
7135		MooTools.upgradeLog('1.1 > 1.2: Elements.filterByAttribute is deprecated. Use Elements.filter.');
7136		var filtered = this.filter('[' + name + (operator || '') + (value || '') + ']');
7137		if (value) filtered = filtered.filter('[' + name + ']');
7138		return filtered;
7139	}
7140
7141});
7142
7143var $E = function(selector, filter){
7144	MooTools.upgradeLog('1.1 > 1.2: $E is deprecated, use document.getElement.');
7145	return ($(filter) || document).getElement(selector);
7146};
7147
7148var $ES = function(selector, filter){
7149	MooTools.upgradeLog('1.1 > 1.2: $ES is deprecated. Use $$.');
7150	return ($(filter) || document).getElements(selector);
7151};(function(){
7152	if (!window.Tips) return;
7153
7154	Tips.implement({
7155
7156		initialize: function(){
7157			MooTools.upgradeLog('1.1 > 1.2: Tips DOM element layout has changed and your CSS classes may need to change.');
7158			var params = Array.link(arguments, {options: Object.type, elements: $defined});
7159			this.setOptions(params.options);
7160			if (this.options.offsets) {
7161				MooTools.upgradeLog('1.1 > 1.2: Tips no longer have an "offsets" option; use "offset".');
7162				this.options.offset = this.options.offsets;
7163			}
7164			document.id(this);
7165			this.addEvent('show', function(){
7166				this.tip.addClass('tool-tip');
7167				this.tip.getElement('.tip-title').addClass('tool-title');
7168				this.tip.getElement('.tip-text').addClass('tool-text');
7169			});
7170			this.parseTitle(params.elements);
7171			if (params.elements) this.attach(params.elements);
7172		},
7173
7174		parseTitle: function(elements){
7175			elements.each(function(element){
7176			var title = element.get('title');
7177				if (title.test('::')) {
7178					MooTools.upgradeLog('1.1 > 1.2: Tips no longer parse the title attribute for "::" for title/caption; use title and rel attributes instead.');
7179					element.store('tip:title', title.split('::')[0]);
7180					element.store('tip:text', title.split('::')[1]);
7181					element.set('title', '');
7182				}
7183			});
7184		}
7185
7186	});
7187
7188})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.