
/** cnPrimaryNavMenuManager

Purpose:
	
	Singleton class to manage "primary" navigation menus on Xerox web sites

Assumptions:

	This class assumes that the menus it manages are structured such that,
	initially, there is a visible "header" element representing a menu in it's
	"off" state.  Also, there is a "menu" element that, initially, is not
	rendered representing the menu in it's "on" state.  When a menu is
	"turned on", the "header" is removed from the displayed content and the
	"menu" is inserted in it's place.
	
Usage:
	
	cnPrimaryNavMenuManager.toggleMenu(menuElemToTurnOn);
	cnPrimaryNavMenuManager.turnOffNow(disableMenuCompletelyFlag);
	cnPrimaryNavaMenuManager.VERSION; // version number of this libary
		
Configuration:

	The following attributes can be adjusted to fit your liking
	
		MENU_ON_DELAY    - delay (in milliseconds) before menu is turned on
		MENU_OFF_DELAY   - delay (in milliseconds) before menu is turned off
		MENU_OFF_CLASS   - String CSS class for "off" menu header elements
		MENU_ON_CLASS    - String CSS class for "on" menu header elements (i.e. menu header for current site)
		MENU_HOVER_CLASS - String CSS class for menu header element currently under mouse pointer
		MENU_MAT_CLASS   - String CSS class for "mat" <IFRAME> used on Windows/IE to prevent <SELECT> elements from occluding menus
		MENU_MAT_SRC     - String to use as "src" attribute value in "mat" <IFRAME>s (must be local to domain hosting page to prevent SSL errors)
		MENU_ELEM_SUFFIX - String of suffix appended to CSS id of "header" element to find "menu" element
		MENU_GROUP_ID    - String of CSS id for element containing all menu elements
		MENU_EXCLUDE_USER_AGENTS - Array of strings (partial or complete) to match against the navigator.userAgent strings when determining whether or not to display menus
		MENU_PRELOAD_URL - String of server URL to use when loading images (assumes all images are in the same directory)
		MENU_PRELOAD_IMAGES - Array of string-names of images to preload
**/

