/* Copyright 2004-2009, Mark B. Rosenthal.  All rights reserved.  Mark
 * B. Rosenthal owns all right, title, and interest in this listing.
 * This listing is confidential and proprietary. Any unauthorized use,
 * reproduction, altering, distribution, or transmission of this
 * listing, or any part thereof, in any form or by any means,
 * electronic or mechanical, is strictly prohibited without a license
 * from Mark B. Rosenthal.
 */
/* Author: Mark B. Rosenthal <mbr@arlsoft.com>, Arlington Software Enterprises */

var ENABLE_DEBUG = true;
var immediateOutput = true;
var isMSIE = navigator.appName == "Microsoft Internet Explorer";
var eol = isMSIE ? "\r\n" : "\n";

/*
 * Function: debug(msg, popToTop, suppressNewline)
 * Description: Append message to end of debug window (named _DEBUG)
 * Argument:
 *	msg		Message to display in debug window
 *	popToTop	Defaults to false.  If true, display debug window on top of all others.
 *	suppressNewline	Defaults to false.  If true, don't advance to next line.
 *	suppressTimestamp	Defaults to false.  If true, don't output timestamp.
 */
function debug(msg, popToTop, suppressNewline, suppressTimestamp)
{
    if (!ENABLE_DEBUG)
	return;

    debugWindow = window.open('', '_DEBUG');

    if (msg != null)
	{
	    if (suppressTimestamp === undefined || !suppressTimestamp)
		{
		    var d = new Date();
		    var h = d.getHours();
		    h = (new String("00")).substr(0, 2-String(h).length) + String(h);
		    var m = d.getMinutes();
		    m = (new String("00")).substr(0, 2-String(m).length) + String(m);
		    var s = d.getSeconds();
		    s = (new String("00")).substr(0, 2-String(s).length) + String(s);
		    var ms = d.getMilliseconds();
		    ms = (new String("000")).substr(0, 3-String(ms).length) + String(ms);
		    msg = h + ":" + m + ":" + s + ":" + ms + ": " + msg;
		}
	    if (suppressNewline === undefined || !suppressNewline)
		msg += eol;
	    preNodes = debugWindow.document.getElementsByTagName("PRE");
	    if (preNodes.length == 0)
		{
		    centerNode = debugWindow.document.createElement("CENTER");
		    debugWindow.document.body.appendChild(centerNode);
		    h1Node = debugWindow.document.createElement("H1");
		    centerNode.appendChild(h1Node);
		    h1TextNode = debugWindow.document.createTextNode("Debugging Output");
		    h1Node.appendChild(h1TextNode);
		    preNodes[0] = debugWindow.document.createElement("PRE");
		    debugWindow.document.body.appendChild(preNodes[0]);
		}
	    var msgNode = debugWindow.document.createTextNode(msg);
	    preNodes[0].appendChild(msgNode);
	    debugWindow.scrollTo(0, 999999999);
	}
    if (popToTop !== undefined && popToTop)
	debugWindow.focus();
}

/*
 * Function: debugVar(name, val, maxRecursionLevel, propsToShow, popToTop)
 * Description: Append name and dump of val to end of debug window (named _DEBUG)
 * Arguments:
 *	name		Name of variable
 *	val		Value to be displayed in debug window
 *	maxRecursionLevel
 *		Optional argument.  Default is 1.
 *		If dumping a property listed in propsToShow, this
 *		limit is not applied.
 *		-1	no limit - AVOID UNLIMITED RECURSION without
 *			propsToShow.  This will try to recurse through
 *			both parentNode and childNodes, dumping the whole
 *			DOM, which will take forever, and blow some
 *			string buffer limit.
 *		>0	limit on depth of recursion
 *	propsToShow
 *		Optional argument.  Associative array of properties to
 *		display.  The value of each property in propsToShow is
 *		irrelevant.  All that matters is the presence of the
 *		property in propsToShow.  The following small set of
 *		properties is a handy set to start with:
 *			nodeName
 *			nodeValue
 *			nodeType
 *			tagName
 *			id
 *			className
 *		To recurse upward, add:
 *			parentNode
 *		To recurse downward, add:
 *			childNodes
 *		DO NOT RECURSE BOTH UPWARD AND DOWNWARD IN THE SAME
 *		CALL.  This will try to dump the whole DOM, which will
 *		take forever, and blow some string buffer limit.
 *	popToTop	if true, display debug window on top of all others
 * Return value: string representing dump of val
 */
