<!--
//345678901234567890123456789012345678901234567890123456789012345678901234567890
//       1         2         3         4         5         6         7         8
//
// Filename:  WebMenu.js
//
// Copyright: All rights reserved.  No part of this software nor its data may be
//            reproduced or transmitted in any form or by any means without the
//            written permission of Freeman & Frederick Consulting Inc.
//
// Purpose:   Builds a drop down menu system using layers and JavaScript that
//            can be used in web pages.
//
// Product:   N/A
//
// Dependencies:
//            Web Browser
//            Document Object Model (DOM)
//            Layers (Div)
//            Compatibility script
//
// Usage:     See mymenu.js for description on usage.
//
// Author:    Dave Frederick
//            Freeman & Frederick Consulting Inc.
//
// Modification History:
// Rev.     By   Date         Reason
// 1.0      DCF  06-Nov-2001  Original release.
// 1.1      DCF  06-Nov-2002  - Change positioning on web page
//                            - Rename methods, classify as commands/queries
//                            - Remove simple get methods, access attribute
//                              directly
//                            - fix border issues with not centering, etc.
//                            - fix positioning of menu to use offsets rather
//                              than physical locations.  Thus you can move
//                              the menu without recalculating 
// 1.2      DCF  09-Dec-2002  Netscape 7.0 compatible.
// 1.3      DCF  27-Nov-2007  Mac Internet Explorer 5.0 compatable.
// 1.4      DCF  04-Jul-2008  - Add debug levels.
//                            - Change how layer names generated
//                            - Support null and not ''
//                            - Add option to open link in a new window
//                            - Fix bug when too many submenus (sorting
//                              by ASCII versus numeric problem)
//
// To Do:   1. Allow text or file as item body
//          2. Allow text or file as item help
//          3. See if the menu should be positioned at the top all the time
//             this means that the current position of the web page needs to
//             be determined and added to the offset of the original menu (and
//             percolate down through the remaining submenus).
//          4. wSubShow, wSubHide, wSubIsShown -- revisit.
//          5. Review z-index as the menu sometimes appears under other areas
//             on a web site.
//

//
// Utility functions
//
function isFile(text) {
  // Checks to see if the text contains a period ('.').  If this is the case, it
  // assumes the text is a file name. 
  return (-1 == text.indexOf('.') ? false : true);
}


//
// WebStyle constructor and methods.
//
//   <         width         >
//   +-----------------------+  ^
//   |            ^          |   
//   |       borderSpace     |   
//   |            v          |   
//   |       +---------+     |
//   | < b > | itemText|     |  height
//   |       +---------+     |    
//   |                       |   
//   |                       |   
//   |                       |
//   +-----------------------+  v
//                ^
//            itemSpace
//                v
//   +-----------------------+                  +------------------------+
//   |                       |                  |                        |
//   |                       |                  |                        |
//   |                       |                  |                        |
//   |       +---------+     |                  |  +-----------------+   |
//   |       | itemText|     | <indicatorSpace> |  | submenuIndicator|   |
//   |       +---------+     |                  |  +-----------------+   |
//   |                       |                  |                        |
//   |                       |                  |                        |
//   |                       |                  |                        |
//   +-----------------------+                  +------------------------+
//
function WebStyle(width, height, itemSpace, borderSpace, submenuIndicator,
                  indicatorSpace, mouseOutBackground, mouseOutCSS, 
                  mouseOverBackground, mouseOverCSS, borderCSS) {
  // Attributes
  this.borderCSS = borderCSS;               // Cascading style sheet border
  this.borderSpace = borderSpace;           // See diagram above
  this.height = height;                     // See diagram above
  this.itemSpace = itemSpace;               // See diagram above
  this.submenuIndicator = submenuIndicator; // See diagram above
  this.indicatorSpace = indicatorSpace;     // See diagram above
  this.outBackground = mouseOutBackground;  // Mouse out background
  this.outCSS = mouseOutCSS;                // Mouse out cascading style sheet
  this.overBackground = mouseOverBackground;// Mouse over background
  this.overCSS = mouseOverCSS;              // Mouse over cascading style sheet
  this.width = width;                       // See diagram above

  // Methods -- Commands
  this.debug = wStyleDebug;                  // Debug object

  return this;
}

