// $Header: /var/cvs/philox/js/oo.js,v 1.1.1.1 2007/06/26 13:04:29 erik Exp $

//Event.observe(window, 'unload', Event.unloadCache, false);
//LIBS=[];

self.OO_BASE_PATH = (self.OO_BASE_PATH)?self.OO_BASE_PATH:'./oo';
self.OO_PROFILE   = (self.OO_PROFILE)?self.OO_PROFILE:false;
self.OO_CATCH_ALL = (self.OO_CATCH_ALL)?self.OO_CATCH_ALL:false;

if (self.console == null 
//|| navigator.appVersion.match(/fari/i)
	) {
	console=new Object();
}
if (console.log == null) {
	console.log = function () {
		return;
		if ( typeof arguments[0] == 'string') {
			pre(arguments);
			//pre( arguments[0](arguments.shift()));
		}
		else if ( typeof arguments[0] == 'object') {
			pre(preo(arguments[0]));
		}
		else {
			pre(arguments);
		}
	}
	//console.error = pre;
	console.error = function(){return;}
}
Object.clone = function(obj){
	var _clone = {};
	for(var p in obj){
		_clone[p]	= obj[p];
	}
	return _clone;
}

Error.prototype.getListFormat = function(){
		var err = [];
		err.push('==============================================');
		err.push('mess : '+this.message);
		this.number     && err.push('num  : '+(this.number&0xffff));
		this.lineNumber && err.push('line : '+this.lineNumber);
		this.fileName   && err.push('file : '+this.fileName);
		err.push('type : '+this.name);
		err.push('_________________________________________________________________________');
		return err;
}

/* Add a unique id to every bound callback*/
Function.prototype.__bindCounter=0;

/**
 * Returns a Fast variant of the bind method. This method can only be used on jClass
 * instances. It returns an anonymous function that will execute a method with the same
 * name as the binded one of the given object.
 * The first param should always be a reference to the object that contains a method with
 * the same name as the binded method.
 * When more than 3 arguments are provided, the more general but much slower "apply"
 * function is used.
 */
Function.prototype.jBind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
	var name = this.jName;
	var cb, bindid=++Function.prototype.__bindCounter;

	/* This is the most general and also the slowest method (used by prototype)
	 */
	if(args.length>2){
		cb = function(){ return __method.apply(object, args.concat($A(arguments))); }
	}

	/* This is about 13 times faster than the first method ...
	 * We call the method directly in the desired context
	 * This method changes the behaviour of bind in the following case:
	 *
	 *      this.someMethod.bind(notThisContext)
	 *
	 * In such a case we want to execute someMethod in the context of notThisContext, and
	 * not  notThisContext.someMethod(). Probably, notThisContext.someMethod does not even
	 * exist. The only thing that makes sense is :
	 * 
	 *       this.someMethod.bind(this, myVar);
	 */
	else{
		if(args.length==0){
			cb = function(v0,v1,v2,v3,v4,v5,v6,v7,v8,v9){ return object[name](v0,v1,v2,v3,v4,v5,v6,v7,v8,v9);}
		}
		else if(args.length==1){
			cb = function(v1,v2,v3,v4,v5,v6,v7,v8,v9){ return object[name](args[0],v1,v2,v3,v4,v5,v6,v7,v8,v9);}
		}
		else if(args.length==2){
			cb = function(v1,v2,v3,v4,v5,v6,v7,v8,v9){ return object[name](args[0],args[1],v2,v3,v4,v5,v6,v7,v8,v9);}
		}
	}
	cb.jClass_bindId=bindid;
	cb.context = object;


	/*We already deal with a bound method*/
	if(__method.code){
		cb.jClass_Id = __method.jClass_Id;
		cb.jClass    = __method.jClass;
		cb.code      = __method.code;
	}
	else{
		cb.jClass_Id = object.__jClassId;
		cb.code      =__method.toString();
		if(cb.jClass_Id){cb.jClass = object.getNS().absPath+'.'+object.getClassName();}
		else{ cb.jClass = null;	}
	}
	return cb;
}

Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
	var name = this.jName;
	var cb, bindid=++Function.prototype.__bindCounter;

	/* This is the most general and also the slowest method (used by prototype)
	 */
	if(args.length>1){
		cb = function(){ return __method.apply(object, args.concat($A(arguments))); }
	}

	/* This is about 10 times faster than the first
	 * method ...
	 * We exeucte the method in the desired context
	 */
	else if(!args.length){
			cb=function(v0,v1,v2,v3,v4,v5,v6,v7,v8,v9){return __method.call(object,v0,v1,v2,v3,v4,v5,v6,v7,v8,v9);}
	}
	else{
		cb=function(v0,v1,v2,v3,v4,v5,v6,v7,v8,v9){return __method.call(object,args[0],v0,v1,v2,v3,v4,v5,v6,v7,v8,v9);}
	}

	cb.jClass_bindId=bindid;
	cb.context = object;


	/*We already deal with a bound method*/
	if(__method.code){
		cb.jClass_Id = __method.jClass_Id;
		cb.jClass    = __method.jClass;
		cb.code      = __method.code;
	}
	else{
		cb.jClass_Id = object.__jClassId;
		cb.code      =__method.toString();
		if(cb.jClass_Id){cb.jClass = object.getNS().absPath+'.'+object.getClassName();}
		else{ cb.jClass = null;	}
	}
	return cb;
}

