/*
Summary:  An expandable menu based on an XHTML list.
Version: 1.66
Version Date: 2009-11-14 04:11
Author:   John Bentley (www.softmake.com.au) 
          Based on a menu by Dave Lindquist (www.gazingus.org)          
Depends on:
          cookies.js  Bill Dortch's Cookie functions
                      http://www.cookiecentral.com/code/cookie.txt (sighted 13 Aug 2005)   
          cookiesEnabledTest.jb        
Usage Requirments:
          1. The XHTML Menu Tree structure needs to be like this (See Usage Example):
            We have four nodes (elements) regarded as:
              Parent - must be an <li>
              Actuator - must be an <a>. This is the element you click to expand or collapse
                        a subMenu.
              SubMenu - is usually a <ul>. This list contains all the leaf nodes.
              Leaf - the <a> in a <li>/<a> combo. Is the actual menu command/ page to choose.    
          2. The id of the menu tree must be "menuTree"    
Usage Example: 
            Example of what to put in your <body> :
            <ul id="menuTreeMyTree" class="menuTree">
              <li><a href="index.html" class="pageExists"><span>Home</span></a></li>
              <!-- An <li> "parent" node. It must have a class "parent" -->
              <li class="parent">
                <!-- The first <a> is the "actuator" node. It must have a class "actuator" -->
                <a href="javascript:%20void%20(0);" class="actuator defaultExpand"><span>Documentation</span></a>
                <!-- The <ul> below is the "subMenu" node -->
                <ul>
                  <li class="parent">
                    <a href="javascript:%20void%20(0);" class="actuator defaultExpand"><span>Features</span></a>
                    <ul>
                      <!-- the <l>i nodes here are mere "leaf" nodes -->
                      <li><a href="featuresForTheUser.html" class="pageExists"><span>For the User</span></a></li>
                      <!-- <spans> surrounding text are important for cross-browser purposes -->
                      <li><a href="featuresForTheDeveloper.html" class="pageExists"><span>For the Developer</span></a></li>
                    </ul>
                  </li>
                  <li><a href="setUp.html" class="pageExists"><span>Set up</span></a></li>
                </ul>
              </li>
              <li class="parent">
              ....
                    
            Put this in your <head>:
              <style type="text/css">
                  <!-- 
                  @import url("menuTree.css"); 
                  -->
              </style>
              
              
              <script type="text/javascript" src="cookies.js"></script>
              <script type="text/javascript" src="cookiesEnabledTest.js"></script>
              <script type="text/javascript" src="menuTree.js"></script>
              <script type="text/javascript">
              <!--
                // You could put this in menuTree.js but that will probably
                // cause confusion for the developer.
                addLoadEvent(function() {
                  // more code to run on page load 
                  menuTreeMain("menuTreeMyTree");
                });                
              //-->
              </script>  
*/
/*
include("cookies.js");
include("cookiesEnabledTest.js");
include("file:///C:/Data/Sda/Code/EcmaScript/Libraries/StandardLibrary/WebScriptLibrary/url.js");
include("http://localhost/SdaCode/EcmaScript/Libraries/StandardLibrary/WebScriptLibrary/url.js");*/

// Simulated Enums: Call like -> ConfigurationEnum.safe("byDefaultExpandClass"));
var ConfigurationEnum = ['expandAll', 'collapseAll', 'byDefaultExpandClass', 'byPersistence']

ConfigurationEnum.safe = function(value) {
  var found = false;
  for (var i = 0; i < this.length; i++) {
    if (this[i].toLowerCase() == value.toLowerCase()) {
      found = true;
      break;
    }
  } 
  if ( found ) {
    return this[i]; 
  } else {
    var msg = "The value '" + value + "' does not exist in ConfigurationEnum. \n";
    msg += "Permitted values: " + ConfigurationEnum.toString();
    throw new Error(msg);
    return false;
  }
}

/********** Options for you to configure **********/

// If the user has not yet set whether they want color guides
// default on? true for on; false for off.
var ColorGuidesDefaultOn = false;

// How shall the tree by configured when either persistence is turned off
// (eg Cookies Off) or user lands on site for first time (So Cookies not yet set)?
// Permitted values: "byDefaultExpandClass" ; "collapseAll"; "expandAll"
// Normally: "byDefaultExpandClass"
var ConfigureTreeWhenNotYetPersist = "byDefaultExpandClass";

