/*
	_JSDebug class, debugging class for javascript.
*/

function _newJSDebug( enabled ){// constructor for the main class
	if(this.name==null || !this.name )	{
		this.name='_JSDebug';
	}
	this.version='2.0.0.3';
	this.enabled=false;

	this.totalProcessedValues=500;	//limit the total number of values that the return result can contain
	this.thisMaxDepth=10;			//the maximum depth to go when recurring through proprieties(arrays & objects)
	this.preTag=new Array('timediff','index');			//prefix text to diplay before the debugging output is made : rand,time,second,msecond,mtime,index,none,timediff
	//this.preTag['timediff']=true;

	this.typeLimits=new Array();		//used by _format(), initialized below
	this.typeLimits['string']=50;		//limit the length of the formatted strings

	this.printFunction=this.dumpFunction_divLoggerAuto;

	this.printFunction_capabilities=null;	//will be initialized by the print functions

	this.CSSStyles=new Array();
	this.JSFunctions=new Array();

	this.heap=new Array();
	this.timer=new Array();

	// CSS style used for displaying different variables, according to their type
	this.CSSStyles['number']=		'color:#ff0000;';
	this.JSFunctions['number']=		'';

	this.CSSStyles['string']=		'color:#008200;';
	this.JSFunctions['string']=		'';

	this.CSSStyles['function']=		'color:#0000ff;';	//function and prototype are the same thing?, should i differentiate them?
	this.JSFunctions['function']=	'';

	this.CSSStyles['prototype']='color:#0088ff;';
	this.JSFunctions['prototype']=	'';

	this.CSSStyles['array']=	'color:#000000;';
	this.JSFunctions['array']=	'';

	this.CSSStyles['object']=	'color:#000000;';
	this.JSFunctions['object']=	'';

	this.CSSStyles['undefined']='color:#88f; ';

	this.CSSStyles['general.childs']	=	'margin-left:28px; padding-left:4px;border-left:1px #ddd solid;';
	this.JSFunctions['general.childs']	=	'';

	this.CSSStyles['general']	=	'color:#444;';


	this.CSSStyles['general.name']		=	'color:#797979;';
	this.JSFunctions['general.name']	=	'';

	this.CSSStyles['general.separators']=	new Array();

	this.CSSStyles['general.separators'].push('border-top:1px solid #444; overflow:hidden; height:2px;');
	this.CSSStyles['general.separators'].push('border-top:1px dashed #444; overflow:hidden; height:2px;');
	this.CSSStyles['general.separators'].push('border-top:1px solid #c00; overflow:hidden; height:2px;');
	this.CSSStyles['general.separators'].push('border-top:1px dashed #c00; overflow:hidden; height:2px;');
	this.CSSStyles['general.separators'].push('border-top:1px dashed #0c0; overflow:hidden; height:2px;');
	this.CSSStyles['general.separators'].push('border-top:1px solid #00c; overflow:hidden; height:2px;');
	this.CSSStyles['general.separators'].push('border-top:1px dashed #00c; overflow:hidden; height:2px;');
	this.CSSStyles['general.separators'].push('border-top:1px solid #f0c; overflow:hidden; height:2px;');
	this.CSSStyles['general.separators'].push('border-top:1px dashed #f0c; overflow:hidden; height:2px;');
	this.CSSStyles['general.separators'].push('border-top:1px solid #000; overflow:hidden; height:2px;');
	this.CSSStyles['general.separators'].push('border-top:1px dashed #00; overflow:hidden; height:2px;');

	this.CSSStyles['general.separators'].push('border-top:2px solid #222; overflow:hidden; height:2px;');
	this.CSSStyles['general.separators'].push('border-top:2px solid #822; overflow:hidden; height:2px;');
	this.CSSStyles['general.separators'].push('border-top:2px solid #282; overflow:hidden; height:2px;');
	this.CSSStyles['general.separators'].push('border-top:2px solid #228; overflow:hidden; height:2px;');

	switch(enabled){
		default:
		case 'enabled': // init everything, but don`t display anything, just stand-by
			this.printFunction('JSDebug call for initialize');			//call for initialize, don`t show anything
			this.enabled=true;
		break;

		case 'global-enabled': // init everything, display output
			this.printFunction('JSDebug Initialized');			//call for initialize
			this.enabled=true;
		break;

		case 'disabled': // do not init anything, stay dead
			this.printFunction('JSDebug call for initialize');	//call for initialize
			this.enabled=false;
		break;
	}
}

