/*
 * This JavaScript runs through an unordered list and makes it menuable.
 * In a horizontal fashion, that is. Modding it to work vertically probably
 * wouldn't be too hard. Some
 * day it'd be nice to use CSS:hover elements for this, but as long as IE6 is
 * around, we won't be.
 *
 * List elements with submenus will automatically get the CSS class "subnav"
 * attached.
 *
 * To use:
 *  1. Include prototype.js
 *  2. Include this file
 *  3. Set the classname to the unordered lists that you want to replace to
 *     'replace-with-zmenu'.
 *
 * Graceful degradation: If the user has JavaScript disabled, they just get the
 * full unordered list dumped out in plain view. Not very pretty, but accessible, and
 * people surfing around with JavaScript disabled are probably used to seeing
 * such things.
 *
 * You can still style the li elements with CSS as long as you observe the following
 * caveats:
 *   - You cannot use width: auto; on li elements -- some sort of width must be
 *     explicitly set, whether it be ems, px, % or otherwise. (If you forget this,
 *     this script will override you and default to 10ems.)
 *   - You cannot apply a margin to the li elements, but padding, borders, and
 *     everything else is fine.
 *
 *
 * Browser compatibility:
 *   - Fully works in these browsers:
 *       - Opera 9 / Win
 *       - Firefox 1.5 / Win
 *   - Works with some caveats:
 *       - IE 6/Win
 *           - <select> controls are windowless and will bleed through the window
 *             elements (because they don't have any z-order). This doesn't come
 *             up very often. Either move the select box out of the way or dynamically
 *             hide it on a rollover event.
 *   - All unlisted browsers are untested
 *
 * @author Nick Piasecki <nick@zincastle.com>, except for the bit about
 * onmouseenter and onmouseleave
 *
 * Changes:
 * 02/21/06 - Firefox will continue to fire mouse events after destroying some
 * objects when the window is unloading. Workaround added to event handlers.
 *
 */
 
/*
 * Thanks to javascript.faqts for this way to mimic IE's onmouseenter and
 * onmouseleave events.
 */
Object.extend( Event, {
		containsDOM: function( oContainer, oContainee ) {
			var bIsParent = false;
			do {
				if ( (bIsParent = oContainer == oContainee) )
					break;
				oContainee = oContainee.parentNode;
			}
			while ( oContainee != null );
			return bIsParent;
		},
		
		onmouseenter: function( oElement, oEvent ) {
			/*
			 * Bug fix 02/01/2006 (nick): Flip the testing of relatedTarget vs contains
			 * Opera 8+ implemented the W3C's .relatedTarget correctly, but still contains
			 * a broken implementation of IE's .contains prop
			 */
			if ( oEvent.relatedTarget ) {
				return !Event.containsDOM( oElement, oEvent.relatedTarget );
			}
			else if ( oElement.contains && oEvent.fromElement ) {
				return !oElement.contains( oEvent.fromElement );
			}
		},
		
		onmouseleave: function( oElement, oEvent ) {
			/*
			 * Bug fix 02/01/2006 (nick): Flip the testing of relatedTarget vs contains
			 * Opera 8+ implemented the W3C's .relatedTarget correctly, but still contains
			 * a broken implementation of IE's .contains prop
			 */
			if ( oEvent.relatedTarget ) {
				return !Event.containsDOM( oElement, oEvent.relatedTarget );
			}
			else if ( oElement.contains && oEvent.toElement ) {
				return !oElement.contains( oEvent.toElement );
			}
		}
	}
);

/******************************************************************************/

var ZMenuMaker = Class.create();

ZMenuMaker.prototype = {
	initialize: function( oUl ) {
		this.oUl = oUl;
		this._recurseTree( oUl, 0 );
	},
	
	onmouseout: function( oEvent ) {
		/* Workaround Firefox bug ... Firefox will destroy objects but sometimes
		 * keep firing registered mouse events as page is unloading
		 */
		if ( !Event.onmouseleave ) {
			return;
		}
		
		if ( Event.onmouseleave( this, oEvent ) ) {
			$A( this.childNodes ).each(
				function( oElem, iIdx ) {
					if ( oElem.tagName && oElem.tagName.match( /ul/i ) ) {
						if ( Event.onmouseleave( oElem, oEvent ) ) { 
							oElem.style.display = 'none';
						};
					}
				}
			);
		}
	},
	
	onmouseover: function( oEvent ) {
		/* Workaround Firefox bug ... Firefox will destroy objects but sometimes
		 * keep firing registered mouse events as page is unloading
		 */
		if ( !Event.onmouseenter ) {
			return;
		}
		
		if ( Event.onmouseenter( this, oEvent ) ) {
			var me = this;
			me.style.cursor = 'pointer';
			$A( this.childNodes ).each(
				function( oElem, iIdx ) {
					if ( oElem.tagName && oElem.tagName.match( /ul/i ) ) {
						oElem.style.display = 'block';
						if ( oElem.iLevel > 0 ) {
							oElem.style.position = 'absolute';
							oElem.style.top = ( oElem.iLevel == 1 ) ? me.offsetHeight + 'px' : 0;
							if ( oElem.iLevel < 2 ) {
								oElem.style.left = 0 + 'px';
							} else {
								oElem.style.left = ( me.offsetWidth - ( ( oElem.offsetWidth - oElem.clientWidth ) / 2  ) ) + 'px';
							}
						}
					}
				}
			);
		}
	},
	
	_recurseTree: function( oUl, iLevel ) {
		var me = this;
		oUl.style.listStyleType = 'none';
		oUl.style.margin = '0';
		oUl.style.padding = '0';
		oUl.iLevel = iLevel;
		$A( oUl.childNodes ).each(
			function( oElem, iIdx ) {
				if ( oElem.tagName && oElem.tagName.match( /li/i ) ) {
					oLi = oElem;
					oLi.style.position = 'relative';
					oLi.style.zIndex = 5;
					if ( iLevel == 0 ) {
						if ( oLi.style.cssFloat != undefined ) {
							oLi.style.cssFloat = 'left';
						} else {
							// IE6 doesn't understand the property they invented
							oLi.style.styleFloat = 'left';
						}
						Element.addClassName( oLi, 'root' );
					} else {
						if ( oLi.offsetWidth == 0 && oLi.style.width == undefined ) {
							//we must force a width for offsetWidth to work correctly
							oLi.style.width = '10em';
						}
					}
					oLi.onmouseover = me.onmouseover.bindAsEventListener( oLi );
					oLi.onmouseout = me.onmouseout.bindAsEventListener( oLi );
					$A( oLi.childNodes ).each(
						function( oElem, iIdx ) {
							if ( oElem.tagName && oElem.tagName.match( /ul/i ) ) {
								if ( iLevel > 0 ) {
									Element.addClassName( oLi, 'subnav' );
								}
								oElem.style.display = 'none';
								me._recurseTree( oElem, iLevel + 1 );
							}
						}
					)
				}
			}
		);
	}
};

Event.observe(
	window,
	'load',
	function( oEvent ) {
		$$( '.replace-with-zmenu' ).each(
			function( oElem, iIdx ) {
				if ( oElem.tagName && oElem.tagName.match( /ul/i ) ) {
					new ZMenuMaker( oElem );
				}
			}
		);
	},
	false
);