/*	Prototype JavaScript framework, version 1.4.0
 *	(c) 2005 Sam Stephenson <sam@conio.net>
 *
 *	Prototype is freely distributable under the terms of an MIT-style license.
 *	For details, see the Prototype web site: http://prototype.conio.net/
 *
/*--------------------------------------------------------------------------*/

var Prototype =
{
	Version: '1.4.0',
	ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',

	emptyFunction: function() {},
	K: function(x) {return x}
}

var Class =
{
	create: function()
	{
		return function()
		{
			this.initialize.apply(this, arguments);
		}
	}
}

var Abstract = new Object();

Object.extend = function(destination, source)
{
	for (property in source)
	{
		destination[property] = source[property];
	}
	return destination;
}

Function.prototype.bind = function()
{
	var __method = this, args = $A(arguments), object = args.shift();
	return function()
	{
		return __method.apply(object, args.concat($A(arguments)));
	}
}

Function.prototype.bindAsEventListener = function(object)
{
	var __method = this;
	return function(event)
	{
		return __method.call(object, event || window.event);
	}
}

var Try =
{
	these: function()
	{
		var returnValue;

		for (var i = 0; i < arguments.length; i++)
		{
			var lambda = arguments[i];
			try
			{
				returnValue = lambda();
				break;
			}
			catch (e)
			{}
		}

		return returnValue;
	}
}
function $()
{
	var elements = new Array();

	for (var i = 0; i < arguments.length; i++)
	{
		var element = arguments[i];
		if (typeof element == 'string')
			element = document.getElementById(element);

		if (arguments.length == 1)
			return element;

		elements.push(element);
	}

	return elements;
}
Object.extend(
	String.prototype,
	{
		stripTags: function()
		{
			return this.replace(/<\/?[^>]+>/gi, '');
		},
	
		stripScripts: function()
		{
			return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
		},
	
		extractScripts: function()
		{
			var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
			var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
			return (this.match(matchAll) || []).map(function(scriptTag)
			{
				return (scriptTag.match(matchOne) || ['', ''])[1];
			});
		},
	
		evalScripts: function()
		{
			return this.extractScripts().map(eval);
		},
	
		escapeHTML: function()
		{
			var div = document.createElement('div');
			var text = document.createTextNode(this);
			div.appendChild(text);
			return div.innerHTML;
		},
	
		unescapeHTML: function()
		{
			var div = document.createElement('div');
			div.innerHTML = this.stripTags();
			return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
		},
	
		toQueryParams: function()
		{
			var pairs = this.match(/^\??(.*)$/)[1].split('&');
			return pairs.inject({}, function(params, pairString)
			{
				var pair = pairString.split('=');
				params[pair[0]] = pair[1];
				return params;
			});
		},
	
		toArray: function()
		{
			return this.split('');
		},
	
		camelize: function()
		{
			var oStringList = this.split('-');
			if (oStringList.length == 1)
				return oStringList[0];
	
			var camelizedString = this.indexOf('-') == 0
				? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
				: oStringList[0];
	
			for (var i = 1, len = oStringList.length; i < len; i++)
			{
				var s = oStringList[i];
				camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
			}
	
			return camelizedString;
		},
	
		inspect: function()
		{
			return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
		}
	}
);

String.prototype.parseQuery = String.prototype.toQueryParams;
var $break		= new Object();
var $continue	= new Object();

