/*##################################################################
Name:	menu.js
Desc:	makes UL LI menu functional

TODO:

	selected page
		- identical match where available
		- reduce address to fit name for next closest match
		
		- need to work BACKWARDS after menu has been initialized

##################################################################*/


function index_of(arr, val) {
	for (var i=0; i<arr.length; i++) {
		if (arr[i] == val) {return i;}
	}
	return -1;
}



function MenuJS2(config) {
	//alert(target);
	this.config =		config;
	this.div = 			document.getElementById(config['id']);
	this.url = 			config['url'];
	this.manual =		(config['manual'] == '1');
	this.popup =		(config['popup'] == '1');
	this.multi =		(config['multi'] == '1');
	
	this.linkList =		new Array();
	this.listCount = 	new Array();
	this.openList =		new Array();
	this.closeList =	new Array();
	this.interval =		null;
	this.firstNode = 	null;
	this.lastNode = 	null;
	
	this.init(this.div);
	
	
	this.firstNode.className =	'first_' + this.firstNode.className;
	this.lastNode.className =	'last_' + this.lastNode.className;
	
	this.selectMenu();
}

//--------------------------------------------------------------INTERNAL FUNCS
//returns menu item in target item's pathArray
MenuJS2.prototype.getFirstChild = function(target) {
	var pathArray = target.pathArray;
	var div = this.div;
	for (var i=0; i<pathArray.length; i++) {
		var index = pathArray[i];
		var div = div.childNodes[index];
		var divChild = div.childNodes[0];
		//alert(index + ' > ' + div.tagName + ' > ' + div + ' > ' + div.innerHTML);
		
		if ((div.tagName == 'LI') && (divChild.tagName == 'A')) {
			return divChild;
		}
	}
	return null;
}

//returns true if passed LINK has a submenu (a sibling UL)
MenuJS2.prototype.getSubMenu = function(target) {
	if ((target.tagName == 'A') && (target.href == '')) {
		var parent = target.parentNode;
		var len = parent.childNodes.length;		
		for (var i=0; i<len; i++) {
			var child = parent.childNodes[i];
			if (child.tagName == 'UL') {
				return child;
			}
		}
	}
	return null;
}

//get parent link in UL/LI list from passed link
MenuJS2.prototype.getParentLink = function(target) {
	var parent;
	try {
				// A	   LI		 UL			LI			A
		parent = target.parentNode.parentNode.parentNode.childNodes[0];
	} catch (e) {
		parent = null;
	} 
	return parent;
}

//set a menu button to hover, or not
MenuJS2.prototype.setHover = function(element, hover) {
	var className = element.className;
	var suffix_pos = String(className).lastIndexOf('_');
	if (suffix_pos >= 0) {
		var suffix = String(className).substr(suffix_pos);
		if (suffix == '_hover') {
			className = String(className).substr(0, suffix_pos);
		}
	}
	className = className + ((hover) ? '_hover' : '');
	element.className = className;
	return className;
}

/**/
//--------------------------------------------------------------EVENTS
//rollOUT of a submenu
MenuJS2.prototype.out = function(target) {
	this.setHover(target, false);
	if (!this.manual) {
		this.close(target);
	}
}
//hover over a menu item
MenuJS2.prototype.over = function(target) {
	this.setHover(target, true);
	if (!this.manual) {
		this.open(target);
	}
}
//opens a submenu
MenuJS2.prototype.toggle = function(target) {
	var submenu = this.getSubMenu(target);
	if (submenu) {
		var toggleOpen = (submenu.style.visibility != 'visible');
		
		if (toggleOpen) { //open
			this.openSequence(target, false);
		} else { //close down to selected level (if not multi)
			if (this.multi) {
				this.close(target);
			} else {
				var parentNode = this.getParentLink(target);
				this.openSequence(parentNode, false);
				this.setHover(target, true);
				this.showClosed(parentNode);
			}
		}
	}
}



//--------------------------------------------------------------ACTIONS
MenuJS2.prototype.openSequence = function(target, force) {
	var pathArray = target.pathArray;
	var node = this.div;
	
	if (!this.multi) {
		this.closeAll(false);
	} else {
		this.closeApply();
	}
	
	if (pathArray) {
		for (var i=0; i<pathArray.length; i++) {
			var index = pathArray[i];
			node = node.childNodes[index];
			
			if (node.tagName == 'LI') {
				var target = node.childNodes[0];
				this.open(target, force);
			}
		}
	}
}



