
/* 
	Accessible Progressively-Enhanced Dropdown Menus
	Copyright 2007, Charlie Fiskeaux II
	
	Usage:
		1. Make sure your main menu is an <ol> or <ul>
		2. Make sure each submenu is an <ol> or <ul> inside of a main menu <li> (and after the <a> for that main menu item)
		3. Give your main menu <ol> or <ul> an "id" attribute and assign that "id" to the sbm_menu_id variable
		4. Attach this script to the page
		5. Call sbm_initialize() immediately after your menu list (to prevent a FOUC-like occurrence).
	
	Sample:
		<ol id="menu">
			<li><a href="#">Main Menu Item</a>
				<ul>
					<li><a href="#">Submenu Item</a></li>
					<li><a href="#">Submenu Item</a></li>
				</ul>
			</li>
		</ol>
		<script type="text/javascript">sbm_initialize();</script>
*/

/* options */
var sbm_close_delay = 250; // milliseconds
var sbm_click_also = false; // whether to activate the menus on click as well as mouseover
var sbm_block_clicks = false; // if click_also is false, then this will determine whether click events are passed on to the <a> tag
/* operational variables */
var sbm_menu_id = "menu"; // the id of the main menu <ul> or <ol>
var sbm_submenu_id_prefix = "submenu_"; // the prefix for the numbered ids which will be applied to each submenu's <ul> or <ol>
var sbm_link_id_prefix = "link_"; // the prefix for the numbered ids which will be applied to each main menu item's <a>
var sbm_submenu_class = "submenu"; // the class which will be applied to each submenu's <ul> or <ol>
var sbm_menu_item_class = "menu_item"; // the class which will be applied to the <a> of each main menu item which contains a submenu
var sbm_persist_class = "persist"; // the class which will be applied to the <a> of a main menu item when its submenu is opened (removed when closed)
var sbm_data_objects = new Array();

