// JavaScript Document
/*
author: Wayne Weibel
created: Monday, August 11, 2008

This is a TRUE flyout menu plugin, it is compatible with all current browsers.
Simply reference this file in your page <HEAD>.
Then create menus wherever you want on your document (as in the example below).

1. REQUIRED: A valid string is needed when creating a Menu, used to assign id to created DIV
2. CAUTION (Invalid Markup): innerHTML can contain any HTML, but is placed within <A></A>
3. NOTE: Since each Menu is its own DIV, the CSS can be set for each individual from the 
		 overall page and other Menus
4. NOTE: You will need to wrap each Menu in an element that you set the WIDTH on, otherwise
		 each item will not be displayed properly
5. LIMITATION: At present the flyout only goes to the left and down
			   and adjusts menus to a height of a single line

EXAMPLE (in page):
<script type="text/javascript">
	var menu = new Menu("ADDITIONAL_INFO");
		var menu1 = new MenuItem();
			menu1._innerHTML = "Additional Information";
	menu.addMenuItem(menu1);
			var menu1c = new MenuItem();
				menu1c._innerHTML = "Hurricane Response";
				menu1c._target = "_blank";
		menu1.addMenuItem(menu1c);
		
				var menu1c_1 = new MenuItem();
					menu1c_1._innerHTML = "TEST " + menu1c._submenu.length;
			menu1c.addMenuItem(menu1c_1);
	menu.generateHTML();
</script>
*/
var MenuArray = new Array();			//reference for every MENU created

function Menu(name) {
	this._MENU = name;					//REQUIRED: used to create the DIV id of the menu for setting CSS (if desired)
	this._submenu = new Array();
	this._uniqueMenuId = new Array();
	this.addMenuItem = addMenuItem;
	this.generateHTML = generateHTML;
	
	MenuArray[this._MENU] = this;
}

function generateHTML() {
//create all of the menu nodes and add them to the created section
	document.write("<DIV id='" + this._MENU + "'></DIV>");
	
	for(var i=0; i < this._submenu.length; i++){
		this._submenu[i].createMenuNode(this._MENU);
		this._submenu[i].hideSubMenu();
	}
}

function MenuItem() {
	this._href = window.location.href;	//default value - references current loaded page
	this._target = "_self";				//default target (can be null)
	this._innerHTML = null;				//CAUTION (Invalid Markup): innerHTML can contain any HTML, but is placed within <A></A>
	this._title = null;					//alternate text for link, must be set manually
	this._name = null;					//set to this._id during addMenuItem(), set manually after if desired
	this._id = null;					//DO NOT CHANGE manually, used in all functions
	this._element = null;				//DO NOT CHANGE manually, reference to generated HTML element
	this._parentMenu = null;
	this._submenu = new Array();
	this.addMenuItem = addMenuItem;
	this.setMenuStyle = setMenuStyle;
	this.createMenuNode = createMenuNode;
	this.displaySubMenu = displaySubMenu;
	this.hideSubMenu = hideSubMenu;
}

function addMenuItem(mItem){
//create a submenu for calling MenuItem out of mItem
	mItem._parentMenu = this;
	if (this._MENU){ 
		mItem._id = "menu" + this._submenu.length;
		mItem._name = mItem._id;
		this._uniqueMenuId[mItem._id] = mItem;
	}
	else {
		mItem._id = this._id + "_sub" + this._submenu.length;
		mItem._name = mItem._id;
		var parent = this;
		while (parent._parentMenu) { parent = parent._parentMenu; } //go up to MENU containing this mItem
		parent._uniqueMenuId[mItem._id] = mItem;
	}
	this._submenu.push(mItem);
}

function createMenuNode(MENU){
//create the HTML element for calling MenuItem and append to MENU
	var mlink = document.createElement("A");
	
	mlink.href = this._href;
	mlink.target = this._target;
	mlink.innerHTML = this._innerHTML;
	mlink.title = this._title;
	mlink.name = this._name;
	mlink.id = this._id;
	mlink.onmouseover = function(e){//here this = mlink, e = mouseover event
							setDisplayOn(this.parentNode.id,this.id);
							setMenuStyle(this.parentNode.id,this.id);
						};
	mlink.onmouseout = function(e){//here this = mlink, e = mouseout event
							setDisplayOff(this.parentNode.id,this.id);
						};
	mlink.onclick = function(e){//here this = mlink, e = onclick event
							setDisplayOff(this.parentNode.id,this.id);
						};

	document.getElementById(MENU).appendChild(mlink);
	this._element = mlink;

	for(var i=0; i < this._submenu.length; i++){
		this._submenu[i].createMenuNode(MENU);
	}
}

