// $Id: nice_menus.js,v 1.1.2.1 2006/03/30 09:57:19 jakeg Exp $

//<craftyspace+  entire file significantly rewritten />

/* TODO:
	Get side arrow to appear on parents of sub-sub-menus (beware of constant reloading of image)
	Look into using drubal.js instead of niceMenuClassManager
	Add rounded corners. See:
		http://www.html.it/articoli/niftycube/index.html
		http://www.booksbypeopleiknow.com/rounded_with_a_bullet.html
		http://www.fireandknowledge.org/archives/2004/05/22/rounded-corners-without-images-part-1/
*/

// part of hack to fix safari bug where it calls mouseout for menu it's entering not the one it's leaving
var nLastOver = null;

function niceMenuOnLoad() {
	// setup debug output area: assumes that server has used drupal_set_message() for at least one message
	// since js trace writes to the same div
	// Warning: if uncommented will cause js errors if there is no div with id of "messages status"
	//var nDebugArea = getElementsByClass("messages status", document, "div")[0];
	//nDebugArea.innerHTML = ""; //"debug:<br/>";
	//trace.nArea = nDebugArea;

	// find all menuparent li tags and add event handlers to them
	var ulNodes = getElementsByClass("nice-menu");
	for (var i = 0; i < ulNodes.length; i++) {
		var liNodes = ulNodes[i].getElementsByTagName("li");
		for (var j = 0; j < liNodes.length; j++) {
			if (liNodes[j].className == "menuparent") {
				liNodes[j].onmouseover = niceMenuMouseover;
				liNodes[j].onmouseout = niceMenuMouseout;
			}
		}
	}

	// find all menu a tags, and add event handlers to them if they are on the top level
	var aAnchors = document.getElementsByTagName("a");
	var n, nParent, bIsTop;
	var reNiceMenu = new RegExp("(^|\\s)nice-menu(\\s|$)");
	for (var i = 0; i < aAnchors.length; i++) {
		n = aAnchors[i];
		if (niceMenuClassManager.has("topLinkOut", n)) {
			bIsTop = false;
			if (!niceMenuGetParentMenu(n)) {
				// it's a top menu without submenus
				n.onmouseover = niceMenuMouseover;
				n.onmouseout = niceMenuMouseout;
				bIsTop = true;
			}
			else if (n.parentNode) {
				nParent = n.parentNode;
				if (nParent.parentNode) {
					nParent = nParent.parentNode;
					if (nParent.className) {
						if (nParent.className.match(reNiceMenu)) {
							// it's a top menu with submenus
							bIsTop = true;
						}
					}
				}
			}
			// if a tag is not on top level, change its class
			if (!bIsTop) {
				niceMenuClassManager.remove("topLink", n);
				niceMenuClassManager.remove("topLinkOut", n);
				niceMenuClassManager.add("linkOut", n);
				if (niceMenuClassManager.has("menuParent", n.parentNode)) {
					//niceMenuClassManager.add("subParent", n);
					niceMenuClassManager.add("subParentOut", n);
				}
			}
		}
	}
}

// for debugging; set up in onload function above
function trace(s) {
	trace.nArea.innerHTML += s + "<br />";
}

function niceMenuMouseover(evt) {
	onNiceMenuOpenMenu();
	var n = evt ? evt.target : event.srcElement;
	var sId = n.id ? n.id : ""; // only for debugging purposes
	//trace("over start: " + n.tagName + ": " + n.className + ": " + sId);
	nLastOver = n;
	// if span, move up to surrounding anchor tag
	if (n.tagName.toLowerCase() == "span") {n = n.parentNode;}
	// if li, move down to first anchor tag (allows selecting item when in box but off text)
	if (n.tagName.toLowerCase() == "li") {n = n.firstChild;}
	if (n.tagName.toLowerCase() == "a") {
		if (niceMenuClassManager.has("topLinkOut", n)) {
			//trace("over topLinkOut: " + n.tagName + ": " + n.className + ": " + sId);
			niceMenuClassManager.remove("topLinkOut", n);
			niceMenuClassManager.add("topLinkOver", n);
		}
		else if(niceMenuClassManager.has("subParentOut", n)) {
			//trace("over subParentOut: " + n.tagName + ": " + n.className + ": " + sId);
			niceMenuClassManager.remove("subParentOut", n);
			niceMenuClassManager.add("subParentOver", n);
		}
		else if(niceMenuClassManager.has("linkOut", n)) {
			//trace("over linkOut: " + n.tagName + ": " + n.className + ": " + sId);
			niceMenuClassManager.remove("linkOut", n);
			niceMenuClassManager.add("linkOver", n);
		}
	}
	// chain up to make sure all ancestor parent menus receive over event (prevents premature closing)
	var nParent = n;
	while (nParent = niceMenuGetParentMenu(nParent)) {
		sId = nParent.id ? nParent.id : "";
		//trace("over parent: " + nParent.tagName + ": " + nParent.className + ": " + sId);
		niceMenuClassManager.add("liOver", nParent);
		nParent = nParent.parentNode;
	}
}