// Turning this off will have a read/writes to the hard drive, via cookies,
// on page load and leaf clicks.
var SelectionPersist = true; 

// Automatically synchronize the page to the menu when you move to a new page.
// If set to true this will make having the "Sync" command redundant
// on the web page.
var AutoSynch = true; 

var ThisFileName = "menuTree.js";

/**************************************************/

/*********** Global Variables  ********************/
var Debug = false;
var MenuTree;
var MenuTreeStructure = "";
// pass hostname to path to ensure the variable has scope over entire domain.
var CookiePathString = "/";

// The following variables are set in setGlobalVariables() 
var MenuTreeIdString = "";
var MenuTreeStructureCookieString = "";
var MenuTreeSelectedNodeCookieString = "";
/**************************************************/

if (!document.getElementById) {  
  document.getElementById = function() { return null; };
}

window.onunload = function () {
  // Cookie expires at the end of the session.

  SetCookie(MenuTreeStructureCookieString, MenuTreeStructure, null, CookiePathString );
}

function setGlobalVariables(menuTreeId) {
  MenuTreeIdString = menuTreeId; 
  MenuTreeStructureCookieString = "MenuTree_" + MenuTreeIdString + "_Structure";
  MenuTreeSelectedNodeCookieString = "MenuTree_" + MenuTreeIdString + "_SelectedNode";
}

/*  Summary: Entry point for menu tree.
    Usage: 
      window.onload = function() {           
        menuTreeMain("menuTree01");
      }                                 */
function menuTreeMain(menuTreeId) {
  cookiesEnabledTest();
  if (menuTreeId == null || menuTreeId == "") {
      var msg = "";
      msg += "You need to pass a menuTreeId to menuTreeMain.";
      msg += " Eg menuTreeMain('menuTreeMyCompany')";
      msg += " The present value of menuTreeId is: " + menuTreeId;
      throw new getErrorObj(msg);  
      return false;
  }
  
  // A Hack for Safari. Just let the menu be fully expanded.
  if (!stylesheetsDisabled() && !isSafari()) {
    initializeMenuTree(menuTreeId);
  }
}

function test() {

  writeOut("Folder Level: " + getHtmlFolderLevel());
  //if (Debug) alert(getLocationProperties());
  return true;
}

/*
Summary: includes external scripts in this external script
         so that you don't have to reference them within the (x)html document
         files. 
Remarks:
         In effect this inserts something like
          <script type="text/javascript" src="includeScriptB.js"></script>
         into the html document.         
Param:   
         scriptUrl (String)
            an absolute or relative url.         
Usage:   In your external scripts, at the top of your documents,
         include("file:///C:/temp/myScript.js");
         include("otherScript");
       
function include(scriptSrc) {
   var headElement = document.getElementsByTagName("head")[0];
   var scriptElement = document.createElement("script");
   scriptElement.setAttribute("type", "text/javascript");
   scriptElement.setAttribute("src", scriptSrc);
   headElement.appendChild(scriptElement);
}  */ 

/*
Returns: the level that the running .html file is relative
         to this script file. Returns Zero length string if in same
         folder.
Example:
         In .html
         <script type="text/javascript" src="../menuTree.js"></script>
         This file is menuTree.js.
         
         Returns "../"  
Depends On: 
         Global Variable: ThisFileName         */
function getHtmlFolderLevel() {
  var docScripts = document.getElementsByTagName("script");
  var scriptSrc = "";
  var re = new RegExp("([\.|/]*)(.*)");
  var matchArray;
  for (var i = 0; i < docScripts.length; i++ ) {
    if (docScripts[i].attributes["src"] != null ) {
      scriptSrc = docScripts[i].attributes["src"].nodeValue;
    } 
    if (scriptSrc.indexOf(ThisFileName) != -1) {
      matchArray = re.exec(scriptSrc);
      if (matchArray.length >= 1) return matchArray[1]; 
    }
  }
  return "";
}


/*  Summary: Initializes the menu tree by collapsing it.
    Remarks: If javascript is turned off then the tree is fully expanded for the
            user.                      */
