
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, d) { return fn(this, b, c, d) } }
	t.deleteItem = function(a, v) { var i; while ((i = a.indexOf(v)) >= 0) a.splice(i, 1); }
	t.swapItems = function(a, i, j) { var e = a[i]; a[i] = a[j]; a[j] = e; };
	t.moveItems = function(a, i, j, l) { l = l || 1; if (j > i) j -= l; var r = a.splice(i, l); r.unshift(j, 0); a.splice.apply(a, r); }
	t.copy = function(o) { var key,r = { }; for (key in o) r[key] = o[key]; return r; }
	t.shallow = function(a) { var i=a.length-1,r = []; if (i>=0) do { r.unshift(a[i]) } while (i--); return r; }
	
	t.forEach(['moveItems','deleteItem','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.multiSplit = function(s) {
		if (!arguments[1]) return s;
		var a = s.split(arguments[1]);
		var e,i,d = [ '' ], r = [];
		for (i=2,e;(e=arguments[i]);i++) d.push(e);
		for (i=0;(i<a.length);i++) { 
			d[0] = a[i];
			r.push(t.multiSplit.apply(this, d));
		}
		return r;
	}
	
	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) }
	};
	
	t.entityEncoder1 = /%([0-9a-fA-F][0-9a-fA-F])/g;
	t.entityEncoder2 = /%u([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])/g;
	t.entityDecoder1 = /&\#x([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]);/g;
	t.entityDecoder2 = /&\#x([0-9a-fA-F][0-9a-fA-F]);/g;
	t.encodeEntities = function(str) { str = str.toString(); return escape(str).replace(/%20/g, ' ').replace(t.entityEncoder2, '&\#x$1;').replace(t.entityEncoder1, '&\#x$1;');	}
	t.decodeEntities = function(str) { str = str.toString(); return unescape(str.replace(t.entityDecoder1, '%u$1').replace(t.entityDecoder2, '%$1')); }
	t.fromEntities = function(s) {  var t=document.createElement("textarea"); t.innerHTML=s.replace(/</g,"&lt;").replace(/>/g,"&gt;"); return t.value; }
	
	t.toInt = function(val, d) { d=d||0; val = parseInt(val, 10); return (isNaN(val))?d:val; }
	
	t.compare = function(s1, s2) { var key; for (key in s1) if (s1[key]!=s2[key]) return false; return true; }
	
	t.structMerge = function(e) {
		for (var key,i=1,e1;(e1=arguments[i]); i++) {
			for (key in e1) e[key] = U.D(e[key])?e[key]:e1[key];
		}
		return e;
	}
	t.toInt = function(i) {
		var r,type=typeof(i);
		switch(type) {
			case 'number'	: return Math.round(i);
			case 'string'	: return (isNaN(r=parseInt(i,10))?0:r);
		}
		return 0;
	}
	t.max = function(n) { for (var i=1;i<arguments.length;i++) n=Math.max(n,arguments[i]); return n; }
	
	t.colour = function(c) { c = c.toString(); while (c.length<6) c='0'+c; return c; }
}

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)); }
Math.sign = function(n) { return (n < 0) ? -1 : 1; }