function niceMenuMouseout(evt) {
	var n = evt ? evt.target : event.srcElement;
	// keeps safari from highlighting multiple menu items (note: only need for mouseout not mouseover as latter not called for text in span)
	while (n.nodeType && n.nodeType == 3) {n = n.parentNode;}
	var sId = n.id ? n.id : "";
	//trace("out start: " + n.tagName + ": " + n.className + ": " + sId);
	if (nLastOver != n) {niceMenuMouseout({target: nLastOver});}
	// if span, move up to surrounding anchor tag
	if (n.tagName.toLowerCase() == "span") {
		n = n.parentNode;
	}
	// if li, move down to first anchor tag (allows unselecting item when selected in box but off text)
	if (n.tagName.toLowerCase() == "li") {n = n.firstChild;}
	if (n.tagName.toLowerCase() == "a") {
		if (niceMenuClassManager.has("topLinkOver", n)) {
			//trace("out topLinkOver: " + n.tagName + ": " + n.className + ": " + sId);
			niceMenuClassManager.remove("topLinkOver", n);
			niceMenuClassManager.add("topLinkOut", n);
		}
		else if(niceMenuClassManager.has("subParentOver", n)) {
			//trace("out subParentOver: " + n.tagName + ": " + n.className + ": " + sId);
			niceMenuClassManager.remove("subParentOver", n);
			niceMenuClassManager.add("subParentOut", n);
		}
		else if(niceMenuClassManager.has("linkOver", n)) {
			//trace("out linkOver: " + n.tagName + ": " + n.className + ": " + sId);
			niceMenuClassManager.remove("linkOver", n);
			niceMenuClassManager.add("linkOut", n);
		}
	}
	// chain up to make sure all ancestor parent menus receive out event (ensures all menus close when done)
	var nParent = n;
	while (nParent = niceMenuGetParentMenu(nParent)) {
		sId = nParent.id ? nParent.id : "";
		//trace("out parent: " + nParent.tagName + ": " + nParent.className + ": " + sId);
		niceMenuClassManager.remove("liOver", nParent);
		nParent = nParent.parentNode;
	}
	onNiceMenuCloseMenu();
}

// return first menuparent whether current node or ancestor
function niceMenuGetParentMenu(n) {
	// keeps safari from highlighting multiple menu items
	while (!n.className.match(niceMenuGetParentMenu.reParent)) {
		// fail if reach top of menu tree
		if (n.className.toLowerCase() == "content") {return false;}
		n = n.parentNode;
	}
	return n;
}
niceMenuGetParentMenu.reParent = new RegExp("(^|\\s)menuparent(\\s|$)");

