/*****************************************************************************
*
*       Object: MMenu
*
* This object displays and maintains a menu than consists of a number of
* MMenuItem's, with each MMenuItem optionally associated with a sub-MMenu.
*
* A menu is created by:
*		1) create a new MMenu object
*		2) add MMenuItem's to it via the MMenu.add() method
*		3) generate the HTML menu code via the MMenu.create() method
*		4) display or hide the menu via the MMenu.show() and MMenu.hide() methods 
*
* When created, the menu will consist of <img> elements (1 per MMenuItem)
* inside a <div> element. For a sub-menu (i.e. an MMenu object that has a
* parent MMenu object), the <div> element will have absolute positioning
* relative to its associated parent menu's MMenuItem and will initially be
* hidden. A main menu (i.e. its parent menu attribute is NULL) will have not
* have a position style (i.e. it will be inline/static) and will initially be
* visible. A position style can be overriden, assigned or eliminiated via the
* optional argument to the create() method.

* The <div> element will also be assigned an id attribute whose value is passed
* as the first parameter. Additional styles can then be assigned to this block
* via this id or the class attribute passed as the last parameter.
*
* The MMenu object also has methods to select/unselect one of its menu items
* and methods to control a timeout. Whenever a timeout occurs, the MMenu object
* unselects its currently selected menu item. This is to facilitate hiding
* sub-menus when the mouse exits the menu area.  
*  
*****************************************************************************/


/*****************************************************************************
*
*       Constructor
*
*****************************************************************************/

function MMenu (

	_id,								// Unique ID for this object.
	parent,							// Parent menu. NULL if main or top-level menu.
   x, y,								// (x,y) position for menu.
	_class)							// class atribute for <div> (optional).
   
{
   // Public properties.
   //
   // myID							Unique ID for this MMenu.
   // parentMenu		         Parent menu or NULL if main (i.e. top-level) menu.
   // menuItems               Set of MMenuItem's.
   // positionStyle				Position style: blank, static, absolute,...
   // xPos							Relative (x,y) position for menu.
   // yPos							     
   // curItem                 Currently selected menu item or NULL if none.
   // timeoutID               ID for Timeout timer.

   // Remember this MMenu instance in MMenu object via its unique ID.

   MMenu[_id] = this;
   this.myID = _id;
  
   // Initialize properties.

   this.parentMenu = parent;
   this.xPos = x;
   this.yPos = y;
   this.curItem = null;
   this.timeoutID = 0;
   this.menuItems = new Array;
   this.positionStyle = (this.parentMenu ? "absolute" : "");
   this.classAttribute = "";
	if (arguments.length >= 5)
   	this.classAttribute = arguments[4];
}

  
/*****************************************************************************
*
*       Public method: addItem
*
* This function creates and adds an MMenuItem to this menu.
*
*****************************************************************************/

MMenu.prototype.addItem = function (

   _sub,								// Optional sub-menu.
   _link,							// Optional http link address.
   _select,							// Location of image to use when item selected.
   _not_select,					// Location of image to use when not selected.
   width,							// Image width (pixels).
   height,							// Image height (pixels).
   _alt)								// Alt attribute for <img> element.
  
{
	// First establish a unique ID for item based on this menu's ID.
   
   var _id = this.myID + "-" + (this.menuItems.length+1);
   
   // Create MMenuItem and add to array.

   this.menuItems[this.menuItems.length] = new MMenuItem(
   	_id, this, _sub, _link, _select, _not_select, width, height, _alt);
}


/*****************************************************************************
*
*       Public method: create
*
* This function creates and renders the <div> element that contains the menu.
* Main menus are initially visible. Sub-menus are initially hidden and have
* absolute positioning. The position style can be changed if a position
* argument is passed. If the menu is vertical, each <img> element is separated
* by a break element (to be done??).
*
*****************************************************************************/

MMenu.prototype.create = function (

   position)						// Optional: new position style.