Function.prototype.__timeStart= (new Date()).getTime();
Function.prototype.__timed	= [];
Function.prototype.jTime	= function(message){
  var __method = this;
  var cb = function(){ 
		var time = {msg:message?message:''};
		time.start = ((new Date()).getTime() - Function.prototype.__timeStart ); 
		var res = __method.apply(this,arguments);
		time.end = ((new Date()).getTime() - Function.prototype.__timeStart ); 
		Function.prototype.__timed.push(time)
		return res;
	}
	cb.__isTimed=true;
	cb.code = __method.toString();
	return cb; 
}

Function.prototype.jCatch= function(message){
  var __method = this;
  var cb = function(){ 
		try{
			return __method.apply(this,arguments);
		}catch(e){
			if(this.__jClassId){
				e.message+="\n"+this.getNS().absPath+'.'+this.getClassName()+'{'+this.__jClassId+'}::'+message;
			}
			e.message +="\n"+__method.toString();
			self.OO_CATCH_ALL(e);
		}
	}
	return cb; 
}


jClass_getTimes = function(all){
	var t = Function.prototype.__timed,l=t.length;
	var start = (all)?0:jClass_getTimes.currentPoint;
	var res = t.slice(start,l).map(function(oTime){
		return {start:oTime.start,end:oTime.end,diff:oTime.end-oTime.start};	
	});
	jClass_getTimes.currentPoint=l;
	return res;
}
jClass_getTimes.currentPoint=0;

jClass_getFormattedTimes= function(all){
	var t = Function.prototype.__timed,out=[],l=t.length;
	var start = (all)?0:jClass_getFormattedTimes.currentPoint;
	t.slice(start,l).each(function(oTimed){
		out.push(((oTimed.start+'').pad(7)+'['+(oTimed.end-oTimed.start)+']').pad(14)+' '+oTimed.msg);
	})
	jClass_getFormattedTimes.currentPoint=l;
	return out;
}
jClass_getFormattedTimes.currentPoint=0;