function initializeMenuTree(menuTreeId) {

    setGlobalVariables(menuTreeId); 
    // MenuTree is a global variable.
    MenuTree = document.getElementById(menuTreeId);
    
    var nextLI;  
    var subMenu;
    var firstAnchor;

    // For each li if there is a first child <a> and second child <ul>
    // use these as actuator and subMenu element respectively.
    // conintue down the tree.
    
    var listItems = MenuTree.getElementsByTagName("li");
    var i = 0;
    
    // Temporarily Hide tree and process it so it doesn't 'wink' at the user.
    MenuTree.style.visibility = "hidden";
    configureTree("byPersistence");
    MenuTree.style.visibility = "visible";

    while (i < listItems.length) {
      nextLI = listItems[i];

      firstAnchor = getChildElementsByTagName(nextLI, "a")[0];
      subMenu = getChildElementsByTagName(nextLI, "ul")[0];
      
      // A Parent node is defined as a <li> that has a <a> AND <ul> 
      // as immediate child (in any order..
      if (subMenu && firstAnchor) {
        writeOut("This is a Branch");
        initializeActuator(subMenu, firstAnchor);
      } else if (firstAnchor) {
        initializeLeaf(firstAnchor);
      }
      i++;
    }  
    return true;
}

/* 
Summary:  Expand or collapse all nodes in the tree. Or expand and collapse
          nodes according to whether the XHTML author has specified
          that the actuator anchor, <a>, has also a "defaultExpand" class.
          or if configureTree("byDefaultExpandClass") called AND structure
          has been saved then used that saved stucture
 Example: 
          configureTree("expandAll"); */
function configureTree(configuration) {
  var actuator;
  var subMenu;
  var display;
  var listItems = MenuTree.getElementsByTagName("li");
  var i = 0;
  var msg = "";


  // This ensures the parameter is off acceptable type.
  ConfigurationEnum.safe(configuration)
  
  // If structure has been stored use that rather than default.
  if (configuration == "byPersistence") {
    MenuTreeStructure = nullToZeroLengthString(GetCookie(MenuTreeStructureCookieString));
    
    // if there is no stored structure then we will rely on the web developers
    // setting of "defaultExpand" classes.
    if (MenuTreeStructure == "") {
      configuration = ConfigureTreeWhenNotYetPersist;
    }
  }
      
  while (i < listItems.length) {
    nextLI = listItems[i];
    nextLI.id = "treeNode" + i;  // For persistence referencing.      
    
    if (isElementAParent(nextLI)) {
      actuator = getChildElementsByTagName(nextLI, "a")[0];
      subMenu = getChildElementsByTagName(nextLI, "ul")[0];
      switch (configuration){
        case "collapseAll":
          setParentExpanded(actuator, subMenu, false);       
          break;
        
        case "expandAll":
          setParentExpanded(actuator, subMenu, true);         
          break;
        
        case "byDefaultExpandClass":
          if (actuator.className.indexOf("defaultExpand") != -1) {
            setParentExpandedORCollapsed(actuator, subMenu, true);
          } else {
            setParentExpandedORCollapsed(actuator, subMenu, false);  
          }
          break; 
        
        case "byPersistence":
          if (getParentExpandedPersist(nextLI.id)) {
            setParentExpandedORCollapsed(actuator, subMenu, true);
          } else {
            setParentExpandedORCollapsed(actuator, subMenu, false);  
          }
          
          break;                      
        
        default:
          msg = "Unexpected (default) clause reached in switch: configuration = " + configuration;
          throw new getErrorObj(msg);              
      } // switch (configuration     
    } // if(isElem
    i++;
  } // while
  
  if (AutoSynch 
      && configuration == "byPersistence" 
      || configuration == "byDefaultExpandClass") {
    synchMenuTreeToPage();
  }
  
  displaySelectedNodePersist(); 
  displayColorGuides();
  
  return true; 
}

// A Parent node is defined as a <li> that has a <a> AND <ul> 
// as immediate child (in any order..
// Don't depend on the page author to put a class="parent" in the <li>
function isElementAParent(ele) {
  var actuator = getChildElementsByTagName(ele, "a")[0];
  var subMenu = getChildElementsByTagName(ele, "ul")[0];
  
  return (subMenu && actuator);
  
}