{
	// Local work variables.
   
   var  menu,
        i;
   
   // Create the menu <div> element by getting and processing all the MMenuItem
   // objects.
   
   menu = '<div ';
   menu += 'id="' + this.myID + '" ';
   if (this.classAttribute.length > 0)
   	menu += 'class="' + this.classAttribute + '" ';
	if (arguments.length > 0)
   	this.positionStyle = position;
	menu += 'style="visibility:' + (this.parentMenu?'hidden;left:0;top:0;':'visible;');
   if (this.positionStyle.length > 0)
   	menu += ' position:' + this.positionStyle + ';';
	menu += '">';
   for (i = 0;  i < this.menuItems.length;  i++)
   {
   	if (i > 0)  menu += '<br />'; 
   	menu += this.menuItems[i].makeElement();
	}   
   menu += "</div>";
   document.write(menu);
}


/*****************************************************************************
*
*       Public method: hide
*
* This function unselects the current menu item and hides the menu.
*
*****************************************************************************/

MMenu.prototype.hide = function ()

{
   if (this.curItem != null)  this.curItem.unselect();
   this.curItem = null;
   var menu = document.getElementById(this.myID);
   menu.style.visibility = "hidden";
}


/*****************************************************************************
*
*       Public method: select
*
* This function "selects" a new menu item. If no new item, the current item
* just gets unselected.
*
*****************************************************************************/

MMenu.prototype.select = function (

	newItem) 						// Menu item to be selected or NULL.

{
   if (this.curItem != null)
   	this.curItem.unselect();
   if (newItem)
   	newItem.select();
   this.curItem = newItem;
}


/*****************************************************************************
*
*       Public method: show
*
* This function displays the menu relative to the passed co-ordinates.
*
*****************************************************************************/

MMenu.prototype.show = function (

	x,									// X position for menu.
   y)									// Y position for menu.

{
   var menu = document.getElementById(this.myID);
   menu.style.left = x + this.xPos;
   menu.style.top = y + this.yPos;
   menu.style.visibility = "visible";
}


/*****************************************************************************
*
*       Public method: startCountdown
*
* For a sub-menu, this function simply calls startCountdown() in its parent
* menu. When we "get" to the main menu, a timeout is started such that, if it
* expires, the mouse has exited the entire menu area and the current menu item
* must be unselected which should also hide all visible sub-menus. 
*
*****************************************************************************/

MMenu.prototype.startCountdown = function ()

{
   if (this.parentMenu)
   	this.parentMenu.startCountdown();
	else
   {
   	// Let's be safe.
   	if (this.timeoutID == 0)
      {
	   	var code = "MMenu['" + this.myID + "'].select(null);";
   		this.timeoutID = window.setTimeout(code, 300);
		}
	}            
}


/*****************************************************************************
*
*       Public method: stopCountdown
*
* For a sub-menu, this function simply calls stopCountdown() in its parent
* menu. When we "get" to the main menu the timeout, if it exists, is stopped
* because the mouse has re-entered the menu area.
*
*****************************************************************************/

MMenu.prototype.stopCountdown = function ()

{
   if (this.parentMenu)
   	this.parentMenu.stopCountdown();
	else
   {
   	if (this.timeoutID != 0)
   		window.clearTimeout(this.timeoutID);
   	this.timeoutID = 0;
	}            
}


/*****************************************************************************
/*****************************************************************************


/*****************************************************************************
*
*       Object: MMenuItem
*
* This object contains properties and methods to describe and process an item 
* in an MMenu.
*  
*****************************************************************************/


/*****************************************************************************
*
*       Constructor
*
* This creates an instance of MMenuItem. The associated parent MMenu must be
* valid. A valid link or sub-menu must be passed but not both.  
*
*****************************************************************************/

function MMenuItem (

	_id,								// Unique ID for this object and <img> element.
	parent,							// Asscoiated MMenu object; must be valid.
   _sub,								// Optional sub-menu.
   _link,							// Optional http link address.
   _select,							// Location of image to use when item selected.
   _not_select,					// Location of image to use when not selected.
   width,							// Image width (pixels).
   height,							// Image height (pixels).
   _alt)								// Alt attribute for <img> element.

