/*
	Debug.js

*/

fl_bCanAccessExternal = false;


//  ###########  fl_isDebug()  ###########
//	fl_isDebug() controls all visible debug activity, including FL_ASSERT & its variants, SafeAlert(), and SafeDebugger().
//	This can be turned on by:
//	1)	Defining the global var DEBUG to true, or
//	2)	Running the app properly in the UIEngine, and the UIEngine responding properly to the IS_DEBUG request.
function fl_isDebug()
{
    if (typeof(window.fl_bIsDebug) != 'undefined')
    {
        return window.fl_bIsDebug;
    }
	else
	{
		var bIsDebug = false;  // default
		if (typeof(DEBUG) == 'boolean')
		{
			bIsDebug = DEBUG;
		}
		else if (fl_bCanAccessExternal)
		{
			try{
				bIsDebug = window.external.isDebug();
			}catch(e){}  // keep page prototypes from breaking in case they use code that tries to log.
		}

	    //  won't change during app session; cache so we don't have to seek registry with every Alt keystroke 
        if (typeof(window) != 'undefined')
            window.fl_bIsDebug = bIsDebug;
		return bIsDebug;
	}
}







function safe_setTimeout(strCode, msTime, strDebugKeyword)
{
	return __safe_setTimeoutOrInterval('timeout', strCode, msTime, strDebugKeyword);
}

function safe_setInterval(strCode, msTime, strDebugKeyword)
{
	return __safe_setTimeoutOrInterval('interval', strCode, msTime, strDebugKeyword);
}

function __safe_setTimeoutOrInterval(type, strCode, msTime, strDebugKeyword)
{
	//	This function runs in the *current* scope and can safely aceess any other variables or functions in this JS file.

	//	the "strDebugKeyword" is critical to debugging the requested code in the later thread.  
	//	It must be present and have valid syntax; if not, do not queue up the code.
	if (typeof(strDebugKeyword) == 'undefined')
	{
		FL_ASSERT(false, '__safe_setTimeoutOrInterval() - missing param "strDebugKeyword"; code will not be queued:\n'+strCode);
		return null;
	}
	try { eval('var testDebugTest = \''+strDebugKeyword+'\';'); }catch(e){}  // this will throw an error if bad syntax
	if (typeof(testDebugTest) == 'undefined')
	{
		FL_ASSERT(false, '__safe_setTimeoutOrInterval() - bad syntax in param "strDebugKeyword"; code will not be queued:\n'+strCode);
		return null;
	}

	strCode = strCode.replace(/\'/g, '\\\'');  // escape single quotes
	strCode = strCode.replace(/\n/g, '\\n');  // escape line breaks
//	strCode = strCode.replace(/\\/g, '\\\\');  // escape backslashes

	//	In Debug mode, we want the real error on-screen so we can attach to the actual place in the call stack 
	//	that threw the error.  If we are not running in the UIEngine (cannot access external) then we are in a 
	//	website or developing prototypes, and should also show the real error.
	//	If we're in the UIEngine and not debugging, then protect the execution with try/catch in the anonymous scope.
	var newCodeStr = '';
	if (!fl_bCanAccessExternal  ||  window.external.isDebug())
	{
		newCodeStr = '__safe_setTimeoutOrIntervalExec(\''+strDebugKeyword+'\', \''+strCode+'\');';
	}
	else
	{
		var outerExecErrorLogStr = '[script]  %%%%%%%%%%%%%%%%  safe_setTimeout (OUTER EXECUTION) failed:  Debug keyword = ['+strDebugKeyword+']  Error = [\'+e.description+\']\\n          %%%%%%%%%%%%%%%%  ['+strCode+']';
		var outerExecErrorAlertStr = 'JS Debug Alert:\\nsafe_setTimeout (OUTER EXECUTION) failed:\\nDebug keyword = ['+strDebugKeyword+']\\nError = [\'+e.description+\']\\nCode = ['+strCode+']';
		newCodeStr = 'try{ __safe_setTimeoutOrIntervalExec(\''+strDebugKeyword+'\', \''+strCode+'\') } catch(e) { try{window.external.log(2, \''+strDebugKeyword+'\', \''+outerExecErrorLogStr+'\'); if (window.external.isDebug()) alert(\''+outerExecErrorAlertStr+'\');}catch(e2){} };';
	}
						// "2" = E_ERROR, but we may not have access to anything in memory.
	if ('timeout' == type)
		return(setTimeout(newCodeStr, msTime));
	else if ('interval' == type)
		return(setInterval(newCodeStr, msTime));
}

