
var useMenuFrames = false;
function initializeMenu()
{
	if (is_ie) useMenuFrames = true;	

	var id = "menu";
	var menu = new Menu(id);	
}

// indicates the menu object that is currently being used, or null if none
var activeMenu = null;
var menus = new Object();
// default is to run in this window.  Iframes will get this set to parent.
var menuExecutionScope = self;

addEvent(window, "onload", initializeMenu);
addEvent(document, "onmousedown", closeActiveMenu);
addEvent(window, "onunload", releaseMenus);

function releaseMenus()
{
	for (var menu in menus)
	{
		menus[menu].release();
	}
}

function closeActiveMenu()
{
	if (activeMenu != null)
	{
		activeMenu.closeMenu();
		activeMenu.clearHighlights();
		activeMenu = null;
	}

	cancelAllDelayedEvents();
}

// sets all links with the given style in the named menu to be enabled
// if enabled is true, and disabled if false
function menuSetItemState(menuId, style, enabled)
{
	var menu = menus[menuId];
	if (menu)
	{
		menu.setItemState(style, enabled);
	}
}

// Adds the given action to all links with the given style
// in the named menu.  The action is called after any other custom actions.
// The action should return true to execute the default
// item (as given by the href/target).  If any action returns false, 
// the remaining actions (including the default action) are skipped.
function menuAddItemAction(menuId, style, action)
{
	var menu = menus[menuId];
	if (menu)
	{
		menu.addItemAction(style, action);
	}
}

// returns true if the link is enabled.  Use this for the onClick event-handler for links
// to ensure they are enabled before the menu-initialization code runs
function menuCheckEnabled(l)
{
	return !hasStyle(l, "disabled");
}

function Menu(id)
{
	var menuElement = document.getElementById(id);
	if (menuElement && !menuElement.INITIALIZED)
	{
		this.menuId = id;
		this.menuOpen = false;
		
		this.menuIds = new Object();
		this.frameIds = new Object();
		this.links = new Object();

		this.actions = new Object();
		this.onClickFunctions = new Object();
		
		this.processUl(menuElement, true);
		menus[id] = this;
		menuElement.INITIALIZED = true;
	}
}

Menu.prototype.release = function ()
{
	for (var id in this.links)
	{
		var l = this.links[id];
		// Mozilla causes errors if we leave these handlers in place after the page is 
		// unloaded: attempts to reference anything in window scope throw an exception
		l.onmouseover = returnFalse;
		l.onmouseout = returnFalse;
		// IE will leak memory if we hold on to these elements
		delete this.links[id];
	}
}


// this assumes there is no more than 100 items in the top row
var __liZIndex = 100;

Menu.prototype.processUl = function (ul, top)
{
	this.menuIds[ul.id] = new Object();
	
	var children = ul.childNodes;
	for (var n = 0; n < children.length; n++)
	{
		var child = children[n];
		if (child.nodeType == 1 && child.nodeName == "LI")
		{
			// assign z-index of decreasing order for top LIs so that
			// the submenus that hang off them properly 
			if (top)
			{
				child.style.zIndex = __liZIndex--;
			}
			this.processLi(child, ul.id, top);
		}
	}
}


Menu.prototype.processLi = function (li, containerId, top)
{
	var children = li.childNodes;
	var l = null;
	var submenu = null;
	
	for (var n = 0; n < children.length; n++)
	{
		var child = children[n];
		if (child.nodeType == 1 && child.nodeName == "A")
		{
			l = child;
			this.menuIds[containerId][l.id] = true;
			// initialize this to an empty list
			this.actions[l.id] = new Array();
		}
		
		if (child.nodeType == 1 && child.nodeName == "UL")
		{
			submenu = child;
			var id = child.id;
			
			if (useMenuFrames)
			{
				var frameId = "frame-" + id;
				this.frameIds[frameId] = true;
				var iframe = document.getElementById("frame-" + id);
				
				iframe.style.height = child.offsetHeight + "px";
				iframe.style.width = child.offsetWidth + "px";
				iframe.style.left = (parseInt(child.offsetLeft) + 00) + "px";
				iframe.style.top = (parseInt(child.offsetTop) + 00) + "px";
			}
			
			this.processUl(submenu, false);
			
			if (useMenuFrames)
			{
				var scope = window.frames["frame-" + id];
				scope.addMenuToFrame(submenu, this);
			}
		}
	}

	if (l != null)
	{
		attachMenuEvent(l, top, this);
		if (top || !useMenuFrames)
		{
			this.links[l.id] = l;
		}
	}
}