//
// WebStyle commands
//
function wStyleDebug(debugLevel) { with (this) {

  // Show everything by default
  if (null == debugLevel) debugLevel = 3;
  
  if (1 <= debugLevel) {
    document.write('......WebStyle.debugLevel: ' + debugLevel + '<BR>');
  }

  if (2 <= debugLevel) {
    document.write('......WebStyle.height: ' + height + '<BR>');
    document.write('......WebStyle.width: ' + width + '<BR>');
  }

  if (3 <= debugLevel) {
    document.write('......WebStyle.borderCSS: ' + borderCSS + '<BR>');
    document.write('......WebStyle.borderSpace: ' + borderSpace + '<BR>');
    document.write('......WebStyle.itemSpace: ' + itemSpace + '<BR>');
    document.write('......WebStyle.submenuIndicator: ' + submenuIndicator + '<BR>');
    document.write('......WebStyle.indicatorSpace: ' + indicatorSpace + '<BR>');
    document.write('......WebStyle.outBackground: ' + outBackground + '<BR>');
    document.write('......WebStyle.outCSS: ' + outCSS + '<BR>');
    document.write('......WebStyle.overBackground: ' + overBackground + '<BR>');
    document.write('......WebStyle.overCSS: ' + overCSS + '<BR>');
  }
}}


//
// WebMenu constructor and methods.
//
// Need to have the object name passed in as a parameter.  This name must match
// the instance of the WebMenu or this script will not work.  The name allows
// multiple menus to be open on the same web page assuming, the names are unique.
//
function WebMenu(name) {
  // Constants
  this.kRoot = 0;                           // Index of the root submenu

  // Attributes
  this.activeSubmenus = new Array();        // Active submenu list
  this.hideDelay = 500;                     // Hide layer delay (milliseconds)
  this.hideTimer = null;                    // Hide timer reference 
  this.name = name;                         // Object name -- see comment
  this.showDelay = 0;                       // Show layer delay (milliseconds)
  this.showTimer = null;                    // Show timer reference
  this.stateText = 'Valid configuration';   // Menu configuration status text
  this.submenus = new Array();              // Submenu list

  this.valid = true;                        // Menu configuration validity

  // Methods - Commands
  this.addItem = wMenuAddItem;              // Add an item to a submenu
  this.addSubmenu = wMenuAddSubmenu;        // Add a submenu to a menu
  this.debug = wMenuDebug;                  // Debug Menu object
  this.mouseClick = wMenuMouseClick;        // Mouse click actions
  this.mouseOut = wMenuMouseOut;            // Mouse out actions
  this.mouseOver = wMenuMouseOver;          // Mouse over actions
  this.setActive = wMenuSetActive;          // Create active submenu list
  this.setInvalid = wMenuSetInvalid;        // Set menu state to invalid
  this.setTimer = wMenuSetTimer;            // Change menu timer settings
  this.show = wMenuShow;                    // Create and display menu
  this.showActive = wMenuShowActive;        // Show active submenus

  // Methods - Queries
  this.submenu = wMenuSubmenu;              // Submenu reference

  return this;
}

//
// WebMenu commands
//
function wMenuAddItem(body, help, link, newWindow, style, width, itemName,
                      parentName) { with (this) {
  var itemFound = false;
  var submenuIndex = 0;
  var submenuFound = false;

  // Parent submenu must already exist.
  while (!submenuFound && submenuIndex < submenus.length) {
    if (parentName == submenus[submenuIndex].name) submenuFound = true;
    else submenuIndex++;
  }

  // itemName must be unique if provided (not null)
  for (var i = 0; i < submenus.length; i++) {
    if (null != itemName &&
        null != submenus[i].item(itemName)) {
      itemFound = true;
    }
  }  

  if (submenuFound && !itemFound) {
    var itemStyle = (style ? style : submenus[submenuIndex].defaultStyle);
    var itemWidth = (width ? width : itemStyle.width);
    submenus[submenuIndex].addItem(body, help, link, newWindow, itemStyle,
                                   itemWidth, itemName);
    if (submenus[submenuIndex].isItemValid()) {
      submenus[submenuIndex].setItemLayerId(name);

    } else {
      setInvalid('ERROR: ' + submenus[submenuIndex].validationMsg() + '');
    }

  } else {
    if (!submenuFound) {
      setInvalid('ERROR: Menu item (' + itemName +
                 ') does not have a defined parent menu (' +
                 parentName + ').');
    }
    if (itemFound) {
      setInvalid('ERROR: Menu item (' + parentName + '.' + itemName +
                 ') already exists and must be unique.');
    }
  }
}}