/***************************************************************************************************/
// create a floating div and insert messages into it, uses prototype(Event.observe) & scriptaculous
_newJSDebug.prototype.dumpFunction_divLoggerAuto=function (str){
	if(this.printFunction_capabilities==null && str==='JSDebug call for initialize'){//initialize was not called
		this.printFunction_capabilities=new Array();
		this.printFunction_capabilities['HTML']=true;
		this.printFunction_capabilities['multipleMessages']=true;

	}

	if(str!=='JSDebug call for initialize'){
		if(document.getElementById('debug')){
			var node=document.createElement('div');
			node.innerHTML=str;
			node.style.cssText=this.CSSStyles['general'];
			node.style.borderBottom='1px solid #fff';
			document.getElementById('debug').insertBefore(node,document.getElementById('debug').firstChild);

		}else{	//, the debug win cannot be found!! fall back to another dumpFunction?
			var container=document.createElement('div');
			var textContainer=document.createElement('div');
			var border=document.createElement('div');

			var controls=new Array();
			controls['maximize']=document.createElement('span');
			controls['maximize_x']=document.createElement('span');
			controls['maximize_y']=document.createElement('span');
			controls['minimize']=document.createElement('span');
			controls['op_clear']=document.createElement('span');
			controls['op_separator']=document.createElement('span');

			//try to restore the last position
			var ca = document.cookie.split(';');
			var position=new Array();
			for(var i=0;i < ca.length;i++) {
				ca[i]=String(ca[i]).strip();
				if (ca[i].indexOf('debugJS') == 0) {
					position=ca[i].evalJSON();
					break;
				}
			}
			// if we don`t have the cookine installed , the fill in some default values
			if(!position[0] && !position[1]){
				position[0]="10px";//top
				position[1]="10px";//left
				position[2]="500px";//width
				position[3]="200px";//height
			}else{// we did find the values from the cookie, now just add "px" to each dimension
				position[0]+="px";//top
				position[1]+="px";//left
				position[2]+="px";//width
				position[3]+="px";//height
			}

			container.style.border="1px solid #000";
			container.style.backgroundColor="#eee";
			container.style.color="#000";
			container.style.position="absolute";
			container.style.top=position[0];
			container.style.left=position[1];
			container.style.width=position[2];
			container.id="debug_";

			border.style.color="#000";
			border.style.borderBottom="1px solid #004";
			border.style.backgroundColor="#dfdfe8";
			border.style.fontWeight="bold";
			border.style.textAlign="center";
			border.style.height="16px";
			border.style.cursor="move";
			border.innerHTML="debugJS";

			controls['maximize'].style.color="#800";
			controls['maximize'].style.fontWeight="bold";
			controls['maximize'].style.textAlign="center";
			controls['maximize'].style.height="16px";
			controls['maximize'].style.cursor="pointer";
			controls['maximize'].innerHTML="&alefsym;";
			Event.observe(controls['maximize'], 'click',
					function(){
						var w=getWindowSize();
						var container=$('debug_');
						var textContainer=$('debug');

						textContainer.style.left="10px";
						textContainer.style.top="10px";
						container.style.width=(w[0]-40)+"px";
						textContainer.style.height=(w[1]-40)+"px";
					});

			controls['maximize_x'].style.color="#f00";
			controls['maximize_x'].style.fontWeight="bold";
			controls['maximize_x'].style.textAlign="center";
			controls['maximize_x'].style.height="16px";
			controls['maximize_x'].style.cursor="pointer";
			controls['maximize_x'].innerHTML="&harr;";
			Event.observe(controls['maximize_x'], 'click',
					function(){
						var w=getWindowSize();
						var container=$('debug_');
						var textContainer=$('debug');

						textContainer.style.left="10px";
						container.style.width=(w[0]-20)+"px";
					});

			controls['maximize_y'].style.color="#f00";
			controls['maximize_y'].style.fontWeight="bold";
			controls['maximize_y'].style.textAlign="center";
			controls['maximize_y'].style.height="16px";
			controls['maximize_y'].style.cursor="pointer";
			controls['maximize_y'].innerHTML="|";
			Event.observe(controls['maximize_y'], 'click',
					function(){
						var w=getWindowSize();
						var container=$('debug_');
						var textContainer=$('debug');

						textContainer.style.top="10px";
						textContainer.style.height=(w[1]-40)+"px";
					});

			controls['minimize'].style.color="#080";
			controls['minimize'].style.fontWeight="bold";
			controls['minimize'].style.textAlign="center";
			controls['minimize'].style.height="16px";
			controls['minimize'].style.cursor="pointer";
			controls['minimize'].innerHTML="-";
			Event.observe(controls['minimize'], 'click',
					function(){
						var w=getWindowSize();
						var container=$('debug_');
						var textContainer=$('debug');

						container.style.width=(w[0]/4)+"px";
						textContainer.style.height=(w[1]/4)+"px";
					});

			controls['op_clear'].style.color="#f00";
			controls['op_clear'].style.fontWeight="bold";
			controls['op_clear'].style.textAlign="center";
			controls['op_clear'].style.height="16px";
			controls['op_clear'].style.cursor="pointer";
			controls['op_clear'].innerHTML="@@";
			Event.observe(controls['op_clear'], 'click',
					function(){
						var w=getWindowSize();
						var container=$('debug_');
						var textContainer=$('debug');

						container.style.width=(w[0]/4)+"px";
						textContainer.innerHTML='';
					});

			controls['op_separator'].style.color="#f00";
			controls['op_separator'].style.fontWeight="bold";
			controls['op_separator'].style.textAlign="center";
			controls['op_separator'].style.height="16px";
			controls['op_separator'].style.cursor="pointer";
			controls['op_separator'].innerHTML="__";
			this.heap['general.separators_index']=0;

			Event.observe(controls['op_separator'], 'click',
					function(){
						var w=getWindowSize();
						var container=$('debug_');
						var textContainer=$('debug');

						var node=document.createElement('div');
						node.innerHTML='&nbsp;';

						node.style.cssText=newdbg.CSSStyles['general.separators'][newdbg.heap['general.separators_index']];

						newdbg.heap['general.separators_index']++;
						if(newdbg.heap['general.separators_index']>=newdbg.CSSStyles['general.separators'].length)newdbg.heap['general.separators_index']=0;

						document.getElementById('debug').insertBefore(node,document.getElementById('debug').firstChild);

					});


			border.appendChild(controls['maximize']);
			border.appendChild(controls['maximize_x']);
			border.appendChild(controls['maximize_y']);
			border.appendChild(controls['minimize']);
			border.appendChild(controls['op_clear']);
			border.appendChild(controls['op_separator']);

			textContainer.id="debug";
			textContainer.style.height=position[3];
			textContainer.style.overflow="scroll";

			container.appendChild(border);
			container.appendChild(textContainer);
			document.body.appendChild(container);
			new Effect.Opacity(container, {duration:0.2, from:1, to:0.85});
			new Draggable(container,
							{
								handle:border,
								snap:[5,5],
								zindex:1000,
								starteffect: function(element) {
									new Effect.Opacity(element, {duration:0.2, from:0.85, to:0.40});
								},
								endeffect: function(element) {
									new Effect.Opacity(element, {duration:0.2, from:0.40, to:0.85});
									// save the new position to a cookie
									var position=new Array(parseInt(element.style.top),parseInt(element.style.left),parseInt(element.getWidth()),parseInt(element.getHeight()));
									document.cookie ='debugJS='+position.toJSON()+'; expires=Fri, 3 Aug 2099 20:47:11 UTC; path=/';
								}
							}
						);
			//retry to write the message into the div
			this.dumpFunction_divLoggerAuto(str);
		}
	}
}

