SliderMenu.hideDelay = 800;
SliderMenu.slideTime = 600;

SliderMenu.reference = {topLeft:1, topRight:2, bottomLeft:3, bottomRight:4};
SliderMenu.registry  = [];
SliderMenu._maxZ     = 100;

SliderMenu.initialize = function initialize() {
  for(var i = 0, menu = null; menu = this.registry[i]; i++) menu.initialize();
};

SliderMenu.renderAll = function renderAll() {
  var aMenuHtml = [];
  for(var i = 0, menu = null; menu = this.registry[i]; i++) aMenuHtml[i] = menu.toString();
  document.write(aMenuHtml.join(""));
};

function SliderMenu(oActuator, iReferencePoint, parentMenuSet) {

  // public methods
  this.addItem    = addItem;
  this.toString   = toString;
  this.initialize = initialize;
  this.isOpen     = false;
  this.show       = show;
  this.hide       = hide;
  this.items      = [];

  // initialization
  this.index = SliderMenu.registry.length;

  SliderMenu.registry[this.index] = this;

  var id = "SliderMenu" + this.index;
  var contentHeight = null;
//  var contentWidth = null;
//  var childMenuSet = null;
  var animating = false;
  var childMenus = [];
  var slideAccel = -1;
  var elmCache = null;
  var ready = false;
  var _this = this;
  var a = null;
  var dim = null;

  // private and public method implimentations
  function addItem(sText, sUrl) {
    var item = new SliderMenuItem(sText, sUrl, this);
    item._index = this.items.length;
    this.items[item._index] = item;
  };

  function initialize() {
    initCache();
    initEvents();
    initSize();
    ready = true;
  };

  function show() {
    if(ready) {
      _this.isOpen = true;
      animating = true;
      setContainerPos();
      elmCache["clip"].style.visibility = "visible";
      elmCache["clip"].style.zIndex     = SliderMenu._maxZ++;
      slideStart();
    };
  };

  function hide() {
    if(ready) {
      _this.isOpen = false;
      animating = true;
      slideStart();
    };
  };

  function setContainerPos() {
    var sub  = (oActuator.constructor == SliderMenuItem);
    var act  = (sub ? oActuator.parentMenu.elmCache["item"][oActuator._index] : oActuator);
    var el   = act;
    var x    = 0;
    var y    = 0;
    var maxX = 10000;
    var maxY = 10000;

    while(sub ? (el.parentNode.className.indexOf("SliderMenu") == -1) : el.offsetParent) {
      x += el.offsetLeft;
      y += el.offsetTop;
      if(el.scrollLeft) x -= el.scrollLeft;
      if(el.scrollTop)  y -= el.scrollTop;
      el = el.offsetParent;
    };

    if(oActuator.constructor == SliderMenuItem) {
      x += parseInt(el.parentNode.style.left);
      y += parseInt(el.parentNode.style.top);
    };

    switch(iReferencePoint) {
      case SliderMenu.reference.topLeft: {
        break;
      }
      case SliderMenu.reference.topRight: {
        x += act.offsetWidth;
        break;
      }
      case SliderMenu.reference.bottomLeft: {
        y += act.offsetHeight;
        break;
      }
      case SliderMenu.reference.bottomRight: {
        x += act.offsetWidth;
        y += act.offsetHeight;
        break;
      }
    };

    elmCache["clip"].style.left = (x + "px");
    elmCache["clip"].style.top  = (y + "px");

  };

  function slideStart() {
    var x0 = parseInt(elmCache["content"].style.top);
    var x1 = (_this.isOpen ? 0 : -dim);
    if(a != null) a.stop();
    a = new Accelimation(x0, x1, SliderMenu.slideTime, slideAccel);
    a.onframe = slideFrame;
    a.onend   = slideEnd;
    a.start();
  };

  function slideFrame(x) {
    elmCache["content"].style.top = (x + "px");
  };

  function slideEnd() {
    if(!_this.isOpen) elmCache["clip"].style.visibility = "hidden";
    animating = false;
  };

  function initSize() {
    elmCache["items"].width = Math.max(elmCache["items"].offsetWidth, 168);
    var ow = elmCache["items"].offsetWidth;
    var oh = elmCache["items"].offsetHeight;
    var ua = navigator.userAgent.toLowerCase();
    elmCache["clip"].style.width  = (ow + "px");
    elmCache["clip"].style.height = (oh + "px");
    elmCache["content"].style.width  = (ow + "px");
    elmCache["content"].style.height = (oh + "px");
    contentHeight = oh;
    dim = contentHeight;
    elmCache["content"].style.top = (-dim + "px");
    elmCache["clip"].style.visibility = "hidden";
    if((ua.indexOf("mac") == -1) || (ua.indexOf("gecko") > -1)) {
      elmCache["background"].style.width  = (ow + "px");
      elmCache["background"].style.height = (oh + "px");
    }
/*
    else {
      // set background div to offset size
      elmCache["background"].firstChild.width  = ow;
      elmCache["background"].firstChild.height = oh;
    };
*/
  };

  function initCache() {
    var menu = document.getElementById(id);
    var all = (menu.all ? menu.all : menu.getElementsByTagName("*")); // IE/win doesn't support * syntax, but does have the document.all thing
    elmCache = {};
    elmCache["clip"] = menu;
    elmCache["item"] = [];
    for(var i = 0, elm = null; elm = all[i]; i++) {
      switch(elm.className) {
        case "items":
        case "content":
        case "background": {
          elmCache[elm.className] = elm;
          break;
        }
        case "item": {
          elm._index = elmCache["item"].length;
          elmCache["item"][elm._index] = elm;
          break;
        }
      };
    };
    _this.elmCache = elmCache;  // hack!
  };

  function initEvents() {
    if(typeof(oActuator.tagName) != "undefined") {
      oActuator.onmouseover = function actuator_mouseover() {
        parentMenuSet.showMenu(_this);
      };
      oActuator.onmouseout  = function actuator_mouseout() {
        parentMenuSet.hideMenu(_this);
      };
    };
    elmCache["content"].onmouseover = function content_mouseover() {
      if(!animating) {
        parentMenuSet.showMenu(_this);
      };
    };
    elmCache["content"].onmouseout = function content_mouseout() {
      if(!animating) parentMenuSet.hideMenu(_this);
    };
  };

  function toString() {
    var aHtml = [];
    for(var i = 0, item = null; item = this.items[i]; i++) aHtml[i] = item.toString(childMenus[i]);
    return '<div id="' + id + '" class="SliderMenu">'
           + '  <div class="content">'
           + '    <table border="0" cellpadding="0" cellspacing="0" class="items">' + aHtml.join('') + '</table>'
           + '    <div class="background"></div>'
           + '  </div>'
           + '</div>';
  };
};