function wMenuAddSubmenu(submenuName, xOffset, yOffset, isVertical,
                         defaultStyle) { with (this) {
  var submenuIndex = 0;
  var submenuFound = false;
  var parentItemFound = false;
  
  while (!submenuFound && submenuIndex < submenus.length) {
    if (submenuName == submenus[submenuIndex].name) submenuFound= true;
    else submenuIndex++;
  }
  
  if (!submenuFound) {
    submenus[submenuIndex] = new WebSubmenu(submenuName, xOffset, yOffset,
                                            isVertical, defaultStyle, submenuIndex);
    submenus[submenuIndex].setLayerId(name);

    // Set the parent item reference.
    if (valid) {
      var i = 0;
      while ((i < submenus.length) && (!parentItemFound)) {
        if (i != submenuIndex) {
          var parentItem = submenus[i].item(submenuName);
          if (null != parentItem) {
            submenus[submenuIndex].setParentItem(parentItem);
            submenus[submenuIndex].parentSubmenu = submenus[i];
            submenus[i].setItemChild(submenuName, submenus[submenuIndex]);
            parentItemFound = true;
          }
        }
        i++;
      }
    }

  } else {
    setInvalid('ERROR: Menu (' + submenuName + ') has more than one definition.');
  }
}}


function wMenuDebug(debugLevel) { with (this) {

  // Show everything by default
  if (null == debugLevel) debugLevel = 3;
  
  if (1 <= debugLevel) {
    document.write('WebMenu.debugLevel: ' + debugLevel + '<BR>');
    document.write('WebMenu.kRoot: ' + kRoot + '<BR>');

    document.write('WebMenu.name: ' + name + '<BR>');

    document.write('WebMenu.submenus.length: ' + submenus.length + '<BR>');
    for (var i = 0; i < submenus.length; i++) {
      document.write('<BR>');
      document.write('WebMenu.submenus [' + i + '] <BR>');
      submenus[i].debug(debugLevel);
    }
  }

  if (2 <= debugLevel) {
    document.write('WebMenu.stateText: ' + stateText + '<BR>');

    document.write('WebMenu.activeSubmenus.length: ' + activeSubmenus.length +
                   '<BR>');
    if (0 < activeSubmenus.length) {
      document.write('WebMenu.activeSubmenus {');
      for (var i = 0; i < activeSubmenus.length; i++) {
        document.write(' ' + activeSubmenus[i]);
      }
      document.write(' } <BR>');
    }
  }

  if (3 <= debugLevel) {
    document.write('WebMenu.hideDelay: ' + hideDelay + '<BR>');
    document.write('WebMenu.showDelay: ' + showDelay + '<BR>');
    document.write('WebMenu.valid: ' + valid + '<BR>');
  }
}}

function wMenuMouseClick(submenuId, itemId) { with (this) {
  clearTimeout(hideTimer);
  clearTimeout(showTimer);
  if (submenus[submenuId].isItemSubmenu(itemId)) {
    var childId = submenus[submenuId].itemSubmenuId(itemId);
    if (submenus[childId].isShown()) {
      setActive(submenuId);
    } else {
      setActive(childId);
    }

  } else {
    // This step is only visible if the hide delay is made large,

    // like 5 seconds, and the item clicked opens in a fresh window.
    // If the user returns to the original before the hide delay
    // expires, then the submenu will still be visible.  Not a

    // usual configuration.
    setActive(submenuId);
  }
  submenus[submenuId].mouseClick(itemId);
  showActive();
}}

function wMenuMouseOut(submenuId, itemId) { with (this) {
  clearTimeout(hideTimer);
  clearTimeout(showTimer);
  setActive(kRoot);
  submenus[submenuId].mouseOut(itemId);
  hideTimer = setTimeout(name + '.showActive()', hideDelay);
}}

function wMenuMouseOver(submenuId, itemId) { with (this) {
  clearTimeout(hideTimer);
  clearTimeout(showTimer);

  if (submenus[submenuId].isItemSubmenu(itemId)) {
    setActive(submenus[submenuId].itemSubmenuId(itemId));
  } else {
    setActive(submenuId);
  }
  submenus[submenuId].mouseOver(itemId);
  showTimer = setTimeout(name + '.showActive()', showDelay);
}}

