/**
 * @fileOverview Ajs Core
 * @author Victor Kozko - Webos, 2009
 */

var Ajs = (function(ff) {
	if (!Ajs) Ajs = {};
	for (var f in ff) Ajs[f] = ff[f];
	return Ajs;
})({
	name: 'Ajs',
	version: '0.42',
	global: (function(){ return this; })(),
	BODY: {}
});

Browser = {
	IE: !!(window.attachEvent && !window.opera),
	IE6: /MSIE 6/.test(navigator.appVersion),
	Opera: !!window.opera,
	Safari: /Apple/.test(navigator.vendor)
};
if (Browser.IE || Browser.Opera) {
	Browser.percents = 1;
	Browser.percents100 = 100;
	Browser.percentsPrecision = 0;
} else {
	Browser.percents = 100;
	Browser.percents100 = 10000;
	Browser.percentsPrecision = 2;
}

var dbg = function() {
	var e, show = function() { e.addTo(Ajs.document.getBody()); }, running = false;
	return function() {
		if (running) return arguments[0];
		running = true;
		if (!e) e = E({ tag: 'DIV',
			style: {
				position: 'fixed', border: '1px solid #000', left: '10px', right: '10px', bottom: '10px',
				backgroundColor: '#888', zIndex: '100500'
			}, inner: [{ tag: 'DIV',
				style: { padding: '2px 4px', backgroundColor: '#000', color: '#fff' },
				inner: [{ tag: 'SPAN',
					inner: '$name $version (dbg view)'.format(Ajs)
				}, { tag: 'DIV', style: { position: 'absolute', top: 2, right: 4 },
					inner: [{
						tag: 'A', inner: ' = ',
						style: { fontWeight: 'bold', textDecoration: 'none', color: '#fff' },
						events: { dbg: { click: function() { e.remove(false); }}}
					}, {
						tag: 'A', inner: ' X ',
						style: { fontWeight: 'bold', textDecoration: 'none', color: '#fff' },
						events: { dbg: { click: function() { e.getLast().clean(); e.remove(false); }}}
					}]
				}]
			}, {
				tag: 'DIV', style: {
					maxHeight: '180px', overflow: 'auto', padding: '2px 4px', lineHeight: '20px'
				}
			}]
		});
		if (!e.getParent() && Ajs.document)
			if (Ajs.document.documentReady) show(); else Ajs.document.addListener('dbg', 'ready', show);
		var msg = e.getLast();
		if (arguments.length) {
			msg.add({ tag: 'SPAN',
				inner: A(arguments).replace(function(a) {
					return String(a);
				}).join(' ').replace('\n', '<br />')
			}).add(E.text(' '));
			msg.setScrollPos(-1);
		}
		else msg.clean();
		running = false;
		return arguments[0];
	}
}();

var isDefined = function(a) { var b; return a !== b; };
var isBoolean = function(a) { return typeof a === 'boolean'; };
var isNumber = function(a) { return typeof a === 'number' && !isNaN(a); };
var isNumeric = function(a) { return !isNaN(a); };
var isString = function(a) { return typeof a === 'string'; };
var isObject = function(a) { return a !== null && typeof a === 'object' && a._Class; };
var isStruct = function(a) { return a !== null && typeof a === 'object' && !a._Class; };
var isFunction = function(a) { return !(a instanceof RegExp) && typeof a === 'function'; };
var isArray = function(a) { return a instanceof Array; };
var isHash = function(a) { return a instanceof Hash; };
var isElement = function(a) { return a instanceof E; };
var isEmpty = function(a) { return !(a || a === 0); };
var isStructEmpty = function(a, except) {
	for (var f in a) if (!(except && (f in except))) return false;
	return true;
};
var any = function() {
	var v;
	for (var i = 0; i < arguments.length; ++i) if (v = arguments[i]) return v;
};
var all = function() {
	var v;
	for (var i = 0; i < arguments.length; ++i) if (!(v = arguments[i])) return v;
	return v;
};
var defVal = function(a, d) { return typeof a !== 'undefined' ? a : d };
var boolVal = function(a, d) { return typeof a !== 'undefined' ? a : d };
var intVal = function(a, d) {
	return isNaN(a = Number(a)) ? defVal(d, 0) : a >= 0 ? Math.floor(a) : Math.ceil(a);
};
var numVal = function(a, d) { return isNaN(a = Number(a)) ? defVal(d, 0) : a; };
var strVal = function(a, d) {
	return (typeof a !== 'undefined' && a !== null && a !== false) ? String(a) : d || '';
};
var fnVal = function(a, d) {
	if (a && a._Callback) return a.toFunction();
	return isFunction(a) ? a : d || null;
};
var structVal = function(a, d) {
	if (a !== null && typeof a === 'object' && !a._Class) return a;
	return arguments.length === 2 ? d : {};
};
var pxVal = function(a, d) {
	var _ = /^(-?\d+(?:\.\d+)?)px$/.exec(a); return _ ? parseInt(_[1]) : defVal(d, 0);
};
var pxStr = function(a) {
	if (typeof a === 'number') a = isNaN(a) ? '' : a + 'px';
	return a || '';
};
var percentVal = function(a, d) {
	var _ = /^(-?\d+(?:\.\d+)?)%$/.exec(a);
	return _ ? Math.round(parseFloat(_[1]) * Browser.percents) : defVal(d, 0);
};
var percentStr = function(a) {
	if (typeof a === 'number')
		a = isNaN(a) ? '' : (a / Browser.percents).toFixed(Browser.percentsPrecision) + '%';
	return a || '';
};
var destroy = function(obj) {
	if (obj && isFunction(obj.destroy)) obj.destroy();
	return null;
};
var replace = function(obj, f, v) {
	if (!obj) return null;
	destroy(obj[f]);
	if (!isDefined(v)) delete obj[f]; else obj[f] = v;
	return v;
};
var fetch = function(obj, name) {
	var r;
	if (obj && name in obj) { r = obj[name]; delete obj[name]; }
	return r;
};
var clone = function(obj) {
	if (isStruct(obj)) {
		var r = obj instanceof O ? new O() : {};
		for (var f in obj) r[f] = obj[f];
		return r;
	}
	else if (isObject(obj) && isFunction(obj.clone)) return obj.clone();
	return obj;
};
var extend = function(base, ext) {
	if (isStruct(base) && (isStruct(ext) && !(ext instanceof O))) {
		var r = {}, v;
		for (var f in base)
			if (f in ext) { if (isDefined(v = extend(base[f], ext[f]))) r[f] = v; }
			else r[f] = base[f];
		for (var f in ext) if (!(f in base) && isDefined(ext[f])) r[f] = ext[f];
		return r;
	}
	return ext;
};
var copy = function(dst, src) {
	if (!(dst !== null && typeof dst === 'object' || typeof dst === 'function')) dst = {};
	if (!isStruct(src)) return dst;
	for (var f in src) dst[f] = src[f];
	return dst;
};
var merge = function(dst, src) {
	if (!(dst !== null && typeof dst === 'object' || typeof dst === 'function')) dst = {};
	if (!isStruct(src)) return dst;
	for (var f in src) dst[f] = extend(dst[f], src[f]);
	return dst;
};
var extract = function(dst, src, fields) {
	if (!isDefined(fields)) fields = src, src = dst, dst = {};
	if (!fields) throw BadParam('fields', fields);
	A(fields).each(function(f) {
		dst[f] = src[f];
	});
	return dst;
};
var getProperty = function(obj, path) {
	path = String(path).split('.');
	for (var i = 0, l = path.length; i < l && obj; ++i)
		obj = obj[path[i]];
	return obj;
};
var O = function(obj) {
	if (this instanceof O) copy(this, obj); else return new O(obj);
};
var instantiate = function(obj, name) {
	var o = obj;
	if (o && (o = obj.prototype) && o[name] === obj[name]) obj[name] = clone(obj[name]);
	return obj[name];
};
var mapObj = function(struct, obj, idField) {
	if (!struct) struct = {};
	if (!(obj && idField in obj)) return struct;
	var id = obj[idField];
	if (!isStruct(struct)) id = '$' + id;
	struct[id] = obj;
	return struct;
};
var unmapObj = function(struct, obj, idField) {
	if (!(struct && obj && idField in obj)) return struct;
	var id = obj[idField];
	if (!isStruct(struct)) id = '$' + id;
	if (struct[id] === obj) delete struct[id];
	return struct;
};
var remapObj = function(struct, obj, idField, id) {
	struct = unmapObj(struct, obj, idField);
	obj[idField] = id;
	return mapObj(struct, obj, idField);
};
Object.empty = {};

var Lang = function(dictionary) {
	copy(Lang, dictionary);
	if (isStruct(dictionary[Ajs.lang])) copy(Lang, dictionary[Ajs.lang]);
};
Ajs.lang = document.documentElement.getAttribute('lang') || 'ru';

copy(Function, {
	empty: function(){},
	stub: function(){ return this; }
});
copy(Function.prototype, {
	setName: function(name, cls) {
		if (!isString(name) || this._Function_name) return this;
		var p = name.lastIndexOf('.');
		if (p !== -1) cls = name.substr(0, p), name = name.substr(p + 1);
		if (cls) this._Function_className = cls;
		this._Function_name = name;
		return this;
	},
	getName: function() {
		var r = this._Function_name;
		if (this._Function_className) r = this._Function_className + '.' + r;
		return r;
	},
	extendPrototype: function(ext) {
		this.prototype = extend(this.prototype, ext);
		return this;
	}
});
var tie = function(ct, fn) {
	if (!ct) ct = window;
	if (!isFunction(fn)) fn = ct[fn];
	return new Callback(fn, ct);
};

var Class = copy(function(name) {
	name = String(name);
	var ext = Array.prototype.slice.call(arguments, 1);
	var c;
	if (!(ext.length && isStruct(c = ext[ext.length - 1])))
		throw Exception('missing class definition: ' + name);
	c = Class._create(name, c.construct);
	for (var i = 0, l = ext.length; i < l; ++i) Class.extend(c, ext[i]);
	if (Lang[name]) extend(c._Class || c, Lang[name]);
	if (c._Class._Class_onCreate) c._Class._Class_onCreate();
	return c;
}.setName('Class'), {
	_create: function(name, c) {
		var p;
		if (isFunction(c)) {
			p = c.setName(name).prototype;
			if (!c.getQ) c.getQ = Class.getQ;
			if (!p.getObjId) p.getObjId = Class.getObjId;
			if (p.toString === Object.prototype.toString) p.toString = Class.toString;
			p.construct = c;
		} else c = p = {};
		c._Class = p._Class = p;
		c._Class_name = p._Class_name = name;
		p['_' + name] = c;
		return c;
	},
	reserved: { _Class: true, _Class_name: true, construct: true },
	extend1: function(base, ext, name, merge) {
		var p = base.prototype || base, t = p, t1, tname = name, stat = name.substr(0, 7) === 'static_';
		if (p !== base && stat) t = base, tname = name.substr(7);
		if (merge && tname in t) return;
		if (ext[name] === t[tname]) return;
		if (isFunction(ext[name])) {
			if (!(ext[name] && ext[name].setName)) alert(ext[name]);
			ext[name].setName(name, p._Class_name);
			if (!stat && isFunction(t[tname]) && t[tname]._Function_className
			&& t[tname] !== Object.prototype[tname]
			&& !(t[t1 = '_' + t[tname]._Function_className + '_' + tname])) t[t1] = t[tname];
			t[tname] = ext[name];
		} else if (!isDefined(t[tname] = extend(t[tname], ext[name]))) delete t[tname];
	},
	extend: function(cls, ext, merge) {
		if (isString(cls)) {
			var name = cls;
			cls = window[cls];
			Class._create(name, cls);
		}
		if (isFunction(ext)) ext = ext.prototype;
		for (var f in ext)
			if (!Class.reserved[f]) this.extend1(cls, ext, f, merge);
		if (ext.toString !== Object.prototype.toString)
			this.extend1(cls, ext, 'toString', merge);
		return cls;
	}.setName('Class.extend'),
	getObjId: function(obj) {
		var c = this._Class;
		if (c === this) return this._Class_name;
		if (this.objId) return this.objId;
		if (!c._Object_lastId) c._Object_lastId = 0;
		return this.objId = this._Class_name + ++c._Object_lastId;
	},
	toString: function() {
		return this.getObjId();
	}
});