var Enumerable =
{
	each: function(iterator)
	{
		var index = 0;
		try
		{
			this._each(
				function(value)
				{
					try
					{
						iterator(value, index++);
					}
					catch (e)
					{
						if (e != $continue)
							throw e;
					}
				}
			);
		}
		catch (e)
		{
			if (e != $break)
				throw e;
		}
	},

	all: function(iterator)
	{
		var result = true;
		this.each(
			function(value, index)
			{
				result = result && !!(iterator || Prototype.K)(value, index);
				if (!result) throw $break;
			}
		);
		return result;
	},

	any: function(iterator)
	{
		var result = true;
		this.each(
			function(value, index)
			{
				if (result = !!(iterator || Prototype.K)(value, index))
					throw $break;
			}
		);
		return result;
	},

	collect: function(iterator)
	{
		var results = [];
		this.each(
			function(value, index)
			{
				results.push(iterator(value, index));
			}
		);
		return results;
	},

	detect: function (iterator)
	{
		var result;
		this.each(
			function(value, index)
			{
				if (iterator(value, index))
				{
					result = value;
					throw $break;
				}
			}
		);
		return result;
	},

	findAll: function(iterator)
	{
		var results = [];
		this.each(
			function(value, index)
			{
				if (iterator(value, index))
					results.push(value);
			}
		);
		return results;
	},

	grep: function(pattern, iterator)
	{
		var results = [];
		this.each(
			function(value, index)
			{
				var stringValue = value.toString();
				if (stringValue.match(pattern))
					results.push((iterator || Prototype.K)(value, index));
			}
		)
		return results;
	},

	include: function(object)
	{
		var found = false;
		this.each(
			function(value)
			{
				if (value == object)
				{
					found = true;
					throw $break;
				}
			}
		);
		return found;
	},

	inject: function(memo, iterator)
	{
		this.each(
			function(value, index)
			{
				memo = iterator(memo, value, index);
			}
		);
		return memo;
	},

	invoke: function(method)
	{
		var args = $A(arguments).slice(1);
		return this.collect(
			function(value)
			{
				return value[method].apply(value, args);
			}
		);
	},

	max: function(iterator)
	{
		var result;
		this.each(
			function(value, index)
			{
				value = (iterator || Prototype.K)(value, index);
				if (value >= (result || value))
					result = value;
			}
		);
		return result;
	},

	min: function(iterator)
	{
		var result;
		this.each(
			function(value, index)
			{
				value = (iterator || Prototype.K)(value, index);
				if (value <= (result || value))
					result = value;
			}
		);
		return result;
	},

	partition: function(iterator)
	{
		var trues = [], falses = [];
		this.each(
			function(value, index)
			{
				((iterator || Prototype.K)(value, index) ? trues : falses).push(value);
			}
		);
		return [trues, falses];
	},

	pluck: function(property)
	{
		var results = [];
		this.each(
			function(value, index)
			{
				results.push(value[property]);
			}
		);
		return results;
	},

	reject: function(iterator)
	{
		var results = [];
		this.each(
			function(value, index)
			{
				if (!iterator(value, index))
					results.push(value);
			}
		);
		return results;
	},

	sortBy: function(iterator)
	{
		return this.collect(
			function(value, index)
			{
				return {value: value, criteria: iterator(value, index)};
			}
		).sort(
			function(left, right)
			{
				var a = left.criteria, b = right.criteria;
				return a < b ? -1 : a > b ? 1 : 0;
			}
		).pluck('value');
	},

	toArray: function()
	{
		return this.collect(Prototype.K);
	},

	zip: function()
	{
		var iterator = Prototype.K, args = $A(arguments);
		if (typeof args.last() == 'function')
			iterator = args.pop();

		var collections = [this].concat(args).map($A);
		return this.map
		(
			function(value, index)
			{
				iterator(value = collections.pluck(index));
				return value;
			}
		);
	},

	inspect: function()
	{
		return '#<Enumerable:' + this.toArray().inspect() + '>';
	}
}