function wMenuSetActive(submenuId) { with (this) {
  activeSubmenus = new Array();
  activeSubmenus[activeSubmenus.length] = submenuId;

  // Find all parent submenus until the root is reached.
  var parent = submenus[submenuId].parentSubmenu;
  while(null != parent) {
    activeSubmenus[activeSubmenus.length] = parent.id;
    parent = parent.parentSubmenu;
  }
  
  // Sort array numerically, ascending.  The function is
  // inlined here as it is not used anywhere else.
  activeSubmenus.sort(function(a,b) {return (a - b)});
}}

function wMenuSetInvalid(message) { with (this) {
  valid = false;
  stateText = message;
}}

function wMenuSetTimer(showDelay, hideDelay) {
  this.showDelay = showDelay;
  this.hideDelay = hideDelay;
}

function wMenuShow() { with (this) {
  if (!isDOM) {
    alert('This site utilizes the document object model (DOM).  This site ' +
          'has detected that your browser does not support DOM.  Please ' +
          'refer to your browser\'s web site for information on DOM.');
    return;
  }

  if (!valid) {
    alert('There is an error in the menu configuration provided.  Please ' +
          're-check the menu configuration.  The error detected is:\r\r' +
          stateText);
    return;
  }

  for (var i = 0; i < submenus.length; i++) {
    submenus[i].setLayer(name);

    document.body.appendChild(submenus[i].layer);
  }

  setActive(kRoot);
  showActive();
//  debug(3);
}}

function wMenuShowActive() { with (this) {
  var j = 0;

  for (var i = 0; i < submenus.length; i++) {
    if (activeSubmenus[j] == submenus[i].id) {
      if (!submenus[i].isShown()) {
        submenus[i].show();
      }
      if (j + 1 < activeSubmenus.length) {
        j++;
      }
    } else {
      if (submenus[i].isShown()) {
        submenus[i].hide();
      }
    }
  }
}}


//
// Menu queries
//
function wMenuSubmenu(itemName) { with (this) {
  var i = 0;
  var found = false;
  
  while (!found && i < submenus.length) {
    if (null != submenus[i].item(itemName)) found= true;
    else i++;
  }
  return (found ? submenus[i] : null);
}}


//
// WebSubmenu constructor and methods.
//
function WebSubmenu(name, xOffset, yOffset, isVertical, defaultStyle, id) {
  // Constants
  this.kHide = 'hidden';                    // Hide layer text
  this.kShow = 'visible';                   // Show layer text
  this.kZIndex = 1000;                      // Layer "Z Index", use high value
  
  // Attributes
  this.defaultStyle = defaultStyle;         // Default item style
  this.name = name;                         // Object name
  this.id = id;                             // Identifier
  this.isVertical = isVertical;             // Vertical versus horizontal
  this.items = new Array();                 // Item list
  this.lastItemAdded = null;                // Index of last item added
  this.layer = null;                        // Layer (division) reference
  this.layerId = null;                      // Unique layer identifier
  this.parentItem = null;                   // Parent item reference
  this.parentSubmenu = null;                // Parent submenu reference
  this.reposition = false;                  // Reposition indicator
  this.validationMessage='';                // Validation results
  this.xOffset = xOffset;                   // X offset (pixels) from parent
  this.yOffset = yOffset;                   // Y offset (pixels) from parent

  // Methods - Commands
  this.addItem = wSubAddItem;               // Add an item to submenu
  this.debug = wSubDebug;                   // Debug submenu object
  this.hide = wSubHide;                     // Hide submenu layer
  this.mouseClick = wSubMouseClick;         // Mouse click actions
  this.mouseOut = wSubMouseOut;	            // Mouse out actions
  this.mouseOver = wSubMouseOver;           // Mouse over actions
  this.setLayer = wSubSetLayer;             // Build submenu layer
  this.setItemLayerId = wSubSetItemLayerId; // Set the unique item layer id
  this.setItemChild = wSubSetItemChild;     // Set the parent item's submenu
  this.setLayerId = wSubSetLayerId;         // Set the unique submenu's layer id
  this.setParentItem = wSubSetParentItem;   // Set submenu parent item reference
  this.show = wSubShow;                     // Show submenu layer

  // Methods - Queries
  this.isItemSubmenu = wSubIsItemSubmenu;   // Is submenu item a submenu?
  this.isItemValid = wSubIsItemValid;       // Validate menu item
  this.isShown = wSubIsShown;               // Is submenu layer shown?
  this.item = wSubItem;                     // Submenu item reference
  this.itemSubmenuId = wSubItemSubmenuId;   // Item's submenu identifier
  this.layerHTML = wSubLayerHTML;           // Submenu layer (division) HTML
  this.parentLRX = wSubParentLRX;           // Parent's lower right x coord.
  this.parentLRY = wSubParentLRY;           // Parent's lower right y coord.
  this.validationMsg = wSubValidationMsg;   // Validation message(s)

  return this;
}

