window.all_menus_locked = false;
function openMenu(o_src, p_group, p_div, o_init) {
	if (isSubmenuUnsupported()) return;
	if (window.all_menus_locked) return;
	// unfocus on the A tag
	if (o_src.blur) o_src.blur();
	//traceFocus();
	// find the target div
	var o_div = MM_findObj(p_div);
	if (o_div) {
		// set up group
		var c_group = "menu_group_" + p_group;
		if (typeof(window[c_group]) == "undefined") window[c_group] = new MenuGroup(c_group);
		// set up controller
		var c_controller = getMenuControllerName(o_src, p_group, p_div, o_init);
		if (typeof(window[c_controller]) == "undefined") window[c_controller] = new MenuController(c_controller, o_src, o_div, window[c_group], o_init);
		// show menu
		window[c_controller].show(o_src); // call controller directly - used to be: o_div.controller.show(o_src);
	}
}
function closeMenu(p_group, p_div) {
	var o_div = MM_findObj(p_div);
	if (o_div) {
		o_div.controller.timerid=1;
		o_div.controller.hideNow();
	}
}
function getMenuControllerName(o_src, p_group, p_div, o_init) {
	var controllerName = "menu_controller_" + p_div;
	if (typeof(o_init.controllerName) == "string") controllerName = o_init.controllerName;
	return controllerName;
}