//open target submenu
MenuJS2.prototype.open = function(target, force) {
	var index = index_of(this.closeList, target);
	if (index >= 0) {this.closeList.splice(index, 1);}
	if (index_of(this.openList, target) < 0) {this.openList.push(target);}
	this.showOpen(target, force);
}

//close the target submenu
MenuJS2.prototype.close = function(target) {
	var index = index_of(this.openList, target);
	if (index >= 0) {this.openList.splice(index, 1);}
	if (index_of(this.closeList, target) < 0) {this.closeList.push(target);}
	
	//start close countdown
	var parent = this;
	this.closeCancel();
	this.interval = setInterval(function() {parent.closeApply(false);}, 10); 
}

//close all closing and open menus
MenuJS2.prototype.closeAll = function(skip) {
	this.closeApply(skip);
	this.closeList = this.openList;
	this.closeApply(skip);
	this.openList = new Array();
}

//do the closing
MenuJS2.prototype.closeApply = function(skip) {
	this.closeCancel();
	
	for (var i=0; i<this.closeList.length; i++) {
		var target = this.closeList[i];
		this.showClosed(target, skip);
	}
	this.closeList = new Array();
}

//cancel a closeApply
MenuJS2.prototype.closeCancel = function() {
	clearInterval(this.interval);
	this.interval = null;
}






//--------------------------------------------------------------VISUALS
//open submenu
MenuJS2.prototype.showOpen = function(target, force) {
	//change look of item
	var submenu = this.getSubMenu(target);
	if (submenu) {
		if (!this.popup) {submenu.style.display = 'block';}
		submenu.style.visibility = 'visible';
		submenu.style.zIndex = 100;
		
		this.styleOpen(target, force);
	}
}
//close submenu
MenuJS2.prototype.showClosed = function(target, skip) {
	var submenu = this.getSubMenu(target);
	if (submenu) {
		if (!this.popup) {submenu.style.display = 'none';}
		submenu.style.visibility = 'hidden';
		
		if (!skip) {
			this.styleClosed(target);
		}
	}
}

MenuJS2.prototype.styleOpen = function(target, force) {
	var style = (this.manual || force) ? 'open' : 'closed';
	var name = target.className;
	name = name.replace('idle', style);
	name = name.replace('closed', style);
	target.className = name;
}
MenuJS2.prototype.styleClosed = function(target) {
	var name = target.className;
	name = name.replace('idle', 'closed');
	if (this.manual) {name = name.replace('open', 'closed');}
	target.className = name;
}



//--------------------------------------------------------------INIT
//inits menu item (link element (A))
MenuJS2.prototype.initItem = function(child) {
	var parent = this;
	var submenu = this.getSubMenu(child);
	
	//default class for A tags
	child.className = 		"idle";
	child.parentNode.onmouseout =		function() {parent.out(child);}
	child.parentNode.onmouseover =		function() {parent.over(child);}
	child.onclick =						function() {parent.toggle(child);}
	child.ondblclick =					function() {parent.toggle(child);}
}