// insert the message in an already created DIV in the main page. The div`s ID _must_ be 'debug'
_newJSDebug.prototype.dumpFunction_divLogger=function (str){
	if(this.printFunction_capabilities==null && str==='JSDebug call for initialize'){//initialize was not called
		this.printFunction_capabilities=new Array();
		this.printFunction_capabilities['HTML']=true;
		this.printFunction_capabilities['multipleMessages']=true;
	}

	if(str!=='JSDebug call for initialize'){
		if(document.getElementById('debug')){
			var node=document.createElement('div');
			node.innerHTML=str;
			node.style.borderBottom='1px solid #ddd';
			node.style.cssText=this.CSSStyles['general'];
			document.getElementById('debug').insertBefore(node,document.getElementById('debug').firstChild);
		}//else, the debug win cannot be found!! fall back to another dumpFunction?
	}
}

//use alert() to display the messages
_newJSDebug.prototype.dumpFunction_alert=function (str){
	if(this.printFunction_capabilities==null && str==='JSDebug call for initialize'){	//initialize was called
		this.printFunction_capabilities=new Array();
		this.printFunction_capabilities['HTML']=false;
		this.printFunction_capabilities['multipleMessages']=false;
	}
	if(str!=='JSDebug call for initialize'){
		alert(str);
	}
}

