/*--- 
Copyright (c) Enigma Interactive 2005
Project:		SOM2 CASK
Filename:  		oo_base.js
Description:	Base JS file for the oo libary

History
ver date  		who     	comment
-----------------------------------------------------------------------------
1	17Jan06		AJL			created
---*/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.copy = function(o) { var key,result = { }; for (key in o) result[key] = o[key]; return result; }

	t.forEach(['forEach','from','indexOf','lastIndexOf','every','some','filter','map','contains'], function(k) {t._bind(Array, k, t[k]); });
}

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);
	}
}

var PAGE = new function() {

	this.OL = [ ];
	this.onload = function(fn) {
		var oThis = this;
		if (!this.OL.length) window.onload = function() { oThis._do(); };
		this.OL.push(fn);
	} 
	this._do = function() { this.OL.forEach( function(fn) { fn(); } ); }
}

var EVENT = new function() {

	this._event = function(e) {
		if (!(e = e || window.event)) return;
		return {	type: e.type, target: e.target || e.srcElement,
					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)  {
		var oThis = this;
		events = events.split(',');
		var eventFn = function(e) { fn.call(o, oThis._event(e)); };
		
		for (var type,index=0; (type = events[index]); index++) {
			if (o.addEventListener) o.addEventListener(type, eventFn, false)
			else if (o.attachEvent) o.attachEvent('on'+type, eventFn);
		}
		
		return eventFn;
	}

	this.removeEvents = function(o, events, fn)  {
		var oThis = this;
		events = events.split(',');
		
		for (var type,index=0; (type = events[index]); index++) { 
			if (o.addEventListener) o.removeEventListener(type, fn, false)
			else if (o.attachEvent) o.detachEvent('on'+type, fn);
		}
	}
	
	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);
		
		return ele;
	}

	this.getElements = function() { var result={}; U.forEach(arguments, function(a) { result[a] = DOM.get(a); } ); return result; }
}

var OO = new function() {
	
	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.obDisposeSelf(); }
}

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';
	},

	ooDisposeSelf: function() {
		this.ooState = 'deleting';
		this.ooDispose.forEach( function(fn) { fn.apply(oThis); } );
		this.ooState = 'deleted';
	},
	
	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() { this.Listener.l.forEach( this.removeBroadcaster, this ); 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() { this.Broadcaster.b.forEach( this.removeListener, this ); 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(val) { val.listenToMessage.apply(val, args); } ); }
}


OO.Timer = function() {
	this.Timer = { t: { }, i: { } };
}
OO.Timer.prototype = {
	disposeSelf: function() {
		this.Timer.t.oForEach( clearTimeout );
		this.Timer.i.oForEach( clearInterval );
		
		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];
		}
	}
}