SliderMenuSet.registry = [];

function SliderMenuSet(iReferencePoint) {

  this.addMenu     = addMenu;
  this.showMenu    = showMenu;
  this.hideMenu    = hideMenu;
  this.hide        = hide;
  this.hideCurrent = hideCurrent;

  var menus = [];
  var _this = this;
  var current = null;

  this.index = SliderMenuSet.registry.length;
  SliderMenuSet.registry[this.index] = this;

  function addMenu(oActuator) {
    var m = new SliderMenu(oActuator, iReferencePoint, this);
    menus[menus.length] = m;
    return m;
  };

  function showMenu(oMenu) {
    if(oMenu != current) {
      if(current != null) hide(current);
      current = oMenu;
      oMenu.show();
    }
    else {
      cancelHide(oMenu);
    };
  };

  function hideMenu(oMenu) {
    if(current == oMenu && oMenu.isOpen) {
      if(!oMenu.hideTimer) scheduleHide(oMenu);
    };
  };

  function scheduleHide(oMenu) {
    oMenu.hideTimer = window.setTimeout("SliderMenuSet.registry[" + _this.index + "].hide(SliderMenu.registry[" + oMenu.index + "])", SliderMenu.hideDelay);
  };

  function cancelHide(oMenu) {
    if(oMenu.hideTimer) {
      window.clearTimeout(oMenu.hideTimer);
      oMenu.hideTimer = null;
    };
  };

  function hide(oMenu) {
    if(!oMenu && current) oMenu = current;
    if(oMenu && (current == oMenu) && oMenu.isOpen) hideCurrent();
  };

  function hideCurrent() {
    if(null != current) {
      cancelHide(current);
      current.hideTimer = null;
      current.hide();
      current = null;
    };
  };
};