self.jClass_errWin;
self.__preNumber = 0;
function pre(msg, newWin){
	if(typeof msg=='object' && msg.push){
		for(var i=0;i<msg.length;i++){ pre(msg[i], newWin); }	
		return;
	}

	var win=self;
	if(newWin){ }

	var node = $('_pre_err');
	if(!node && win.document.body){
		node = $(win.document.createElement('pre'));
		win.document.body.appendChild(node);
		node.id = '_pre_err';
		node.setStyle({
			width:'40%',
			cursor:'crosshair',
			padding:'10px',
			border:'solid black 2px',
			zIndex:10000,
			backgroundColor:'#fff',
			position:'absolute'
		});
		node.style.left='10px';
		node.style.top='10px';
		node.addClassName('open_err');
		Event.observe(node,'dblclick',function(){
			if(this.hasClassName('open_err')){
				this.setDim(10,10);	
				this.removeClassName('open_err');
				this.addClassName('closed_err');
				this.setStyle({overflow:'hidden'});
			}
			else{
				this.removeClassName('closed_err');
				this.addClassName('open_err');
				this.setStyle({height:'',width:'90%', overflow:'auto'});
			}
		})
		if(self.Draggable){ new Draggable(node); }
	}
	if(msg.message){
		if(node){
			var el = win.document.createElement('div');
			el.className='Error';
			el.style.backgroundColor	= '#FFFFE7';
			el.style.color = 'red';
			el.innerHTML = '('+ ++self.__preNumber +')'+msg.getListFormat().join("<br />\n");
			node.insertBefore(el, node.firstChild);
		}
		else{ alert(err.join("\n")); }
		//	throw (msg);
		setTimeout( "throw (msg)",5)
	}
	else if(node){
		node.insertBefore(win.document.createTextNode('('+ ++self.__preNumber +') '+msg),node.firstChild);	
		node.insertBefore(win.document.createElement('br'),node.firstChild);	
	}
	else{
		alert(msg.replace(/<br.*?>/g,"\n"));	
	}
}

/*
function assert(eq){
	if(!self.USE_ASSERT){return;}

	if(typeof eq=='string'){
		if(!this.eval('('+eq+');')){
			throw new Error('Assertion failed "('+eq+')"'); 	
		}
	}
	else{
		if(!(eq)){
			throw new Error('Assert failed');
		}
	}	
}
*/

/**
 * DEPRICATED use jFinal instead.
 */
function nStatic(nClass){
	var c;
	if((typeof nClass)=='string'){
		c = new this[nClass]();
	}
	else{
		c = new nClass();
	}
	this[c.getClassName()]=c;
}

/**
 * This function will create an instance of the given Class only once.
 * example : 
 * 
 * 	nameSpace.jFinal(nameSpace.nClass('MyFinal',{
 *     eggs : function(){alert('spam')} 
 *  }));
 *  
 *  MyFinal.eggs(); // alerts "spam"
 */
function jFinal(jClass){
	//if(jClass.className=='NotificationPoller'){pre('<<<<<<'+jClass.className);return;};
	//if(jClass.className=='Broadcaster'){pre('<<<<<<'+jClass.className);return;};
	//if(jClass.className=='Messages'){pre('<<<<<<'+jClass.className);return;};
	//if(jClass.className=='Login'){pre('<<<<<<'+jClass.className);return;};

	if(oo.__finals){
		oo.__finals.push(jClass);
	}
	else{
		var c = new jClass();
		c.getNS()[c.getClassName()]= c
		//pre(c.getClassName());
	}
}

/** USE WITH CARE!!!
 * Merge two objects.
 * This does basicaly the same as Object.extend, but only recursively.
 * This is espeacialy usefull when mergering an JSON option object. For example the
 * options used for the CE() method.
 * 
 * NOTE use with care!!: This can become a very exensive operation.
 * NOTE no check is done for cyclic references.
 */
Object.merge = function(defaultObj,extendedObj){
	if(!extendedObj){
		extendedObj = defaultObj;
		defaultObj	= this;	

	}
	for(var opt in extendedObj){
		if(typeof extendedObj[opt]=='function'){continue;}

		/* recurse */
		if(typeof extendedObj[opt]=='object' && defaultObj[opt]){
			defaultObj[opt] = Object.merge(defaultObj[opt], extendedObj[opt])	
			continue;
		}

		defaultObj[opt] = extendedObj[opt];	
	}
	return defaultObj;
}

/*
 * JCLASSS IMPLEMENTATION 
 */
