//=================================================================
// JavaScript Tree
// Version 1.0
// by Nicholas C. Zakas, nicholas@nczonline.net
// Copyright (c) 2002 Nicholas C. Zakas.  All Rights Reserved.
//-----------------------------------------------------------------
// Browsers Supported:
//  * Netscape 6.1+
//  * Internet Explorer 5.0+
//=================================================================
// History
//-----------------------------------------------------------------
// January 27, 2002 (Version 1.0)
//  - Works in Netscape 6.1+ and IE 5.0+  
//=================================================================
// Software License
// Copyright (c) 2002 Nicholas C. Zakas.  All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer. 
//
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materials provided with the
//    distribution.
//
// 3. The end-user documentation included with the redistribution,
//    if any, must include the following acknowledgment:  
//       "This product includes software developed by the
//        Nicholas C. Zakas (http://www.nczonline.net/)."
//    Alternately, this acknowledgment may appear in the software itself,
//    if and wherever such third-party acknowledgments normally appear.
//
// 4. Redistributions of any form are free for use in non-commercial
//    ventures. If intent is to use in a commercial product, contact
//    Nicholas C. Zakas at nicholas@nczonline.net for purchasing of
//    a commercial license.
//
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED.  IN NO EVENT SHALL NICHOLAS C. ZAKAS  BE LIABLE FOR ANY 
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------
// Any questions, comments, or suggestions should be e-mailed to 
// nicholas@nczonline.net.  For more information, please visit
// http://www.nczonline.net/. 
//=================================================================

var kImagesDir = ""
var kImagePlus = kImagesDir + "arrow_r.gif"
var kImageMinus = kImagesDir + "arrow_d.gif"
var kImagePlusOver = kImagesDir + "arrow_r_o.gif"
var kImageMinusOver = kImagesDir + "arrow_d_o.gif"

var kDefaltTarget = null;

var imgPlus = new Image()
imgPlus.src = kImagePlus

var imgMinus = new Image()
imgMinus.src = kImageMinus

var imgPlusOver = new Image()
imgPlusOver.src = kImagePlusOver

var imgMinusOver = new Image()
imgMinusOver.src = kImageMinusOver

//var objLocalTree = null;

var INDENT_WIDTH = 10;

//-----------------------------------------------------------------
// Class jsTree
//-----------------------------------------------------------------
// Author(s)
//  Nicholas C. Zakas (NCZ), 1/27/02
//
// Description
//  The jsTreeNode class encapsulates the functionality of a tree.
//
// Parameters
//  (none)
//-----------------------------------------------------------------
function jsTree(index)
{
  //Public Properties (NCZ, 1/27/02)
  this.root = null;             // The root node of the tree
  this.index = index;

   //Public Collections (NCZ, 1/27/02)
  this.nodes = new Array;       // Array for all nodes in the tree
  this.selectedObject = null;   // The currently selected node
}

//-----------------------------------------------------------------
// Method jsTree.createRoot()
//-----------------------------------------------------------------
// Author(s)
//  Nicholas C. Zakas (NCZ), 1/27/02
//
// Description
//  This method creates the root of the tree.
//
// Parameters
//  strIcon (string) - the icon to display for the root.
//  label (string) - the text to display for the root.
//  URL (string) - the URL to navigate to when the root is clicked.
//  strTarget (string) - the target for the URL (optional).
//
// Returns
//  The jsTreeNode that was created.
//-----------------------------------------------------------------
jsTree.prototype.createRoot = function(strIcon, id, label, URL, strTarget)
{
  // Create a new node (NCZ, 1/27/02)
  this.root = new jsTreeNode(strIcon, label, URL, strTarget, this, null);
  
  //assign an ID for internal tracking (NCZ, 1/27/02)
  this.root.id = id;
  
  //add it into the array of all nodes (NCZ, 1/27/02)
  this.nodes[this.root.id] = this.root;
  
  //make sure that the root is expanded (NCZ, 1/27/02)
  this.root.expanded = true;
  
  //return the created node (NCZ, 1/27/02)
  return this.root;
}

