/*-----------------------------------------------------------------------------
 * Menu code used to register and supply the class name changes for mouse and
 * keyboard handlers.  This code is meant to work in conjunction with standard
 * html elements along with CSS definitions.  The CSS definitions are tricky
 * to get correct so edit and test each property change in all browsers before 
 * changing other properties.
 *
 * Prior to 12/23/06, had some code and notes about hiding form input elements
 * and embedded objects so that the menu could overlap those items.  Had an open
 * timer going to but this had issues of opening after closing.  Plus this way
 * is simplier, faster, and works better - although some menus will open and close
 * quickly as they are moused over.
 *
 * Also, tried pure CSS solution but had inconsistant behavior with the <A> tag.
 *
 * TIMER NOTE: Timers are put onto a stack when "setTimeout" method is called.
 * To get around this the "clearTimeout" method is provided.  There was code in
 * to handle clearing etc. but it's probably easier on the script engine to just
 * execute the close even if the previous close already handled.  
 *
 * POSSIBLE ISSUE: It is possible that some users will mouse over and stay on at
 * the precise time the off event is triggered thus hiding a menu which should be
 * shown - e.g. current menu.  In testing it hasn't been an issue so far...
 * 
 * Modifications:
 * Date        By          Description
 * ----------  ----------  -------------------------------------------------
 * 11/10/2006  Garth       Initial split from general.js file.
 * 12/23/2006  Garth       Converted from straight JS function calls to object.
 * 02/22/2007  Garth       Changed name from ScriptEnabledNavigationMenu to the
 *                         more general MenuControl name.  Moved the ScriptEnabled
 *                         class registration out since this really needs to be
 *                         at a higher level because other items were needing to
 *                         to rely on the ScriptEnabled class being in the <body>.
 * 03/20/2007  Garth       Reworked the code to not fire onclick and to clear all
 *                         menu items when item clicked on to fix FF issue.
 * 04/09/2007  Garth       Refined registerMenuItems to handle both top and left
 *                         name menu id's to remove hard coding of top menu tabs.
 *---------------------------------------------------------------------------*/

// Contructor that contains the classname and element type properites.  This 
// object also contains the actual on/off event handlers.  Use the register 
// methods to register/attach event handlers to the appropriate elements.
// Arguments: 
//   menuContainerElementType = Container element type that the code
//     uses to check for child sub menus.  Defaults to "span".
//   menuContainerClassName = Container element CSS class name that the 
//     uses to verify child sub menus.  Defaults to "NavigationMenu".
//   menuItemElementType = HTML Element type that the code checks for 
//      to identify menu items that may contain child sub menus.  Defaults to "span".
//   menuItemShowClassName = CSS class name that the is appended to the 
//      menu item's classname when moused/keyed on which causes the sub menu to 
//      be displayed. Defaults to "ShowSubMenu".
// Returns: New menu control object.
function ShowHideMenuControl
(
	menuContainerElementType, 
	menuContainerClassName,
	menuItemElementType, 
	menuItemShowClassName
)
{
	// private var so handlers can access above properites and methods.
	var menuControl = this;	

	// basic properties
	this.menuContainerElementType = (menuContainerElementType ? menuContainerElementType : "span");
	this.menuContainerClassName   = (menuContainerClassName   ? menuContainerClassName   : "MenuContainer");
	this.menuItemElementType      = (menuItemElementType      ? menuItemElementType      : "span");
	this.menuItemShowClassName    = (menuItemShowClassName    ? menuItemShowClassName    : "ShowSubMenu");


	// hash that is used to store the menu items that have lost focus and need to be closed.
	var _menuItemsToHide = new Object();

	// Timer used to close menu items after x msecs.
	var _hideMenuItemsTimer = null;
	this.hideMenuItemsTimerDelay = 150; 	// timer delay in ms. 100 is kinda quick, 150+ leaves trails

	// flag used to not fire show/hide event code and timer to reisntate to handle
	// the user pressing the browser back button.
	var _menuItemClicked = false;
	this.resetMenuItemClickedTimerDelay = 500; 	// timer delay in ms



	// Moused over/focus on event handler.  
	this.menuItemOnHandler = function()
	{
		// If just clicked, then don't readd the show classname...
		if(_menuItemClicked) return;

//textAreaDebugOutput.writeln("on: " +this.serialNumber);
		// If this item is in the hash to be closed then clear it from the hash.
		// This check is needed because the main parent container element losses
		// focus for a brief second when the child <A> element is activated.
		// Otherwise this is a new show sub request so change the class name of 
		// the item so that the browser will display the menu.
		// NOTE: since this is an event handler the "menuControl" is used to gain
		// access to the controller properties.  The "this." is actually the element
		// clicked on so the "this.serialNumber" is a property of the EnhancedElement
		// which "this" actaully is due to how the object is handled via registration.
		if(_menuItemsToHide[this.serialNumber])
			delete(_menuItemsToHide[this.serialNumber]);
		else
			this.addClassName(menuControl.menuItemShowClassName);
//textAreaDebugOutput.writeln(this.className);
	} //~~~~~~~~~~~~~~~~~~~~~~~ End of Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


	// Moused off/lost focus event handler.  
	this.menuItemOffHandler = function()
	{
		// if just clicked then exit - don't add into the hash nor start the timer.
		if(_menuItemClicked) return;

		// Set the item to be closed in the items to hide hash then set the timer.
		// This is needed so that we don't close then repoen the item due to a 
		// child element causing the parent container to lose focus for a brief
		// moment.  If regains focus then the on handler will clear.  Otherwise 
		// the function called after the timeout period will loop through all 
		// items and remove the classname thus causing them to be hidden.
		_menuItemsToHide[this.serialNumber] = this;
		_hideMenuItemsTimer = setTimeout(menuControl.hideMenuItems, menuControl.hideMenuItemsTimerDelay);
	} //~~~~~~~~~~~~~~~~~~~~~~~ End of Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


	// Actual method called to close all element contained within the hide/close
	// hash object.  This function is called via a timer which is set by the 
	// actual off handler.  All menu items that are in the hash are looped through
	// and hidden (show classname which was added on focus/over is removed).
	this.hideMenuItems = function()
	{
		// clear the timer
		if(_hideMenuItemsTimer != null) clearTimeout(_hideMenuItemsTimer);

		// loop for all hash objects to be closed.  Remove the show classname 
		// and clear the element from the hash for next time this code fires.
		for(serialNumber in _menuItemsToHide)
		{
			_menuItemsToHide[serialNumber].removeClassName(menuControl.menuItemShowClassName);
			delete(_menuItemsToHide[this.serialNumber]);
		}
	} //~~~~~~~~~~~~~~~~~~~~~~~ End of Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


	// Menu Item Click handler.  This event handler closes all the open sub menus
	// and clears the menu close items timer so that the next page will not have
	// a timer event firing to close menu items that are not open.
	this.menuItemClickedHandler = function()
	{
		// Set the "menuItemClicked = true" flag so that the hide function won't 
		// try to hide the items and so that if the user moves the mouse a new
		// mouse over event is not fired.  Next add this current element into the
		// has of menu items to be closed and call the hide sub menus function.
		_menuItemClicked = true;
		_menuItemsToHide[this.serialNumber] = this;
		menuControl.hideMenuItems();

		// set the timer to clear the menuItemClicked variable.
		setTimeout(menuControl.resetMenuItemClicked, menuControl.resetMenuItemClickedTimerDelay);
	} //~~~~~~~~~~~~~~~~~~~~~~~ End of Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


	// function to simply reset the previous, just clicked on page's flag variable
	// so that if the user presses the back button that the menus will work again.
	this.resetMenuItemClicked = function()
	{
		_menuItemClicked = false;
	} //~~~~~~~~~~~~~~~~~~~~~~~ End of Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

} //~~~~~~~~~~~~~~~~~~~~~~~~~~ End of Constructor ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~




