/******************************************************************************************************************
Filename = wemenubar.js

Copyright, Woodland Engineering, 2008
------------------------------------------------------------------------------------------------------------------
Description:

This file contains JavaScript code for implementing a menu bar consisting of either a horizontal or vertical
table. See wemenubar.txt for a description of the structure.

Menus are specified in this file as though there were a single object, WeMenuBar that contains all properties
and methods to implement a menuing system. For documentation, please request by email from (pardon format, but
it's to avoid bots from picking it up).

          wdlndengrg // AT // embarqmail // DOT COM //

Please identify yourself and tell us why you want it.

------------------------------------------------------------------------------------------------------------------
Rev history:

Revised 18-Nov-2008 Ken Curtis
  Original release.

******************************************************************************************************************/

// Declare a single object that will be the namespace for everything else
var WeMenuBar =
{

// Initialize some constant values
menuType:                 0,    // Menu type: 0 = table, 1 = unordered list
majorTagName:             [ "TABLE", "UL"], // Menu type tag name string
elementTagName:           [ "TD", "LI" ], // Component's tag name string relative to the majorTagName
menuSidePrefixStr:        ["hzm_", "lfm_", "rtm_"], // Specifies which side of screen menu is located.
HORIZ_SIDE:               0,    // Name associated with horizontal menu table - menu folds down.
VERT_LEFT_SIDE:           1,    // Name associated with vertical menu table on left side of screen - menu folds out to right.
VERT_RIGHT_SIDE:          2,    // Name associated with vertical menu table on right side of screen - menu folds out to left.
FOLDOUT_DOWN:             0,    // Name associated with menu foldout in down direction.
FOLDOUT_TO_RIGHT:         1,    // Name associated with menu foldout to right direction.
FOLDOUT_TO_LEFT:          2,    // Name associated with menu foldout to left direction.
WATCHDOG_TIME_VALUE:      250,  // Number of milliseconds after mouseout until user abandonment declared.
CELL_IDLE_ST:             0,    // Menubar cell state, mouse not over cell or child.
CELL_ACTIVE_ST:           1,    // Menubar cell state, mouse over cell.
CELL_CHILD_ACTIVE_ST:     2,    // Menubar cell state, mouse over a child or descendent.
MENU_INACTIVE_ST:         0,    // Menu state, waiting to be deleted.
MENU_IDLE_ST:             1,    // Menu state, no menu item active.
MENU_ITEM_ACTIVE_ST:      2,    // Menu state, a menu item is active.
MNUITEM_INACTIVE_ST:      0,    // Menu item state, waiting to be deleted.
MNUITEM_IDLE_ST:          1,    // Menu item state, mouse not over item or child.
MNUITEM_ACTIVE_ST:        2,    // Menu item state, mouse over item.
MNUITEM_CHILD_ACTIVE_ST:  3,    // Menu item state, mouse over a child or descendent.
ESCAPE_KEY_CODE:          27,   // The key code for an escape key
FIRST_MENU_ZINDEX:        255,  // The z-index of the first level menu to be displayed.
linkPrefix:               "../" // A prefix put in front of links in the menu arrays. If the path to the link reference
                                // require a different prefix, as it often does for the home page, this value
                                // can be overridden by putting the proper prefix between the script tags associated
                                // with this source file. For example: WeMenuBar.linkPrefix = ""

}; // endof var WeMenuBar


/*****************************************************************************************************************
This function computes the desired location where the argument object should be located on the screen and places
the dimensions in the object's style.top and style.left positions.

Only horizontal menu bar cells should fold down. If a menu folds down, the menu should be centered immediately
below the parent menu bar cell if it is possible. Sub-menus that originate from a horizontal menu bar should
fold out to the right.

Menus fold out to the right from a vertical menu bar on the left side of a page. Sub-menus that originate from
a vertical menu bar on the left side should also fold out to the right.

Menus fold out to the left from a vertical menu bar on the right side of a page. Sub-menus that originate from
a vertical menu bar on the right side should also fold out to the left.

Menus that fold out to the left or right sides align the top of the menu with the top of the parent element.

*****************************************************************************************************************/
WeMenuBar.SetMenuLocation = function
  (
  obj, // Object needing top and left screen locations
  parRect, // Rectangle enclosing parent
  foldoutDir // Which direction the menu should foldout
  )
{
var objRect; // Rectangle enclosing the obj.
var objWidth; // The width of objRect.
var objHeight; // The heigh of objRect.
var parMidX; // Horizontal midpoint location of parent.

// Get rectangle for object where ever it is on the screen. The menu dimensions are needed from this information.
objRect = WeMenuBar.GetElementRect(obj);
objWidth = 1 + objRect.right - objRect.left;
objHeight = 1 + objRect.bottom - objRect.top;

// Check if menu folds out down
if (foldoutDir === WeMenuBar.FOLDOUT_DOWN)
  {
  // Determine new x location
  parMidX = (parRect.left + parRect.right) / 2;
  objRect.left = parMidX - (objWidth / 2);

  // Determine new y location
  objRect.top = parRect.bottom; // Leaves a 1 pixel gap
  }

else if (foldoutDir === WeMenuBar.FOLDOUT_TO_RIGHT)
  {
  // Determine new x location
  objRect.left = parRect.right;

  // Determine new y location
  objRect.top = parRect.top;
  }

else // foldoutdir === WeMenuBar.FOLDOUT_TO_LEFT
  {
  // Determine new x location
  objRect.left = parRect.left - objWidth - 1;

  // Determine new y location
  objRect.top = parRect.top;
  }


obj.style.top = objRect.top + "px";
obj.style.left = objRect.left + "px";

} // end WeMenuBar.SetMenuLocation()


