var P = {
	Browser: {
		IE:     !!(window.attachEvent && !window.opera),
		IEWin:  navigator.appVersion.indexOf('MSIE') != -1,
		Opera:  !!window.opera,
		WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
		Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
		MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
	},
	
	emptyFunction: function() {},
	
	ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
		
	encodeURIComponent: function(str) {
		return escape(str).replace('+', '%2B').replace('&', '%26').replace('@', '%40');
	},
	
	toQueryString: function(array, value) {
		if (array != null && typeof array == 'object' && 'splice' in array && 'join' in array) {
			var qs = '';
			for (var i = 0, length = array.length; i < length; i++) {
				qs += P.encodeURIComponent(array[i][0]) + '=' + P.encodeURIComponent(array[i][1]);
			}
			
			return qs;
		} else if (typeof value != 'undefined') return P.encodeURIComponent(array) + '=' + P.encodeURIComponent(value);
	}
};

P.Browser.IE7 = P.Browser.IE && (parseInt(navigator.userAgent.toLowerCase().match(/msie (\d+)/)[1]) >= 7);
P.Browser.IE6 = P.Browser.IE && !P.Browser.IE7;

Object.extend = function(destination, source) {
	for (var property in source) destination[property] = source[property];
	return destination;
};
	
Object.extendNative = function(destination, source) {
	for (var property in source) 
		if (!destination[property]) destination[property] = source[property];
	return destination;
};

Object.extendNative(Object, {
	keys: function(object) {
		var keys = [], l = 0;
		for (var property in object) {
			keys[l++] = property;
		}
		return keys;
	},
	
	values: function(object) {
		var values = [], l = 0;
		for (var property in object)
			values[l++] = object[property];
		return values;
	},
	
	clone: function(object) {
		return Object.extend({}, object);
	},
	
	isElement: function(object) {
		return object && object.nodeType === 1;
	},
	
	isArray: function(object) {
		return object !== null && typeof object == 'object' && object.splice && object.join;
	},
	
	isHash: function(object) {
		return object instanceof Hash;
	},
	
	isFunction: function(object) {
		return typeof object == 'function';
	},
	
	isString: function(object) {
		return typeof object == 'string';
	},
	
	isNumber: function(object) {
		return typeof object == 'number';
	},
	
	isUndefined: function(object) {
		return typeof object == 'undefined';
	}
});

Object.extendNative(String.prototype, {
	truncate: function(len, suffix) {
		len = len || 30;
		suffix = suffix || '...';
		if (this.length > len) {
			return this.substr(0, len - suffix.length) + suffix;
		}
		
		return this;
	},
	
	trim: function() {
		return this.replace(/^\s+|\s+$/, '');
	},
	
	strip: function() {
		return this.replace(/^\s+|\s+$/, '');
	},
	
	ltrim: function() {
		return this.replace(/^\s+/, '');
	},
	
	rtrim: function() {
		return this.replace(/\s+$/, '');
	},
	
	clean: function() {
		return this.replace(/\s{2,}/g, ' ').trim();
	},
	
	escapeHTML: function() {
		return this.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
	},
	
	unescapeHTML: function() {
		return this.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
	},
	
	toInt: function() {
		return parseInt(this, 10);
	},
	
	toFloat: function() {
		return parseFloat(this);
	},
	
	stripScripts: function() {
		return this.replace(new RegExp(P.ScriptFragment, 'img'), '');
	},
	
	extractScripts: function() {
		var matchAll = new RegExp(P.ScriptFragment, 'img');
		var matchOne = new RegExp(P.ScriptFragment, 'im');
		return (this.match(matchAll) || []).map(function(scriptTag) {
			return (scriptTag.match(matchOne) || ['', ''])[1];
		});
	},
	
	evalScripts: function() {
		return this.extractScripts().map(function(script) {return eval(script)});
	},
	
	encode: function() {
		return P.encodeURIComponent(this)
	},
	
	camelize: function() {
		var parts = this.split('-'), len = parts.length;
		if (len == 1) return parts[0];
		
		var camelized = this.charAt(0) == '-' ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) : parts[0];
		
		for (var i = 1; i < len; i++) camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
		
		return camelized;
	}
});

if ('a~b'.split(/(~)/).length !== 3) {
	String.prototype.nativeSplit = String.prototype.split;
	String.prototype.split = function (s, limit) {
		if (!(s instanceof RegExp)) return String.prototype.nativeSplit.apply(this, arguments);
	
		if (limit === undefined || +limit < 0) {
			limit = false;
		} else {
			limit = Math.floor(+limit);
			if (!limit) return [];
		}
	
		var	flags = (s.global ? "g" : "") + (s.ignoreCase ? "i" : "") + (s.multiline ? "m" : ""),
			s2 = new RegExp("^" + s.source + "$", flags),
			output = [],
			lastLastIndex = 0,
			i = 0,
			match;
	
		if (!s.global) s = new RegExp(s.source, "g" + flags);
	
		while ((!limit || i++ <= limit) && (match = s.exec(this))) {
			var zeroLengthMatch = !match[0].length;
	
			if (zeroLengthMatch && s.lastIndex > match.index) s.lastIndex = match.index;
	
			if (s.lastIndex > lastLastIndex) {
				if (match.length > 1) {
					match[0].replace(s2, function () {
						for (var j = 1; j < arguments.length - 2; j++) {
							if (arguments[j] === undefined) match[j] = undefined;
						}
					});
				}
	
				output = output.concat(this.slice(lastLastIndex, match.index), (match.index === this.length ? [] : match.slice(1)));
				lastLastIndex = s.lastIndex;
			}
	
			if (zeroLengthMatch) s.lastIndex++;
		}
	
		return (lastLastIndex === this.length) ? (s.test("") ? output : output.concat("")) : (limit ? output : output.concat(this.slice(lastLastIndex)));
	};
}

var _A = function(iterable) {
	if (!iterable) return [];
	var length = iterable.length || 0, results = new Array(length);
    while (length--) results[length] = iterable[length];
    return results;
}

Object.extendNative(Array.prototype, {
	each: Array.prototype.forEach ? Array.prototype.forEach : function(iterator, bind) {
		for (var i = 0, j = this.length; i < j; i++) iterator.call(bind, this[i], i, this);
	},
	
	map: function(iterator, bind) {
		var results = [];
		for (var i = 0, j = this.length; i < j; i++) results[i] = iterator.call(bind, this[i], i, this);
		return results;
	},
	
	filter: function(iterator, bind) {
		var results = [];
		for (var i = 0, j = 0, l = this.length; i < l; i++) {
			if (iterator.call(bind, this[i], i, this)) results[j++] = this[i];
		}
		
		return results;
	},

	every: function(iterator, bind){
		for (var i = 0, l = this.length; i < l; i++) if (!iterator.call(bind, this[i], i, this)) return false;
		return true;
	},

	some: function(iterator, bind){
		for (var i = 0, l = this.length; i < l; i++) if (iterator.call(bind, this[i], i, this)) return true;
		return false;
	},

	copy: function(start, length){
		start = start || 0;
		if (start < 0) start = this.length + start;
		length = length || (this.length - start);
		return this.slice(start, length);
	},

	remove: function(item){
		var i = 0, l = this.length;
		while (i < l){
			if (this[i] === item){
				this.splice(i, 1);
				l--;
			} else {
				i++;
			}
		}
		
		return this;
	},
	
	indexOf: function(elem, from) {
		var from = isNaN(from) ? 0 : from, length = this.length;
		if (from < 0) from += length;
		if (from >= length) return -1;
		for (; from < length; from++) {
			if (this[from] === elem) return from;
		}
		
		return -1;
	},
	
	lastIndexOf: function(elem, from) {
		var length = this.length;
		var from = isNaN(from) || from >= length ? length - 1 : (from > 0 ? from + length : from);
		for (; from > -1; from--) {
			if (this[from] === elem) return from;
		}
		
		return -1;
	},
	
	concat: function() {
		var array = [], idx = 0;
		for (var i = 0, l = this.length; i < l; i++) array[idx++] = this[i];
		for (var i = 0, length = arguments.length; i < length; i++) {
			if (Object.isArray(arguments[i]))
				for (var j = 0, l = arguments[i].length; j < l; j++) array[idx++] = arguments[i][j];
			else
				array[idx++] = arguments[i];
		}
		
		return array;
	},
	
	collect: function(iterator, bind) {
		var array = [], idx = 0;
		iterator = bind ? iterator.bind(bind) : iterator;
		for (var i = 0, l = this.length; i < l; i++) array[idx++] = iterator(this[i]);
		return array;
	},
	
	inject: function(memo, iterator, bind) {
		iterator = bind ? iterator.bind(bind) : iterator;
		for (var i = 0, l = this.length; i < l; i++) memo = iterator(memo, this[i], i);
		return memo;
	},
	
	reject: function(iterator, bind) {
		iterator = bind ? iterator.bind(bind) : iterator;
		var results = [], idx = 0;
		for (var i = 0, l = this.length; i < l; i++) if (!iterator(this[i], i)) results[idx++] = this[i];
		return results;
	},
	
	flatten: function() {
		return this.inject([], function(array, value) {
			return array.concat(Object.isArray(value) ? value.flatten() : [value]);
		});
	},
	
	detect: function(iterator, bind) {
		iterator = bind ? iterator.bind(bind) : iterator;
		for (var i = 0, l = this.length; i < l; i++) if (iterator(this[i], i)) return this[i];
		return null;
	}
});

