/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Copyright Cylo.co.uk
Written by Graham Vincent 
Contact :: loudsphiers@gmail.com
Last Updated: 17/06/2008
*/
CyloStackTrace = new Object();
CyloStackTrace.st = "s";
CyloStackTrace.store_xml_raw = '<?xml version="1.0"?><stacktrace>';
CyloStackTrace.store_html_raw = '';
CyloStackTrace.store_plain_raw = '';
CyloStackTrace.auto_display = false;
CyloStackTrace.display_type = "plain";//default display type
CyloStackTrace.stack = Array();
CyloStackTrace.logging = false;
CyloStackTrace.AutoDisplay = function(bol)
{
	CyloStackTrace.auto_display = bol;
}
CyloStackTrace.stackTrace = function(args,msg,extra)
{
	//hits this when there is an error then traverses
	//back through the calling code recursvially calling back
	//here to set the string below
	end_trace=false;
	if(msg.indexOf("OK")==-1)//if its an error, start logging
	{
		CyloStackTrace.logging = true;
		CyloStackTrace.stack=Array();//Clear stack from any previouse error
	}
	if(CyloStackTrace.logging==true)
	{
		halt=false;
		//create a tempory object which will be placed into the stack array
		var trace = new Object();
		
		thistypeof = typeof(args.callee);

		if(thistypeof=="undefined")
		{
			halt=true;//args.callee.caller="";
		}
		// OR if calle has reached an event that sparked this off
		thistypeof = CyloStackTrace.getFunctionName(args.callee.caller)
		if(thistypeof=="onclick" ||
		thistypeof=="onmousein" ||
		thistypeof=="onmouseup" ||
		thistypeof=="onmouseout" ||
		thistypeof=="onmouseover" ||
		thistypeof=="onmousedown" ||
		thistypeof=="onfocus" ||
		thistypeof=="onblur" ||
		thistypeof=="onkeyup" ||
		thistypeof=="onkeydown" ||
		thistypeof=="onkeypress" ||
		thistypeof=="onchange" ||
		thistypeof=="onabort" ||
		thistypeof=="onkeypress" ||
		thistypeof=="ondblclick" ||
		thistypeof=="onerror" ||
		thistypeof=="onload" ||
		thistypeof=="onreset" ||
		thistypeof=="onresize" ||
		thistypeof=="onselect" ||
		thistypeof=="onsubmit" ||
		thistypeof=="onload" ||
		thistypeof=="onunload" ||
		thistypeof=="onscroll")
		{
			halt=true;//args.callee.caller="";
		}
		trace.func_name = CyloStackTrace.getFunctionName(args.callee);
		trace.parameters_object_type = Array();
		trace.parameters_value = Array();
		
		for(var x=0; x < args.length; x++)
		{
			trace.parameters_object_type[x]=typeof(args[x]);
			trace.parameters_value[x]=args[x];
			
		}
		
		//Set a passed or failed flag | will be failed when an error is hit and will beign the stack
		if(msg.indexOf("OK")==-1)
		{
			trace.outcome_status = "failed";
			trace.outcome_msg = msg;
			_u = document.location + "";
			trace.document_location = CyloStackTrace.replaceAll(_u,"#CycloStackTrace","");
		}else{
			trace.outcome_status = "passed";
			trace.outcome_msg = "";
		}
		//Get any extra parameters that were sent
		if(extra!="" && typeof(extra)!="undefined")
		{
			trace.extra = extra;
		}
		
		if(halt == true || CyloStackTrace.getFunctionName(args.callee.caller)=="" || CyloStackTrace.getFunctionName(args.callee.caller)=="anonymous")//Reached the top of the calling stack.
		{
			CyloStackTrace.logging = false;//reset logging to false
			end_trace=true;
		}else{
			//get the name of the function which called the current function
			trace.caller = CyloStackTrace.getFunctionName(args.callee.caller);
		}
		
		CyloStackTrace.stack.push(trace);
		
		if(end_trace==true)
		{
			
			if(CyloStackTrace.auto_display==true)
				CyloStackTrace.DispStackTrace(CyloStackTrace.display_type);
		}
	}
}