/*****************************************************************************************************************
This function returns a rectangle whose dimensions are the size of the screen where the body element is displayed.
*****************************************************************************************************************/
WeMenuBar.GetBodyRect = function()
{

var bodyRect = { left: 0, top: 0 }; // Declare an object and initialize left and top sides.

// Set the right and bottom sides
bodyRect.right = innerWidth;      // Note vertical scroll bars fall within this width
bodyRect.bottom = innerHeight;    // Note horizontal scroll bars fall within this width

return bodyRect;

}; // end function()


/*****************************************************************************************************************
This function returns a rectangle object having the pixel locations of all 4 sides of the rectangle enclosing
an element in DOM
*****************************************************************************************************************/
WeMenuBar.GetElementRect = function
  (
  element // DOM element
  )
{

// Declare a rectangle
var rect = {left: 0, top: 0 };

// Temporarily save width and heighth in rectangle
rect.right = element.offsetWidth;
rect.bottom = element.offsetHeight;

while (element !== null && element.tagName !== "HTML")
  {

  // Add the offsets to left and top sides
  rect.left += element.offsetLeft;
  rect.top += element.offsetTop;

  // Go to the next higher node in the DOM
  element = element.offsetParent;
  }

// Now correct the location of right and bottom sides
rect.right += rect.left;
rect.bottom += rect.top;

return rect;

}; // end WeMenuBar.GetElementRect()


/*****************************************************************************************************************
This utility function returns true if the argument is an array; otherwise, false.
*****************************************************************************************************************/
WeMenuBar.IsAnArray = function (a)
{

// Define a variable and assign it true if the argument is not an array.
var notArray = typeof a !== "object" || typeof a.length !== "number" ||
               typeof a.splice !== "function" || a.propertyIsEnumerable("length");

return !notArray;

}; // end WeMenuBar.IsAnArray()


/*****************************************************************************************************************
This function is called by menu bar cell objects when they receive a mouseover notification from the system or
one of the descendent sub-menus.
*****************************************************************************************************************/
WeMenuBar.NotifyOfMouseover = function
  (
  index // Index of the menu bar cell making the call
  )
{

// Check for valid index number and if the caller's index is not the currently active cell object
if (index <= WeMenuBar.menuBarCellObj.length && index !== WeMenuBar.currActiveIndex)
  {

  // Check if no cell is currently active
  if (WeMenuBar.currActiveIndex !== -1)
    // Deactivate the currently active cell
    WeMenuBar.menuBarCellObj[WeMenuBar.currActiveIndex].Deactivate();

  // Note which cell is active
  WeMenuBar.currActiveIndex = index;

  } // endif index <= WeMenuBar.menuBarCellObj.length && index !== WeMenuBar.currActiveIndex

}; // end WeMenuBar.NotifyOfMouseover()


/*****************************************************************************************************************
This function is called by menu bar cell objects when they detect the user is no longer interested in the menu
bar cell or its descendent sub-menus.
*****************************************************************************************************************/
WeMenuBar.NotifyOfCellInactive = function
  (
  index // Index of the menu bar cell making the call
  )
{

// Check for valid index number and if the caller's index is the currently active cell object
if (index <= WeMenuBar.menuBarCellObj.length && index === WeMenuBar.currActiveIndex)
  WeMenuBar.currActiveIndex = -1;

}; // end WeMenuBar.NotifyOfCellInactive()