//use the Firebug console to display the messages
_newJSDebug.prototype.dumpFunction_Firebug=function (str){

	if(this.printFunction_capabilities==null && str==='JSDebug call for initialize'){	//initialize was called
		this.printFunction_capabilities=new Array();
		this.printFunction_capabilities['HTML']=false;
		this.printFunction_capabilities['multipleMessages']=true;
	}
	if(str!=='JSDebug call for initialize'){
		console.info(str); //put this into a try-catch block, and fall back to another dumpFunction in case of error
	}
}

//open a new window, and window.write the messages over there;)
_newJSDebug.prototype.dumpFunction_windowOpen=function (str){

	if(this.printFunction_capabilities==null ){	//initialize was called
		this.printFunction_capabilities=new Array();
		this.printFunction_capabilities['HTML']=true;
		this.printFunction_capabilities['multipleMessages']=true;

		if(str!=='JSDebug call for initialize'){
			this.printFunction_capabilities['private.window']=window.open('','dbg_checkQueryList','width=1024,height=150,toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes');
			if(!this.printFunction_capabilities['private.window']){
				alert('JSDebug: error: popup windows are blocked . Please allow them!');
				return false;
			}
			this.printFunction_capabilities['private.document']=this.printFunction_capabilities['private.window'].document;
			//try to clear the window
			this.printFunction_capabilities['private.document'].innerHTML="";
			this.printFunction_capabilities['private.document'].clear();
			this.printFunction_capabilities['private.window'].scroll(0,5000);// scroll to the bottom of the window

			this.printFunction_capabilities['private.document'].writeln('<hr><h2>JSDebug</h2>');
		}
	}

	if(str!=='JSDebug call for initialize'){
		try{
			if(this.printFunction_capabilities['private.window'].closed) {// works on firefox, on IE we will get an exception when trying to writeln to that window(if its closed...:)
				throw('dummy throwed exception');//trigger an exception, so the catch() will begin execution;)
			}
			this.printFunction_capabilities['private.window'].scroll(0,5000);
			this.printFunction_capabilities['private.document'].writeln('<div>'+str+'</div>');
		}catch(err){// an error was generated, this mostly means that the popup window was closed(on IE). Reinitialize!
			this.printFunction_capabilities=null;
			this.dumpFunction_windowOpen('JSDebug REcall for initialize');
			this.dumpFunction_windowOpen(str);
		}
	}
}

/***************************************************************************************************/

_newJSDebug.prototype._createPreTag=function(){
	function padLeft(number,digits){//pads the number with 0`oes to the left, ex 13 will become 0013, 132 will become 0132
		var _0='000000000000000000';
		return String(_0).substr(0,digits-String(number).length)+number;
	}
	var txt='';
	var preTag;
	for(var i=0;i<this.preTag.length;i++){
		preTag=this.preTag[i];
		if(i>0)txt+=' ';
		switch(preTag){
			case 'msecond':
				var now=new Date();
				var ms=padLeft(now.getMilliseconds(),3);
				txt+=String("::"+ms);
			break;

			case 'mtime':
				var now=new Date();
				var h=padLeft(now.getHours(),2);
				var m=padLeft(now.getMinutes(),2);
				var s=padLeft(now.getSeconds(),2);
				var ms=padLeft(now.getMilliseconds(),3);
				txt+=String(h+":"+m+":"+s+"."+ms);
			break;

			case 'time':
				var now=new Date();
				var h=padLeft(now.getHours(),2);
				var m=padLeft(now.getMinutes(),2);
				var s=padLeft(now.getSeconds(),2);
				txt+=String(h+":"+m+":"+s);
			break;

			case 'timediff':
				var now=new Date().getTime();
				if(this.printFunction_capabilities['private.lastPrint']==null)this.printFunction_capabilities['private.lastPrint']=now;
				var lastmsg=this.printFunction_capabilities['private.lastPrint'];
				this.printFunction_capabilities['private.lastPrint']=now;
				txt+=String(padLeft(Number(now)-Number(lastmsg),3))+'ms';
			break;

			default:
			case 'index':
				if(this.printFunction_capabilities['private.printIndex']==null)this.printFunction_capabilities['private.printIndex']=Number(0);
				txt+=padLeft(this.printFunction_capabilities['private.printIndex'],3);
				this.printFunction_capabilities['private.printIndex']++;
			break;

			case 'none':
			break;
		}
	}
	return txt;
}