// Summary:   Define the configuration for the onclick event of the actuator <a> 
//            (under the parent <li>)
// Created By: Dave Lindquist (www.gazingus.org)
// Modified By: John Bentley (www.softmake.com.au) 
function initializeActuator(subMenu, actuator) {
    
    // Only if a <li> has both
    if (subMenu === null || actuator === null) { 
      return false; 
    }
    var parent = actuator.parentNode
    try {
      
      if (parent.className.indexOf("parent") == -1) {
        msg = "The Developer of this page did not markup the tree menu properly: \n";
        msg += "Any <li> that acts as a parent node must have a 'parent' class ";
        msg += " otherwise the correct node image won't display when javascript is turned off.\n";
        msg += "For example the correct structure is:\n\n";
        msg += "<li class='parent'>\n";
        msg += "  <a href='javascript:%20void%20(0);' class='actuator'>Applied</a>\n";
        msg += "  <ul>\n";
        msg += "    <li><a href='javascript:%20void%20(0);'>Discrimination</a></li>\n\n";
        msg += "The developer should add 'parent' as a class, eg class='parent', to the parent <li> ";
        msg += " that contains markup that starts ... \n";
        msg += actuator.parentNode.innerHTML.substr(0,200);
        throw new getErrorObj(msg);
      }
      
     if (actuator.href.indexOf("javascript:%20void%20(0);") == - 1) {
        msg = "The Developer of this page did not markup the tree menu properly: \n";
        msg += "Any <a> that acts as an actuator node must not be linked to a page \n";
        msg += " That is, it's href must be 'javascript:%20void%20(0);' but is: \n";
        /* msg += "Protocol: " + actuator.protocol + "\n";
        msg += "hostname: " + actuator.hostname + "\n";
        msg += "host: " + actuator.host + "\n";
        msg += "pathname: " + actuator.pathname + "\n";
        msg += "href: " + actuator.href + "\n";
        msg += "lastIndexOf('#'): " + actuator.href.lastIndexOf("#") + "\n";
        msg += "actuator.href.length: " + actuator.href.length + "\n"; */
        msg += actuator.href + "\n";
        msg += "\nThe correct structure is:\n\n";
        msg += "<li class='parent'>\n";
        msg += "  <a href='javascript:%20void%20(0);' class='actuator'>Applied</a>\n";
        msg += "  <ul>\n";
        msg += "    <li><a href='somePage.html'>Discrimination</a></li>\n\n";
        msg += "The developer should change the href attribute of actuator <a> that contains the "
        msg += " text '" + actuator.innerHTML + "' to have href='javascript:%20void%20(0);'";
        msg += "\n\nAlternatively the developer could dive in an do some coding to remove this";
        msg += " constraint.";
        throw new getErrorObj(msg);      
      }
 
      // John Bentley: this is the clever part that Dave did.
      // Assigning a function to the onclick event of Parent nodes here 
      // (in JavaScript) means you don't have to clutter your XHTML source files.
      writeOut("initializing actuator (parnetNode.id): " + actuator.parentNode.id);
      writeOut("----- getHtmlFolderLevel():" + getHtmlFolderLevel());
      var htmlFolderLevel = getHtmlFolderLevel();      
      actuator.onclick = function() {
        var display = subMenu.style.display;
        this.parentNode.style.backgroundImage = (display == "block") ? "url(" + htmlFolderLevel + "images/menuTree/collapsed.gif)" : "url(" + htmlFolderLevel + "images/menuTree/expanded.gif)";
        setParentExpandedPersist(actuator.parentNode.id, !(display == "block"));
        writeOut("Parent Clicked MenuTreeStructure now: " + MenuTreeStructure);
        subMenu.style.display = (display == "block") ? "none" : "block";
        return true;
      };
    
    } catch (err)  {
      // Throwing an error 3: Catch the specific error using err.message
      switch (err.name) {
        case "MyError" :
         
          alert(msg);
          //throw err
          break;
        default:
          msg = "The Developer of this page did not think of this circumstance: \n";
          msg += "Details: Error Name - " + err.name + "; Error String - \n" + err.toString();
          msg += "\n You've done nothing wrong. If this doesn't mean anything you can ignore it.";
          alert(msg);        
          // Throwing an error 4: This ensures the unanticpated error is reported
          // to the Browser Error Reporter
          throw err;
      }
      return false;
    } finally {
      // Executes no matter what.
    }      
}

/*
Summary: Set the onclick event for a leaf node 
        which should be an anchor <a> 
          */
