/////////////////////////////////////////////////////////////////////////////
//
// Gladiator Components
//
// http://www.gladiatorweb.com
//
// (c) 2006 by Edward H. Trager .  All Rights Reserved
//
// NAME: gladiatorMenu.js
//
// DESCRIPTION: Implements menu interface elements
//
// AUTHOR: Edward H. Trager
//
// LAST UPDATE: 2006.10.18
//
// NOTE: SEE "LICENSE" FILE FOR LICENSING TERMS
//
//////////////////////////////////////////////////////////////////////////////

//
// DEPENDENCY CHECKING:
//
if(typeof GLADIATOR_CORE_INCLUDED == 'undefined') alert('NOTE BENE: "gladiatorMenu.js" REQUIRES "gladiatorCore.js"!');
if(typeof GLADIATOR_WINDOWS_INCLUDED == 'undefined') alert('NOTE BENE: "gladiatorMenu.js" REQUIRES "gladiatorWindows.js"!');

var GLADIATOR_MENU_INCLUDED=true;


//
// gMenuItem class
//
function gMenuItem(menu,index,label,id,actionOrSubMenu,hasSubMenu,isDisabled,showIcon,iconClass){
	
	var _self = this;
	
	var _menu  = menu;
	var _index = index;
	
	var _action     = (actionOrSubMenu ? actionOrSubMenu : noAction   );
	var _hasSubMenu = (hasSubMenu      ? hasSubMenu      : false      );
	var _isDisabled = (isDisabled      ? isDisabled      : false      );
	var _showIcon   = (showIcon        ? showIcon        : false      );
	//
	// _dirClass adds "_rtl" to the end of the class names for
	// right-to-left environments:
	//
	var _dirClass   = "";
	var _isRTL      = false;
	if( typeof LC != "undefined"){
		if( LC.getDirection() == "rtl" ){
			_dirClass="_rtl";
			_isRTL=true;
		}
	}
	
	//
	// Menu Icon:
	//
	var _iconClass  = (iconClass ? iconClass : "gMenuIcon"+_dirClass);
	
	//
	// Arrow:
	//
	var _arrowClass = "gMenuArrow"+_dirClass;
	
	//
	// timeoutId and duration:
	//
	var _timeoutId;
	var _timeoutDuration=500;
	
	//////////////////////////
	//
	// Create the DOM divs:
	//
	//////////////////////////
	var _menuItem = document.createElementNS(NS.xhtml,"div"); 
	
	if(_isDisabled) _menuItem.setAttribute("class","gMenuItemDisabled"+_dirClass);
	else            _menuItem.setAttribute("class","gMenuItem"        +_dirClass);
	
	//
	// Append icon before text because it is floated:
	//
	var _icon=false;
	if(_showIcon){
		_icon = document.createElementNS(NS.xhtml,"div");
		_icon.setAttribute( "class" , _iconClass );
		_menuItem.appendChild(_icon);
	}
	
	//
	// Append arrow before text label because it is floated:
	//
	var _arrow=false;
	if(_hasSubMenu){
		_arrow = document.createElementNS(NS.xhtml,"div");
		_arrow.setAttribute( "class" , _arrowClass );
		_menuItem.appendChild(_arrow);
	}
	
	//
	// Append menu item text:
	//
	var _menuItemText = document.createTextNode(label);
	_menuItem.appendChild(_menuItemText);
	
	//
	// The menuItem appends itself to the menu:
	//
	_menu.getNode().appendChild(_menuItem);
	
	//////////////////////////
	//
	// PRIVATE METHODS
	//
	//////////////////////////
	function noAction(){
		
		alert("No action has been assigned to this menu item");
		
	}
	
	//
	// mouseOverAction
	//
	function mouseOverAction(){
		//
		// Cancel any pending timeout on the parent menu:
		//
		_menu.clearTimeout();
		
		//
		// If item links to a submenu, popup the submenu after
		// the specified timeout (unless cancelled by endPendingTimeout() ):
		//
		if(_hasSubMenu) _timeoutId = window.setTimeout(_self.executeAction,_timeoutDuration);
		
	}
	
	//
	// mouseOutAction
	//
	function mouseOutAction(){
		//
		// If a timeout action is pending, cancel it:
		//
		if( _timeoutId ) window.clearTimeout(_timeoutId);
		
		//
		// If item has a submenu, and the submenu is open, close it
		// after timeout duration:
		//
		if(_hasSubMenu && _action.isVisible()) {
			_action.hideAfterTimeout();
		}
		
	}
	
	//////////////////////////
	//
	// PUBLIC METHODS
	//
	//////////////////////////
	
	//
	// setDisabled
	//
	this.setDisabled = function(isDisabled){
		// No state change:
		if(isDisabled == _isDisabled) return;
		// State changed:
		_isDisabled = isDisabled;
		if(_isDisabled){
			_menuItem.setAttribute("class","gMenuItemDisabled" + _dirClass );
		}else{
			_menuItem.setAttribute("class","gMenuItem"         + _dirClass );
		}
		
	}
	
	//
	// enable
	//
	this.enable = function(){
		_self.setDisabled(false);
	}
	
	//
	// disable
	//
	this.disable = function(){
		_self.setDisabled(true);
	}
	
	//
	// getNode(): Get the DOM node
	//
	this.getNode = function(){
		return _menuItem;
	}
	
	this.getIndex = function(){
		return _index;
	}
	
	//
	// executeAction: If the menu item has a submenu, then 
	// set the submenu's position and call
	// the submenu's show() method.  Otherwise, just call the 
	// action itself.
	//
	// If calling the action itself, and this menu is a submenu,
	// then hide the menu and all previous menus.
	//
	this.executeAction = function(){
		//
		// Don't do anything if disabled:
		//
		if(_isDisabled) return;
		
		if( _hasSubMenu ){
			//
			// get submenu position:
			//
			var RTLOffset=0;
			if(_isRTL) RTLOffset = _action.getWidth();
			var location = _menu.getSubMenuLocation(_index,RTLOffset);
			//
			// Check if out-of-bounds:
			//
			var LEFT=0;
			var TOP =1;
			if(location[LEFT]<0                                      ) location[LEFT]=0;
			if(location[LEFT]>DIMENSIONS.width - _action.getWidth()  ) location[LEFT]=DIMENSIONS.width  - _action.getWidth();
			if(location[TOP ]<0                                      ) location[TOP ]=0;
			if(location[TOP ]>DIMENSIONS.height - _action.getHeight()) location[TOP ]=DIMENSIONS.height - _action.getHeight();
			//
			// Set location:
			//
			_action.setLocation( location );
			_action.show();
			
		}else{
			if(_menu.isSubMenu()) _menu.cascadeUpForceHide();
			_action();
			
		}
		
	}
	
	//
	// hideSubMenu
	//
	this.hideSubMenu = function(){
		
		if( ! _hasSubMenu ) return;
		
		_action.cascadeDownHide();
		
	}
	
	//
	// addAction
	//
	this.addAction = function(action){
		_action = (action? action : noAction);
	}
	
	///////////////////////
	//
	// Add Event Listeners:
	//
	///////////////////////
	_menuItem.addEventListener("click",this.executeAction,false);
	_menuItem.addEventListener('mouseover',mouseOverAction,false);
	_menuItem.addEventListener('mouseout' ,mouseOutAction ,false);
	
}

