/* web-utils.js
----------------------------------------------------------------------
Eddie Khoo Ee Lek / created 21 Jun 2005 10:37:56
                                                                  *//*
    Client-side Utilities

    provides:
        function pair (car, cdr);
        function makeCascadingHandler (current_handler, new_handler, arg);
        function makeHandler (user_func, arg); 
        function map (node, operation, argv, nodeChildren, stop_cond, aggregate);
        function nodeChildren (node);
        function ag_or (car, cdr);
        function ag_cdr (car, cdr);
        function ag_car (car, cdr);
        function ag_getString (val, result);
        function ag_getNode (node1, node2);
        function op_map (node, map_argv);
        function maptree (node, operation, argv, nodeChildren, stop_cond, aggregate);
        function cond_untilFalse (result);
        function cond_untilTrue (result);
        function cond_traverseAll (result);
        function searchTree (node, op_match, argv, nodeChildren);
        function traverseTree (node, op_match, argv, nodeChildren);
        function fold (menu);
        function op_unlinkcurrent(node, url);
        function unlinkCurrentItem (menu); 
        function removeLink (node);
        function currentPageURL ();
        function op_matchURL (node, url);
        function op_unlinkMatchedURL (node, url);
        function op_foldSubMenu (submenu, url);
        function foldMenu (menu);
        function op_foldAllSubmenus(node, url);
        function foldAllSubmenus (menu, currenturl);
        function op_matchTypeAndName (node, argv);
        function findNextNode (node, nodeType, nodeName);
        function op_isCurrentSectionNode (menu, url);
        function currentSectionNode (menu, url);
        function currentSection (menu, url);
        function xmlGet ();
        function loadXMLDocNoWait (req, url, processReqChange);
        function _onXMLLoad (argv);
        function onXMLLoad (req, userfunc);
        function loadOKNotify (done);
        function loadXMLDoc (url);
        function doctree (mythis, level);

    notes
    -----
        tree-folding functions here (foldMenu, currentPageURL etc) assume that all
        files are placed in the same directory!

*/

//
// General routiens
// -------------------------------------------------------------------


function pair (car, cdr)
{
        this.car = car;
        this.cdr = cdr;
        return this;
}


function makeCascadingHandler (current_handler, new_handler, arg)
{
        if (typeof(current_handler)=='undefined') {
            return makeHandler(new_handler, arg);
        } else {

            var usercall = new pair(new_handler, arg);
            if (!document.handlerReg) document.handlerReg = new Array();
            var hI = document.handlerReg.length
            document.handlerReg[hI] = usercall;

            var currentcall = new pair(current_handler);
            var hJ = document.handlerReg.length
            document.handlerReg[hJ] = currentcall;

            return new Function("", "document.handlerReg["+hJ+"].car(document.handlerReg["+hJ+"].cdr); document.handlerReg["+hI+"].car(document.handlerReg["+hI+"].cdr);");
        }
}


function makeHandler (user_func, arg) 
{
        var usercall = new pair(user_func, arg);
        if (!document.handlerReg) document.handlerReg = new Array();
        var hI = document.handlerReg.length
        document.handlerReg[hI] = usercall;

        return new Function("", "document.handlerReg["+hI+"].car(document.handlerReg["+hI+"].cdr);");
}


//
// Routine set for tree traversal
// -------------------------------------------------------------------

function map (node, operation, argv, nodeChildren, stop_cond, aggregate)
{
        /*
                node            any object
                operation       function operation (node, argv) => result
                argv            any object
                nodeChildren    function nodeChildren (node) => children of node
                stop_cond       function stop_cond(result)
                aggregate       function agregate(result_cumulative, result) => result_cumulative

                returns result_cumulative
        */
        
        if (!nodeChildren(node)) return null;

        var k, i=0;
        var result = null;
        while ((i<nodeChildren(node).length) && (k = nodeChildren(node)[i++]) && !stop_cond(result)) {
                result = aggregate(operation(k, argv), result);
        }
        return result;
}


function nodeChildren (node)
{
        if (node && node.hasChildNodes()) {
            return node.childNodes;
        }
        return null;
}


function ag_or (car, cdr)
{
        return (car || cdr)==true;
}


function ag_cdr (car, cdr)
{
        return cdr;
}


function ag_car (car, cdr)
{
        return car;
}


function ag_getString (val, result)
{
        if (result && result.length) return result;
        if (val && val.length) return val;
        return "";
}


function ag_getNode (node1, node2)
{
        if (node2) return node2;
        if (node1) return node1;
        return null;
}


function op_map (node, map_argv)
{
        var operation           = map_argv[0];
        var argv                = map_argv[1];
        var stop_cond           = map_argv[2];
        var aggregate           = map_argv[3];
        var nodeChildren        = map_argv[4];
        var result;

        result = operation(node, argv);
        result = aggregate(maptree(node, operation, argv, nodeChildren, stop_cond, aggregate), result);
        return result;
}