function debugVar(name, val, maxRecursionLevel, propsToShow, popToTop)
{
    debug(name + " = ", popToTop, true);
    dumpVar(val, maxRecursionLevel, propsToShow, undefined, immediateOutput, popToTop);
    debug("", popToTop, false, true);
}

/*
 * Function: dumpVar(val, maxRecursionLevel, propsToShow, recursionLevel, print, popToTop)
 * Description: Return string containing dump of val.
 * Arguments:
 *	val		Value to be dumped
 *	maxRecursionLevel
 *		Optional argument.  Default is 1.
 *		If dumping a property listed in propsToShow, this
 *		limit is not applied.
 *		-1	no limit - AVOID UNLIMITED RECURSION without
 *			propsToShow.  This will try to recurse through
 *			both parentNode and childNodes, dumping the whole
 *			DOM, which will take forever, and blow some
 *			string buffer limit.
 *		>0	limit on depth of recursion
 *	propsToShow
 *		Optional argument.  Associative array of properties to
 *		display.  The value of each property in propsToShow is
 *		irrelevant.  All that matters is the presence of the
 *		property in propsToShow.  The following small set of
 *		properties is a handy set to start with:
 *			nodeName
 *			nodeValue
 *			nodeType
 *			tagName
 *			id
 *			className
 *		To recurse upward, add:
 *			parentNode
 *		To recurse downward, add:
 *			childNodes
 *		DO NOT RECURSE BOTH UPWARD AND DOWNWARD IN THE SAME
 *		CALL.  This will try to dump the whole DOM, which will
 *		take forever, and blow some string buffer limit.
 *	recursionLevel	Optional argument.  Default is 0.  Always pass this as 0.
 *	print		if true, do output by calling debug() during recursion; return ""
 *			if false, return the variable formatted as a string
 *	popToTop	if true, display debug window on top of all others
 * Return value: string representing dump of val
 */
