[DOM Menu] :: Example 1 :: Horizontal Menu : Menu « GUI Components « JavaScript DHTML






[DOM Menu] :: Example 1 :: Horizontal Menu


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"
            "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
  <head>
    <title>[DOM Menu] :: Example 1 :: Horizontal Menu</title>
    <style>
body {
    margin: 0;
    padding: 10px;
}
a:link, a:visited, a:active {
    font-family: Verdana, sans-serif; 
    font-size: 12px;
    text-decoration: underline;
    color: #000066;
    font-weight: bold;
}
a:hover {
    text-decoration: none;
}
div.p {
    font-family: Verdana, sans-serif; 
    font-size: 12px;
    margin: 0;
    padding: 10px;
}
div.small {
    font-family: Verdana, sans-serif; 
    font-size: 10px;
}
div.title {
    color: #000066;
    padding-left: 1px;
    font-family: monospace;
    letter-spacing: 2px;
    font-size: 12px;
    line-height: 9px;
    height: 9px;
    margin-bottom: 1px;
}
div.main {
    border: 1px solid #000066;
}
/* Default Style (Opera inspired) */
div.domMenu_menuBar {
    border: solid #7E7E7E;  
    border-width: 1px 0 0 1px;
}
div.domMenu_menuElement {
    font-family: Arial, sans-serif; 
    font-size: 12px;
    border: solid #7E7E7E;  
    border-width: 0 1px 1px 0;
    background: url(gradient.png) repeat-x; 
    color: #0F0F0F;
    text-align: center;
    height: 28px;
    line-height: 28px;
    vertical-align: middle;
}
div.domMenu_menuElementHover {
    background: url(gradient_hover.png) repeat-x;
}
div.domMenu_subMenuBar {
    border: solid #7E7E7E 1px;
    background-color: #FFFFFF;
    padding-bottom: 1px;
    opacity: .9;
    filter: alpha(opacity=90);
}
div.domMenu_subMenuElement {
    font-family: Arial, sans-serif; 
    font-size: 12px;
    border: solid #CCCCCC 1px;
    margin: 1px 1px 0 1px;
    color: #0F0F0F;
    padding: 2px 7px;
}
div.domMenu_subMenuElementHover {
    background-color: #EFEFEF;
}
/* Keramik Style */
div.keramik_menuBar {
    padding: 2px 4px 0 4px;
}
div.keramik_subMenuBar {
    background: url(keramik_gradient_h.gif) repeat-y;
    border: 1px solid;
    border-color: #FFFFFF #535352 #535352 #FFFFFF;
}
div.keramik_menuElement, div.keramik_subMenuElement {
    border: 0;
    color: #535352;
    font-family: serif; 
    font-size: 12px;
    line-height: 14px;
    text-align: left;
    padding: 3px 5px;
}
div.keramik_subMenuElement {
    padding: 3px 3px 3px 13px;
}
div.keramik_menuElementHover {
    padding: 2px 4px;
    border: 1px solid;
    border-color: #535352 #FFFFFF #FFFFFF #535352;
    background-color: #FFFFFF;
}
div.keramik_subMenuElementHover {
    padding: 2px 2px 2px 12px;
    border: 1px solid;
    border-color: #82A0C2;
    background: url(keramik_bubble.gif) repeat-x;
}
div.domMenu_subMenuElementHeading {
    font-weight: bold;
}
/* BrainJar Style */
div.BJ_menuBar, div.BJ_menuElement,
div.BJ_subMenuBar, div.BJ_subMenuElement {
    font-family: 'MS Sans Serif', Arial, sans-serif;
    font-size: 10px;
    color: #000000;
    text-align: left;
}
div.BJ_menuBar,
div.BJ_subMenuBar {
    background-color: #c09070;
    border: 2px solid;
    border-color: #e0b090 #906040 #906040 #e0b090;
}
div.BJ_menuBar {
    padding: 1px 4px;
}
div.BJ_menuElement {
    border: 1px solid #c09070;
    padding: 2px 6px 2px 6px;
    font-weight: bold;
}
div.BJ_subMenuElement {
    padding: 2px 6px 2px 6px;
    margin: 0 1px 1px 0;
}
div.BJ_subMenuElementHover {
    background-color: #906040;
    color: #FFFFFF;
}
div.BJ_subMenuElementHeading {
    font-weight: bold;
}
div.BJ_menuElementHover {
    border-color: #e0b090 #906040 #906040 #e0b090;
}
div.BJ_menuElementActive {
    background-color: #906040;
    color: #FFFFFF;
    border-color: #906040 #e0b090 #e0b090 #906040;
}
div.hr {
    border-top: 1px solid #906040;
    border-bottom: 1px solid #e0b090;
}
/* NBLSA styles */
.domMenuNBLSA_subMenuElement {
  background-color: #000000;
  font-family: Verdana;
  color: #EBCC72;
  font-weight: bold;
  font-size: 10px;
  text-align: left;
  vertical-align: top;
  padding: 6px 10px;
  white-space: nowrap;
}
.domMenuNBLSA_subMenuElementHover {
  background-color: #EBCC72;
  color: #000000;
}

    </style>
    <!-- domLib.js -->
    <script language="javascript">
    /** $Id: domLib.js 1891 2005-05-25 05:01:19Z dallen $ */
// {{{ docs <-- this is a VIM (text editor) text fold

/**
 * Title: DOM Library Core
 * Version: 0.65
 *
 * Summary:
 * A set of commonly used functions that make it easier to create javascript
 * applications that rely on the DOM.
 *
 * Updated: 2005/05/17
 *
 * Maintainer: Dan Allen <dan.allen@mojavelinux.com>
 * Maintainer: Jason Rust <jrust@rustyparts.com>
 *
 * License: LGPL
 */

// }}}
// {{{ global constants (DO NOT EDIT)

/**
 * Global constants (DO NOT EDIT)
 */

// -- Browser Detection --
var domLib_userAgent = navigator.userAgent.toLowerCase();
var domLib_isMac = navigator.appVersion.indexOf('Mac') != -1;
var domLib_isWin = domLib_userAgent.indexOf('windows') != -1;
var domLib_isOpera = domLib_userAgent.indexOf('opera') != -1;
var domLib_isOpera7up = domLib_userAgent.match(/opera.(7|8)/i);
var domLib_isSafari = domLib_userAgent.indexOf('safari') != -1;
var domLib_isKonq = domLib_userAgent.indexOf('konqueror') != -1;
// Both konqueror and safari use the khtml rendering engine
var domLib_isKHTML = (domLib_isKonq || domLib_isSafari || domLib_userAgent.indexOf('khtml') != -1);
var domLib_isIE = (!domLib_isKHTML && !domLib_isOpera && (domLib_userAgent.indexOf('msie 5') != -1 || domLib_userAgent.indexOf('msie 6') != -1));
var domLib_isIE5up = domLib_isIE;
var domLib_isIE50 = (domLib_isIE && domLib_userAgent.indexOf('msie 5.0') != -1);
var domLib_isIE55 = (domLib_isIE && domLib_userAgent.indexOf('msie 5.5') != -1);
var domLib_isIE5 = (domLib_isIE50 || domLib_isIE55);
// safari and konq may use string "khtml, like gecko", so check for destinctive /
var domLib_isGecko = domLib_userAgent.indexOf('gecko/') != -1;
var domLib_isMacIE = (domLib_isIE && domLib_isMac);
var domLib_isIE55up = domLib_isIE5up && !domLib_isIE50 && !domLib_isMacIE;
var domLib_isIE6up = domLib_isIE55up && !domLib_isIE55;

// -- Browser Abilities --
var domLib_standardsMode = (document.compatMode && document.compatMode == 'CSS1Compat');
var domLib_useLibrary = (domLib_isOpera7up || domLib_isKHTML || domLib_isIE5up || domLib_isGecko || domLib_isMacIE || document.defaultView);
var domLib_hasBrokenTimeout = (domLib_isMacIE || (domLib_isKonq && domLib_userAgent.match(/konqueror\/3.([2-9])/) == null));
var domLib_canFade = (domLib_isGecko || domLib_isIE || domLib_isSafari || domLib_isOpera);
var domLib_canDrawOverSelect = (domLib_isMac || domLib_isOpera || domLib_isGecko);
var domLib_canDrawOverFlash = (domLib_isMac || domLib_isWin);

// -- Event Variables --
var domLib_eventTarget = domLib_isIE ? 'srcElement' : 'currentTarget';
var domLib_eventButton = domLib_isIE ? 'button' : 'which';
var domLib_eventTo = domLib_isIE ? 'toElement' : 'relatedTarget';
var domLib_stylePointer = domLib_isIE ? 'hand' : 'pointer';
// NOTE: a bug exists in Opera that prevents maxWidth from being set to 'none', so we make it huge
var domLib_styleNoMaxWidth = domLib_isOpera ? '10000px' : 'none';
var domLib_hidePosition = '-1000px';
var domLib_scrollbarWidth = 14;
var domLib_autoId = 1;
var domLib_zIndex = 100;

// -- Detection --
var domLib_collisionElements;
var domLib_collisionsCached = false;

var domLib_timeoutStateId = 0;
var domLib_timeoutStates = new Hash();

// }}}
// {{{ DOM enhancements

if (!document.ELEMENT_NODE)
{
  document.ELEMENT_NODE = 1;
  document.ATTRIBUTE_NODE = 2;
  document.TEXT_NODE = 3;
  document.DOCUMENT_NODE = 9;
  document.DOCUMENT_FRAGMENT_NODE = 11;
}