function attachMenuEvent (l, top, menu)
{
	l.onmouseover = mouseOverHandler;
	l.onmouseout = mouseOutHandler;

	// first keep the action for this link, if any
	if (l.onclick)
	{
		menu.onClickFunctions[l.id] = l.onclick;
	}
	
	// we use up/down for most things instead of click, so we disable it
	l.onclick = stopPropagation;
	
	var isSubmenu = true;
	if (l.href) isSubmenu = false;
	
	if (isSubmenu)
	{
		if (top)
		{	
			l.onmousedown = topMenuEvent;
		}
		else
		{
			l.onmousedown = subMenuEventHandler;
		}
	}
	else
	{	
		if (top)
		{
			// a click for the top menu
			l.onclick = executeMenuItemHandler;
		}
		else
		{
			// we use mouseup here because we don't care where the mouse down
			// occurred--if the mouse is over a link when you release the button, that's a click.
			l.onmouseup = executeMenuItemHandler;
		}		
		
		// the mousedown can't get to the document, or it will close the menu
		l.onmousedown = stopPropagation;

		l.originalHref = l.href;
	}
	
	if (is_ie)
	{
		l.onselectstart = returnFalse;
		l.ondragstart = returnFalse;
	}
}

function mouseOverHandler(e)
{
	stopPropagation(e);	
	return menuExecutionScope.mouseOver(this);
}

function mouseOutHandler(e)
{
	stopPropagation(e);	
	return menuExecutionScope.mouseOut(this);
}

function subMenuEventHandler(e)
{
	stopPropagation(e);
	menuExecutionScope.subMenuEvent(this);
	
	return false;
}

function executeMenuItemHandler(e)
{
	stopPropagation(e);
	return menuExecutionScope.executeMenuItem(this);
}

var menuHighlightModifier = new ClassModifier("highlight");

function mouseOver(l)
{
	menuHighlightModifier.addRedundantly(l);

	if (activeMenu)
	{
		activeMenu.mouseOver(l.id);
	}

	window.status = l.title;
	return true;
}

function mouseOut(l)
{
	menuHighlightModifier.removeRedundantly(l);
	window.status = '';
	return true;
}

function topMenuEvent(e)
{
	// we have to check for a modal window here since otherwise we will open
	// the dropdown menu by running this code.  One way to stop this would be
	// to have the onFocus code that makes the window modal block other events
	// on the page, but I don't know how to do that.
	if (!checkModalWindow())
	{
		stopPropagation(e);
		if (menuExecutionScope.activeMenu)
		{
			menuExecutionScope.closeActiveMenu();
		}
		else
		{
			var menu = menuExecutionScope.menus[extractRootFromLink(this.id)];
			menuExecutionScope.activeMenu = menu;
			menu.openMenu(this.id);
		}
	}	
	return false;
}

function subMenuEvent(l)
{
	var menu = menus[extractRootFromLink(l.id)];
	activeMenu = menu;
	menu.closeMenu();
	menu.openMenu(l.id);
}


function executeMenuItem(l)
{
	if (hasStyle(l, "disabled") == false)
	{
		closeActiveMenu();
		
		var menu = menuExecutionScope.menus[extractRootFromLink(l.id)];
	
		var skip = false;	
		
		// execute the custom actions, in order, and stop 
		// if one returns false
		var actions = menu.actions[l.id];
		for (var n = 0; n < actions.length; n++)
		{
			var result = actions[n].apply(l);
			if (result !== true)
			{
				skip = true;
				break;
			}
		}
		
		if (!skip && menu.onClickFunctions[l.id])
		{
			var result = menu.onClickFunctions[l.id].apply(l);
			if (result !== true)
			{
				skip = true;
			}
		}
			
		if (!skip)
		{
			followLink.apply(l);
		}
	}
	return false;
}



Menu.prototype.mouseOver = function (linkId)
{
	var me = this;
	
	this.highlightPath(linkId);
	
	var id = convertLinkIdToSubmenu(linkId);
	id = stripLevel(id);
	var top = (id == this.menuId);
	
	// windows at the top are switched instantly
	scheduleDelayedEvent(openSubmenu, top ? 1 : 300);
	
	function openSubmenu()
	{
		// first close all menus
		me.closeMenu();
		
		me.openMenu(linkId);	
	}
}

Menu.prototype.openMenu = function (linkId)
{
	activeMenu = this;	
	this.menuOpen = true;

	var id = convertLinkIdToSubmenu(linkId);
	if (document.getElementById(id) == null)
	{
		id = stripLevel(id);
	}
	
	while (id != this.menuId)
	{
		var node = document.getElementById(id);
		addStyle(node, "submenu-shown");

		if (useMenuFrames)
		{
			// the UL has to be hidden to avoid some IE rendering bugs (?)
			node.style.visibility = "hidden";
			var node = document.getElementById("frame-" + id);
			addStyle(node, "submenu-shown");
		}

		id = stripLevel(id);
	}
}