function MenuGroup(p_name) {
	this.name = p_name;
	this.menus = new Object(); // []
	this.currentmenu = "";
	// register a controller with this menu group
	this.register = function(o_controller) {
		if (this.menus[o_controller.name]) return; // use index
		//this.menus.push(o_controller); // add controller to menus array
		this.menus[o_controller.name] = o_controller; // create indexed entry too
	}
	// check if allowed to show a menu
	this.allowShow = function() {
		return (!window.all_menus_locked);
	}
	// a controller has shown a menu (hide any other menus in this group)
	this.onShow = function(o_controller) {
		if (window.all_menus_locked) return;
		if (this.currentmenu != "") {
			this.menus[this.currentmenu].hideNow(); // use index
		}
		this.currentmenu = o_controller.name;
	}
	// close the open menu, if any is open
	this.hideNow = function() {
		this.unlock();
		if (this.currentmenu != "") {
			this.menus[this.currentmenu].hideNow(); // use index
		}
		this.currentmenu = "";
	}
	// a controller has hidden itself
	this.onHideNow = function(o_controller) {
		if (window.all_menus_locked) return;
		if (o_controller.name == this.currentmenu) this.currentmenu = "";
	}
	// return the current controller (the menu in this group that is active)
	this.getCurrentController = function() {
		return this.getController(this.currentmenu);
	}
	// return a specific controller from this menu group
	this.getController = function(p_name) {
		if (this.menus[p_name]) {
			return this.menus[p_name]; // use index
		} else {
			return false;
		}
	}
	// lock the menus; usually comes from a MenuController; this stops menus from affecting each other
	this.lock = function() {
		window.all_menus_locked = true;
	}
	// unlock the menus; usually comes from a MenuController; this allows menus to affecting each other
	this.unlock = function() {
		window.all_menus_locked = false;
	}
	// simple string representation
	this.toString = function() {
		return "[object MenuGroup:" + this.name + "]";
	}
}
function MenuController(p_name, o_src, o_div, o_group, o_init) {
	// default init variables
	this.parentGroup = null; // if a child DHTML menu, parentGroup should contain the ID of the parent group - this will create relationships to current open menu, and keep parent menu(s) open while it is open - also overrides "align" and "valign" init features
	this.method = "nav"; // one of [ nav , visible ] - nav is for standard menus, visible is for single div show / hide functionality
	this.slide = false; // whether or not to slide the menu into place (vertically downward sliding)
	this.slideDuration = 0.5; // slide duration in seconds - never really accurate for some reason - probably window.setInterval issues
	this.slideFPS = 15; // approximate slide FPS - used to calculate time between movements
	this.align = null; // align to source - right side or left side of source (auto defaults if child menu or not) one of [ left , right , leftside , rightside ]
	this.valign = null; // align to source - bottom or top of source (auto defaults if child menu or not) one of [ top , bottom , bottomtop, bottombottom ]
	this.width = null; // width of submenu - null = CSS or HTML defined, 0 = resize to source width, [n] = actual px width
	this.onShow = null; // custom function to call when menu appears
	this.onHide = null; // custom function to call when menu disappears
	this.relativeID = null; // unique ID of DHTML element to position the submenu relative to
	this.offsetX = 0; // place the submenu n pixels to the right of the source (negative is to the left)
	this.offsetY = 0; // place the submenu below the source (negative is above)
	this.zIndex = 1000; // z-index for the menu (all the menus can be the same z-index, just need to know so the iFrame can lay below the menu)
	this.overlayMode = "iframe"; // method used to overlay the submenu on <select> tags: one of [ iframe , hide ] (any other mode means "off")
	this.overlayInset = null; // method used to insert the overlay (used for iFrames) when a portion of the menu is supposed to show through to the page beneath (ie: and shouldn't show a white corner)
	this.mouseGroup = []; // array of other objects' IDs that should qualify as a mouseover/out trigger once this menu has opened (these items keep it open)
	this.autoclose = []; // array of other menus to close when this one is opened
	// timeouts
	this.hidePause = 200;
	this.unlockHidePause = 30;
	// accept init variables
	if (typeof(o_init) == "object") for (var a in o_init) if (typeof(this[a]) != "undefined") this[a] = o_init[a];
	// set custom defaults
	if (this.valign == null) this.valign = (this.parentGroup == null) ? "bottom" : "top";
	if (this.align == null) this.align = (this.parentGroup == null) ? "right" : "auto";
	// build variables
	this.name = p_name;
	this.timerid = 0;
	this.submenu = 0;
	this.status = 0;
	this.inslide = 0;
	this.parent = null;
	// assign to group
	this.group = o_group;
	this.group.register(this);
	// build relationships
	this.menu = o_div;
	this.menu.controller = this;
	// assign source of menu popup
	this.assignSource = function(o_src) {
		this.source = o_src;
		this.source.controller = this;
		this.source.controllerOnShow = this.onShow; // assign onshow and onhide function to the source element
		this.source.controllerOnHide = this.onHide; // so that "this" references are intact
		this.placementSource = o_src;
	}
	this.assignSource(o_src);
	// build placement relationship
	if (typeof(this.relativeID) == "string") {
		var o_tmp = MM_findObj(this.relativeID);
		if (o_tmp) {
			this.placementSource = o_tmp;
			this.placementSource.controller = this;
		}
	}
	// functionality
	this.show = MenuController_show;
	this.hide = MenuController_hide;
	this.hideNow = MenuController_hideNow;
	this.forceHideNow = MenuController_forceHideNow;
	this.catchHide = MenuController_catchHide;
	this.slideSetup = MenuController_slideSetup;
	this.slideStep = MenuController_slideStep;
	this.stopSlider = MenuController_stopSlider;
	this.showIFrame = MenuController_showIFrame;
	this.hideIFrame = MenuController_hideIFrame;
	this.setWidth = MenuController_setWidth;
	this.showOverlay = MenuController_showOverlay;
	this.hideOverlay = MenuController_hideOverlay;
	this.assignParent = MenuController_assignParent;
	this.includeObjectInMenuGroup = MenuController_includeObjectInMenuGroup;
	this.unincludeObjectInMenuGroup = MenuController_unincludeObjectInMenuGroup;
	this.catchFormSelects = MenuController_catchFormSelects;
	this.uncatchFormSelects = MenuController_uncatchFormSelects;
	this.lockMenu = MenuController_lockMenu;
	this.unlockMenu = MenuController_unlockMenu;
	this.catchUnlockHide = MenuController_catchUnlockHide;
	this.toString = function() { return "[object MenuController:" + this.name + "]"; };
	// complete initialization
	this.slideSetup();
	this.setWidth();
}

function MenuController_includeObjectInMenuGroup(o) {
	o.controller = this;
	o.onclick = function() { this.controller.catchUnlockHide(); };
	o.onmouseover = function() { this.controller.catchHide(); };
	o.onmouseout = function() { this.controller.hide(); };
}
function MenuController_unincludeObjectInMenuGroup(o) {
	// leave o.controller for subsequent mouseovers
	o.onfocus = function() {};
	o.onmouseover = function() {};
	o.onmouseout = function() {};
}