function sbm_initialize() {
	var menu_node = document.getElementById(sbm_menu_id);
	var menu_items = menu_node.childNodes;
	/* loop through all the menu item nodes */
	for (n=0,n_limit=menu_items.length; n<n_limit; n++) {
		var menu_item = menu_items[n];
		if (menu_item.nodeType == 1) { // 1 is an element node, not a text node
			if (menu_item.tagName.toLowerCase() == "li") {
				menu_item.style.position = "relative";
				var item_children = menu_item.childNodes;
				var has_submenu = false;
				/* loop through this menu item's children to see if it has a submenu */
				for (c1=0,c1_limit=item_children.length; c1<c1_limit; c1++) {
					if (item_children[c1].nodeType == 1) {
						if (item_children[c1].tagName.toLowerCase() == ("ul" || "ol")) {
							has_submenu = true;
							var submenu_node = item_children[c1];
							break;
						}
					}
				}
				if (has_submenu) {
					/* loop through this menu item's children to find the link node */
					for (c2=0,c2_limit=item_children.length; c2<c2_limit; c2++) {
						if (item_children[c2].nodeType == 1) {
							if (item_children[c2].tagName.toLowerCase() == "a") {
								var link_node = item_children[c2];
								break;
							}
						}
					}
					/* set the starting status of the submenu */
					submenu_node.setAttribute("id",sbm_submenu_id_prefix+n);
					submenu_node.style.display = "none";
					submenu_node.style.position = "absolute";
					if (submenu_node.className.length > 0) {
						submenu_node.className += " "+sbm_submenu_class;
					} else {
						submenu_node.className = sbm_submenu_class;
					}
					/* modify the link */
					link_node.setAttribute("id",sbm_link_id_prefix+n);
					if (link_node.className.length > 0) {
						link_node.className += " "+sbm_menu_item_class;
					} else {
						link_node.className = sbm_menu_item_class;
					}
					link_node.onmouseover = function(event) {
						if (!event) { var event = window.event; }
						var from_id = event.relatedTarget ? event.relatedTarget.id : event.fromElement.id;
						if (from_id === this.id) {
							return; // this fixes Safari 2 bug where a link and it's text both fire events for the link
						}
						var id_pieces = this.id.split("_");
						var id_num = id_pieces[id_pieces.length-1];
						sbm_activate(id_num, "onmouseover");
					}
					link_node.onmouseout = function(event) {
						if (!event) { var event = window.event; }
						var to_id = event.relatedTarget ? event.relatedTarget.id : event.toElement.id;
						if (to_id === this.id) {
							return; // this fixes Safari 2 bug where a link and it's text both fire events for the link
						}
						var id_pieces = this.id.split("_");
						var id_num = id_pieces[id_pieces.length-1];
						sbm_activate(id_num, "onmouseout");
					}
					link_node.onfocus = function() {
						var id_pieces = this.id.split("_");
						var id_num = id_pieces[id_pieces.length-1];
						sbm_activate(id_num, "onfocus");
					}
					link_node.onblur = function() {
						var id_pieces = this.id.split("_");
						var id_num = id_pieces[id_pieces.length-1];
						sbm_activate(id_num, "onblur");
					}
					/* if they have enabled click_also, then add the click event */
					if (sbm_click_also === true) {
						link_node.onclick = function() {
							var id_pieces = this.id.split("_");
							var id_num = id_pieces[id_pieces.length-1];
							sbm_activate(id_num, "onclick");
							return false;
						}
					} else if (sbm_block_clicks === true) {
						link_node.onclick = function() {
							return false;
						}
					} else {
						link_node.onclick = function() {
							return true;
						}
					}
					/* add stuff to each submenu link node */
					var submenu_children = submenu_node.childNodes;
					for (c3=0,c3_limit=submenu_children.length; c3<c3_limit; c3++) {
						if (submenu_children[c3].nodeType == 1) { // 1 is an element node, not a text node
							if (submenu_children[c3].tagName.toLowerCase() == "li") {
								var item_children = submenu_children[c3].childNodes;
								for (c4=0,c4_limit=item_children.length; c4<c4_limit; c4++) {
									if (item_children[c4].nodeType == 1) { // 1 is an element node, not a text node
										if (item_children[c4].tagName.toLowerCase() == "a") {
											var submenu_link_node = item_children[c4];
											/* add event handlers to each submenu link node */
											submenu_link_node.onmouseover = function() {
												var id_pieces = this.parentNode.parentNode.id.split("_");
												var id_num = id_pieces[id_pieces.length-1];
												sbm_persist(id_num, "onmouseover");
											}
											submenu_link_node.onmouseout = function() {
												var id_pieces = this.parentNode.parentNode.id.split("_");
												var id_num = id_pieces[id_pieces.length-1];
												sbm_persist(id_num, "onmouseout");
											}
											submenu_link_node.onfocus = function() {
												var id_pieces = this.parentNode.parentNode.id.split("_");
												var id_num = id_pieces[id_pieces.length-1];
												sbm_persist(id_num, "onfocus");
											}
											submenu_link_node.onblur = function() {
												var id_pieces = this.parentNode.parentNode.id.split("_");
												var id_num = id_pieces[id_pieces.length-1];
												sbm_persist(id_num, "onblur");
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
}

function sbm_activate(id_num, event) {
	var submenu_node = document.getElementById(sbm_submenu_id_prefix+id_num);
	/* test to determine whether it's opening or closing */
	if (event == "onclick") {
		if (submenu_node.style.display.toLowerCase == "block") {
			var action = "closing";
		} else if (submenu_node.style.display.toLowerCase == "none") {
			var action = "opening";
		}
	} else if (event == "onmouseover" || event == "onfocus") {
		sbm_persist(id_num, event);
		var action = "opening";
	} else if (event == "onmouseout" || event == "onblur") {
		var action = "closing";
	}
	if (action == "opening") {
		submenu_node.style.display = "block";
		sbm_autoclose(id_num);
		var submenu_data_object = {"id":id_num};
		sbm_data_objects[id_num] = submenu_data_object;
	} else {
		var submenu_data_object = {"id":id_num};
		sbm_data_objects[id_num] = submenu_data_object;
		sbm_data_objects[id_num].timeout = window.setTimeout("sbm_close("+id_num+")", sbm_close_delay)
	}
}

function sbm_close(id_num) {
	window.clearTimeout(sbm_data_objects[id_num].timeout);
	sbm_clear_persistence(id_num);
	var submenu_node = document.getElementById(sbm_submenu_id_prefix+id_num);
	submenu_node.style.display = "none";
}

function sbm_persist(id_num, event) {
	var menu_link_node = document.getElementById(sbm_link_id_prefix+id_num);
	if (event == "onmouseover" || event == "onfocus") {
		if (sbm_data_objects[id_num]) {
			window.clearTimeout(sbm_data_objects[id_num].timeout);
		}
		/* if the menu item link for this submenu doesn't have the 'persist' class, then add it */
		if (menu_link_node.className.search(eval("/"+sbm_persist_class+"/")) == -1) {
			if (menu_link_node.className.length > 0) {
				menu_link_node.className += " "+sbm_persist_class;
			} else {
				menu_link_node.className = sbm_persist_class;
			}
		}
	} else if (event == "onmouseout" || event == "onblur") {
		sbm_data_objects[id_num].timeout = window.setTimeout("sbm_close("+id_num+")", sbm_close_delay);
	}
}

function sbm_autoclose(id_num) {
	for (s=0,s_limit=sbm_data_objects.length; s<s_limit; s++) {
		if (typeof(sbm_data_objects[s]) == "object") {
			if (sbm_data_objects[s].id != id_num) {
				sbm_close(sbm_data_objects[s].id);
			}
		}
	}
}

function sbm_clear_persistence(id_num) {
	var menu_link_node = document.getElementById(sbm_link_id_prefix+id_num);
	if (menu_link_node.className.search(eval("/"+sbm_persist_class+"/")) >= 0) {
		var index_w_space = menu_link_node.className.search(eval("/ "+sbm_persist_class+"/"));
		if (index_w_space >= 0) {
			var new_className = menu_link_node.className.substring(0,index_w_space);
			new_className += menu_link_node.className.substring(index_w_space+8);
		} else {
			var new_className = "";
		}
		menu_link_node.className = new_className;
	}
}

