/* ---------------------------------------------------------------------------------- 

File: ErrorHandler.js   

Javascript functions to provide custom Error handling for Reliance Software developed products or applications

©2005 Reliance Software Systems - All rights reserved

The software and functionality provided herein are provided for use with Reliance Software developed products or applications only.
Copying or modifying any code herein is considered a violation of copyright law.

---------------------------------------------------------------------------------- */
/*
	Variable: Errors
	
	A new array called Errors is always created and added to the document.  This array will hold the errors that are created
*/


var Errors = new Array();
document.Errors = Errors;


/*
	Variable: errorHandler
	
	A new ErrorHandler is always created and added to the document.
	
*/
var errorHandler = new ErrorHandler();
document.errorHandler = errorHandler;


document.onError = HandleError;

function HandleError(pError)
{
	try
	{		
		return document.errorHandler.HandleError(pError);
	}
	catch(e)
	{ 
		alert ("[ErrorHandler.js]HandleError: " + (e.description!=null ? e.description : e.message))
	}	
}




/*
	Function: ParseError(pException)

	Parses the error information and returns the parsed string

	Parameters:
		pException - The error that occured to be parsed

	Exceptions:
		o TODO  Put exceptions thrown here.

	Returns:
		The parsed string containing information on the error that occured.

	Comments:
	TODO  Put comments in here
	(start example)
		TODO  Put code example here, if desired.
	(end)

	See Also:
		<HandleError(pException)>
*/
function ParseError(pException) {
	try {
		var e = pException;
		
		var strParsed = [];
		if (e.source != null)
			strParsed.push(e.source + ": ");
		
		if (e.number != null)
			strParsed.push("Error #" + e.number + " ");
			
		if (e.description != null)
			strParsed.push(e.description + " ");
		else if (e.message != null)
			strParsed.push(e.message + " ");
		
		if (e.innerException != null) {
			strParsed.push("{ " + ParseError(e.innerException) + "} ");
			}
		
		return strParsed.join("");
		}
	catch(e) { 
		alert ("[ErrorHandler.js]ParseError: " + (e.description!=null ? e.description : e.message))
		}	
	}


/*
	Constructor: AppError(pSource, pNumber, pMessage, pInnerException)

	An object containing information about the error that returns itself.

	Parameters:
		pSource - Source of the error
		pNumber - Error Number
		pMessage - Message to be displayed with the error
		pInnerException - The error that occured

	Exceptions:
		o TODO  Put exceptions thrown here.

	Returns:
		The AppError object containing information regarding the error

	Comments:
	TODO  Put comments in here
	(start example)
		TODO  Put code example here, if desired.
	(end)

	See Also:
		<HandleError(pException)>
*/
function AppError(pSource, pNumber, pMessage, pInnerException) {
	try {
		this.source = pSource;
		this.number = pNumber;
		this.message = pMessage;
		this.innerException = pInnerException;
		return this;
		}
	catch(e) { 
		alert ("[ErrorHandler.js]AppError: " + (e.description!=null ? e.description : e.message))
		}	
	}


/*
	Constructor: ErrorHandler()

	An object for managing the handling of errors.

	Parameters:
		None

	Exceptions:
		NOTE: Errors are not thrown by members of this class, as they themselves are the handlers.  Therefore, they only alert at this time.

	Returns:
		The ErrorHandler object

	Comments:
		NOTE: There typically should only be one error handler object that is manipulated by other scripts.

	See Also:
		<HandleError(pError)>
*/
function ErrorHandler()
{
	try
	{
		this.alertErrors = true;		// by default, alert our errors
		this.debugErrors = false;
		this.tattleErrors = false;
	
		this.alertFunction = alert;		// using the "alert" function
		this.debugFunction = null;
		this.tattleFunction = null;
		
		this.HandleError = ErrorHandlerHandleError;
		
		return this;
	}
	catch(e)
	{ 
		alert ("[ErrorHandler.js]ErrorHandler: " + (e.description!=null ? e.description : e.message))
	}	
}


