/*
Script: Core.js
	MooTools - My Object Oriented JavaScript Tools.

License:
	MIT-style license.

Copyright:
	Copyright (c) 2006-2007 [Valerio Proietti](http://mad4milk.net/).

Code & Documentation:
	[The MooTools production team](http://mootools.net/developers/).

Inspiration:
	- 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)
	- Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
*/

var MooTools = {
	'version': '1.2.1dev',
	'build': '%build%'
};

var Native = function(options){
	options = options || {};
	var name = options.name;
	var legacy = options.legacy;
	var protect = options.protect;
	var methods = options.implement;
	var generics = options.generics;
	var initialize = options.initialize;
	var afterImplement = options.afterImplement || function(){};
	var object = initialize || legacy;
	generics = generics !== false;

	object.constructor = Native;
	object.$family = {name: 'native'};
	if (legacy && initialize) object.prototype = legacy.prototype;
	object.prototype.constructor = object;

	if (name){
		var family = name.toLowerCase();
		object.prototype.$family = {name: family};
		Native.typize(object, family);
	}

	var add = function(obj, name, method, force){
		if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
		if (generics) Native.genericize(obj, name, protect);
		afterImplement.call(obj, name, method);
		return obj;
	};

	object.alias = function(a1, a2, a3){
		if (typeof a1 == 'string'){
			if ((a1 = this.prototype[a1])) return add(this, a2, a1, a3);
		}
		for (var a in a1) this.alias(a, a1[a], a2);
		return this;
	};

	object.implement = function(a1, a2, a3){
		if (typeof a1 == 'string') return add(this, a1, a2, a3);
		for (var p in a1) add(this, p, a1[p], a2);
		return this;
	};

	if (methods) object.implement(methods);

	return object;
};

Native.genericize = function(object, property, check){
	if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){
		var args = Array.prototype.slice.call(arguments);
		return object.prototype[property].apply(args.shift(), args);
	};
};

Native.implement = function(objects, properties){
	for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
};

Native.typize = function(object, family){
	if (!object.type) object.type = function(item){
		return ($type(item) === family);
	};
};

(function(){
	var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String};
	for (var n in natives) new Native({name: n, initialize: natives[n], protect: true});

	var types = {'boolean': Boolean, 'native': Native, 'object': Object};
	for (var t in types) Native.typize(types[t], t);

	var generics = {
		'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"],
		'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
	};
	for (var g in generics){
		for (var i = generics[g].length; i--;) Native.genericize(window[g], generics[g][i], true);
	};
})();

var Hash = new Native({

	name: 'Hash',

	initialize: function(object){
		if ($type(object) == 'hash') object = $unlink(object.getClean());
		for (var key in object) this[key] = object[key];
		return this;
	}

});

Hash.implement({

	forEach: function(fn, bind){
		for (var key in this){
			if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
		}
	},

	getClean: function(){
		var clean = {};
		for (var key in this){
			if (this.hasOwnProperty(key)) clean[key] = this[key];
		}
		return clean;
	},

	getLength: function(){
		var length = 0;
		for (var key in this){
			if (this.hasOwnProperty(key)) length++;
		}
		return length;
	}

});

Hash.alias('forEach', 'each');

Array.implement({

	forEach: function(fn, bind){
		for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
	}

});

Array.alias('forEach', 'each');

function $A(iterable){
	if (iterable.item){
		var array = [];
		for (var i = 0, l = iterable.length; i < l; i++) array[i] = iterable[i];
		return array;
	}
	return Array.prototype.slice.call(iterable);
};

function $arguments(i){
	return function(){
		return arguments[i];
	};
};

function $chk(obj){
	return !!(obj || obj === 0);
};

function $clear(timer){
	clearTimeout(timer);
	clearInterval(timer);
	return null;
};

function $defined(obj){
	return (obj != undefined);
};

function $each(iterable, fn, bind){
	var type = $type(iterable);
	((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
};

function $empty(){};

function $extend(original, extended){
	for (var key in (extended || {})) original[key] = extended[key];
	return original;
};

function $H(object){
	return new Hash(object);
};

function $lambda(value){
	return (typeof value == 'function') ? value : function(){
		return value;
	};
};

function $merge(){
	var mix = {};
	for (var i = 0, l = arguments.length; i < l; i++){
		var object = arguments[i];
		if ($type(object) != 'object') continue;
		for (var key in object){
			var op = object[key], mp = mix[key];
			mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $merge(mp, op) : $unlink(op);
		}
	}
	return mix;
};

function $pick(){
	for (var i = 0, l = arguments.length; i < l; i++){
		if (arguments[i] != undefined) return arguments[i];
	}
	return null;
};

function $random(min, max){
	return Math.floor(Math.random() * (max - min + 1) + min);
};

function $splat(obj){
	var type = $type(obj);
	return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
};

var $time = Date.now || function(){
	return +new Date;
};

function $try(){
	for (var i = 0, l = arguments.length; i < l; i++){
		try {
			return arguments[i]();
		} catch(e){}
	}
	return null;
};

function $type(obj){
	if (obj == undefined) return false;
	if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
	if (obj.nodeName){
		switch (obj.nodeType){
			case 1: return 'element';
			case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
		}
	} else if (typeof obj.length == 'number'){
		if (obj.callee) return 'arguments';
		else if (obj.item) return 'collection';
	}
	return typeof obj;
};

function $unlink(object){
	var unlinked;
	switch ($type(object)){
		case 'object':
			unlinked = {};
			for (var p in object) unlinked[p] = $unlink(object[p]);
		break;
		case 'hash':
			unlinked = new Hash(object);
		break;
		case 'array':
			unlinked = [];
			for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
		break;
		default: return object;
	}
	return unlinked;
};/*
Script: Browser.js
	The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.

License:
	MIT-style license.
*/

var Browser = new Hash({
	Engine: {name: 'unknown', version: ''},
	Platform: {name: ($defined(window.orientation)) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},
	Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},
	Plugins: {}
});

if (window.opera) Browser.Engine = {name: 'presto', version: (document.getElementsByClassName) ? 950 : 925};
else if (window.ActiveXObject) Browser.Engine = {name: 'trident', version: (window.XMLHttpRequest) ? 5 : 4};
else if (!navigator.taintEnabled) Browser.Engine = {name: 'webkit', version: (Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419};
else if (document.getBoxObjectFor != null) Browser.Engine = {name: 'gecko', version: (document.getElementsByClassName) ? 19 : 18};
Browser.Engine[Browser.Engine.name] = Browser.Engine[Browser.Engine.name + Browser.Engine.version] = true;

Browser.Platform[Browser.Platform.name] = true;

Browser.Request = function(){
	return $try(function(){
		return new XMLHttpRequest();
	}, function(){
		return new ActiveXObject('MSXML2.XMLHTTP');
	});
};

Browser.Features.xhr = !!(Browser.Request());

Browser.Plugins.Flash = (function(){
	var version = ($try(function(){
		return navigator.plugins['Shockwave Flash'].description;
	}, function(){
		return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
	}) || '0 r0').match(/\d+/g);
	return {version: parseInt(version[0] || 0 + '.' + version[1] || 0), build: parseInt(version[2] || 0)};
})();

function $exec(text){
	if (!text) return text;
	if (window.execScript){
		window.execScript(text);
	} else {
		var script = document.createElement('script');
		script.setAttribute('type', 'text/javascript');
		script.text = text;
		document.head.appendChild(script);
		document.head.removeChild(script);
	}
	return text;
};

Native.UID = 1;

var $uid = (Browser.Engine.trident) ? function(item){
	return (item.uid || (item.uid = [Native.UID++]))[0];
} : function(item){
	return item.uid || (item.uid = Native.UID++);
};

var Window = new Native({

	name: 'Window',

	legacy: (Browser.Engine.trident) ? null: window.Window,

	initialize: function(win){
		$uid(win);
		if (!win.Element){
			win.Element = $empty;
			if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2
			win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {};
		}
		win.document.window = win;
		return $extend(win, Window.Prototype);
	},

	afterImplement: function(property, value){
		window[property] = Window.Prototype[property] = value;
	}

});

Window.Prototype = {$family: {name: 'window'}};

new Window(window);

var Document = new Native({

	name: 'Document',

	legacy: (Browser.Engine.trident) ? null: window.Document,

	initialize: function(doc){
		$uid(doc);
		doc.head = doc.getElementsByTagName('head')[0];
		doc.html = doc.getElementsByTagName('html')[0];
		if (Browser.Engine.trident4) $try(function(){
			doc.execCommand("BackgroundImageCache", false, true);
		});
		if (Browser.Engine.trident) doc.parentWindow.attachEvent('onunload', function() {
			doc.parentWindow.detachEvent('onunload', arguments.callee);
			doc.head = doc.html = doc.window = null;
		});
		return $extend(doc, Document.Prototype);
	},

	afterImplement: function(property, value){
		document[property] = Document.Prototype[property] = value;
	}

});

Document.Prototype = {$family: {name: 'document'}};

new Document(document);
/*
Script: Array.js
	Contains Array Prototypes like each, contains, and erase.

License:
	MIT-style license.
*/

Array.implement({

	every: function(fn, bind){
		for (var i = 0, l = this.length; i < l; i++){
			if (!fn.call(bind, this[i], i, this)) return false;
		}
		return true;
	},

	filter: function(fn, bind){
		var results = [];
		for (var i = 0, l = this.length; i < l; i++){
			if (fn.call(bind, this[i], i, this)) results.push(this[i]);
		}
		return results;
	},

	clean: function() {
		return this.filter($defined);
	},

	indexOf: function(item, from){
		var len = this.length;
		for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
			if (this[i] === item) return i;
		}
		return -1;
	},

	map: function(fn, bind){
		var results = [];
		for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this);
		return results;
	},

	some: function(fn, bind){
		for (var i = 0, l = this.length; i < l; i++){
			if (fn.call(bind, this[i], i, this)) return true;
		}
		return false;
	},

	associate: function(keys){
		var obj = {}, length = Math.min(this.length, keys.length);
		for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
		return obj;
	},

	link: function(object){
		var result = {};
		for (var i = 0, l = this.length; i < l; i++){
			for (var key in object){
				if (object[key](this[i])){
					result[key] = this[i];
					delete object[key];
					break;
				}
			}
		}
		return result;
	},

	contains: function(item, from){
		return this.indexOf(item, from) != -1;
	},

	extend: function(array){
		for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
		return this;
	},

	getLast: function(){
		return (this.length) ? this[this.length - 1] : null;
	},

	getRandom: function(){
		return (this.length) ? this[$random(0, this.length - 1)] : null;
	},

	include: function(item){
		if (!this.contains(item)) this.push(item);
		return this;
	},

	combine: function(array){
		for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
		return this;
	},

	erase: function(item){
		for (var i = this.length; i--; i){
			if (this[i] === item) this.splice(i, 1);
		}
		return this;
	},

	empty: function(){
		this.length = 0;
		return this;
	},

	flatten: function(){
		var array = [];
		for (var i = 0, l = this.length; i < l; i++){
			var type = $type(this[i]);
			if (!type) continue;
			array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]);
		}
		return array;
	},

	hexToRgb: function(array){
		if (this.length != 3) return null;
		var rgb = this.map(function(value){
			if (value.length == 1) value += value;
			return value.toInt(16);
		});
		return (array) ? rgb : 'rgb(' + rgb + ')';
	},

	rgbToHex: function(array){
		if (this.length < 3) return null;
		if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
		var hex = [];
		for (var i = 0; i < 3; i++){
			var bit = (this[i] - 0).toString(16);
			hex.push((bit.length == 1) ? '0' + bit : bit);
		}
		return (array) ? hex : '#' + hex.join('');
	}

});/*
Script: Function.js
	Contains Function Prototypes like create, bind, pass, and delay.

License:
	MIT-style license.
*/

Function.implement({

	extend: function(properties){
		for (var property in properties) this[property] = properties[property];
		return this;
	},

	create: function(options){
		var self = this;
		options = options || {};
		return function(event){
			var args = options.arguments;
			args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
			if (options.event) args = [event || window.event].extend(args);
			var returns = function(){
				return self.apply(options.bind || null, args);
			};
			if (options.delay) return setTimeout(returns, options.delay);
			if (options.periodical) return setInterval(returns, options.periodical);
			if (options.attempt) return $try(returns);
			return returns();
		};
	},

	run: function(args, bind){
		return this.apply(bind, $splat(args));
	},

	pass: function(args, bind){
		return this.create({bind: bind, arguments: args});
	},
	
	// better for debugging
	bind: function(bind, args) {
		var self = this;
		return function() {
			return self.apply(bind, args != undefined ? $splat(args) : arguments);
		};
	},

	/*bind: function(bind, args){
		return this.create({bind: bind, arguments: args});
	},*/

	bindWithEvent: function(bind, args){
		return this.create({bind: bind, arguments: args, event: true});
	},

	attempt: function(args, bind){
		return this.create({bind: bind, arguments: args, attempt: true})();
	},

	delay: function(delay, bind, args){
		return this.create({bind: bind, arguments: args, delay: delay})();
	},

	periodical: function(periodical, bind, args){
		return this.create({bind: bind, arguments: args, periodical: periodical})();
	}

});
/*
Script: Number.js
	Contains Number Prototypes like limit, round, times, and ceil.

License:
	MIT-style license.
*/

Number.implement({

	limit: function(min, max){
		return Math.min(max, Math.max(min, this));
	},

	round: function(precision){
		precision = Math.pow(10, precision || 0);
		return Math.round(this * precision) / precision;
	},

	times: function(fn, bind){
		for (var i = 0; i < this; i++) fn.call(bind, i, this);
	},

	toFloat: function(){
		return parseFloat(this);
	},

	toInt: function(base){
		return parseInt(this, base || 10);
	}

});

Number.alias('times', 'each');

(function(math){
	var methods = {};
	math.each(function(name){
		if (!Number[name]) methods[name] = function(){
			return Math[name].apply(null, [this].concat($A(arguments)));
		};
	});
	Number.implement(methods);
})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);/*
Script: String.js
	Contains String Prototypes like camelCase, capitalize, test, and toInt.

License:
	MIT-style license.
*/

String.implement({

	test: function(regex, params){
		return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
	},

	contains: function(string, separator){
		return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
	},

	trim: function(){
		return this.replace(/^\s+|\s+$/g, '');
	},

	clean: function(){
		return this.replace(/\s+/g, ' ').trim();
	},

	camelCase: function(){
		return this.replace(/-\D/g, function(match){
			return match.charAt(1).toUpperCase();
		});
	},

	hyphenate: function(){
		return this.replace(/[A-Z]/g, function(match){
			return ('-' + match.charAt(0).toLowerCase());
		});
	},

	capitalize: function(){
		return this.replace(/\b[a-z]/g, function(match){
			return match.toUpperCase();
		});
	},

	escapeRegExp: function(){
		return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
	},

	toInt: function(base){
		return parseInt(this, base || 10);
	},

	toFloat: function(){
		return parseFloat(this);
	},

	hexToRgb: function(array){
		var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
		return (hex) ? hex.slice(1).hexToRgb(array) : null;
	},

	rgbToHex: function(array){
		var rgb = this.match(/\d{1,3}/g);
		return (rgb) ? rgb.rgbToHex(array) : null;
	},

	stripScripts: function(option){
		var scripts = '';
		var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
			scripts += arguments[1] + '\n';
			return '';
		});
		if (option === true) $exec(scripts);
		else if ($type(option) == 'function') option(scripts, text);
		return text;
	},

	substitute: function(object, regexp){
		return this.replace(regexp || (/\\?\{([^}]+)\}/g), function(match, name){
			if (match.charAt(0) == '\\') return match.slice(1);
			return (object[name] != undefined) ? object[name] : '';
		});
	}

});/*
Script: Hash.js
	Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.

License:
	MIT-style license.
*/

Hash.implement({

	has: Object.prototype.hasOwnProperty,

	keyOf: function(value){
		for (var key in this){
			if (this.hasOwnProperty(key) && this[key] === value) return key;
		}
		return null;
	},

	hasValue: function(value){
		return (Hash.keyOf(this, value) !== null);
	},

	extend: function(properties){
		Hash.each(properties, function(value, key){
			Hash.set(this, key, value);
		}, this);
		return this;
	},

	combine: function(properties){
		Hash.each(properties, function(value, key){
			Hash.include(this, key, value);
		}, this);
		return this;
	},

	erase: function(key){
		if (this.hasOwnProperty(key)) delete this[key];
		return this;
	},

	get: function(key){
		return (this.hasOwnProperty(key)) ? this[key] : null;
	},

	set: function(key, value){
		if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
		return this;
	},

	empty: function(){
		Hash.each(this, function(value, key){
			delete this[key];
		}, this);
		return this;
	},

	include: function(key, value){
		var k = this[key];
		if (k == undefined) this[key] = value;
		return this;
	},

	map: function(fn, bind){
		var results = new Hash;
		Hash.each(this, function(value, key){
			results.set(key, fn.call(bind, value, key, this));
		}, this);
		return results;
	},

	filter: function(fn, bind){
		var results = new Hash;
		Hash.each(this, function(value, key){
			if (fn.call(bind, value, key, this)) results.set(key, value);
		}, this);
		return results;
	},

	every: function(fn, bind){
		for (var key in this){
			if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
		}
		return true;
	},

	some: function(fn, bind){
		for (var key in this){
			if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
		}
		return false;
	},

	getKeys: function(){
		var keys = [];
		Hash.each(this, function(value, key){
			keys.push(key);
		});
		return keys;
	},

	getValues: function(){
		var values = [];
		Hash.each(this, function(value){
			values.push(value);
		});
		return values;
	},

	toQueryString: function(base){
		var queryString = [];
		Hash.each(this, function(value, key){
			if (base) key = base + '[' + key + ']';
			var result;
			switch ($type(value)){
				case 'object': result = Hash.toQueryString(value, key); break;
				case 'array':
					var qs = {};
					value.each(function(val, i){
						qs[i] = val;
					});
					result = Hash.toQueryString(qs, key);
				break;
				default: result = key + '=' + encodeURIComponent(value);
			}
			if (value != undefined) queryString.push(result);
		});

		return queryString.join('&');
	}

});

Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});/*
Script: Event.js
	Contains the Event Native, to make the event object completely crossbrowser.

License:
	MIT-style license.
*/

var Event = new Native({

	name: 'Event',

	initialize: function(event, win){
		win = win || window;
		var doc = win.document;
		event = event || win.event;
		if (event.$extended) return event;
		this.$extended = true;
		var type = event.type;
		var target = event.target || event.srcElement;
		while (target && target.nodeType == 3) target = target.parentNode;

		if (type.test(/key/)){
			var code = event.which || event.keyCode;
			var key = Event.Keys.keyOf(code);
			if (type == 'keydown'){
				var fKey = code - 111;
				if (fKey > 0 && fKey < 13) key = 'f' + fKey;
			}
			key = key || String.fromCharCode(code).toLowerCase();
		} else if (type.match(/(click|mouse|menu)/i)){
			var standardMode = !doc.compatMode || doc.compatMode == 'CSS1Compat';
			doc = standardMode ? doc.html : doc.body;
			var page = {
				x: event.pageX || event.clientX + doc.scrollLeft,
				y: event.pageY || event.clientY + doc.scrollTop
			};
			var client = {
				x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
				y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
			};
			// fix ie 2px bug			
			if (Browser.Engine.trident && standardMode) {
			    var cl = doc.clientLeft, ct = doc.clientTop;
			    page.x -= cl; 
			    page.y -= ct;
			    client.x -= cl; 
			    client.y -= ct;
			}
			if (type.match(/DOMMouseScroll|mousewheel/)){
				var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
			}
			var rightClick = (event.which == 3) || (event.button == 2);
			var related = null;
			if (type.match(/over|out/)){
				switch (type){
					case 'mouseover': related = event.relatedTarget || event.fromElement; break;
					case 'mouseout': related = event.relatedTarget || event.toElement;
				}
				if (!(function(){
					while (related && related.nodeType == 3) related = related.parentNode;
					return true;
				}).create({attempt: Browser.Engine.gecko})()) related = false;
			}
		}

		return $extend(this, {
			event: event,
			type: type,

			page: page,
			client: client,
			rightClick: rightClick,

			wheel: wheel,

			relatedTarget: related,
			target: target,

			code: code,
			key: key,

			shift: event.shiftKey,
			control: event.ctrlKey,
			alt: event.altKey,
			meta: event.metaKey
		});
	}

});

Event.Keys = new Hash({
	'enter': 13,
	'up': 38,
	'down': 40,
	'left': 37,
	'right': 39,
	'esc': 27,
	'space': 32,
	'backspace': 8,
	'tab': 9,
	'delete': 46
});

Event.implement({

	stop: function(){
		return this.stopPropagation().preventDefault();
	},

	stopPropagation: function(){
		if (this.event.stopPropagation) this.event.stopPropagation();
		else this.event.cancelBubble = true;
		return this;
	},

	preventDefault: function(){
		if (this.event.preventDefault) this.event.preventDefault();
		else this.event.returnValue = false;
		return this;
	}

});/*
Script: Class.js
	Contains the Class Function for easily creating, extending, and implementing reusable Classes.

License:
	MIT-style license.
*/

var Class = new Native({

	name: 'Class',

	initialize: function(properties){
		properties = properties || {};
		var klass = function(){
			for (var key in this){
				if ($type(this[key]) != 'function') this[key] = $unlink(this[key]);
			}
			this.constructor = klass;
			if (Class.prototyping) return this;
			var instance = (this.initialize) ? this.initialize.apply(this, arguments) : this;
			if (this.options && this.options.initialize) this.options.initialize.call(this);
			return instance;
		};

		for (var mutator in Class.Mutators){
			if (!properties[mutator]) continue;
			properties = Class.Mutators[mutator](properties, properties[mutator]);
			delete properties[mutator];
		}

		$extend(klass, this);
		klass.constructor = Class;
		klass.prototype = properties;
		return klass;
	}

});

Class.Mutators = {

	Extends: function(self, klass){
		Class.prototyping = klass.prototype;
		var subclass = new klass;
		delete subclass.parent;
		subclass = Class.inherit(subclass, self);
		delete Class.prototyping;
		return subclass;
	},

	Implements: function(self, klasses){
		$splat(klasses).each(function(klass){
			Class.prototyping = klass; //here was: prototying
			$extend(self, ($type(klass) == 'class') ? new klass : klass);
			delete Class.prototyping;
		});
		return self;
	}

};

Class.extend({

	inherit: function(object, properties){
		var caller = arguments.callee.caller;
		for (var key in properties){
			var override = properties[key];
			var previous = object[key];
			var type = $type(override);
			if (previous && type == 'function'){
				if (override != previous){
					if (caller){
						override.__parent = previous;
						object[key] = override;
					} else {
						Class.override(object, key, override);
					}
				}
			} else if(type == 'object'){
				object[key] = $merge(previous, override);
			} else {
				object[key] = override;
			}
		}

		if (caller) object.parent = function(){
			return arguments.callee.caller.__parent.apply(this, arguments);
		};

		return object;
	},

	override: function(object, name, method){
		var parent = Class.prototyping;
		if (parent && object[name] != parent[name]) parent = null;
		var override = function(){
			var previous = this.parent;
			this.parent = parent ? parent[name] : object[name];
			var value = method.apply(this, arguments);
			this.parent = previous;
			return value;
		};
		object[name] = override;
	}

});

Class.implement({

	implement: function(){
		var proto = this.prototype;
		$each(arguments, function(properties){
			Class.inherit(proto, properties);
		});
		return this;
	}

});/*
Script: Class.Extras.js
	Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.

License:
	MIT-style license.
*/

var Chain = new Class({

	$chain: [],

	chain: function(){
		this.$chain.extend(Array.flatten(arguments));
		return this;
	},

	callChain: function(){
		return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
	},

	clearChain: function(){
		this.$chain.empty();
		return this;
	}

});

var Events = new Class({

	addEvent: function(type, fn, internal){
		type = Events.removeOn(type);
		if (fn != $empty){
			this.$events = this.$events || {};
			this.$events[type] = this.$events[type] || [];
			this.$events[type].include(fn);
			if (internal) fn.internal = true;
		}
		return this;
	},
	
	// autoremoving-onceFiring-flash events
	addEventOnce: function(type, fn) {
		this.addEvent(type, function() {
			this.removeEvent(type, arguments.callee);
			fn.run(arguments);			
		});
	},
	
	addEventsOnce: function(events){
		for (var type in events) this.addEventOnce(type, events[type]);
		return this;
	},	

	addEvents: function(events){
		for (var type in events) this.addEvent(type, events[type]);
		return this;
	},

	fireEvent: function(type, args, delay){
		type = Events.removeOn(type);
		if (!this.$events || !this.$events[type]) return this;
		$A(this.$events[type]).each(function(fn){
			fn.create({'bind': this, 'delay': delay, 'arguments': args})();
		}, this);
		return this;
	},

	removeEvent: function(type, fn){
		type = Events.removeOn(type);
		if (!this.$events || !this.$events[type]) return this;
		if (!fn.internal) this.$events[type].erase(fn);
		return this;
	},

	removeEvents: function(what){
		if ($type(what) == 'object'){
			for (var type in what) this.removeEvent(type, what[type]);
			return this;
		}
		if (what) what = Events.removeOn(what);
		for (var type in this.$events){
			if (what && what != type) continue;
			var fns = this.$events[type];
			for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]);
		}
		return this;
	}
	
});