Object.prototype.clone = function()
{
  var copy = {};
  for (var i in this)
  {
    var value = this[i];
    try
    {
      if (value != null && typeof(value) == 'object' && value != window && !value.nodeType)
      {
        // for IE5 which doesn't inherit prototype
        value.clone = Object.clone;
        copy[i] = value.clone();
      }
      else
      {
        copy[i] = value;
      }
    }
    catch(e)
    {
      copy[i] = value;
    }
  }

  return copy;
}

// }}}
// {{{ class Hash()

function Hash()
{
  this.length = 0;
  this.numericLength = 0; 
  this.elementData = [];
  for (var i = 0; i < arguments.length; i += 2)
  {
    if (typeof(arguments[i + 1]) != 'undefined')
    {
      this.elementData[arguments[i]] = arguments[i + 1];
      this.length++;
      if (arguments[i] == parseInt(arguments[i])) 
      {
        this.numericLength++;
      }
    }
  }
}

// using prototype as opposed to inner functions saves on memory 
Hash.prototype.get = function(in_key)
{
  return this.elementData[in_key];
}

Hash.prototype.set = function(in_key, in_value)
{
  if (typeof(in_value) != 'undefined')
  {
    if (typeof(this.elementData[in_key]) == 'undefined')
    {
      this.length++;
      if (in_key == parseInt(in_key)) 
      {
        this.numericLength++;
      }
    }

    return this.elementData[in_key] = in_value;
  }

  return false;
}

Hash.prototype.remove = function(in_key)
{
  var tmp_value;
  if (typeof(this.elementData[in_key]) != 'undefined')
  {
    this.length--;
    if (in_key == parseInt(in_key)) 
    {
      this.numericLength--;
    }

    tmp_value = this.elementData[in_key];
    delete this.elementData[in_key];
  }

  return tmp_value;
}

Hash.prototype.size = function()
{
  return this.length;
}

Hash.prototype.has = function(in_key)
{
  return typeof(this.elementData[in_key]) != 'undefined';
}

Hash.prototype.find = function(in_obj)
{
  for (var tmp_key in this.elementData) 
  {
    if (this.elementData[tmp_key] == in_obj) 
    {
      return tmp_key;
    }
  }
}

Hash.prototype.merge = function(in_hash)
{
  for (var tmp_key in in_hash.elementData) 
  {
    if (typeof(this.elementData[tmp_key]) == 'undefined') 
    {
      this.length++;
      if (tmp_key == parseInt(tmp_key)) 
      {
        this.numericLength++;
      }
    }

    this.elementData[tmp_key] = in_hash.elementData[tmp_key];
  }
}

Hash.prototype.compare = function(in_hash)
{
  if (this.length != in_hash.length) 
  {
    return false;
  }

  for (var tmp_key in this.elementData) 
  {
    if (this.elementData[tmp_key] != in_hash.elementData[tmp_key]) 
    {
      return false;
    }
  }
  
  return true;
}

// }}}
// {{{ domLib_isDescendantOf()

function domLib_isDescendantOf(in_object, in_ancestor)
{
  if (in_object == in_ancestor)
  {
    return true;
  }

  while (in_object != document.documentElement)
  {
    try
    {
      if ((tmp_object = in_object.offsetParent) && tmp_object == in_ancestor)
      {
        return true;
      }
      else if ((tmp_object = in_object.parentNode) == in_ancestor)
      {
        return true;
      }
      else
      {
        in_object = tmp_object;
      }
    }
    // in case we get some wierd error, just assume we haven't gone out yet
    catch(e)
    {
      return true;
    }
  }

  return false;
}

// }}}
// {{{ domLib_detectCollisions()

/**
 * For any given target element, determine if elements on the page
 * are colliding with it that do not obey the rules of z-index.
 */
function domLib_detectCollisions(in_object, in_recover, in_useCache)
{
  // the reason for the cache is that if the root menu is built before
  // the page is done loading, then it might not find all the elements.
  // so really the only time you don't use cache is when building the
  // menu as part of the page load
  if (!domLib_collisionsCached)
  {
    var tags = [];

    if (!domLib_canDrawOverFlash)
    {
      tags[tags.length] = 'object';
    }

    if (!domLib_canDrawOverSelect)
    {
      tags[tags.length] = 'select';
    }

    domLib_collisionElements = domLib_getElementsByTagNames(tags);
    domLib_collisionsCached = in_useCache;
  }

  // if we don't have a tip, then unhide selects
  if (in_recover)
  {
    for (var cnt = 0; cnt < domLib_collisionElements.length; cnt++)
    {
      var thisElement = domLib_collisionElements[cnt];

      if (!thisElement.hideList)
      {
        thisElement.hideList = new Hash();
      }

      thisElement.hideList.remove(in_object.id);
      if (!thisElement.hideList.length)
      {
        domLib_collisionElements[cnt].style.visibility = 'visible';
        if (domLib_isKonq)
        {
          domLib_collisionElements[cnt].style.display = '';
        }
      }
    }

    return;
  }
  else if (domLib_collisionElements.length == 0)
  {
    return;
  }

  // okay, we have a tip, so hunt and destroy
  var objectOffsets = domLib_getOffsets(in_object);

  for (var cnt = 0; cnt < domLib_collisionElements.length; cnt++)
  {
    var thisElement = domLib_collisionElements[cnt];

    // if collision element is in active element, move on
    // WARNING: is this too costly?
    if (domLib_isDescendantOf(thisElement, in_object))
    {
      continue;
    }

    // konqueror only has trouble with multirow selects
    if (domLib_isKonq &&
      thisElement.tagName == 'SELECT' &&
      (thisElement.size <= 1 && !thisElement.multiple))
    {
      continue;
    }

    if (!thisElement.hideList)
    {
      thisElement.hideList = new Hash();
    }

    var selectOffsets = domLib_getOffsets(thisElement); 
    var center2centerDistance = Math.sqrt(Math.pow(selectOffsets.get('leftCenter') - objectOffsets.get('leftCenter'), 2) + Math.pow(selectOffsets.get('topCenter') - objectOffsets.get('topCenter'), 2));
    var radiusSum = selectOffsets.get('radius') + objectOffsets.get('radius');
    // the encompassing circles are overlapping, get in for a closer look
    if (center2centerDistance < radiusSum)
    {
      // tip is left of select
      if ((objectOffsets.get('leftCenter') <= selectOffsets.get('leftCenter') && objectOffsets.get('right') < selectOffsets.get('left')) ||
      // tip is right of select
        (objectOffsets.get('leftCenter') > selectOffsets.get('leftCenter') && objectOffsets.get('left') > selectOffsets.get('right')) ||
      // tip is above select
        (objectOffsets.get('topCenter') <= selectOffsets.get('topCenter') && objectOffsets.get('bottom') < selectOffsets.get('top')) ||
      // tip is below select
        (objectOffsets.get('topCenter') > selectOffsets.get('topCenter') && objectOffsets.get('top') > selectOffsets.get('bottom')))
      {
        thisElement.hideList.remove(in_object.id);
        if (!thisElement.hideList.length)
        {
          thisElement.style.visibility = 'visible';
          if (domLib_isKonq)
          {
            thisElement.style.display = '';
          }
        }
      }
      else
      {
        thisElement.hideList.set(in_object.id, true);
        thisElement.style.visibility = 'hidden';
        if (domLib_isKonq)
        {
          thisElement.style.display = 'none';
        }
      }
    }
  }
}

// }}}
// {{{ domLib_getOffsets()

function domLib_getOffsets(in_object)
{
  var originalObject = in_object;
  var originalWidth = in_object.offsetWidth;
  var originalHeight = in_object.offsetHeight;
  var offsetLeft = 0;
  var offsetTop = 0;

  while (in_object)
  {
    offsetLeft += in_object.offsetLeft;
    offsetTop += in_object.offsetTop;
    in_object = in_object.offsetParent;
  }

  // MacIE misreports the offsets (even with margin: 0 in body{}), still not perfect
  if (domLib_isMacIE) {
    offsetLeft += 10;
    offsetTop += 10;
  }

  return new Hash(
    'left',    offsetLeft,
    'top',    offsetTop,
    'right',  offsetLeft + originalWidth,
    'bottom',  offsetTop + originalHeight,
    'leftCenter',  offsetLeft + originalWidth/2,
    'topCenter',  offsetTop + originalHeight/2,
    'radius',  Math.max(originalWidth, originalHeight) 
  );
}

// }}}
// {{{ domLib_setTimeout()

function domLib_setTimeout(in_function, in_timeout, in_args)
{
  if (typeof(in_args) == 'undefined')
  {
    in_args = [];
  }

  if (in_timeout == -1)
  {
    // timeout event is disabled
    return;
  }
  else if (in_timeout == 0)
  {
    in_function(in_args);
    return 0;
  }

  // must make a copy of the arguments so that we release the reference
  if (typeof(in_args.clone) != 'function')
  {
    in_args.clone = Object.clone;
  }

  var args = in_args.clone();

  if (!domLib_hasBrokenTimeout)
  {
    return setTimeout(function() { in_function(args); }, in_timeout);
  }
  else
  {
    var id = domLib_timeoutStateId++;
    var data = new Hash();
    data.set('function', in_function);
    data.set('args', args);
    domLib_timeoutStates.set(id, data);

    data.set('timeoutId', setTimeout('domLib_timeoutStates.get(' + id + ').get(\'function\')(domLib_timeoutStates.get(' + id + ').get(\'args\')); domLib_timeoutStates.remove(' + id + ');', in_timeout));
    return id;
  }
}

// }}}
// {{{ domLib_clearTimeout()

function domLib_clearTimeout(in_id)
{
  if (!domLib_hasBrokenTimeout)
  {
    clearTimeout(in_id);
  }
  else
  {
    if (domLib_timeoutStates.has(in_id))
    {
      clearTimeout(domLib_timeoutStates.get(in_id).get('timeoutId'))
      domLib_timeoutStates.remove(in_id);
    }
  }
}

