

/* 
   Compatibility notes:

   (1) The following works in Mozilla/Konqueror, but not in IE: 
   
       e.style.setProperty("display", "inline", "");

       So, need to use constructs like e.style.display = "inline";
       
       Naming convention: properties with hyphens should be replaced
       by corresponding camel-case (?) names, e.g.:

       font-size -> fontSize

*/


/*
Important fact: scope
=====================

 Global

  A variable that is declared outside any functions is global. This
  means it can be referenced anywhere in the current document.

    * Declared outside any functions, with or without the var keyword.

    * Declared inside a function without using the var keyword, but
      only once the function is called.

 Local

  A variable that is declared inside a function is local. This means
  it can only be referenced within the function it is declared.

    * Declared in a function with the var keyword.


Question: what about function arguments?  

For now, I'll assume they are local.


*/




var req;

function loadHtmlDocument(url) 
{
    // branch for native XMLHttpRequest object
    if (window.XMLHttpRequest != null) {
        addMessage("Using XML Http Request for " + url);
        req = new XMLHttpRequest();
	// branch for IE/Windows ActiveX version
    } else if (window.ActiveXObject != null) {
        addMessage("Using microsoft XMLHTTP for " + url);
        req = new ActiveXObject("Microsoft.XMLHTTP");
    }
    if (req) {
	if (req.overrideMimeType != null) 
	    req.overrideMimeType('text/xml'); // needed for gecko?
	req.onreadystatechange = handleReqChangeUsingInnerHTML;
	req.open("GET", url, true);
	//req.open("POST", url, true);
	req.send(null);
    }
}


function handleReqChange() 
{
    if (req.readyState == 4) {   // "complete"
        if (req.status == 200) {   // OK
            addMessage("Request OK");
	    addMessage("Content-type: " + req.getResponseHeader("Content-type"));
            // addMessage(req.responseText);
            
	    // addMessage(req.responseXML);
            // addMessage(req.responseXML.documentElement);


	    /* OLD CODE:

	       var urldoc = req.responseXML; // works with firefox, but not konq
	       var urldoc = req.responseXML.documentElement; // works with both
	       // neither work with IE 
	    */


	    var urldoc;
	    if (req.responseXML.documentElement != null) 
		urldoc = req.responseXML.documentElement; // Konqueror
	    else 
		urldoc = req.responseXML; // IE (Mozilla probably OK
					  // with both
            addMessage("urldoc: " + urldoc);

	    var num_child;
	    var contentNode = document.getElementById("contentContainer");
	    // addMessage("contentContainer: " + contentNode);

	    /* First, remove existing content */
	    num_child = contentNode.childNodes.length;
	    addMessage("contentContainer has " + num_child + " childNodes");

	    if (num_child > 1) {
		var i;
		for (i = 1; i < num_child; i++) {
		    // addMessage(i + " : contentNode has " + contentNode.childNodes.length + " children");
		    contentNode.removeChild(contentNode.childNodes[1]);
		}
	    }

	    // get body of newly downloaded content
	    addMessage("Trying to get body node");
	    // var bodynode = urldoc.getElementsByTagName('body').item(0);
	    var bodynode = urldoc.getElementsByTagName('body')[0];
	    addMessage("body: " +  bodynode);

	    // Want to insert elements of bodynode one by one.
	    // Problem is: konqueror throws a WRONG_DOCUMENT_ERR (= 4)
	    // exception (as it should).

	    // to work around this, one option is to use importNode,
	    // which is supposedly in the DOM 2 specs
	    // (http://www.w3.org/DOM/faq.html#ownerdoc).

	    var dup;
	    if (document.importNode != null) 
		dup = document.importNode(bodynode, true);
	    else 
		dup = bodynode;

	    addMessage("dup: " + dup);

	    var itemsToInsert = dup.childNodes;
	    num_child = itemsToInsert.length;
	    addMessage("body has " +  num_child + " children");

	    if (num_child > 0) {

	      // contentNode.appendChild(bodynode); inserts the full
	      // <body> node with all its children, but seems to work
	      // too

	      var i;

	      for (i = 0; i < num_child; i++) {
		  // addMessage(i + " " + itemsToInsert[0] + " " + itemsToInsert[0].nodeName);
		  contentNode.appendChild(itemsToInsert[0]);
	      }
	    }
        } // not-so-good cases
	else {
	    addMessage("Complete but failed: " + req.statusText);
            addMessage(req.status);
        }
	document.getElementById("loadingMessage").className = "loadingInvisible";
    } 
    else {
	addMessage("state: " + req.readyState);
	document.getElementById("loadingMessage").className = "loadingVisible";
    }
}


/* uses non-W3C-standard innerHTML, but works! on at least IE,Konq,Moz */

function handleReqChangeUsingInnerHTML() 
{
    if (req.readyState == 4) {   // "complete"
        if (req.status == 200) {   // OK
            addMessage("Request OK");
	    addMessage("Content-type: " + req.getResponseHeader("Content-type"));
	    // addMessage(req.responseText);
	    var contentNode = document.getElementById("contentContainer");
	    contentNode.innerHTML = req.responseText;
        } // not-so-good cases
	else {
	    addMessage("Complete but failed: " + req.statusText);
            addMessage(req.status);
        }
	document.getElementById("loadingMessage").className = "loadingInvisible";
    }
    else {
	addMessage("state: " + req.readyState);
	document.getElementById("loadingMessage").className = "loadingVisible";
    }
}



/* Global variables: keep track of currently highlighted entries */

var highlighted_topic;
var highlighted_content;

/* Utilities */


function highlightTopic(ht) { 
    if (ht != null) {
	unhighlightTopic(highlighted_topic);
	paragraph(ht).className = "htopic"; 
	highlighted_topic = ht;
    }
}
function unhighlightTopic(ut) { if (ut != null) paragraph(ut).className = "topic"; }