function initializeLeaf(anchor) {
  anchor.onclick = function () {
     setSelectedNodePersist(anchor.parentNode.id);
  } 
}

/*
  Summary: set the parent to expand Or collapse parent.

  Params:  
      actuator - a node (element object) serving as an actuator
      subMenu - a node (element object) serving as a submenu
      setToExpand - a boolean. true to expand; false to collapse.
  Example:      
      actuator = getChildElementsByTagName(nextLI, "a")[0];
      subMenu = getChildElementsByTagName(nextLI, "ul")[0];
      
      setParentExpandedORCollapsed(actuator, subMenu, false);
*/      
function setParentExpandedORCollapsed(actuator, subMenu, setToExpand) {
  if (setToExpand) {
    setParentExpanded(actuator, subMenu, true); 
  } else {
    setParentExpanded(actuator, subMenu, false);        
  } // if 
}

/*  
  Summary: Expands or contracts the submenu under the supplied parent.
  Remarks:
    The structure is like this:
    <li class="parent">
      <a href="javascript:%20void%20(0);" class="actuator">Applied</a>
      <!-- The ul below is the "subMenu" node -->
      <ul>
        <!-- the li node here is a mere "leaf" node -->
        <li><a href="javascript:%20void%20(0);">Discrimination</a></li>

  Params:  
      actuator - a node (element object) serving as an actuator
      subMenu - a node (element object) serving as a submenu
      setToExpand - a boolean. true to expand; false to collapse.
  Example:
      actuator = getChildElementsByTagName(nextLI, "a")[0];
      subMenu = getChildElementsByTagName(nextLI, "ul")[0];
      
      setParentExpanded(actuator, subMenu, false);
*/
function setParentExpanded(actuator, subMenu, setToExpand) {
  var htmlFolderLevel = getHtmlFolderLevel(); 
  actuator.parentNode.style.backgroundImage = (setToExpand) ? "url(" + htmlFolderLevel + "images/menuTree/expanded.gif)" : "url(" + htmlFolderLevel + "images/menuTree/collapsed.gif)";
  subMenu.style.display = (setToExpand) ? "block" : "none"; 
  setParentExpandedPersist(actuator.parentNode.id, setToExpand);   
}

// Summary: Stores information about whether the parent node
//          is expanded or collapsed.
// Remarks: 
//           Expanded = 1 = true
//           Collapsed = 0 = false.
// Params: nodeId "treeNodeXX"
//         setToExpand (boolean) true or false.
// Example:
function setParentExpandedPersist(nodeId, setToExpand){
  var newString = "";
  var nodeIndex = parseInt(nodeId.substr(8));
  
  if (nodeId.substr(0,8) != "treeNode") {
    throw new getErrorObj("setParentExpandedPersist: nodeId must"
      + " be in the form 'treeNode' + num but was: " + nodeId);
  }
  
  if (MenuTreeStructure.length < nodeIndex) {
    // Pad out string to accomodate node.
    newString = MenuTreeStructure;
    for (var i = MenuTreeStructure.length; i < nodeIndex; i++ ) {
      MenuTreeStructure += "0";
    }
    MenuTreeStructure += (+setToExpand);  // Type conversion to number. (fast technique)
  } else {
    newString = MenuTreeStructure.substring(0, nodeIndex);
    newString += (+setToExpand);  // Type conversion to number. (fast technique)
    newString += MenuTreeStructure.substring(nodeIndex + 1, MenuTreeStructure.length);
    MenuTreeStructure = newString;
  }
  
  // Menu Tree Structure is saved to cookie on the page's (window) unload.
  return true;
}

// Returns: True if the parent node is expanded; false if collapsed
// Param:    nodeId
//            A string eg "treeNode6"
// Remarks: 
//           Expanded = 1 = true
//           Collapsed = 0 = false.
function getParentExpandedPersist(nodeId) {
  if (nodeId.substr(0,8) != "treeNode") {
    throw new getErrorObj("getParentExpandedPersist: nodeId must"
      + " be in the form 'treeNode' + num but was: " + nodeId);
  }
  var nodeIndex = parseInt(nodeId.substr(8));
  var result = parseInt(MenuTreeStructure.charAt(nodeIndex));
  return Boolean(result);
}