//
// WebSubmenu commands
//
function wSubAddItem(body, help, link, newWindow, style, width, itemName) { with (this) {
  var lastItemAdded = items.length;
  
  items[lastItemAdded] = new WebItem(body, help, link, newWindow, style, width, 
                                     itemName, lastItemAdded, this);
}}

function wSubDebug(debugLevel) { with (this) {
  // Show everything by default
  if (null == debugLevel) debugLevel = 3;

  if (1 <= debugLevel) {
    document.write('..WebSubMenu.debugLevel: ' + debugLevel + '<BR>');
    document.write('..WebSubmenu.name: ' + name + '<BR>');
    document.write('..WebSubmenu.id: ' + id + '<BR>');

    document.write('..WebSubmenu.defaultStyle:<BR>');
    defaultStyle.debug(debugLevel);

//remove    document.write('..WebSubmenu.parentSubmenu.id: ' + 
//remove                    (null == parentSubmenu ? null :  parentSubmenu.id) +
//remove                    '<BR>');
    document.write('..WebSubmenu.parentItem.id: ' + 
                   (null == parentItem ? null : parentItem.id) +
                   '<BR>');
    document.write('..WebSubmenu.layerId: ' + layerId + '<BR>');

    document.write('..WebSubmenu.items.length: ' + items.length + '<BR>');
    for (var i = 0; i < items.length; i++) {
      document.write('<BR>');
      document.write('..WebSubmenu.items [' + i + '] <BR>');
      items[i].debug(debugLevel);
    }
  }

  if (2 <= debugLevel) {
    document.write('..WebSubmenu.kHide: ' + kHide + '<BR>');
    document.write('..WebSubmenu.kShow: ' + kShow + '<BR>');
    document.write('..WebSubmenu.kZIndex: ' + kZIndex + '<BR>');

    document.write('..WebSubmenu.xOffset: ' + xOffset + '<BR>');
    document.write('..WebSubmenu.yOffset: ' + yOffset + '<BR>');
  }

  if (3 <= debugLevel) {
    document.write('..WebSubmenu.isVertical: ' + isVertical + '<BR>');
    document.write('..WebSubmenu.reposition: ' + reposition + '<BR>');
  }
}}

function wSubHide() { with (this) {
  layer.style.visibility = kHide;

//dcf 
// if I try to use the docElement, then the menus hide/show incorrectly.  Check the
// the docElement Id to make sure we have the right element.  Then investigate how
// to set/check the document element field.  Seems to work when creating the document
// element.
//  var docElement = document.getElementById(layerId);
//document.write('DEBUG..layerId: ' + layerId + '<BR>');
//document.write('DEBUG..visibility.hidden: ' + docElement.style.visibility + '<BR>');
//  docElement.style.visibility = kHide;
}}

function wSubSetItemLayerId(menuName) { with (this) {
  // Set the layer id of the last item added.  Since the item name is regularly
  // null, use the lastItemAdded field to track which item needs a layer id.
  if (lastItemAdded < items.length) {
    items[lastItemAdded].setItemLayerId(menuName, id)
  }
}}

function wSubSetItemChild(itemName, childSubmenu) { with (this) {
  var i = 0;
  var itemFound = false;

  while ((i < items.length) && (!itemFound)) {
    if (itemName == items[i].name) {
      items[i].childSubmenu = childSubmenu;
      itemFound = true;
    }
    i++;
  }
}}

function wSubSetLayerId(menuName) { with (this) {
  layerId = menuName + '_' + id;
}}