//init menu (open, close, highlight)
MenuJS2.prototype.init = function(target, depth, pathArray) {
	if (depth == null) {depth = 0;}
	
	var listCount = 0;
	var isOpen = false;
	var count = target.childNodes.length;
	
	if (pathArray == null) {pathArray = new Array();}
	pathArray = pathArray.slice(0);
	var index = pathArray.length;
	
	//run thru the elements children
	for (var i=0; i<count; i++) {
		var child = 	target.childNodes[i];	//get child
		var tagName = 	child.tagName;			//get child tag
		var isList =	false;
		pathArray[index] = i;
		
		switch (tagName) {
			case 'A':
				this.initItem(child, target);	//init A element as menu item
				child.pathArray = pathArray.slice(0);
				this.linkList.push(child);
				this.showClosed(child);
				if (depth == 2) {
					if (!this.firstNode) {
						this.firstNode = child;
					}
					this.lastNode = child;
				}
				break;
			case 'LI':
				isList = true;			//flag when menu items found
				listCount++;			//count number of menu items in this menu
				//nobreak
			case 'UL':
				isOpen &= this.init(child, depth + ((isList)?1:0), pathArray); //test children (append OPEN flag)
				
				if ((tagName == 'UL') && (depth > 1)) {
					var topcap = document.createElement('DIV');
					var bottomcap = document.createElement('DIV');					
					topcap.className = 		(depth == 2) ? 	'sub_top_cap' : 	'deep_top_cap';
					bottomcap.className = 	(depth == 2) ? 	'sub_bottom_cap' : 	'deep_bottom_cap';

					child.appendChild(topcap);
					child.appendChild(bottomcap);
				}
				break;
		}
	}
	
	//keep track of the largest submenu lists at each depth
	this.listCount[depth] = Math.max(this.listCount[depth], listCount);
	
	//return OPEN flag (iterates the opening of the submenu branch)
	return isOpen;
}



//highlight the menu the matches the page we're on
MenuJS2.prototype.selectMenu = function() {
	var bestIndex = -1;
	var bestRatio = 0;
	var bestFound = false;
	
	var i = 0;
	while (!bestFound) {
		//get the link URL
		var href = this.linkList[i].href;
		
		//do some comparisons
		var exact =		(this.url == href);
		var contains = 	(this.url.substr(0, href.length) == href);
		var ratio =		(href.length / this.url.length);
		//alert('Compare\n' + href + '\n' + this.url + '\nExact: ' + exact + ', Contains: ' + contains + ', Ratio: ' + ratio);
		
		if (exact) {
			bestFound = true;
			bestIndex = i;
		} else if (contains) {
			if (ratio > bestRatio) {
				bestRatio = ratio;
				bestIndex = i;
			}
		}
		if (i<this.linkList.length-1) {i++;} else {bestFound = true;}
	}
	
	if (bestIndex >= 0) {
		var child = this.linkList[bestIndex];
		if (this.manual) {
			this.openSequence(child, false);
		} else {
			this.openSequence(child, true);
			this.closeAll(false);
			//var first = this.getFirstChild(child);
			//this.styleOpen(first, true);
		}
		
		
		child.className = 'selected';
	}
}






















































function MenuJS(config) {
	this.config =		config;
	this.div = 			document.getElementById(config['id']);
	this.url = 			config['url'];
	
	//also store a TRIMMED version of the URL (removes page numbers so they can be matched to link URL)
	var reg = 			new RegExp('(.*?)(\\/\\d)*$', 'gi');
	var matches = 		reg.exec(this.url);
	this.trimmed_url = 	matches[1];
	
	this.multi = 		false;
	this.target =		null;
	this.listCount = 	new Array();
	this.maxChildren = 	0;
	
	this.init(this.div);
	//this.createAllowance();
}


//--------------------------------------------------------------PUBLIC
//opens a submenu
MenuJS.prototype.show = function(target) {
	var submenu = target.parentNode.childNodes[1];
	if (this.isSubMenu(target)) {
		if (submenu.style.display != 'block') {
			this.open(target);
		} else {
			this.close(target);
		}
	}
}

//hover over a menu item
MenuJS.prototype.hover = function(target) {
	setStateSuffix(target, {over:true});
}

//rollOUT of a submenu
MenuJS.prototype.up = function(target) {
	setStateSuffix(target, {over:false});
}

//--------------------------------------------------------------INTERNAL FUNCS
//returns true if item has a submenu
MenuJS.prototype.isSubMenu = function(target) {
	var group = false;
	//target MUST be link
	if ((target.tagName == 'A') && (target.href == '')) {
		//get target parents second child: will be a UL element, so if it exists then its a sub-menu
		group = (target.parentNode.childNodes[1] != null);
		if (group) {
			group = (target.parentNode.childNodes[1].childNodes.length > 0);
		}
	}
	return group;
}





var ELEMENT_STATE_SELECTED =		'_selected';
var ELEMENT_STATE_OVER = 			'_over';
var ELEMENT_STATE_OVERSELECTED = 	'_overselected';
var ELEMENT_STATE_UP = 				'';
var ELEMENT_STATE_DOWN = 			'_down';
var ELEMENT_STATE_DISABLED = 		'_disabled';
var ELEMENT_STATE_INVALID = 		'_invalid';