//-----------------------------------------------------------------
// Method jsTree.buildDOM()
//-----------------------------------------------------------------
// Author(s)
//  Nicholas C. Zakas (NCZ), 1/27/02
//
// Description
//  This method creates the HTML for the tree.
//
// Parameters
//  (none)
//
// Returns
//  (nothing)
//-----------------------------------------------------------------
jsTree.prototype.buildDOM = function(id, objName)
{
  // Call method to add root to document, which will recursively
  //add all other nodes (NCZ, 1/27/02)
  // Allow the user to pass in where to put the menu.
  objDOMParent = (id == null) ? document.body : document.getElementById (id)
  this.root.addToDOM(objDOMParent, objName);
  //objDOMParent = objDOMParent.appendChild(CreateElement('<div>'))
  //objDOMParent.appendChild(CreateElement('<img src="TransparentBox.png" width="250">'))
}

function SetClassByElementName (id, style)
  {
  var elements = document.getElementsByName (id)
  var index
  
  // for (var element in elements) This does not work in IE
  for (index = 0; index < elements.length; index++)
  {
    elements[index].className = style
  }
}

//-----------------------------------------------------------------
// Method jsTree.OnMouseOut()
//-----------------------------------------------------------------
// Author(s)
//  jsmith
//
// Description
//  On mouse out change the style
//
// Parameters
//  strNodeID (string) - The ID of the node that is being expanded/collapsed.
//
// Returns
//  (nothing)
//-----------------------------------------------------------------
jsTree.prototype.OnMouseOut = function(strNodeID)
{
  //get the node (NCZ, 1/27/02)
  var objNode = this.nodes[strNodeID]
  var selectedObject = objNode.root.selectedObject
  var imgPM = document.getElementById ("imgPM_"+strNodeID)
  
  // If this is not the currently selected item change the class back
  if (selectedObject != objNode)
  { 
    SetClassByElementName (strNodeID, "treeIndent"+objNode.indent)
    if (imgPM) imgPM.src = imgPlus.src
    if (imgPM) imgPM.src = (objNode.expanded) ? imgMinus.src : imgPlus.src
  }
}

//-----------------------------------------------------------------
// Method jsTree.OnMouseOver()
//-----------------------------------------------------------------
// Author(s)
//  jsmith
//
// Description
//  On mouse over change the style
//
// Parameters
//  strNodeID (string) - The ID of the node that is being expanded/collapsed.
//
// Returns
//  (nothing)
//-----------------------------------------------------------------
jsTree.prototype.OnMouseOver = function(strNodeID)
{
  //get the node (NCZ, 1/27/02)
  var objNode = this.nodes[strNodeID]
  var selectedObject = objNode.root.selectedObject
  var imgPM = document.getElementById ("imgPM_"+strNodeID)
  
  // If this is not the currently selected item change the class
  if (selectedObject != objNode)
  { 
    SetClassByElementName (strNodeID, "treeIndent"+objNode.indent+"Selected")
    if (imgPM) imgPM.src = (objNode.expanded) ? imgMinusOver.src : imgPlusOver.src
  }
}

//-----------------------------------------------------------------
// Method jsTree.selectNode()
//-----------------------------------------------------------------
// Author(s)
//  jsmith
//
// Description
//  Saves a pointer to the current node and sets the content
//
// Parameters
//  strNodeID (string) - the ID of the node that is being expanded/collapsed.
//
// Returns
//  (nothing)
//-----------------------------------------------------------------
jsTree.prototype.selectNode = function(strNodeID)
{
  //get the node (NCZ, 1/27/02)
  var objNode = this.nodes[strNodeID];
  var selectedObject = objNode.root.selectedObject;
  
  // Reset the class of the previously selected node
  if (selectedObject != null)
  { 
    // If we changed the main menu then the old row will be gone
    SetClassByElementName (selectedObject.id, "treeIndent" + selectedObject.indent)
  }
  // Set class of the currently selected node
  SetClassByElementName (objNode.id, "treeIndent"+objNode.indent+"Selected")
  objNode.root.selectedObject = objNode; 
  
  var sURL = objNode.url;

  if (sURL.substring(2, 0) != "u_" &&
      searchParams &&
      searchParams.content)
  {
    sURL = searchParams.content;
  }
  SetContent (sURL);
}