function MenuController_catchFormSelects() {
	// find all the form selects inside the menu element
	var ar_selects = this.menu.getElementsByTagName("SELECT");
	for (var i=0; i<ar_selects.length; i++) {
		// when select is in focus, lock the menu, when blur unlock the menu
		ar_selects[i].controller = this;
		ar_selects[i].onfocus = function() { this.controller.lockMenu(); };
		ar_selects[i].onblur = function() { this.controller.unlockMenu(); this.controller.hide(); };
	}
}
function MenuController_uncatchFormSelects() {
	// find all the form selects inside the menu element
	var ar_selects = this.menu.getElementsByTagName("SELECT");
	for (var i=0; i<ar_selects.length; i++) {
		// when select is in focus, lock the menu, when blur unlock the menu
		ar_selects[i].onfocus = function() { };
		ar_selects[i].onblur = function() { };
	}
}

function MenuController_lockMenu() {
	this.group.lock();
	this.locked = true;
}
function MenuController_unlockMenu() {
	this.locked = false;
	this.group.unlock();
}
function MenuController_catchUnlockHide() {
	this.unlocktimerid = window.setTimeout(this.name + ".catchHide();", this.unlockHidePause);
}

function MenuController_show(o_src) {
	// verify parent is same
	if (o_src && o_src != this.source) this.assignSource(o_src);
	// check if group allows showing a menu currently
	if (!this.group.allowShow()) return;
	// check for parent groups - if this is a subnav
	this.assignParent();
	// if currently visible, just stop any pending hide calls
	if (this.status) {
		this.catchHide();
	} else {
		// tell group
		this.group.onShow(this);
		// assign functions
		this.source.onmouseout = function() { this.controller.hide(); };
		this.includeObjectInMenuGroup(this.menu);
		// assign functions to additional mouse-group items
		for (var i=0; i<this.mouseGroup.length; i++) {
			var o=MM_findObj(this.mouseGroup[i]);
			if (o) this.includeObjectInMenuGroup(o);
		}
		// check for forms with select tags
		this.catchFormSelects();
		// close other menus as specified
		for (var i=0; i<this.autoclose.length; i++) {
			var o=MM_findObj(this.autoclose[i]);
			if (o && o.controller) o.controller.forceHideNow();
		}
		// standard navigation popup
		if (this.method == "nav") {
			// find x,y for menu
			var ar_loc = findPos(this.placementSource); // item to align to
			ar_loc[0] += this.offsetX;
			ar_loc[1] += this.offsetY;
			if (this.valign.toLowerCase() == "bottom") ar_loc[1] += this.placementSource.offsetHeight;
			if (this.valign.toLowerCase() == "bottomtop") ar_loc[1] -= this.menu.offsetHeight;
			if (this.valign.toLowerCase() == "bottombottom") ar_loc[1] -= (this.menu.offsetHeight - this.placementSource.offsetHeight);
			// position the menu
			if (this.parent) {
				// sub menus
				var my_align = this.align.toLowerCase();
				if (my_align == "auto") { // determine if it will fit to the right, else place at left
					if (ar_loc[0] + this.placementSource.offsetWidth + this.menu.offsetWidth > document.body.clientWidth) {
						my_align = "left";
					} else {
						my_align = "right";
					}
				}
				//var my_left = (my_align == "right") ? ar_loc[0] + this.placementSource.offsetWidth : ar_loc[0] - this.menu.offsetWidth;
				if (my_align == "right") my_left = ar_loc[0] + this.placementSource.offsetWidth;
				if (my_align == "left") my_left = ar_loc[0] - this.menu.offsetWidth;
				if (my_align == "leftside") my_left = ar_loc[0] - this.menu.offsetWidth;
				if (my_align == "rightside") my_left = ar_loc[0] + this.placementSource.offsetWidth;
			} else {
				// primary menus
				var my_align = this.align.toLowerCase();
				var my_left = 0;
				if (my_align == "right") my_left = ar_loc[0] - this.menu.offsetWidth + this.placementSource.offsetWidth;
				if (my_align == "left") my_left = ar_loc[0];
				if (my_align == "leftside") my_left = ar_loc[0] - this.menu.offsetWidth;
				if (my_align == "rightside") my_left = ar_loc[0] + this.placementSource.offsetWidth;
				//var my_left = (this.align.toLowerCase() == "right") ? ar_loc[0] - this.menu.offsetWidth + this.placementSource.offsetWidth : ar_loc[0];
			}
			this.menu.style.left = my_left + "px";
			if (this.slide) {
				// start sliding
				this.clipTop = ar_loc[1];
				this.slideStart = ar_loc[1] - this.menu.offsetHeight;
				this.slideEnd = (ar_loc[1]);
				this.sliderid = window.setInterval(this.name + ".slideStep();", this.slidems);
			} else {
				this.menu.style.top = (ar_loc[1]) + "px";
				this.menu.style.visibility = "visible";
			}
			this.menu.style.zIndex = this.zIndex;
		} else if (this.method == "visible") {
			// simply make the div visible
			this.menu.style.visibility = "visible";
		}
		if (this.method == "nav" && !this.slide) this.showOverlay();
		this.status=true;
		// call user-defined function for onShow
		if (typeof(this.source.controllerOnShow) == "function") this.source.controllerOnShow();
	}
}
function MenuController_hide() {
	if (!this.locked && !this.timerid) {
		this.timerid = window.setTimeout(this.name + ".hideNow();", this.hidePause);
		if (this.parent) this.parent.hide();
	}
}
function MenuController_hideNow() {
	if (this.timerid) {
		// unset functions
		this.source.onmouseout = function() {};
		this.unincludeObjectInMenuGroup(this.menu);
		// assign functions to additional mouse-group items
		for (var i=0; i<this.mouseGroup.length; i++) {
			var o=MM_findObj(this.mouseGroup[i]);
			if (o) this.unincludeObjectInMenuGroup(o);
		}
		// check for forms with select tags
		this.uncatchFormSelects();
		// hide
		this.menu.style.visibility = "hidden";
		this.group.onHideNow(this);
		this.timerid = 0;
		// standard navigation popup
		if (this.method == "nav") {
			this.menu.style.left = "0px"; // keeps menus inside page, if page is resized when menus are hidden
			// stop slider
			this.stopSlider();
			this.hideOverlay();
		}
		this.status=false;
		// hide submenu
		if (this.submenu) this.submenu.hideNow();
		// call user-defined function for onHide
		if (typeof(this.source.controllerOnHide) == "function") this.source.controllerOnHide();
	}
}
function MenuController_forceHideNow() {
	this.timerid = 1;
	this.hideNow();
}
function MenuController_catchHide() {
	if (this.timerid) {
		window.clearTimeout(this.timerid);
		this.timerid = 0;
	}
	if (this.parent) this.parent.catchHide();
}
function MenuController_slideSetup() {
	if (this.slide == true && !isNaN(this.slideDuration) && !isNaN(this.slideFPS)) {
		this.slidems = Math.floor(1000 / this.slideFPS);
		this.slideFrames = this.slideDuration * this.slideFPS;
		this.slidePosition = 0;
		this.inslide = 0;
	}
}
function MenuController_slideStep() {
	this.slidePosition++;
	if (this.slidePosition > this.slideFrames) this.slidePosition = this.slideFrames;
	if (!this.inslide) {
		this.inslide = 1;
		var slideOffset = (this.slideEnd - this.slideStart) * (this.slidePosition / this.slideFrames);
		var myClipTop = this.clipTop - slideOffset;
		// build clip and show menu
		if (this.menu.style.visibility != "visible") this.menu.style.visibility = "visible";
		this.menu.style.clip = "rect(" + Math.ceil(myClipTop - this.slideStart) + "px " + this.menu.offsetWidth + "px " + this.menu.offsetHeight + "px 0px)";
		this.menu.style.top = (this.slideStart + slideOffset) + "px";
		// stop sliding if at end
		if (this.slidePosition >= this.slideFrames) this.stopSlider();
		this.inslide = 0;
	}
}
function MenuController_stopSlider() {
	if (this.sliderid) {
		if (this.slide) this.showOverlay();
		window.clearInterval(this.sliderid);
		this.sliderid = 0;
		this.slidePosition = 0;
	}
}
function MenuController_showOverlay() {
	if (this.overlayMode.toLowerCase() == "iframe") {
		this.showIFrame();
	} else if (this.overlayMode.toLowerCase() == "hide") {
		hideF();
	}
}
function MenuController_hideOverlay() {
	if (this.overlayMode.toLowerCase() == "iframe") {
		this.hideIFrame();
	} else if (this.overlayMode.toLowerCase() == "hide") {
		showF();
	}
}
function MenuController_showIFrame() {
	if (isexplorer) {
		if (!this.frameBackground) {
			var o_frame = document.createElement("IFRAME")
			o_frame.style.position = "absolute";
			o_frame.style.border = "0px";
			o_frame.frameBorder = "0px";
			o_frame.style.zIndex = (this.zIndex - 1);
			document.body.appendChild(o_frame);
			this.frameBackground = o_frame;
		}
		if (this.overlayInset) {
			this.frameBackground.style.top = (this.menu.offsetTop + this.overlayInset[0]) + "px";
			this.frameBackground.style.left = (this.menu.offsetLeft + this.overlayInset[3]) + "px";
			this.frameBackground.style.height = (this.menu.offsetHeight - this.overlayInset[0] - this.overlayInset[2]) + "px";
			this.frameBackground.style.width = (this.menu.offsetWidth - this.overlayInset[1] - this.overlayInset[3]) + "px";
		} else {
			this.frameBackground.style.top = (this.menu.offsetTop) + "px";
			this.frameBackground.style.left = (this.menu.offsetLeft) + "px";
			this.frameBackground.style.height = (this.menu.offsetHeight) + "px";
			this.frameBackground.style.width = (this.menu.offsetWidth) + "px";
		}
	}
}
function MenuController_hideIFrame() {
	if (isexplorer) {
		if (this.frameBackground) {
			document.body.removeChild(this.frameBackground);
			this.frameBackground = null;
			delete(this.frameBackground);
		}
	}
}
function MenuController_setWidth() {
	if (!isNaN(this.width)) {
		if (this.width == 0) this.menu.style.width = this.source.offsetWidth;
		else this.menu.style.width = this.width;
	}
}
function MenuController_assignParent() {
	if (this.parentGroup) {
		var o_parentGroup = window["menu_group_" + this.parentGroup];
		if (o_parentGroup) {
			var o_parent = o_parentGroup.getCurrentController();
			if (o_parent) {
				this.parent = o_parent;
				this.parent.child = this;
				//alert("assigned " + this.name + " as child to " + this.parent.name);
			}
		}
	}
}