// Summary: Stores which node is selected so that this may be 
//          identified when the next page loads.
// Param: A string in the form 'treeNodeX' where X is a number.
// Example: setSelectedNodePersist("treeNode3")
function setSelectedNodePersist(nodeId) {
  if (SelectionPersist && navigator.cookieEnabled) {
    if (nodeId.substr(0,8) != "treeNode") {
      throw new getErrorObj("setSelectedNodePersist: nodeId must"
        + " be in the form 'treeNode' + num but was: " + nodeId);
    }
    addClassToElement("selected", document.getElementById(nodeId));
    var selectedId = getSelectedNodePersist();
    if (selectedId && selectedId != nodeId) {
      removeClassFromElement("selected", document.getElementById(selectedId));
    } 
    SetCookie(MenuTreeSelectedNodeCookieString, nodeId, null, CookiePathString);
  }
}

/*
Returns: A string of the Id of the currently selected node (by Persistence)
Example: 'treeNode16' */
function getSelectedNodePersist() {
  return SelectionPersist && navigator.cookieEnabled ? GetCookie(MenuTreeSelectedNodeCookieString) : null;
}

/*
Summary: Sets the <li> node that has been selected on a previous page,
          via persistence, to the selected style.
Remarks: Depends on the CSS value
       #menuTree li.selected { anything you like } */
function displaySelectedNodePersist() {
  if (SelectionPersist) {
    var selectedId = getSelectedNodePersist();
    if (selectedId) {
      if (document.getElementById(selectedId).className.indexOf("selected") == -1) {
        addClassToElement("selected", document.getElementById(selectedId));
      }
      var menuTreeNode =  document.getElementById(MenuTreeIdString)
      var menuTreeLIs = menuTreeNode.getElementsByTagName("li");
      
      var nodeLoop;
      writeOut("selectedID : " + selectedId + "<br /> ");
      for (var i = 0; i < menuTreeLIs.length; i++ ) {
        nodeLoop = menuTreeLIs[i]
        //writeOut("nodeId : " + i + " " + nodeLoop.getElementsByTagName("a")[0].innerHTML + " Class: " + nodeLoop.className);
        if (nodeLoop.id != selectedId && nodeLoop.className.indexOf("selected") != -1) {
          removeClassFromElement("selected", nodeLoop);
        } 
      } // for
    } // if (SelectedId
  } // if (SelectionPersist
}

// Param: on is a boolean. true for on; false for off.
function setColorPersist(on) {
  SetCookie(MenuTreeIdString + "_ColorGuides", on, null, CookiePathString);
}

function isColorOnPersist() {
  switch (GetCookie(MenuTreeIdString + "_ColorGuides")){
    case "true":
      return true;
      break;
    case "false":
      return false;
      break;
    case null:
      return null;
      break;
    default: 
      throw new getErrorObj("isColorOnPersist(): Unexpected Case 'ColorGuides' switch. cookie was " + GetCookie(MenuTreeIdString + "_ColorGuides"));
  }
}

/*
Summary: Displays or hides the tree menu color guides depending on the
          persisted value 
Remarks: If the user has set the ColorGuides cookie then use that.
         otherwise depend on the Web Developers setting of ColorGuidesDefaultOn*/
function displayColorGuides() {
  var menuTreeNode = document.getElementById(MenuTreeIdString)
  switch (isColorOnPersist()){
    case true:
      addClassToElement("colorGuides", menuTreeNode);
      break;
    case false:
      removeClassFromElement("colorGuides", menuTreeNode);
      break;
    case null:
      if (ColorGuidesDefaultOn) {
        addClassToElement("colorGuides", menuTreeNode);
      } else {
        removeClassFromElement("colorGuides", menuTreeNode);
      }
      break;
    default: 
      throw new getErrorObj("displayColorGuides(): Unexpected Case in  'ColorGuides' switch. cookie was " + GetCookie("ColorGuides"));
  }  
} 
                   
/*
Summary: Turns the menu color guides off or on */
function guidesToggle() {
  var menuTreeNode = document.getElementById(MenuTreeIdString)
  // if already colored then...
  if (menuTreeNode.className.indexOf("colorGuides") != -1) {
    removeClassFromElement("colorGuides", menuTreeNode);
    setColorPersist(false);
  } else {
    addClassToElement("colorGuides", menuTreeNode);
    setColorPersist(true);
  }
}