self.nClass = function(){
	var className = arguments[0];

	nClass.prototype.argSlice = function(args, start){
		var c=0;a = new Array(args.length);
		for(var i=start;i<args.length;i++){
			a[c++]=args[i];	
		}
		return a;
	}
	var ns = this;
	this[className]     = function __jClass(){
		this.getNS        = function(){return ns;}
		this.getClassName = function(){ return className;	}	
		this.__jClassId   = ++self.nClass.prototype.jClassIdCounter;
		this.getClassId   = function(){return this.__jClassId};

		this.time0 = function() {
			this._ms_at_load = (new Date()).getTime();
		};
		this.pre = function (s ) {
			var pad = function(s,l) {
				return (s+'                  ').substring(0,l);
			}
			var dt = ((new Date()).getTime() - this._ms_at_load);
			var msg=pad(dt,7)+ 
				pad(this.getClassId(),5) +
				pad(this.getNS().absPath +'.'+
				this.getClassName(), 40);
			if(s.message){
				s.message = msg+s.message;
			}
			else{
				s = msg+s;	
			}
			pre(s);
		};
		this.error = this.pre;

		this.parent = function(parent, method, a1,a2,a3,a4,a5,a6,a7){
			if(!parent){
				pre(new Error(this.getNS().absPath+'.'+this.getClassName()+'::'+ method +	
					'() has a reference to an undefined parent'));
			}
			if(parent.prototype && parent.prototype[method]){
				return parent.prototype[method].call(this,a1,a2,a3,a4,a5,a6,a7);
				//return parent.prototype[method].apply(this,nClass.prototype.argSlice(arguments,2));
			}
			else if(parent[method]){
				return parent[method].call(this,a1,a2,a3,a4,a5,a6,a7);
				//return parent[method].apply(this,nClass.prototype.argSlice(arguments,2));
			}
			else{
				pre(new Error(parent.prototype.getNS().absPath+'.'+parent.className+'::'+ method +'() is undefined'));
			}
		}

		if(this.__construct){
			this.__construct.apply(this, arguments);
		}
		else if(this.initialize){
			this.initialize.apply(this, arguments);
		}
	}
	this[className].className = className;
	this[className].prototype.__parents__ = [];

	var max=arguments.length-1,__parents__ = [];
	for(var i=1;i<arguments.length;i++){
		if(!arguments[i]){alert('trying to extend an undefined object on nClass :"'+this.absPath+'.'+className+'"');}

		/* Auto profiling all nClass methods if the OO_PROFILE contstand is TRUE*/
		if(i==max){ 
			if(self.OO_CATCH_ALL){
				for(var m in arguments[i]){
					//instanceof operator leaks memory in IE so we use the stable typeof operator
					if((typeof arguments[i][m])=='function'
						&& arguments[i][m].jCatch &&
					!arguments[i][m].toString().match(/^\s*function\s__jClass\(/))	{
						arguments[i][m]=arguments[i][m].jCatch(m);	
					}
				}	
			}
			if(self.OO_PROFILE){
				for(var m in arguments[i]){
					//instanceof operator leaks memory in IE so we use the stable typeof operator
					if((typeof arguments[i][m])=='function' && !arguments[i][m].toString().match(/^\s*function\s__jClass\(/))	{
						arguments[i][m]=arguments[i][m].jTime(this.absPath+'.'+className+'{'+self.nClass.prototype.jClassIdCounter+'}::'+m);	
					}
				}	
			}
		}

		if(arguments[i] && arguments[i].prototype){
			Object.extend(this[className].prototype, arguments[i].prototype)
		}
		else{
			Object.extend(this[className].prototype, arguments[i])
		}
		if(i<arguments.length-1){ __parents__.push(arguments[i]); }
	}

	if(__parents__.length){ 
		this[className].prototype.__parents__ = __parents__;
	}

	this[className].prototype.getParents = function(recursive){
		if(recursive){
			var parents = [];
			this.__parents__.each(function(parent){
				if(!parent.prototype.getParents){return;}
				parents = parents.concat(parents,parent.prototype.getParents(true));
			});	
			parents = parents.concat(this.__parents__, parents).sortBy(function(p){return p.className;});
			var res = [];
			parents.each(function(p){
				if(!res.length || (res.last().className!=p.className)){
					res.push(p);	
				}
			});
			return res;
		}
		return this.__parents__;	
	}.bind(this[className].prototype);

	return this[className];
}
self.nClass.prototype.jClassIdCounter=0;
