
// dropdown menu scripts

var branch; 		/* tracks menu that the mouse most recently left, to help with smooth opening and closing */

// menu initialization
function dropdownMenu (navid, placementid)
{
		// check for Internet Explorer
		var isie = (typeof document.all != 'undefined'	//document.all only defined in IE and Opera
					&& typeof window.opera == 'undefined'	// window.opera identifies Opera
					&& navigator.vendor != 'KDE');		// KDE identifies Konqueror
	
		// See if browser is recent enough to support Javascript
		if (typeof document.getElementById == 'undefined'	
			|| (navigator.vendor == 'Apple Computer, Inc.'
				&& typeof window.XMLHttpRequest == 'undefined')	// eliminate old versions of Safari
			|| isie && typeof document.uniqueID == 'undefined') // eliminate old Mac OS IE
			{
				// No point in continuing if there's no useful Javascript support
				return;		
			}

		var tree = document.getElementById(navid);
		if (tree)
		{
			branch = tree;
			
			// set the position of the menu
			var placementkey = document.getElementById(placementid);
			if (placementkey)
			{
				tree.style.left = getRoughPosition(placementkey,'x') + 'px';
				tree.style.top = getRoughPosition(placementkey,'y') + 'px';
			}
			
			//	each list item in the menu gets the trigger initialization function
			var items = tree.getElementsByTagName('li');
			for (var i = 0; i < items.length; i++)
			{
				dropdownTrigger(tree, items[i], navid, isie);
			}
		}
}


// close all menus from a given root node
function clearMenus(root)
{
		var menus = root.getElementsByTagName('ul');
		for (var i = 0; i < menus.length; i++)
		{
				menus[i].style.left = '-100em';
		}
}

// Safari generates events for mouse movement between text node and its surrounding padding
// Need to be able to ignore these.
function unwantedTextEvent()
{
		return (navigator.vendor == 'Apple Computer, Inc.'
					&& (event.target == event.relatedTarget.parentNode
						|| (event.eventPhase == 3
							&& event.target.parentNode == event.relatedTarget)));
}