Object.extend(Enumerable,
{
	map:		Enumerable.collect,
	find:		Enumerable.detect,
	select:		Enumerable.findAll,
	member:		Enumerable.include,
	entries:	Enumerable.toArray
});
var $A = Array.from = function(iterable)
{
	if (!iterable)
		return [];
	if (iterable.toArray)
	{
		return iterable.toArray();
	}
	else
	{
		var results = [];
		for (var i = 0; i < iterable.length; i++)
			results.push(iterable[i]);
		return results;
	}
}
Object.extend(Array.prototype,
{
	_each: function(iterator)
	{
		for (var i = 0; i < this.length; i++)
			iterator(this[i]);
	},

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

	first: function()
	{
		return this[0];
	},

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

	compact: function()
	{
		return this.select(
			function(value)
			{
				return value != undefined || value != null;
			}
		);
	},

	flatten: function()
	{
		return this.inject([],
			function(array, value)
			{
				return array.concat(value.constructor == Array ?
					value.flatten() : [value]);
			}
		);
	},

	without: function()
	{
		var values = $A(arguments);
		return this.select(
			function(value)
			{
				return !values.include(value);
			}
		);
	},

	indexOf: function(object)
	{
		for (var i = 0; i < this.length; i++)
			if (this[i] == object) return i;
		return -1;
	},

	reverse: function(inline)
	{
		return (inline !== false ? this : this.toArray())._reverse();
	},

	shift: function()
	{
		var result = this[0];
		for (var i = 0; i < this.length - 1; i++)
			this[i] = this[i + 1];
		this.length--;
		return result;
	},

	inspect: function()
	{
		return '[' + this.map(Object.inspect).join(', ') + ']';
	}
});

var Ajax =
{
	getTransport: function()
	{
		return Try.these
		(
			function()
			{
				return new ActiveXObject('Msxml2.XMLHTTP')
			},
			function()
			{
				return new ActiveXObject('Microsoft.XMLHTTP')
			},
			function()
			{
				return new XMLHttpRequest()
			}
		) || false;
	},

	activeRequestCount: 0
}

Ajax.Responders =
{
	responders: [],

	_each: function(iterator)
	{
		this.responders._each(iterator);
	},

	register: function(responderToAdd)
	{
		if (!this.include(responderToAdd))
			this.responders.push(responderToAdd);
	},

	unregister: function(responderToRemove)
	{
		this.responders = this.responders.without(responderToRemove);
	},

	dispatch: function(callback, request, transport, json)
	{
		this.each(
			function(responder)
			{
				if (responder[callback] && typeof responder[callback] == 'function')
				{
					try
					{
						responder[callback].apply(responder, [request, transport, json]);
					}
					catch (e)
					{}
				}
			}
		);
	}
};

Object.extend(Ajax.Responders, Enumerable);

Ajax.Responders.register(
{
	onCreate: function()
	{
		Ajax.activeRequestCount++;
	},

	onComplete: function()
	{
		Ajax.activeRequestCount--;
	}
});

Ajax.Base = function() {};
Ajax.Base.prototype =
{
	setOptions: function(options)
	{
		this.options =
		{
			method:			 'post',
			asynchronous: true,
			parameters:	 ''
		}
		Object.extend(this.options, options || {});
	},

	responseIsSuccess: function()
	{
		return this.transport.status == undefined || this.transport.status == 0 || (this.transport.status >= 200 && this.transport.status < 300);
	},

	responseIsFailure: function()
	{
		return !this.responseIsSuccess();
	}
}

Ajax.Request = Class.create();
Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