function dumpVar(val, maxRecursionLevel, propsToShow, recursionLevel, print, popToTop)
{
/*
debug("In dumpVar(" + eol +
      "        val = " + val + "," + eol +
      "        typeof(val) = " + typeof(val) + "," + eol +
      "        maxRecursionLevel = " + maxRecursionLevel + "," + eol +
      "        propsToShow = " + propsToShow + "," + eol +
      "        recursionLevel = " + recursionLevel + "," + eol +
      "        print = " + print + "," + eol +
      "        popToTop = " + popToTop + ")" + eol,
      false);
*/

    var retval, s1, s2;
    switch (typeof(val))
	{
	case "number":	  retval = "[number] " + Number(val).toString();    break;
	case "string":	  retval = '[string] "' + val + '"';		    break;
	case "boolean":	  retval = "[boolean] " + (val ? "true" : "false"); break;
	case "function":  retval = "[function]";			    break;
	case "undefined": retval = "[undefined]";			    break;
	case "object":
	    if (val === null) { retval = "[object] null"; break; }
	    var isArray = val instanceof Array;
	    var isNodeList = isMSIE ? false : val instanceof NodeList;
	    if (isArray)
		retval = "[array " + val.length + "]";
	    else if (isNodeList)
		retval = "[NodeList " + val.length + "]";
	    else
		retval = "[object]";
	    retval += " {";
	    if (print)
		{
		    debug(retval, popToTop, true, true);
		    retval = undefined;
		}
//debug("recursionLevel = " + recursionLevel);
//debug("maxRecursionLevel = " + maxRecursionLevel);
	    if (recursionLevel == undefined)
		recursionLevel = 0;
	    if (maxRecursionLevel == undefined)
		maxRecursionLevel = 1;
	    if (propsToShow == undefined)
		propsToShow = 0;
	    if (maxRecursionLevel >= 0  && recursionLevel >= maxRecursionLevel)
		break;
	    var indentString = makeIndentString(recursionLevel + 1, 4);
//debug(indentString + "indentString = '" + indentString + "'");
	    if (isArray || isNodeList)
		{
//debug(indentString + "val is an Array of length " + val.length);
		    for (var k = 0; k < val.length; k++)
			{
			    if (val[k] !== undefined)
				{
//debug(indentString + "val[" + k + "] is defined");
				    s1 = eol + indentString + "[" + k + "] = ";
				    if (print)
					{
					    debug(s1, popToTop, true, true);
					    retval = undefined;
					}
				    s2 = dumpVar(val[k], maxRecursionLevel, propsToShow, recursionLevel+1, print, popToTop);
				    if (!print)
					retval += s1 + s2;
				}
			    else
				{
//debug(indentString + "val[" + k + "] is NOT defined");
				}
			}
		}
	    var all_digit_pattern = /^\d+$/;
	    if (typeof(propsToShow) == "object")
		{
//debug(indentString + "val is an Object");
		    for (var k in propsToShow)
			{
			    if ((isArray || isNodeList) &&
				all_digit_pattern.test(String(k)) &&
				(k - 0) < val.length)
				continue;
			    if (val[k] !== undefined)
				{
//debug(indentString + "val[" + k + "] is defined");
				    s1 = eol + indentString + k + " = ";
				    if (print)
					{
					    debug(s1, popToTop, true, true);
					    retval = undefined;
					}
				    s2 = dumpVar(val[k], maxRecursionLevel, propsToShow, recursionLevel+1, print, popToTop);
				    if (!print)
					retval += s1 + s2;
				}
			    else
				{
//debug(indentString + "val[" + k + "] is NOT defined");
				}
			}
		}
	    else
		{
//debug(indentString + "val is an object");
		    for (var k in val)
			{
			    if ((isArray || isNodeList) &&
				all_digit_pattern.test(String(k)) &&
				(k - 0) < val.length)
				continue;
//debug(indentString + "val[" + k + "] is defined");
			    s1 = eol + indentString + k + " = ";
			    if (print)
				{
				    debug(s1, popToTop, true, true);
				    retval = undefined;
				}
			    if (k == "domain") continue;
			    s2 = dumpVar(val[k], maxRecursionLevel, propsToShow, recursionLevel+1, print, popToTop);
			    if (!print)
				retval += s1 + s2;
			}
		}
	    s1 = eol + indentString + "}";
	    if (print)
	    {
		debug(s1, popToTop, true, true);
		retval = undefined;
	    }
	    else
		retval += s1;
	    break;
	}
    if (print)
	{
	    if (retval !== undefined)
		debug(retval, popToTop, true, true);
	    return "";
	}
    else
	return retval;
}

function makeIndentString(ntabs, tabwidth)
{
    var spacesForTab = makeSpacesForTab(tabwidth);
    var retval = "";
    var i;
    for (i = 0; i < ntabs; i++)
	retval += spacesForTab;
    return retval;
}

function makeSpacesForTab(tabwidth)
{
    var retval = "";
    var i;
    for (i = 0; i < tabwidth; i++)
	    retval += " ";
    return retval;
}

function nodeTypeToString(nt)
{
    switch(nt)
	{
	case 1: /* Node.ELEMENT_NODE */ return "ELEMENT_NODE";
	case 2: /* Node.ATTRIBUTE_NODE */ return "ATTRIBUTE_NODE";
	case 3: /* Node.TEXT_NODE */ return "TEXT_NODE";
	case 8: /* Node.COMMENT_NODE */ return "COMMENT_NODE";
	case 9: /* Node.DOCUMENT_NODE */ return "DOCUMENT_NODE";
	case 11: /* Node.DOCUMENT_FRAGMENT_NODE */ return "DOCUMENT_FRAGMENT_NODE";
	default: return "UNKNOWN_NODE";
	}
}

/*
 * Function: dumpNode(n)
 * Description: Return string containing dump of a node.
 * Arguments:
 *	n		Node to be dumped
 * Return value: string representing dump of node
 */
function dumpNode(n)
{
    var retval;

    switch(n.nodeType)
	{
	case 1: /* Node.ELEMENT_NODE */
	    retval = n.nodeName + ' = <' + n.tagName + '>';
	    break;
	case 3: /* Node.TEXT_NODE */
	    retval = n.nodeName + ' = "' + n.nodeValue + '"';
	    break;
	default:
	    retval = n.nodeName + ' type is: ' + nodeTypeToString(n.nodeType);
	    break;
	}
    return retval;
}