Object.extendNative(Function.prototype, {
	argumentNames: function() {
		var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(',');
		for (var i = 0, length = names.length; i < length; i++)
			names[i] = names[i].replace(/^\s+/, '').replace(/\s+$/, '');
		return names.length == 1 && !names[0] ? [] : names;
	},
	
	bind: function() {
		if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
		var __method = this, args = _A(arguments), object = args.shift();
		return (function() {
			return __method.apply(object, args.concat(_A(arguments)));
		});
	},
	
	curry: function() {
		if (!arguments.length) return this;
		var __method = this, args = _A(arguments);
		return (function() {
			return __method.apply(this, args.concat(_A(arguments)));
		});
	},
	
	delay: function() {
		var __method = this, args = _A(arguments), timeout = args.shift() * 1000;
		return window.setTimeout(function() {
			return __method.apply(__method, args);
		}, timeout);
	},
	
	wrap: function(wrapper) {
		var __method = this;
		return (function() {
			return wrapper.apply(this, [__method.bind(this)].concat(_A(arguments)));
		});
	},
	
	methodize: function() {
		if (this._methodized) return this._methodized;
		var __method = this;
		return this._methodized = function() {
			return __method.apply(null, [this].concat(_A(arguments)));
		};
	}
});

Function.prototype.defer = Function.prototype.delay.curry(0.01);

Object.extendNative(Number.prototype, {
	toInt: function() {
		return this << 1 >> 1;
	},
	
	toFloat: function() {
		return parseFloat(this);
	},
	
	round: function(precision){
		precision = Math.pow(10, precision || 0);
		return Math.round(this * precision) / precision;
	},

	times: function(fn){
		for (var i = 0; i < this; i++) fn(i);
	}
});

P.Form = {
	getElements: function(form) {
		form = _(form);
		var results = [], elements = form.getElementsByTagName('*');
		for (var i = 0, length = elements.length; i < length; i++)
			if (P.Form.Serializers[elements[i].tagName.toLowerCase()])
				results[results.length] = elements[i];
		
		return results;
	},
	
	serialize: function(form) {
		var queryString = [], elements = P.Form.getElements(form), field;
		for (var i = 0, length = elements.length; i < length; i++) {
			if (field = P.Field.serialize(elements[i])) queryString[queryString.length] = field;
		}
		
		return queryString.join('&');
	}
};

P.Field = {
	getValue: function(element) {
	  element = _(element);
		return P.Form.Serializers[element.tagName.toLowerCase()](element);
		
		
	},
	
	setValue: function(element, value) {
		element = _(element);
		P.Form.Serializers[element.tagName.toLowerCase()](element, value);
		return element;
	},
	
	serialize: function(element) {
		element = _(element);
		if (!element.disabled && element.name) {
			var value = P.Field.getValue(element);
			if (typeof value != 'undefined' && value !== null) return P.toQueryString(element.name, value);
		}
		
		return '';
	}
};

P.Form.Serializers = {
	input: function(element, value) {
		switch (element.type.toLowerCase()) {
			case 'checkbox':
			case 'radio':
				return P.Form.Serializers.inputSelector(element, value);
			default:
				return P.Form.Serializers.textarea(element, value);
		}
	},
	
	inputSelector: function(element, value) {
		if (typeof value == 'undefined') return element.checked ? element.value : null;
		element.checked = !!value;
	},
	
	textarea: function(element, value) {
		if (typeof value == 'undefined') return element.value;
		element.value = value;
	},
	
	select: function(element, index) {
		if (typeof index == 'undefined') return P.Form.Serializers[element.type == 'select-one' ? 'selectOne' : 'selectMany'](element);
		var opt, value, single = (index != null && typeof index == 'object' && 'splice' in index && 'join' in index);
		
		for (var i = 0, length = element.length; i < length; i++) {
			opt = element.options[i];
			value = P.Form.Serializers.optionValue(opt);
			if (single) {
				if (value == index) {
					opt.selected = true;
					return;
				}
			} else {
				opt.selected = false;
				for (var j = 0, l = index.length; j < l; j++) {
					if (value == index[j]) {
						opt.selected = true;
						break;
					}
				}
			}
		}
	},
	
	selectOne: function(element) {
		var index = element.selectedIndex;
		return index >= 0 ? P.Form.Serializers.optionValue(element.options[index]) : null;
	},
	
	selectMany: function(element) {
		var values, length = element.length;
		if (!length) return null;
		
		for (var i = 0, values = []; i < length; i++) {
			var opt = element.options[i];
			if (opt.selected) values[values.length] = P.Form.Serializers.optionValue(opt);
		}
		
		return values;
	},
	
	optionValue: function(opt) {
		return typeof opt.value == 'undefined' ? opt.text : opt.value;
	}
};

var _F = P.Field.getValue;

var _visible = function(element) {
	return _(element).style.display != 'none';
};

var _show = function(element) {
	if (Object.isArray(element)) return element.map(_show);
	element = _(element);
	element.style.display = '';
	return element;
};

var _hide = function(element) {
	if (Object.isArray(element)) return element.map(_hide);
	element = _(element);
	element.style.display = 'none';
	return element;
};

var _toggle = function(element) {
	if (Object.isArray(element)) return element.map(_toggle);
	element = _(element);
	element.style.display = _visible(element) ? 'none' : '';
	return element;
};

var _remove = function(element) {
	if (Object.isArray(element)) return element.map(_remove);
	element = _(element);
	element.parentNode.removeChild(element);
	return element;
};

var _cleanWhitespace = function(element) {
	element = _(element);
	var node = element.firstChild;
	while (node) {
		var nextNode = node.nextSibling;
		if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) element.removeChild(node);
		node = nextNode;
	}
	
	return element;
};

var _firstDescendant = function(element) {
	var node = _(element).firstChild;
	while (node && node.nodeType != 1) node = node.nextSibling;
	return node;
}

var _down = function(element, selector) {
	if (Object.isArray(element)) return element.map(function(element) {return _down(element, selector)});
	element = _(element);
	var results = __(selector, element, 1);
	if (results.length > 0) return results[0];
	return null;
};

var _hasClassName = function(element, className) {
	element = _(element);
	var elementClassName = element.className;
	return (elementClassName.length > 0 && (elementClassName == className || new RegExp('(^|\\s)' + className + '(\\s|$)').test(elementClassName)));
};

var _addClassName = function(element, className) {
	if (Object.isArray(element)) return element.map(function(element) {return _addClassName(element, className)});
	element = _(element);
	if (!_hasClassName(element, className)) element.className += (element.className ? ' ' : '') + className;
	return element;
};

var _removeClassName = function(element, className) {
	if (Object.isArray(element)) return element.map(function(element) {return _removeClassName(element, className)});
	element = _(element);
	element.className = element.className.replace(new RegExp('(^|\\s+)' + className + '(\\s+|$)'), ' ').trim();
	return element;
};

var _toggleClassName = function(element, className) {
	if (Object.isArray(element)) return element.map(function(element) {return _toggleClassName(element, className)});
	element = _(element);
	return (_hasClassName(element, className) ? _removeClassName : _addClassName)(element, className);
}

var _classNames = function(element) {
	element = _(element);
	var classNames = element.className.split(/\s+/);
	var results = [], idx = 0;
	for (var i = 0, length = classNames.length; i < length; i++) if (classNames[i].length > 0) results[idx++] = classNames[i];
	return results;
}

