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

License:
	MIT-style license.

Copyright:
	Copyright (c) 2006-2008 [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.2',
	'build': 'f0491d62fbb7e906789aa3733d6a67d43e5af7c9'
};

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 l = iterable.length, array = new Array(l);
		while (l--) array[l] = iterable[l];
		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 args = Array.slice(arguments);
	args.unshift({});
	return $mixin.apply(null, args);
};

function $mixin(mix){
	for (var i = 1, 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') ? $mixin(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 = $merge({

	Engine: {name: 'unknown', version: 0},

	Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},

	Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},

	Plugins: {},

	Engines: {

		presto: function(){
			return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
		},

		trident: function(){
			return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? 5 : 4);
		},

		webkit: function(){
			return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
		},

		gecko: function(){
			return (document.getBoxObjectFor == undefined) ? false : ((document.getElementsByClassName) ? 19 : 18);
		}

	}

}, Browser || {});

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

Browser.detect = function(){

	for (var engine in this.Engines){
		var version = this.Engines[engine]();
		if (version){
			this.Engine = {name: engine, version: version};
			this.Engine[engine] = this.Engine[engine + version] = true;
			break;
		}
	}

	return {name: engine, version: version};

};

Browser.detect();

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], 10) || 0, build: parseInt(version[2], 10) || 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[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : '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.trident && Browser.Engine.version <= 4) $try(function(){
			doc.execCommand("BackgroundImageCache", false, true);
		});
		if (Browser.Engine.trident) doc.window.attachEvent('onunload', function() {
			doc.window.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});
	},

	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){
		if (this[key] == 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)){
			doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? 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
			};
			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.
*/

function Class(params){
	
	if (params instanceof Function) params = {initialize: params};
	
	var newClass = function(){
		Object.reset(this);
		if (newClass._prototyping) return this;
		this._current = $empty;
		var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
		delete this._current; delete this.caller;
		return value;
	}.extend(this);
	
	newClass.implement(params);
	
	newClass.constructor = Class;
	newClass.prototype.constructor = newClass;

	return newClass;

};

Function.prototype.protect = function(){
	this._protected = true;
	return this;
};

Object.reset = function(object, key){
		
	if (key == null){
		for (var p in object) Object.reset(object, p);
		return object;
	}
	
	delete object[key];
	
	switch ($type(object[key])){
		case 'object':
			var F = function(){};
			F.prototype = object[key];
			var i = new F;
			object[key] = Object.reset(i);
		break;
		case 'array': object[key] = $unlink(object[key]); break;
	}
	
	return object;
	
};

new Native({name: 'Class', initialize: Class}).extend({

	instantiate: function(F){
		F._prototyping = true;
		var proto = new F;
		delete F._prototyping;
		return proto;
	},
	
	wrap: function(self, key, method){
		if (method._origin) method = method._origin;
		
		return function(){
			if (method._protected && this._current == null) throw new Error('The method "' + key + '" cannot be called.');
			var caller = this.caller, current = this._current;
			this.caller = current; this._current = arguments.callee;
			var result = method.apply(this, arguments);
			this._current = current; this.caller = caller;
			return result;
		}.extend({_owner: self, _origin: method, _name: key});

	}
	
});

Class.implement({
	
	implement: function(key, value){
		
		if ($type(key) == 'object'){
			for (var p in key) this.implement(p, key[p]);
			return this;
		}
		
		var mutator = Class.Mutators[key];
		
		if (mutator){
			value = mutator.call(this, value);
			if (value == null) return this;
		}
		
		var proto = this.prototype;

		switch ($type(value)){
			
			case 'function':
				if (value._hidden) return this;
				proto[key] = Class.wrap(this, key, value);
			break;
			
			case 'object':
				var previous = proto[key];
				if ($type(previous) == 'object') $mixin(previous, value);
				else proto[key] = $unlink(value);
			break;
			
			case 'array':
				proto[key] = $unlink(value);
			break;
			
			default: proto[key] = value;

		}
		
		return this;

	}
	
});

Class.Mutators = {
	
	Extends: function(parent){

		this.parent = parent;
		this.prototype = Class.instantiate(parent);

		this.implement('parent', function(){
			var name = this.caller._name, previous = this.caller._owner.parent.prototype[name];
			if (!previous) throw new Error('The method "' + name + '" has no parent.');
			return previous.apply(this, arguments);
		}.protect());

	},

	Implements: function(items){
		$splat(items).each(function(item){
			if (item instanceof Function) item = Class.instantiate(item);
			this.implement(item);
		}, 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({

	$events: {},

	addEvent: function(type, fn, internal){
		type = Events.removeOn(type);
		if (fn != $empty){
			this.$events[type] = this.$events[type] || [];
			this.$events[type].include(fn);
			if (internal) fn.internal = true;
		}
		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;
		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[type]) return this;
		if (!fn.internal) this.$events[type].erase(fn);
		return this;
	},

	removeEvents: function(events){
		var type;
		if ($type(events) == 'object'){
			for (type in events) this.removeEvent(type, events[type]);
			return this;
		}
		if (events) events = Events.removeOn(events);
		for (type in this.$events){
			if (events && events != 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;
	}

});

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.webkit && Browser.Engine.version < 420) ? 'innerHTML' : 'value'};

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

var clean = function(item, retain){
	if (!item) return;
	var uid = item.uid;
	if (Browser.Engine.trident){
		if (item.clearAttributes){
			var clone = retain && 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] = $empty;
			}
			Element.dispose(item);
		}
	}	
	if (!uid) return;
	collected[uid] = storage[uid] = null;
};

var purge = function(){
	Hash.each(collected, clean);
	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.webkit && Browser.Engine.version < 420)) ? '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'];

bools = bools.associate(bools);

Hash.extend(attributes, 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 (value == undefined) 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){
		return this.hasClass(className) ? this.removeClass(className) : this.addClass(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);
	},
	
	getSiblings: function(match, nocash) {
		return this.getParent().getChildren(match, nocash).erase(this);
	},

	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;
	},

	empty: function(){
		$A(this.childNodes).each(function(node){
			Element.destroy(node);
		});
		return this;
	},

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

	hasChild: function(el){
		el = $(el, true);
		if (!el) return false;
		if (Browser.Engine.webkit && Browser.Engine.version < 420) 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 || (tag == this) || (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 (dflt != undefined && prop == undefined) 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 = {
		table: [1, '<table>', '</table>'],
		select: [1, '<select>', '</select>'],
		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.webkit && Browser.Engine.version < 420) 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;
	},

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

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

	fireEvent: function(type, args, delay){
		var events = this.retrieve('events');
		if (!events || !events[type]) return this;
		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, 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 + ')';
		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';
		}
		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());
		}
		if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result, 10)))){
			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 null;
	},

	getOffsets: function(){		
		if (Browser.Engine.trident){
			var bound = this.getBoundingClientRect(), html = this.getDocument().documentElement;
			var isFixed = styleString(this, 'position') == 'fixed';
			return {
				x: bound.left + ((isFixed) ? 0 : html.scrollLeft) - html.clientLeft,
				y: bound.top +  ((isFixed) ? 0 : html.scrollTop)  - html.clientTop
			};
		}

		var element = this, position = {x: 0, y: 0};
		if (isBody(this)) return position;

		while (element && !isBody(element)){
			position.x += element.offsetLeft;
			position.y += element.offsetTop;

			if (Browser.Engine.gecko){
				if (!borderBox(element)){
					position.x += leftBorder(element);
					position.y += topBorder(element);
				}
				var parent = element.parentNode;
				if (parent && styleString(parent, 'overflow') != 'visible'){
					position.x += leftBorder(parent);
					position.y += topBorder(parent);
				}
			} else if (element != this && Browser.Engine.webkit){
				position.x += leftBorder(element);
				position.y += topBorder(element);
			}

			element = element.offsetParent;
		}
		if (Browser.Engine.gecko && !borderBox(this)){
			position.x -= leftBorder(this);
			position.y -= topBorder(this);
		}
		return position;
	},

	getPosition: function(relative){
		if (isBody(this)) return {x: 0, y: 0};
		var offset = this.getOffsets(), scroll = this.getScrolls();
		var position = {x: offset.x - scroll.x, y: offset.y - scroll.y};
		var relativePosition = (relative && (relative = $(relative))) ? relative.getPosition() : {x: 0, y: 0};
		return {x: position.x - relativePosition.x, y: position.y - relativePosition.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(){
		if (Browser.Engine.presto || Browser.Engine.webkit) {
			var win = this.getWindow();
			return {x: win.innerWidth, y: win.innerHeight};
		}
		var doc = getCompatElement(this);
		return {x: doc.clientWidth, y: doc.clientHeight};
	},

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

	getScrollSize: function(){
		var doc = getCompatElement(this), 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 styleString = Element.getComputedStyle;

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

function borderBox(element){
	return styleString(element, '-moz-box-sizing') == 'border-box';
};

function topBorder(element){
	return styleNumber(element, 'border-top-width');
};

function leftBorder(element){
	return styleNumber(element, 'border-left-width');
};

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

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 || (selector == this)) 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], 10);
		var a = (inta || inta === 0) ? inta : 1;
		var special = parsed[2] || false;
		var b = parseInt(parsed[3], 10) || 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);
	},
	
	selected: function() {
		return this.selected;
	}

});


/*
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);
};


/*
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(){
		if (!this.timer) return true;
		switch (this.options.link){
			case 'cancel': this.cancel(); return true;
			case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
		}
		return false;
	},

	start: function(from, to){
		if (!this.check(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)();
		value = (typeof value == 'string') ? value.split(' ') : $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 selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, 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(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(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,
		noCache: 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(){
		if (!this.running) return true;
		switch (this.options.link){
			case 'cancel': this.cancel(); return true;
			case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
		}
		return false;
	},

	send: function(options){
		if (!this.check(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(this.options.noCache) {
			var noCache = "noCache=" + new Date().getTime();
			data = (data) ? noCache + '&' + data : noCache;
		}


		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);

})();

/*
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,
		append: 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];
			if (!root) return null;
			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().set('html', response.html);
		else if (options.append) $(options.append).adopt(temp.getChildren());
		if (options.evalScripts) $exec(response.javascript);

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

});

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.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({

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

	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);
	}

});

Element.implement({
 disableSelection: function() {
  if(Browser.Engine.trident || Browser.Engine.webkit) {
   this.onselectstart = function() { return false; }
  }
  else if(Browser.Engine.gecko) {
   this.setStyle('-moz-user-select', 'none');
  }
  else {
   this.onmousedown = function() { return false; }
  }
  
  return this;
 },
 
 enableSelection: function() {
  if(Browser.Engine.trident || Browser.Engine.webkit) {
   this.onselectstart = function() { return true; }
  }
  else if(Browser.Engine.gecko) {
   this.setStyle('-moz-user-select', '');
  }
  else {
   this.onmousedown = function() { return true; }
  }
  return this;
 }
})

if (!window.console) {
  window.console = {
    log: function() {},
    warn: function() {},
    debug: function() {},
    info: function() {},
    error: function() {},
    assert: function() {},
    dir: function() {},
    dirxml: function() {},
    trace: function() {},
    group: function() {},
    groupEnd: function() {},
    time: function() {},
    timeEnd: function() {},
    profile: function() {},
    profileEnd: function() {},
    count: function() {}
  }
}

document.blur = function(){
  // Nie uzywam tutaj mootools specjalnie zeby nie tworzyc referencji i nie zuzywac dodatkowej pamieci
  var tmp = document.createElement('input');
  tmp.style.position = 'absolute';
  tmp.style.left = window.getScroll().x + 'px';
  tmp.style.top = window.getScroll().y + 'px';
  tmp.style.width = '1px';
  tmp.style.height = '1px';
  document.body.appendChild(tmp);
  tmp.focus();
  tmp.parentNode.removeChild(tmp);
  tmp = null;
}

/**
 * Dodanie funkcji string repeat - Birkin
 */
String.prototype.repeat = function(l){
  return new Array(l+1).join(this);
}

String.prototype.toIntZero = function(){
  var v = this.toInt();

  if (isNaN(v)) {
    return 0;
  }
  
  return v;
}
Number.prototype.toIntZero = function(){
  var v = this.toInt();

  if (isNaN(v)) {
    return 0;
  }
  
  return v;
}


//MooTools More, <http://mootools.net/more>. Copyright (c) 2006-2009 Aaron Newton <http://clientcide.com/>, Valerio Proietti <http://mad4milk.net> & the MooTools team <http://mootools.net/developers>, MIT Style License.

MooTools.More = {
	'version': '1.2.2.1'
};

/*
Script: MooTools.Lang.js
	Provides methods for localization.

	License:
		MIT-style license.

	Authors:
		Aaron Newton
*/

(function(){

	var data = {
		language: 'en-US',
		languages: {
			'en-US': {}
		},
		cascades: ['en-US']
	};
	
	var cascaded;

	MooTools.lang = new Events();

	$extend(MooTools.lang, {

		setLanguage: function(lang){
			if (!data.languages[lang]) return this;
			data.language = lang;
			this.load();
			this.fireEvent('langChange', lang);
			return this;
		},

		load: function() {
			var langs = this.cascade(this.getCurrentLanguage());
			cascaded = {};
			$each(langs, function(set, setName){
				cascaded[setName] = this.lambda(set);
			}, this);
		},

		getCurrentLanguage: function(){
			return data.language;
		},

		addLanguage: function(lang){
			data.languages[lang] = data.languages[lang] || {};
			return this;
		},

		cascade: function(lang){
			var cascades = (data.languages[lang] || {}).cascades || [];
			cascades.combine(data.cascades);
			cascades.erase(lang).push(lang);
			var langs = cascades.map(function(lng){
				return data.languages[lng];
			}, this);
			return $merge.apply(this, langs);
		},

		lambda: function(set) {
			(set || {}).get = function(key, args){
				return $lambda(set[key]).apply(this, $splat(args));
			};
			return set;
		},

		get: function(set, key, args){
			if (cascaded && cascaded[set]) return (key ? cascaded[set].get(key, args) : cascaded[set]);
		},

		set: function(lang, set, members){
			this.addLanguage(lang);
			langData = data.languages[lang];
			if (!langData[set]) langData[set] = {};
			$extend(langData[set], members);
			if (lang == this.getCurrentLanguage()){
				this.load();
				this.fireEvent('langChange', lang);
			}
			return this;
		},

		list: function(){
			return Hash.getKeys(data.languages);
		}

	});

})();

/*
Script: Log.js
	Provides basic logging functionality for plugins to implement.

	License:
		MIT-style license.

	Authors:
		Guillermo Rauch
*/

var Log = new Class({
	
	log: function(){
		Log.logger.call(this, arguments);
	}
	
});

Log.logged = [];

Log.logger = function(){
	if(window.console && console.log) console.log.apply(console, arguments);
	else Log.logged.push(arguments);
};

/*
Script: Class.Refactor.js
	Extends a class onto itself with new property, preserving any items attached to the class's namespace.

	License:
		MIT-style license.

	Authors:
		Aaron Newton
*/

Class.refactor = function(original, refactors){

	$each(refactors, function(item, name){
		var origin = original.prototype[name];
		if (origin && (origin = origin._origin) && typeof item == 'function') original.implement(name, function(){
			var old = this.previous;
			this.previous = origin;
			var value = item.apply(this, arguments);
			this.previous = old;
			return value;
		}); else original.implement(name, item);
	});

	return original;

};

/*
Script: Class.Binds.js
	Automagically binds specified methods in a class to the instance of the class.

	License:
		MIT-style license.

	Authors:
		Aaron Newton
*/

Class.Mutators.Binds = function(binds){
    return binds;
};

Class.Mutators.initialize = function(initialize){
	return function(){
		$splat(this.Binds).each(function(name){
			var original = this[name];
			if (original) this[name] = original.bind(this);
		}, this);
		return initialize.apply(this, arguments);
	};
};

/*
Script: Class.Occlude.js
	Prevents a class from being applied to a DOM element twice.

	License:
		MIT-style license.

	Authors:
		Aaron Newton
*/

Class.Occlude = new Class({

	occlude: function(property, element){
		element = $(element || this.element);
		var instance = element.retrieve(property || this.property);
		if (instance && !$defined(this.occluded)){
			this.occluded = instance;
		} else {
			this.occluded = false;
			element.store(property || this.property, this);
		}
		return this.occluded;
	}

});

/*
Script: Chain.Wait.js
	Adds a method to inject pauses between chained events.

	License:
		MIT-style license.

	Authors:
		Aaron Newton
*/

(function(){

	var wait = {
		wait: function(duration){
			return this.chain(function(){
				this.callChain.delay($pick(duration, 500), this);
			}.bind(this));
		}
	};

	Chain.implement(wait);

	if (window.Fx){
		Fx.implement(wait);
		['Css', 'Tween', 'Elements'].each(function(cls){
			if (Fx[cls]) Fx[cls].implement(wait);
		});
	}

	try {
		Element.implement({
			chains: function(effects){
				$splat($pick(effects, ['tween', 'morph', 'reveal'])).each(function(effect){
					effect = this.get(effect);
					if (!effect) return;
					effect.setOptions({
						link:'chain'
					});
				}, this);
				return this;
			},
			pauseFx: function(duration, effect){
				this.chains(effect).get($pick(effect, 'tween')).wait(duration);
				return this;
			}
		});
	} catch(e){}

})();

/*
Script: Array.Extras.js
	Extends the Array native object to include useful methods to work with arrays.

	License:
		MIT-style license.

	Authors:
		Christoph Pojer

*/
Array.implement({

	min: function(){
		return Math.min.apply(null, this);
	},

	max: function(){
		return Math.max.apply(null, this);
	},

	average: function(){
		return this.length ? this.sum() / this.length : 0;
	},

	sum: function(){
		var result = 0, l = this.length;
		if (l){
			do {
				result += this[--l];
			} while (l);
		}
		return result;
	},

	unique: function(){
		return [].combine(this);
	}

});

/*
Script: Date.js
	Extends the Date native object to include methods useful in managing dates.

	License:
		MIT-style license.

	Authors:
		Aaron Newton
		Nicholas Barthelemy - https://svn.nbarthelemy.com/date-js/
		Harald Kirshner - mail [at] digitarald.de; http://digitarald.de

*/

(function(){

new Native({name: 'Date', initialize: Date, protect: true});

['now','parse','UTC'].each(function(method){
	Native.genericize(Date, method, true);
});

Date.Methods = {};

['Date', 'Day', 'FullYear', 'Hours', 'Milliseconds', 'Minutes', 'Month', 'Seconds', 'Time', 'TimezoneOffset',
	'Week', 'Timezone', 'GMTOffset', 'DayOfYear', 'LastMonth', 'UTCDate', 'UTCDay', 'UTCFullYear',
	'AMPM', 'UTCHours', 'UTCMilliseconds', 'UTCMinutes', 'UTCMonth', 'UTCSeconds'].each(function(method){
	Date.Methods[method.toLowerCase()] = method;
});

$each({
	ms: 'Milliseconds',
	year: 'FullYear',
	min: 'Minutes',
	mo: 'Month',
	sec: 'Seconds',
	hr: 'Hours'
}, function(value, key){
	Date.Methods[key] = value;
});

var zeroize = function(what, length){
	return '0'.repeat(length - what.toString().length) + what;
};

Date.implement({

	set: function(prop, value){
		switch ($type(prop)){
			case 'object':
				for (var p in prop) this.set(p, prop[p]);
				break;
			case 'string':
				prop = prop.toLowerCase();
				var m = Date.Methods;
				if (m[prop]) this['set' + m[prop]](value);
		}
		return this;
	},

	get: function(key){
		key = key.toLowerCase();
		var m = Date.Methods;
		if (m[key]) return this['get' + m[key]]();
		return null;
	},

	clone: function(){
		return new Date(this.get('time'));
	},

	increment: function(interval, times){
		return this.multiply(interval, times);
	},

	decrement: function(interval, times){
		return this.multiply(interval, times, false);
	},

	multiply: function(interval, times, increment){
		interval = interval || 'day';
		times = $pick(times, 1);
		increment = $pick(increment, true);
		var multiplier = increment ? 1 : -1;
		var month = this.format('%m').toInt() - 1;
		var year = this.format('%Y').toInt();
		var time = this.get('time');
		var offset = 0;
		switch (interval) {
				case 'year':
					times.times(function(val) {
						if (Date.isLeapYear(year+val) && month > 1 && multiplier > 0) val++;
						if (Date.isLeapYear(year+val) && month <= 1 && multiplier < 0) val--;
						offset += Date.units.year(year+val);
					});
					break;
				case 'month':
					times.times(function(val){
						if (multiplier < 0) val++;
						var mo = month+(val * multiplier);
						var year = year;
						if (mo < 0) {
							year--;
							mo = 12+mo;
						}
						if (mo > 11 || mo < 0) {
							year += (mo / 12).toInt() * multiplier;
							mo = mo % 12;
						}
						offset += Date.units.month(mo, year);
					});
					break;
				case 'day':
					return this.set('date', this.get('date')+(multiplier*times));
				default:
					offset = Date.units[interval]() * times;
					break;
		}
		this.set('time', time + (offset * multiplier));
		return this;
	},

	isLeapYear: function(){
		return Date.isLeapYear(this.get('year'));
	},

	clearTime: function(){
		['hr', 'min', 'sec', 'ms'].each(function(t){
			this.set(t, 0);
		}, this);
		return this;
	},

	diff: function(d, resolution){
		resolution = resolution || 'day';
		if ($type(d) == 'string') d = Date.parse(d);
		switch (resolution){
			case 'year':
				return d.format('%Y').toInt() - this.format('%Y').toInt();
				break;
			case 'month':
				var months = (d.format('%Y').toInt() - this.format('%Y').toInt())*12;
				return months + d.format('%m').toInt() - this.format('%m').toInt();
				break;
			default:
				var diff = d.get('time') - this.get('time');
				if (diff < 0 && Date.units[resolution]() > (-1*(diff))) return 0;
				else if (diff >= 0 && diff < Date.units[resolution]()) return 0;
				return ((d.get('time') - this.get('time')) / Date.units[resolution]()).round();
		}
		return null;
	},

	getWeek: function(){
		var day = (new Date(this.get('year'), 0, 1)).get('date');
		return Math.round((this.get('dayofyear') + (day > 3 ? day - 4 : day + 3)) / 7);
	},

	getTimezone: function(){
		return this.toString()
			.replace(/^.*? ([A-Z]{3}).[0-9]{4}.*$/, '$1')
			.replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, '$1$2$3');
	},

	getGMTOffset: function(){
		var off = this.get('timezoneOffset');
		return ((off > 0) ? '-' : ' + ')
			+ zeroize(Math.floor(Math.abs(off) / 60), 2)
			+ zeroize(off % 60, 2);
	},

	parse: function(str){
		this.set('time', Date.parse(str));
		return this;
	},

	isValid: function(date) {
		return !!(date || this).valueOf();
	},

	format: function(f){
		if (!this.isValid()) return 'invalid date';
		f = f || '%x %X';
		//replace short-hand with actual format
		f = ({
			db: '%Y-%m-%d %H:%M:%S',
			compact: '%Y%m%dT%H%M%S',
			iso8601: '%Y-%m-%dT%H:%M:%S%T',
			rfc822: '%a, %d %b %Y %H:%M:%S %Z',
			'short': '%d %b %H:%M',
			'long': '%B %d, %Y %H:%M'
		})[f.toLowerCase()] || f;
		var d = this;
		return f.replace(/\%([aAbBcdHIjmMpSUWwxXyYTZ\%])/g,
			function($1, $2){
				switch ($2){
					case 'a': return Date.getMsg('days')[d.get('day')].substr(0, 3);
					case 'A': return Date.getMsg('days')[d.get('day')];
					case 'b': return Date.getMsg('months')[d.get('month')].substr(0, 3);
					case 'B': return Date.getMsg('months')[d.get('month')];
					case 'c': return d.toString();
					case 'd': return zeroize(d.get('date'), 2);
					case 'H': return zeroize(d.get('hr'), 2);
					case 'I': return ((d.get('hr') % 12) || 12);
					case 'j': return zeroize(d.get('dayofyear'), 3);
					case 'm': return zeroize((d.get('mo') + 1), 2);
					case 'M': return zeroize(d.get('min'), 2);
					case 'p': return Date.getMsg(d.get('hr') < 12 ? 'AM' : 'PM');
					case 'S': return zeroize(d.get('seconds'), 2);
					case 'U': return zeroize(d.get('week'), 2);
					case 'W': throw new Error('%W is not supported yet');
					case 'w': return d.get('day');
					case 'x': return d.format(Date.getMsg('shortDate'));
					case 'X': return d.format(Date.getMsg('shortTime'));
					case 'y': return d.get('year').toString().substr(2);
					case 'Y': return d.get('year');
					case 'T': return d.get('GMTOffset');
					case 'Z': return d.get('Timezone');
					case '%': return '%';
				}
				return $2;
			}
		);
	},

	setAMPM: function(ampm){
		ampm = ampm.toUpperCase();
		if (this.format('%H').toInt() > 11 && ampm == 'AM')
			return this.decrement('hour', 12);
		else if (this.format('%H').toInt() < 12 && ampm == 'PM')
			return this.increment('hour', 12);
		return this;
	}

});

Date.alias('diff', 'compare');
Date.alias('format', 'strftime');

var nativeParse = Date.parse;

var daysInMonth = function(monthIndex, year){
	if (Date.isLeapYear(year.toInt()) && monthIndex === 1) return 29;
	return [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][monthIndex];
};


$extend(Date, {

	getMsg: function(key, args) {
		return MooTools.lang.get('Date', key, args);
	},

	units: {
		ms: $lambda(1),
		second: $lambda(1000),
		minute: $lambda(60000),
		hour: $lambda(3600000),
		day: $lambda(86400000),
		week: $lambda(608400000),
		month: function(monthIndex, year){
			var d = new Date();
			return daysInMonth($pick(monthIndex,d.format('%m').toInt()), $pick(year,d.format('%Y').toInt())) * 86400000;
		},
		year: function(year){
			year = year || new Date().format('%Y').toInt();
			return Date.isLeapYear(year.toInt()) ? 31622400000 : 31536000000;
		}
	},

	isLeapYear: function(yr){
		return new Date(yr , 1, 29).getDate() == 29;
	},

	fixY2K: function(d){
		if (!isNaN(d)){
			var newDate = new Date(d);
			if (newDate.get('year') < 2000 && d.toString().indexOf(newDate.get('year')) < 0) newDate.increment('year', 100);
			return newDate;
		} else {
			return d;
		}
	},

	parse: function(from){
		var t = $type(from);
		if (t == 'number') return new Date(from);
		if (t != 'string') return from;
		if (!from.length) return null;
		var parsed;
		Date.parsePatterns.each(function(pattern, i){
			if (parsed) return;
			var r = pattern.re.exec(from);
			if (r) parsed = pattern.handler(r);
		});
		return parsed || new Date(nativeParse(from));
	},

	parseDay: function(day, num){
		var ret = -1;
		switch ($type(day)){
			case 'number':
				ret = Date.getMsg('days')[day - 1] || false;
				if (!ret) throw new Error('Invalid day index value must be between 1 and 7');
				break;
			case 'string':
				var match = Date.getMsg('days').filter(function(name){
					return this.test(name);
				}, new RegExp('^' + day, 'i'));
				if (!match.length) throw new Error('Invalid day string');
				if (match.length > 1) throw new Error('Ambiguous day');
				ret = match[0];
		}
		return (num) ? Date.getMsg('days').indexOf(ret) : ret;
	},

	parseMonth: function(month, num){
		var ret = -1;
		switch ($type(month)){
			case 'object':
				ret = Date.getMsg('months')[month.get('mo')];
				break;
			case 'number':
				ret = Date.getMsg('months')[month - 1] || false;
				if (!ret) throw new Error('Invalid month index value must be between 1 and 12:' + index);
				break;
			case 'string':
				var match = Date.getMsg('months').filter(function(name){
					return this.test(name);
				}, new RegExp('^' + month, 'i'));
				if (!match.length) throw new Error('Invalid month string');
				if (match.length > 1) throw new Error('Ambiguous month');
				ret = match[0];
		}
		return (num) ? Date.getMsg('months').indexOf(ret) : ret;
	},

	parseUTC: function(value){
		var localDate = new Date(value);
		var utcSeconds = Date.UTC(localDate.get('year'), localDate.get('mo'),
		localDate.get('date'), localDate.get('hr'), localDate.get('min'), localDate.get('sec'));
		return new Date(utcSeconds);
	},

	orderIndex: function(unit){
		return Date.getMsg('dateOrder').indexOf(unit) + 1;
	},

	parsePatterns: [
		{
			//"1999-12-31"
			re: /^(\d{4})[\.\-\/](\d{1,2})[\.\-\/](\d{1,2})$/,
			handler: function(bits){
				return new Date(bits[1], bits[2] - 1, bits[3]);
			}
		},
		{
			//"1999-12-31 23:59:59"
			re: /^(\d{4})[\.\-\/](\d{1,2})[\.\-\/](\d{1,2})\s(\d{1,2}):(\d{1,2})(?:\:(\d{1,2}))?(\w{2})?$/,
			handler: function(bits){
				var d = new Date(bits[1], bits[2] - 1, bits[3]);
				d.set('hr', bits[4]);
				d.set('min', bits[5]);
				d.set('sec', bits[6] || 0);
				if (bits[7]) d.set('ampm', bits[7]);
				return d;
			}
		},
		{
			//"12.31.08", "12-31-08", "12/31/08", "12.31.2008", "12-31-2008", "12/31/2008"
			re: /^(\d{1,2})[\.\-\/](\d{1,2})[\.\-\/](\d{2,4})$/,
			handler: function(bits){
				var d = new Date(bits[Date.orderIndex('year')],
								 bits[Date.orderIndex('month')] - 1,
								 bits[Date.orderIndex('date')]);
				return Date.fixY2K(d);
			}
		},
		//"12.31.08", "12-31-08", "12/31/08", "12.31.2008", "12-31-2008", "12/31/2008"
		//above plus "10:45pm" ex: 12.31.08 10:45pm
		{
			re: /^(\d{1,2})[\.\-\/](\d{1,2})[\.\-\/](\d{2,4})\s(\d{1,2})[:\.](\d{1,2})(?:[\:\.](\d{1,2}))?(\w{2})?$/,
			handler: function(bits){
				var d = new Date(bits[Date.orderIndex('year')],
								 bits[Date.orderIndex('month')] - 1,
								 bits[Date.orderIndex('date')]);
				d.set('hr', bits[4]);
				d.set('min', bits[5]);
				d.set('sec', bits[6] || 0);
				if (bits[7]) d.set('ampm', bits[7]);
				return Date.fixY2K(d);
			}
		}
	]

});

})();

/*
Script: Date.Extras.js
	Extends the Date native object to include extra methods (on top of those in Date.js).

	License:
		MIT-style license.

	Authors:
		Aaron Newton

*/

['LastDayOfMonth', 'Ordinal'].each(function(method){
	Date.Methods[method.toLowerCase()] = method;
});


Date.implement({

	timeDiffInWords: function(relative_to){
		return Date.distanceOfTimeInWords(this, relative_to || new Date);
	},

	getOrdinal: function(dayOfMonth){
		return Date.getMsg('ordinal', dayOfMonth || this.get('date'));
	},

	getDayOfYear: function(){
		return ((Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 1, 0, 0, 0)
			- Date.UTC(this.getFullYear(), 0, 1, 0, 0, 0) ) / Date.units.day());
	},

	getLastDayOfMonth: function(){
		var ret = this.clone();
		ret.setMonth(ret.getMonth() + 1, 0);
		return ret.getDate();
	}

});

Date.alias('timeDiffInWords', 'timeAgoInWords');

$extend(Date, {

	distanceOfTimeInWords: function(fromTime, toTime){
		return this.getTimePhrase(((toTime.getTime() - fromTime.getTime()) / 1000).toInt(), fromTime, toTime);
	},

	getTimePhrase: function(delta, fromTime, toTime){
		var getPhrase = function(){
			var suffix;
			if (delta >= 0){
				suffix = 'Ago';
			} else {
				delta = delta * -1;
				suffix = 'Until';
			}
			if (delta < 60){
				return Date.getMsg('lessThanMinute' + suffix, delta);
			} else if (delta < 120){
				return Date.getMsg('minute' + suffix, delta);
			} else if (delta < (45 * 60)){
				delta = (delta / 60).round();
				return Date.getMsg('minutes' + suffix, delta);
			} else if (delta < (90 * 60)){
				return Date.getMsg('hour' + suffix, delta);
			} else if (delta < (24 * 60 * 60)){
				delta = (delta / 3600).round();
				return Date.getMsg('hours' + suffix, delta);
			} else if (delta < (48 * 60 * 60)){
				return Date.getMsg('day' + suffix, delta);
			} else {
				delta = (delta / 86400).round();
				return Date.getMsg('days' + suffix, delta);
			}
		};
		return getPhrase().substitute({delta: delta});
	}

});


Date.parsePatterns.extend([

	{
		// yyyy-mm-ddTHH:MM:SS-0500 (ISO8601) i.e.2007-04-17T23:15:22Z
		// inspired by: http://delete.me.uk/2005/03/iso8601.html
		re: /^(\d{4})(?:-?(\d{2})(?:-?(\d{2})(?:[T ](\d{2})(?::?(\d{2})(?::?(\d{2})(?:\.(\d+))?)?)?(?:Z|(?:([-+])(\d{2})(?::?(\d{2}))?)?)?)?)?)?$/,
		handler: function(bits){
			var offset = 0;
			var d = new Date(bits[1], 0, 1);
			if (bits[3]) d.set('date', bits[3]);
			if (bits[2]) d.set('mo', bits[2] - 1);
			if (bits[4]) d.set('hr', bits[4]);
			if (bits[5]) d.set('min', bits[5]);
			if (bits[6]) d.set('sec', bits[6]);
			if (bits[7]) d.set('ms', ('0.' + bits[7]).toInt() * 1000);
			if (bits[9]){
				offset = (bits[9].toInt() * 60) + bits[10].toInt();
				offset *= ((bits[8] == '-') ? 1 : -1);
			}
			//offset -= d.getTimezoneOffset();
			d.setTime((d * 1) + (offset * 60 * 1000).toInt());
			return d;
		}
	},

	{
		//"today"
		re: /^tod/i,
		handler: function(){
			return new Date();
		}
	},

	{
		//"tomorow"
		re: /^tom/i,
		handler: function(){
			return new Date().increment();
		}
	},

	{
		//"yesterday"
		re: /^yes/i,
		handler: function(){
			return new Date().decrement();
		}
	},

	{
		//4th, 23rd
		re: /^(\d{1,2})(st|nd|rd|th)?$/i,
		handler: function(bits){
			var d = new Date();
			d.set('date', bits[1].toInt());
			return d;
		}
	},

	{
		//4th Jan, 23rd May
		re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+)$/i,
		handler: function(bits){
			var d = new Date();
			d.set('mo', Date.parseMonth(bits[2], true), bits[1].toInt());
			return d;
		}
	},

	{
		//4th Jan 2000, 23rd May 2004
		re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+),? (\d{4})$/i,
		handler: function(bits){
			var d = new Date();
			d.set('mo', Date.parseMonth(bits[2], true), bits[1].toInt());
			d.setYear(bits[3]);
			return d;
		}
	},

	{
		//Jan 4th
		re: /^(\w+) (\d{1,2})(?:st|nd|rd|th)?,? (\d{4})$/i,
		handler: function(bits){
			var d = new Date();
			d.set('mo', Date.parseMonth(bits[1], true), bits[2].toInt());
			d.setYear(bits[3]);
			return d;
		}
	},

	{
		//Jan 4th 2003
		re: /^next (\w+)$/i,
		handler: function(bits){
			var d = new Date();
			var day = d.getDay();
			var newDay = Date.parseDay(bits[1], true);
			var addDays = newDay - day;
			if (newDay <= day){
				addDays += 7;
			}
			d.set('date', d.getDate() + addDays);
			return d;
		}
	},

	{
		//4 May 08:12
		re: /^\d+\s[a-zA-z]..\s\d.\:\d.$/,
		handler: function(bits){
			var d = new Date();
			bits = bits[0].split(' ');
			d.set('date', bits[0]);
			var m;
			Date.getMsg('months').each(function(mo, i){
				if (new RegExp('^' + bits[1]).test(mo)) m = i;
			});
			d.set('mo', m);
			d.set('hr', bits[2].split(':')[0]);
			d.set('min', bits[2].split(':')[1]);
			d.set('ms', 0);
			return d;
		}
	},

	{
		re: /^last (\w+)$/i,
		handler: function(bits){
			return Date.parse('next ' + bits[0]).decrement('day', 7);
		}
	}

]);