String.prototype.toCapitalised = function() {
	return this.charAt(0).toUpperCase() + this.substr(1).toLowerCase();
}
String.prototype.trim = function(l, r) {
	var result = this;
	l=(U.D(l))?l:true;
	r=(U.D(r))?r:true;
	if (l) result = result.replace(/^[ \t\r\n]*/, '');
	if (r) result = result.replace(/[ \t\r\n]*$/, '');
	return result;
}
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.overTypes = { mouseover: 'hover', mouseout: 'normal' };
		
	this._event = function(e, o) {		
		if (!(e = e || window.event)) return;
		var r= {	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, overType: this.overTypes[e.type],
					shiftKey: e.shiftKey, ctrlKey: e.ctrlKey, altKey: e.altKey, event: e }
		return r;
	}
	
	this.e = this._event;
	
	this.attachEvents = function(o, events, fn, on, direct)  {
		var oThis = this;
		var eIndex = this.nextIndex++;
		
		on = on || o;
		on._EVENT = on._EVENT || { eventList: [ ], direct: { }, types: { } };
		on._EVENT.eventList.push(eIndex);
		
		events = events.split(',');
		var eventFn = function(e) { return fn.call(on, oThis._event(e, o)); };
		var clickFn = function(e) { return oThis.clickFn(oThis._event(e, o), eIndex); }
		
		for (var type,index=0; (type = events[index]); index++) {
			var useFn = ((type == 'click') || (type == 'dblclick')) ? clickFn : eventFn; 
			on._EVENT.types[type] = on._EVENT.types[type]||0;
			on._EVENT.types[type]++;
			if (direct) this.directEvent(o, type, useFn, on);
			else 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, direct: direct };
				
		return eIndex;
	}

	this.directEvent = function(o, type, fn, on) {
		o._EVENT = o._EVENT || { eventList: [ ], direct: { } };
		(o._EVENT.direct[type] = o._EVENT.direct[type] || [ ]).push(fn);
		o['on'+type] = this.directFn;
	},
	
	this.directFn = function(e) { 
		if (!this._EVENT) return;
		e = EVENT._event(e, this);
		var l = this._EVENT.direct[e.type];
		if (!l) return;
	
		var i=l.length-1;
		if (i>=0) do { if (l[i](e)==false) return false; } while (i--);
	},
	
	this.clickFn = function(e, eRef) {
		if (EVENT._killClicks) return;
		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) {
									if (on._EVENT.types.dblclick) {
										on._EVENT.clickTimeout = setTimeout(U.FN(on, fn, e), 300); 
									} else {
										return fn.call(on, e.event);
									}
								}
								break;
			case 'dblclick'	:	fn.call(on, e); break;
		}
	},
	
	this.killNextClick = function(on) {
		on._EVENT = on._EVENT || {};
		on._EVENT.killNextClick = true;
	}
	
	this.killClicks = function() {
		if (EVENT._kcTimeout) clearTimeout(EVENT._kcTimeout);
		EVENT._kcTimeout = setTimeout(EVENT.enableClicks, 100);
		EVENT._killClicks = true;
	}
	
	this.enableClicks = function() {
		EVENT._kcTimeout = null;
		EVENT._killClicks = false;
	}
	
	this.removeEvents = function()  {
		for (var i=0,e;(e=arguments[i]);i++) this._removeEvents(e);
	},

	this._removeEvents = function(eIndex)  {
		var oThis = this;
		
		if (U.isObject(eIndex)) {
			if (!eIndex._EVENT) return;
			if (!eIndex._EVENT.eventList) return;
			while (eIndex._EVENT.eventList.length) this._removeEvents(eIndex._EVENT.eventList[0]);
		}
		
		var e = this.eventList[eIndex];
		
		if (!e) { 
			//printfire('NO event for ID: '+eIndex); 
			return; 
		}
		e.on._EVENT.eventList.splice(e.on._EVENT.eventList.indexOf(eIndex), 1);
				
		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; 
			e.on._EVENT.types[type]--;
			if (e.direct) e.o._EVENT.direct[type].deleteItem(useFn);
			else 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) try {
			e.event.cancelBubble = true;
			e.event.returnValue = false;
			if (e.event.stopPropagation) e.event.stopPropagation();
		} catch(e) { }
	}

	this.preventDefault = function(e) {
		if (e && e.event && e.event.preventDefault) e.event.preventDefault();
	}

	this.stopAll = function(e) { this.stopPropagation(e); this.preventDefault(e); return false; }
}