//
// gMenu
//
function gMenu(title,id,left,top,width,showTitleBar,isSubMenu){
	
	var _itemCount=0;
	var _lastSelected="";
	
	var _width = width;
	var _self = this;
	
	var _timeoutDuration = 500;
	var _timeoutId       = false;
	
	var _isActive = false; // Only true when mouse is over the menu
	
	//
	// The height of 200 is arbitrary and is changed depending on
	// the number of menu items:
	//
	var _menuWindow = new gWindow(title,id,left,top,_width,200,"",true);
	var _itemHeight=_determineMenuItemHeight();
	var _parentMenu=false; // use setParentMenu() to set.
	
	//
	// Do these individually rather than call allowAll()
	// because there are cases where we want to turn allowClose()
	// back on ...
	//
	_menuWindow.allowIconify(false);
	_menuWindow.allowMinMax(false);
	_menuWindow.allowClose(false);
	
	var _showTitleBar;
	if(isNull(showTitleBar)){
		_showTitleBar=false;
	}else{
		_showTitleBar = showTitleBar;
	}
	
	var _isSubMenu;
	if(isNull(isSubMenu)){
		_isSubMenu=false;
	}else{
		_isSubMenu = isSubMenu;
	}
	if(_isSubMenu) _menuWindow.show(false);
	
	//
	// By default windows show their title bars, so only set if false:
	// Also, menus are not resizable:
	//
	if(!_showTitleBar) _menuWindow.showTitleBar(false);
	_menuWindow.allowResize(false);
	
	//
	// the menu items are added directly 
	// to the window canvas:
	//
	var _listContainer = _menuWindow.getNode();
	//
	// The list of menu items:
	//
	var _items = new Object();
	
	
	///////////////////////////////
	//
	// PRIVATE METHODS
	//
	///////////////////////////////
	
	//
	// determineMenuItemHeight()
	//
	// => This method culls the .gMenuItem height
	//    directly out of the gladiatorMenu style sheet:
	//
	function _determineMenuItemHeight(){
		
		var k=-1;
		var safeDefaultHeight=24;
		//
		// Find the style sheet:
		//
		var styleSheet = getStyleSheet( "gladiatorMenu" );
		if(!styleSheet) return safeDefaultHeight;
		
		//
		// Get here if we have the stylesheet: Now find the "height" of ".gMenuItem":
		//
		// => According to the W3C CSS standard, this is the content height:
		//
		var heightValue = getCSSClassRuleValue(styleSheet,".gMenuItem","height");
		if(!heightValue) return safeDefaultHeight;
		
		//
		// Get the padding: 
		//
		var paddingValue = getCSSClassRuleValue(styleSheet,".gMenuItem","padding");
		//
		// If we didn't get "padding", try "padding-top" instead:
		//
		if(!paddingValue){
			paddingValue = getCSSClassRuleValue(styleSheet,".gMenuItem","padding\-top");
		}
		if(!paddingValue) paddingValue=0;
		
		//
		// Total height is content height plus padding on both top and bottom:
		// (We assume that the menuItems don't have borders or margins):
		//
		return parseInt(heightValue)+2*parseInt(paddingValue);
		
	}
	
	///////////////////////
	//
	// PUBLIC METHODS:
	//
	///////////////////////
	
	/////////////////////////////////////////////////////////////////////////////////
	//
	// addItem:
	//
	// Parameters: label            : Text label for the menu item.
	//             isSubMenu        : Set to true if adding a submenu.
	//             menuActionMethod : If it is a submenu, pass the submenu instance.
	//                                If it is an action, pass the method.
	//
	/////////////////////////////////////////////////////////////////////////////////
	this.addItem = function(label,id,actionOrSubMenu,hasSubMenu,isDisabled,showIcon,iconClass){
		
		_itemCount++;
		//
		// Each gMenuItem adds itself to the DOM and knows its own index:
		//
		_items[id] = new gMenuItem(_self,_itemCount,label,id,actionOrSubMenu,hasSubMenu,isDisabled,showIcon,iconClass);
		
		//
		// Resize the window height to match number of labels:
		//
		_menuWindow.setHeight( _itemCount * _itemHeight );
		
	}
	
	//
	// show():
	//
	this.show = function(){
		_menuWindow.bringToTop();
		_menuWindow.show(true);
	}
	
	//
	// hide():
	//
	this.hide = function(){
		_menuWindow.show(false);
	}
	
	//
	// enableItem
	//
	this.enableItem = function(id){
		_items[id].enable();
	}
	
	//
	// disableItem
	//
	this.disableItem = function(id){
		_items[id].disable();
	}
	
	//
	// addItemAction
	//
	this.addItemAction = function(id,action){
		_items[id].addAction(action);
	}
	
	//
	// allowClose
	//
	this.allowClose = function(tf){
		_menuWindow.allowClose(tf);
	}
	
	//
	// cascadeDownHide:
	//
	this.cascadeDownHide = function(){
		
		for(property in _items){
			_items[property].hideSubMenu();
		}
	}
	
	//
	// cascadeUpHide: Hides self and cascades up, hiding
	//                (presumably visible) parent menus 
	//                which are themselves sub-menus.
	//
	this.cascadeUpHide = function(){
		
		if(!_isSubMenu) return;
		if(_isActive) return;
		//
		// Hide self ... :
		//
		_menuWindow.show(false);
		//
		// ... and also parent if the
		// parent is also a sub-menu:
		//
		if(_parentMenu && _parentMenu.isSubMenu()) _parentMenu.cascadeUpHide();
		
	}
	
	//
	// cascadeUpForceHide()
	//
	this.cascadeUpForceHide = function(){
		if(!_isSubMenu) return;
		_menuWindow.show(false);
		if(_parentMenu && _parentMenu.isSubMenu()) _parentMenu.cascadeUpForceHide();
		
	}
	
	//
	// setParentMenu
	//
	this.setParentMenu = function(parentMenu){
		_parentMenu = parentMenu;
	}
	
	//
	// isSubMenu
	//
	this.isSubMenu = function(){
		return _isSubMenu;
	}
	
	//
	// itemCount():
	//
	this.itemCount = function(){
		return _itemCount;
	}
	
	//
	// getNode()
	//
	this.getNode = function(){
		return _menuWindow.getNode();
	}
	
	//
	// getWidth()
	//
	this.getWidth = function(){
		return _width;
	}
	
	//
	// getHeight()
	//
	this.getHeight = function(){
		return _menuWindow.getHeight();
	}
	
	//
	// setLocation
	//
	this.setLocation = function( locationArray ){
		
		_menuWindow.setLocation( locationArray[0],locationArray[1] );
		
	}
	
	//
	// getSubMenuLocation
	//
	// index: The index for this menu item
	// RTLOffset: The width of the *submenu* (i.e., of the "_action" menu
	//            if the menu item is a RTL menu item.  Otherwise zero (0).
	//
	this.getSubMenuLocation = function(index,RTLOffset){
		
		index--;
		
		var menuLeftTop = _menuWindow.getLocation();
		var shadowWidth = _menuWindow.getShadowWidth();
		//
		// Looks better if the sub menu is a bit closer to the main
		// menu by cutting down the shadow width gap:
		//
		shadowWidth*=0.5;
		if(RTLOffset){
			return [ menuLeftTop[0] - RTLOffset + shadowWidth , menuLeftTop[1] + _menuWindow.getTitleBarHeight() + _itemHeight*index ];
		}else{
			return [ menuLeftTop[0] + _width    - shadowWidth , menuLeftTop[1] + _menuWindow.getTitleBarHeight() + _itemHeight*index ];
		}
		
	}
	
	//
	// isVisible(): True if the menu is visible
	//
	this.isVisible = function(){
		return _menuWindow.isVisible();
	}
	
	//
	//
	//
	this.hideAfterTimeout = function(){
		
		//
		// Clear any pre-existing timout:
		//
		if(_timeoutId) window.clearTimeout(_timeoutId);
		//
		// Set new timeout:
		//
		_timeoutId = window.setTimeout(_self.cascadeUpHide,_timeoutDuration);
		
	}
	
	//
	// clearTimeout
	//
	this.clearTimeout = function(){
		
		//
		// clear any pending timeout on this menu itself:
		//
		if(_timeoutId) window.clearTimeout(_timeoutId);
		
		//
		// Also clear any pending timeout on the parent
		// menu:
		//
		if(_parentMenu && _parentMenu.isSubMenu()) _parentMenu.clearTimeout();
		
	}
	
	//
	// Set activity state on the whole menu: 
	//
	this.setActive = function(isActive){
		_isActive = isActive;
	}
	
	//
	// isActive():
	//
	this.isActive = function(){ 
		return _isActive; 
	};
	
	
	this.mouseOverAction = function(){
		
		_isActive = true;
		_self.clearTimeout();
		
	}
	
	this.mouseOutAction = function(){
		
		_isActive=false;
		_self.hideAfterTimeout();
	}
	
	//////////////////////////
	//
	// Set event handlers:
	//
	//////////////////////////
	if(_isSubMenu){
		_menuWindow.getNode().addEventListener("mouseover",this.mouseOverAction,false);
		_menuWindow.getNode().addEventListener("mouseout",this.mouseOutAction  ,false);
	}
	
}