/*
Script: Hash.Extras.js
	Extends the Hash native object to include getFromPath which allows a path notation to child elements.

	License:
		MIT-style license.

	Authors:
		Aaron Newton
*/

Hash.implement({

	getFromPath: function(notation){
		var source = this.getClean();
		notation.replace(/\[([^\]]+)\]|\.([^.[]+)|[^[.]+/g, function(match){
			if (!source) return null;
			var prop = arguments[2] || arguments[1] || arguments[0];
			source = (prop in source) ? source[prop] : null;
			return match;
		});
		return source;
	},

	cleanValues: function(method){
		method = method || $defined;
		this.each(function(v, k){
			if (!method(v)) this.erase(k);
		}, this);
		return this;
	},

	run: function(){
		var args = arguments;
		this.each(function(v, k){
			if ($type(v) == 'function') v.run(args);
		});
	}

});

/*
Script: String.Extras.js
	Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc).

	License:
		MIT-style license.

	Authors:
		Aaron Newton
		Guillermo Rauch

*/

(function(){
  
var special = ['À','à','Á','á','Â','â','Ã','ã','Ä','ä','Å','å','Ă','ă','Ą','ą','Ć','ć','Č','č','Ç','ç', 'Ď','ď','Đ','đ', 'È','è','É','é','Ê','ê','Ë','ë','Ě','ě','Ę','ę', 'Ğ','ğ','Ì','ì','Í','í','Î','î','Ï','ï', 'Ĺ','ĺ','Ľ','ľ','Ł','ł', 'Ñ','ñ','Ň','ň','Ń','ń','Ò','ò','Ó','ó','Ô','ô','Õ','õ','Ö','ö','Ø','ø','ő','Ř','ř','Ŕ','ŕ','Š','š','Ş','ş','Ś','ś', 'Ť','ť','Ť','ť','Ţ','ţ','Ù','ù','Ú','ú','Û','û','Ü','ü','Ů','ů', 'Ÿ','ÿ','ý','Ý','Ž','ž','Ź','ź','Ż','ż', 'Þ','þ','Ð','ð','ß','Œ','œ','Æ','æ','µ'];

var standard = ['A','a','A','a','A','a','A','a','Ae','ae','A','a','A','a','A','a','C','c','C','c','C','c','D','d','D','d', 'E','e','E','e','E','e','E','e','E','e','E','e','G','g','I','i','I','i','I','i','I','i','L','l','L','l','L','l', 'N','n','N','n','N','n', 'O','o','O','o','O','o','O','o','Oe','oe','O','o','o', 'R','r','R','r', 'S','s','S','s','S','s','T','t','T','t','T','t', 'U','u','U','u','U','u','Ue','ue','U','u','Y','y','Y','y','Z','z','Z','z','Z','z','TH','th','DH','dh','ss','OE','oe','AE','ae','u'];

var tidymap = {
	"[\xa0\u2002\u2003\u2009]": " ",
	"\xb7": "*",
	"[\u2018\u2019]": "'",
	"[\u201c\u201d]": '"',
	"\u2026": "...",
	"\u2013": "-",
	"\u2014": "--",
	"\uFFFD": "&raquo;"
};

String.implement({

	standardize: function(){
		var text = this;
		special.each(function(ch, i){
			text = text.replace(new RegExp(ch, 'g'), standard[i]);
		});
		return text;
	},

	repeat: function(times){
		return new Array(times + 1).join(this);
	},

	pad: function(length, str, dir){
		if (this.length >= length) return this;
		str = str || ' ';
		var pad = str.repeat(length - this.length).substr(0, length - this.length);
		if (!dir || dir == 'right') return this + pad;
		if (dir == 'left') return pad + this;
		return pad.substr(0, (pad.length / 2).floor()) + this + pad.substr(0, (pad.length / 2).ceil());
	},

	stripTags: function(){
		return this.replace(/<\/?[^>]+>/gi, '');
	},

	tidy: function(){
		var txt = this.toString();
		$each(tidymap, function(value, key){
			txt = txt.replace(new RegExp(key, 'g'), value);
		});
		return txt;
	}

});

})();

﻿/*
Script: String.QueryString.js
	...

	License:
		MIT-style license.

	Authors:
		Sebastian Markbåge, Aaron Newton, Lennart Pilon, Valerio Proietti
*/

String.implement({

	parseQueryString: function(){
		var vars = this.split(/[&;]/), res = {};
		if (vars.length) vars.each(function(val){
			var index = val.indexOf('='),
				keys = index < 0 ? [''] : val.substr(0, index).match(/[^\]\[]+/g),
				value = decodeURIComponent(val.substr(index + 1)),
				obj = res;
			keys.each(function(key, i){
				var current = obj[key];
				if(i < keys.length - 1)
					obj = obj[key] = current || {};
				else if($type(current) == 'array')
					current.push(value);
				else
					obj[key] = $defined(current) ? [current, value] : value;
			});
		});
		return res;
	},

	cleanQueryString: function(method){
		return this.split('&').filter(function(val){
			var index = val.indexOf('='),
			key = index < 0 ? '' : val.substr(0, index),
			value = val.substr(index + 1);
			return method ? method.run([key, value]) : $chk(value);
		}).join('&');
	}

});

/*
Script: URI.js
	Provides methods useful in managing the window location and uris.

	License:
		MIT-style license.

	Authors:
		Sebastian Markb�ge, Aaron Newton
*/

var URI = new Class({

	Implements: Options,

	/*
	options: {
		base: false
	},
	*/

	regex: /^(?:(\w+):)?(?:\/\/(?:(?:([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)?(\.\.?$|(?:[^?#\/]*\/)*)([^?#]*)(?:\?([^#]*))?(?:#(.*))?/,
	parts: ['scheme', 'user', 'password', 'host', 'port', 'directory', 'file', 'query', 'fragment'],
	schemes: { http: 80, https: 443, ftp: 21, rtsp: 554, mms: 1755, file: 0 },

	initialize: function(uri, options){
		this.setOptions(options);
		var base = this.options.base || URI.base;
		uri = uri || base;
		if (uri && uri.parsed)
			this.parsed = $unlink(uri.parsed);
		else
			this.set('value', uri.href || uri.toString(), base ? new URI(base) : false);
	},

	parse: function(value, base){
		var bits = value.match(this.regex);
		if (!bits) return false;
		bits.shift();
		return this.merge(bits.associate(this.parts), base);
	},

	merge: function(bits, base){
		if (!bits.scheme && !base.scheme) return false;
		if (base){
			this.parts.every(function(part){
				if (bits[part]) return false;
				bits[part] = base[part] || '';
				return true;
			});
		}
		bits.port = bits.port || this.schemes[bits.scheme.toLowerCase()];
		bits.directory = bits.directory ? this.parseDirectory(bits.directory, base ? base.directory : '') : '/';
		return bits;
	},

	parseDirectory: function(directory, baseDirectory) {
		directory = (directory.substr(0, 1) == '/' ? '' : (baseDirectory || '/')) + directory;
		if (!directory.test(URI.regs.directoryDot)) return directory;
		var result = [];
		directory.replace(URI.regs.endSlash, '').split('/').each(function(dir){
			if (dir == '..' && result.length > 0) result.pop();
			else if (dir != '.') result.push(dir);
		});
		return result.join('/') + '/';
	},

	combine: function(bits){
		return bits.value || bits.scheme + '://' +
			(bits.user ? bits.user + (bits.password ? ':' + bits.password : '') + '@' : '') +
			(bits.host || '') + (bits.port && bits.port != this.schemes[bits.scheme] ? ':' + bits.port : '') +
			(bits.directory || '/') + (bits.file || '') +
			(bits.query ? '?' + bits.query : '') +
			(bits.fragment ? '#' + bits.fragment : '');
	},

	set: function(part, value, base){
		if (part == 'value'){
			var scheme = value.match(URI.regs.scheme);
			if (scheme) scheme = scheme[1];
			if (scheme && !$defined(this.schemes[scheme.toLowerCase()])) this.parsed = { scheme: scheme, value: value };
			else this.parsed = this.parse(value, (base || this).parsed) || (scheme ? { scheme: scheme, value: value } : { value: value });
		} else {
			this.parsed[part] = value;
		}
		return this;
	},

	get: function(part, base){
		switch(part){
			case 'value': return this.combine(this.parsed, base ? base.parsed : false);
			case 'data' : return this.getData();
		}
		return this.parsed[part] || undefined;
	},

	go: function(){
		document.location.href = this.toString();
	},

	toURI: function(){
		return this;
	},

	getData: function(key, part){
		var qs = this.get(part || 'query');
		if (!$chk(qs)) return key ? null : {};
		var obj = qs.parseQueryString();
		return key ? obj[key] : obj;
	},

	setData: function(values, merge, part){
		if ($type(arguments[0]) == 'string'){ 
			values = this.getData(); 
			values[arguments[0]] = arguments[1]; 
		} else if (merge) {
			values = $merge(this.getData(), values);
		}
		return this.set(part || 'query', Hash.toQueryString(values));
	},

	clearData: function(part){
		return this.set(part || 'query', '');
	}

});

['toString', 'valueOf'].each(function(method){
	URI.prototype[method] = function(){
		return this.get('value');
	};
});


URI.regs = {
	endSlash: /\/$/,
	scheme: /^(\w+):/,
	directoryDot: /\.\/|\.$/
};

URI.base = new URI($$('base[href]').getLast(), { base: document.location });

String.implement({

	toURI: function(options){ return new URI(this, options); }

});

﻿/*
Script: URI.Relative.js
	Extends the URI class to add methods for computing relative and absolute urls.

	License:
		MIT-style license.

	Authors:
		Sebastian Markbåge
*/

URI = Class.refactor(URI, {

	combine: function(bits, base){
		if (!base || bits.scheme != base.scheme || bits.host != base.host || bits.port != base.port)
			return this.previous.apply(this, arguments);
		var end = bits.file + (bits.query ? '?' + bits.query : '') + (bits.fragment ? '#' + bits.fragment : '');

		if (!base.directory) return (bits.directory || (bits.file ? '' : './')) + end;

		var baseDir = base.directory.split('/'),
			relDir = bits.directory.split('/'),
			path = '',
			offset;

		var i = 0;
		for(offset = 0; offset < baseDir.length && offset < relDir.length && baseDir[offset] == relDir[offset]; offset++);
		for(i = 0; i < baseDir.length - offset - 1; i++) path += '../';
		for(i = offset; i < relDir.length - 1; i++) path += relDir[i] + '/';

		return (path || (bits.file ? '' : './')) + end;
	},

	toAbsolute: function(base){
		base = new URI(base);
		if (base) base.set('directory', '').set('file', '');
		return this.toRelative(base);
	},

	toRelative: function(base){
		return this.get('value', new URI(base));
	}

});

/*
Script: Element.Forms.js
	Extends the Element native object to include methods useful in managing inputs.

	License:
		MIT-style license.

	Authors:
		Aaron Newton

*/
Element.implement({

	tidy: function(){
		this.set('value', this.get('value').tidy());
	},

	getTextInRange: function(start, end){
		return this.get('value').substring(start, end);
	},

	getSelectedText: function(){
		if (document.selection && document.selection.createRange) return document.selection.createRange().text;
		return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd());
	},

	getSelectedRange: function() {
		if ($defined(this.selectionStart)) return {start: this.selectionStart, end: this.selectionEnd};
		var pos = {start: 0, end: 0};
		var range = this.getDocument().selection.createRange();
		if (!range || range.parentElement() != this) return pos;
		var dup = range.duplicate();
		if (this.type == 'text') {
			pos.start = 0 - dup.moveStart('character', -100000);
			pos.end = pos.start + range.text.length;
		} else {
			var value = this.get('value');
			var offset = value.length - value.match(/[\n\r]*$/)[0].length;
			dup.moveToElementText(this);
			dup.setEndPoint('StartToEnd', range);
			pos.end = offset - dup.text.length;
			dup.setEndPoint('StartToStart', range);
			pos.start = offset - dup.text.length;
		}
		return pos;
	},

	getSelectionStart: function(){
		return this.getSelectedRange().start;
	},

	getSelectionEnd: function(){
		return this.getSelectedRange().end;
	},

	setCaretPosition: function(pos){
		if (pos == 'end') pos = this.get('value').length;
		this.selectRange(pos, pos);
		return this;
	},

	getCaretPosition: function(){
		return this.getSelectedRange().start;
	},

	selectRange: function(start, end){
		if (this.createTextRange){
			var value = this.get('value');
			var diff = value.substr(start, end - start).replace(/\r/g, '').length;
			start = value.substr(0, start).replace(/\r/g, '').length;
			var range = this.createTextRange();
			range.collapse(true);
			range.moveEnd('character', start + diff);
			range.moveStart('character', start);
			range.select();
		} else {
			this.focus();
			this.setSelectionRange(start, end);
		}
		return this;
	},

	insertAtCursor: function(value, select){
		var pos = this.getSelectedRange();
		var text = this.get('value');
		this.set('value', text.substring(0, pos.start) + value + text.substring(pos.end, text.length));
		if ($pick(select, true)) this.selectRange(pos.start, pos.start + value.length);
		else this.setCaretPosition(pos.start + value.length);
		return this;
	},

	insertAroundCursor: function(options, select){
		options = $extend({
			before: '',
			defaultMiddle: '',
			after: ''
		}, options);
		var value = this.getSelectedText() || options.defaultMiddle;
		var pos = this.getSelectedRange();
		var text = this.get('value');
		if (pos.start == pos.end){
			this.set('value', text.substring(0, pos.start) + options.before + value + options.after + text.substring(pos.end, text.length));
			this.selectRange(pos.start + options.before.length, pos.end + options.before.length + value.length);
		} else {
			var current = text.substring(pos.start, pos.end);
			this.set('value', text.substring(0, pos.start) + options.before + current + options.after + text.substring(pos.end, text.length));
			var selStart = pos.start + options.before.length;
			if ($pick(select, true)) this.selectRange(selStart, selStart + current.length);
			else this.setCaretPosition(selStart + text.length);
		}
		return this;
	}

});

/*
Script: Element.Measure.js
	Extends the Element native object to include methods useful in measuring dimensions.

	Element.measure / .expose methods by Daniel Steigerwald
	License: MIT-style license.
	Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz

	License:
		MIT-style license.

	Authors:
		Aaron Newton

*/

Element.implement({

	measure: function(fn){
		var vis = function(el) {
			return !!(!el || el.offsetHeight || el.offsetWidth);
		};
		if (vis(this)) return fn.apply(this);
		var parent = this.getParent(),
			toMeasure = [], 
			restorers = [];
		while (!vis(parent) && parent != document.body) {
			toMeasure.push(parent.expose());
			parent = parent.getParent();
		}
		var restore = this.expose();
		var result = fn.apply(this);
		restore();
		toMeasure.each(function(restore){
			restore();
		});
		return result;
	},

	expose: function(){
		if (this.getStyle('display') != 'none') return $empty;
		var before = this.getStyles('display', 'position', 'visibility');
		return this.setStyles({
			display: 'block',
			position: 'absolute',
			visibility: 'hidden'
		}).setStyles.pass(before, this);
	},

	getDimensions: function(options){
		options = $merge({computeSize: false},options);
		var dim = {};
		var getSize = function(el, options){
			return (options.computeSize)?el.getComputedSize(options):el.getSize();
		};
		if (this.getStyle('display') == 'none'){
			dim = this.measure(function(){
				return getSize(this, options);
			});
		} else {
			try { //safari sometimes crashes here, so catch it
				dim = getSize(this, options);
			}catch(e){}
		}
		return $chk(dim.x) ? $extend(dim, {width: dim.x, height: dim.y}) : $extend(dim, {x: dim.width, y: dim.height});
	},

	getComputedSize: function(options){
		options = $merge({
			styles: ['padding','border'],
			plains: {
				height: ['top','bottom'],
				width: ['left','right']
			},
			mode: 'both'
		}, options);
		var size = {width: 0,height: 0};
		switch (options.mode){
			case 'vertical':
				delete size.width;
				delete options.plains.width;
				break;
			case 'horizontal':
				delete size.height;
				delete options.plains.height;
				break;
		}
		var getStyles = [];
		//this function might be useful in other places; perhaps it should be outside this function?
		$each(options.plains, function(plain, key){
			plain.each(function(edge){
				options.styles.each(function(style){
					getStyles.push((style == 'border') ? style + '-' + edge + '-' + 'width' : style + '-' + edge);
				});
			});
		});
		var styles = {};
		getStyles.each(function(style){ styles[style] = this.getComputedStyle(style); }, this);
		var subtracted = [];
		$each(options.plains, function(plain, key){ //keys: width, height, plains: ['left', 'right'], ['top','bottom']
			var capitalized = key.capitalize();
			size['total' + capitalized] = 0;
			size['computed' + capitalized] = 0;
			plain.each(function(edge){ //top, left, right, bottom
				size['computed' + edge.capitalize()] = 0;
				getStyles.each(function(style, i){ //padding, border, etc.
					//'padding-left'.test('left') size['totalWidth'] = size['width'] + [padding-left]
					if (style.test(edge)){
						styles[style] = styles[style].toInt() || 0; //styles['padding-left'] = 5;
						size['total' + capitalized] = size['total' + capitalized] + styles[style];
						size['computed' + edge.capitalize()] = size['computed' + edge.capitalize()] + styles[style];
					}
					//if width != width (so, padding-left, for instance), then subtract that from the total
					if (style.test(edge) && key != style &&
						(style.test('border') || style.test('padding')) && !subtracted.contains(style)){
						subtracted.push(style);
						size['computed' + capitalized] = size['computed' + capitalized]-styles[style];
					}
				});
			});
		});

		['Width', 'Height'].each(function(value){
			var lower = value.toLowerCase();
			if(!$chk(size[lower])) return;

			size[lower] = size[lower] + this['offset' + value] + size['computed' + value];
			size['total' + value] = size[lower] + size['total' + value];
			delete size['computed' + value];
		}, this);

		return $extend(styles, size);
	}

});

/*
Script: Element.Pin.js
	Extends the Element native object to include the pin method useful for fixed positioning for elements.

	License:
		MIT-style license.

	Authors:
		Aaron Newton
*/

(function(){
	var supportsPositionFixed = false;
	window.addEvent('domready', function(){
		var test = new Element('div').setStyles({
			position: 'fixed',
			top: 0,
			right: 0
		}).inject(document.body);
		supportsPositionFixed = (test.offsetTop === 0);
		test.dispose();
	});

	Element.implement({

		pin: function(enable){
			if (this.getStyle('display') == 'none') return null;
			
			var p;
			if (enable !== false){
				p = this.getPosition();
				if (!this.retrieve('pinned')){
					var pos = {
						top: p.y - window.getScroll().y,
						left: p.x - window.getScroll().x
					};
					if (supportsPositionFixed){
						this.setStyle('position', 'fixed').setStyles(pos);
					} else {
						this.store('pinnedByJS', true);
						this.setStyles({
							position: 'absolute',
							top: p.y,
							left: p.x
						});
						this.store('scrollFixer', (function(){
							if (this.retrieve('pinned'))
								this.setStyles({
									top: pos.top.toInt() + window.getScroll().y,
									left: pos.left.toInt() + window.getScroll().x
								});
						}).bind(this));
						window.addEvent('scroll', this.retrieve('scrollFixer'));
					}
					this.store('pinned', true);
				}
			} else {
				var op;
				if (!Browser.Engine.trident){
					if (this.getParent().getComputedStyle('position') != 'static') op = this.getParent();
					else op = this.getParent().getOffsetParent();
				}
				p = this.getPosition(op);
				this.store('pinned', false);
				var reposition;
				if (supportsPositionFixed && !this.retrieve('pinnedByJS')){
					reposition = {
						top: p.y + window.getScroll().y,
						left: p.x + window.getScroll().x
					};
				} else {
					this.store('pinnedByJS', false);
					window.removeEvent('scroll', this.retrieve('scrollFixer'));
					reposition = {
						top: p.y,
						left: p.x
					};
				}
				this.setStyles($merge(reposition, {position: 'absolute'}));
			}
			return this.addClass('isPinned');
		},

		unpin: function(){
			return this.pin(false).removeClass('isPinned');
		},

		togglepin: function(){
			this.pin(!this.retrieve('pinned'));
		}

	});

})();

/*
Script: Element.Position.js
	Extends the Element native object to include methods useful positioning elements relative to others.

	License:
		MIT-style license.

	Authors:
		Aaron Newton
*/

(function(){

var original = Element.prototype.position;

Element.implement({

	position: function(options){
		//call original position if the options are x/y values
		if (options && ($defined(options.x) || $defined(options.y))) return original ? original.apply(this, arguments) : this;
		$each(options||{}, function(v, k){ if (!$defined(v)) delete options[k]; });
		options = $merge({
			relativeTo: document.body,
			position: {
				x: 'center', //left, center, right
				y: 'center' //top, center, bottom
			},
			edge: false,
			offset: {x: 0, y: 0},
			returnPos: false,
			relFixedPosition: false,
			ignoreMargins: false,
			allowNegative: false
		}, options);
		//compute the offset of the parent positioned element if this element is in one
		var parentOffset = {x: 0, y: 0};
		var parentPositioned = false;
		/* dollar around getOffsetParent should not be necessary, but as it does not return
		 * a mootools extended element in IE, an error occurs on the call to expose. See:
		 * http://mootools.lighthouseapp.com/projects/2706/tickets/333-element-getoffsetparent-inconsistency-between-ie-and-other-browsers */
		var offsetParent = this.measure(function(){
			return $(this.getOffsetParent());
		});
		if (offsetParent && offsetParent != this.getDocument().body){
			parentOffset = offsetParent.measure(function(){
				return this.getPosition();
			});
			parentPositioned = true;
			options.offset.x = options.offset.x - parentOffset.x;
			options.offset.y = options.offset.y - parentOffset.y;
		}
		//upperRight, bottomRight, centerRight, upperLeft, bottomLeft, centerLeft
		//topRight, topLeft, centerTop, centerBottom, center
		var fixValue = function(option){
			if ($type(option) != 'string') return option;
			option = option.toLowerCase();
			var val = {};
			if (option.test('left')) val.x = 'left';
			else if (option.test('right')) val.x = 'right';
			else val.x = 'center';
			if (option.test('upper') || option.test('top')) val.y = 'top';
			else if (option.test('bottom')) val.y = 'bottom';
			else val.y = 'center';
			return val;
		};
		options.edge = fixValue(options.edge);
		options.position = fixValue(options.position);
		if (!options.edge){
			if (options.position.x == 'center' && options.position.y == 'center') options.edge = {x:'center', y:'center'};
			else options.edge = {x:'left', y:'top'};
		}

		this.setStyle('position', 'absolute');
		var rel = $(options.relativeTo) || document.body;
		var calc = rel == document.body ? window.getScroll() : rel.getPosition();
		var top = calc.y;
		var left = calc.x;

		if (Browser.Engine.trident){
			var scrolls = rel.getScrolls();
			top += scrolls.y;
			left += scrolls.x;
		}

		var dim = this.getDimensions({computeSize: true, styles:['padding', 'border','margin']});
		if (options.ignoreMargins){
			options.offset.x = options.offset.x - dim['margin-left'];
			options.offset.y = options.offset.y - dim['margin-top'];
		}
		var pos = {};
		var prefY = options.offset.y;
		var prefX = options.offset.x;
		var winSize = window.getSize();
		switch(options.position.x){
			case 'left':
				pos.x = left + prefX;
				break;
			case 'right':
				pos.x = left + prefX + rel.offsetWidth;
				break;
			default: //center
				pos.x = left + ((rel == document.body ? winSize.x : rel.offsetWidth)/2) + prefX;
				break;
		}
		switch(options.position.y){
			case 'top':
				pos.y = top + prefY;
				break;
			case 'bottom':
				pos.y = top + prefY + rel.offsetHeight;
				break;
			default: //center
				pos.y = top + ((rel == document.body ? winSize.y : rel.offsetHeight)/2) + prefY;
				break;
		}

		if (options.edge){
			var edgeOffset = {};

			switch(options.edge.x){
				case 'left':
					edgeOffset.x = 0;
					break;
				case 'right':
					edgeOffset.x = -dim.x-dim.computedRight-dim.computedLeft;
					break;
				default: //center
					edgeOffset.x = -(dim.x/2);
					break;
			}
			switch(options.edge.y){
				case 'top':
					edgeOffset.y = 0;
					break;
				case 'bottom':
					edgeOffset.y = -dim.y-dim.computedTop-dim.computedBottom;
					break;
				default: //center
					edgeOffset.y = -(dim.y/2);
					break;
			}
			pos.x = pos.x + edgeOffset.x;
			pos.y = pos.y + edgeOffset.y;
		}
		pos = {
			left: ((pos.x >= 0 || parentPositioned || options.allowNegative) ? pos.x : 0).toInt(),
			top: ((pos.y >= 0 || parentPositioned || options.allowNegative) ? pos.y : 0).toInt()
		};
		if (rel.getStyle('position') == 'fixed' || options.relFixedPosition){
			var winScroll = window.getScroll();
			pos.top = pos.top.toInt() + winScroll.y;
			pos.left = pos.left.toInt() + winScroll.x;
		}

		if (options.returnPos) return pos;
		else this.setStyles(pos);
		return this;
	}

});

})();

/*
Script: Element.Shortcuts.js
	Extends the Element native object to include some shortcut methods.

	License:
		MIT-style license.

	Authors:
		Aaron Newton

*/

Element.implement({

	isDisplayed: function(){
		return this.getStyle('display') != 'none';
	},

	toggle: function(){
		return this[this.isDisplayed() ? 'hide' : 'show']();
	},

	hide: function(){
		var d;
		try {
			//IE fails here if the element is not in the dom
			if ('none' != this.getStyle('display')) d = this.getStyle('display');
		} catch(e){}

		return this.store('originalDisplay', d || 'block').setStyle('display', 'none');
	},

	show: function(display){
		return this.setStyle('display', display || this.retrieve('originalDisplay') || 'block');
	},

	swapClass: function(remove, add){
		return this.removeClass(remove).addClass(add);
	}

});


/*
Script: OverText.js
	Shows text over an input that disappears when the user clicks into it. The text remains hidden if the user adds a value.

	License:
		MIT-style license.

	Authors:
		Aaron Newton
*/

var OverText = new Class({

	Implements: [Options, Events, Class.Occlude],

	Binds: ['reposition', 'assert', 'focus'],

	options: {/*
		textOverride: null,
		onFocus: $empty()
		onTextHide: $empty(textEl, inputEl),
		onTextShow: $empty(textEl, inputEl), */
		positionOptions: {
			position: 'upperLeft',
			edge: 'upperLeft',
			offset: {
				x: 4,
				y: 2
			}
		},
		poll: false,
		pollInterval: 250
	},

	property: 'OverText',

	initialize: function(element, options){
		this.element = $(element);
		if (this.occlude()) return this.occluded;
		this.setOptions(options);
		this.attach(this.element);
		OverText.instances.push(this);
		if (this.options.poll) this.poll();
		return this;
	},

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

	attach: function(){
		var val = this.options.textOverride || this.element.get('alt') || this.element.get('title');
		if (!val) return;
		this.text = new Element('div', {
			'class': 'overTxtDiv',
			styles: {
				lineHeight: 'normal',
				position: 'absolute'
			},
			html: val,
			events: {
				click: this.hide.pass(true, this)
			}
		}).inject(this.element, 'after');
		this.element.addEvents({
			focus: this.focus,
			blur: this.assert,
			change: this.assert
		}).store('OverTextDiv', this.text);
		window.addEvent('resize', this.reposition.bind(this));
		this.assert();
		this.reposition();
	},

	startPolling: function(){
		this.pollingPaused = false;
		return this.poll();
	},

	poll: function(stop){
		//start immediately
		//pause on focus
		//resumeon blur
		if (this.poller && !stop) return this;
		var test = function(){
			if (!this.pollingPaused) this.assert();
		}.bind(this);
		if (stop) $clear(this.poller);
		else this.poller = test.periodical(this.options.pollInterval, this);
		return this;
	},

	stopPolling: function(){
		this.pollingPaused = true;
		return this.poll(true);
	},

	focus: function(){
		if (!this.text.isDisplayed() || this.element.get('disabled')) return;
		this.hide();
	},

	hide: function(){
		if (this.text.isDisplayed() && !this.element.get('disabled')){
			this.text.hide();
			this.fireEvent('textHide', [this.text, this.element]);
			this.pollingPaused = true;
			try {
				this.element.fireEvent('focus').focus();
			} catch(e){} //IE barfs if you call focus on hidden elements
		}
		return this;
	},

	show: function(){
		if (!this.text.isDisplayed()){
			this.text.show();
			this.reposition();
			this.fireEvent('textShow', [this.text, this.element]);
			this.pollingPaused = false;
		}
		return this;
	},

	assert: function(){
		this[this.test() ? 'show' : 'hide']();
	},

	test: function(){
		var v = this.element.get('value');
		return !v;
	},

	reposition: function(){
		try {
			this.assert();
			if (!this.element.getParent() || !this.element.offsetHeight) return this.hide();
			if (this.test()) this.text.position($merge(this.options.positionOptions, {relativeTo: this.element}));
		} catch(e){	}
		return this;
	}

});

OverText.instances = [];

OverText.update = function(){

	return OverText.instances.map(function(ot){
		if (ot.element && ot.text) return ot.reposition();
		return null; //the input or the text was destroyed
	});

};

if (window.Fx && Fx.Reveal) {
	Fx.Reveal.implement({
		hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed, .overTxtDiv' : false
	});
}

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

	License:
		MIT-style license.

	Authors:
		Valerio Proietti
*/

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){
		for (var i in now){
			var iNow = 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(obj)) return this;
		var from = {}, to = {};
		for (var i in obj){
			var iProps = 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: Fx.Accordion.js
	An Fx.Elements extension which allows you to easily create accordion type controls.

	License:
		MIT-style license.

	Authors:
		Valerio Proietti
*/

var Accordion = Fx.Accordion = new Class({

	Extends: Fx.Elements,

	options: {/*
		onActive: $empty(toggler, section),
		onBackground: $empty(toggler, section),*/
		display: 0,
		show: false,
		height: true,
		width: false,
		opacity: true,
		fixedHeight: false,
		fixedWidth: false,
		wait: false,
		alwaysHide: false,
		trigger: 'click',
		initialDisplayFx: true
	},

	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, this.options.initialDisplayFx);
	},

	addSection: function(toggler, element){
		toggler = $(toggler);
		element = $(element);
		var test = this.togglers.contains(toggler);
		this.togglers.include(toggler);
		this.elements.include(element);
		var idx = this.togglers.indexOf(toggler);
		toggler.addEvent(this.options.trigger, 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, useFx){
		useFx = $pick(useFx, true);
		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 useFx ? this.start(obj) : this.set(obj);
	}

});

/*
Script: Fx.Move.js
	Defines Fx.Move, a class that works with Element.Position.js to transition an element from one location to another.

	License:
		MIT-style license.

	Authors:
		Aaron Newton

*/

Fx.Move = new Class({

	Extends: Fx.Morph,

	options: {
		relativeTo: document.body,
		position: 'center',
		edge: false,
		offset: {x: 0, y: 0}
	},

	start: function(destination){
		return this.parent(this.element.position($merge(this.options, destination, {returnPos: true})));
	}

});

Element.Properties.move = {

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

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

};

Element.implement({

	move: function(options){
		this.get('move').start(options);
		return this;
	}

});


/*
Script: Fx.Reveal.js
	Defines Fx.Reveal, a class that shows and hides elements with a transition.

	License:
		MIT-style license.

	Authors:
		Aaron Newton

*/

Fx.Reveal = new Class({

	Extends: Fx.Morph,

	options: {/*	  
		onShow: $empty(thisElemeng),
		onHide: $empty(thisElemeng),
		onComplete: $empty(thisElemeng),
		heightOverride: null,
		widthOverride: null, */
		styles: ['padding', 'border', 'margin'],
		transitionOpacity: !Browser.Engine.trident4,
		mode: 'vertical',
		display: 'block',
		hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed' : false
	},

	dissolve: function(){
		try {
			if (!this.hiding && !this.showing){
				if (this.element.getStyle('display') != 'none'){
					this.hiding = true;
					this.showing = false;
					this.hidden = true;
					var startStyles = this.element.getComputedSize({
						styles: this.options.styles,
						mode: this.options.mode
					});
					var setToAuto = (this.element.style.height === ''||this.element.style.height == 'auto');
					this.element.setStyle('display', 'block');
					if (this.options.transitionOpacity) startStyles.opacity = 1;
					var zero = {};
					$each(startStyles, function(style, name){
						zero[name] = [style, 0];
					}, this);
					var overflowBefore = this.element.getStyle('overflow');
					this.element.setStyle('overflow', 'hidden');
					var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
					this.$chain.unshift(function(){
						if (this.hidden){
							this.hiding = false;
							$each(startStyles, function(style, name){
								startStyles[name] = style;
							}, this);
							this.element.setStyles($merge({display: 'none', overflow: overflowBefore}, startStyles));
							if (setToAuto){
								if (['vertical', 'both'].contains(this.options.mode)) this.element.style.height = '';
								if (['width', 'both'].contains(this.options.mode)) this.element.style.width = '';
							}
							if (hideThese) hideThese.setStyle('visibility', 'visible');
						}
						this.fireEvent('hide', this.element);
						this.callChain();
					}.bind(this));
					if (hideThese) hideThese.setStyle('visibility', 'hidden');
					this.start(zero);
				} else {
					this.callChain.delay(10, this);
					this.fireEvent('complete', this.element);
					this.fireEvent('hide', this.element);
				}
			} else if (this.options.link == 'chain'){
				this.chain(this.dissolve.bind(this));
			} else if (this.options.link == 'cancel' && !this.hiding){
				this.cancel();
				this.dissolve();
			}
		} catch(e){
			this.hiding = false;
			this.element.setStyle('display', 'none');
			this.callChain.delay(10, this);
			this.fireEvent('complete', this.element);
			this.fireEvent('hide', this.element);
		}
		return this;
	},

	reveal: function(){
		try {
			if (!this.showing && !this.hiding){
				if (this.element.getStyle('display') == 'none' ||
					 this.element.getStyle('visiblity') == 'hidden' ||
					 this.element.getStyle('opacity') == 0){
					this.showing = true;
					this.hiding = false;
					this.hidden = false;
					var setToAuto, startStyles;
					//toggle display, but hide it
					this.element.measure(function(){
						setToAuto = (this.element.style.height === '' || this.element.style.height == 'auto');
						//create the styles for the opened/visible state
						startStyles = this.element.getComputedSize({
							styles: this.options.styles,
							mode: this.options.mode
						});
					}.bind(this));
					$each(startStyles, function(style, name){
						startStyles[name] = style;
					});
					//if we're overridding height/width
					if ($chk(this.options.heightOverride)) startStyles.height = this.options.heightOverride.toInt();
					if ($chk(this.options.widthOverride)) startStyles.width = this.options.widthOverride.toInt();
					if (this.options.transitionOpacity) {
						this.element.setStyle('opacity', 0);
						startStyles.opacity = 1;
					}
					//create the zero state for the beginning of the transition
					var zero = {
						height: 0,
						display: this.options.display
					};
					$each(startStyles, function(style, name){ zero[name] = 0; });
					var overflowBefore = this.element.getStyle('overflow');
					//set to zero
					this.element.setStyles($merge(zero, {overflow: 'hidden'}));
					//hide inputs
					var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
					if (hideThese) hideThese.setStyle('visibility', 'hidden');
					//start the effect
					this.start(startStyles);
					this.$chain.unshift(function(){
						this.element.setStyle('overflow', overflowBefore);
						if (!this.options.heightOverride && setToAuto){
							if (['vertical', 'both'].contains(this.options.mode)) this.element.style.height = '';
							if (['width', 'both'].contains(this.options.mode)) this.element.style.width = '';
						}
						if (!this.hidden) this.showing = false;
						if (hideThese) hideThese.setStyle('visibility', 'visible');
						this.callChain();
						this.fireEvent('show', this.element);
					}.bind(this));
				} else {
					this.callChain();
					this.fireEvent('complete', this.element);
					this.fireEvent('show', this.element);
				}
			} else if (this.options.link == 'chain'){
				this.chain(this.reveal.bind(this));
			} else if (this.options.link == 'cancel' && !this.showing){
				this.cancel();
				this.reveal();
			}
		} catch(e){
			this.element.setStyles({
				display: this.options.display,
				visiblity: 'visible',
				opacity: 1
			});
			this.showing = false;
			this.callChain.delay(10, this);
			this.fireEvent('complete', this.element);
			this.fireEvent('show', this.element);
		}
		return this;
	},

	toggle: function(){
		if (this.element.getStyle('display') == 'none' ||
			 this.element.getStyle('visiblity') == 'hidden' ||
			 this.element.getStyle('opacity') == 0){
			this.reveal();
		} else {
			this.dissolve();
		}
		return this;
	}

});

Element.Properties.reveal = {

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

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

};

Element.Properties.dissolve = Element.Properties.reveal;

Element.implement({

	reveal: function(options){
		this.get('reveal', options).reveal();
		return this;
	},

	dissolve: function(options){
		this.get('reveal', options).dissolve();
		return this;
	},

	nix: function(){
		var params = Array.link(arguments, {destroy: Boolean.type, options: Object.type});
		this.get('reveal', params.options).dissolve().chain(function(){
			this[params.destroy ? 'destroy' : 'dispose']();
		}.bind(this));
		return this;
	},

	wink: function(){
		var params = Array.link(arguments, {duration: Number.type, options: Object.type});
		var reveal = this.get('reveal', params.options);
		reveal.reveal().chain(function(){
			(function(){
				reveal.dissolve();
			}).delay(params.duration || 2000);
		});
	}


});

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

	License:
		MIT-style license.

	Authors:
		Valerio Proietti
*/

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){
		return [0, 1].map(function(i){
			return Fx.compute(from[i], to[i], delta);
		});
	},

	start: function(x, y){
		if (!this.check(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.Slide.js
	Effect to slide an element in and out of view.

	License:
		MIT-style license.

	Authors:
		Valerio Proietti
*/

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){
		return [0, 1].map(function(i){
			return Fx.compute(from[i], to[i], delta);
		});
	},

	start: function(how, mode){
		if (!this.check(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 = (layout == 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.SmoothScroll.js
	Class for creating a smooth scrolling effect to all internal links on the page.

	License:
		MIT-style license.

	Authors:
		Valerio Proietti
*/

var SmoothScroll = Fx.SmoothScroll = new Class({

	Extends: Fx.Scroll,

	initialize: function(options, context){
		context = context || document;
		this.doc = context.getDocument();
		var win = context.getWindow();
		this.parent(this.doc, options);
		this.links = this.options.links ? $$(this.options.links) : $$(this.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) this.useLink(link, anchor);
		}, this);
		if (!Browser.Engine.webkit419) {
			this.addEvent('complete', function(){
				win.location.hash = this.anchor;
			}, true);
		}
	},

	useLink: function(link, anchor){
		var el;
		link.addEvent('click', function(event){
			if (el !== false && !el) el = $(anchor) || this.doc.getElement('a[name=' + anchor + ']');
			if (el) {
				event.preventDefault();
				this.anchor = anchor;
				this.toElement(el);
				link.blur();
			}
		}.bind(this));
	}

});

/*
Script: Fx.Sort.js
	Defines Fx.Sort, a class that reorders lists with a transition.

	License:
		MIT-style license.

	Authors:
		Aaron Newton

*/

Fx.Sort = new Class({

	Extends: Fx.Elements,

	options: {
		mode: 'vertical'
	},

	initialize: function(elements, options){
		this.parent(elements, options);
		this.elements.each(function(el){
			if (el.getStyle('position') == 'static') el.setStyle('position', 'relative');
		});
		this.setDefaultOrder();
	},

	setDefaultOrder: function(){
		this.currentOrder = this.elements.map(function(el, index){
			return index;
		});
	},

	sort: function(newOrder){
		if ($type(newOrder) != 'array') return false;
		var top = 0;
		var left = 0;
		var zero = {};
		var vert = this.options.mode == 'vertical';
		var current = this.elements.map(function(el, index){
			var size = el.getComputedSize({styles: ['border', 'padding', 'margin']});
			var val;
			if (vert){
				val = {
					top: top,
					margin: size['margin-top'],
					height: size.totalHeight
				};
				top += val.height - size['margin-top'];
			} else {
				val = {
					left: left,
					margin: size['margin-left'],
					width: size.totalWidth
				};
				left += val.width;
			}
			var plain = vert ? 'top' : 'left';
			zero[index] = {};
			var start = el.getStyle(plain).toInt();
			zero[index][plain] = start || 0;
			return val;
		}, this);
		this.set(zero);
		newOrder = newOrder.map(function(i){ return i.toInt(); });
		if (newOrder.length != this.elements.length){
			this.currentOrder.each(function(index){
				if (!newOrder.contains(index)) newOrder.push(index);
			});
			if (newOrder.length > this.elements.length)
				newOrder.splice(this.elements.length-1, newOrder.length - this.elements.length);
		}
		top = 0;
		left = 0;
		var margin = 0;
		var next = {};
		newOrder.each(function(item, index){
			var newPos = {};
			if (vert){
				newPos.top = top - current[item].top - margin;
				top += current[item].height;
			} else {
				newPos.left = left - current[item].left;
				left += current[item].width;
			}
			margin = margin + current[item].margin;
			next[item]=newPos;
		}, this);
		var mapped = {};
		$A(newOrder).sort().each(function(index){
			mapped[index] = next[index];
		});
		this.start(mapped);
		this.currentOrder = newOrder;
		return this;
	},

	rearrangeDOM: function(newOrder){
		newOrder = newOrder || this.currentOrder;
		var parent = this.elements[0].getParent();
		var rearranged = [];
		this.elements.setStyle('opacity', 0);
		//move each element and store the new default order
		newOrder.each(function(index){
			rearranged.push(this.elements[index].inject(parent).setStyles({
				top: 0,
				left: 0
			}));
		}, this);
		this.elements.setStyle('opacity', 1);
		this.elements = $$(rearranged);
		this.setDefaultOrder();
		return this;
	},

	getDefaultOrder: function(){
		return this.elements.map(function(el, index){
			return index;
		});
	},

	forward: function(){
		return this.sort(this.getDefaultOrder());
	},

	backward: function(){
		return this.sort(this.getDefaultOrder().reverse());
	},

	reverse: function(){
		return this.sort(this.currentOrder.reverse());
	},

	sortByElements: function(elements){
		return this.sort(elements.map(function(el){
			return this.elements.indexOf(el);
		}, this));
	},

	swap: function(one, two){
		if ($type(one) == 'element') one = this.elements.indexOf(one);
		if ($type(two) == 'element') two = this.elements.indexOf(two);
		
		var newOrder = $A(this.currentOrder);
		newOrder[this.currentOrder.indexOf(one)] = two;
		newOrder[this.currentOrder.indexOf(two)] = one;
		this.sort(newOrder);
	}

});

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

	License:
		MIT-style license.

	Authors:
		Valerio Proietti
		Tom Occhinno
		Jan Kassens
*/

var Drag = new Class({

	Implements: [Events, Options],

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

	initialize: function(){
		var params = Array.link(arguments, {'options': Object.type, 'element': $defined});
		this.element = $(params.element);
		this.document = 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(){
		this.handles.addEvent('mousedown', this.bound.start);
		return this;
	},

	detach: function(){
		this.handles.removeEvent('mousedown', this.bound.start);
		return this;
	},

	start: function(event){
		if (this.options.preventDefault) event.preventDefault();
		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.getStyle(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])();
				}
			}
		}
		if ($type(this.options.grid) == 'number') this.options.grid = {x: this.options.grid, y: this.options.grid};
		this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
		this.document.addEvent(this.selection, this.bound.eventStop);
	},

	check: function(event){
		if (this.options.preventDefault) event.preventDefault();
		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.document.addEvents({
				mousemove: this.bound.drag,
				mouseup: this.bound.stop
			});
			this.fireEvent('start', [this.element, event]).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];
			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.limit[z][0]) % 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, event]);
	},

	cancel: function(event){
		this.document.removeEvent('mousemove', this.bound.check);
		this.document.removeEvent('mouseup', this.bound.cancel);
		if (event){
			this.document.removeEvent(this.selection, this.bound.eventStop);
			this.fireEvent('cancel', this.element);
		}
	},

	stop: function(event){
		this.document.removeEvent(this.selection, this.bound.eventStop);
		this.document.removeEvent('mousemove', this.bound.drag);
		this.document.removeEvent('mouseup', this.bound.stop);
		if (event) this.fireEvent('complete', [this.element, event]);
	}

});

Element.implement({

	makeResizable: function(options){
		var drag = new Drag(this, $merge({modifiers: {x: 'width', y: 'height'}}, options));
		this.store('resizer', drag);
		return drag.addEvent('drag', function(){
			this.fireEvent('resize', drag);
		}.bind(this));
	}

});


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

	License:
		MIT-style license.

	Authors:
		Valerio Proietti
		Tom Occhinno
		Jan Kassens*/

Drag.Move = new Class({

	Extends: Drag,

	options: {/*
		onEnter: $empty(thisElement, overed),
		onLeave: $empty(thisElement, overed),
		onDrop: $empty(thisElement, overed, event),*/
		droppables: [],
		container: false,
		precalculate: false,
		includeMargins: true,
		checkDroppables: true
	},

	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);

		var position = this.element.getStyle('position');
		if (position=='static') position = 'absolute';
		if ([this.element.getStyle('left'), this.element.getStyle('top')].contains('auto')) this.element.position(this.element.getPosition(this.element.offsetParent));
		this.element.setStyle('position', position);

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

		this.overed = null;
	},

	start: function(event){
		if (this.container){
			var ccoo = this.container.getCoordinates(this.element.getOffsetParent()), cbs = {}, ems = {};

			['top', 'right', 'bottom', 'left'].each(function(pad){
				cbs[pad] = this.container.getStyle('border-' + pad).toInt();
				ems[pad] = this.element.getStyle('margin-' + pad).toInt();
			}, this);

			var width = this.element.offsetWidth + ems.left + ems.right;
			var height = this.element.offsetHeight + ems.top + ems.bottom;

			if (this.options.includeMargins) {
				$each(ems, function(value, key) {
					ems[key] = 0;
				});
			}
			if (this.container == this.element.getOffsetParent()) {
				this.options.limit = {
					x: [0 - ems.left, ccoo.right - cbs.left - cbs.right - width + ems.right],
					y: [0 - ems.top, ccoo.bottom - cbs.top - cbs.bottom - height + ems.bottom]
				};
			} else {
				this.options.limit = {
					x: [ccoo.left + cbs.left - ems.left, ccoo.right - cbs.right - width + ems.right],
					y: [ccoo.top + cbs.top - ems.top, ccoo.bottom - cbs.bottom - height + ems.bottom]
				};
			}

		}
		if (this.options.precalculate){
			this.positions = this.droppables.map(function(el) {
				return el.getCoordinates();
			});
		}
		this.parent(event);
	},

	checkAgainst: function(el, i){
		el = (this.positions) ? this.positions[i] : 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.options.checkDroppables && this.droppables.length) this.checkDroppables();
	},

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

});