var _collectTextNodes = function(element) {  
	return _A(_(element).childNodes).collect(function(node) {
		return (node.nodeType == 3 ? node.nodeValue : (node.hasChildNodes() ? _collectTextNodes(node) : ''));
	}).flatten().join('');
};

var _collectTextNodesIgnoreClass = function(element, className) {  
	return _A(_(element).childNodes).collect(function(node) {
		return (node.nodeType == 3 ? node.nodeValue : ((node.hasChildNodes() && !_hasClassName(node, className)) ? _collectTextNodesIgnoreClass(node, className) : ''));
	}).flatten().join('');
};

if (P.Browser.IE) {
	var _setOpacity = function(element, opacity) {
		if (Object.isArray(element)) return element.map(function(element) {return _setOpacity(element, opacity)});
		element = _(element);
		element.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
	}
} else {
	var _setOpacity = function(element, opacity) {
		if (Object.isArray(element)) return element.map(function(element) {return _setOpacity(element, opacity)});
		element = _(element);
		element.style.opacity = opacity;
	}
};

var _getStyle = function(element, style) {
	element = _(element);
	style = style == 'float' ? 'cssFloat' : style.camelize();
	var value = element.style[style];
	if (!value) {
		var css = document.defaultView.getComputedStyle(element, null);
		value = css ? css[style] : null;
	}
	
	if (style == 'opacity') return value ? parseFloat(value) : 1.0;
	return value == 'auto' ? null : value;
};

if (P.Browser.IE) {
	var _getStyle = function(element, style) {
		element = _(element);
		style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
		var value = element.style[style];
		if (!value && element.currentStyle) value = element.currentStyle[style];
		
		if (style == 'opacity') {
			if (value = (_getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
				if (value[1]) return parseFloat(value[1]) / 100;
					return 1.0;
		}
		
		if (value == 'auto') {
			if ((style == 'width' || style == 'height') && (_getStyle(element, 'display') != 'none'))
				return element['offset' + style.capitalize()] + 'px';
			return null;
		}
		
		return value;
	};
}

var _isParent = function(child, element) {
	child = _(child);
	element = _(element);
	if (!child.parentNode || child == element) return false;
	while (child = child.parentNode) {
		if (child == element) return true;
	}
	
	return false;
};

var _findParent = function(element, tagName) {
	var parent = _(element).parentNode;
	if (!parent) return null;
	
	while (parent) {
		if (parent.nodeName.toUpperCase() == tagName.toUpperCase()) return parent;
		parent = parent.parentNode;
	}
	
	
	return null;
};

var _findChildren = function(element, only, recursive, tagName) {   
	element = _(element);
	if (!element.hasChildNodes()) return null;
	tagName = tagName.toUpperCase();
	if (only) only = Object.isArray(only) ? only : [only];
	var elements = [], grandchildren, classNames;
	for (var i = 0; (e = element.childNodes[i]); i++) {
		classNames = _classNames(e);
		if (e.tagName && e.tagName.toUpperCase() == tagName && (!only || (only.detect(function(v) { return classNames.indexOf(v) !== -1})))) elements.push(e);
		if (recursive) {
			grandchildren = _findChildren(e, only, recursive, tagName);
			if (grandchildren) elements.concat(grandchildren);
		}
	}
	
	return elements;
};

var _submit = function(element, onlyOnce) {
	var submitFunc = function() {
		element = _(element);
		if (onlyOnce && element._submitted) return false;
		if (element.onsubmit) {
			if (element.onsubmit()) {
				if (onlyOnce) element._submitted = true;
				element.submit();
				return true;
			}
			
			return false;
		}
		
		if (onlyOnce) element._submitted = true;
		element.submit();
		return true;
	}
	
	if (Ajax.activeRequestCount > 0) {
		Ajax.Responders.register({
			onComplete: function() {
				if (Ajax.activeRequestCount == 0) {
					submitFunc();
				}
			}
		});
		
		return true;
	} else {
		return submitFunc();
	}
};

var _disable = function(element) {
	element = _(element);
	element.disabled = true;
	return element;
};

var _enable = function(element) {
	element = _(element);
	element.disabled = false;
	return element;
};

var _ = function(element) {
	if (Object.isArray(element)) return element.map(_);
	if (Object.isString(element)) {
		var elm = document.getElementById(element);
		if (elm && elm['id'] && elm.id != element && document.all) {
			elm = document.all[element];
			if (elm && elm['id'] && elm.id == element) return elm;
			if (elm.length > 0) {
				for (var e = null, i = 0, l = elm.length; i < l; i++) {
					if (elm[i] && elm[i]['id'] && elm[i].id == element) return elm[i];
				}
			}
			return null;
		}
		
		return elm;
	}
	
	return element;
};

window.getInnerHeight = function() {
	if (document.documentElement && document.documentElement.clientHeight) {
		return (window.getInnerHeight = function() {
			return document.documentElement.clientHeight;
		})();
	} else if (document.body && document.body.clientHeight) {
		return (window.getInnerHeight = function() {
			return document.body.clientHeight;
		})();
	} else if (document.innerHeight) {
		return (window.getInnerHeight = function() {
			return document.innerHeight;
		})();
	} else {
		return (window.getInnerHeight = function() {
			return 0;
		})();
	}
};

window.getInnerWidth = function() {
	if (document.documentElement && document.documentElement.clientWidth) {
		return (window.getInnerWidth = function() {
			return document.documentElement.clientWidth;
		})();
	} else if (document.body && document.body.clientWidth) {
		return (window.getInnerWidth = function() {
			return document.body.clientWidth;
		})();
	} else if (document.innerWidth) {
		return (window.getInnerWidth = function() {
			return document.innerWidth;
		})();
	} else {
		return (window.getInnerWidth = function() {
			return 0;
		})();
	}
};

window.getScrollTop = function() {
	if (document.documentElement && document.documentElement.scrollTop) {
		return document.documentElement.scrollTop;
	} else if (document.body && document.body.scrollTop) {
		return document.body.scrollTop;
	} else if (document.pageYOffset) {
		return document.pageYOffset;
	}
	
	return 0;
};

window.getScrollLeft = function() {
	if (document.documentElement && document.documentElement.scrollLeft) {
		return document.documentElement.scrollLeft;
	} else if (document.body && document.body.scrollLeft) {
		return document.body.scrollLeft;
	} else if (document.pageXOffset) {
		return document.pageXOffset;
	}
	
	return 0;
};

window.getScrollHeight = function() {
	if (document.documentElement && document.documentElement.scrollHeight) {
		return (window.getScrollHeight = function() {
			return document.documentElement.scrollHeight;
		})();
	} else if (document.body && document.body.scrollHeight) {
		return (window.getScrollHeight = function() {
			return document.body.scrollHeight;
		})();
	} else {
		return (window.getScrollHeight = function() {
			return 0;
		})();
	}
}

window.getScrollWidth = function() {
	if (document.documentElement && document.documentElement.scrollWidth) {
		return (window.getScrollWidth = function() {
			return document.documentElement.scrollWidth;
		})();
	} else if (document.body && document.body.scrollWidth) {
		return (window.getScrollWidth = function() {
			return document.body.scrollWidth;
		})();
	} else {
		return (window.getScrollWidth = function() {
			return 0;
		})();
	}
}

var Position = {
	prepare: function() {
		this.deltaX =  window.pageXOffset
					|| document.documentElement.scrollLeft
					|| document.body.scrollLeft
					|| 0;
		this.deltaY =  window.pageYOffset
					|| document.documentElement.scrollTop
					|| document.body.scrollTop
					|| 0;
	},
	
	cumulativeOffset: function(element) {
		var element = _(element), valueT = 0, valueL = 0;
		do {
			valueT += element.offsetTop  || 0;
			valueL += element.offsetLeft || 0;
			element = element.offsetParent;
		} while (element);
		
		return [valueL, valueT];
	},
	
	cumulativeScrollOffset: function(element) {
		var element = _(element), valueT = 0, valueL = 0;
		do {
			valueT += element.scrollTop  || 0;
			valueL += element.scrollLeft || 0;
			element = element.parentNode;
		} while (element);
		
		return [valueL, valueT];
	},
	
	within: function(element, x, y, nooffset) {
		element = _(element);
		if (!nooffset || typeof element._offset == 'undefined') element._offset = Position.cumulativeOffset(element);
		this.x = x;
		this.y = y;
		return (y >= element._offset[1] && y < element._offset[1] + element.offsetHeight &&
            	x >= element._offset[0] && x < element._offset[0] + element.offsetWidth);
	},
	
	withinIncludingScrolloffsets: function(element, x, y) {
		var offsetcache = Position.cumulativeScrollOffset(element);
		
		this.x = x + offsetcache[0] - this.deltaX;
		this.y = y + offsetcache[1] - this.deltaY;
		element._offset = Position.cumulativeOffset(element);
		
		return (this.y >= element._offset[1] && this.y < element._offset[1] + element.offsetHeight &&
				this.x >= element._offset[0] && this.x < element._offset[0] + element.offsetWidth);
	},
	
	overlap: function(element, mode, x, y) {
		if (!mode) return 0;
		element = _(element);
		if (x && y) {
			this.x = x;
			this.y = y;
		}
		if (mode == 'vertical') return ((element._offset[1] + element.offsetHeight) - this.y) / element.offsetHeight;
		if (mode == 'horizontal') return ((element._offset[0] + element.offsetWidth) - this.x) / element.offsetWidth;
	},
	
	makePositioned: function(element) {
		element = _(element);
		var pos = element.style.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;
			}
		}
		
		return element;
	},
	
	undoPositioned: function(element) {
		element = _(element);
		if (element._madePositioned) {
			element._madePositioned = undefined;
			element.style.position = element.style.top = element.style.left = element.style.bottom = element.style.right = '';
		}
		
		return element;
	},
	
	makeClipping: function(element) {
		element = _(element);
		if (element._overflow) return element;
		element._overflow = element.style.overflow || 'auto';
		if (element._overflow !== 'hidden') element.style.overflow = 'hidden';
		return element;
	},
	
	undoClipping: function(element) {
		element = _(element);
		if (!element._overflow) return element;
		element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
		element._overflow = null;
		return element;
	},
	
	viewportOffset: function(forElement) {
		var valueT = 0, valueL = 0;
		
		var element = forElement;
		do {
			valueT += element.offsetTop  || 0;
			valueL += element.offsetLeft || 0;
			
			// Safari fix
			if (element.offsetParent == document.body && _getStyle(element, 'position') == 'absolute') break;
		} while (element = element.offsetParent);
		
		element = forElement;
		do {
			if (!P.Browser.Opera || element.tagName == 'BODY') {
				valueT -= element.scrollTop  || 0;
				valueL -= element.scrollLeft || 0;
			}
		} while (element = element.parentNode);
		
		return [valueL, valueT];
	},
	
	getOffsetParent: function(element) {
		if (element.offsetParent) return _(element.offsetParent);
		if (element == document.body) return _(element);
		
		while ((element = element.parentNode) && element != document.body)
			if (_getStyle(element, 'position') != 'static') return _(element);
		
		return document.body;
	},
	
	clone: function(source, element) {
		var options = Object.extend({
			setLeft:    true,
			setTop:     true,
			setWidth:   true,
			setHeight:  true,
			offsetTop:  0,
			offsetLeft: 0
		}, arguments[2] || { });
		
		// find page position of source
		source = _(source);
		var p = Position.viewportOffset(source);
		
		// find coordinate system to use
		element = _(element);
		var delta = [0, 0];
		var parent = null;
		// delta [0,0] will do fine with position: fixed elements,
		// position:absolute needs offsetParent deltas
		if (_getStyle(element, 'position') == 'absolute') {
			parent = Position.getOffsetParent(element);
			delta = Position.viewportOffset(parent);
		}
		
		// correct by body offsets (fixes Safari)
		if (parent == document.body) {
			delta[0] -= document.body.offsetLeft;
			delta[1] -= document.body.offsetTop;
		}
		
		// set position
		if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
		if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
		if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
		if (options.setHeight) element.style.height = source.offsetHeight + 'px';
		return element;
	}
}