function maptree (node, operation, argv, nodeChildren, stop_cond, aggregate)
{
        return map(node, op_map, [operation, argv, stop_cond, aggregate, nodeChildren], nodeChildren, stop_cond, aggregate);
}


function cond_untilFalse (result)
{
        return !result;
}


function cond_untilTrue (result)
{
        return result;
}


function cond_traverseAll (result)
{
        return false;
}


function searchTree (node, op_match, argv, nodeChildren)
{
        return maptree(node, op_match, argv, nodeChildren, cond_untilTrue, ag_or);
}


function traverseTree (node, op_match, argv, nodeChildren)
{
        return maptree(node, op_match, argv, nodeChildren, cond_traverseAll, ag_or);
}

//
// Routine set to hide submenu items depending on current url
// -------------------------------------------------------------------

/*
        For a menu looking thus:
        ------------------------
  
        <ul id="submenus">
                <li><a id=1 href="main.html">Home</a><ul>
                </ul></li>
                <li><a href="pcsupport.html">Services</a><ul>
                        <li><a href="pcsupport.html">PC Support</a></li>
                        <li><a href="ecadsupport.html">ECAD Support</a></li>
                </ul></li>
        </ul>

        The basic premise is that the target menu is a list [of lists [of lists ...] ] of links, 
        and that the link must contain only the menu item name between the <a></a> tags.
  
        To fold the above, call:
  
                fold(sebmenus);
  
        Routine hides all other submenus except for that matching current filename

        To simply unlink the link to the current page / section, use

                unlinkCurrentItem(menu)
*/
  

function fold (menu)
{
        current = currentSectionNode(menu, currentPageURL());
        if (current && findNextNode(current.parentNode, 1, "LI")) {
                foldMenu(menu);
        } else {
                foldAllSubmenus(menu, currentPageURL());
        }
}


function op_unlinkcurrent(node, url)
{
        var child 

        if (node) {
                child = findNextNode(node, 1, "A");
                if (child && op_matchURL(child, url)) removeLink(child);
        }
        
        return node;
}


function unlinkCurrentItem (menu) 
{
    current = currentSectionNode(menu, currentPageURL());
    removeLink(current);
    traverseTree(menu, op_unlinkcurrent, currentPageURL(), nodeChildren);
}


function removeLink (node)
{
        i = document.createElement("DIV");
        for (j=0; j<node.childNodes.length; j++) {
                i.appendChild(node.childNodes[j].cloneNode(true));
        }
        node.parentNode.insertBefore(i, node);
        node.parentNode.removeChild(node);
        node = i;
        return node;
}