Element.implement({

	makeDraggable: function(options){
		var drag = new Drag.Move(this, options);
		this.store('dragger', drag);
		return drag;
	}

});


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

	License:
		MIT-style license.

	Authors:
		Valerio Proietti
*/

var Slider = new Class({

	Implements: [Events, Options],

	Binds: ['clickedElement', 'draggedKnob', 'scrolledElement'],

	options: {/*
		onTick: $empty(intPosition),
		onChange: $empty(intStep),
		onComplete: $empty(strStep),*/
		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;
		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.bound = {
			clickedElement: this.clickedElement.bind(this),
			scrolledElement: this.scrolledElement.bindWithEvent(this),
			draggedKnob: this.draggedKnob.bind(this)
		};

		var dragOptions = {
			snap: 0,
			limit: limit,
			modifiers: modifiers,
			onDrag: this.bound.draggedKnob,
			onStart: this.bound.draggedKnob,
			onBeforeStart: (function(){
				this.isDragging = true;
			}).bind(this),
			onComplete: function(){
				this.isDragging = false;
				this.draggedKnob();
				this.end();
			}.bind(this)
		};
		if (this.options.snap){
			dragOptions.grid = Math.ceil(this.stepWidth);
			dragOptions.limit[this.axis][1] = this.full;
		}

		this.drag = new Drag(this.knob, dragOptions);
		this.attach();
	},

	attach: function(){
		this.element.addEvent('mousedown', this.bound.clickedElement);
		if (this.options.wheel) this.element.addEvent('mousewheel', this.bound.scrolledElement);
		this.drag.attach();
		return this;
	},

	detach: function(){
		this.element.removeEvent('mousedown', this.bound.clickedElement);
		this.element.removeEvent('mousewheel', this.bound.scrolledElement);
		this.drag.detach();
		return this;
	},

	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.fireEvent('tick', this.toPosition(this.step));
		this.end();
		return this;
	},

	clickedElement: function(event){
		if (this.isDragging || event.target == this.knob) return;

		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.fireEvent('tick', position);
		this.end();
	},

	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: Sortables.js
	Class for creating a drag and drop sorting interface for lists of items.

	License:
		MIT-style license.

	Authors:
		Tom Occhino
*/

var Sortables = new Class({

	Implements: [Events, Options],

	options: {/*
		onSort: $empty(element, clone),
		onStart: $empty(element, clone),
		onComplete: $empty(element),*/
		snap: 4,
		opacity: 1,
		clone: false,
		revert: false,
		handle: false,
		constrain: false
	},

	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(){
		return $$(Array.flatten(arguments).map(function(element){
			this.elements.erase(element);
			var start = element.retrieve('sortables:start');
			(this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
			
			return element;
		}, this));
	},

	removeLists: function(){
		return $$(Array.flatten(arguments).map(function(list){
			this.lists.erase(list);
			this.removeItems(list.getChildren());
			
			return list;
		}, this));
	},

	getClone: function(event, element){
		if (!this.options.clone) return new Element('div').inject(document.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: 'absolute',
			visibility: 'hidden',
			'width': element.getStyle('width')
		}).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 = this.element.getStyles('width', 'height');
			var pos = this.clone.computePosition(this.element.getPosition(this.clone.offsetParent));
			this.effect.element = this.clone;
			this.effect.start({
				top: pos.top,
				left: pos.left,
				width: dim.width,
				height: dim.height,
				opacity: 0.25
			}).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: Request.JSONP.js
	Defines Request.JSONP, a class for cross domain javascript via script injection.

	License:
		MIT-style license.

	Authors:
		Aaron Newton
		Guillermo Rauch
*/

Request.JSONP = new Class({

	Implements: [Chain, Events, Options, Log],

	options: {/*
		onRetry: $empty(intRetries),
		onRequest: $empty(scriptElement),
		onComplete: $empty(data),
		onSuccess: $empty(data),
		onCancel: $empty(),*/
		url: '',
		data: {},
		retries: 0,
		timeout: 0,
		link: 'ignore',
		callbackKey: 'callback',
		injectScript: document.head
	},

	initialize: function(options){
		this.setOptions(options);
		this.running = false;
		this.requests = 0;
		this.triesRemaining = [];
	},

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

	send: function(options){
		if (!$chk(arguments[1]) && !this.check(options)) return this;

		var type = $type(options), old = this.options, index = $chk(arguments[1]) ? arguments[1] : this.requests++;
		if (type == 'string' || type == 'element') options = {data: options};

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

		if (!$chk(this.triesRemaining[index])) this.triesRemaining[index] = this.options.retries;
		var remaining = this.triesRemaining[index];

		(function(){
			var script = this.getScript(options);
			this.log('JSONP retrieving script with url: ' + script.get('src'));
			this.fireEvent('request', script);
			this.running = true;

			(function(){
				if (remaining){
					this.triesRemaining[index] = remaining - 1;
					if (script){
						script.destroy();
						this.request(options, index);
						this.fireEvent('retry', this.triesRemaining[index]);
					}
				} else if(script && this.options.timeout){
					script.destroy();
					this.cancel();
					this.fireEvent('failure');
				}
			}).delay(this.options.timeout, this);
		}).delay(Browser.Engine.trident ? 50 : 0, this);
		return this;
	},

	cancel: function(){
		if (!this.running) return this;
		this.running = false;
		this.fireEvent('cancel');
		return this;
	},

	getScript: function(options){
		var index = Request.JSONP.counter, data;
		Request.JSONP.counter++;

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

		var src = options.url + 
			 (options.url.test('\\?') ? '&' :'?') + 
			 (options.callbackKey || this.options.callbackKey) + 
			 '=Request.JSONP.request_map.request_'+ index + 
			 (data ? '&' + data : '');
		if (src.length > 2083) this.log('JSONP '+ src +' will fail in Internet Explorer, which enforces a 2083 bytes length limit on URIs');

		var script = new Element('script', {type: 'text/javascript', src: src});
		Request.JSONP.request_map['request_' + index] = function(data){ this.success(data, script); }.bind(this);
		return script.inject(this.options.injectScript);
	},

	success: function(data, script){
		if (script) script.destroy();
		this.running = false;
		this.log('JSONP successfully retrieved: ', data);
		this.fireEvent('complete', [data]).fireEvent('success', [data]).callChain();
	}

});

Request.JSONP.counter = 0;
Request.JSONP.request_map = {};

/*
Script: Request.Queue.js
	Controls several instances of Request and its variants to run only one request at a time.

	License:
		MIT-style license.

	Authors:
		Aaron Newton
*/

Request.Queue = new Class({

	Implements: [Options, Events],

	Binds: ['attach', 'request', 'complete', 'cancel', 'success', 'failure', 'exception'],

	options: {/*
		onRequest: $empty(argsPassedToOnRequest),
		onSuccess: $empty(argsPassedToOnSuccess),
		onComplete: $empty(argsPassedToOnComplete),
		onCancel: $empty(argsPassedToOnCancel),
		onException: $empty(argsPassedToOnException),
		onFailure: $empty(argsPassedToOnFailure),*/
		stopOnFailure: true,
		autoAdvance: true,
		concurrent: 1,
		requests: {}
	},

	initialize: function(options){
		this.setOptions(options);
		this.requests = new Hash;
		this.addRequests(this.options.requests);
		this.queue = [];
		this.reqBinders = {};
	},

	addRequest: function(name, request){
		this.requests.set(name, request);
		this.attach(name, request);
		return this;
	},

	addRequests: function(obj){
		$each(obj, this.addRequest, this);
		return this;
	},

	getName: function(req){
		return this.requests.keyOf(req);
	},

	attach: function(name, req){
		if (req._groupSend) return this;
		['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){
			if(!this.reqBinders[name]) this.reqBinders[name] = {};
			this.reqBinders[name][evt] = function(){
				this['on' + evt.capitalize()].apply(this, [name, req].extend(arguments));
			}.bind(this);
			req.addEvent(evt, this.reqBinders[name][evt]);
		}, this);
		req._groupSend = req.send;
		req.send = function(options){
			this.send(name, options);
			return req;
		}.bind(this);
		return this;
	},

	removeRequest: function(req){
		var name = $type(req) == 'object' ? this.getName(req) : req;
		if (!name && $type(name) != 'string') return this;
		req = this.requests.get(name);
		if (!req) return this;
		['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){
			req.removeEvent(evt, this.reqBinders[name][evt]);
		}, this);
		req.send = req._groupSend;
		delete req._groupSend;
		return this;
	},

	getRunning: function(){
		return this.requests.filter(function(r){ return r.running; });
	},

	isRunning: function(){
		return !!this.getRunning().getKeys().length;
	},

	send: function(name, options){
		var q = function(){
			this.requests.get(name)._groupSend(options);
			this.queue.erase(q);
		}.bind(this);
		q.name = name;
		if (this.getRunning().getKeys().length >= this.options.concurrent || (this.error && this.options.stopOnFailure)) this.queue.push(q);
		else q();
		return this;
	},

	hasNext: function(name){
		return (!name) ? !!this.queue.length : !!this.queue.filter(function(q){ return q.name == name; }).length;
	},

	resume: function(){
		this.error = false;
		(this.options.concurrent - this.getRunning().getKeys().length).times(this.runNext, this);
		return this;
	},

	runNext: function(name){
		if (!this.queue.length) return this;
		if (!name){
			this.queue[0]();
		} else {
			var found;
			this.queue.each(function(q){
				if (!found && q.name == name){
					found = true;
					q();
				}
			});
		}
		return this;
	},

	runAll: function() {
		this.queue.each(function(q) {
			q();
		});
		return this;
	},

	clear: function(name){
		if (!name){
			this.queue.empty();
		} else {
			this.queue = this.queue.map(function(q){
				if (q.name != name) return q;
				else return false;
			}).filter(function(q){ return q; });
		}
		return this;
	},

	cancel: function(name){
		this.requests.get(name).cancel();
		return this;
	},

	onRequest: function(){
		this.fireEvent('request', arguments);
	},

	onComplete: function(){
		this.fireEvent('complete', arguments);
	},

	onCancel: function(){
		if (this.options.autoAdvance && !this.error) this.runNext();
		this.fireEvent('cancel', arguments);
	},

	onSuccess: function(){
		if (this.options.autoAdvance && !this.error) this.runNext();
		this.fireEvent('success', arguments);
	},

	onFailure: function(){
		this.error = true;
		if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext();
		this.fireEvent('failure', arguments);
	},

	onException: function(){
		this.error = true;
		if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext();
		this.fireEvent('exception', arguments);
	}

});


/*
Script: Request.Periodical.js
	Requests the same url at a time interval that increases when no data is returned from the requested server

	License:
		MIT-style license.

	Authors:
		Christoph Pojer

*/

Request.implement({

	options: {
		initialDelay: 5000,
		delay: 5000,
		limit: 60000
	},

	startTimer: function(data){
		var fn = (function(){
			if (!this.running) this.send({data: data});
		});
		this.timer = fn.delay(this.options.initialDelay, this);
		this.lastDelay = this.options.initialDelay;
		this.completeCheck = function(j){
			$clear(this.timer);
			if (j) this.lastDelay = this.options.delay;
			else this.lastDelay = (this.lastDelay+this.options.delay).min(this.options.limit);
			this.timer = fn.delay(this.lastDelay, this);
		};
		this.addEvent('complete', this.completeCheck);
		return this;
	},

	stopTimer: function(){
		$clear(this.timer);
		this.removeEvent('complete', this.completeCheck);
		return this;
	}

});

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

	License:
		MIT-style license.

	Authors:
		Valerio Proietti
*/

var Asset = {

	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();
			}
		}).set(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.set(properties);
	},

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

};

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

	License:
		MIT-style license.

	Authors:
		Valerio Proietti
*/

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');
	}

});

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

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