// }}}
// {{{ domLib_getEventPosition()

function domLib_getEventPosition(in_eventObj)
{
  var eventPosition = new Hash('x', 0, 'y', 0, 'scrollX', 0, 'scrollY', 0);

  // IE varies depending on standard compliance mode
  if (domLib_isIE)
  {
    var doc = (domLib_standardsMode ? document.documentElement : document.body);
    // NOTE: events may fire before the body has been loaded
    if (doc)
    {
      eventPosition.set('x', in_eventObj.clientX + doc.scrollLeft);
      eventPosition.set('y', in_eventObj.clientY + doc.scrollTop);
      eventPosition.set('scrollX', doc.scrollLeft);
      eventPosition.set('scrollY', doc.scrollTop);
    }
  }
  else
  {
    eventPosition.set('x', in_eventObj.pageX);
    eventPosition.set('y', in_eventObj.pageY);
    eventPosition.set('scrollX', in_eventObj.pageX - in_eventObj.clientX);
    eventPosition.set('scrollY', in_eventObj.pageY - in_eventObj.clientY);
  }

  return eventPosition;
}

// }}}
// {{{ domLib_cancelBubble()

function domLib_cancelBubble(in_event)
{
  var eventObj = in_event ? in_event : window.event;
  eventObj.cancelBubble = true;
}

// }}}
// {{{ domLib_getIFrameReference()

function domLib_getIFrameReference(in_frame)
{
  if (domLib_isGecko || domLib_isIE)
  {
    return in_frame.frameElement;
  }
  else
  {
    // we could either do it this way or require an id on the frame
    // equivalent to the name
    var name = in_frame.name;
    if (!name || !in_frame.parent)
    {
      return;
    }

    var candidates = in_frame.parent.document.getElementsByTagName('iframe');
    for (var i = 0; i < candidates.length; i++)
    {
      if (candidates[i].name == name)
      {
        return candidates[i];
      }
    }
  }
}

// }}}
// {{{ domLib_getElementsByClass()

function domLib_getElementsByClass(in_class)
{
  var elements = domLib_isIE5 ? document.all : document.getElementsByTagName('*');  
  var matches = [];  
  var cnt = 0;
  for (var i = 0; i < elements.length; i++)
  {
    if ((" " + elements[i].className + " ").indexOf(" " + in_class + " ") != -1)
    {
      matches[cnt++] = elements[i];
    }
  }

  return matches;
}

// }}}
// {{{

function domLib_getElementsByTagNames(in_list)
{
  var elements = [];
  for (var i = 0; i < in_list.length; i++)
  {
    var matches = document.getElementsByTagName(in_list[i]);
    for (var j = 0; j < matches.length; j++)
    {
      elements[elements.length] = matches[j];  
    }
  }

  return elements;
}

// }}}
// {{{ makeTrue()

function makeTrue()
{
  return true;
}

// }}}
// {{{ makeFalse()

function makeFalse()
{
  return false;
}

// }}}

    </script>
    <!-- domMenu.js -->
    <script language="javascript">
    /** $Id: domMenu.js 1884 2005-05-24 05:08:38Z dallen $ */
// {{{ docs <-- this is a VIM (text editor) text fold

/**
 * DOM Menu 0.3.4
 *
 * Summary: Allows developers to add dynamic drop down menus on webpages.  The
 *          menu can either be horizontal or vertical, and can open in either
 *          direction.  It has both edge detection and <select> tag detection
 *          (for browsers that cannot hide these form elements).  The styles
 *          for the menu items are controlled almost entirely through CSS and
 *          the menus are created and destroyed using the DOM.  Menu configuration
 *          is done using a custom Hash() class and is very portable from a PHP
 *          type array structure.
 *
 * Dependency: domLib.js version 0.67
 *
 * Maintainer (lead): Dan Allen <dan@mojavelinux.com>
 * Maintainer: Jason Rust <jrust@rustyparts.com>
 *
 * License: LGPL - however, if you use this library, please post to my forum where you
 *          use it so that I get a chance to see my baby in action.  If you are doing
 *          this for commercial work perhaps you could send me a few Starbucks Coffee
 *          gift dollars to encourage future developement (NOT REQUIRED).  E-mail me
 *          for and address.
 *
 * Homepage: http://www.mojavelinux.com/forum/viewtopic.php
 *
 * Freshmeat Project: http://freshmeat.net/projects/dommenu/?topic_id=92
 *
 * Updated: $Id: domMenu.js 1884 2005-05-24 05:08:38Z dallen $
 *
 * Supported Browsers: Mozilla (Gecko), IE 5.0+, IE on Mac, Safari, Konqueror, Opera 7+
 *
 * Usage: 
 *
 * Menu Options: Each option is followed by the value for that option. The options avaiable are:
 *            'contents'
 *            'rolloverContents',
 *            'uri' (may be javascript)
 *            'statusText'
 *            'target'
 *            [0-9] an index to create a submenu item
 *
 * API:
 *
 * menuElementObject {
 *     ** properties **
 *     data
 *       contents
 *       uri
 *       target
 *       statusText
 *       parentElement
 *       subMenu
 *       childElements
 *       level
 *       index (index within this level)
 *     id
 *     className
 *     style
 *     cellSpacing (Konq only)
 *     
 *     ** events **
 *     mouseover/click -> domMenu_openEvent
 *     mouseout        -> domMenu_closeEvent
 *     click           -> domMenu_resolveLink
 * }
 *
 * If there is a non-negative click open delay, then any uri of the element will be ignored
 *
 * The alternate contents for a hover element are treated by creating to <span> wrapper elements
 * and then alternating the display of them.  This avoids the need for innerHTML, which can
 * do nasty things to the browsers.  If <span> turns out to be a bad choice for tags, then a
 * non-HTML element can be used instead.
 *
 * Dev Notes:
 * - added cellSpacing = 0 for domLib_isMacIE (two places)
 * - seems that Safari and Firefox share an offset problem of menu under parent (pmp example)
 * - must use createTextNode() to add the "\n" that is required for Mac to
 *   render the appendChild element (two places); this might be the solution for
 *   the sub menus as well
 * - Safari seems to have a problem with offsetTop if a descendent of body has a margin; solution
 *   is to use padding on the body
 */

// }}}
// {{{ settings (editable)

var domMenu_data = new Hash();
var domMenu_settings = new Hash();

domMenu_settings.set('global', new Hash(
    'menuBarClass', 'domMenu_menuBar',
    'menuElementClass', 'domMenu_menuElement',
    'menuElementHoverClass', 'domMenu_menuElementHover',
    'menuElementActiveClass', 'domMenu_menuElementHover',
    'subMenuBarClass', 'domMenu_subMenuBar',
    'subMenuElementClass', 'domMenu_subMenuElement',
    'subMenuElementHoverClass', 'domMenu_subMenuElementHover',
    'subMenuElementActiveClass', 'domMenu_subMenuElementHover',
    'subMenuElementHeadingClass', 'domMenu_subMenuElementHeading',
    'menuBarWidth', '100%',
    'subMenuMinWidth', 'inherit',
    'distributeSpace', true,
    'axis', 'horizontal',
    'verticalExpand', 'south',
    'horizontalExpand', 'east',
    'expandMenuArrowUrl', 'arrow.gif',
    'subMenuWidthCorrection', 0,
    'verticalSubMenuOffsetY', 0,
    'verticalSubMenuOffsetX', 0,
    'horizontalSubMenuOffsetX', 0,
    'horizontalSubMenuOffsetY', 0,
    'screenPadding', 0,
    'openMouseoverMenuDelay', 300,
    'openMousedownMenuDelay', -1,
    'closeMouseoutMenuDelay', 800,
    'closeClickMenuDelay', -1,
    'openMouseoverSubMenuDelay', 300,
    'openClickSubMenuDelay', -1,
    'closeMouseoutSubMenuDelay', 300,
    'closeClickSubMenuDelay', -1,
    'baseZIndex', 100,
    'baseUri', ''
));

// }}}
// {{{ global variables

/**
 * The data for the menu is stored here, loaded from an external file
 * @hash domMenu_data
 */
var domMenu_data;

var domMenu_selectElements;
var domMenu_scrollbarWidth = 14;
var domMenu_eventTo = domLib_isIE ? 'toElement' : 'relatedTarget';
var domMenu_eventFrom = domLib_isIE ? 'fromElement' : 'relatedTarget';

var domMenu_activeElement = new Hash();

/**
 * Array of hashes listing the timouts currently running for opening/closing menus
 * @array domMenu_timeouts
 */
var domMenu_timeouts = new Array();
domMenu_timeouts['open'] = new Hash();
domMenu_timeouts['close'] = new Hash();

/**
 * Style to use for a link pointer, which is different between Gecko and IE
 * @var domMenu_pointerStyle
 */
var domMenu_pointerStyle = domLib_isIE ? 'hand' : 'pointer';

// }}}
// {{{ domMenu_activate()