/*****************************************************************************************************************
This function is called by sub-menus when the user abandons the menuing system. It creates an event that is
sent to the active cell in the menu bar. The event is a keydown event with a key code emulating what would happen
if the Escape key were hit.
*****************************************************************************************************************/
WeMenuBar.PostUserAbandonEvent = function ()
{

// Check that there is an active index
if (WeMenuBar.currActiveIndex === -1)
  return;

var activeNode = WeMenuBar.menuBarCellObj[WeMenuBar.currActiveIndex].node; // Active DOM node

// Check to see how events are created. First try DOM 2 method
if (document.createEvent)
  {

  // Check if window.KeyEvent is supported
  if (window.KeyEvent)
    {
    var evtObj = document.createEvent("KeyEvents");
    evtObj.initKeyEvent("keydown",true,true,window,false,false,false,0,
                        WeMenuBar.ESCAPE_KEY_CODE,WeMenuBar.ESCAPE_KEY_CODE);
    }
  else
    {
    var evtObj = document.createEvent("UIEvents");
    evtObj.initUIEvent("keydown", true, true, window,1);
    evtObj.keyCode = WeMenuBar.ESCAPE_KEY_CODE;
    evtObj.weKeyCode = WeMenuBar.ESCAPE_KEY_CODE;
    }

  activeNode.dispatchEvent(evtObj);

  }

else // document.createEvent not supported, try IE way
  {

  var evtObj = document.createEventObject();
  evtObj.weKeyCode = WeMenuBar.ESCAPE_KEY_CODE;

  activeNode.fireEvent("onkeydown",evtObj);

  }

}; // end WeMenuBar.PostUserAbandonEvent()


