// Filename: menu.js
// Author:   Steve Wallgren, ITO Grand Rapids, Michigan
// Purpose:  This script converts the double nested unordered lists that make up the left hand
//           menu into a collapsable menu. If this script fails to execute for any reason, the
//           menu remains unaltered, and therefore usable.
// Version:  0.4 - Fourth try. Adding check for first child as text so we don't stomp a preexisting
//           anchor tag in a submenu header. This also prevents dropping the menu header text if
//           the initializeMenu function is called more than once per page load. Added code to
//           create a "Fake" submenu list whenever a submenu header is found that doesn't have
//           a subordinate submenu list. The fake is necessary to keep the two object array's
//           indexes in line with each other.

// Var name prefixes have the following meanings
// boo = boolean
// i   = integer
// o   = object
// oa  = object array
// s   = string

// Several functions need to work on and with the outer LIs and the Inner ULs, so these arrays global
var oaMenuSections = new Array();
var oaInnerLists = new Array();
var oOuterList;
var oMenu;
var booClick = false;	// False when in hover mode, true when in click mode
var iLastClick;			// When in click mode contains offset of active submenu
var sActiveColor = "#F00";
var sInactiveColor = "#FF6";
var sSubmenuBGColor = "#102290";


// This function adds a function onto the window's onload event handler,
// without overriding anything that was already there.
// addLoadEvent is from 'DOM Scripting' by J. Keith. ISBN: 1-59059-533-5
function addLoadEvent(func)
{
	var oldOnload = window.onload;
	if( typeof window.onload != 'function')
	{
		window.onload = func;
	}
	else
	{
		window.onload = function() { oldOnload(); func(); }
	}
}

function initializeMenu()
{
	// Check to make sure the browser can handle the DOM functions
	// used by this script. Abort if capabilities lacking.
	// Never do browser detection, it is better to do capabilities detection.
	if(!document.getElementById || !document.getElementsByTagName ||
	   !document.createElement ||  !document.createTextNode)
	{
		return false;
	}
	
	// Find the menu DIV
	oMenu = document.getElementById("menu");
	
	// Find the menu list (the outer UL layer)
	oOuterList = oMenu.getElementsByTagName("UL")[0];
	
	// Populate array of inner lists
	// oaInnerLists = oOuterList.getElementsByTagName("UL");
	// Moved this population into loop through the menu sections
	// so that fake inner lists can be created if necessary. If
	// we kept this population here then it would be too late to
	// add the fakes by the time we could detect them.
	
	// Find each LI in oOuterList
	var i = 0;
	var x = 0;
	for(x=0; x < oOuterList.childNodes.length; x++)
	{
		if(oOuterList.childNodes[x].nodeName == 'LI')
		{
			oaMenuSections[i++] = oOuterList.childNodes[x];
		}
	}
	
	// Set up Menu Sections
	for(x=0; x < oaMenuSections.length; x++)
	{
		// Remember the offset into the object array
		oaMenuSections[x].offset = x;
		
		// If first child node is a text node, replace that node with
		// an anchor tag node containing the text. Anchors are needed
		// to provide tab stops for mouseless web browsers, such as
		// speaking browsers for the visually challenged. If the first
		// child is not a text node, we skip this process. Normally the
		// first child will be a text node of the submenu's title, but
		// some sites have made links out of these titles. We don't want
		// to insert an anchor if one is already there. This also corrects
		// for the possibility of this code executing twice for a single
		// page load. First pass will do the anchor tag insertion, second
		// pass will skip them all.
		oOldChild = oaMenuSections[x].firstChild;
		if(oOldChild.nodeType == 3)	// text nodes are type 3
		{
			oAnchor = document.createElement("a");
			sText = oOldChild.nodeValue;
			oAnchorText = document.createTextNode(sText)
			oAnchor.appendChild(oAnchorText);
			oAnchor.href = "#";
			oAnchor.style.color = sInactiveColor;
			oAnchor.style.textDecoration = "none";
			oaMenuSections[x].replaceChild(oAnchor,oOldChild);
		}
		
		// Attach event handlers
		oaMenuSections[x].onclick = function() {menuOnclick(this)};
		oaMenuSections[x].onmouseover = function() {menuMouseover(this)};
		oaMenuSections[x].onmouseout = function() {menuMouseout(this)};
		oaMenuSections[x].onfocus = function() {menuMouseover(this)};
		oaMenuSections[x].onblur = function() {menuMouseout(this)};
		
		// Delink menu sections from normal layout flow
		oaMenuSections[x].style.position = "relative";
	
		// Give menu headers a bit more separation since they are now click
		// and hover targets, this makes them easier for the user to pick out.
		oaMenuSections[x].style.paddingTop = "4px";
		oaMenuSections[x].style.paddingBottom = "4px";
		
		// Override default colors just to ensure consistency
		oaMenuSections[x].style.color = sInactiveColor;
		
		// Locate and style subordinate UL, create empty one if none exists
		if(oaMenuSections[x].getElementsByTagName("UL").length > 0)
		{
			oaInnerLists[x] = oaMenuSections[x].getElementsByTagName("UL")[0];
				
			// Restyle the inner list
			oaInnerLists[x].style.display = "none";
			oaInnerLists[x].style.position = "absolute";
			oaInnerLists[x].style.left = "120px";
			oaInnerLists[x].style.top = "-2px";
			oaInnerLists[x].style.padding = "8px";
			oaInnerLists[x].style.width = "125px";
			oaInnerLists[x].style.zIndex = "999";
			oaInnerLists[x].style.backgroundColor = sSubmenuBGColor;
		}
		else	// Force a fake inner list if one wasn't already there
		{
			oFakeList = document.createElement("ul");
			oFakeItem = document.createElement("li");
			oFakeText = document.createTextNode(" ");
			oFakeItem.appendChild(oFakeText);
			oFakeList.appendChild(oFakeItem);
			oaMenuSections[x].appendChild(oFakeList);
			oaInnerLists[x] = oFakeList;
			
			// Style the Fake List different than a Real List
			oaInnerLists[x].style.display = "none";
			oaInnerLists[x].style.position = "absolute";
			oaInnerLists[x].style.left = "0px";
			oaInnerLists[x].style.top = "0px";
			oaInnerLists[x].style.padding = "0px";
			oaInnerLists[x].style.width = "0px";
			oaInnerLists[x].style.backgroundColor = "transparent";
		}
	}
}