function wSubMouseClick(itemId) { with (this) {
//
// If a link was found, open the link.  If a new window is desired, open a new 
// window.  If a new window is opened without any window attributes, the new
// window will clone the attributes of the current window (when it was initially
// opened).
  with (items[itemId]) {
    if (null == childSubmenu && link) {
      if (newWindow) {
        window.open(link, '_blank'); 

      } else {
        window.open(link, '_self'); 
      }
    }
  }
}}

function wSubMouseOut(itemId) { with (this.items[itemId]) {
  var isOver = false;
  changeLayer(isOver);
}}

function wSubMouseOver(itemId) { with (this.items[itemId]) {
  var isOver = true;
  changeLayer(isOver);
}}

function wSubSetLayer(menuName) { with (this) {
  layer = document.createElement('DIV');
  layer.id = layerId;
  layer.innerHTML = layerHTML(menuName);
  layer.style.zIndex = kZIndex;
  layer.style.cursor = bcPointer;
  hide();
}}

function wSubSetParentItem(parentItem) {
  this.parentItem = parentItem;
}

function wSubShow() { with (this) {
// dcf
  layer.style.visibility = kShow;
//  var docElement = document.getElementById(layerId);
//  docElement.style.visibility = kShow;
}}

//
// WebSubmenu queries
//
function wSubIsItemSubmenu(itemId) { with (this) {
  return (items[itemId].isSubmenu());
}}

function wSubIsItemValid() { with (this) {
  var validItem = false;                    // if not found, item is invalid

  // Validate the last item added.  Since the item name is regularly null, use
  // the lastItemAdded field to track which item needs to be validated.
  if (lastItemAdded < items.length) {
    validItem = items[lastItemAdded].isValid();
  }
  return (validItem);
}}

function wSubIsShown() { with (this) {
//dcf
  return (kShow == layer.style.visibility ? true : false);
//  var docElement = document.getElementById(layerId);
//  return (kShow == docElement.style.visibility ? true : false);
}}

function wSubItem(itemName) { with (this) {
  var i = 0;
  var found = false;
  
  while (!found && i < items.length) {
    if (itemName == items[i].name) found = true;
    else i++;
  }
  return (found ? items[i] : null);
}}

function wSubLayerHTML(menuName) { with (this) {
  var html = '';
  var xPosition = parentLRX() + xOffset;
  var yPosition = parentLRY() + yOffset;
  
  for (var i = 0; i < items.length; i++) {
    var borderSpace = (items[i].style.borderCSS ? items[i].style.borderSpace
                                                : 0);

    // IE -- automatically adds border space, adjust height/width.
    // NS -- added programmatically, no need to adjust height/width.
    var itemHeight = items[i].style.height +
                     (isIE ? borderSpace * 2 : 0);
    var itemWidth = (items[i].width ? items[i].width 
                                    : items[i].style.width) +
                    (isIE ? borderSpace * 2 : 0);

    html += '<DIV id="' + items[i].layerId + '" ' +
                 (items[i].style.borderCSS ? 'class="' +
                                             items[i].style.borderCSS +
                                             '" '
                                           : '') +
                 'style="position: absolute; ' +
                        'left: ' + xPosition + '; ' +
                        'top: ' + yPosition + '; ' +
                        'width: ' + itemWidth + '; ' +
                        'height: ' + itemHeight + '; ' +
                        (items[i].style.outBackground
                         ? 'background: ' +
                           (isFile(items[i].style.outBackground)
                            ? 'url(' + items[i].style.outBackground + ')'
                            : items[i].style.outBackground) +
                           '; ' 
                         : '') +
                       '" ';

    //
    // Design: the submenu and item id are built as part of the html mouse
    //         events for the layer and are thus passed into the menu mouse
    //         event methods which will eventually work their way down to the
    //         item to determine what needs to occur.
    //
    html += 'onMouseOver="' + menuName + '.mouseOver(' + 
                            id + ',' + items[i].id + ')" ' +
            'onMouseOut="' + menuName + '.mouseOut(' +
                            id + ',' + items[i].id + ')" ' +
            'onClick="' + menuName + '.mouseClick(' +
                            id + ',' + items[i].id + ')" ';

    html += '>' + this.items[i].layerHTML() + '</DIV>';

    items[i].setLRPosition(xPosition + itemWidth + borderSpace * 2,
                           yPosition + itemHeight + borderSpace * 2);

    // 1. remove one borderSpace so that the border looks consistent.
    // 2. IE you need to subtract the border as automatically added.
    // 3. NS you need to add border.
    if (isVertical) {
      yPosition += itemHeight + items[i].style.itemSpace +
                   (isIE ? -1 : 1) *
                   (items[i].style.borderCSS ? items[i].style.borderSpace : 0);
       
    } else {
      xPosition += itemWidth + items[i].style.itemSpace +
                   (isIE ? -1 : 1) *
                   (items[i].style.borderCSS ? items[i].style.borderSpace : 0);
    }
  }
  
  return html;
}}

