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