/*
USAGE:
	d.dump($('filter_2').getInputs(),
									{
										filterOut:{
												'prototype':true,
												'function':true
												},
										thisMaxDepth:1
									}
								);
*/
_newJSDebug.prototype.dump=function (obj,cfgArr){
	if(!this.enabled)return false;
	var filterOut=new Array();

	var preCfgs=new Array();
	//alert(cfgArr.thisMaxDepth);
	if(cfgArr==null){
		cfgArr={
				filterOut:{
						'prototype':true,
						'function':true
						},
				thisMaxDepth:1
			}
	}

	if(cfgArr!=null){
		if(cfgArr.filterOut) filterOut=cfgArr.filterOut;
		if(cfgArr.thisMaxDepth) {
			preCfgs['thisMaxDepth']=this.thisMaxDepth;
			this.thisMaxDepth=cfgArr.thisMaxDepth;
		}
	}
	//filterOut['function']=true;
	var arr=this._dump(obj,null,null);
	var str=this._format(arr,null,null,filterOut);

	//restore the initial values, if cfgArr was specified
	if(cfgArr!=null){
		if(preCfgs['thisMaxDepth']){
			this.thisMaxDepth=preCfgs['thisMaxDepth'];
		}
	}
	return this.print(str);
}

_newJSDebug.prototype._format=function (arr,depth,totalValues,filterOut){
	var i=0;
	var str='';
	var elem;
	var uniqID='';

	if(depth==null)depth=Number(0);
	if(totalValues==null)totalValues=Number(0);
	if(filterOut==null)filterOut=new Array();
	if(depth==0){
		if(this.printFunction_capabilities['HTML']) str='<u style="float:right;">'+this._createPreTag()+'</u> ';
			else str=''+this._createPreTag()+': ';
	}
	for(i=0;i<arr.length;i++){
		elem=arr[i];
		if(!filterOut[elem['type']] || elem['childs']){
			totalValues++;
			if(this.printFunction_capabilities['HTML']){
				//the output function supports HTML formatting
				str+='<div>';
				if(elem['name']!==''){
					str+='<span style="'+this.CSSStyles['general.name']+'">['+elem['name']+'] =&gt; </span>';
				}
				if(this.typeLimits[elem['type']]){	//truncate the output
					if(String(elem['value']).length > this.typeLimits[elem['type']]){
						elem['value']=String(elem['value']).substr(0,this.typeLimits[elem['type']]-3)+'...';
					}
				}
				if(elem['type']=='string'){
					elem['value']=(elem['value']!=''?String(elem['value']).htmlEntities():'(empty string)');
				}
				str+='<span style="'+this.CSSStyles[elem['type']]+'">'+elem['value']+'</span>';
				if(elem['childs']){
					str+='<div style="'+this.CSSStyles['general.childs']+'">'+this._format(elem['childs'],Number(depth)+1,totalValues,filterOut)+'</div>';
				}
				str+='</div>';
			}else{
				// non-HTML formatting(text only, padding is done by adding spaces & other ASCII characters)
				str+='\n';
				for(var j=0; j < depth;j++)str+='    ';
				if(this.typeLimits[elem['type']]){	//truncate the output
					if(String(elem['value']).length > this.typeLimits[elem['type']]){
						elem['value']=String(elem['value']).substr(0,this.typeLimits[elem['type']]-3)+'...';
					}
				}
				if(elem['type']=='string'){
					elem['value']=(elem['value']!=''?String(elem['value']).htmlEntities():'(empty string)');
				}
				if(elem['name']!==''){
					str+='['+elem['name']+'] =>';
				}
				str+=''+elem['value'];
				if(elem['childs']){
					str+=this._format(elem['childs'],Number(depth)+1,totalValues,filterOut);
				}
			}
		}
	}
	return str;
}