function menuMouseover(oMe)
{
	// Disable when in click mode
	if(booClick == true)
	{
		return true;
	}
	else
	{
		// Loop through all submenus, showing current
		// and hiding all others
		for(x=0; x < oaMenuSections.length; x++)
		{
			oaInnerLists[x].style.display = (oMe.offset == x) ? "block" : "none";
			oaMenuSections[x].firstChild.style.color = (oMe.offset == x) ? sActiveColor : sInactiveColor;
		}
	}
}

function menuMouseout(oMe)
{
	// Disable when in click mode
	if(booClick == true)
	{
		return true;
	}
	else
	{
		// Hide submenu and restore coloring
		oaInnerLists[oMe.offset].style.display = "none";
		oMe.firstChild.style.color = sInactiveColor;
	}
}

function menuOnclick(oMe)
{
	if(booClick == false)
	{
		// Display current submenu and lock
		oaInnerLists[oMe.offset].style.display = "block";
		iLastClick = oMe.offset;
		booClick = true;
	}
	else
	{
		if(iLastClick == oMe.offset)
		{
			// Hide current submenu and unlock
			oaInnerLists[oMe.offset].style.display = "none";
			booClick = false;
		}
		else
		{
			// Hide last submenu, display current submenu, recolor and lock
			oaInnerLists[iLastClick].style.display = "none";
			oaMenuSections[iLastClick].firstChild.style.color = sInactiveColor;
			oaInnerLists[oMe.offset].style.display = "block";
			oaMenuSections[oMe.offset].firstChild.style.color = sActiveColor;
			iLastClick = oMe.offset;
			booClick = true;
		}
	}
	return false;
}

// Put menu initialization into the onload event handler
addLoadEvent(initializeMenu);