Ajax.Request.prototype = Object.extend(new Ajax.Base(),
{
	initialize: function(url, options)
	{
		this.transport = Ajax.getTransport();
		this.setOptions(options);
		this.request(url);
	},

	request: function(url)
	{
		var parameters = this.options.parameters || '';
		if (parameters.length > 0) parameters += '&_=';

		try
		{
			this.url = url;
			if (this.options.method == 'get' && parameters.length > 0)
				this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;

			Ajax.Responders.dispatch('onCreate', this, this.transport);

			this.transport.open(this.options.method, this.url, this.options.asynchronous);

			if (this.options.asynchronous)
			{
				this.transport.onreadystatechange = this.onStateChange.bind(this);
				setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
			}

			this.setRequestHeaders();

			var body = this.options.postBody ? this.options.postBody : parameters;
			this.transport.send(this.options.method == 'post' ? body : null);

		}
		catch (e)
		{
			this.dispatchException(e);
		}
	},

	setRequestHeaders: function()
	{
		var requestHeaders = ['X-Requested-With', 'XMLHttpRequest', 'X-Prototype-Version', Prototype.Version];

		if (this.options.method == 'post')
		{
			requestHeaders.push('Content-type', 'application/x-www-form-urlencoded');

			/* Force "Connection: close" for Mozilla browsers to work around
			 * a bug where XMLHttpReqeuest sends an incorrect Content-length
			 * header. See Mozilla Bugzilla #246651.
			 */
			if (this.transport.overrideMimeType)
				requestHeaders.push('Connection', 'close');
		}

		if (this.options.requestHeaders)
			requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);

		for (var i = 0; i < requestHeaders.length; i += 2)
			this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
	},

	onStateChange: function()
	{
		var readyState = this.transport.readyState;
		if (readyState != 1)
			this.respondToReadyState(this.transport.readyState);
	},

	header: function(name)
	{
		try
		{
			return this.transport.getResponseHeader(name);
		}
		catch (e)
		{}
	},

	evalJSON: function()
	{
		try
		{
			return eval(this.header('X-JSON'));
		}
		catch (e)
		{}
	},

	evalResponse: function()
	{
		try
		{
			return eval(this.transport.responseText);
		}
		catch (e)
		{
			this.dispatchException(e);
		}
	},

	respondToReadyState: function(readyState)
	{
		var event = Ajax.Request.Events[readyState];
		var transport = this.transport, json = this.evalJSON();

		if (event == 'Complete')
		{
			try
			{
				(this.options['on' + this.transport.status] || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] || Prototype.emptyFunction)(transport, json);
			}
			catch (e)
			{
				this.dispatchException(e);
			}

			if ((this.header('Content-type') || '').match(/^text\/javascript/i))
				this.evalResponse();
		}

		try
		{
			(this.options['on' + event] || Prototype.emptyFunction)(transport, json);
			Ajax.Responders.dispatch('on' + event, this, transport, json);
		}
		catch (e)
		{
			this.dispatchException(e);
		}

		/* Avoid memory leak in MSIE: clean up the oncomplete event handler */
		if (event == 'Complete')
			this.transport.onreadystatechange = Prototype.emptyFunction;
	},

	dispatchException: function(exception)
	{
		(this.options.onException || Prototype.emptyFunction)(this, exception);
		Ajax.Responders.dispatch('onException', this, exception);
	}
});

Ajax.Updater = Class.create();

Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype),
{
	initialize: function(container, url, options)
	{
		this.containers =
		{
			success: container.success ? $(container.success) : $(container),
			failure: container.failure ? $(container.failure) :	(container.success ? null : $(container))
		}

		this.transport = Ajax.getTransport();
		this.setOptions(options);

		var onComplete = this.options.onComplete || Prototype.emptyFunction;
		this.options.onComplete = (
			function(transport, object)
			{
				this.updateContent();
				onComplete(transport, object);
			}
		).bind(this);

		this.request(url);
	},

	updateContent: function()
	{
		var receiver = this.responseIsSuccess() ? this.containers.success : this.containers.failure;
		var response = this.transport.responseText;

		if (!this.options.evalScripts)
			response = response.stripScripts();

		if (receiver)
		{
			if (this.options.insertion)
			{
				new this.options.insertion(receiver, response);
			}
			else
			{
				Element.update(receiver, response);
			}
		}

		if (this.responseIsSuccess())
		{
			if (this.onComplete)
				setTimeout(this.onComplete.bind(this), 10);
		}
	}
});