var Event = {
	_wrappers: {},
	
	KEY_BACKSPACE: 8,
	KEY_TAB:       9,
	KEY_RETURN:   13,
	KEY_ESC:      27,
	KEY_LEFT:     37,
	KEY_UP:       38,
	KEY_RIGHT:    39,
	KEY_DOWN:     40,
	KEY_DELETE:   46,
	KEY_HOME:     36,
	KEY_END:      35,
	KEY_PAGEUP:   33,
	KEY_PAGEDOWN: 34,
	KEY_INSERT:   45,
	
	getKey: function(event) {
		try {
			var keyCode = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode;
			return keyCode;
		} catch (e) {
			return null;
		}
	},
	
	element: function(event) {
		var target = event.target || event.srcElement;
		return target.nodeType == 1 ? target : target.parentNode;
	},
	
	pointer: function(event) {
		return [Event.pointerX(event), Event.pointerY(event)];
	},
	
	pointerX: function(event) {
		return event.pageX || (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft));
	},
	
	pointerY: function(event) {
		return event.pageY || (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop));
	},
	
	stop: function(event) {
		if (P.Browser.IE) {
			return (Event.stop = function(event) {
				event.cancelBubble = true;
				event.returnValue = false;
				event.stopped = true;
			})(event);
		} else {
			return (Event.stop = function(event) {
				event.stopPropagation();
				event.preventDefault();
				event.stopped = true;
			})(event);
		}
	},
	
	stopPropagation: function(event) {
		event.stopPropagation()
	},
	
	preventDefault: function(event) {
		event.preventDefault()
	},
	
	isLeftClick: function(event) {
		return Event.isButton(event, 0);
	},
	
	isMiddleClick: function(event) {
		return Event.isButton(event, 1);
	},
	
	isRightClick: function(event) {
		return Event.isButton(event, 2);
	},
	
	_eventID: 0,
	getWrapper: function(instance, eventName, listener) {
		var id = instance.sourceIndex ? instance.sourceIndex : (instance._id || (instance._id = Event._eventID++));
		if (!Event._wrappers[id]) Event._wrappers[id] = {};
		if (!Event._wrappers[id][eventName]) Event._wrappers[id][eventName] = [];
		var wrappers = Event._wrappers[id][eventName];
		for (var i = 0, length = wrappers.length; i < length; i++) {
			if (wrappers[i].listener === listener) return false;
		}
		
		if (P.Browser.IE) {
			var wrapper = function(event) {
				var event = event || window.event;
				if (event.eventName && event.eventName != eventName) return false;
				listener.call(instance, event);
			}
		} else {
			var wrapper = function(event) {
				if (event.eventName && event.eventName != eventName) return false;
				try {
					listener.call(instance, event);
				} catch (e) {
					if ((typeof console != 'undefined') && console.log) {
						console.log(e);
					} else {
						//alert(e);
					}
				}
			}
		}
		
		wrapper.listener = listener;
		wrappers.push(wrapper);
		return wrapper;
	}
};

if (P.Browser.IE) {
	Event.stopPropagation = function(event) {event.cancelBubble = true};
	Event.preventDefault = function(event) {event.returnValue = false};
}

if (P.Browser.IE) {
	Event.buttonMap = {0: 1, 1: 4, 2: 2};
	Event.isButton = function(event, code) {
		return event.button == Event.buttonMap[code];
	};
} else if (P.Browser.WebKit) {
	Event.isButton = function(event, code) {
		switch (code) {
			case 0: return event.which == 1 && !event.metaKey;
			case 1: return event.which == 1 && event.metaKey;
			default: return false;
		}
	};
} else {
	Event.isButton = function(event, code) {
		return event.which ? (event.which === code + 1) : (event.button === code);
	};
}

Event.binds = 0;