function highlightContent(hc) { 
    if (hc != null) {
	unhighlightContent(highlighted_content);
	paragraph(hc).className = "hcontent"; 
	highlighted_content = hc;
    }
}
function unhighlightContent(uc) { if (uc != null) paragraph(uc).className = "content"; }








function showTopic(cur) {
    /* show not only e, but all immediate children of e as well.  In
       this case e will definitely be a <li>.  If it's a content node,
       there shouldn't be any need to show or hide anything (we _do_
       need to show the corresponding content, but that's the task of
       some other function), and this function shouldn't have been
       called.  Since it's a topic node, we should 

       (1) hide all siblings (both topic and content nodes)
       (2) highlight self, but also
           (a) unhighlight parent topic node
	   (b) unhighlight sibling content node (if any)
       (3) show immediate children (both topic and content nodes)

       Here's one algorithm that should achieve this:

       (a) unhighlight both topic and content
       (b) hide parent and ALL its children
       (c) show just parent
       (d) show and highlight just current
       (e) show all immediate children
       
       Let's try this.  

    */

    var parentUL = cur.parentNode;
    var parentLI = parentUL.parentNode;

    addMessage("showTopic called with: " + cur);

    if (parentLI.nodeName == "LI") {

	/* We're not at the top level, so safely do things to
	   parent. */

	/* (b) hide parentLI and all its children */
	recursiveHide(parentLI);

	/* (c) show just parent */
	show(parentLI);
    } 
    else {
	/* We're at top level, so hide myself recursively (this
	   automatically happens in the other branch when parentLI is
	   hidden) */
	recursiveHide(cur);
    }

    /* (d) show and highlight just current (also unhighlights old) */
    show(cur);
    highlightTopic(cur);
    
    /* (e) show all immediate children (but not their children) */

    /* cur should have exactly 1 <UL> child  */

    var ul_child = ulist(cur);

    /* We will show all its <LI> children  */

    if (ul_child != null) {
	var i;
	var li_list = ul_child.childNodes;
	var num_li = li_list.length;
	if (num_li > 0) {
	    for (i = 0; i < num_li; i++) {
		if (li_list[i].nodeName == "LI") {
		    show(li_list[i]);
		}
	    }
	}
    }

    addMessage("showTopic succeeded");
}





function recursiveHide(e) {
    /* 
       e should be a LI element.  

       (Case 1) If it's a topic node, it will have exactly one <UL>
       child.  In that case, we will recursively hide all <LI>
       children of that <UL>.

       (Case 2) If it's a content node, it will not have a <UL> child,
       so we won't go into the loop.

       In either case, we still hide e itself.
    */
    var i, li_list, num_li;
    var ul_child = ulist(e);

    // addMessage("calling recursiveHide with: " + e + ul_child);

    if (ul_child != null) {
	li_list = ul_child.getElementsByTagName("LI");
	num_li = li_list.length;
	if (num_li > 0) {
	    for (i = 0; i < num_li; i++) {
		recursiveHide(li_list[i]);
	    }
	}
    }
    hide(e);

    // addMessage("recursiveHide succeeded");
}


function show(e) {
    if (e != null) {
	// addMessage("Showing " + e.id);
	/* doesn't work in IE: e.style.setProperty("display", "inline", ""); */
	e.style.display = "inline";
    }
}

function hide(e) {
    if (e != null) {
	// tryToWait(1000);
	// addMessage("Hiding " + e.id);
	/* This does not work in IE: e.style.setProperty("display", "none", ""); */
	e.style.display = "none";
    }
}

/* FIXME: getElementsByTagName gives ALL descendants, not just
   children  */

/* function paragraph(e) { */
/*     return e.getElementsByTagName("p")[0]; */
/* } */

function paragraph(e) {
    var i;
    var ch = e.childNodes;
    var num = ch.length;
    if (num > 0) {
	for (i = 0; i < num; i++) {
	    if (ch[i].nodeName == "P")
		return ch[i];
	}
    }
    return null;
}


function ulist(e) {
    ullist = e.getElementsByTagName("ul");
    if (ullist.length == 0) return null;
    else return ullist[0];
}


function topicClicked(id) {
    current_li = document.getElementById(id);
    current_p = paragraph(current_li);
    current_text = current_p.firstChild;

    addMessage(current_text.nodeValue);
    showTopic(current_li);
}

function showContent(cur) {
    /* This will involve a XMLHttpRequest, but no CSS stuff except to
       unhighlight the old highlighted entry and highlight cur */
    highlightContent(cur);
    var url = paragraph(cur).getAttribute("contLoc");
    clearMessageArea();
    addMessage(url);
    loadHtmlDocument(url);
    // window.open(url);
}


function contentClicked(id) {
    current_li = document.getElementById(id);
/*     current_p = paragraph(current_li); */
/*     current_text = current_p.firstChild; */
    showContent(current_li);
}


function initiateTOC(topid) {
    /* After loading the page, this is run to pretend that the top
       level id (topid) has been clicked. */
    topicClicked(topid);
}


function addMessage(s) {
    document.feedback.msg.value += s + "\n";
}

function clearMessageArea() {
    document.feedback.msg.value = "";
}

function blankMessage() {
    addMessage(" Waiting... ");
}

/* function tryToWait(delay) { */
/*     setTimeout('blankMessage()', delay); */
/* } */


/* function tryToWait(delay) { */
/*     var i, j; */
/*     for (i = 0; i < 100 * delay; i++) { */
/* 	j = i + 1; */
/*     } */
/* } */

function tryToWait(delay) {
    /* No use, give up */
    0;
}