function __safe_setTimeoutOrIntervalExec(strDebugKeyword, str)
{
	//	This function runs in an *independent* scope and MIGHT NOT be able to aceess any other variables or functions in this JS file.

	var fl_bCanAccessExternal = false;
	try
	{
		var bIsDebug = window.external.isDebug();
		fl_bCanAccessExternal = true;
	}
	catch(e) { }

	if (!fl_bCanAccessExternal  ||  window.external.isDebug())
	{
		//	In Debug mode, we want the real error on-screen so we can attach to the actual place in the call stack 
		//	that threw the error.  If we are not running in the UIEngine (cannot access external) then we are in a 
		//	website or developing prototypes, and should also show the real error.
		eval(str);
	}
	else
	{
		try
		{
			eval(str);
		}
		catch(e)
		{
			globalLogger.Error(strDebugKeyword, '%%%%%%%%%%%%%%%%  setTimeout failed:  Error=['+e.description+'] Code=['+str+']');
//			if (window.external.isDebug())
//				alert('safe_setTimeout (inner execution) failed:\n    Debug keyword=['+strDebugKeyword+']\n    Error=['+e.description+']\n    Code=['+str+']');
		}
	}
}



function fl_objectToString(obj, dimLevel)
/*	
	takes an array and returns a nicely-formatted report of its contexts as a 
	string, wihch can be then used in alerts, logs, or rendered to the document.

	This is different from  Array.toString()  since this will inspect multiple
	dimensions, show indentation for each new dimension, and show position 
	numbers for each member within a dimension.

	Usage:  nicelyFormattedText = fl_objectToString(myArray);

	dimLevel is not needed.  It is used internally when additional dimensions 
	make the function call itself recursively.

	jmb 12/7/2004 - created 
*/
{
	if (typeof(obj) == 'undefined')
	{
		return '[undefined]';
	}
	else if (null === obj)
	{
		return '[null]';
	}
	else if (typeof(obj) != 'object')  // simple data type
	{
		return String(obj);
	}

	if (typeof(dimLevel) == 'undefined')  dimLevel = 0;
	var tmpString, z, i;
	tmpString = '';
	for (var i in obj)
	{
		var strIndent = '';
		// Add appropriate indentation for dimensions above 0 
		for (z=0; z<dimLevel; z++)
		{
			strIndent += '		';
		}
		tmpString += strIndent + '['+i+'] = ';  // each line begins with key

		if (null === obj[i])
		{
			tmpString += '[null]\n';
		}
		else if (typeof(obj[i]) == 'boolean')
		{
			tmpString += '[boolean] ' + obj[i].toString() + '\n';
		}
		else if (typeof(obj[i]) == 'string')
		{
			tmpString += '"' + obj[i] + '"\n';
		}
		else if (typeof(obj[i]) == 'function')
		{
			var functionText = obj[i].toString();
			var ifunctionTextEol = functionText.indexOf(')');
			if (ifunctionTextEol >= 0)
			{
				functionText = functionText.substr(0, ifunctionTextEol+1);
			}
			tmpString += functionText + '\n';
		}
		else if (typeof(obj[i]) != 'object')  // other simple data type
		{
			tmpString += '[' + typeof(obj[i]) + '] ' + String(obj[i]) + '\n';
		}
		else if (dimLevel >= 3)  // if we are inspecting DOM, we might recurse to infinity b/c of "document owner" references
		{
			tmpString += '[ >3 recursions]\n';
		}
		else // recurse (but avoid windows or dom elements)
		{
			if (typeof(obj[i].nodeType) != 'undefined')
			{
				tmpString += '[HTML or DOM element]\n';
			}
			else if (typeof(obj[i].self) != 'undefined'  &&  typeof(obj[i].top) != 'undefined'  &&  typeof(obj[i].frames) != 'undefined')
			{
				tmpString += '[Window]\n';
			}
			else
			{
				tmpString += '[object]...\n' + fl_objectToString(obj[i], dimLevel+1);
			}
		}
	}
	return(tmpString);
}

function fl_alertObject(myObject, title)
/*	puts a formatted version of an array in an alert box.
	Optional -- "title" is to put some intelligent text in the alert box.
	jmb 12/7/2004 - created 
*/
{
	if (typeof(myObject) != 'undefined')
	{
		alert((title ? title+'\n\n' : '') + fl_objectToString(myObject));
	} else {
		alert('fl_alertObject > object was undefined');
	}
}

