// The PopMenu object is used to create a pop-up menu. The menu is composed of a
// label and a menu which must be separately defined HTML elements that have unique
// id's.
// (c) 2005 Embellished Visions Studios
//
// The PopMenu is instantiated by calling this constructor with the ids for the
// label and the menu. The width determines the width of the menu. The anchors must be
// one of {n, s, e, w, ne, nw, se, sw, center} These parameters determine which way the
// menu will expand relative to the label by anchoring a compass point on the menu to a
// compass point on the label. If rollover is set to true, the menu will open automatically
// when the mouse passes over the label. Otherwise, the label is responsible for calling
// the open() method. Submenu indicates the menu is opened from another menu.

function PopMenu (labelId, menuId, width, labelAnchor, menuAnchor, rollover, parentId) {

// Private functions

// onmousover for label
function mouseOver (evt) {
    var popmenu = window.popmenuById[this.id];
    if (popmenu == null) {
    	return true;
    }

	if (popmenu.rollover) {
		popmenu.showMenu(true);
	}

	if (popmenu.labelMouseOverHandler) {
		return popmenu.labelMouseOverHandler(evt == null ? window.event : evt);
 	} else {
		return false;
	}
}

// onmousout for label
function mouseOut (evt) {
    var popmenu = window.popmenuById[this.id];
    if (popmenu == null) {
    	return true;
    }

	if (popmenu.labelMouseOutHandler) {
		return popmenu.labelMouseOutHandler(evt == null ? window.event : evt);
 	} else {
		return false;
	}
}

// document mouse move listener
function mouseMoveListener (evt) {
	return closeEvent(window.popmenu, evt, false, "mouseMoveHandler");
}

// document mouse down listener
function mouseDownListener (evt) {
	return closeEvent(window.popmenu, evt, false, "mouseDownHandler");
}

// window resize listener
function resizeListener (evt) {
	return closeEvent(window.popmenu, evt, true, "resizeHandler");
}

function closeEvent (popmenu, evt, forceClose, superHandler) {
	if (popmenu == null) {
		return false;
	}

	if (evt == null) {
		evt = window.event;
	}

	var superEventHandler = popmenu[superHandler];

	if (forceClose || !isInMenu(popmenu, evt)) {
		popmenu.hideMenu();
	}

	if (superEventHandler) {
		return superEventHandler(evt);
	} else {
		return true;
	}
}

// Check if the mouse is inside either the menu or the label
function isInMenu (popmenu, event) {
	var scrollX = (window.scrollX ? window.scrollX : document.body.scrollLeft);
	var scrollY = (window.scrollY ? window.scrollY : document.body.scrollTop);

	var x = (event.clientX ? event.clientX : event.pageX) + scrollX;
	var y = (event.clientY ? event.clientY : event.pageY) + scrollY;

	var menu = popmenu.menu;
	if (isInRect(x, y, menu.offsetLeft, menu.offsetTop, menu.offsetWidth + 1, menu.offsetHeight + 1)) {
		return true;
	} else {
		var rect = popmenu.labelRect;
		if (isInRect(x, y, rect.left, rect.top, rect.width + 1, rect.height + 1)) {
			return true;
		} else {
			return false;
		}
	}
}

// Check if a coordinate is inside a rectangle
function isInRect (x, y, left, top, width, height) {
	if (x < left || x > left + width) {
		return false;
	} else if (y < top || y > top + height) {
		return false;
	} else {
		return true;
	}
}

// Left coordinate of an element
function getLeft (element) {
	var x = element.offsetLeft;
	var parent = element.offsetParent;
	while (parent) {
		x += parent.offsetLeft;
		parent = parent.offsetParent;
	}

	return x;
}

// Top coordinate of an element
function getTop (element) {
	var y = element.offsetTop;
	var parent = element.offsetParent;
	while (parent) {
		y += parent.offsetTop;
		parent = parent.offsetParent;
	}

	return y;
}

// Blur all elements in a node
function blurElements (element, siblings) {
	while (element != null) {
		if (element.blur) {
			element.blur();
		}
		blurElements(element.firstChild, true);
		if (!siblings) {
			break;
		}
		element = element.nextSibling;
	}
}

function getAnchorOffset (anchor, width, height) {
    var left = 0;
    var top = 0;

	if (anchor == "n") {
		left += width / 2;
	} else if (anchor == "s") {
		left += width / 2;
		top += height;
	} else if (anchor == "e") {
		left += width;
		top += height / 2;
	} else if (anchor == "w") {
		top += height / 2;
	} else if (anchor == "ne") {
		left += width;
	} else if (anchor == "nw") {
		// no adjustments
	} else if (anchor == "se") {
		left += width;
		top += height;
	} else if (anchor == "sw") {
		top += height;
	} else {
 		left += width / 2;
		top += height / 2;
	}
	
	var offsets = new Object();
	offsets.left = Math.floor(left);
	offsets.top = Math.floor(top);
	
	return offsets;
}

// Show the menu
function showMenu (rollover) {
	if (this.visible) {
		return;
	}

	// Only one main menu at a time.
	if (window.popmenu != null) {
	    if (this.parentId == null) {
			window.popmenu.hideMenu();
     	} else {
     	    while (window.popmenu != null && window.popmenu.menu.id != this.parentId) {
     			window.popmenu.hideMenu();
          	}
          	this.parent = window.popmenu;
      	}
	}

	this.visible = true;

	var menu = this.menu;
	var label = this.label;
	var anchor = this.anchor;

	if (this.width) {
		menu.style.width = this.width;
 	}

	var lLeft = getLeft(label);
	var lTop = getTop(label);
	var mWidth = menu.offsetWidth;
	var mHeight = menu.offsetHeight;
	var lWidth = label.offsetWidth;
	var lHeight = label.offsetHeight;

	var labelOffsets = getAnchorOffset(this.labelAnchor, lWidth, lHeight);
	var menuOffsets = getAnchorOffset(this.menuAnchor, mWidth, mHeight);

	var mLeft = lLeft + labelOffsets.left - menuOffsets.left;
	var mTop = lTop + labelOffsets.top - menuOffsets.top;

	if (mLeft < 0) {
		mLeft = 0;
	} else if (mTop < 0) {
		mTop = 0;
	}

	// This rectangle will be extruded from the label to overlap with the menu
	var rect = new Object();
	rect.left = Math.min(lLeft, mLeft);
	rect.top = Math.min(lTop, mTop);
	rect.width = Math.max(lLeft + label.offsetWidth, mLeft + menu.offsetWidth) - rect.left;
	rect.height = Math.max(lTop + label.offsetHeight, mTop + menu.offsetHeight) - rect.top;

	this.labelRect = rect;

	menu.style.left = mLeft + "px";
	menu.style.top = mTop + "px";
	menu.style.visibility = "visible";

	if (window.popmenu == null) {
     	// Mouse listeners to hide the menu when appropriate
     	if (document.onmousedown != mouseDownListener) {
        	this.mouseDownHandler = document.onmousedown;
        	document.onmousedown = mouseDownListener;
      	}

		if (document.onmousemove != mouseMoveListener) {
        	this.mouseMoveHandler = document.onmousemove;
        	if (rollover) {
        		document.onmousemove = mouseMoveListener;
        	}
     	}

    	// Resize listener to hide the menu
    	if (window.onresize != resizeListener) {
    		this.resizeHandler = window.onresize;
    		window.onresize = resizeListener;
     	}
 	}

 	window.popmenu = this;

	if (this.onMenuOpen) {
	    this.onMenuOpen();
	}
}

// Hide the menu
function hideMenu () {
    if (this.submenu != null) {
        this.submenu.hideMenu();
        this.submenu = null;
    }

	blurElements (this.menu, false);
	this.menu.style.visibility = "hidden";
	this.visible = false;

	window.popmenu = this.parent;

	if (this.parent == null) {
  		document.onmousemove = this.mouseMoveHandler;
  		document.onmousedown = this.mouseDownHandler;
  		this.mouseMoveHandler = null;
  		this.mouseDownHandler = null;

  		window.onresize = this.resizeHandler;
  		this.resizeHandler = null;
	} else {
		this.parent.submenu = null;
		this.parent = null;
	}

	if (this.onMenuClose != null) {
	    this.onMenuClose();
	}
}

function openMenu (rollover, altUrl) {
	if (!this.menu) {
	    if (altUrl != null) {
	    	window.location = altUrl;
     	}
		return false;
	}
	
	this.showMenu(rollover);
	return true;
}

function closeMenu () {
	if (!this.menu) {
		return false;
	}

	this.hideMenu();
	return true;
}

// End private function and begin constuctor logic

this.open = openMenu;
this.close = closeMenu;

if (!document.getElementById) {
	return;
}

this.visible = false;

var menu = document.getElementById(menuId);
if (menu == null || menu.offsetWidth == null) {
	return;
 	}

var label = document.getElementById(labelId);
if (label == null) {
	return;
}

this.menu = menu;
this.label = label;

if (window.popmenuById == null) {
    window.popmenuById = new Object();
}
window.popmenuById[labelId] = this;

menu.style.position = "absolute";
menu.style.visibility = "hidden";
this.width = width;
if (width) {
	if (!isNaN(this.width)) {
		this.width += "px";
	}
	menu.style.width = this.width;
}

this.labelAnchor = (labelAnchor == null ? "center" : labelAnchor.toLowerCase());
this.menuAnchor = (menuAnchor == null ? "center" : menuAnchor.toLowerCase());

this.rollover = (rollover ? true : false);
this.parentId = parentId;
this.parent = null;
this.submenu = null;

this.showMenu = showMenu;
this.hideMenu = hideMenu;
this.onMenuOpen = null;
this.onMenuClose = null;
this.mouseMoveHandler = null;
this.mouseDownHandler = null;
this.resizeHandler = null;

// Add mouse handlers to the label
this.labelMouseOverHandler = label.onmouseover;
this.labelMouseOutHandler = label.onmouseout;
label.onmouseover = mouseOver;
label.onmouseout = mouseOut;
}