//-----------------------------------------------------------------
// Method jsTree.toggleExpand()
//-----------------------------------------------------------------
// Author(s)
//  Nicholas C. Zakas (NCZ), 1/27/02
//
// Description
//  This toggles the expansion of a node identified by an ID.
//
// Parameters
//  strNodeID (string) - The ID of the node that is being expanded/collapsed.
//
// Returns
//  (nothing)
//-----------------------------------------------------------------
jsTree.prototype.toggleExpand = function(strNodeID)
{
  //get the node (NCZ, 1/27/02)
  var objNode = this.nodes[strNodeID];
  
  //determine whether to expand or collapse
  if (objNode.expanded)
      objNode.collapse();
  else
      objNode.expand();
}

//-----------------------------------------------------------------
// Class jsTreeNode
//-----------------------------------------------------------------
// Author(s)
//  Nicholas C. Zakas (NCZ), 1/27/02
//
// Description
//  The jsTreeNode class encapsulates the basic information for a node
//  in the tree.
//
// Parameters
//  strIcon (string) - the icon to display for this node.
//  label (string) - the text to display for this node.
//  URL (string) - the URL to navigate to when this node is clicked.
//  strTarget (string) - the target for the URL (optional).
//-----------------------------------------------------------------
function jsTreeNode(strIcon, label, URL, strTarget, root, condition, parent)
{
  //Public Properties (NCZ, 1/27/02)
  this.icon = strIcon;            // The icon to display
  this.label = label;             // The text to display
  this.url = URL;              // The URL to link to
  // The target for the URL
  this.target = (strTarget && strTarget != "") ? strTarget : kDefaltTarget;
  
  //Private Properties (NCZ, 1/27/02)
  this.indent = -1;               // The indent for the node
  
  //Public States (NCZ, 1/27/02)
  this.expanded = false;          //is this node expanded?

  //Public Collections (NCZ, 1/27/02)   
  this.childNodes = new Array;    // The collection of child nodes
  this.condition = condition;     // The condition on which to show this node.
  this.root=root;                 // A pointer to the root node.
  this.parent=parent;             // The parent of this node.
}

//-----------------------------------------------------------------
// Method jsTreeNode.addChild()
//-----------------------------------------------------------------
// Author(s)
//  Nicholas C. Zakas (NCZ), 1/27/02
//
// Description
//  This method adds a child node to the current node.
//
// Parameters
//  strIcon (string) - the icon to display for this node.
//  label (string) - the text to display for this node.
//  URL (string) - the URL to navigate to when this node is clicked.
//  strTarget (string) - the target for the URL (optional).
//
// Returns
//  The jsTreeNode that was created.
//-----------------------------------------------------------------
jsTreeNode.prototype.addChild = function (id, strIcon, label,
  URL, strTarget, condition)
{
    // Create a new node (NCZ, 1/27/02)
  var objNode = new jsTreeNode(strIcon, label, URL, strTarget, 
    this.root, condition, this);
  
  //assign an ID for internal tracking (NCZ, 1/27/02)
  objNode.id = this.id + "_" + id;
  
  //assign the indent for this node
  objNode.indent = this.indent + 1;
  
  //add into the array of child nodes (NCZ, 1/27/02)
  this.childNodes[this.childNodes.length] = objNode;
  
  //add it into the array of all nodes (NCZ, 1/27/02)
  this.root.nodes[objNode.id] = objNode;
  
  //return the created node (NCZ, 1/27/02)
  return objNode;
}