Ajax.PeriodicalUpdater = Class.create();
Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(),
{
	initialize: function(container, url, options)
	{
		this.setOptions(options);
		this.onComplete = this.options.onComplete;

		this.frequency = (this.options.frequency || 2);
		this.decay = (this.options.decay || 1);

		this.updater = {};
		this.container = container;
		this.url = url;

		this.start();
	},

	start: function()
	{
		this.options.onComplete = this.updateComplete.bind(this);
		this.onTimerEvent();
	},

	stop: function()
	{
		this.updater.onComplete = undefined;
		clearTimeout(this.timer);
		(this.onComplete || Prototype.emptyFunction).apply(this, arguments);
	},

	updateComplete: function(request)
	{
		if (this.options.decay)
		{
			this.decay = (request.responseText == this.lastText ? this.decay * this.options.decay : 1);
			this.lastText = request.responseText;
		}
		this.timer = setTimeout(this.onTimerEvent.bind(this), this.decay * this.frequency * 1000);
	},

	onTimerEvent: function()
	{
		this.updater = new Ajax.Updater(this.container, this.url, this.options);
	}
});

if (!window.Element)
{
	var Element = new Object();
}

Object.extend(Element,
{
	remove: function(element)
	{
		element = $(element);
		element.parentNode.removeChild(element);
	},

	hasClassName: function(element, className)
	{
		if (!(element = $(element)))
			return;
		return Element.classNames(element).include(className);
	},

	addClassName: function(element, className)
	{
		if (!(element = $(element)))
			return;
		return Element.classNames(element).add(className);
	},

	removeClassName: function(element, className)
	{
		if (!(element = $(element)))
			return;
		return Element.classNames(element).remove(className);
	},

	// removes whitespace-only text node children
	cleanWhitespace: function(element)
	{
		element = $(element);
		for (var i = 0; i < element.childNodes.length; i++)
		{
			var node = element.childNodes[i];
			if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
				Element.remove(node);
		}
	},
	
	getStyle: function(element, style)
	{
		element = $(element);
		var value = element.style[style.camelize()];
		if (!value)
		{
			if (document.defaultView && document.defaultView.getComputedStyle)
			{
				var css = document.defaultView.getComputedStyle(element, null);
				value = css ? css.getPropertyValue(style) : null;
			}
			else if (element.currentStyle)
			{
				value = element.currentStyle[style.camelize()];
			}
		}

		if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
			if (Element.getStyle(element, 'position') == 'static') value = 'auto';

		return value == 'auto' ? null : value;
	},

	makePositioned: function(element)
	{
		element = $(element);
		var pos = Element.getStyle(element, 'position');
		if (pos == 'static' || !pos)
		{
			element._madePositioned = true;
			element.style.position = 'relative';
			// Opera returns the offset relative to the positioning context, when an
			// element is position relative but top and left have not been defined
			if (window.opera)
			{
				element.style.top = 0;
				element.style.left = 0;
			}
		}
	},
	
	makeClipping: function(element)
	{
		element = $(element);
		if (element._overflow)
			return;
		element._overflow = element.style.overflow;
		if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
			element.style.overflow = 'hidden';
	},

	undoClipping: function(element)
	{
		element = $(element);
		if (element._overflow)
			return;
		element.style.overflow = element._overflow;
		element._overflow = undefined;
	}
});

/*--------------------------------------------------------------------------*/

var Form =
{
	serialize: function(form)
	{
		var elements = Form.getElements($(form));
		var queryComponents = new Array();

		for (var i = 0; i < elements.length; i++)
		{
			var queryComponent = Form.Element.serialize(elements[i]);
			if (queryComponent)
				queryComponents.push(queryComponent);
		}

		return queryComponents.join('&');
	},

	getElements: function(form)
	{
		form = $(form);
		var elements = new Array();

		for (tagName in Form.Element.Serializers)
		{
			var tagElements = form.getElementsByTagName(tagName);
			for (var j = 0; j < tagElements.length; j++)
				elements.push(tagElements[j]);
		}
		return elements;
	},

	getInputs: function(form, typeName, name)
	{
		form = $(form);
		var inputs = form.getElementsByTagName('input');

		if (!typeName && !name)
			return inputs;

		var matchingInputs = new Array();
		for (var i = 0; i < inputs.length; i++)
		{
			var input = inputs[i];
			if ((typeName && input.type != typeName) || (name && input.name != name))
				continue;
			matchingInputs.push(input);
		}

		return matchingInputs;
	},

	disable: function(form)
	{
		var elements = Form.getElements(form);
		for (var i = 0; i < elements.length; i++)
		{
			var element = elements[i];
			element.blur();
			element.disabled = 'true';
		}
	},

	enable: function(form)
	{
		var elements = Form.getElements(form);
		for (var i = 0; i < elements.length; i++)
		{
			var element = elements[i];
			element.disabled = '';
		}
	},

	findFirstElement: function(form)
	{
		return Form.getElements(form).find
		(
			function(element)
			{
				return element.type != 'hidden' && !element.disabled && ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
			}
		);
	},

	focusFirstElement: function(form)
	{
		Field.activate(Form.findFirstElement(form));
	},

	reset: function(form)
	{
		$(form).reset();
	}
}

