// -----------------------------------------------------------------------------
// [ideally, all debug calls would be removed before shipping; however, due to
// the difficulty of debugging HTML event handlers (silent failure), will
// probably leave those calls in place;
// - obfuscate only external IDs until I have time to test thoroughly any 
// obfuscation of internal IDs as it's tricky and errors in the debugging 
// library are really undesirable]

function  dbgPr (txt) {
    if (txt === undefined) txt="undefined";
    else if (txt === null) txt="null";
    // change newlines to <br/> and all non-first spaces to &nbsp;
    // [[regexp usage kills all outerHTML objects which aren't chars
    txt = txt.toString()/*.
            replace(/\n/g, "<br/>").
            replace(/_/g, "__").
            replace(/ ( +)/g, "_\1").
            replace(/ /g, "&nbsp;").
            replace(/_(?!_)/g, " ").
            replace(/__/g, "_")*/;
    var dbgDiv = document.createElement("PRE");
    dbgDiv.innerHTML = txt;
    document.body.appendChild(dbgDiv);
} // dbgPr()


function  dbgPrObj (title, obj, fnsShow /*default:false*/) {
    if (arguments.length < 3) fnsShow = false;
    var pr = "---------- "+title+" ("+typeOf(obj)+") ----------<br/>";
    if (obj === undefined)
        pr += "undefined";
    else if (obj === null)
        pr += "null";
    else {
        var first = true;
        var keys = new Array();
        for (var key in obj) keys.push(key);
        keys.sort();
        for (var i = 0; i < keys.length; i++) {
            var key = keys[i];
            var val = obj[key];
            if (! fnsShow && (
                    (val != null && (val instanceof Function)) ||
                    (key.indexOf("on") === 0)))  // bit of a guess
                continue;
            if (first) first = false; else pr += ", ";
            pr+= key+"="+HTML_toString(val); }}
    if (!fnsShow) pr += "<br/>(function props display suppressed)";
    pr += "<br/>------------------------------";
    dbgPr(pr);
} // dbgPrObj()


function  dbgPrTree (
        caption, rootHTMLNode, showLevels, isAbbrev /*default:true*/) {

    if (arguments.length < 4) isAbbrev = true;

    var html = "<span style=\"font-size:12px; font-family:Arial\">";
    html += "<hr />";
    html += "<h3>" + caption + "</h3>";
    if (rootHTMLNode) {
        var tree = new TreeWalkerX(  // replaces document.createTreeWalker
                rootHTMLNode, ~NodeFilterX.SHOW_COMMENT, null, false);
        html += HTML_node (
                "", rootHTMLNode, tree, showLevels, 0, isAbbrev); }
    html += "</span>";
    
    var dbgDiv = document.createElement("DIV");
    dbgDiv.innerHTML = html;
    document.body.appendChild(dbgDiv);
    
    
    function  HTML_node (nodePath, node, tree, 
            showLevels, indentLevel, isAbbrev) {
        if (showLevels == 0) return "";

        var html = 
        "<table width=\"100%\" " +
                "border=\"0\" cellspacing=\"0\" cellpadding=\"0\">" +
            "<tr valign=\"top\">" +
                "<td style=\"" +
                        "width:" + (15*indentLevel) + "px; " +
                        "padding:2px 5px; " +
                        "border-left:1px solid black; " +
                        "border-top:1px solid black\">" + 
                    nodePath + 
                "</td>" +
                "<td style=\"" +
                        "background-color:#FFFFDD; width:55px; " +
                        "padding: 2px 5px; " +
                        "border-top:1px solid black\">" + 
                    "&lt;" + typeOf(node) + "&gt;" +
                "</td>" +
                "<td style=\"" +
                        "color:#808080; background-color:#FFFFDD; " +
                        "padding:2px 5px; " +
                        "border-top:1px solid black; " +
                        "border-right:1px solid black\">";
                    var specVal = HTML_specialNodeValue(node);
                    if (specVal != null)
                        html += 
                        "<span style=\"color:black\">" + 
                            specVal + 
                        "</span><br/>"
                    var keys = new Array();
                    for (var key in node) keys.push(key);
//if (indentLevel==0) alert("keys="+keys);
                    keys.sort();
                    var first = true;
                    for (var i = 0; i < keys.length; i++) {
                        var key = keys[i];
                        var val = node[key];
                        // abbreviated version omits functions & HTML
                        if (isAbbrev && (
                            (val != null && (val instanceof Function)) ||
                            (key.indexOf("on") === 0) ||  // bit of a guess
                            (key == "innerHTML") ||
                            (key == "outerHTML") ||
                            (key == "innerText") ||
                            (key == "outerText")))
                            continue;
                        if (first) first = false; else html += ", ";
                        html += 
                        "<span style=\"color:red\">" + 
                            key + 
                        "</span>=" + 
                        ((key == "innerHTML" || key == "outerHTML")
                            ? "<i>nested html</i>" // nesting HTML problematic
                            :HTML_toString(val)); }
                html +=
                "</td>" +
            "</tr>" +
        "</table>";
                            
        // recurse to display child nodes
        var currentNode = tree.currentNode;
        var childI = 0;
        for (var c = tree.firstChild(); c != null; c = tree.nextSibling())
            html += HTML_node (
                    nodePath + (nodePath ? "." : "") + childI++, 
                    c, tree, showLevels - 1, indentLevel + 1, isAbbrev);
        tree.currentNode = currentNode;
        
        return html;
    } // HTML_node()
            
} // dbgPrTree()