/*****************************************************************************************************************
This function creates a new menu bar cell object.
*****************************************************************************************************************/
WeMenuBar.CreateMenuBarCellObj = function
  (
  parMnuBar, // Parent menu bar (highest level in hierarchy).
  node, // DOM node
  topMenuArray, // Top level menu array (uses IDs for first element of each sub-array)
  index // A number to be returned to the parent when up-calls are made. The number is the index in parent's array.
  )
{
var idx; // Index
var menuArray; // The menu array associated with this cell
var nodeId = node.id; // ID associated with node argument

// Determine if there is a matching node ID in the array
for (idx = 0; idx < topMenuArray.length; idx++)
  {
  // Check for matching ID values
  if (nodeId === topMenuArray[idx][0])
    break;
  }

// Check if matching ID not found
if (idx >= topMenuArray.length)
  return null;

// Define a constructor function
var menuCellObject = function (parMnuBar,node,nodeId,menuArray,index)
  {

  var menuLayoutPrefix = nodeId.slice(0,4); // The prefix associated with the node ID specifies menu layout
  var that = this; // A variable that object functions use to refer to the proper object.

  // Initialize some properties of this object.
  this.parentMenuBar = parMnuBar;
    // A menu object that owns this object.
  this.node = node;
    // Which DOM node this object is associated
  this.nodeId = nodeId;
    // The identifier associated with this cell.
  this.menuArray = menuArray;
    // If menuArray is an array, then it specifies a sub-menu associated with this cell. However, if menuArray is
    // not an array, then it contains a string specifying a hyper-link.
  this.hasSubMenu = WeMenuBar.IsAnArray(menuArray);
    // true = this cell has a sub-menu; false indicates there is only a hyper link
  this.index = index;
    // The number is the index in parent's array to be returned in up-calls to parent functions.
  this.state = WeMenuBar.CELL_IDLE_ST;
    // Current operating state
  this.watchdogTimerId = -1;
    // The identifier associated with the watchdog timer.
  this.rect;
    // A rectangle object specifying where the corresponding cell is located on the window client area
  this.foldoutMenu = null;
    // A foldout menu associated with this object

  // Set foldout direction. this.foldoutDirection specifies which way menu folds out
  if (menuLayoutPrefix === WeMenuBar.menuSidePrefixStr[WeMenuBar.VERT_LEFT_SIDE])
    this.foldoutDirection = WeMenuBar.FOLDOUT_TO_RIGHT;
  else if (menuLayoutPrefix === WeMenuBar.menuSidePrefixStr[WeMenuBar.VERT_RIGHT_SIDE])
    this.foldoutDirection = WeMenuBar.FOLDOUT_TO_LEFT;
  else
    this.foldoutDirection = WeMenuBar.FOLDOUT_DOWN;

  // Create a function that the parent menu bar calls to deactivate this object
  this.Deactivate = function ()
    {

    // Check for active state
    if (that.state === WeMenuBar.CELL_ACTIVE_ST || that.state === WeMenuBar.CELL_CHILD_ACTIVE_ST)
      {

      // Kill watchdog timer
      if (that.watchdogTimerId !== -1)
        {
        clearTimeout(that.watchdogTimerId);
        that.watchdogTimerId = -1;
        }

      // Check that there really is a foldoutMenu
      if (that.foldoutMenu !== null)
        {
        // Issue destruct message to child message
        that.foldoutMenu.Destruct();

        // Delete child menu
        delete that.foldoutMenu;

        // Reset to null
        that.foldoutMenu = null;
        }

      // Change back to normal to emphasized coloring
      that.node.className = WeMenuBar.mainMenuNormClassName;

      // Specify new state
      that.state = WeMenuBar.CELL_IDLE_ST;

      } // endif that.state === WeMenuBar.CELL_ACTIVE_ST || that.state === WeMenuBar.CELL_CHILD_ACTIVE_ST

    }; // end Deactivate()

  // Create and install an event handler for the click action
  this.ClickAction = function ()
    {
    var nextPage; // The next page we link to

    // Clear timeout if it is in progress
    if (that.watchdogTimerId !== -1)
      clearTimeout(that.watchdogTimerId);

    // Check if there is no sub-menu
    if (that.hasSubMenu === false)
      {
      // Post an event to notify parent that user abandoned to destroy all menus if user hits back
      WeMenuBar.PostUserAbandonEvent();

      // Specify new state
      that.state = WeMenuBar.CELL_IDLE_ST;

      // Follow the link.
      nextPage = WeMenuBar.linkPrefix + that.menuArray;
      window.open(nextPage,"_self");
      }

    };

  // Set function in this object for click from the system
  this.node.onclick = this.ClickAction;

  // Create and install an event handler for the mouseover from the system
  this.MouseoverAction = function ()
    {

    // Clear timeout if it is in progress
    if (that.watchdogTimerId !== -1)
      clearTimeout(that.watchdogTimerId);

    // Check if this cell is in the idle state
    if (that.state === WeMenuBar.CELL_IDLE_ST)
      {

      // Send mouseover notification to parent so it can deactivate another cell if another is active.
      WeMenuBar.NotifyOfMouseover(that.index);

      // Change to emphasized coloring
      that.node.className = WeMenuBar.mainCellEmphClassName;

      // Check if there is a sub-menu
      if (that.hasSubMenu === true)
        {
        // Get rectangle where this object is located
        that.rect = WeMenuBar.GetElementRect(that.node);

        // Create and display sub-menu
        that.foldoutMenu = WeMenuBar.CreateFoldoutMenu(that,that.rect,that.foldoutDirection,that.menuArray);
        }

      }

    // Check if my child was active and mouse reentered this cell
    else if (that.state === WeMenuBar.CELL_CHILD_ACTIVE_ST)
      // Notify child to deactivate a sub-menu if there is such active
      that.foldoutMenu.Deactivate();

    // Specify new state
    that.state = WeMenuBar.CELL_ACTIVE_ST;

    }; // end MouseoverAction()

  // Set function in this object for mouseover from the system
  node.onmouseover = this.MouseoverAction;

  // Create and install an event handler for the mouseout action
  this.MouseoutAction = function ()
    {
    that.watchdogTimerId = setTimeout(that.WatchdogTimeout, WeMenuBar.WATCHDOG_TIME_VALUE);
    // Check if our sentinel -1 is assigned
    if (that.watchdogTimerId === -1)
      {
      // Clear the timeout and get another timer ID
      clearTimeout(that.watchdogTimerId);
      that.watchdogTimerId = setTimeout(that.WatchdogTimeout, WeMenuBar.WATCHDOG_TIME_VALUE);
      }
    };

  // Set function in this object for mouseout action
  node.onmouseout = this.MouseoutAction;

  // Install an event handler for keydown action
  // Check if IE browser
  if (document.createEventObject)
    {
    this.KeydownAction = function ()
      {
      if (event.weKeyCode === WeMenuBar.ESCAPE_KEY_CODE)
        that.NotifyOfUserAbandon();
      };
    }
  else // Not IE browser
    {
    this.KeydownAction = function (evt)
      {
      var key; // Which key

      if (typeof(evt.weKeyCode) === "number")
        key = evt.weKeyCode;
      else if (typeof(evt.keyCode) === "number")
        key = evt.keyCode;

      if (key === WeMenuBar.ESCAPE_KEY_CODE)
        that.NotifyOfUserAbandon();
      };
    }

  // Set function in this object for keydown action
  node.onkeydown = this.KeydownAction;

  // This function is called by a child to notify this object the child received a mouseover event or up-call
  this.NotifyOfChildMouseover = function ()
    {

    // Clear timeout if it is in progress
    if (that.watchdogTimerId !== -1)
      clearTimeout(that.watchdogTimerId);

    // Check if we are in the active state
    if (that.state === WeMenuBar.CELL_ACTIVE_ST)
      // Specify new state
      that.state = WeMenuBar.CELL_CHILD_ACTIVE_ST;

    }; // end NotifyOfChildMouseover()

  // This function is called by children to get the menu's z-index
  this.GetMenuZIndex = function ()
    {
    return WeMenuBar.menuZIndex;
    };

  // This function is called by a keydown event handler when a child menu or its descendents post the event
  // to indicate that the user abandoned the menuing
  this.NotifyOfUserAbandon = function ()
    {
    // Call my deactivate function
    that.Deactivate();

    // Notify parent that this cell is no longer active
    that.parentMenuBar.NotifyOfCellInactive(that.index);
    }; // end NotifyOfUserAbandon()

  // This function specifies the appropriate actions when a watchdog timeout occurs.
  this.WatchdogTimeout = function ()
    {
    // Call my deactivate function
    that.Deactivate();

    // Notify parent that this cell is no longer active
    that.parentMenuBar.NotifyOfCellInactive(that.index);
    }; // end WatchdogTimeout()

  }; // end function to create menuCellObject

// Do the actual object creation and return with an object reference
return new menuCellObject(parMnuBar,node,nodeId,topMenuArray[idx][1],index);

}; // end WeMenuBar.CreateMenuBarCellObj()