//-----------------------------------------------------------------
// Method jsTreeNode.addToDOM()
//-----------------------------------------------------------------
// Author(s)
//  Nicholas C. Zakas (NCZ), 1/27/02
//
// Description
//  This method adds DOM elements to a parent DOM element.
//
// Parameters
//  objDOMParent (HTMLElement) - the parent DOM element to add to.
//
// Returns
//  (nothing)
//-----------------------------------------------------------------
jsTreeNode.prototype.addToDOM = function (objDOMParent, objName)
{
  if (this.condition == null || this.condition == "" || eval(this.condition))
  {
    // Dont show the root
    if (this.indent >= 0)
    {
      var HTMLLink;

      // Create the layer for the node (NCZ, 1/27/02)
      var objNodeDiv = CreateElement('<div class="sideNavigation'+this.indent+'">');
    
      //add it to the DOM parent element (NCZ, 1/27/02)
      objDOMParent.appendChild(objNodeDiv);
    
      // Create string buffer (NCZ, 1/27/02)
      var d = new jsDocument;
    
      //begin the table (NCZ, 1/27/02)
      d.writeln('<table class="sideNavigation">')
      if (this.IsSeperator ())
      {
        d.writeln('<tr><td class="treeSeperator"></td></tr>')
      }
      else
      {
        var style = "treeIndent" + this.indent
        var tbImageName = this.id + "tbimage"
        HTMLLink = (this.childNodes.length > 0) ? (objName+".toggleExpand('"+this.id+"');") :
          (this.url.match (/^MM_/)) ? this.url :
            (objName+".selectNode('" + this.id + "');")
        
        d.writeln('<tr class="tree" onmouseout="'+objName+
          '.OnMouseOut(\''+this.id+'\')" onmouseover="'+objName+'.OnMouseOver(\''+
          this.id+'\')" onClick="'+HTMLLink+'">')
    
      //no indent needed for level under root (NCZ, 1/27/02)
        var indentIndex;
        for (indentIndex = 0; indentIndex < this.indent; indentIndex++)
        {
          d.writeln('<td id="'+this.id+'" name="'+this.id+'" class="'+style+'" width="10px">')

            d.write('<img id="'+tbImageName+'" name="'+tbImageName+
              '" src="TransparentBox.png" border="0" hspace="1" />')
          d.writeln('</td>')
        }
        d.write('<td id="'+this.id+'" name="'+this.id+'" class="'+style+'" width="10px">')
        //if there are children, then add a plus/minus image (NCZ, 1/27/02)
          if (this.childNodes.length > 0)
          {
            d.write('<img src="'+(this.expanded ? imgMinus.src : imgPlus.src)+'"'
             + ' border="0" hspace="1" id="imgPM_' + this.id + '" />')
          }
          else
          {
            d.write('<img id="'+tbImageName+this.childNodes.length+'" name="'+
              tbImageName+this.childNodes.length+'" src="TransparentBox.png" border="0" hspace="1" />')
          }
        d.write("</td>")
    
      //finish by drawing the icon and text (NCZ, 1/27/02)
      if (this.icon != "")
      {
          d.write('<td id="'+this.id+'" name="'+this.id+'" class="'+style+'">')
            d.write("<img hspace=\"1\" src=\"" + this.icon + "\" border=\"0\" align=\"absmiddle\" />")
          d.write("</td>")
      }
      else
      {
          d.write('<td id="'+this.id+'" name="'+this.id+'" class="'+
            style+'" nowrap>' + this.label + '</td>')
      }
        d.writeln("</tr>")
        d.writeln("</table>")
      }
        
      //assign the HTML to the layer (NCZ, 1/27/02)
      objNodeDiv.innerHTML = d
    }
    // Create the layer for the children (NCZ, 1/27/02)
    if (this.childNodes.length > 0)
    {
    var style = (this.indent < 0) ? "sideNavigation0" : ""
    var objChildNodesLayer = CreateElement ('<div class="'+style+'">')
    objChildNodesLayer.setAttribute("id", "divChildren_" + this.id);
    //objChildNodesLayer.style.position = "relative";
    objChildNodesLayer.style.display = (this.expanded ? "block" : "none");
    if (this.indent >= 0)
    {
       objNodeDiv.appendChild(objChildNodesLayer);
    }
    else
    {
       objDOMParent.appendChild(objChildNodesLayer);
    }
    // Call for all children (NCZ, 1/27/02)
    for (var i=0; i < this.childNodes.length; i++)
        this.childNodes[i].addToDOM(objChildNodesLayer, objName);
  }
}
}

