<commit_before>if (!this.__proto__) {
	var fix = [Function, Number, Boolean, String, Array, Date, RegExp];
	for (var i in fix)
		fix[i].prototype.__proto__ = fix[i].prototype;
}

new function() { 
	function inject(dest, src, base, generics) {
		function field(name, generics) {
			var val = src[name], res = val, prev = dest[name];
			if (val !== Object.prototype[name]) {
				if (typeof val == 'function') {
					if (generics) generics[name] = function(bind) {
						return bind && dest[name].apply(bind,
							Array.prototype.slice.call(arguments, 1));
					}
					if (prev && /\bthis\.base\b/.test(val)) {
						var fromBase = base && base[name] == prev;
						res = (function() {
							var tmp = this.base;
							this.base = fromBase ? base[name] : prev;
							try { return val.apply(this, arguments); }
							finally { this.base = tmp; }
						}).pretend(val);
					}
				}
				dest[name] = res;
			}
		}
		if (src) {
			for (var name in src)
				if (visible(src, name) && !/^(prototype|constructor|toString|valueOf|statics|_generics)$/.test(name))
					field(name, generics);
			field('toString');
			field('valueOf');
		}
	}

	function extend(obj) {
		function ctor(dont) {
			if (!this.__proto__) this.__proto__ = obj;
			if (this.initialize && dont !== ctor.dont)
				return this.initialize.apply(this, arguments);
		}
		ctor.prototype = obj;
		return ctor;
	}

	function visible(obj, name) {
		return obj[name] !== obj.__proto__[name]&& name.indexOf('__') != 0;
	}

	inject(Function.prototype, {
		inject: function(src) {
			var proto = this.prototype, base = proto.__proto__ && proto.__proto__.constructor;
			inject(proto, src, base && base.prototype, src && src._generics && this);
			inject(this, src && src.statics, base);
			for (var i = 1, j = arguments.length; i < j; i++)
				this.inject(arguments[i]);
			return this;
		},

		extend: function(src) {
			var proto = new this(this.dont), ctor = proto.constructor = extend(proto);
			ctor.dont = {};
			inject(ctor, this);
			return this.inject.apply(ctor, arguments);
		},

		pretend: function(fn) {
			this.toString = function() {
				return fn.toString();
			}
			this.valueOf = function() {
				return fn.valueOf();
			}
			return this;
		}
	});

	Base = Object.extend({
		has: function(name) {
			return visible(this, name);
		},

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

		extend: function() {
			var res = new (extend(this));
			return res.inject.apply(res, arguments);
		}
	});
}