function  HTML_toString (val) {
    var t = typeOf(val);
    if (t == "string") return "\"" + val + "\"";
    if (t == "number" || t == "boolean" || t == "undefined" || t == "null")
        return val;
    var asStr = (val.toString ? val.toString() : val);
    if (asStr != "[object]") return asStr;
    return "[" + t + "]";
} // HTML_toString()


function  HTML_specialNodeValue (val) {  // returns null if none
    var t = typeOf(val);
    if (t == "#text") {
        if (val.nodeValue != undefined)
            return HTML_toString(val.nodeValue); }
    if (t == "INPUT")
        return val.name + " = \"" + val.value + "\"&nbsp; <i>(" +
                (val.type == "hidden" ? "hidden " : "") +
                "input)</i>";
    if (t == "TEXTAREA")
        return val.name + " = \"" + val.value + "\"&nbsp; <i>(input)</i>";
    if (t == "IMG") {
        if (val.src)
            return val.src; }

    return null;
} // HTML_specialNodeValue()


function  typeOf (val) {
    var t = typeof val;
    if (t == "string" || t == "number" || t == "boolean" || t == "undefined")
        return t;
    if (val === null)
        return "null";  // since typeof null is "object"
    var t = Object.prototype.toString.apply(val); // e.g. "[object Foo]"
    t = t.substring(8, t.length - 1);
    if (t != "Object") 
        return t;
    if (val.constructor) t = val.constructor;
    if (t != "Object")
        return t;
    if (val.nodeName) t = val.nodeName;
    else if (val.tagName) t = val.tagName;
    return t;
} // typeOf()
    


function  TreeWalkerX (rootHTMLNode, whatToShow, filter, expandEntityRefs) {
    // DOM 2 TreeWalker unavail so do simple simulation of the parts we need.
    // Constructor mirrors Document.createTreeWalker().  expandEntityRefs is
    // currently ignored.

    // public properties

    this.currentNode = rootHTMLNode;
    this.root = rootHTMLNode;  // read-only
    this.whatToShow = whatToShow;  // read-only
    this.filter = filter;  // read-only
    this.expandEntityReferences = expandEntityRefs;  // read-only; ignored
} // TreeWalkerX()

TreeWalkerX.prototype.firstChild = function () {
    if (! this.currentNode || 

            ! (this.currentNode = this.currentNode.firstChild)) 

        return null;

    if (this.shouldShowCurrent()) return this.currentNode;
    return this.nextSibling();
} // TreeWalkerX.firstChild()

TreeWalkerX.prototype.nextSibling = function () {
    if (! this.currentNode || 
            ! (this.currentNode = this.currentNode.nextSibling)) 
        return null;
    if (this.shouldShowCurrent()) return this.currentNode;
    return this.nextSibling();
} // TreeWalkerX.nextSibling()

TreeWalkerX.prototype.shouldShowCurrent = function () { // private
//???   if (this.currentNode.  ???whatToShow
    return (! this.filter ||
            (this.filter(this.currentNode) == NodeFilterX.FILTER_ACCEPT));
} // TreeWalkerX.shouldShowCurrent()


function  NodeFilterX () {}

NodeFilterX.FILTER_ACCEPT = 1;
NodeFilterX.FILTER_REJECT = 2;
NodeFilterX.FILTER_SKIP = 3;

NodeFilterX.SHOW_ALL = 0xFFFFFFFF;
NodeFilterX.SHOW_ELEMENT = 0x00000001;
NodeFilterX.SHOW_ATTRIBUTE = 0x00000002;
NodeFilterX.SHOW_TEXT = 0x00000004;
NodeFilterX.SHOW_ENTITY_REFERENCE = 0x00000010;
NodeFilterX.SHOW_ENTITY = 0x00000020;
NodeFilterX.SHOW_PROCESSING_INSTRUCTION = 0x00000040;
NodeFilterX.SHOW_COMMENT = 0x00000080;
NodeFilterX.SHOW_DOCUMENT = 0x00000100;
NodeFilterX.SHOW_DOCUMENT_TYPE = 0x00000200;
NodeFilterX.SHOW_DOCUMENT_FRAGMENT = 0x00000400;
NodeFilterX.SHOW_NOTATION = 0x00000800;