if (document.addEventListener) {
	Event.bind = function(element, eventName, listener) {
		Event.binds++;
		element = _(element);
		var wrapper = Event.getWrapper(element, eventName, listener);
		if (!wrapper) return element;
		if (eventName && eventName.indexOf(':') !== -1) {
			element.addEventListener('dataavailable', wrapper, false);
		} else {
			return element.addEventListener(eventName, wrapper, false);
		}
		
		return element;
	};
	
	Event.unbind = function(element, eventName, listener) {
		Event.binds--;
		element = _(element);
		var id = element.sourceIndex ? element.sourceIndex : (element._id || (element._id = Event._eventID++));
		var wrappers = ((Event._wrappers[id] || 0)[eventName] || []);
		for (var i = 0, length = wrappers.length; i < length; i++) {
			if (wrappers[i].listener === listener) {
				if (eventName && eventName.indexOf(':') !== -1)
					element.removeEventListener('dataavailable', wrappers[i], false);
				else
					element.removeEventListener(eventName, wrappers[i], false);
				
				wrappers.splice(i, 1);
				return;
			}
		}
	}
} else if (document.attachEvent) {
	Event.bind = function(element, eventName, listener) {
		Event.binds++;
		element = _(element);
		var wrapper = Event.getWrapper(element, eventName, listener);
		if (!wrapper) return element;
		if (eventName && eventName.indexOf(':') !== -1) {
			element.attachEvent('ondataavailable', wrapper);
		} else {
			element.attachEvent('on' + eventName, wrapper);
		}
		
		return element;
	};
	
	Event.unbind = function(element, eventName, listener) {
		Event.binds--;
		element = _(element);
		var id = element.sourceIndex ? element.sourceIndex : (element._id || (element._id = Event._eventID++));
		var wrappers = ((Event._wrappers[id] || 0)[eventName] || []);
		for (var i = 0, length = wrappers.length; i < length; i++) {
			if (wrappers[i].listener === listener) {
				if (eventName && eventName.indexOf(':') !== -1)
					element.detachEvent('ondataavailable', wrappers[i]);
				else
					element.detachEvent('on' + eventName, wrappers[i]);
				
				wrappers.splice(i, 1);
				return;
			}
		}
	}
	
	window.attachEvent('onunload', function() {
		for (var eventName in Event._wrappers) {
			delete Event._wrappers[eventName];
		}
	});
}

if (document.createEvent) {
	Event.fire = function(element, eventName) {
		if (element == document && !element.dispatchEvent)
			element = document.documentElement;
		
		var event = document.createEvent('HTMLEvents');
		event.initEvent('dataavailable', true, true);
		event.eventName = eventName;
		element.dispatchEvent(event);
		return event;
	}
} else {
	Event.fire = function(element, eventName) {
		var event = document.createEventObject();
		event.eventType = 'ondataavailable';
		event.eventName = eventName;
		element.fireEvent(event.eventType, event);
		return event;
	}
}

(function() {
	var timer;
	
	fireContentLoadedEvent = function() {
		if (document.loaded) return;
		if (timer) window.clearInterval(timer);
		Event.fire(document, 'dom:ready');
		document.loaded = true;
	};

	if (document.addEventListener) {
		if (P.Browser.WebKit) {
			timer = window.setInterval(function() {
				if (/loaded|complete/.test(document.readyState)) fireContentLoadedEvent();
			}, 0);
			
			window.addEventListener('load', fireContentLoadedEvent, false);
		} else {
			document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
		}
	} else {
		document.write('<script id="__onDOMContentLoaded" defer src="//:"><\/script>');
		_('__onDOMContentLoaded').onreadystatechange = function() {
			if (this.readyState == 'complete') {
				this.onreadystatechange = null;
				fireContentLoadedEvent();
			}
		};
	}
})();

var Class = {
	create: function() {
		var parent = null, properties = _A(arguments);
		if (Object.isFunction(properties[0])) parent = properties.shift();
		
		function klass() {
			this.initialize.apply(this, arguments);
		}
		
		Object.extend(klass, Class.Methods);
		klass.superclass = parent;
		klass.subclasses = [];
		
		if (parent) {
			var subclass = function() {};
			subclass.prototype = parent.prototype;
			klass.prototype = new subclass;
			parent.subclasses.push(klass);
		}
		
		for (var i = 0, length = properties.length; i < length; i++) {
			klass.addMethods(properties[i]);
		}
		
		if (!klass.prototype.initialize) klass.prototype.initialize = P.emptyFunction;
		
		klass.prototype.constructor = klass;
		
		return klass;
	}
};

Class.Methods = {
	addMethods: function(source) {
		var ancestor = this.superclass && this.superclass.prototype;
		var properties = Object.keys(source);
		
		if (!Object.keys({toString: true}).length) properties.push('toString', 'valueOf');
		
		for (var i = 0, length = properties.length; i < length; i++) {
			var property = properties[i], value = source[property];
			if (ancestor && Object.isFunction(value) && value.argumentNames()[0] == '$super') {
				var method = value, value = Object.extend((function(m) {
					return function() { return ancestor[m].apply(this, arguments) };
				})(property).wrap(method), {
					valueOf: function() {return method},
					toString: function() {return method.toString()}
				});
			}
			
			this.prototype[property] = value;
		}
		
		return this;
	}
};

var Ajax = {
	getTransport: function() {return null},
	activeRequestCount: 0
}

try {
	var test = new XMLHttpRequest();
	Ajax.getTransport = function() {return new XMLHttpRequest();};
} catch (e) {
	try {
		var test = new ActiveXObject('Msxml2.XMLHTTP');
		Ajax.getTransport = function() {return new ActiveXObject('Msxml2.XMLHTTP');};
	} catch (e) {
		try {
			var test = new ActiveXObject('Microsoft.XMLHTTP');
			Ajax.getTransport = function() {return new ActiveXObject('Microsoft.XMLHTTP');};
		} catch (e) {
			Ajax.getTransport = function() {return null};
		}
	}
}

Ajax.Responders = {
	responders: [],
	
	register: function(responder) {
		if (this.responders.indexOf(responder) === -1) this.responders.push(responder);
	},
	
	unregister: function(responder) {
		var i = this.responders.indexOf(responder);
		if (i === -1) return;
		this.responders.splice(i, 1);
	},
	
	dispatch: function(callback, request, transport) {
		this.responders.each(function(responder) {
			if (Object.isFunction(responder[callback])) {
				try {
					responder[callback].apply(responder, [request, transport]);
				} catch (e) {}
			}
		});
	}
};

Ajax.Responders.register({
	onCreate: function() {Ajax.activeRequestCount++},
	onComplete: function() {Ajax.activeRequestCount--}
});

Ajax.Base = Class.create({
	initialize: function(options) {
		this.options = Object.extend({
			method:       'get',
			asynchronous: true,
			contentType:  'application/x-www-form-urlencoded',
			encoding:     'UTF-8',
			parameters:   '',
			evalJS:       true
		} || {}, options);
		
		this.options.method = this.options.method.toLowerCase();
	}
});