Events.removeOn = function(string){
	return string.replace(/^on([A-Z])/, function(full, first) {
		return first.toLowerCase();
	});
};

var Options = new Class({

	setOptions: function(){
		this.options = $merge.run([this.options].extend(arguments));
		if (!this.addEvent) return this;
		for (var option in this.options){
			if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
			this.addEvent(option, this.options[option]);
			delete this.options[option];
		}
		return this;
	}

});/*
Script: Element.js
	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.

License:
	MIT-style license.
*/

var Element = new Native({

	name: 'Element',

	legacy: window.Element,

	initialize: function(tag, props){
		var konstructor = Element.Constructors.get(tag);
		if (konstructor) return konstructor(props);
		if (typeof tag == 'string') return document.newElement(tag, props);
		return $(tag).set(props);
	},

	afterImplement: function(key, value){
		Element.Prototype[key] = value;
		if (Array[key]) return;
		Elements.implement(key, function(){
			var items = [], elements = true;
			for (var i = 0, j = this.length; i < j; i++){
				var returns = this[i][key].apply(this[i], arguments);
				items.push(returns);
				if (elements) elements = ($type(returns) == 'element');
			}
			return (elements) ? new Elements(items) : items;
		});
	}

});

Element.Prototype = {$family: {name: 'element'}};

Element.Constructors = new Hash;

var IFrame = new Native({

	name: 'IFrame',

	generics: false,

	initialize: function(){
		var params = Array.link(arguments, {properties: Object.type, iframe: $defined});
		var props = params.properties || {};
		var iframe = $(params.iframe) || false;
		var onload = props.onload || $empty;
		delete props.onload;
		props.id = props.name = $pick(props.id, props.name, iframe.id, iframe.name, 'IFrame_' + $time());
		iframe = new Element(iframe || 'iframe', props);
		var onFrameLoad = function(){
			var host = $try(function(){
				return iframe.contentWindow.location.host;
			});
			if (host && host == window.location.host){
				var win = new Window(iframe.contentWindow);
				new Document(iframe.contentWindow.document);
				$extend(win.Element.prototype, Element.Prototype);
			}
			onload.call(iframe.contentWindow, iframe.contentWindow.document);
		};
		(window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad);
		return iframe;
	}

});

var Elements = new Native({

	initialize: function(elements, options){
		options = $extend({ddup: true, cash: true}, options);
		elements = elements || [];
		if (options.ddup || options.cash){
			var uniques = {}, returned = [];
			for (var i = 0, l = elements.length; i < l; i++){
				var el = $.element(elements[i], !options.cash);
				if (options.ddup){
					if (uniques[el.uid]) continue;
					uniques[el.uid] = true;
				}
				returned.push(el);
			}
			elements = returned;
		}
		return (options.cash) ? $extend(elements, this) : elements;
	}

});

Elements.implement({

	filter: function(filter, bind){
		if (!filter) return this;
		return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){
			return item.match(filter);
		} : filter, bind));
	}

});

Document.implement({

	newElement: function(tag, props){
		if (Browser.Engine.trident && props){
			['name', 'type', 'checked'].each(function(attribute){
				if (!props[attribute]) return;
				tag += ' ' + attribute + '="' + props[attribute] + '"';
				if (attribute != 'checked') delete props[attribute];
			});
			tag = '<' + tag + '>';
		}
		return $.element(this.createElement(tag)).set(props);
	},

	newTextNode: function(text){
		return this.createTextNode(text);
	},

	getDocument: function(){
		return this;
	},

	getWindow: function(){
		return this.window;
	},
	
	boxMode: function() {
		return !Browser.Engine.trident || this.compatMode == "CSS1Compat";
	}

});

Window.implement({

	$: function(el, nocash){
		if (el && el.$family && el.uid) return el;
		var type = $type(el);
		return ($[type]) ? $[type](el, nocash, this.document) : null;
	},

	$$: function(selector){
		if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector);
		var elements = [];
		var args = Array.flatten(arguments);
		for (var i = 0, l = args.length; i < l; i++){
			var item = args[i];
			switch ($type(item)){
				case 'element': elements.push(item); break;
				case 'string': elements.extend(this.document.getElements(item, true));
			}
		}
		return new Elements(elements);
	},

	getDocument: function(){
		return this.document;
	},

	getWindow: function(){
		return this;
	}

});

$.string = function(id, nocash, doc){
	id = doc.getElementById(id);
	return (id) ? $.element(id, nocash) : null;
};

$.element = function(el, nocash){
	$uid(el);
	if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
		var proto = Element.Prototype;
		for (var p in proto) el[p] = proto[p];
	};
	return el;
};

$.object = function(obj, nocash, doc){
	if (obj.toElement) return $.element(obj.toElement(doc), nocash);
	return null;
};

$.textnode = $.whitespace = $.window = $.document = $arguments(0);

Native.implement([Element, Document], {

	getElement: function(selector, nocash){
		return $(this.getElements(selector, true)[0] || null, nocash);
	},

	getElements: function(tags, nocash){
		tags = tags.split(',');
		var elements = [];
		var ddup = (tags.length > 1);
		tags.each(function(tag){
			var partial = this.getElementsByTagName(tag.trim());
			(ddup) ? elements.extend(partial) : elements = partial;
		}, this);
		return new Elements(elements, {ddup: ddup, cash: !nocash});
	}

});

(function(){

var collected = {}, storage = {};
var props = {input: 'checked', option: 'selected', textarea: (Browser.Engine.webkit419) ? 'innerHTML' : 'value'};

var get = function(uid){
	return (storage[uid] || (storage[uid] = {}));
};

// just because we are using document.write, which make errors during unload in IE
DeleteFromMooPurgeByDocument = function(doc) {
	for(var k in collected) {
		var el = collected[k];
		if (el && el.ownerDocument === doc) clean(el);
	}
}

var clean = function(item, retainMethods){
	if (!item) return;
	var uid = item.uid;
	if (Browser.Engine.trident){
		if (item.clearAttributes) {
			var clone = retainMethods === true && item.cloneNode(false);
			item.clearAttributes();
			if (clone) item.mergeAttributes(clone);
		}
		else if (item.removeEvents) item.removeEvents();
		if ((/object/i).test(item.tagName)){
			for (var p in item){
				if (typeof item[p] == 'function') item[p] = null;
			}
			Element.dispose(item);
		}
	}	
	if (!uid) return;
	collected[uid] = storage[uid] = item.uid = null;
};

var purge = function() {
	//p: uz mne ty hlasky ve Firebugu fakt serou.. garbage maze objekty bez ohledu na dependence = nahodne chybove blitky
	//d: verim ze te to sere, ale neverim ze to ma neco spolecnyho s timhle kodem..
	for (var id in collected) {
		var item = collected[id];
		if (!item) continue;
		if (item.removeEvents) item.removeEvents();
		if (item.clearAttributes) item.clearAttributes();
	}
	if (Browser.Engine.trident) $A(document.getElementsByTagName('object')).each(clean);
	if (window.CollectGarbage) CollectGarbage();
	collected = storage = null;
};

var walk = function(element, walk, start, match, all, nocash){
	var el = element[start || walk];
	var elements = [];
	while (el){
		if (el.nodeType == 1 && (!match || Element.match(el, match))){
			if (!all) return $(el, nocash);
			elements.push(el);
		}
		el = el[walk];
	}
	return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : null;
};

var attributes = {'html': 'innerHTML', 'class': 'className', 'for': 'htmlFor', 'text': (Browser.Engine.trident || Browser.Engine.webkit419) ? 'innerText' : 'textContent'};
var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'];
var camels = ['value', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'];

Hash.extend(attributes, bools.associate(bools));
Hash.extend(attributes, camels.associate(camels.map(String.toLowerCase)));

var inserters = {

	before: function(context, element){
		if (element.parentNode) element.parentNode.insertBefore(context, element);
	},

	after: function(context, element){
		if (!element.parentNode) return;
		var next = element.nextSibling;
		(next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
	},

	bottom: function(context, element){
		element.appendChild(context);
	},

	top: function(context, element){
		var first = element.firstChild;
		(first) ? element.insertBefore(context, first) : element.appendChild(context);
	}

};

inserters.inside = inserters.bottom;

Hash.each(inserters, function(inserter, where){

	where = where.capitalize();

	Element.implement('inject' + where, function(el){
		inserter(this, $(el, true));
		return this;
	});

	Element.implement('grab' + where, function(el){
		inserter($(el, true), this);
		return this;
	});

});

Element.implement({

	set: function(prop, value){
		switch ($type(prop)){
			case 'object':
				for (var p in prop) this.set(p, prop[p]);
				break;
			case 'string':
				var property = Element.Properties.get(prop);
				(property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
		}
		return this;
	},

	get: function(prop){
		var property = Element.Properties.get(prop);
		return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
	},

	erase: function(prop){
		var property = Element.Properties.get(prop);
		(property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
		return this;
	},

	setProperty: function(attribute, value){
		var key = attributes[attribute];
		if (!$defined(value)) return this.removeProperty(attribute);
		if (key && bools[attribute]) value = !!value;
		(key) ? this[key] = value : this.setAttribute(attribute, '' + value);
		return this;
	},

	setProperties: function(attributes){
		for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
		return this;
	},

	getProperty: function(attribute){
		var key = attributes[attribute];
		var value = (key) ? this[key] : this.getAttribute(attribute, 2);
		return (bools[attribute]) ? !!value : (key) ? value : value || null;
	},

	getProperties: function(){
		var args = $A(arguments);
		return args.map(this.getProperty, this).associate(args);
	},

	removeProperty: function(attribute){
		var key = attributes[attribute];
		(key) ? this[key] = (key && bools[attribute]) ? false : '' : this.removeAttribute(attribute);
		return this;
	},

	removeProperties: function(){
		Array.each(arguments, this.removeProperty, this);
		return this;
	},

	hasClass: function(className){
		return this.className.contains(className, ' ');
	},

	addClass: function(className){
		if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
		return this;
	},

	removeClass: function(className){
		this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
		return this;
	},

	toggleClass: function(className, force){
        return ($defined(force) ? force : !this.hasClass(className))
            ? this.addClass(className)
            : this.removeClass(className);
    },
    
	adopt: function(){
		Array.flatten(arguments).each(function(element){
			element = $(element, true);
			if (element) this.appendChild(element);
		}, this);
		return this;
	},

	appendText: function(text, where){
		return this.grab(this.getDocument().newTextNode(text), where);
	},

	grab: function(el, where){
		inserters[where || 'bottom']($(el, true), this);
		return this;
	},

	inject: function(el, where){
		inserters[where || 'bottom'](this, $(el, true));
		return this;
	},

	replaces: function(el){
		el = $(el, true);
		el.parentNode.replaceChild(this, el);
		return this;
	},

	wraps: function(el, where){
		el = $(el, true);
		return this.replaces(el).grab(el, where);
	},

	getPrevious: function(match, nocash){
		return walk(this, 'previousSibling', null, match, false, nocash);
	},

	getAllPrevious: function(match, nocash){
		return walk(this, 'previousSibling', null, match, true, nocash);
	},

	getNext: function(match, nocash){
		return walk(this, 'nextSibling', null, match, false, nocash);
	},

	getAllNext: function(match, nocash){
		return walk(this, 'nextSibling', null, match, true, nocash);
	},

	getFirst: function(match, nocash){
		return walk(this, 'nextSibling', 'firstChild', match, false, nocash);
	},

	getLast: function(match, nocash){
		return walk(this, 'previousSibling', 'lastChild', match, false, nocash);
	},

	getParent: function(match, nocash){
		return walk(this, 'parentNode', null, match, false, nocash);
	},

	getParents: function(match, nocash){
		return walk(this, 'parentNode', null, match, true, nocash);
	},

	getChildren: function(match, nocash){
		return walk(this, 'nextSibling', 'firstChild', match, true, nocash);
	},

	getWindow: function(){
		return this.ownerDocument.window;
	},

	getDocument: function(){
		return this.ownerDocument;
	},

	getElementById: function(id, nocash){
		var el = this.ownerDocument.getElementById(id);
		if (!el) return null;
		for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
			if (!parent) return null;
		}
		return $.element(el, nocash);
	},

	getSelected: function(){
		return new Elements($A(this.options).filter(function(option){
			return option.selected;
		}));
	},

	getComputedStyle: function(property){
		if (this.currentStyle) return this.currentStyle[property.camelCase()];
		var computed = this.getDocument().defaultView.getComputedStyle(this, null);
		return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
	},

	toQueryString: function(){
		var queryString = [];
		this.getElements('input, select, textarea', true).each(function(el){
			if (!el.name || el.disabled) return;
			var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
				return opt.value;
			}) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
			$splat(value).each(function(val){
				if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val));
			});
		});
		return queryString.join('&');
	},

	clone: function(contents, keepid){
		contents = contents !== false;
		var clone = this.cloneNode(contents);
		var clean = function(node, element){
			if (!keepid) node.removeAttribute('id');
			if (Browser.Engine.trident){
				node.clearAttributes();
				node.mergeAttributes(element);
				node.removeAttribute('uid');
				if (node.options){
					var no = node.options, eo = element.options;
					for (var j = no.length; j--;) no[j].selected = eo[j].selected;
				}
			}
			var prop = props[element.tagName.toLowerCase()];
			if (prop && element[prop]) node[prop] = element[prop];
		};

		if (contents){
			var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
			for (var i = ce.length; i--;) clean(ce[i], te[i]);
		}

		clean(clone, this);
		return $(clone);
	},

	destroy: function(){
		Element.empty(this);
		Element.dispose(this);
		clean(this, true);
		return null;
	},

	dispose: function(){
		return (this.parentNode) ? this.parentNode.removeChild(this) : this;
	},

	// just remove all childs
	empty: function(){
		$A(this.childNodes).each(function(node){			
			Element.destroy(node);
		});
		return this;
	},

	hasChild: function(el){
		el = $(el, true);
		if (!el) return false;
		if (Browser.Engine.webkit419) return $A(this.getElementsByTagName(el.tagName)).contains(el);
		return (this.contains) ? (this != el && this.contains(el)) : !!(this.compareDocumentPosition(el) & 16);
	},

	match: function(tag){
		return (!tag || Element.get(this, 'tag') == tag);
	}

});

Native.implement([Element, Window, Document], {

	addListener: function(type, fn){
		if (type == 'unload'){
			var old = fn, self = this;
			fn = function(){
				self.removeListener('unload', fn);
				old();
			};
		} else {
			collected[this.uid] = this;
		}
		if (this.addEventListener) this.addEventListener(type, fn, false);
		else this.attachEvent('on' + type, fn);
		return this;
	},

	removeListener: function(type, fn){
		if (this.removeEventListener) this.removeEventListener(type, fn, false);
		else this.detachEvent('on' + type, fn);
		return this;
	},
	
	retrieve: function(property, dflt){
		var storage = get(this.uid), prop = storage[property];
		if ($defined(dflt) && !$defined(prop)) prop = storage[property] = dflt;
		return $pick(prop);
	},

	store: function(property, value){		
		var storage = get(this.uid);
		storage[property] = value;
		return this;
	},

	eliminate: function(property){
		var storage = get(this.uid);
		delete storage[property];
		return this;
	}

});

window.addListener('unload', purge);

})();

Element.Properties = new Hash;

Element.Properties.style = {

	set: function(style){
		this.style.cssText = style;
	},

	get: function(){
		return this.style.cssText;
	},

	erase: function(){
		this.style.cssText = '';
	}

};

Element.Properties.tag = {

	get: function(){
		return this.tagName.toLowerCase();
	}

};

Element.Properties.html = (function(){
	var wrapper = document.createElement('div');

	var translations = {
		select: [1, '<select>', '</select>'],
		table: [1, '<table>', '</table>'],
		tbody: [2, '<table><tbody>', '</tbody></table>'],
		tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
	};
	translations.thead = translations.tfoot = translations.tbody;

	var html = {
		set: function(){
			var html = Array.flatten(arguments).join('');
			var wrap = Browser.Engine.trident && translations[this.get('tag')];
			if (wrap){
				var first = wrapper;
				first.innerHTML = wrap[1] + html + wrap[2];
				for (var i = wrap[0]; i--;) first = first.firstChild;
				this.empty().adopt(first.childNodes);
			} else {
				this.innerHTML = html;
			}
		}
	};

	html.erase = html.set;

	return html;
})();

if (Browser.Engine.webkit419) Element.Properties.text = {
	get: function(){
		if (this.innerText) return this.innerText;
		var temp = this.ownerDocument.newElement('div', {html: this.innerHTML}).inject(this.ownerDocument.body);
		var text = temp.innerText;
		temp.destroy();
		return text;
	}
};
/*
Script: Element.Event.js
	Contains Element methods for dealing with events, and custom Events.

License:
	MIT-style license.
*/

Element.Properties.events = {set: function(events){
	this.addEvents(events);
}};

Native.implement([Element, Window, Document], {

	addEvent: function(type, fn){
		var events = this.retrieve('events', {});
		events[type] = events[type] || {'keys': [], 'values': []};
		if (events[type].keys.contains(fn)) return this;
		events[type].keys.push(fn);
		var realType = type, custom = Element.Events.get(type), condition = fn, self = this;
		if (custom){
			if (custom.onAdd) custom.onAdd.call(this, fn);
			if (custom.condition){
				condition = function(event){
					if (custom.condition.call(this, event)) return fn.call(this, event);
					return true;
				};
			}
			realType = custom.base || realType;
		}
		var defn = function(){
			return fn.call(self);
		};
		var nativeEvent = Element.NativeEvents[realType];
		if (nativeEvent){
			if (nativeEvent == 2){
				defn = function(event){
					event = new Event(event, self.getWindow());
					if (condition.call(self, event) === false) event.stop();
				};
			}
			this.addListener(realType, defn);
		}
		events[type].values.push(defn);
		return this;
	},

	removeEvent: function(type, fn){
		var events = this.retrieve('events');
		if (!events || !events[type]) return this;
		var pos = events[type].keys.indexOf(fn);
		if (pos == -1) return this;
		events[type].keys.splice(pos, 1);
		var value = events[type].values.splice(pos, 1)[0];
		var custom = Element.Events.get(type);
		if (custom){
			if (custom.onRemove) custom.onRemove.call(this, fn);
			type = custom.base || type;
		}
		return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this;
	},

	toggleEvent: function(type, fn, add) {
		return add ? this.addEvent(type, fn) : this.removeEvent(type, fn);
	},

	toggleEvents: function(events, add) {
		return add ? this.addEvents(events) : this.removeEvents(events);
	},

	addEvents: function(events){
		for (var event in events) this.addEvent(event, events[event]);
		return this;
	},

	removeEvents: function(what) {
		if (/(object|hash)/.test($type(what))) {
			for (var type in what) this.removeEvent(type, what[type]);
			return this;
		}
		var events = this.retrieve('events');
		if (!events) return this;
		if (!what) {
			for (var type in events) this.removeEvents(type);
			this.eliminate('events');
		} else if (events[what]) {
			while (events[what].keys[0]) this.removeEvent(what, events[what].keys[0]);
			events[what] = null;
		}
		return this;
	},

	fireEvent: function(type, args, delay){
		var events = this.retrieve('events');
		type = Events.removeOn(type);
		if (!events || !events[type]) return this;
		$A(events[type].keys).each(function(fn){
			fn.create({'bind': this, 'delay': delay, 'arguments': args})();
		}, this);
		return this;
	},

	cloneEvents: function(from, type){
		from = $(from);
		var fevents = from.retrieve('events');
		if (!fevents) return this;
		if (!type){
			for (var evType in fevents) this.cloneEvents(from, evType);
		} else if (fevents[type]){
			fevents[type].keys.each(function(fn){
				this.addEvent(type, fn);
			}, this);
		}
		return this;
	}

});

Element.NativeEvents = {
	click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
	mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
	mouseover: 2, mouseout: 2, mousemove: 2, dragstart: 1, selectstart: 2, selectend: 2, //mouse movement
	keydown: 2, keypress: 2, keyup: 2, //keyboard
	focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements
	load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
	error: 1, abort: 1, scroll: 1 //misc
};

(function(){

var $check = function(event){
	var related = event.relatedTarget;
	if (related == undefined) return true;
	if (related === false) return false;
	return ($type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related));
};

Element.Events = new Hash({

	mouseenter: {
		base: 'mouseover',
		condition: $check
	},

	mouseleave: {
		base: 'mouseout',
		condition: $check
	},

	mousewheel: {
		base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel'
	}

});

})();/*
Script: Element.Style.js
	Contains methods for interacting with the styles of Elements in a fashionable way.

License:
	MIT-style license.
*/

Element.Properties.styles = {set: function(styles){
	this.setStyles(styles);
}};

Element.Properties.opacity = {

	set: function(opacity, novisibility){
		if (!novisibility){
			if (opacity == 0){
				if (this.style.visibility != 'hidden') this.style.visibility = 'hidden';
			} else {
				if (this.style.visibility != 'visible') this.style.visibility = 'visible';
			}
		}
		if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
		//if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
		if (Browser.Engine.trident) {
		    if (opacity == 1) this.style.removeAttribute('filter');
		    else this.style.filter = 'alpha(opacity=' + opacity * 100 + ')';
		}
		this.style.opacity = opacity;
		this.store('opacity', opacity);
	},

	get: function(){
		return this.retrieve('opacity', 1);
	}

};

Element.implement({

	setOpacity: function(value){
		return this.set('opacity', value/*, true*/);
	},

	getOpacity: function(){
		return this.get('opacity');
	},

	setStyle: function(property, value){
		switch (property){
			case 'opacity': return this.set('opacity', parseFloat(value));
			case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
		}
		property = property.camelCase();
		if ($type(value) != 'string'){
			var map = (Element.Styles.get(property) || '@').split(' ');
			value = $splat(value).map(function(val, i){
				if (!map[i]) return '';
				return ($type(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
			}).join(' ');
		} else if (value == String(Number(value))){
			value = Math.round(value);
		}
		this.style[property] = value;
		return this;
	},

	getStyle: function(property) {
        switch (property) {
            case 'opacity': return this.get('opacity');
            case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
            case 'width': return this.offsetWidth;
            case 'height': return this.offsetHeight;
        }
        property = property.camelCase();
        var result = this.style[property];
        if (!$chk(result)) {
            result = [];
            for (var style in Element.ShortStyles) {
                if (property != style) continue;
                for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
                return result.join(' ');
            }
            result = this.getComputedStyle(property);
        }
        if (result) {
            result = String(result);
            var color = result.match(/rgba?\([\d\s,]+\)/);
            if (color) result = result.replace(color[0], color[0].rgbToHex());
            else if (Browser.Engine.trident && !/^\d+(px)?$/i.test(result) && /^\d/.test(result) && !/\s/.test(result)) {
                // If we're not dealing with a regular pixel number
                // but a number that has a weird ending, we need to convert it to pixels
                // Remember the original values
                var left = this.style.left, rsLeft = this.runtimeStyle.left;
                // Put in the new values to get a computed value out
                this.runtimeStyle.left = this.currentStyle.left;
                this.style.left = result || 0;
                result = this.style.pixelLeft + "px";
                // Revert the changed values
                this.style.left = left;
                this.runtimeStyle.left = rsLeft;
            }
        }
        if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result)))) {
            if (property.test(/^(height|width)$/)) {
                var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
                values.each(function(value) {
                    size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
                }, this);
                return this['offset' + property.capitalize()] - size + 'px';
            }
            if (Browser.Engine.presto && String(result).test('px')) return result;
            if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
        }
        return result;
    },

	setStyles: function(styles){
		for (var style in styles) this.setStyle(style, styles[style]);
		return this;
	},

	getStyles: function(){
		var result = {};
		Array.each(arguments, function(key){
			result[key] = this.getStyle(key);
		}, this);
		return result;
	}

});

Element.Styles = new Hash({
	left: '@px', top: '@px', bottom: '@px', right: '@px',
	width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
	backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
	fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
	margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
	borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
	zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
});

Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};

['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
	var Short = Element.ShortStyles;
	var All = Element.Styles;
	['margin', 'padding'].each(function(style){
		var sd = style + direction;
		Short[style][sd] = All[sd] = '@px';
	});
	var bd = 'border' + direction;
	Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
	var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
	Short[bd] = {};
	Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
	Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
	Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
});
/*
Script: Element.Dimensions.js
	Contains methods to work with size, scroll, or positioning of Elements and the window object.

License:
	MIT-style license.

Credits:
	- Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
	- Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
*/