var $HEX = function(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.

	Authors:
		Valerio Proietti
*/

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: Hash.Cookie.js
	Class for creating, reading, and deleting Cookies in JSON format.

	License:
		MIT-style license.

	Authors:
		Valerio Proietti
		Aaron Newton
*/

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.each(Hash.prototype, function(method, name){
	if (typeof method == 'function') Hash.Cookie.implement(name, function(){
		var value = method.apply(this.hash, arguments);
		if (this.options.autoSave) this.save();
		return value;
	});
});

/*
Script: IframeShim.js
	Defines IframeShim, a class for obscuring select lists and flash objects in IE.

	License:
		MIT-style license.

	Authors:
		Aaron Newton
*/

var IframeShim = new Class({

	Implements: [Options, Events, Class.Occlude],

	options: {
		className: 'iframeShim',
		display: false,
		zIndex: null,
		margin: 0,
		offset: {x: 0, y: 0},
		browsers: true || (Browser.Engine.trident4 || (Browser.Engine.gecko && !Browser.Engine.gecko19 && Browser.Platform.mac))
	},

	property: 'IframeShim',

	initialize: function(element, options){
		this.element = $(element);
		if (this.occlude()) return this.occluded;
		this.setOptions(options);
		this.makeShim();
		return this;
	},

	makeShim: function(){
		if(this.options.browsers){
			var zIndex = this.element.getStyle('zIndex').toInt();

			if (!zIndex){
				var pos = this.element.getStyle('position');
				if (pos == 'static' || !pos) this.element.setStyle('position', 'relative');
				this.element.setStyle('zIndex', zIndex || 1);
			}
			zIndex = ($chk(this.options.zIndex) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1;
			if (zIndex < 0) zIndex = 1;
			this.shim = new Element('iframe', {
				src: (window.location.protocol == 'https') ? '://0' : 'javascript:void(0)',
				scrolling: 'no',
				frameborder: 0,
				styles: {
					zIndex: zIndex,
					position: 'absolute',
					border: 'none',
					filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'
				},
				'class': this.options.className
			}).store('IframeShim', this);
			var inject = (function(){
				this.shim.inject(this.element, 'after');
				this[this.options.display ? 'show' : 'hide']();
				this.fireEvent('inject');
			}).bind(this);
			if (Browser.Engine.trident && !IframeShim.ready) window.addEvent('load', inject);
			else inject();
		} else {
			this.position = this.hide = this.show = this.dispose = $lambda(this);
		}
	},

	position: function(){
		if (!IframeShim.ready) return this;
		var size = this.element.measure(function(){ return this.getSize(); });
		if ($type(this.options.margin)){
			size.x = size.x - (this.options.margin * 2);
			size.y = size.y - (this.options.margin * 2);
			this.options.offset.x += this.options.margin;
			this.options.offset.y += this.options.margin;
		}
		if (this.shim) {
			this.shim.set({width: size.x, height: size.y}).position({
				relativeTo: this.element,
				offset: this.options.offset
			});
		}
		return this;
	},

	hide: function(){
		if (this.shim) this.shim.setStyle('display', 'none');
		return this;
	},

	show: function(){
		if (this.shim) this.shim.setStyle('display', 'block');
		return this.position();
	},

	dispose: function(){
		if (this.shim) this.shim.dispose();
		return this;
	},

	destroy: function(){
		if (this.shim) this.shim.destroy();
		return this;
	}

});

window.addEvent('load', function(){
	IframeShim.ready = true;
});

/*
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.

	Authors:
		Valerio Proietti
*/

var Scroller = new Class({

	Implements: [Events, Options],

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

	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.bound = {
			attach: this.attach.bind(this),
			detach: this.detach.bind(this),
			getCoords: this.getCoords.bind(this)
		};
	},

	start: function(){
		this.listener.addEvents({
			mouseenter: this.bound.attach,
			mouseleave: this.bound.detach
		});
	},

	stop: function(){
		this.listener.removeEvents({
			mouseenter: this.bound.attach,
			mouseleave: this.bound.detach
		});
		this.timer = $clear(this.timer);
	},

	attach: function(){
		this.listener.addEvent('mousemove', this.bound.getCoords);
	},

	detach: function(){
		this.listener.removeEvent('mousemove', this.bound.getCoords);
		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(Math.round(1000 / this.options.fps), this);
	},

	scroll: function(){
		var size = this.element.getSize(), 
			scroll = this.element.getScroll(), 
			pos = this.element.getOffsets(), 
			scrollSize = this.element.getScrollSize(), 
			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]) && scroll[z] + size[z] != scrollSize[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: Tips.js
	Class for creating nice tips that follow the mouse cursor when hovering an element.

	License:
		MIT-style license.

	Authors:
		Valerio Proietti
		Christoph Pojer
*/

var Tips = new Class({

	Implements: [Events, Options],

	options: {
		onShow: function(tip){
			tip.setStyle('visibility', 'visible');
		},
		onHide: function(tip){
			tip.setStyle('visibility', 'hidden');
		},
		title: 'title',
		text: function(el){
			return el.get('rel') || el.get('href');
		},
		showDelay: 100,
		hideDelay: 100,
		className: null,
		offset: {x: 16, y: 16},
		fixed: false
	},

	initialize: function(){
		var params = Array.link(arguments, {options: Object.type, elements: $defined});
		if (params.options && params.options.offsets) params.options.offset = params.options.offsets;
		this.setOptions(params.options);
		this.container = new Element('div', {'class': 'tip'});
		this.tip = this.getTip();
		
		if (params.elements) this.attach(params.elements);
	},

	getTip: function(){
		return new Element('div', {
			'class': this.options.className,
			styles: {
				visibility: 'hidden',
				display: 'none',
				position: 'absolute',
				top: 0,
				left: 0
			}
		}).adopt(
			new Element('div', {'class': 'tip-top'}),
			this.container,
			new Element('div', {'class': 'tip-bottom'})
		).inject(document.body);
	},

	attach: function(elements){
		var read = function(option, element){
			if (option == null) return '';
			return $type(option) == 'function' ? option(element) : element.get(option);
		};
		$$(elements).each(function(element){
			var title = read(this.options.title, element);
			element.erase('title').store('tip:native', title).retrieve('tip:title', title);
			element.retrieve('tip:text', read(this.options.text, element));
			
			var events = ['enter', 'leave'];
			if (!this.options.fixed) events.push('move');
			
			events.each(function(value){
				element.addEvent('mouse' + value, element.retrieve('tip:' + value, this['element' + value.capitalize()].bindWithEvent(this, element)));
			}, this);
		}, this);
		
		return this;
	},

	detach: function(elements){
		$$(elements).each(function(element){
			['enter', 'leave', 'move'].each(function(value){
				element.removeEvent('mouse' + value, element.retrieve('tip:' + value) || $empty);
			});
			
			element.eliminate('tip:enter').eliminate('tip:leave').eliminate('tip:move');
			
			if ($type(this.options.title) == 'string' && this.options.title == 'title'){
				var original = element.retrieve('tip:native');
				if (original) element.set('title', original);
			}
		}, this);
		
		return this;
	},

	elementEnter: function(event, element){
		$A(this.container.childNodes).each(Element.dispose);
		
		['title', 'text'].each(function(value){
			var content = element.retrieve('tip:' + value);
			if (!content) return;
			
			this[value + 'Element'] = new Element('div', {'class': 'tip-' + value}).inject(this.container);
			this.fill(this[value + 'Element'], content);
		}, this);
		
		this.timer = $clear(this.timer);
		this.timer = this.show.delay(this.options.showDelay, this, element);
		this.tip.setStyle('display', 'block');
		this.position((!this.options.fixed) ? event : {page: element.getPosition()});
	},

	elementLeave: function(event, element){
		$clear(this.timer);
		this.tip.setStyle('display', 'none');
		this.timer = this.hide.delay(this.options.hideDelay, this, element);
	},

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

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

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

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

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

});

/*
Script: Date.English.US.js
	Date messages for US English.

	License:
		MIT-style license.

	Authors:
		Aaron Newton

*/

MooTools.lang.set('en-US', 'Date', {

	months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
	days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
	//culture's date order: MM/DD/YYYY
	dateOrder: ['month', 'date', 'year'],
	shortDate: '%m/%d/%Y',
	shortTime: '%I:%M%p',
	AM: 'AM',
	PM: 'PM',

	/* Date.Extras */
	ordinal: function(dayOfMonth){
		//1st, 2nd, 3rd, etc.
		return (dayOfMonth > 3 && dayOfMonth < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][Math.min(dayOfMonth % 10, 4)];
	},

	lessThanMinuteAgo: 'less than a minute ago',
	minuteAgo: 'about a minute ago',
	minutesAgo: '{delta} minutes ago',
	hourAgo: 'about an hour ago',
	hoursAgo: 'about {delta} hours ago',
	dayAgo: '1 day ago',
	daysAgo: '{delta} days ago',
	lessThanMinuteUntil: 'less than a minute from now',
	minuteUntil: 'about a minute from now',
	minutesUntil: '{delta} minutes from now',
	hourUntil: 'about an hour from now',
	hoursUntil: 'about {delta} hours from now',
	dayUntil: '1 day from now',
	daysUntil: '{delta} days from now'

});
// ---------------------------------------------------------------------
// Browser Detection 
// 
// ---------------------------------------------------------------------
isMac = (navigator.appVersion.indexOf("Mac")!=-1) ? true : false; 
isDOM=(document.getElementById)?true:false
isOpera=isOpera5=window.opera && isDOM
isOpera6=isOpera && window.print
isOpera7=isOpera && navigator.userAgent.indexOf("Opera 7") > 0 || navigator.userAgent.indexOf("Opera/7") >= 0
isMSIE=isIE=document.all && document.all.item && !isOpera
isMSIE4 = ((document.all)&&(navigator.appVersion.indexOf("MSIE 4.")!=-1)) ? true : false; 
isMSIEmac = ((document.all)&&(isMac)) ? true : false; 
isNC=navigator.appName=="Netscape"
isNC4=isNC && !isDOM
isNC6=isNC && isDOM



// ---------------------------------------------------------------------
// Safe loading into window.onload
//
// Body onload utility (supports multiple onload functions) 
// ---------------------------------------------------------------------
var gSafeOnload = new Array(); 
var gSafeOnloadDelay = new Array();

function SafeAddOnload(f) {
  SafeAddOnloadDelayed(f, 0);
}

function SafeAddOnloadDelayed(f, d) {
  if (isMSIEmac && isMSIE4)  // IE 4.5 blows out on testing window.onload 
  { 
    window.onload = SafeOnload; 
    gSafeOnload[gSafeOnload.length] = f;
    gSafeOnloadDelay[gSafeOnloadDelay.length] = d;
  } 
  else if (window.onload) 
  { 
    if (window.onload != SafeOnload) 
    { 
      gSafeOnload[0] = window.onload; 
      gSafeOnloadDelay[0] = 0;
      window.onload = SafeOnload; 
    } 
    gSafeOnload[gSafeOnload.length] = f; 
    gSafeOnloadDelay[gSafeOnloadDelay.length] = d;
  } 
  else {
    window.onload = f;
  }
} 

function SafeOnload() 
{ 
  var xyziilen = gSafeOnload.length;
  
  for (xyzii = 0; xyzii < xyziilen; xyzii++) {
    
    if (typeof(gSafeOnload[xyzii]) != 'function') {
      gSafeOnload[xyzii] = new Function(gSafeOnload[xyzii]);
    }
    
    if (gSafeOnloadDelay[xyzii] > 0) {
      setTimeout(gSafeOnload[xyzii], gSafeOnloadDelay[xyzii]); 
    }
    else {
      gSafeOnload[xyzii]();
    }  
  }
} 

// ---------------------------------------------------------------------
// Set a layer opacity
// 
// ---------------------------------------------------------------------
function SetLayerOpacity(layername, opacitylevel) {
  if (!isDOM) {
    return false;
  }
  if(isMSIE) {
    document.getElementById(layername).style.filter='alpha(opacity='+opacitylevel+')';
  }
        if(isNC6) {
    document.getElementById(layername).style.MozOpacity = opacitylevel/100;
  }
}

// ---------------------------------------------------------------------
// Open new browser window
// 
// Params:
//
//   pWidth - popup width in px
//   pHeight - popup height in px
//   pXpos - x window position or one of: center, left, right
//   pYpos - y window position or one of: center, top, bottom
//   pURL - URL to open in window - optional
//   pWindowName - name of window - optional
//   pFocusAfterLoad - focus window after load
//   pAddon - turn on addons. give what addons you want to enable separated by ,
//            ie: scrollbars,hotkeys,location,menubar,resizable,titlebar,toolbar
// ---------------------------------------------------------------------
function PopupWindow(pWidth, pHeight, pXpos, pYpos, pURL, pWindowName, pFocusAfterLoad, pAddon) {

  // Position X
  if ('center' == pXpos) {
    pXpos = Math.round((screen.width -  pWidth) / 2);
  }
  else if('left' == pXpos) {
    pXpos = 0;
  }
  else if('right' == pXpos) {
    pXpos = screen.width -  pWidth;
  }
  
  // Position Y
  if ('center' == pYpos) {
    pYpos = Math.round((screen.height - pHeight) / 2);
  }
  else if('top' == pYpos) {
    pYpos = 0;
  }
  else if('bottom' == pYpos) {
    pYpos = screen.height - pHeight;
  }
  
  var addons = '' + 
    'hotkeys=no' + 
    ',location=no' + 
    ',menubar=no' + 
    ',resizable=no' + 
    ',scrollbars=no' + 
    ',status=no' + 
    ',titlebar=no' + 
    ',toolbar=no';
    
  // Replace addons to yes - ie. status=no -> status=yes
  if (pAddon) {
    splitaddons = pAddon.split(',');
    
    for(i = 0; i < splitaddons.length; i++) {
      regexp = new RegExp(splitaddons[i] + '=no', 'i');
      addons = addons.replace(regexp, splitaddons[i] + '=yes');
    }
  }
    
  windowhandle = window.open(
    pURL, pWindowName, 
    'height=' + pHeight +
    ',width=' + pWidth +
    ',screenX=' + pXpos + 
    ',screenY=' + pYpos + 
    ',left=' + pXpos +
    ',top=' + pYpos + 
    addons
  );
  
  // Raise window after window load
  if (true == pFocusAfterLoad) {
    windowhandle.focus();
  }
  
  //windowhandle.opener = window;
  
  return windowhandle;
}


function PopupWindowResize(pWidth, pHeight, pXpos, pYpos) {
  
  // Position X
  if ('center' == pXpos) {
    pXpos = Math.round((screen.width -  pWidth) / 2);
  }
  else if('left' == pXpos) {
    pXpos = 0;
  }
  else if('right' == pXpos) {
    pXpos = screen.width -  pWidth;
  }
  
  // Position Y
  if ('center' == pYpos) {
    pYpos = Math.round((screen.height - pHeight) / 2);
  }
  else if('top' == pYpos) {
    pYpos = 0;
  }
  else if('bottom' == pYpos) {
    pYpos = screen.height - pHeight;
  }
    
  window.resizeTo(pWidth, pHeight);
  
  if (pXpos > 0 && pYpos > 0) {
    window.moveTo(pXpos, pYpos);  
  }
}

// ---------------------------------------------------------------------
//  Loads dynamically script
// ---------------------------------------------------------------------
var gLoadedScripts = new Array();

function LoadScript(pScriptSrc) {
  
  // Check if script was'nt loaded already
  if (true == in_array(pScriptSrc, gLoadedScripts)) {
    return;  
  }
  
  gLoadedScripts.push(pScriptSrc);
  
  script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = pScriptSrc;
  
  document.getElementsByTagName("head")[0].appendChild(script);
  
  return script;
}

function in_array(pNeedle, pHaystack) {
  for(var i in pHaystack) {
    if (pNeedle == pHaystack[i]) {
      return true;
    }  
  }  
  return false;
}



function print_r(pMixed, pReturn) {
  var tmp = new String;
  
  for(var i in pMixed) {
    tmp += i + ' = ' + pMixed[i] + '\n';
  }
  
  if (true === pReturn) {
    return tmp;
  }
  else {
    alert(tmp);
  }
}


// ---------------------------------------------------------------------
// Add event to object
// 
// ---------------------------------------------------------------------
function AddEvent(pObject, pHandler, pFunction){

  if (!document.all && document.getElementById){
    pObject.setAttribute(pHandler, pFunction);
  }    
  
  //workaround for IE 5.x and IE 6
  if (document.all && document.getElementById){
    pObject[pHandler.toLowerCase()] = new Function(pFunction);
  }
}


function GetWindowWidth() {
  return parseInt(
    (document.body && !isOpera && document.body.clientWidth)? document.body.clientWidth : (window.innerWidth || 0)
  );
};

function GetWindowHeight() {
  return parseInt(
    (document.body && !isOpera && document.body.clientHeight)? document.body.clientHeight : (window.innerHeight || 0)
  );
};

function CenterLayerOnPage(pLayer, pWidth, pHeight) {
  
  if (document.documentElement.clientWidth < pWidth) {
    leftPom = Math.round((document.documentElement.scrollWidth - pWidth)/2);
  }
  else {
    leftPom = Math.round((document.documentElement.clientWidth - pWidth)/2) + document.documentElement.scrollLeft;
  }
  
  if (document.documentElement.clientHeight < pHeight) {
    topPom = Math.round((document.documentElement.scrollHeight - pHeight)/2);
  }
  else {
    topPom = Math.round((document.documentElement.clientHeight - pHeight)/2) + document.documentElement.scrollTop;
  }
  
  $(pLayer).style.top = topPom + 'px';
  $(pLayer).style.left = leftPom  + 'px';
}

function ImgAlpha(pSrc, pWidth, pHeight, pIEScalingOff) {

  if (Browser.Engine.trident) {
    
    if (pIEScalingOff) {
      scale = 'none';
    }
    else {
      scale = 'scale';
    }
    
    var img_el = new Element('div');
    img_el.style.width = pWidth + 'px';
    img_el.style.height = pHeight + 'px';
    img_el.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + pSrc + "',sizingMethod='" + scale + "')";
  }
  else {
    var img_el = new Element('img');
    img_el.width = pWidth;
    img_el.height = pHeight;
    img_el.src = pSrc;
  }
  
  return img_el;
}

// Mouse positions are stored in MouseX and MouseY
var MouseX = 0;
var MouseY = 0;

function MousePositionXYHandler(ev){
  if (isNC6) {
    MouseX = ev.pageX;
    MouseY = ev.pageY;
  }
  else if(isMSIE4) {
    MouseX = document.body.scrollLeft + window.event.x;
    MouseY = document.body.scrollTop + window.event.y;
  }
  else if(isMSIE && event && document.documentElement && document.body) {
    MouseX = event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
    MouseY = event.clientY + document.documentElement.scrollTop + document.body.scrollTop;
  }
  else {
    MouseX = event.pageX ? event.pageX - window.pageXOffset : event.clientX;
    MouseY = event.pageY ? event.pageY - window.pageYOffset : event.clientY;
  }
}
// Mouse handler - stores mouse position in var MouseX and MouseY
document.onmousemove = MousePositionXYHandler;

var WindowPosition = {
  GetX: function() {
    if (isMSIE) {
      return window.screenLeft;
    }
    return window.screenX;
  },
  
  GetY: function() {
    if (isMSIE) {
      return window.screenTop;
    }
    return window.screenY;
  }
}

/*
funkcja kopiujaca obiekt
*/
copyObject = function(pObject) {
  var newObj = $type(pObject) == 'array' ? [] : {};
  for (i in pObject){
    if (pObject[i] && $type(pObject[i]) == "object") {
      newObj[i] = copyObject(pObject[i]);
    }
    else{
      newObj[i] = pObject[i]
    }
  }
  return newObj;
}

var customUploader = new Class({
  
  'mEventsEl': null,
  'mFileboxEl': null,
  'mCustomButtonEl': null,
  'mStrictEl': null,
  'mEventAttached': false,
  'mEventsElementPosition': null,
  'mEventsElementSize': null,
  'mBrowserButtonPosition': 130,
  
  'mStart': null,
  
  'initialize': function(pEventsEl, pFileboxEl, pCustomButtonEl, pStrictEl){
    
    this.mEventAttached = false;
    
    if(!pStrictEl){
      pStrictEl = false;
    }
    
    this.mEventsEl = pEventsEl;
    this.mFileboxEl = pFileboxEl;
    this.mCustomButtonEl = pCustomButtonEl;
    this.mStrictEl = pStrictEl;
    
    this.mEventsElementPosition = pEventsEl.getPosition();
    this.mEventsElementSize = pEventsEl.getSize();
    
    this.setBrowserDifferencePosition();
    this.allocateElements();
    
    this.registerEvents();
    
  },
  
  'setBrowserDifferencePosition': function(){
  	if(Browser.Engine.gecko){
  	  this.mBrowserButtonPosition = 180;
  	}
  	else if(Browser.Engine.presto){
  	  this.mBrowserButtonPosition = 150;
  	}
  },
  
  'allocateElements': function(){
    
  	if(false == this.mStrictEl)
  	  this.mEventsEl.getParent().getParent().adopt(this.mFileboxEl);
  	else
  	  this.mEventsEl.adopt(this.mFileboxEl);
    
    this.mFileboxEl.setStyles({
      'opacity': 0.01,
      'left': -400,
      'position': 'absolute'
    });    
   
    this.mFileboxEl.addClass('curDefault');
     
  },
  
  'attachEvent': function(){
    
  	this.mStart = this.mEventsEl.getPosition();
  	
  	if (false === this.mEventAttached) {
	  	this.mEventsEl.addEvent('mousemove', this.followButton.bindWithEvent(this));
			this.mEventAttached = true;
  	}
  	
  	this.mFileboxEl.setStyle('display', '');
  	
  },
  
  'detachEvent': function(){
    
    this.mEventsEl.removeEvents('mousemove');
    this.mEventsAttached = false;
    this.mFileboxEl.setStyles({
      'display': 'none',
      'left': -400
    });
    
  },
  
  'followButton': function(pE){
    
    if(!this.mStart){
      this.mStart = {'x': 0, 'y': 0};
    }
    
		this.mFileboxEl.setStyles({
      'left': pE.page.x - this.mStart.x - this.mBrowserButtonPosition,
      'top': pE.page.y - this.mStart.y - 13
    });
    
    this.checkPosition(pE);
    
  },
  
  'checkPosition': function(pE) {
    
  	if (pE.page.x < this.mEventsElementPosition.x || pE.page.x > (this.mEventsElementPosition.x + this.mEventsElementSize.x) || pE.page.y < this.mEventsElementPosition.y || pE.page.y > (this.mEventsElementPosition.y + this.mEventsElementSize.y)){
  		this.detachEvent();
  	}
  	
  },
  
  'registerEvents': function(){
    
    this.mEventsEl.addEvent('mousemove', this.followButton.bindWithEvent(this));
    this.mFileboxEl.addEvent('mousemove', this.followButton.bindWithEvent(this));

    this.mEventsEl.addEvent('mouseenter', function(){
      this.attachEvent();
    }.bind(this));

    this.mFileboxEl.addEvent('mousedown', function(pE){
      this.mCustomButtonEl.fireEvent('mousedown', pE);
    }.bindWithEvent(this));
    
    document.addEvent('mouseup', function(pE){
      this.mCustomButtonEl.fireEvent('mouseup', pE);
    }.bindWithEvent(this));
    
  }
  
});
DeForm = {};
DeForm.Version = 'stable/3.0.12';

var gDeFormScriptStack   = [];
var gDeFormScriptStackTimeoutID;

DeForm.LoadScript = function(pScriptSrc, pCallBack) {
  
  // Add to stack
  gDeFormScriptStack.push({src: pScriptSrc, callback: pCallBack});

  // Start loader
  clearTimeout(gDeFormScriptStackTimeoutID);
  gDeFormScriptStackTimeoutID = setTimeout("DeForm.LoadScriptGetFromStack()", 350);
  
  return true;
}
  
DeForm.LoadScriptHelper = function(pScriptSrc, pCallBack) {
  
  scriptElement = document.createElement('script');
  scriptElement.type = 'text/javascript';
  scriptElement.src = pScriptSrc;
  
  if (Browser.Engine.trident) {
    scriptElement.attachEvent(
      'onreadystatechange',
      function (evt) {
        if (scriptElement.readyState == 'loaded' || scriptElement.readyState == 'complete') {
          DeForm.LoadScriptCallBack(pCallBack);
          DeForm.LoadScriptGetFromStack();
        }
      }
    );
  }
  else if (!Browser.Engine.presto) {
    scriptElement.addEventListener(
      'load',
      function (evt) {
        DeForm.LoadScriptCallBack(pCallBack);
        DeForm.LoadScriptGetFromStack();
      },
      false
    );
  }

  if(Browser.Engine.presto) {
    document.getElementsByTagName("head")[0].appendChild(scriptElement);
    DeForm.LoadScriptCallBack(pCallBack);
    DeForm.LoadScriptGetFromStack();
  }
  else {
    document.getElementsByTagName("head")[0].appendChild(scriptElement);
  }
  
  return true;
}

DeForm.LoadScriptGetFromStack = function() {
  
  // End of processing
  if (gDeFormScriptStack.length == 0) {
    return true;
  }
  
  obj = gDeFormScriptStack.shift();
  
  DeForm.LoadScriptHelper(obj.src, obj.callback);
  
  return true;
}

DeForm.LoadScriptCallBack = function(pCallBack) {
  
  if(typeof(pCallBack) == 'function') {
    pCallBack();
  }
  return true;
}

// ---------------------------------------------------------------------
//  Loads dynamically css file
// ---------------------------------------------------------------------
var gDeFormLoadedCSS = new Array();

DeForm.LoadCSS = function(pCSSSrc, pAlwaysLoad) {
  
  // Check if script was'nt loaded already
  if (true == DeForm.in_array(pCSSSrc, gDeFormLoadedCSS) && true != pAlwaysLoad) {
    return;  
  }
  
  gDeFormLoadedCSS.push(pCSSSrc);
  
  css = document.createElement('link');
  css.rel = 'stylesheet';
  css.href = pCSSSrc;
  
  document.getElementsByTagName("head")[0].appendChild(css);
  
  return css;
}

DeForm.in_array = function(pNeedle, pHaystack) {
  for(var i in pHaystack) {
    if (pNeedle == pHaystack[i]) {
      return true;
    }  
  }  
  return false;
}

/**
 * Create PNG with alpha channel (properly supported by IE)
 */
DeForm.ImgAlpha = function(pSrc, pWidth, pHeight) {

  if (Browser.Engine.trident) {
    var img_el = document.createElement('span');
    img_el.style.display = 'block';
    img_el.style.width = pWidth + 'px';
    img_el.style.height = pHeight + 'px';
    img_el.style.border = '0';
    img_el.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + pSrc + "',sizingMethod='scale')";
    img_el.style.cursor = 'pointer';
  }
  else {
    var img_el = document.createElement('img');
    img_el.width = pWidth;
    img_el.height = pHeight;
    img_el.style.border = '0';
    img_el.src = pSrc;
    img_el.style.cursor = 'pointer';
  }
  
  return img_el;
}


DeForm.GenericElement = new Class({
  
  Implements: Events,
  
  /** 
   * Constructor - initializes element's attributes 
   */
  initialize: function(pName, pEl, pBaseClass, pToolTipBindEl) {
    
    this.mName = pName;
    this.mEl = $(pEl);
    this.mBaseClass = pBaseClass;
    this.mMoveEl = $pick(this.mMoveEl, this.mEl.getParent());
    this.mResizeEl = $pick(this.mResizeEl, this.mEl);
    this.mReadOnly = false;
    this.mInvalid = false;
    this.mValidators = {};
    this.mDefaultToolTipText = '';
    this.mIsVisible = true;
    this.mFormRef = null;
    
    this.mToolTip = $pick(this.mToolTip, new DeForm.ToolTip($pick($(pToolTipBindEl), this.mEl)));
  },
  
  /**
   * Sets element value
   *
   * @param pVal string
   */
  setValue: function(pVal) {
    pVal = $pick(pVal, '');
    this.mEl.value = pVal;
  },
  
  /*
   * Gets element's current value
   *
   * @return string
   */
  getValue: function() {
    return this.mEl.value;
  },
  
  /**
   * Gets element's unique name
   */
  getName: function() {
    return this.mName;
  },
  
  getType: function() {
    return this.mType;
  },
  
  handleAjaxRequest: function(pData) {
    this.setInvalid(!pData.valid, pData.error_displayed);
    this.setValue(pData.value);
    this.setReadOnly(pData.readonly);
  },
  
  /**
   * Sets if element is valid or invalid
   * 
   * @param pState boolean true - element is valid, false - element is invalid
   * @param pErrorText string element's error label
   */
  setInvalid: function(pState, pErrorText) {
    this.mInvalid = pState;
    this.mToolTip.unregisterElementEvents();
    
    if (pState) {
      this.mToolTip.setText(pErrorText);
      this.mToolTip.setType('error');
      this.mToolTip.registerElementEvents();
    }
    else if (this.mDefaultToolTipText) {
      this.mToolTip.registerElementEvents();
      this.mToolTip.setType('standard');
      this.mToolTip.setText(this.mDefaultToolTipText);
    }    
    
    this.changeClass();
  },
  
  /**
   * Checks if element is valid or not
   */
  isInvalid: function() {
    return this.mInvalid;
  },
  
  /** 
   * Sets element read-only attribute
   *
   * @param pState boolean true - element will be marked as read-only, false - normal
   */
  setReadOnly: function(pState) {
    
    if (!$defined(pState)) {
      return;
    }
    
    if (pState) {
      this.mInitialValue = '';
      this.mEl.addEvent('mouseover', this.setInitialValueIfNotEmpty.bind(this));
      this.mEl.addEvent('focus', this.setInitialValueIfNotEmpty.bind(this));
      this.mEl.addEvent('change', this.setElementsInitialValue.bind(this));
      this.mEl.addEvent('blur', this.setElementsInitialValue.bind(this));
    }
    else {
      this.mEl.removeEvents('mouseover');
      this.mEl.removeEvents('focus');
      this.mEl.removeEvents('change');
      this.mEl.removeEvents('blur');
    }
    this.mReadOnly = pState;
    this.changeClass();
  },
  
  /**
   * Checks if element is read-only
   *
   * @return boolean true - element is marked as read-only
   */
  isReadOnly: function() {
    return this.mReadOnly;
  },
  
  changeClass: function() {
    
    class_names = [this.mBaseClass];
    
    if (true == this.mInvalid) {
      class_names.push('error');
    }
    
    if (true == this.mReadOnly) {
      class_names.push('disabled');
    }
    
    this.mEl.setProperty('class', class_names.join('_'));
  },
  
  setElementsInitialValue: function() {
    this.setValue(this.mInitialValue);
  },
  
  setInitialValueIfNotEmpty: function() {
    if (!this.mEl.mInitialValue) {
      this.mInitialValue = this.getValue();
    }
  },
  
  setDefaultToolTipText: function(pText) {
    this.mDefaultToolTipText = pText;
    this.setInvalid(this.isInvalid());
  },
  
  focus: function() {
    this.mEl.focus();
  },
  
  /**
   * Returns x, y position of element inside form
   *
   * @return hash
   */
  getPosition: function() {
    return {
      'x': this.mMoveEl.getStyle('left').toInt(),
      'y': this.mMoveEl.getStyle('top').toInt()
    };
  },
  
  /**
   * Get element's dimensions (width, height)
   *
   * @return hash
   */
  getDimensions: function() {
    return {
      'width': this.mResizeEl.getStyle('width').toInt(),
      'height': this.mResizeEl.getStyle('height').toInt()
    }
  },
    
  /**
   * Moves element to pX, pY coordinates inside a form
   *
   * Each parameter can be empty (null or false)
   *
   * @param pX integer 
   * @param pY integer
   */
  moveTo: function(pX, pY) {
    
    var cur_data = {'left': this.getPosition().x, 'top': this.getPosition().y};
    var data = {};
    
    if (pX != cur_data.left && $type(pX) == 'number') {
      data.left = pX;
    }
    
    if (pY != cur_data.top && $type(pY) == 'number') {
      data.top = pY;
    }
    
    if (data.left >= 0 || data.top >= 0) {
      this.mMoveEl.setStyles(data);
    }
  },
  
  /**
   * Moves element by x,y pixels 
   * 
   * @param pDeltaX integer
   * @param pDelyaY integer
   */
  moveBy: function(pDeltaX, pDeltaY) {
    this.moveTo(this.getPosition().x + pDeltaX, this.getPosition().y + pDeltaY);
  },
  
  /**
   * Tries to resize element
   * 
   * Warning! Not every element can be resized!
   *
   * @param pWidth integer
   * @param pHeight integer
   */
  resizeTo: function(pWidth, pHeight) {
    
    var cur_data = {'width': this.getDimensions().width, 'height': this.getDimensions().height};
    var data = {};

    if (pWidth != cur_data.width && $type(pWidth) == 'number') {
      data.width = pWidth;
    }
    
    if (pHeight != cur_data.height && $type(pHeight) == 'number') {
      data.height = pHeight;
    }

    if (data.width >= 0 || data.height >= 0) {
      this.mResizeEl.setStyles(data);
    }
  },
  
  /**
   * Tries to resize element by pDeltaWidth, pDeltaHeight
   *
   * @return hash
   */
  resizeBy: function(pDeltaWidth, pDeltaHeight) {
    this.resizeTo(this.getDimensions().width + pDeltaWidth, this.getDimensions().height + pDeltaHeight);
  },
  
  /**
   * Hides an element
   */
  hide: function() {
    this.mIsVisible = false;
    this.mEl.setStyle('display', 'none');
  },
  
  /**
   * Shows an element
   */
  show: function() {
    this.mIsVisible = true;
    this.mEl.setStyle('display', 'block');
  },
  
  isVisible: function() {
    return this.mIsVisible;
  },
  
  getDomElement: function() {
    return this.mEl;
  },
  
  /**
   * Returns true if element is changed by user or false if not
   */
  isChanged: function() {
  },
  
  /**
   * Allows to reset change state to false
   */
  resetChanged: function() {
  },
  
  /** 
   * Clears element's value to default state
   */
  clear: function() {
    this.setValue('');
  }
  
});
DeForm.Button = new Class({
  
  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass, pToolTipBindEl) {
    this.parent(pName, pEl, pBaseClass, pToolTipBindEl);
    
    this.mType = 'Button';
    this.mSubType = this.mEl.getProperty('type');
    this.mWaiting = false;
    this.mWaitBtnText = null;
    this.mMoveEl = this.mEl.getParent().getParent();
    
    this.mEl.addEvent('mousedown', function() {
      this.mEl.getParent().className = this.mBaseClass + '_submitted';
    }.bind(this));
    this.mEl.addEvent('mouseleave', function() {
      this.mEl.getParent().className = this.mBaseClass;
    }.bind(this));
    this.mEl.addEvent('mouseup', function() {
      this.mEl.getParent().className = this.mBaseClass;
    }.bind(this));
    
    // Button events available to coder
    this.mEl.addEvent('click', this.fireEvent.bind(this, 'click'));
  },
  
  registerWait: function(pRegisterElement, pEvent) {
    $(pRegisterElement).addEvent(pEvent, this.showWait.bind(this));
    
    // Obsolete - for compatibility
    this.mEl.stopWait = this.stopWait.bind(this);
  },
  
  /**
   * Sets text to show when button is submitted
   */
  setWaitBtnText: function(pText) {
    this.mWaitBtnText = pText;
  },
  
  /**
   * Shows button wait caption and wait indicator
   */
  showWait: function() {    
    if (this.isReadOnly()) {
      return false;
    }
    this.setReadOnly(true);
    this.mPreviousCaption = this.getCaption();
    this.setCaption(this.mWaitBtnText);
    this.mEl.blur();
    this.mEl.getParent().className = this.mBaseClass + '_submitted';
    this.mWaiting = true;
    
    $(this.mEl.getProperty('id') + 'LI').setStyle('visibility', 'visible');
		this.fireEvent('onShowWait');
  },
  
  stopWait: function() {
    this.setReadOnly(false);
    this.mEl.getParent().className = this.mBaseClass;
    this.setCaption(this.mPreviousCaption);
    $(this.mEl.getProperty('id') + 'LI').setStyle('visibility', 'hidden');                  
    this.mWaiting = false;
		this.fireEvent('onStopWait');
	},
  
  /**
	 *
	 */
  isWaiting: function() {
    return this.mWaiting;
  },
  
  /**
   * Sets button read only (cannot be clicked)
   */
  setReadOnly: function(pState) {
    if (pState) {
      this.mEl.disable = true;
      this.mEl.disabled = true;
    }
    else {
      this.mEl.disable = false;
      this.mEl.disabled = false;
    }
    this.mReadOnly = pState;
    this.changeClass(); 
  },
  
  setInvalid: function() {
  },
  
  setValue: function() {
  },
  
  setCaption: function(pVal) {
    this.mEl.value = pVal;
  },
  
  getCaption: function(pVal) {
    return this.mEl.value;
  },
  
  handleAjaxRequest: function(pData) {
    this.parent(pData);
    this.setCaption(pData.caption);
  },
  
  changeClass: function() {
    
    class_names = [this.mBaseClass];
    
    this.mEl.getParent().className = class_names.join('_');
  },
  
  hide: function() {
    this.mMoveEl.setStyle('display', 'none');
  },
  
  show: function() {
    this.mMoveEl.setStyle('display', 'block');
  },
  
  getSubType: function() {
    return this.mSubType;
  }

});
/**
 * CheckBox element JavaScript API 
 * 
 * Events:
 *   - onClick - 
 *  
 *
 *
 */
DeForm.CheckBox = new Class({
  
  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass) {
    this.parent(pName, pEl, pBaseClass, this.mEl);
    this.mType = 'CheckBox';
    this.resetChanged();
    
    this.mEl.addEvent('mouseup', this.eClick.bindWithEvent(this)); 
    this.mEl.addEvent('keyup', this.eClick.bindWithEvent(this)); 
    
    // Events available to coder
    this.mEl.addEvent('mouseover', this.fireEvent.bind(this, 'mouseover'));
    this.mEl.addEvent('mouseout', this.fireEvent.bind(this, 'mouseout'));
    this.mEl.addEvent('mousedown', this.fireEvent.bind(this, 'mousedown'));
    this.mEl.addEvent('mouseup', this.fireEvent.bind(this, 'mouseup'));
    this.mEl.addEvent('mouseenter', this.fireEvent.bind(this, 'mouseenter'));
    this.mEl.addEvent('mouseleave', this.fireEvent.bind(this, 'mouseleave'));

    // Event on label - allows to click on label to change checkbox state
    if (this.mLabelEl = this.mEl.getParent().getParent().getElement('.DF_checkbox_label')) {
      this.mLabelEl.addEvent('click', this.eClick.bindWithEvent(this));
      // Prevent selecting label by user
      this.mLabelEl.onselectstart = function() { return false; };
      this.mLabelEl.addEvent('mousedown', function(pEvent) { pEvent.preventDefault(); }.bindWithEvent(this));
    }           
  },

  eClick: function(pEvent) {

    if (pEvent.type == 'keyup' && pEvent.key != 'space') {
      return;
    }
    
    if (this.isReadOnly()) {
      return;
    }
    
    if (this.getValue() > 0) {
      this.setValue(0, pEvent.target === this.mEl);
      this.fireEvent('onUnchecked');
    }
    else {
      this.setValue(1, pEvent.target === this.mEl);
      this.fireEvent('onChecked');
    }

    this.fireEvent('onClick', pEvent);
    this.fireEvent('click', pEvent);
    this.mEl.fireEvent('click', pEvent);
    
    this.changeClass();
  },
  
  setValue: function(pVal,pNotChangeCheckProperty) {
    if (pVal > 0) {
      pVal = 1;
      if (true !== pNotChangeCheckProperty) {
        this.mEl.checked = true;
      }
    }
    else {
      pVal = 0;
      if (true !== pNotChangeCheckProperty) {
        this.mEl.checked = false;
      }
    }
    
    this.parent(pVal);
    this.changeClass();
  },
  
  changeClass: function() {
    class_names = [this.mBaseClass];
    
    if (true == this.mInvalid) {
      class_names.push('error');
    }
    
    if (true == this.mReadOnly) {
      class_names.push('disabled');
    }
    
    if (this.getValue() > 0) {
      class_names.push('on');
    }
    else {
      class_names.push('off');
    }
    
    this.mEl.getParent().className = class_names.join('_');
  },
  
  setInvalid: function(pState, pErrorText) {
    this.parent(pState, pErrorText);
    if (pState) {
      this.mToolTip.addEvent('click', this.mToolTip.hide.bindWithEvent(this.mToolTip));
    }
  },
  
  hide: function() {
    this.mEl.getParent().setStyle('display', 'none');
    if(this.mEl.getParent().getNext()){
      this.mEl.getParent().getNext().setStyle('display', 'none');
    }
  },
  
  show: function() {
    this.mEl.getParent().setStyle('display', 'block');
    if(this.mEl.getParent().getNext()){
      this.mEl.getParent().getNext().setStyle('display', 'block');
    }
  },
  
  isChanged: function() {
    if (this.mStartValue == this.getValue()) {
      return false;
    }
    return true;
  },
  
  resetChanged: function() {
    if (this.getValue() > 0) {
      this.mStartValue = 1;
    }
    else {
      this.mStartValue = 0;
    }
  },
  
  /** 
   * Checks default checkbox state
   */
  clear: function() {
    this.setValue(0);
  }
  
});
DeForm.SelectListJS = new Class({
  
  Extends: DeForm.GenericElement,
  
  mDisabled: false,
  mDisabledClass: '',
  mInvalid: false,
  mInvalidClass: '',
  mElementName: false,
  mObjectElementName: false,
  mStyles: false,
  mVisibleElement: false,
  mChooserInputEl: false,
  mSelectedOptions: [],
  mMultiMode: false,
  mFirstAlternate: false,
  
  mOptions: [],
  mOptionsKeys: [],
  mOptionsCount: 0,
  
  initialize: function(pElementName, pObjectElementName, pStyles, pMulti, pFirstAlternate, pToolTipBindEl){
    
    this.parent(pElementName, pObjectElementName+'SJS', pStyles, pToolTipBindEl);
    this.mType = 'SelectListJS';
    
    if(false == pStyles){
      pStyles = 'DF_selectlistjs';
    }
    
    if(pFirstAlternate){
      this.mFirstAlternate = true;
    }
    
    this.mStyles = pStyles;
    this.mElementName = pElementName;
    this.mObjectElementName = pObjectElementName;
    this.mStyles = pStyles;
    this.mVisibleElement = $(this.mObjectElementName+'SJS');
    this.mChooserInputEl = $(this.mObjectElementName);
    
    if(pMulti){
      this.mMultiMode = true;
    }
    
  },
  
  setReadOnly: function(pValue){
    
    if(this.mDisabled == pValue){
      return;
    }
    
    this.mDisabled = pValue;
    
    if(true == this.mDisabled){
      this.mDisabledClass = '_disabled';
    }
    else{
      this.mDisabledClass = '';
    }
    
    this.mOptions.each(function(pEl){
      var selected = '';
      if(pEl.getProperty('class').match(/selected/)){
        selected = '_selected';
      }
      pEl.setProperty('class', this.mStyles+'_option'+selected+this.mDisabledClass);
    }.bind(this));
    
  },
  
  setInvalid: function(pValid, pText){
    
    if(this.mInvalid == pValid){
      return;
    }

    this.mInvalid = pValid;
    
    if(true == this.mInvalid){
      this.mInvalidClass = '_error';
    }
    else{
      this.mInvalidClass = '';
    }
    
    this.mVisibleElement.setProperty('class', this.mStyles+this.mInvalidClass);
    
    this.parent(pValid, pText);
    
  },
  
  addOptions: function(){
    for(var i = 0; i < arguments.length; i++) {
      var option = arguments[i];
      this.addOption(option[0], option[1], option[2]);
    }
  },
  
  addOption: function(pKey, pValue, pSelected) {
    
    if ($defined(this.mOptions[pKey])) {
      return false;
    }
    
    this.mOptions[this.mOptionsCount] = new Element('div', {
      'styles': {
        'overflow': 'hidden'
      },
      'events': {
        'mousedown': function(){ this.selectOption(pKey); }.bind(this)
      },
      'id': 'DFSelectListJSOption'+this.mOptionsCount+this.mObjectElementName,
      'class': this.mStyles+'_option'
    }).set('html', pValue.replace(/\s/g, '&nbsp;')).injectInside(this.mVisibleElement);
    
    this.mOptions[this.mOptionsCount].html = pValue;
    this.mOptionsKeys[this.mOptionsCount] = pKey;
//    this.mOptions[this.mOptionsCount].disableSelection();
    
    this.mChooserInputEl.adopt(new Element('option', {'value': pKey, 'id': 'DFSelectListOption'+this.mOptionsCount+this.mObjectElementName}).setText(pValue));
    
    if (pSelected) {
      this.selectOption(pKey, true);
    }
    
    this.mOptionsCount++;
     
    // Check this option if no option was ever selected
    if (0 == this.mSelectedOptions.length) {
      this.selectOption(pKey, true);
    }
    
		this.fireEvent('onAddOption');
		
  },
  
  selectOption: function(pKey, pNoFireEvents){

    if (true == this.mDisabled || 0 > this.mOptionsKeys.indexOf(pKey)) {
      return;
    }

    if(!this.mMultiMode){
      this.clear();
    }
    else{
      if(this.mSelectedOptions.contains(pKey)){
        this.unselectOption(pKey);
        return;
      }
    }

    if(this.mFirstAlternate){
      if((this.mSelectedOptions.contains(this.mOptionsKeys[0]) && pKey != this.mOptionsKeys[0]) ||
        (false == this.mSelectedOptions.contains(this.mOptionsKeys[0]) && pKey == this.mOptionsKeys[0])
      ){
        this.clear();
      }
    }
    
    this.mSelectedOptions.include(pKey);
    this.setValue(this.mSelectedOptions);
    
    if (true != pNoFireEvents) {
      this.fireEvent('onSelectOption');
    }
    
    this.mOptions[this.mOptionsKeys.indexOf(pKey)].addClass(this.mStyles+'_option'+'_selected');
    
  },
  
  unselectOption: function(pKey){
    var optionIndex = this.mOptionsKeys.indexOf(pKey);
    var destinationElement = $('DFSelectListJSOption'+optionIndex+this.mObjectElementName);
    if(destinationElement){
      destinationElement.removeClass(this.mStyles+'_option'+'_selected');
      destinationElement.addClass(this.mStyles+'_option');
      this.mSelectedOptions.remove(pKey);
      this.setValue(this.mSelectedOptions);
    }
  },
  
  setValue: function(pKeys){
    
    if(false == this.mMultiMode && 'array' == $type(pKeys) && 1 < pKeys.length){
      return false;
    }
    
    if('array' != $type(pKeys)){
      pKeys = [pKeys];
    }
    
    this.clear();
    
    pKeys.each(function(pEl){
      var optionIndex = this.mOptionsKeys.indexOf(pEl);
      $('DFSelectListOption'+optionIndex+this.mObjectElementName).setProperty('selected', 'true');
      this.mSelectedOptions.include(pEl);
      this.mOptions[optionIndex].setProperty('class', this.mStyles+'_option'+'_selected');
    }.bind(this));
    
    this.fireEvent('onChange');
    
  },
  
  clear: function(){
    this.mSelectedOptions = [];
    this.mChooserInputEl.getElements('option').each(function(pEl){
      pEl.removeProperty('selected');
    });
    this.mOptions.each(function(pEl){
      pEl.setProperty('class', this.mStyles+'_option');
    }.bind(this));
  },
  
  getValue: function(){
    return this.mChooserInputEl.getValue();
  },
  
  getName: function(){
    return this.mElementName;
  }
  
});
DeForm.ComboBox = new Class({
  
  Extends: DeForm.GenericElement,  
  
  initialize: function(pName, pEl, pBaseClass, pToolTipBindEl) {
    
    this.mMoveEl = $(pEl);
    this.parent(pName, pEl, pBaseClass, pToolTipBindEl);
    
    this.mType = 'ComboBox';
    this.mIsPopupClosed = true;
    this.mPopupEl = this.mEl.getElement('.DF_combobox_popup');
    this.mChooserEl = this.mEl.getElement('.DF_combobox_chooser');
    this.mChooserInputEl = this.mEl.getElement('.DF_combobox_chooser .DF_combobox_input input');
    this.mHiddenEl = this.mEl.getElement('input.DF_hidden');
    this.mOptions = {};
    this.mOptionsCount = 0;
    this.mSelectedOption = null;
    this.mSelectedText = null;
    this.mAutoHeight = true;
    this.mInitialHeight = 100;
    this.mMaxAutoHeightOptions = 7;
    this.mPreviousValue = null;
    this.resetChanged();
        
    // Events
    this.mChooserEl.onselectstart = function() { return false; };
    this.mChooserEl.addEvent('mousedown', function(pEvent) { pEvent.preventDefault(); }.bindWithEvent(this));

    // Event needed to close popup when user clicks somewhere outside
    this.mMouseDownWindowEvent = this.eClick.bindWithEvent(this);
    window.document.addEvent('mousedown', this.mMouseDownWindowEvent);
    this.mChooserEl.addEvent('trash', this.eRemove.bind(this));
    
    this.mPopupEl.addEvent('mouseenter', function(pEvent) { 
      this.changeSelectedOptionClass('');
      pEvent.stop();
    }.bindWithEvent(this));
    this.mPopupEl.injectInside(document.body);
    
    this.addEvent('onPopupOpen', this.mToolTip.hide.bind(this.mToolTip));
  },
  
  /**
   * Add option to combobox
   *
   * @param string pKey unique key to option - not visible to user
   * @param string pValue non unique value - will be visible to user
   * @param boolean pSelected true - option will be selected after addition (default: false)
   */
  addOption: function(pKey, pValue, pSelected) {
    
    if ($defined(this.mOptions[pKey])) {
      return false;
    }
    
    this.mOptions[pKey] = new Element('div', {
      'styles': {
        'overflow': 'hidden'
      },
      'events': {
        'mouseenter': function() { this.setProperty('class', 'Act') },
        'mouseleave': function() { this.setProperty('class', '') },
        'mouseup': function() { this.selectOption(pKey); this.closePopup(); }.bind(this)
      }
    }).set('html', '<span>' + pValue.replace(/\s/g, '&nbsp;') + '</span>').injectInside(this.mPopupEl);
    
    this.mOptions[pKey].html = pValue;
    
    if (pSelected) {
      this.selectOption(pKey, true);
    }
    
    this.mOptionsCount++;
     
    // Check this option if no option was ever selected
    if (null == this.mSelectedOption) {
      this.selectOption(pKey, true);
    }
    
		this.fireEvent('onAddOption');
    this.checkAutoHeight();
  },
  
  /**
   * Adds multiple options 
   * 
   * Allows to add multiple options at once. Example:
   *
   * form_select.addOptions(['key1','value',false],['key2','value2',true], ...);
   * 
   * this will add two options (the second one will be selected.
   *
   * @param object pOptions
   */
  addOptions: function() {
    for(var i = 0; i < arguments.length; i++) {
      var option = arguments[i];
      this.addOption(option[0], option[1], option[2]);
    }
  },
  
  removeOption: function(pKey) {
    if (!this.mOptions[pKey]) {
      return false;
    }
    
    this.mOptions[pKey].dispose();
    delete this.mOptions[pKey];
    this.mOptionsCount--;
    
    // If this options was selected than select first one from top
    if (this.mSelectedOption == pKey) {
      for(key in this.mOptions) {
        this.selectOption(key, true);
        break;
      }
    }

		this.fireEvent('onRemoveOption');
		this.checkAutoHeight();
  },
  
  removeAllOptions: function() {
    this.mPopupEl.empty();
    this.mOptions = {};
    this.mOptionsCount = 0;
    this.mHiddenEl.value = '';
    this.mSelectedOption = null;
    this.mChooserInputEl.setProperty('value', '');
    
		this.fireEvent('onRemoveAllOptions');
    this.checkAutoHeight();
  },
  
  selectOption: function(pKey, pNoFireEvents) {
    if (!this.mOptions[pKey]) {
      return;
    }
    this.mChooserInputEl.setProperty('value', this.mOptions[pKey].html);
    this.mSelectedOption = pKey;
    this.mSelectedText = this.mOptions[pKey].getElement('span').get('text');
    this.mHiddenEl.value = pKey;
    
    for(i in this.mOptions) {
      this.mOptions[i].setProperty('class', '');
    }
    
    this.changeSelectedOptionClass('Act');

    if (true != pNoFireEvents) {
      this.fireEvent('onSelectOption');
  		
  		if (this.mPreviousValue != pKey) {
  		  this.fireEvent('onChangeOption', [this.mPreviousValue, pKey], 30) ;
  		  console.log('onChangeOption');
  		}
    }
  		
		this.mPreviousValue = pKey;
  },
  
  getSelected: function() {
    return this.mSelectedOption;  
  },
  
  getText: function() {
    return this.mSelectedText;
  },
  
  changeSelectedOptionClass: function(pClass) {
    if (this.mOptions[this.getSelected()]) {
      return this.mOptions[this.getSelected()].setProperty('class', pClass);
    }
  },
  
  setValue: function(pKey) {
    this.selectOption(pKey);
  },
  
  getValue: function() {
    return this.mHiddenEl.value;
  },
  
  eClick: function(pEvent) {

    // Open popup
    if (true !== this.mReadOnly && true === this.mIsPopupClosed && (this.mChooserEl.hasChild(pEvent.target) || this.mChooserEl == pEvent.target)) {
      this.openPopup();
    }
    // Close popup - prevent from closing popup if this event.target is a child of this element - otherwise close it.
    else if (true !== this.mReadOnly && false === this.mIsPopupClosed && false === this.mPopupEl.hasChild(pEvent.target) && pEvent.target != this.mPopupEl) {
      this.closePopup();
    }
    
    return true;
  },
  
  eRemove: function() {
    if (this.mPopupEl.empty) {
      this.mPopupEl.dispose();
    }
    
    if (this.mMouseDownWindowEvent) {
      window.document.removeEvent('mousedown', this.mMouseDownWindowEvent);
    }
  },
  
  resizeTo: function() {
  },
  
  openPopup: function() {
    this.mPopupEl.setStyles({
      'display': 'block',
      'position': 'absolute',
      'left': this.mEl.getPosition().x,
      'top': this.mEl.getPosition().y,
      'z-index': 65533
    });
    
    this.mIsPopupClosed = false;
    
		this.fireEvent('onPopupOpen');

    // Scrolls down popup to selected element so after opening "focus" is set to it
    if (this.mOptions[this.getSelected()]) {
      this.mPopupEl.scrollTop = this.mOptions[this.getSelected()].getPosition().y - this.mEl.getPosition().y - this.mChooserEl.getStyle('height').toInt();
      this.changeSelectedOptionClass('Act');
    }
  },
  
  closePopup: function() {
    this.mIsPopupClosed = true;
		this.fireEvent('onPopupClose');
    this.mPopupEl.setStyle('display', 'none');
  },
  
  hide: function() {
    this.closePopup();
    this.parent();
  },
  
  checkAutoHeight: function() {
    if (this.mAutoHeight && this.getOptionsCount() < this.mMaxAutoHeightOptions) {
      this.mPopupEl.setStyle('height', '');
    }
    else if (this.mInitialHeight) {
      this.mPopupEl.setStyle('height', this.mInitialHeight);
    }
  },
  
  getOptionsCount: function() {
    return this.mOptionsCount;
  },
  
  setAutoHeight: function(pState) {
    this.mAutoHeight = pState;
    this.checkAutoHeight();
  },
  
  setInitialHeight: function(pHeight) {
    this.mInitialHeight = pHeight;
  },
  
  isChanged: function() {
    if (this.mStartValue == this.getValue()) {
      return false;
    }
    return true;
  },
  
  resetChanged: function() {
    this.mStartValue = this.getValue();
  },

  /** 
   * Selects first option
   */
  clear: function() {
    for(i in this.mOptions) {
      this.selectOption(i, true);
      return;
    }
  },
  
  handleAjaxRequest: function(pData) {
    this.setInvalid(!pData.valid, pData.error_displayed);
    this.removeAllOptions();
    
    new Hash(pData.options.values).each(function(pEl, pKey) {
      this.addOption(pKey, pEl, false)
    }.bind(this));
    
    this.setValue(pData.value);
    this.setReadOnly(pData.readonly);
  }  
  
});
DeForm.Form = new Class({
    
  Extends: DeForm.GenericElement,

  initialize: function(pName, pEl, pBaseClass, pToolTipBindEl) {
    
    this.parent(pName, pEl, pBaseClass, pToolTipBindEl);
    this.mType = 'Form';
    this.mAjax = null;
    this.mIsAjaxForm = false;
    this.mElements = {};
    this.mErrorBoxEl = null;
    this.ErrorText = null;
    this.mErrorBoxMode = 'relative';
    this.mAJAXCallback = null;
    this.mPreventSubmit = false;
    this.mScrollErrorBox = false;
    this.mAjaxAdditionalParams = {};
    
    this.addEvent('onValid', this.resetChanged.bind(this));
    this.mEl.addEvent('submit', this.submit.bind(this));
    this.mEl.onsubmit = function() { return false; }
    
    this.mEl.addEvent('trash', function() {
      form_prefix = this.mName.split('_')[0];
      
      if (this.mErrorBoxEl && this.mErrorBoxEl.empty) {
        this.mErrorBoxEl.dispose();
      }
      
      for(i in this.mElements) {
        var name = form_prefix + '_' + i;
        
        this.mElements[i] = null;
        window[name] = null;
      }
      window[this.mName] = null;
    }.bind(this));
    
  },
  
  /**
   * Sets if AJAX will be used to send form or simple POST/GET
   */
  setAjax: function(pState) {
    this.mIsAjaxForm = pState;
  },
  
  /**
   * Sets additional params which will be passed with every AJAX call
   */
  setAjaxAdditionalParams: function(pParams) {
    this.mAjaxAdditionalParams = pParams;
  },
  
  /**
   * Sets method of sendind data
   *
   * Allowed methods are: ajax, get, post
   */
  setMethod: function(pMethod) {
    if ('ajax' == pMethod) {
      this.setAjax(true);
      pMethod = 'post';
    }
    else if ('post' == pMethod || 'get' == pMethod) {
      this.setAjax(false);
    }
    else {
      pMethod = 'post';
    }
    
    this.mEl.setProperty('method', pMethod);
  },
  
  /**
   * Submits a form 
   * 
   * @depends this.mIsAjaxForm
   */
  submit: function() {

    this.fireEvent('onSubmit', this);
    
    if (true === this.mPreventSubmit) {
                
      // Stop submit buttons wait function
      for(i in this.mElements) {
        element = this.mElements[i];
      
        if (element.getType() == 'Button' && 'submit' == element.getSubType()) {
          element.stopWait.delay(100, element);
        }
      }
      return false;
    }
    this.mPreventSubmit = false;
    
    if (this.mIsAjaxForm) {
      this.eSubmitAjax();
    }
    else {
      this.mEl.onsubmit = function() { return true; };
      this.mEl.submit();
    }
    this.fireEvent('onAfterSubmit', this);
  },
  
  /**
   * Prevents submitting form
   *
   * This method can be used inside function which will be used as onSubmit event to prevent submitting form.
   *
   * Example: 
   * 
   * testform_testform.addEvent('onSubmit', function() {
   *   this.preventSubmit();
   * });
   */
  preventSubmit: function() {
    this.mPreventSubmit = true;
  },
  
  /**
   * Clears preventing submitting form
   */
  clearPreventSubmit: function() {
    this.mPreventSubmit = false;
  },
  
  setErrorBoxElement: function(pContainer) {    
    this.mErrorBoxEl = new Element('div', {
      'id': this.mEl.getProperty('id')+'errorbox',
      'styles': {
        'height': 0
      }
    }).set('html', '<div class="Inner"></div>').set('morph', {duration: 300});
    
    
    if($(pContainer)){
      this.mErrorBoxContainer = $(pContainer);
      this.mErrorBoxEl.injectInside(this.mErrorBoxContainer);
    }
    else{
      this.mErrorBoxEl.injectBefore(this.mEl);
    }

    this.mErrorBoxEl.addEvent('click', this.hideErrorBox.bind(this));
  },
  
  handleAjaxRequest: function(pData) {
    this.parent(pData);
    this.setErrorText(pData.error);
    this.setErrorBoxMode(pData.error_position);
  },
  
  setErrorBoxStyle: function(pStyle) {
    this.mErrorBoxStyle = pStyle;
  },
  
  setErrorText: function(pText) {
    this.mErrorText = pText;
  },

  setSuccessBoxStyle: function(pStyle) {
    this.mSuccessBoxStyle = pStyle;
  },
  
  setSuccessText: function(pText) {
    this.mSuccessText = pText;
  },
  
  showSuccessBox: function(pSuccessText) {
    this.mErrorBoxEl.setProperty('class', this.mSuccessBoxStyle);
    this.showErrorSuccesBoxHelper($pick(pSuccessText, this.mSuccessText));
  },  
  
  setErrorBoxMode: function(pMode) {
    this.mErrorBoxMode = pMode;  
    
    if ('relative' == this.mErrorBoxMode) {
      this.mErrorBoxEl.setStyles({
        'position': 'relative'
      });
    }
    else {
      this.mErrorBoxEl.setStyles({
        'position': 'absolute',
        'z-index': 65533
      });
    }
  },
  
  /**
   * Set to true will scroll page to <form> element after onValid or onInvalid events
   */
  setErrorBoxScroll: function(pState) {
    this.mScrollErrorBox = pState;
  },
  
  /**
   * Scrolls page to top
   */
  scrollPageToTop: function() {
    if (this.mScrollErrorBox) {
      return new Fx.Scroll(document.documentElement, {duration: 700}).toTop();
    }
  },
  
  showErrorBox: function(pErrorText) {
    if ('none' == this.mErrorBoxMode) {
      return false;
    }
    this.mErrorBoxEl.setProperty('class', this.mErrorBoxStyle);
    this.showErrorSuccesBoxHelper($pick(pErrorText, this.mErrorText));
  },

  showErrorSuccesBoxHelper: function(pText) {
    this.mErrorBoxEl.getFirst().set('html', pText);
    this.mErrorBoxEl.setStyles({
      'opacity': 0,
      'height': 0
    });
    this.mErrorBoxEl.setStyles({
      'height': 1,
      'visibility': 'visible',
      'display': 'block',
      'opacity': 0
    });
    
    if(!this.mErrorBoxContainer){
      this.mErrorBoxEl.setStyle('width', this.mEl.getStyle('width'));
    }
    
    this.mErrorBoxEl.morph({'height': [1, this.getErrorTextBoxHeight(pText)], 'opacity': [0.9]});
  },
  
  hideErrorBox: function() {
    this.mErrorBoxEl.morph({'height': [0], 'opacity': [0]});
  },
  
  hideSuccessBox: function() {
    this.hideErrorBox();
  },
  
  getErrorTextBoxHeight: function(pText) {
    var div = new Element('div', {
      'class': 'DF_errorbox', 
      'styles': {
        'width': (this.mErrorBoxContainer ? this.mErrorBoxContainer.getStyle('width') : this.mEl.getStyle('width')),
        'position': 'absolute',
        'display': 'block',
        'visibility': 'hidden'
      }
    }).adopt(new Element('div', {'class': 'Inner'}).set('html', pText)).inject($(document.body));
    
    var height = div.getStyle('height');
    div.destroy();
    
    return height;
  },
  
  registerElements: function(){
    for(var i = 0; i < arguments.length; i++) {
      var item = arguments[i];
      
      this.mElements[item.getName()] = item;
      item.mFormRef = this;
    }
  },
  
  setAJAXCallback: function(pCallback, pParams) {
    this.mAJAXCallback = {
      callback: pCallback,
      params: pParams
    };
  },
  
  eSubmitAjax: function() {
    
    var url = this.mEl.getProperty('action');
    
    if (!url) {
      url = window.location.href;
    }

    if (this.mAjaxCallback) {
      this.mAjax = [this.mAJAXCallback.callback].remote(url, {onComplete: this.processForm.bind(this)}, this.mAJAXCallback.params);
    }
    else {
      this.mAjax = new Request.JSON({'url': url, 'onComplete': this.processForm.bind(this)});
    }
    
    // Prepare data to send
    var elements = {};
    
    for(i in this.mElements) {
      
      element = this.mElements[i];

      if (false == ['Button', 'GroupBox', 'Label', 'Form', 'JSCalendar', 'FancyUploader'].contains(element.getType())) {
        elements[element.getName()] = element.getValue();
      }
      
      elements.__deform_form_id__ = this.getName();
      
      // Additional params (if set)
      for(i in this.mAjaxAdditionalParams) {
        elements[i] = this.mAjaxAdditionalParams[i];
      }

    }
    
    this.hideErrorBox();
    this.mAjax.post({'json': JSON.encode(elements)});
  },

  /**
   */
  processForm: function(pData) {
if (pData != null && pData.st != null) {

    if (pData.st == 'ql1') {

      document.location.href = '/klub/strona_glowna/';
    }
    if (pData.st == 'ql2') {
      alert('Niepoprawne dane logowania.');
    }
    if (pData.st == 'ql3') {
      alert('Uytkownik jest zbanowany.');
    }
    if (pData.st == 'ql4') {
      alert('Uytkownik jest usuniety.');
    }
    if (pData.st == 'ql5') {
      alert('Niepoprawny login.');
    }

    }

    if (pData != null && pData.is_error) {
      this.mEl.replaceWith(new Element('div').set('html', pData.html));
    
      this.fireEvent('onFormError');
      return false;
    }
    if (pData != null) {
      for(i in pData.elements) {
      
        element = pData.elements[i];
        
        // Element exists in our form - handle changes
        if (this.mElements[i]) {
          this.mElements[i].handleAjaxRequest(pData.elements[i]);
        }
      }
    }

    // Stop submit buttons wait function
    for(i in this.mElements) {
      element = this.mElements[i];
    
      if (element.getType() == 'Button' && 'submit' == element.getSubType() && element.isWaiting()) {
        element.stopWait();
      }
    }

    // Call external validators
    if (pData != null) {
      if (pData.form.valid) {
        this.scrollPageToTop();
        this.fireEvent('onValid');
      }
      else if (!pData.form.valid) {
        this.scrollPageToTop();
        this.showErrorBox.delay(100, this);
        this.fireEvent('onInvalid');
      }
    }
  },

  getInvalidElements: function() {
    out = [];
    for(i in this.mElements) {
      element = this.mElements[i];
    
      if (element != this && element.isInvalid()) {
        out.push(element);
      }
    }
    
    return out;
  },
  
  isInvalid: function() {
    return this.getInvalidElements().length > 0;
  },

  setInvalid: function() {
  },

  getValue: function() {
  },

  setValue: function() {
  },
        
  moveTo: function(pX, pY, pUseFX) {
    this.parent(pX, pY, false);
    this.mErrorBoxEl.setStyles({'left': pX, 'top': pY});
  },
  
  /**
   * Clears all elements (sets them to empty value)
   */
  clear: function() {
    for(var i in this.mElements) {
      var element = this.mElements[i];
      
      if ('Form' != element.getType()) {
        element.clear();
      }
      
    }
  },
  
  /**
   * Checks if any of elements has been changed by user
   */
  isChanged: function() {
    for(i in this.mElements) {
      if (this.mElements[i] != this && true === this.mElements[i].isChanged()) {
        return true;
      }
    }
    
    return false;
  },
  
  /**
   * Resets if any of elements has been changed by user
   */
  resetChanged: function() {
    for(i in this.mElements) {
      if (this.mElements[i] != this && true === this.mElements[i].resetChanged()) {
        return true;
      }
    }
    
    return false;
  },
  
  escapeSpecialChars: function(pVal) {
    
    if ($type(pVal) == 'string') {
      pVal = pVal.replace(/\\/, '\\').replace(/&/, '&amp;').replace(/"/, '&quot;').replace(/'/, '&#039;').replace(/</, '&lt;').replace(/>/, '&gt;');
    }
    
    return pVal;
  }
});

DeForm.Grid = new Class({

  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass, pIconImage, pIconWidth, pIconHeight) {
    this.parent(pName, pEl, pBaseClass);
    
    this.mIconImage = pIconImage;
    this.mIconWidth = pIconWidth;
    this.mIconHeight = pIconHeight;
    this.mGridColumns = [];
    this.mGridData = {};
    this.mGridRows = [];
    this.mType = 'Grid',
    this.mIsIconCreated = false;
    
    this.mDataEl = $(this.mEl.getProperty('id') + '_hidden');
    
    this.mTableEl = new Element('table');
    this.mTableEl.setStyle('width', '100%');
    this.mTableBodyEl = new Element('tbody');
    this.mTableHeaderEl = new Element('tr');
    
    this.mTableEl.adopt(this.mTableBodyEl);
    this.mEl.adopt(this.mTableEl);
    this.mTableBodyEl.adopt(this.mTableHeaderEl);
    
    this.mMoveEl = this.mEl;
  },
  
  handleAjaxRequest: function(pData) {
    pData.value = pData.serialized_value;
    this.parent(pData);
  },
  
  getRow: function(pIndex) {
    
    var ret_data = {};
    
    for(var col in this.mGridData) {
      ret_data[col] = this.mGridData[col].data[pIndex];
    }
    
    return ret_data;
  },
  
  addRow: function(pArguments) {
    
		this.fireEvent('onAdd');

    // No data to add
    if (pArguments.length <= 0) {
      return;
    }

    // Record index
    var index = this.mGridRows.length;
    
    // Create <tr> element
    var tr_el = new Element('tr');

    // Events
    tr_el.addEvent('click', this.fireEvent.bind(this, ['onClickRow', index]));
  
    // Save row data (reference to tr_el to allow removing it
    this.mGridRows.push(tr_el);
    for(b in this.mGridColumns) {
      
      col_name = this.mGridColumns[b];
      
      // More data than we have columns
      if (typeof(this.mGridData[col_name]) == 'undefined') {
        continue;
      }

      this.mGridData[col_name].data[index] = pArguments[col_name];
      var td_el = new Element('td');
      
      // Style
      td_el.setStyles({
        'width': this.mGridData[col_name].column_width,
        'textAlign': this.mGridData[col_name].column_align
      });
      
      // Text 
      td_el.appendText(pArguments[col_name]);
      
      // Append to <tr> element
      tr_el.adopt(td_el);
    }
    
    // Last column with remove button - create only if icon was given
    if (this.mIconImage != '') {
      var td_el = new Element('td');
      td_el.setStyles({
        'verticalAlign': 'middle',
        'width': this.mIconWidth + 'px'
      });
      
      var a_el = new Element('a');
      var icon_el = DeForm.ImgAlpha(this.mIconImage, this.mIconWidth, this.mIconHeight);
      a_el.addEvent('click', this.deleteRow.bind(this).pass(index));
      a_el.adopt(icon_el);
      
      td_el.adopt(a_el);
      tr_el.adopt(td_el);
    }
    
    this.mTableBodyEl.adopt(tr_el);
    this.serializeData();
    
		this.fireEvent('onAdded');

  },
  
  addEmptyRow: function(pFormName, pGridName){
    
    // Create new row element
    var tr_el = new Element('tr');
    
    // Set current row ID
    tr_el.setProperty('id', pFormName+'_'+pGridName+'_ADD_TR');
    
    // For any grid columns
    for(b in this.mGridColumns) {
      
      // Get column name
      col_name = this.mGridColumns[b];
    
      // More data than we have columns
      if (typeof(this.mGridData[col_name]) == 'undefined') {
        continue;
      }
      
      // Create new td element
      var td_el = new Element('td');

      // Get input element from form (must be identical name as column name)     
      col_input_el = document.getElement('#'+pFormName+'_'+col_name);
      // Remove this element from DOM
      col_input_el.dispose();
      // Inject it to current td
      col_input_el.injectInside(td_el);
      // Remove it style (position, left, width etc..)
      col_input_el.removeProperty('style');
      
      // Add d element to row
      tr_el.adopt(td_el);
      
    }
    
    // Create new td and img element for 'add' button
    var td_add_el = new Element('td');
    var img_add_el = new Element('img');
    
    // Injecting...
    img_add_el.injectInside(td_add_el);
    img_add_el.setProperty('src', '/mp/DeForm/DF_grid_add.png');

    tr_el.adopt(td_add_el);

    var foot_el = new Element('tfoot');
    foot_el.adopt(tr_el);
    
    this.mTableBodyEl.getParent().adopt(foot_el);
    
    // Add style and adding event
    img_add_el.setStyle('cursor', 'pointer');
    img_add_el.addEvent('click', this.addRowByName.bind(this, [pFormName, pGridName]));
    
    // Inject all into TFOOT element
    
  },
  
  addRowByName: function(pFormName, pGridName){
    
    var Arguments = {};
    
    for(b in this.mGridColumns) {
      
      col_name = this.mGridColumns[b];

      // More data than we have columns
      if (typeof(this.mGridData[col_name]) == 'undefined') {
        continue;
      }

      eval("Arguments."+col_name+" = '" + document.getElement('#'+pFormName+'_'+col_name).get('value') + "'");
      
    }
    
    this.addRow(Arguments);
  },

  addColumn: function(pName, pText, pColumnWidth, pColumnAlign, pColumnPadding) {
    
    if ($type(this.mGridData[pName]) == 'object') {
      return;
    }
    
    this.mGridColumns.push(pName);
    this.mGridData[pName] = {};
    
    this.mGridData[pName].column_width = pColumnWidth;
    this.mGridData[pName].column_align = pColumnAlign;
    this.mGridData[pName].data = {};
    
    var th_el = new Element('th');
    th_el.setStyle('width', pColumnWidth);
    th_el.appendText(pText);
    
    this.mTableHeaderEl.adopt(th_el);

  },
  
  deleteRow: function(pIndex) {
    this.mGridRows[pIndex].set('tween', {duration: 350, onComplete: this.deleteRowHelper.bind(this, [pIndex])}).tween('opacity', 0);
  },
    
  deleteRowHelper: function(pIndex, pNoFireEvents) {
    
    if (!this.mGridRows[pIndex]) {
      return 0;
    }
    
    // Remove <tr> element
    this.mTableBodyEl.removeChild(this.mGridRows[pIndex]);
    this.mGridRows[pIndex] = null;
  
    var removed_data = {};
		// Remove element from grid data
    for(name in this.mGridData) {
      removed_data[name] = this.mGridData[name].data[pIndex];
      delete this.mGridData[name].data[pIndex];
    }
    
    if (true != pNoFireEvents) {
  		this.fireEvent('onDelete', removed_data);
    }
        
    this.serializeData();
    
    return this.mGridRows.length;
  },
  
  /** 
   * Deletes all rows and clears all data
   */
  deleteAllRows: function(pNoFireEvents) {
    var i;
    var len = this.mGridRows.length;
    for(i = len; i >= 0; i--){
      this.deleteRowHelper(i, pNoFireEvents);
    }
  },
  
  setInvalid: function() {
  },
  
  serializeData: function() {
    this.mDataEl.value = DeForm.Grid.json_encode(this.mGridData);
  },
  
  getValue: function() {
    return this.mDataEl.value;
  },
  
  setValue: function(pVal) {
    this.deleteAllRows(true);
    this.mDataEl.value = pVal;
    this.unserializeData();
  },
  
  unserializeData: function() {
    var value = this.mDataEl.value;

    if (value != '') {
      var data = DeForm.Grid.json_decode(value)
      
      
      // Detect first column
      var first_col_name = '';
      
      for(name in data) {
        first_col_name = name;
        break;
      }
      
      if (first_col_name == '') {
        return;
      }
      
      for(i in data[first_col_name].data) {
        
        if (parseInt(i) != i) {
          continue;
        }
        
        var add_row_data = {};
      
        for(col_name in data) {
          if (typeof(data[col_name].data[i]) == 'undefined') {
            add_row_data[col_name] = '';
          }
          else {
            add_row_data[col_name] = data[col_name].data[i];
          }
        }
        this.addRow(add_row_data);
      }
    }
  
    // Create blank header column (where remove button is kept) - only when icon was given and such element was not created
    if (this.mIconImage != '' && !this.mIsIconCreated) {
      this.mTableHeaderEl.adopt(new Element('th'));
      this.mIsIconCreated = true;
    }
  }
  
});

// Metody wyciagniete ze starych Mootools - przekopiowane tutaj ze wzgledu na to, ze w nowych Mootoolsach nie sa kompatybilne z tymi ktore tu sa (toString i evaluate)
DeForm.Grid.specialChars = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};