CyloStackTrace.getFunctionName = function(func)
{
	func = func + "";name = func.substring(func.indexOf(" ")+1);name = name.substring(0,name.indexOf("("));
	return name;
}
CyloStackTrace.SetDisplayType = function(t)
{
	t = t.toLowerCase();
	o=CyloStackTrace.display_type;
	switch(t)
	{
		case "xml":;break;
		case "html":;break;
		case "plain":;break;
		case "message":;break;
		case "redirect":;break;//Not a valid display type so set it back to prevouse value
		case "none":;break;//stop displaying
		default:t = o;break;
	}
	CyloStackTrace.display_type = t;
}
CyloStackTrace.DispStackTrace = function(t)
{
	
	t = t.toLowerCase();
	err_div=CyloStackTrace.$("SkTcMain");
	if(!err_div)
	{
		err_div = document.createElement("div");
		err_div.id="SkTcMain";
	}
	document.body.appendChild(err_div);
	
	err_div = document.createElement("div");
	err_div.id="SkTc";
	CyloStackTrace.$("SkTcMain").appendChild(err_div);

	switch(t)
	{
		case "html":    CyloStackTrace.html_stack(); break;
		case "xml":     CyloStackTrace.xml_stack();  break;
		case "plain":   CyloStackTrace.plain_stack();break;
		case "message":   CyloStackTrace.html_stack("message");break;
		case "redirect":CyloStackTrace.redirect_stack();break;
		case "none":return "";break;
		default:return "Not a valid display type";break;
	}
	CyloStackTrace.displayOtherOptions();
	document.body.scrollTop = document.body.scrollHeight;
}
CyloStackTrace.html_stack=function(which)
{
	if(typeof(which)=="undefined" || which=="")
	{
		which="";
	}
	return_string="";
	sl = CyloStackTrace.stack.length;
	
	for(st_c=0;st_c<sl;st_c++)
	{
		//for each trace in the stack
		sig = "<span style='color:blue'>"+CyloStackTrace.stack[st_c].func_name;
		info ="(";
		sig +="(";
		for(var x=0; x < CyloStackTrace.stack[st_c].parameters_object_type.length; x++)
		{
			info += CyloStackTrace.stack[st_c].parameters_object_type[x] + ",";
			sig += "'"+CyloStackTrace.stack[st_c].parameters_value[x] + "',";
		}
		info=info.substring(0,info.length-1);
		info +=")";
		sig=sig.substring(0,sig.length-1);
		sig +=") </span>";
		span_colour = "green";
		if(CyloStackTrace.stack[st_c].outcome_status.indexOf("failed")==0)
		{
			return_string +="<div style='border:1px black solid;'>Print Stack Trace <strong>"+CyloStackTrace.stack[st_c].document_location+"</strong> " + Date()+"<br/>";
			span_colour = "red";
		}
		return_string +="<strong>Function </strong> " + sig + " <strong>Objects parsed</strong> " + info + " <span style='color:"+span_colour+"'>"+CyloStackTrace.stack[st_c].outcome_status+" :: "+ CyloStackTrace.stack[st_c].outcome_msg + "</span>";
		if(CyloStackTrace.stack[st_c].extra!="" && typeof(CyloStackTrace.stack[st_c].extra)!="undefined")
		{
			return_string +="<strong>Extra info:: </strong>"+CyloStackTrace.stack[st_c].extra;
		}
		if(CyloStackTrace.stack[st_c].caller=="" || typeof(CyloStackTrace.stack[st_c].caller)=="undefined")
		{
			return_string +="<strong>END</strong><br/></div><br/>";
		}else{
			return_string +=" <strong>Called from </strong><span style='color:blue'>"+CyloStackTrace.stack[st_c].caller+"()</span><br/>";	
		}
	}
	if(which=="message")
	{
		//use with CyloDialog.js
		//CyloDialog.OKConfirmation(return_string,"SELF_CLOSE","OK","small","Error: Stack Trace",false);//for some reason the modal cover goes over the message, so dont set it in this case
	}else{
		err_div = CyloStackTrace.$("SkTc");
		err_div.innerHTML=return_string;	
	}
}

CyloStackTrace.plain_stack=function()
{
	return_string="";
	sl = CyloStackTrace.stack.length;
	
	for(st_c=0;st_c<sl;st_c++)
	{
		//for each trace in the stack
		sig = ""+CyloStackTrace.stack[st_c].func_name;
		info ="(";
		sig +="(";
		for(var x=0; x < CyloStackTrace.stack[st_c].parameters_object_type.length; x++)
		{
			info += CyloStackTrace.stack[st_c].parameters_object_type[x] + ",";
			sig += "'"+CyloStackTrace.stack[st_c].parameters_value[x] + "',";
		}
		info=info.substring(0,info.length-1);
		info +=")";
		sig=sig.substring(0,sig.length-1);
		sig +=") ";
		
		if(CyloStackTrace.stack[st_c].outcome_status.indexOf("failed")==0)
		{
			return_string +="Print Stack Trace! file="+CyloStackTrace.stack[st_c].document_location+" dt="+Date()+"\n";
		
		}
		return_string +="Function " + sig + " Objects parsed" + info + " "+CyloStackTrace.stack[st_c].outcome_status+" :: "+ CyloStackTrace.stack[st_c].outcome_msg + "";
		if(CyloStackTrace.stack[st_c].extra!="" && typeof(CyloStackTrace.stack[st_c].extra)!="undefined")
		{
			return_string +="Extra info:: "+CyloStackTrace.stack[st_c].extra + "\n";
		}
		if(CyloStackTrace.stack[st_c].caller=="" || typeof(CyloStackTrace.stack[st_c].caller)=="undefined")
		{
			return_string +="END\n\n";
		}else{
			return_string +=" Called from "+CyloStackTrace.stack[st_c].caller+"()\n";	
		}
	}
	err_div = CyloStackTrace.$("SkTc");
	err_div.innerHTML="<br/><textarea cols='100' rows='5'>"+return_string+"</textarea>";	
}