function domMenu_activate(in_containerId, in_disableWarning)
{
    var container;
    var data;

    // make sure we can use the menu system
    if (!domLib_useLibrary)
    {
        if (!in_disableWarning)
        {
                alert('domMenu: Browser not supported.  Menu will be disabled.');
        }

        return;
    }

    // make sure that this is a valid menu, 
    // and that the menu actually has data
    if (!(container = document.getElementById(in_containerId)) || 
        !(data = domMenu_data.get(in_containerId)) ||
        data.numericLength == 0) {
        if (!in_disableWarning) {
                alert('domMenu: Menu failed to load.');
        }

        return;
    }

    if (window.attachEvent) {
        window.attachEvent('onunload', domMenu_fixCircleRefs);
    }

    // start with the global settings and merge in the local changes
    if (!domMenu_settings.has(in_containerId)) {
        domMenu_settings.set(in_containerId, new Hash());
    }

    var settings = domMenu_settings.get(in_containerId);
    for (var i in domMenu_settings.get('global').elementData) {
        if (!settings.has(i)) {
            settings.set(i, domMenu_settings.get('global').get(i));
        }
    }

    // populate the zero level element
    container.data = new Hash(
        'parentElement', false,
        'numChildren', data.numericLength,
        'childElements', new Hash(),
        'level', 0,
        'index', 1
    );
    
    // if we choose to distribute either height or width, determine ratio of each cell
    var distributeRatio = Math.round(100/container.data.get('numChildren')) + '%';
    
    // the first menu is the rootMenu, which is a child of the zero level element
    var rootMenu = document.createElement('div');
    rootMenu.id = in_containerId + '-0';
    rootMenu.className = settings.get('menuBarClass');
    container.data.set('subMenu', rootMenu);

    var rootMenuTable = rootMenu.appendChild(document.createElement('table'));
    if (domLib_isKonq || domLib_isMacIE) {
        rootMenuTable.cellSpacing = 0;
    }

    rootMenuTable.style.border = 0;
    rootMenuTable.style.borderCollapse = 'collapse';
    rootMenuTable.style.width = settings.get('menuBarWidth');
    var rootMenuTableBody = rootMenuTable.appendChild(document.createElement('tbody'));

    var numSiblings = container.data.get('numChildren');
    for (var index = 1; index <= numSiblings; index++) {
        // create a row the first time if horizontal or each time if vertical
        if (index == 1 || settings.get('axis') == 'vertical') {
            var rootMenuTableRow = rootMenuTableBody.appendChild(document.createElement('tr'));
        }

        // create an instance of the root level menu element
        var rootMenuTableCell = rootMenuTableRow.appendChild(document.createElement('td'));
        rootMenuTableCell.style.padding = 0;
        rootMenuTableCell.id = in_containerId + '-' + index;
        // add element to list of parent children
        container.data.get('childElements').set(rootMenuTableCell.id, rootMenuTableCell);

        // assign the settings to the root level element
        // NOTE: this is a problem if two menus are using the same data
        rootMenuTableCell.data = data.get(index);
        rootMenuTableCell.data.merge(new Hash(
            'basename', in_containerId,
            'parentElement', container,
            'numChildren', rootMenuTableCell.data.numericLength,
            'childElements', new Hash(),
            'offsets', new Hash(),
            'level', container.data.get('level') + 1,
            'index', index
        ));

        // assign the styles
        rootMenuTableCell.style.cursor = 'default';
        if (settings.get('axis') == 'horizontal') {
            if (settings.get('distributeSpace')) {
                rootMenuTableCell.style.width = distributeRatio;
            }
        }

        // Needed for when the text wraps
        rootMenuTableCell.style.verticalAlign = 'top';

        var rootElement = rootMenuTableCell.appendChild(document.createElement('div'));
        rootElement.className = settings.get('menuElementClass');
        // fill in the menu element contents
        var spanElement = rootElement.appendChild(document.createElement('span'));
        // can't use createTextNode() because there might be img tags in the contents
        spanElement.innerHTML = rootMenuTableCell.data.get('contents').replace(/\/\/\//, settings.get('baseUri'));
        // add hover contents if needed
        if (rootMenuTableCell.data.has('contentsHover')) {
            spanElement = rootElement.appendChild(document.createElement('span'));
            spanElement.style.display = 'none';
            spanElement.innerHTML = rootMenuTableCell.data.get('contentsHover').replace(/\/\/\//, settings.get('baseUri'));
        }

        // MacIE has to have a newline at the end or else it barfs
        // additionally, it MUST be added using createTextNode() or IE will crash!
        if (domLib_isMacIE) {
            rootMenuTableCell.appendChild(document.createTextNode("\n"));
        }

        // attach the events
        rootMenuTableCell.onmouseover = domMenu_runMouseoverOpenEvent;
        rootMenuTableCell.onmouseout = domMenu_runCloseEvent;

        if (settings.get('openMousedownMenuDelay') >= 0 && rootMenuTableCell.data.get('numChildren')) {
            rootMenuTableCell.onmousedown = domMenu_runMousedownOpenEvent;
            // cancel mouseup so that it doesn't propogate to global mouseup event
            rootMenuTableCell.onmouseup = domLib_cancelBubble;
            if (domLib_isIE) {
                rootMenuTableCell.ondblclick = domMenu_runMousedownOpenEvent;
            }
        }
        else if (rootMenuTableCell.data.get('uri')) {
            rootMenuTableCell.style.cursor = domMenu_pointerStyle;
            rootMenuTableCell.onclick = domMenu_runResolveLink;
        }

        // prevent highlighting of text
        if (domLib_isIE) {
            rootMenuTableCell.onselectstart = makeFalse; 
        }

        rootMenuTableCell.oncontextmenu = makeFalse; 
    }
    
    // add the menu rootMenu to the zero level element
    rootMenu = container.appendChild(rootMenu);

    // even though most cases the top level menu does not go away, it could
    // if this menu system is used by another process
    domLib_detectCollisions(rootMenu, false, false);
}

// }}}
// {{{ domMenu_activateSubMenu()

function domMenu_activateSubMenu(in_parentElement)
{
    // NOTE: submenus not supported in MacIE because of problems using
    // appendChild on document.body
    if (domLib_isMacIE) {
        return;
    }

    // see if submenu already exists
    if (in_parentElement.data.has('subMenu')) {
        domMenu_toggleSubMenu(in_parentElement, 'visible');
        return;
    }

    var settings = domMenu_settings.get(in_parentElement.data.get('basename'));

    // build the submenu
    var menu = document.createElement('div');
    menu.id = in_parentElement.id + '-0';
    menu.className = settings.get('subMenuBarClass');
    menu.style.zIndex = settings.get('baseZIndex');
    menu.style.position = 'absolute';
    // position the menu in the upper left corner hidden so that we can work on it
    menu.style.visibility = 'hidden';
    menu.style.top = 0;
    menu.style.left = 0;

    in_parentElement.data.set('subMenu', menu);

    var menuTable = menu.appendChild(document.createElement('table'));
    // ** opera wants to make absolute tables width 100% **
    if (domLib_isOpera) {
        menuTable.style.width = '1px';
        menuTable.style.whiteSpace = 'nowrap';
    }

    if (domLib_isKonq || domLib_isMacIE) {
        menuTable.cellSpacing = 0;
    }

    menuTable.style.border = 0;
    menuTable.style.borderCollapse = 'collapse';
    var menuTableBody = menuTable.appendChild(document.createElement('tbody'));

    var numSiblings = in_parentElement.data.get('numChildren');
    for (var index = 1; index <= numSiblings; index++) {
        var dataIndex = in_parentElement.data.get('level') == 1 && settings.get('verticalExpand') == 'north' && settings.get('axis') == 'horizontal' ? numSiblings + 1 - index : index;
        var menuTableCell = menuTableBody.appendChild(document.createElement('tr')).appendChild(document.createElement('td'));
        menuTableCell.style.padding = 0;
        menuTableCell.id = in_parentElement.id + '-' + dataIndex;

        // add element to list of parent children
        in_parentElement.data.get('childElements').set(menuTableCell.id, menuTableCell);

        // assign the settings to nth level element
        menuTableCell.data = in_parentElement.data.get(dataIndex);
        menuTableCell.data.merge(new Hash(
            'basename', in_parentElement.data.get('basename'),
            'parentElement', in_parentElement,
            'numChildren', menuTableCell.data.numericLength,
            'childElements', new Hash(),
            'offsets', new Hash(),
            'level', in_parentElement.data.get('level') + 1,
            'index', index
        ));
        
        // assign the styles
        menuTableCell.style.cursor = 'default';
        
        var element = menuTableCell.appendChild(document.createElement('div')); 
        var outerElement = element;
        outerElement.className = settings.get('subMenuElementClass'); 

        if (menuTableCell.data.get('numChildren')) {
            element = outerElement.appendChild(document.createElement('div'));
            // FIXME: this should depend on which way we are opening the menu!
            element.style.backgroundImage = 'url(' + settings.get('expandMenuArrowUrl') + ')';
            element.style.backgroundRepeat = 'no-repeat';
            element.style.backgroundPosition = 'right center';
            // add appropriate padding to fit the arrow
            element.style.paddingRight = '12px';
        }

        // fill in the menu item contents
        if (domLib_isMacIE) {
            // we don't support images in sub-menu elements in MacIE because in order for
            // the menu to work consistently the data has to be added with createTextNode()
            element.appendChild(document.createTextNode(menuTableCell.data.get('contents')));
            // MacIE has to have a newline and it has to be added with createTextNode!
            menuTableCell.appendChild(document.createTextNode("\n"));
        }
        else {
            element.innerHTML = menuTableCell.data.get('contents');
        }

        // attach the events
        menuTableCell.onmouseover = domMenu_runMouseoverSubOpenEvent;
        menuTableCell.onmouseout = domMenu_runCloseEvent;

        if (settings.get('openClickSubMenuDelay') >= 0 && menuTableCell.data.get('numChildren')) {
            menuTableCell.onmousedown = domMenu_runClickSubOpenEvent;
            menuTableCell.onmouseup = domLib_cancelBubble;
            if (domLib_isIE) {
                menuTableCell.ondblclick = domMenu_runClickSubOpenEvent;
            }
        }
        else if (menuTableCell.data.get('uri')) {
            menuTableCell.style.cursor = domMenu_pointerStyle;
            menuTableCell.onclick = domMenu_runResolveLink;
        }
        else if (!menuTableCell.data.get('numChildren')) {
            outerElement.className += ' ' + settings.get('subMenuElementHeadingClass');
        }

        // prevent highlighting of text
        if (domLib_isIE) {
            menuTableCell.onselectstart = makeFalse;
        }

        menuTableCell.oncontextmenu = makeFalse;
    }

    menu = document.body.appendChild(menu);
    domMenu_toggleSubMenu(in_parentElement, 'visible');
}

// }}}
// {{{ domMenu_changeActivePath()

/**
 * Close the old active path up to the new active element
 * and return the value of the new active element (or the same if unchanged)
 * NOTE: If the new active element is not set (false), the top level is assumed
 *
 * @return mixed new active element or false if not set
 */
function domMenu_changeActivePath(in_newActiveElement, in_oldActiveElement, in_closeDelay)
{
    // protect against crap
    if (!in_oldActiveElement && !in_newActiveElement) {
        return false;
    }

    // cancel open timeouts since we know we are opening something different now
    for (var i in domMenu_timeouts['open'].elementData) {
        domLib_clearTimeout(domMenu_timeouts['open'].get(i));
    }

    // grab some info about this menu system...will this ever be null?
    var basename = in_oldActiveElement ? in_oldActiveElement.data.get('basename') : in_newActiveElement.data.get('basename');
    var settings = domMenu_settings.get(basename);

    // build the old active path and unhighlight previously selected element, if appropriate
    var oldActivePath = new Hash();
    if (in_oldActiveElement) {
        var tmp_newActiveLevel = in_newActiveElement ? in_newActiveElement.data.get('level') : -1;
        var tmp_oldActivePathElement = in_oldActiveElement;
        do {
            // NOTE: using set() causes IE to lag and leaves behind highlighted artifacts!
            oldActivePath.elementData[tmp_oldActivePathElement.id] = tmp_oldActivePathElement; 
            // unhighlight if sibling of new element, even if it has open submenus
            if (tmp_newActiveLevel >= 0 && tmp_oldActivePathElement.data.get('level') == tmp_newActiveLevel) {
                domMenu_toggleHighlight(tmp_oldActivePathElement, false);
            }
        } while ((tmp_oldActivePathElement = tmp_oldActivePathElement.data.get('parentElement')) && tmp_oldActivePathElement.id != basename);

        // unhighlight element immediately if no submenu (or submenu is closed)
        if (!in_oldActiveElement.data.get('subMenu') || in_oldActiveElement.data.get('subMenu').style.visibility == 'hidden') {
            domMenu_toggleHighlight(in_oldActiveElement, false);
        }
    }

    // build the new path and...(explain me!)
    var newActivePath = new Hash();
    var intersectPoint;
    if (in_newActiveElement) {
        var actualActiveElement = in_newActiveElement;
        window.status = in_newActiveElement.data.get('statusText') + ' ';

        // in the event we have no old active element, just highlight new one and return
        // without setting the new active element (handled later)
        if (!in_oldActiveElement) {
            domLib_clearTimeout(domMenu_timeouts['close'].get(in_newActiveElement.id));
            domMenu_toggleHighlight(in_newActiveElement, true);
            return false;
        }
        // if the new element is in the path of the old element, then pretend event is
        // on the old active element
        else if (oldActivePath.has(in_newActiveElement.id)) {
            in_newActiveElement = in_oldActiveElement;
        }

        var tmp_newActivePathElement = in_newActiveElement;
        do {
            // if we have met up with the old active path, then record merge point
            if (!intersectPoint && oldActivePath.has(tmp_newActivePathElement.id)) {
                intersectPoint = tmp_newActivePathElement;
            }

            newActivePath.set(tmp_newActivePathElement.id, tmp_newActivePathElement); 
            domLib_clearTimeout(domMenu_timeouts['close'].get(tmp_newActivePathElement.id));
            // FIXME: this is ugly!
            if (tmp_newActivePathElement != in_oldActiveElement || actualActiveElement == in_oldActiveElement) {
                domMenu_toggleHighlight(tmp_newActivePathElement, true);
            }
        } while ((tmp_newActivePathElement = tmp_newActivePathElement.data.get('parentElement')) && tmp_newActivePathElement.id != basename);

        // if we move to the child of the old active element
        if (in_newActiveElement.data.get('parentElement') == in_oldActiveElement) {
            return in_newActiveElement;
        }
        // if the new active element is in the old active path
        else if (in_newActiveElement == in_oldActiveElement) {
            return in_newActiveElement;
        }

        // find the sibling element
        var intersectSibling;
        if (intersectPoint && oldActivePath.length > 0) {
            for (var i in oldActivePath.elementData) {
                if (oldActivePath.get(i).data.get('parentElement') == intersectPoint) {
                    intersectSibling = oldActivePath.get(i);
                    break;
                }
            }
        }

        var isRootLevel = in_newActiveElement.data.get('level') == 1 ? true : false;
        var closeDelay = isRootLevel ? settings.get('closeMouseoutMenuDelay') : settings.get('closeMouseoutSubMenuDelay');
    }
    else {
        var isRootLevel = false;
        var closeDelay = settings.get('closeMouseoutMenuDelay');
        window.status = window.defaultStatus;
    }

    // override the close delay with that passed in
    if (typeof(in_closeDelay) != 'undefined') {
        closeDelay = in_closeDelay;
    }

    // if there is an intersect sibling, then we need to work from there up to 
    // preserve the active path
    if (intersectSibling) {
        // only if this is not the root level to we allow the scheduled close
        // events to persist...otherwise we close immediately
        if (!isRootLevel) {
            // toggle the sibling highlight (only one sibling highlighted at a time)
            domMenu_toggleHighlight(intersectSibling, false);
        }
        // we are moving to another top level menu
        // FIXME: clean this up
        else {
            // add lingering menus outside of old active path to active path
            for (var i in domMenu_timeouts['close'].elementData) {
                if (!oldActivePath.has(i)) {
                    var tmp_element = document.getElementById(i);
                    if (tmp_element.data.get('basename') == basename) {
                        oldActivePath.set(i, tmp_element);
                    }
                }
            }
        }
    }

    // schedule the old active path to be closed
    for (var i in oldActivePath.elementData) {
        if (newActivePath.has(i)) {
            continue;
        }

        // make sure we don't double schedule here
        domLib_clearTimeout(domMenu_timeouts['close'].get(i));

        if (isRootLevel) {
            domMenu_toggleHighlight(oldActivePath.get(i), false); 
            domMenu_toggleSubMenu(oldActivePath.get(i), 'hidden');
        }
        else {
            domMenu_timeouts['close'].set(i, domLib_setTimeout(domMenu_runCloseMenu, closeDelay, [oldActivePath.get(i), basename]));
        }
    }
    
    return in_newActiveElement;
}

// }}}
// {{{ domMenu_deactivate()

function domMenu_deactivate(in_basename, in_delay)
{
    if (!in_delay) {
        in_delay = 0;
    }

    domMenu_changeActivePath(false, domMenu_activeElement.get(in_basename), in_delay);
}

// }}}
// {{{ domMenu_openEvent()

/**
 * Handle the mouse event to open a menu
 *
 * When an event is received to open the menu, this function is
 * called, handles reinitialization of the menu state and sets
 * a timeout interval for opening the submenu (if one exists)
 */
function domMenu_openEvent(in_this, in_event, in_delayType)
{
    if (domLib_isGecko) {
        window.getSelection().removeAllRanges();
    }

    // setup the cross-browser event object and target
    var eventObj = domLib_isIE ? event : in_event;
    var currentTarget = domLib_isIE ? in_this : eventObj.currentTarget;
    var basename = currentTarget.data.get('basename');
    var settings = domMenu_settings.get(basename);

    // if we are moving amoungst DOM children of the same element, just ignore event
    if (eventObj.type != 'mousedown' && domMenu_getElement(eventObj[domMenu_eventFrom], basename) == currentTarget) {
        return;
    }

    // if we click on an open menu, close it
    if (eventObj.type == 'mousedown' && domMenu_activeElement.get(basename)) {
        domMenu_changeActivePath(false, domMenu_activeElement.get(basename), currentTarget.data.get('level') == 1 ? settings.get('closeClickMenuDelay') : settings.get('closeClickSubMenuDelay'));
        return;
    }

    // if this element has children, popup the child menu
    if (currentTarget.data.get('numChildren')) {
        // the top level menus have no delay when moving between them
        // so activate submenu immediately
        if (currentTarget.data.get('level') == 1 && domMenu_activeElement.get(basename)) {
            // ** I place changeActivePath() call here so the hiding of selects does not flicker **
            // THOUGHT: instead I could tell changeActivePath to clear select ownership but not
            // toggle visibility....hmmm....
            domMenu_activateSubMenu(currentTarget);
            // clear the active path and initialize the new one
            domMenu_activeElement.set(basename, domMenu_changeActivePath(currentTarget, domMenu_activeElement.get(basename)));
        }
        else {
            // clear the active path and initialize the new one
            domMenu_activeElement.set(basename, domMenu_changeActivePath(currentTarget, domMenu_activeElement.get(basename)));
            domMenu_timeouts['open'].set(currentTarget.id, domLib_setTimeout(domMenu_runOpenMenu, settings.get(in_delayType), [currentTarget, basename]));
        }
    }
    else {
        // clear the active path and initialize the new one
        domMenu_activeElement.set(basename, domMenu_changeActivePath(currentTarget, domMenu_activeElement.get(basename)));
    }
}

// }}}
// {{{ domMenu_closeEvent()

/**
 * Handle the mouse event to close a menu
 *
 * When an mouseout event is received to close the menu, this function is
 * called, sets a timeout interval for closing the menu.
 */
function domMenu_closeEvent(in_this, in_event)
{
    // setup the cross-browser event object and target
    var eventObj = domLib_isIE ? event : in_event;
    var currentTarget = domLib_isIE ? in_this : eventObj.currentTarget;
    var basename = currentTarget.data.get('basename');
    var relatedTarget = domMenu_getElement(eventObj[domMenu_eventTo], basename);

    // if the related target is not a menu element then we left the menu system
    // at this point (or cannot discern where we are in the menu)
    if (domMenu_activeElement.get(basename)) {
        if (!relatedTarget) {
            domMenu_changeActivePath(false, domMenu_activeElement.get(basename));
        }
    }
    // we are highlighting the top level, but menu is not yet 'active'
    else {
        if (currentTarget != relatedTarget) {
            domLib_clearTimeout(domMenu_timeouts['open'].get(currentTarget.id));
            domMenu_toggleHighlight(currentTarget, false);
        }
    }
}    

// }}}
// {{{ domMenu_getElement()

function domMenu_getElement(in_object, in_basename)
{
    while (in_object) {
        try {
            if (in_object.id && in_object.id.search(new RegExp('^' + in_basename + '(\\[[0-9]\\])*\\[[1-9]\\]$')) == 0) {
                return in_object;
            }
            else {
                in_object = in_object.parentNode;
            }
        }
        catch(e) {
            return false;
        }
    }
    
    return false;
}

// }}}
// {{{ domMenu_correctEdgeBleed()

function domMenu_correctEdgeBleed(in_width, in_height, in_x, in_y, in_padding, in_axis)
{
    if (domLib_isIE && !domLib_isIE5 && !domLib_isMacIE) {
        var pageHeight = document.documentElement.clientHeight;
    }
    else if (!domLib_isKonq) {
        var pageHeight = document.body.clientHeight;
    }
    else {
        var pageHeight = window.innerHeight;
    }

    var pageYOffset = domLib_isIE ? document.body.scrollTop : window.pageYOffset;
    var pageXOffset = domLib_isIE ? document.body.scrollLeft : window.pageXOffset;
    
    if (in_axis == 'horizontal') {
        var bleedRight = (in_x - pageXOffset) + in_width - (document.body.clientWidth - in_padding);
        var bleedLeft = (in_x - pageXOffset) - in_padding;
        // we are bleeding off the right, move menu to stay on page
        if (bleedRight > 0) {
            in_x -= bleedRight;
        }

        // we are bleeding to the left, move menu over to stay on page
        // we don't want an 'else if' here, because if it doesn't fit we will bleed off the right
        if (bleedLeft < 0) {
            in_x += bleedLeft;
        }
    }
    else {
        var bleedTop = (in_y - pageYOffset) - in_padding;
        var bleedBottom = (in_y - pageYOffset) + in_height - (pageHeight - in_padding);
        // if we are bleeding off the bottom, move menu to stay on page
        if (bleedBottom > 0) {
            in_y -= bleedBottom;
        }

        // if we are bleeding off the top, move menu down
        // we don't want an 'else if' here, because if we just can't fit it, bleed off the bottom
        if (bleedTop < 0) {
            in_y += bleedTop;
        }
    }
    
    return new Array(in_x, in_y);
}

// }}}
// {{{ domMenu_toggleSubMenu()

function domMenu_toggleSubMenu(in_parentElement, in_style)
{
    var subMenu = in_parentElement.data.get('subMenu');
    if (subMenu && subMenu.style.visibility != in_style) {
        var settings = domMenu_settings.get(in_parentElement.data.get('basename'));
        var prefix = in_parentElement.data.get('level') == 1 ? 'menu' : 'subMenu';
        var className = settings.get(prefix + 'ElementClass');
        // :BUG: this is a problem if submenus click to open, then it won't
        // have the right class when you click to close
        if (in_style == 'visible') {
            className += ' ' + settings.get(prefix + 'Element' + (in_style == 'visible' ? 'Active' : 'Hover') + 'Class');
        }

        in_parentElement.firstChild.className = className;
        
        // position our submenu
        if (in_style == 'visible') {
            var tmp_offsets = domLib_getOffsets(in_parentElement);
            if (in_parentElement.data.get('level') == 1) {
                tmp_offsets.set('top', tmp_offsets.get('top') + settings.get('verticalSubMenuOffsetY'));
                tmp_offsets.set('bottom', tmp_offsets.get('bottom') + settings.get('verticalSubMenuOffsetY'));
                tmp_offsets.set('left', tmp_offsets.get('left') + settings.get('verticalSubMenuOffsetX'));
                tmp_offsets.set('right', tmp_offsets.get('right') + settings.get('verticalSubMenuOffsetX'));
            }

            // reposition if there was a change in the parent position/size
            if (!in_parentElement.data.get('offsets').compare(tmp_offsets)) {
                in_parentElement.data.set('offsets', tmp_offsets);

                if (settings.get('axis') == 'horizontal' && in_parentElement.data.get('level') == 1) {
                    var xCoor = tmp_offsets.get('left');
                    if (settings.get('verticalExpand') == 'north') {
                        var yCoor = tmp_offsets.get('top') - subMenu.offsetHeight - settings.get('verticalSubMenuOffsetY');
                    }
                    else {
                        var yCoor = tmp_offsets.get('bottom');
                    }
                }
                else {
                    var xCoor = tmp_offsets.get('right') + settings.get('horizontalSubMenuOffsetX');
                    var yCoor = tmp_offsets.get('top') + settings.get('horizontalSubMenuOffsetY');
                    if (domLib_isOpera || domLib_isSafari) {
                        var marginLeft = parseInt(document.defaultView.getComputedStyle(document.body, '').getPropertyValue('margin-left'));
                        var marginTop = parseInt(document.defaultView.getComputedStyle(document.body, '').getPropertyValue('margin-top'));
                        xCoor -= marginLeft;
                        yCoor -= marginTop;
                    }
                }

                var minWidth = settings.get('subMenuMinWidth');
                var renderedWidth = subMenu.offsetWidth;
                if (minWidth == 'inherit') {
                    minWidth = in_parentElement.offsetWidth + settings.get('subMenuWidthCorrection');
                }
                else if (minWidth == 'auto') {
                    minWidth = renderedWidth;
                }

                if (domLib_isKonq) {
                    // change with width of the first cell
                    subMenu.firstChild.firstChild.firstChild.firstChild.style.width = Math.max(minWidth, renderedWidth) + 'px';
                }
                else {
                    // change the width of the table
                    subMenu.firstChild.style.width = Math.max(minWidth, renderedWidth) + 'px';
                }
                
                var coordinates = domMenu_correctEdgeBleed(subMenu.offsetWidth, subMenu.offsetHeight, xCoor, yCoor, settings.get('screenPadding'), settings.get('axis'));
                subMenu.style.left = coordinates[0] + 'px';
                subMenu.style.top = coordinates[1] + 'px';

                // ** if we inherit, it is necessary to check the parent element width again **
                if (settings.get('axis') == 'horizontal' && settings.get('subMenuMinWidth') == 'inherit') {
                    subMenu.firstChild.style.width = Math.max(in_parentElement.offsetWidth + settings.get('subMenuWidthCorrection'), renderedWidth) + 'px';
                }
            }
        }

        // force konqueror to change the styles
        if (domLib_isKonq) {
            in_parentElement.firstChild.style.display = 'none';
            in_parentElement.firstChild.style.display = '';
        }

        subMenu.style.visibility = in_style;
        domLib_detectCollisions(subMenu, (in_style == 'hidden'), true);
    }
}

// }}}
// {{{ domMenu_toggleHighlight()

function domMenu_toggleHighlight(in_element, in_status)
{
    // if this is a heading, don't change the style
    if (!in_element.data.get('numChildren') && !in_element.data.get('uri')) {
        return;
    }

    var settings = domMenu_settings.get(in_element.data.get('basename'));
    var prefix = in_element.data.get('level') == 1 ? 'menu' : 'subMenu';
    var className = settings.get(prefix + 'ElementClass');
    var highlightElement = in_element.firstChild;

    var pseudoClass;
    if (in_status) {
        if (in_element.data.has('subMenu') && in_element.data.get('subMenu').style.visibility == 'visible') {
            pseudoClass = 'Active';
        }
        else if (in_element.data.get('numChildren') || in_element.data.get('uri')) {
            pseudoClass = 'Hover';
        }
    }

    if (pseudoClass) {
        className += ' ' + settings.get(prefix + 'Element' + pseudoClass + 'Class');
        // if we are changing to hover, change the alt contents (only change if needs it)
        if (highlightElement.childNodes.length == 2) {
            //alert(highlightElement.lastChild);
        }
        if (highlightElement.childNodes.length == 2 && highlightElement.lastChild.style.display == 'none') {
            highlightElement.firstChild.style.display = 'none';
            highlightElement.lastChild.style.display = '';
        }
    }
    else {
        // if we are changing to non-hover, change the alt contents (only change if needs it)
        if (highlightElement.childNodes.length == 2 && highlightElement.firstChild.style.display == 'none') {
            highlightElement.lastChild.style.display = 'none';
            highlightElement.firstChild.style.display = '';
        }
    }

    highlightElement.className = className;

    // force konqueror to change the styles
    if (domLib_isKonq) {
        highlightElement.style.display = 'none';
        highlightElement.style.display = '';
    }
}

// }}}
// {{{ domMenu_resolveLink()

function domMenu_resolveLink(in_this, in_event)
{
    var eventObj = domLib_isIE ? event : in_event;
    var currentTarget = domLib_isIE ? in_this : eventObj.currentTarget;
    var basename = currentTarget.data.get('basename');

    // close the menu system immediately when we resolve the uri
    domMenu_changeActivePath(false, domMenu_activeElement.get(basename), 0);

    var uri = currentTarget.data.get('uri');
    if (uri) {
        window.status = 'Resolving Link...';
        uri = uri.replace(/\/\/\//, domMenu_settings.get(basename).get('baseUri'));

        // open in current window
        if (!currentTarget.data.get('target') || currentTarget.data.get('target') == '_self') {
            window.location = uri;
        }
        // open in new window
        else {
            window.open(uri, currentTarget.data.get('target'));
        }
    }
}

// }}}
// {{{ domMenu_fixCircleRefs()

// We try and get rid of all circular references by using the domMenu_runXXX()
// methods, but some are still left, so we run this function for IE
// @see http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=bcslfd%24ahl%241%248300dec7%40news.demon.co.uk
function domMenu_fixCircleRefs()
{
    var clearElementProps = ['data', 'onmouseover', 'onmouseout', 'onmousedown', 
        'onmouseup', 'ondblclick', 'onclick', 'onselectstart', 'oncontextmenu'];
    var el;
    for (var d = document.all.length; d--;) {
        el = document.all[d];
        for (var c = clearElementProps.length; c--;) {
            el[clearElementProps[c]] = null;
        }
    }
}

// }}}
// {{{ domMenu_runXXX()

// All of these domMenu_runXXX() methods are used by the event handling sections to
// avoid the circular memory leaks caused by inner functions
function domMenu_runMouseoverOpenEvent(in_event) { domMenu_openEvent(this, in_event, 'openMouseoverMenuDelay'); }
function domMenu_runMousedownOpenEvent(in_event) { domMenu_openEvent(this, in_event, 'openMousedownMenuDelay'); }
function domMenu_runMouseoverSubOpenEvent(in_event) { domMenu_openEvent(this, in_event, 'openMouseoverSubMenuDelay'); }
function domMenu_runClickSubOpenEvent(in_event) { domMenu_openEvent(this, in_event, 'openClickSubMenuDelay'); }
function domMenu_runCloseEvent(in_event) { domMenu_closeEvent(this, in_event); }
function domMenu_runResolveLink(in_event) { domMenu_resolveLink(this, in_event); };
function domMenu_runCloseMenu(argv) 
{
    domMenu_toggleHighlight(argv[0], false); 
    domMenu_toggleSubMenu(argv[0], 'hidden');
    // if this is the top level, then the menu is being deactivated
    if (argv[0].data.get('level') == 1) {
        domMenu_activeElement.set(argv[1], false);
    }
}
function domMenu_runOpenMenu(argv)
{
    if (!domMenu_activeElement.get(argv[1])) { 
        domMenu_activeElement.set(argv[1], argv[0]); 
    } 

    domMenu_activateSubMenu(argv[0]);
}

// }}}

    </script>
    
    <script language="javascript">
// {{{ domMenu_main: data

domMenu_data.set('domMenu_main', new Hash(
    1, new Hash(
        'contents', 'Home',
        'contentsHover', 'Home',
        'uri', 'http://mojavelinux.com',
        'target', '_self',
        'statusText', 'Mojavelinux.com homepages',
        1, new Hash(
            'contents', 'News',
            'uri', 'http://mojavelinux.com',
            'target', '_blank',
            'statusText', 'Latest mojavelinux.com news'
        ),
        2, new Hash(
            'contents', 'Cooker',
            'uri', 'http://www.java2s.com',
            'statusText', 'Released open source programs',
            1, new Hash(
                'contents', 'Demos',
                'uri', 'http://www.java2s.com',
                'statusText', 'Program demos'
            ),
            2, new Hash(
                'contents', 'Beta',
                'uri', 'http://www.java2s.com',
                'statusText', 'Program betas'
            )
        ),
        3, new Hash(
            'contents', 'Pictures',
            'uri', 'http://www.java2s.com',
            'statusText', 'Pictureview picture catalog'
        ),
        4, new Hash(
            'contents', 'Tutorials',
            'uri', 'http://www.java2s.com',
            'statusText', 'Various tutorials I have put together'
        ),
        5, new Hash(
            'contents', 'Stats',
            'uri', 'http://www.java2s.com',
            'statusText', 'website statistics')),
    2, new Hash(
        'contents', 'Forums',
        'contentsHover', 'Forums',
        'uri', '',
        'statusText', 'Mojave forums',
        1, new Hash(
            'contents', 'Cooker',
            'uri', 'http://www.java2s.com',
            'statusText', 'Released programs'
        ),
        2, new Hash(
            'contents', 'phpBB Mods',
            'uri', 'http://www.java2s.com',
            'statusText', 'phpBB Forum Modifications'
        ),
        3, new Hash(
            'contents', 'MyCalendar Mod',
            'uri', 'http://www.java2s.com',
            'statusText', 'MyCalendar add-on for phpBB')),
    3, new Hash(
        'contents', 'Demos',
        'contentsHover', 'Demos',
        'uri', '',
        'statusText', 'Demo Sites',
        1, new Hash(
            'contents', 'DOM Menu',
            'uri', 'http://www.java2s.com',
            'statusText', 'Dynamic hierarchial menu using the DOM'
        ),
        2, new Hash(
            'contents', 'DOM Tooltip',
            'uri', 'http://www.java2s.com',
            'statusText', 'Dynamic tooltips using the DOM'
        ),
        3, new Hash(
            'contents', 'Popup Calendar',
            'uri', 'http://www.java2s.com',
            'statusText', 'Date selector popup calendar')),
    4, new Hash(
        'contents', 'Tutorials',
        'contentsHover', 'Tutorials',
        'uri', '',
        'statusText', 'Various tutorials',
        1, new Hash(
            'contents', 'Hash Tables',
            'uri', 'http://www.java2s.com',
            'statusText', 'Creating hash tables in javascript'
        ),
        2, new Hash(
            'contents', 'Understanding Events',
            'uri', 'http://www.java2s.com',
            'statusText', 'Understanding Events in javascript'
        ),
        3, new Hash(
            'contents', 'Using setTimeout',
            'uri', 'http://www.java2s.com',
            'statusText', 'Using the setTimeout method in javascript'
        ),
        4, new Hash(
            'contents', 'Tips & Tricks',
            'uri', 'http://www.java2s.com',
            'statusText', 'Random tips and tricks')),
    5, new Hash(
        'contents', 'Pictures',
        'contentsHover', 'Pictures',
        'uri', 'http://www.java2s.com',
        'statusText', 'Picture sites',
        1, new Hash(
            'contents', 'Pictureview',
            'uri', 'http://www.java2s.com',
            'statusText', 'Mojavelinux.com picture catalog'
        ),
        2, new Hash(
            'contents', 'Chintoons',
            'uri', 'http://www.java2s.com',
            'statusText', 'Chinchilla cartoons')),
    6, new Hash(
        'contents', 'Stats',
        'contentsHover', 'Stats',
        'uri', 'http://www.java2s.com',
        'statusText', 'Mojavelinux.com site statistics',
        1, new Hash(
            'contents', 'Overall Site Statistics for mojavelinux.com',
            'uri', 'http://www.java2s.com',
            'statusText', 'Overall statistics for mojavelinux.com'))
));

// }}}
// {{{ domMenu_main: settings

domMenu_settings.set('domMenu_main', new Hash(
    'subMenuWidthCorrection', -1,
    'verticalSubMenuOffsetX', -1,
    'verticalSubMenuOffsetY', -1,
    'horizontalSubMenuOffsetX', 1,
    'openMouseoverMenuDelay', 300,
    'closeMouseoutMenuDelay', 500,
    'expandMenuArrowUrl', 'arrow.gif'
));

// }}}
// {{{ domMenu_keramik: data

domMenu_data.set('domMenu_keramik', new Hash(
    1, new Hash(
        'contents', 'Home',
        'uri', '',
        'statusText', 'Home',
        1, new Hash(
            'contents', 'Main Page',
            'uri', 'http://www.java2s.com',
            'statusText', 'Mojave Page'
        ),
        2, new Hash(
            'contents', 'Contact mojavelinux.com',
            'uri', '',
            'statusText', 'Contact mojavelinux.com',
            1, new Hash(
                'contents', 'Dan',
                'uri', 'http://www.java2s.com',
                'statusText', 'Dan'
            ),
            2, new Hash(
                'contents', 'Sarah',
                'uri', 'http://www.java2s.com',
                'statusText', 'Sarah'
            )
        ),
        3, new Hash(
            'contents', 'Terms of Use',
            'uri', 'http://www.java2s.com',
            'statusText', 'Terms of Use'
        ),
        4, new Hash(
            'contents', 'Search this site',
            'uri', 'http://www.java2s.com',
            'statusText', 'Search this site'
        ),
        5, new Hash(
            'contents', 'Customize',
            'uri', '',
            'statusText', 'Customize',
            1, new Hash(
                'contents', 'Style Schemas',
                'uri', '',
                'statusText', 'Style Schemas'
            ),
            2, new Hash(
                'contents', 'Blue',
                'uri', 'http://java2s.com',
                'statusText', 'Blue'
            ),
            3, new Hash(
                'contents', 'Green',
                'uri', 'http://java2s.com',
                'statusText', 'Green',
                1, new Hash(
                    'contents', 'Green',
                    'uri', 'http://java2s.com',
                    'statusText', 'Green'
                )
            )
        )
    ),
    2, new Hash(
        'contents', 'CSS',
        'uri', '',
        'statusText', 'CSS',
        1, new Hash(
            'contents', 'Tutorials',
            'uri', '',
            'statusText', 'Tutorial Links'
        ),
        2, new Hash(
            'contents', 'Using Stylesheets',
            'uri', 'http://www.java2s.com',
            'statusText', ''
        ),
        3, new Hash(
            'contents', 'CSS Positioning',
            'uri', 'http://www.java2s.com',
            'statusText', 'Learning how to position elements with CSS'
        )
    ),
    3, new Hash(
        'contents', 'JavaScript',
        'uri', '',
        'statusText', 'JavaScript Section',
        1, new Hash(
            'contents', 'Tutorials',
            'uri', '',
            'statusText', 'JavaScript Tutorials'
        ),
        2, new Hash(
            'contents', 'Custom Hash() Class',
            'uri', 'http://www.java2s.com',
            'statusText', 'Making your own associative arrays in javascript'
        )
    ),
    4, new Hash(
        'contents', 'DHTML',
        'uri', '',
        'statusText', 'Dynamic HTML',
        1, new Hash(
            'contents', 'Tutorials',
            'uri', '',
            'statusText', 'Dynamic HTML Tutorials'
        ),
        2, new Hash(
            'contents', 'DOM Tooltip',
            'uri', 'http://www.java2s.com',
            'statusText', 'Making custom tooltips using the DOM'
        )
    ),
    5, new Hash(
        'contents', 'PHP',
        'uri', '',
        'statusText', 'PHP Section',
        1, new Hash(
            'contents', 'Tutorials',
            'uri', '',
            'statusText', 'PHP Tutorials'
        ),
        2, new Hash(
            'contents', 'Handling actions',
            'uri', 'http://www.java2s.com',
            'statusText', 'Form actions in PHP'
        )
    )
));

// }}}
// {{{ domMenu_keramik: settings

domMenu_settings.set('domMenu_keramik', new Hash(
    'menuBarWidth', '0%',
    'menuBarClass', 'keramik_menuBar',
    'menuElementClass', 'keramik_menuElement',
    'menuElementHoverClass', 'keramik_menuElementHover',
    'menuElementActiveClass', 'keramik_menuElementHover',
    'subMenuBarClass', 'keramik_subMenuBar',
    'subMenuElementClass', 'keramik_subMenuElement',
    'subMenuElementHoverClass', 'keramik_subMenuElementHover',
    'subMenuElementActiveClass', 'keramik_subMenuElementHover',
    'subMenuMinWidth', 'auto',
    'horizontalSubMenuOffsetX', -5,
    'horizontalSubMenuOffsetY', 3,
    'distributeSpace', false,
    'openMouseoverMenuDelay', -1,
    'openMousedownMenuDelay', 0,
    'closeClickMenuDelay', 0,
    'closeMouseoutMenuDelay', -1,
    'expandMenuArrowUrl', 'arrow.gif'
));

// }}}
// {{{ domMenu_BJ: data

domMenu_data.set('domMenu_BJ', domMenu_data.elementData['domMenu_keramik']);

// }}}
// {{{ domMenu_BJ: settings

domMenu_settings.set('domMenu_BJ', new Hash(
    'menuBarWidth', '0%',
    'menuBarClass', 'BJ_menuBar',
    'menuElementClass', 'BJ_menuElement',
    'menuElementHoverClass', 'BJ_menuElementHover',
    'menuElementActiveClass', 'BJ_menuElementActive',
    'subMenuBarClass', 'BJ_subMenuBar',
    'subMenuElementClass', 'BJ_subMenuElement',
    'subMenuElementHoverClass', 'BJ_subMenuElementHover',
    'subMenuElementActiveClass', 'BJ_subMenuElementHover',
    'subMenuMinWidth', 'auto',
    'distributeSpace', false,
    'openMouseoverMenuDelay', -1,
    'openMousedownMenuDelay', 0,
    'closeClickMenuDelay', 0,
    'closeMouseoutMenuDelay', -1,
    'expandMenuArrowUrl', 'arrow.gif'
));

// }}}
// {{{ domMenu_vertical: data

domMenu_data.set('domMenu_vertical', new Hash(
    1, new Hash(
        'contents', 'Home',
        'uri', 'http://mojavelinux.com',
        'target', '_self',
        'statusText', 'Mojavelinux.com homepages',
        1, new Hash(
            'contents', 'News',
            'uri', 'http://mojavelinux.com',
            'target', '_blank',
            'statusText', 'Latest mojavelinux.com news'
        ),
        2, new Hash(
            'contents', 'Cooker',
            'uri', 'http://www.java2s.com',
            'statusText', 'Released open source programs'
        ),
        3, new Hash(
            'contents', 'Demos',
            'uri', 'http://www.java2s.com',
            'statusText', 'Program demos'
        ),
        4, new Hash(
            'contents', 'Pictures',
            'uri', 'http://www.java2s.com',
            'statusText', 'Pictureview picture catalog'
        ),
        5, new Hash(
            'contents', 'Tutorials',
            'uri', 'http://www.java2s.com',
            'statusText', 'Various tutorials I have put together'
        ),
        6, new Hash(
            'contents', 'Stats',
            'uri', 'http://www.java2s.com',
            'statusText', 'website statistics'
        )
    ),
    2, new Hash(
        'contents', 'Forums',
        'uri', '',
        'statusText', 'Mojave forums',
        1, new Hash(
            'contents', 'Cooker',
            'uri', 'http://www.java2s.com',
            'statusText', 'Released programs'
        ),
        2, new Hash(
            'contents', 'phpBB Mods',
            'uri', 'http://www.java2s.com',
            'statusText', 'phpBB Forum Modifications'
        ),
        3, new Hash(
            'contents', 'MyCalendar Mod',
            'uri', 'http://www.java2s.com',
            'statusText', 'MyCalendar add-on for phpBB'
        )
    ),
    3, new Hash(
        'contents', 'Demos',
        'uri', '',
        'statusText', 'Demo Sites',
        1, new Hash(
            'contents', 'DOM Menu',
            'uri', 'http://www.java2s.com',
            'statusText', 'Dynamic hierarchial menu using the DOM'
        ),
        2, new Hash(
            'contents', 'DOM Tooltip',
            'uri', 'http://www.java2s.com',
            'statusText', 'Dynamic tooltips using the DOM'
        ),
        3, new Hash(
            'contents', 'Popup Calendar',
            'uri', 'http://www.java2s.com',
            'statusText', 'Date selector popup calendar'
        )
    ),
    4, new Hash(
        'contents', 'Tutorials',
        'uri', '',
        'statusText', 'Various tutorials',
        1, new Hash(
            'contents', 'Hash Tables',
            'uri', 'http://www.java2s.com',
            'statusText', 'Creating hash tables in javascript'
        ),
        2, new Hash(
            'contents', 'Understanding Events',
            'uri', 'http://www.java2s.com',
            'statusText', 'Understanding Events in javascript'
        ),
        3, new Hash(
            'contents', 'Using setTimeout',
            'uri', 'http://www.java2s.com',
            'statusText', 'Using the setTimeout method in javascript'
        ),
        4, new Hash(
            'contents', 'Tips & Tricks',
            'uri', 'http://www.java2s.com',
            'statusText', 'Random tips and tricks'
        )
    ),
    5, new Hash(
        'contents', 'Pictures',
        'uri', 'http://',
        'statusText', 'Picture sites',
        1, new Hash(
            'contents', 'Pictureview',
            'uri', 'http://',
            'statusText', 'status text'
        ),
        2, new Hash(
            'contents', 'Chintoons',
            'uri', 'http://',
            'statusText', 'status text'
        )
    ),
    6, new Hash(
        'contents', 'Stats',
        'uri', 'http://',
        'statusText', 'status text',
        1, new Hash(
            'contents', 'Overall Site Statistics',
            'uri', 'http://',
            'statusText', 'Overall statistics for'
        )
    )
));

// }}}
// {{{ domMenu_vertical: settings

domMenu_settings.set('domMenu_vertical', new Hash(
    'axis', 'vertical',
    'subMenuWidthCorrection', -1,
    'verticalSubMenuOffsetX', -1,
    'verticalSubMenuOffsetY', -1,
    'horizontalSubMenuOffsetX', 0,
    'horizontalSubMenuOffsetY', 0,
    'expandMenuArrowUrl', 'arrow.gif'
));

// }}}
    
    </script>
  </head>
  <body>
    <div class="title">Example 1: Horizontal Menu</div>
    <div class="main">
      <div class="p">
        <div id="domMenu_main"></div>
        <script language="javascript">
  domMenu_activate('domMenu_main');
        </script>
      </div>
    </div>
    
  </body>
</html>

           
       








dommenu-0.3.5.zip( 113 k)

Related examples in the same category

1.Application Menubar Example
2.[DOM Menu] :: Example 2 :: KDE Keramik Style Menu
3.[DOM Menu] :: Example 3: Brainjar.com 'Revenge of the Menubar' Style Menu
4.[DOM Menu] Example 4: Vertical Menu
5.[DOM Menu] :: Example 5 :: Two Menus
6.[DOM Menu] :: Example 6 :: Flash Hiding
7.Menu bar for an inner fake window
8.Fly in Menu item
9.Not too fancy menu with toolbar
10.Custom Contextual Menu(content sensitive)
11.Drop-Down Menus
12.Menu with sound
13.Menu based on Javascript
14.popup menu (content sensitive menu)
15.Complete Source Code for the Menu
16.Slide out menu
17.Dynamic menu: fly in
18.Menu and submenu
19.Slide out menu with i18N
20.Menu: XP, win 98 style
21.Simple drop-down menu example with layer
22.Build a simple fancy menu
23.Add/delete menu items
24.Customizable layout: customize menu layout
25.Vertical layout menu
26.Easy skinable menu with CSS
27.Menu Item properties
28.Direct link menu
29.Context menu: popup menu
30.Black Menu
31.Dropdown menu