var Exception = Class('Exception', {
	construct: function(msg, context) {
		if (!this._Exception) {
			return new Exception(msg, context || arguments.callee.caller);
		}
		this.msg = msg;
		this.traceStack(context || arguments.callee.caller);
		this.report();
		debug;
	},
	report: function() {
		dbg(this.toString().replace('\n', '<br />'));
	},
	toString: function() {
		var r = 'Exception:\n';
		r += this.msg || 'unknown error';
		if (this.stack) r += '\nStack:\n' + this.stack;
		return r;
	},
	traceStack: function(caller) {
		if (!caller) return;
		var r = '', fns = [];
		while (caller) {
			if (r) r += ' <= ';
			r += (caller.getName() || '...');
			if (fns.indexOf(caller) !== -1) { r += ' ... etc.'; break; }
			fns.push(caller);
			caller = caller.caller;
		}
		this.stack = r;
	}
});
var BadParam = Class('BadParam', Exception, {
	construct: function(name, value, context) {
		if (!this._BadParam) return new BadParam(name, value, arguments.callee.caller);
		this._Exception('bad param: ' + name + ' = ' + value, context || arguments.callee.caller);
	}
});

var Callback = Class('Callback', {
	construct: function(fn, ct) {
		if (!this._Callback) {
			if (fn) {
				if (fn._Callback) return fn;
				if (isString(fn) && ct) fn = ct[fn];
				return isFunction(fn) ? new Callback(fn, ct) : null;
			}
			return null;
		}
		if (fn && fn._Callback) this.ct = fn.ct, this.fn = fn.fn;
		else this.fn = fn, this.ct = ct || window;
	},
	toFunction: function() {
		var callback = this;
		if (!this.fnVal) this.fnVal = function() {
			if (this.idDelay) this.cancel();
			return callback.fn.apply(callback.ct, arguments);
		}
		return this.fnVal;
	},
	timer: function(interval) {
		if (this.idTimer) this.stop();
		this.idTimer = window.setInterval(this.toFunction(), Math.constrain(interval * 1000, 100));
		return this;
	},
	delay: function(timeout) {
		if (this.idDelay) this.cancel();
		this.idDelay = window.setTimeout(this.toFunction(), Math.constrain(timeout * 1000, 10));
		return this;
	},
	defer: function() { return this.delay(0); },
	stop: function() {
		if (this.idTimer) window.clearInterval(this.idTimer), this.idTimer = null;
	},
	cancel: function() {
		if (this.idDelay) window.clearTimeout(this.idDelay), this.idDelay = null;
	}
});

var format = function(template, params) {
	if (isString(template)) return template.format(params);
	if (isArray(template)) return template.copy(function(v) { return format(v, params); });
	if (isStruct(template)) template = Hash(template);
	if (isHash(template)) return template.copy(function(v) { return format(v, params); }).h;
	return template;
};

copy(Function.prototype, {
	tie: function(ct) { return new Callback(this, ct); },
	_Function: Function
});

Class.extend('String', {
	_const: {
		re1: /^\$(\w+)$/,
		re: /\$\{?(\w+)(?::([rc])?(\d+))?\}?/g
	},
	format1: function(size, align) {
		var r = String(this), fill = ' ';
		if (size.charAt(0) === '0') fill = '0', align = 'r';
		for (var i = 0, l = intVal(size) - r.length; i < l; ++i)
			switch (align) {
				case 'r': r = fill + r; break;
				case 'c': r = i % 2 ? r + fill : fill + r; break;
				default: r += fill; break;
			}
		return r;
	},
	format: function(params) {
		var replace = function(matched, name, align, size) {
			var p = params ? params[name] : '';
			if (size) p = String(p).format1(size, align);
			return p;
		};
		var _ = this._const.re1.exec(this);
		if (_) return params ? params[_[1]] : '';
		return this.replace(this._const.re.reset(), replace);
	},
	trim: function() { return this.replace(/^\s+/, '').replace(/\s+$/, ''); },
	capitalize: function(lower) {
		if (!this.length) return String(this);
		var first = this.charAt(0).toUpperCase();
		if (this.charAt(0) === first && !lower) return String(this);
		var other = this.substr(1);
		if (lower) other = other.toLowerCase();
		return first + other;
	},
	isInt: function() {
		var i = Number(this);
		return !isNaN(i) && Math.floor(i) === i;
	},
	isFloat: function() { return isNaN(this); }
});

Class.extend('RegExp', {
	reset: function() { this.lastIndex = 0; return this; }
});

Class.extend('Math', {
	sign: function(a) { return a < 0 ? -1 : (a > 0 ? 1 : 0); },
	constrain: function(a, max, min) {
		if (isDefined(min)) {
			a = Math.min(min, a);
			if (isNaN(a)) a = min;
		}
		if (isDefined(max)) {
			a = Math.max(max, a) || max;
			if (isNaN(a)) a = max;
		}
		return a;
	},
	trunc: function(a) { return a < 0 ? Math.ceil(a) : Math.floor(a); },
	roundTo: function(a, n) { n = Math.pow(10, n); return Math.round(a * n) / n; }
});

Lang({ ru: {
	month: {
		1: 'январь', 2: 'февраль', 3: 'март', 4: 'апрель', 5: 'май', 6: 'июнь',
		7: 'июль', 8: 'август', 9: 'сентябрь', 10: 'октябрь', 11: 'ноябрь', 12: 'декабрь'
	},
	weekDays0: { 1: 'пн', 2: 'вт', 3: 'ср', 4: 'чт', 5: 'пт', 6: 'сб', 7: 'вс' }
}});

Class.extend('Date', {
	static__daysPerMonth: [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ],
	static__sqlRe: /^(\d{4})(\d{2})(\d{2})(?:(\d{2})(\d{2})(\d{2}))?$/,
	static_now: function() { return new Date(); },
	static_val: function(a, d) {
		if (isNumber(a)) return new Date(a);
		if (a instanceof Date) return new Date(a.getTime());
		var _;
		if (isString(a)) {
			if (_ = Date._sqlRe.exec(a)) {
				a = new Date(intVal(_[1]), intVal(_[2]) - 1, intVal(_[3]));
				if (_[4]) a.setHours(intVal(_[4])), a.setMinutes(intVal(_[5])), a.setSeconds(intVal(_[6]));
				return a;
			}
		}
		return d || new Date();
	},
	static_getDaysPerMonth: function(month, year) {
		return month === 1 ? year % 4 ? 28 : 29 : Date._daysPerMonth[month];
	},
	clearTime: function() {
		this.setHours(0), this.setMinutes(0), this.setSeconds(0), this.setMilliseconds(0);
		return this;
	},
	getCYear: function() { return this.getFullYear(); },
	getYMonth: function() { return this.getMonth() + 1; },
	getYMonthStr: function() { return Lang.month[this.getYMonth()]; },
	getMDay: function() { return this.getDate(); },
	getWDay: function() { var r = this.getDay(); return r ? r : 7; },
	setMDay: function(day) { this.setDate(day); return this; },
	addDays: function(days) {
		this.setDate(this.getDate() + days);
		return this;
	},
	addMonths: function(months) {
		var d = this.getDate(), m = this.getMonth(), y = this.getFullYear();
		y += Math.floor((m + months) / 12);
		m = ((m + months) % 12 + 12) % 12;
		d = Math.min(Date.getDaysPerMonth(m, y), d);
		this.setDate(1), this.setMonth(m), this.setFullYear(y), this.setDate(d);
		return this;
	},
	toString: function() {
		if (isNaN(this.getDate())) return '';
		return '$year:04$month:02$day:02'.format({
			year: this.getCYear(), month: this.getYMonth(), day: this.getMDay()
		});
	}
});

var A = function(a, from, to) {
	var r = [];
	if (!a) return r;
	if (isString(a))
		r = a.split(' ');
	else if (a.length)
		for (var i = 0, l = a.length; i < l; ++i)
			r[i] = a[i];
	else if (isArray(a))
		r = a;
	else return r;
	if (isDefined(from)) if (isDefined(to)) r = r.slice(from, to); else r = r.slice(from);
	return r;
};

Class.extend('Array', {
	indexOf: function(searchElement, fromIndex) {
		if (fromIndex) {
			if ((fromIndex = intVal(fromIndex)) < 0) fromIndex = this.length - fromIndex;
		} else fromIndex = 0;
		for (var i = fromIndex, l = this.length; i < l; ++i)
			if (this[i] === searchElement)
				return i;
		return -1;
	}
}, true);
Class.extend('Array', {
	eindexOf: function(callback, from) {
		if (!(callback = Callback(callback))) return;
		if (from) {
			if ((from = intVal(from)) < 0) from = this.length - from;
		} else from = 0;
		for (var i = from, l = this.length; i < l; ++i)
			if (callback.fn.call(callback.ct, this[i], i))
				return i;
		return -1;
	},
	efill: function(count, callback) {
		callback = Callback(callback);
		this.length = count;
		for (var i = 0; i < count; ++i) this[i] = callback ? callback.fn.call(callback.ct) : undefined;
		return this;
	},
	epush: function() {
		this.push.apply(this, arguments);
		return this;
	},
	eunshift: function() {
		this.unshift.apply(this, arguments);
		return this;
	},
	each: function(callback) {
		if (!(callback = Callback(callback))) return;
		for (var i = 0, l = this.length; i < l; ++i)
			callback.fn.call(callback.ct, this[i], i);
	},
	replace: function(callback) {
		if (!(callback = Callback(callback))) return this;
		for (var i = 0, l = this.length; i < l; ++i)
			this[i] = callback.fn.call(callback.ct, this[i], i);
		return this;
	},
	copy: function(callback) {
		var r = Array(this.length);
		for (var i = 0, l = this.length; i < l; ++i)
			r[i] = callback ? callback.fn.call(callback.ct, this[i], i) : this[i];
		return r;
	},
	clone: function() {
		return this.copy();
	},
	emap: function(callback) {
		var r = [];
		if (!(callback = Callback(callback))) return r;
		for (var i = 0, l = this.length, v; i < l; ++i) {
			v = callback.fn.call(callback.ct, this[i], i);
			if (v instanceof Array) r.push.apply(r, v);
			else if (v !== undefined) r.push(v);
		}
		return r;
	},
	remove: function(callback) {
		if (!(callback = Callback(callback))) return this;
		var ir = 0;
		for (var i = 0, l = this.length; i < l; ++i)
			if (!callback.fn.call(callback.ct, this[i], i)) this[ir++] = this[i];
		this.length = ir;
		return this;
	},
	all: function(callback) {
		callback = Callback(callback);
		for (var i = 0, l = this.length, v; i < l; ++i)
			if (!(v = callback ? callback.fn.call(callback.ct, this[i], i) : this[i])) return v;
		return true;
	},
	any: function(callback) {
		callback = Callback(callback);
		for (var i = 0, l = this.length, v; i < l; ++i)
			if (v = callback ? callback.fn.call(callback.ct, this[i], i) : this[i]) return v;
		return false;
	},
	select: function(callback, count, skip, step) {
		if (isNumber(callback)) step = skip, skip = count, count = callback, callback = null;
		count = intVal(count), skip = intVal(skip), step = intVal(step);
		var r = count === 1 ? undefined : [], l = this.length;
		callback = Callback(callback);
		if (!step) step = 1;
		if (skip < 0) skip = l - skip, step = -step;
		for (var i = skip; i >= 0 && i < l; i += step) {
			if (!callback || callback.fn.call(callback.ct, this[i], i)) {
				if (r) r.push(this[i]); else return this[i];
				if (count && !--count) return r;
			}
		}
		return r;
	},
	pack: function(v, callback) {
		if (!(callback = Callback(callback))) return v;
		for (var i = 0, l = this.length; i < l; ++i)
			v = callback.fn.call(callback.ct, v, this[i], i);
		return v;
	},
	last: function() {
		if (this.length) return this[this.length - 1];
	}
});