(function(){

Element.implement({

	scrollTo: function(x, y){
		if (isBody(this)){
			this.getWindow().scrollTo(x, y);
		} else {
			this.scrollLeft = x;
			this.scrollTop = y;
		}
		return this;
	},

	getSize: function(){
		if (isBody(this)) return this.getWindow().getSize();
		return {x: this.offsetWidth, y: this.offsetHeight};
	},

	getScrollSize: function(){
		if (isBody(this)) return this.getWindow().getScrollSize();
		return {x: this.scrollWidth, y: this.scrollHeight};
	},

	getScroll: function(){
		if (isBody(this)) return this.getWindow().getScroll();
		return {x: this.scrollLeft, y: this.scrollTop};
	},

	getScrolls: function(){
		var element = this, position = {x: 0, y: 0};
		while (element && !isBody(element)){
			position.x += element.scrollLeft;
			position.y += element.scrollTop;
			element = element.parentNode;
		}
		return position;
	},
	
	getOffsetParent: function() {
		var element = this;
		if (isBody(element)) return null;
		if (!Browser.Engine.trident) return element.offsetParent;
		while ((element = element.parentNode) && !isBody(element)) {
			if (styleString(element, 'position') != 'static') return element;
		}
		return element;
	},

	getPosition: (function() {

		return document.documentElement.getBoundingClientRect ?
			function(relative) {
				lazyCheckBugs();
				if (isBody(this)) return bodyOffset(this);
				var box = this.getBoundingClientRect(),
					doc = this.ownerDocument,
					boxMode = doc.boxMode(),
					body = doc.body,
					html = doc.documentElement,
					clientLeft = html.clientLeft || body.clientLeft || 0,
					clientTop = html.clientTop || body.clientTop || 0,
					x = box.left + (self.pageXOffset || boxMode && html.scrollLeft || body.scrollLeft) - clientLeft,
					y = box.top + (self.pageYOffset || boxMode && html.scrollTop || body.scrollTop) - clientTop,
					relativePosition;
				if (relative && (relative = $(relative)) && !isBody(relative)) {
					relativePosition = relative.getPosition();
					x -= relativePosition.x, y -= relativePosition.y;
				}
				return { x: x, y: y };
			} :
			function(relative) {
				lazyCheckBugs();
				if (isBody(this)) return bodyOffset(this);
				var el = this,
					offsetParent = el.offsetParent,
					prevOffsetParent = el,
					doc = el.ownerDocument,
					computedStyle,
					html = doc.documentElement,
					body = doc.body,
					defaultView = doc.defaultView,
					prevComputedStyle = defaultView.getComputedStyle(el, null),
					x = el.offsetLeft,
					y = el.offsetTop,
					relativePosition;
				while ((el = el.parentNode) && el !== body && el !== html) {
					computedStyle = defaultView.getComputedStyle(el, null);
					x -= el.scrollLeft, y -= el.scrollTop;
					if (el === offsetParent) {
						x += el.offsetLeft, y += el.offsetTop;
						if (bugs.doesNotAddBorder && !(bugs.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(el.tagName)))
							x += parseInt(computedStyle.borderLeftWidth, 10) || 0, y += parseInt(computedStyle.borderTopWidth, 10) || 0;
						prevOffsetParent = offsetParent, offsetParent = el.offsetParent;
					}
					if (bugs.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible")
						x += parseInt(computedStyle.borderLeftWidth, 10) || 0, y += parseInt(computedStyle.borderTopWidth, 10) || 0;
					prevComputedStyle = computedStyle;
				}
				if (prevComputedStyle.position === "relative" || prevComputedStyle.position === "static")
					x += body.offsetLeft, y += body.offsetTop;
				if (prevComputedStyle.position === "fixed")
					x += Math.max(html.scrollLeft, body.scrollLeft), y += Math.max(html.scrollTop, body.scrollTop);					
				if (relative && (relative = $(relative)) && !isBody(relative)) {
					relativePosition = relative.getPosition();
					x -= relativePosition.x, y -= relativePosition.y;
				}
				return { x: x, y: y };
			};
	})(),

	getCoordinates: function(element){
		if (isBody(this)) return this.getWindow().getCoordinates();
		var position = this.getPosition(element), size = this.getSize();
		var obj = {left: position.x, top: position.y, width: size.x, height: size.y};
		obj.right = obj.left + obj.width;
		obj.bottom = obj.top + obj.height;
		return obj;
	},

	computePosition: function(obj){
		return {left: obj.x - styleNumber(this, 'margin-left'), top: obj.y - styleNumber(this, 'margin-top')};
	},

	position: function(obj){
		return this.setStyles(this.computePosition(obj));
	}

});

Native.implement([Document, Window], {

	getSize: function(){
		var win = this.getWindow();
		if (Browser.Engine.presto || Browser.Engine.webkit) return {x: win.innerWidth, y: win.innerHeight};
		var doc = getCompatElement(this);
		return {x: doc.clientWidth, y: doc.clientHeight};
	},

	getScroll: function(){
		var win = this.getWindow();
		var doc = getCompatElement(this);
		return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
	},

	getScrollSize: function(){
		var doc = getCompatElement(this);
		var min = this.getSize();
		return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)};
	},

	getPosition: function(){
		return {x: 0, y: 0};
	},

	getCoordinates: function(){
		var size = this.getSize();
		return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
	}

});

// private methods

var bugs;

function lazyCheckBugs() {
	if (bugs) return;
	bugs = {};
	var body = document.body, container = document.createElement('div'),
		html = '<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"cellpadding="0"cellspacing="0"><tr><td></td></tr></table>';
	Hash.each({ position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' }, function(v, k) {
		container.style[k] = v;
	});
	container.innerHTML = html;
	body.insertBefore(container, body.firstChild);
	var innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;
	bugs.doesNotAddBorder = (checkDiv.offsetTop !== 5);
	bugs.doesAddBorderForTableAndCells = (td.offsetTop === 5);
	innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
	bugs.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
	var bodyMarginTop = body.style.marginTop;
	body.style.marginTop = '1px';
	bugs.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0);
	body.style.marginTop = bodyMarginTop;
	body.removeChild(container);
}

var styleString = Element.getComputedStyle;

function styleNumber(element, style) {
	return styleString(element, style).toInt() || 0;
};

function bodyOffset(body) {
	lazyCheckBugs();
	var left = body.offsetLeft, top = body.offsetTop;
	if (bugs.doesNotIncludeMarginInBodyOffset)		
		left += parseInt(styleString(body, 'marginLeft'), 10) || 0,
		top += parseInt(styleString(body, 'marginTop'), 10) || 0;
	return { x: left, y: top };
}

function isBody(element){
	//return (/^(?:body|html)$/i).test(element.tagName);
	return element === element.ownerDocument.body;
};

function getCompatElement(element){
	var doc = element.getDocument();
	return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
};

})();

//aliases

Native.implement([Window, Document, Element], {

	getHeight: function(){
		return this.getSize().y;
	},

	getWidth: function(){
		return this.getSize().x;
	},

	getScrollTop: function(){
		return this.getScroll().y;
	},

	getScrollLeft: function(){
		return this.getScroll().x;
	},

	getScrollHeight: function(){
		return this.getScrollSize().y;
	},

	getScrollWidth: function(){
		return this.getScrollSize().x;
	},

	getTop: function(){
		return this.getPosition().y;
	},

	getLeft: function(){
		return this.getPosition().x;
	}

});/*
Script: Selectors.js
	Adds advanced CSS Querying capabilities for targeting elements. Also includes pseudoselectors support.

License:
	MIT-style license.
*/

Native.implement([Document, Element], {

	getElements: function(expression, nocash){
		expression = expression.split(',');
		var items, local = {};
		for (var i = 0, l = expression.length; i < l; i++){
			var selector = expression[i], elements = Selectors.Utils.search(this, selector, local);
			if (i != 0 && elements.item) elements = $A(elements);
			items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements);
		}
		return new Elements(items, {ddup: (expression.length > 1), cash: !nocash});
	}

});

Element.implement({

	match: function(selector){
		if (!selector) return true;
		var tagid = Selectors.Utils.parseTagAndID(selector);
		var tag = tagid[0], id = tagid[1];
		if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false;
		var parsed = Selectors.Utils.parseSelector(selector);
		return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
	}

});

var Selectors = {Cache: {nth: {}, parsed: {}}};

Selectors.RegExps = {
	id: (/#([\w-]+)/),
	tag: (/^(\w+|\*)/),
	quick: (/^(\w+|\*)$/),
	splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
	combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)(["']?)([^\4]*?)\4)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
};

Selectors.Utils = {

	chk: function(item, uniques){
		if (!uniques) return true;
		var uid = $uid(item);
		if (!uniques[uid]) return uniques[uid] = true;
		return false;
	},

	parseNthArgument: function(argument){
		if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
		var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
		if (!parsed) return false;
		var inta = parseInt(parsed[1]);
		var a = (inta || inta === 0) ? inta : 1;
		var special = parsed[2] || false;
		var b = parseInt(parsed[3]) || 0;
		if (a != 0){
			b--;
			while (b < 1) b += a;
			while (b >= a) b -= a;
		} else {
			a = b;
			special = 'index';
		}
		switch (special){
			case 'n': parsed = {a: a, b: b, special: 'n'}; break;
			case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
			case 'even': parsed = {a: 2, b: 1, special: 'n'}; break;
			case 'first': parsed = {a: 0, special: 'index'}; break;
			case 'last': parsed = {special: 'last-child'}; break;
			case 'only': parsed = {special: 'only-child'}; break;
			default: parsed = {a: (a - 1), special: 'index'};
		}

		return Selectors.Cache.nth[argument] = parsed;
	},

	parseSelector: function(selector){
		if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
		var m, parsed = {classes: [], pseudos: [], attributes: []};
		while ((m = Selectors.RegExps.combined.exec(selector))){
			var cn = m[1], an = m[2], ao = m[3], av = m[5], pn = m[6], pa = m[7];
			if (cn){
				parsed.classes.push(cn);
			} else if (pn){
				var parser = Selectors.Pseudo.get(pn);
				if (parser) parsed.pseudos.push({parser: parser, argument: pa});
				else parsed.attributes.push({name: pn, operator: '=', value: pa});
			} else if (an){
				parsed.attributes.push({name: an, operator: ao, value: av});
			}
		}
		if (!parsed.classes.length) delete parsed.classes;
		if (!parsed.attributes.length) delete parsed.attributes;
		if (!parsed.pseudos.length) delete parsed.pseudos;
		if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null;
		return Selectors.Cache.parsed[selector] = parsed;
	},

	parseTagAndID: function(selector){
		var tag = selector.match(Selectors.RegExps.tag);
		var id = selector.match(Selectors.RegExps.id);
		return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
	},

	filter: function(item, parsed, local){
		var i;
		if (parsed.classes){
			for (i = parsed.classes.length; i--; i){
				var cn = parsed.classes[i];
				if (!Selectors.Filters.byClass(item, cn)) return false;
			}
		}
		if (parsed.attributes){
			for (i = parsed.attributes.length; i--; i){
				var att = parsed.attributes[i];
				if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false;
			}
		}
		if (parsed.pseudos){
			for (i = parsed.pseudos.length; i--; i){
				var psd = parsed.pseudos[i];
				if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false;
			}
		}
		return true;
	},

	getByTagAndID: function(ctx, tag, id){
		if (id){
			var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true);
			return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
		} else {
			return ctx.getElementsByTagName(tag);
		}
	},

	search: function(self, expression, local){
		var splitters = [];

		var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){
			splitters.push(m1);
			return ':)' + m2;
		}).split(':)');

		var items, filtered, item;

		for (var i = 0, l = selectors.length; i < l; i++){

			var selector = selectors[i];

			if (i == 0 && Selectors.RegExps.quick.test(selector)){
				items = self.getElementsByTagName(selector);
				continue;
			}

			var splitter = splitters[i - 1];

			var tagid = Selectors.Utils.parseTagAndID(selector);
			var tag = tagid[0], id = tagid[1];

			if (i == 0){
				items = Selectors.Utils.getByTagAndID(self, tag, id);
			} else {
				var uniques = {}, found = [];
				for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
				items = found;
			}

			var parsed = Selectors.Utils.parseSelector(selector);

			if (parsed){
				filtered = [];
				for (var m = 0, n = items.length; m < n; m++){
					item = items[m];
					if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
				}
				items = filtered;
			}

		}

		return items;

	}

};

Selectors.Getters = {

	' ': function(found, self, tag, id, uniques){
		var items = Selectors.Utils.getByTagAndID(self, tag, id);
		for (var i = 0, l = items.length; i < l; i++){
			var item = items[i];
			if (Selectors.Utils.chk(item, uniques)) found.push(item);
		}
		return found;
	},

	'>': function(found, self, tag, id, uniques){
		var children = Selectors.Utils.getByTagAndID(self, tag, id);
		for (var i = 0, l = children.length; i < l; i++){
			var child = children[i];
			if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child);
		}
		return found;
	},

	'+': function(found, self, tag, id, uniques){
		while ((self = self.nextSibling)){
			if (self.nodeType == 1){
				if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
				break;
			}
		}
		return found;
	},

	'~': function(found, self, tag, id, uniques){
		while ((self = self.nextSibling)){
			if (self.nodeType == 1){
				if (!Selectors.Utils.chk(self, uniques)) break;
				if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
			}
		}
		return found;
	}

};

Selectors.Filters = {

	byTag: function(self, tag){
		return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag));
	},

	byID: function(self, id){
		return (!id || (self.id && self.id == id));
	},

	byClass: function(self, klass){
		return (self.className && self.className.contains(klass, ' '));
	},

	byPseudo: function(self, parser, argument, local){
		return parser.call(self, argument, local);
	},

	byAttribute: function(self, name, operator, value){
		var result = Element.prototype.getProperty.call(self, name);
		if (!result) return (operator == '!=');
		if (!operator || value == undefined) return true;
		switch (operator){
			case '=': return (result == value);
			case '*=': return (result.contains(value));
			case '^=': return (result.substr(0, value.length) == value);
			case '$=': return (result.substr(result.length - value.length) == value);
			case '!=': return (result != value);
			case '~=': return result.contains(value, ' ');
			case '|=': return result.contains(value, '-');
		}
		return false;
	}

};

Selectors.Pseudo = new Hash({

	// w3c pseudo selectors

	checked: function(){
		return this.checked;
	},

	empty: function(){
		return !(this.innerText || this.textContent || '').length;
	},

	not: function(selector){
		return !Element.match(this, selector);
	},

	contains: function(text){
		return (this.innerText || this.textContent || '').contains(text);
	},

	'first-child': function(){
		return Selectors.Pseudo.index.call(this, 0);
	},

	'last-child': function(){
		var element = this;
		while ((element = element.nextSibling)){
			if (element.nodeType == 1) return false;
		}
		return true;
	},

	'only-child': function(){
		var prev = this;
		while ((prev = prev.previousSibling)){
			if (prev.nodeType == 1) return false;
		}
		var next = this;
		while ((next = next.nextSibling)){
			if (next.nodeType == 1) return false;
		}
		return true;
	},

	'nth-child': function(argument, local){
		argument = (argument == undefined) ? 'n' : argument;
		var parsed = Selectors.Utils.parseNthArgument(argument);
		if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local);
		var count = 0;
		local.positions = local.positions || {};
		var uid = $uid(this);
		if (!local.positions[uid]){
			var self = this;
			while ((self = self.previousSibling)){
				if (self.nodeType != 1) continue;
				count ++;
				var position = local.positions[$uid(self)];
				if (position != undefined){
					count = position + count;
					break;
				}
			}
			local.positions[uid] = count;
		}
		return (local.positions[uid] % parsed.a == parsed.b);
	},

	// custom pseudo selectors

	index: function(index){
		var element = this, count = 0;
		while ((element = element.previousSibling)){
			if (element.nodeType == 1 && ++count > index) return false;
		}
		return (count == index);
	},

	even: function(argument, local){
		return Selectors.Pseudo['nth-child'].call(this, '2n+1', local);
	},

	odd: function(argument, local){
		return Selectors.Pseudo['nth-child'].call(this, '2n', local);
	}

});
/*
Script: Domready.js
	Contains the domready custom event.

License:
	MIT-style license.
*/

Element.Events.domready = {

	onAdd: function(fn){
		if (Browser.loaded) fn.call(this);
	}

};

(function(){

	var domready = function(){
		if (Browser.loaded) return;
		Browser.loaded = true;
		window.fireEvent('domready');
		document.fireEvent('domready');
	};

	if (Browser.Engine.trident){
		var temp = document.createElement('div');
		(function(){
			($try(function(){
				temp.doScroll('left');
				return $(temp).inject(document.body).set('html', 'temp').dispose();
			})) ? domready() : arguments.callee.delay(50);
		})();
	} else if (Browser.Engine.webkit && Browser.Engine.version < 525){
		(function(){
			(['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50);
		})();
	} else {
		window.addEvent('load', domready);
		document.addEvent('DOMContentLoaded', domready);
	}

})();
/*
Script: JSON.js
	JSON encoder and decoder.

License:
	MIT-style license.

See Also:
	<http://www.json.org/>
*/

var JSON = new Hash({

	$specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'},

	$replaceChars: function(chr){
		return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
	},

	encode: function(obj){
		switch ($type(obj)){
			case 'string':
				return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"';
			case 'array':
				return '[' + String(obj.map(JSON.encode).filter($defined)) + ']';
			case 'object': case 'hash':
				var string = [];
				Hash.each(obj, function(value, key){
					var json = JSON.encode(value);
					if (json) string.push(JSON.encode(key) + ':' + json);
				});
				return '{' + string + '}';
			case 'number': case 'boolean': return String(obj);
			case false: return 'null';
		}
		return null;
	},

	decode: function(string, secure){
		if ($type(string) != 'string' || !string.length) return null;
		if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null;
		return eval('(' + string + ')');
	}

});

Native.implement([Hash, Array, String, Number], {

	toJSON: function(){
		return JSON.encode(this);
	}

});
/*
Script: Cookie.js
	Class for creating, loading, and saving browser Cookies.

License:
	MIT-style license.

Credits:
	Based on the functions by Peter-Paul Koch (http://quirksmode.org).
*/

var Cookie = new Class({

	Implements: Options,

	options: {
		path: false,
		domain: false,
		duration: false,
		secure: false,
		document: document
	},

	initialize: function(key, options){
		this.key = key;
		this.setOptions(options);
	},

	write: function(value){
		value = encodeURIComponent(value);
		if (this.options.domain) value += '; domain=' + this.options.domain;
		if (this.options.path) value += '; path=' + this.options.path;
		if (this.options.duration){
			var date = new Date();
			date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
			value += '; expires=' + date.toGMTString();
		}
		if (this.options.secure) value += '; secure';
		this.options.document.cookie = this.key + '=' + value;
		return this;
	},

	read: function(){
		var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
		return (value) ? decodeURIComponent(value[1]) : null;
	},

	dispose: function(){
		new Cookie(this.key, $merge(this.options, {duration: -1})).write('');
		return this;
	}

});

Cookie.write = function(key, value, options){
	return new Cookie(key, options).write(value);
};

Cookie.read = function(key){
	return new Cookie(key).read();
};

Cookie.dispose = function(key, options){
	return new Cookie(key, options).dispose();
};/*
Script: Swiff.js
	Wrapper for embedding SWF movies. Supports (and fixes) External Interface Communication.

License:
	MIT-style license.

Credits:
	Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
*/

var Swiff = new Class({

	Implements: [Options],

	options: {
		id: null,
		height: 1,
		width: 1,
		container: null,
		properties: {},
		params: {
			quality: 'high',
			allowScriptAccess: 'always',
			wMode: 'transparent',
			swLiveConnect: true
		},
		callBacks: {},
		vars: {}
	},

	toElement: function(){
		return this.object;
	},

	initialize: function(path, options){
		this.instance = 'Swiff_' + $time();

		this.setOptions(options);
		options = this.options;
		var id = this.id = options.id || this.instance;
		var container = $(options.container);

		Swiff.CallBacks[this.instance] = {};

		var params = options.params, vars = options.vars, callBacks = options.callBacks;
		var properties = $extend({height: options.height, width: options.width}, options.properties);

		var self = this;

		for (var callBack in callBacks){
			Swiff.CallBacks[this.instance][callBack] = (function(option){
				return function(){
					return option.apply(self.object, arguments);
				};
			})(callBacks[callBack]);
			vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
		}

		params.flashVars = Hash.toQueryString(vars);
		if (Browser.Engine.trident){
			properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
			params.movie = path;
		} else {
			properties.type = 'application/x-shockwave-flash';
			properties.data = path;
		}
		var build = '<object id="' + id + '"';
		for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
		build += '>';
		for (var param in params){
			if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
		}
		build += '</object>';
		this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
	},

	replaces: function(element){
		element = $(element, true);
		element.parentNode.replaceChild(this.toElement(), element);
		return this;
	},

	inject: function(element){
		$(element, true).appendChild(this.toElement());
		return this;
	},

	remote: function(){
		return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments));
	}

});

Swiff.CallBacks = {};

Swiff.remote = function(obj, fn){
	var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
	return eval(rs);
};

// this is needed for FancyUpload and possibly other Flash vs JavaScript communication to eliminate effects of scoping.
self['Swiff'] = Swiff;
/*
Script: Fx.js
	Contains the basic animation logic to be extended by all other Fx Classes.

License:
	MIT-style license.
*/

var Fx = new Class({

	Implements: [Chain, Events, Options],

	options: {
		/*
		onStart: $empty,
		onCancel: $empty,
		onComplete: $empty,
		*/
		fps: 50,
		unit: false,
		duration: 500,
		link: 'ignore'
	},

	initialize: function(options){
		this.subject = this.subject || this;
		this.setOptions(options);
		this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt();
		var wait = this.options.wait;
		if (wait === false) this.options.link = 'cancel';
	},

	getTransition: function(){
		return function(p){
			return -(Math.cos(Math.PI * p) - 1) / 2;
		};
	},

	step: function(){
		var time = $time();
		if (time < this.time + this.options.duration){
			var delta = this.transition((time - this.time) / this.options.duration);
			this.set(this.compute(this.from, this.to, delta));
		} else {
			this.set(this.compute(this.from, this.to, 1));
			this.complete();
		}
	},

	set: function(now){
		return now;
	},

	compute: function(from, to, delta){
		return Fx.compute(from, to, delta);
	},

	check: function(caller){
		if (!this.timer) return true;
		switch (this.options.link){
			case 'cancel': this.cancel(); return true;
			case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return false;
		}
		return false;
	},

	start: function(from, to){
		if (!this.check(arguments.callee, from, to)) return this;
		this.from = from;
		this.to = to;
		this.time = 0;
		this.transition = this.getTransition();
		this.startTimer();
		this.onStart();
		return this;
	},

	complete: function(){
		if (this.stopTimer()) this.onComplete();
		return this;
	},

	cancel: function(){
		if (this.stopTimer()) this.onCancel();
		return this;
	},

	onStart: function(){
		this.fireEvent('start', this.subject);
	},

	onComplete: function(){
		this.fireEvent('complete', this.subject);
		if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
	},

	onCancel: function(){
		this.fireEvent('cancel', this.subject).clearChain();
	},

	pause: function(){
		this.stopTimer();
		return this;
	},

	resume: function(){
		this.startTimer();
		return this;
	},

	stopTimer: function(){
		if (!this.timer) return false;
		this.time = $time() - this.time;
		this.timer = $clear(this.timer);
		return true;
	},

	startTimer: function(){
		if (this.timer) return false;
		this.time = $time() - this.time;
		this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
		return true;
	}

});

Fx.compute = function(from, to, delta){
	return (to - from) * delta + from;
};

Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
/*
Script: Fx.CSS.js
	Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.

License:
	MIT-style license.
*/

Fx.CSS = new Class({

	Extends: Fx,

	//prepares the base from/to object

	prepare: function(element, property, values){
		values = $splat(values);
		var values1 = values[1];
		if (!$chk(values1)){
			values[1] = values[0];
			values[0] = element.getStyle(property);
		}
		var parsed = values.map(this.parse);
		return {from: parsed[0], to: parsed[1]};
	},

	//parses a value into an array

	parse: function(value){
		value = $lambda(value)(); // what is it?
		if (typeof value == 'string') {
			// stupid fix for value == rect(@px @px @px @px)
			value = value.replace('rect(', '').replace(')', '').split(' ');
		}
		else {
			value = $splat(value);
		}
		return value.map(function(val){
			val = String(val);
			var found = false;
			Fx.CSS.Parsers.each(function(parser, key){
				if (found) return;
				var parsed = parser.parse(val);
				if ($chk(parsed)) found = {value: parsed, parser: parser};
			});
			found = found || {value: val, parser: Fx.CSS.Parsers.String};
			return found;
		});
	},

	//computes by a from and to prepared objects, using their parsers.

	compute: function(from, to, delta){
		var computed = [];
		(Math.min(from.length, to.length)).times(function(i){
			computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
		});
		computed.$family = {name: 'fx:css:value'};
		return computed;
	},

	//serves the value as settable

	serve: function(value, unit){
		if ($type(value) != 'fx:css:value') value = this.parse(value);
		var returned = [];
		value.each(function(bit){
			returned = returned.concat(bit.parser.serve(bit.value, unit));
		});
		return returned;
	},

	//renders the change to an element

	render: function(element, property, value, unit){
		element.setStyle(property, this.serve(value, unit));
	},

	//searches inside the page css to find the values for a selector

	search: function(selector){
		if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
		var to = {};
		Array.each(document.styleSheets, function(sheet, j){
			var href = sheet.href;
			if (href && href.contains('://') && !href.contains(document.domain)) return;
			var rules = sheet.rules || sheet.cssRules;
			Array.each(rules, function(rule, i){
				if (!rule.style) return;
				var regex = Browser.Engine.webkit ? /^\w+/ : /^\w+(?=\W){0,1}|[, ]\w+(?=\W){0,1}/g;
				var selectorText = (rule.selectorText) ? rule.selectorText.replace(regex, function(m){
					return m.toLowerCase();
				}) : null;
				if (!selectorText || !selectorText.test('^' + selector + '$')) return;
				Element.Styles.each(function(value, style){
					if (!rule.style[style] || Element.ShortStyles[style]) return;
					value = String(rule.style[style]);
					to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value;
				});
			});
		});
		return Fx.CSS.Cache[selector] = to;
	}

});