/*****************************************************************************************************************
This function creates a foldout menu, displays it on the screen and returns a reference to the object.

A foldout menu is a container for the menu items.
*****************************************************************************************************************/
WeMenuBar.CreateFoldoutMenu = function
  (
  parent, // The object that owns this menu.
  parRect, // A rectangle object indicating parent's location on the screen.
  foldoutDir, // Which way the menu should fold out from parent.
  menuArray // The menu array contains an array of label-link and label-subarray objects.
  )
{
var idx; // Index
var domListItem; // A DOM list item that appears in the unordered list.
var arrayElem; // An element in the menu array. This element contains either a label-link or label-subarray object

// Define a constructor function
var menuObject = function (parent,parRect,foldoutDir,menuArray)
  {

  var that = this; // A variable that object functions use to refer to the proper object.

  // Initialize some properties of this object.
  this.parent = parent;
    // A menu object that owns this object.
  this.menuZIndex = parent.GetMenuZIndex() + 1;
    // This is the menu's z-index for this menu. Each descendent increments by one
  this.parRect = parRect;
    // A rectangle object indicating parent's location on the screen.
  this.foldoutDir = foldoutDir;
    // Which direction this menu folds out from parent
  this.menuArray = menuArray;
    // The menu array contains an array of label-link and label-subarray objects.
  this.isTerminalLeaf = false;
    // true = this menu is a terminal leaf in the menu tree and does not branch further.
  this.menuItemObj = [];
    // An array of menu items
  this.state = WeMenuBar.MENU_IDLE_ST;
    // Current operating state
  this.currActiveIndex = -1;
    // Which element in the menuItemObj[] array is currently active. -1 means none.

  // Create a new unordered list to use for the menu.
  this.unorderedMenuList = document.createElement("UL");
  this.unorderedMenuList.className = WeMenuBar.unorderedListClassName;

  // Create the menu items and insert into the unordered list
  for (idx = 0; idx < this.menuArray.length; idx++)
    {

    // Get an element from the menuArray
    arrayElem = this.menuArray[idx];

    // Insert in the document
    domListItem = document.createElement("LI");
    domListItem.className = WeMenuBar.listItemNormClassName;
    domListItem.innerHTML = arrayElem[0]; // The label
    domListItem = this.unorderedMenuList.appendChild(domListItem);

    // Create the menu item
    this.menuItemObj[idx] = WeMenuBar.CreateMenuItemObj(domListItem,arrayElem[1],this,idx);
    }

  // Place into DOM but do not display yet
  this.unorderedMenuList.style.zIndex = this.menuZIndex;
  document.body.appendChild(this.unorderedMenuList);

  // Determine position on screen
  WeMenuBar.SetMenuLocation(this.unorderedMenuList,this.parRect,this.foldoutDir);

  // Display it
  this.unorderedMenuList.style.visibility = "visible";

  // This function is called by parent to destruct all my children delete the memory used to allocate them.
  this.Destruct = function ()
    {

    if (that.state === WeMenuBar.MENU_IDLE_ST || that.state === WeMenuBar.MENU_ITEM_ACTIVE_ST)
      {

      // Remove display from screen
      that.unorderedMenuList.style.visibility = "hidden";

      // Destruct children in reverse order they were constructed and delete their memory.
      for (idx = menuArray.length - 1; idx >= 0; idx--)
        {
        that.menuItemObj[idx].Destruct();
        delete that.menuItemObj[idx];
        }

      // Specify new state
      that.state = WeMenuBar.MENU_INACTIVE_ST;

      }

    }; // end Destruct()

  // This function is called by parent to deactivate an active menu item
  this.Deactivate = function ()
    {

    // Check if any itme is currently active
    if (that.state === WeMenuBar.MENU_ITEM_ACTIVE_ST && that.currActiveIndex !== -1)
      {

      // Destruct active child
      that.menuItemObj[that.currActiveIndex].Deactivate();

      // Change the current active index
      that.currActiveIndex = -1;

      // Specify new state
      that.state = WeMenuBar.MENU_IDLE_ST;

      }

    };

  // This function returns the direction this menu folds out
  this.GetFoldoutDir = function ()
    {
    return that.foldoutDir;
    };

  //  This function is called by children to get the menu's z-index
  this.GetMenuZIndex = function ()
    {
    return that.menuZIndex;
    };

  // This function is called by a child when it receives a mouseover notification from the system or
  // one of the descendent sub-menus.
  this.NotifyOfMouseover = function
      (
      index // Index of the menu item making the call
      )
    {

    // Check that we are not in the inactive state
    if (that.state !== WeMenuBar.MENU_INACTIVE_ST)
      {

      // Check for valid index number and if the caller's index is not the currently active cell object
      if (index <= that.menuItemObj.length)
        {

        // Notify parent that it's child has an active item.
        that.parent.NotifyOfChildMouseover();

        // Check if a valid index number
        if (index !== that.currActiveIndex)
          {

          // Check if no menu item is currently active
          if (that.currActiveIndex !== -1)
            // Deactivate the currently active menu item
            that.menuItemObj[that.currActiveIndex].Deactivate();

          // Note which cell is active
          that.currActiveIndex = index;

          } // endif index !== WeMenuBar.currActiveIndex

        // Specify new state
        that.state = WeMenuBar.MENU_ITEM_ACTIVE_ST;

        } // endif index <= that.menuItemObj.length

      } // endif that.state !== WeMenuBar.MENU_ITEM_ACTIVE_ST

    }; // end this.NotifyOfMouseover()

  }; // end function to create menuObject

// Do the actual object creation and return with an object reference
return new menuObject(parent,parRect,foldoutDir,menuArray);

}; // end WeMenuBar.CreateFoldoutMenu()


