/******************************************************************************
 expandtree.js
 
 Include this file as an external javascript file at the END of any HTML
 document.  It will turn all unordered lists in the document into
 expandable/collapsible trees - including nested lists.
 
 Many of these functions rely on results from the external js file
    compatibility.js
******************************************************************************/
var countULs = 0;

initTree();  //convert the HTML unordered lists (must run after document is loaded/created)


//--------------------------------------------------------------
// initTree
//--------------------------------------------------------------
// Inputs: none
// Output: none
//--------------------------------------------------------------
// Intended Use:
//    initTree();
//--------------------------------------------------------------
// Dependencies:
//    external file: compatibility.js
//    stylesheet:    project5.css
//    global var     countULs = 0
//--------------------------------------------------------------
// Notes:  Reads through the HTML document and assigns the
//         appropriate CSS and event handlers to make all
//         unordered lists (<UL>) into expandable/collapsible
//         trees.
//--------------------------------------------------------------
function initTree()
{
  var doc = (g_browserIE4 ? document.body : document);
  var ul = doc.getElementsByTagName("ul");

  if (ul != null) {
    for (var i = 0, len = ul.length; i < len; i++)
    {
      if ( ul[i].hasChildNodes() )
      {
        if (countULs > 0) //don't process the root node of the tree
        {
          ul[i].className = "nExp";
  
          var li = ul[i].parentNode;
          li.onclick   = toggleNode;
          li.className = "nExp";
        } //end if countULs
        ++countULs;
      }
    } //end for i
  }
} //end initTree()

//--------------------------------------------------------------
// toggleNode
//--------------------------------------------------------------
// Inputs:
//    event      Element's Event object
// Output:
//    none
//--------------------------------------------------------------
// Intended Use:
//    element.onclick = toggleNode;
//--------------------------------------------------------------
// Dependencies:
//    external file:  compatibility.js
//    function        setDisplayStyle()
//--------------------------------------------------------------
// Notes:  OnClick event handler for <li> elements within the
//         lists.
//--------------------------------------------------------------
function toggleNode(event)
{
  var n = (g_browserIE4 ? window.event.srcElement : event.target);

  /* Check to make sure we're dealing with an <li> node.  This is necessary in
   Mozilla (DOM Level 2 event handling?) because every element winds up firing
   toggleNode via an onclick event.  I think what is happening is that the
   parent node event handlers are bleeding over - despite the bubble cancellation
   code below. */
  if (n.tagName.toUpperCase() == "LI")
  {
    var kids = n.childNodes;
    
    if (kids != null)
    {
      for (var i = 0; i < kids.length; i++)
      {
        if (kids[i].nodeType == 1 /* ELEMENT_NODE */ )
        {
          if ("inline" == kids[i].style.display) {
            setDisplayStyle(kids[i], "none", n);
          } else {
            setDisplayStyle(kids[i], "inline", n);
          }
        }
      } //end for
    }
  } //end if LI
  
  if (event && event.stopPropagation) {
    event.stopPropagation();
  } else {
    window.event.cancelBubble = true;
  }
} //end toggle(event)

//--------------------------------------------------------------
// setDisplayStyle
//--------------------------------------------------------------
// Inputs:
//    element    The element to set the display style for.
//    state      The display value to set.
//    parent     The parent element of the current element.
// Output:
//    none
//--------------------------------------------------------------
// Intended Use:
//    setDisplayStyle(element, "inline", parent);
//--------------------------------------------------------------
// Dependencies:
//    external file:  compatibility.js
//    function        addStyle()
//--------------------------------------------------------------
// Notes:  Sets the display style value and toggles the list
//         style image accordingly.
//         This is a compatiblity wrapper function, since
//         Mozilla seems to have problems updating style changes
//         when they are made by directly setting the properties
//         of the style object.
//--------------------------------------------------------------
function setDisplayStyle(element, state, parent)
{
  var imgFile = (state == 'none') ? 'closed' : 'open';
  imgFile += ".gif";  //extension
  
  if (g_browserMOZ)
  {
    addStyle(element, "display", state);  //preserves existing style settings
    addStyle(parent, "list-style-image", "url(images/"+imgFile+")");  //preserves existing style settings
  }
  else
  {
    element.style.display = state;
    parent.style.listStyleImage = "url(images/"+imgFile+")";
  }
} //end setDisplay(e, state);

//--------------------------------------------------------------
// addStyle
//--------------------------------------------------------------
// Inputs:
//    element    Element to set the style value for
//    key        Style property to set
//    val        Value to set for the given style property
// Output:
//    none
//--------------------------------------------------------------
// Intended Use:
//    addStyle(element, "display", "none");
//--------------------------------------------------------------
// Dependencies:
//    function Trim()
//--------------------------------------------------------------
// Notes:  Another compatibility wrapper function.  Saves off
//         existing style values (only ones set in HTML), then
//         rebuilds style attribute with the new values.
//--------------------------------------------------------------
function addStyle(element, key, val)
{
  var buffer = element.getAttribute("style");
  var newStyle = "";
  
  key += ':';
  
  if (buffer != null)
  {
    var arrStyle = buffer.split(';');
    
    for (var i = 0; i < arrStyle.length; i++)
    {
      arrStyle[i] = Trim(arrStyle[i]);  //Trim leadin/trailing whitespace
      
      //rebuild the style rules, ignoring the existence
      //of the one we're going to add.
      var tmp = arrStyle[i].substr(0, key.length);
      if ( (tmp.length > 0) && (tmp != key) )
      {
        newStyle += (arrStyle[i] + ';');
      }
    }
  } //end buffer != null
  
  newStyle += (key+val+';');  //append the new rule
  element.setAttribute("style", newStyle);
} //end addStyle(element, key, val)

//--------------------------------------------------------------
// Trim
//--------------------------------------------------------------
// Inputs:
//    str        The string to trim.
// Output:
//    none
//--------------------------------------------------------------
// Intended Use:
//    sTrimmedString = Trim(sString);
//--------------------------------------------------------------
// Notes:  Removes trailing and leading spaces.
//--------------------------------------------------------------
function Trim(str)
{
  //remove leading whitespace
  str = str.replace(/^\s*/,"");
  //remove trailing whitespace
  str = str.replace(/\s*$/,"");

  return str;
} // end Trim