var H = function(h, re) {
	return Hash.make(h, re, true);
};

var Hash = Class('Hash', {
	static_make: function(h, re, ref) {
		var r = {};
		if (h) {
			if (isString(h) && re) {
				for (var _ = re.reset(); _ = re.exec(h); ) r[_[1]] = _[2];
				return r;
			}
			if (h._Hash) h = h.h;
			if (isStruct(h)) {
				if (ref) return h;
				for (var n in h) r[n] = h[n];
			}
		}
		return r;
	},
	construct: function(h, re, ref) {
		if (!this._Hash) return (h && h._Hash) ? h : new Hash(h, re, true);
		this.h = Hash.make(h, re, ref);
	},
	add: function(h, re) {
		var h = Hash.make(h, re);
		for (var _ in h) this.h[_] = h[_];
		return this;
	},
	length: function() {
		var r = 0;
		for (var k in this.h) ++r;
		return r;
	},
	keys: function() {
		var r = [];
		for (var k in this.h) r.push(k);
		return r;
	},
	each: function(callback) {
		if (!(callback = Callback(callback))) return;
		for (var k in this.h) callback.fn.call(callback.ct, k, this.h[k]);
	},
	copy: function(callback) {
		callback = Callback(callback);
		var r = new Hash();
		for (var k in this.h)
			r.h[k] = callback ? callback.fn.call(callback.ct, k, this.h[k]) : this.h[k];
		return r;
	},
	values: function(callback) {
		var r = [], v;
		callback = Callback(callback);
		for (var k in this.h) {
			v = callback ? callback.fn.call(callback.ct, k, this.h[k]) : this.h[k];
			if (isArray(v)) r.concat(v);
			else if (v !== undefined) r.push(v);
		}
		return r;
	},
	all: function(callback) {
		callback = Callback(callback);
		var r;
		for (var k in this.h)
			if (!(r = callback ? callback.fn.call(callback.ct, k, this.h[k]) : this.h[k])) return r;
		return true;
	},
	any: function(callback) {
		callback = Callback(callback);
		var r;
		for (var k in this.h)
			if (r = callback ? callback.fn.call(callback.ct, k, this.h[k]) : this.h[k]) return r;
		return false;
	}
});

var F = function(f) {
	return Flags.make(f, true);
};
var Flags = Class('Flags', {
	static__re: /(-?)(\S+)/g,
	static_make: function(f, ref) {
		var r = {};
		if (f) {
			if (isString(f)) {
				for (var _ = Flags._re.reset(); _ = Flags._re.exec(f); )
					r[_[2]] = _[1] ? undefined : true;
				return r;
			}
			if (f._Flags) f = f.f;
			if (isStruct(f)) {
				if (ref) return f;
				for (var n in f) r[n] = Boolean(f[n]);
			}
		}
		return r;
	},
	construct: function(f, ref) {
		if (!this._Flags) return (f && f._Flags) ? f : new Flags(f, true);
		this.f = Flags.make(f, ref);
	},
	add: function(f) {
		for (var _ in f = Flags.make(f, true))
			if (f[_]) this.f[_] = true; else delete this.f[_];
		return this;
	},
	rm: function(f) {
		for (var _ in f = Flags.make(f))
			if (f[_]) delete this.f[_]; else this.f[_] = true;
		return this;
	},
	toggle: function(f, set) {
		for (var _ in f = Flags.make(f))
			if ((f[_] && set) || !(f[_] || set)) this.f[_] = true; else delete this.f[_];
		return this;
	},
	invert: function(f) {
		for (var _ in f = Flags.make(f))
			if ((f[_] && this.f[_]) || !(f[_] || this.f[_])) delete this.f[_]; else this.f[_] = true;
		return this;
	},
	toString: function() {
		var r = '';
		for (var _ in this.f) { if (r) r += ' '; r += _; }
		return r;
	},
	all: function(f) {
		for (var _ in f = Flags.make(f))
			if (Boolean(this.f[_]) == Boolean(f[_])) return false;
		return true;
	},
	any: function(f) {
		for (var _ in f = Flags.make(f))
			if (Boolean(this.f[_]) == Boolean(f[_])) return true;
		return false;
	},
	each: function(cb) {
		if (!(cb = Callback(cb))) throw BadParam('cb', cb);
		for (var f in this.f) if (this.f[f]) cb.fn.call(cb.ct, f);
		return this;
	}
});

var I = copy(function() {
	var r = {}, a = A(arguments), p = 0, id0;
	for (var i = 0, l = a.length; i < l; ++i) {
		var $ = i, c = a[i];
		if (isStruct(c)) {
			if ('$' in c) $ = c.$; else if ('ipos' in c) p = $ = c.ipos; else $ = p;
			if (!('ipos' in c)) c.ipos = p;
		} else $ = a[i], c = undefined;
		r[$] = c;
		p = Math.ceil(p + 1);
	}
	return r;
}, {
	toArray: function(items) {
		if (isArray(items)) return items;
		if (!isStruct(items)) return null;
		var r = [];
		for (var name in items) if (isStruct(items[name])) r.push(items[name]);
		r.sort(function(a, b) { return a.ipos - b.ipos; });
		return r;
	}
});

var TreeNode = Class('TreeNode', {
	_const: {
		siblings: { 0: 'next', 1: 'prev' }
	},
	q: function(cls) {
		if (isString(cls)) return Callback(function(o) { return o[cls]; });
		else if (cls && cls._TreeNode) return Callback(function(o) { return o === cls; });
		return Callback(cls, this);
	},
	destroy: function(cascade) {
		if (cascade !== false) {
			if (this._Tree) this.destroyChilds();
			this.remove(false);
		}
	},
	set$: function($) {
		var p = this.parent;
		if (p) remapObj(p, this, '$', $);
		return this;
	},
	addTo: function(tree, before) {
		if (!(tree && tree._Tree)) throw BadParam('tree', tree);
		tree.add(this, before);
		return this;
	},
	remove: function(destroy) {
		if (this.parent) this.parent.rmChild(this, destroy);
		return this;
	},
	parents: function(q, flags) {
		q = this.q(q);
		flags = isString(flags) && flags.indexOf('s') !== -1;
		for (n = flags ? this : this.parent; n; n = n.parent) {
			if (!q || q.fn.call(q.ct, n)) return n;
			if (n.root) break;
		}
		return null;
	},
	siblings: function(q, flags, count, skip) {
		q = this.q(q), count = intVal(count, 1), skip = intVal(skip);
		var rev = 0;
		if (isString(flags)) {
			if (flags.indexOf('r') !== -1) rev = 1;
			flags = flags.indexOf('s') !== -1;
		} else flags = false;
		var sibling = this._const.siblings[rev], r = count === 1 ? null : [];
		for (var n = flags ? this : this[sibling]; n; n = n[sibling]) {
			if (q && !q.fn.call(q.ct, n, this)) continue;
			if (skip) { --skip; continue; }
			if (r) r.push(n); else return n;
			if (count && !--count) break;
		}
		return r;
	}
});

var Tree = Class('Tree', TreeNode, {
	_const: { childs: { 0: 'first', 1: 'last' }},
	construct: function(childs) {
		this.setChilds(childs);
	},
	destroyChilds: function() {
		if (this.first) this.find( function(n) {
			n.destroy(false);
		}, 'r');
	},
	getNode: function(node) {
		if (!node) return null;
		if (!(node._TreeNode || !((node = this['$' + node]) && node._Tree))) return null;
		return node.parent === this ? node : null;
	},
	add: function(node, before) {
		if (!(node && node._TreeNode)) throw BadParam('node', node);
		if (node.parent) node.remove(false);
		var v;
		if (before) {
			if (v = this.getNode(before)) before = v; else throw BadParam('before', before);
		} else if (before === 0) before = this.first;
		if (node.parent === this && (before === node.next || before === node)) return this;
		node.parent = this;
		if (!before) {
			if (this.last) this.last.next = node, node.prev = this.last, this.last = node;
			else this.first = this.last = node;
		} else {
			if (before.prev) before.prev.next = node, node.prev = before.prev;
			else this.first = node;
			before.prev = node, node.next = before;
		}
		if ('$' in node) mapObj(this, node, '$');
		return this;
	},
	rmChild: function(node, destroy) {
		node = this.getNode(node);
		if (!node) throw BadParam('node', node);
		if ('$' in node) unmapObj(this, node, '$');
		var v;
		if (node.name && this[v = '$' + node.name] === node) delete this[v];
		if (this.first === node) this.first = node.next;
		if (this.last === node) this.last = node.prev;
		if (node.prev) node.prev.next = node.next;
		if (node.next) node.next.prev = node.prev;
		node.prev = node.next = null;
		node.parent = null;
		if (destroy !== false) node.destroy();
		return this;
	},
	replace: function(node, destroy) {
		if (this === node) return this;
		var p;
		if (!(node && node._TreeNode && (p = node.parent))) throw BadParam('node', node0);
		p.add(this, node);
		p.rmChild(node, destroy);
		return this;
	},
	clean: function() {
		this.destroyChilds();
		this.first = this.last = null;
		return this;
	},
	setChilds: function(childs) {
		childs = I.toArray(childs);
		if (childs) {
			var first;
			this.clean();
			childs.each(function(c) {
				this.add(c);
				if (!first) first = this.last;
			}.tie(this));
			while (this.first && this.first !== first) this.rmChild(this.first);
		} else this.clean();
		return this;
	},
	childs: function(q, flags, count, skip) {
		q = this.q(q), count = intVal(count, 1), skip = intVal(skip);
		var rev = 0;
		if (isString(flags) && flags.indexOf('r') !== -1) rev = 1;
		var child = this._const.childs[rev], sibling = this._const.siblings[rev],
			r = count === 1 ? null : [];
		for (var n = this[child]; n; n = n[sibling]) {
			if (q && !q.fn.call(q.ct, n, this)) continue;
			if (skip) { --skip; continue; }
			if (r) r.push(n); else return n;
			if (count && !--count) break;
		}
		return r;
	},
	find: function(q, flags, from, count, skip) {
		q = this.q(q), count = intVal(count, 1), skip = intVal(skip);
		var rev = 0;
		if (isString(flags)) {
			if (flags.indexOf('r') !== -1) rev = 1;
			flags = flags.indexOf('s') !== -1;
		} else flags = false;
		var child = this._const.childs[rev], sibling = this._const.siblings[rev],
			r = count === 1 ? null : [];
		if (from && !from._TreeNode) throw BadParam('from', from);
		var n = from ? from[sibling] || from.parent : flags ? this : this[child];
		if (!n || (!flags && n === this)) return r;
		var next = function() {
			if (skip) { --skip; return; }
			if (r) r.push(n); else { r = n; return true; }
			if (count && !--count) return true;
		}
	l_mainloop:
		while(true) {
			if (!rev && (!q || q.fn.call(q.ct, n)) && next()) return r;
			if (n[child] && !n.root) { n = n[child]; continue; }
			else if (rev && (!q || q.fn.call(q.ct, n)) && next(n)) return r;
			if (n === this) return r;
			while (!n[sibling]) {
				n = n.parent;
				if (rev && (!(n === this || n.root) || flags) && (!q || q.fn.call(q.ct, n)) && next(n))
					return r;
				if (n === this || n.root) return r;
			}
			n = n[sibling];
		}
		return r;
	}
});