DeForm.Grid.json_encode = function(obj){
  switch($type(obj)){
    case false:
      return 'null';
    case 'number':
      return isFinite(obj) ? String(obj) : "null";
    case 'boolean':
      return String(obj);
    case 'string':
      return '"' + obj.replace(/([\x00-\x1f\\"])/g, function(a, b){
        var c = DeForm.Grid.specialChars[b];
        if(c) return c;
        c = b.charCodeAt();
        return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
      }) + '"';
    case 'arguments':
    case 'array':
      return '[' + $A(obj).map(DeForm.Grid.json_encode).remove(false).join(',') + ']';
    case 'object':
      var string = [];
      for(var prop in obj){
        var val = DeForm.Grid.json_encode(obj[prop]);
        if(val) string.push(DeForm.Grid.json_encode(prop) + ':' + val);
      }
      return '{' + string.join(',') + '}';
  }
  return false;
};  

DeForm.Grid.json_decode = function(str, secure){
  return (($type(str) != 'string') || (secure && !str.test(/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/))) ? false : eval('(' + str + ')');
}    

DeForm.TextArea = new Class({
  
  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass, pToolTipBindEl) {
    this.parent(pName, pEl, pBaseClass, pToolTipBindEl);
    
    this.mType = 'TextArea';
    this.mElCounter = $(this.mEl.getProperty('id') + 'counter');
    this.mMaxLength = 0;
    this.mCounterAllowPass = false;
    this.mCounterVisible = false;
    this.resetChanged();
    
    if ('visible' == this.mElCounter.getStyle('visibility')) {
      this.mCounterVisible = true;
    }
    else {
      this.mCounterVisible = false;
    }
  },
  
  setMaxLength: function(pVal) {
    this.mMaxLength = pVal;
    this.checkMaxLength();
  },
  
  setCounterPass: function(pVal) {
    if (pVal != '') {
      this.mCounterAllowPass = true;
    }
    else {
      this.mCounterAllowPass = false;
    }
    this.eMaxLength();
  },
  
  setCounterVisible: function(pVal) {
    if (pVal != '') {
      this.mCounterVisible = true;
      this.mElCounter.setStyle('visibility', 'visible');
    }
    else {
      this.mCounterVisible = false;
      this.mElCounter.setStyle('visibility', 'hidden');
    }
  },
  
  isCounterVisible: function() {
    return this.mCounterVisible;
  },
  
  checkMaxLength: function() {

    if (this.mMaxLength > 0) {
      this.mEl.addEvent('click', this.eMaxLength.bind(this));
      this.mEl.addEvent('keypress', this.eMaxLength.bind(this));
      this.mEl.addEvent('keyup', this.eMaxLength.bind(this));
      this.mEl.addEvent('keydown', this.eMaxLength.bind(this));
      this.eMaxLength();
    }
    else {
      this.mEl.removeEvents('click');
      this.mEl.removeEvents('keypress');
      this.mEl.removeEvents('keyup');
      this.mEl.removeEvents('keydown');
    }
    
  },
  
  setValue: function(pValue) {
    this.parent(pValue);
//    this.eMaxLength();
  },
  
  eMaxLength: function() {
    if (this.getValue().length >= this.mMaxLength) {
      
      if (false === this.mCounterAllowPass) {
        this.setValue(this.getValue().substr(0, this.mMaxLength));
      }
      counter_html = '<span style="color:#F00">' + parseInt(this.mMaxLength - this.getValue().length) + '</span>';
    }
    else {
      counter_html = this.mMaxLength - this.getValue().length;
    }
    
    this.mElCounter.set('html', counter_html);
  },
  
  setElementsInitialValue: function() {
    this.parent();
    this.checkMaxLength();
  },
  
  resizeTo: function(pWidth, pHeight, pUseFX) {
    this.parent(pWidth, pHeight, pUseFX);
    this.mElCounter.setStyle('width', pWidth);
  },
  
  setInvalid: function(pState, pErrorText) {
    this.parent(pState, pErrorText);
    if (pState) {
      this.mToolTip.addEvent('keydown', this.mToolTip.hide.bindWithEvent(this.mToolTip));
    }
  },
  
  setReadOnly: function(pState) {
    
    if (!$defined(pState)) {
      return;
    }
    
    if (pState) {
      this.mEl.setProperty('readonly', 'readonly');
    }
    else {
      this.mEl.removeProperty('readonly');
    }
    this.mReadOnly = pState;
    this.changeClass();
  },
  
  hide: function() {
    this.parent();
    
    if (this.mCounterVisible) {
      this.mElCounter.setStyle('display', 'none');
    }
  },
  
  show: function() {
    this.parent();
    
    if (this.mCounterVisible) {
      this.mElCounter.setStyle('display', 'block');
    }
  },
  
  isChanged: function() {
    if (this.mStartValue == this.getValue()) {
      return false;
    }
    return true;
  },
  
  resetChanged: function() {
    this.mStartValue = this.getValue();
  },
  
  addLabel: function(){
    new OverText(this.mEl);
  }
  
});