var EVT = new function() {
	this.nextIndex = 1;
	this.events = { };
	this.byId = { };
	this.overTypes = { mouseover: 'hover', mouseout: 'normal' };
	this._event = this.e = EVENT.e;
	this._disabledEvents = [ ];
	this._locks = { };
	this.e = this._event;	
	
	this.attach = function(o, types, fn, on)  {
		var i,item,evts,evt={};
		var id = this.nextIndex++;

		o._hasEVT=true;
		types = types.split(',');
		if (on) evt=on._EVT=on._EVT||{};
		item = this.byId[id] = { i: id, o: o, fn: fn, on: on, types: types }
		for (var i=0,type;(type=types[i]);i++) {
			this.events[type].push(item);
			evt[type]=evt[type]||[];
			evt[type].push(id);
		}
		return id;
	}

	this.removeEvents = function() {
		for (var i=0,id;(id=arguments[i]);i++) this.remove(id);
	}
	
	this.lock = function(o, events) {
		var i,e;
		events = events.split(',');
		for (i=0;(e=events[i]);i++) { this._locks[events[i]] = o; }
	}
	
	this.unlock = function(o, events) {
		var i, e;
		events = events.split(',');
		for (i in events) {
			e = events[i];
			if (this._locks[e] = o) { delete this._locks[e]; } 
		}
	}
	
	this.createDisabledRegExp = function() {
		this._disabledRegExp = new RegExp('('+this._disabledEvents.join('|')+')');
	}
	
	this.remove = function(id)  {
		var type,i,item,a;
		var item = this.byId[id];
		if (!item) return;
		var on = (item.on && item.on._EVT)?item.on:{};
		
		for (i=0;(type=item.types[i]);i++) {
			this.events[type].deleteItem(item);
			try { if (on[type]) on[type].deleteItem(item.i); } catch(e) {}
		}
		delete this.byId[id];
	},
	
	this.removeObject = function(o)  {
		var type,id,item,a;
		for (id in this.byId) {
			item = this.byId[id];
			if (item.o==o) this.remove(id);
		}
	},

	this.killNextClick = function() { this.KNC=true; if (EVT.clickTimeout) clearTimeout(EVT.clickTimeout); }
	
	this.report = function(mess) {
		var reportDiv = DOM.get('mainContent');
		if (reportDiv) {
			reportDiv.innerHTML = mess+'<br>'+reportDiv.innerHTML;
		}		
	}
	
	this.delayedClick = function() {
		var e = EVT.cachedEvent;
		EVT.cachedEvent = null;
		
		if (e) EVT.handleEvent(e, true); 
	}
	
	this.handleEvent = function(e, postponed) {
		var type, o;
		
		if (!postponed) {
			if (!(e=EVT.e(e))) return;
			if (e.pageX&&e.type.indexOf('mouse')!=-1) EVT._current = { shiftKey: e.shiftKey, altKey: e.altKey, keyCode: e.keyCode, pageX: e.pageX, pageY: e.pageY }
			switch (e.type) {
				case 'click'	:	if ((!e.target.getAttribute('clickAction')&&!e.target.getAttribute('buttonAction'))||e.target.getAttribute('moveaction')) {
										if (!EVT.KNC) { EVT.cachedEvent = e; EVT.clickTimeout = setTimeout(EVT.delayedClick, 500); }
										EVT.KNC=EVT.mouseDown=false; 
										type='blockclick';
										break;
									}
				case 'mousedown':	EVT.KNC=false; EVT.mouseDown=true; break;
				case 'mouseup'	:	EVT.mouseDown=false; break;
				case 'dblclick'	:	EVT.mouseDown=false; EVT.cachedEvent = null; clearTimeout(EVT.clickTimeout); break;
			}
		}
		else if (e.pageX) EVT._current = { shiftKey: e.shiftKey, altKey: e.altKey, keyCode: e.keyCode, pageX: e.pageX, pageY: e.pageY }
		var r,i,h,handlers=EVT.getHandlers(type||e.type, e.target);

		for (i=0;(h=handlers[i]);i++) {
			if (!(h = EVT.byId[h])) continue;
			if ((!(o=EVT._locks[e.type]))||(o==h.o)) r = h.fn.call(h.o, e);
			if (U.D(r)&&!r) { 
				if (e.type=='blockclick') {
					clearTimeout(EVT.clickTimeout);
					EVT.cachedEvent = null;
				}
				return EVT.stopAll(e); 
			}
		}
	}
	
	this.getHandlers = function(type, target) {
		var i,item,handlers = [], baseList = this.events[type];

		while (target) {
			if (target._EVT&&target._EVT[type]) handlers.push.apply(handlers, target._EVT[type]);
			try { target = target.parentNode; } catch(e) { break; };
		}
		for (i=baseList.length-1;(item=baseList[i]);i--) if (!item.on && handlers.indexOf(item.i)==-1) handlers.push(item.i);
		
		return handlers;
	}

	this.stopPropagation = function(e) { 
		if (e && e.event) try {
			e.event.cancelBubble = true;
			e.event.returnValue = false;
			if (e.event.stopPropagation) e.event.stopPropagation();
		} catch(e) { }
	}
	this.preventDefault = function(e) { if (e && e.event && e.event.preventDefault) e.event.preventDefault(); }
	this.stopAll = function(e) { this.stopPropagation(e); this.preventDefault(e); return false; }

	this.initDocEvent = function(type) { this.initEvent(type, document); }
	this.initBodyEvent = function(type) { this.initEvent(type, document.body); }
	this.initWinEvent = function(type) { this.initEvent(type, window); }
	this.initEvent = function(type, b) {
		this.events[type]=[];
		switch (this.eventType) {
			case 'add'		: b.addEventListener(type, EVT.handleEvent, false); break;
			case 'attach'	: b.attachEvent('on'+type, EVT.handleEvent); break;
		}
	}
	
	this.init = function() { 
		EVT.eventType = (document.body.addEventListener)?'add':'attach';
		'keypress,keydown,keyup'.split(',').forEach(EVT.initDocEvent, EVT);
		'mousedown,mouseup,mouseover,mouseout,click,dblclick,mousemove,change,scroll,resize'.split(',').forEach(EVT.initBodyEvent, EVT);
		EVT.events.blockclick=[];
	}
	PAGE.onload(this.init);
}

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;
	}

}