Menu.prototype.closeMenu = function()
{
	this.menuOpen = false;

	if (useMenuFrames)
	{
		for (id in this.frameIds)
		{
			var el = document.getElementById(id);
			removeStyle(el, "submenu-shown");	
		}
	}
	
	for (id in this.menuIds)
	{
		var el = document.getElementById(id);
		removeStyle(el, "submenu-shown");	
	}	
}

// match .NN at the end of the string
var menuStrip = /\.\d+$/;

// matches everything before the .NN.NN... characters
//  (menulist).3.2.3.4.5.2.3.2
var rootMatch = /([^\.]*)/;

function stripLevel(id)
{
	return id.replace(menuStrip, "");
}

function extractRootFromLink(linkId)
{
	var results = rootMatch.exec(linkId);
	return results[1].replace("link-", "");
}

function convertLinkIdToSubmenu(id)
{
	return id.replace("link-", "");
}

function getScopeForLink(linkId)
{
	var id = "frame-" + stripLevel(convertLinkIdToSubmenu(linkId));
	var scope = window.frames[id];
	if (useMenuFrames && scope)
	{
		return scope;
	}
	else
	{	
		return self;
	}
}

function getContainerIdForLink(linkId)
{
	return stripLevel(convertLinkIdToSubmenu(linkId));
}

Menu.prototype.highlightPath = function (linkId)
{
	this.clearHighlights();

	var topLinkId = "link-" + this.menuId;
	while (linkId != topLinkId)
	{
		var scope = getScopeForLink(linkId);
		var node = scope.document.getElementById(linkId);
		menuHighlightModifier.addRedundantly(node);
		
		linkId = stripLevel(linkId);
	}
}

Menu.prototype.executeOnAllLinks = function (f)
{
	for (var id in this.links)
	{
		f(this.links[id]);
	}
}

Menu.prototype.clearHighlights = function ()
{
	var f = function(el) 
	{ 
		menuHighlightModifier.removeRedundantly(el); 
	}
	
	this.executeOnAllLinks(f);
}

var disabledMenuModifier = new ClassModifier("disabled");

Menu.prototype.setItemState = function (style, enabled)
{
	// construct this once, before we test each menu item
	var testMod = new ClassModifier(style);
	
	for (var id in this.links)
	{
		var l = this.links[id];
		
		if (testMod.has(l))
		{
			if (enabled)
			{
				disabledMenuModifier.remove(l);	
			}
			else
			{
				disabledMenuModifier.add(l);	
			}
		}
	}
}

Menu.prototype.addItemAction = function (style, action)
{
	var me = this;
	var f = function(el)
	{
		if (hasStyle(el, style))
		{
			var actions = me.actions[el.id];
			actions[actions.length] = action;
		}
	}

	this.executeOnAllLinks(f);
}

// Adds the contents of the given UL (submenu) to the frame that this 
// function is running in.  If you want it added to frame f, call it like this:
// f.addMenuToFrame(submenu)
function addMenuToFrame(submenu, menu)
{
	// a kludge to get the menu to be shown in the frame
	addStyle(submenu, "submenu-shown");
	
	var body = document.getElementById("body");
	addChildrenToFrame(submenu, body, menu);

	removeStyle(submenu, "submenu-shown");
	
	menuExecutionScope = parent;
}

function addChildrenToFrame(source, dest, menu)
{
	if (source.nodeType == 1)
	{
		// make a new element
		var node = document.createElement(source.nodeName);
		node.className = source.className;
		node.id = source.id;
		if (source.href) node.href = source.href;
		if (source.target) node.target = source.target;
		if (source.title) node.title = source.title;
		if (node.nodeName == "A")
		{
			attachMenuEvent(node, false, menu);
			menu.links[node.id] = node;
		}
			
		dest.appendChild(node);
						
		// recursively copy its children
		var children = source.childNodes;
		for (var n = 0; n < children.length; n++)
		{
			child = children[n];
			if ((child.nodeType == 1 && child.nodeName != "UL" && child.nodeName != "IFRAME") || child.nodeType == 3)
			{
				addChildrenToFrame(child, node, menu);
			}
		}		
	}
	else if (source.nodeType == 3)
	{
		// just clone the text
		var node = document.createTextNode(source.nodeValue);
		dest.appendChild(node);
	}
}