// object to manage adding, removing or checking for a css class
// stores regex's so only have to compile once per page
var niceMenuClassManager = new Object();
niceMenuClassManager.oReList = new Object();
niceMenuClassManager.oReList.linkOut = new Object();
niceMenuClassManager.oReList.linkOut.str = "nice-menu-a-out";
niceMenuClassManager.oReList.linkOut.re = new RegExp("(^|\\s)nice-menu-a-out(\\s|$)");
niceMenuClassManager.oReList.linkOver = new Object();
niceMenuClassManager.oReList.linkOver.str = "nice-menu-a-over";
niceMenuClassManager.oReList.linkOver.re = new RegExp("(^|\\s)nice-menu-a-over(\\s|$)");
niceMenuClassManager.oReList.topLink = new Object();
niceMenuClassManager.oReList.topLink.str = "nice-menu-a-top";
niceMenuClassManager.oReList.topLink.re = new RegExp("(^|\\s)nice-menu-a-top(\\s|$)");
niceMenuClassManager.oReList.topLinkOut = new Object();
niceMenuClassManager.oReList.topLinkOut.str = "nice-menu-a-top-out";
niceMenuClassManager.oReList.topLinkOut.re = new RegExp("(^|\\s)nice-menu-a-top-out(\\s|$)");
niceMenuClassManager.oReList.topLinkOver = new Object();
niceMenuClassManager.oReList.topLinkOver.str = "nice-menu-a-top-over";
niceMenuClassManager.oReList.topLinkOver.re = new RegExp("(^|\\s)nice-menu-a-top-over(\\s|$)");
niceMenuClassManager.oReList.liOver = new Object();
niceMenuClassManager.oReList.liOver.str = "over";
niceMenuClassManager.oReList.liOver.re = new RegExp("(^|\\s)over(\\s|$)");
niceMenuClassManager.oReList.menuParent = new Object();
niceMenuClassManager.oReList.menuParent.str = "menuParent";
niceMenuClassManager.oReList.menuParent.re = new RegExp("(^|\\s)menuparent(\\s|$)");
niceMenuClassManager.oReList.subParent = new Object();
niceMenuClassManager.oReList.subParent.str = "niceMenuSubParent";
niceMenuClassManager.oReList.subParent.re = new RegExp("(^|\\s)niceMenuSubParent(\\s|$)");
niceMenuClassManager.oReList.subParentOut = new Object();
niceMenuClassManager.oReList.subParentOut.str = "niceMenuSubParentOut";
niceMenuClassManager.oReList.subParentOut.re = new RegExp("(^|\\s)niceMenuSubParentOut(\\s|$)");
niceMenuClassManager.oReList.subParentOver = new Object();
niceMenuClassManager.oReList.subParentOver.str = "niceMenuSubParentOver";
niceMenuClassManager.oReList.subParentOver.re = new RegExp("(^|\\s)niceMenuSubParentOver(\\s|$)");
niceMenuClassManager.reDoubleSpace = new RegExp("  ", "g");
// add a css class to node n; sType can be any sub object in oReList: e.g. linkOut, linkOver
niceMenuClassManager.add = function(sType, n) {
	var oClassInfo = this.oReList[sType];
	if (!n.className.match(oClassInfo.re)) {
		n.className += " " + oClassInfo.str;
	}
};
// remove a css class from node n; sType can be any sub object in oReList: e.g. linkOut, linkOver
niceMenuClassManager.remove = function(sType, n) {
	var oClassInfo = this.oReList[sType];
	n.className = n.className.replace(oClassInfo.re, " ");
	n.className = n.className.replace(this.reDoubleSpace, "");
};
// check wheher node n has css class represented by sType
niceMenuClassManager.has = function(sType, n) {
	if (!n.className) {return false;};
	var oClassInfo = this.oReList[sType];
	return n.className.match(oClassInfo.re);
};

function getElementsByClass(searchClass, node, tag) {
	var classElements = new Array();
	if (node == null) {node = document;}
	if (tag == null) {tag = '*';}
	var els = node.getElementsByTagName(tag);
	var elsLen = els.length;
	var pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)");
	for (i = 0; i < elsLen; i++) {
		if (pattern.test(els[i].className)) {
			classElements.push(els[i]);
		}
	}
	return classElements;
}

// EVENTS
var iNiceMenuCounter = 0;
// fires each time a menu item is entered (including moving from item to item within a menu)
function onNiceMenuOpenMenu() {
	iNiceMenuCounter++;
	var nFilter = gebi("fPushThroughElements");
	if (nFilter) {nFilter.className = "fHidePushThroughElements";}
}
// fires each time a menu item is exited (including moving from item to item within a menu)
function onNiceMenuCloseMenu() {
	iNiceMenuCounter--;
	var nFilter = gebi("fPushThroughElements");
	if (nFilter) {setTimeout("completeNiceMenuClose()", 400);}
}
// take action if no menu has been open for duration of timeout
function completeNiceMenuClose() {
	// leave item(s) hidden if menu is open
	if (iNiceMenuCounter) {return;}
	var nFilter = gebi("fPushThroughElements");
	if (nFilter) {nFilter.className = "fShowPushThroughElements";}
}

//This is the Drupal method of adding a function to the BODY onload event.	(See misc/drupal.js)
addLoadEvent(niceMenuOnLoad);
