//	Debug.js

//	debug: escape control characters
function escapeControls( s )
{
	var r = "";
	
	for (var i = 0; i < s.length; ++i)
	{
		switch (s.charAt( i ))
		{
			case '\0':
				r += "\\0";
				break;
			case '\b':
				r += "\\b";
				break;
			case '\t':
				r += "\\t";
				break;
			case '\n':
				r += "\\n";
				break;
			case '\v':
				r += "\\v";
				break;
			case '\f':
				r += "\\f";
				break;
			case '\r':
				r += "\\r";
				break;
			case '\"':
				r += "\\\"";
				break;
			case '\'':
				r += "\\\'";
				break;
			case '\\':
				r += "\\\\";
				break;
			default:
				if (s.charCodeAt( i ) < 0x20)
					alert( s.charAt( i ) ), r += "\\x" + s.charCodeAt( i ).toString( 16 );
				else
					r += s.charAt( i );
				break;
		}
	}
	
	return r;
}

//	debug: format string of DOM for node and its children
function showNode( node, indent )
{
	if (! indent)
		indent = "";
	
	if (node == null)
		return indent + "null node";
	
	var s = "\n" + indent;
	
	try
	{
		if (node.nodeType == 1)
		{
			//	Element
			s += "<" + node.nodeName;
			//	Include the class and id attributes if they're not whitespace. 
			if (node.className && 0 <= node.className.search( /\S/ ))
				s += " class=\"" + node.className + "\"";
			if (node.id && 0 <= node.id.search( /\S/ ))
				s += " id=\"" + node.id + "\"";
			
			if (node.hasChildNodes())
				s += ">";
			
			for (var i = 0; i < node.childNodes.length; ++i)
			{
				s += showNode( node.childNodes[i], indent + "\t" );
			}
			
			if (node.hasChildNodes())
				s += "\n" + indent + "</" + node.nodeName + ">";
			else
				s += "/>";
		}
		else
		if (node.nodeType == 2)
		{
			//	Attribute
			s += node.nodeName + "=\"" + node.nodeValue + "\"";
		}
		else
		if (node.nodeType == 3)
		{
			//	Text
			s += node.nodeName + ": \"" + escapeControls( node.nodeValue ) + "\"";
		}
		else
		if (node.nodeType == 8)
		{
			//	Comment
			s += node.nodeName + ": <!-- " + node.nodeValue + " -->";
		}
		else
		if (node.nodeType == 9)
		{
			//	Document
			s += node.nodeName + ": \"" + node.nodeValue + "\"";
		}
		else
		{
			//	unknown
			s += "unknown nodeType: " + node.nodeType + "; " + node.nodeName + ": \"" + node.nodeValue + "\"";
		}
	}
	catch (e)
	{
		s += "showNode: Error: " + e;
	}
	
	return s;
}

//	debug: show DOM in an alert
function showDom( node )
{
	return showNode( node, "" );
}

function truncString( s )
{
	var t = s.indexOf( '\n' );
	if (t)
		s = s.substr( 0, t );
	return s;
}

//	debug: format string of type info for JavaScript object
function getObjectType( o )
{
	try
	{
		switch (typeof o)
		{
			case 'string':
				return '"' + o + '"';
			
			case 'number':
				return o.toString();
			
			case 'boolean':
				return o.toString();
			
			case 'function':
				return truncString( o.toString() );
			
			default:
				if (o == null)
					return "null";
				else
				if (o == undefined)
					return "undefined";
				else
				{
					var s = Object.prototype.toString.apply( o );
					var s2 = '[' + s.substring( 8, s.length-1 ) + ']';
					return o + ' ' + s2;
					/*
					if (o.toString() == '[object]')
						return s2;
					else
						return s2 + ' ' + o.toString();
					*/
				}
		}
	}
	catch (e)
	{
		return "getObjectType: Error: " + e.name + ": \"" + e.message + "\"";
	}
}

//	debug: format string of all properties of JavaScript object
function getObjectProperties( o )
{
	var a = new Array();
	for (var prop in o)
	{
		a.push( prop );
	}
	a.sort();
	
	var s = "properties:";
	for (var i = 0; i < a.length; ++i)
	{
		var objectType = null;
		try
		{
			objectType = getObjectType( o[a[i]] );
		}
		catch (e)
		{
			objectType = "Error: " + e.name + ": \"" + e.message + "\"";
		}
		
		s += "\n" + a[i] + " = " + objectType;
	}
	
	return s;
}

//	debug: get type info of a JavaScript object
function getObjectTypeOrProperties( o, showFunctions )
{
	if ((typeof o) != 'object')
		return getObjectType( o );
	else
		return getObjectProperties( o );
}

//	debug: show type info of a JavaScript object in an alert
function showObject( o, showFunctions )
{
	try
	{
		return getObjectTypeOrProperties( o, showFunctions );
	}
	catch (e)
	{
		return "showObject: Error: " + e.name + ": \"" + e.message + "\"";
	}
}

//	debug: format string of all properties of JavaScript object
function getPropertyNames( o )
{
	var a = new Array();
	for (var prop in o)
	{
		a.push( prop );
	}
	a.sort();

	var s = "properties: ";
	for (var i = 0; i < a.length; ++i)
	{
		if (0 < i)
			s += ", ";
		s += a[i];
	}
	
	return s;
}

var theLog = null;

function logTime()
{
	var d = new Date();
	return d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds() + "." + d.getMilliseconds();
}

function log( message )
{
	if (theLog == null)
		theLog = new Array();
	
	theLog.push( logTime() + ": " + message );
}

function getLog()
{
	if (theLog && 0 < theLog.length)
		return theLog.join( "\n" );
	else
		return "no log";
}

function clearLog()
{
	while (isLog())
		theLog.pop();
}

function isLog()
{
	return theLog && 0 < theLog.length;
}