/*****************************************************************************************************************
This function creates a new menuItem Object.

A menu item is one of multiple items in a menu. The parent of a menu item is a foldout menu.
*****************************************************************************************************************/
WeMenuBar.CreateMenuItemObj = function
  (
  domNode, // This is the associated node in the DOM
  linkOrArrayObj, // This is an object that contains either a link string or an array reference.
  parMenu, // The parent menu object that the menu item belongs to
  index // A number to be returned to the parent when up-calls are made. The number is the index in parent's array.
  )
{

var menuItem = function (domNode, linkOrArrayObj, parMenu, index)
  {

  var that = this; // A variable that object functions use to refer to the proper object.

  // Initialize some properties of this object.
  this.parentMenu = parMenu;
    // A menu object that owns this object.
  this.nextBranch = linkOrArrayObj;
    // If a terminal leaf, this is string providing an href link string; otherwise, this is a sub-menu array
  this.isTerminalLeaf = !WeMenuBar.IsAnArray(linkOrArrayObj);
    // This is a terminal leaf in the menu tree. It does not branch further.
  this.liNode = domNode;
    // The list item DOM node in the un-ordered list having tag "LI".
  this.index = index;
    // The number is the index in parent's array to be returned in up-calls to parent functions.
  this.expandDirection = WeMenuBar.FOLDOUT_TO_RIGHT;
    // Which way to open a menu from this cell: Uses the WeMenuBar.FOLDOUT_xxxx variables.
    // Only applies if this item branches to another menu.
  this.state = WeMenuBar.MNUITEM_IDLE_ST;
    // Current operating state
  this.watchdogTimerId = -1;
    // The identifier associated with the watchdog timer.
  this.rect;
    // A rectangle object specifying where the corresponding cell is located on the window client area
  this.foldoutMenu = null;
    // A foldout menu associated with this object

  // Create and install an event handler for the click action
  this.ClickAction = function ()
    {
    var nextPage; // The next page we link to

    // Clear timeout if it is in progress
    if (that.watchdogTimerId !== -1)
      clearTimeout(that.watchdogTimerId);

    // Check if this is terminal leaf
    if (that.isTerminalLeaf === true)
      {
      // Post an event to notify parent that user abandoned to destroy all menus if user hits back
      WeMenuBar.PostUserAbandonEvent();

      // Specify new state
      that.state = WeMenuBar.MNUITEM_INACTIVE_ST;

      // Follow the link.
      nextPage = WeMenuBar.linkPrefix + that.nextBranch;
      window.open(nextPage,"_self");
      }

    };

  // Set function in this object for click from the system
  this.liNode.onclick = this.ClickAction;

  // Create and install an event handler for the mouseover from the system
  this.MouseoverAction = function ()
    {

    // Clear timeout if it is in progress
    if (that.watchdogTimerId !== -1)
      clearTimeout(that.watchdogTimerId);

    // Send mouseover notification to parent so it can deactivate another item if another is active.
    that.parentMenu.NotifyOfMouseover(that.index);

    // Check if this item is not already active
    if (that.state === WeMenuBar.MNUITEM_IDLE_ST)
      {

      // Change to emphasized coloring
      that.liNode.className = WeMenuBar.listItemEmphClassName;

      // Check if there is a sub-menu
      if (that.isTerminalLeaf === false)
        {
        // Get rectangle where this object is located
        that.rect = WeMenuBar.GetElementRect(that.liNode);

        // Determine foldout direction
        var parFoldoutDir = that.parentMenu.GetFoldoutDir();
        var foldoutDir = (parFoldoutDir === WeMenuBar.FOLDOUT_TO_LEFT) ?
                                                              WeMenuBar.FOLDOUT_TO_LEFT : WeMenuBar.FOLDOUT_TO_RIGHT;

        // Create and display sub-menu
        that.foldoutMenu = WeMenuBar.CreateFoldoutMenu(that,that.rect,foldoutDir,that.nextBranch);
        }

      }

    // Check if child is active and mouse returned to this item
    else if (that.state === WeMenuBar.MNUITEM_CHILD_ACTIVE_ST && that.foldoutMenu !== null)
      // Issue deactivate message to child to prevent it's timeout and to remove any active menu items it has
      that.foldoutMenu.Deactivate();

    // Specify new state
    that.state = WeMenuBar.MNUITEM_ACTIVE_ST;

    }; // end MouseoverAction()

  // Set function in this object for mouseover from the system
  this.liNode.onmouseover = this.MouseoverAction;

  // Create and install an event handler for the mouseout action
  this.MouseoutAction = function ()
    {

    that.watchdogTimerId = setTimeout(that.WatchdogTimeout, WeMenuBar.WATCHDOG_TIME_VALUE);
    // Check if our sentinel -1 is assigned
    if (that.watchdogTimerId === -1)
      {
      // Clear the timeout and get another timer ID
      clearTimeout(that.watchdogTimerId);
      that.watchdogTimerId = setTimeout(that.WatchdogTimeout, WeMenuBar.WATCHDOG_TIME_VALUE);
      }

    };

  // Set function in this object for mouseout action
  this.liNode.onmouseout = this.MouseoutAction;

  // This function is called by a parent to deactivate a menu item.
  this.Deactivate = function ()
    {

    // Clear timeout if it is in progress
    if (that.watchdogTimerId !== -1)
      clearTimeout(that.watchdogTimerId);

    // Change to normal coloring
    that.liNode.className = WeMenuBar.listItemNormClassName;

    // Check if there is a sub-menu and we are in an active or child active state
    if (that.isTerminalLeaf === false &&
        (that.state === WeMenuBar.MNUITEM_ACTIVE_ST || that.state === WeMenuBar.MNUITEM_CHILD_ACTIVE_ST))
      {

      // Check if there is a foldout menu object
      if (that.foldoutMenu !== null)
        {
        // Destroy foldout menu
        that.foldoutMenu.Destruct();

        that.foldoutMenu = null;
        }

      }

    // Change to the idle state
    that.state = WeMenuBar.MNUITEM_IDLE_ST;

    };

  // This function is called by parent to destruct all my children delete the memory used to allocate them.
  this.Destruct = function ()
    {

    // Clear timeout if it is in progress
    if (that.watchdogTimerId !== -1)
      clearTimeout(that.watchdogTimerId);

    // Check if there is a sub-menu and we are in an active or child active state
    if (that.isTerminalLeaf === false &&
        (that.state === WeMenuBar.MNUITEM_ACTIVE_ST || that.state === WeMenuBar.MNUITEM_CHILD_ACTIVE_ST))
      {

      // Check if there is a foldout menu object
      if (that.foldoutMenu !== null)
        {
        // Destroy foldout menu
        that.foldoutMenu.Destruct();

        that.foldoutMenu = null;
        }

      }

    // Specify new state
    that.state = WeMenuBar.MNUITEM_INACTIVE_ST;

    }; // end Destruct()

  // This function is called by children to get the menu's z-index
  this.GetMenuZIndex = function ()
    {
    return that.parentMenu.GetMenuZIndex();
    };

  // Create a function that children call to notify this object the child received a mouseover event or up-call
  this.NotifyOfChildMouseover = function ()
    {

    // Clear timeout if it is in progress
    if (that.watchdogTimerId !== -1)
      clearTimeout(that.watchdogTimerId);

    // Check if there is a sub-menu
    if (that.isTerminalLeaf === false)
      {
      // Notify parent
      that.parentMenu.NotifyOfMouseover(that.index);

      // Check if we are in the active state
      if (that.state === WeMenuBar.MNUITEM_ACTIVE_ST)
        // Specify new state
        that.state = WeMenuBar.MNUITEM_CHILD_ACTIVE_ST;
      }

    }; // end NotifyOfChildMouseover()

  // This function specifies the appropriate actions when a watchdog timeout occurs.
  this.WatchdogTimeout = function ()
    {

    // Post an event to notify parent that user abandoned
    WeMenuBar.PostUserAbandonEvent();

    // Specify new state
    that.state = WeMenuBar.MNUITEM_INACTIVE_ST;

    }; // end WatchdogTimeout()

  };

return new menuItem(domNode, linkOrArrayObj, parMenu, index);

}; // end WeMenuBar.CreateMenuItemObj()