function currentPageURL ()
{
        return location.href.replace(/.*\//g, ""); 
}


function op_matchURL (node, url)
{
        if (node.nodeName=="A") {
                href = node.getAttribute("href");
                if (
                        (href.match(/\?..*/) && href.replace(/.*\//g, "")==url)
                        ||
                        (!href.match(/\?/) && href.replace(/.*\//g, "")==url.replace(/\?.*/, ""))
                        ||
                        (href.match(/\?/) && !href.match(/\?..*/) && href.replace(/.*\//g, "").replace(/\?/, "")==url.replace(/\?.*/, ""))
                ) {
                        return true;
                }
        }
        return false;
}


function op_unlinkMatchedURL (node, url)
{
        if (node.nodeName=="A") {
                href = node.getAttribute("href");
                if (
                        (href.match(/\?..*/) && href.replace(/.*\//g, "")==url)
                        ||
                        (!href.match(/\?/) && href.replace(/.*\//g, "")==url.replace(/\?.*/, ""))
                        ||
                        (href.match(/\?/) && !href.match(/\?..*/) && href.replace(/.*\//g, "").replace(/\?/, "")==url.replace(/\?.*/, ""))
                ) {
                        node = removeLink(node);
                        return true;
                }
        }
        return false;
}


function op_foldSubMenu (submenu, url)
{
        // hide menu if it does not eventually branch into specified url
        if (url==null || !searchTree(submenu, op_unlinkMatchedURL, url, nodeChildren)) {
                if (submenu.nodeType==1) submenu.style.display="none";
                //if (submenu.nodeType==1) findNextNode(submenu, 1, "UL").style.display="none";
                return true;
        }
        return false;
}


function foldMenu (menu)
{
        if (!map(menu, op_foldSubMenu, currentPageURL(), nodeChildren, cond_traverseAll, ag_or)) {
              map(menu, op_foldSubMenu, null, nodeChildren, cond_traverseAll, ag_or)  
        }
}


function op_foldAllSubmenus(node, url)
{
        var child 

        if (node) {
                child = findNextNode(node, 1, "A");
                if (child && op_matchURL(child, url)) removeLink(child);
        }
        
        child = findNextNode(node, 1, "UL");
        if (child) child.style.display = "none";
        return node;
}


function foldAllSubmenus (menu, currenturl)
{
        traverseTree(menu, op_foldAllSubmenus, currenturl, nodeChildren);
}


function op_matchTypeAndName (node, argv)
{
        nodeType = argv[0];
        nodeName = argv[1];

        if 
        (
        ((nodeName && nodeType) && (node.nodeName == nodeName) && (node.nodeType == nodeType)) || 
        ((nodeName && !nodeType) && (node.nodeName == nodeName)                              ) || 
        ((!nodeName && nodeType)                               && (node.nodeType == nodeType)) ){
                return node;
        } else {
                return null;
        }

        if (nodeType && (node.nodeType == nodeType)) return node;
        if (nodeName && (node.nodeName == nodeName)) return node;
        return null;
}


function findNextNode (node, nodeType, nodeName)
{
        return maptree(node, op_matchTypeAndName, [nodeType, nodeName], nodeChildren, cond_untilTrue, ag_getNode);
}


function op_isCurrentSectionNode (menu, url)
{
        data = new pair(url, 0); 
        if (searchTree(menu, op_matchURL, url, nodeChildren)) {
                return findNextNode(menu, 1, "A");
        }
        return null;
}


function currentSectionNode (menu, url)
{
        return map(menu, op_isCurrentSectionNode, url, nodeChildren, cond_untilTrue, ag_getNode);
}


function currentSection (menu, url)
{
        var node = currentSectionNode(menu,url)
        if (node) return node.getAttribute("href").replace(/.*\//g, "");
        return "";
}


//
// XML-related
// -------------------------------------------------------------------


function xmlGet ()
{
        if (window.XMLHttpRequest) {
                // branch for native XMLHttpRequest object
                req = new XMLHttpRequest();
        } else if (window.ActiveXObject) {
                // branch for IE/Windows ActiveX version
                req = new ActiveXObject("Microsoft.XMLHTTP");
        }
        return req
}


function loadXMLDocNoWait (req, url, processReqChange)
{
        // processReqChange code sample
        //
        // function processReqChange() 
        // {
        //     // only if req shows "complete"
        //     if (req.readyState == 4) {
        //         // only if "OK"
        //         if (req.status == 200) {
        //             // ...processing statements go here...
        //         } else {
        //             alert("There was a problem retrieving 
        //                the XML data:\n" + req.statusText);
        //         }
        //     }
        // }

        if (window.XMLHttpRequest) {
                // branch for native XMLHttpRequest object
                // req = new XMLHttpRequest();
                req.onreadystatechange = processReqChange;
                req.open("GET", url, true);
                req.send(null);
        } else if (window.ActiveXObject) {
                // branch for IE/Windows ActiveX version
                // req = new ActiveXObject("Microsoft.XMLHTTP");
                if (req) {
                        req.onreadystatechange = processReqChange;
                        req.open("GET", url, true);
                        req.send();
                }
        }
    return req;
}


function _onXMLLoad (argv)
{
        req      = argv[0];
        userfunc = argv[1];

        if (req.readyState == 4) {
                if (req.status == 200) {
                        userfunc();  
                } else {
                        alert("There was a problem retrieving the XML data:\n" +req.statusText);
                }
        }
}


function onXMLLoad (req, userfunc)
{
        return makeHandler(_onXMLLoad, [req, userfunc]);
}


function loadOKNotify (done)
{
        done = true;
}


function loadXMLDoc (url)
{
        var req, done = false;
        done = 0;
        req = xmlGet();
        loadXMLDocNoWait(req, url, onXMLLoad(req, makeHandler(loadOKNotify, done)));

        //while (!done) {};
}


//
// DEBUG functions
// -------------------------------------------------------------------
function doctree (mythis, level)
{
        if (!mythis.hasChildNodes()) return null;
        var i, t;
        t = "";
        for(i=0; i<mythis.childNodes.length; i++) {
                t += "<br>"+level+"* "+mythis.childNodes[i].nodeType;
                t += "<br>"+level+"* "+mythis.childNodes[i].nodeName;
                if (mythis.childNodes[i].nodeType==1) {
                        t += "<br>"+level+"-o "+mythis.childNodes[i].getAttribute("class");
                        t += "<br>"+level+"-o "+mythis.childNodes[i].getAttribute("className");
                        t += "<br>"+level+"-o "+mythis.childNodes[i].getAttribute("id");
                } else if (mythis.childNodes[i].nodeType==3) {
                        t += "<br>"+level+"-o ["+mythis.childNodes[i].nodeValue + "]";
                }
                t += doctree(mythis.childNodes[i], level + "--");
        }
        return t;
}