Fx.CSS.Cache = {};

Fx.CSS.Parsers = new Hash({

	Color: {
		parse: function(value){
			if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
			return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
		},
		compute: function(from, to, delta){
			return from.map(function(value, i){
				return Math.round(Fx.compute(from[i], to[i], delta));
			});
		},
		serve: function(value){
			return value.map(Number);
		}
	},

	Number: {
		parse: parseFloat,
		compute: Fx.compute,
		serve: function(value, unit){
			return (unit) ? value + unit : value;
		}
	},

	String: {
		parse: $lambda(false),
		compute: $arguments(1),
		serve: $arguments(0)
	}

});
/*
Script: Fx.Tween.js
	Formerly Fx.Style, effect to transition any CSS property for an element.

License:
	MIT-style license.
*/

Fx.Tween = new Class({

	Extends: Fx.CSS,

	initialize: function(element, options){
		this.element = this.subject = $(element);
		this.parent(options);
	},

	set: function(property, now){
		if (arguments.length == 1){
			now = property;
			property = this.property || this.options.property;
		}
		this.render(this.element, property, now, this.options.unit);
		return this;
	},

	start: function(property, from, to){
		if (!this.check(arguments.callee, property, from, to)) return this;
		var args = Array.flatten(arguments);
		this.property = this.options.property || args.shift();
		var parsed = this.prepare(this.element, this.property, args);
		return this.parent(parsed.from, parsed.to);
	}

});

Element.Properties.tween = {

	set: function(options){
		var tween = this.retrieve('tween');
		if (tween) tween.cancel();
		return this.eliminate('tween').store('tween:options', $extend({link: 'cancel'}, options));
	},

	get: function(options){
		if (options || !this.retrieve('tween')){
			if (options || !this.retrieve('tween:options')) this.set('tween', options);
			this.store('tween', new Fx.Tween(this, this.retrieve('tween:options')));
		}
		return this.retrieve('tween');
	}

};

Element.implement({

	tween: function(property, from, to){
		this.get('tween').start(arguments);
		return this;
	},

	fade: function(how){
		var fade = this.get('tween'), o = 'opacity', toggle;
		how = $pick(how, 'toggle');
		switch (how){
			case 'in': fade.start(o, 1); break;
			case 'out': fade.start(o, 0); break;
			case 'show': fade.set(o, 1); break;
			case 'hide': fade.set(o, 0); break;
			case 'toggle':
				var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
				fade.start(o, (flag) ? 0 : 1);
				this.store('fade:flag', !flag);
				toggle = true;
			break;
			default: fade.start(o, arguments);
		}
		if (!toggle) this.eliminate('fade:flag');
		return this;
	},

	highlight: function(start, end){
		if (!end){
			end = this.retrieve('highlight:original', this.getStyle('background-color'));
			end = (end == 'transparent') ? '#fff' : end;
		}
		var tween = this.get('tween');
		tween.start('background-color', start || '#ffff88', end).chain(function(){
			this.setStyle('background-color', this.retrieve('highlight:original'));
			tween.callChain();
		}.bind(this));
		return this;
	}

});
/*
Script: Fx.Morph.js
	Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.

License:
	MIT-style license.
*/

Fx.Morph = new Class({

	Extends: Fx.CSS,

	initialize: function(element, options){
		this.element = this.subject = $(element);
		this.parent(options);
	},

	set: function(now){
		if (typeof now == 'string') now = this.search(now);
		for (var p in now) this.render(this.element, p, now[p], this.options.unit);
		return this;
	},

	compute: function(from, to, delta){
		var now = {};
		for (var p in from) now[p] = this.parent(from[p], to[p], delta);
		return now;
	},

	start: function(properties){
		if (!this.check(arguments.callee, properties)) return this;
		if (typeof properties == 'string') properties = this.search(properties);
		var from = {}, to = {};
		for (var p in properties){
			var parsed = this.prepare(this.element, p, properties[p]);
			from[p] = parsed.from;
			to[p] = parsed.to;
		}
		return this.parent(from, to);
	}

});

Element.Properties.morph = {

	set: function(options){
		var morph = this.retrieve('morph');
		if (morph) morph.cancel();
		return this.eliminate('morph').store('morph:options', $extend({link: 'cancel'}, options));
	},

	get: function(options){
		if (options || !this.retrieve('morph')){
			if (options || !this.retrieve('morph:options')) this.set('morph', options);
			this.store('morph', new Fx.Morph(this, this.retrieve('morph:options')));
		}
		return this.retrieve('morph');
	}

};

Element.implement({

	morph: function(props){
		this.get('morph').start(props);
		return this;
	}

});/*
Script: Fx.Transitions.js
	Contains a set of advanced transitions to be used with any of the Fx Classes.

License:
	MIT-style license.

Credits:
	Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
*/

Fx.implement({

	getTransition: function(){
		var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
		if (typeof trans == 'string'){
            var data = trans.split(':');
			trans = Fx.Transitions;
			trans = trans[data[0]] || trans[data[0].capitalize()];
            if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
        }

		return trans;
	}

});

Fx.Transition = function(transition, params){
	params = $splat(params);
	return $extend(transition, {
		easeIn: function(pos){
			return transition(pos, params);
		},
		easeOut: function(pos){
			return 1 - transition(1 - pos, params);
		},
		easeInOut: function(pos){
			return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
		}
	});
};

Fx.Transitions = new Hash({

	linear: $arguments(0)

});

Fx.Transitions.extend = function(transitions){
	for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
};

Fx.Transitions.extend({

	Pow: function(p, x){
		return Math.pow(p, x[0] || 6);
	},

	Expo: function(p){
		return Math.pow(2, 8 * (p - 1));
	},

	Circ: function(p){
		return 1 - Math.sin(Math.acos(p));
	},

	Sine: function(p){
		return 1 - Math.sin((1 - p) * Math.PI / 2);
	},

	Back: function(p, x){
		x = x[0] || 1.618;
		return Math.pow(p, 2) * ((x + 1) * p - x);
	},

	Bounce: function(p){
		var value;
		for (var a = 0, b = 1; 1; a += b, b /= 2){
			if (p >= (7 - 4 * a) / 11){
				value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
				break;
			}
		}
		return value;
	},

	Elastic: function(p, x){
		return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
	}

});

['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
	Fx.Transitions[transition] = new Fx.Transition(function(p){
		return Math.pow(p, [i + 2]);
	});
});
/*
Script: Request.js
	Powerful all purpose Request Class. Uses XMLHTTPRequest.

License:
	MIT-style license.
*/

var Request = new Class({

	Implements: [Chain, Events, Options],

	options: {/*
		onRequest: $empty,
		onComplete: $empty,
		onCancel: $empty,
		onSuccess: $empty,
		onFailure: $empty,
		onException: $empty,*/
		url: '',
		data: '',
		headers: {
			'X-Requested-With': 'XMLHttpRequest',
			'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
		},
		async: true,
		format: false,
		method: 'post',
		link: 'ignore',
		isSuccess: null,
		emulation: true,
		urlEncoded: true,
		encoding: 'utf-8',
		evalScripts: false,
		evalResponse: false
	},

	initialize: function(options){
		this.xhr = new Browser.Request();
		this.setOptions(options);
		this.options.isSuccess = this.options.isSuccess || this.isSuccess;
		this.headers = new Hash(this.options.headers);
	},

	onStateChange: function(){
		if (this.xhr.readyState != 4 || !this.running) return;
		this.running = false;
		this.status = 0;
		$try(function(){
			this.status = this.xhr.status;
		}.bind(this));
		if (this.options.isSuccess.call(this, this.status)){
			this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
			this.success(this.response.text, this.response.xml);
		} else {
			this.response = {text: null, xml: null};
			this.failure();
		}
		this.xhr.onreadystatechange = $empty;
	},

	isSuccess: function(){
		return ((this.status >= 200) && (this.status < 300));
	},

	processScripts: function(text){
		if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
		return text.stripScripts(this.options.evalScripts);
	},

	success: function(text, xml){
		this.onSuccess(this.processScripts(text), xml);
	},

	onSuccess: function(){
		this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
	},

	failure: function(){
		this.onFailure();
	},

	onFailure: function(){
		this.fireEvent('complete').fireEvent('failure', this.xhr);
	},

	setHeader: function(name, value){
		this.headers.set(name, value);
		return this;
	},

	getHeader: function(name){
		return $try(function(){
			return this.xhr.getResponseHeader(name);
		}.bind(this));
	},

	check: function(caller){
		if (!this.running) return true;
		switch (this.options.link){
			case 'cancel': this.cancel(); return true;
			case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return false;
		}
		return false;
	},

	send: function(options){
		if (!this.check(arguments.callee, options)) return this;
		this.running = true;

		var type = $type(options);
		if (type == 'string' || type == 'element') options = {data: options};

		var old = this.options;
		options = $extend({data: old.data, url: old.url, method: old.method}, options);
		var data = options.data, url = options.url, method = options.method;

		switch ($type(data)){
			case 'element': data = $(data).toQueryString(); break;
			case 'object': case 'hash': data = Hash.toQueryString(data);
		}

		if (this.options.format){
			var format = 'format=' + this.options.format;
			data = (data) ? format + '&' + data : format;
		}

		if (this.options.emulation && ['put', 'delete'].contains(method)){
			var _method = '_method=' + method;
			data = (data) ? _method + '&' + data : _method;
			method = 'post';
		}

		if (this.options.urlEncoded && method == 'post'){
			var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
			this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
		}

		if (data && method == 'get'){
			url = url + (url.contains('?') ? '&' : '?') + data;
			data = null;
		}

		this.xhr.open(method.toUpperCase(), url, this.options.async);

		this.xhr.onreadystatechange = this.onStateChange.bind(this);

		this.headers.each(function(value, key){
			try {
				this.xhr.setRequestHeader(key, value);
			} catch (e){
				this.fireEvent('exception', [key, value]);
			}
		}, this);

		this.fireEvent('request');
		this.xhr.send(data);
		if (!this.options.async) this.onStateChange();
		return this;
	},

	cancel: function(){
		if (!this.running) return this;
		this.running = false;
		this.xhr.abort();
		this.xhr.onreadystatechange = $empty;
		this.xhr = new Browser.Request();
		this.fireEvent('cancel');
		return this;
	}

});

(function(){

var methods = {};
['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
	methods[method] = function(){
		var params = Array.link(arguments, {url: String.type, data: $defined});
		return this.send($extend(params, {method: method.toLowerCase()}));
	};
});

Request.implement(methods);

})();

Element.Properties.send = {

	set: function(options){
		var send = this.retrieve('send');
		if (send) send.cancel();
		return this.eliminate('send').store('send:options', $extend({
			data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
		}, options));
	},

	get: function(options){
		if (options || !this.retrieve('send')){
			if (options || !this.retrieve('send:options')) this.set('send', options);
			this.store('send', new Request(this.retrieve('send:options')));
		}
		return this.retrieve('send');
	}

};

Element.implement({

	send: function(url){
		var sender = this.get('send');
		sender.send({data: this, url: url || sender.options.url});
		return this;
	}

});
/*
Script: Request.HTML.js
	Extends the basic Request Class with additional methods for interacting with HTML responses.

License:
	MIT-style license.
*/

Request.HTML = new Class({

	Extends: Request,

	options: {
		update: false,
		evalScripts: true,
		filter: false
	},

	processHTML: function(text){
		var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
		text = (match) ? match[1] : text;

		var container = new Element('div');

		return $try(function(){
			var root = '<root>' + text + '</root>', doc;
			if (Browser.Engine.trident){
				doc = new ActiveXObject('Microsoft.XMLDOM');
				doc.async = false;
				doc.loadXML(root);
			} else {
				doc = new DOMParser().parseFromString(root, 'text/xml');
			}
			root = doc.getElementsByTagName('root')[0];
			for (var i = 0, k = root.childNodes.length; i < k; i++){
				var child = Element.clone(root.childNodes[i], true, true);
				if (child) container.grab(child);
			}
			return container;
		}) || container.set('html', text);
	},

	success: function(text){
		var options = this.options, response = this.response;

		response.html = text.stripScripts(function(script){
			response.javascript = script;
		});

		var temp = this.processHTML(response.html);

		response.tree = temp.childNodes;
		response.elements = temp.getElements('*');

		if (options.filter) response.tree = response.elements.filter(options.filter);
		if (options.update) $(options.update).empty().adopt(response.tree);
		if (options.evalScripts) $exec(response.javascript);

		this.onSuccess(response.tree, response.elements, response.html, response.javascript);
	}

});

Element.Properties.load = {

	set: function(options){
		var load = this.retrieve('load');
		if (load) load.cancel();
		return this.eliminate('load').store('load:options', $extend({data: this, link: 'cancel', update: this, method: 'get'}, options));
	},

	get: function(options){
		if (options || ! this.retrieve('load')){
			if (options || !this.retrieve('load:options')) this.set('load', options);
			this.store('load', new Request.HTML(this.retrieve('load:options')));
		}
		return this.retrieve('load');
	}

};

Element.implement({

	load: function(){
		this.get('load').send(Array.link(arguments, {data: Object.type, url: String.type}));
		return this;
	}

});
/*
Script: Request.JSON.js
	Extends the basic Request Class with additional methods for sending and receiving JSON data.

License:
	MIT-style license.
*/

Request.JSON = new Class({

	Extends: Request,

	options: {
		secure: true
	},

	initialize: function(options){
		this.parent(options);
		this.headers.extend({'Accept': 'application/json', 'X-Request': 'JSON'});
	},

	success: function(text){
		this.response.json = JSON.decode(text, this.options.secure);
		this.onSuccess(this.response.json, text);
	}

});/*
Script: Fx.Slide.js
	Effect to slide an element in and out of view.

License:
	MIT-style license.
*/

Fx.Slide = new Class({

	Extends: Fx,

	options: {
		mode: 'vertical'
	},

	initialize: function(element, options){
		this.addEvent('complete', function(){
			this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);
			if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper);
		}, true);
		this.element = this.subject = $(element);
		this.parent(options);
		var wrapper = this.element.retrieve('wrapper');
		this.wrapper = wrapper || new Element('div', {
			styles: $extend(this.element.getStyles('margin', 'position'), {'overflow': 'hidden'})
		}).wraps(this.element);
		this.element.store('wrapper', this.wrapper).setStyle('margin', 0);
		this.now = [];
		this.open = true;
	},

	vertical: function(){
		this.margin = 'margin-top';
		this.layout = 'height';
		this.offset = this.element.offsetHeight;
	},

	horizontal: function(){
		this.margin = 'margin-left';
		this.layout = 'width';
		this.offset = this.element.offsetWidth;
	},

	set: function(now){
		this.element.setStyle(this.margin, now[0]);
		this.wrapper.setStyle(this.layout, now[1]);
		return this;
	},

	compute: function(from, to, delta){
		var now = [];
		var x = 2;
		x.times(function(i){
			now[i] = Fx.compute(from[i], to[i], delta);
		});
		return now;
	},

	start: function(how, mode){
		if (!this.check(arguments.callee, how, mode)) return this;
		this[mode || this.options.mode]();
		var margin = this.element.getStyle(this.margin).toInt();
		var layout = this.wrapper.getStyle(this.layout).toInt();
		var caseIn = [[margin, layout], [0, this.offset]];
		var caseOut = [[margin, layout], [-this.offset, 0]];
		var start;
		switch (how){
			case 'in': start = caseIn; break;
			case 'out': start = caseOut; break;
			case 'toggle': start = (this.wrapper['offset' + this.layout.capitalize()] == 0) ? caseIn : caseOut;
		}
		return this.parent(start[0], start[1]);
	},

	slideIn: function(mode){
		return this.start('in', mode);
	},

	slideOut: function(mode){
		return this.start('out', mode);
	},

	hide: function(mode){
		this[mode || this.options.mode]();
		this.open = false;
		return this.set([-this.offset, 0]);
	},

	show: function(mode){
		this[mode || this.options.mode]();
		this.open = true;
		return this.set([0, this.offset]);
	},

	toggle: function(mode){
		return this.start('toggle', mode);
	}

});

Element.Properties.slide = {

	set: function(options){
		var slide = this.retrieve('slide');
		if (slide) slide.cancel();
		return this.eliminate('slide').store('slide:options', $extend({link: 'cancel'}, options));
	},

	get: function(options){
		if (options || !this.retrieve('slide')){
			if (options || !this.retrieve('slide:options')) this.set('slide', options);
			this.store('slide', new Fx.Slide(this, this.retrieve('slide:options')));
		}
		return this.retrieve('slide');
	}

};

Element.implement({

	slide: function(how, mode){
		how = how || 'toggle';
		var slide = this.get('slide'), toggle;
		switch (how){
			case 'hide': slide.hide(mode); break;
			case 'show': slide.show(mode); break;
			case 'toggle':
				var flag = this.retrieve('slide:flag', slide.open);
				slide[(flag) ? 'slideOut' : 'slideIn'](mode);
				this.store('slide:flag', !flag);
				toggle = true;
			break;
			default: slide.start(how, mode);
		}
		if (!toggle) this.eliminate('slide:flag');
		return this;
	}

});
/*
Script: Fx.Scroll.js
	Effect to smoothly scroll any element, including the window.

License:
	MIT-style license.
*/

Fx.Scroll = new Class({

	Extends: Fx,

	options: {
		offset: {'x': 0, 'y': 0},
		wheelStops: true
	},

	initialize: function(element, options){
		this.element = this.subject = $(element);
		this.parent(options);
		var cancel = this.cancel.bind(this, false);

		if ($type(this.element) != 'element') this.element = $(this.element.getDocument().body);

		var stopper = this.element;

		if (this.options.wheelStops){
			this.addEvent('start', function(){
				stopper.addEvent('mousewheel', cancel);
			}, true);
			this.addEvent('complete', function(){
				stopper.removeEvent('mousewheel', cancel);
			}, true);
		}
	},

	set: function(){
		var now = Array.flatten(arguments);
		this.element.scrollTo(now[0], now[1]);
	},

	compute: function(from, to, delta){
		var now = [];
		var x = 2;
		x.times(function(i){
			now.push(Fx.compute(from[i], to[i], delta));
		});
		return now;
	},

	start: function(x, y){
		if (!this.check(arguments.callee, x, y)) return this;
		var offsetSize = this.element.getSize(), scrollSize = this.element.getScrollSize();
		var scroll = this.element.getScroll(), values = {x: x, y: y};
		for (var z in values){
			var max = scrollSize[z] - offsetSize[z];
			if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z].limit(0, max) : max;
			else values[z] = scroll[z];
			values[z] += this.options.offset[z];
		}
		return this.parent([scroll.x, scroll.y], [values.x, values.y]);
	},

	toTop: function(){
		return this.start(false, 0);
	},

	toLeft: function(){
		return this.start(0, false);
	},

	toRight: function(){
		return this.start('right', false);
	},

	toBottom: function(){
		return this.start(false, 'bottom');
	},

	toElement: function(el){
		var position = $(el).getPosition(this.element);
		return this.start(position.x, position.y);
	}

});
/*
Script: Fx.Elements.js
	Effect to change any number of CSS properties of any number of Elements.

License:
	MIT-style license.
*/

Fx.Elements = new Class({

	Extends: Fx.CSS,

	initialize: function(elements, options){
		this.elements = this.subject = $$(elements);
		this.parent(options);
	},

	compute: function(from, to, delta){
		var now = {};
		for (var i in from){
			var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
			for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
		}
		return now;
	},

	set: function(now) {
	    if ($type(now) == 'array') {
	        now = now.associate(now.map(function(foo,i) { return i }));
	    }
	    for (var i in now) {
	        var iNow = $type(now[i]) == 'string' && this.search(now[i]) || now[i];
	        for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
	    }
	    return this;
	},

	start: function(obj) {		
	    if (!this.check(arguments.callee/*, obj - why?*/)) return this;
	    if ($type(obj) == 'array') {
	        obj = obj.associate(obj.map(function(foo,i) { return i }));
	    }
	    var from = {}, to = {};
	    for (var i in obj) {
	        var iProps = $type(obj[i]) == 'string' && this.search(obj[i]) || obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
	        for (var p in iProps) {
	            var parsed = this.prepare(this.elements[i], p, iProps[p]);
	            iFrom[p] = parsed.from;
	            iTo[p] = parsed.to;
	        }
	    }
	    return this.parent(from, to);
	}

});/*
Script: Drag.js
	The base Drag Class. Can be used to drag and resize Elements using mouse events.

License:
	MIT-style license.
*/

var Drag = new Class({

	Implements: [Events, Options],

	options: {/*
		onBeforeStart: $empty,
		onStart: $empty,
		onDrag: $empty,
		onCancel: $empty,
		onComplete: $empty,*/
		snap: 6,
		unit: 'px',
		grid: false,
		style: true,
		limit: false,
		handle: false,
		invert: false,
		preventDefault: false,
		stopEvent: false,
		eventElement: null,
		modifiers: {x: 'left', y: 'top'}
	},

	initialize: function(){
		var params = Array.link(arguments, {'options': Object.type, 'element': $defined});
		this.element = $(params.element);
		this.options.eventElement = this.options.eventElement || this.element.getDocument();
		this.setOptions(params.options || {});
		var htype = $type(this.options.handle);
		this.handles = (htype == 'array' || htype == 'collection') ? $$(this.options.handle) : $(this.options.handle) || this.element;
		this.mouse = {'now': {}, 'pos': {}};
		this.value = {'start': {}, 'now': {}};
		
		this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';
		
		this.bound = {
			start: this.start.bind(this),
			check: this.check.bind(this),
			drag: this.drag.bind(this),
			stop: this.stop.bind(this),
			cancel: this.cancel.bind(this),
			eventStop: $lambda(false)
		};
		this.attach();
	},

	attach: function(){
		if (this.attached) return this;
		this.attached = true;
		this.handles.addEvent('mousedown', this.bound.start);
		return this;
	},

	detach: function(){
		if (!this.attached) return this;
		this.attached = false;
		this.handles.removeEvent('mousedown', this.bound.start);
		return this;
	},

	start: function(event){
		if (this.options.preventDefault) event.preventDefault();
		if (this.options.stopEvent) event.stop();
		this.mouse.start = event.page;
		this.fireEvent('beforeStart', this.element);		
		var limit = this.options.limit;
		this.limit = {'x': [], 'y': []};
		for (var z in this.options.modifiers){
			if (!this.options.modifiers[z]) continue;
			if (this.options.style) this.value.now[z] = this.element.getComputedStyle(this.options.modifiers[z]).toInt();
			else this.value.now[z] = this.element[this.options.modifiers[z]];
			if (this.options.invert) this.value.now[z] *= -1;
			this.mouse.pos[z] = event.page[z] - this.value.now[z];
			if (limit && limit[z]){
				for (var i = 2; i--; i){
					if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
				}
			}
		}
		//document.title = JSON.encode(this.value.now);
		if ($type(this.options.grid) == 'number') this.options.grid = {'x': this.options.grid, 'y': this.options.grid};
		$(this.options.eventElement).addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
		this.options.eventElement.addEvent(this.selection, this.bound.eventStop);
	},

	check: function(event){
		if (this.options.preventDefault) event.preventDefault();
		if (this.options.stopEvent) event.stop();
		var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
		if (distance > this.options.snap){
			this.cancel();
			this.options.eventElement.addEvents({
				mousemove: this.bound.drag,
				mouseup: this.bound.stop
			});
			this.fireEvent('start', this.element).fireEvent('snap', this.element);
		}
	},

	drag: function(event){
		if (this.options.preventDefault) event.preventDefault();		
		this.mouse.now = event.page;
		for (var z in this.options.modifiers){
			if (!this.options.modifiers[z]) continue;
			this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
			//document.title = JSON.encode([this.mouse.now, this.mouse.pos]);
			if (this.options.invert) this.value.now[z] *= -1;
			if (this.options.limit && this.limit[z]){
				if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){
					this.value.now[z] = this.limit[z][1];
				} else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){
					this.value.now[z] = this.limit[z][0];
				}
			}
			if (this.options.grid[z]) this.value.now[z] -= (this.value.now[z] % this.options.grid[z]);
			if (this.options.style) this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
			else this.element[this.options.modifiers[z]] = this.value.now[z];
		}
		this.fireEvent('drag', this.element);
	},

	cancel: function(event){
		if (event && this.options.stopEvent) event.stop();
		this.options.eventElement.removeEvent('mousemove', this.bound.check);
		this.options.eventElement.removeEvent('mouseup', this.bound.cancel);
		if (event){
			this.options.eventElement.removeEvent(this.selection, this.bound.eventStop);
			this.fireEvent('cancel', this.element);
		}
	},

	stop: function(event){
		if (event && this.options.stopEvent) event.stop();
		this.options.eventElement.removeEvent(this.selection, this.bound.eventStop);
		this.options.eventElement.removeEvent('mousemove', this.bound.drag);
		this.options.eventElement.removeEvent('mouseup', this.bound.stop);
		if (event) this.fireEvent('complete', this.element);
	}

});