// Registers a group of menu items based on the parent container element ID passed in.
// Arguments: menuContainerElementID = HTML element ID of the element which contains
//   all the menu element definitions.
// Returns: nothing.
ShowHideMenuControl.prototype.registerMenuItems = function(menuContainerElementID)
{
	// get the container element.  If the element is NOT a menu container (based on 
	// its class) then get the first child element.  This is a kludge as the exact
	// structure is known.  In the future an EnhancedElement method might be 
	// implemented that recurses the object tree to find the first matching element.
	// Until this works.  After finding the container then kick off the registration
	// process for each menu item.
	var menuContainer = new EnhancedElement(menuContainerElementID);
	if(!menuContainer.containsClassName(this.menuContainerClassName))
	{
		menuContainer = menuContainer.getChildElement(this.menuContainerElementType, this.menuContainerClassName);
		menuContainer = new EnhancedElement(menuContainer);
	}

	// This kicks the recursive registering function off...
	var menuItems = menuContainer.getChildElements(this.menuItemElementType);
	for(var i=0; i<menuItems.length; i++)
		this.registerMenuItem(menuItems[i]);
} //~~~~~~~~~~~~~~~~~~~~~~~~~~~ End of Prototype ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~




// Registers event handlers for the item IF it has a sub menu.  This is recusrively
// called as it walks down the DOM structure registering all items that have child
// menu elements based on the element type and css class name properties.
// Arguments: menuItemObjectOrElementID = HTML element object to be registered.
// Returns: nothing.
ShowHideMenuControl.prototype.registerMenuItem = function(menuItemObjectOrElementID)
{
	// get any child elements.  If class contains a nav menu then register this 
	// element for the handler events so that this menu can be displayed on mouse 
	// over/focus.  Then get the sub menu nav item elements and loop through 
	// calling this func to recurse all items.
	var menuItem = new EnhancedElement(menuItemObjectOrElementID);
	var subMenu  = menuItem.getChildElement(this.menuContainerElementType);
	if(subMenu && (subMenu.className.indexOf(this.menuContainerClassName) > -1))
	{
		menuItem.onmouseover = this.menuItemOnHandler;
		menuItem.onmouseout  = this.menuItemOffHandler;
		menuItem.onfocus     = this.menuItemOnHandler;
		menuItem.onblur      = this.menuItemOffHandler;

		// ie doesn't bubble up onfocus and onblur events to parent container 
		// (menuitem span) for keyboard events (mouse over/out works at this 
		// level).  So added ie specific handlers that do fire...
		menuItem.onfocusin   = this.menuItemOnHandler;
		menuItem.onfocusout  = this.menuItemOffHandler;

		// kludge that sets a flag to not hide any sub menus via the timer...
		menuItem.onclick     = this.menuItemClickedHandler;

		var subMenuItems = new EnhancedElement(subMenu).getChildElements(this.menuItemElementType);
		for(var i=0; i<subMenuItems.length; i++)
			this.registerMenuItem(subMenuItems[i]);
	}
} //~~~~~~~~~~~~~~~~~~~~~~~~~~~ End of Prototype ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/*=============================== End of File ================================*/