DeForm.TextBox = new Class({

  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass, pToolTipBindEl) {
    this.parent(pName, pEl, pBaseClass, pToolTipBindEl);
    this.mType = 'TextBox';
    this.resetChanged();
    
    this.mIsPassword = this.mEl.getProperty('type') == 'password';
    
    // Events available to coder
    this.mEl.addEvent('mouseover', this.fireEvent.bind(this, 'mouseover'));
    this.mEl.addEvent('mouseout', this.fireEvent.bind(this, 'mouseout'));
    this.mEl.addEvent('mousedown', this.fireEvent.bind(this, 'mousedown'));
    this.mEl.addEvent('mouseup', this.fireEvent.bind(this, 'mouseup'));
    this.mEl.addEvent('keyup', this.fireEvent.bind(this, 'keyup'));
    this.mEl.addEvent('keydown', this.fireEvent.bind(this, 'keydown'));
    this.mEl.addEvent('click', this.fireEvent.bind(this, 'click'));
    this.mEl.addEvent('mouseenter', this.fireEvent.bind(this, 'mouseenter'));
    this.mEl.addEvent('mouseleave', this.fireEvent.bind(this, 'mouseleave'));
    this.mEl.addEvent('focus', this.fireEvent.bind(this, 'focus'));
    this.mEl.addEvent('blur', this.fireEvent.bind(this, 'blur'));
  },
      
  resizeBy: function(pWidth, pFake, pUseFX) {
    this.parent(pWidth, null, pUseFX);
  },
  
  resizeTo: function(pWidth, pFake, pUseFX) {
    this.parent(pWidth, null, pUseFX);
  },

  setInvalid: function(pState, pErrorText) {
    this.parent(pState, pErrorText);
    if (pState) {
      this.mToolTip.addEvent('keydown', this.mToolTip.hide.bindWithEvent(this.mToolTip));
    }
  },
  
  setReadOnly: function(pState) {
    
    if (!$defined(pState)) {
      return;
    }
    
    if (pState) {
      this.mEl.setProperty('readonly', 'readonly');
    }
    else {
      this.mEl.removeProperty('readonly');
    }
    this.mReadOnly = pState;
    this.changeClass();
  },
  
  isChanged: function() {
    if (this.mStartValue == this.getValue()) {
      return false;
    }
    return true;
  },
  
  resetChanged: function() {
    this.mStartValue = this.getValue();
  },
  
  addLabel: function(){
    new OverText(this.mEl);
  },
  
  /** 
   * Dodaje ikonke z okiem
   */
  processPassword: function() {

    // Glupie IE nie lubia jak sie im ustawia property type wiec niestety w IE to nie zadziala na razie...
    if (Browser.Engine.trident) {
      return;
    }
    
    var el = this.mEyeEl = new Element('div', {
      'styles': {
        'position': 'absolute',
        'left': this.mEl.getStyle('left').toInt() + this.mEl.getStyle('width').toInt() - 17 ,
        'top': this.mEl.getStyle('top').toInt() + Math.floor((this.mEl.getSize().y - 16) / 2),
        'width': 16,
        'height': 16
      }
    }).adopt(new Element('img', {
      'styles': {
        'width': 16,
        'height': 16
      }
    }));
    
    // Tooltip 
    var tooltip = this.mEyeTooltip = new DeForm.ToolTip(el);
    tooltip.addEvent('mouseover', tooltip.show.bindWithEvent(tooltip));
    tooltip.addEvent('mouseout', tooltip.hide.bindWithEvent(tooltip));
    tooltip.addEvent('mousemove', tooltip.locate.bindWithEvent(tooltip));
    
    // Dodaje do DOMa
    this.mEl.getParent().adopt(el);
  },
  
  showPassword: function() {
    if (Browser.Engine.trident) {
      return;
    }    
    this.mEyeEl.removeEvents('dblclick');
    this.mEyeEl.addEvent('click', this.hidePassword.bind(this));
    this.mEyeTooltip.setText(this.mEyeHintText.enabled);
    this.mEyeEl.getElement('img').setProperty('src', '/mp/DeForm/DF_eye_enabled.png');

    this.mEl.setProperty('type', 'text');
  },
  
  hidePassword: function() {
    if (Browser.Engine.trident) {
      return;
    }    
    this.mEyeEl.removeEvents('click');
    this.mEyeEl.addEvent('dblclick', this.showPassword.bind(this));
    this.mEyeEl.getElement('img').setProperty('src', '/mp/DeForm/DF_eye_disabled.png');
    
    this.mEyeTooltip.setText(this.mEyeHintText.disabled);
    
    this.mEl.setProperty('type', 'password');
  },
  
  setEyeHintTexts: function(pTexts) {
    this.mEyeHintText = pTexts;  
  }
  
});
DeForm.DateBox = new Class({

  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass, pToolTipBindEl) {
    this.parent(pName, pEl, pBaseClass, pToolTipBindEl);
    this.mType = 'DateBox';
  },
    
  setInvalid: function(pState, pErrorText) {
    this.parent(pState, pErrorText);
    
    if (!pState && this.mDefaultToolTipText) {
      this.mToolTip.addEvent('keydown', this.mToolTip.hide.bind(this.mToolTip));
    }
  }
  
});
DeForm.SwfUpload = new Class({
  
  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass, pLibJSPath, pFlashPath) {
    
    this.mObjName = pName;
    this.parent(pName, pEl, pBaseClass);
    this.mType = 'SwfUpload';
    
    this.mLibJSPath = pLibJSPath;
    this.mFlashPath = pFlashPath;
    this.mProgressContainer = $(this.mObjName + '_progress_containrer');
    this.mProgressBar =  $(this.mObjName + '_progress_bar');
    this.mButtons = $(this.mObjName + '_button');
    
    this.mResizeProgresContainerFX = this.mProgressContainer.effects({duration: this.mResizeFXDuration});
    this.mResizeProgresBarFX = this.mProgressBar.effects({duration: this.mResizeFXDuration});
    this.mButtonsFX = this.mButtons.effects({duration: this.mResizeFXDuration});
     
    DeForm.LoadScript(this.mLibJSPath, this.initializeSwfUpload.bind(this));
  },
   
  initializeSwfUpload: function(){
    
    this.mSwfu = new SWFUpload({
  		upload_script : '',
  		target : this.mObjName+'_button',
  		flash_path : this.mFlashPath,
  		allowed_filesize : 4000,	
  		allowed_filetypes : "*.*",
  		allowed_filetypes_description : "All files",
  		browse_link_innerhtml : "Browse",
  		upload_link_innerhtml : "Upload",
  		browse_link_class : "DF_swf_browse",
  		upload_link_class : "DF_swf_upload",
  		flash_loaded_callback : this.mObjName+'.mSwfu.flashLoaded',
  		upload_file_queued_callback : this.mObjName+'.fileQueued',
  		upload_file_start_callback : this.mObjName+'.uploadFileStart',
  		upload_progress_callback : this.mObjName+'.uploadProgress',
  		upload_file_complete_callback : this.mObjName+'.uploadFileComplete',
  		upload_file_cancel_callback : this.mObjName+'.uploadFileCancelled',
  		upload_file_error_callback : this.mObjName+'.errorsFunction',
  		upload_error_callback : this.mObjName+'.uploadError',
  		upload_queue_complete_callback : this.mObjName+'.uploadQueueComplete',
  		upload_cancel_callback : this.mObjName+'.uploadCancel',
  		auto_upload : false			
    });
    
    this.last_id = 0;
    /*
    if ($(this.mSwfu.movieName) != null && !$(this.mSwfu.movieName+"BrowseBtn") && Browser.Engine.trident) {
      if($(this.mSwfu.movieName).PercentLoaded() == 100){ 
        this.mSwfu.loadUI();
      }
    }
    */
   
   $('SWFUpload_0CancelBtn').addEvent('click', this.cancelQueue.bind(this));
  },
  
  /**
  *   Wywolywana gdy plik jest dodawany do kolejki. Nie obslugujemy kolejek dlatego przed kazdym dodaniu do
  *   kolejki, kolejka jest czyszczona, i zawsze ma dlugosc jeden.
  *
  *   pFile - obiekt pliku
  *   pQueueLength - dlugosc kolejki
  */
  fileQueued: function(pFile, pQueueLength) {
  	this.mEl.value = pFile.name;
    $(this.mProgressBar).style.width = '0px';
    if(this.last_id != 0){
  	  this.mSwfu.cancelFile(this.last_id);
  	}
  	this.last_id = pFile.id;
  },

  /**
  *   Wywolywana gdy uploadowanie pliku jest przerwane.
  *
  */
  uploadFileCancelled: function(pFile, pQueueLength) {
  	
  },

  /**
  *  Wywolywana gdy upload pliku sie rozpocznie. 
  *  
  *  pFile - obiekt pliku 
  *  pPosition - ktory z koleji w kolejce jest obecnie uploadowany
  *  pQueueLength - dlugosc kolejki 
  */
  uploadFileStart: function(pFile, pPosition, pQueueLength) {
    $(this.mProgressBar).style.visibility = 'visible';
    
    $('SWFUpload_0CancelBtn').setStyle('top', '0px');
    var left = $('SWFUpload_0UploadBtn').offsetLeft;
    left += $('swfuploader_test_button').getStyle('left').toInt();  
    $('SWFUpload_0CancelBtn').setStyle('left', left+'px'); 
    $('SWFUpload_0CancelBtn').setStyle('visibility', 'visible'); 
  },

  /**
  *   Wywolywana regularnie podczas uploadowania pliku. Ustala dlugosc progres bara.
  *   pFile - obiekt pliku
  *   pBytesLoaded - liczba juz uploadnietych bajtow
  */  
  uploadProgress: function(pFile, pBytesLoaded){
  	var max_size = parseInt($(this.mProgressContainer).style.width);
  	var percent = (pBytesLoaded / pFile.size);
  	$(this.mProgressBar).style.width = Math.ceil(max_size*percent)+'px';
  },

  /**
  *
  */
  uploadError: function(errno) {
  	SWFUpload.debug(errno);
  },

  /**
  *   Wywolywana po zakonczeniu uploadowania pliku. Ukrywa klawisz cancel.
  */
  uploadFileComplete: function(pFile) {
    $('SWFUpload_0CancelBtn').setStyle('visibility', 'hidden'); 
  },

  /**
  *   Wywolywana po nacisnieciu buttona cancel. Usuwa wszystko z kolejki i usuwa klawisz cancel.
  */
  cancelQueue: function() {
    this.mSwfu.cancelQueue();
    $('SWFUpload_0CancelBtn').setStyle('visibility', 'hidden'); 
  },
  
  /**
  *   Wywolywana po zakonczeniu uploadowania. Czysci kolejke.
  *   pFile - obiekt pliku  
  */
  uploadQueueComplete: function(pFile) {
    this.mSwfu.cancelQueue();
  },
  
  /**
  *   Wywolywana podczas wystapienia bledu. Narazie obsluga tylko bledu zaduzego pliku.
  *   pErrorCode - cod bledu
  *   pFile - obiekt pliku
  *   pMsq - message ?
  */
  errorsFunction: function(pErrorCode, pFile, pMsg){
    // plik jest za duzy
    if(-50 == pErrorCode){
  	  alert(pFile.name + ' - File is too big');
    }
  },
  
  /**
  *   Sluzy do ustawiania parametrow.
  *   pName - nazwa parametru
  *   pValue - wartosc parametru
  */
  addSeting: function(pName, pValue){
    
    if (this.mSwfu) {
      this.mSwfu.addSetting(pName, pValue);
      this.mSwfu.loadFlash();
    }
    else {
      this.addSeting.delay(200, this, [pName, pValue]);
    }
  },
  
  setFileType: function(pFileType, pDescription){
    
    if(pFileType){
      this.addSeting('allowed_filetypes', pFileType);
    }
    if(pDescription){
      this.addSeting('allowed_filetypes_description', pDescription);
    }
    
  },
  
  setMaxFileSize: function(pMaxFileSize){
    this.addSeting('allowed_filesize', pMaxFileSize);
  },
  
  setUploadScript: function(pUploadScript){
    this.addSeting('upload_script', pUploadScript);
  },
  
  setCallback: function(pCallbackName, pFunction){
    this.addSeting(pCallbackName, pFunction);
  },
  
  resizeBy: function(pWidth, pFake, pUseFX) {
    this.parent(pWidth, null, pUseFX);
  },
  
  resizeTo: function(pWidth, pHeight, pUseFX) {
    
    data = {'width': this.getDimensions().width};
    button_data = {'left': this.mButtons.getPosition().x};
    
    if ($type(pWidth) == 'number') {
      data.width = pWidth;
      button_data.left = pWidth + 10;
    }
    
    if ($type(pHeight) == 'number') {
      data.height = pHeight;
    }
   
    this.mResizeFX.start(data);
    this.mResizeProgresContainerFX.start(data);
    this.mResizeProgresBarFX.start(data);   
    this.mButtonsFX.start(button_data);    
  }
  
  
});
DeForm.FancyUploader = new Class({
  
  Extends: DeForm.GenericElement,
  
  mContainer: false,
  
  mOffset: 50,
  
  initialize: function(pName, pEl, pBaseClass, pFancyUploadJs, pSwiffBaseJs, pSwiffUploaderJs, pSwiffUploaderSwf, pTypes, pTypesDesc, pSrc, pMaxFileSize, pLimitFiles, pMultiple, pInstantStart) {
    this.parent(pName, pEl, '');
    if(pTypes && pTypesDesc){
      types = {};
      types[pTypesDesc] = pTypes;
    }
    
    this.mContainer = new Element('div', {
      styles: {
        'height': 1,
        'position': 'absolute',
        'top': this.getContainerTop(),
        'left': this.getContainerLeft()
      }
    }).injectBefore(document.getElement('div'));
    window.addEvent('scroll', this.moveContainer.bind(this));
    this.moveContainer();
    
    if(pMaxFileSize > 0){
      pMaxFileSize = pMaxFileSize*1024;
    }
    DeForm.LoadScript(pSwiffBaseJs, function(){
      DeForm.LoadScript(pSwiffUploaderJs, function(){
        DeForm.LoadScript(pFancyUploadJs, this.newFancy.bind(this, [pEl, types, pSrc, pSwiffUploaderSwf, pMaxFileSize, pLimitFiles, pMultiple, pInstantStart]));
      }.bind(this));
    }.bind(this));
        
  },
  
  newFancy: function(pEl, pTypes, pSrc, pSwiffUploaderSwf, pMaxFileSize, pLimitFiles, pMultiple, pInstantStart){

//    console.log('Limit: '+pLimitFiles+', Multiple: '+pMultiple+', Instant: '+pInstantStart);
    
    this.mFancyObj = new FancyUpload(pEl, {
      swf: pSwiffUploaderSwf,
      queued: true,
      queueList: pEl+'_upload_queue',
      limitFiles: pLimitFiles.toInt(),
      multiple: pMultiple,
      types: pTypes,
      url: pSrc,
      limitSize: pMaxFileSize,
      container: this.mContainer,
      instantStart: pInstantStart,
      createReplacement: function(){},
      onComplete: this.fireEvent.bind(this, 'onComplete'),
      onSelect: function(name, size){this.fireEvent('onSelect', [name, size])}.bind(this)
    });
    
    this.browse = this.mFancyObj.uploader.browse.bind(this.mFancyObj.uploader);
    
    this.addEvent('onSelect', function(pName, pSize){
      this.mFormRef.hideErrorBox();
  		if (pMaxFileSize && (pSize > pMaxFileSize)){
        this.mFormRef.showErrorBox(this.mErrorFileSize);
  		}
    });

    if($(pEl+'_browse_button')){
      $(pEl+'_browse_button').addEvent('click', this.browse.bind(this));
    }
        
  },
    
  setErrorFileSize: function(pError){
    this.mErrorFileSize = pError;
  },
  
  upload: function(){
    this.mFancyObj.uploader.send(this.mFancyObj.options.url);
  },
  
  setAction: function(pSrc){
    var options = {
      url: pSrc        
    }
    if(!this.mFancyObj){
      this.setAction.delay(300, this, pSrc);
      return false;
    }
    this.mFancyObj.setOptions(options);
    return true;
  },
  
  setValue: function(){
  },

  getValue: function(){
    return new String();
  },
  
  getContainerLeft: function(){
    return window.getScrollLeft()+this.mOffset;
  },

  getContainerTop: function(){
    return window.getScrollTop()+this.mOffset;
  },

  moveContainer: function(){
    this.mContainer.setStyles({
      'top': this.getContainerTop()+'px',
      'left': this.getContainerLeft()+'px'
    });
  }

  
});
DeForm.FileBox = new Class({
  
  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass, pToolTipBindEl) {
    this.parent(pName, pEl, pBaseClass, pToolTipBindEl);
    this.mType = 'FileBox';
  }
  
  
});
DeForm.JSCalendar = new Class({
  
  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pInputObj, pBaseClass, pCSS, pLib, pLang, pDateFormat) {
    this.parent(pName, pEl, pBaseClass);
    
    this.mType = 'JSCalendar';
    this.mCal = null;
    this.mFirstDayOfWeek = 0;
    this.mDateFormat = pDateFormat;
    eval('this.mInputObj = ' + pInputObj);
    
    DeForm.LoadCSS(pCSS);
    DeForm.LoadScript(pLib);
    DeForm.LoadScript(pLang, this.initializeCalendar.bind(this));
    
    this.registerIconEvent(this.mEl);
  },
  
  registerButtonEvent: function(pEl) {
    pEl.addEvent('click', this.showCalendarAtElement.bind(this).pass(pEl));
  },
  
  registerIconEvent: function(pEl) {
    pEl.addEvent('click', this.showCalendarAtElement.bind(this).pass(pEl));
  },
  
  calendarCallback: function(pCal, pDate) {
    this.mInputObj.setValue(pDate.toString());
    this.mInputObj.fireEvent('onSelectDate', pDate.toString());

    if (this.mCal.dateClicked) {
      this.mCal.callCloseHandler();
    }
  },
  
  hide: function() {
    this.mCal.hide();    
  },

  show: function() {
    this.mCal.show();
  },

  initializeCalendar: function() {
    this.mCal = new Calendar(this.mFirstDayOfWeek, Date.parseDate(this.mInputObj.getValue(), this.mDateFormat), this.calendarCallback.bind(this), this.hide.bind(this));
    this.mCal.weekNumbers = '';
    this.mCal.time24 = 1;
    this.mCal.setDateFormat(this.mDateFormat);
    this.mCal.create();
  },
  
  showCalendarAtElement: function(pEl) {
    if (this.mCal) {
      this.mCal.showAtElement(pEl,'Br'); // $this->DeForm->Defaultval($pDeform['calendar_align'], 'Br')
      
      with (this.mCal.element.style) {
        left = pEl.getPosition().x + 'px';
        top = pEl.getPosition().y + 17 + 'px';
        display = 'block';
      }
    }
    else {
      this.showCalendarAtElement.delay(300, this, pEl);
    }
    
  },
  
  resizeTo: function() {
    this.hide();
  },
  
  moveTo: function(pX, pY, pUseFX) {
    this.parent(pX, pY, pUseFX);
    this.hide();
  }
  
});
DeForm.Label = new Class({
  
  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass) {
    this.parent(pName, pEl, pBaseClass);
    this.mType = 'Label';
    this.mMoveEl = this.mEl;
    this.mResizeEl = this.mEl;
    this.mAsterix = false;
  },
  
  handleAjaxRequest: function(pData) {
    this.parent(pData);
    this.setText(pData.text);
  },
  
  setInvalid: function() {
  },
  
  setReadOnly: function(){
  },
  
  setAsterix: function(pVal){
    var star = this.mEl.getElement('sup');
    
    if(true == pVal){
      star.setStyle('visibility', 'visible'); 
      this.mAsterix = true;
    }
    else{
      star.setStyle('visibility', 'hidden');
      this.mAsterix = false;
    }
    
  },
  
  getText: function(){
    return this.mEl.getElement('span').get('text');
  },
  
  setText: function(pVal){
    this.mEl.getElement('span').set('html', pVal);
  },
  
  getValue: function(){
  },
  
  setValue: function(pVal){
  }
  
});
DeForm.Groupbox = new Class({
  
  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass, pToolTipBindEl) {
    this.parent(pName, pEl, pBaseClass, pToolTipBindEl);
		this.mType = 'GroupBox';
	}

});

DeForm.Fieldset = new Class({
  
  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass, pToolTipBindEl) {
    this.parent(pName, pEl, pBaseClass, pToolTipBindEl);
		this.mType = 'Fieldset';
	}

});

DeForm.ToolTip = new Class({
  
  initialize: function(pToolTipEl) {
    this.mToolTipEl = pToolTipEl;
    this.mToolTipEl.addEvent('trash', this.eRemove.bind(this));
    this.mClasses = {
      'error': 'DF_ToolTip_error',
      'standard': 'DF_ToolTip_standard'
    };
    this.mOffsets = {
      'error': {
        'x': 11,
        'y': -10
      },
      'standard': {
        'x': 10,
        'y': 12
      }
    };
    this.mText = '';
    this.mRegisteredEvents = [];
    this.mType = 'standard';
    this.mPreviousTitle = '';
    this.create();
  },

  registerElementEvents: function() {
    this.addEvent('mouseenter', this.show.bindWithEvent(this));
    this.addEvent('mouseleave', this.hide.bindWithEvent(this));
    this.addEvent('mousemove', this.locate.bindWithEvent(this));
  },
  
  unregisterElementEvents: function() {
    for(i = 0; i < this.mRegisteredEvents.length; i++) {
      var e = this.mRegisteredEvents[i];
      this.removeEvent(e.event, e.ref);
      delete this.mRegisteredEvents[e];
    }
  },
  
  addEvent: function(pEvent, pRef) {
    this.mRegisteredEvents.push({'event': pEvent, 'ref': pRef});
    this.mToolTipEl.addEvent(pEvent, pRef);
  },
  
  removeEvent: function(pEvent, pRef) {
    this.mToolTipEl.removeEvent(pEvent, pRef);
  },
  
  show: function(pEvent) {
    $clear(this.timer);

    $('DeForm_ToolTipEl').setProperty('class', this.mClasses[this.mType]);
    $('DeForm_ToolTipEl').tween('opacity', 1);
    
    this.locate(pEvent);
    this.setText(this.mText);
    this.timer = this.hide.delay(8000, this);
    
    if (this.mToolTipEl.getProperty('title')) {
      this.mPreviousTitle = this.mToolTipEl.getProperty('title');
      this.mToolTipEl.removeProperty('title');
    }
  },
  
  hide: function() {
    this.mToolTipEl.setProperty('title', this.mPreviousTitle);
    $('DeForm_ToolTipEl').tween('opacity', 0);
    $clear(this.timer);
  },
  
  setText: function(pText) {
    pText = pText.replace(/^\n+/g, '').replace(/\n/g, '<br/>');
    
    if ($('DeForm_ToolTipEl')) {
      $('DeForm_ToolTipEl').getFirst().set('html', pText);
    }
    this.mText = pText;
  },
  
  setType: function(pType) {
    this.mType = pType;
    if ($('DeForm_ToolTipEl')) {
      $('DeForm_ToolTipEl').className = this.mClasses[pType];
    }
  },
  
  create: function() {
    
    if (!$('DeForm_ToolTipEl')) {
      new Element('div', {'id': 'DeForm_ToolTipEl'}).inject($(document.body));
      $('DeForm_ToolTipEl').adopt(new Element('div', {'class': 'DF_ToolTip_inner'}));
      
      $('DeForm_ToolTipEl').set('tween', {duration: 130});
      
      $('DeForm_ToolTipEl').setStyles({
        'opacity': 0,
        'position': 'absolute',
        'left': 0,
        'top': 0
      });
    }
  },
  
	locate: function(event){
	  
		var win = {'x': window.getWidth(), 'y': window.getHeight()};
		var scroll = {'x': window.getScrollLeft(), 'y': window.getScrollTop()};
		var tip = {'x': $('DeForm_ToolTipEl').offsetWidth, 'y': $('DeForm_ToolTipEl').offsetHeight};
		var prop = {'x': 'left', 'y': 'top'};

		for (var z in prop) {
			var pos = event.page[z] + this.mOffsets[this.mType][z];
			
			if (pos + tip[z] - scroll[z] > win[z]) {
			  pos = event.page[z] - this.mOffsets[this.mType][z] - tip[z];
			}
			
			$('DeForm_ToolTipEl').setStyle(prop[z], pos);
		};
	},
	
  eRemove: function() {
    this.mToolTipEl = null;
  }
  
});

DeForm.SelectList = new Class({

  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass, pToolTipBindEl) {
    this.parent(pName, pEl, pBaseClass, pToolTipBindEl);
    this.mType = 'SelectList';
  },
      
  resizeBy: function(pWidth, pFake, pUseFX) {
    this.parent(pWidth, null, pUseFX);
  },
  
  resizeTo: function(pWidth, pFake, pUseFX) {
    this.parent(pWidth, null, pUseFX);
  },

  setReadOnly: function(pState) {
    
    if (!$defined(pState)) {
      return;
    }
    
    if (pState) {
      this.mEl.setProperty('disabled', 'disabled');
    }
    else {
      this.mEl.removeProperty('disabled');
    }
    this.mReadOnly = pState;
    this.changeClass();
  }

});
DeForm.Radio = new Class({

  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass, pToolTipBindEl) {
    
    this.mMoveEl = $(pEl);
    this.parent(pName, pEl, pBaseClass, pToolTipBindEl);
    
    this.mType = 'Radio';    
    this.mRadios = {};
    this.mStartValue = null;
    this.mPreviousValue = null;
  },
  
  registerRadio: function(pEl) {
    this.mRadios[$(pEl).value] = $(pEl);
    
    // Hack na IE6 robiacy ladne png z alpha
    if (Browser.Engine.trident) {
      this.changeClass();
      $(pEl).getParent().addEvent('click', this.setValue.bind(this, [$(pEl).value, true]));
    }
    
    if ($(pEl).getNext && $(pEl).getNext()) {
      var label = $(pEl).getNext();
      label.addEvent('click', this.setValue.bind(this, [$(pEl).value, true]));
      // Prevent selecting label by user
      label.onselectstart = function() { return false; };
      label.addEvent('mousedown', function(pEvent) { pEvent.preventDefault(); }.bindWithEvent(this));
    }
    
    $(pEl).addEvent('click', this.setValue.bind(this, [$(pEl).value, true]));
    $(pEl).addEvent('keyup', this.setValue.bind(this, [$(pEl).value, true]));
    this.mPreviousValue = this.getValue();
    this.resetChanged();
  },
  
  setValue: function(pVal, pCheckDisabled) {
    if (this.mRadios[pVal] && (false == this.mRadios[pVal].getProperty('disabled') || !pCheckDisabled)) {
      this.mRadios[pVal].setProperty('checked', true);
      
      this.fireEvent('onClick');
      
      if (this.mPreviousValue != pVal) {
        this.fireEvent('onChange');
      }
      
      this.mPreviousValue = pVal;
      this.changeClass();
      return true;
    }
    
    return false;
  },
  
  getValue: function() {
    for (value in this.mRadios) {
      
      radio = this.mRadios[value];
      
      if (radio.getProperty('checked')) {
        return radio.get('value');
      }
    }
    return new String();
  },
  
  /**
   * Sets radio which has value pRadioVal to readonly (disabled = true) or write (disabled = false)
   */
  setReadOnly: function(pState, pRadioVal) {
    if (this.mRadios[pRadioVal]) {
      this.mRadios[pRadioVal].disabled = pState;
      return true;
    }
    
    return false;
  },
  
  resizeTo: function() {
  },
  
  isChanged: function() {
    if (this.mStartValue == this.getValue()) {
      return false;
    }
    return true;
  },
  
  resetChanged: function() {
    this.mStartValue = this.getValue();
  },
  
  /** 
   * Checks default radio
   */
  clear: function() {
    for (value in this.mRadios) {
      this.mRadios[value].checked = 0;
    }
    this.changeClass();
  },
  
  changeClass: function() {
   
    for (value in this.mRadios) {
 
      radio = this.mRadios[value];
      
      class_names = [this.mBaseClass];
      
      if (Browser.Engine.trident4) {
        class_names.push('ie');
      }
      
      if (true == radio.getProperty('checked')) {
        class_names.push('on');
      }
      else {
        class_names.push('off');
      }
      
      if (true == this.mInvalid) {
        class_names.push('error');
      }      
    
      if (true == radio.getProperty('disabled')) {
        class_names.push('disabled');
      }
      
      radio.getParent().setProperty('class', class_names.join('_'));
    }
  },
  
  /**
   * Sets if element is valid or invalid
   * 
   * @param pState boolean true - element is valid, false - element is invalid
   * @param pErrorText string element's error label
   */
  setInvalid: function(pState, pErrorText) {
    this.mInvalid = pState;
    this.mToolTip.unregisterElementEvents();
    
    if (pState) {
      this.mToolTip.setText(pErrorText);
      this.mToolTip.setType('error');
      this.mToolTip.registerElementEvents();
    }
    else if (this.mDefaultToolTipText) {
      this.mToolTip.registerElementEvents();
      this.mToolTip.setType('standard');
      this.mToolTip.setText(this.mDefaultToolTipText);
    }    
    
    this.changeClass();
  }  
  
});
DeForm.Captcha = new Class({
  
  Extends: DeForm.GenericElement,
  
  initialize: function(pName, pEl, pBaseClass, pToolTipBindEl) {
    this.parent(pName, pEl, pBaseClass, pToolTipBindEl);
		this.mType = 'Captcha';
	},
	
	setImgSrc: function(pImgSrc) {
	  this.mImgSrc = pImgSrc;
	},
	
	reload: function() {
	  this.mEl.setProperty('src', this.mImgSrc + '&' + (new Date).getTime());
	}

});


/**
 * Klasa służąca do obsługi requestów AJAXowych
 * 
 * Wykorzystuje mootoolsową klasę Json.Remonte
 * 
 * Przykład uzycia:
 * 
 * 
 *     DejaxRequest.newRequest({
 *       url: '/ajax/dmsDPPage/SetActivePage/',
 *       eval: true,
 *       mode: 'cancel',
 *       data: {},
 *       onComplete: this.switchActivePage.bind(this)
 *     });
 * 
 * @copyright This file is DeSmart module. Copyright DeSmart.com. All rights reserved. www.desmart.com desmart@desmart.com
 */
Dejax = new Class({

  Implements: [Events],
  
  /**
  * Po tym czasie request zostanie uznany za "zaginiony" i zraportowany do debugera
  */
  mTTL: 30000,
  
  /**
   * Request handler - uchwyt aktywnego połączenia, które należy wykorzystać, chyba że zapytanie należy wykonać w trybie free (mode: free)
   * 
` */
  mRh: null,
  
  /**
   * Flaga informująca czy istnieje aktywne połączenie
   * 
` */
  mIsActiveConnection: false,
  
  /**
   * stos requestów do wykonania
   * 
` */
  mQueue: new Array,
  
  /**
   * mode - tryb zapytania, możliwe wartości:
   *    1) queue - zapytanie dodaje się do stosu i jest wykonywane gdy poprzednie zostanie zakończone
   *    2) cancel - zapytanie anuluje istniejące połączenie i wywołuje nowe
   *    4) free - poza kontrolą trybów - tworzy nowy obiekt Json.Request
   
   * eval - decyduje o tym czy zwrócaona treść ma być evalowana
   * onComplete - event uruchamiany po zakończeniu zapytania.
   * completePass - Agumenty które zostaną przekazane do onComplete.
   * data - dane do przesłania
   * prevent - uniemożliwia wykonanie jakiegokolwiek zapytania do czasu zakończenia istniejącego
   * 
   */
	options: {
    mode: 'free',
    completePass: new Array(),
    data: null,
    prevent: false,
    url: null,
    eval: true,
    evalData: null,
    debugRequest: false
	},
	
	initialize: function(){
	  this.resetOptions();
	},

  /**
   * Tworzy nowy obiekt Json.Remonte i przypisuje go do requestHandlera.
   * 
   */
	createRequestHandler: function(){
    this.mRh = new Request.JSON({
      onComplete: this.onCompleteCall.bind(this),
      evalScripts: false,
      url: this.options.url
    });
	},
	
	resetOptions: function(){
	  this.options = {
      mode: 'free',
      eval: true,
      completePass: new Array(),
      data: null,
      prevent: false,
      url: null,
      evalData: null,
      debugRequest: false
  	}
	  this.removeEvents('onComplete');
	},
	
	setOptions: function(pOptions){
    if(pOptions.onComplete){
      this.addEvent('onComplete', pOptions.onComplete);
    }
    for(i in this.options){
      this.options[i] = pOptions[i]
    }
	},
	
	newRequest: function(pOptions){
	  if(pOptions.delay){
	    this.newRequestHelper.delay(pOptions.delay, this, [ pOptions ]);
	  }
	  else{
	    this.newRequestHelper(pOptions);
	  }
	},
	
  /**
   * inicjuje nowe zapytanie, w zależności od trybu
   * 
   */
	newRequestHelper: function(pOptions){

    switch(pOptions.mode){
      default:
      case 'free':
        var tmp = new Dejax();
        if(this.$events.end && this.$events.end[0]){
          tmp.addEvent('onEnd', this.$events.end[0]);
        }
        tmp.send(pOptions);
        return true;
        break;
      case 'cancel':
    	  if(true == this.options.prevent){
          // Jeżeli w kolejce poprzednim trybem jest cancel to usuwamy ostatniego requesta z kolejki
          var prev = this.mQueue.length.toInt() - 1;
          if(this.mQueue[prev] && this.mQueue[prev].mode == 'cancel'){
            this.mQueue.pop();
          }
          
    	    // Jeżeli włączony jest tryb prevent to request jest kolejkowany
          this.mQueue.include(pOptions);
    	    return false;
    	  }
        if(true == this.mIsActiveConnection){
          $clear(this.options.debugDelay);
          this.mRh.cancel();
        }
        break;
      case 'queue':
        if(true == this.mIsActiveConnection){          
          this.mQueue.extend([pOptions]);
    	    return true;
        }
        break;
    }
	  
	  if(true == this.options.prevent){
	    return false;
	  }
    this.send(pOptions);
	},
	
	send: function(pOptions){
    if(pOptions){
  	  this.resetOptions();
  	  this.setOptions(pOptions);
    }    
    if(!this.options){
      return false;
    }
    
    this.fireEvent('onStart');

    this.mIsActiveConnection = true;
    // czyscimy urla z  parametru __ajax_request jeżeli juz go posiada
    this.options.url = this.options.url.replace(/(\?|&)__ajax_request=1/g, '');
    if(!this.options.url.match(/\?/)){
      this.options.url +='?__ajax_request=1'
	  }
	  else{
      this.options.url +='&__ajax_request=1'
	  }

	  var data = pOptions || this.options;
	  
	  // Poprzedni request się zakończył więc trzeba stworzyć nowy!
	  this.createRequestHandler();
    this.options.debugDelay = this.runDebug.delay(this.mTTL, this, [data]);
    this.mRh.post({'json': JSON.encode(this.options.data)});
	},
	
	showAlert: function(){
	  alert('nie wolno!')
	},
  
  /**
   * Metoda wywoływana po zakończeniu requesta ajaxowego.
   * 
   * evaluje również wszystkie skrypty które zostały zwrócone
   * jako parametr zwracane są dane przesłane z requesta
   *
   * @param object pData
   */  
  onCompleteCall: function(pData){
    $clear(this.options.debugDelay);
    
    this.mIsActiveConnection = false;
    this.options.prevent = false;

    if(!this.options.completePass){
      this.options.completePass = new Array();
    }
    this.options.completePass.extend([pData])
    
    this.fireEvent('onComplete', this.options.completePass);
    
    if(true == this.options.eval){
      if(this.options.evalData){
        this.options.evalData.each(function(pEl){
          this.evalScripts(pData[pEl]);
        }.bind(this))
      }
      else{
        this.evalScripts(pData);
      }
    }
    
    this.fireEvent('onEnd');

    
    if(this.mQueue.length > 0){
      this.send(this.mQueue.pop());
    }
      
    return pData;
  },
  
  /**
   * Jeżeli request trwa zbyt długo to informacje na jego temat sa zapisywane do bzy danych,
   * sam request jest cancelowany i uruchamiany ponownie
   * 
   * @param object pData
   */  
  runDebug: function(pData){
    
    if(false == this.mIsActiveConnection){
      return false;
    }
    
    this.mRh.cancel();
    // Jeżeli jest to request Debugowy to anulujemy trwający ale nie wysyłamy kolejnego requesta
    if(true === this.options.debugRequest){
      return true;
    }
    
    this.newRequest({
      url: '/ajax/Dejax/DeBug/',
      eval: false,
      debugRequest: true,
      data: {'data': pData}
    });
    
    this.newRequest(pData);
  },
  
  /**
   * Metoda evalujaca javascripta z podanego stringa.
   * 
   * @param string pData
   */  
	evalScripts: function(pData){
		scripts_to_asset = [];
		var regexp_to_asset = /<script[^>]* src="([\s\S]*?)"[^>]*?>[\s\S]*?<\/script>/gi;
		while ((script_to_asset = regexp_to_asset.exec(pData))) new Asset.javascript(script_to_asset[1]);

	  this.evalInLine.delay(1, this, pData);
	},
  
	evalInLine: function(pData){
		scripts = [];
		var regexp = /<script[^>]*>([\s\S]*?)<\/script>/gi;
		while ((script = regexp.exec(pData))) scripts.push(script[1]);
		scripts = scripts.join('\n');
		
		if (scripts) (window.execScript) ? window.execScript(scripts) : window.setTimeout(scripts, 0);
	}
	
	
});
var DejaxRequest;
window.addEvent('domready', function(){
  DejaxRequest = new Dejax();
});

/**
 * SWFObject v1.5: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
 *
 * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 */