/*
Summary: Synchronizes the current web page with the Tree menu.
Remarks: Selects and opens the tree to the leaf node that corresponds to 
         the current page. */
function synchMenuTreeToPage() {
  //Find web page among leaf nodes.
  var anchors = MenuTree.getElementsByTagName("a");
  var currentPagePath = stripHashOffEndOfNonZeroString(document.location.pathname);
  writeOut("currentPagePath " +currentPagePath);
  var nodeToSynch;
  var loopAnchorPath = "";
  var isHashAtEnd;

  for (var i = 0; i < anchors.length; i++) {
      loopAnchorPath = getSafePath(anchors[i].pathname);
      isHashAtEnd = isCharAtEndOfString(anchors[i].href, "#");
      if (!isHashAtEnd && loopAnchorPath  == currentPagePath) {
        writeOut("loopAnchorPath " + loopAnchorPath);
        nodeToSynch = anchors[i];
        break;
      }
      // For when the loading page is loaded from something like www.mydomain.com
      // That is, without a page name specified.
      var loopAnchorFileName = getPageFileName(loopAnchorPath);
      var loopAnchorBaseName = getBaseName(loopAnchorFileName);
      if (loopAnchorBaseName.toLowerCase() == "index" || loopAnchorBaseName.toLowerCase() == "default" ) {
        if (((currentPagePath + loopAnchorFileName) == loopAnchorPath)) {
          nodeToSynch = anchors[i];
          break;
        }
      }
  }  
  /*var anchors = MenuTree.getElementsByTagName("a");
  var currentPageFileName = stripHashOffEndOfNonZeroString(getPageFileName(document.location.href));
  var nodeToSynch;
  var loopAnchorPageFileName = "";
  var isHashAtEnd;
  for (var i = 0; i < anchors.length; i++) {
      loopAnchorPageFileName = getPageFileName(anchors[i].href);
      isHashAtEnd = isCharAtEndOfString(loopAnchorPageFileName, "#");
      if (!isHashAtEnd && loopAnchorPageFileName  == currentPageFileName) {
        nodeToSynch = anchors[i];
        break;
      }
  }*/
  
  if (nodeToSynch) { 
    // select leaf node.
    setSelectedNodePersist(nodeToSynch.parentNode.id);
    
    // expand all menus above leaf node.
    var parentLI 
    if (nodeToSynch.parentNode.id == MenuTreeIdString || nodeToSynch.parentNode.parentNode.id == MenuTreeIdString) {
      parentLI = null;
    } else {
      parentLI = nodeToSynch.parentNode.parentNode.parentNode;
    }
    
    while (parentLI != null) {
      actuator = getChildElementsByTagName(parentLI, "a")[0];
      subMenu = getChildElementsByTagName(parentLI, "ul")[0];  
      setParentExpandedORCollapsed(actuator, subMenu, true); 
      
      if (parentLI.parentNode.id == MenuTreeIdString) {
        parentLI = null;
      } else {
        parentLI = parentLI.parentNode.parentNode;
      }
    }
    
    return true;
  } else {
    return false;
  }
}         

/*
    Utility  
*/

/*
Returns: The Page reference in a url. */
function getPageFileName(href) {
  return href.substr(href.lastIndexOf("/") + 1);
}

/* 
Returns: The Base name from the fileName supplied
Example: getBaseName("index.html") -> "index" */
function getBaseName(fileName) {
  var re = /\w+/;
  // Perform matching
  
  if (fileName.match(re)) {
    var matchArray = re.exec(fileName);
    return matchArray[0];
  } else {
    return "";
  }
}

/*
Returns: True if the last character of the supplied str is a match
         with the supplied chr. */
function isCharAtEndOfString(str, chr) {
  return (str.lastIndexOf(chr) == str.length - 1) ; 
}

// Strip hash at the end of page names. eg take '#' off 'myPage.html#'
function stripHashOffEndOfNonZeroString(str) {
  
  if (str.length > 1 && str.lastIndexOf("#") == str.length - 1) {
    str = str.substr(0, str.length - 1);
  }
  
  return str;
}

