//==================================================================================================
// jQuery Prototype library
// version 1.0
//--------------------------------------------------------------------------------------------------
// This is a altered version of the Prototype framework (http://www.prototypejs.org/) designed
// to work with jQuery (and it eliminates much of the functionality already provided by
// jQuery.
//==================================================================================================

(function($)
{
	var _proto, $b = {}, a = navigator.userAgent.toLowerCase();
	$.extend($.browser, { chrome: /chrome/.test(a), safari: /webkit/.test(a) && !/chrome/.test(a) });
	
	//================================================================================================
	// Helper functions
	//================================================================================================

	$I = function(x) { return x };
	$A = function(iterable) { if (!iterable) return []; if (iterable.toArray) return iterable.toArray(); var l = iterable.length || 0, r = new Array(l); while (l--) r[l] = iterable[l]; return r };
	$H = function(object) { return new Hash(object) };
	$R = function(start, end, exclusive) { return new ObjectRange(start, end, exclusive) };

	//================================================================================================
	// Class
	//================================================================================================

	Class =
	{
		create: function() { var p = null, a = $A(arguments); if (Object.isFunction(a[0])) p = a.shift(); function k() { this._init.apply(this, arguments) } $.extend(k, Class.Methods); k.superclass = p; k.subclasses = []; if (p) { var subclass = function() {}; subclass.prototype = p.prototype; k.prototype = new subclass; p.subclasses.push(k) } for (var i = 0; i < a.length; i++) k.addMethods(a[i]); if (!k.prototype._init) k.prototype._init = function() {}; k.prototype.constructor = k; return k }
	};

	Class.Methods = 
	{
		addMethods: function(source) { var a = this.superclass && this.superclass.prototype, k = Object.keys(source); if (!Object.keys({ toString: true }).length) k.push("toString", "valueOf"); for (var i = 0, l = k.length; i < l; i++) { var p = k[i], v = source[p]; if (a && Object.isFunction(v) && v.argumentNames().first() == "$super") { var _m = v; v = (function(m) { return function() { return a[m].apply(this, arguments) } })(p).wrap(_m); v.valueOf = _m.valueOf.bind(_m); v.toString = _m.toString.bind(_m) } this.prototype[p] = v } return this }
	};

	//================================================================================================
	// Object
	//================================================================================================

	$.extend(Object, 
	{
		isUndefined: function(object) { return typeof object == 'undefined' },
		isFunction: function(object) { return $.isFunction(object) },
		isArray: function(object) { return $.isArray(object) },
		isString: function(object) { return typeof object == 'string' },
		isNumber: function(object) { return typeof object == 'number' },
		isHash: function(object) { return object instanceof Hash },
		inspect: function(object) { var o = object; try { if (Object.isUndefined(o)) return 'undefined'; if (o === null) return 'null'; return o.inspect ? o.inspect() : String(o) } catch (e) { if (e instanceof RangeError) return '...'; throw e } },
		keys: function(object) { var k = []; for (var p in object) k.push(p); return k },
		values: function(object) { var v = []; for (var p in object) v.push(object[p]); return v },
		clone: function(object) { return $.extend({}, object); },	
		toParams: function(object) { return $H(object).toParams() },
		toJSON: function(object) { var o = object; switch(typeof o) { case 'undefined': case 'function': case 'unknown': return; case 'boolean': return o.toString() } if (o === null) return 'null'; if (o.toJSON) return o.toJSON(); var r = []; for (var p in o) { var v = Object.toJSON(o[p]); if (!Object.isUndefined(v)) r.push(p.toJSON() + ': ' + v) } return '{ ' + r.join(', ') + ' }' }
	});

	//================================================================================================
	// Function
	//================================================================================================
	
	$.extend(Function.prototype, 
	{
		argumentNames: function() { var n = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g, '').split(','); return n.length == 1 && !n[0] ? [] : n },
		bind: function() { if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; var _m = this, a = $A(arguments), o = a.shift(); return function() { return _m.apply(o, a.concat($A(arguments))) } },
		curry: function() { if (!arguments.length) return this; var _m = this, a = $A(arguments); return function() { return _m.apply(this, a.concat($A(arguments))) } },
		delay: function() { var _m = this, a = $A(arguments), timeout = a.shift() * 1000; return window.setTimeout(function() { return _m.apply(_m, a) }, timeout) },
		defer: function() { var a = [0.01].concat($A(arguments)); return this.delay.apply(this, a) },
		wrap: function(wrapper) { var _m = this; return function() { return wrapper.apply(this, [_m.bind(this)].concat($A(arguments))) } },
		methodize: function() { if (this._methodized) return this._methodized; var _m = this; return this._methodized = function() { return _m.apply(null, [this].concat($A(arguments))) } }
	});

	//================================================================================================
	// Date
	//================================================================================================
	
	Date.prototype.toJSON = function() { return '"' + this.getUTCFullYear() + '-' + (this.getUTCMonth() + 1).toPadded(2) + '-' + this.getUTCDate().toPadded(2) + 'T' + this.getUTCHours().toPadded(2) + ':' + this.getUTCMinutes().toPadded(2) + ':' + this.getUTCSeconds().toPadded(2) + 'Z"' };

	//================================================================================================
	// RegExp
	//================================================================================================

	RegExp.prototype.match = RegExp.prototype.test;

	//================================================================================================
	// PeriodicalExecuter
	//================================================================================================
	
	PeriodicalExecuter = Class.create(
	{
		_init: function(callback, frequency) { this.callback = callback; this.frequency = frequency; this.executing = false; this.start() },
		_execute: function() { this.callback(this) },
		_timer: function() { if (!this.executing) { try { this.executing = true; this._execute() } finally { this.executing = false } } },
		start: function() { this.timer = setInterval(this._timer.bind(this), this.frequency * 1000) },
		stop: function() { if (!this.timer) return; clearInterval(this.timer); this.timer = null }
	});

	//================================================================================================
	// String
	//================================================================================================
	
	$.extend(String, 
	{
		interpret: function(value) { return value == null ? '' : String(value) },
		specialChar: { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\\': '\\\\' },
		scriptPattern: '<script[^>]*>([\\S\\s]*?)<\/script>'
	});

	$.extend(String.prototype, 
	{
		gsub: function(pattern, replacement) { var r = '', s = this, m; replacement = arguments.callee.prepare(replacement); while (s.length > 0) { if (m = s.match(pattern)) { r += s.slice(0, m.index); r += String.interpret(replacement(m)); s = s.slice(m.index + m[0].length) } else { r += s; s = '' } } return r },
		sub: function(pattern, replacement, count) { var r = this.gsub.prepare(replacement), c = Object.isUndefined(count) ? 1 : count; return this.gsub(pattern, function(m) { if (--c < 0) return m[0]; return r(m) }) },
		scan: function(pattern, iterator) { this.gsub(pattern, iterator); return String(this) },
		truncate: function(length, truncation) { var l = length || 30,	t = Object.isUndefined(truncation) ? '...' : truncation; return this.length > l ? this.slice(0, l - t.length) + t : String(this) },
		strip: function() { return this.replace(/^\s+/, '').replace(/\s+$/, '') },
		stripTags: function() { return this.replace(/<\/?[^>]+>/gi, '') },
		stripScripts: function() { return this.replace(new RegExp(String.scriptPattern, 'img'), '') },
		extractScripts: function() { var a = new RegExp(String.scriptPattern, 'img'), o = new RegExp(String.scriptPattern, 'im'); return (this.match(a) || []).map(function(t) { return (t.match(o) || ['', ''])[1] }) },
		evalScripts: function() { return this.extractScripts().map(function(s) { return eval(s) }) },
		succ: function() { return this.slice(0, this.length - 1) + String.fromCharCode(this.charCodeAt(this.length - 1) + 1) },
		times: function(count) { return count < 1 ? '' : new Array(count + 1).join(this) },
		camelize: function() { var p = this.split('-'), l = p.length; if (l == 1) return p[0]; var c = this.charAt(0) == '-' ? p[0].charAt(0).toUpperCase() + p[0].substring(1) : p[0]; for (var i = 1; i < l; i++) c += p[i].charAt(0).toUpperCase() + p[i].substring(1); return c },
		capitalize: function() { return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase() },
		underscore: function() { return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase() },
		dasherize: function() { return this.gsub(/_/,'-') },
		inspect: function(doubleQuotes) { var s = this.gsub(/[\x00-\x1f\\]/, function(m) { var c = String.specialChar[m[0]]; return c ? c : '\\u00' + m[0].charCodeAt().toPadded(2, 16) }); if (doubleQuotes) return '"' + s.replace(/"/g, '\\"') + '"'; return "'" + s.replace(/'/g, '\\\'') + "'" },
		include: function(pattern) { return this.indexOf(pattern) > -1 },
		startsWith: function(pattern) { return this.indexOf(pattern) === 0 },
		endsWith: function(pattern) { var d = this.length - pattern.length; return d >= 0 && this.lastIndexOf(pattern) === d },
		empty: function() { return this == ''; },
		blank: function() { return /^\s*$/.test(this) },
		interpolate: function(object, pattern) { return new Template(this, pattern).eval(object) },
		fromParams: function(separator) { var m = this.strip().match(/([^?#]*)(#.*)?$/); if (!m) return {}; return m[1].split(separator || '&').inject({}, function(h, p) { if ((p = p.split('='))[0]) { var k = decodeURIComponent(p.shift()), v = p.length > 1 ? p.join('=') : p[0]; if (v != undefined) v = decodeURIComponent(v); if (k in h) { if (!Object.isArray(h[k])) h[k] = [h[k]]; h[k].push(v) } else h[k] = v } return h }) },
		toArray: function() { return this.split('') },
		toJSON: function() { return this.inspect(true) }
	});

	String.prototype.gsub.prepare = function(replacement) { if (Object.isFunction(replacement)) return replacement; var template = new Template(replacement); return function(match) { return template.eval(match) } };

	//==================================================================================================
	// Template
	//==================================================================================================

	Template = Class.create(
	{
		_pattern: /(^|.|\r|\n)(#\{(.*?)\})/,
		_init: function(template, pattern) { this.template = template.toString(); this.pattern = pattern || this._pattern },
		eval: function(object) { if (Object.isHash(object)) object = object.toObject(); return this.template.gsub(this.pattern, function(m) { if (object == null) return ''; var b = m[1] || ''; if (b == '\\') return m[2]; var c = object, e = m[3], p = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; m = p.exec(e); if (m == null) return b; while (m != null) { var k = m[1].startsWith('[') ? m[2].gsub('\\\\]', ']') : m[1]; c = c[k]; if (c == null || m[3] == '') break; e = e.substring(m[3] == '[' ? m[1].length : m[0].length); m = p.exec(e) } return b + String.interpret(c) }) }
	});

	//================================================================================================
	// Enumerable
	//================================================================================================
	
	Enumerable = 
	{
		each: function(iterator, context) { var i = 0; try { this._each(function(v) { iterator.call(context, v, i++) }) } catch (e) { if (e != $b) throw e } return this },
		eachSlice: function(number, iterator, context) { var i = -number, s = [], a = this.toArray(); if (number < 1) return a; while ((i += number) < a.length) s.push(a.slice(i, i + number)); return s.map(iterator, context) },
		all: function(iterator, context) { iterator = iterator || $I; var r = true; this.each(function(v, i) { r = r && !!iterator.call(context, v, i); if (!r) throw $b }); return r },
		any: function(iterator, context) { iterator = iterator || $I; var r = false; this.each(function(v, i) { if (r = !!iterator.call(context, v, i)) throw $b }); return r },
		map: function(iterator, context) { iterator = iterator || $I; var r = []; this.each(function(v, i) { r.push(iterator.call(context, v, i)) }); return r },
		find: function(iterator, context) { var r; this.each(function(v, i) { if (iterator.call(context, v, i)) { r = v; throw $b } }); return r },
		filter: function(iterator, context) { var r = []; this.each(function(v, i) { if (iterator.call(context, v, i)) r.push(v) }); return r },
		grep: function(filter, iterator, context) { iterator = iterator || $I; var r = []; if (Object.isString(filter)) filter = new RegExp(filter); this.each(function(v, i) { if (filter.match(v)) r.push(iterator.call(context, v, i)) }); return r },
		include: function(object) { if (Object.isFunction(this.indexOf)) return (this.indexOf(object) != -1); var f = false; this.each(function(v) { if (v == object) { f = true; throw $b } }); return f },
		inGroupsOf: function(number, fill) { fill = Object.isUndefined(fill) ? null : fill; return this.eachSlice(number, function(s) { while(s.length < number) s.push(fill); return s }) },
		inject: function(memo, iterator, context) { this.each(function(v, i) { memo = iterator.call(context, memo, v, i) }); return memo },
		invoke: function(method) { var a = $A(arguments).slice(1); return this.map(function(v) { return v[method].apply(v, a) }) },
		max: function(iterator, context) { iterator = iterator || $I; var r; this.each(function(v, i) { v = iterator.call(context, v, i); if (r == null || v >= r) r = v }); return r },
		min: function(iterator, context) { iterator = iterator || $I; var r; this.each(function(v, i) { v = iterator.call(context, v, i); if (r == null || v < r) r = v }); return r },
		partition: function(iterator, context) { iterator = iterator || $I; var t = [], f = []; this.each(function(v, i) { (iterator.call(context, v, i) ? t : f).push(v) }); return [t, f] },
		pluck: function(property) { var r = []; this.each(function(v) { r.push(v[property]) }); return r },
		reject: function(iterator, context) { var r = []; this.each(function(v, i) { if (!iterator.call(context, v, i)) r.push(v) }); return r },
		sortBy: function(iterator, context) { return this.map(function(v, i) { return { value: v, criteria: iterator.call(context, v, i) } }).sort(function(l, r) { var a = l.criteria, b = r.criteria; return a < b ? -1 : a > b ? 1 : 0 }).pluck('value') },
		zip: function() { var o = $I, a = $A(arguments); if (Object.isFunction(a.last())) o = a.pop(); var c = [this].concat(a).map($A); return this.map(function(v, i) { return o(c.pluck(i)) }) },
		size: function() { return this.toArray().length },
		inspect: function() { return '#<Enumerable:' + this.toArray().inspect() + '>' },
		toArray: function() { return this.map() }
	};

	//================================================================================================
	// Array
	//================================================================================================
	
	_proto = Array.prototype;
	_proto._reverse = _proto.reverse;

	$.extend(_proto, Enumerable, 
	{
		_each: function(iterator) { for (var i = 0, l = this.length; i < l; 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(v) { return v != null }) },
		flatten: function() { return this.inject([], function(a, v) { return a.concat(Object.isArray(v) ? v.flatten() : [v]) }) },
		without: function() { var v = $A(arguments); return this.select(function(v) { return !v.include(v) }) },
		reverse: function(inline) { return (inline !== false ? this : this.toArray())._reverse() },
		reduce: function() { return this.length > 1 ? this : this[0] },
		uniq: function(sorted) { return this.inject([], function(a, v, i) { if (i == 0 || (sorted ? a.last() != v : !a.include(v))) a.push(v); return a }) },
		intersect: function(array) { return this.uniq().filter(function(i) { return array.find(function(v) { return i === v }) }) },
		size: function() { return this.length },
		inspect: function() { return '[ ' + this.map(Object.inspect).join(', ') + ' ]' },
		clone: function() { return [].concat(this) },
		toArray: function() { return this.clone() },
		toJSON: function() { var r = []; this.each(function(o) { var v = Object.toJSON(o); if (!Object.isUndefined(v)) r.push(v) }); return '[ ' + r.join(', ') + ' ]' }
	});

	if (Object.isFunction(_proto.forEach)) _proto._each = _proto.forEach;
	if (!_proto.indexOf) _proto.indexOf = function(item, i) { i || (i = 0); var l = this.length; if (i < 0) i = l + i; for (; i < l; i++) if (this[i] === item) return i; return -1 };
	if (!_proto.lastIndexOf) _proto.lastIndexOf = function(item, i) { i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; var n = this.slice(0, i).reverse().indexOf(item); return (n < 0) ? n : i - n - 1 };

	//================================================================================================
	// Number
	//================================================================================================

	_proto = Number.prototype;
	$.extend(_proto, 
	{
		succ: function() { return this + 1; },
		times: function(iterator, context) { $R(0, this, true).each(iterator, context); return this },
		toHex: function() { return this.toPadded(2, 16) },
		toPadded: function(length, radix) { var s = this.toString(radix || 10); return '0'.times(length - s.length) + s; },
		toJSON: function() { return isFinite(this) ? this.toString() : 'null' }
	});

	['abs', 'round', 'ceil', 'floor'].each(function(m) { _proto[m] = Math[m].methodize() });

	//================================================================================================
	// Hash
	//================================================================================================

	Hash = Class.create(Enumerable, 
	{
		_init: function(object) { this._object = Object.isHash(object) ? object.toObject() : Object.clone(object) },
		_each: function(iterator) { for (var k in this._object) { var v = this._object[k], p = [k, v]; p.key = k; p.value = v; iterator(p) } },
		set: function(key, value) { return this._object[key] = value },
		get: function(key) { if (this._object[key] !== Object.prototype[key]) return this._object[key] },
		unset: function(key) { var v = this._object[key]; delete this._object[key]; return v },
		keys: function() { return this.pluck('key') },
		values: function() { return this.pluck('value') },
		index: function(value) { var m = this.find(function(p) { return p.value === value }); return m && m.key },
		merge: function(object) { return this.clone().update(object) },
		update: function(object) { return new Hash(object).inject(this, function(r, p) { r.set(p.key, p.value); return r }) },
		inspect: function() {	return '#<Hash:{ ' + this.map(function(pair) { return pair.map(Object.inspect).join(': ') }).join(', ') + ' }>' },
		clone: function() { return new Hash(this) },
		toObject: function() { return Object.clone(this._object) },
		toParams: function() { return this.inject([], function(r, p) { var k = encodeURIComponent(p.key), v = p.value; if (v && typeof v == 'object') { if (Object.isArray(v)) return r.concat(v.map(Hash.toPair.curry(k))) } else r.push(Hash.toPair(k, v)); return r }).join('&') },
		toJSON: function() { return Object.toJSON(this.toObject()) }
	});

	Hash.toPair = function(key, value) { if (Object.isUndefined(value)) return key; return key + '=' + encodeURIComponent(String.interpret(value))	};
	
	//================================================================================================
	// Object range
	//================================================================================================

	ObjectRange = Class.create(Enumerable, 
	{
		_init: function(start, end, exclusive) { this.start = start; this.end = end; this.exclusive = exclusive },
		_each: function(iterator) { var v = this.start; while (this.include(v)) { iterator(v); v = v.succ() } },
		include: function(value) { if (value < this.start) return false; if (this.exclusive) return value < this.end; return value <= this.end }
	});

})(jQuery);