var menu = null;

function Menu(section, subsection) {
  /*
   Animated menu object
   ==========================
   Arguments:
   -----------------
   section       // (int) section (if any) to show as "highlighted" (fixed open, static)
   subsection    // (int) subsection (if any) to highlight
  */
  var self = this;
  this.menu = null;
  this.menuID = 'menu'; // menu container element ID
  this.containers = [];
  this.frames = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
  this.tweens = [];
  this.menuGroups = [];
  this.menuHeight = [];
  this.menuItems = [];
  this.items = [];
  this.itemCount = 0;

  this.createTweens = function() {
    // Generate array of pixel offsets for nav containers
    for (var i = 0; i < this.containers.length; i++) {
      this.tweens[i] = [0];
      this.tmp = 0;
      menuOffset = ua.isIE ? 10 : 9;

      for (var j = 0; j < this.frames.length; j++) {
        this.tmp += ((this.menuHeight[i] + menuOffset) * (this.frames[j] * .01));
        this.tweens[i][this.tweens[i].length] = parseInt(this.tmp);
      }

      this.tweens[i][this.tweens[i].length] = this.menuHeight[i] + menuOffset;
    }
  }

  this.findNode = function(n) {
    // find a non-text node
    if (! n || ! n.nodeType)
      return null;

    while (n.nodeType == 3 && n.parentNode) {
      n = n.parentNode;
    }

    return n;
  }

  this.isChildOfId = function(targetId, target) {
    if (! target)
      return false;

    do {
      if (target.id && target.id.indexOf(targetId) + 1) {
        return true;
      }
      else if (target.parentNode) {
        target = target.parentNode;
      }
    } while (target.parentNode && target.parentNode.nodeName != 'BODY');

    return false;
  }

  this.init = function() {
    // Grab items and create menu item objects, assign event handlers
    this.menu = document.getElementById(this.menuID);
    itemsTmp = this.menu.getElementsByTagName('li');

    for (var i = 0; i < itemsTmp.length; i++) {
      if (itemsTmp[i].className && itemsTmp[i].className.indexOf('nav-groups') + 1) {
        this.menuGroups[this.menuGroups.length] = itemsTmp[i];

        // only add to item count if menu has sub-items
        if (this.menuGroups[this.menuGroups.length - 1].getElementsByTagName('ul').length)
          this.itemCount++;
      }
    }

    if (this.supported) {
      // remove CSS offset for static containers
      for (i = 0; i < this.itemCount; i++) {
        if (section != i) {
          // don't remove padding from "stuck" open section
          document.getElementById('sub-group' + i).style.paddingTop = '0px';
        }
        else if (section == i && subsection + 1) {
          // show highlighted sub-menu item, if applicable
          document.getElementById('nav-group' + i).getElementsByTagName('a')[subsection].className = 'active';
        }
      }
    }

    for (i = 0; i < this.itemCount; i++) {
      // create container objects and nav item objects
      this.containers[i] = this.menuGroups[i].getElementsByTagName('ul')[0];
      this.menuHeight[i] = this.containers[i].offsetHeight; // height plus offset of margin
      this.menuItems[i] = new this.MenuItem(i);

      if (this.supported) {
        // supported browsers: assign animation event handlers or show static based on global "section" variable
        this.menuItems[i].show();

        if (section != i) {
          // roll up menu if not selected
          this.containers[i].style.marginTop = ((this.menuHeight[i]) * - 1) + 'px';
        }
      }
      else {
        // unsupported browsers: point to static show/hide function instead
        this.menuItems[i].move = this.menuItems[i].moveStatic;

        if (section == i && subsection + 1) {
          document.getElementById('nav-group' + i).getElementsByTagName('a')[subsection].className = 'active';
        }
      }

      if ('nav-group' + section != this.menuGroups[i].id) {
        // Assign event handler to top-level anchor tag
        handler = this.menuItems[i].eventHandler;
        this.menuGroups[i].getElementsByTagName('a')[0].onmouseover = handler;
        this.menuGroups[i].getElementsByTagName('a')[0].onmouseout = handler;
        this.menuGroups[i].getElementsByTagName('div')[1].onmouseout = handler;
      }
      else {
        // do not assign event handlers - show nav and leave open
        this.menuItems[i].show();
      }
    }
  }

  this.ns6Fix = function() {
    // netscape 6.x "invisible links/disappearing float" nav workaround (strange browser bug?)
    // fix: change a display property of menu container (causing a reflow)
    document.getElementById(this.menuID).style.width = '100%';
  }

  this.MenuItem = function(menuIndex) {
    this.direction = null;
    this.menuIndex = menuIndex;
    this.frameIndex = 0;
    this.defaultClassName = '';
    this.timers = [];
    this.active = 0;
    var me = this;

    this.animate = function() {
      if (this.frameIndex < 0 || this.frameIndex > self.tweens[this.menuIndex].length) {
        this.frameIndex = 0;
      }
      else {
        if (this.frameIndex >= self.tweens[this.menuIndex].length) {}
        else {
          self.containers[this.menuIndex].style.marginTop
            = (self.menuHeight[menuIndex] * - 1) + (this.direction == 1
              ? 0 : - 1) + (self.tweens[this.menuIndex][this.frameIndex]) + 'px';
        }
      }

      this.frameIndex += this.direction;
    }

    this.reset = function(msg) {
      // reset after previous animation
      this.active = 0;
      this.timers = [];

      // catch potential frame mis-counts from excessive event firing
      if (this.frameIndex >= self.tweens[this.menuIndex].length) {
        this.frameIndex = self.tweens[this.menuIndex].length - 1;
      }
    }

    this.clearTimers = function() {
      for (var i = 0; i < this.timers.length; i++) {
        clearTimeout(this.timers[i]);
      }

      this.timers = [];
    }

    this.eventHandler = function(e) {
      e = e || window.event;

      if (e.relatedTarget || e.target) {
        var eRelatedTarget = self.findNode(e.relatedTarget);
        var eTarget = self.findNode(e.target);
      }

      if (e.type == 'mouseover' && ! e.srcElement) {
        // W3C-style mouseover
        if (e.target == this) {
          me.move(1);
        }
      }
      else if (e.type == 'mouseover') {
        // IE-style mouseover
        me.move(1);
      }
      else if (e.type == 'mouseout' && ! e.srcElement) {
        // W3C-style mouseout
        if (e.target == this) {
          if (! menu.isChildOfId('nav-group' + me.menuIndex, eRelatedTarget)) {
            me.move(- 1);
          }
        }
        else if (! menu.isChildOfId('nav-group' + me.menuIndex, eRelatedTarget)) {
          // not related, nor a child.
          me.move(- 1);
        }
      }
      else if (e.type == 'mouseout') {
        // IE-style mouseout
        if (! menu.isChildOfId('nav-group' + me.menuIndex, e.toElement)) {
          me.move(- 1);
        }
      }
    }

    this.hide = function() {
      with (self.containers[me.menuIndex].style) {
        if (visibility != 'hidden')
          visibility = 'hidden';
      }
    }

    this.show = function() {
      with (self.containers[me.menuIndex].style) {
        if (visibility != 'visible')
          visibility = 'visible';
      }
    }

    this.moveStatic = function(direction, evt) {
      if (direction == 1) {
        me.show();
      }
      else {
        me.hide();
      }
    }

    this.move = function(direction) {
      if (this.active == 0) {
        // not currently moving
        this.clearTimers();

        if (this.frameIndex > 1 && direction == 1) {
          return false;
        }

        this.active = 1;
        this.direction = direction;
        this.timeout = 0;

        for (var i = 0; i < self.tweens[this.menuIndex].length; i++) {
          this.timeout += 20;
          this.timers[this.timers.length] = setTimeout("menu.menuItems[" + this.menuIndex + "].animate()",
                                                       this.timeout);
        }

        this.timers[this.timers.length] = setTimeout("menu.menuItems[" + this.menuIndex + "].reset()", this.timeout);
      }
      else {
        // nav is currently moving
        this.clearTimers();
        this.reset();
        this.timeout = 0;
        this.direction = (this.direction == - 1 ? 1 : - 1);

        if (this.direction == 1) {
          for (var i = this.frameIndex; i < self.tweens[this.menuIndex].length; i++) {
            this.timeout += 20;
            this.timers[this.timers.length] = setTimeout("menu.menuItems[" + this.menuIndex + "].animate()",
                                                         this.timeout);
          }
        }
        else {
          for (var i = this.frameIndex; i > 0; i--) {
            this.timeout += 20;
            this.timers[this.timers.length] = setTimeout("menu.menuItems[" + this.menuIndex + "].animate()",
                                                         this.timeout);
          }
        }

        this.timers[this.timers.length] = setTimeout("menu.menuItems[" + this.menuIndex + "].reset()", this.timeout);
      }
    }
  } // MenuItem

  // Menu() constructor

  // determine browser type, assign animate handlers only where supported
  this.supported = true;

  if (ua.isMac && (ua.isSafari || ua.isIE || ua.equivalentMozilla < 1.0)) {
    this.supported = false;
  }
  else if (ua.isMac && ua.equivalentMozilla >= 1.1) {
    this.supported = true;
  }
  else if (ua.isMac) {
    this.supported = false;
  }
  else if (ua.isWin32) {
    if ((ua.isNS && ua.equivalentMozilla < 1.1) || ua.isNS6x) {
      this.supported = false;
    }
  }

  if (ua.isNS6x) {
    // check for netscape 6 display bug
    setTimeout("menu.ns6Fix()", 20); // give it time to react
  }

  this.init();
  this.createTweens();
}