$ = function(ids) {
	return ids.split(',').map(DOM.get);
}
var DOM = new function() {
	
	this.__trash = [ ];
	
	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, h) {
		if (U.isFunction(s)) return s();
		s.t = s.t || 'div';
		var key, item, index, ele = (s.f) ? s.f() : document.createElement(s.t);
		top = top || ele;
		
		if (h) h.appendChild(ele);
		
		if (s.r) top[s.r] = ele;
		if (s.a) for (key in s.a) ele.setAttribute(key, s.a[key]);
		if (s.e) { ele.ooCleanupQ = []; for (key in s.e) { ele.ooCleanupQ.push[key]; ele[key] = s.e[key]; } }
		if (U.D(s.x)) ele.appendChild(document.createTextNode(s.x));
		if (s.s) s.s.forEach(function(item) { ele.appendChild (DOM.structure(item, top)); }, this);
		if (s.sl) s.sl.forEach(function(item) { ele.appendChild (DOM.structure({ cn: item }, top)); }, this);
		if (s.y) for (key in s.y) ele.style[key] = s.y[key];
		if (s.tr) s[key] = top;
		if (s.d) DHTML.sizeTo(ele, s.d[0],s.d[1]);
		if (U.D(s.W)) DHTML.setWidth(ele, s.W);
		if (U.D(s.H)) DHTML.setHeight(ele, s.H);
		if (U.D(s.X)) DHTML.divX(ele, s.X);
		if (U.D(s.Y)) DHTML.divY(ele, s.Y);
		if (s.cn) ele.className = s.cn;
		if (U.D(s.id)) ele.id = s.id;
		if (s.c) { DHTML.bind.apply(DHTML, [ ele ].concat(s.c)); }			
		
		for (key in s) if (key.charAt(0)=='_') ele[key.substring(1)]=s[key];

		return ele;
	}

	this.div = function(cn, html, id, t) {
		t = t || 'div';
		var r = document.createElement(t);
		
		r.className = cn;
		if (id) r.id = id;
		if (html) r.innerHTML = html;
		
		return r;
	}
	
	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;
	}
	
	this.trash = function() {
		for (var i=0;i<arguments.length;i++) DOM.__trash.push(arguments[i]);
	}
	
	this.emptyTrash = function() {
		var e;
		if (!this.trashEle) this.trashEle = DOM.structure({ y: { top: '-100px', position: 'absolute', height: '1px', display: 'none' }, e: { stat: 'trashNode' } });
		
		while (this.__trash.length) {
			e=this.__trash.pop();
			if (e) {
				if (e.ooState=='active') OO.dispose(e);
				e.innerHTML = '';
				this.trashEle.appendChild(e);
			}
		}
		this.trashEle.innerHTML = '';

	}
	
}

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) { if ((obj.ooState != 'deleted') && (obj.ooState != 'deleting')) try { obj.ooDisposeSelf(); } catch(err) { var k,s=[]; if (window.console) for (k in err) s.push(err[k]); if (window.console) { alert('Disposal error: '+s.join(',')); } } return null; }
	this.dispose = function(obj) { if (obj && obj.ooDisposeSelf && (obj.ooState != 'deleted') && (obj.ooState != 'deleting')) obj.ooDisposeSelf(); return null; }
}