_newJSDebug.prototype._dump=function (obj,depth,totalValues){
	var structure=new Array();	//array of propreties(contains arrays as well)
	var prop=new Array();	//array with proprieties for a specific object
	if(depth==null)depth=Number(0);
	if(totalValues==null)totalValues=Number(0);

	this.heap['totalValues']=Number(totalValues);
	this.heap['depth']=Number(depth);

	prop['name']='';
	prop['type']=typeof(obj);
	prop['value']=obj;
	prop['childs']=null;

	try{
		if(prop['type']=='object' && obj == null){
			prop['type']='undefined';
			prop['value']='null';
		}else if(prop['type']=='object' && obj.constructor == null){
			prop['type']='object';
			prop['value']='';
		}else if(prop['type']=='object' && obj.constructor == Array){
			prop['type']='array';
			prop['value']='';
		}else if(prop['type']=='object' && typeof(obj.constructor) == 'function'){
			prop['type']='object';
			prop['value']=String(obj.constructor).replace(/(function)\s([a-z_]+)(\([a-z_,]*\))([\w\W]*)/im,'$2$3');
		}else if(prop['type']=='function' ){
			prop['type']='function';
			//recompose the function header as function name + function params
			//prop['name']+
			prop['value']=String(obj).replace(/^[\s]*(function)[\s]*([a-z0-9_]*)[\s]*(\([\sa-z0-9_,]*\))([\w\W]*)/im,'$2$3');
		}
	}catch(err){prop['value']='JSDebug:_dump():Exception caught : '+err.name+':'+err.description+'';}
	structure.push(prop);
	if(prop['type']=='object' || prop['type']=='array'){// Classes and Arrays can have descendants
		for (var key in obj){
			var prop=new Array();	//array with proprieties for a specific object
									// this must be here, else array.push will only push a reference to this, not the real object
			this.heap['totalValues']++;
			//initial values, will get overwritten somewhere below...
			try{
				prop['name']=key;
				prop['type']=typeof(obj[key]);
				prop['value']=obj[key];
				prop['childs']=null;
			}catch(err){
				prop['name']=key;
				prop['type']='Error';
				prop['value']='Exception thrown!';
				prop['childs']=null;
			}

			if(this.heap['totalValues']>this.totalProcessedValues){	// the maximum node count was hit, abort parsing the tree
				prop['name']='Processing aborted';
				prop['type']='__message';
				prop['value']='Processing aborted, processed nodes : '+this.heap['totalValues']+'';
				prop['childs']=null;
				structure.push(prop);
				return structure;
			}

			try{
				if(prop['type']=='object' && obj[key] == null){
					prop['type']='undefined';
					prop['value']='null';
				}else if(prop['type']=='object' && obj[key].constructor == null){
					prop['type']='object';
					prop['value']='EMPTY object';
				}else if(prop['type']=='object' && obj[key].constructor == Array){
					prop['type']='array';
					prop['value']='Array()';
				}else if(prop['type']=='object' && typeof(obj[key].constructor) == 'function'){
					prop['type']='object';
					prop['value']=String(obj[key].constructor).replace(/(function)\s([a-z_]+)(\([a-z_,]*\))([\w\W]*)/im,'$2$3');
				}else if(prop['type']=='function' ){
					prop['type']='function';
					//recompose the function header as function name + function params
					prop['value']=String(obj[key]).replace(/^[\s]*(function)[\s]*([a-z0-9_]*)[\s]*(\([\sa-z0-9_,]*\))([\w\W]*)/im,'$2$3');
				}
			}catch(err){prop['value']='JSDebug:_dump():Exception caught : '+err.name+':'+err.description+''}

			// recurse to get props for the childs
			if(prop['type']=='array' || prop['type']=='object'){
				if(depth<this.thisMaxDepth){
					prop['childs']=this._dump(obj[key],Number(this.heap['depth'])+1,Number(this.heap['totalValues']));
				}else{ //max depth was reached. ABOOOORT! :)
				}
			}
			structure.push(prop);
		}
	}
	return structure;
}

// C-like printf function(will call _print() )
_newJSDebug.prototype.printf=function (format,values){
	if(!this.enabled)return false;
	var format;
	if(this.printFunction_capabilities['HTML']) format='<u style="float:right;">'+this._createPreTag()+'</u> '+String(format);
		else format=''+this._createPreTag()+': '+String(format);
	var params=new Array();
	var i;
	for(i=1;i<arguments.length;i++){
		params.push(arguments[i]);
	}
	return this.printFunction(this._printf(format,params));
}

//direct-output a string, no parsing, so formatting will occur
_newJSDebug.prototype.print=function (str){
	if(!this.enabled)return false;
	return this.printFunction(str);
}