/*function trace(t) {
	var o = MM_findObj("txt_stdout"); if (o) o.value += t + "\n";
}
function traceFocus() {
	var o = MM_findObj("txt_stdout")
	if (o) o.focus();
}*/


// ================================================= //
// Show and hide all <select> objects in all forms   //
// ================================================= //
function hideF(){for(var i=document.forms.length-1;i>=0;i--){var form=document.forms[i];for(var j=form.elements.length-1;j>=0;j--){var elmt=form.elements[j];var e_s=elmt.style;if((elmt.type=="select-one"||elmt.type=="select-multiple")&&elmt.className!='donthide'&&e_s.vbak==null){e_s.vbak=e_s.visibility;e_s.visibility="hidden";}}}};

function showF(){for(var i=document.forms.length-1;i>=0;i--){var form=document.forms[i];for(var j=form.elements.length-1;j>=0;j--){var elmt=form.elements[j];var e_s=elmt.style;if((elmt.type=="select-one"||elmt.type=="select-multiple")&&e_s.vbak!=null){e_s.visibility=e_s.vbak;e_s.vbak=null;}}}};

// ================================================= //
// Find object location in pixels as array [x,y]     //
// ================================================= //
isopera = (navigator.userAgent.indexOf("Opera")>=0);
isexplorer = (!isopera && navigator.userAgent.indexOf("MSIE")>=0);
issafari = (navigator.userAgent.indexOf("Safari")>=0);
isgecko = (navigator.userAgent.indexOf("Gecko")>=0) && !issafari;
function findPos(obj) {
	var curleft = 0;
	var curtop = 0;
	var self = true;
	while (obj.offsetParent) {
		curleft += obj.offsetLeft;
		curtop += obj.offsetTop;
		if (isexplorer && !self) {
			// fix for IE - some CSS borders cause offset
			curleft += obj.clientLeft;
			curtop += obj.clientTop;
		}
		obj = obj.offsetParent;
		self = false;
	}
	if (issafari) {
		// safari doesn't consider
		curleft += document.body.offsetLeft;
		curtop += document.body.offsetTop;
	}
	return [curleft,curtop];
}
function isSubmenuUnsupported() {
	if (isexplorer) {
		var loc = navigator.userAgent.indexOf("MSIE") + 5;
		var ver = parseFloat(navigator.userAgent.substr(loc, 3));
		if (!isNaN(ver) && ver < 5) return true;
	}
	return false;
}