function wSubItemSubmenuId(itemId) { with (this.items[itemId]) {
  return (itemSubmenuId());
}}

function wSubParentLRX() {with (this) {
  return (null == parentItem ? 0 : parentItem.xPositionLR);
}}

function wSubParentLRY() {with (this) {
  return (null == parentItem ? 0 : parentItem.yPositionLR);
}}

function wSubValidationMsg() {with (this) {
  var validationText = validationMessage;

  for (var i = 0; i < items.length; i++) {
    validationText += items[i].validationMsg();
  }
  return (validationText);
}}

//
// WebItem constructor and methods.
//
function WebItem(body, help, link, newWindow, style, width, name, id, parentSubmenu) {
  // Constants
  this.kLayerIT = 'IT';                     // itemText layer name
  this.kLayerMI = 'MI';                     // submenuIndicator layer name

  // Attributes
  this.body = body;                         // Menu item body
  this.childSubmenu = null;                 // Child submenu reference
  this.help = help;                         // Help shown on mouse over
  this.id = id;                             // Identifier
  this.layerId = null;                      // Unique layer identifier
  this.link = link;                         // Web page link (optional)
  this.name = name;                         // Object name
  this.newWindow = newWindow;               // Open link in a new window
  this.parentSubmenu = parentSubmenu;       // Parent menu reference
  this.style = style;                       // Display style
  this.validationMessage='';                // Validation results
  this.width = width;                       // Display width
  this.xPositionLR = null;                  // Lower right x coordinate
  this.yPositionLR = null;                  // Lower right y coordinate

  // Methods - Commands
  this.changeLayer = wItemChangeLayer       // Change layer properties
  this.debug = wItemDebug;                  // Debug object
  this.setChild = wItemSetChild;            // Set child submenu reference
  this.setItemLayerId = wItemSetItemLayerId;// Set the unique item layer id
  this.setLRPosition = wItemSetLRPosition;  // Set lower right coordinates

  // Methods - Queries
  this.isSubmenu = wItemIsSubmenu;          // Is item a submenu?
  this.isValid = wItemIsValid;              // Validate the item configuration
  this.itemSubmenuId = wItemItemSubmenuId;  // Item's submenu identifier
  this.layerHTML = wItemLayerHTML;          // Item layer (division) HTML
  this.layerIdIT = wItemLayerIdIT;          // Item item text (IT) layer id
  this.layerIdMI = wItemLayerIdMI;          // Item submenu indicator (MI) layer id
  this.validationMsg = wItemValidationMsg;  // Validation message(s)

  return this;
}

//
// WebItem commands
//
function wItemChangeLayer (isOver) { with (this) {
  var itemBackground = (isOver ? style.overBackground : style.outBackground);

  var docElement = document.getElementById(layerIdIT());
  docElement.className = (isOver ? style.overCSS : style.outCSS);

  if (isFile(itemBackground)) {
    docElement.style.background = 'url(' + itemBackground + ')';
  } else {
    docElement.style.background = itemBackground;
  }

  if (childSubmenu && style.submenuIndicator) {
    var docElement = document.getElementById(layerIdMI());
    docElement.className = (isOver ? style.overCSS : style.outCSS);
    if (isFile(itemBackground)) {
      docElement.style.background = 'url(' + itemBackground + ')';
                                                    itemBackground + ')';
    } else {
      docElement.style.background = itemBackground;
    }
  }
}}