Element.implement({

	makeResizable: function(options){
		return new Drag(this, $merge({modifiers: {'x': 'width', 'y': 'height'}}, options));
	}

});
/*
Script: Drag.Move.js
	A Drag extension that provides support for the constraining of draggables to containers and droppables.

License:
	MIT-style license.
*/

Drag.Move = new Class({

	Extends: Drag,

	options: {/*
		onEnter: $empty,
		onLeave: $empty,
		onDrop: $empty,*/
		droppables: [],
		container: false
	},

	initialize: function(element, options){
		this.parent(element, options);
		this.droppables = $$(this.options.droppables);
		this.container = $(this.options.container);
		if (this.container && $type(this.container) != 'element') this.container = $(this.container.getDocument().body);
		element = this.element;

		var current = element.getStyle('position');
		var position = (current != 'static') ? current : 'absolute';
		// position styles must be pixies !!!
		//if (element.getStyle('left') == 'auto' || element.getStyle('top') == 'auto') 
		element.position(element.getPosition(element.offsetParent));
		element.setStyle('position', position);

		this.addEvent('start', this.checkDroppables, true);

		this.overed = null;
	},

	start: function(event){
		if (this.container){
			var el = this.element, cont = this.container, ccoo = cont.getCoordinates(el.offsetParent), cps = {}, ems = {};

			['top', 'right', 'bottom', 'left'].each(function(pad){
				cps[pad] = cont.getStyle('padding-' + pad).toInt();
				ems[pad] = el.getStyle('margin-' + pad).toInt();
			}, this);

			var width = el.offsetWidth + ems.left + ems.right, height = el.offsetHeight + ems.top + ems.bottom;
			var x = [ccoo.left + cps.left, ccoo.right - cps.right - width];
			var y = [ccoo.top + cps.top, ccoo.bottom - cps.bottom - height];

			this.options.limit = {x: x, y: y};
		}
		this.parent(event);
	},

	checkAgainst: function(el){
		el = el.getCoordinates();
		var now = this.mouse.now;
		return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
	},

	checkDroppables: function(){
		var overed = this.droppables.filter(this.checkAgainst, this).getLast();
		if (this.overed != overed){
			if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
			if (overed) this.fireEvent('enter', [this.element, overed]);
			this.overed = overed;
		}
	},

	drag: function(event){
		this.parent(event);
		if (this.droppables.length) this.checkDroppables();
	},

	stop: function(event){
		this.checkDroppables();
		this.fireEvent('drop', [this.element, this.overed]);
		this.overed = null;
		return this.parent(event);
	}

});

Element.implement({

	makeDraggable: function(options){
		return new Drag.Move(this, options);
	}

});
/*
Script: Hash.Cookie.js
	Class for creating, reading, and deleting Cookies in JSON format.

License:
	MIT-style license.
*/

Hash.Cookie = new Class({

	Extends: Cookie,

	options: {
		autoSave: true
	},

	initialize: function(name, options){
		this.parent(name, options);
		this.load();
	},

	save: function(){
		var value = JSON.encode(this.hash);
		if (!value || value.length > 4096) return false; //cookie would be truncated!
		if (value == '{}') this.dispose();
		else this.write(value);
		return true;
	},

	load: function(){
		this.hash = new Hash(JSON.decode(this.read(), true));
		return this;
	}

});

Hash.Cookie.implement((function(){

	var methods = {};

	Hash.each(Hash.prototype, function(method, name){
		methods[name] = function(){
			var value = method.apply(this.hash, arguments);
			if (this.options.autoSave) this.save();
			return value;
		};
	});

	return methods;

})());
/*
Script: Color.js
	Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.

License:
	MIT-style license.
*/

var Color = new Native({

	initialize: function(color, type){
		if (arguments.length >= 3){
			type = "rgb"; color = Array.slice(arguments, 0, 3);
		} else if (typeof color == 'string'){
			if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true);
			else if (color.match(/hsb/)) color = color.hsbToRgb();
			else color = color.hexToRgb(true);
		}
		type = type || 'rgb';
		switch (type){
			case 'hsb':
				var old = color;
				color = color.hsbToRgb();
				color.hsb = old;
			break;
			case 'hex': color = color.hexToRgb(true); break;
		}
		color.rgb = color.slice(0, 3);
		color.hsb = color.hsb || color.rgbToHsb();
		color.hex = color.rgbToHex();
		return $extend(color, this);
	}

});

Color.implement({

	mix: function(){
		var colors = Array.slice(arguments);
		var alpha = ($type(colors.getLast()) == 'number') ? colors.pop() : 50;
		var rgb = this.slice();
		colors.each(function(color){
			color = new Color(color);
			for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha));
		});
		return new Color(rgb, 'rgb');
	},

	invert: function(){
		return new Color(this.map(function(value){
			return 255 - value;
		}));
	},

	setHue: function(value){
		return new Color([value, this.hsb[1], this.hsb[2]], 'hsb');
	},

	setSaturation: function(percent){
		return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb');
	},

	setBrightness: function(percent){
		return new Color([this.hsb[0], this.hsb[1], percent], 'hsb');
	}

});

function $RGB(r, g, b){
	return new Color([r, g, b], 'rgb');
};

function $HSB(h, s, b){
	return new Color([h, s, b], 'hsb');
};

function $HEX(hex){
	return new Color(hex, 'hex');
};

Array.implement({

	rgbToHsb: function(){
		var red = this[0], green = this[1], blue = this[2];
		var hue, saturation, brightness;
		var max = Math.max(red, green, blue), min = Math.min(red, green, blue);
		var delta = max - min;
		brightness = max / 255;
		saturation = (max != 0) ? delta / max : 0;
		if (saturation == 0){
			hue = 0;
		} else {
			var rr = (max - red) / delta;
			var gr = (max - green) / delta;
			var br = (max - blue) / delta;
			if (red == max) hue = br - gr;
			else if (green == max) hue = 2 + rr - br;
			else hue = 4 + gr - rr;
			hue /= 6;
			if (hue < 0) hue++;
		}
		return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)];
	},

	hsbToRgb: function(){
		var br = Math.round(this[2] / 100 * 255);
		if (this[1] == 0){
			return [br, br, br];
		} else {
			var hue = this[0] % 360;
			var f = hue % 60;
			var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
			var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255);
			var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255);
			switch (Math.floor(hue / 60)){
				case 0: return [br, t, p];
				case 1: return [q, br, p];
				case 2: return [p, br, t];
				case 3: return [p, q, br];
				case 4: return [t, p, br];
				case 5: return [br, p, q];
			}
		}
		return false;
	}

});

String.implement({

	rgbToHsb: function(){
		var rgb = this.match(/\d{1,3}/g);
		return (rgb) ? rgb.rgbToHsb() : null;
	},

	hsbToRgb: function(){
		var hsb = this.match(/\d{1,3}/g);
		return (hsb) ? hsb.hsbToRgb() : null;
	}

});
/*
Script: Group.js
	Class for monitoring collections of events

License:
	MIT-style license.
*/

var Group = new Class({

	initialize: function(){
		this.instances = Array.flatten(arguments);
		this.events = {};
		this.checker = {};
	},

	addEvent: function(type, fn){
		this.checker[type] = this.checker[type] || {};
		this.events[type] = this.events[type] || [];
		if (this.events[type].contains(fn)) return false;
		else this.events[type].push(fn);
		this.instances.each(function(instance, i){
			instance.addEvent(type, this.check.bind(this, [type, instance, i]));
		}, this);
		return this;
	},

	check: function(type, instance, i){
		this.checker[type][i] = true;
		var every = this.instances.every(function(current, j){
			return this.checker[type][j] || false;
		}, this);
		if (!every) return;
		this.checker[type] = {};
		this.events[type].each(function(event){
			event.call(this, this.instances, instance);
		}, this);
	}

});
/*
Script: Assets.js
	Provides methods to dynamically load JavaScript, CSS, and Image files into the document.

License:
	MIT-style license.
*/

var Asset = new Hash({

	javascript: function(source, properties){
		properties = $extend({
			onload: $empty,
			document: document,
			check: $lambda(true)
		}, properties);

		var script = new Element('script', {'src': source, 'type': 'text/javascript'});

		var load = properties.onload.bind(script), check = properties.check, doc = properties.document;
		delete properties.onload; delete properties.check; delete properties.document;

		script.addEvents({
			load: load,
			readystatechange: function(){
				if (['loaded', 'complete'].contains(this.readyState)) load();
			}
		}).setProperties(properties);


		if (Browser.Engine.webkit419) var checker = (function(){
			if (!$try(check)) return;
			$clear(checker);
			load();
		}).periodical(50);

		return script.inject(doc.head);
	},

	css: function(source, properties){
		return new Element('link', $merge({
			'rel': 'stylesheet', 'media': 'screen', 'type': 'text/css', 'href': source
		}, properties)).inject(document.head);
	},

	image: function(source, properties){
		properties = $merge({
			'onload': $empty,
			'onabort': $empty,
			'onerror': $empty
		}, properties);
		var image = new Image();
		var element = $(image) || new Element('img');
		['load', 'abort', 'error'].each(function(name){
			var type = 'on' + name;
			var event = properties[type];
			delete properties[type];
			image[type] = function(){
				if (!image) return;
				if (!element.parentNode){
					element.width = image.width;
					element.height = image.height;
				}
				image = image.onload = image.onabort = image.onerror = null;
				event.delay(1, element, element);
				element.fireEvent(name, element, 1);
			};
		});
		image.src = element.src = source;
		if (image && image.complete) image.onload.delay(1);
		return element.setProperties(properties);
	},

	images: function(sources, options){
		options = $merge({
			onComplete: $empty,
			onProgress: $empty
		}, options);
		if (!sources.push) sources = [sources];
		var images = [];
		var counter = 0;
		sources.each(function(source){
			var img = new Asset.image(source, {
				'onload': function(){
					options.onProgress.call(this, counter, sources.indexOf(source));
					counter++;
					if (counter == sources.length) options.onComplete();
				}
			});
			images.push(img);
		});
		return new Elements(images);
	}

});/*
Script: Sortables.js
	Class for creating a drag and drop sorting interface for lists of items.

License:
	MIT-style license.
*/

var Sortables = new Class({

	Implements: [Events, Options],

	options: {/*
		onSort: $empty,
		onStart: $empty,
		onComplete: $empty,*/
		snap: 4,
		opacity: 1,
		clone: false,
		revert: false,
		handle: false,
		constrain: false,
		document: document,
		position: 'absolute'
	},

	initialize: function(lists, options){
		this.setOptions(options);
		this.elements = [];
		this.lists = [];
		this.idle = true;

		this.addLists($$($(lists) || lists));
		if (!this.options.clone) this.options.revert = false;
		if (this.options.revert) this.effect = new Fx.Morph(null, $merge({duration: 250, link: 'cancel'}, this.options.revert));
	},

	attach: function(){
		this.addLists(this.lists);
		return this;
	},

	detach: function(){
		this.lists = this.removeLists(this.lists);
		return this;
	},

	addItems: function(){
		Array.flatten(arguments).each(function(element){
			this.elements.push(element);
			var start = element.retrieve('sortables:start', this.start.bindWithEvent(this, element));
			(this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
		}, this);
		return this;
	},

	addLists: function(){
		Array.flatten(arguments).each(function(list){
			this.lists.push(list);
			this.addItems(list.getChildren());
		}, this);
		return this;
	},

	removeItems: function(){
		var elements = [];
		Array.flatten(arguments).each(function(element){
			elements.push(element);
			this.elements.erase(element);
			var start = element.retrieve('sortables:start');
			(this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
		}, this);
		return $$(elements);
	},

	removeLists: function(){
		var lists = [];
		Array.flatten(arguments).each(function(list){
			lists.push(list);
			this.lists.erase(list);
			this.removeItems(list.getChildren());
		}, this);
		return $$(lists);
	},

	getClone: function(event, element){
		var doc = this.options.document;
		if (!this.options.clone) return doc.newElement('div').inject(doc.body);
		if ($type(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
		return element.clone(true).setStyles({
			'margin': '0px',
			'position': this.options.position,
			'visibility': 'hidden',
			'width': element.offsetWidth
		}).inject(this.list).position(element.getPosition(element.getOffsetParent()));
	},

	getDroppables: function(){
		var droppables = this.list.getChildren();
		if (!this.options.constrain) droppables = this.lists.concat(droppables).erase(this.list);
		return droppables.erase(this.clone).erase(this.element);
	},

	insert: function(dragging, element){
		var where = 'inside';
		if (this.lists.contains(element)){
			this.list = element;
			this.drag.droppables = this.getDroppables();
		} else {
			where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
		}
		this.element.inject(element, where);
		this.fireEvent('sort', [this.element, this.clone]);
	},

	start: function(event, element){
		if (!this.idle) return;
		this.idle = false;
		this.element = element;
		this.opacity = element.get('opacity');
		this.list = element.getParent();
		this.clone = this.getClone(event, element);
		this.drag = new Drag.Move(this.clone, {
			snap: this.options.snap,
			container: this.options.constrain && this.element.getParent(),
			droppables: this.getDroppables(),
			onSnap: function(){
				event.stop();
				this.clone.setStyle('visibility', 'visible');
				this.element.set('opacity', this.options.opacity || 0);
				this.fireEvent('start', [this.element, this.clone]);
			}.bind(this),
			onEnter: this.insert.bind(this),
			onCancel: this.reset.bind(this),
			onComplete: this.end.bind(this)
		});
		this.clone.inject(this.element, 'before');
		this.drag.start(event);
	},

	end: function(){
		this.drag.detach();
		this.element.set('opacity', this.opacity);
		if (this.effect){
			var dim = {
				width: this.element.offsetWidth,
				height: this.element.offsetHeight				
			};
			var pos = this.clone.computePosition(this.element.getPosition(this.clone.offsetParent));
			this.effect.element = this.clone;
			var params = $H({
				top: pos.top,
				left: pos.left,
				width: dim.width,
				height: dim.height,
				opacity: 0.25
			}).filter(function(v, k) { return v != 'auto'; }).getClean();
			for (var k in params)
				this.effect.start(params).chain(this.reset.bind(this));
		} else {
			this.reset();
		}
	},

	reset: function(){
		this.idle = true;
		this.clone.destroy();
		this.fireEvent('complete', this.element);
	},

	serialize: function(){
		var params = Array.link(arguments, {modifier: Function.type, index: $defined});
		var serial = this.lists.map(function(list){
			return list.getChildren().map(params.modifier || function(element){
				return element.get('id');
			}, this);
		}, this);

		var index = params.index;
		if (this.lists.length == 1) index = 0;
		return $chk(index) && index >= 0 && index < this.lists.length ? serial[index] : serial;
	}

});
/*
Script: Tips.js
	Class for creating nice tips that follow the mouse cursor when hovering an element.

License:
	MIT-style license.
*/

var Tips = new Class({

	Implements: [Events, Options],

	options: {
		onShow: function(tip){
			tip.setStyle('visibility', 'visible');
		},
		onHide: function(tip){
			tip.setStyle('visibility', 'hidden');
		},
		showDelay: 100,
		hideDelay: 100,
		className: null,
		offsets: {x: 16, y: 16},
		fixed: false
	},

	initialize: function(){
		var params = Array.link(arguments, {options: Object.type, elements: $defined});
		this.setOptions(params.options || null);

		this.tip = new Element('div').inject(document.body);

		if (this.options.className) this.tip.addClass(this.options.className);

		var top = new Element('div', {'class': 'tip-top'}).inject(this.tip);
		this.container = new Element('div', {'class': 'tip'}).inject(this.tip);
		var bottom = new Element('div', {'class': 'tip-bottom'}).inject(this.tip);

		this.tip.setStyles({position: 'absolute', top: 0, left: 0, visibility: 'hidden'});

		if (params.elements) this.attach(params.elements);
	},

	attach: function(elements){
		$$(elements).each(function(element){
			var title = element.retrieve('tip:title', element.get('title'));
			var text = element.retrieve('tip:text', element.get('rel') || element.get('href'));
			var enter = element.retrieve('tip:enter', this.elementEnter.bindWithEvent(this, element));
			var leave = element.retrieve('tip:leave', this.elementLeave.bindWithEvent(this, element));
			element.addEvents({mouseenter: enter, mouseleave: leave});
			if (!this.options.fixed){
				var move = element.retrieve('tip:move', this.elementMove.bindWithEvent(this, element));
				element.addEvent('mousemove', move);
			}
			element.store('tip:native', element.get('title'));
			element.erase('title');
		}, this);
		return this;
	},

	detach: function(elements){
		$$(elements).each(function(element){
			element.removeEvent('mouseenter', element.retrieve('tip:enter') || $empty);
			element.removeEvent('mouseleave', element.retrieve('tip:leave') || $empty);
			element.removeEvent('mousemove', element.retrieve('tip:move') || $empty);
			element.eliminate('tip:enter').eliminate('tip:leave').eliminate('tip:move');
			var original = element.retrieve('tip:native');
			if (original) element.set('title', original);
		});
		return this;
	},

	elementEnter: function(event, element){

		$A(this.container.childNodes).each(Element.dispose);

		var title = element.retrieve('tip:title');

		if (title){
			this.titleElement = new Element('div', {'class': 'tip-title'}).inject(this.container);
			this.fill(this.titleElement, title);
		}

		var text = element.retrieve('tip:text');
		if (text){
			this.textElement = new Element('div', {'class': 'tip-text'}).inject(this.container);
			this.fill(this.textElement, text);
		}

		this.timer = $clear(this.timer);
		this.timer = this.show.delay(this.options.showDelay, this);

		this.position((!this.options.fixed) ? event : {page: element.getPosition()});
	},

	elementLeave: function(event){
		$clear(this.timer);
		this.timer = this.hide.delay(this.options.hideDelay, this);
	},

	elementMove: function(event){
		this.position(event);
	},

	position: function(event){
		var size = window.getSize(), scroll = window.getScroll();
		var tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight};
		var props = {x: 'left', y: 'top'};
		for (var z in props){
			var pos = event.page[z] + this.options.offsets[z];
			if ((pos + tip[z] - scroll[z]) > size[z]) pos = event.page[z] - this.options.offsets[z] - tip[z];
			this.tip.setStyle(props[z], pos);
		}
	},

	fill: function(element, contents){
		(typeof contents == 'string') ? element.set('html', contents) : element.adopt(contents);
	},

	show: function(){
		this.fireEvent('show', this.tip);
	},

	hide: function(){
		this.fireEvent('hide', this.tip);
	}

});/*
Script: SmoothScroll.js
	Class for creating a smooth scrolling effect to all internal links on the page.

License:
	MIT-style license.
*/

var SmoothScroll = new Class({

	Extends: Fx.Scroll,

	initialize: function(options, context){
		context = context || document;
		var doc = context.getDocument(), win = context.getWindow();
		this.parent(doc, options);
		this.links = (this.options.links) ? $$(this.options.links) : $$(doc.links);
		var location = win.location.href.match(/^[^#]*/)[0] + '#';
		this.links.each(function(link){
			if (link.href.indexOf(location) != 0) return;
			var anchor = link.href.substr(location.length);
			if (anchor && $(anchor)) this.useLink(link, anchor);
		}, this);
		if (!Browser.Engine.webkit419) this.addEvent('complete', function(){
			win.location.hash = this.anchor;
		}, true);
	},

	useLink: function(link, anchor){
		link.addEvent('click', function(event){
			this.anchor = anchor;
			this.toElement(anchor);
			event.stop();
		}.bind(this));
	}

});/*
Script: Slider.js
	Class for creating horizontal and vertical slider controls.

License:
	MIT-style license.
*/

var Slider = new Class({

	Implements: [Events, Options],

	options: {/*
		onChange: $empty,
		onComplete: $empty,*/
		onTick: function(position){
			if (this.options.snap) position = this.toPosition(this.step);
			this.knob.setStyle(this.property, position);
		},
		snap: false,
		offset: 0,
		range: false,
		wheel: false,
		steps: 100,
		mode: 'horizontal'
	},

	initialize: function(element, knob, options){
		this.setOptions(options);
		this.element = $(element);
		this.knob = $(knob);
		this.previousChange = this.previousEnd = this.step = -1;
		this.element.addEvent('mousedown', this.clickedElement.bind(this));
		if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement.bindWithEvent(this));
		var offset, limit = {}, modifiers = {'x': false, 'y': false};
		switch (this.options.mode){
			case 'vertical':
				this.axis = 'y';
				this.property = 'top';
				offset = 'offsetHeight';
				break;
			case 'horizontal':
				this.axis = 'x';
				this.property = 'left';
				offset = 'offsetWidth';
		}
		this.half = this.knob[offset] / 2;
		this.full = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
		this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
		this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
		this.range = this.max - this.min;
		this.steps = this.options.steps || this.full;
		this.stepSize = Math.abs(this.range) / this.steps;
		this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;

		this.knob.setStyle('position', 'relative').setStyle(this.property, - this.options.offset);
		modifiers[this.axis] = this.property;
		limit[this.axis] = [- this.options.offset, this.full - this.options.offset];
		this.drag = new Drag(this.knob, {
			snap: 0,
			limit: limit,
			modifiers: modifiers,
			onDrag: this.draggedKnob.bind(this),
			onStart: this.draggedKnob.bind(this),
			onComplete: function(){
				this.draggedKnob();
				this.end();
			}.bind(this)
		});
		if (this.options.snap) {
			this.drag.options.grid = Math.ceil(this.stepWidth);
			this.drag.options.limit[this.axis][1] = this.full;
		}
	},

	set: function(step){
		if (!((this.range > 0) ^ (step < this.min))) step = this.min;
		if (!((this.range > 0) ^ (step > this.max))) step = this.max;

		this.step = Math.round(step);
		this.checkStep();
		this.end();
		this.fireEvent('tick', this.toPosition(this.step));
		return this;
	},

	clickedElement: function(event){
		var dir = this.range < 0 ? -1 : 1;
		var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
		position = position.limit(-this.options.offset, this.full -this.options.offset);

		this.step = Math.round(this.min + dir * this.toStep(position));
		this.checkStep();
		this.end();
		this.fireEvent('tick', position);
	},

	scrolledElement: function(event){
		var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
		this.set(!mode ? this.step - this.stepSize : this.step + this.stepSize);
		event.stop();
	},

	draggedKnob: function(){
		var dir = this.range < 0 ? -1 : 1;
		var position = this.drag.value.now[this.axis];
		position = position.limit(-this.options.offset, this.full -this.options.offset);
		this.step = Math.round(this.min + dir * this.toStep(position));
		this.checkStep();
	},

	checkStep: function(){
		if (this.previousChange != this.step){
			this.previousChange = this.step;
			this.fireEvent('change', this.step);
		}
	},

	end: function(){
		if (this.previousEnd !== this.step){
			this.previousEnd = this.step;
			this.fireEvent('complete', this.step + '');
		}
	},

	toStep: function(position){
		var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
		return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
	},

	toPosition: function(step){
		return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
	}

});/*
Script: Scroller.js
	Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries.

License:
	MIT-style license.
*/

var Scroller = new Class({

	Implements: [Events, Options],

	options: {
		area: 20,
		velocity: 1,
		onChange: function(x, y){
			this.element.scrollTo(x, y);
		}
	},

	initialize: function(element, options){
		this.setOptions(options);
		this.element = $(element);
		this.listener = ($type(this.element) != 'element') ? $(this.element.getDocument().body) : this.element;
		this.timer = null;
		this.coord = this.getCoords.bind(this);
	},

	start: function(){
		this.listener.addEvent('mousemove', this.coord);
	},

	stop: function(){
		this.listener.removeEvent('mousemove', this.coord);
		this.timer = $clear(this.timer);
	},

	getCoords: function(event){
		this.page = (this.listener.get('tag') == 'body') ? event.client : event.page;
		if (!this.timer) this.timer = this.scroll.periodical(50, this);
	},

	scroll: function(){
		var size = this.element.getSize(), scroll = this.element.getScroll(), pos = this.element.getPosition(), change = {'x': 0, 'y': 0};
		for (var z in this.page){
			if (this.page[z] < (this.options.area + pos[z]) && scroll[z] != 0)
				change[z] = (this.page[z] - this.options.area - pos[z]) * this.options.velocity;
			else if (this.page[z] + this.options.area > (size[z] + pos[z]) && size[z] + size[z] != scroll[z])
				change[z] = (this.page[z] - size[z] + this.options.area - pos[z]) * this.options.velocity;
		}
		if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x, scroll.y + change.y]);
	}

});/*
Script: Accordion.js
	An Fx.Elements extension which allows you to easily create accordion type controls.

License:
	MIT-style license.
*/

var Accordion = new Class({

	Extends: Fx.Elements,

	options: {/*
		onActive: $empty,
		onBackground: $empty,*/
		display: 0,
		show: false,
		height: true,
		width: false,
		opacity: true,
		fixedHeight: false,
		fixedWidth: false,
		wait: false,
		alwaysHide: false
	},

	initialize: function(){
		var params = Array.link(arguments, {'container': Element.type, 'options': Object.type, 'togglers': $defined, 'elements': $defined});
		this.parent(params.elements, params.options);
		this.togglers = $$(params.togglers);
		this.container = $(params.container);
		this.previous = -1;
		if (this.options.alwaysHide) this.options.wait = true;
		if ($chk(this.options.show)){
			this.options.display = false;
			this.previous = this.options.show;
		}
		if (this.options.start){
			this.options.display = false;
			this.options.show = false;
		}
		this.effects = {};
		if (this.options.opacity) this.effects.opacity = 'fullOpacity';
		if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';
		if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';
		for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);
		this.elements.each(function(el, i){
			if (this.options.show === i){
				this.fireEvent('active', [this.togglers[i], el]);
			} else {
				for (var fx in this.effects) el.setStyle(fx, 0);
			}
		}, this);
		if ($chk(this.options.display)) this.display(this.options.display);
	},

	addSection: function(toggler, element, pos){
		toggler = $(toggler);
		element = $(element);
		var test = this.togglers.contains(toggler);
		var len = this.togglers.length;
		this.togglers.include(toggler);
		this.elements.include(element);
		if (len && (!test || pos)){
			pos = $pick(pos, len - 1);
			toggler.inject(this.togglers[pos], 'before');
			element.inject(toggler, 'after');
		} else if (this.container && !test){
			toggler.inject(this.container);
			element.inject(this.container);
		}
		var idx = this.togglers.indexOf(toggler);
		toggler.addEvent('click', this.display.bind(this, idx));
		if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
		if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
		element.fullOpacity = 1;
		if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;
		if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;
		element.setStyle('overflow', 'hidden');
		if (!test){
			for (var fx in this.effects) element.setStyle(fx, 0);
		}
		return this;
	},

	display: function(index){
		index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
		if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
		this.previous = index;
		var obj = {};
		this.elements.each(function(el, i){
			obj[i] = {};
			var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight > 0));
			this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
			for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
		}, this);
		return this.start(obj);
	}

});
// Ataxo UI