_newJSDebug.prototype._printf=function (format,values){
	/*
		scan through format & create a formatted final string, which will be sent to this.printFunction

		- available formatings
			%x - hex
			%i - integer
			%f - float
			%s - string
			%. - automatic (consider it as string) (we use typeof to determine the type from all the above types)
			%% - %

			%** - bold
			%`` - italic
			%-- - undeline
			%!! - red & bold
			%## - green & bold
			%@@ - blue & bold
			%__ - output a line marker(<hr>)

			%#rgb - use the rgb determined by the specified values to compose the resulting color
	*/
	var i=Number(-1);
	var result=String('');
	var currentParam=0;
	format=String(format);
	i=format.indexOf('%',0);
	/*
		parsing method:
		parse from 0 to indexOf(%),
			copy from str1 to str2 (0-indexOf(%)
			add to str2 the formatted value of indexOf(%)+1
			go up to the parse..(copy-add-goup...)
	*/
	var currentIndex=0;
	var iterations=0;
	while(currentIndex<format.length){
		iterations++;
		if(iterations>100) return 'JSDebug:Error ; reccurence detected -> HALT';
		if(i!=-1){
			result+=format.substring(currentIndex,i);
			currentIndex=i;
		}else {//no more occurences of % found in string. add the remainder of the string and exit loop
			result+=format.substring(currentIndex,format.length);
			currentIndex=format.length;
			break;
		}
		formatCode=format.substring(i+1,i+5);
		switch(formatCode){
			case '#f00':	case '#c00':	case '#800':	case '#400':	case '#200':		// Red
			case '#0f0':	case '#0c0':	case '#080':	case '#040':	case '#020':		// Green
			case '#00f':	case '#00c':	case '#008':	case '#004':	case '#002':		// Blue
			case '#ff0':	case '#cc0':	case '#880':	case '#440':	case '#220':		// Yellow
			case '#fff':	case '#ccc':	case '#888':	case '#444':	case '#222':		// Grey
			case '#000':																		// Black
			case '#0':
				if(this.printFunction_capabilities['HTML'])	result+='<b style="color:'+formatCode+'">'+values[currentParam]+'</b>';
					else result+=''+values[currentParam]+'';
				i+=5;
				currentParam++;
				currentIndex=i;
			break;

			default:
				formatCode=format.substring(i+1,i+3);
				switch(formatCode){
					case '**':
						if(this.printFunction_capabilities['HTML']) result+='<b>'+values[currentParam]+'</b>';
							else result+=' '+values[currentParam]+' ';
						i+=3;				//consume 3 chars from fromat string(the %** part)
						currentParam++;		//consume 1 parameter with witch the function was called
						currentIndex=i;		//consume as much as the string was consumed so far:)
						break;
					case '``':
						if(this.printFunction_capabilities['HTML'])	result+='<em>'+values[currentParam]+'</em>';
							else result+=' _'+values[currentParam]+'_ ';
						i+=3;
						currentParam++;
						currentIndex=i;
						break;
					case '--':
						if(this.printFunction_capabilities['HTML'])	result+='<u>'+values[currentParam]+'</u>';
							else result+=' _'+values[currentParam]+'_ ';
						i+=3;
						currentParam++;
						currentIndex=i;
						break;
					case '!!':
						if(this.printFunction_capabilities['HTML'])	result+='<b style="color:#f00">'+values[currentParam]+'</b>';
							else result+=' __'+values[currentParam]+'__ ';
						i+=3;
						currentParam++;
						currentIndex=i;
						break;
					case '##':
						if(this.printFunction_capabilities['HTML'])	result+='<b style="color:#0f0">'+values[currentParam]+'</b>';
							else result+=' __'+values[currentParam]+'__ ';
						i+=3;
						currentParam++;
						currentIndex=i;
						break;
					case '@@':
						if(this.printFunction_capabilities['HTML'])	result+='<b style="color:#00f">'+values[currentParam]+'</b>';
							else result+=' __'+values[currentParam]+'__ ';
						i+=3;
						currentParam++;
						currentIndex=i;
						break;
					case '__':
						if(this.printFunction_capabilities['HTML'])	result+='<hr>';
							else result+=' ___________________________________________ ';
						i+=3;
						currentIndex=i;
						break;
					default:
						formatCode=format.substring(i+1,i+2);
						switch(formatCode){
							case 'x'://hex output
								var x=Number(Number(values[currentParam]).toFixed());
								var remainder=0;
								var hex='';
								var hexMap=Array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
								while(x){
									remainder=x%16;
									x=Number((x-remainder)/16);
									hex=hexMap[remainder]+hex;
								}
								result+='0x'+hex;
								i+=2;
								currentIndex=i;
								currentParam++;
								break;
							case 'i'://int output
								var x=Number(Number(values[currentParam]).toFixed());
								result+=x;
								i+=2;
								currentIndex=i;
								currentParam++;
								break;
							case 'f'://float output
								var x=Number(Number(values[currentParam]).toFixed(3));
								result+=x;
								i+=2;
								currentIndex=i;
								currentParam++;
								break;
							case '.'://autodetect
							case 's'://string output
								var x=String(values[currentParam]);
								result+=x;
								i+=2;
								currentIndex=i;
								currentParam++;
								break;
							case '%':
								result+='%';
								i+=2;
								currentIndex=i;
								break;
							default:
								return 'JSDebug: JSDebug.printf : unknown type "'+formatCode+'"';
						}
						break;
				}
		}
		/*alert(format+'\n'+
				i+':'+format.indexOf('%',i)+'\n'+
				format.substring(i,format.indexOf('%',i))
			);*/
		i=format.indexOf('%',i);
	}
	return result;
}
/****************************************************************************************************/
_newJSDebug.prototype.dumpCallStack= function (){
	var parent=_newJSDebug.prototype.dumpCallStack;
	var functionList=new Array();
	var cutTo;

	//	while we have a parent, add it to the stack list, and try to het his parent too...
	functionList.push('CALL stack dump follows: ');
	while(parent){
		text=String(parent.caller);
		text=String(text).replace(/^[\s]*(function)[\s]*([a-z0-9_]*)[\s]*(\([\sa-z0-9_,]*\))([\w\W]*)/im,'$1 $2$3');//extract the function name
		functionList.push(text);

		parent=parent.caller;
	}
	//use normal style dump
	this.dump(functionList);
}