{
   // Public properties.
   //
   // myID							Unique ID for this and associated <img> element.
   // parentMenu		         Parent menu.
   // subMenu						Sub-menu or NULL.
   // httpLink	               Web page to display upon item click or empty.
   // selectImg					Image used when selected.
   // unselectImg					Image used when not selected.
   // width							Image width.
   // height						Image height.
	// altAttr						Alt attribute for <img> element.

   // Remember this MMenuItem instance in MMenuItem object via its unique ID.
  
   MMenuItem[_id] = this;
   this.myID = _id;
  
   // Initializations.

   this.parentMenu = parent;
   this.subMenu = _sub;
   this.httpLink = _link;
   this.width = width;
   this.height = height;
   this.altAttr = _alt;

   // Pre-load the images.
   
   this.selectImg = new Image(width, height);
   this.selectImg.src = _select;
   this.unselectImg = new Image(width, height);
   this.unselectImg.src = _not_select;
}

  
/*****************************************************************************
*
*       Public method: handleMouseClick
*
* This function processes a mouse click event. If there is a link, it launches 
* that web page; otherwise it does nothing.
*
*****************************************************************************/

MMenuItem.prototype.handleMouseClick = function ()

{
   if (this.httpLink.length > 0)
      window.location.href = this.httpLink;
}


/*****************************************************************************
*
*       Public method: handleMouseOut
*
* This function processes a mouse out event. It shows the unselect image and
* tells the parent menu to start the unselect countdown.
*
*****************************************************************************/

MMenuItem.prototype.handleMouseOut = function ()

{
   document.images[this.myID].src = this.unselectImg.src;
   this.parentMenu.startCountdown();
}


/*****************************************************************************
*
*       Public method: handleMouseOver
*
* This function processes a mouse over event. It shows the select image,
* tells the parent menu that this item is selected and stops the unselect
* countdown.
*
*****************************************************************************/

MMenuItem.prototype.handleMouseOver = function ()

{
   document.images[this.myID].src = this.selectImg.src;
   this.parentMenu.stopCountdown();
   this.parentMenu.select(this);
}


/*****************************************************************************
*
*       Public method: makeElement
*
* This function returns the <img> element that is to be part of the HTML code
* comprising the menu using this menu item.
*
*****************************************************************************/

MMenuItem.prototype.makeElement = function ()

{
	var text = '<img border="0" ';
   text += 'id="' + this.myID + '" ';
   text += 'width="' + this.width + '" ';
   text += 'height="' + this.height + '" ';
   text += 'src="' + this.unselectImg.src + '" ';
	text += 'alt="' + this.altAttr + '" ';
   text += 'onmouseover="MMenuItem[\'' + this.myID + '\'].handleMouseOver();" ';
   text += 'onmouseout="MMenuItem[\'' + this.myID + '\'].handleMouseOut();" ';
   text += 'onclick="MMenuItem[\'' + this.myID + '\'].handleMouseClick();" ';
   text += ' />';
	return  text;
}


/*****************************************************************************
*
*       Public method: select
*
* This function "selects" this menu item by displaying its sub-menu iff it has
* one. The sub-menu's position is relative to this item's image location.
*
*****************************************************************************/

MMenuItem.prototype.select = function ()

{
   if (this.subMenu)
   {
      var thisImage = document.images[this.myID];
      var x = findPosX (thisImage);
      var y = findPosY (thisImage);
      this.subMenu.show(x, y);
   }
}


/*****************************************************************************
*
*       Public method: unselect
*
* This function "unselects" this menu item by hiding its sub-menu iff it has
* one. If there is no sub-menu, this function does nothing.
*
*****************************************************************************/

MMenuItem.prototype.unselect = function ()

{
   if (this.subMenu)
   	this.subMenu.hide();
}