var Url = Class('Url', {
	static_curPath: function() { return window.location.pathname.replace(/[^\/]*$/, ''); },
	construct: function(url) {
		if (!this._Url)
			return url && url._Url ? url : new Url(url);
		if (url)
			if (url._Url) extract(this, url, 'proto user password host path file anchor params');
			else this.parse(url);
	},
	_re0: /(?:([\w]+)\:)?(?:([\w]+)@)?([^\/|\:|#|\?]+)?(?:\:(\d+))?(.*)?/,
	_re1: /^((?:[^\/]*\/)+)?([^#?]*)(?:#([^?]+))?(\?.*)?/,
	parse: function(url, full) {
		var _;
		this.proto = this.user = this.password = this.host = this.path = this.file = this.anchor = _;
		this.params = {};
		url = strVal(url);
		if (full === undefined) full = url.charAt(0) !== '/';
		if (full) {
			if (_ = this._re0.exec(url)) {
				this.proto = _[1], this.user = _[2], this.password = _[3], this.host = _[4],
				this.port = _[5], url = _[6];
			} else return false;
		}
		if (!url) return true;
		_ = this._re1.exec(url);
		if (full && _[1].charAt(0) !== '/') return false;
		this.path = _[1], this.file = _[2] || undefined, this.anchor = _[3], this.parseParams(_[4]);
		return true;
	},
	get: function(full) {
		if (full === undefined) full = Boolean(this.host);
		var r = '';
		if (full) {
			r += (this.proto ? this.proto + ':' : window.location.protocol) + '//';
			if (this.host) {
				if (this.user) {
					r += this.user;
					if (this.password) r += ':' + this.password;
					r += '@';
				}
				r += this.getHost();
			} else r += window.location.host;
			if (full !== 42 && this.path) r += this.getPath();
		} else if (this.path) r = this.path || '';
		if (this.file) r += this.file;
		if (this.anchor) r += '#' + this.anchor;
		r += this.getParams(true);
		return r;
	},
	toString: function() { return this.get(); },
	getHost: function() {
		if (!this.host) return window.location.host;
		var r = this.host;
		if (this.port) r += ':' + this.port;
		return r;
	},
	getPath: function() {
		if (!this.path) return this.host ? '/' : Url.curPath();
		if (this.path.charAt(0) === '/') return this.path;
		return Url.curPath() + this.path;
	},
	setParams: function(params) {
		if (isString(params)) return this.parseParams(params);
		this.params = isStruct(params) ? params : {};
		return this;
	},
	parseParams: function(params) {
		this.params = {};
		if (!params) return true;
		if (params.charAt(0) === '?') params = params.substr(1);
		params.split('&').each(function(s) {
			var a = s.split('=', 2);
			this.params[decodeURIComponent(a[0])] = decodeURIComponent(a[1]);
		}.tie(this));
		return true;
	},
	getParams: function(qmark) {
		if (!this.params) return '';
		if (!isDefined(qmark)) qmark = true;
		var r = Hash(this.params).values(function(n, v) {
			return encodeURIComponent(n) + '=' + encodeURIComponent(v);
		}.tie(this)).join('&');
		if (qmark && r) r = '?' + r;
		return r;
	}
});

var Cookie = Class('Cookie', {
	set: function(name, value, days, domain, path) {
		value = strVal(value);
		var c = name + '=' + value + ';';
		if (days = intVal(days) || !value) {
			var date = new Date();
			date.setTime((value ? date.getTime() + days * 24 * 60 * 60 * 1000 : 0));
			c += 'expires=' + date.toGMTString() + ';';
		}
		if (!isDefined(path)) path = '/';
		c += 'path=' + path + ';';
		if (isDefined(domain)) c += 'domain=' + domain + ';';
		document.cookie = c;
		return Cookie;
	},
	get: function(name) {
		var _ = RegExp('\\b' + name + '=([^;]+)').exec(document.cookie);
		if (_) return _[1];
	},
	remove: function(name, domain, path) {
		return Cookie.set(name, '', 0, domain, path);
	}
});

var Ajax = Class('Ajax', {
	method: 'POST',
	async: true,
	xRequestedWith: 'XMLHttpRequest',
	accept: 'text/javascript, text/html, application/xml, text/xml, */*',
	contentType: 'application/x-www-form-urlencoded',
	encoding: 'UTF-8',
	construct: function(url, callback, options) {
		if (!this._Ajax)
			return url && url._Ajax ? url : new Ajax(url, callback, options);
		if (url)
			if (url._Ajax) extract(this, url,
				'url method async xRequestedWith accept contentType encoding callback');
			else this.url = Url(url);
		extend(this, options);
		this.callback = callback;
	},
	_getUrl: function() {
		var temp;
		if (this.method === 'POST') temp = this.url.params, this.url.params = null;
		var r = this.url.get();
		if (temp) this.url.params = temp;
		return r;
	},
	_getSendBody: function() {
		if (this.method === 'GET') return '';
		if (this.method === 'POST' && this.contentType === 'application/x-www-form-urlencoded')
			return this.url.getParams(false);
		return this.sendBody || '';
	},
	_assignHeaders: function(t) {
		t.setRequestHeader('X-Requested-With', this.xRequestedWith);
		t.setRequestHeader('Accept', this.accept);
		if (isStruct(this.headers))
			for (var h in this.headers)
				t.setRequestHeader(h, this.headers[h]);
		if (this.method !== 'GET' && this.contentType)
			t.setRequestHeader('Content-type', this.contentType
				+ (this.encoding ? '; charset=' + this.encoding : ''));
	},
	run: function(callback) {
		if (!(this.url && this.url._Url)) throw Exception('missing url');
		return new AjaxResponse(this, callback);
	}
});
AjaxResponse = Class('AjaxResponse', {
	construct: function(ajax, callback) {
		this.ajax = ajax;
		this.callback = Callback(callback || ajax.callback);
		var t = this.transport = this._create();
		t.onreadystatechange = this._mkResponseHandler();
		t.open(ajax.method, ajax._getUrl(), ajax.async);
		ajax._assignHeaders(t);
		t.send(ajax._getSendBody());
	},
	_create: function() {
		return [
			function() { return new XMLHttpRequest(); },
			function() { return new ActiveXObject('MSXML2.XMLHTTP'); },
			function() { return new ActiveXObject('Microsoft.XMLHTTP'); }
		].any(function(fn) {
			try {
				var r = fn();
				this._AjaxResponse._create = fn;
				return r;
			} catch(err) {}
		}.tie(this));
	},
	_mkResponseHandler: function() {
		var r = this;
		return function() {
			var t = r.transport;
			if (t.readyState === 4) r.success = t.status >= 200 && t.status < 300;
			else if (!r.ajax.allStates) return;
			if (r.success) {
				r.status = t.status || 0;
				r.statusText = t.statusText || '';
				r.responseText = r.success ? t.responseText : '';
			}
			if (r.callback) r.callback.fn.call(r.callback.ct, r);
			if (t.readyState === 4) t.onreadystatechange = Function.empty, this.transport = null;
		}
	},
	getJSON: function() {
		if (!this.responseText) return;
		try {
			var r = eval('(' + this.responseText + ')');
			return r;
		} catch (err) {}
	}
});

var Pos = Class('Pos', {
	construct: function(x, y) {
		if (!this._Pos)
			return x && x._Pos ? x : new Pos(x, y);
		if (x && x._Pos) y = x.y, x = x.x;
		this.x = x, this.y = y;
	},
	toString: function() {
		return 'Pos(' + this.x + ', ' + this.y + ')';
	},
	sub: function(x, y) {
		if (x && x._Pos) y = x.y, x = x.x;
		if (x && x._Size) y = x.h, x = x.w;
		this.x -= x, this.y -= y;
		return this;
	},
	add: function(x, y) {
		if (x && x._Pos) y = x.y, x = x.x;
		if (x && x._Size) y = x.h, x = x.w;
		this.x += x, this.y += y;
		return this;
	},
	diff: function(x, y) { return new Pos(this).sub(x, y); },
	offset: function(x, y) { return new Pos(this).add(x, y); }
});

var Size = Class('Size', {
	construct: function(w, h) {
		if (!this._Size) {
			if (w && w._Size) return w;
			return new Size(w, h);
		}
		this.w = intVal(w), this.h = intVal(h);
	},
	toString: function() { return 'Size(' + this.w + 'x' + this.h + ')'; }
});

var Observable = Class('Observable', {
	destroy: function() {
		this.rmListener();
		this.unlisten();
	},
	addListener1: function(id, name, callback) {
		if (!isString(name)) throw BadParam('name', name);
		var o = this, ev = (ev = this._events || (this._events = {}))[name] || (ev[name] = {});
		if (isObject(id)) o = id, id = o.getObjId();
		else if (isString(callback)) { ev[id] = callback; return; }
		if (isFunction(callback)) callback.setName(name, 'event: ' + id);
		if (!(ev[id] = Callback(callback, o))) throw BadParam('callback', callback);
	},
	addListener: function(id, name, callback) {
		switch (arguments.length) {
			case 1:
				if (!isStruct(id)) throw BadParam('callback', id);
				for (id in callback = id)
					if (!isStruct(callback[id])) this.addListener1('', id, callback[id]);
					else for (name in callback[id]) this.addListener1(id, name, callback[id][name]);
				break;
			case 2:
				if (!isStruct(name)) throw BadParam('callback', id);
				for (name in callback = name)
					this.addListener1(id, name, callback[name]);
				break;
			default:
				this.addListener1(id, name, callback);
		}
		return this;
	},
	hasListener: function(name) {
		var ev = this._events;
		if (ev && (ev = ev[name])) for (var id in ev) return true;
		return false;
	},
	_fire: function(name, args) {
		var ev = this._events, r = true, cb, o;
		if (!(ev && (ev = ev[name]))) return r;
		for (var id in ev)
			if (isString(cb = ev[id])) {
				if (!(this.getEventTarget && (o = this.getEventTarget(id)))) continue;
				if (isFunction(cb = o[cb]) && cb.apply(o, args) === false) r = false;
			} else if (cb.fn.apply(cb.ct, args) === false) r = false;
		return r;
	},
	fire: function(name) {
		if (!isString(name)) throw BadParam('name', name);
		return this._events && this._events[name]
			? this._fire(name, A(arguments, 1)) : true;
	},
	fireThis: function(name) {
		if (!isString(name)) throw BadParam('name', name);
		return this._events && this._events[name]
			? this._fire(name, A(arguments, 1).eunshift(this)) : true;
	},
	callFireThis: function(name) {
		if (!isString(name)) throw BadParam('name', name);
		var fnName = 'on' + name.capitalize(), args;
		if (this[fnName]) {
			args = A(arguments, 1);
			this[fnName].apply(this, args);
		}
		if (this._events && this._events[name]) {
			if (!args) args = A(arguments, 1);
			args.unshift(this);
			return this._fire(name, args);
		}
		return true;
	},
	rmListener: function(id, name) {
		var o, ev = this._events;
		if (!ev) return this;
		if (isStruct(id)) {
			for (name in o = id) if (ev[name]) for (id in o[name]) delete ev[name][id];
		} else if (isDefined(id)) {
			if (isObject(id)) o = id, id = o.getObjId();
			if (isStruct(name)) for (name in o = name) if (ev[name]) delete ev[name][id];
			if (name) { if (ev[name]) delete ev[name][id]; }
			else for (name in ev) delete ev[name][id];
		} else if (name) {
			delete ev[name];
		} else delete this._events;
		return this;
	},
	toggleListener: function(id, name, callback, toggle) {
		switch (arguments.length) {
			case 2:
				if (name) this.addListener(id); else this.rmListener(id);
				break;
			case 3:
				if (callback) this.addListener(id, name); else this.rmListener(id, name);
				break;
			case 4:
				if (toggle) this.addListener(id, name, callback); else this.rmListener(id, name);
				break;
		}
		return this;
	},
	listen1: function(observable, id, name, callback) {
		var o = (o = this._obsevables || (this._observables = {}))[id] || (o[id] = {});
		if (o[name]) o[name].rmListener(this, name), o[name] = null;
		if (observable) {
			var cb = Callback(callback, this);
			if (!cb) throw BadParam('callback', callback);
			o[name] = observable;
			observable.addListener1(this, name, cb);
		}
	},
	listen: function(observable, id, name, callback) {
		switch (arguments.length) {
			case 2:
				if (!isStruct(id)) throw BadParam('callback', id);
				for (id in callback = id) {
					if (!isStruct(callback[id])) this.listen1(observable, '', id, callback[id]);
					else for (name in callback[id]) this.listen1(observable, id, name, callback[id][name]);
				}
				break;
			case 3:
				if (!isStruct(name)) throw BadParam('callback', name);
				for (name in callback = name)
					this.listen1(observable, id, name, callback[name]);
				break;
			case 4:
				this.listen1(observable, id, name, callback);
				break;
		}
		return this;
	},
	unlisten1: function(id, name) {
		var o = this._observables;
		if (!o) return this;
		if ((o = o[id]) && o[name]) o[name].rmListener(this, name);
	},
	unlisten: function(id, name) {
		var o = this._observables;
		if (!o) return;
		if (isDefined(id)) this.unlisten1(id, name);
		else for (id in o) this.unlisten1(id);
		return this;
	}
});

var DomBase = Class('DomBase', {
	static__cache: { length: 0, freeId: [] },
	static__addToCache: function(obj) {
		var c = DomBase._cache, id;
		if (c.freeId.length) id = c.freeId.pop();
		else id = c.length++;
		c[id] = obj;
		obj._cacheId = id;
	},
	static__removeFromCache: function(obj) {
		var c = DomBase._cache, id = obj._cacheId;
		if (!id) return;
		c[id] = null;
		c.freeId[c.freeId.length] = id;
	},
	static_destroy: function() {
		for (var o = DomBase._cache, i = 0; i < o.length; ++i)
			if (o[i]) o[i].destroy(false);
	},
	construct: function(e) {
		this.e = e, e._ajs = this;
		if (Browser.IE || Browser.Safari) DomBase._addToCache(this);
	},
	destroy: function() {
		if (this.e) {
			this.e._ajs = null;
			this.e = null;
		}
		if (Browser.IE || Browser.Safari) DomBase._removeFromCache(this);
	}
});

var DomObservable = Class('DomObservable', Observable, {
	static__cache: { length: 0, freeId: [] },
	static__addToCache: function(obj) {
		var c = DomBase._cache, id;
		if (c.freeId.length) id = c.freeId.pop();
		else id = c.length++;
		c[id] = obj;
		obj._cacheId = id;
	},
	static__removeFromCache: function(obj) {
		var c = DomBase._cache, id = obj._cacheId;
		if (!id) return;
		c[id] = null;
		c.freeId[c.freeId.length] = id;
	},
	static_destroy: function() {
		for (var o = DomBase._cache, i = 0; i < o.length; ++i)
			if (o[i]) o[i].destroy(false);
	},
	construct: function(e) {
		this.e = e, e._ajs = this;
		if (Browser.IE || Browser.Safari) DomObservable._addToCache(this);
	},
	destroy: function() {
		this.rmDomListener();
		if (this.e) this.e = this.e._ajs = null;
		if (Browser.IE || Browser.Safari) DomObservable._removeFromCache(this);
		this._Observable_destroy();
	},
	_addDomListener: Browser.IE
		? function(name, handler) { this.e.attachEvent('on' + name, handler); }
		: function(name, handler) { this.e.addEventListener(name, handler, false); },
	_rmDomListener: Browser.IE
		? function(name, handler) { this.e.detachEvent('on' + name, handler); }
		: function(name, handler) { this.e.removeEventListener(name, handler, false); },
	addDomListener: function(id, name, handler) {
		var ev = (ev = this._domEvents || (this._domEvents = {}))[id] || (ev[id] = {});
		if (!handler) handler = this._getDomEventHandler();
		if (!ev[name]) ev[name] = handler;
		else if (ev[name]) this._rmDomListener(name, ev[name]);
		ev[name] = handler;
		this._addDomListener(name, handler);
		if ((Browser.IE || Browser.Safari) && name === 'keypress') {
			this.addDomListener('', 'keydown');
			this.addDomListener('', 'keyup');
		}
		return this;
	},
	rmDomListener: function(id, name) {
		var ev = this._domEvents;
		if (!ev) return this;
		if (id) {
			if (name) { this._rmDomListener(name, ev[id][name]); delete ev[id][name]; }
			else {
				for (name in ev) this._rmDomListener(name, ev[id][name]);
				delete ev[id];
			}
		} else {
			for (id in ev) for (name in ev[id]) this._rmDomListener(name, ev[id][name]);
			delete this._domEvents;
		}
		return this;
	},
	_eventName: function(name) {
		var _ = /^(.*)(?:right|middle)$/.exec(name);
		if (_) name = _[1];
		return name in this._domEventNames ? name : '';
	},
	addListener1: function(id, name, callback) {
		this._Observable_addListener1(id, name, callback);
		if (name = this._eventName(name)) this.addDomListener('', name);
		return this;
	},
	_getDomEventHandler: function() {
		if (this._domEventHandler) return this._domEventHandler;
		var ob = this;
		return this._domEventHandler = function(ev) {
			ev = new DomEvent(ob, ev);
			if (!ev.silent) {
				var r = ob.fire(ev.type, ev);
				if (!r) ev.cancel(), ev.stop();
			}
			ev.destroy();
		}.setName('_domEventHandler', this._Class_name);
	}
});

var Key = {
	backspace: 8, tab: 9, enter: 13, esc: 27, left: 37, up: 38, right: 39, down: 40,
	del: 46, home: 36, end: 35, pgup: 33, pgdn: 34, ins: 45, space: 32
};

var DomEvent = Class('DomEvent', {
	static__mouseBtnEvents: F('click dblclick mousedown mouseup'),
	static__keyEvents: F('keydown keyup keypress'),
	static__buttonNames: A(' middle right'),
	construct: function(owner, ev) { this.init(owner, ev); },
	onInit: function() {
		switch (this.type) {
			case 'keydown':
				if (DomEvent._keydown === (this.ev.which || this.ev.keyCode)) return false;
				else DomEvent._keydown = this.ev.which || this.ev.keyCode;
				break;
			case 'keyup':
				DomEvent._keydown = false;
				break;
		}
	},
	onDestroy: function() {
		switch (this.ev.type) {
			case 'keydown':
				if (this.ev.keyCode < 0x32 && this.ev.keyCode !== 32) {
					if (Browser.IE) this.owner.e.fireEvent('onkeypress', this.ev);
					else if (Browser.Safari) {
						var evt = document.createEvent('KeyboardEvent'), mods = [];
						if (this.ev.crtlKey) mods.push('Control');
						if (this.ev.shiftKey) mods.push('Shift');
						if (this.ev.altKey) mods.push('Alt');
						if (this.ev.metaKey) mods.push('Meta');
						evt.initKeyboardEvent('keypress', false, true, window,
							this.ev.keyIdentifier, this.ev.keyLocation, mods.join(' '));
						DomEvent._lastKey = this.ev.which;
						this.owner.e.dispatchEvent(evt);
					}
				}
				break;
			case 'focus':
				if (Browser.Opera) window.getSelection().collapseToStart();
				break;
			case 'mousedown':
				if (Browser.IE && this.owner._focusOk) tie(this.owner, 'focus').defer();
				break;
		}
	},
	destroy: function() {
		this.onDestroy();
		this.ev = null;
	},
	toString: function() {
		return this.type + '(' + this.getTarget() + ')';
	},
	init: function(owner, ev) {
		this.ev = ev;
		this.owner = owner;
		this.type = this.ev.type;
		if (this.onInit() === false) { this.silent = true; return; }
		if (this.type in DomEvent._mouseBtnEvents) {
			this.button = 0;
			if (Browser.IE) {
				if (this.ev.button & 1) this.button = 1;
				if (this.ev.button & 2) this.button = 3;
				else if (this.ev.button & 4) this.button = 2;
			} else if (Browser.WebKit) {
				if (this.ev.which === 1)
					if (this.ev.metaKey) this.button = 3; else this.button = 1;
			} else this.button = this.ev.which;
			if (this.button) this.type = this.type + DomEvent._buttonNames[this.button - 1];
		} else if (this.type in DomEvent._keyEvents) {
			if (DomEvent._lastKey)
				this.keyCode = DomEvent._lastKey, DomEvent._lastKey = 0;
			else this.keyCode = this.ev.which || this.ev.keyCode,
				this.keyChar = String.fromCharCode(this.keyCode);
		}
	},
	getTarget: function() {
		return E(this.ev.target || this.ev.srcElement);
	},
	targets: function(q) {
		var e = this.getTarget();
		if (!(e && q)) return e;
		return e.parents(q, 's');
	},
	getRTarget: function() {
		var e;
		if (Browser.IE)
			switch (this.ev.type) {
				case 'mouseover': e = this.fromElement; break;
				case 'mouseout': e = this.toElement; break;
				default: return null;
			}
		else e = this.ev.relatedTarget;
		return E(e);
	},
	rtargets: function(q) {
		var e = this.getRTarget();
		if (!(e && q)) return e;
		return e.parents(q, 's');
	},
	getMousePos: function() {
		if (isDefined(this.ev.pageX)) return new Pos(this.ev.pageX, this.ev.pageY);
		return this.getTarget().getHtml().getScrollPos().add(this.ev.clientX, this.ev.clientY);
	},
	cancel: function() {
		if (this.ev.preventDefault) this.ev.preventDefault();
		else this.ev.returnValue = false;
		return this;
	},
	stop: function() {
		if (this.ev.stopPropagation) this.ev.stopPropagation();
		else this.ev.cancelBubble = true;
		return this;
	},
	kill: function() {
		this.cancel();
		this.stop();
		return this;
	}
});

var $ = function(id) {
	var r;
	if (isString(id)) {
		if (!(r = document.getElementById(id))) throw Exception('Element id="' + id + '" not found');
		return E(r);
	} else if (id === Ajs.BODY) {
		if (!document.body) throw Exception('Document body not exists yet');
		return Ajs.document.getBody();
	} else if (id) {
		if (id._E) return id;
		if (id.nodeVal === 1) return E(id);
	}
	throw BadParam('id', id);
}.setName('$');
var is$ = function(id) {
	if (id === Ajs.BODY) return Boolean(document.body);
	return Boolean(id && document.getElementById(id));
};

var E = Class('E', DomObservable, {
	static_text: function(text) {
		return document.createTextNode(text);
	},
	static__re: /^([A-Z\d]+)?(?:#(\w+))?(?::([\[\]\w]+))?(?:\.([-\w.]+))?(.*)$/,
	static_val: function(e, create) {
		if (!e) return null;
		if (e && e.toE && !(e = e.toE())) return null;
		if (e._E) return e;
		if (e.nodeType === 1) return new E(e);
		return E(create ? e : document.getElementById(e));
	},
	static_nodeVal: function(e, create) {
		if (e) {
			if (e.nodeType === 1 || e.nodeType === 3) return e;
			if (e.toE && !(e = e.toE())) return null;
			if (isString(e)) return E.text(e);
			return e._E ? e.e : (create ? ((e = E(e)) && e.e) : document.getElementById(e));
		}
		return null;
	},
	_domEventNames: F(
		'click dblclick mousemove mousedown mouseup mouseover mouseout ' +
		'keypress keyup keydown change focus blur scroll submit'
	),
	_const: {
		attrsRe: /(\w+)="([^"]*)/g,
		styleRe: /([-\w]+):\s*([^;]*)/g,
		focusEvents: /^(?:focus|blur|keydown|keyup|keypress)/,
		sizes: { o: 0, c: 1, i: 2 },
		sizeStyle: { 0: 'width', 1: 'height' },
		sizeOffset: { 0: 'offsetWidth', 1: 'offsetHeight' },
		sizeClient: { 0: 'clientWidth', 1: 'clientHeight' },
		bounds: { a: 0, m: 1 },
		sides: { l: 0, t: 1, r: 2, b: 3 },
		edges: { l: 1, t: 2, r: 4, b: 8, w: 5, h: 8 },
		sideStyle: { 0: 'left', 1: 'top', 2: 'right', 3: 'bottom' },
		sideOffset: { 0: 'offsetLeft', 1: 'offsetTop' },
		styleEdges: {
			margin: { 0: 'marginLeft', 1: 'marginTop', 2: 'marginRight', 3: 'marginBottom' },
			padding: { 0: 'paddingLeft', 1: 'paddingTop', 2: 'paddingRight', 3: 'paddingBottom' },
			bwidth: { 0: 'borderLeftWidth', 1: 'borderTopWidth',
				2: 'borderRightWidth', 3: 'borderBottomWidth' }
		},
		childs: { 0: 'firstChild', 1: 'lastChild' },
		siblings: { 0: 'nextSibling', 1: 'prevSibling' },
		nativeAttrs: F('id name disabled checked selected'),
		ieAttrMap: A('align cellSpacing colSpan height hideFocus rowSpan tabIndex type vAlign width')
			.pack({}, function(r, v) { r[v.toLowerCase()] = v; return r; })
	},
	construct: function(e) {
		if (!e) return null;
		if (e._E) return e;
		if (this._E) { this._DomObservable(e); return; }
		if (e.nodeType) {
			if (e.nodeType !== 1) return null;
			return e._ajs || new E(e);
		}
		if (e.toE) return e.toE();
		var _;
		if (isStruct(e)) {
			if (!e.tag) throw BadParam('e', e);
			_ = e;
			if (_.name && Browser.IE)
				e = document.createElement('<$tag name="$name">'.format(_));
			else e = document.createElement(String(_.tag));
			e = new E(e);
			e.setAttrs(_);
		} else if (isString(e)) {
			if (!((_ = E._re.exec(e)) && _[1])) throw BadParam('e', e);
			if (!(e = Browser.IE && _[3]
				? document.createElement('<$1 name="$2">'.format(_))
				: e = document.createElement(_[1])
			)) return null;
			e = new E(e);
			if (_[2]) e.setAttr('id', _[2]);
			if (!Browser.IE && _[3]) e.setAttr('name', _[3]);
			if (_[4]) e.setAttr('class', _[4]);
			if (_[5]) e.setAttrs(_[5]);
		} else return null;
		if (e.getTagName() === 'A' && !e.getAttr('href')) e.setAttr('href', 'javascript:void(0)');
		if (e.getTagName() === 'TABLE' && !e.getAttr('cellspacing')) e.setAttr('cellspacing', 0);
		return e;
	},
	setOwner: function(owner) {
		this.owner = owner;
		return this;
	},
	getEventTarget: function(cls) {
		for (var o, e = this.e; e; e = e.parentNode) {
			if (e.nodeType !== 1) break;
			if (e._ajs && (o = e._ajs.owner)) {
				if (o[cls]) return o;
				else if (o.getEventTarget) return o.getEventTarget(cls);
				break;
			}
		}
		return null;
	},
	addDomListener: function(id, name, handler) {
		var ev;
		if (!this._focusOk && this._const.focusEvents.test(name)) {
			if (!this.getAttr('tabindex')) this.setAttr('tabindex', 0);
			if (Browser.IE) this.addDomListener('', 'mousedown'), this.e.hideFocus = true;
			this._focusOk = true;
		}
		return this._DomObservable_addDomListener(id, name, handler);
	},
	destroyChilds: function() {
		this.find(function(e) {
			e.destroy(false);
		});
	},
	destroy: function(cascade) {
		if (!this.e) return;
		if (this.mouseCaptured) this.releaseMouse();
		if (cascade !== false) {
			this.destroyChilds();
			this.remove(false);
		}
		this._DomObservable_destroy(this);
	},
	toString: function() {
		if (!this.e) return 'destroyed!';
		var r = this.e.tagName, a;
		if (a = this.e.id) r += '#' + a;
		if (a = this.e.name) r += ':' + a;
		if (this.e.className) r += '.' + this.e.className.replace(/ /g.reset(), '.');
		return r;
	},
	set$: function($) {
		var $0, p = this.getParent();
		if (p && this.eid && p[$0 = '$' + this.$] === this) delete p[$0];
		this.$ = $;
		if (p) p['$' + $] = this;
		return this;
	},
	getWindow: function() { var r;
		return (r = this.e.ownerDocument) && Window(r.defaultView || r.parentWindow); },
	getDoc: function() { return Document(this.e.ownerDocument); },
	getHtml: function() { var r; return (r = this.e.ownerDocument) && E(r.documentElement); },
	getBody: function() { var r; return (r = this.e.ownerDocument) && E(r.body); },
	getOffsetParent: function() { return this.e.offsetParent ? E(this.e.offsetParent) : null; },
	getTagName: function() { return this.e.tagName; },
	addCls: function(c) {
		if (isString(c) && !this.e.className) this.e.className = c;
		else this.e.className = new Flags(this.e.className).add(c).toString();
		return this;
	},
	rmCls: function(c) {
		if (this.e.className === c) this.e.className = '';
		else this.e.className = new Flags(this.e.className).rm(c).toString();
		return this;
	},
	isCls: function(c) { return (' ' + this.e.className + ' ').indexOf(' ' + c + ' ') !== -1; },
	toggleCls: function(c, set) {
		if (arguments.length < 2) this.e.className = new F(this.e.className).invert(c).toString();
		else if (set) this.addCls(c);
		else this.rmCls(c);
		return this;
	},
	setValue: function(value) { this.e.value = value; return this; },
	getValue: function() { return this.e.value; },
	setAttr: function(name, value) {
		switch (name) {
			case '$': return this.set$(value);
			case 'role': this.role = null; return this.addRole(value);
			case 'ipos':
			case 'tag': return this;
			case 'cls':
			case 'class': this.e.className = ''; return this.addCls(value);
			case 'style':
				if (isStruct(value) || isHash(value)) {
					this.e.style.cssText = ''; this.setStyles(value);
				} else this.e.style.cssText = value;
				return this;
			case 'value': return this.setValue(value);
			case 'inner':
			case 'childs': return this.setInner(value);
			case 'events': return this.addListener(value);
		}
		var n;
		if (this._const.nativeAttrs[name]) this.e[name] = value;
		else if (Browser.IE && (name in (n = this._const.ieAttrMap))) {
			if (n = n[name]) this.e[n] = value;
			return this;
		}
		else if (!isDefined(value)) this.e.removeAttribute(name);
		else this.e.setAttribute(name, value);
		return this;
	},
	getAttr: function(name) {
		switch (name) {
			case 'role': return this.role ? this.role.toString() : null;
			case '$': return this.$;
			case 'ipos': return;
			case 'eid': return this.eid;
			case 'tag': return this.e.tagName;
			case 'cls':
			case 'class': return this.e.className;
			case 'style':
				name = this.e.style.cssText;
				if (Browser.IE) name = name.toLowerCase();
				return name;
			case 'value': return this.getValue();
			case 'inner':
			case 'childs': return this.e.innerHTML;
			case 'events': return this._events;
		}
		var n;
		if (this._const.nativeAttrs[name]) return this.e[n];
		else if (Browser.IE) {
			if (name in (n = this._const.ieAttrMap)) {
				if (n = n[name]) return this.e[n];
			}
			var r = this.e.getAttribute(name);
			if (r !== null) return r;
		} else if (this.e.hasAttribute(name))
			return this.e.getAttribute(name);
	},
	setAttrs: function(attrs) {
		for (var n in attrs = H(attrs, this._const.attrsRe)) this.setAttr(n, attrs[n]);
		return this;
	},
	_name2style: function(s) {
		s = s.replace(/-(\w)/g.reset(), function(p, p1, o) { return o ? p1.toUpperCase() : p1; });
		if (s === 'float') s = 'styleFloat' in this.e.style ? 'styleFloat' : 'cssFloat';
		return s;
	},
	setStyle: function(name, value) {
		this.e.style[this._name2style(name)] = pxStr(value);
		return this;
	},
	getStyle: function(name) {
		return this.e.style[this._name2style(name)];
	},
	getComputedStyle: function(name) {
		name = this._name2style(name);
		if (Browser.IE) return this.e.currentStyle ? this.e.currentStyle[name] : this.e.style[name];
		return this.getWindow().e.getComputedStyle(this.e, '')[name];
	},
	setStyles: function(styles) {
		for (var n in styles = H(styles, this._const.styleRe)) this.setStyle(n, styles[n]);
		return this;
	},
	setDisplay: function(value) {
		if (value === true) this.e.style.display = '';
		else if (value === false) this.e.style.display = 'none';
		else if (value === 0) this.e.style.display = this.e.style.display === 'none' ? '' : 'none';
		else this.e.style.display = value;
		return this;
	},
	getDisplay: function() { return this.e.style.display !== 'none'; },
	setPosition: function(p) {
		if (p === true) p = 'absolute';
		else if (p === false) {
			p = 'relative';
			if (Browser.IE6) this.e.style.zoom = 1;
		} else p = 'static';
		this.e.style.position = p;
		return this;
	},
	getPosition: function() {
		p = this.setStyle('position');
		if (p === 'absolute' || p === 'fixed') return true;
		else if (p === 'relative') return false;
	},
	_setSize: function(dir, min, type, v) {
		if (type in this._const.sizes) type = this._const.sizes[type];
		else v = type, type = 0;
		if (isNumber(v)) {
			if (!(type & 3)) v -= this.getBWidths(5 << dir);
			if (!(type & 2)) v -= this.getPaddings(5 << dir);
			if (v < 0) v = 0;
			v = pxStr(v);
		} else if (!v) v = '';
		type = (type & 4 && !Browser.IE6 ? this._const.sizeMinStyle : this._const.sizeStyle)[dir];
		this.e.style[type] = v;
		return this;
	},
	_getSize: function(dir, type) {
		if (type in this._const.sizes) type = this._const.sizes[type];
		if (type & 1 && !(type & 2) && this.e[this._const.sizeClient[dir]])
			return this.e[this._const.sizeClient[dir]];
		var v = this.e[this._const.sizeOffset[dir]];
		if (type & 3) v -= this.getBWidths(5 << dir);
		if (type & 2) v -= this.getPaddings(5 << dir);
		if (v < 0) v = 0;
		return v;
	},
	setWidth: function(type, w) { return this._setSize(0, false, type, w); },
	getWidth: function(type) { return this._getSize(0, type); },
	setHeight: function(type, h) { return this._setSize(1, false, type, h); },
	setMinHeight: function(type, h) { return this._setSize(1, true, type, h); },
	getHeight: function(type) { return this._getSize(1, type); },
	setSize: function(type, w, h) {
		if (!(type in this._const.sizes)) h = w, w = type, type = 'o';
		if (w && w._Size) h = w.h, w = w.w;
		this._setSize(0, false, type, w);
		this._setSize(1, false, type, h);
		return this;
	},
	getSize: function(type) { return new Size(this._getSize(0, type), this._getSize(1, type)); },
	getMarginsCollapse: function(side, e) {
		if (side in this._const.sides) side = this._const.sides[side];
		var m1 = this.getMargins(1 << side), m2;
		if (isElement(e)) m2 = e.getMargins(1 << (side ^ 2));
		else {
			var op = this.offsetParent();
			m2 = op ? op.paddings(1 << side) : 0;
		}
		if (m1 >= 0 && m2 >= 0) return m1 >= m2 ? 0 : m2;
		else if (m1 < 0 && m2 < 0) return m1 <= m2 ? 0 : m2;
		else return m1 + m2;
	},
	_setBound: function(side, margin, v) {
		if (margin in this._const.bounds) margin = this._const.bounds[margin];
		else v = margin, margin = 0;
		if (isNumber(v)) {
			if (!margin) v -= this.getMargins(1 << side);
			v = pxStr(v);
		}
		this.e.style[this._const.sideStyle[side]] = v;
		return this;
	},
	_getBound: function(side, margin) {
		if (margin in this._const.bounds) margin = this._const.bounds[margin];
		else margin = 0;
		var v = this.e[this._const.sideOffset[side & 1]];
		if (side & 2) {
			var p = this.offsetParent();
			if (p) v = p.getWidth('c') - this.getWidth() - v;
		}
		if (margin) v -= this.getMargins(1 << side);
		return v;
	},
	setLeft: function(margin, l) { return this._setBound(0, margin, l); },
	getLeft: function(margin) { return this._getBound(0); },
	setTop: function(margin, t) { return this._setBound(1, margin, t); },
	getTop: function(margin) { return this._getBound(1); },
	setRight: function(margin, r) { return this._setBound(2, margin, r); },
	getRight: function(margin) { return this._getBound(2, margin); },
	setBottom: function(margin, b) { return this._setBound(3, margin, b); },
	getBottom: function(margin) { return this._getBound(3, margin); },
	setBounds: function(margin, l, t, r, b) {
		if (!(margin in this._const.bounds)) b = r, r = t, t = l, l = margin, margin = 'a';
		this._setBound(0, margin, l);
		this._setBound(1, margin, t);
		this._setBound(2, margin, r);
		this._setBound(3, margin, b);
		return this;
	},
	setPos: function(margin, x, y) {
		if (!(margin in this._const.bounds)) y = x, x = margin, margin = 'a';
		if (x instanceof Pos) y = x.y, x = x.x;
		this._setBound(0, margin, x);
		this._setBound(1, margin, y);
		return this;
	},
	getPos: function(margin) {
		return new Pos(this._getBound(0, margin), this._getBound(1, margin));
	},
	resetPos: function() {
		var s = this.e.style;
		s.position = s.top = s.left = s.bottom = s.right = s.width = s.height = s.zIndex = '';
		return this;
	},
	getLayerPos: function(self) {
		var p = new Pos(0, 0);
		if (self) p.add(this.e.offsetLeft, this.e.offsetTop);
		for (var n = this.e.offsetParent; n; n = n.offsetParent) {
			p.add(n.offsetLeft, n.offsetTop);
			if (!Browser.Opera) {
				var e = E(n);
				p.add(e.getBWidths(1), e.getBWidths(2));
			}
		}
		return p;
	},
	setScrollPos: function(x, y) {
		if (x._Pos) y = x.y, x = x.x;
		else if (!isDefined(y)) y = x, x = undefined;
		if (x === -1) x = this.e.scrollWidth;
		if (y === -1) y = this.e.scrollHeight;
		if (isDefined(x)) this.e.scrollLeft = x;
		if (isDefined(y)) this.e.scrollTop = y;
		return this;
	},
	getScrollPos: function() { return Pos(this.e.scrollLeft, this.e.scrollTop); },
	refresh: function() { this.addCls('redraw').rmCls('redraw'); return this; },
	_edgeWidth: function(style, edge) {
		if (edge in this._const.edges) edge = this._const.edges;
		var r = 0;
		for (var i = 0, s = this._const.styleEdges[style]; i < 4; ++i)
			if (edge & 1 << i) r += pxVal(this.getComputedStyle(s[i]));
		return r;
	},
	getMargins: function(edge) { return this._edgeWidth('margin', edge); },
	getBWidths: function(edge) { return this._edgeWidth('bwidth', edge); },
	getPaddings: function(edge) { return this._edgeWidth('padding', edge); },
	setInner: function(childs) {
		var first;
		if (isArray(childs) || isStruct(childs)) {
			I.toArray(childs).each(function(e) {
				this.add(e);
				if (!first) first = this.e.lastChild;
			}.tie(this));
			while (this.e.firstChild && this.e.firstChild !== first)
				this.rmChild(this.e.firstChild);
		} else {
			this.clean();
			this.e.innerHTML = childs;
		}
		return this;
	},
	getInner: function() { return this.e.innerHTML; },
	add: function(e, before) {
		var n = E.nodeVal(e, true), bf = null;
		if (!n) throw BadParam('e', e);
		if (before) {
			if (!(bf = E.nodeVal(before))) BadParam('before', before);
		} else if (before === 0) bf = this.e.firstChild;
		this.e.insertBefore(n, bf);
		if (e = E(n)) {
			if (e.$) this['$' + e.$] = e;
		}
		return this;
	},
	addTo: function(e, before) {
		var el = E.val(e);
		if (!el) throw BadParam('e', e);
		el.add(this, before);
		return this;
	},
	rmChild: function(e, destroy) {
		var n = E.nodeVal(e), $0;
		if (!n) throw BadParam('e', e);
		if (e = E(n)) {
			if (e.$ && this[$0 = '$' + e.$] === this) delete e[$0];
		}
		this.e.removeChild(n);
		if (e && destroy !== false) e.destroy();
		return this;
	},
	remove: function(destroy) {
		var p = this.getParent();
		if (p) p.rmChild(this, destroy);
		return this;
	},
	replace: function(e, destroy) {
		var n = E.nodeVal(e), p;
		if (n === this.e) return this;
		if (!(n && (p = E(n.parentNode)))) throw BadParam('e', e);
		p.add(this, n).remove(n, destroy);
		return this;
	},
	clean: function() {
		this.destroyChilds();
		for (var e; e = this.e.firstChild; ) this.e.removeChild(e);
		return this;
	},
	q: function(q) {
		if (!q) return null;
		if (q && q._Callback) return q;
		if (isFunction(q)) return Callback(q, this);
		if (q && q._E) return Callback(function(e) { return e === q ? e : null; });
		if (_ = E.re.exec(q)) {
			_ = { tag: _[1], id: _[2], name: _[3],
				cls: _[4] ? _[4].split('.').emap(function(c) {
					if (c) return new RegExp('(?: |^)' + c + '(?: |$)');
				}) : null,
				attrs: _[5] ? Hash(_[5], E._const.attrsRe) : null
			}
			return Callback(function(e) {
				return (!_.tag || e.e.tagName === _.tag)
					&& (!_.id || e.e.id === _.id)
					&& (!_.name || e.e.name === _.name)
					&& (!_.cls || _.cls.all(function(re) { return re.test(e.e.className); }))
					&& (!_.attrs || _.attrs.all(function(v, a) { return e.getAttr(a) === v; }))
					? e : null;
			});
		}
		if (isString(q)) {
			_ = new RegExp('(?: |^)' + q + '(?: |$)');
			return function(e) {
				return _.test(e.e.className) ? e : null;
			}
		}
		throw BadParam('q', q);
	},
	getParent: function() {
		var r = this.e.parentNode;
		if (r && r.nodeType === 1) return E(r);
		return null;
	},
	parents: function(q, flags) {
		q = this.q(q);
		flags = isString(flags) && flags.indexOf('s') !== -1;
		for (var e, n = flags ? this.e : this.e.parentNode; n && n.nodeType === 1; n = n.parentNode) {
			e = E(n);
			if (q.fn.call(q.ct, e)) return e;
		}
		return null;
	},
	getFirst: function() {
		for (var e = this.e.prevSibling; e; e = e.prevSibling)
			if (e.nodeType === 1) return E(e);
		return null;
	},
	getNext: function() {
		for (var e = this.e.nextSibling; e; e = e.nextSibling)
			if (e.nodeType === 1) return E(e);
		return null;
	},
	siblings: function(q, flags, count, skip) {
		q = this.q(q), count = intVal(count, 1), skip = intVal(skip);
		var rev = 0;
		if (isString(flags)) {
			if (flags.indexOf('r') !== -1) rev = 1;
			flags = flags.indexOf('s') !== -1;
		} else flags = false;
		var sibling = this._const.siblings[rev], r = count === 1 ? null : [];
		for (var e, n = flags ? this.e : this.e[sibling]; n; n = n[sibling]) {
			if (n.nodeType !== 1) continue;
			e = E(n);
			if (q && !q.fn.call(q.ct, e)) continue;
			if (skip) { --skip; continue; }
			if (count && !--count)
				if (r) r.push(e); else return e;
		}
		return r;
	},
	getFirst: function() {
		for (var e = this.e.firstChild; e; e = e.nextSibling)
			if (e.nodeType === 1) return E(e);
		return null;
	},
	getLast: function() {
		for (var e = this.e.lastChild; e; e = e.prevSibling)
			if (e.nodeType === 1) return E(e);
		return null;
	},
	childs: function(q, flags, count, skip) {
		q = this.q(q), count = intVal(count, 1), skip = intVal(skip);
		var rev = 0;
		if (isString(flags) && flags.indexOf('r') !== -1) rev = 1;
		var child = this._const.childs[rev], sibling = this._const.siblings[rev],
			r = count === 1 ? null : [];
		for (var e, n = this.e[child]; n; n = n[sibling]) {
			if (n.nodeType !== 1) continue;
			e = E(n);
			if (q && !q(e)) continue;
			if (skip) { --skip; continue; }
			if (count && !--count)
				if (r) r.push(e); else return e;
		}
		return r;
	},
	find: function(q, flags, from, count, skip) {
		q = this.q(q), count = intVal(count), skip = intVal(skip);
		var rev = 0;
		if (isString(flags)) {
			if (flags.indexOf('r') !== -1) rev = 1;
			flags = flags.indexOf('s') !== -1;
		} else flags = false;
		var child = this._const.childs[rev], sibling = this._const.siblings[rev],
			r = count === 1 ? null : [];
		var e, n = E.nodeVal(from) || (flags ? this.e : this.e[child]);
		if (!n) return r;
		var next = function(e) {
			if (skip) { --skip; return; }
			if (r) r.push(e); else { r = e; return true; }
			if (count && !--count) return true;
		}
	l_mainloop:
		while(true) {
			if (n.nodeType === 1) {
				e = E(n);
				if (!rev && (!q || q.fn.call(q.ct, e)) && next(e)) return r;
				if (n[child] && !e.owner) { n = n[child]; continue; }
				else if (rev && (!q || q.fn.call(q.ct, e)) && next(e)) return r;
			}
			while (!n[sibling]) {
				n = n.parentNode;
				if (rev && ((n === this.e || (n._ajs && n._ajs.owner)) || flags)) {
					e = E(n);
					if ((!q || q.fn.call(q.ct, e)) && next(e)) return r;
				}
				if (n === this.e || (n._ajs && n._ajs.owner)) return r;
			}
			n = n[sibling];
		}
		return r;
	},
	findByPos: function(pos, q, nearest) {
		var oparent, opos, e1 = null, dr = Number.MAX_VALUE;
		q = Q(q);
		for (var e, n = nearest ? this.e.firstChild : this.e, l, t, r, b; n; ) {
			if (n.nodeType === 1) {
				e = E(n);
				if (oparent !== n.offsetParent) oparent = n.offsetParent, opos = e.layerPos();
				l = opos.x + n.offsetLeft, t = opos.y + n.offsetTop,
					r = l + n.offsetWidth, b = t + n.offsetHeight;
				if (pos.x >= l && pos.y >= t && pos.x < r && pos.y < b) {
					if (!q || q.fn.call(q.ct, e)) return e;
					n = n.firstChild;
					continue;
				} else if (nearest && (!q || q.fn.call(q.ct, e))) {
					var d = (pos.x < l ? l - pos.x : pos.x >= r ? pos.x - r + 1 : 0)
						+ (pos.y < t ? t - pos.y : pos.y >= b ? pos.y - b + 1 : 0);
					if (d < dr) e1 = e, dr = d;
				}
			}
			n = n.nextSibling;
		}
		return e1;
	},
	getCell: function(j, i) {
		if (!this.e.rows.length) return null;
		if (!(j >= 0 && j < this.e.rows.length)) j = this.e.rows.length - 1;
		var r = this.e.rows[j];
		if (!r.cells.length) return null;
		if (!(i >= 0 && i < r.cells.length)) i = r.cells.length - 1;
		return E(r.cells[i]);
	},
	getRow: function(j) {
		if (!this.e.rows.length) return null;
		if (!(j >= 0 && j < this.e.rows.length)) j = this.e.rows.length - 1;
		return E(this.e.rows[j]);
	},
	getTableBody: function(i) {
		return E(this.e.tBodies[i || 0]);
	},
	addRow: function(j, content) {
		if (!(j >= 0 && j < this.e.rows.length)) j = -1;
		return E(this.e.insertRow(j));
	},
	addCell: function(j, i) {
		if (j && j._E)
			if (j.getTagName() === 'TD' && j.parentNode)
				i = j.e.cellIndex, j = j.parentNode.rowIndex;
		if (!this.e.rows.length) this.e.insertRow(-1);
		if (!isDefined(i)) i = j, j = -1;
		var r = this.e.rows[j >= 0 && j < this.e.rows.length ? j : this.e.rows.length - 1];
		if (!(i >= 0 && i < r.cells.length)) i = -1;
		return E(r.insertCell(i));
	},
	removeCell: function(j, i, destroy) {
		if (!this.e.rows.length) return null;
		if (j === -1) j = this.e.rows.length - 1;
		if (!(j >= 0 && j < this.e.rows.length)) return null;
		var r = this.e.rows[j];
		if (!r.cells.length) return null;
		if (i === -1) i = r.cells.length - 1;
		if (!(i >= 0 && i < r.cells.length)) return null;
		var c = r.cells[i];
		r.deleteCell(i);
		if (destroy !== false) return c;
		c.destroy();
	},
	removeRow: function(j, destroy) {
		if (!this.e.rows.length) return null;
		if (j === -1) j = this.e.rows.length - 1;
		if (!(j >= 0 && j < this.e.rows.length)) return null;
		var r = this.e.rows[j];
		this.e.deleteRow(j);
		if (destroy !== false) return r;
		r.destroy();
	},
	getTableDynWidth: function() {
		var width = 0, cells = this.e.rows[0].cells;
		for (var i = 0; i < cells.length; ++i) {
			var w = cells[i].style.width;
			if (w.length && w.charAt(w.length - 1) === '%') width += cells[i].offsetWidth;
		}
		return width;
	},
	focus: function() {
		this.e.focus();
		return this;
	},
	blur: function() {
		this.e.blur();
		return this;
	}
});

Class.extend(E, Browser.IE ? {
	captureMouse: function() {
		if (!this._looseCaptureHandler)
			this.addDomListener('capture', 'losecapture', this._getLooseCaptureHandler());
		E.mouseCaptor = this;
		this.mouseCaptured = true;
		this.e.setCapture(false);
	},
	_getLooseCaptureHandler: function() {
		var e = this;
		return this._looseCaptureHandler = function() {
			var c = E.mouseCaptor;
			if (c === e) c.mouseCapture = false, E.mouseCaptor = null, e.fire('losecapture');
		};
	},
	releaseMouse: function() {
		var c = E.mouseCaptor;
		if (c) c.mouseCapture = false, E.mouseCaptor = null, c.e.releaseCapture();
		return this;
	}
} : { // !Browser.IE
	static__eventsToCapture: A('mousemove mousedown mouseup mouseover mouseout click dblclick'),
	static__eventsToRelease: A('blur contextmenu resize'),
	static__captureHandler: function(ev) {
		if (!E.mouseCaptor) return;
		for (var e = ev.target; e; e = e.parentNode)
			if (e === E.mouseCaptor.e) return;
		if (E.mouseCaptor._domEventHandler) E.mouseCaptor._domEventHandler(ev);
		ev.preventDefault();
		ev.stopPropagation();
	},
	static__captureReleaser: function(ev) {
		var c = E.mouseCaptor;
		if (!(c && ev.target === window)) return;
		c.mouseCaptured = false;
		E.mouseCaptor = null;
		c.fire('losecapture');
	},
	static__removeCaptureListeners: function(w) {
		E._eventsToCapture.each(function(name) {
			w.e.removeEventListener(name, E._captureHandler, true);
		});
		E._eventsToRelease.each(function(name) {
			w.e.removeEventListener(name, E._captureReleaser, true);
		});
	},
	captureMouse: function() {
		this.mouseCaptured = true;
		E.mouseCaptor = this;
		var w = this.getWindow();
		if (w._captureListenersOn) return this;
		E._eventsToCapture.each(function(name){
			w.e.addEventListener(name, E._captureHandler, true);
		});
		E._eventsToRelease.each(function(name){
			w.e.addEventListener(name, E._captureReleaser, true);
		});
		w._captureListenersOn = true;
		return this;
	},
	releaseMouse: function() {
		var c = E.mouseCaptor;
		if (c) c.mouseCaptured = false, E.mouseCaptor = null;
		return this;
	}
});

var Window = Class('Window', DomObservable, {
	_domEventNames: F('load unload blur focus resize scroll'),
	construct: function(e) {
		if (!this._Window) {
			if (!e) return null;
			if (e._Window) return e;
			if (!(e.window && e.window === e.window.window)) return null;
			if (e._ajs) return e._ajs;
			return new Window(e);
		}
		this._DomObservable(e);
	},
	destroy: function() {
		if (this._captureListenersOn) E._removeCaptureListeners(this);
		this._DomObservable_destroy();
	},
	getDoc: function() { return Document(this.e.document); },
	getScrollPos: function() {
		if (typeof this.e.scrollX !== 'undefined') return new Pos(this.e.scrollX, this.e.scrollY);
		else return this.getDoc().getHtml().getScrollPos();
	},
	getClientSize: function() {
		return this.getDoc().getHtml().getSize();
	}
});

Ajs.window = new Window(window);
if (Browser.IE)
	Ajs.window.addDomListener('ajs', 'unload', function() {
		DomBase.destroy();
	});

var Document = Class('Document', DomObservable, {
	_domEventNames: E.prototype._domEventNames,
	construct: function(e) {
		if (!this._Document) {
			if (!e) return null;
			if (e._Document) return e;
			if (e.nodeType !== 9) return null;
			if (e._ajs) return e._ajs;
			return new Document(e);
		}
		this._DomObservable(e);
		this._createDocReadyListener();
	},
	getWindow: function() { return Window(this.e.defaultView || this.e.parentWindow); },
	getHtml: function() { return E(this.e.documentElement); },
	getBody: function() { return E(this.e.body); },
	addListener1: function(id, name, callback) {
		this._DomObservable_addListener1(id, name, callback);
		if (name === 'ready' && this.documentReady) {
			this.fire('ready');
			this.rmListener(undefined, 'ready');
		}
	},
	_createDocReadyListener: function() {
		if ('documentReady' in this) return;
		var timer, d = this, handler = function() {
			if (d.documentReady) return;
			d.documentReady = true;
			if (timer) timer = timer.stop();
			if (Browser.Safary || Browser.IE) d.getWindow().rmDomListener('ajs', 'load');
			else d.rmDomListener('ajs', 'DOMContentLoaded');
			d.fire('ready');
			d.rmListener(undefined, 'ready');
		}.setName('DOM content loaded');
		this.documentReady = false;
		if (Browser.Safari || Browser.IE) {
			timer = function(timer) {
				if (/loaded|complete/.test(d.readyState)) handler.fn.call(handler.ct);
			}.tie(this).timer();
			this.getWindow().addDomListener('ajs', 'load', handler);
		} else this.addDomListener('ajs', 'DOMContentLoaded', handler, true);
	}
});
Ajs.document = new Document(document);
Ajs.onReady = function(fn) {
	var id = Ajs._nextReadyId || 0;
	this.document.addListener('ajs' + id, 'ready', fn);
	Ajs._nextReadyId = id + 1;
};

