
var U;
var UTIL = U = new function() {
	var t=this;
	t.D = t.def = function(e) { return (typeof(e) != 'undefined'); }
	t.Try = function() { for (var fn,i = 0; ( fn = arguments[i] ); i++) try { return fn(); } catch(e) { } }
	
	t.forEach = function(a, fn, o) { for (var l=a.length,i = 0; i<l; i++) (o) ? fn.call(o, a[i]) : fn(a[i]); }
	t.from = function(a, f, fn, o) { for (var l=a.length,i = f; i<l; i++) (o) ? fn.call(o, a[i]) : fn(a[i]); }
	t.indexOf = function(a, v, f) { for (var i = (U.D(f)) ? f : 0,l = a.length; i < l; i++) if (a[i] == v) return i; return -1; }
	t.lastIndexOf = function(a, v, f) { for (var i = Math.min((U.D(f)) ? f : a.length, a.length - 1); i >= 0; i--) if (a[i] == v) return i; return -1;}
	t.every = function(a, fn, o) { for (var l=a.length,i = 0; i < l; i++) if ( !((o) ? fn.call(o, a[i]) : fn(a[i]) )) return false; return true; }
	t.some = function(a, fn, o) { for (var l=a.length,i = 0; i < l; i++) if ( (o) ? fn.call(o, a[i]) : fn(a[i]) ) return true; return false; }
	t.filter = function(a, fn, o) { for (var r = [],l=a.length,i = 0; i < l; i++) if ( (o) ? fn.call(o, a[i]) : fn(a[i]) ) r.push(a[i]); return r;	}
	t.map = function(a, fn, o) { for (var r = [],l=a.length,i = 0; i < l; i++) r.push ( (o) ? fn.call(o, a[i]) : fn(a[i]) ); return r; }
	t.contains = function(a, v) { return (U.indexOf(a, v) > -1); },
	t._bind = function(o, k, fn) { if (!o.prototype[k]) o.prototype[k] = function(b,c) { return fn(this, b, c) } }
	t.deleteItem = function(a, v) { var i; while ((i = a.indexOf(v)) >= 0) a.splice(i, 1); }
	t.copy = function(o) { var key,result = { }; for (key in o) result[key] = o[key]; return result; }

	t.forEach(['delete','forEach','from','indexOf','lastIndexOf','every','some','filter','map','contains'], function(k) {t._bind(Array, k, t[k]); });
	//t.forEach(['forEach','from','indexOf','lastIndexOf','every','some','filter','map','contains'], function(k) {t._bind(HTMLCollection, k, t[k]); });

	t.copy = function(o) {
		var result, k;
		
		if (U.isArray(o)) { result = [ ]; o.forEach(function(e) { result.push(U.copy(e)) }); }
		else if (U.isObject(o)) { result = { }; for (k in o) { result[k] = U.copy(o[k]); }; }
		else result = o;
	
		return result;
	}
	
	t.isFunction = function(a) { return (typeof(a) == 'function'); }
	t.isObject = function(a) { return (a && typeof(a) == 'object') || U.isFunction(a); }
	t.isAlien = function(a) { return U.isObject(a) && typeof(a.constructor) != 'function'; }
	t.isArray = function(a) { return U.isObject(a) && a.constructor == Array; }

	t.FN = function(o, fn) { 
		for (var args=[], i=2, len=arguments.length; i<len; i++) args.push(arguments[i]);
		return function() { return fn.apply(o, args) }
	};
}

var LOG = new function() {
	this.setPanel = function(panelId) { this.panel = DOM.get(panelId); this.clean(); }
	this.log = function(mess) { if (this.panel) this.panel.innerHTML += this.panel.counter++ + '): ' + mess + '<br />'; }
	this.clean = function() { if (this.panel) { this.panel.innerHTML = ''; this.panel.counter = 1; } }
	
	this.dump = function(o) {
		var key, mess = '{ ', sep = '';
		for (key in o) { mess += sep + key + ': \'' + o[key] + '\''; sep = ', '; }
		mess += ' }';
		this.log(mess);
	}
}