function setMenuStyle(rootId,elemId){
//called during mouseover of HTML element - creates the cascade for submenu
	var mlink = null;
	var regexW = /sub/g;	//regular expression to evaluate HTML element horizontal position
	var regexH = /[^\d]/g;	//regular expression to evaluate HTML element vertical position
	var offsetW = 0;
	var offsetH = 0;
		
	for(var i=0; i < MenuArray[rootId]._uniqueMenuId[elemId]._submenu.length; i++){
		mlink = MenuArray[rootId]._uniqueMenuId[elemId]._submenu[i]._element;
		
		mlink.style.width = getWidth(MenuArray[rootId]._uniqueMenuId[elemId]._element) + "px";
		mlink.style.height = getHeight(MenuArray[rootId]._uniqueMenuId[elemId]._element) + "px";

		offsetW = mlink.id.match(regexW).length;
		offsetH = parseMenuLevels(regexH, mlink.id);
		
		mlink.style.position = "absolute";
		mlink.style.fontSize = "14px";
		mlink.style.left = findPosX(document.getElementById(rootId)) + (getWidth(mlink) * offsetW) + "px";
		mlink.style.top = findPosY(document.getElementById(rootId)) + (getHeight(mlink) * offsetH) + "px";
	}
}

function parseMenuLevels(regexH, mlinkId){
//utility function to obtain the multiplier for setting the HTML element vertical position
	var offsetH = 0;
	var levels = mlinkId.split("_");
	for(var j=0; j < levels.length; j++){
		offsetH = offsetH + parseInt(levels[j].replace(regexH,""));
	}
	return offsetH;
}

function displaySubMenu(){
//make submenu visible
	for(var i=0; i < this._submenu.length; i++){
		this._submenu[i]._element.style.display = "block";
	}
}

function hideSubMenu(){
//recursively make submenus hidden
	for(var i=0; i < this._submenu.length; i++){
		this._submenu[i]._element.style.display = "none";
		this._submenu[i].hideSubMenu();
	}
}

function setDisplayOn(rootId,elemId){
//called during mouseover of HTML element
	var menu_item = MenuArray[rootId]._uniqueMenuId[elemId];
	var parent = menu_item._parentMenu;
	menu_item._element.style.display = "block";	//make this menu stay visible, nullifies hideSubMenu
	
	while (parent.displaySubMenu){ 
	//nullifies the hideSubMenu triggered on a mouseout event
		parent.displaySubMenu();
		parent = parent._parentMenu;
	}

	menu_item.displaySubMenu();
}

function setDisplayOff(rootId,elemId){
//called during mouseout of HTML element
	var menu_item = MenuArray[rootId]._uniqueMenuId[elemId];
	var parent = menu_item._parentMenu;
	
	while (parent.hideSubMenu){
	//make all submenus hidden - if complete mouseout
		parent.hideSubMenu();
		parent = parent._parentMenu;
	}

	menu_item.hideSubMenu();
}

//MODIFIED: getHeight and getWidth by John Kim @ http://frontendframework.com/javascript/javascript-element-width-height/
function getHeight(elem){
	var currentStyle;
	if (elem.currentStyle) { currentStyle = elem.currentStyle; }
	else if (window.getComputedStyle) {	try{ currentStyle = document.defaultView.getComputedStyle(elem, null); } catch(err){alert(err);}}
	else { currentStyle = elem.style; }
	return elem.offsetHeight;
}

function getWidth(elem){
	var currentStyle;
	if (elem.currentStyle) { currentStyle = elem.currentStyle; }
	else if (window.getComputedStyle) {	try{ currentStyle = document.defaultView.getComputedStyle(elem, null); } catch(err){alert(err);}}
	else { currentStyle = elem.style; }
	return elem.offsetWidth;
}

//findPosX and findPosY by Peter-Paul Koch & Alex Tingle @ http://blog.firetree.net/2005/07/04/javascript-find-position/
function findPosX(obj){
    var curleft = 0;
    if(obj.offsetParent){
        while(1) 
        {
          curleft += obj.offsetLeft;
          if(!obj.offsetParent){ break; }
		  obj = obj.offsetParent;
        }
	}
    else if(obj.x){ curleft += obj.x; }
    return curleft;
}

function findPosY(obj){
    var curtop = 0;
    if(obj.offsetParent){
        while(1)
        {
          curtop += obj.offsetTop;
          if(!obj.offsetParent){ break; }
          obj = obj.offsetParent;
        }
	}
    else if(obj.y){ curtop += obj.y; }
    return curtop;
}
//EOF