/*****************************************************************************************************************
This function creates and initializes various information needed to set up the menuing and it's style.
*****************************************************************************************************************/
WeMenuBar.Initialize = function ()
{
var menuTable = []; // An array of menu tables
var menuCellNode = []; // An array of nodes in the table having tag name of TD
var idx, jdx, kdx; // Indexes

// Define and initialize properties from arguments
WeMenuBar.mainMenuArray = WeMainMenuArr;
WeMenuBar.mainMenuNormClassName = 'mmenucell';
WeMenuBar.mainCellEmphClassName = 'mmenucell_emph';
WeMenuBar.unorderedListClassName = 'menublock';
WeMenuBar.listItemNormClassName = 'menuitem_norm';
WeMenuBar.listItemEmphClassName = 'menuitem_emph';
WeMenuBar.menuZIndex = WeMenuBar.FIRST_MENU_ZINDEX - 1;

// Set type of menu if not externally set: 0 = table, 1 = unordered list
if (WeMenuBar.menuType === undefined || WeMenuBar.menuType >= 1)
  WeMenuBar.menuType = 0;

// Create an array of menu bar objects
WeMenuBar.menuBarCellObj = [];
kdx = 0; // kdx is index into WeMenuBar.menuBarCellObj

// Get an array of all the nodes with TABLE tag
menuTable = document.getElementsByTagName(WeMenuBar.majorTagName[WeMenuBar.menuType]);

// Cycle through tables
for (idx = 0; idx < menuTable.length; idx++)
  {

  // Check if table has the class name we are looking for 'mmenutable'
  if (menuTable[idx].className === 'mmenutable')
    {

    // Get nodes in table with TD tag
    menuCellNode = menuTable[idx].getElementsByTagName(WeMenuBar.elementTagName[WeMenuBar.menuType]);

    // Cycle through the td node elements
    for (jdx = 0; jdx < menuCellNode.length; jdx++)
      {

      // Check if the node has className mnuCellClassNormNameStr
      if (menuCellNode[jdx].className === WeMenuBar.mainMenuNormClassName)
        {
        // Create a menu bar cell object
        WeMenuBar.menuBarCellObj[kdx] =
                           WeMenuBar.CreateMenuBarCellObj(WeMenuBar,menuCellNode[jdx],WeMenuBar.mainMenuArray,kdx);
        kdx++;
        }
      } // endfor jdx

    } // endif table has correct class name

  } // endfor idx

// Define and initialize other properties
WeMenuBar.currActiveIndex = -1; // Which element in the WeMenuBar.menuBarCellObj[] array is currently active
                                // -1 means none.

}; // end WeMenuBar.Initialize()


/*****************************************************************************************************************
This function causes the WeMemberMaint.Init() to be called on startup
*****************************************************************************************************************/
WeMenuBar.Start = function()
{

// Check to see how events are created. First try DOM 2 method
if (document.addEventListener)
  {
  window.addEventListener("load",WeMenuBar.Initialize,false);
  }

else // document.createEvent not supported, try IE way
  {
  window.attachEvent("onload",WeMenuBar.Initialize);
  }

}; // end WeMenuBar.Start()


//...................................................................................................................

WeMenuBar.Start();