function fl_argumentsToString(argumentsObj)
{
	var args = [];
	for (var i=0; i<argumentsObj.length; i++)
	{
		if (i>0)  args.push(', ');
		if (typeof(argumentsObj[i]) == 'string')
			args.push('\'', escapeSingleQuotes(argumentsObj[i]), '\'');
		else if (typeof(argumentsObj[i]) == 'number')
			args.push(argumentsObj[i]);
		else
			args.push(String(argumentsObj[i]));
	}
	return args.join('');
}




function fl_formElementInfo(elementObj)
{
	var tmp = '';
	tmp += '=== Form Element Properties ===\n';
	tmp += 'element type = ['+elementObj.tagName+']\n';
	tmp += 'name = ['+elementObj.name+']\n';
	tmp += 'id = ['+elementObj.id+']\n';
	tmp += 'type attribute = ['+elementObj.type+']\n';
	tmp += 'value = ['+elementObj.value+']\n';

	tmp += '=== Event handlers ===\n';
	tmp += 'onchange = ['+elementObj.onchange+']\n';
	tmp += 'onclick = ['+elementObj.onclick+']\n';
	tmp += 'onkeypress = ['+elementObj.onkeypress+']\n';
	tmp += 'onkeydown = ['+elementObj.onkeydown+']\n';
	tmp += 'onkeyup = ['+elementObj.onkeyup+']\n';

	if (
		(elementObj.tagName == 'INPUT' && elementObj.type.toLowerCase() == 'text')
		||  (elementObj.tagName == 'TEXTAREA')
		)
	{
		tmp += 'size = ['+elementObj.size+']\n';
		tmp += 'maxlength = ['+elementObj.maxlength+']\n';
	}

	//	checkboxes 
	if (elementObj.tagName == 'INPUT' && elementObj.type.toLowerCase() == 'checkbox')
	{
		tmp += 'checked = ['+elementObj.checked+']\n';
	}
		
	//	select boxes (dropdowns)
	if (elementObj.tagName == 'SELECT')
	{
		var value = '';
		if (elementObj.selectedIndex >= 0)
			value = elementObj.options[elementObj.selectedIndex].value;
		tmp += 'value=[' + value + ']\n';
		tmp += 'length=['+elementObj.length+']\n';
		tmp += 'selectedIndex=['+elementObj.selectedIndex+']\n';
		tmp += '\n=== Child Options ===\n';
		for (var i=0; i<elementObj.options.length; i++)
		{
			var zz = elementObj.options[i];
			tmp += '['+i+']  text=['+zz.text+']   value=['+zz.value+']   selected=['+zz.selected+']   defaultSelected=['+zz.defaultSelected+']\n';
		}
	}
	return(tmp);
}





function fl_safeMethodQuery(objRef, methodName)
{
	var retVal = 'not implemented';
	try{
		eval("retVal = objRef."+methodName+"()");
	}catch(e){}
	return retVal;
}

function fl_styleSheetReport()
{
	var loadedList = [];
	var bAnyNotFound = false;
	var report = window.document.styleSheets.length + ' stylesheets loaded in\n\n'+window.location.href
		+ '\n\nProperty=The requested relative path    Value=The calculated absolute path\n';
	for (var i=0; i<window.document.styleSheets.length; i++)
	{
		var relPath = '['+i+'] ';
		var href = window.document.styleSheets[i].href;
		var bFoundInFramework = false;
		for (var key in window.fl_styleSheetList)
		{
			if (window.fl_styleSheetList[key] == href)
			{
				bFoundInFramework = true;
				relPath += key;  // the "key" in this case is the relative path used by the link-stylesheet-framework
				break;
			}
		}
		if (!bFoundInFramework)
		{
			bAnyNotFound = true;
			relPath += '***';
		}
		loadedList[relPath] = href;
	}
	report += fl_objectToString(loadedList);
	if (bAnyNotFound)
	{
		report += '\n\n***These stylesheets were loaded without using the linkStyleSheet() framework.';
	}
	alert(report);
}


function fl_isWindowValid(windowRef)
{
	//	When previously-referenced windows disappear, they still look like objects but will fail on any access to properties.
	//	One way of knowing this happened is that testing typeof on any property will return "unknown".
	try
	{
		return(
				typeof(windowRef) != 'undefined'			// does it exist
				&&  null !== windowRef						// not null
				&&  typeof(windowRef.name) != 'unknown'	// is the window valid (if not a window type at all, this will be "undefined" instead)
//				&&  typeof(windowRef.self) != 'undefined'	// does it have a "self" property
//problematic	&&  windowRef.self === windowRef			// does the self property refer to itself
				);
	} catch(e) {
		return false;
	}
}