/*
	Base Class: ErrorHandler
	
	Method: HandleError(pException)

	Displays information regarding the error, including the location of the error and what the error was

	Parameters:
		pException - The error that occured
		pHandler - [Optional] This is a handle to an ErrorHandler object that is to be used for handling this error.  (Default = document.errorHandler)
		
	Exceptions:
		Note: Exceptions are handled via an alert here instead of a standard error handler, due to the fact that this IS the error handler

	Returns:
		void

	Comments:
	TODO  Put comments in here
	(start example)
		TODO  Put code example here, if desired.
	(end)

	See Also:
		<AppError(pSource, pNumber, pMessage, pInnerException)>
*/
function ErrorHandlerHandleError(pException, pHandler) {
	try {
		var e = pException;
		
		document.Errors[document.Errors.length] = e;
		
		var strParsed = ParseError(e);
/*
+PERF+
RM 06/12/2008
Replaced parmObj with full code to prevent an odd dependency
*/		
		var handler = null;
		if(pHandler) handler = pHandler;
		else handler = document.errorHandler;
		//parmObj(pHandler, document.errorHandler);
		
		if (handler.alertErrors && handler.alertFunction)
			handler.alertFunction(strParsed);

		if (handler.debugErrors && handler.debugFunction)
		{
			handler.debugFunction('<table border="1px solid DodgerBlue;" style="background: #FFFFCC;width: 100%;"><tr><th style="background: DodgerBlue; color: white;" colspan="3">Error</th></tr><tr><td style="padding: 10px;"><b>'+strParsed+'</b></td></tr></table>',null,'html');
			StackTraceRun(arguments.callee);
		}

		if (handler.tattleErrors && handler.tattleFunction)
			handler.tattleFunction(strParsed);

		
		}
	catch(e) { 
		alert ("[ErrorHandler.js]HandleError: " + (e.description!=null ? e.description : e.message))
		}

	}