/*
Returns: A path with a forward slash, "/", prefixed to
        the string if it doesn't already have one.
Remarks: Used to handle IE anchor.pathname which ommits this front forward slash.
        if pathString is undefined, null, or zero length,
        or already has a front string then pathString
        is returned unaltered. 
Example:  "Now/This/Is" -> "/Now/This/Is"
          "/Cool/Times/Ahead" -> "/Cool/Times/Ahead"         */
function getSafePath(pathString) {
  var re = /\w/;  // Letter, numeral, or underscore.
  
  pathString = "%20";
  if (pathString == null || pathString == "undefined" || pathString == "") {
    return pathString;
  } else if (pathString.charAt(0) == "/") {
    return pathString;
  } else if (re.test(pathString.charAt(0))) {
    return "/" + pathString;
  } else {
    // Do nothing. Return Path.
//     throw new getErrorObj("Unexpected Else reached in getSafePath(), a custom function, path Error: " + pathString);
  }

  return pathString;
} 

function trim(str, chars) {
	return ltrim(rtrim(str, chars), chars);
}

function ltrim(str, chars) {
	chars = chars || "\\s";
	return str.replace(new RegExp("^[" + chars + "]+", "g"), "");
}

function rtrim(str, chars) {
	chars = chars || "\\s";
	return str.replace(new RegExp("[" + chars + "]+$", "g"), "");
}

/* 
Returns: True if all stylesheets disabled; false if any stylesheet 
        enabled */
function stylesheetsDisabled() {
  var result = false;
  
  for (var i = 0; i < document.styleSheets.length; i++) {
    if (document.styleSheets[i].disabled) result = true;
  }
  
  return result;
}

/*
Summary:  Adds the supplied class to the element.
Remarks:  We are careful to add a class when the element already has 
          multiple classes.
          We are also careful NOT to add the same class multiple times 
Example:  addClassToElement("coolness", ele)          */
function addClassToElement(classString, element) {
  // If classString is not already in class then add it.
  if (element.className.indexOf(classString) == -1) {
    element.className = element.className + " " + classString;
  }
}

/*
Summary: Removes the class name from the element
Remarks:  We are careful to remove a class when the element already has 
          multiple classes */
function removeClassFromElement(classString, element) {
  var re = new RegExp(classString + "\s*");
  element.className = element.className.replace(re,"");
}          

function nullToZeroLengthString(value) {
  if (value == null) {
    return "";
  } else {
    return value;
  }
}
// Summary: Returns an array of "tag" elements that are immediate children
//          of node.
function getChildElementsByTagName(node, tag) {
  var loopNode;
  var result = new Array();
  var j = 0;
  for (var i = 0; i < node.childNodes.length; i++) {
    if (node.childNodes[i].tagName == tag.toUpperCase()) {
      result[j] = node.childNodes[i];
      j++;
    }
  }
  
  return result;
}

function blankStatus() {
  window.status = "";
  return true;
}

/*
Summary: Use this to throw errors.
Example: 
  throw new getErrorObj(msg); */
function getErrorObj(message) {
  var err = new Error(message);
  err.name = "MyError";
  return err;
}

/*
Summary:  adds a function to the onload event without overwriting
          previous functions assignments to onload.
Usage Examples: 
          addLoadEvent(nameOfSomeFunctionToRunOnPageLoad);
          // Or
          addLoadEvent(function() {
            // more code to run on page load 
          });
Source:  Simon Willson > Executing JavaScript on page load  
        http://simon.incutio.com/archive/2004/05/26/addLoadEvent */
function addLoadEvent(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      oldonload();
      func();
    }
  }
}

/*
Returns: If the client broswer if Safari.
Source: browserEnvironment.js
          Goodman 2004 > JavaScript Bible 5th Edition
          Danny Goodman, Mar 2004. > Bonus Chapter Page 71 */
function isSafari() {
  return (navigator.userAgent.indexOf("Safari") != -1);
}

/*
Returns: If the client broswer if Firefox.
Source: browserEnvironment.js */
function isFirefox() {
  return (navigator.userAgent.indexOf("Firefox") != -1);
}

/*
  Debug
*/

function writeOut(message) {
  if (Debug && document.getElementById("debug")) {
    // Output message to the top
    document.getElementById("debug").innerHTML = message + "<br />" + document.getElementById("debug").innerHTML;
  }
}

function writeClear() {
  if (Debug && document.getElementById("debug")) {
    document.getElementById("debug").innerHTML = "";
  }
}