if(typeof deconcept=="undefined"){var deconcept=new Object();}if(typeof deconcept.util=="undefined"){deconcept.util=new Object();}if(typeof deconcept.SWFObjectUtil=="undefined"){deconcept.SWFObjectUtil=new Object();}deconcept.SWFObject=function(_1,id,w,h,_5,c,_7,_8,_9,_a){if(!document.getElementById){return;}this.DETECT_KEY=_a?_a:"detectflash";this.skipDetect=deconcept.util.getRequestParameter(this.DETECT_KEY);this.params=new Object();this.variables=new Object();this.attributes=new Array();if(_1){this.setAttribute("swf",_1);}if(id){this.setAttribute("id",id);}if(w){this.setAttribute("width",w);}if(h){this.setAttribute("height",h);}if(_5){this.setAttribute("version",new deconcept.PlayerVersion(_5.toString().split(".")));}this.installedVer=deconcept.SWFObjectUtil.getPlayerVersion();if(!window.opera&&document.all&&this.installedVer.major>7){deconcept.SWFObject.doPrepUnload=true;}if(c){this.addParam("bgcolor",c);}var q=_7?_7:"high";this.addParam("quality",q);this.setAttribute("useExpressInstall",false);this.setAttribute("doExpressInstall",false);var _c=(_8)?_8:window.location;this.setAttribute("xiRedirectUrl",_c);this.setAttribute("redirectUrl","");if(_9){this.setAttribute("redirectUrl",_9);}};deconcept.SWFObject.prototype={useExpressInstall:function(_d){this.xiSWFPath=!_d?"expressinstall.swf":_d;this.setAttribute("useExpressInstall",true);},setAttribute:function(_e,_f){this.attributes[_e]=_f;},getAttribute:function(_10){return this.attributes[_10];},addParam:function(_11,_12){this.params[_11]=_12;},getParams:function(){return this.params;},addVariable:function(_13,_14){this.variables[_13]=_14;},getVariable:function(_15){return this.variables[_15];},getVariables:function(){return this.variables;},getVariablePairs:function(){var _16=new Array();var key;var _18=this.getVariables();for(key in _18){_16[_16.length]=key+"="+_18[key];}return _16;},getSWFHTML:function(){var _19="";if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","PlugIn");this.setAttribute("swf",this.xiSWFPath);}_19="<embed type=\"application/x-shockwave-flash\" src=\""+this.getAttribute("swf")+"\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\"";_19+=" id=\""+this.getAttribute("id")+"\" name=\""+this.getAttribute("id")+"\" ";var _1a=this.getParams();for(var key in _1a){_19+=[key]+"=\""+_1a[key]+"\" ";}var _1c=this.getVariablePairs().join("&");if(_1c.length>0){_19+="flashvars=\""+_1c+"\"";}_19+="/>";}else{if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","ActiveX");this.setAttribute("swf",this.xiSWFPath);}_19="<object id=\""+this.getAttribute("id")+"\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\">";_19+="<param name=\"movie\" value=\""+this.getAttribute("swf")+"\" />";var _1d=this.getParams();for(var key in _1d){_19+="<param name=\""+key+"\" value=\""+_1d[key]+"\" />";}var _1f=this.getVariablePairs().join("&");if(_1f.length>0){_19+="<param name=\"flashvars\" value=\""+_1f+"\" />";}_19+="</object>";}return _19;},write:function(_20){if(this.getAttribute("useExpressInstall")){var _21=new deconcept.PlayerVersion([6,0,65]);if(this.installedVer.versionIsValid(_21)&&!this.installedVer.versionIsValid(this.getAttribute("version"))){this.setAttribute("doExpressInstall",true);this.addVariable("MMredirectURL",escape(this.getAttribute("xiRedirectUrl")));document.title=document.title.slice(0,47)+" - Flash Player Installation";this.addVariable("MMdoctitle",document.title);}}if(this.skipDetect||this.getAttribute("doExpressInstall")||this.installedVer.versionIsValid(this.getAttribute("version"))){var n=(typeof _20=="string")?document.getElementById(_20):_20;n.innerHTML=this.getSWFHTML();return true;}else{if(this.getAttribute("redirectUrl")!=""){document.location.replace(this.getAttribute("redirectUrl"));}}return false;}};deconcept.SWFObjectUtil.getPlayerVersion=function(){var _23=new deconcept.PlayerVersion([0,0,0]);if(navigator.plugins&&navigator.mimeTypes.length){var x=navigator.plugins["Shockwave Flash"];if(x&&x.description){_23=new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".").split("."));}}else{if(navigator.userAgent&&navigator.userAgent.indexOf("Windows CE")>=0){var axo=1;var _26=3;while(axo){try{_26++;axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+_26);_23=new deconcept.PlayerVersion([_26,0,0]);}catch(e){axo=null;}}}else{try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");_23=new deconcept.PlayerVersion([6,0,21]);axo.AllowScriptAccess="always";}catch(e){if(_23.major==6){return _23;}}try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(e){}}if(axo!=null){_23=new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));}}}return _23;};deconcept.PlayerVersion=function(_29){this.major=_29[0]!=null?parseInt(_29[0]):0;this.minor=_29[1]!=null?parseInt(_29[1]):0;this.rev=_29[2]!=null?parseInt(_29[2]):0;};deconcept.PlayerVersion.prototype.versionIsValid=function(fv){if(this.major<fv.major){return false;}if(this.major>fv.major){return true;}if(this.minor<fv.minor){return false;}if(this.minor>fv.minor){return true;}if(this.rev<fv.rev){return false;}return true;};deconcept.util={getRequestParameter:function(_2b){var q=document.location.search||document.location.hash;if(_2b==null){return q;}if(q){var _2d=q.substring(1).split("&");for(var i=0;i<_2d.length;i++){if(_2d[i].substring(0,_2d[i].indexOf("="))==_2b){return _2d[i].substring((_2d[i].indexOf("=")+1));}}}return "";}};deconcept.SWFObjectUtil.cleanupSWFs=function(){var _2f=document.getElementsByTagName("OBJECT");for(var i=_2f.length-1;i>=0;i--){_2f[i].style.display="none";for(var x in _2f[i]){if(typeof _2f[i][x]=="function"){_2f[i][x]=function(){};}}}};if(deconcept.SWFObject.doPrepUnload){if(!deconcept.unloadSet){deconcept.SWFObjectUtil.prepUnload=function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};window.attachEvent("onunload",deconcept.SWFObjectUtil.cleanupSWFs);};window.attachEvent("onbeforeunload",deconcept.SWFObjectUtil.prepUnload);deconcept.unloadSet=true;}}if(!document.getElementById&&document.all){document.getElementById=function(id){return document.all[id];};}var getQueryParamValue=deconcept.util.getRequestParameter;var FlashObject=deconcept.SWFObject;var SWFObject=deconcept.SWFObject;
var DeWaiter = new Class({
  
  Implements: [ Options ],
  mElement: null,
  mWaitElement: null,
  
  initialize: function(pElement){
    this.mElement = pElement;
    this.createWaitElement();
  },
  
  createWaitElement: function(){
    this.mWaitElement = new Element('div', {
      'styles': {
        'position': 'absolute',
        'background-color': '#ffffff',
        'background-image': 'url(/cmp/DeWaiter/waiter.gif)',
        'background-position': 'center center',
        'background-repeat': 'no-repeat',
        'opacity': 0,
        'display': 'none',
        'z-index': 999999
      }
    });
    
    this.mWaitElement.injectInside(document.body);
    
  },
  
  updatePosition: function(){
    var position = this.mElement.getPosition();
    var sourceSize = this.mElement.getSize();
    
    this.mWaitElement.setStyles({
      'width': sourceSize.x,
      'height': sourceSize.y,
      'left': position.x,
      'top': position.y      
    });
  },
  
  start: function(pOptions){
    this.setOptions(pOptions);
    this.updatePosition();
    
    if(pOptions.image){
      this.mWaitElement.setStyle('background-image', 'url("'+pOptions.image+'")');
    }
    else{
      this.mWaitElement.setStyle('background-image', 'url("/cmp/DeWaiter/waiter.gif")');
    }
    
    if(pOptions.opacity){
      this.mElement.fade(0.2);
      this.mWaitElement.setStyles({'display': 'block', 'background-color': 'transparent'}).fade(1);
    }
    else{
      this.mWaitElement.setStyle('display', 'block').fade(0.8);
    }
    
  },
  
  stop: function(){
    this.updatePosition();
    this.mWaitElement.fade(0);
    
    if(this.options.opacity){
      this.mElement.fade(1);
    }
    
  },
  
  hide: function(){
    this.mWaitElement.setStyle('display', 'none');
  }
  
});

Element.implement({

	wait: function(options){
	  
	  if(!(waiter = this.retrieve('waiter'))){
	    waiter = new DeWaiter(this);
	    this.store('waiter', waiter);
	  }
	  
		waiter.start(options);
		
		return this;
		
	},
	
	release: function(){
	  
	  if(!(waiter = this.retrieve('waiter'))){
	    waiter = new DeWaiter(this);
	    this.store('waiter', waiter);
	  }
	  
	  waiter.stop();
	  
		return this;
	}

});
/**
 * EVENTY:
 * onChangePage - wykonywany po kliknięciu na stronę
 * onLoadPage - wykonywany po kliknięciu i załadowaniu strony
 * ----------
 * onBeforeLoad - wykonywany przed po kliknięciu i przez załadowaniem
 */
var PageNavigatorAJAXOverlay = new Class({

  Implements: new Events(),
  
  'mContainerEl': null,
  'mTargetEl': null,
  'mCurrentPage': null,
  
  'initialize': function(pContainerEl, pTargetEl){
    this.mContainerEl = pContainerEl;
    this.mTargetEl = pTargetEl;
    
    if(this.mContainerEl){
      this.mContainerEl.getElements('a').each(function(pEl){
        pEl.setProperty('href', '#'+pEl.getProperty('rel'));
      });
    }
    
    if(this.mContainerEl){
      this.registerAjaxRequests();
    }
    
  },
  
  'registerAjaxRequests': function(){
    this.mContainerEl.getElements('a').each(function(pEl){
      pEl.addEvent('click', this.getData.bind(this, pEl.getProperty('href').match(/#(\d+)/)[1]));
    }.bind(this));
  },
  
  'getData': function(pPageNo){
    this.mTargetEl.wait();
    
    this.fireEvent('onChangePage');
//    if('function' == $type(this.onBeforeLoad)){
//      this.onBeforeLoad(pE, pEl);
//    }
    
    DejaxRequest.newRequest({
      url: window.location.href,
      eval: true,
      mode: 'free',
      data: {'page_no': pPageNo},
      onComplete: this.reloadContent.bind(this)
    });
  },
  
  'reloadContent': function(pData){
    this.mTargetEl.set('html', pData);
    this.mTargetEl.release();
    this.fireEvent('onLoadPage');
    new Fx.Scroll(document.body).toElement($('top'));
  }
  
});
/**
* Dzięki tej klasie łatwo i szybko powinienes stworzyć mechanizm do przesuwania elementów wewnątrz kontenera z ustawionym overflow: hidden
*
* Klasa bedzie musiała zostać rozbudowana. Na chwilę obecna znajduje się tu tylko to czego potrzebuję.
*
* Przykład użycia i opcje: config/example.html
*
*/
var ContentSlider = new Class({

  Implements: Events,
  
  mOffset: 50,
  
  mSingleElWidth: 0,
  
  mStep: 0,
  
  mMaxStep: 0,
  
  mModes: {
    'vertical': 'top',
    'horizontal': 'left'
  },
  
  initialize: function(pOptions){    
    this.mOptions = pOptions;
    this.mOptions.move = this.mOptions.move || 'horizontal';
    
    this.addSlidersEvents();
    
    this.mMaxStep = Math.ceil(this.mOptions.single_elements.length / this.mOptions.show_count) - this.mOptions.el_in_a_row + 1;
    
    this.mFX = new Fx.Tween(this.mOptions.slide_element, {property: this.mModes[this.mOptions.move]});
  },
  
  /**
  * Resetuje mOptions z danych poprzednich instancji
  *
  */
  resetOptions: function(){
    this.mOptions = {
      'slide_element': null,
      'single_elements': null,
      'slide_step_px': 0,
      'show_count': 1,
      'slide_left': null,
      'slide_right': null,
      'el_in_a_row': 0
    }
  },
    
  /**
  * Ustawia szerokość głównego elementu, tak aby tło sięgało na wszystkie elemeny
  * Szerokość wyliczana jest na podstawie ilości elementów w środku i dodawany jest offset
  *
  *
  */
  setContainerWidth: function(){
    
    var width = this.mOptions.single_elements.length * this.mOptions.slide_step_px + this.mOffset;
    this.mOptions.slide_element.setStyle('width', width);
  },
  
  /**
  * Dodaje eventy na przyciski do przesuwania
  */
  addSlidersEvents: function(){
    this.mOptions.slide_right.addEvent('click', this.callStep.bindWithEvent(this, 1));
    this.mOptions.slide_left.addEvent('click', this.callStep.bindWithEvent(this, -1));
  },
  
  callStep: function(pE, pStep){
    if(pE){
      pE.stop();
    }
    
    this.step(this.mStep+pStep);
    this.fireEvent('onChange', this.mStep);
  },
  
  step: function(pStep){
    if(pStep < 0 || pStep >= this.mMaxStep){
      return false;
    }
    
    this.mStep = pStep
    var new_left = this.mStep*this.mOptions.slide_step_px*this.mOptions.show_count*(-1);

    this.slide(new_left);
  },
  
  /**
  * Przesuwa kontener w odpowiednią stronę o odpowiedna ilość
  */
  slide: function(pLeft){
    this.mFX.start(pLeft);
  }
  
  
});
/*!
	Slimbox v1.7 - The ultimate lightweight Lightbox clone
	(c) 2007-2009 Christophe Beyls <http://www.digitalia.be>
	MIT-style license.
*/

var Slimbox = (function() {

	// Global variables, accessible to Slimbox only
	var win = window, ie6 = Browser.Engine.trident4, options, images, activeImage = -1, activeURL, prevImage, nextImage, compatibleOverlay, middle, centerWidth, centerHeight,

	// Preload images
	preload = {}, preloadPrev = new Image(), preloadNext = new Image(),

	// DOM elements
	overlay, center, image, sizer, prevLink, nextLink, bottomContainer, bottom, caption, number,

	// Effects
	fxOverlay, fxResize, fxImage, fxBottom;

	/*
		Initialization
	*/

	win.addEvent("domready", function() {
		// Append the Slimbox HTML code at the bottom of the document
		$(document.body).adopt(
			$$(
				overlay = new Element("div", {id: "lbOverlay", events: {click: close}}),
				center = new Element("div", {id: "lbCenter"}),
				bottomContainer = new Element("div", {id: "lbBottomContainer"})
			).setStyle("display", "none")
		);

		image = new Element("div", {id: "lbImage"}).injectInside(center).adopt(
			sizer = new Element("div", {styles: {position: "relative"}}).adopt(
				prevLink = new Element("a", {id: "lbPrevLink", href: "#", events: {click: previous}}),
				nextLink = new Element("a", {id: "lbNextLink", href: "#", events: {click: next}})
			)
		);

		bottom = new Element("div", {id: "lbBottom"}).injectInside(bottomContainer).adopt(
			new Element("a", {id: "lbCloseLink", href: "#", events: {click: close}}),
			caption = new Element("div", {id: "lbCaption"}),
			number = new Element("div", {id: "lbNumber"}),
			new Element("div", {styles: {clear: "both"}})
		);
	});


	/*
		Internal functions
	*/

	function position() {
		var scroll = win.getScroll(), size = win.getSize();
		$$(center, bottomContainer).setStyle("left", scroll.x + (size.x / 2));
		if (compatibleOverlay) overlay.setStyles({left: scroll.x, top: scroll.y, width: size.x, height: size.y});
	}

	function setup(open) {
		["object", ie6 ? "select" : "embed"].forEach(function(tag) {
			Array.forEach(document.getElementsByTagName(tag), function(el) {
				if (open) el._slimbox = el.style.visibility;
				el.style.visibility = open ? "hidden" : el._slimbox;
			});
		});

		overlay.style.display = open ? "" : "none";

		var fn = open ? "addEvent" : "removeEvent";
		win[fn]("scroll", position)[fn]("resize", position);
		document[fn]("keydown", keyDown);
	}

	function keyDown(event) {
		var code = event.code;
		// Prevent default keyboard action (like navigating inside the page)
		return options.closeKeys.contains(code) ? close()
			: options.nextKeys.contains(code) ? next()
			: options.previousKeys.contains(code) ? previous()
			: false;
	}

	function previous() {
		return changeImage(prevImage);
	}

	function next() {
		return changeImage(nextImage);
	}

	function changeImage(imageIndex) {
		if (imageIndex >= 0) {
			activeImage = imageIndex;
			activeURL = images[imageIndex][0];
			prevImage = (activeImage || (options.loop ? images.length : 0)) - 1;
			nextImage = ((activeImage + 1) % images.length) || (options.loop ? 0 : -1);

			stop();
			center.className = "lbLoading";

			preload = new Image();
			preload.onload = animateBox;
			preload.src = activeURL;
		}

		return false;
	}

	function animateBox() {
		center.className = "";
		fxImage.set(0);
		image.setStyles({backgroundImage: "url(" + activeURL + ")", display: ""});
		sizer.setStyle("width", preload.width);
		$$(sizer, prevLink, nextLink).setStyle("height", preload.height);

		caption.set("html", images[activeImage][1] || "");
		number.set("html", (((images.length > 1) && options.counterText) || "").replace(/{x}/, activeImage + 1).replace(/{y}/, images.length));

		if (prevImage >= 0) preloadPrev.src = images[prevImage][0];
		if (nextImage >= 0) preloadNext.src = images[nextImage][0];

		centerWidth = image.offsetWidth;
		centerHeight = image.offsetHeight;
		var top = Math.max(0, middle - (centerHeight / 2)), check = 0, fn;
		if (center.offsetHeight != centerHeight) {
			check = fxResize.start({height: centerHeight, top: top});
		}
		if (center.offsetWidth != centerWidth) {
			check = fxResize.start({width: centerWidth, marginLeft: -centerWidth/2});
		}
		fn = function() {
			bottomContainer.setStyles({width: centerWidth, top: top + centerHeight, marginLeft: -centerWidth/2, visibility: "hidden", display: ""});
			fxImage.start(1);
		};
		if (check) {
			fxResize.chain(fn);
		}
		else {
			fn();
		}
	}

	function animateCaption() {
		if (prevImage >= 0) prevLink.style.display = "";
		if (nextImage >= 0) nextLink.style.display = "";
		fxBottom.set(-bottom.offsetHeight).start(0);
		bottomContainer.style.visibility = "";
	}

	function stop() {
		preload.onload = $empty;
		preload.src = preloadPrev.src = preloadNext.src = activeURL;
		fxResize.cancel();
		fxImage.cancel();
		fxBottom.cancel();
		$$(prevLink, nextLink, image, bottomContainer).setStyle("display", "none");
	}

	function close() {
		if (activeImage >= 0) {
			stop();
			activeImage = prevImage = nextImage = -1;
			center.style.display = "none";
			fxOverlay.cancel().chain(setup).start(0);
		}

		return false;
	}


	/*
		API
	*/

	Element.implement({
		slimbox: function(_options, linkMapper) {
			// The processing of a single element is similar to the processing of a collection with a single element
			$$(this).slimbox(_options, linkMapper);

			return this;
		}
	});

	Elements.implement({
		/*
			options:	Optional options object, see Slimbox.open()
			linkMapper:	Optional function taking a link DOM element and an index as arguments and returning an array containing 2 elements:
					the image URL and the image caption (may contain HTML)
			linksFilter:	Optional function taking a link DOM element and an index as arguments and returning true if the element is part of
					the image collection that will be shown on click, false if not. "this" refers to the element that was clicked.
					This function must always return true when the DOM element argument is "this".
		*/
		slimbox: function(_options, linkMapper, linksFilter) {
			linkMapper = linkMapper || function(el) {
				return [el.href, el.title];
			};

			linksFilter = linksFilter || function() {
				return true;
			};

			var links = this;

			links.removeEvents("click").addEvent("click", function() {
				// Build the list of images that will be displayed
				var filteredLinks = links.filter(linksFilter, this);
				return Slimbox.open(filteredLinks.map(linkMapper), filteredLinks.indexOf(this), _options);
			});

			return links;
		}
	});

	return {
		open: function(_images, startImage, _options) {
			options = $extend({
				loop: false,				// Allows to navigate between first and last images
				overlayOpacity: 0.8,			// 1 is opaque, 0 is completely transparent (change the color in the CSS file)
				overlayFadeDuration: 400,		// Duration of the overlay fade-in and fade-out animations (in milliseconds)
				resizeDuration: 400,			// Duration of each of the box resize animations (in milliseconds)
				resizeTransition: false,		// false uses the mootools default transition
				initialWidth: 250,			// Initial width of the box (in pixels)
				initialHeight: 250,			// Initial height of the box (in pixels)
				imageFadeDuration: 400,			// Duration of the image fade-in animation (in milliseconds)
				captionAnimationDuration: 400,		// Duration of the caption animation (in milliseconds)
				counterText: "Zdjęcie {x} z {y}",	// Translate or change as you wish, or set it to false to disable counter text for image groups
				closeKeys: [27, 88, 67],		// Array of keycodes to close Slimbox, default: Esc (27), 'x' (88), 'c' (67)
				previousKeys: [37, 80],			// Array of keycodes to navigate to the previous image, default: Left arrow (37), 'p' (80)
				nextKeys: [39, 78]			// Array of keycodes to navigate to the next image, default: Right arrow (39), 'n' (78)
			}, _options || {});

			// Setup effects
			fxOverlay = new Fx.Tween(overlay, {property: "opacity", duration: options.overlayFadeDuration});
			fxResize = new Fx.Morph(center, $extend({duration: options.resizeDuration, link: "chain"}, options.resizeTransition ? {transition: options.resizeTransition} : {}));
			fxImage = new Fx.Tween(image, {property: "opacity", duration: options.imageFadeDuration, onComplete: animateCaption});
			fxBottom = new Fx.Tween(bottom, {property: "margin-top", duration: options.captionAnimationDuration});

			// The function is called for a single image, with URL and Title as first two arguments
			if (typeof _images == "string") {
				_images = [[_images, startImage]];
				startImage = 0;
			}

			middle = win.getScrollTop() + (win.getHeight() / 2);
			centerWidth = options.initialWidth;
			centerHeight = options.initialHeight;
			center.setStyles({top: Math.max(0, middle - (centerHeight / 2)), width: centerWidth, height: centerHeight, marginLeft: -centerWidth/2, display: ""});
			compatibleOverlay = ie6 || (overlay.currentStyle && (overlay.currentStyle.position != "fixed"));
			if (compatibleOverlay) overlay.style.position = "absolute";
			fxOverlay.set(0).start(options.overlayOpacity);
			position();
			setup(1);

			images = _images;
			options.loop = options.loop && (images.length > 1);
			return changeImage(startImage);
		}
	};

})();

// AUTOLOAD CODE BLOCK (MAY BE CHANGED OR REMOVED)
Slimbox.scanPage = function() {
	$$(document.links).filter(function(el) {
		return el.rel && el.rel.test(/^lightbox/i);
	}).slimbox({/* Put custom options here */}, null, function(el) {
		return (this == el) || ((this.rel.length > 8) && (this.rel == el.rel));
	});
};
window.addEvent("domready", Slimbox.scanPage);
/**
 * Swiff.Uploader - Flash FileReference Control
 *
 * @version		3.0
 *
 * @license		MIT License
 *
 * @author		Harald Kirschner <http://digitarald.de>
 * @author		Valerio Proietti, <http://mad4milk.net>
 * @copyright	Authors
 */

Swiff.Uploader = new Class({

	Extends: Swiff,

	Implements: Events,

	options: {
		path: 'Swiff.Uploader.swf',
		
		target: null,
		zIndex: 9999,
		
		height: 30,
		width: 100,
		callBacks: null,
		params: {
			wMode: 'opaque',
			menu: 'false',
			allowScriptAccess: 'always'
		},

		typeFilter: null,
		multiple: true,
		queued: true,
		verbose: false,

		url: null,
		method: null,
		data: null,
		mergeData: true,
		fieldName: null,

		fileSizeMin: 1,
		fileSizeMax: null, // Official limit is 100 MB for FileReference, but I tested up to 2Gb!
		allowDuplicates: false,
		timeLimit: (Browser.Platform.linux) ? 0 : 30,

		buttonImage: null,
		policyFile: null,
		
		fileListMax: 0,
		fileListSizeMax: 0,

		instantStart: false,
		appendCookieData: false,
		
		fileClass: null
		/*
		onLoad: $empty,
		onFail: $empty,
		onStart: $empty,
		onQueue: $empty,
		onComplete: $empty,
		onBrowse: $empty,
		onDisabledBrowse: $empty,
		onCancel: $empty,
		onSelect: $empty,
		onSelectSuccess: $empty,
		onSelectFail: $empty,
		
		onButtonEnter: $empty,
		onButtonLeave: $empty,
		onButtonDown: $empty,
		onButtonDisable: $empty,
		
		onFileStart: $empty,
		onFileStop: $empty,
		onFileRequeue: $empty,
		onFileOpen: $empty,
		onFileProgress: $empty,
		onFileComplete: $empty,
		onFileRemove: $empty,
		
		onBeforeStart: $empty,
		onBeforeStop: $empty,
		onBeforeRemove: $empty
		*/
	},

	initialize: function(options) {
		// protected events to control the class, added
		// before setting options (which adds own events)
		this.addEvent('load', this.initializeSwiff, true)
			.addEvent('select', this.processFiles, true)
			.addEvent('complete', this.update, true)
			.addEvent('fileRemove', function(file) {
				this.fileList.erase(file);
			}.bind(this), true);

		this.setOptions(options);

		// callbacks are no longer in the options, every callback
		// is fired as event, this is just compat
		if (this.options.callBacks) {
			Hash.each(this.options.callBacks, function(fn, name) {
				this.addEvent(name, fn);
			}, this);
		}

		this.options.callBacks = {
			fireCallback: this.fireCallback.bind(this)
		};

		var path = this.options.path;
		if (!path.contains('?')) path += '?noCache=' + $time(); // cache in IE

		// container options for Swiff class
		this.options.container = this.box = new Element('span', {'class': 'swiff-uploader-box'}).inject($(this.options.container) || document.body);

		// target 
		this.target = $(this.options.target);
		if (this.target) {
			var scroll = window.getScroll();
			this.box.setStyles({
				position: 'absolute',
				visibility: 'visible',
				zIndex: this.options.zIndex,
				overflow: 'hidden',
				height: 1, width: 1,
				top: scroll.y, left: scroll.x
			});
			
			// we force wMode to transparent for the overlay effect
			this.parent(path, {
				params: {
					wMode: 'transparent'
				},
				height: '100%',
				width: '100%'
			});
			
			this.target.addEvent('mouseenter', this.reposition.bind(this, []));
			
			// button interactions, relayed to to the target
			this.addEvents({
				buttonEnter: this.targetRelay.bind(this, ['mouseenter']),
				buttonLeave: this.targetRelay.bind(this, ['mouseleave']),
				buttonDown: this.targetRelay.bind(this, ['mousedown']),
				buttonDisable: this.targetRelay.bind(this, ['disable'])
			});
			
			this.reposition();
			window.addEvent('resize', this.reposition.bind(this, []));
		} else {
			this.parent(path);
		}

		this.inject(this.box);

		this.fileList = [];
		
		this.size = this.uploading = this.bytesLoaded = this.percentLoaded = 0;
		
		if (Browser.Plugins.Flash.version < 9) {
			this.fireEvent('fail', ['flash']);
		} else {
			this.verifyLoad.delay(1000, this);
		}
	},
	
	verifyLoad: function() {
		if (this.loaded) return;
		if (!this.object.parentNode) {
			this.fireEvent('fail', ['disabled']);
		} else if (this.object.style.display == 'none') {
			this.fireEvent('fail', ['hidden']);
		} else if (!this.object.offsetWidth) {
			this.fireEvent('fail', ['empty']);
		}
	},

	fireCallback: function(name, args) {
		// file* callbacks are relayed to the specific file
		if (name.substr(0, 4) == 'file') {
			// updated queue data is the second argument
			if (args.length > 1) this.update(args[1]);
			var data = args[0];
			
			var file = this.findFile(data.id);
			this.fireEvent(name, file || data, 5);
			if (file) {
				var fire = name.replace(/^file([A-Z])/, function($0, $1) {
					return $1.toLowerCase();
				});
				file.update(data).fireEvent(fire, [data], 10);
			}
		} else {
			this.fireEvent(name, args, 5);
		}
	},

	update: function(data) {
		// the data is saved right to the instance 
		$extend(this, data);
		this.fireEvent('queue', [this], 10);
		return this;
	},

	findFile: function(id) {
		for (var i = 0; i < this.fileList.length; i++) {
			if (this.fileList[i].id == id) return this.fileList[i];
		}
		return null;
	},

	initializeSwiff: function() {
		// extracted options for the swf 
		this.remote('initialize', {
			width: this.options.width,
			height: this.options.height,
			typeFilter: this.options.typeFilter,
			multiple: this.options.multiple,
			queued: this.options.queued,
			url: this.options.url,
			method: this.options.method,
			data: this.options.data,
			mergeData: this.options.mergeData,
			fieldName: this.options.fieldName,
			verbose: this.options.verbose,
			fileSizeMin: this.options.fileSizeMin,
			fileSizeMax: this.options.fileSizeMax,
			allowDuplicates: this.options.allowDuplicates,
			timeLimit: this.options.timeLimit,
			buttonImage: this.options.buttonImage,
			policyFile: this.options.policyFile
		});

		this.loaded = true;

		this.appendCookieData();
	},
	
	targetRelay: function(name) {
		if (this.target) this.target.fireEvent(name);
	},

	reposition: function(coords) {
		// update coordinates, manual or automatically
		coords = coords || (this.target && this.target.offsetHeight)
			? this.target.getCoordinates(this.box.getOffsetParent())
			: {top: window.getScrollTop(), left: 0, width: 40, height: 40}
		this.box.setStyles(coords);
		this.fireEvent('reposition', [coords, this.box, this.target]);
	},

	setOptions: function(options) {
		if (options) {
			if (options.url) options.url = Swiff.Uploader.qualifyPath(options.url);
			if (options.buttonImage) options.buttonImage = Swiff.Uploader.qualifyPath(options.buttonImage);
			this.parent(options);
			if (this.loaded) this.remote('setOptions', options);
		}
		return this;
	},

	setEnabled: function(status) {
		this.remote('setEnabled', status);
	},

	start: function() {
		this.fireEvent('beforeStart');
		this.remote('start');
	},

	stop: function() {
		this.fireEvent('beforeStop');
		this.remote('stop');
	},

	remove: function() {
		this.fireEvent('beforeRemove');
		this.remote('remove');
	},

	fileStart: function(file) {
		this.remote('fileStart', file.id);
	},

	fileStop: function(file) {
		this.remote('fileStop', file.id);
	},

	fileRemove: function(file) {
		this.remote('fileRemove', file.id);
	},

	fileRequeue: function(file) {
		this.remote('fileRequeue', file.id);
	},

	appendCookieData: function() {
		var append = this.options.appendCookieData;
		if (!append) return;
		
		var hash = {};
		document.cookie.split(/;\s*/).each(function(cookie) {
			cookie = cookie.split('=');
			if (cookie.length == 2) {
				hash[decodeURIComponent(cookie[0])] = decodeURIComponent(cookie[1]);
			}
		});

		var data = this.options.data || {};
		if ($type(append) == 'string') data[append] = hash;
		else $extend(data, hash);

		this.setOptions({data: data});
	},

	processFiles: function(successraw, failraw, queue) {
		var cls = this.options.fileClass || Swiff.Uploader.File;

		var fail = [], success = [];

		if (successraw) {
			successraw.each(function(data) {
				var ret = new cls(this, data);
				if (!ret.validate()) {
					ret.remove.delay(10, ret);
					fail.push(ret);
				} else {
					this.size += data.size;
					this.fileList.push(ret);
					success.push(ret);
					ret.render();
				}
			}, this);

			this.fireEvent('selectSuccess', [success], 10);
		}

		if (failraw || fail.length) {
			fail.extend((failraw) ? failraw.map(function(data) {
				return new cls(this, data);
			}, this) : []).each(function(file) {
				file.invalidate().render();
			});

			this.fireEvent('selectFail', [fail], 10);
		}

		this.update(queue);

		if (this.options.instantStart && success.length) this.start();
	}

});

$extend(Swiff.Uploader, {

	STATUS_QUEUED: 0,
	STATUS_RUNNING: 1,
	STATUS_ERROR: 2,
	STATUS_COMPLETE: 3,
	STATUS_STOPPED: 4,

	log: function() {
		if (window.console && console.info) console.info.apply(console, arguments);
	},

	unitLabels: {
		b: [{min: 1, unit: 'B'}, {min: 1024, unit: 'kB'}, {min: 1048576, unit: 'MB'}, {min: 1073741824, unit: 'GB'}],
		s: [{min: 1, unit: 's'}, {min: 60, unit: 'm'}, {min: 3600, unit: 'h'}, {min: 86400, unit: 'd'}]
	},

	formatUnit: function(base, type, join) {
		var labels = Swiff.Uploader.unitLabels[(type == 'bps') ? 'b' : type];
		var append = (type == 'bps') ? '/s' : '';
		var i, l = labels.length, value;

		if (base < 1) return '0 ' + labels[0].unit + append;

		if (type == 's') {
			var units = [];

			for (i = l - 1; i >= 0; i--) {
				value = Math.floor(base / labels[i].min);
				if (value) {
					units.push(value + ' ' + labels[i].unit);
					base -= value * labels[i].min;
					if (!base) break;
				}
			}

			return (join === false) ? units : units.join(join || ', ');
		}

		for (i = l - 1; i >= 0; i--) {
			value = labels[i].min;
			if (base >= value) break;
		}

		return (base / value).toFixed(1) + ' ' + labels[i].unit + append;
	}

});

Swiff.Uploader.qualifyPath = (function() {
	
	var anchor;
	
	return function(path) {
		(anchor || (anchor = new Element('a'))).href = path;
		return anchor.href;
	};

})();

Swiff.Uploader.File = new Class({

	Implements: Events,

	initialize: function(base, data) {
		this.base = base;
		this.update(data);
	},

	update: function(data) {
		return $extend(this, data);
	},

	validate: function() {
		var options = this.base.options;
		
		if (options.fileListMax && this.base.fileList.length >= options.fileListMax) {
			this.validationError = 'fileListMax';
			return false;
		}
		
		if (options.fileListSizeMax && (this.base.size + this.size) > options.fileListSizeMax) {
			this.validationError = 'fileListSizeMax';
			return false;
		}
		
		return true;
	},

	invalidate: function() {
		this.invalid = true;
		this.base.fireEvent('fileInvalid', this, 10);
		return this.fireEvent('invalid', this, 10);
	},

	render: function() {
		return this;
	},

	setOptions: function(options) {
		if (options) {
			if (options.url) options.url = Swiff.Uploader.qualifyPath(options.url);
			this.base.remote('fileSetOptions', this.id, options);
			this.options = $merge(this.options, options);
		}
		return this;
	},

	start: function() {
		this.base.fileStart(this);
		return this;
	},

	stop: function() {
		this.base.fileStop(this);
		return this;
	},

	remove: function() {
		this.base.fileRemove(this);
		return this;
	},

	requeue: function() {
		this.base.fileRequeue(this);
	} 

});

/*
Script: SexyAlertBox.js
  http://www.coders.me/web-js-html/javascript/sexy-alert-box
  
Version:
  1.1

Author: 
  Eduardo D. Sada 
  http://www.coders.me

License:
	MIT license.

Based in <PBBAcpBox> (Pokemon_JOJO, <http://www.mibhouse.org/pokemon_jojo>)

Features:
  Mootools 1.2 100% Compatible
  Chain Implemented (Cola de mensajes)
  More styles (info, error, alert, prompt, confirm)

*/

/*
Class: SexyAlertBox
	Clone class of original javascript function : 'alert', 'confirm' and 'prompt'

Arguments:
	options - see Options below

Options:
	name - name of the box for use different style
	zIndex - integer, zindex of the box
	onReturn - return value when box is closed. defaults to false
	onReturnFunction - a function to fire when return box value
	BoxStyles - stylesheets of the box
	OverlayStyles - stylesheets of overlay
	showDuration - duration of the box transition when showing (defaults to 200 ms)
	showEffect - transitions, to be used when showing
	closeDuration - Duration of the box transition when closing (defaults to 100 ms)
	closeEffect - transitions, to be used when closing
	onShowStart - a function to fire when box start to showing
	onCloseStart - a function to fire when box start to closing
	onShowComplete - a function to fire when box done showing
	onCloseComplete - a function to fire when box done closing
*/

var SexyAlertBox = new Class({
  Implements: [Chain, Options],

	getOptions: function(){
		return {
			name: 'SexyAlertBox',
			zIndex: 65555,
			onReturn: false,
			onReturnFunction : $empty,
			BoxStyles: {
				'width': 500
			},
			OverlayStyles: {
				'background-color': '#000',
				'opacity': 0.7
			},
			showDuration: 200,
			showEffect: Fx.Transitions.linear,
      closeDuration: 100,
			closeEffect: Fx.Transitions.linear,
			moveDuration: 500,
			moveEffect: Fx.Transitions.Back.easeOut,
			onShowStart : $empty,
			onShowComplete : $empty,
			onCloseStart : $empty,
			onCloseComplete : function(properties) {
				this.options.onReturnFunction(this.options.onReturn);
			}.bind(this)
		};
	},

	initialize: function(options){
    this.i=0;
    
		this.setOptions(this.getOptions(), options);

		this.Overlay = new Element('div', {
			'id': 'BoxOverlay',
			'styles': {
				'display': 'none',
				'z-index': this.options.zIndex,
				'position': 'absolute',
				'top': '0',
				'left': '0',
				'background-color': this.options.OverlayStyles['background-color'],
				'opacity': 0,
				'height': window.getScrollHeight() + 'px',
				'width': window.getScrollWidth() + 'px'
			}
		});

		this.Content = new Element('div', {
			'id': this.options.name + '-BoxContenedor'
		});

    this.Contenedor = new Element('div', {
      'id': this.options.name + '-BoxContent'
    }).adopt(this.Content);

		this.InBox = new Element('div', {
			'id': this.options.name + '-InBox'
		}).adopt(this.Contenedor);;
		
		this.Box = new Element('div', {
			'id': this.options.name + '-Box',
			'styles': {
				'display': 'none',
				'z-index': this.options.zIndex + 2,
				'position': 'absolute',
				'top': '0',
				'left': '0',
				'width': this.options.BoxStyles['width'] + 'px'
			}
		}).adopt(this.InBox);

    this.Overlay.injectInside(document.body);
    this.Box.injectInside(document.body);

    this.preloadImages();
    
		window.addEvent('resize', function() {
			if(this.options.display == 1) {
				this.Overlay.setStyles({
					'height': window.getScrollHeight() + 'px',
					'width': window.getScrollWidth() + 'px'
				});
				this.replaceBox();
			}
		}.bind(this));
		
		window.addEvent('scroll', this.replaceBox.bind(this));
	},

  preloadImages: function() {
    var img = new Array(2);
    img[0] = new Image();img[1] = new Image();img[2] = new Image();
    img[0].src = this.Box.getStyle('background-image').replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1");
    img[1].src = this.InBox.getStyle('background-image').replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1");
    img[2].src = this.Contenedor.getStyle('background-image').replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1");
  },


	/*
	Property: display
		Show or close box
		
	Argument:
		option - integer, 1 to Show box and 0 to close box (with a transition).
	*/	
	display: function(option){
		if(this.Transition)
			this.Transition.cancel();				

		// Show Box	
		if(this.options.display == 0 && option != 0 || option == 1) {

      if(Browser.Engine.trident4)
        $$('select', 'object', 'embed').each(function(node){ node.style.visibility = 'hidden' });

			this.Overlay.setStyle('display', 'block');
			this.options.display = 1;
			this.fireEvent('onShowStart', [this.Overlay]);

			this.Transition = new Fx.Tween(this.Overlay,
				{
          property: 'opacity',
					duration: this.options.showDuration,
					transition: this.options.showEffect,
					onComplete: function() {

						sizes = window.getSize();
						scrollito = window.getScroll();
						this.Box.setStyles({
							'display': 'block',
							'left': (scrollito.x + (sizes.x - this.options.BoxStyles['width']) / 2).toInt()
						});

						this.replaceBox();
						this.fireEvent('onShowComplete', [this.Overlay]);

					}.bind(this)
				}
			).start(this.options.OverlayStyles['opacity']);

		}
		// Close Box
		else {

      if(Browser.Engine.trident4)
        $$('select', 'object', 'embed').each(function(node){ node.style.visibility = 'visible' });

      this.queue.delay(500,this);

			this.Box.setStyles({
				'display': 'none',
				'top': 0
			});
			this.Content.empty();
			this.options.display = 0;

			this.fireEvent('onCloseStart', [this.Overlay]);

      if(this.i==1) {
        this.Transition = new Fx.Tween(this.Overlay,
          {
            property: 'opacity',
            duration: this.options.closeDuration,
            transition: this.options.closeEffect,
            onComplete: function() {
                this.fireEvent('onCloseComplete', [this.Overlay]);
            }.bind(this)
          }
        ).start(0);
      }

		}			
	},

	/*
	Property: replaceBox
		Move Box in screen center when brower is resize or scroll
	*/
	replaceBox: function() {
		if(this.options.display == 1) {
			sizes = window.getSize();
      scrollito = window.getScroll();

			if(this.MoveBox)
				this.MoveBox.cancel();
			
			this.MoveBox = new Fx.Morph(this.Box, {
				duration: this.options.moveDuration,
				transition: this.options.moveEffect
			}).start({

				'left': (scrollito.x + (sizes.x - this.options.BoxStyles['width']) / 2).toInt(),
				'top': (scrollito.y + (sizes.y - this.Box.offsetHeight) / 2).toInt()

			});

		}
	},


	queue: function() {
		this.i--;
		this.callChain();
	},


	/*
	Property: messageBox
		Core system for show all type of box
		
	Argument:
		type - string, 'alert' or 'confirm' or 'prompt'
		message - text to show in the box
		properties - see Options below
		input - text value of default 'input' when prompt
		
	Options:
		textBoxBtnOk - text value of 'Ok' button
		textBoxBtnCancel - text value of 'Cancel' button
		onComplete - a function to fire when return box value
	*/	
	messageBox: function(type, message, properties, input) {

		this.chain(function () {

      properties = $extend({
        'textBoxBtnOk': 'OK',
        'textBoxBtnCancel': 'Cancelar',
        'textBoxInputPrompt': null,
        'password': false,
        'onComplete': $empty
      }, properties || {});


      this.options.onReturnFunction = properties.onComplete;

      this.ContenedorBotones = new Element('div', {
        'id': this.options.name + '-Buttons'
      });
      


      if(type == 'alert' || type == 'info' || type == 'error')
      {
          this.AlertBtnOk = new Element('input', {
            'id': 'BoxAlertBtnOk',
            'type': 'submit',
            'value': properties.textBoxBtnOk,
            'styles': {
              'width': '70px'
            }
          });
          
          this.AlertBtnOk.addEvent('click', function() {
            this.options.onReturn = true;
            this.display(0);
          }.bind(this));
        
          if(type == 'alert')
            this.clase = 'BoxAlert';
          else if(type == 'error')
            this.clase = 'BoxError';
          else if(type == 'info')
            this.clase = 'BoxInfo';
        
          this.Content.setProperty('class',this.clase).set('html',message);

          this.AlertBtnOk.injectInside(this.ContenedorBotones);

          this.ContenedorBotones.injectInside(this.Content);
          this.display(1);
      }
      else if(type == 'confirm')
      {
          this.ConfirmBtnOk = new Element('input', {
            'id': 'BoxConfirmBtnOk',
            'type': 'submit',
            'value': properties.textBoxBtnOk,
            'styles': {
              'width': '70px'
            }
          });

          this.ConfirmBtnCancel = new Element('input', {
            'id': 'BoxConfirmBtnCancel',
            'type': 'submit',
            'value': properties.textBoxBtnCancel,
            'styles': {
              'width': '70px'
            }
          });

          this.ConfirmBtnOk.addEvent('click', function() {
            this.options.onReturn = true;
            this.display(0);
          }.bind(this));

          this.ConfirmBtnCancel.addEvent('click', function() {
            this.options.onReturn = false;
            this.display(0);
          }.bind(this));

          this.Content.setProperty('class','BoxConfirm').set('html',message);

          this.ConfirmBtnOk.injectInside(this.ContenedorBotones);
          this.ConfirmBtnCancel.injectInside(this.ContenedorBotones);
          
          this.ContenedorBotones.injectInside(this.Content);
          this.display(1);
      }
      else if(type == 'prompt')
      {
          this.PromptBtnOk = new Element('input', {
            'id': 'BoxPromptBtnOk',
            'type': 'submit',
            'value': properties.textBoxBtnOk,
            'styles': {
              'width': '70px'
            }
          });

          this.PromptBtnCancel = new Element('input', {
            'id': 'BoxPromptBtnCancel',
            'type': 'submit',
            'value': properties.textBoxBtnCancel,
            'styles': {
              'width': '70px'
            }
          });
          
          type = properties.password ? 'password' : 'text';
          this.PromptInput = new Element('input', {
            'id': 'BoxPromptInput',
            'type': type,
            'value': input,
            'styles': {
              'width': '250px'
            }
          });

          this.PromptBtnOk.addEvent('click', function() {
            this.options.onReturn = this.PromptInput.value;
            this.display(0);
          }.bind(this));

          this.PromptBtnCancel.addEvent('click', function() {
            this.options.onReturn = false;
            this.display(0);
          }.bind(this));

          this.Content.setProperty('class','BoxPrompt').set('html',message + '<br />');
          this.PromptInput.injectInside(this.Content);
          new Element('br').injectInside(this.Content);
          this.PromptBtnOk.injectInside(this.ContenedorBotones);
          this.PromptBtnCancel.injectInside(this.ContenedorBotones);


          this.ContenedorBotones.injectInside(this.Content);

          this.display(1);
      }
      else
      {
          this.options.onReturn = false;
          this.display(0);		
      }

    });

		this.i++;

		if(this.i==1) this.callChain();

	},

	/*
	Property: alert
		Shortcut for alert
		
	Argument:
		properties - see Options in messageBox
	*/		
	alert: function(message, properties){
		this.messageBox('alert', message, properties);
	},

	/*
	Property: info
		Shortcut for alert info
		
	Argument:
		properties - see Options in messageBox
	*/		
	info: function(message, properties){
		this.messageBox('info', message, properties);
	},

	/*
	Property: error
		Shortcut for alert error
		
	Argument:
		properties - see Options in messageBox
	*/		
	error: function(message, properties){
		this.messageBox('error', message, properties);
	},

	/*
	Property: confirm
		Shortcut for confirm
		
	Argument:
		properties - see Options in messageBox
	*/
	confirm: function(message, properties){
		this.messageBox('confirm', message, properties);
	},

	/*
	Property: prompt
		Shortcut for prompt
		
	Argument:
		properties - see Options in messageBox
	*/	
	prompt: function(message, input, properties){
		this.messageBox('prompt', message, properties, input);
	}
});

SexyAlertBox.implement(new Events, new Options);
var CulturalNotes_Calendar = new Class({
  
  mContainer: null,
  mPreviousButton: null,
  mNextButton: null,
  mLabel: null,
  mList: null,
  mDate: null,
  mCityID: null,
  
  mIndex: 0,
  
  initialize: function(pContainer, pDate, pCityID){
    this.mContainer = pContainer;
    
    this.mPreviousButton = this.mContainer.getElement('a.button-previous');
    this.mNextButton = this.mContainer.getElement('a.button-next');
    this.mList = this.mContainer.getElement('ol');
    this.mLabel = this.mContainer.getElement('h5');
    this.mDate = pDate;
    this.mCityID = pCityID;
    
    this.registerEvents();
  },
  
  registerEvents: function(){
    
    this.mPreviousButton.addEvent('click', this.goPrevious.bind(this));
    this.mNextButton.addEvent('click', this.goNext.bind(this));
    
  },
  
  goPrevious: function(pE){
    pE.stop();
    this.mIndex--;
    this.loadCalendar();
  },
  
  goNext: function(pE){
    pE.stop();
    this.mIndex++;
    this.loadCalendar();
  },
  
  loadCalendar: function(){
    
    this.mList.wait({opacity: 1, image: '/p/calendar-load.gif'});
    
    if(null == this.mCityID){
      this.mCityID = '';
    }
    
    DejaxRequest.newRequest({
      url: '/ajax/CulturalNotes/GetCalendar/?id_city='+this.mCityID,
      mode: 'cancel',
      data: {'index': this.mIndex, 'date': this.mDate, 'id_city': this.mCityID},
      onComplete: this.loadCalendarHelper.bind(this)
    });
    
  },
  
  loadCalendarHelper: function(pData){
    this.mList.set('html', pData.html);
    this.mLabel.set('html', pData.month);
    this.loadCities(pData.cities);
    this.mList.release();
  },
  
  loadCities: function(pData){
    var dd = rmfDD.get('culturalNotes_cities');
    dd.setList(pData);
    dd.addEvent('select', this.setCity.bind(this));
  },
  
  setCity: function(pEl){
    this.mCityID = pEl.getProperty('rel');
    this.loadCalendar();
  }
  
});
var rmfDropDownItem = new Class({
  
  'Implements': Events,
  
  'mElement': null,
  'mDropDownElement': null,
  
  'initialize': function(pEl){
    
    this.mElement = pEl;
    this.mDropDownElement = document.getElement('*[rel='+pEl.getProperty('id')+']');
    
    this.resetPosition();
    this.registerEvents();
    
  },
  
  'resetPosition': function(){
    
    this.mDropDownElement.injectInside(document.body);
    this.mDropDownElement.setStyle('display', 'block');
    var diff = ( this.mDropDownElement.getSize().x - this.mElement.getSize().x );
    this.mDropDownElement.setStyle('display', 'none');
    
    this.mDropDownElement.setStyles({
      'left': this.mElement.getPosition().x - diff,
      'top': this.mElement.getPosition().y + 19,
      'opacity': 0
    });
    
  },
  
  'registerEvents': function(){
    
    this.mElement.addEvents({
      'click': this.show.bind(this)
    });
    
    document.body.addEvent('click', this.hide.bind(this));
    
  },
  
  'show': function(pE){
    pE.stop();
    this.mDropDownElement.setStyle('display', 'block').fade(1);
  },
  
  'hide': function(pE){
    
    var close = true;
    
    if(pE){
      
      for(curEl = pE.target; curEl.getParent && curEl.getParent() && curEl != document.body; curEl = curEl.getParent()){
        
        if(curEl == this.mDropDownElement){
          close = false;
          break;
        }
      }
      
    }
    
    if(true == close){
      this.mDropDownElement.fade(0);
    }
    
  },
  
  'getName': function(){
    return this.mElement.getProperty('id');
  },
  
  'setList': function(pData){
    this.removeEvents('select');
    this.mDropDownElement.set('html', '');
    
    var max = 0;
    for(i in pData){
      max++;
    }
    
    if(max == 0){
      return;
    }
    
    var mid = Math.ceil(max / 2);
    var olEl = new Element('ol', {'class': 'vertical'}).injectInside(this.mDropDownElement);
    var lastEl = null;
    
    var index = 0;
    for(i in pData){
      if(index == mid){
        lastEl.addClass('last');
        olEl = new Element('ol', {'class': 'vertical'}).injectInside(this.mDropDownElement);
      }
      lastEl = (new Element('li')).set('html', '<a rel="'+i+'">'+pData[i]+'</a>').injectInside(olEl);
      index++;
    }
    
    lastEl.addClass('last');
    
    this.registerItems();
    this.resetPosition();
    
  },
  
  'registerItems': function(){
    this.mDropDownElement.getElements('a').each(function(pEl){
      pEl.removeEvents('click');
      pEl.addEvent('click', this.selectItem.bindWithEvent(this, [pEl]));
    }.bind(this));
  },
  
  'selectItem': function(pE, pEl){
    this.setLabel(pEl.get('text'));
    this.fireEvent('select', [pEl]);
    this.hide();
  },
  
  'setLabel': function(pLabel){
    
    var tmpEl = new Element('span', {
      'styles': {
        'float': 'left',
        'font': '10px "Trebuchet MS",Tahoma,Verdana'
      }
    }).injectInside(document.body);
    
    tmpEl.set('text', pLabel);
    var tmpWidth = tmpEl.getSize().x + 25;
    tmpEl.destroy();
    
    this.mElement.getElement('span').set('html', '<img src="/p/btn-arr-blt.png" />'+pLabel);
    this.mElement.setStyle('width', tmpWidth+20);
    this.resetPosition();
    
  }
  
});

var rmfDropDown = new Class({
  
  'mDropDowns': {},
  
  'initialize': function(){
    this.registerItems();
  },
  
  'registerItems': function(){
    document.getElements('.dropdown').each(this.registerItem.bind(this));
  },
  
  'registerItem': function(pEl){
    var dd = new rmfDropDownItem(pEl);
    this.mDropDowns[dd.getName()] = dd;
  },
  
  'get': function(pName){
    return this.mDropDowns[pName];
  }
  
});

var rmfDD;

window.addEvent('domready', function(){
  rmfDD = new rmfDropDown();
});
var PeopleBox = new Class({
  
  'mContainer': null,
  'mElements': [],
  'mIndex': 0,
  
  'initialize': function(pContainer){
    this.mContainer = pContainer;
    this.loadItems();
    this.startLoop();
  },
  
  'loadItems': function(){
    this.mContainer.getElements('div').each(this.registerItem.bind(this));
    this.updateZIndexes();
    this.resetPointer();
  },
  
  'registerItem': function(pEl){
    this.mElements.include(pEl);
  },
  
  'updateZIndexes': function(){
    for(i = this.mElements.length; i > 0; i--){
      this.mElements[i - 1].setStyle('z-index', this.mElements.length - i + 1);
    }
  },
  
  'resetPointer': function(){
    this.mIndex = 0;
    this.mElements[this.mIndex].setStyles({'opacity': 1, 'display': 'block'});
  },
  
  'startLoop': function(){
    if(this.mElements.length <= 1){
      return false;
    }
    this.iterate.delay(5000, this);
  },
  
  'iterate': function(){
    
    var currentElement = this.mElements[this.mIndex];
    var nextElement = this.mElements[this.getNextIndex()];
    
    currentElement.set('tween', {
      'duration': 'long',
      'complete': function(){ currentElement.setStyle('display', 'none'); }
    });
    
    nextElement.set('tween', {
      'duration': 'long',
      'complete': function(){}
    });
    
    currentElement.fade(0);
    nextElement.setStyles({'display': 'block', 'opacity': 0});
    nextElement.fade(1);
    
    this.mIndex = this.getNextIndex();
    this.startLoop();
    
  },
  
  'getNextIndex': function(){
    if(this.mIndex < this.mElements.length - 1){
      return this.mIndex + 1;
    }
    return 0;
  }
  
});
var RankingSwitcher = new Class({
  
  mContainer: null,
  mContent: null,
  mActive: null,
  mActiveElement: null,
  mSkipLinkIds: ['ranking_more'],
  
  'initialize': function(pContainer, pContent){
    this.mContainer = pContainer;
    this.mContent = pContent;
    this.registerDefault();
    this.registerLinks();
  },
  
  'registerDefault': function(){
    var activeElement = this.mContainer.getElement('a.active');
    this.mActive = activeElement.getProperty('rel');
    this.mActiveElement = activeElement;
  },
  
  'registerLinks': function(){
    this.mContainer.getElements('a').each(function(pEl){
      pEl.addEvent('click', this.loadContent.bind(this, [pEl]));
    }.bind(this));
    this.registerVoteLinks();
  },
  
  'registerVoteLinks': function(){
    this.mContent.getElements('a.button').each(function(pEl){
      var do_register = true;
      for ( var i = 0; i < this.mSkipLinkIds.length; i++ ){
        // pomijamy elemenety, które znajdują się na liście do pominięcia:
        if (this.mSkipLinkIds[i] == pEl.id) {
          do_register = false;
          break;
        }
      }
      if ( do_register ) {
        pEl.addEvent('click', this.vote.bindWithEvent(this, [pEl]));
      }
    }.bind(this));
  },
  
  'loadContent': function(pEl){
    var type = pEl.getProperty('rel');
    
    if(this.mActive == type){
      return;
    }
    
    this.mActiveElement.removeClass('active');
    pEl.addClass('active');
    this.mActive = type;
    this.mActiveElement = pEl;
    
    this.getList(type);
    
  },
  
  'getList': function(pType){
    
    this.mContent.wait({opacity: 1, image: '/p/calendar-load.gif'});
    
    DejaxRequest.newRequest({
      url: '/ajax/Ranking/GetList/',
      mode: 'cancel',
      data: {'type': pType},
      onComplete: this.getListHelper.bind(this)
    });
    
  },
  
  'getListHelper': function(pData){
    
    this.mContent.set('html', pData);
    this.registerVoteLinks();
    this.mContent.release();
    
  },
  
  'vote': function(pE, pEl){
    pE.stop();
    
    this.mContent.wait({opacity: 1, image: '/p/calendar-load.gif'});
    
    DejaxRequest.newRequest({
      url: pEl.getProperty('href'),
      mode: 'cancel',
      onComplete: this.handleVote.bind(this)
    });
    
  },
  
  'handleVote': function(pData){
    console.log(pData);
    this.getList(this.mActive);
  }
  
});
var FacebookConnect = new Class({
  
  mMissingPermissionsStr: '',
  mLinkAccountPageUrl: '',
  mSexyAlert: null,
  
  initialize: function(pMissingPermissionsStr, pLinkAccountPageUrl) {
    this.mMissingPermissionsStr = pMissingPermissionsStr;
    this.mLinkAccountPageUrl = pLinkAccountPageUrl;
    this.mSexyAlert = new SexyAlertBox();
  },
  
  // Actions:
  
  moveToLinkAccountPage: function(){
    window.location.href = this.mLinkAccountPageUrl;
  },
  
  requestMissingPermissions: function(){
    if (this.mMissingPermissionsStr.length > 0) {
      FB_RequireFeatures(["Connect"], function() {
        FB.Connect.showPermissionDialog(
            this.mMissingPermissionsStr,
            this.onPermissionDialogComplete.bind(this)
        );
      }.bind(this));
    }
  },
  
  logout: function() {
    FB_RequireFeatures(["Connect"], function() {
      FB.Connect.logoutAndRedirect('/');
    });
  },
  
  // Event handlers:
  
  onPermissionsMissing: function() {
    
    var ask_str = 'Nie możesz zalogować się przez Facebooka o ile '
      + 'nie zezwolisz na dodatkowe ustawienia. Ponowić próbę?';

    this.mSexyAlert.confirm(ask_str, {
      'textBoxBtnCancel': 'Anuluj',
      'onComplete': function(pResp) { 
        
        if(pResp) {
          this.requestMissingPermissions();
        }
        else {
          FB.Connect.logoutAndRedirect('/');
        }
      }.bind(this)
    });
  },

  onLoginComplete: function(pSkipMissingPermissionsRequest) {
    if (this.mMissingPermissionsStr.length > 0 && !pSkipMissingPermissionsRequest) {
      this.requestMissingPermissions();
    }
    else {
      this.moveToLinkAccountPage();
    }
  },
  
  onPermissionDialogComplete: function(permissions){
    this.moveToLinkAccountPage();
  }
  
});

var Bookmarks = new Class({
  
  mContainer: null,
  mListEl: null,
  mUser: null,
  
  initialize: function(pContainer, pUser){
    
    this.mContainer = pContainer;
    this.mListEl = this.mContainer.getElement('ul');
    this.mUser = pUser;
    
    this.mContainer.getElement('.filter').getElements('a').each(function(pEl){
      pEl.addEvent('click', this.loadTab.bind(this, [ pEl.getProperty('rel') ]));
    }.bind(this));
    
  },
  
  loadTab: function(pType, pForce){
    
    var oldEl = this.mContainer.getElement('.filter a.active');
    var newEl = this.mContainer.getElement('.filter a[rel='+pType+']');
    
    if(oldEl == newEl && !pForce){
      return;
    }
    
    if(oldEl){
      oldEl.removeClass('active');
    }
    
    if(newEl){
      newEl.addClass('active');
    }
    
    this.mListEl.wait({opacity: 1, image: '/p/profile-load.gif'});
    
    DejaxRequest.newRequest({
      url: '/ajax/UsersProfiles/GetBookmarks/',
      mode: 'cancel',
      data: { type: pType, user: this.mUser },
      onComplete: function(pData){
        
        this.mListEl.set('html', pData.html);
        this.mListEl.release();
        
        this.mListEl.getElements('a.icon').each(function(pEl){
          pEl.addEvent('click', function(pE, pEl){
            
            this.mListEl.wait({opacity: 1, image: '/p/profile-load.gif'});
            pE.stop();
            
            new Request({
              url: pEl.getProperty('href'),
              link: 'chain',
              onComplete: function(){
                this.loadTab(pType, true);
              }.bind(this)
            }).send();
            
          }.bindWithEvent(this, [ pEl ]))
        }.bind(this));
        
      }.bind(this)
    });
    
  }
  
});
var SettingsLayer = new Class({
  
  mModalBox: null,

  mSettingsBox: null,
  
  mDefaultInner: null,
  
  mDelay: null,

  initialize: function(){
    this.mModalBox = new Element('div', {'class': 'WindowModalLoading'}).injectInside(document.body);
    this.mSettingsBox = new Element('div', {'class': 'WindowContentLoading', 'id': 'SettingsBox'}).injectInside(document.body);
    this.mSettingsBox.set('html', '<div style="padding:15px">Proszę czekać - trwa ładowanie treści <div id="IndicatorLoading"></div></div>');
    
    this.mMoveFx = new Fx.Tween(this.mSettingsBox, {property: 'opacity', duration:700,transition:Fx.Transitions.quadInOut}),
    
    window.addEvent('resize', function(){
      this.move();
      this.resizeModal();
    }.bind(this));
    window.addEvent('scroll', this.move.bind(this));
    this.resizeModal();
  },
  
  hide: function(){
    this.mModalBox.setStyle('display', 'none');
    this.mSettingsBox.setStyle('display', 'none');
    this.mSettingsBox.empty();
    this.mSettingsBox.set('html', this.mDefaultInner);
  },

  show: function(pURL){
    $clear(this.mDelay);
    this.mModalBox.setStyle('display', 'block');
    this.mSettingsBox.setStyle('display', 'block');
    this.mModalBox.setStyle('opacity', '0.25');
    
    this.mSettingsBox.setStyles(this.calculatePosition());
    this.resizeModal();
    this.getContent(pURL);
  },

  move: function(pNoEffect){
    if('none' == this.mModalBox.getStyle('display')){
      return true;
    }
    if (pNoEffect == true) {
      this.mSettingsBox.setStyles(this.calculatePosition());
    }
    else {
      this.mMoveFx.start(this.calculatePosition());    
    }
  },
  
  calculatePosition: function() {    
		if (window.getHeight() < this.mSettingsBox.offsetHeight - 100){
			return {
    		'left': Math.round((window.getWidth() - this.mSettingsBox.offsetWidth) / 2),
    		'top': Math.round(50)
			}
		}
  	return {
      'left': Math.round((window.getWidth() - this.mSettingsBox.offsetWidth) / 2),
      'top': Math.round(((window.getHeight() - this.mSettingsBox.offsetHeight) / 2) + window.getScrollTop())
    }
  },

  resizeModal: function(){
    if(this.mModalBox && 'none' == this.mModalBox.getStyle('display')){
      return true;
    }
    
    this.mModalBox.setStyles({
      'height': window.getScrollHeight()
    });
    return true;  
  },
  
  getContent: function(pURL){
    DejaxRequest.newRequest({
      url: pURL,
      onComplete: this.updateBox.bind(this),
      eval: true,
      evalData: ['html']
    });
  },
  
  updateBox: function(pData){
    this.mDefaultInner = this.mSettingsBox.get('html');
    this.mSettingsBox.set('html', pData.html);
    this.move(true);
    this.resizeModal();
  }
  
});
var SettingsLayerBox;
window.addEvent('domready', function(){
  SettingsLayerBox = new SettingsLayer();
});

/**
 * Zwykle podczepienie SettingsLayer pod linki z odpowiednia klasa 
 */

window.addEvent('domready', function() {
  document.getElements('.SettingsLayer').each(function(pEl) {
   
    pEl.addEvent('click', function(pE) {
      pE.stop();
      var url = pEl.getProperty('href');
     
      SettingsLayerBox.show(url);
      
    }.bindWithEvent(this));
    
  });
});
var HiddenUploader = new Class({
  
  'mEventsEl': null,
  'mFileboxEl': null,
  'mCustomButtonEl': null,
  'mStrictEl': null,
  'mEventAttached': false,
  'mEventsElementPosition': null,
  'mEventsElementSize': null,
  'mBrowserButtonPosition': 130,
  
  'mStart': null,
  
  'initialize': function(pEventsEl, pFileboxEl, pCustomButtonEl, pStrictEl){
    
    this.mEventAttached = false;
    
    if(!pStrictEl){
      pStrictEl = false;
    }
    
    this.mEventsEl = pEventsEl;
    this.mFileboxEl = pFileboxEl;
    this.mCustomButtonEl = pCustomButtonEl;
    this.mStrictEl = pStrictEl;
    
    this.mEventsElementPosition = pEventsEl.getPosition();
    this.mEventsElementSize = pEventsEl.getSize();
    
    this.setBrowserDifferencePosition();
    this.allocateElements();
    
    this.registerEvents();
    
  },
  
  'setBrowserDifferencePosition': function(){
  	if(window.gecko){
  	  this.mBrowserButtonPosition = 180;
  	}
  	else if(window.opera){
  	  this.mBrowserButtonPosition = 150;
  	}
  },
  
  'allocateElements': function(){
    
  	if(false == this.mStrictEl)
  	  this.mEventsEl.getParent().getParent().adopt(this.mFileboxEl);
  	else
  	  this.mEventsEl.adopt(this.mFileboxEl);
    
    this.mFileboxEl.setStyles({
      'opacity': 0.01,
      'left': -400,
      'position': 'absolute'
    });    
   
    this.mFileboxEl.addClass('curDefault');
     
  },
  
  'attachEvent': function(){
    
  	this.mStart = this.mEventsEl.getPosition();
  	
    this.mEventsElementPosition = this.mEventsEl.getPosition();
    this.mEventsElementSize = this.mEventsEl.getSize();  	
    
  	if (false === this.mEventAttached) {
	  	this.mEventsEl.addEvent('mousemove', this.followButton.bindWithEvent(this));
			this.mEventAttached = true;
  	}
  	
  	this.mFileboxEl.setStyle('display', '');
  	
  },
  
  'detachEvent': function(){
    
    this.mEventsEl.removeEvents('mousemove');
    this.mEventsAttached = false;
    this.mFileboxEl.setStyles({
      'display': 'none',
      'left': -400
    });
    
  },
  
  'followButton': function(pE){
    
    if(!this.mStart){
      this.mStart = {'x': 0, 'y': 0};
    }
    
		this.mFileboxEl.setStyles({
      'left': pE.page.x - this.mStart.x - this.mBrowserButtonPosition,
      'top': pE.page.y - this.mStart.y - 13
    });
    
    this.checkPosition(pE);
    
  },
  
  'checkPosition': function(pE) {
    
  	if (pE.page.x < this.mEventsElementPosition.x || pE.page.x > (this.mEventsElementPosition.x + this.mEventsElementSize.x) || pE.page.y < this.mEventsElementPosition.y || pE.page.y > (this.mEventsElementPosition.y + this.mEventsElementSize.y)){
  		this.detachEvent();
  	}
  	
  },
  
  'registerEvents': function(){
    
    this.mEventsEl.addEvent('mousemove', this.followButton.bindWithEvent(this));
    this.mFileboxEl.addEvent('mousemove', this.followButton.bindWithEvent(this));

    this.mEventsEl.addEvent('mouseenter', function(){
      this.attachEvent();
    }.bind(this));

    this.mFileboxEl.addEvent('mousedown', function(pE){
      this.mCustomButtonEl.fireEvent('mousedown', pE);
    }.bindWithEvent(this));
    
    document.addEvent('mouseup', function(pE){
      this.mCustomButtonEl.fireEvent('mouseup', pE);
    }.bindWithEvent(this));
    
  }
  
});
var FileUploader = new Class({
  
  'mID': null,
  'mContainerEl': null,
  'mFormEl': null,
  'mInputHTMLEl': null,
  'mUploadMaxSize': null,
  'mErrorFX': null,
  'mDmsUrl': null,
  'mDejac': null,
  
  'initialize': function(pID, pContainerEl, pUploadMaxSize, pDmsURL){
    
    this.mID = pID;
    this.mContainerEl = pContainerEl;
    this.mUploadMaxSize = pUploadMaxSize;
    this.mDmsUrl = pDmsURL;
    this.mDejax = new Dejax();
    
    window.initializedFileUploader = this;
    
    if(!this.mContainerEl || 'function' != $type(this.mContainerEl.set)){
      console.log('FileUploader can\'t start because container is not valid.');
      return;
    }
    
    this.mContainerEl.set('html', '<div class="FileUploader_Container"><div class="ErrorBox"><span></span></div><div class="FileUploader_FilePreview"></div><div class="FileUploader_FileList"><ul></ul></div><iframe name="FileUploader_HiddenUpload" src="/mod/FileUploader/Upload/?id='+this.mID+'"></iframe></div>');
    
    var divEl = new Element('div').injectInside(document.body);
    divEl.set('html', '<form id="FileUploader_temporaryHiddenUploadForm" action="/mod/FileUploader/Upload/" method="post" enctype="multipart/form-data" target="FileUploader_HiddenUpload"></form>');
    
    this.mFormEl = $('FileUploader_temporaryHiddenUploadForm');
    
    this.mContainerEl.getElement('.ErrorBox').setStyle('opacity', 0);
    this.mContainerEl.getElement('.FileUploader_Container').addEvent('mousedown', this.hideError.bind(this));
    
    this.mErrorFX = new Fx.Tween(this.mContainerEl.getElement('.ErrorBox'), {'property': 'opacity'})
    this.mFormEl.injectInside(document.body);
    
    this.mInputHTMLEl = new Element('input', {'type': 'file', 'name': 'upload_file'});
    this.mInputHTMLEl.injectInside(this.mFormEl);
    
    new Element('input', {'type': 'hidden', 'name': 'hash', 'value': this.mID}).injectInside(this.mFormEl);
    new Element('input', {'type': 'hidden', 'name': 'MAX_FILE_SIZE', 'value': this.mUploadMaxSize}).injectInside(this.mFormEl);
    
    new Element('input', {'type': 'hidden', 'name': '__file_uploader_id', 'value': this.mID}).injectInside(this.mContainerEl);
    
    new HiddenUploader(this.mContainerEl.getElement('.FileUploader_FilePreview'), this.mInputHTMLEl, this.mContainerEl.getElement('.FileUploader_FilePreview'));
    this.mInputHTMLEl.addEvent('change', function(){
      
      this.mInputHTMLEl.injectInside(this.mFormEl);
      this.mContainerEl.getElement('.FileUploader_FilePreview').addClass('Load');
      
      this.mFormEl.submit();
      
    }.bind(this));
    
    if(window.ie6){
      this.setJSSizes();
    }
    
  },
  
  'hideError': function(){
    this.mErrorFX.start(0);
  },
  
  'showError': function(pMessage){
    this.mContainerEl.getElement('.ErrorBox').setStyle('display', 'block');
    this.mContainerEl.getElement('.ErrorBox span').set('html', pMessage);
    
    this.mErrorFX.start(1);
  },
  
  'showStatus': function(pData){
    
    var ul_el = this.mContainerEl.getElement('.FileUploader_FileList ul');
    ul_el.set('html', '');
    var lastEl;
    var index = 0;
    
    for(i in pData.params.files){
      
      if('ignore' == i){
        continue;
      }
      
      pEl = pData.params.files[i];
      lastEl = new Element('li').injectInside(ul_el).set('html', '<span class="name">'+pEl.original_name+'</span> <span class="size">'+(parseInt(pEl.size)/1024).toFixed(2)+' KB</span><span class="remove"><img src="/cmp/FileUploader/delete.png"></strong></span>');
      lastEl.getElement('span.remove').addEvent('click', this.removeFileRequest.bindWithEvent(this, [ pEl ]));
      
      if(index++ % 2 != 0){
        lastEl.addClass('odd');
      }
      
      lastEl.addEvent('click', this.showDetails.bind(this, pEl));
    };
    
    if(lastEl){
      lastEl.fireEvent('click');
    }
    else{
      this.mContainerEl.getElement('.FileUploader_FilePreview').setProperty('style', '');
    }
  },
  
  'removeFileRequest': function(pE, pEl){
    pE.stop();
    this.mContainerEl.getElement('.FileUploader_FilePreview').addClass('Load');
    
    this.mDejax.newRequest({
      url: '/ajax/FileUploader/AbandonFile/',
      mode: 'free',
      data: {
        'input': this.mID, 
        'id_file': pEl.id_file
      },
      onComplete: this.handleResponse.bind(this)
    });
  },
  
  'handleResponse': function(pData){
    
    this.mContainerEl.getElement('.FileUploader_FilePreview').removeClass('Load');
    
    if(pData.error){
      this.showError(pData.message);
    }
    else{
      this.showStatus(pData);
    }
    
    this.mInputHTMLEl.injectInside(this.mContainerEl.getElement('.FileUploader_FilePreview'));
    
  },
  
  'showDetails': function(pEl){
    var container = this.mContainerEl.getElement('.FileUploader_FilePreview');
    
    if(pEl.type.match(/image/)){
      var url = this.mDmsUrl+'/ResizeImage/'+pEl.path+container.getSize().x+','+container.getSize().y+',85,2,maxwh:1_'+pEl.original_name;
      container.setStyle('background-image', 'url("'+url+'")');
      container.setStyle('background-repeat', 'no-repeat');
      container.setStyle('background-position', 'center center');
    }
    else{
      
    }
  },
  
  'setJSSizes': function(){
    var sizes = this.mContainerEl.getSize().size;
    document.getElement('div.FileUploader_Container').setStyles({
      'width': sizes.x,
      'height': sizes.y
    });
  }
  
});