cnPrimaryNavMenuManager = new function()
{
	// public configuration data
	this.VERSION          = '1.1';
	this.MENU_ON_DELAY    = 5;
	this.MENU_OFF_DELAY   = 5;
	this.MENU_OFF_CLASS   = 'cn_pnav_link';
	this.MENU_ON_CLASS    = 'cn_pnav_link_on';
	this.MENU_HOVER_CLASS = 'cn_pnav_link_hover';
	this.MENU_MAT_CLASS   = 'cn_pnav_fmenu_ie_select_block';
	this.MENU_MAT_SRC     = '/img/icons/spacer.gif';
	this.MENU_ELEM_SUFFIX = '_menu';
	this.MENU_GROUP_ID    = 'cn_pnav';
	this.MENU_EXCLUDE_USER_AGENTS = [];
	this.MENU_PRELOAD_URL    = '/img/icons/';
	this.MENU_PRELOAD_IMAGES = ['bg-menulink-blue.png','gradient_bg.jpg','bg_drop.png','bottom_cap_drop.png','top_cap_drop.png','country_icon.gif','xog_menu_slice.gif','psg_menu_slice.gif','bg-menulink-green.png','xgs_menu_slice.gif','bg-menulink-purple.png','module_top_740x5.gif','footer_740x31.gif'];
	
	// "private" data/methods for set/clearInterval calls
	this._onTimerId     = null;
	this._offTimerId    = null;
	this._turnOnMenu    = function(){};
	this._turnOffMenu   = function(){};
	
	this._clearOnTimer = function ()
	{
		clearTimeout(this._onTimerId);
		
		this._onTimerId  = null;
		this._turnOnMenu = function(){};
	};
	
	this._clearOffTimer = function ()
	{
		clearTimeout(this._offTimerId);
		
		this._offTimerId  = null;
		this._turnOffMenu = function(){};
	};
	
	// "private" data
	this._skipMenuDisplay = false; // assume we want to show the menu
	this._onHdrElemId     = null;  // current "on" element
	this._activeHdrElemId = null;  // header of "active" site
	this._hdrMenuObjs     = null;  // array of Objects holding header/menu data
	this._menuDisabled    = false; // flag indicating that menus are disabled
	
	// declare our public methods
	this.turnOffMenuNow = function(){}; // this is actually defined when a menu is activated
	
	this.toggleMenu = function(toggleElem)
	{
		if (this._menuDisabled)       return;
		if (!document.getElementById) return;
		if (!this._hdrMenuObjs)       this._initialize();
		if (this._onTimerId)          this._clearOnTimer();
		
		var self = this; // set up local copy of instance for use via closure by on/off functions
		
		for ( var i = 0; i < this._hdrMenuObjs.length; ++i )
		{
			var hdrMenuObj = this._hdrMenuObjs[i];
			var hdrElem    = hdrMenuObj['hdr'];
			var menuElem   = hdrMenuObj['menu'];
		
			if ( toggleElem.id && toggleElem.id == hdrElem.id && menuElem )
			{
				var onHdrElem  = document.getElementById(toggleElem.id);
				var onMenuElem = menuElem;
				
				onHdrElem.className = this.MENU_HOVER_CLASS;
				
				// register mouseout handler in case they leave before menu displays
				registerEventHandler(onHdrElem, 'mouseout', mouseOutHandler);
				
				if (this._skipMenuDisplay) continue;

				this._turnOnMenu = function()
				{
					if ( self._onHdrElemId )
					{
						self._turnOffMenu();
						self._clearOffTimer();
					}
					
					self._showMenu(onHdrElem, onMenuElem);
	
					unregisterEventHandler(onHdrElem, 'mouseout', mouseOutHandler);
					
					registerEventHandler(document,   'mousedown', mouseDownHandler);
					registerEventHandler(onMenuElem, 'mouseout',  mouseOutHandler);
					
					self._clearOnTimer();
					self._onHdrElemId = onHdrElem.id;
					
					self._resetHdrClass(onHdrElem);
				};
				
				this._onTimerId = setTimeout('cnPrimaryNavMenuManager._turnOnMenu()', this.MENU_ON_DELAY);
				
				this.turnOffMenuNow = function(disableCompletely)
				{
					turnOffMenu(onHdrElem, onMenuElem);
					
					self._clearOffTimer();
					self._onHdrElemId = null;
					
					if (disableCompletely) this._menuDisabled = true;
				};
			}
			else if ( menuElem && menuElem.style.display == 'block' )
			{
				var offHdrElem  = hdrElem;
				var offMenuElem = menuElem;
				
				this._resetHdrClass(offHdrElem);
				
				if (this._skipMenuDisplay) continue;
				
				this._turnOffMenu = function()
				{
					turnOffMenu(offHdrElem, offMenuElem);
					
					self._clearOffTimer();
					self._onHdrElemId = null;
				};
				
				this._offTimerId = setTimeout('cnPrimaryNavMenuManager._turnOffMenu()', this.MENU_OFF_DELAY);
			}
		}
		
		// define our handler functions to take advantage of closure
		function mouseOutHandler(e)
		{
			var event  = pullEvent(e);
			var target = pullTarget(event);
			var toElem = pullDestination(event);

			if ( !self._skipMenuDisplay && toElem && !isChildOf(toElem, onMenuElem) )
			{
				var myHdrElem  = onHdrElem;
				var myMenuElem = onMenuElem;
				
				if (self._onTimerId) self._clearOnTimer();
				
				self._turnOffMenu = function()
				{
					turnOffMenu(myHdrElem, myMenuElem);
					
					self._clearOffTimer();
				};

				self._offTimerId = setTimeout('cnPrimaryNavMenuManager._turnOffMenu()', self.MENU_OFF_DELAY);
			}
			// no else, just cancel bubbling
			
			self._resetHdrClass(onHdrElem);
			
			if (event.stopPropagation) event.stopPropagation();
			else event.cancelBubble = true;
		}
		
		function mouseDownHandler(e)
		{
			var event  = pullEvent(e);
			var target = pullTarget(event);
	
			if ( !isChildOf(target, onMenuElem) )
			{
				turnOffMenu(onHdrElem, onMenuElem);
						
				self._turnOffMenu();
				self._clearOffTimer();
				self._resetHdrClass(onHdrElem);
			}
		}
		
		function turnOffMenu(hdrElem, menuElem)
		{
			hideMenu(hdrElem, menuElem);
			
			unregisterEventHandler(hdrElem,  'mouseout',  mouseOutHandler);
			unregisterEventHandler(document, 'mousedown', mouseDownHandler);
		}
	};
	
	
	
	this.preloadImages = function()
	{
		var preloadImages = this.MENU_PRELOAD_IMAGES;
		var tmpLoad       = new Array(preloadImages.length);
		
		for ( var i = 0; i < preloadImages.length; ++i )
		{
			tmpLoad[i]     = new Image();
			tmpLoad[i].src = this.MENU_PRELOAD_URL + preloadImages[i];
		}
	}
	
	// "private" methods
	this._initialize = function ()
	{
		this._skipMenuDisplay = this._verifyUserAgent();
		
		this._hdrMenuObjs = new Array();
		
		var hdrElems = getElementsByClassNames(this.MENU_GROUP_ID, [this.MENU_OFF_CLASS, this.MENU_ON_CLASS]);
		
		for ( var i = 0; i < hdrElems.length; ++i )
		{
			var hdrElem   = hdrElems[i];
			var hdrElemId = hdrElem.id;
			
			if ( hdrElemId )
			{
				var menuElem = document.getElementById(hdrElemId + this.MENU_ELEM_SUFFIX);

				if ( menuElem )
				{
					var menuId = menuElem.id;
					
					if (menuId) this._hdrMenuObjs[this._hdrMenuObjs.length] = { hdr: hdrElem, menu: menuElem };
					
					if (hdrElem.className == this.MENU_ON_CLASS) this._activeHdrElemId = hdrElemId;
				}
			}
		}
		
		return;
	};
	
	this._resetHdrClass = function (hdrElem)
	{
		hdrElem.className = (hdrElem.id == this._activeHdrElemId) ? this.MENU_ON_CLASS : this.MENU_OFF_CLASS;
	}
	
	
	
	this._showMenu = function (hdrElem, menuElem)
	{
		hdrElem.style.display  = 'none';
		menuElem.style.display = 'block';
		
		if ( document.all && navigator.appVersion.indexOf('Win') != -1 )
		{
			var matId = menuElem.id+'_ie_mat';
			var ieMat = document.getElementById(matId);
			
			if ( !ieMat )
			{
				ieMat = document.createElement('IFRAME');
				ieMat.setAttribute('scrolling', 'no');
				ieMat.setAttribute('frameborder', 0);
				ieMat.setAttribute('src', this.MENU_MAT_SRC);
				
				var menu  = (menuElem.getElementsByTagName('div'))[1];

				ieMat.id            = matId;
				ieMat.className     = this.MENU_MAT_CLASS;
				ieMat.style.width   = menu.offsetWidth;
				ieMat.style.height  = menu.offsetHeight;
				ieMat.style.zIndex  = menu.style.zIndex - 1;
				ieMat.style.display = 'block';
								
				menu.appendChild(ieMat);
			}
		}
	}
	
	this._verifyUserAgent = function()
	{
		var userAgent = navigator.userAgent;
		
		for ( var i = 0; i < this.MENU_EXCLUDE_USER_AGENTS.length ; ++i )
		{
			if ( userAgent.indexOf(this.MENU_EXCLUDE_USER_AGENTS[i]) != -1 )
			{
				return true;
			}
		}
		
		return false;
	};
	
	/** PRIVATE FUNCTIONS (alphabetized) **/
	
	function getElementsByClassNames(rootElemId, classNames)
	{
		var rootElem         = document.getElementById(rootElemId);	
		var elements         = new Array();
		var captureClassName = new Object();

		for ( var i = 0; i < classNames.length; ++i ) captureClassName[classNames[i]] = true;
		
		if (rootElem) _pullElements(rootElem);
	
		function _pullElements(node)
		{
			if (captureClassName[node.className]) elements[elements.length] = node;
			
			if ( node.hasChildNodes )
			{
				var children = node.childNodes;
				
				for ( var i = 0; i < children.length; ++i )
				{
					_pullElements(children[i]);
				}
			}
		}
	
		return elements;
	}
	
	function hideMenu(hdrElem, menuElem)
	{
		hdrElem.style.display  = 'block';
		menuElem.style.display = 'none';
	}
	
	function isChildOf(node, parentNode)
	{
		var isChild = parentNode == node;
		
		if ( !isChild && node.parentNode )
		{
			isChild = isChildOf(node.parentNode, parentNode);
		}
		
		return isChild;
	}
	
	function pullDestination(e)
	{
		return (e.relatedTarget) ? e.relatedTarget : (e.toElement) ? e.toElement : null;
	}
	
	function pullEvent(e)
	{
		return (e) ? e : (window.event) ? window.event : null;
	}
	
	function pullTarget(e)
	{
		return (e.target) ? e.target : (e.srcElement) ? e.srcElement : null;
	}
	
	function registerEventHandler(elem, event, handler)
	{
		if ( document.addEventListener )
		{
			elem.addEventListener(event, handler, false);
		}
		else if ( document.attachEvent )
		{
			elem.attachEvent('on'+event,  handler);
		}
		else // probably on a Mac IE
		{
			elem['on'+event] = handler;
		}
	}
	
	function unregisterEventHandler(elem, event, handler)
	{
		if ( document.removeEventListener )
		{
			elem.removeEventListener(event, handler, false);
		}
		else if ( document.detachEvent )
		{
			elem.detachEvent('on'+event, handler);
		}
		else // probably on a Mac on IE
		{
			elem['on'+event] = null;
		}
	}
}

// function used for secondary navigation
function changeClass(elemId, newClass)
{
	var elem = null;
	
	if ( document.getElementById )
	{
		elem = document.getElementById(elemId);
	}
	else if ( document.all )
	{
		elem = document.all[elemId];
	}
	
	if (elem) elem.className = newClass;
}
cnPrimaryNavMenuManager.preloadImages();

// end of Xerox Office Javascript    http://www.office.xerox.com
// 