//apply state suffix to target element
function setStateSuffix(element, stateList) {
	var suffix = 	'';
	
	if (stateList != null) {
		var over = 		stateList['over'];
		var selected = 	stateList['selected'];
		var disabled = 	stateList['disabled'];
		var invalid = 	stateList['invalid'];
	
		if (disabled) {
			suffix = ELEMENT_STATE_DISABLED;
		} else if (over && selected) {
			suffix = ELEMENT_STATE_OVERSELECTED;
		} else if (over) {
			suffix = ELEMENT_STATE_OVER;
		} else if (selected) {
			suffix = ELEMENT_STATE_SELECTED;
		} else if (invalid) {
			suffix = ELEMENT_STATE_INVALID;
		}
	}
	
	var className = element.className;
	var suffix_pos = String(className).lastIndexOf('_');
	if (suffix_pos >= 0) {
		className = String(className).substr(0, suffix_pos);
	}
	element.className = className + suffix;
	return className;
}


//inits menu item (link element (A))
MenuJS.prototype.initItem = function(target) {
	var parent = this;
	
	//default class for A tags
	target.className = 		"menuitem";
	target.onmouseout =		function() {parent.up(this);}
	target.onmouseover =	function() {parent.hover(this);}
	target.onclick =		function() {parent.show(this);}
	target.ondblclick =		function() {parent.show(this);}
}


//init menu (open, close, highlight)
MenuJS.prototype.init = function(target, depth) {
	if (depth == null) {depth = 0;}
	var currentCount = 0;
	var isOpen = false;
	var count = target.childNodes.length;
	
	//run thru the elements children
	for (var i=0; i<count; i++) {
		var child = 	target.childNodes[i];	//get child
		var tagName = 	child.tagName;			//get child tag
		var isList =	false;
		
		switch (tagName) {
			case 'A':
				this.initItem(child);	//init A element as menu item
				isOpen = ((child.href == this.url) || (child.href == this.trimmed_url)); //if match set the OPEN flag
				child.className = (isOpen) ? 'selected' : (this.isSubMenu(child)) ? 'closed' : child.className;
				break;
			case 'LI':
				isList = true;			//flag when menu items found
				currentCount++;			//count number of menu items in this menu
			case 'UL':
				isOpen &= this.init(child, depth + ((isList)?1:0)); //test children (append OPEN flag)
				break;
		}
	}
	
	//store new count if greater than last count (this will be used to find max menu length later)
	if (this.listCount[depth] == null) {this.listCount[depth] = 0;}
	this.listCount[depth] = Math.max(this.listCount[depth], currentCount);

	//if OPEN flag then open the parent menu to show current item
	if (isOpen && (depth > 0) && (tagName == 'A')) {
		this.show(target.parentNode.parentNode.childNodes[0]);
	}
	
	if (depth == 0) {
		this.div.style.display = 'block';
	}
	//return OPEN flag (iterates the opening of the submenu branch)
	return isOpen;
}


//sets max menu height to allow for largest submenu nest open/closing
MenuJS.prototype.createAllowance = function() {
	var height = this.config['item_height'];
	
	//calculate the MAXIMUM number of menu items visible at one time (assume auto other-collapse, counts nested too)
	for (var i=0; i<this.listCount.length; i++) {
		this.maxChildren += this.listCount[i];
	}
	//set height of div container
	this.div.style.height = ((this.maxChildren * height) + 10)+'px';
}


//open target submenu
MenuJS.prototype.open = function(target) {
	if (this.multi == false) {
		this.closeOthers();
	}
	var submenu = target.parentNode.childNodes[1];
	submenu.style.display = 'block';
	target.className = 'menuopen';
	this.target = target;
	this.up(target);
}

//close other open submenus
MenuJS.prototype.closeOthers = function() {
	if (this.target != null) {
		this.close(this.target);
		this.target.className = 'closed';
		this.target = null;
	}
}

//close the target submenu
MenuJS.prototype.close = function(target) {
	var submenu = target.parentNode.childNodes[1];
	submenu.style.display = 'none';
	target.className = 'closed';
}