/********************************* BEGIN: STACK TRACE FUNCTIONS ****************************/
/*
	Function: FunctionNameGet(objFn)

		Gets the name of the function object passed in. 

	Parameters:
		objFn - The function object of which the name is to be retrieved.
		
	Exceptions:
		Note: Exceptions are handled via an alert or logdebug here instead of a standard error handler, due to the fact that this IS part of the error handler

	Returns:
		The name of function.  Returns "anonymous" for anonymous functions.

	See Also:
		<FunctionSignatureGet>
		<StackTraceRun>
*/
function FunctionNameGet(objFn)
{
	try
	{
		if(typeof objFn != "function") return "? Not-A-Function ?";
		
		// mozilla
		if(objFn.name) return objFn.name;

		// parse name from string representation
		var str = objFn.toString();
		var name = str.substring(str.indexOf('function') + 8,str.indexOf('('));
		if(name) return name;
		
		return "anonymous";
	}
	catch(e)
	{
		if(typeof logdebug == "function") logdebug("[ErrorHandler.js]FunctionNameGet: " + (e.description!=null ? e.description : e.message));
		else alert ("[ErrorHandler.js]FunctionNameGet: " + (e.description!=null ? e.description : e.message));
	}		
}
/*
	Function: FunctionSignatureGet(objFn)

		Gets the arguments that were passed in to the function object and translates them into a string function signature. 

	Parameters:
		objFn - The function object of which the signature is to be retrieved.
		
	Exceptions:
		Note: Exceptions are handled via an alert or logdebug here instead of a standard error handler, due to the fact that this IS part of the error handler

	Returns:
		The signature of function, formatted inside of a html table TD element. 

	See Also:
		<FunctionNameGet>
		<StackTraceRun>
*/
function FunctionSignatureGet(objFn)
{
	try
	{
		var sig = ['<td valign="top"><b>'+FunctionNameGet(objFn)+'</b></td>'];
		if(sig[0].indexOf("HandleError") > -1 && sig[0].indexOf("HandleError") < 35) return null; //skip these functions to put last erroring function on top
		sig.push('<td valign="top">');
		if(objFn.arguments)
		{
			for(var i = 0, argument, formatted; i<objFn.arguments.length; i++)
			{	
				argument = objFn.arguments[i];
				formatted = argument;
				switch (typeof argument) {
					case 'function':
						formatted = "function " + FunctionNameGet(argument); break;
					case 'object':
						formatted = "null";	// (typeof null == object)
						if(argument) {
							var identifier, objType = (argument instanceof Array ? "ARRAY" : "OBJECT"), properties = logdebug(argument,null,"array");	// use logdebug to get all properties
							if(argument.id) identifier = ': id="'+argument.id+'"';	//try to use object id
							else if(argument.name) identifier = ': name="'+argument.name+'"'; //or object name
							else if(argument.nodeName) identifier = ': nodeName="'+argument.nodeName+'"'; //or object nodeName to quickly identify
							else if(argument.tagName) identifier = ': tagName="'+argument.tagName+'"'; //or object tagName to quickly identify					
							else { //just grab one property
								for(var prop in argument) {
									if(prop != "______array") {
										identifier = ': '+prop+'="'+argument[prop]+'"';	//get first property to help identify
										break;
									}
								}	
							}

							for(var j = 0; j < properties.length; j++)
							{
								properties[j] = properties[j].outerHTML;
							}
							formatted = ('<span onmouseover="this.style.cursor=\'hand\'" onmouseout="this.style.cursor=\'\'" onclick="this.style.display=\'none\';this.nextSibling.style.display=\'\';">[...'+objType+identifier+'...]</span>' +
										 '<span style="display: none;">'+properties.join("")+'</span>');
						}
						break;
					case 'string':
						//if(argument.length > 100) argument = argument.substring(0, 100) + " ...";	//trim
						formatted = '"' + argument.xmlEncode() + '"'; 	
						break;
				}
				sig.push('<div'+((i%2==1)?' style="color: blue;">':'>')+formatted+((i < objFn.arguments.length - 1)?',':'')+'</div>');
			}
		}
		sig.push('&#160;</td><td valign="top">&#160;');
		
		// mainly to help identify anonymous functions by showing function body
		var body = objFn.toString().xmlEncode();
		body = body.substring(body.indexOf("{"));
		body = body.replace(/\s*\{\s*/g," { ").replace(/\s*\}\s*/g," } ");
		var showBodyLength = 150; //# characters of function body to show initially
		if(body.length > showBodyLength)
		{
			var  extBody = body.substring(showBodyLength);
			body = body.substring(0,showBodyLength).replace(/\s*$/,"") + '<span onmouseover="this.style.cursor=\'hand\'" onmouseout="this.style.cursor=\'\'" onclick="this.style.display=\'none\';this.nextSibling.style.display=\'\';">[...MORE...]</span>';
			body += '<span style="display: none;">'+extBody+'</span>';
		}
		sig.push(body.replace(/\s*\n\s*/g,"<br/>"));//makes easier to read function body
	
		sig.push("</td>");
		return sig.join("");
	}
	catch(e)
	{
		if(typeof logdebug == "function") logdebug("[ErrorHandler.js]FunctionSignatureGet: " + (e.description!=null ? e.description : e.message));
		else alert ("[ErrorHandler.js]FunctionSignatureGet: " + (e.description!=null ? e.description : e.message));
	}	
}
/*
	Function: StackTraceRun(objStartFn)

		Runs a stack trace on the function object passed in, listing calling functions and their arguments. Used for debugging. 

	Parameters:
		objStartFn - The function object where to start the stack trace.  This function should be called from within an
		 							executing function, passing that currently execution function as argument to StackTraceRun.
		
	Exceptions:
		Note: Exceptions are handled via an alert here instead of a standard error handler, due to the fact that this IS part of the error handler

	Returns:
		An html table that show the function names, their arguments, and function bodies along the trace.

	See Also:
		<FunctionNameGet>
		<FunctionSignatureGet>
*/
function StackTraceRun(objStartFn)
{
	try
	{
		if(typeof logdebug == "undefined") return;
		if(typeof objStartFn != "function")
		{
			alert("Must pass function object into StackTraceRun function");
			return;
		}
			// table and headers
		var stackTrace = ['<table border="1px solid black;" style="background: #FFFFCC;" cellpadding="2"><tr><th style=" background: DodgerBlue; color: white;" colspan="3">Stack Trace</th><tr><th style="background: gold;">Function Name</th><th style="background: gold;">Function Arguments()</th><th style="background: gold;">Function Body{}</th></tr>'];
		var prevCaller = objStartFn;
		while(prevCaller)
		{
			sig = FunctionSignatureGet(prevCaller);
			if(sig)
			{
				stackTrace.push('<tr>'+sig+'</tr>'); // table rows with function, args, function body
			}		
			prevCaller = prevCaller.caller;  //how stack trace finds previous function. Note: this property is not ECMA standard :(
		}
		stackTrace.push('</table>');
		logdebug(stackTrace.join(""),null,"html"); //use logdebug for output
	}
	catch(e)
	{
		if(typeof logdebug == "function") logdebug("[ErrorHandler.js]StackTraceRun: " + (e.description!=null ? e.description : e.message));
		else alert ("[ErrorHandler.js]StackTraceRun: " + (e.description!=null ? e.description : e.message));
	}
}	
/********************************* END: STACK TRACE FUNCTIONS ****************************/

/* Register this file with the script mapper */
//ScriptMapper.register("ErrorHandler");