/************************* time & profiling functions ***********************************************/
_newJSDebug.prototype.startTimer=function (print){/*	start the timer */
	if(print==null)print=true;

	this.timer['start']=new Date().getTime();
	if(print) this.printf('Timer %!!.','started');
	return 0;
}

_newJSDebug.prototype.checkTimer=function (print){/*	return the microseconds passed since the start was called */
	if(print==null)print=true;

	if(this.timer['start']==null) return 0;
	var dt=new Date().getTime();
	var diff=dt-this.timer['start'];
	if(print) this.printf('Timer check : %**ms.',Number(diff));
	return diff;
}
_newJSDebug.prototype.stopTimer=function (print){/*		stop the timer and return the passed time */
	if(print==null)print=true;

	if(this.timer['start']==null) return 0;
	var dt=new Date().getTime();
	var diff=dt-this.timer['start'];
	this.timer['start']=null;
	if(print) this.printf('Timer %!! after %**ms.','stop',Number(diff));
	return diff;
}

/************************** misc functions **********************************************************/
if(!String.prototype.htmlEntities) {// also defined by prototype... do not overwrite the one from prototype
	String.prototype.htmlEntities= function()
	{
	  var chars = new Array ('&','\"','<', '>');

	  var entities = new Array ('amp','quot','lt','gt');

	  newString = this;
	  for (var i = 0; i < chars.length; i++)
	  {
		myRegExp = new RegExp();
		myRegExp.compile(chars[i],'g')
		newString = newString.replace (myRegExp, '&' + entities[i] + ';');
	  }
	  return newString;
	}
}

if(typeof(getWindowSize)!='function'){
	function getWindowSize() {
		var myWidth = 0, myHeight = 0;
		if( typeof( window.innerWidth ) == 'number' ) {
			//Non-IE
			myWidth = window.innerWidth;
			myHeight = window.innerHeight;
		} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
			//IE 6+ in 'standards compliant mode'
			myWidth = document.documentElement.clientWidth;
			myHeight = document.documentElement.clientHeight;
		} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
			//IE 4 compatible
			myWidth = document.body.clientWidth;
			myHeight = document.body.clientHeight;
		}
		var size=new Array(Number(myWidth),Number(myHeight));
		return size;
	}
}



// construnct the JSDebug object.
// disabled or nothing 		will completly disable it.
// enabled 					will enable it and show the console only on requtest for display-ing something.
// global-enabled 			will open the console on page-loading
var newdbg= new _newJSDebug('enabled');
var d=newdbg;