var NavMenu = {
  /**
   * "Private" variables. Don't change. 
   */
  //Keep track of which div <a> tags we've added observer methods to
  //so we don't keep adding more and more.
  alreadyObserved : new Array(),
  //The order of displayed menus
  menuTree : new Array(),
  //Which menu you're currently hovering on
  currentMenu : null,
  hideTimer : null,
  
  /**
   * "Public" variables. Modify to change behavior of menu.
   */
  
  /** 
   * Set to false for a vertical menu.
   */
  horizontal : true,
  
  /**
   * 1st-level menus can have a special offset from the root menu. 
   */
  rootTopOffset: 0,
  rootLeftOffset: 0,
  
  /**
   * All subsequent menus use these offsets as they pop up from each other.
   */
  topOffset: -3,
  leftOffset: 1,
  
  /**
   * TODO: Since IE and FF differ in padding and widths, it may be necessary
   * to add an IE-specific set of offsets.
   */
  
  /**
   * Menu hide delay (ms)
   */
  hideDelay: 250,
  
  /**
   * <a> hover class
   */
  hoverClass: "navMenuHover",
  
  /**
   * Start from the last menu and hide back to pos.
   * 
   * @param {int} pos Only trim down to navMenu pos. Defaults to 0.
   */
  hideMenus : function(pos) {
    pos = (pos) ? pos : 0;
    //If pos is -1, then this was called from the timeout hideMenu. This is 
    //when we have to remove the highlighting from the root menu, too.
    if (pos == -1) {
      $(NavMenu.menuTree[0]).select("a").invoke("removeClassName", NavMenu.hoverClass);
      pos = 0;
    }
    for (i = (NavMenu.menuTree.length-1); i > pos; i--) {
      $(NavMenu.menuTree[i]).hide();
      $(NavMenu.menuTree[i])
        .select("a").invoke("removeClassName", NavMenu.hoverClass);
      NavMenu.menuTree[i] = null;
    }
    NavMenu.menuTree = NavMenu.menuTree.compact();
  },
  
  /**
   * Trims children from the menuTree up until the current parent menu.
   * child is the submenu that should now follow parent. If the child is 
   * different, then the user has moved the cursor to another submenu. So
   * we need to trim off the tree that existed under the previous menu
   * so navMenu new one can display its children.
   * @param {string} parent
   * @param {string} child
   */
  trimTree : function(parent, child) {
    //The parent menu will at least be 0 (the top menu item)
    pos = NavMenu.menuTree.indexOf(parent);
    //The next link in the tree should be child. If it's not
    //then the user has moved to another menu option, so trim the tree
    //from that point down.
    if (NavMenu.menuTree[pos+1] != child) {
      NavMenu.hideMenus(pos);
    }
  },
  
  /**
   * Shows a menu. If the parent is the 1st item in the menuList
   * and horizontal menus is set, then the submenu is displayed
   * underneath.
   *  
   * @param {string} parent
   * @param {Object} link
   * @param {string} target
   */
  showMenu : function(parent, link, target) {
    topOffset  = (parent == NavMenu.menuTree[0])
               ? NavMenu.rootTopOffset
               : NavMenu.topOffset;
    leftOffset = (parent == NavMenu.menuTree[0])
               ? NavMenu.rootLeftOffset
               : NavMenu.leftOffset;
    parent = $(parent);
    link   = $(link);
    parentDimensions = parent.getDimensions();
    
    linkOffset = link.cumulativeOffset();
    linkDimensions = link.getDimensions();

    /**
     * If you're using the left:-999em option, then the div won't actually be
     * invisible. If you position it and want to add a fade in effect, the first
     * time it'll just appear and only fade after that. So, hide it if it's
     * visible and then show it.
     */
    if ($(target).visible()) { $(target).hide(); }
    $(target).style.position = "absolute";
    if (parent.id == NavMenu.menuTree[0] && NavMenu.horizontal) {
      $(target).style.top  =  topOffset + linkOffset.top  + linkDimensions.height + "px";
      $(target).style.left = leftOffset + linkOffset.left + "px";
    } else {
      $(target).style.top = topOffset + parent.offsetTop + link.offsetTop + "px";
      $(target).style.left = leftOffset + parent.offsetLeft + parentDimensions.width + "px";
    }
    NavMenu.initMenu(target);
    $(target).show();
  },
  
  /**
   * Initializes a menu before displaying it. Used to initialize the root menu
   * as well as all child menus as soon as you hover an an <a> tag.
   * @param {string} menuId Menu to initialize
   */
  initMenu : function(menuId) {
    //Add navMenu menu to the tree if it's not already in there.
    if (NavMenu.menuTree.indexOf(menuId) == -1) {
      NavMenu.menuTree[NavMenu.menuTree.length] = menuId;
    }
    //Once we've added all these observe events, we don't want to add
    //them again, so first check if navMenu menu item has been marked
    if (!NavMenu.alreadyObserved[menuId]) {
      NavMenu.alreadyObserved[menuId] = true;
      
      /* 
       * Get all the <a> tags in navMenu element and add a mouseover event to
       * show the submenu indicated by its rel tag. Also set its rev tag
       * to point back to the parent menu. The <a> tags essentially form 
       * a link between menus, with rev pointing to the element containing
       * the link and rel pointing to the elewent to display when it's hovered
       * on.
       */
      $(menuId).select("a").each(function(link){
        $(link).rev = menuId;
        $(link).observe("mouseover", function(){
          
          $(menuId).select("a").invoke("removeClassName", NavMenu.hoverClass);
          $(link).addClassName(NavMenu.hoverClass);
          NavMenu.trimTree(menuId, link.rel);
          if (link.rel != "") {
            NavMenu.showMenu(menuId, $(link), link.rel);
          }
        });
      });
      
      //As long as we're hovering on one of the menu elements,
      //prevent menu hiding
      $(menuId).observe("mouseover", function() {
        clearTimeout(NavMenu.hideTimer);
        NavMenu.currentMenu = menuId;
      });
  
      //Initiate a timed menu hide. It will get stopped if
      //we move on to any other menu element. See above.   
      $(menuId).observe("mouseout", function(){
        NavMenu.hideTimer = setTimeout("NavMenu.hideMenus(-1);", NavMenu.hideDelay);
      });
    }
  }

};