//-----------------------------------------------------------------
// Method jsTreeNode.IsSeperator()
//-----------------------------------------------------------------
// Author(s)
//  Nicholas C. Zakas (NCZ), 1/27/02
//
// Description
//  Return true if this system is just a Seperator (it has no children
// and no action/url.
//
// Parameters
//  (none)
//
// Returns
//  true or false.
//-----------------------------------------------------------------
jsTreeNode.prototype.IsSeperator = function ()
{
  return (this.childNodes.length == 0 && (this.url == null || this.url == ""))
}

//-----------------------------------------------------------------
// Method jsTreeNode.collapse()
//-----------------------------------------------------------------
// Author(s)
//  Nicholas C. Zakas (NCZ), 1/27/02
//
// Description
//  This method expands the jsTreeNode's children to be hidden.
//
// Parameters
//  (none)
//
// Returns
//  (nothing)
//-----------------------------------------------------------------
jsTreeNode.prototype.collapse = function ()
{
  // Check to see if the node is already collapsed (NCZ, 1/27/02)
  if (!this.expanded)
  {
    // Throw an error (NCZ, 1/27/02)
    throw "Node is already collapsed"
  }
  else
  {
    // Change the state of the node (NCZ, 1/27/02)
    this.expanded = false;
    
    // Change the plus/minus image to be plus (NCZ, 1/27/02)
    document.images["imgPM_" + this.id].src = imgPlusOver.src;
    
    // Hide the child nodes (NCZ, 1/27/02)
    document.getElementById("divChildren_" + this.id).style.display = "none";
  }
}

//-----------------------------------------------------------------
// Method jsTreeNode.expand()
//-----------------------------------------------------------------
// Author(s)
//  Nicholas C. Zakas (NCZ), 1/27/02
//
// Description
//  This method expands the jsTreeNode's children to be displayed.
//
// Parameters
//  (none)
//
// Returns
//  (nothing)
//-----------------------------------------------------------------
jsTreeNode.prototype.expand = function ()
{
  // Check to see if the node is already expanded (NCZ, 1/27/02)
  if (this.expanded)
  {
    // Throw an error (NCZ, 1/27/02)
    throw "Node is already expanded"
  }
  else
  {
    // Change the state of the node (NCZ, 1/27/02)
    this.expanded = true;
    
    // Change the plus/minus image to be minus (NCZ, 1/27/02)
    document.images["imgPM_" + this.id].src = imgMinusOver.src;
    
    // Show the child nodes (NCZ, 1/27/02)
    document.getElementById("divChildren_" + this.id).style.display = "block";
  }
}

//-----------------------------------------------------------------
// Method jsTree.findNode(url)
//-----------------------------------------------------------------
// Author(s)
//  jsmith
//
// Description
//   Find a node in the tree using the URL
//
// Parameters
//  url (string) - The url of the node.
//
// Returns
//  The node
//-----------------------------------------------------------------

jsTree.prototype.FindNodeId = function (url)
{
  for (var node in this.nodes)
  {
    if (this.nodes[node] != this.root && this.nodes[node].url == url)
      return this.nodes[node].id
  }
  return null
}