Ajax.Request = Class.create(Ajax.Base, {
	_complete: false,

	initialize: function($super, url, options) {
		$super(options);
		this.transport = Ajax.getTransport();
		this.request(url);
	},
	
	request: function(url) {
		this.url = url;
		this.method = this.options.method;
		if (!Object.isString(this.options.parameters)) {
			var params = Object.clone(this.options.parameters);
			this.parameters = params;
		} else {
			var params = this.options.parameters;
		}
	
		if (this.method == 'get') {
			if (params) {
				this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params + '&_r_=' + (new Date()).getTime();
			} else {
				this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + '_r_=' + (new Date()).getTime();
			}
		}
	
		try {
			var response = new Ajax.Response(this);
			if (this.options.onCreate) this.options.onCreate(response);
			Ajax.Responders.dispatch('onCreate', this, response);
			
			this.transport.open(this.method.toUpperCase(), this.url, this.options.asynchronous);
			
			if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
			
			this.transport.onreadystatechange = this.onStateChange.bind(this);
			this.setRequestHeaders();
			
			this.body = this.method == 'post' ? (this.options.postBody || params) : null;
			this.transport.send(this.body);
			
			if (!this.options.asynchronous && this.transport.overrideMimeType)
				this.onStateChange();
			
		} catch (e) {
			this.dispatchException(e);
		}
	},
	
	onStateChange: function() {
		var readyState = this.transport.readyState;
		if (readyState > 1 && !((readyState == 4) && this._complete))
		this.respondToReadyState(this.transport.readyState);
	},
	
	setRequestHeaders: function() {
		var headers = {
			'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
		};
		
		if (this.method == 'post') {
			headers['Content-type'] = this.options.contentType + (this.options.encoding ? '; charset=' + this.options.encoding : '');
			
			if (this.transport.overrideMimeType && (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
				headers['Connection'] = 'close';
		}
		
		for (var name in headers)
			this.transport.setRequestHeader(name, headers[name]);
	},
	
	success: function() {
		var status = this.getStatus();
		return !status || (status >= 200 && status < 300);
	},
	
	getStatus: function() {
		if (this.transport && this.transport.status)
			return this.transport.status;
		return 0;
	},
	
	respondToReadyState: function(readyState) {
		var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
		
		if (state == 'Complete') {
			try {
				this._complete = true;
				(this.options['on' + response.status] || this.options['on' + (this.success() ? 'Success' : 'Failure')] || P.emptyFunction)(response);
			} catch (e) {
				this.dispatchException(e);
			}
		
			var contentType = response.getHeader('Content-type');
			if (this.options.evalJS == 'force' || (this.options.evalJS && this.isSameOrigin() && contentType && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
				this.evalResponse();
		}
		
		try {
			(this.options['on' + state] || P.emptyFunction)(response);
			Ajax.Responders.dispatch('on' + state, this, response);
		} catch (e) {
			this.dispatchException(e);
		}
		
		if (state == 'Complete') this.transport.onreadystatechange = P.emptyFunction;
	},
	
	isSameOrigin: function() {
		var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
		return !m || (m[0] == location.protocol + '//' + document.domain + (location.port ? ':' + location.port : ''));
	},
	
	getHeader: function(name) {
		try {
			return this.transport.getResponseHeader(name);
		} catch (e) {return null}
	},

	evalResponse: function() {
		try {
			return eval(this.transport.responseText || '');
		} catch (e) {
			this.dispatchException(e);
		}
	},
	
	dispatchException: function(exception) {
		(this.options.onException || P.emptyFunction)(this, exception);
		Ajax.Responders.dispatch('onException', this, exception);
	}
});
	
Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
	
Ajax.Response = Class.create({
	initialize: function(request){
		this.request = request;
		var transport = this.transport = request.transport,	readyState = this.readyState = transport.readyState;
		
		if((readyState > 2 && !P.Browser.IE) || readyState == 4) {
			this.status = this.getStatus();
			this.statusText = this.getStatusText();
			this.responseText = transport.responseText == null ? '' : transport.responseText;
		}
	
		if(readyState == 4) {
			var xml = transport.responseXML;
			this.responseXML = Object.isUndefined(xml) ? null : xml;
		}
	},
	
	status: 0,
	statusText: '',
	
	getStatus: Ajax.Request.prototype.getStatus,
	
	getStatusText: function() {
		try {
			return this.transport.statusText || '';
		} catch (e) {return '';}
	},
	
	getHeader: Ajax.Request.prototype.getHeader,
	
	getAllHeaders: function() {
		try {
			return this.getAllResponseHeaders();
		} catch (e) {return null;}
	},
	
	getResponseHeader: function(name) {
		return this.transport.getResponseHeader(name);
	},
	
	getAllResponseHeaders: function() {
		return this.transport.getAllResponseHeaders();
	}
});

Ajax.Updater = Class.create(Ajax.Request, {
	initialize: function($super, container, url, options) {
		this.container = {
			success: (container.success || container),
			failure: (container.failure || (container.success ? null : container))
		};
		
		options = options || { };
		this.onComplete = options.onComplete;
		options.onComplete = (function(response, param) {
			this.updateContent(response.responseText);
		}).bind(this);
		
		$super(url, options);
	},
	
	updateContent: function(responseText) {
		var receiver = _(this.container[this.success() ? 'success' : 'failure']);
		
		receiver.innerHTML = responseText.stripScripts();
		if (this.options.evalScripts) {
			if (this.success()) {
				(function() {
					responseText.evalScripts();
					if (this.onComplete) this.onComplete.bind(this).defer();
				}).bind(this).defer();
			} else {
				responseText.evalScripts.bind(responseText).defer();
			}
		} else {
			if (this.success()) {
				if (this.onComplete) this.onComplete.bind(this).defer().defer().defer();
			}
		}
	}
});

if (document.getElementsByClassName) {
	P.getElementsByClassName = function(classes, parent, iterator) {
		parent = parent || document;
		iterator = iterator || function(x) {return x};
		var elements = parent.getElementsByClassName(classes);
		Array(elements).map(iterator);
		return elements;
	}
} else if (document.evaluate) {
	P.getElementsByClassName = function(classes, parent, iterator) {
		parent = parent || document;
		iterator = iterator || function(x) {return x};
		var query = document.evaluate('.//*' + Array(classes).map(function(name) {
			return "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
		}).join(''), parent, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
		
		var elements = [];
		for (var i = 0, length = query.snapshotLength; i < length; i++) {
			elements[i] = iterator(query.snapshotItem(i));
		}
		
		return elements;
	}
} else {
	P.getElementsByClassName = function(classes, parent, iterator) {
		parent = parent || document;
		iterator = iterator || function(x) {return x};
		
		var element, className, results = [];
		var elements = P.Browser.IE ? parent.all : parent.getElementsByTagName('*');
		var l = classes.length;
		outerLoop:
		for (var i = 0; element = elements[i]; i++) {
			if (!element.className) continue;
			className = ' ' + element.className + ' ';
			for (var j = 0; j < l; j++)
				if (className.indexOf(' ' + classes[j] + ' ') === -1) continue outerLoop;
			
			results[i] = iterator(element);
		}
		
		return results;
	}
}

if (document.evaluate) {
	P.select = (function() {
		var __match = /^([\w\-_]+)?(?:#([\w0-9\-_]+))?(?:\.([\w0-9\-_]+(?:\.[\w0-9\-_]+)*))?$/;
		
		var prepareSelector = function(s, init) {
			var p = 0, p1, p2, p3, p4, splitter = ' ', sel = [init], rsel = [], m, j = 0, c;
			while (true) {
				p1 = s.indexOf(' ');
				p2 = s.indexOf('>');
				p3 = s.indexOf('+');
				p4 = s.indexOf('~');
				p5 = s.indexOf(',');
				p1 = p1 === -1 ? 250 : p1;
				p2 = p2 === -1 ? 250 : p2;
				p3 = p3 === -1 ? 250 : p3;
				p4 = p4 === -1 ? 250 : p4;
				p5 = p5 === -1 ? 250 : p5;
				p = Math.min(p1, p2, p3, p4, p5);
				
				switch (splitter) {
					case ' ':
						sel.push('/descendant::');
						break;
					case ',':
						sel.push(' | ');
						sel.push(init);
						sel.push('/descendant::');
						break;
					case '>':
						sel.push('/child::');
						break;
					case '+':
						sel.push('/following-sibling::*[1][self::');
						rsel.push(']');
						break;
					case '~':
						sel.push('/following-sibling::');
						break;
				}
				
				if (p === 250) {
					m = __match.exec(s);
				} else {
					m = __match.exec(s.substr(0, p));
				}
				
				if (m[1]) {
					sel.push(m[1]);
				} else {
					sel.push('*');
				}
				
				if (m[2]) {
					sel.push('[@id=\'');
					sel.push(m[2]);
					sel.push('\'][1]');
				}
				
				if (m[3]) {
					sel.push('[contains(concat(\' \',@class,\' \'),\' ');
					sel.push(m[3].replace('.', ' \')][contains(concat(\' \',@class,\' \'),\' '));
					sel.push(' \')]');
				}
				
				if (p === 250) break;
				
				splitter = s.substr(p, 1);
				s = s.substr(p + 1);
			}
			
			return sel.concat(rsel.reverse());
		}
		
		return function(selectors, source) {
			selectors = selectors.replace(/\s+/g, ' ').replace(/\s*(,|>|\+|~)\s*/g, '$1');
			source = source || document;
			var query, results = [], j = 0, n = 0;
			var selector = prepareSelector(selectors, '.').join('');
			var doc = (document && document.documentElement) ? document : document.ownerDocument;
			query = document.evaluate(selector, source, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
			for (j = 0, length = query.snapshotLength; j < length; j++) {
				results[n++] = query.snapshotItem(j);
			}
			
			return results;
	}})();
} else {
	P.select = (function() {
		var __classHist = {};
		var __xxid = 1;
		var __added = {};
		var __cnt = 0;
		var __match = /^([\w\-_]+)?(?:#([\w0-9\-_]+))?(?:\.([\w0-9\-_]+(?:\.[\w0-9\-_]+)*))?$/;
		var __matched = {};
		var __p = {};
		var selector = [];
		var results = [];
		var length = 0;
		
		var __genClassHist = function(classes) {
			for (var i = classes.length - 1; (name = classes[i]); i--)
				__classHist[name] = __classHist[name] || new RegExp('(^|\\s)' + name + '(\\s|$)');
		};
		
		var __checkClass = function(className, classes) {
			for (var i = classes.length - 1; i !== -1; i--) if (!__classHist[classes[i]].test(className)) return false;
			return true;
		};
		
		var __nextElementSibling = function(element) {
			while (element = element.nextSibling) if (element.nodeType === 1) return element;		
			return null;
		};
		
		if (document.createElement('span').sourceIndex) {
			var __genid = function(element) {
				if (__added[element.sourceIndex]) return false;
				return __added[element.sourceIndex] = true;
			};
		} else {
			var __genid = function(element) {
				if (!element.xid) element.xid = __xxid++;
				if (__added[element.xid]) return false;
				return __added[element.xid] = true;
			};
		}
		
		var findThemByClass = {
			'': function(m, source, iterator) {
				var classes = m[3], elements = source.getElementsByTagName('*');
				for (var i = 0; (source = elements[i]); i++) {
					if (source.className && __checkClass(source.className, classes)) iterator(source);
				}
			},
			' ': function(m, source, iterator) {
				var classes = m[3], elements = source.getElementsByTagName('*');
				for (var i = 0; (source = elements[i]); i++) {
					if (source.className && __genid(source) && __checkClass(source.className, classes)) iterator(source);
				}
			},
			'>': function(m, source, iterator) {
				var element, classes = m[3], elements = source.childNodes;
				for (var i = 0; (element = elements[i]); i++) {
					if (element.className && element.parentNode === source && __checkClass(element.className, classes))
						iterator(element);
				}
			},
			'+': function(m, source, iterator) {
				var classes = m[3];
				while (source = source.nextSibling) {
					if (source.nodeType === 1) {
						if (source.className && __checkClass(source.className, classes)) iterator(source);
						break;
					}
				}
			},
			'~': function(m, source, iterator) {
				var classes = m[3];
				while ((source = source.nextSibling) && !source.added) {
					if (source.className && __checkClass(source.className, classes)) {
						iterator(source);
						source.added = __cnt;
					}
				}
			}
		};
		
		var findThemByTag = {
			'': function(m, source, iterator) {
				var elements = source.getElementsByTagName(m[1]);
				for (var i = 0, l = elements.length; i < l; i++) iterator(elements[i]);
			},
			' ': function(m, source, iterator) {
				var elements = source.getElementsByTagName(m[1]);
				for (var i = 0; (source = elements[i]); i++) if (__genid(source)) iterator(source);
			},
			'>': function(m, source, iterator) {
				var element, elements = source.getElementsByTagName(m[1]);
				for (var i = 0; (element = elements[i]); i++) if (element.parentNode === source) iterator(element);
			},
			'+': function(m, source, iterator) {
				while (source = source.nextSibling) if (source.nodeType === 1) break;
				if (source && source.nodeName === m[1].toUpperCase()) iterator(source);
			},
			'~': function(m, source, iterator) {
				var tag = m[1].toUpperCase();
				while ((source = source.nextSibling) && !source.added) {
					if (source.nodeName === tag) {
						source.added = __cnt;
						iterator(source);
					}
				}
			}
		};
		
		var findThemByTagAndClass = {
			'': function(m, source, iterator) {
				var elements = source.getElementsByTagName(m[1]);
				for (var i = 0; (source = elements[i]); i++) {
					if (source.className && __checkClass(source.className, m[3])) iterator(source);
				}
			},
			' ': function(m, source, iterator) {
				var elements = source.getElementsByTagName(m[1]);
				for (var i = 0; (source = elements[i]); i++) {
					if (source.className && __genid(source) && __checkClass(source.className, m[3])) iterator(source);
				}
			},
			'>': function(m, source, iterator) {
				var element, elements = source.getElementsByTagName(m[1]);
				for (var i = 0; (element = elements[i]); i++) {
					if (element.className && element.parentNode === source && __checkClass(element.className, m[3]))
						iterator(element);
				}
			},
			'+': function(m, source, iterator) {
				var tag = m[1].toUpperCase();
				while (source = source.nextSibling) {
					if (source.nodeType === 1) {
						if (source.className && source.nodeName === tag && __checkClass(source.className, m[3]))
							iterator(element);
						break;
					}
				}
			},
			'~': function(m, source, iterator) {
				var tag = m[1].toUpperCase();
				while ((source = source.nextSibling) && !source.added) {
					if (source.className && source.nodeName === tag && __checkClass(source.className, m[3])) {
						iterator(source);
						source.added = __cnt;
					}
				}
			}
		};
		
		var findThemById = {
			'': function(m, source, iterator) {
				var element = document.getElementById(m[2]);
				if (!element) return;
				if (m[1] && element.nodeName !== m[1].toUpperCase()) return;
				if (m[3]) if (!element.className || !__checkClass(element.className, m[3])) return;
				if (source === document) return iterator(element);
				var parent = element;
				while (parent = parent.parentNode) if (parent === source) iterator(element);
			},
			' ': function(m, source, iterator) {
				var element = document.getElementById(m[2]);
				if (!element) return;
				if (m[1] && element.nodeName !== m[1].toUpperCase()) return;
				if (m[3]) if (!element.className || !__checkClass(element.className, m[3])) return;
				if (source === document) return iterator(element);
				var parent = element;
				while (parent = parent.parentNode) if (parent === source) iterator(element);
			},
			'>': function(m, source, iterator) {
				var element = document.getElementById(m[2]);
				if (!element) return;
				if (m[1] && element.nodeName !== m[1].toUpperCase()) return;
				if (m[3]) if (!element.className || !__checkClass(element.className, m[3])) return;
				if (!element.parentNode && source === document) return iterator(element);
				if (element.parentNode === source) iterator(element);
			},
			'+': function(m, source, iterator) {
				var element = document.getElementById(m[2]);
				if (!element) return;
				if (m[1] && element.nodeName !== m[1].toUpperCase()) return;
				if (m[3]) if (!element.className || !__checkClass(element.className, m[3])) return;
				if (__nextElementSibling(source) === element) iterator(element);
			},
			'~': function(m, source, iterator) {
				var element = document.getElementById(m[2]);
				if (!element) return;
				if (m[1] && element.nodeName !== m[1].toUpperCase()) return;
				if (m[3]) if (!element.className || !__checkClass(element.className, m[3])) return;
				while ((source = source.nextSibling) !== element) {}
				if (source) iterator(element);
			}
		};
		
		var findThem = function(m, splitter) {
			if (m[2]) return findThemById[splitter];
			else if (m[1]) {
				if (m[3]) return findThemByTagAndClass[splitter];
				return findThemByTag[splitter];
			} else if (m[3]) return findThemByClass[splitter];
		};
		
		var matchSelector = function(s) {
			var m;
			if (!(m = __matched[s])) {
				m = __matched[s] = __match.exec(s);
				if (m && m[3] && typeof m[3] === 'string') {
					__genClassHist(m[3] = m[3].split('.'));
				}
			}
			return m;
		};
		
		var findElements = function(source, i, m, func) {
			if (selector.length === i) {
				func(m, source, (function(element) {
					results[length++] = element;
				}), true);
			} else {
				var s = selector[i], s0 = s[0], s1 = s[1], j = i + 1;
				func(m, source, (function(element) {
					findElements(element, j, s0, s1);
				}));
			}
		};
		
		var prepareSelector = function(s) {
			var p = 0, p1, p2, p3, p4, splitter = '', sel = [], m, j = 0;
			while (true) {
				p1 = s.indexOf(' ');
				p2 = s.indexOf('>');
				p3 = s.indexOf('+');
				p4 = s.indexOf('~');
				p1 = p1 === -1 ? 250 : p1;
				p2 = p2 === -1 ? 250 : p2;
				p3 = p3 === -1 ? 250 : p3;
				p4 = p4 === -1 ? 250 : p4;
				p = p1 < p2 ?  (p1 < p3 ? (p1 < p4 ? p1 : p4) : (p3 < p4 ? p3 : p4)) : (p2 < p3 ? (p2 < p4 ? p2 : p4) : (p3 < p4 ? p3 : p4));
				if (p === 250) {
					if (!(m = matchSelector(s))) return sel;
					sel[j++] = [m, findThem(m, splitter)];
					break;
				}
				
				if (!(m = matchSelector(s.substr(0, p)))) return sel;
				sel[j++] = [m, findThem(m, splitter)];
				
				splitter = s.substr(p, 1);
				s = s.substr(p + 1);
			}
			
			return sel;
		}
		
		return function(selectors, source) {
			selectors = selectors.replace(/\s+/g, ' ').replace(/\s*(,|>|\+|~)\s*/g, '$1').split(',');
			source = source || document;
			results = [];
			length = 0;
			for (var i = 0, l = selectors.length; i < l; i++) {
				selector = prepareSelector(selectors[i]);
				if (selector.length === 0) continue;
				__added = {};
				__cnt++;
				findElements(source, 1, selector[0][0], selector[0][1]);
			}
			
			return results;
	}})();
}

var __ = P.select;

var FX = {
	setOpacity: function(element, opacity, displayNone) {
		if (displayNone) {
			if (opacity == 0) {
				element.style.display = 'none';
				_setOpacity(element, 1);
			} else {
				_setOpacity(element, opacity);
				element.style.display = '';
			}
		} else {
			if (opacity == 0) {
				element.style.visibility = 'hidden';
			} else {
				element.style.visibility = 'visible';
				_setOpacity(element, opacity);
			}
		}
	}
};

FX.Base = Class.create({
	initialize: function(element) {
		this.element = _(element);
		this.options = Object.extend({
			start: 0.0,
			stop: 1.0,
			duration: 0.75,
			fps: 150,
			onFinish: P.emptyFunction,
			onStart: P.emptyFunction
		}, arguments[1] || {});
		
		this.duration = this.options.duration * 1000;
		this.diff = this.options.stop - this.options.start;
		this.step = this.diff / this.duration;
		this.start();
	},
	
	start: function() {
		this.stopOn = (new Date()).getTime() + this.duration;
		this.options.onStart();
		this.timer = setInterval(this.loop.bind(this), 1000 / (this.options.fps * this.options.duration));
	},
	
	loop: function() {
		var actual = (new Date()).getTime();
		if (actual > this.stopOn) {
			clearInterval(this.timer);
			this.render(this.diff);
			this.options.onFinish();
			return;
		}
		
		this.render(this.diff - (this.stopOn - actual) * this.step);
	},
	
	render: function(pos) {}
});

FX.Appear = Class.create(FX.Base, {
	initialize: function($super, element) {
		$super(element, arguments[2] || {});
		FX.setOpacity(this.element, this.options.start, true);
	},
	
	render: function(pos) {
		FX.setOpacity(this.element, this.options.start + pos, true);
	}
});

FX.Disappear = Class.create(FX.Appear, {
	initialize: function($super, element) {
		$super(element, Object.extend({
			start: 1.0,
			stop: 0.0
		}, arguments[2] || {}));
	}
});

FX.Show = Class.create(FX.Base, {
	initialize: function($super, element) {
		$super(element, arguments[2] || {});
		FX.setOpacity(this.element, this.options.start);
	},
	
	render: function(pos) {
		FX.setOpacity(this.element, this.options.start + pos);
	}
});

FX.Hide = Class.create(FX.Show, {
	initialize: function($super, element) {
		$super(element, Object.extend({
			start: 1.0,
			stop: 0.0
		}, arguments[2] || {}));
	}
});

FX.Move = Class.create(FX.Base, {
	initialize: function($super, element) {
		this.element = _(element);
		var options = Object.extend({
			x:    0,
			y:    0,
			mode: 'relative'
		}, arguments[2] || {});
		
    	Position.makePositioned(this.element);
    	this.style = this.element.style;
    	
		this.originalLeft = parseFloat(this.style.left || '0');
		this.originalTop  = parseFloat(this.style.top || '0');
		
		if (options.mode == 'absolute') {
			options.x = options.x - this.originalLeft;
			options.y = options.y - this.originalTop;
		}
		
		$super(element, options);
	},
	
	render: function(pos) {
		this.style.left = (this.options.x * pos + this.originalLeft).round() + 'px';
		this.style.top = (this.options.y * pos + this.originalTop).round() + 'px';
    }
});

FX.Highlight = Class.create(FX.Base, {
	initialize: function($super, element) {
		this.element = _(element);
		var options = Object.extend({
			startColor: [255, 0, 0],
			endColor: [255, 255, 255]
		}, arguments[2] || {});
		
    	this.style = this.element.style;
    	
		this.originalColor = this.style.backgroundColor;
		
		$super(element, options);
	},
	
	render: function(pos) {
		if (pos === 1) {
			this.style.backgroundColor = this.originalColor;
		} else {
			var startColor = this.options.startColor;
			this.style.backgroundColor = ['rgb(', 
										  (startColor[0] + pos * (this.options.endColor[0] - startColor[0])).round(), ', ', 
										  (startColor[1] + pos * (this.options.endColor[1] - startColor[1])).round(), ', ', 
										  (startColor[2] + pos * (this.options.endColor[2] - startColor[2])).round(), ')'].join('');
		}
    }
});

/*
Copyright (c) Copyright (c) 2007, Carl S. Yestrau All rights reserved.
Code licensed under the BSD License: http://www.featureblend.com/license.txt
Version: 1.0.3
*/
var FlashDetect = new function(){
	var self = this;
	self.installed = false;
	self.raw = "";
	self.major = -1;
	self.minor = -1;
	self.revision = -1;
	self.revisionStr = "";
	var activeXDetectRules = [
		{
			"name":"ShockwaveFlash.ShockwaveFlash.7",
			"version":function(obj){
				return getActiveXVersion(obj);
			}
		},
		{
			"name":"ShockwaveFlash.ShockwaveFlash.6",
			"version":function(obj){
				var version = "6,0,21";
				try{
					obj.AllowScriptAccess = "always";
					version = getActiveXVersion(obj);
				}catch(err){}
				return version;
			}
		},
		{
			"name":"ShockwaveFlash.ShockwaveFlash",
			"version":function(obj){
				return getActiveXVersion(obj);
			}
		}
	];
	var getActiveXVersion = function(activeXObj){
		var version = -1;
		try{
			version = activeXObj.GetVariable("$version");
		}catch(err){}
		return version;
	};
	var getActiveXObject = function(name){
		var obj = -1;
		try{
			obj = new ActiveXObject(name);
		}catch(err){}
		return obj;
	};
	var parseActiveXVersion = function(str){
		var versionArray = str.split(",");//replace with regex
		return {
			"raw":str,
			"major":parseInt(versionArray[0].split(" ")[1], 10),
			"minor":parseInt(versionArray[1], 10),
			"revision":parseInt(versionArray[2], 10),
			"revisionStr":versionArray[2]
		};
	};
	var parseStandardVersion = function(str){
		var descParts = str.split(/ +/);
		var majorMinor = descParts[2].split(/\./);
		var revisionStr = descParts[3];
		return {
			"raw":str,
			"major":parseInt(majorMinor[0], 10),
			"minor":parseInt(majorMinor[1], 10), 
			"revisionStr":revisionStr,
			"revision":parseRevisionStrToInt(revisionStr)
		};
	};
	var parseRevisionStrToInt = function(str){
		return parseInt(str.replace(/[a-zA-Z]/g, ""), 10) || self.revision;
	};
	self.majorAtLeast = function(version){
		return self.major >= version;
	};
	self.FlashDetect = function(){
		if(navigator.plugins && navigator.plugins.length>0){
			var type = 'application/x-shockwave-flash';
			var mimeTypes = navigator.mimeTypes;
			if(mimeTypes && mimeTypes[type] && mimeTypes[type].enabledPlugin && mimeTypes[type].enabledPlugin.description){
				var version = mimeTypes[type].enabledPlugin.description;
				var versionObj = parseStandardVersion(version);
				self.raw = versionObj.raw;
				self.major = versionObj.major;
				self.minor = versionObj.minor; 
				self.revisionStr = versionObj.revisionStr;
				self.revision = versionObj.revision;
				self.installed = true;
			}
		}else if(navigator.appVersion.indexOf("Mac")==-1 && window.execScript){
			var version = -1;
			for(var i=0; i<activeXDetectRules.length && version==-1; i++){
				var obj = getActiveXObject(activeXDetectRules[i].name);
				if(typeof obj == "object"){
					self.installed = true;
					version = activeXDetectRules[i].version(obj);
					if(version!=-1){
						var versionObj = parseActiveXVersion(version);
						self.raw = versionObj.raw;
						self.major = versionObj.major;
						self.minor = versionObj.minor; 
						self.revision = versionObj.revision;
						self.revisionStr = versionObj.revisionStr;
					}
				}
			}
		}
	}();
};
FlashDetect.release = "1.0.3";