Math.between = function(v, min, max) {
	return Math.max(min, Math.min(v, max));
}

var PAGE = new function() {

	this.LOADS = [ ];
	this.UNLOADS = [ ];
	this.onload = function(fn) {
		if (!this.LOADS.length) window.onload = U.FN(this, this._do, 'LOADS');
		this.LOADS.push(fn);
	} 

	this.onUnload = function(fn) {
		if (!this.UNLOADS.length) window.onunload = U.FN(this, this._do, 'UNLOADS');
		this.UNLOADS.push(fn);
	} 
	this._do = function(type) { this[type].forEach( function(fn) { fn(); } ); }
}

var EVENT = new function() {
		this.eventList = { };
		this.nextIndex = 1;

	this._event = function(e, o) {		
		if (!(e = e || window.event)) return;
		return {	type: e.type, target: e.target || e.srcElement, baseTarget: o,
					pageX: e.pageX || (e.clientX + document.body.scrollLeft), 
					pageY: e.pageY || (e.clientY + document.body.scrollTop),
					keyCode: e.keyCode || e.which,
					shiftKey: e.shiftKey, ctrlKey: e.ctrlKey, altKey: e.altKey, event: e }
	}
	
	this.e = this._event;
	
	this.attachEvents = function(o, events, fn, on)  {
		var oThis = this;
		var eIndex = this.nextIndex++;
		
		on = on || o;
		on._EVENT = on._EVENT || { eventList: [ ] };
		on._EVENT.eventList.push(eIndex);
		
		events = events.split(',');
		var eventFn = function(e) { fn.call(on, oThis._event(e, o)); };
		var clickFn = function(e) { oThis.clickFn(oThis._event(e, o), eIndex); }
		
		for (var type,index=0; (type = events[index]); index++) {
			var useFn = ((type == 'click') || (type == 'dblclick')) ? clickFn : eventFn; 
			if (o.addEventListener) o.addEventListener(type, useFn, false)
			else if (o.attachEvent) o.attachEvent('on'+type, useFn);
		}
		
		this.eventList[eIndex] = { o: o, events: events, fn: eventFn, clickFn: clickFn, actualFn: fn, on: on };
				
		return eIndex;
	}

	this.clickFn = function(e, eRef) {
		
		var item = EVENT.eventList[eRef];
		var on = item.on;
		var o = item.o;
		var fn = item.fn;
		if (!on._EVENT) alert(EVENT.eventList[eRef].actualFn);
		var killClick = on._EVENT.killNextClick;
		on._EVENT.killNextClick = false;
		
		if (on._EVENT.clickTimeout) clearTimeout(on._EVENT.clickTimeout);
		
		switch (e.type) {
			case 'click'	:	if (!killClick) on._EVENT.clickTimeout = setTimeout(U.FN(on, fn, e), 200); break;
			case 'dblclick'	:	fn.call(on, e); break;
		}
	},
	
	this.removeEvents = function()  {
		for (var i=0,e;(e=arguments[i]);i++) this._removeEvents(e);
	},

	this._removeEvents = function(eIndex)  {
		var oThis = this;
		
		var e = this.eventList[eIndex];
		
		e.on._EVENT.eventList.splice(e.on._EVENT.eventList.indexOf(eIndex), 1);
		
		if (!e) { printfire('NO event for ID: '+eIndex); return; }
		
		var events = e.events;
		var o = e.o;
		
		delete this.eventList[eIndex];
		
		for (var type,index=0; (type = events[index]); index++) { 
			var useFn = ((type == 'click') || (type == 'dblclick')) ? e.clickFn : e.fn; 
			if (o.addEventListener) o.removeEventListener(type, useFn, false)
			else if (o.attachEvent) o.detachEvent('on'+type, useFn);
		}
	}
	
	this.stopPropagation = function(e) {
		if (e && e.event && e.event.stopPropagation) e.event.stopPropagation(); 
		else if (window.event) window.event.cancelBubble = true;
	}

	this.preventDefault = function(e) {
		if (e && e.event && e.event.preventDefault) e.event.preventDefault();
		else if (window.event) window.event.returnValue = false;
	}

	this.stopAll = function(e) { this.preventDefault(e); this.stopPropagation(e); }
}