Function.inject(new function() {
	function timer(that, type, args, ms) {
		var fn = that.bind.apply(that, Array.slice(args, 1));
		var timer = window['set' + type](fn, ms);
		fn.clear = function() {
			clearTimeout(timer);
			clearInterval(timer);
		};
		return fn;
	}

	return {
		_generics: true,

		name: function() {
			var match = this.toString().match(/^\s*function\s*(\w*)/);
			return match && match[1];
		},

		parameters: function() {
			var str = this.toString().match(/^\s*function[^\(]*\(([^\)]*)/)[1];
			return str ? str.split(/\s*,\s*/) : [];
		},

		body: function() {
			return this.toString().match(/^\s*function[^\{]*\{([\s\S]*)\}\s*$/)[1];
		},

		delay: function(ms) {
			return timer(this, 'Timeout', arguments, ms);
		},

		periodic: function(ms) {
			return timer(this, 'Interval', arguments, ms);
		},

		bind: function(obj) {
			var that = this, args = Array.slice(arguments, 1);
			return function() {
				return that.apply(obj, args.concat(Array.create(arguments)));
			}
		},

		attempt: function(obj) {
			var that = this, args = Array.slice(arguments, 1);
			return function() {
				try {
					return that.apply(obj, args.concat(Array.create(arguments)));
				} catch(e) {
					return e;
				}
			}
		}
	}
});

Enumerable = new function() {
	Base.iterate = function(fn, name) {
		return function(iter, bind) {
			if (!iter) iter = function(val) { return val };
			else if (typeof iter != 'function') iter = function(val) { return val == iter };
			if (!bind) bind = this;
			var prev = bind[name];
			bind[name] = iter;
			try { return fn.call(this, iter, bind, this); }
			finally { prev ? bind[name] = prev : delete bind[name] }
		};
	};

	Base.stop = {};

	var each_Array = Array.prototype.forEach || function(iter, bind) {
		for (var i = 0, j = this.length; i < j; ++i)
			bind.__each(this[i], i, this);
	};

	var each_Object = function(iter, bind) {
		for (var i in this) {
			var val = this[i];
			if (val !== this.__proto__[i]&& i.indexOf('__') != 0)
				bind.__each(val, i, this);
		}
	};

	return {
		_generics: true,

		each: Base.iterate(function(iter, bind) {
			try { (this.length != null ? each_Array : each_Object).call(this, iter, bind); }
			catch (e) { if (e !== Base.stop) throw e; }
			return bind;
		}, '__each'),

		findEntry: Base.iterate(function(iter, bind, that) {
			return this.each(function(val, key) {
				this.result = bind.__findEntry(val, key, that);
				if (this.result) {
					this.key = key;
					this.value = val;
					throw Base.stop;
				}
			}, {});
		}, '__findEntry'),

		find: function(iter, bind) {
			return this.findEntry(iter, bind).result;
		},

		remove: function(iter, bind) {
			var entry = this.findEntry(iter, bind);
			delete this[entry.key];
			return entry.value;
		},

		some: function(iter, bind) {
			return this.find(iter, bind) != null;
		},

		every: Base.iterate(function(iter, bind, that) {
			return this.find(function(val, i) {
				return !this.__every(val, i, that);
			}, bind) == null;
		}, '__every'),

		map: Base.iterate(function(iter, bind, that) {
			return this.each(function(val, i) {
				this[this.length] = bind.__map(val, i, that);
			}, []);
		}, '__map'),

		filter: Base.iterate(function(iter, bind, that) {
			return this.each(function(val, i) {
				if (bind.__filter(val, i, that))
					this[this.length] = val;
			}, []);
		}, '__filter'),

		max: Base.iterate(function(iter, bind, that) {
			return this.each(function(val, i) {
				val = bind.__max(val, i, that);
				if (val >= (this.max || val)) this.max = val;
			}, {}).max;
		}, '__max'),

		min: Base.iterate(function(iter, bind, that) {
			return this.each(function(val, i) {
				val = bind.__min(val, i, that);
				if (val <= (this.min || val)) this.min = val;
			}, {}).min;
		}, '__min'),

		pluck: function(prop) {
			return this.map(function(val) {
				return val[prop];
			});
		},

		sortBy: Base.iterate(function(iter, bind, that) {
			return this.map(function(val, i) {
				return { value: val, compare: bind.__sortBy(val, i, that) };
			}, bind).sort(function(left, right) {
				var a = left.compare, b = right.compare;
				return a < b ? -1 : a > b ? 1 : 0;
			}).pluck('value');
		}, '__sortBy'),

		toArray: function() {
			return this.map();
		}
	};
}

Base.inject({
	_generics: true,

	each: Enumerable.each,

	debug: function() {
		return /^(string|number|function|regexp)$/.test(Base.type(this)) ? this
			: this.each(function(val, key) { this.push(key + ': ' + val); }, []).join(', ');
	},

	clone: function() {
		return Base.each(this, function(val, i) {
			this[i] = val;
		}, new this.constructor());
	},

	statics: {
		inject: function() {
			var args = arguments;
			Base.each([Array, Number, RegExp, String], function(ctor) {
				ctor.inject.apply(ctor, args);
			});
			return this.base.apply(this, args);
		},

		extend: function() {
			var ret = this.base();
			ret.extend = Function.extend;
			ret.inject = Function.inject;
			ret.inject.apply(ret, arguments);
			return ret;
		},

		check: function(obj) {
			return !!(obj || obj === 0);
		},

		type: function(obj) {
			return (obj || obj === 0) && ((obj._type || obj.nodeName && obj.nodeType == 1 && 'element') || typeof obj) || null;
		},

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

}, Base.prototype);

$each = Base.each;
$stop = $break = Base.stop;
$check = Base.check;
$type = Base.type;

Hash = Base.extend(Enumerable, {
	_generics: true,

	initialize: function() {
		return this.merge.apply(this, arguments);
	},

	merge: function() {
		return Base.each(arguments, function(obj) {
			Base.each(obj, function(val, key) {
				this[key] = Base.type(this[key]) == 'object'
					? Hash.prototype.merge.call(this[key], val) : val;
			}, this);
		}, this);
	},

	keys: function() {
		return this.map(function(val, key) {
			return key;
		});
	},

	values: Enumerable.toArray,

	statics: {
		create: function(obj) {
			return arguments.length == 1 && obj.constructor == Hash
				? obj : Hash.prototype.initialize.apply(new Hash(), arguments);
		}
	}
});

$H = Hash.create;

Array.inject(new function() {
	var proto = Array.prototype;

	var fields = Hash.merge({}, Enumerable, {
		_generics: true,
		_type: 'array',

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

		lastIndexOf: proto.lastIndexOf || function(obj, i) {
			i = i != null ? i : this.length - 1;
			if (i < 0) i = Math.max(0, this.length + i);
			for (; i >= 0; i--)
				if (this[i] == obj) return i;
			return -1;
		},

		filter: Base.iterate(proto.filter || function(iter, bind, that) {
			var res = [];
			for (var i = 0, j = this.length; i < j; ++i)
				if (bind.__filter(this[i], i, that))
					res[res.length] = this[i];
			return res;
		}, '__filter'),

		map: Base.iterate(proto.map || function(iter, bind, that) {
			var res = new Array(this.length);
			for (var i = 0, j = this.length; i < j; ++i)
				res[i] = bind.__map(this[i], i, that);
			return res;
		}, '__map'),

		every: Base.iterate(proto.every || function(iter, bind, that) {
			for (var i = 0, j = this.length; i < j; ++i)
				if (!bind.__every(this[i], i, that))
					return false;
			return true;
		}, '__every'),

		some: Base.iterate(proto.some || function(iter, bind, that) {
			for (var i = 0, j = this.length; i < j; ++i)
				if (bind.__some(this[i], i, that))
					return true;
			return false;
		}, '__some'),

		reduce: proto.reduce || function(fn, value) {
			var i = 0;
			if (arguments.length < 2 && this.length) value = this[i++];
			for (var l = this.length; i < l; i++)
				value = fn.call(null, value, this[i], i, this);
			return value;
		},

		findEntry: function(iter, bind) {
			if (iter && !/^(function|regexp)$/.test(Base.type(iter))) {
				var i = this.indexOf(iter);
				return { key: i != -1 ? i : null, value: this[i], result: i != -1 };
			}
			return Enumerable.findEntry.call(this, iter, bind);
		},

		remove: function(iter, bind) {
			var entry = this.findEntry(iter, bind);
			if (entry.key != null)
				this.splice(entry.key, 1);
			return entry.value;
		},

		toArray: function() {
			var res = this.concat([]);
			return res[0] == this ? Enumerable.toArray.call(this) : res;
		},

		clone: function() {
			return this.toArray();
		},

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

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

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

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

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

		subtract: function(items) {
			for (var i = 0, j = items.length; i < j; ++i)
				Array.remove(this, items[i]);
			return this;
		},

		intersect: function(items) {
			for (var i = this.length - 1; i >= 0; i--)
				if (!items.find(this[i]))
					this.splice(i, 1);
			return this;
		},

		associate: function(obj) {
			if (obj.length != null) {
				var that = this;
				return Base.each(obj, function(name, index) {
					this[name] = that[index];
					if (index == that.length) throw Base.stop;
				}, {});
			} else {
				obj = Hash.create(obj);
				return Base.each(this, function(val) {
					var type = Base.type(val);
					obj.each(function(hint, name) {
						if (hint == 'any' || type == hint) {
							this[name] = val;
							delete obj[name];
							throw Base.stop;
						}
					}, this);
				}, {});
			}
		},

		flatten: function() {
			return this.each(function(val) {
				if (val != null && val.flatten) this.append(val.flatten());
				else this.push(val);
			}, []);
		},

		swap: function(i, j) {
			var tmp = this[j];
			this[j] = this[i];
			this[i] = tmp;
			return tmp;
		},

		shuffle: function() {
			var res = this.clone();
			var i = this.length;
			while (i--) res.swap(i, Math.rand(0, i));
			return res;
		},

		statics: {
			create: function(list) {
				if (!Base.check(list)) return [];
				if (Base.type(list) == 'array') return list;
				if (list.toArray)
					return list.toArray();
				if (list.length != null) {
					var res = [];
					for (var i = 0, j = list.length; i < j; ++i)
						res[i] = list[i];
				} else {
					res = [list];
				}
				return res;
			},

			extend: function(src) {
				var ret = Base.extend(extend, src);
				ret.extend = Function.extend;
				return ret;
			}
		}
	});
	['push','pop','shift','unshift','sort','reverse','join','slice','splice','concat'].each(function(name) {
		fields[name] = proto[name];
	});
	var extend = Base.clone(fields);
	extend.length = 0;
	extend.toString = proto.join;
	return fields;
});

$A = Array.create;

String.inject({
	_type: 'string',

	test: function(exp, param) {
		return new RegExp(exp, param || '').test(this);
	},

	toArray: function() {
		return this ? this.split(/\s+/) : [];
	},

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

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

	camelize: function(separator) {
		return this.replace(new RegExp(separator || '-', 'g'), function(match) {
			return match.charAt(1).toUpperCase();
		});
	},

	uncamelize: function(separator) {
		separator = separator || '-';
		return this.replace(/[a-zA-Z][A-Z0-9]|[0-9][a-zA-Z]/g, function(match) {
			return match.charAt(0) + separator + match.charAt(1);
		});
	},

	hyphenate: function(separator) {
		return this.uncamelize(separator).toLowerCase();
	},

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

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

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

	contains: function(string, s) {
		return (s ? (s + this + s).indexOf(s + string + s) : this.indexOf(string)) != -1;
	},

	times: function(count) {
		return count < 1 ? '' : new Array(count + 1).join(this);
	},
});

Number.inject({
	_type: 'number',

	toInt: String.prototype.toInt,

	toFloat: String.prototype.toFloat,

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

	toPaddedString: function(length, base) {
		var str = this.toString(base || 10);
		return '0'.times(length - str.length) + str;
	}
});

RegExp.inject({
	_type: 'regexp'
});

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

Array.inject({

	hexToRgb: function(toArray) {
		if (this.length >= 3) {
			var rgb = [];
			for (var i = 0; i < 3; i++)
				rgb.push((this[i].length == 1 ? this[i] + this[i] : this[i]).toInt(16));
			return toArray ? rgb : 'rgb(' + rgb.join(',') + ')';
		}
	},

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

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

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

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

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

Json = new function() {
	var special = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' };

	function replace(chr) {
		return special[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
	}

	return {
		encode: function(obj) {
			switch (Base.type(obj)) {
				case 'string':
					return '"' + obj.replace(/[\x00-\x1f\\"]/g, replace) + '"';
				case 'array':
					return '[' + obj.map(this.encode).compact().join(',') + ']';
				case 'object':
					return '{' + Base.each(obj, function(val, key) {
						if (val != undefined)
							this.push(Json.encode(key) + ':' + Json.encode(val));
					}, []) + '}';
				default:
					return obj + "";
			}
			return null;
		},

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

Browser = new function() {
	var name = (navigator.platform.match(/(MAC)|(WIN)|(LINUX)|(NIX)/i) || ['OTHER'])[0].toUpperCase();
	var js/*@cc_on=@_jscript_version@*/, xpath = !!document.evaluate;
	var webkit = document.childNodes && !document.all && !navigator.taintEnabled;
	var ret = {
		PLATFORM: name,
		WEBKIT: webkit,
		WEBKIT2: webkit && !xpath,
		WEBKIT3: webkit && xpath,
		OPERA: !!window.opera,
		GECKO: !!document.getBoxObjectFor,
		IE: !!js,
		IE5: js >= 5 && js < 5.5,
		IE55: js == 5.5,
		IE6: js == 5.6,
		IE7: js == 5.7,
		MACIE: js && name == 'MAC',
		XPATH: xpath
	};
	ret[name] = true;
	return ret;
};

DomElements = Array.extend(new function() {
	var unique = 0;
	return {
		initialize: function(els) {
			this._unique = unique++;
			this.append(els && els.length != null && !els.nodeType ? els : arguments);
		},

		push: function() {
			this.append(arguments);
			return this.length;
		},

		append: function(items) {
			for (var i = 0, l = items.length; i < l; ++i) {
				var el = items[i];
				if ((el = el && (el._wrapper || DomElement.get(el))) && el._unique != this._unique) {
					el._unique = this._unique;
					this[this.length++] = el;
				}
			}
			return this;
		},

		statics: {
			inject: function(src) {
				return this.base(Base.each(src || {}, function(val, key) {
					this[key] = typeof val != 'function' ? val : function() {
						var args = arguments, values;
						this.each(function(obj) {
							var ret = (obj[key] || val).apply(obj, args);
							if (ret !== undefined && ret != obj) {
								values = values || (Base.type(ret) == 'element'
									? new obj._elements() : []);
								values.push(ret);
							}
						});
						return values || this;
					}
				}, {}));
			}
		}
	};
});

DomElement = Base.extend(new function() {
	var elements = [];
	var tags = {}, classes = {}, classCheck, unique = 0;

	function dispose(force) {
		for (var i = elements.length - 1; i >= 0; i--) {
			var el = elements[i];
	        if (force || (!el || el != window && el != document &&
				(!el.parentNode || !el.offsetParent))) {
	            if (el) {
					var obj = el._wrapper;
					if (obj && obj.dispose) obj.dispose();
					el._wrapper = el._unique = null;
				}
				if (!force) elements.splice(i, 1);
	        }
		}
	}

	function inject(src) {
		src = src || {};
		(src._methods || []).each(function(name) {
			src[name] = function(arg) {
				var ret = this.$[name] && this.$[name](arg);
				return ret === undefined ? this : ret;
			}
		});
		(src._properties || []).each(function(name) {
			var part = name.capitalize();
			src['get' + part] = function() {
				return this.$[name];
			}
			src['set' + part] = function(value) {
				this.$[name] = value;
				return this;
			}
		});
		delete src._methods;
		delete src._properties;
		return Function.inject.call(this, src);
	}

	function getConstructor(el) {
		var match;
		return classCheck && el.className && (match = el.className.match(classCheck)) && match[2] && classes[match[2]] ||
			el.tagName && tags[el.tagName] ||
			(el.className === undefined ? DomElement : HtmlElement)
	}

	var dont = {};

	return {
		_type: 'element',
		_elements: DomElements,

		initialize: function(el, props) {
			if (this._tag && Base.type(el) == 'object') {
				props = el;
				el = this._tag;
			}
			if (typeof(el) == 'string') {
				if (Browser.IE && props && (props.name || props.type))
					el = '<' + el
						+ (props.name ? ' name="' + props.name + '"' : '')
						+ (props.type ? ' type="' + props.type + '"' : '') + '>';
				el = document.createElement(el);
			} else {
				if (el._wrapper) return el._wrapper;
			}
			if (props == dont) props = null;
			else {
				var ctor = getConstructor(el);
				if (ctor != this.constructor)
					return new ctor(el, props);
			}
			this.$ = el;
			el._wrapper = this;
			elements[elements.length] = el;
			if (props) this.set(props);
		},

		statics: {
			inject: function(src) {
				if (src) {
					var proto = this.prototype, that = this;
					src.statics = Base.each(src, function(val, name) {
						if (typeof val == 'function' && !this[name] && !that[name]) {
							this[name] = function(el, param1, param2) {
								if (el) try {
									proto.$ = el.$ || el;
									return proto[name](param1, param2);
								} finally {
									delete proto.$;
								}
							}
						}
					}, src.statics || {});
					inject.call(this, src);
					delete src.toString;
					proto._elements.inject(src);
				}
				return this;
			},

			extend: function(src) {
				var ret = this.base();
				var init = src.initialize;
				if (init) src.initialize = function(el, props) {
					var ret = this.base(el, props);
					if (ret) return ret;
					init.call(this);
				}
				inject.call(ret, src);
				ret.inject = inject;
				if (src) {
					if (src._tag) tags[src._tag.toLowerCase()] = tags[src._tag.toUpperCase()] = ret;
					if (src._class) {
						classes[src._class] = ret;
						classCheck = new RegExp('(^|\\s)(' + Base.each(classes, function(val, name) {
							this.push(name);
						}, []).join('|') + ')(\\s|$)');
						if (src.initialize) Window.addEvent('domready', function() {
							Document.getElements('.' + src._class);
						});
					}
				}
				return ret;
			},

			get: function(el) {
				return el ? typeof el == 'string'
					? Document.getElement(el)
					: el._wrapper || el._elements && el || new (getConstructor(el))(el, dont)
						: null;
			},

			unwrap: function(el) {
				return el && el.$ || el;
			},

			collect: function(el) {
				elements.push(el);
			},

			unique: function(el) {
				if (!el._unique) {
					DomElement.collect(el);
					el._unique = ++unique;
				}
			},

			isAncestor: function(el, parent) {
				if (parent != document)
					for (el = el && el.parentNode; el != parent; el = el.parentNode)
						if (!el) return false;
				return true;
			},

			dispose: function() {
				dispose(true);
			}
		}
	}
});

DomElement.inject(new function() {
	var properties = {
		'class': 'className', className: 'className', 'for': 'htmlFor',
		colspan: 'colSpan', rowspan: 'rowSpan', accesskey: 'accessKey',
		tabindex: 'tabIndex', maxlength: 'maxLength', readonly: 'readOnly',
		value: 'value', disabled: 'disabled', checked: 'checked',
		multiple: 'multiple', selected: 'selected'
	};

	var flags = {
		href: 2, src: 2
	};

	var handlers = { get: {}, set: {} };

	function handle(that, prefix, name, val) {
		var list = handlers[prefix];
		var fn = name == 'events' && prefix == 'set' ? that.addEvents : list[name];
		if (fn === undefined)
			fn = list[name] = that[prefix +
				name.charAt(0).toUpperCase() + name.substring(1)] || null;
		if (fn) return fn[val && val.push ? 'apply' : 'call'](that, val);
		else return that[prefix + 'Property'](name, val);
	}

	function walk(el, name, start) {
		el = el[start ? start : name];
		while (el && Base.type(el) != 'element') el = el[name];
		return DomElement.get(el);
	}

	function create(where) {
		return function() {
			return this.create(arguments)['insert' + where](this);
		}
	}

	return {
		set: function(name, value) {
			switch (Base.type(name)) {
				case 'string':
					return handle(this, 'set', name, value);
				case 'object':
					return Base.each(name, function(val, key) {
						handle(this, 'set', key, val);
					}, this);
			}
			return this;
		},

		get: function(name) {
			return handle(this, 'get', name);
		},

		getTag: function() {
			return this.$.tagName.toLowerCase();
		},

		getId: function() {
			return this.$.id;
		},

		getPrevious: function() {
			return walk(this.$, 'previousSibling');
		},

		getNext: function() {
			return walk(this.$, 'nextSibling');
		},

		getFirst: function() {
			return walk(this.$, 'nextSibling', 'firstChild');
		},

		getLast: function() {
			return walk(this.$, 'previousSibling', 'lastChild');
		},

		getParent: function() {
			return DomElement.get(this.$.parentNode);
		},

		getChildren: function() {
		 	return new this._elements(this.$.childNodes);
		},

		hasChildren: function() {
			return this.$.hasChildNodes();
		},

		hasParent: function(el) {
			return DomElement.isAncestor(this.$, DomElement.unwrap(el));
		},

		hasChild: function(el) {
			return DomElement.isAncestor(DomElement.unwrap(el), this.$);
		},

		appendChild: function(el) {
			if (el = DomElement.get(el)) {
				var text = Browser.IE && el.$.text;
		 		this.$.appendChild(el.$);
				if (text) el.$.text = text;
			}
			return this;
		},

		insertBefore: function(el) {
			if (el = DomElement.get(el)) {
				var text = Browser.IE && el.text;
				el.$.parentNode.insertBefore(this.$, el.$);
				if (text) this.$.text = text;
			}
			return this;
		},

		insertAfter: function(el) {
			if (el = DomElement.get(el)) {
				var next = el.getNext();
				if (next) this.insertBefore(next);
				else el.getParent().appendChild(this);
			}
			return this;
		},

		insertFirst: function(el) {
			if (el = DomElement.get(el)) {
				var first = el.getFirst();
				if (first) this.insertBefore(first);
				else el.appendChild(this);
			}
			return this;
		},

		insertInside: function(el) {
			if (el = DomElement.get(el))
				el.appendChild(this);
			return this;
		},

		appendText: function(text) {
			this.$.appendChild(document.createTextNode(text));
			return this;
		},

		create: function(arg) {
			var items = Base.type(arg) == 'array' ? arg : arguments;
			var elements = new this._elements();
			for (var i = 0, j = items.length; i < j; i ++) {
				var item = items[i];
				var props = item[1], content = item[2];
				if (!content && Base.type(props) != 'object') {
					content = props;
					props = null;
				}
				var el = new DomElement(item[0], props);
				if (content) {
					if (content.push) this.create(Base.type(content[0]) == 'string'
						? [content] : content).insertInside(el);
					else el.appendText(content);
				}
				elements.push(el);
			}
			return elements.length > 1 ? elements : elements[0];
		},

		createBefore: create('Before'),

		createAfter: create('After'),

		createFirst: create('First'),

		createInside: create('Inside'),

		remove: function() {
			if (this.$.parentNode)
				this.$.parentNode.removeChild(this.$);
			return this;
		},

		removeChild: function(el) {
			el = DomElement.get(el);
			this.$.removeChild(el.$);
			return el;
		},

		removeChildren: function() {
			this.getChildren().remove();
		},

		replaceWith: function(el) {
			el = DomElement.get(el);
			if (this.$.parentNode)
				this.$.parentNode.replaceChild(el.$, this.$);
			return el;
		},

		clone: function(contents) {
			return DomElement.get(this.$.cloneNode(!!contents));
		},

		getProperty: function(name) {
			var key = properties[name];
			if (key) return this.$[key];
			var flag = flags[name];
			if (!Browser.IE || flag) return this.$.getAttribute(name, flag);
			var node = this.$.attributes[name];
			return node && node.nodeValue;
		},

		setProperty: function(name, value) {
			var key = properties[name];
			if (key) this.$[key] = value;
			else this.$.setAttribute(name, value);
			return this;
		},

		removeProperty: function(name) {
			var key = properties[name];
			if (key) this.$[key] = '';
			else this.$.removeAttribute(name);
			return this;
		},

		setProperties: function(src) {
			return Base.each(src, function(val, name) {
				this.setProperty(name, val);
			}, this);
		},

		toString: function() {
			return this.getTag() + (this.$.id ? '#' + this.$.id : '');
		}
	}
});

DomEvent = Base.extend(new function() {
	var keys = {
		 '8': 'backspace',
		'13': 'enter',
		'27': 'esc',
		'32': 'space',
		'37': 'left',
		'38': 'up',
		'39': 'right',
		'40': 'down',
		'46': 'delete'
	};

	function hover(name, type) {
		return {
			type: type,
			listener: function(event) {
				if (event.relatedTarget != this && !this.hasChild(event.relatedTarget))
					this.fireEvent(name, event);
			}
		}
	}

	return {
		initialize: function(event) {
			this.event = event = event || window.event;
			this.type = event.type;
			this.target = DomElement.get(event.target || event.srcElement);
			if (this.target.nodeType == 3)
				this.target = this.target.getParent(); 
			this.shift = event.shiftKey;
			this.control = event.ctrlKey;
			this.alt = event.altKey;
			this.meta = event.metaKey;
			if (/^(mousewheel|DOMMouseScroll)$/.test(this.type)) {
				this.wheel = event.wheelDelta ?
					event.wheelDelta / (window.opera ? -120 : 120) : 
					- (event.detail || 0) / 3;
			} else if (/^key/.test(this.type)) {
				this.code = event.which || event.keyCode;
				this.key = keys[this.code] || String.fromCharCode(this.code).toLowerCase();
			} else if (/^mouse|^click$/.test(this.type)) {
				this.page = {
					x: event.pageX || event.clientX + document.documentElement.scrollLeft,
					y: event.pageY || event.clientY + document.documentElement.scrollTop
				};
				this.client = {
					x: event.pageX ? event.pageX - window.pageXOffset : event.clientX,
					y: event.pageY ? event.pageY - window.pageYOffset : event.clientY
				};
				var offset = this.target.getOffset();
				this.offset = {
					x: this.page.x - offset.x,
					y: this.page.y - offset.y
				}
				this.rightClick = event.which == 3 || event.button == 2;
				if (/^mouse(over|out)$/.test(this.type))
					this.relatedTarget = DomElement.get(event.relatedTarget ||
						this.type == 'mouseout' ? event.toElement : event.fromElement);
			}
		},

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

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

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

		statics: {
			events: new Hash({
				mouseenter: hover('mouseenter', 'mouseover'),

				mouseleave: hover('mouseleave', 'mouseout'),

				mousewheel: { type: Browser.GECKO ? 'DOMMouseScroll' : 'mousewheel' },

				domready: function(func) { 
					if (this.loaded) func.call(this);
					else if (!this.domReady) {
						this.domReady = true;
						var domReady = function() {
							if (this.loaded) return;
							this.loaded = true;
							if (this.timer) this.timer = this.timer.clear();
							this.fireEvent('domready');
						}.bind(this);
						if (document.readyState && (Browser.WEBKIT || Browser.MACIE)) { 
							this.timer = (function() {
								if (/^(loaded|complete)$/.test(document.readyState)) domReady();
							}).periodic(50);
						} else if (document.readyState && Browser.IE) { 
							document.write('<script id=ie_domready defer src="'
								+ (window.location.protocol == 'https:' ? '://0' : 'javascript:void(0)')
								+ '"><\/script>');
							$('ie_domready').addEvent('readystatechange', function() {
								if (this.$.readyState == 'complete') domReady();
							});
						} else { 
							Window.addEvent('load', domReady);
							Document.addEvent('DOMContentLoaded', domReady);
						}
					}
				}
			}),

			add: function(events) {
				this.events.merge(events);
			}
		}
	};
});

DomElement.inject({
	addEvent: function(type, func) {
		this.events = this.events || {};
		var entries = this.events[type] = this.events[type] || [];
		if (func && !entries.find(function(entry) { return entry.func == func })) {
			var listener = func, name = type, pseudo = DomEvent.events[type];
			if (pseudo) {
				if (typeof pseudo == 'function') pseudo = pseudo.call(this, func);
				listener = pseudo && pseudo.listener || listener;
				name = pseudo && pseudo.type;
			}
			var that = this, bound = listener.parameters().length == 0
				? listener.bind(this)
				: function(event) { 
					event = new DomEvent(event);
					if (listener.call(that, event) === false)
						event.stop();
				};
			if (name) {
				if (this.$.addEventListener) {
					this.$.addEventListener(name, bound, false);
				} else if (this.$.attachEvent) {
					this.$.attachEvent('on' + name, bound);
				}
			}
			entries.push({ func: func, name: name, bound: bound });
		}
		return this;
	},

	removeEvent: function(type, func) {
		var entries = (this.events || {})[type], entry;
		if (func && entries) {
			if (entry = entries.remove(function(entry) { return entry.func == func })) {
				var name = entry.name, pseudo = DomEvent.events[type];
				if (pseudo && pseudo.remove) pseudo.remove.call(this, func);
				if (name) {
					if (this.$.removeEventListener) {
						this.$.removeEventListener(name, entry.bound, false);
					} else if (this.$.detachEvent) {
						this.$.detachEvent('on' + name, entry.bound);
					}
				}
			}
		}
		return this;
	},

	addEvents: function(events) {
		return Base.each(events || [], function(fn, type) {
			this.addEvent(type, fn);
		}, this);
	},

	removeEvents: function(type) {
		if (this.events) {
			if (type) {
				(this.events[type] || []).each(function(fn) {
					this.removeEvent(type, fn);
				}, this);
				delete this.events[type];
			} else {
				Base.each(this.events, function(ev, type) {
					this.removeEvents(type);
				}, this);
				this.events = null;
			}
		}
		return this;
	},

	fireEvent: function(type, event) {
		var entries = (this.events || {})[type];
		if (entries) {
			if (event) event = event.event ? event : new DomEvent(event);
			entries.each(function(entry) {
				entry.func.call(this, event);
			}, this);
		}
		return !!entries;
	},

	dispose: function() {
		this.removeEvents();
	}
});

Document = DomElement.get(document).inject({
	getTag: function() {
		return 'document';
	}
});

function $(selector, root) {
	return DomElement.get(root || Document).getElement(selector);
}

function $$(selector, root) {
	return DomElement.get(root || Document).getElements(selector);
}

/*@cc_on
try { document.execCommand('BackgroundImageCache', false, true); }
catch (e) {}
@*/

Window = DomElement.get(window).inject({
	getTag: function() {
		return 'window';
	},

	open: function(url, title, params) {
		var focus;
		if (params && typeof params != 'string') {
			if (params.confirm && !confirm(params.confirm))
				return null;
			(['toolbar','menubar','location','status','resizable','scrollbars']).each(function(d) {
				if (!params[d]) params[d] = 0;
			});
			if (params.width && params.height) {
				if (params.left == null) params.left = Math.round(
					Math.max(0, (screen.width - params.width) / 2));
				if (params.top == null) params.top = Math.round(
					Math.max(0, (screen.height - params.height) / 2 - 40));
			}
			focus = params.focus;
			params = Base.each(params, function(p, n) {
				if (!/^(focus|confirm)$/.test(n))
					this.push(n + '=' + p);
			}, []).join(',');
		}
		var win = window.open(url, title.replace(/\s+|\.+|-+/gi, ''), params);
		if (win && focus) win.focus();
		return win;
	}
});

Window.addEvent('unload', DomElement.dispose);

DomEvent.add(new function() {
	var object, last;

	function dragStart(event) {
		if (object != this) {
			event.type = 'dragstart';
			last = event.page;
			this.fireEvent('dragstart', event);
			if (!event.stopped) {
				event.stop();
				Document.addEvent('mousemove', drag);
				Document.addEvent('mouseup', dragEnd);
				object = this;
			}
		}
	}

	function drag(event) {
		event.type = 'drag';
		event.delta = {
			x: event.page.x - last.x,
			y: event.page.y - last.y
		}
		last = event.page;
		object.fireEvent('drag', event);
		event.preventDefault();
	}

	function dragEnd(event) {
		if (object) {
			event.type = 'dragend';
			object.fireEvent('dragend', event);
			event.preventDefault();
			Document.removeEvent('mousemove', drag);
			Document.removeEvent('mouseup', dragEnd);
			object = null;
		}
	}

	return {
		dragstart: {
			type: 'mousedown',
			listener: dragStart
		},

		drag: {
			type: 'mousedown',
			listener: dragStart
		},

		dragend: {}
	};
});

DomElement.inject(new function() {
	var XPATH= 0, FILTER = 1;

	var methods = [{ 
		getParam: function(items, separator, context, params) {
			var str = context.namespaceURI ? 'xhtml:' + params.tag : params.tag;
			if (separator && (separator = DomElement.separators[separator]))
				str = separator[XPATH] + str;
			for (var i = params.pseudos.length; i--;) {
				var pseudo = params.pseudos[i];
				str += pseudo.handler[XPATH](pseudo.argument);
			}
			if (params.id) str += '[@id="' + params.id + '"]';
			for (var i = params.classes.length; i--;)
				str += '[contains(concat(" ", @class, " "), " ' + params.classes[i] + ' ")]';
			for (var i = params.attributes.length; i--;) {
				var attribute = params.attributes[i];
				var operator = DomElement.operators[attribute[1]];
				if (operator) str += operator[XPATH](attribute[0], attribute[2]);
				else str += '[@' + attribute[0] + ']';
			}
			items.push(str);
			return items;
		},

		getElements: function(items, elements, context) {
			function resolver(prefix) {
				return prefix == 'xhtml' ? 'http://www.w3.org/1999/xhtml' : false;
			}
			var res = document.evaluate('.//' + items.join(''), context,
				resolver, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
			for (var i = 0, j = res.snapshotLength; i < j; ++i)
				elements.push(res.snapshotItem(i));
		}
	}, { 
		getParam: function(items, separator, context, params, data) {
			var found = [];
			var tag = params.tag;
			if (separator && (separator = DomElement.separators[separator])) {
				separator = separator[FILTER];
				var uniques = {};
				function add(item) {
					if (!item._unique)
						DomElement.unique(item);
					if (!uniques[item._unique] && match(item, params, data)) {
						uniques[item._unique] = true;
						found.push(item);
						return true;
					}
				}
				for (var i = 0, l = items.length; i < l; i++)
					separator(items[i], params, add);
				if (params.clearTag)
					params.tag = params.match = params.clearTag = null;
				return found;
			}
			if (params.id) {
				var el = document.getElementById(params.id);
				params.id = params.match = null;
				return el && DomElement.isAncestor(el, context)
					&& match(el, params, data) ? [el] : null;
			} else {
				items = context.getElementsByTagName(tag);
				params.tag = params.match = null;
				for (var i = 0, l = items.length; i < l; i++)
					if (match(items[i], params, data))
						found.push(items[i]);
			}
			return found;
		},

		getElements: function(items, elements, context) {
			elements.append(items);
		}
	}];

	function parse(selector) {
		var params = { tag: '*', id: null, classes: [], attributes: [], pseudos: [] };
		selector.replace(/:([^:(]+)*(?:\((["']?)(.*?)\2\))?|\[(\w+)(?:([!*^$~|]?=)(["']?)(.*?)\6)?\]|\.[\w-]+|#[\w-]+|\w+|\*/g, function(part) {
			switch (part.charAt(0)) {
				case '.': params.classes.push(part.slice(1)); break;
				case '#': params.id = part.slice(1); break;
				case '[': params.attributes.push([arguments[4], arguments[5], arguments[7]]); break;
				case ':':
					var handler = DomElement.pseudos[arguments[1]];
					if (!handler) {
						params.attributes.push([arguments[1], arguments[3] ? '=' : '', arguments[3]]);
						break;
					}
					params.pseudos.push({<commit_msg>Updated bootstrap.js (now contains DomElement#wrap).<commit_after>if (!this.__proto__) {
	var fix = [Function, Number, Boolean, String, Array, Date, RegExp];
	for (var i in fix)
		fix[i].prototype.__proto__ = fix[i].prototype;
}

new function() { 
	function inject(dest, src, base, generics) {
		function field(name, generics) {
			var val = src[name], res = val, prev = dest[name];
			if (val !== Object.prototype[name]) {
				if (typeof val == 'function') {
					if (generics) generics[name] = function(bind) {
						return bind && dest[name].apply(bind,
							Array.prototype.slice.call(arguments, 1));
					}
					if (prev && /\bthis\.base\b/.test(val)) {
						var fromBase = base && base[name] == prev;
						res = (function() {
							var tmp = this.base;
							this.base = fromBase ? base[name] : prev;
							try { return val.apply(this, arguments); }
							finally { this.base = tmp; }
						}).pretend(val);
					}
				}
				dest[name] = res;
			}
		}
		if (src) {
			for (var name in src)
				if (visible(src, name) && !/^(prototype|constructor|toString|valueOf|statics|_generics)$/.test(name))
					field(name, generics);
			field('toString');
			field('valueOf');
		}
	}

	function extend(obj) {
		function ctor(dont) {
			if (!this.__proto__) this.__proto__ = obj;
			if (this.initialize && dont !== ctor.dont)
				return this.initialize.apply(this, arguments);
		}
		ctor.prototype = obj;
		return ctor;
	}

	function visible(obj, name) {
		return obj[name] !== obj.__proto__[name]&& name.indexOf('__') != 0;
	}

	inject(Function.prototype, {
		inject: function(src) {
			var proto = this.prototype, base = proto.__proto__ && proto.__proto__.constructor;
			inject(proto, src, base && base.prototype, src && src._generics && this);
			inject(this, src && src.statics, base);
			for (var i = 1, j = arguments.length; i < j; i++)
				this.inject(arguments[i]);
			return this;
		},

		extend: function(src) {
			var proto = new this(this.dont), ctor = proto.constructor = extend(proto);
			ctor.dont = {};
			inject(ctor, this);
			return this.inject.apply(ctor, arguments);
		},

		pretend: function(fn) {
			this.toString = function() {
				return fn.toString();
			}
			this.valueOf = function() {
				return fn.valueOf();
			}
			return this;
		}
	});

	Base = Object.extend({
		has: function(name) {
			return visible(this, name);
		},

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

		extend: function() {
			var res = new (extend(this));
			return res.inject.apply(res, arguments);
		}
	});
}

Function.inject(new function() {
	function timer(that, type, args, ms) {
		var fn = that.bind.apply(that, Array.slice(args, 1));
		var timer = window['set' + type](fn, ms);
		fn.clear = function() {
			clearTimeout(timer);
			clearInterval(timer);
		};
		return fn;
	}

	return {
		_generics: true,

		name: function() {
			var match = this.toString().match(/^\s*function\s*(\w*)/);
			return match && match[1];
		},

		parameters: function() {
			var str = this.toString().match(/^\s*function[^\(]*\(([^\)]*)/)[1];
			return str ? str.split(/\s*,\s*/) : [];
		},

		body: function() {
			return this.toString().match(/^\s*function[^\{]*\{([\s\S]*)\}\s*$/)[1];
		},

		delay: function(ms) {
			return timer(this, 'Timeout', arguments, ms);
		},

		periodic: function(ms) {
			return timer(this, 'Interval', arguments, ms);
		},

		bind: function(obj) {
			var that = this, args = Array.slice(arguments, 1);
			return function() {
				return that.apply(obj, args.concat(Array.create(arguments)));
			}
		},

		attempt: function(obj) {
			var that = this, args = Array.slice(arguments, 1);
			return function() {
				try {
					return that.apply(obj, args.concat(Array.create(arguments)));
				} catch(e) {
					return e;
				}
			}
		}
	}
});

Enumerable = new function() {
	Base.iterate = function(fn, name) {
		return function(iter, bind) {
			if (!iter) iter = function(val) { return val };
			else if (typeof iter != 'function') iter = function(val) { return val == iter };
			if (!bind) bind = this;
			var prev = bind[name];
			bind[name] = iter;
			try { return fn.call(this, iter, bind, this); }
			finally { prev ? bind[name] = prev : delete bind[name] }
		};
	};

	Base.stop = {};

	var each_Array = Array.prototype.forEach || function(iter, bind) {
		for (var i = 0, j = this.length; i < j; ++i)
			bind.__each(this[i], i, this);
	};

	var each_Object = function(iter, bind) {
		for (var i in this) {
			var val = this[i];
			if (val !== this.__proto__[i]&& i.indexOf('__') != 0)
				bind.__each(val, i, this);
		}
	};

	return {
		_generics: true,

		each: Base.iterate(function(iter, bind) {
			try { (this.length != null ? each_Array : each_Object).call(this, iter, bind); }
			catch (e) { if (e !== Base.stop) throw e; }
			return bind;
		}, '__each'),

		findEntry: Base.iterate(function(iter, bind, that) {
			return this.each(function(val, key) {
				this.result = bind.__findEntry(val, key, that);
				if (this.result) {
					this.key = key;
					this.value = val;
					throw Base.stop;
				}
			}, {});
		}, '__findEntry'),

		find: function(iter, bind) {
			return this.findEntry(iter, bind).result;
		},

		remove: function(iter, bind) {
			var entry = this.findEntry(iter, bind);
			delete this[entry.key];
			return entry.value;
		},

		some: function(iter, bind) {
			return this.find(iter, bind) != null;
		},

		every: Base.iterate(function(iter, bind, that) {
			return this.find(function(val, i) {
				return !this.__every(val, i, that);
			}, bind) == null;
		}, '__every'),

		map: Base.iterate(function(iter, bind, that) {
			return this.each(function(val, i) {
				this[this.length] = bind.__map(val, i, that);
			}, []);
		}, '__map'),

		filter: Base.iterate(function(iter, bind, that) {
			return this.each(function(val, i) {
				if (bind.__filter(val, i, that))
					this[this.length] = val;
			}, []);
		}, '__filter'),

		max: Base.iterate(function(iter, bind, that) {
			return this.each(function(val, i) {
				val = bind.__max(val, i, that);
				if (val >= (this.max || val)) this.max = val;
			}, {}).max;
		}, '__max'),

		min: Base.iterate(function(iter, bind, that) {
			return this.each(function(val, i) {
				val = bind.__min(val, i, that);
				if (val <= (this.min || val)) this.min = val;
			}, {}).min;
		}, '__min'),

		pluck: function(prop) {
			return this.map(function(val) {
				return val[prop];
			});
		},

		sortBy: Base.iterate(function(iter, bind, that) {
			return this.map(function(val, i) {
				return { value: val, compare: bind.__sortBy(val, i, that) };
			}, bind).sort(function(left, right) {
				var a = left.compare, b = right.compare;
				return a < b ? -1 : a > b ? 1 : 0;
			}).pluck('value');
		}, '__sortBy'),

		toArray: function() {
			return this.map();
		}
	};
}

Base.inject({
	_generics: true,

	each: Enumerable.each,

	debug: function() {
		return /^(string|number|function|regexp)$/.test(Base.type(this)) ? this
			: this.each(function(val, key) { this.push(key + ': ' + val); }, []).join(', ');
	},

	clone: function() {
		return Base.each(this, function(val, i) {
			this[i] = val;
		}, new this.constructor());
	},

	statics: {
		inject: function() {
			var args = arguments;
			Base.each([Array, Number, RegExp, String], function(ctor) {
				ctor.inject.apply(ctor, args);
			});
			return this.base.apply(this, args);
		},

		extend: function() {
			var ret = this.base();
			ret.extend = Function.extend;
			ret.inject = Function.inject;
			ret.inject.apply(ret, arguments);
			return ret;
		},

		check: function(obj) {
			return !!(obj || obj === 0);
		},

		type: function(obj) {
			return (obj || obj === 0) && ((obj._type || obj.nodeName && obj.nodeType == 1 && 'element') || typeof obj) || null;
		},

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

}, Base.prototype);

$each = Base.each;
$stop = $break = Base.stop;
$check = Base.check;
$type = Base.type;

Hash = Base.extend(Enumerable, {
	_generics: true,

	initialize: function() {
		return this.merge.apply(this, arguments);
	},

	merge: function() {
		return Base.each(arguments, function(obj) {
			Base.each(obj, function(val, key) {
				this[key] = Base.type(this[key]) == 'object'
					? Hash.prototype.merge.call(this[key], val) : val;
			}, this);
		}, this);
	},

	keys: function() {
		return this.map(function(val, key) {
			return key;
		});
	},

	values: Enumerable.toArray,

	statics: {
		create: function(obj) {
			return arguments.length == 1 && obj.constructor == Hash
				? obj : Hash.prototype.initialize.apply(new Hash(), arguments);
		}
	}
});

$H = Hash.create;

Array.inject(new function() {
	var proto = Array.prototype;

	var fields = Hash.merge({}, Enumerable, {
		_generics: true,
		_type: 'array',

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

		lastIndexOf: proto.lastIndexOf || function(obj, i) {
			i = i != null ? i : this.length - 1;
			if (i < 0) i = Math.max(0, this.length + i);
			for (; i >= 0; i--)
				if (this[i] == obj) return i;
			return -1;
		},

		filter: Base.iterate(proto.filter || function(iter, bind, that) {
			var res = [];
			for (var i = 0, j = this.length; i < j; ++i)
				if (bind.__filter(this[i], i, that))
					res[res.length] = this[i];
			return res;
		}, '__filter'),

		map: Base.iterate(proto.map || function(iter, bind, that) {
			var res = new Array(this.length);
			for (var i = 0, j = this.length; i < j; ++i)
				res[i] = bind.__map(this[i], i, that);
			return res;
		}, '__map'),

		every: Base.iterate(proto.every || function(iter, bind, that) {
			for (var i = 0, j = this.length; i < j; ++i)
				if (!bind.__every(this[i], i, that))
					return false;
			return true;
		}, '__every'),

		some: Base.iterate(proto.some || function(iter, bind, that) {
			for (var i = 0, j = this.length; i < j; ++i)
				if (bind.__some(this[i], i, that))
					return true;
			return false;
		}, '__some'),

		reduce: proto.reduce || function(fn, value) {
			var i = 0;
			if (arguments.length < 2 && this.length) value = this[i++];
			for (var l = this.length; i < l; i++)
				value = fn.call(null, value, this[i], i, this);
			return value;
		},

		findEntry: function(iter, bind) {
			if (iter && !/^(function|regexp)$/.test(Base.type(iter))) {
				var i = this.indexOf(iter);
				return { key: i != -1 ? i : null, value: this[i], result: i != -1 };
			}
			return Enumerable.findEntry.call(this, iter, bind);
		},

		remove: function(iter, bind) {
			var entry = this.findEntry(iter, bind);
			if (entry.key != null)
				this.splice(entry.key, 1);
			return entry.value;
		},

		toArray: function() {
			var res = this.concat([]);
			return res[0] == this ? Enumerable.toArray.call(this) : res;
		},

		clone: function() {
			return this.toArray();
		},

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

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

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

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

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

		subtract: function(items) {
			for (var i = 0, j = items.length; i < j; ++i)
				Array.remove(this, items[i]);
			return this;
		},

		intersect: function(items) {
			for (var i = this.length - 1; i >= 0; i--)
				if (!items.find(this[i]))
					this.splice(i, 1);
			return this;
		},

		associate: function(obj) {
			if (obj.length != null) {
				var that = this;
				return Base.each(obj, function(name, index) {
					this[name] = that[index];
					if (index == that.length) throw Base.stop;
				}, {});
			} else {
				obj = Hash.create(obj);
				return Base.each(this, function(val) {
					var type = Base.type(val);
					obj.each(function(hint, name) {
						if (hint == 'any' || type == hint) {
							this[name] = val;
							delete obj[name];
							throw Base.stop;
						}
					}, this);
				}, {});
			}
		},

		flatten: function() {
			return this.each(function(val) {
				if (val != null && val.flatten) this.append(val.flatten());
				else this.push(val);
			}, []);
		},

		swap: function(i, j) {
			var tmp = this[j];
			this[j] = this[i];
			this[i] = tmp;
			return tmp;
		},

		shuffle: function() {
			var res = this.clone();
			var i = this.length;
			while (i--) res.swap(i, Math.rand(0, i));
			return res;
		},

		statics: {
			create: function(list) {
				if (!Base.check(list)) return [];
				if (Base.type(list) == 'array') return list;
				if (list.toArray)
					return list.toArray();
				if (list.length != null) {
					var res = [];
					for (var i = 0, j = list.length; i < j; ++i)
						res[i] = list[i];
				} else {
					res = [list];
				}
				return res;
			},

			extend: function(src) {
				var ret = Base.extend(extend, src);
				ret.extend = Function.extend;
				return ret;
			}
		}
	});
	['push','pop','shift','unshift','sort','reverse','join','slice','splice','concat'].each(function(name) {
		fields[name] = proto[name];
	});
	var extend = Base.clone(fields);
	extend.length = 0;
	extend.toString = proto.join;
	return fields;
});

$A = Array.create;

String.inject({
	_type: 'string',

	test: function(exp, param) {
		return new RegExp(exp, param || '').test(this);
	},

	toArray: function() {
		return this ? this.split(/\s+/) : [];
	},

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

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

	camelize: function(separator) {
		return this.replace(new RegExp(separator || '-', 'g'), function(match) {
			return match.charAt(1).toUpperCase();
		});
	},

	uncamelize: function(separator) {
		separator = separator || '-';
		return this.replace(/[a-zA-Z][A-Z0-9]|[0-9][a-zA-Z]/g, function(match) {
			return match.charAt(0) + separator + match.charAt(1);
		});
	},

	hyphenate: function(separator) {
		return this.uncamelize(separator).toLowerCase();
	},

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

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

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

	contains: function(string, s) {
		return (s ? (s + this + s).indexOf(s + string + s) : this.indexOf(string)) != -1;
	},

	times: function(count) {
		return count < 1 ? '' : new Array(count + 1).join(this);
	},
});

Number.inject({
	_type: 'number',

	toInt: String.prototype.toInt,

	toFloat: String.prototype.toFloat,

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

	toPaddedString: function(length, base) {
		var str = this.toString(base || 10);
		return '0'.times(length - str.length) + str;
	}
});

RegExp.inject({
	_type: 'regexp'
});

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

Array.inject({

	hexToRgb: function(toArray) {
		if (this.length >= 3) {
			var rgb = [];
			for (var i = 0; i < 3; i++)
				rgb.push((this[i].length == 1 ? this[i] + this[i] : this[i]).toInt(16));
			return toArray ? rgb : 'rgb(' + rgb.join(',') + ')';
		}
	},

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

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

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

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

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

Json = new function() {
	var special = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' };

	function replace(chr) {
		return special[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
	}

	return {
		encode: function(obj) {
			switch (Base.type(obj)) {
				case 'string':
					return '"' + obj.replace(/[\x00-\x1f\\"]/g, replace) + '"';
				case 'array':
					return '[' + obj.map(this.encode).compact().join(',') + ']';
				case 'object':
					return '{' + Base.each(obj, function(val, key) {
						if (val != undefined)
							this.push(Json.encode(key) + ':' + Json.encode(val));
					}, []) + '}';
				default:
					return obj + "";
			}
			return null;
		},

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

Browser = new function() {
	var name = (navigator.platform.match(/(MAC)|(WIN)|(LINUX)|(NIX)/i) || ['OTHER'])[0].toUpperCase();
	var js/*@cc_on=@_jscript_version@*/, xpath = !!document.evaluate;
	var webkit = document.childNodes && !document.all && !navigator.taintEnabled;
	var ret = {
		PLATFORM: name,
		WEBKIT: webkit,
		WEBKIT2: webkit && !xpath,
		WEBKIT3: webkit && xpath,
		OPERA: !!window.opera,
		GECKO: !!document.getBoxObjectFor,
		IE: !!js,
		IE5: js >= 5 && js < 5.5,
		IE55: js == 5.5,
		IE6: js == 5.6,
		IE7: js == 5.7,
		MACIE: js && name == 'MAC',
		XPATH: xpath
	};
	ret[name] = true;
	return ret;
};

DomElements = Array.extend(new function() {
	var unique = 0;
	return {
		initialize: function(els) {
			this._unique = unique++;
			this.append(els && els.length != null && !els.nodeType ? els : arguments);
		},

		push: function() {
			this.append(arguments);
			return this.length;
		},

		append: function(items) {
			for (var i = 0, l = items.length; i < l; ++i) {
				var el = items[i];
				if ((el = el && (el._wrapper || DomElement.get(el))) && el._unique != this._unique) {
					el._unique = this._unique;
					this[this.length++] = el;
				}
			}
			return this;
		},

		statics: {
			inject: function(src) {
				return this.base(Base.each(src || {}, function(val, key) {
					this[key] = typeof val != 'function' ? val : function() {
						var args = arguments, values;
						this.each(function(obj) {
							var ret = (obj[key] || val).apply(obj, args);
							if (ret !== undefined && ret != obj) {
								values = values || (Base.type(ret) == 'element'
									? new obj._elements() : []);
								values.push(ret);
							}
						});
						return values || this;
					}
				}, {}));
			}
		}
	};
});

DomElement = Base.extend(new function() {
	var elements = [];
	var tags = {}, classes = {}, classCheck, unique = 0;

	function dispose(force) {
		for (var i = elements.length - 1; i >= 0; i--) {
			var el = elements[i];
	        if (force || (!el || el != window && el != document &&
				(!el.parentNode || !el.offsetParent))) {
	            if (el) {
					var obj = el._wrapper;
					if (obj && obj.dispose) obj.dispose();
					el._wrapper = el._unique = null;
				}
				if (!force) elements.splice(i, 1);
	        }
		}
	}

	function inject(src) {
		src = src || {};
		(src._methods || []).each(function(name) {
			src[name] = function(arg) {
				var ret = this.$[name] && this.$[name](arg);
				return ret === undefined ? this : ret;
			}
		});
		(src._properties || []).each(function(name) {
			var part = name.capitalize();
			src['get' + part] = function() {
				return this.$[name];
			}
			src['set' + part] = function(value) {
				this.$[name] = value;
				return this;
			}
		});
		delete src._methods;
		delete src._properties;
		return Function.inject.call(this, src);
	}

	function getConstructor(el) {
		var match;
		return classCheck && el.className && (match = el.className.match(classCheck)) && match[2] && classes[match[2]] ||
			el.tagName && tags[el.tagName] ||
			(el.className === undefined ? DomElement : HtmlElement)
	}

	var dont = {};

	return {
		_type: 'element',
		_elements: DomElements,

		initialize: function(el, props) {
			if (this._tag && Base.type(el) == 'object') {
				props = el;
				el = this._tag;
			}
			if (typeof(el) == 'string') {
				if (Browser.IE && props && (props.name || props.type))
					el = '<' + el
						+ (props.name ? ' name="' + props.name + '"' : '')
						+ (props.type ? ' type="' + props.type + '"' : '') + '>';
				el = document.createElement(el);
			} else {
				if (el._wrapper) return el._wrapper;
			}
			if (props == dont) props = null;
			else {
				var ctor = getConstructor(el);
				if (ctor != this.constructor)
					return new ctor(el, props);
			}
			this.$ = el;
			el._wrapper = this;
			elements[elements.length] = el;
			if (props) this.set(props);
		},

		statics: {
			inject: function(src) {
				if (src) {
					var proto = this.prototype, that = this;
					src.statics = Base.each(src, function(val, name) {
						if (typeof val == 'function' && !this[name] && !that[name]) {
							this[name] = function(el, param1, param2) {
								if (el) try {
									proto.$ = el.$ || el;
									return proto[name](param1, param2);
								} finally {
									delete proto.$;
								}
							}
						}
					}, src.statics || {});
					inject.call(this, src);
					delete src.toString;
					proto._elements.inject(src);
				}
				return this;
			},

			extend: function(src) {
				var ret = this.base();
				var init = src.initialize;
				if (init) src.initialize = function(el, props) {
					var ret = this.base(el, props);
					if (ret) return ret;
					init.call(this);
				}
				inject.call(ret, src);
				ret.inject = inject;
				if (src) {
					if (src._tag) tags[src._tag.toLowerCase()] = tags[src._tag.toUpperCase()] = ret;
					if (src._class) {
						classes[src._class] = ret;
						classCheck = new RegExp('(^|\\s)(' + Base.each(classes, function(val, name) {
							this.push(name);
						}, []).join('|') + ')(\\s|$)');
						if (src.initialize) Window.addEvent('domready', function() {
							Document.getElements('.' + src._class);
						});
					}
				}
				return ret;
			},

			get: function(el) {
				return el ? typeof el == 'string'
					? Document.getElement(el)
					: el._wrapper || el._elements && el || new (getConstructor(el))(el, dont)
						: null;
			},

			unwrap: function(el) {
				return el && el.$ || el;
			},

			collect: function(el) {
				elements.push(el);
			},

			unique: function(el) {
				if (!el._unique) {
					DomElement.collect(el);
					el._unique = ++unique;
				}
			},

			isAncestor: function(el, parent) {
				if (parent != document)
					for (el = el && el.parentNode; el != parent; el = el.parentNode)
						if (!el) return false;
				return true;
			},

			dispose: function() {
				dispose(true);
			}
		}
	}
});

DomElement.inject(new function() {
	var properties = {
		'class': 'className', className: 'className', 'for': 'htmlFor',
		colspan: 'colSpan', rowspan: 'rowSpan', accesskey: 'accessKey',
		tabindex: 'tabIndex', maxlength: 'maxLength', readonly: 'readOnly',
		value: 'value', disabled: 'disabled', checked: 'checked',
		multiple: 'multiple', selected: 'selected'
	};

	var flags = {
		href: 2, src: 2
	};

	var handlers = { get: {}, set: {} };

	function handle(that, prefix, name, val) {
		var list = handlers[prefix];
		var fn = name == 'events' && prefix == 'set' ? that.addEvents : list[name];
		if (fn === undefined)
			fn = list[name] = that[prefix +
				name.charAt(0).toUpperCase() + name.substring(1)] || null;
		if (fn) return fn[val && val.push ? 'apply' : 'call'](that, val);
		else return that[prefix + 'Property'](name, val);
	}

	function walk(el, name, start) {
		el = el[start ? start : name];
		while (el && Base.type(el) != 'element') el = el[name];
		return DomElement.get(el);
	}

	function create(where) {
		return function() {
			return this.create(arguments)['insert' + where](this);
		}
	}

	return {
		set: function(name, value) {
			switch (Base.type(name)) {
				case 'string':
					return handle(this, 'set', name, value);
				case 'object':
					return Base.each(name, function(val, key) {
						handle(this, 'set', key, val);
					}, this);
			}
			return this;
		},

		get: function(name) {
			return handle(this, 'get', name);
		},

		getTag: function() {
			return this.$.tagName.toLowerCase();
		},

		getId: function() {
			return this.$.id;
		},

		getPrevious: function() {
			return walk(this.$, 'previousSibling');
		},

		getNext: function() {
			return walk(this.$, 'nextSibling');
		},

		getFirst: function() {
			return walk(this.$, 'nextSibling', 'firstChild');
		},

		getLast: function() {
			return walk(this.$, 'previousSibling', 'lastChild');
		},

		getParent: function() {
			return DomElement.get(this.$.parentNode);
		},

		getChildren: function() {
		 	return new this._elements(this.$.childNodes);
		},

		hasChildren: function() {
			return this.$.hasChildNodes();
		},

		hasParent: function(el) {
			return DomElement.isAncestor(this.$, DomElement.unwrap(el));
		},

		hasChild: function(el) {
			return DomElement.isAncestor(DomElement.unwrap(el), this.$);
		},

		appendChild: function(el) {
			if (el = DomElement.get(el)) {
				var text = Browser.IE && el.$.text;
		 		this.$.appendChild(el.$);
				if (text) el.$.text = text;
			}
			return this;
		},

		insertBefore: function(el) {
			if (el = DomElement.get(el)) {
				var text = Browser.IE && el.text;
				el.$.parentNode.insertBefore(this.$, el.$);
				if (text) this.$.text = text;
			}
			return this;
		},

		insertAfter: function(el) {
			if (el = DomElement.get(el)) {
				var next = el.getNext();
				if (next) this.insertBefore(next);
				else el.getParent().appendChild(this);
			}
			return this;
		},

		insertFirst: function(el) {
			if (el = DomElement.get(el)) {
				var first = el.getFirst();
				if (first) this.insertBefore(first);
				else el.appendChild(this);
			}
			return this;
		},

		insertInside: function(el) {
			if (el = DomElement.get(el))
				el.appendChild(this);
			return this;
		},

		appendText: function(text) {
			this.$.appendChild(document.createTextNode(text));
			return this;
		},

		create: function(arg) {
			var items = Base.type(arg) == 'array' ? arg : arguments;
			var elements = new this._elements();
			for (var i = 0, j = items.length; i < j; i ++) {
				var item = items[i];
				var props = item[1], content = item[2];
				if (!content && Base.type(props) != 'object') {
					content = props;
					props = null;
				}
				var el = new DomElement(item[0], props);
				if (content) {
					if (content.push) this.create(Base.type(content[0]) == 'string'
						? [content] : content).insertInside(el);
					else el.appendText(content);
				}
				elements.push(el);
			}
			return elements.length > 1 ? elements : elements[0];
		},

		createBefore: create('Before'),

		createAfter: create('After'),

		createFirst: create('First'),

		createInside: create('Inside'),

		wrap: function() {
			var el = this.create(arguments), last;
			el.insertBefore(this);
			do {
				last = el;
				el = el.getFirst();
			} while(el);
			last.appendChild(this);
			return last;
		},

		remove: function() {
			if (this.$.parentNode)
				this.$.parentNode.removeChild(this.$);
			return this;
		},

		removeChild: function(el) {
			el = DomElement.get(el);
			this.$.removeChild(el.$);
			return el;
		},

		removeChildren: function() {
			this.getChildren().remove();
		},

		replaceWith: function(el) {
			el = DomElement.get(el);
			if (this.$.parentNode)
				this.$.parentNode.replaceChild(el.$, this.$);
			return el;
		},

		clone: function(contents) {
			return DomElement.get(this.$.cloneNode(!!contents));
		},

		getProperty: function(name) {
			var key = properties[name];
			if (key) return this.$[key];
			var flag = flags[name];
			if (!Browser.IE || flag) return this.$.getAttribute(name, flag);
			var node = this.$.attributes[name];
			return node && node.nodeValue;
		},

		setProperty: function(name, value) {
			var key = properties[name];
			if (key) this.$[key] = value;
			else this.$.setAttribute(name, value);
			return this;
		},

		removeProperty: function(name) {
			var key = properties[name];
			if (key) this.$[key] = '';
			else this.$.removeAttribute(name);
			return this;
		},

		setProperties: function(src) {
			return Base.each(src, function(val, name) {
				this.setProperty(name, val);
			}, this);
		},

		toString: function() {
			return this.getTag() + (this.$.id ? '#' + this.$.id : '');
		}
	}
});

DomEvent = Base.extend(new function() {
	var keys = {
		 '8': 'backspace',
		'13': 'enter',
		'27': 'esc',
		'32': 'space',
		'37': 'left',
		'38': 'up',
		'39': 'right',
		'40': 'down',
		'46': 'delete'
	};

	function hover(name, type) {
		return {
			type: type,
			listener: function(event) {
				if (event.relatedTarget != this && !this.hasChild(event.relatedTarget))
					this.fireEvent(name, event);
			}
		}
	}

	return {
		initialize: function(event) {
			this.event = event = event || window.event;
			this.type = event.type;
			this.target = DomElement.get(event.target || event.srcElement);
			if (this.target.nodeType == 3)
				this.target = this.target.getParent(); 
			this.shift = event.shiftKey;
			this.control = event.ctrlKey;
			this.alt = event.altKey;
			this.meta = event.metaKey;
			if (/^(mousewheel|DOMMouseScroll)$/.test(this.type)) {
				this.wheel = event.wheelDelta ?
					event.wheelDelta / (window.opera ? -120 : 120) : 
					- (event.detail || 0) / 3;
			} else if (/^key/.test(this.type)) {
				this.code = event.which || event.keyCode;
				this.key = keys[this.code] || String.fromCharCode(this.code).toLowerCase();
			} else if (/^mouse|^click$/.test(this.type)) {
				this.page = {
					x: event.pageX || event.clientX + document.documentElement.scrollLeft,
					y: event.pageY || event.clientY + document.documentElement.scrollTop
				};
				this.client = {
					x: event.pageX ? event.pageX - window.pageXOffset : event.clientX,
					y: event.pageY ? event.pageY - window.pageYOffset : event.clientY
				};
				var offset = this.target.getOffset();
				this.offset = {
					x: this.page.x - offset.x,
					y: this.page.y - offset.y
				}
				this.rightClick = event.which == 3 || event.button == 2;
				if (/^mouse(over|out)$/.test(this.type))
					this.relatedTarget = DomElement.get(event.relatedTarget ||
						this.type == 'mouseout' ? event.toElement : event.fromElement);
			}
		},

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

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

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

		statics: {
			events: new Hash({
				mouseenter: hover('mouseenter', 'mouseover'),

				mouseleave: hover('mouseleave', 'mouseout'),

				mousewheel: { type: Browser.GECKO ? 'DOMMouseScroll' : 'mousewheel' },

				domready: function(func) { 
					if (this.loaded) func.call(this);
					else if (!this.domReady) {
						this.domReady = true;
						var domReady = function() {
							if (this.loaded) return;
							this.loaded = true;
							if (this.timer) this.timer = this.timer.clear();
							this.fireEvent('domready');
						}.bind(this);
						if (document.readyState && (Browser.WEBKIT || Browser.MACIE)) { 
							this.timer = (function() {
								if (/^(loaded|complete)$/.test(document.readyState)) domReady();
							}).periodic(50);
						} else if (document.readyState && Browser.IE) { 
							document.write('<script id=ie_domready defer src="'
								+ (window.location.protocol == 'https:' ? '://0' : 'javascript:void(0)')
								+ '"><\/script>');
							$('ie_domready').addEvent('readystatechange', function() {
								if (this.$.readyState == 'complete') domReady();
							});
						} else { 
							Window.addEvent('load', domReady);
							Document.addEvent('DOMContentLoaded', domReady);
						}
					}
				}
			}),

			add: function(events) {
				this.events.merge(events);
			}
		}
	};
});

DomElement.inject({
	addEvent: function(type, func) {
		this.events = this.events || {};
		var entries = this.events[type] = this.events[type] || [];
		if (func && !entries.find(function(entry) { return entry.func == func })) {
			var listener = func, name = type, pseudo = DomEvent.events[type];
			if (pseudo) {
				if (typeof pseudo == 'function') pseudo = pseudo.call(this, func);
				listener = pseudo && pseudo.listener || listener;
				name = pseudo && pseudo.type;
			}
			var that = this, bound = listener.parameters().length == 0
				? listener.bind(this)
				: function(event) { 
					event = new DomEvent(event);
					if (listener.call(that, event) === false)
						event.stop();
				};
			if (name) {
				if (this.$.addEventListener) {
					this.$.addEventListener(name, bound, false);
				} else if (this.$.attachEvent) {
					this.$.attachEvent('on' + name, bound);
				}
			}
			entries.push({ func: func, name: name, bound: bound });
		}
		return this;
	},

	removeEvent: function(type, func) {
		var entries = (this.events || {})[type], entry;
		if (func && entries) {
			if (entry = entries.remove(function(entry) { return entry.func == func })) {
				var name = entry.name, pseudo = DomEvent.events[type];
				if (pseudo && pseudo.remove) pseudo.remove.call(this, func);
				if (name) {
					if (this.$.removeEventListener) {
						this.$.removeEventListener(name, entry.bound, false);
					} else if (this.$.detachEvent) {
						this.$.detachEvent('on' + name, entry.bound);
					}
				}
			}
		}
		return this;
	},

	addEvents: function(events) {
		return Base.each(events || [], function(fn, type) {
			this.addEvent(type, fn);
		}, this);
	},

	removeEvents: function(type) {
		if (this.events) {
			if (type) {
				(this.events[type] || []).each(function(fn) {
					this.removeEvent(type, fn);
				}, this);
				delete this.events[type];
			} else {
				Base.each(this.events, function(ev, type) {
					this.removeEvents(type);
				}, this);
				this.events = null;
			}
		}
		return this;
	},

	fireEvent: function(type, event) {
		var entries = (this.events || {})[type];
		if (entries) {
			if (event) event = event.event ? event : new DomEvent(event);
			entries.each(function(entry) {
				entry.func.call(this, event);
			}, this);
		}
		return !!entries;
	},

	dispose: function() {
		this.removeEvents();
	}
});

Document = DomElement.get(document).inject({
	getTag: function() {
		return 'document';
	}
});

function $(selector, root) {
	return (DomElement.get(root) || Document).getElement(selector);
}

function $$(selector, root) {
	return (DomElement.get(root) || Document).getElements(selector);
}

/*@cc_on
try { document.execCommand('BackgroundImageCache', false, true); }
catch (e) {}
@*/

Window = DomElement.get(window).inject({
	getTag: function() {
		return 'window';
	},

	open: function(url, title, params) {
		var focus;
		if (params && typeof params != 'string') {
			if (params.confirm && !confirm(params.confirm))
				return null;
			(['toolbar','menubar','location','status','resizable','scrollbars']).each(function(d) {
				if (!params[d]) params[d] = 0;
			});
			if (params.width && params.height) {
				if (params.left == null) params.left = Math.round(
					Math.max(0, (screen.width - params.width) / 2));
				if (params.top == null) params.top = Math.round(
					Math.max(0, (screen.height - params.height) / 2 - 40));
			}
			focus = params.focus;
			params = Base.each(params, function(p, n) {
				if (!/^(focus|confirm)$/.test(n))
					this.push(n + '=' + p);
			}, []).join(',');
		}
		var win = window.open(url, title.replace(/\s+|\.+|-+/gi, ''), params);
		if (win && focus) win.focus();
		return win;
	}
});

Window.addEvent('unload', DomElement.dispose);

DomEvent.add(new function() {
	var object, last;

	function dragStart(event) {
		if (object != this) {
			event.type = 'dragstart';
			last = event.page;
			this.fireEvent('dragstart', event);
			if (!event.stopped) {
				event.stop();
				Document.addEvent('mousemove', drag);
				Document.addEvent('mouseup', dragEnd);
				object = this;
			}
		}
	}

	function drag(event) {
		event.type = 'drag';
		event.delta = {
			x: event.page.x - last.x,
			y: event.page.y - last.y
		}
		last = event.page;
		object.fireEvent('drag', event);
		event.preventDefault();
	}

	function dragEnd(event) {
		if (object) {
			event.type = 'dragend';
			object.fireEvent('dragend', event);
			event.preventDefault();
			Document.removeEvent('mousemove', drag);
			Document.removeEvent('mouseup', dragEnd);
			object = null;
		}
	}

	return {
		dragstart: {
			type: 'mousedown',
			listener: dragStart
		},

		drag: {
			type: 'mousedown',
			listener: dragStart
		},

		dragend: {}
	};
});

DomElement.inject(new function() {
	var XPATH= 0, FILTER = 1;

	var methods = [{ 
		getParam: function(items, separator, context, params) {
			var str = context.namespaceURI ? 'xhtml:' + params.tag : params.tag;
			if (separator && (separator = DomElement.separators[separator]))
				str = separator[XPATH] + str;
			for (var i = params.pseudos.length; i--;) {
				var pseudo = params.pseudos[i];
				str += pseudo.handler[XPATH](pseudo.argument);
			}
			if (params.id) str += '[@id="' + params.id + '"]';
			for (var i = params.classes.length; i--;)
				str += '[contains(concat(" ", @class, " "), " ' + params.classes[i] + ' ")]';
			for (var i = params.attributes.length; i--;) {
				var attribute = params.attributes[i];
				var operator = DomElement.operators[attribute[1]];
				if (operator) str += operator[XPATH](attribute[0], attribute[2]);
				else str += '[@' + attribute[0] + ']';
			}
			items.push(str);
			return items;
		},

		getElements: function(items, elements, context) {
			function resolver(prefix) {
				return prefix == 'xhtml' ? 'http://www.w3.org/1999/xhtml' : false;
			}
			var res = document.evaluate('.//' + items.join(''), context,
				resolver, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
			for (var i = 0, j = res.snapshotLength; i < j; ++i)
				elements.push(res.snapshotItem(i));
		}
	}, { 
		getParam: function(items, separator, context, params, data) {
			var found = [];
			var tag = params.tag;
			if (separator && (separator = DomElement.separators[separator])) {
				separator = separator[FILTER];
				var uniques = {};
				function add(item) {
					if (!item._unique)
						DomElement.unique(item);
					if (!uniques[item._unique] && match(item, params, data)) {
						uniques[item._unique] = true;
						found.push(item);
						return true;
					}
				}
				for (var i = 0, l = items.length; i < l; i++)
					separator(items[i], params, add);
				if (params.clearTag)
					params.tag = params.clearTag = null;
				return found;
			}
			if (params.id) {
				var el = document.getElementById(params.id);
				params.id = null;
				return el && DomElement.isAncestor(el, context)
					&& match(el, params, data) ? [el] : null;
			} else {
				items = context.getElementsByTagName(tag);
				params.tag = null;
				for (var i = 0, l = items.length; i < l; i++)
					if (match(items[i], params, data))
						found.push(items[i]);
			}
			return found;
		},

		getElements: function(items, elements, context) {
			elements.append(items);
		}
	}];

	function parse(selector) {
		var params = { tag: '*', id: null, classes: [], attributes: [], pseudos: [] };
		selector.replace(/:([^:(]+)*(?:\((["']?)(.*?)\2\))?|\[(\w+)(?:([!*^$~|]?=)(["']?)(.*?)\6)?\]|\.[\w-]+|#[\w-]+|\w+|\*/g, function(part) {
			switch (part.charAt(0)) {
				case '.': params.classes.push(part.slice(1)); break;
				case '#': params.id = part.slice(1); break;
				case '[': params.attributes.push([arguments[4], arguments[5], arguments[7]]); break;
				case ':':
					var handler = DomElement.pseudos[arguments[1]];
					if (!handler) {
						params.attributes.push([arguments[1], arguments[3] ? '=' : '', arguments[3]]);
						break;
					}
					params.pseudos.push({
{'fraction_non_alphanumeric': 0.19391905756255357, 'fraction_numerical': 0.007386932853745971, 'mean_word_length': 2.912078572103126, 'pattern_counts': {'":': 0, '<': 67, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4524032', 'n_tokens_mistral': 35753, 'n_tokens_neox': 29360, 'n_words': 6682}