Form.Element =
{
	serialize: function(element)
	{
		element = $(element);
		var method = element.tagName.toLowerCase();
		var parameter = Form.Element.Serializers[method](element);

		if (parameter)
		{
			var key = encodeURIComponent(parameter[0]);
			
			if (key.length == 0)
				return;
			var parameters = new Array();
			
			if (parameter[1].constructor != Array)
			{
				parameters.push(key + '=' + encodeURIComponent(parameter[1]));
			}
			else
			{
				for (value in parameter[1])
				{
					if(parseInt(value) >= 0)
						parameters.push(key + '=' + encodeURIComponent(parameter[1][value]));
				}			
			}
			return parameters.join('&');
			//return parameter[1].map(function(value) {
			//	return key + '=' + encodeURIComponent(value);
			//}).join('&');
		}
	},

	getValue: function(element)
	{
		element = $(element);
		var method = element.tagName.toLowerCase();
		var parameter = Form.Element.Serializers[method](element);

		if (parameter)
			return parameter[1];
	}
}

Form.Element.Serializers =
{
	input: function(element)
	{
		switch (element.type.toLowerCase())
		{
			case 'submit':
			case 'hidden':
			case 'password':
			case 'text':
				return Form.Element.Serializers.textarea(element);
			case 'checkbox':
			case 'radio':
				return Form.Element.Serializers.inputSelector(element);
		}
		return false;
	},

	inputSelector: function(element)
	{
		if (element.checked)
			return [element.name, element.value];
	},

	textarea: function(element)
	{
		return [element.name, element.value];
	},

	select: function(element)
	{
		return Form.Element.Serializers[element.type == 'select-one' ? 'selectOne' : 'selectMany'](element);
	},

	selectOne: function(element)
	{
		var value = '', opt, index = element.selectedIndex;
		if (index >= 0)
		{
			opt = element.options[index];
			value = opt.value;
			if (!value && !('value' in opt))
				value = opt.text;
		}
		return [element.name, value];
	},

	selectMany: function(element)
	{
		var value = new Array();
		for (var i = 0; i < element.length; i++)
		{
			var opt = element.options[i];
			if (opt.selected)
			{
				var optValue = opt.value;
				if (!optValue && !('value' in opt))
					optValue = opt.text;
				value.push(optValue);
			}
		}
		return [element.name, value];
	}
}

/*--------------------------------------------------------------------------*/

var $F = Form.Element.getValue;

/*---------------------------------------------------------------------------*/
function getAbsolutePosition(element)
{
	var r = { x: element.offsetLeft, y: element.offsetTop };
	if (element.offsetParent)
	{
		var tmp = getAbsolutePosition(element.offsetParent);
		r.x += tmp.x;
		r.y += tmp.y;
	}
	return r;
}

function getElementsByClassName(node, classname)
{
	var a = [];
	var re = new RegExp('\\b' + classname + '\\b');
	var els = node.getElementsByTagName("*");
	for (var i=0,j=els.length; i<j; i++)
		if(re.test(els[i].className))
			a.push(els[i]);
	return a;
}
