// Object used to associate a menu's unique name with its Menu object.
var menus = new Object();

// Function that retargets menu events to their menu object handlers.
function retarget(evt) {
  evt = translateEvent(evt);
  var itemElem = getElementFromEvent(evt);
  var itemText;
  if (itemElem.id) {
    itemText = itemElem.id;
  }
  else {
    itemText = itemElem.firstChild.nodeValue;
  }
  var menuElem = itemElem.parentNode;
  var menuObj = menus[menuElem.id];
  var itemObj;
  for (i in menuObj.items) {
    if (menuObj.items[i].item == itemText) {
      itemObj = menuObj.items[i];
      break;
    }
  }
  switch (evt.type) {
    case 'mouseover': menuObj.mouseOn(menuElem, itemElem, itemObj); break;
    case 'mouseout': menuObj.mouseOff(menuElem, itemElem, itemObj); break;
    case 'click': menuObj.click(menuElem, itemElem, itemObj); break;
  }
  if (evt) evt.cancelBubble = true;
}

// This function retargets a timeout event to the menu object that requested
// the timeout.
function hideMenu(name) {
  var obj = menus[name];
  if (obj) {
    obj.timer = 0;
    obj.hide();
  }
}

// Menu object constructor.
function Menu(name, items) {
  this.name = name;
  this.items = items;
  this.node = null;
  this.timer = 0;
  this.activeSubmenu = null;
}

Menu.prototype.createSubmenu = function(loc, curSel, menuElement, itemName, submenu) {
  var elem = menuElement.firstChild;
  while (elem && elem.firstChild.nodeValue != itemName) {
    elem = elem.nextSibling;
  }
  if (!elem) return;
  var height = elem.offsetHeight;
  var subtop = 0;
  var subleft = 0;
  while (elem.id != loc) {
    subtop += elem.offsetTop;
    subleft += elem.offsetLeft;
    elem = elem.offsetParent;
  }
  var ul = document.createElement("ul");
  submenu.node = ul;
  ul.id = submenu.name;
  ul.className = "subMenu";
  menus[submenu.name] = submenu;
  ul.style.top = subtop + height + "px";
  ul.style.left = subleft + "px";
  for (var i in submenu.items) {
    var li = document.createElement("li");
    li.className = "subitem hot lowlight";
    var text = document.createTextNode(submenu.items[i].item);
    li.appendChild(text);
    li.onmouseover = retarget;
    li.onmouseout  = retarget;
    if (submenu.items[i].item != curSel) {
      li.onclick = retarget;
    }
    else {
      li.className = "subitem cool lowlight";
    }
    ul.appendChild(li);
  }
  elem.appendChild(ul);
}

Menu.prototype.show = function() {
  if (this.node) {
    this.node.style.visibility = "visible";
  }
}

Menu.prototype.hide = function() {
  if (this.node) {
    this.node.style.visibility = "hidden";
  }
}

Menu.prototype.delayHide = function() {
  var hideStr = "hideMenu('" + this.name + "');";
  this.timer = window.setTimeout(hideStr, 250);
}

Menu.prototype.cancelHide = function() {
  if (this.timer != 0) {
    window.clearTimeout(this.timer);
  }
}

Menu.prototype.create = function(loc, curSel) {
  var elem = document.getElementById(loc);
  var ul = document.createElement("ul");
  this.node = ul;
  ul.id = this.name;
  menus[this.name] = this;
  
  var i;
  for (i in this.items) {
    var li = document.createElement("li");
    li.className = "menuitem hot lowlight";
    var text = document.createTextNode(this.items[i].item);
    li.appendChild(text);
    if (this.items[i].item != curSel) {
      li.onmouseover = retarget;
      li.onmouseout  = retarget;
      li.onclick     = retarget;
    }
    else {
      li.className = "menuitem cool lowlight";
    }
    ul.appendChild(li);
  }
  elem.appendChild(ul);
  
  for (i in this.items) {
    if (this.items[i].submenu) {
      this.createSubmenu(loc, curSel, ul, this.items[i].item, this.items[i].submenu);
    }
  }
}

Menu.prototype.mouseOn = function(menuElem, itemElem, itemObj) {
  if (itemElem) {
    classStr = itemElem.className;
    classStr = classStr.replace(/lowlight/, "highlight");
    itemElem.className = classStr;
  }
  // If the mouse moved from a submenu back to a parent menu
  // (activeSubmenu is non-null), keep submenu up.
  if (this.activeSubmenu) {
    this.activeSubmenu.cancelHide();
    // But if we've moved onto a menu item that doesn't
    // have a submenu or has a new submenu then hide the
    // existing submenu.
    if (!itemObj.submenu || (this.activeSubmenu != itemObj.submenu)) {
        this.activeSubmenu.hide();
        this.activeSubmenu = null;
    }
  }
  // If the menu item we've moused onto has a submenu, deal with it.
  if (itemObj.submenu) {
    itemObj.submenu.show();
    this.activeSubmenu = itemObj.submenu;
  }
  // If the mouse moved onto a submenu item, keep that
  // submenu up.
  if (itemElem.className.match(/.*subitem.*/)) {
    this.cancelHide();
  }
}

Menu.prototype.mouseOff = function(menuElem, itemElem, itemObj) {
  if (itemElem) {
    classStr = itemElem.className;
    classStr = classStr.replace("highlight", "lowlight");
    itemElem.className = classStr;
  }
  if (itemObj.submenu) {
    itemObj.submenu.delayHide();
  }
  else if (itemElem.className.match(/.*subitem.*/)) {
    this.delayHide();
  }
}

Menu.prototype.click = function(menuElem, itemElem, itemObj) {
  if (itemObj && itemObj.action) {
    this.mouseOff(menuElem, itemElem, itemObj);
    eval(itemObj.action);
  }
}