var XML = new function() {
	
	this.extract = function(node) {
		var result = { };
		for (var key,index = 1; ( key = arguments[index] ); index++)
			result[key] = node.getAttribute(key);
		
		return result;
	}

}

var DOM = new function() {
	
	this.get = function(e) {
		if(typeof(e)!='string') return e;
		return U.Try(function() { return document.getElementById(e); }, function() { return document.all[e]; }, function() { return null; } );
	}
	
	this.structure = function(s, top) {
		s.t = s.t || 'div';
		var key, index, ele = document.createElement(s.t);
		top = top || ele;
		
		if (s.a) for (key in s.a) ele.setAttribute(key, s.a[key]);
		if (s.e) for (key in s.e) ele[key] = s.e[key];
		if (s.r) top[s.r] = ele;
		if (s.c) { DHTML.bind.apply(DHTML, [ ele ].concat(s.c)); }			
		if (s.s) s.s.forEach(function(item) { ele.appendChild (this.structure(item, top)); }, this);
		if (s.x) ele.appendChild(document.createTextNode(s.x));
		if (s.y) for (key in s.y) ele.style[key] = s.y[key];
		if (s.cn) ele.className = s.cn;
		
		return ele;
	}

	this.getElements = function() { var result={}; U.forEach(arguments, function(a) { result[a] = DOM.get(a); } ); return result; },

	this.parentOf = function(o, p) {
		if (U.isArray(p)) return p.some(function(e) { return DOM.parentOf(o, e); });

		var parent = o;
		if (p == o) return true;
		
		while (parent = parent.parentNode) {
			if (p == parent) return true;
		}
		
		return false;
	}	
}

var OO = new function() {
	
	this.objectCount = 0;
	
	this.create = function(superClass) {
		var args = [], obj = new OO.Base();
		U.from(arguments, 1, function(i) { args.push(i); } );
		
		obj.inherit(superClass);
		obj.ooInitSelf.apply(obj, args);
	
		return obj;
	}
	
	this.apply = function(obj, fromClass) {
		var args = []; U.from(arguments, 1, function(i) { args.push(i); } );
		
		OO.Base.apply(obj);
		for (key in OO.Base.prototype) obj[key] = OO.Base.prototype[key];
		obj.inherit(fromClass);
		obj.ooInitSelf.apply(obj, args);
	           
		return obj;
	}
	
	this.dispose = function(obj) { obj.ooDisposeSelf(); }
}

OO.Base = function() {
	this.ooState = 'created';
	this.ooInit = [ ];
	this.ooDispose = [ ];
	this.ooSupers = [ OO.Base ];
}

OO.Base.prototype = { 
	ooInitSelf: function() {
		var oThis = this;
		var args = arguments;
	
		this.ooState = 'initialising';
		this.ooInit.forEach( function(fn) { fn.apply(oThis, args); } );
		this.ooState = 'active';
		
		OO.objectCount++;
	},

	ooDisposeSelf: function() {
		var oThis = this;
		this.ooState = 'deleting';
		this.ooDispose.forEach( function(fn) { fn.apply(oThis); } );
		
		if (this._EVENT && this._EVENT.eventList.length) { EVENT.removeEvents.apply(EVENT, this._EVENT.eventList); };
		
		for (key in this) {	try { delete this[key]; } catch(e) { }; }
		
		OO.objectCount--;
	},
	
	inherit: function() {
		for (var superClass,index = 0; (superClass = arguments[index]); index++) {
			if (this.ooSupers.contains(superClass))
				break;
	
			superClass.apply(this);
			for (key in superClass.prototype) {
				this[key] = superClass.prototype[key];
				this[key].ooOwner = superClass;
				this[key].ooFnKey = key;
			}
			
			if (this.initSelf) { this.ooInit.push(this.initSelf); }
			if (this.disposeSelf) { this.ooDispose.push(this.disposeSelf); }
			
			this.initSelf = null;
			this.disposeSelf = null;
			this.ooSupers.push(superClass); 
		}
	},
	
	SUPER: function(fArgs) {
		var fn = fArgs.callee, args = [];
		var current = this.ooSupers.indexOf(fn.ooOwner);
		var superFn;
		
		if (current == -1) return;
		
		while (current > 0) {
			if (superFn = this.ooSupers[--current].prototype[fn.ooFnKey]) break; 
		}
		if (!superFn) return;
		
		U.from(arguments, 1, function(i) { args.push(i); } );
		
		return superFn.apply(this, args);
	}
}