function wItemDebug(debugLevel) { with (this) {
  // Show everything by default
  if (null == debugLevel) debugLevel = 3;

  if (1 <= debugLevel) {
    document.write('....WebItem.debugLevel: ' + debugLevel + '<BR>');

    document.write('....WebItem.kLayerIT: ' + kLayerIT + '<BR>');
    document.write('....WebItem.kLayerMI: ' + kLayerMI + '<BR>');

    document.write('....WebItem.id: ' + id + '<BR>');
    document.write('....WebItem.layerId: ' + layerId + '<BR>');
    document.write('....WebItem.layerIdIT: ' + layerIdIT() + '<BR>');
    document.write('....WebItem.layerIdMI: ' + layerIdMI() + '<BR>');
    document.write('....WebItem.link: ' + link + '<BR>');
    document.write('....WebItem.newWindow: ' + newWindow + '<BR>');

    document.write('....WebItem.name: ' + name + '<BR>');
    document.write('....WebItem.parentSubmenu.name: ' + 
                   (null == parentSubmenu ? null : parentSubmenu.name) + '<BR>');

    document.write('....WebItem.style: ' + '<BR>');
    style.debug(debugLevel);
  }

  if (2 <= debugLevel) {
    document.write('....WebItem.body: ' + body + '<BR>');
    document.write('....WebItem.childSubmenu.id: ' +
                   (null == childSubmenu ? null : childSubmenu.name) + '<BR>');
    document.write('....WebItem.width: ' + width + '<BR>');
    document.write('....WebItem.xPositionLR: ' + xPositionLR + '<BR>');
    document.write('....WebItem.yPositionLR: ' + yPositionLR + '<BR>');
  }

  if (3 <= debugLevel) {
    document.write('....WebItem.help: ' + help + '<BR>');
  }
}}

function wItemSetChild(submenu) {
  this.childSubmenu = submenu;
}

function wItemSetItemLayerId(menuName, submenuId) { with (this) {

  layerId = menuName + '_' + submenuId + '_' + id;
}}

function wItemSetLRPosition(xPositionLR, yPositionLR) {
  this.xPositionLR = xPositionLR;
  this.yPositionLR = yPositionLR;
}


//
// WebItem queries
//
function wItemIsSubmenu() { with (this) {
  return (null == childSubmenu ? false : true);
}}

function wItemIsValid() {with (this) {
  var valid = true;

  if (null == body) {
    validationMessage += 'The menu item (' + name + ') needs to ';
                         'have a body value. ';
    valid = false;
  }

  if ((null == name) && (null == link)) {
    validationMessage += 'The menu item (' + name + ') needs to ';
                         'have either a name or a link. ';
    valid = false;
  }

  return (valid);
}}

function wItemItemSubmenuId() { with (this) {
  return (isSubmenu() ? childSubmenu.id : null);
}}

function wItemLayerHTML() { with (this) {
  // IE automatically adds the border and thus there is no need to 
  // adjust for the extra pixels.  Was previously adding the border
  // space to adjust for the border.
  var borderSpace = 0;
  var html = '';
  var itemHeight = style.height - borderSpace * 2;
  var itemWidth = (width ? width : style.width) - borderSpace * 2;

  html += '<DIV id="' + layerIdIT() + '" ' +
               'class="' + style.outCSS + '" ' +
               (help ? 'title="' + help + '" ' : '') +
               'style="position: absolute; ' +
                      'left: ' + borderSpace + '; ' +
                      'top: ' + borderSpace + '; ' +
                      'width: ' + itemWidth + '; ' +
                      'height: ' + itemHeight + '; ' +
                      '" ';

  html += '>' + body + '</DIV>';

  if (childSubmenu && style.submenuIndicator) {
    html += '<DIV id="' + layerIdMI() + '" ' +
                 'class="' + style.outCSS + '" ' +
                 (help ? 'title="' + help + '" ' : '') +
                 'style="position: absolute; ' +
                        'left: ' + (borderSpace +
                                    itemWidth +
                                    style.indicatorSpace) + '; ' +
                        'top: ' + borderSpace + '; ' +
                        'height: ' + itemHeight + '; ' +
                        '" ';

    html += '>' + style.submenuIndicator + '</DIV>';
  }
 
  return html;
}}

function wItemLayerIdIT() { with (this) {
  return layerId + '_' + kLayerIT;
}}

function wItemLayerIdMI() { with (this) {
  return layerId + '_' + kLayerMI;
}}

function wItemValidationMsg() {with (this) {
  return (validationMessage);
}}
-->