// trigger initialization
// The Mouseover and Mouseout behavior is in in-line functions here.
// tree: the top ul level of the menu
// li: one list item in the menu
// navid: the id of the menu
// isie: is the browser IE?
function dropdownTrigger(tree, li, navid, isie)
{
	
	var opentime, closetime;							// ids of the timers that ensure smooth opening and closing
														// the timers are specific to each menu 
														
	var a = li.getElementsByTagName('a')[0];			// a: the link
	var menu = li.getElementsByTagName('ul').length > 0
		? li.getElementsByTagName('ul')[0] : null;		// menu: the submenu, if there is one, or null
	var horiz = tree.className.indexOf('horizontal') != -1;		// horiz: is this a horizontal or vertical menu?
	var issub = li.parentNode.id == navid;				// issub: is this a first-level menu or a submenu?
		
	// Give it 'hasmenu' class so that arrow will show to indicate submenu
	if (menu)
	{
		li.className += (li.className == '' ? '' : ' ') + 'hasmenu';
	}
	
	// On mouseover
	attachEventListener(li, 'mouseover', function(e)
	{
			// ignore spurious events caused by text nodes in Safari
			if (unwantedTextEvent()) {return;}
			
			// if this menu was waiting to be closed, ignore the close request
			clearTimeout(closetime);
			
			// if the previous menu is THIS one (i.e. the mouse moved off it then moved back on) don't want to close it
			if (branch == li) {branch = null; }
			
			// Give the link the 'rollover' class to change appearance
			// avoid leading space
			a.className += (a.className == '' ? '' : ' ') + 'rollover';
			
			// We're going to check the position of the menu to make sure it stays inside the viewport
			// but need to apply any repositioning to the lowest menu branch, not another intermediate element 
			var target = typeof e.target != 'undefined' ? e.target : e.srcElement;
			while (target.nodeName.toUpperCase() != 'LI')
			{
				target = target.parentNode;
			}
			if (target != li) {return;}
			
			if (menu)
			{
					// Last arg of setTimeout is the delay, in milliseconds
					// First arg (the inline function) is what happens after the delay
					// If clearTimeout occurs before the delay finishes, the action doesn't happen
					// This prevents us from opening menus inadvertently because of accidental mouseover
					opentime = window.setTimeout(function()
					{
						// before opening a menu, close the menu that was previously open.
						if (branch)
						{
							clearMenus(branch);
							branch = null;
						}
														  
						// show the submenu by giving it an offset in the visible range
						// position the menu at the right edge of the list item.
						menu.style.left = horiz ? (isie ? li.offsetLeft + 'px' : 'auto') : '0';
						menu.style.top = horiz && issub ? (isie ? a.offsetHeight + 'px' : 'auto')
							: (isie ? li.offsetTop + 'px' : '0');
							
						repositionMenu(menu);
						
					}, 250);
			}
	}, false);
	
	// On mouseout
	// and hide the submenu by giving it an offset off the screen
	attachEventListener(li, 'mouseout', function(e)
	{
			// ignore events created by text nodes in Safari
			if (unwantedTextEvent()) {return; }
			
			// Remove the 'rollover' class
			// But only if the mouse is moving to a node outside this one
			// related: the node the mouse is moving to
			var related = typeof e.relatedTarget != 'undefined' ? e.relatedTarget : e.toElement;
			if (!li.contains(related))
			{
					// if the menu hasn't been opened yet, cancel the request to open it
					clearTimeout (opentime);
					
					// remember the menu the mouse is moving off of
					branch = li;
					
					a.className = a.className.replace(/ ?rollover/g, '');
					if (menu)
					{
						// Second arg to setTimeout is delay in milliseconds
						// First arg is what happens after the delay
						closetime = window.setTimeout(function ()
						{
							// hide the menu by moving it offscreen	
							menu.style.left = '-100em';
						}, 600);
					}
			}
	}, false);
	
	// define the 'contains' function for browsers that don't have it
	if (!isie)
	{
		li.contains = function(node)
		{
			if (node == null) {return false;}
			if (node == this) {return true;}
			else {return this.contains(node.parentNode); }
		};
	}
	
}


// returns rough x or y position of element within the window
// not absolutely accurate because of borders and browser quirks
// ele: element
// dir: x or y
function getRoughPosition (ele, dir)
{
	var pos = dir == 'x' ? ele.offsetLeft : ele.offsetTop;		// Left if x is wanted, Top if y is wanted
	
	// add up the offsets of all the parents
	var tmp = ele.offsetParent;						
	while (tmp != null)
	{
		pos += dir == 'x' ? tmp.offsetLeft : tmp.offsetTop;
		tmp = tmp.offsetParent;
	}
	return pos;
}


// if necessary, reposition the menu so that it stays within the viewport
function repositionMenu(menu)
{
	// extent is the offset plus the width and height plus a fudge factor to cover inaccuracies in getRoughPosition
	var extent = [
				  	getRoughPosition(menu, 'x') + menu.offsetWidth + 2,
					getRoughPosition(menu, 'y') + menu.offsetHeight + 2
				];
	
	var viewsize = getViewportSize();
	
	if (extent[0] > viewsize[0])
	{
			// Width of menu exceeds width of window, need to correct
	
	
			// This shifts the menu to the left of its parent, 
			// with the same a tiny overlap it would have had in its normal position
			var offset = menu.offsetWidth +
				menu.parentNode.parentNode.offsetWidth;
			var inset = menu.parentNode.offsetWidth - menu.offsetLeft;
			menu.style.left = (0 - offset + (inset * 2)) + 'px';
			
			
			// Just shift the submenu to the left enough to be visible
			var current = parseInt(menu.style.left, 10);
			var difference = (extent[0] - viewsize[0]);
			menu.style.left = (current - difference) + 'px';   
	}
	if (extent[1] > viewsize[1])
	{
			// Height of menu exceeds height of window, need to correct
			// Just shift the menu up far enough to keep it inside the window
			var current = parseInt(menu.style.top, 10);
			var difference = (extent[1] - viewsize[1]);
			menu.style.top = (current - difference) + 'px';
	}
}