OO.Listener = function() {
	this.Listener = { l: [ ] };
}
OO.Listener.prototype = {
	disposeSelf: function() { var o; while (o = this.Listener.l[0]) this.removeBroadcaster(o); this.Listener = null; },
	addBroadcaster: function(b) { if (!this.Listener.l.contains(b)) { this.Listener.l.push(b); b.addListener(this); } },

	removeBroadcaster: function(b) {
		var i = this.Listener.l.indexOf(b);
		if (i >= 0) { this.Listener.l.splice(i, 1); b.removeListener(this); }
	},

	listenToMessage: function(message, param) { }
}


OO.Broadcaster = function() {
	this.Broadcaster = { b: [ ] };
}
OO.Broadcaster.prototype = {
	disposeSelf: function() { var o; while (o = this.Broadcaster.b[0]) this.removeListener(o); this.Broadcaster = null; },
	addListener: function(l) { if (!this.Broadcaster.b.contains(l)) { this.Broadcaster.b.push(l); l.addBroadcaster(this); } },

	removeListener: function(l) {
		var i = this.Broadcaster.b.indexOf(l);
		if (i >= 0) { this.Broadcaster.b.splice(i, 1); l.removeBroadcaster(this); }
	},

	sendMessage: function() { var args = arguments; this.Broadcaster.b.forEach( function(o) { o.listenToMessage.apply(o, args); } ); }
}


OO.Timer = function() {
	this.Timer = { t: { }, i: { } };
}
OO.Timer.prototype = {
	disposeSelf: function() {
		var key;
		for (key in this.Timer.t) this.clearTimeout(key);
		for (key in this.Timer.t) this.clearInterval(key);
		
		this.Timer = null;
	},
	
	setTimeout: function(label, period, fn) {
		var i, oThis = this, args = [];
		if (typeof(fn) == 'string') fn = this[fn];
	
		for (i = 3; i<arguments.length; i++) args.push(arguments[i]);
		this.clearTimeout(label);
		this.Timer.t[label] = setTimeout(function() { fn.apply (oThis, args); delete oThis.Timer.t[label]; } , period);
	},
	
	setInterval: function(label, period, fn) {
		var i, oThis = this, args = [];
		if (typeof(fn) == 'string') fn = this[fn];
	
		for (i = 3; i<arguments.length; i++) args.push(arguments[i]);
		this.clearInterval(label);
		this.Timer.i[label] = setInterval(function() { fn.apply (oThis, args); } , period);
	},
	
	clearTimeout: function(label) {
		if (this.Timer.t[label]) {
			clearTimeout(this.Timer.t[label]);
			delete this.Timer.t[label];
		}
	},
	
	clearInterval: function(label) {
		if (this.Timer.i[label]) {
			clearInterval(this.Timer.i[label]);
			delete this.Timer.i[label];
		}
	}
}

OO.Cleaner = function() {
	this.inherit(OO.Timer);
}
OO.Cleaner.prototype = {
	initSelf: function() {
		this.nextId = 1;
	},
	
	clean: function(o, t) {
		this.setTimeout('clean'+this.nextId, t, this.doClean, o);
		this.nextId++;
	},
	
	doClean: function(o) {
		OO.dispose(o);
	}
}
U.Cleaner = OO.create(OO.Cleaner);