OO.Base = function() {
	this.ooState = 'created';
	this.ooInit = [ ];
	this.ooDispose = [ ];
	this.ooSupers = [ OO.Base ];
	this.ooRefs = [];
	this.ooCleanupQ = [];
}

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';
		this.ooIsObject = true;
		this.CQ('CQ,ooInitSelf,ooDisposeSelf,ooSupers,initSelf');
		OO.objectCount++;
	},

	CQ: function() {
		var a,s,i = arguments.length-1;
		if (i>=0) do { 
			a = arguments[i];
			switch (typeof(a)) {
				case 'string'	:	a = a.split(','); while (s=a.pop()) this.ooCleanupQ.push(s); break;
				default			:	this.ooCleanupQ.push(a);
			}
		} while (i--);
	},
	
	CQE: function(list) {
		var a = list.split(',');
		
		for (var i=0,s;(s=a[i]);i++) this.CQ(s, this[s]);
	},
	
	CREF: function(o, r) {
		this.ooRefs.push({o: o, r: r});
	},
	
	ooDisposeSelf: function() {
		if (!this.ooIsObject) return;
		var fn, key, o, cq = this.ooCleanupQ
		
		this.ooState = 'deleting';
		while (fn = this.ooDispose.pop()) fn.apply(this);
		
		if (this._EVENT && this._EVENT.eventList.length) { EVENT.removeEvents.apply(EVENT, this._EVENT.eventList); };
		if (window.EVT && this._hasEVT) EVT.removeObject(this);
		
		for (key in this.prototype) {	try { if (key.CQ) OO.dispose(this[key]); this[key] = null; } catch(e) { }; }
		
		while (cq.length) {
			o = cq.pop();
			if (typeof(o)=='string') this[o] = null;
			else OO.dispose(o)
		}
		while (o = this.ooRefs.pop()) o.o[o.r] = null;
		
		OO.objectCount--;
		
		this.ooState = 'deleted';
	},
	
	inherit: function() {
		var key;
		
		this.ooCleanupQ = this.ooCleanupQ || [];
		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;
				this.ooCleanupQ.push(key);
			}
			
			if (superClass.prototype.initSelf) { this.ooInit.push(superClass.prototype.initSelf); }
			if (superClass.prototype.disposeSelf) { this.ooDispose.push(superClass.prototype.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._L = { l: [ ], h: { }, b: 0 };
}
OO.Listener.prototype = {
	disposeSelf: function() { var o; while (o = this._L.l[0]) this.removeBroadcaster(o); this._L = null; },
	addBroadcaster: function(b) { if (!this._L.l.contains(b)) { this._L.l.push(b); b.addListener(this); } },

	addHandler: function(m, fn) { this._L.h[m] = fn },
	removeHandler: function(m) { this._L.h[m] = null },
	
	removeBroadcaster: function(b) {
		var i = this._L.l.indexOf(b);
		if (i >= 0) { this._L.l.splice(i, 1); b.removeListener(this); }
	},

	_listenToMessage: function(m, p) { 
		if (m=='startListening') this._L.b=0;
		else if (m=='stopListening') this._L.b=1;
		else if (this['listen_'+m]) this['listen_'+m].call(this, p);
		else if (this._L.b) return;
		else if (this._L.h[m]) this._L.h[m].call(this, p);
		else this.listenToMessage.apply(this, arguments); 
	},

	listenToMessage: function(message, param) { 
	}
}


OO.Broadcaster = function() {
	this.Broadcaster = { b: [ ] };
}
OO.Broadcaster.prototype = {
	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 b = this.Broadcaster.b;
		var i = b.length-1;
		
		if (i >= 0) do { b[i]._listenToMessage.apply(b[i], arguments); continue;
			try { b[i]._listenToMessage.apply(b[i], arguments); }
			catch (err) { console.error(err); console.trace(); }
		} while (i--)
	},
		
	disposeSelf: function() { var o; while (o = this.Broadcaster.b[0]) this.removeListener(o); this.Broadcaster = null; }
}


OO.Timer = function() {
	this.Timer = { t: { }, i: { } };
}
OO.Timer.prototype = {
	initSelf: function() {
		if (this.timedInit)	this.setTimeout('timedInit', 50, this.timedInit);
	},
	
	disposeSelf: function() {
		var key;
		for (key in this.Timer.t) this.clearTimeout(key);
		for (key in this.Timer.i) this.clearInterval(key);
		
		this.Timer = null;
	},
	
	setTimeout: function(label, period, fn) {
		if (!this.Timer) return;
		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() { if (oThis.Timer) fn.apply (oThis, args); if (oThis.Timer) 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() { if (oThis.Timer) fn.apply (oThis, args); } , period);
	},
	
	clearTimeout: function(label) {
		if (this.Timer && 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) {
		t = t || 1000;
		this.setTimeout('clean'+this.nextId, t, this.doClean, o);
		this.nextId++;
	},
	
	doClean: function(o) {
		OO.dispose(o);
	}
}
U.Cleaner = OO.create(OO.Cleaner);