var AUI = {

    version: '0.9 alpha',

    // CONSTANTS

    FX_SLOW: { link: 'cancel', duration: 200, transition: 'linear' },
    FX_FAST: { link: 'cancel', duration: 100, transition: 'linear' },
    IMAGE_PATH: 'http://' + location.hostname + '/AtaxoUI/design/',
    GOLDEN_RATIO: 1.61803399
    
};

AUI.REG_EX = {
    WS: /[ \f\n\r\t\v\u2028\u2029]+/
};

// element storage keys
(function() {
    AUI.EL_STORE_KEY = {};
    var keys = 'template,hover,pngfixed,freeze,fixCap'.split(',');
    for (var i = keys.length; i--; )
        AUI.EL_STORE_KEY[keys[i].toUpperCase()] = keys[i];
})();

/*var STOREKEY_ELEMENT_PNGFIXED = 'png:fixed',
STOREKEY_ELEMENT_MARK = 'mark',
STOREKEY_ELEMENT_HOVER = 'hover',
STOREKEY_ELEMENT_LOCALE = 'locale',
STOREKEY_ELEMENT_TEMPLATE = 'template',
STOREKEY_ELEMENT_ROUNDED = 'rounded',
STOREKEY_ELEMENT_FREEZE = 'freeze',
STOREKEY_ELEMENT_NIKO = 'niko';

*/

AUI.COLOR_PALETTES = {
    WEB_SAFE: ['!transparent', '#cccccc', '#999999', '#666666', '#333333', '#000000', '#ffcc00', '#ff9900', '#ff6600', '#ff3300', '#000000', '#333333', '#666666', '#999999', '#cccccc', '#ffffff', '#99cc00', '#000000', '#000000', '#000000', '#000000', '#cc9900', '#ffcc33', '#ffcc66', '#ff9966', '#ff6633', '#cc3300', '#000000', '#000000', '#000000', '#000000', '#cc0033', '#ccff00', '#ccff33', '#333300', '#666600', '#999900', '#cccc00', '#ffff00', '#cc9933', '#cc6633', '#330000', '#660000', '#990000', '#cc0000', '#ff0000', '#ff3366', '#ff0033', '#99ff00', '#ccff66', '#99cc33', '#666633', '#999933', '#cccc33', '#ffff33', '#996600', '#993300', '#663333', '#993333', '#cc3333', '#ff3333', '#cc3366', '#ff6699', '#ff0066', '#66ff00', '#99ff66', '#66cc33', '#669900', '#999966', '#cccc66', '#ffff66', '#996633', '#663300', '#996666', '#cc6666', '#ff6666', '#990033', '#cc3399', '#ff66cc', '#ff0099', '#33ff00', '#66ff33', '#339900', '#66cc00', '#99ff33', '#cccc99', '#ffff99', '#cc9966', '#cc6600', '#cc9999', '#ff9999', '#ff3399', '#cc0066', '#990066', '#ff33cc', '#ff00cc', '#00cc00', '#33cc00', '#336600', '#669933', '#99cc66', '#ccff99', '#ffffcc', '#ffcc99', '#ff9933', '#ffcccc', '#ff99cc', '#cc6699', '#993366', '#660033', '#cc0099', '#330033', '#33cc33', '#66cc66', '#00ff00', '#33ff33', '#66ff66', '#99ff99', '#ccffcc', '#000000', '#000000', '#000000', '#cc99cc', '#996699', '#993399', '#990099', '#663366', '#660066', '#006600', '#336633', '#009900', '#339933', '#669966', '#99cc99', '#000000', '#000000', '#000000', '#ffccff', '#ff99ff', '#ff66ff', '#ff33ff', '#ff00ff', '#cc66cc', '#cc33cc', '#003300', '#00cc33', '#006633', '#339966', '#66cc99', '#99ffcc', '#ccffff', '#3399ff', '#99ccff', '#ccccff', '#cc99ff', '#9966cc', '#663399', '#330066', '#9900cc', '#cc00cc', '#00ff33', '#33ff66', '#009933', '#00cc66', '#33ff99', '#99ffff', '#99cccc', '#0066cc', '#6699cc', '#9999ff', '#9999cc', '#9933ff', '#6600cc', '#660099', '#cc33ff', '#cc00ff', '#00ff66', '#66ff99', '#33cc66', '#009966', '#66ffff', '#66cccc', '#669999', '#003366', '#336699', '#6666ff', '#6666cc', '#666699', '#330099', '#9933cc', '#cc66ff', '#9900ff', '#00ff99', '#66ffcc', '#33cc99', '#33ffff', '#33cccc', '#339999', '#336666', '#006699', '#003399', '#3333ff', '#3333cc', '#333399', '#333366', '#6633cc', '#9966ff', '#6600ff', '#00ffcc', '#33ffcc', '#00ffff', '#00cccc', '#009999', '#006666', '#003333', '#3399cc', '#3366cc', '#0000ff', '#0000cc', '#000099', '#000066', '#000033', '#6633ff', '#3300ff', '#00cc99', '#000000', '#000000', '#000000', '#000000', '#0099cc', '#33ccff', '#66ccff', '#6699ff', '#3366ff', '#0033cc', '#000000', '#000000', '#000000', '#000000', '#3300cc', '#ffffff', '#cccccc', '#999999', '#666666', '#333333', '#000000', '#00ccff', '#0099ff', '#0066ff', '#0033ff', '#000000', '#333333', '#666666', '#999999', '#cccccc', '#ffffff'],
    CRAYON: [
            ['800000', '808000', '008000', '008080', '000080', '800080', '888888', '808080'],
            ['804000', '408000', '008040', '004080', '400080', '800040', '666666', '999999'],
            ['ff0000', 'ffff00', '00ff00', '00ffff', '0000ff', 'ff00ff', '4c4c4c', 'b2b2b2'],
            ['ff8000', '80ff00', '00ff80', '0080ff', '8000ff', 'ff0080', '333333', 'c8c8c8'],
            ['ff6666', 'fafa66', '66ff66', '66ffff', '6666ff', 'ff66ff', '191919', 'e5e5e5'],
            ['ffca66', 'caff66', '66ffca', '66caff', 'cc66ff', 'ff6fcf', '000000', 'ffffff']

        ]
};
var CONSTANTS = {
	HOVER: {
		TABS: {
			selector: 'a',
			fxOptions: AUI.FX_FAST,
			overOut: ['#w-tabs a.over', '#w-tabs a.out'],
			active: '#w-tabs a.active'
		}
	},
	MIN_HORIZ_TAB_HEIGHT: 350,
	MIN_PAGE_HEIGHT: 140
};

var MARKER_CLASSES = {
	 TEMPLATE_IMAGE_CONTAINER: 'wbn-template-image-container'
};
/*
Element extensions

Properties:

classoptions,
outerhtml,
unselectable,
dom,
template,
delegation,
hover,
xhtml

Methods:
transparize,
doSomethingInDOM,
getPositionWithoutBorders,
getSizeWithoutBorders,
isInDocument,
setStylesFast,
selectRange,
toggle,
poof,
makeRounded,
fixPng,
fixCap,
isBlock,
isInvisible,
setWordWrap,
centerPosition

ResourceManager implements property locale.

*/

if (Browser.Engine.trident) {
	Element.Properties.href = {
		set: function(href) {
			// fix ie href bug: http://www.hedgerwow.com/360/bugs/ie-anchor-bug.html
			var el = !this.getElementsByTagName('*').length && this.ownerDocument.createDocumentFragment();
			if (el) while (this.firstChild) el.appendChild(this.firstChild);
			this.href = href;
			if (el) this.appendChild(el);
		}
	}
}

Element.Properties.extend({

	/*
	var options = new Element('<div class="o-rounded-2"></div>').get('classoptions');
	options.rounded == 2; // true
	*/
	classoptions: {
		get: function() {
			var options = {};
			(this.get('class') + '').split(' ').each(function(className) {
				className = className.trim();
				var ops;
				if (/^o-/.test(className) && (ops = className.split('-')).length == 3) {
					options[ops[1].trim()] = ops[2].trim();
				}
			});
			return options;
		}
	},

	outerhtml: {
		get: Browser.Engine.trident
                ? function() { return this.outerHTML; }
                : function() { return this.getDocument().newElement('div').adopt(this.cloneNode(true)).innerHTML; }
	},

	// make element text unselectable, use it for every clickable text to prevent unwanted selection
	unselectable: {
		set: function(value) {
			Browser.Engine.gecko && (this.style.MozUserSelect = value ? 'none' : 'text') ||
                Browser.Engine.webkit && (this.style.KhtmlUserSelect = value ? 'none' : 'text') ||
                (Browser.Engine.trident || Browser.Engine.opera) && (this.unselectable = value ? 'on' : 'off');
			this.getChildren('*').set('unselectable', value);
			return this;
		}
	},

	/*
	DOM builder

	new Element('div', {
	dom: [
	'h1', { text: 'Heading' },
	'p', { dom: ['a' { href: 'http://ataxo.com', text: 'go' }] }
	]
	}).inject(document.body);
	*/
	dom: {
		set: function(def) {
			def = $splat(def);
			var cur = this, doc = this.getDocument(), cache = [];
			for (var i = 0, l = def.length; i < l; i++) {
				var item = def[i], type = $type(item);
				type == 'function' && (type = $type(item = item()));
				switch (type) {
					case 'string': cache.push(/^=/.test(item) ? doc.newTextNode(item.substring(1)) : (cur = doc.newElement(item))); break;
					case 'element': cache.push(cur = $(item)); break;
					case 'object': cur.set(item); break;
					case 'array': cur.set('dom', item);
				}
			}
			for (var i = 0, l = cache.length; i < l; i++) this.appendChild(cache[i]);
			return this;
		}
	},

	// inspirated by Resig javascript template
	template: {
		set: function(templateHTML) {
			return this.store(AUI.EL_STORE_KEY.TEMPLATE, new Function('item', [
                'var p=[];with(item){p.push(\'',
                templateHTML
                  .replace(/[\r\t\n]/g, " ")
                  .split("<%").join("\t")
                  .replace(/((^|%>)[^\t]*)'/g, "$1\r")
                  .replace(/\t=(.*?)%>/g, "',$1,'")
                  .split("\t").join("');")
                  .split("%>").join("p.push('")
                  .split("\r").join("\\'"),
                '\');}return p.join("");'].join('')
            ));
		},
		get: function() {
			return this.retrieve(AUI.EL_STORE_KEY.TEMPLATE);
		}
	}
});

/*
Event Delegation - really powerfull, if you can understant why :-)

<div id='paroubekSpeech'>
<span class='spittle'>foooo blah blah foo <strong class='lie'>blaaaah</strong></span>
<span class='spittle'>blah blah foooooooo <strong class='lie'>foo?</strong></span>
</div>

$('paroubekSpeech').set({
relayedEvents: {
'.spittle': { click: function(e, spittleEl) { if (this == stittleEl) this.destroy(); // yes! }}
}
});

Click to spittle, or click to one strong '.lie', ... will destroy whole speach item.

*/

(function() {

	function check(e, selector, fn) {
		var target = e.target,
			els = this.getElements(selector),
            isOverOut = /^(mouseover|mouseout)$/.test(e.type);
		for (var i = els.length; i--; ) {
			var el = els[i];
			if (el == target || el.hasChild(target)) {
				if (isOverOut && !checkOverOut.call(el, e)) {
					return;
				}
				fn.call(el, e, el);
				return;
			}
		}
	}

	function checkOverOut(e) {
		var related = e.relatedTarget;
		return (related == undefined || (related !== false && $type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related)));
	}

	Element.Properties.relayedEvents = {
		set: function(obj) {
			this.relayEvents(obj);
		}
	};

	Element.implement({

		relayEvent: function(selector, type, fn) {
			type = Element.Events[type] && Element.Events[type].base || type;
			var revents = this.retrieve('$moo:revents');
			if (!revents) this.store('$moo:revents', revents = {});
			var sel = revents[selector] = revents[selector] || {};
			sel[type] = sel[type] || [];
			var fn = check.bindWithEvent(this, [selector, fn]);
			sel[type].push(fn);
			return this.addEvent(type, fn);
		},

		relayEvents: function(selector, events) {
			if (/hash|object/.test($type(selector))) {
				for (var sel in selector) this.relayEvents(sel, selector[sel]);
			}
			else {
				for (var type in events) this.relayEvent(selector, type, events[type]);
			}
			return this;
		},

		unrelayEvents: function(selector) {
			var events = this.retrieve('$moo:revents');
			if (events && events[selector]) {
				for (var type in events[selector]) {
					events[selector][type].each(function(fn) {
						this.removeEvent(type, fn);
					}, this);
				}
			}
			return this;
		},
		
		unrelayAllEvents: function() {
		    var events = this.retrieve('$moo:revents');
		    if (events) {
		        $H(events).each((function(val, selector) { this.unrelayEvents(selector); }).bind(this));
		    }
		}

	});

})();


/*
Hover - lazy morph

- you can morph to css classes or style object: { color: '#000' }
- items could be selectable
- see options

see: AtaxoUI/demos/hover.htm
*/

(function() {

	var Hover = new Class({

		Implements: [Events, Options],

		options: {/*
            onSelectionChanged: function(e, el, selected element(s)) {}
            onHovered: function(e, el, isOver) {}
            */
			selector: 'a',
			overOut: null, // ['.over', '.out'],
			active: null, // '.active' or ['.active', '.inactive'],
			fxOptions: AUI.FX_SLOW,
			animated: true, // will morph to styles, if false, just add class to element
			allowRepeatableSelectForAll: false, // allow repeated selection, when true overrides allowRepeatableSelectFor option
			allowRepeatableSelectFor: [], // allow repeated selection for items in the list (it means, clicking on selected item will trigger the onSelectionChanged event)
			selectable: false, // false, true, multiple
			selectionEventType: 'mouseup', // or mousedown, or click
			selectCondition: null, // to precheck if select is allowed
			selected: null, // null, one element, array of elements
			selectOnStart: true,
			stop: false,
			overOutIgnoresActive: false
		},

		initialize: function(el, options) {
			this.element = el;
			this.setOptions(options);
			var events = this.options.overOut
                ? {
                	mouseover: this.onOverOut.bind(this),
                	mouseout: this.onOverOut.bind(this)
                }
                : {};
			events[this.options.selectionEventType] = this.onSelect.bind(this);
			if (this.options.selectable) {
				this.options.selected = $splat(this.options.selected);
				this.options.overOut = $splat(this.options.overOut);
				if (!this.options.active) this.options.active = this.options.overOut;
				else if ($type(this.options.active) != 'array')
					this.options.active = [this.options.active, this.options.overOut[1]];
				if (this.options.selectOnStart) this.setSelected(this.options.selected);
			}
			this.element.relayEvents(this.options.selector, events);
		},

		onOverOut: function(e, el) {
			if (this.options.stop) e.stop();
			if (this.options.overOutIgnoresActive || (!this.options.selectable || !this.options.selected.contains(el))) {
			    var isOver = e.isOver();
				this.setState(el, isOver);
				this.fireEvent('hovered', [e, el, isOver]);
			}
		},

		onSelect: function(e, el) {
			if (this.options.stop) e.stop();
			if (this.options.selectCondition && !this.options.selectCondition(e, el)) return;
			var unselected;
			if (this.options.selectable == 'multiple') {
				var wasSelected = this.options.selected.contains(el);
				this.options.selected[wasSelected ? 'erase' : 'include'](el);
				this.setState(el, !wasSelected, true);
				unselected = wasSelected && el;
			}
			else if (this.options.selectable) {
				var selected = this.options.selected[0];
				if (selected) {
					if (selected == el && !(this.options.allowRepeatableSelectFor.contains(selected) || this.options.allowRepeatableSelectForAll)) return;
					this.setState(selected, false, true);
				}
				this.options.selected = [el];
				this.setState(el, true, true);
			}
			this.fireEvent('selectionChanged', [e, el, this.getSelected(), unselected]);
		},

		setState: function(el, isOver, active, noAnim) {
			if (this.options.animated) {
				var v = this.options[active ? 'active' : 'overOut'][isOver ? 0 : 1];
				var morph = el.retrieve('morph');
				if (!morph) morph = el.set('morph', this.options.fxOptions).get('morph');
				noAnim ? morph.set(v) : el.morph(v);
			}
			else {
				var v = $splat(this.options[active ? 'active' : 'overOut'])[0];
				if (v && v.charAt(0) == '.') v = v.substring(1);
				if (v) el[isOver ? 'addClass' : 'removeClass'](v);
			}
		},

		getSelected: function() {
			return this.options.selectable == 'multiple'
                ? this.options.selected
                : this.options.selectable && this.options.selected[0];
		},

		setSelected: function(items) {
			this.options.selected = Array.flatten(arguments);
			if (this.options.selected.length == 1) this.mouseOutAll();
			this.options.selected.each(function(el) {
				this.setState(el, true, true, true);
			}, this);
		},

		mouseOutAll: function() {
			this.element.getElements(this.options.selector).each(function(el) {
				this.setState(el, false, false, true);
			}, this);
		},

		getActiveColor: function() {
			if (this.options.animated) {
				return Fx.CSS.prototype.search(this.options.active[0]).color;
			}
		}

	});

	Element.Properties.hover = {
		set: function(options) {
			return this.store(AUI.EL_STORE_KEY.HOVER, new Hover(this, options));
		},
		get: function() {
			return this.retrieve(AUI.EL_STORE_KEY.HOVER);
		}
	}

})();