CyloStackTrace.xml_stack=function ()
{
	end_trace=false;
	return_string='<?xml version="1.0"?><stacktrace>';
	sl = CyloStackTrace.stack.length;
	
	for(st_c=0;st_c<sl;st_c++)
	{
		//for each trace in the stack
		if(CyloStackTrace.stack[st_c].document_location !="" && typeof(CyloStackTrace.stack[st_c].document_location)!="undefined")
		{
			return_string +='<trace document_location="'+CyloStackTrace.stack[st_c].document_location+'" date="'+Date()+'">';
		}else{
			return_string +='<trace>';
		}
		
		return_string +='<func_name>'+CyloStackTrace.stack[st_c].func_name+'</func_name>';
		return_string +='<parameters>';
		
		params='';
		for(var x=0; x < CyloStackTrace.stack[st_c].parameters_object_type.length; x++)
		{
			params += '<param object_type="'+CyloStackTrace.stack[st_c].parameters_object_type[x]+'">';
			params += CyloStackTrace.stack[st_c].parameters_value[x];
			params += '</param>';
		}
		
		return_string +=params;
		return_string +='</parameters>';

		return_string +='<outcome v="'+CyloStackTrace.stack[st_c].outcome_status+'">'+ CyloStackTrace.stack[st_c].outcome_msg + '</outcome>';
		//Get any extra parameters that were sent
		if(CyloStackTrace.stack[st_c].extra!="" && typeof(CyloStackTrace.stack[st_c].extra)!="undefined")
		{
			return_string +='<extra>'+ CyloStackTrace.stack[st_c].extra + '</extra>';
		}
		
		if(CyloStackTrace.stack[st_c].caller=="" || typeof(CyloStackTrace.stack[st_c].caller)=="undefined")
		{
			end_trace=true;
		}else{
			return_string +='<called_from>'+CyloStackTrace.stack[st_c].caller+'</called_from>';	
		}
		
		return_string +='</trace>';
		if(end_trace==true)
		{
			return_string +='</stacktrace>';
		}
	}
	err_div = CyloStackTrace.$("SkTc");
	err_div.innerHTML=CyloStackTrace.enc_xml(return_string);	
}
CyloStackTrace.displayOtherOptions=function()
{
	if(CyloStackTrace.$("displayOtherOptions") || CyloStackTrace.display_type=="message"){}
	else{
	otherOption = "<div id='displayOtherOptions'><br/><button onmouseup='CyloStackTrace.DispStackTrace(\"html\");'>HTML version</button><button onmouseup='CyloStackTrace.DispStackTrace(\"xml\");'>XML version</button><button onmouseup='CyloStackTrace.DispStackTrace(\"plain\");'>Plain Text version</button><button onmouseup='CyloStackTrace.copyToClipboard(document.getElementById(\"SkTc\").innerHTML);'>Copy To Clipboard</button></div>";
	err_div = CyloStackTrace.$("SkTcMain");
	err_div.innerHTML+=otherOption;	
	}
}
CyloStackTrace.redirect_stack=function()
{
	//to send the error to remote server using JSON
}
CyloStackTrace.enc_xml=function(w)
{
	w = CyloStackTrace.replaceAll(w,"<","&lt;");
	w = CyloStackTrace.replaceAll(w,">","&gt;");
	return w;
}

CyloStackTrace.replaceAll=function(str,from,to)
{ var idx = str.indexOf( from ); while ( idx > -1 ) { str = str.replace( from, to ); idx = str.indexOf( from );}
return str;}

CyloStackTrace.copyToClipboard = function(s)
{
	clipboardData.setData("Text", s);
}

CyloStackTrace.$ = function()
{
	 //http://javascript.internet.com/snippets/prototype-dollar-function.html
  var elements = new Array();
  for (var i = 0; i < arguments.length; i++) {
    var element = arguments[i];
    if (typeof element == 'string')
      element = document.getElementById(element);
    if (arguments.length == 1)
      return element;
    elements.push(element);
  }
  return elements;
}

CyloStackTrace.replaceAll = function(str,from,to)
{ var idx = str.indexOf( from ); while ( idx > -1 ) { str = str.replace( from, to ); idx = str.indexOf( from );}
return str;}

CyloStackTrace.lTrim = function( value ) { var re = /\s*((\S+\s*)*)/; return value.replace(re, "$1");}
CyloStackTrace.rTrim = function( value ) { var re = /((\s*\S+)*)\s*/; return value.replace(re, "$1");}
CyloStackTrace.trim = function( value ) { return CyloStackTrace.lTrim(CyloStackTrace.rTrim(value));}