function SliderMenuItem(sText, sUrl, oParent) {
  this.toString   = toString;
  this.text       = sText;
  this.url        = sUrl;
  this.parentMenu = oParent;
  function toString(bDingbat) {
    return ('<tr class="item"><td><a href="' + sUrl + '" alt="">' + sText + '</a></td></tr>');
  };
};


//=====================================================================
// bezier functions lifted from the lib_animation.js file in the 
// 13th Parallel API. www.13thparallel.org
//=====================================================================

function Accelimation(from, to, time, zip) {
  if(typeof(zip)  == "undefined") zip  = 0;
  if(typeof(unit) == "undefined") unit = "px";
  this.x0      = from;
  this.x1      = to;
  this.dt      = time;
  this.zip     = -zip;
  this.unit    = unit;
  this.timer   = null;
  this.onend   = new Function();
  this.onframe = new Function();
};

Accelimation.prototype.start = function Accelimation_start() {
  this.t0 = (new Date()).getTime();
  this.t1 = this.t0 + this.dt;
  var dx  = this.x1 - this.x0;
  this.c1 = this.x0 + ((1 + this.zip) * dx / 3);
  this.c2 = this.x0 + ((2 + this.zip) * dx / 3);
  Accelimation._add(this);
};

Accelimation.prototype.stop = function() {
  Accelimation._remove(this);
};

Accelimation.prototype._paint = function Accelimation__paint(time) {
  if(time < this.t1) {
    var elapsed = time - this.t0;
    this.onframe(Accelimation._getBezier(elapsed/this.dt, this.x0, this.x1, this.c1, this.c2));
  }
  else {
    this._end();
  };
};

Accelimation.prototype._end = function Accelimation__end() {
  Accelimation._remove(this);
  this.onframe(this.x1);
  this.onend();
};

Accelimation._add = function Accelimation__add(o) {
  var index = this.instances.length;
  this.instances[index] = o;
  if(this.instances.length == 1) {
    this.timerID = window.setInterval("Accelimation._paintAll()", 12);
  };
};

Accelimation._remove = function Accelimation__remove(o) {
  for(var i = 0; i < this.instances.length; i++) {
    if(o == this.instances[i]) {
      this.instances = this.instances.slice(0, i).concat( this.instances.slice(i + 1) );
      break;
    };
  };
  if(this.instances.length == 0) {
    window.clearInterval(this.timerID);
    this.timerID = null;
  };
};

Accelimation._paintAll = function Accelimation__paintAll() {
  var now = (new Date()).getTime();
  for(var i = 0; i < this.instances.length; i++) {
    this.instances[i]._paint(now);
  };
};

Accelimation._B1 = function Accelimation__B1(t) {
  return t*t*t;
};
Accelimation._B2 = function Accelimation__B2(t) {
  return 3*t*t*(1-t);
};
Accelimation._B3 = function Accelimation__B3(t) {
  return 3*t*(1-t)*(1-t);
};
Accelimation._B4 = function Accelimation__B4(t) {
  return (1-t)*(1-t)*(1-t);
};

Accelimation._getBezier = function Accelimation__getBezier(percent,startPos,endPos,control1,control2) {
  return ((endPos * this._B1(percent)) + (control2 * this._B2(percent)) + (control1 * this._B3(percent)) + (startPos * this._B4(percent)));
}

Accelimation.instances = [];
Accelimation.timerID   = null;

if(window.attachEvent) {
  var cearElementProps = ["data", "onmouseover", "onmouseout", "onmousedown", "onmouseup", "ondblclick", "onclick", "onselectstart", "oncontextmenu"];
  window.attachEvent("onunload", function() {
    var el;
    for(var d = document.all.length; d--; ) {
      el = document.all[d];
      for(var c = cearElementProps.length; c--; ) el[cearElementProps[c]] = null;
    };
  });
};