// xhtml
(function() {

	var nodeDoc,
        nodeBody;

	var STRING_EMPTY = '',
        STRING_LF = '\n',
        STRING_INDENT = '    ';

	var BASE_ATTS = 'title,style,class'.split(','),
        SPEC_ATTS = {
        	img: 'src,alt,width,height'.split(','),
        	iframe: 'id,width,height,frameBorder,allowTransparency,src'.split(','), // component iframes
        	a: 'href,target'.split(','),
        	table: 'cellSpacing,cellPadding,border'.split(','),
        	td: 'colSpan,rowSpan,align,vAlign'.split(',')
        };

	var attCache = {},
        allowedTags = makeMap('div,img,h1,h2,h3,h4,p,blockquote,ul,ol,strong,em,li,a,style,title,head,body,iframe,br,table,tbody,thead,tfoot,tr,td,th'),
	    blockTags = makeMap('body,head,div,h1,h2,h3,h4,p,blockquote,ul,ol,li');

	function makeMap(str) {
		var obj = {}, items = str.split(',');
		for (var i = 0; i < items.length; i++)
			obj[items[i]] = true;
		return obj;
	}

	function tagIsAllowed(el, tagName) {
		var ignored = el.retrieve && el.retrieve('htmlSerializationIgnore');
		if (ignored || $(el).isInvisible()) return false;
		return allowedTags[tagName];
	};

	function tagIsBlock(tagName) {
		return blockTags[tagName];
	}

	// returns linebreaks array definition: beforeOpening,afterOpening,beforeClosing,afterClosing
	function tagGetLineBreaks(tagName) {
		var defs = {};
		(function(tags, def) {
			tags.split(',').each(function(t) { defs[t] = def });
			return arguments.callee;
		})
        ('body,div,ul,ol', [true, true, true, true])
        ('h1,h2,h3,h4,li,p,td,th', [true, false, false, true])
        ('tr', [true, false, true, false])
        ('tbody,thead,tfoot', [false, true, true, false]);
		return (tagGetLineBreaks = function(tagName) {
			return defs[tagName] || [false, false, false, false];
		})(tagName);
	}

	function getAttributes(tagName, node) {
		var output = [],
            atts = attCache[tagName] = attCache[tagName] || BASE_ATTS.concat(SPEC_ATTS[tagName] || []);
            
		for (var i = 0, l = atts.length; i < l; i++) {
			var k = atts[i],
                v = k == 'class'
                    ? node.className
                    : k == 'style'
                        ? node.style && node.style.cssText.toLowerCase().replace(/-(moz|webkit)-[^;]+;/g, STRING_EMPTY)
                        : node[k];
			'iframe' == tagName && 'allowTransparency' == k && (v = 'true'); // ie property that is undefined when inserted via other browsers. unfortunate hack..
			var ignore =
                v == undefined ||
                v == null ||
                (typeof v == 'string' && !(v = v.replace(/^\s+|\s+$/g, STRING_EMPTY)));
			!ignore && output.push(' ' + k.toLowerCase() + '="' + ('' + v).replace(/&/g, '&amp;').replace(/"/g, '&quot;') + '"');
		}
		return output.length ? output.join(STRING_EMPTY) : STRING_EMPTY;
	}

	function getIndent(length) {
		if (length === 0) return STRING_EMPTY;
		var r = [];
		for (var i = length; i--; ) r.push(STRING_INDENT);
		return r.join(STRING_EMPTY);
	}

	function checkEdge(node, end) {
		var sibling = node[end ? 'nextSibling' : 'previousSibling'],
            child = end ? 'lastChild' : 'firstChild',
            warped = sibling && sibling.nodeType == 1 && $(sibling).isBlock();
		if (!warped) {
			var c = node, p = node.parentNode;
			while (p != nodeBody && p[child] == c && !(warped = $(p).isBlock()))
				p = (c = p).parentNode;
		}
		return warped;
	}

	function checkWsEdgeEatingSituations(node, text, output) {
		var l = text.length;
		AUI.REG_EX.WS.test(text.charAt(0)) && checkEdge(node) && (text = text.substring(1));
		AUI.REG_EX.WS.test(text.charAt(l - 1)) && checkEdge(node, true) && (text = text.substring(0, l - 1));
		output.push(text);
	}

	Element.Properties.xhtml = {
		get: function(customTagAllowedCallback, askForElements) {

            askForElements = askForElements || [];

			nodeDoc = this.ownerDocument;
			nodeBody = nodeDoc.body;

			var output = [],
                indentLength = 0;

			(function(el, indentLength) {
				var elIsBlock = tagIsBlock(el.tagName.toLowerCase()),
                    breakAndIndent = STRING_LF + getIndent(indentLength),
                    childs = el.childNodes;
				for (var i = 0, l = childs.length; i < l; i++) {
					var child = childs[i],
                        type = child.nodeType;
					if (type == 1) {
						var tagName = child.tagName.toLowerCase();
						if (askForElements.contains(tagName)) {
						    if (!customTagAllowedCallback(child, tagName)) continue;
						} else {
						    if (!tagIsAllowed(child, tagName) && (!customTagAllowedCallback || !customTagAllowedCallback(child, tagName))) continue;
					    }
						var lbDef = tagGetLineBreaks(tagName),
                            beforeOpening = lbDef[0],
                            beforeClosing = lbDef[2];
						beforeOpening && output.push(breakAndIndent);
						output.push('<' + tagName + getAttributes(tagName, child) + '>');
						arguments.callee(child, tagIsBlock(tagName) ? indentLength + 1 : 0);
						beforeClosing && output.push(breakAndIndent);
						output.push('</' + tagName + '>');
					}
					else if (type == 3) {
						var data = child.data.replace(/[\n\r\t\v]/g, STRING_EMPTY).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); // corrects < > & in textnode
						checkWsEdgeEatingSituations(child, data, output);
					}
				}
			})(this, 1);

			return output.join(STRING_EMPTY).replace(/><\/img>/g, ' />').replace(/<br><\/br>/g, '<br />').replace(/\u00A0/g, '&nbsp;').replace(/\n{2,}/g, '\n');
		}
	}

})();

// setSyntax
Element.implement({
	addSyntaxHighlight: function() {
	    // Safari 4 fixes the Regexp problem, temporary hack
		var tag = (Browser.Engine.webkit && !navigator.appVersion.contains('Version/4')) ? /&lt;\/{0,1}[^>]*>/g : /&lt;\/{0,1}[^&]*&[^g]*g[^t]*t[^;]*;/g,
            att = /([\w]*)=("[^"]*")/g;
		this.innerHTML = this.innerHTML.replace(tag, function(m) {
			return '<span class="tagname">' + m.replace(att, '<span class="attname">$1</span>=<span class="attvalue">$2</span>') + '</span>';
		});
		return this;
	}
});

Element.togglers = {
	visibility: function(value) {
		this.style.visibility = value ? 'visible' : 'hidden';
	},
	display: function(value) {
		this.style.display = value ? '' : 'none';
	}
}

Element.implement({

	doSomethingInDOM: function(fn) {
		if (this.isInDocument()) fn.apply(this);
		else {
			var style = this.style, old = style.cssText;
			style.position = 'absolute';
			style.visibility = 'hidden';
			this.ownerDocument.body.appendChild(this);
			fn.apply(this);
			style.cssText = old;
			this.dispose();
		}
		return this;
	},

	transparize: function() {
		return this.setStyles({ backgroundImage: 'none', backgroundColor: 'transparent', borderColor: 'transparent' });
	},

	getPositionWithoutBorders: function() {
		var b = {}, pos = this.getPosition();
		'Top,Right,Bottom,Left'.split(',').each(function(dir) { b[dir] = parseInt(this.getStyle('border' + dir + 'Width')) }, this);
		// adjust by borders
		pos.x += b.Left;
		pos.y += b.Top;
		return pos;
	},

	getSizeWithoutBorders: function() {
		var b = {}, size = this.getSize();
		'Top,Right,Bottom,Left'.split(',').each(function(dir) { b[dir] = parseInt(this.getStyle('border' + dir + 'Width')) }, this);
		size.x -= (b.Left + b.Right);
		size.y -= (b.Top + b.Bottom);
		return size;
	},

	isInDocument: function() {
		return $(this.ownerDocument.documentElement).hasChild(this);
	},

	// much faster than setStyles, usefull in loops
	setStylesFast: function(styles) {
		var style = this.style;
		for (var name in styles) {
			//var addPx = /^margin|width|height|top|right|bottom|left$/.test(name) && !isNaN(styles[name]);
			//if (styles[name]) style[name] = styles[name] + (addPx ? 'px' : '');
			style[name] = styles[name] + (isNaN(styles[name]) ? '' : 'px');
		}
		return this;
	},

	// make selection in input or textarea
	selectRange: function(start, end) {
		start = $pick(start, 0);
		end = $pick(end, this.value.length);
		if (this.setSelectionRange) {
			this.focus();
			this.setSelectionRange(start, end);
		}
		else if (this.createTextRange) {
			var range = this.createTextRange();
			range.collapse(true);
			range.moveEnd('character', end);
			range.moveStart('character', start);
			range.select();
		}
		return this;
	},

	toggle: function(property, value) {
		Element.togglers[property].call(this, !!value);
		return this;
	},

	toggleFreeze: function(show) {
		var el = this.retrieve(AUI.EL_STORE_KEY.FREEZE);
		if (!el) {
			var doc = this.getDocument();
			this.store(AUI.EL_STORE_KEY.FREEZE, el = doc.newElement('div', {
				style: 'position: absolute; cursor: wait; background-color: #000; overflow: hidden; z-index: 9999',
				opacity: 0,
				tween: {
					link: 'cancel', duration: 350, transition: 'quad:out',
					onStart: function() { if (!el.getOpacity()) el.inject(doc.body); },
					onComplete: function() { if (!el.getOpacity()) el.dispose(); }
				},
				events: { mousedown: function(e) { e.stop() } },
				unselectable: true
			}));
		}
		el.setStyles(this.getCoordinates()).tween('opacity', show ? .2 : 0);
		return this;
	}

});

/*
Rounded Corners - Gives the element anti-aliased rounded corners.

@param radius - int - corner radius in pixels

notes:

- horizontal padding will not change rounded width because inner el has width auto
- el rounding means, that rounded el get parent with its styles and rounds els
- element styled by its ID can not be rounded

Examples: viz. demo RoundedCorners.htm

*/

(function() {

	var snippet = Browser.Engine.trident4 ?
            '<div style="display:inline;float:right;margin-right:{m}px;width:1px;height:1px;font-size:0;background:{sc};{o};overflow:hidden"></div><div style="overflow:hidden;{ih}"><div style="display:inline;float:left;margin-left:{m}px;width:1px;height:1px;font-size:0;{o};background:{sc};overflow:hidden"></div><div style="display:inline;position:absolute;width:100%;height:1px;font-size:0;background:transparent;overflow:hidden"><div style="width:auto;margin-right:{md}px;height:1px;font-size:0;background:{bc};overflow:hidden"></div></div></div>' :
    		'<div style="float:right;margin-right:{m}px;width:1px;height:1px;font-size:0;background:{sc};{o};overflow:hidden"></div><div><div style="float:left;margin-left:{m}px;width:1px;height:1px;font-size:0;{o};background:{sc};"></div><div style="height:1px;font-size:0;margin:0 {ma}px;background:{bc}"></div></div>';

	// note: should be one innerHTML for all containers
	function renderSnippet(container, margin, opacity, strokeColor, backgroundColor) {
		container.innerHTML = snippet
		    .replace(/\{m\}/g, margin)
		    .replace(/\{ma\}/g, margin + 1)
		    .replace(/\{md\}/g, 0 < margin ? margin - 2 : 2)
		    .replace(/\{sc\}/g, strokeColor).replace(/\{bc\}/g, backgroundColor)
		    .replace(/\{o\}/g, Browser.Engine.trident ? 'filter:alpha(opacity=' + Math.floor(opacity * 100) + ')' : 'opacity:' + opacity)
		    .replace(/\{ih\}/g, 0 < margin ? 'height:1%' : '');
		return Browser.Engine.trident4 ? container.lastChild.lastChild.lastChild : container.lastChild.lastChild;
	}

	Element.implement({

		makeRounded: function(radius) {
			if (isNaN(radius)) return this;

			var doc = this.getDocument(),
                fg_color, position, visibility, that = this;

			//this.doSomethingInDOM(function() {
			fg_color = that.getComputedStyle('backgroundColor');
			position = that.getComputedStyle('position');
			//});

			var round_container = doc.newElement('div', {
				style: this.style.cssText,
				styles: { textAlign: 'left', visibility: 'hidden', overflow: 'visible', background: 'transparent', padding: 0 },
				'class': this.get('class')
			});

			var clonner = doc.createElement('div'),
                top_container = doc.newElement('div'),
                bottom_container = doc.newElement('div'),
                last_arc = 0,
                i, j, coverage, arc, arc2, arc3, n_bg, n_fg, n_aa, el, el_x, el_y, map = [];

			if (Browser.Engine.presto) fg_color = fg_color.replace(/"/g, '');
			clonner.style.cssText = 'height: 1px; overflow: hidden; font-size: 0';

			// fix cliping height for ff and ie
			if (!['absolute', 'fixed'].contains(position))
				round_container.setStyle('position', 'relative');
			// fix ie7 opacity issue related to wrong height computation
			if (Browser.Engine.trident5) {
				var padding = parseInt(this.getStyle('padding-top')) + parseInt(this.getStyle('padding-bottom'));
				round_container.setStyle('padding-bottom', padding + (radius * 2));
			}

			function rfunc(n) {
				return Math.sqrt(1 - (n * n)) * radius;
			}

			for (i = 1; i <= radius; i++) {
				arc = rfunc(1 - i / radius);
				n_bg = radius - Math.ceil(arc);
				n_fg = Math.floor(last_arc);
				n_aa = radius - n_bg - n_fg;
				el_x = [n_bg, 0, []];
				el_y = map;
				for (j = 1; j <= n_aa; j++) {
					if (j == 1) {
						if (j == n_aa) {
							coverage = (arc + last_arc) * 0.5 - n_fg;
						} else {
							arc2 = rfunc(1 - (n_bg + 1) / radius);
							coverage = (arc2 - (radius - i)) * (arc - n_fg - n_aa + 1) * 0.5;
						}
					} else if (j == n_aa) {
						arc2 = rfunc((radius - n_bg - j + 1) / radius);
						coverage = 1 - (1 - (arc2 - (radius - i))) * (1 - (last_arc - n_fg)) * 0.5;
					} else {
						arc3 = rfunc((radius - n_bg - j) / radius);
						arc2 = rfunc((radius - n_bg - j + 1) / radius);
						coverage = (arc2 + arc3) * 0.5 - (radius - i);
					}
					el_x[1] = coverage;
					el_y.push(el_x);
					el_y = el_x[2];
					el_x = [1, 0, []];
				}
				el_y.push(el_x);
				last_arc = arc;
			}

			for (i = 0; i < map.length; i++) {
				var item = map[i],
                    is_first = true,
                    container = clonner.cloneNode(false);
				top_container.appendChild(container);
				while (container && 0 < item[2].length) {
					container = renderSnippet(container, is_first ? item[0] : 0, item[1], fg_color, 'transparent');
					is_first = false;
					item = item[2][0];
				}
				container.style.backgroundColor = fg_color;
			}
			var bottom_els = top_container.cloneNode(true).childNodes;
			for (var i = bottom_els.length; i--; ) bottom_container.appendChild(bottom_els[i]);
			var s = this.style;
			s.visibility = 'hidden'; s.top = 0; s.left = 0; s.width = 'auto'; s.height = '100%'; s.position = 'relative'; s.margin = 0;
			this.set('opacity', 1, true);
			this.parentNode && this.parentNode.replaceChild(round_container, this);
			round_container.adopt(top_container, this, bottom_container);
			(function() { s.visibility = round_container.style.visibility = 'visible'; }).delay(1);
			return this;
		}
	});
})();

/*
.fixPng - png background fix - with support background repeat and position (left, % and px) !!!

- fix element and its childs if element style background image has .png
- set element className property will update element background
*/

(function() {

	var cachedSizes = {}

	function IE6PngFix(element) {

		var el = element,
            changeTimeout,
            doc = el.ownerDocument,
            win = el.getWindow(),
            bg = {}, snippet, tiles,
            lastBgState;

		loadPos();
		loadSize(init);

		function loadPos() {
			var cs = el.currentStyle,
                s = el.style;
			if (!cs || !cs.hasLayout) s.zoom = 1;
			s.overflow = s.visibility = 'hidden';
			var w = el.offsetWidth, h = el.offsetHeight,
                x = cs['backgroundPositionX'], y = cs['backgroundPositionY'];
			if (!/absolute|relative/.test(cs['position'])) s.position = 'relative';
			var english = { top: '0%', left: '0%', center: '50%', bottom: '100%', right: '100%' };
			x = english[x] || x;
			y = english[y] || y;
			if (x.match(/(\d+)%/)) x = Math.round(w * (parseInt(x.match(/(\d+)%/)[1]) / 100));
			if (y.match(/(\d+)%/)) y = Math.round(h * (parseInt(y.match(/(\d+)%/)[1]) / 100));
			bg.src = cs['backgroundImage'].split('"')[1];
			bg.pos = [parseInt(x), parseInt(y)];
			bg.repeat = cs['backgroundRepeat'];
			lastBgState = bg.src + bg.pos.join() + bg.repeat;
		}

		/*function loadSize(onLoad) {
		var img = doc.createElement('img');
		if (img.complete) {
		bg.size = [img.width, img.height];
		onLoad();
		}
		else {
		img.onload = function() {
		img.onload = null;
		bg.size = [img.width, img.height];
		onLoad();
		};
		img.src = bg.src;
		}
		}*/

		function loadSize(onLoad) {
			// TODO: fix this cache, maybe remove it all
			if (bg.size = cachedSizes[bg.src]) onLoad();
			else {
				var img = doc.createElement('img');
				img.onload = function() {
					img.onload = null;
					bg.size = cachedSizes[bg.src] = [img.width, img.height];
					onLoad();
				}
				img.src = bg.src + '?foo=' + $time(); // ie needs to retrieve right image size
			}
		}

		function init() {
			snippet = '<div style="filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'{0}\',sizingMethod=\'{1}\');position:absolute;z-index:-999;border:none;background-image:none;background-color:transparent;margin:0;padding:0;overflow:hidden;width:{2}px;height:{3}px;left:{4}px;top:{5}px"></div>'
                .substitute([bg.src, 'crop', bg.size[0], bg.size[1], '{left}', '{top}']);
			update();
			el.addListener('propertychange', function() {
				if (win.event.propertyName == 'className') {
					clearTimeout(changeTimeout);
					changeTimeout = setTimeout(function() {
						loadPos();
						update();
					}, 1);

				}
			});
		}

		function update() {
			var parser = doc.createElement('div'), i;
			parser.innerHTML = getHtml();
			if (tiles) for (i = tiles.length; i--; ) tiles[i].parentNode.removeChild(tiles[i]);
			tiles = $A(parser.childNodes);
			for (i = 0, l = tiles.length; i < l; i++) el.appendChild(tiles[i]);
			el.style.backgroundImage = 'none';
			el.style.visibility = 'visible';
		}

		function getHtml() {
			var x = bg.pos[0], y = bg.pos[1];
			if (bg.repeat == 'no-repeat') return snippet.replace(/{left}/, x).replace(/{top}/, y);
			else {
				var html = [],
                    tW = bg.size[0], tH = bg.size[1],
                    w = el.offsetWidth, h = el.offsetHeight;
				var tX = x % tW; if (tX > 0) tX -= tW;
				var tY = y % tH; if (tY > 0) tY -= tH;
				var repeatX = bg.repeat == 'repeat-x',
                    repeatY = bg.repeat == 'repeat-y';
				if (repeatX) tY = y; else if (repeatY) { tX = x; w = tX + 1; }
				for (; tX < w; tX += tW) {
					html.push(snippet.replace(/{left}/, tX).replace(/{top}/, tY));
					for (var tY2 = tY + tH; !repeatX && tY2 < h; tY2 += tH) {
						html.push(snippet.replace(/{left}/, tX).replace(/{top}/, tY2));
					}
				}
				return html.join('');
			}
		}
	}

	Element.implement({
		fixPng: function() {
			if (!Browser.Engine.trident4) return this;
			if (!this.retrieve(AUI.EL_STORE_KEY.PNGFIXED)) {
				this.store(AUI.EL_STORE_KEY.PNGFIXED, true);
				var els = $A(this.getElementsByTagName('*')), cs, url;
				els.push(this);
				for (var i = els.length; i--; ) {
					if ((cs = els[i].currentStyle) && (url = cs.backgroundImage) && /\.png/i.test(url))
						new IE6PngFix($(els[i]));
				}
			}
			return this;
		}
	});

})();


/*
.fixCap - conflicting absolute positions fix for IE6

- http://www.alistapart.com/articles/conflictingabsolutepositions

TODO: propertychange bubble, so I can track all el style settings, and doc stylesheets can be parsed to

*/

(function() {

	var els = [], inited, windowSize, html;

	function lazyInit() {
		if (inited) return;
		inited = true;
		html = document.documentElement;
		window.addEvent('resize', function() {
			var size = [html.clientWidth, html.clientHeight].join();
			if (windowSize != size) {
				windowSize = size;
				for (var i = els.length; i--; ) setSize(els[i]);
			}
		});
	}

	function fix(el) {
		lazyInit();
		if (!els.contains(el)) {
			els.push(el);
			el.addListener('propertychange', function() {
				var n = event.propertyName;
				if (n == 'className' || (/style/.test(n) && n != 'style.width' && n != 'style.height')) {
					setSize(el);
				}
			});
		}
		setSize(el);
	}

	function setSize(el) {
		var p = el.offsetParent, x, y;
		if (/body|html/i.test(p.tagName)) {
			p = p.ownerDocument.documentElement;
			x = p.clientWidth; y = p.clientHeight;
		}
		else {
			x = p.offsetWidth; y = p.offsetHeight;
		}
		var s = el.style,
			hw = getPx(el, 'left') + getPx(el, 'right'),
			vh = getPx(el, 'top') + getPx(el, 'bottom');
		if (!isNaN(hw)) el.style.width = Math.max(x - hw - getPx(el, 'paddingLeft') - getPx(el, 'paddingRight'), 0) + 'px';
		if (!isNaN(vh)) el.style.height = Math.max(y - vh - getPx(el, 'paddingTop') - getPx(el, 'paddingBottom'), 0) + 'px';
	}

	function getPx(el, prop) {
		var result = el.currentStyle[prop];
		if (!/^\d+(px)?$/i.test(result) && /^\d/.test(result) && !/\s/.test(result)) {
			var left = el.style.left, rsLeft = el.runtimeStyle.left;
			// Put in the new values to get a computed value out
			el.runtimeStyle.left = el.currentStyle.left;
			el.style.left = result || 0;
			result = el.style.pixelLeft + "px";
			el.style.left = left;
			el.runtimeStyle.left = rsLeft;
		}
		return parseInt(result);
	}

	Element.implement({
		fixCap: function() {
			//if (Browser.Engine.trident4) fix(this);
			return this;
		}
	});
})();

// some css related.. no better place yet
Element.implement({

	isBlock: Browser.Engine.trident ?
        function() {
        	return this.getComputedStyle('display') == 'block' || (this.currentStyle && this.currentStyle.hasLayout);
        } :
        function() {
        	return /block|list/.test(this.getComputedStyle('display'));
        }
    ,

	isInvisible: function() {
		return this.getComputedStyle('display') == 'none' || this.getComputedStyle('visibility') == 'hidden';
	},

	setWordWrap: function() {
		var specific = (Browser.Engine.trident || Browser.Engine.presto) ? 'white-space: -o-pre-wrap; word-wrap: break-word' : '';
		this.style.cssText = 'white-space: pre-wrap; white-space: -moz-pre-wrap;' + specific;
		return this;
	},

	// minimalTopOffset - to have clossable windows
	centerPosition: function(minimalTopOffset) {
		var eSize = this.getSize(),
			pSize = $(this.getOffsetParent()).getSize()/*,
			k = 2 * (2 - AUI.GOLDEN_RATIO),
			h = pSize.y*/;

		// d: nejak blbe se to pocita, na 1024*768 mam form zarovnanej na spodek okna
		// taky mi nepride logicke za kazdou cenu zobrazovat spodek, pac pak nelze form zavrit

		/*return this.setStyles({
		left: (pSize.x / 2) - (eSize.x / 2),
		top: Math.min(h - eSize.y, h - (h * k) - 6) // correct the GR calculation in cases where bottom part of the box wouldn't be visible
		});*/
		var top = (pSize.y / 2) - (eSize.y / 2);
		top = $defined(minimalTopOffset) ? Math.max(minimalTopOffset, top) : top;
		return this.setStyles({
			left: (pSize.x / 2) - (eSize.x / 2),
			top: top
		});
	}
});


Element.implement({
    makeReflected: function(opacity, height) { // opacity: <0,1>, height <0,1> 0.5 means reflection height below image is half the height of the image
        var cnt = new Element('div', {
                'class': 'reflection',
                styles: this.getStyles()
            }).inject(this, 'before'),
            imageWidth = parseInt(this.getComputedStyle('width')),
            imageHeight = parseInt(this.getComputedStyle('height')),
            reflectionHeight = imageHeight * height,
            el = null;

        this.inject(cnt);
        cnt.setStyles({
            height: reflectionHeight + imageHeight,
            overflow: 'hidden'
        });
        if (Browser.Engine.trident) {
			el = new Element('img', {
			    src: this.src,
			    styles: {
			        width: imageWidth,
			        height: imageHeight,
			        display: 'block',
			        marginBottom: - imageHeight + reflectionHeight,
			        filter: ['flipv progid:DXImageTransform.Microsoft.Alpha(opacity=', opacity * 100, ', style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=', height * 100, ')'].join('')
			    }
			});
        } else {
			el = new Element('canvas', {
			    styles: {
			        width: imageWidth,
			        height: reflectionHeight,
			        display: 'block',
			        clear: 'both'
			    },
			    width: imageWidth,
			    height: reflectionHeight
			});
    		var	ctx = el.getContext('2d'),
    			gradient = ctx.createLinearGradient(0, 0, 0, reflectionHeight);

    		ctx.save();
    		ctx.translate(0, imageHeight - 1);
            ctx.scale(1, -1);
            try {
    		    ctx.drawImage(this, 0, 0, imageWidth, imageHeight);
		    } catch (e) {}
    		ctx.restore();
    		ctx.globalCompositeOperation = 'destination-out';
    		gradient.addColorStop(1, 'rgba(255, 255, 255, 1.0)');
    		gradient.addColorStop(0, 'rgba(255, 255, 255, ' + (1 - opacity) + ')');
    		ctx.fillStyle = gradient;
            ctx.rect(0, 0, imageWidth, reflectionHeight * 2);
            ctx.fill();
        }
        if (el) cnt.adopt(el);
        return this;
    }
});

// rewamped moo getComputedStyle, extracted and refactored ViewCSS.js from Dean Edwards Base
// getComputedStyle should always return pixies and colors in rgb also in IE (as w3c does)
// by: Daniel Steigerwald (daniel.steigerwald.cz)
// + fix also for white
// + fix for backgroundPosition
// + fix for float
if (Browser.Engine.trident) {
	(function() {
		var _PIXEL = /^\d+(px)?$/i,
			_METRICS = /(width|height|top|bottom|left|right|fontSize)$/i,
			_COLOR = /^(color|backgroundColor)$/,
			_EDGE_COLOR_TEST_FOR_CRAPPY_IE = {
				'rgb(0, 0, 0)': { black: 1, '#000': 1, '#000000': 1 },
				// I had to add this white check addition to base2, probably from same reason as Dean had added black. In my bigass app, sometimes its returned rgb(255, 255, 255) instead of correct color. Other colors works well.
				'rgb(255, 255, 255)': { white: 1, '#fff': 1, '#ffffff': 1 }
			};

		function getPixelValue(el, value) {
			if (_PIXEL.test(value)) return parseInt(value);
			var styleLeft = el.style.left,
			runtimeStyleLeft = el.runtimeStyle.left;
			el.runtimeStyle.left = el.currentStyle.left;
			value = value || 0;
			if (isNaN(value)) value = 0; // fix for medium and other shit IE reports
			el.style.left = value || 0;
			value = el.style.pixelLeft;
			el.style.left = styleLeft;
			el.runtimeStyle.left = runtimeStyleLeft;
			return value;
		};

		function getColorValue(el, type) {
			var range, zoom = el.style.zoom;
			// Dean: elements need to have "layout" for this to work, Steida: So I add layout temporally
			if (!el.currentStyle || !el.currentStyle.hasLayout) el.style.zoom = 1;
			if (el.createTextRange) range = el.createTextRange();
			else {
				range = el.document.body.createTextRange();
				range.moveToElementText(el);
			}
			var color = range.queryCommandValue(type);
			el.style.zoom = zoom;
			return 'rgb({0}, {1}, {2})'.substitute([color & 0xff, (color & 0xff00) >> 8, (color & 0xff0000) >> 16]);
		};

		Element.implement({
			getComputedStyle: function(property) {
				property = property.camelCase();
				if (property == 'backgroundPosition') {
					return this.currentStyle[property + 'X'] + ' ' + this.currentStyle[property + 'Y'];
				}
				if (property == 'float') return this.currentStyle['styleFloat'];
				var value = this.currentStyle[property];
				if (_METRICS.test(property)) return getPixelValue(this, value) + 'px';
				if (_COLOR.test(property)) {
					var rgb = getColorValue(this, property == 'color' ? 'ForeColor' : 'BackColor'),
						test = _EDGE_COLOR_TEST_FOR_CRAPPY_IE[rgb];
					// todo: add named color conversion for value
					return (test && !test[value]) ? ('rgb(' + new Color(value) + ')') : rgb;
				}
				return value;
			}
		});

	})();
}
else {
	Element.implement({
		getComputedStyle: function(property) {
			var computed = this.getDocument().defaultView.getComputedStyle(this, null);
			return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
		}
	});
}
(function() {

	window.addEvent('domready', init);
	
	var locales;

	String.prototype.injectCssInHeader = function() {
		var el = document.createElement('style');
		el.setAttribute('type', 'text/css');
		Browser.Engine.trident ? el.styleSheet.cssText = this : el.appendText(this);
		document.head.appendChild(el);
	}

	var formWidth = 430, formTop = 175, fx, elBg, elForm, els, timeOut = 10000, processLogin, okPressed, shouldCreatePageAfterLogin = false;

	function init() {
		locales = wbnData.result;
		buildForm();
		setStyles();
		initFx();
		registerEvents();
	}

	function buildForm() {
		var cntCss = 'margin: 0; padding: 0; visibility: hidden; position: ' + (Browser.Engine.trident4 ? 'absolute' : 'fixed') + '; width: ' + formWidth + 'px; height: 190px; z-index: 997; top: ' + formTop + 'px; -webkit-border-radius:15px; -moz-border-radius: 15px; ';
		elBg = new Element('div', { style: cntCss + "background-color: #000" });
		elForm = new Element('div', { id: 'wbn-login-form', style: cntCss });
		els = [elBg, elForm];
		elForm.innerHTML = '<h1>' + locales.title + '</h1><label for="email">' + locales.email + '</label><input name="email" id="email" type="text" /><span>' + locales.emailValidation + '</span><br /><label for="pass">' + locales.password + '</label><input name="pass" id="pass" type="password" /><span>' + locales.passwordValidation + '</span><br /><div class="checkbox"><input type="checkbox" name="remember" id="remember" /><label for="remember">' + locales.labRemember + '</label></div><br /><div><input class="submit" type="submit" value="' + locales.close + '" /><input id="btn-submit" type="submit" value="' + locales.submit + '" /></div><span class="info"></span>';
		elForm.getElements('input[type=text], input[type=password]')
	        .setStyles({ width: 180, paddingTop: Browser.Engine.gecko ? 2 : 1 });
		elForm.getElements('input[type=submit]').setStyles({ 'float': 'right', margin: 4 });
		$$(els).inject(document.body);
	}

	function setStyles() {
		['#wbn-login-form h1, #wbn-login-form label, #wbn-login-form span { color: white; font-family: arial;}',
	     '#wbn-login-form h1 { font-size: 16px; margin: 17px 35px;}',
	     '#wbn-login-form br { clear: both;}',
	     '#wbn-login-form label, #wbn-login-form input, #wbn-login-form span { float: left; font-size: 13px; font-family: arial; margin: 2px; padding: 1px}',
	     '#wbn-login-form span { color: #ff0066; margin: 4px; visibility: hidden}',
	     '#wbn-login-form div { margin-right: 110px; }',
	     '#wbn-login-form div.checkbox { margin-left: 128px; }',
	     '#wbn-login-form div.checkbox label { float: left; margin:0; padding:2px; width:160px; font-size:11px; color:#bbb}',
	     '#wbn-login-form label { margin: 4px 8px 0px 40px; width: 83px; text-align: right; }',
	     '#wbn-login-form .info { visibility: visible; position: absolute; left: 10px; bottom: 2px; }'].join('').injectCssInHeader();
	}

	function initFx() {
		fx = new Fx.Elements(els, {
			duration: 700,
			transition: Fx.Transitions.Quad.easeOut,
			link: 'cancel',
			onComplete: function() {
				$$(els).setStyle('top', formTop);
				fx.options.duration = 700;
				if (elForm.style.visibility == 'visible' && !processLogin) {
					var inpFirst = elForm.getElement('input'),
					    inpPassw = elForm.getElement("input[type='password']");
					// if both fields have values, assume password has been entered incorrectly and select the password field
					if (inpFirst.value && inpPassw.value) {
						inpPassw.select();
					} else {
						inpFirst.focus();
					}
				}
			}
		});
	}

	function showForm() {
		if (window.wbnData && wbnData.userToken) {
			return onServerResponse('({statusCode: 200, result: {sites:{}}})');
		}
		setToMiddle();
		resetText();
		$('btn-submit').value = locales[shouldCreatePageAfterLogin ? 'submitAndCreate' : 'submit'];
		fx.start({ 0: { opacity: [0, 0.8] }, 1: { opacity: [0, 1]} });
	}

	function resetText() {
		elForm.getElement('.info').set('text', '');
		elForm.getElements('input[type=text]').set('value', '');
	}

	function hideForm() {
		fx.start({ 0: { opacity: [0], top: [320] }, 1: { opacity: [0], top: [320]} });
	}

	function toggleForm(disable) {
		fx.options.duration = 200;
		fx.start({ 0: { opacity: [disable ? 0.5 : 0.8] }, 1: { opacity: [disable ? 0.7 : 1]} });
		if (disable) window.focus();
	}

	function setToMiddle(el) {
		$$(els).setStyle('left', (window.getSize().x / 2) - (formWidth / 2));
	}

	function registerEvents() {
		if ($('wbn-login')) {
    		elForm.addEvents({
    			click: function(e) {
    				if (!e.target || processLogin) return;
    				var type = $(e.target).get('type'), action = $(e.target).get('value');
    				if (type != 'submit') return;
    				action == locales.close ? hideForm() : tryLogin();
    			},
    			keyup: function() { if (okPressed) validate(); },
    			keypress: function(e) { if (e.key == 'enter') tryLogin(); }
    		});
    		document.addEvent('keypress', function(e) {
    			if (e.key == 'esc') hideForm();
    		});
    		$('wbn-login').addEvent('click', function(e) { shouldCreatePageAfterLogin = false; showForm(); e.stop(); });
    		if ($('wbn-login-create')) {
    		    $('wbn-login-create').addEvent('click', function(e) { shouldCreatePageAfterLogin = true; showForm(); e.stop(); });
		    }
		}
		// mail publishing
        // return;
		if ($('wbn-mail-publish')) {
    		var body = $(document.body),
                head = document.getElementsByTagName('head')[0],
                link = $('wbn-mail-publish'),
                content = body.getElement('.wbn-part-content'),
                newHead = document.createElement('head'),
                lst = head.getElementsByTagName('*'),
    		    source = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
		    
		    if (content) {
        		for (var i = 0, n = lst.length; i<n; i++) {
        		    var el = lst.item(i);
        		    if (!el) continue;
    		        var tag = el.tagName.toLowerCase();
        		    if ('link' == tag || 'base' == tag) newHead.appendChild(el.cloneNode(true));
        		}
        		source += newHead.innerHTML + '</head><body><div class="' + content.get('class') + '" id="' + content.get('id') + '">' + content.get('html') + '</div></body></html>';
                link.set('href', 'mailto:publish@' + link.get('class') + '?' + link.get('href').substr("mailto:?".length) + '&body=' + escape(source));
    		}
		}
	}

	function tryLogin() {
		okPressed = true;
		if (!validate()) return;
		processLogin = true;
		toggleForm(true);
		var data = elForm.toQueryString();
		data += '&pageUrl=' + window.location.pathname.substr(1);
		new Request({
			url: ['http://', window.location.hostname, '/wbn-services/user/login'].join(''),
			data: data,
			onSuccess: onServerResponse
		}).send();
	}

  function redirect(url) {
	// disallow hash at the end of the url to make the redirect happen in ie8 (probably elsewhere too)
	if (url[url.length - 1] == "#") {
		url = url.substring(0, url.length - 1);
	}
	
    if (Browser.Engine.gecko) {
      //p: vypada, ze pracuje..
      document.open();
      document.write('<script type="text/javascript">');
      document.write('setTimeout(function(){top.location.href="' + url + '";}, 200);')
      document.write('</script>');
      document.close();
    } else {
      top.location.href = url;
    }
  }

	function goToLastEditingPage(pageUrl) {
		redirect(['http://', window.location.hostname, '/', pageUrl].join(''));
	}

	function goToCurrentEditingPage() {
	  redirect(window.location.href);
	}

	function onServerResponse(resp) {
		processLogin = false;
		resp = JSON.decode(resp);
		if (resp.statusCode == 200) {
			var sitename = window.location.hostname.split('.')[0], // p.webeena.cz -> p
                pageUrl = resp.result[sitename] || '';
			if (shouldCreatePageAfterLogin) {
				// creating the page prior redirect
				var pageTitle = window.location.pathname.split('#')[0].substr(1); // /hello#world -> hello
				new Request({
					url: ['http://', window.location.hostname, '/wbn-services/page/create'].join(''),
					data: { title: pageTitle, siteID: wbnData.siteID },
					onSuccess: function(resp) {
						resp = JSON.decode(resp);
						if (200 != resp.statusCode || true != resp.result.operation) {
							goToLastEditingPage('');
						}
						else {
							goToCurrentEditingPage();
						}
					}
				}).send();
			} else {
				if ('log-to-last' == $('wbn-login').className) {
					// when logging from non-existent page, we cannot just reload. we are using returned service data (list of sites with last edited pages) instead
					goToLastEditingPage(pageUrl);
				} else {
					goToCurrentEditingPage();
				}
			}
		}
		else {
			toggleForm();
			elForm.getElement('.info').set('text', resp.result);
		}
	}

	function validate() {
		var emailOk = elForm.getElement('input[name=email]').value.trim(),
	        passOk = elForm.getElement('input[name=pass]').value.trim(),
	        fn = function setVis(el, state) { el.setStyle('visibility', state ? 'hidden' : 'visible') },
	        spans = elForm.getElements('span');
		fn(spans[0], emailOk);
		fn(spans[1], passOk);
		return emailOk && passOk;
	}
})()
/**
 * SqueezeBox - Expandable Lightbox
 *
 * Allows to open various content as modal,
 * centered and animated box.
 *
 * Dependencies: MooTools 1.2
 *
 * Inspired by
 *  ... Lokesh Dhakar	- The original Lightbox v2
 *
 * @version		1.1 rc4
 *
 * @license		MIT-style license
 * @author		Harald Kirschner <mail [at] digitarald.de>
 * @copyright	Author
 */

var SqueezeBox = {

	presets: {
		onOpen: $empty,
		onClose: $empty,
		onUpdate: $empty,
		onResize: $empty,
		onMove: $empty,
		onShow: $empty,
		onHide: $empty,
		size: {x: 600, y: 450},
		sizeLoading: {x: 200, y: 150},
		marginInner: {x: 20, y: 20},
		marginImage: {x: 50, y: 75},
		handler: false,
		target: null,
		closable: true,
		closeBtn: true,
		zIndex: 65555,
		overlayOpacity: 0.7,
		classWindow: '',
		classOverlay: '',
		overlayFx: {},
		resizeFx: {},
		contentFx: {},
		parse: false, // 'rel'
		parseSecure: false,
		shadow: true,
		document: null,
		ajaxOptions: {}
	},

	initialize: function(presets) {
		if (this.options) return this;

		this.presets = $merge(this.presets, presets);
		this.doc = this.presets.document || document;
		this.options = {};
		this.setOptions(this.presets).build();
		this.bound = {
			window: this.reposition.bind(this, [null]),
			scroll: this.checkTarget.bind(this),
			close: this.close.bind(this),
			key: this.onKey.bind(this)
		};
		this.isOpen = this.isLoading = false;
		return this;
	},

	build: function() {
		this.overlay = new Element('div', {
			id: 'sbox-overlay',
			styles: {display: 'none', zIndex: this.options.zIndex}
		});
		this.win = new Element('div', {
			id: 'sbox-window',
			styles: {display: 'none', zIndex: this.options.zIndex + 2}
		});
		if (this.options.shadow) {
			if (Browser.Engine.webkit420) {
				this.win.setStyle('-webkit-box-shadow', '0 0 10px rgba(0, 0, 0, 0.7)');
			} else if (!Browser.Engine.trident4) {
				var shadow = new Element('div', {'class': 'sbox-bg-wrap'}).inject(this.win);
				var relay = function(e) {
					this.overlay.fireEvent('click', [e]);
				}.bind(this);
				['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'].each(function(dir) {
					new Element('div', {'class': 'sbox-bg sbox-bg-' + dir}).inject(shadow).addEvent('click', relay);
				});
			}
		}
		this.content = new Element('div', {id: 'sbox-content'}).inject(this.win);
		this.closeBtn = new Element('a', {id: 'sbox-btn-close', href: '#'}).inject(this.win);
		this.fx = {
			overlay: new Fx.Tween(this.overlay, $merge({
				property: 'opacity',
				onStart: Events.prototype.clearChain,
				duration: 250,
				link: 'cancel'
			}, this.options.overlayFx)).set(0),
			win: new Fx.Morph(this.win, $merge({
				onStart: Events.prototype.clearChain,
				unit: 'px',
				duration: 750,
				transition: Fx.Transitions.Quint.easeOut,
				link: 'cancel',
				unit: 'px'
			}, this.options.resizeFx)),
			content: new Fx.Tween(this.content, $merge({
				property: 'opacity',
				duration: 250,
				link: 'cancel'
			}, this.options.contentFx)).set(0)
		};
		$(this.doc.body).adopt(this.overlay, this.win);
	},

	assign: function(to, options) {
		return ($(to) || $$(to)).addEvent('click', function() {
			return !SqueezeBox.fromElement(this, options);
		});
	},
	
	open: function(subject, options) {
		this.initialize();

		if (this.element != null) this.trash();
		this.element = $(subject) || false;
		
		this.setOptions($merge(this.presets, options || {}));
		
		if (this.element && this.options.parse) {
			var obj = this.element.getProperty(this.options.parse);
			if (obj && (obj = JSON.decode(obj, this.options.parseSecure))) this.setOptions(obj);
		}
		this.url = ((this.element) ? (this.element.get('href')) : subject) || this.options.url || '';

		this.assignOptions();
		
		var handler = handler || this.options.handler;
		if (handler) return this.setContent(handler, this.parsers[handler].call(this, true));
		var ret = false;
		return this.parsers.some(function(parser, key) {
			var content = parser.call(this);
			if (content) {
				ret = this.setContent(key, content);
				return true;
			}
			return false;
		}, this);
	},
	
	fromElement: function(from, options) {
		return this.open(from, options);
	},

	assignOptions: function() {
		this.overlay.set('class', this.options.classOverlay);
		this.win.set('class', this.options.classWindow);
		if (Browser.Engine.trident4) this.win.addClass('sbox-window-ie6');
	},

	close: function(e) {
		var stoppable = ($type(e) == 'event');
		if (stoppable) e.stop();
		if (!this.isOpen || (stoppable && !$lambda(this.options.closable).call(this, e))) return this;
		this.fx.overlay.start(0).chain(this.toggleOverlay.bind(this));
		this.win.setStyle('display', 'none');
		this.fireEvent('onClose', [this.content]);
		this.trash();
		this.toggleListeners();
		this.isOpen = false;
		return this;
	},

	trash: function() {
		this.element = this.asset = null;
		this.content.empty();
		this.options = {};
		this.removeEvents().setOptions(this.presets).callChain();
	},

	onError: function() {
		this.asset = null;
		this.setContent('string', this.options.errorMsg || 'An error occurred');
	},

	setContent: function(handler, content) {
		if (!this.handlers[handler]) return false;
		this.content.className = 'sbox-content-' + handler;
		this.applyTimer = this.applyContent.delay(this.fx.overlay.options.duration, this, this.handlers[handler].call(this, content));
		if (this.overlay.retrieve('opacity')) return this;
		this.toggleOverlay(true);
		this.fx.overlay.start(this.options.overlayOpacity);
		return this.reposition();
	},

	applyContent: function(content, size) {
		if (!this.isOpen && !this.applyTimer) return;
		this.applyTimer = $clear(this.applyTimer);
		this.hideContent();
		if (!content) {
			this.toggleLoading(true);
		} else {
			if (this.isLoading) this.toggleLoading(false);
			this.fireEvent('onUpdate', [this.content], 20);
		}
		if (content) {
			if (['string', 'array'].contains($type(content))) this.content.set('html', content);
			else if (!this.content.hasChild(content)) this.content.adopt(content);
		}
		this.callChain();
		if (!this.isOpen) {
			this.toggleListeners(true);
			this.resize(size, true);
			this.isOpen = true;
			this.fireEvent('onOpen', [this.content]);
		} else {
			this.resize(size);
		}
	},

	resize: function(size, instantly) {
		this.showTimer = $clear(this.showTimer || null);
		var box = this.doc.getSize(), scroll = this.doc.getScroll();
		this.size = $merge((this.isLoading) ? this.options.sizeLoading : this.options.size, size);
		var to = {
			width: this.size.x,
			height: this.size.y,
			left: (scroll.x + (box.x - this.size.x - this.options.marginInner.x) / 2).toInt(),
			top: (scroll.y + (box.y - this.size.y - this.options.marginInner.y) / 2).toInt()
		};
		this.hideContent();
		if (!instantly) {
			this.fx.win.start(to).chain(this.showContent.bind(this));
		} else {
			this.win.setStyles(to).setStyle('display', '');
			this.showTimer = this.showContent.delay(50, this);
		}
		return this.reposition();
	},

	toggleListeners: function(state) {
		var fn = (state) ? 'addEvent' : 'removeEvent';
		this.closeBtn[fn]('click', this.bound.close);
		this.overlay[fn]('click', this.bound.close);
		this.doc[fn]('keydown', this.bound.key)[fn]('mousewheel', this.bound.scroll);
		this.doc.getWindow()[fn]('resize', this.bound.window)[fn]('scroll', this.bound.window);
	},

	toggleLoading: function(state) {
		this.isLoading = state;
		this.win[(state) ? 'addClass' : 'removeClass']('sbox-loading');
		if (state) this.fireEvent('onLoading', [this.win]);
	},

	toggleOverlay: function(state) {
		var full = this.doc.getSize().x;
		this.overlay.setStyle('display', (state) ? '' : 'none');
		this.doc.body[(state) ? 'addClass' : 'removeClass']('body-overlayed');
		if (state) {
			this.scrollOffset = this.doc.getWindow().getSize().x - full;
			this.doc.body.setStyle('margin-right', this.scrollOffset);
		} else {
			this.doc.body.setStyle('margin-right', '');
		}
	},

	showContent: function() {
		if (this.content.get('opacity')) this.fireEvent('onShow', [this.win]);
		this.fx.content.start(1);
	},

	hideContent: function() {
		if (!this.content.get('opacity')) this.fireEvent('onHide', [this.win]);
		this.fx.content.cancel().set(0);
	},

	onKey: function(e) {
		switch (e.key) {
			case 'esc': this.close(e);
			case 'up': case 'down': return false;
		}
	},

	checkTarget: function(e) {
		return this.content.hasChild(e.target);
	},

	reposition: function() {
		var size = this.doc.getSize(), scroll = this.doc.getScroll(), ssize = this.doc.getScrollSize();
		this.overlay.setStyles({
			width: ssize.x + 'px',
			height: ssize.y + 'px'
		});
		this.win.setStyles({
			left: (scroll.x + (size.x - this.win.offsetWidth) / 2 - this.scrollOffset).toInt() + 'px',
			top: (scroll.y + (size.y - this.win.offsetHeight) / 2).toInt() + 'px'
		});
		return this.fireEvent('onMove', [this.overlay, this.win]);
	},

	removeEvents: function(type){
		if (!this.$events) return this;
		if (!type) this.$events = null;
		else if (this.$events[type]) this.$events[type] = null;
		return this;
	},

	extend: function(properties) {
		return $extend(this, properties);
	},

	handlers: new Hash(),

	parsers: new Hash()

};

SqueezeBox.extend(new Events($empty)).extend(new Options($empty)).extend(new Chain($empty));

SqueezeBox.parsers.extend({

	image: function(preset) {
		return (preset || (/\.(?:jpg|png|gif)$/i).test(this.url)) ? this.url : false;
	},

	clone: function(preset) {
		if ($(this.options.target)) return $(this.options.target);
		if (this.element && !this.element.parentNode) return this.element;
		var bits = this.url.match(/#([\w-]+)$/);
		return (bits) ? $(bits[1]) : (preset ? this.element : false);
	},

	ajax: function(preset) {
		return (preset || (this.url && !(/^(?:javascript|#)/i).test(this.url))) ? this.url : false;
	},

	iframe: function(preset) {
		return (preset || this.url) ? this.url : false;
	},

	string: function(preset) {
		return true;
	}
});

SqueezeBox.handlers.extend({

	image: function(url) {
		var size, tmp = new Image();
		this.asset = null;
		tmp.onload = tmp.onabort = tmp.onerror = (function() {
			tmp.onload = tmp.onabort = tmp.onerror = null;
			if (!tmp.width) {
				this.onError.delay(10, this);
				return;
			}
			var box = this.doc.getSize();
			box.x -= this.options.marginImage.x;
			box.y -= this.options.marginImage.y;
			size = {x: tmp.width, y: tmp.height};
			for (var i = 2; i--;) {
				if (size.x > box.x) {
					size.y *= box.x / size.x;
					size.x = box.x;
				} else if (size.y > box.y) {
					size.x *= box.y / size.y;
					size.y = box.y;
				}
			}
			size.x = size.x.toInt();
			size.y = size.y.toInt();
			this.asset = $(tmp);
			tmp = null;
			this.asset.width = size.x;
			this.asset.height = size.y;
			this.applyContent(this.asset, size);
		}).bind(this);
		tmp.src = url;
		if (tmp && tmp.onload && tmp.complete) tmp.onload();
		return (this.asset) ? [this.asset, size] : null;
	},

	clone: function(el) {
		if (el) return el.clone();
		return this.onError();
	},

	adopt: function(el) {
		if (el) return el;
		return this.onError();
	},

	ajax: function(url) {
		var options = this.options.ajaxOptions || {};
		this.asset = new Request.HTML($merge({
			method: 'get',
			evalScripts: false
		}, this.options.ajaxOptions)).addEvents({
			onSuccess: function(resp) {
				this.applyContent(resp);
				if (options.evalScripts !== null && !options.evalScripts) $exec(this.asset.response.javascript);
				this.fireEvent('onAjax', [resp, this.asset]);
				this.asset = null;
			}.bind(this),
			onFailure: this.onError.bind(this)
		});
		this.asset.send.delay(10, this.asset, [{url: url}]);
	},

	iframe: function(url) {
		this.asset = new Element('iframe', $merge({
			src: url,
			frameBorder: 0,
			width: this.options.size.x,
			height: this.options.size.y
		}, this.options.iframeOptions));
		if (this.options.iframePreload) {
			this.asset.addEvent('load', function() {
				this.applyContent(this.asset.setStyle('display', ''));
			}.bind(this));
			this.asset.setStyle('display', 'none').inject(this.content);
			return false;
		}
		return this.asset;
	},

	string: function(str) {
		return str;
	}

});

SqueezeBox.handlers.url = SqueezeBox.handlers.ajax;
SqueezeBox.parsers.url = SqueezeBox.parsers.ajax;
SqueezeBox.parsers.adopt = SqueezeBox.parsers.clone;
/**
 * A thin wrapper around SqueezeBox lightbox script. Just include to a published
 * page and WbnSqueezeBox will take care of the rest. It will load SqueezeBox
 * css lazily when and if there are any elements for which need to be
 * lightboxified.
 */
var WbnSqueezeBox = new new Class({

	Implements: Options,

	options: {
		elements: {
			images: '.wbn-image a.wbn-popup'
		},
		cssFile: '/skins/default/squeezebox/SqueezeBox.css',
		document: document
	},

	initialize: function(options) {
		this.setOptions(options);
		window.addEvent('domready', this.onDomReady.bind(this));
	},

	onDomReady: function() {
		var targets = $$(this.options.elements.images);

		if (targets.length === 0) {
			return;
		}

		this.loadStyles();

		SqueezeBox.initialize();
		SqueezeBox.assign(targets);
	},

	loadStyles: function() {
		// vanilla dom, cause the mootools way using new Element() crashes ie
		// when rel and href are used together. damn you, ie!
		// see http://www.nabble.com/attr()-on-%3Clink%3E-crashing-IE6-td24575357s27240.html
		var style = document.createElement('link');
		style.type  = 'text/css';
		style.rel   = 'stylesheet';
		style.media = 'screen';
		style.href  = this.options.cssFile;

		var head = $(this.options.document).getElement("head");
		head.adopt(style);
	}
});

