/*  ------------------------
*	Library classes for constructing a template
*	all functions and properties that are preceded by _ are private
*	and should not be accessed from exterior code
*
*	There are classes currently available. Some of these may be removed when not neccessary
*	1) TemplateUtils() 
*	This is always present and always invoked as oTplUtils. A collection of properties
*	and functions for getting navitem information and other useful information.
*	
*	2) Trail()
*	Optional. Builds a trail of navids from the selected navitem to root
*
*	3) NavigationItemMenu()
*	Optional. Builds a navigation menu from the navitem array
*
*	3) DropDownMenu()
*	Optional. Builds a drop down or slid out menu. Needs the external style sheet to work
*	If it is present then it will always be invoked as oDDM
*	Only one dropdown menu can exist on a page at present.
*  ------------------------  */



/* ----------------- browsers DOM extensions -------------------------*/
//this is included to implement the Array.push function in ieMac
if (typeof Array.prototype.push == 'undefined') {
	Array.prototype.push=function(){
		var i=0;
	    b=this.length,a=arguments;
		for(i;i<a.length;i++)this[b+i]=a[i];
	    return this.length
	}
}

//this is included to implement the Array.inArray function
if (typeof Array.prototype.inArray == 'undefined') {
	Array.prototype.inArray = function(value){
		for (var i=0; i<=this.length-1; i++) {
			if (this[i] == value) {
				return true;
			}
		}

		return false;
	}
}
/* -----------------  end browsers DOM extensions -------------------------*/


var oTplUtils = new TemplateUtils()
function TemplateUtils() {
	
	/*  ------------------------
	*	Version 1.0
	*	Collection of utilities functins and properties for use with other
	*	classes in this package and for general use with templates.
	*  ------------------------  */
	
	/* properties that describe the browser type */
	this.BUA = navigator.userAgent;
	this.BIE = this.BUA.indexOf("MSIE");
	this.BIsIE = this.BIE>=0;
	this.BIsMaccak = this.BUA.indexOf("Mac")!=-1;
	this.BVer = this.BIE>=0 ? parseFloat(this.BUA.substring(this.BIE+5, this.BIE+6)+"."+this.BUA.substring(this.BIE+7, this.BIE+8)) : parseInt(navigator.appVersion.substring(0,1));

	/* properties for more descriptive accessing of navitems nodes */
	this.NODE_ID = 0;
	this.PARENT_NODE = 1;
	this.DISPLAY_TEXT = 2;
	this.HELP = 3;
	this.URL = 4;
	this.URL_TARGET = 5;
	
	this.hasChildren = function(navId){
		//pre:		true
		//post:		if navitem for the given navId has children then true will be returned else false
		//return:	boolean
		
		for (var i=0; i<=NavItems.length-1; i++) {
			if (NavItems[i][this.PARENT_NODE] == navId) {
				return true
			}
		}
	
		return false;
	}

	this.getNavItem = function(navId){
		//pre:		true
		//post:		An array containing the items for the navitem is returned
		//return:	boolean
	
		var navItem = Array();
	
		for (var i=0; i<NavItems.length; i++) {
			if (NavItems[i][this.NODE_ID] == navId) {
				navItem[this.NODE_ID] = NavItems[i][this.NODE_ID];
				navItem[this.PARENT_NODE] = NavItems[i][this.PARENT_NODE];
				navItem[this.DISPLAY_TEXT] = NavItems[i][this.DISPLAY_TEXT];
				navItem[this.HELP] = NavItems[i][this.HELP];
				navItem[this.URL] = NavItems[i][this.URL];
				navItem[this.URL_TARGET] = NavItems[i][this.URL_TARGET];
			}
		}
	
		return navItem;
	}

	this.navItemExists = function(id){
		//pre:		true
		//post:		if navitem for the given navId exists then true will be returned else false
		//return:	boolean

		var navItem = this.getNavItem(id)
		return navItem.length > 0;
	}
	
	this.buildAHREF = function(nodeId){
		//pre:		the navitem exists
		//post:		an <A HREF> tag string is returned based on the navitem values
		//return:	string
		
		var node = this.getNavItem(nodeId);
		
		return '<a href="'+node[this.URL]+'" '+(node[this.URL_TARGET] != '' ? 'target="'+node[this.URL_TARGET]+'"' : '')+'>'+node[this.DISPLAY_TEXT]+'</a>';
	}
}


function Trail() {
	
	/*  ------------------------
	*	Version 1.0
	*	Builds a trail of ids from the root to the selected nav id
	*	usage:
	*	1) "setSelectecNavId(x)"
	*	2) "makeTrail()"
	*  ------------------------  */
	
	this._selectedNavId = 0;
	this._trail = Array();
	this._rootNavId = -1;
	
	this.setSelectedNavId = function(navId){
		//pre:		true
		//post:		the selected nav item to build the trail from is set
		//return:	
		
		this._selectedNavId = navId;
	}

	this.makeTrail = function() {
		//pre:		the selectedNavItem is set
		//post:		the trail is contstructed
		//return:	Array
		
		if (!oTplUtils.navItemExists(this._selectedNavId)) {return this._trail;}
		this._makeTrailRc(this._selectedNavId);
		this._trail.reverse();
		return this._trail
	}

	this._makeTrailRc = function(nodeId){
		if (nodeId == this._rootNavId){
			this._trail.push(this._rootNavId);
		
		} else {
			this._trail.push(nodeId);
	
			var node = oTplUtils.getNavItem(nodeId);
			this._makeTrailRc(node[oTplUtils.PARENT_NODE]);
		}
	}
}

function NavigationItemMenu () {

	/*  ------------------------
	*	Version 1.0
	*	Builds a Treed UL menu for transforming with a style sheet
	*	Shows the children of the selected node and any children its children.
	*	The LI tag of selected nodes will have "NIMSelected" in the class attribue
	*	The LI tage of nodes that lay in the trail will have "NIMSelected" in the class attibute
	*
	*	The classes will only work if the trail is supplied
	*
	*	example of output:
	*	<ul>
	*		<li>item 1</li>
	*		<li>item 2</li>
	*		<li class="NIMSelected">item 3
	*			<ul>
	*				<li class="NIMSelected">item 4</li>
	*				<li>item 5</li>
	*			</ul>
	*		</li>
	*	</ul>
	*
	*	usage:
	*	1) "setTopNavId(topNavId, selectedNavId)"
	*	2) "setTrail(trail)" - optional
	*	2) "buildMenu()"
	*  ------------------------  */
	
	this._topNavId = null;
	this._selectedNavId = null;
	this._trail = Array();
	this._menuDepthLimit = 10;
	
	this.init = function(topNavId, selectedNavId) {
		//pre:		true
		//post:		initates the properties of the class
		//			topNavId sets the top id of the menu
		//			selectedNavId sets what is currently selected
		//return:	
		
		this._topNavId = topNavId;
		this._selectedNavId = selectedNavId;
	}
	
	this.setTrail = function(trailArr) {
		//pre:		true
		//post:		sets the trail of node ids back to the root
		//			use the trail class to create this array
		//return:	
		
		this._trail = trailArr;
	}

	this.buildMenu = function() {
		if (!oTplUtils.navItemExists(this._topNavId)) {
			return '';
		}
		return this._buildMenuRC(this._topNavId, 0)
	}
	
	this._buildMenuRC = function(navId, depth) {
		var out = '';
	
		if (!oTplUtils.hasChildren(navId) || (depth == this._menuDepthLimit)){
			return out;
		}
	
		out += "<ul>";
		depth++;
	
		for (var i=0; i<= NavItems.length-1; i++){
			if (NavItems[i][oTplUtils.PARENT_NODE] == navId) {
				
				if ( (NavItems[i][oTplUtils.NODE_ID] == this._selectedNavId) || 
					(this._trail.inArray(NavItems[i][oTplUtils.NODE_ID])) ) {
					var classStr = ' class="NIMSelected"';
				} else {
					var classStr = '';
				}
				
				out += '<li'+classStr+'>';
				out += "<a href=\""+NavItems[i][oTplUtils.URL]+"\" "+(NavItems[i][oTplUtils.URL_TARGET] != '' ? 'target="'+NavItems[i][oTplUtils.URL_TARGET]+'"' : '')+"><div>"+NavItems[i][oTplUtils.DISPLAY_TEXT]+"</div></a>";
				out += this._buildMenuRC(NavItems[i][oTplUtils.NODE_ID], depth);
				out += "</li>";
			}
		}
		
		out += "</ul>";
		return out;		
	}
}


var oDDM = new DropDownMenu();
function DropDownMenu() {
	
	/*  ------------------------
	*	Version 1.1
	*	Generates a drop down or slide out menu.
	*	This class generates a treed UL list from the navitem array and uses
	*	the CSS file "DDMenu.css". To change colour size etc of the menu
	*	manipulate the CSS file.
	*
	*	Usage: 
	*	Before page loads
	*	1) set the starting nav item "oDDM.setStartingNavId(x)"
	*	2) set the dom id of menu "oDDM.setMenuDomId('yy')"
	*	3) "oDDM.buildMenu()"
	*	After page loads
	*	4) "oDDM.enableMenu()"
	*  ------------------------  */
	
	this._startingNavId = 0;
	this._menuDomId = '';
	this._useDIVWrapperInsideAHREFs = false;
	
	this._menuDepthLimit = -1;  //-1 = no limit on depth 
	this._menuCount = 0;
	this._menuULs = Array(); //internal variable for detecting menu direction


	this.setStartingNavId = function(startingNavId) {
		//pre:		true
		//post:		the navitem id to build the menu from is set
		//return:	
		
		this._startingNavId = startingNavId;
	}
	
	this.useDIVWrapperInsideAHREFs = function() {
		//pre:		true
		//post:		if you want DIV tags inside the A tags of each item in the menu then
		//			call this before the page loads
		//return:	_useDIVWrapperInsideAHREFs is true
		
		this._useDIVWrapperInsideAHREFs = true;
	}
	
	this.setMenuDomId = function(menuDomId) {
		//pre:		true
		//post:		the menu dom id is set
		//			do this before loading
		//return:	
		
		this._menuDomId = menuDomId;
	}
	
	this.buildMenu = function() {
		//pre:		true
		//post:		the menu is built and returned as a string
		//			do before loading
		//return:	string		
		
		var out = '';

		if (!oTplUtils.navItemExists(this._startingNavId)) {
			alert('DropDownMenu.buildMenu() : the startNavId does not exist!');
			return '';
		} else {
			out = this._buildMenuRC(this._startingNavId, 0);
			return out;
		}
	}

	this.enableMenu = function() {
		//pre:		true
		//post:		the menu is activated so sub menus roll out
		//			do after the page loads
		//return:	
		
		this._DDMHover();
		document.getElementById(this._menuDomId).style.display = "block";
	}
	
	this._buildMenuRC = function(navId, depth){
		var out = '';
	
		if (!oTplUtils.hasChildren(navId) || (depth == this._menuDepthLimit)){
			return out;
		}
	
		if (this._menuCount == 0){
			out += '<ul id="'+this._menuDomId+'" style="display:none">';
			this._menuCount++;
		} else {
			out += "<ul>";
		}
		
		depth++;
	
		for (var i=0; i<= NavItems.length-1; i++){
			if (NavItems[i][oTplUtils.PARENT_NODE] == navId) {
				out += "<li>";
				out += "<a href=\""+NavItems[i][oTplUtils.URL]+"\" "+(NavItems[i][oTplUtils.URL_TARGET] != '' ? 'target="'+NavItems[i][oTplUtils.URL_TARGET]+'"' : '')+"><div>"+NavItems[i][oTplUtils.DISPLAY_TEXT]+"</div></a>";
				out += this._buildMenuRC(NavItems[i][oTplUtils.NODE_ID], depth);
				out += "</li>";
			}
		}
		
		out += "</ul>";
		return out;
	}	
	
	this._DDMHover = function() {
		var menuDomId = oDDM._menuDomId;
		var DDMEls = document.getElementById(menuDomId).getElementsByTagName("LI");
		
		for (var i=0; i<DDMEls.length; i++) {
			DDMEls[i].onmouseover = function(e) {
				if (oTplUtils.BIsIE) {
					this.className += " DDMhover";
				}
				oDDM._hoverDetectScreenOverflow(e ? e : event);
			}
			DDMEls[i].onmouseout = function() {
				if (oTplUtils.BIsIE) {
					this.className = this.className.replace(new RegExp(" DDMhover\\b"), "");
				}
			}
			
			if (DDMEls[i].parentNode == document.getElementById(menuDomId) && oTplUtils.BIsIE) {
				if (oTplUtils.BIsIE) {
					//DDMEls[i].style.width = '1px';
				}
			}
		}
	}
	
	this._hoverDetectScreenOverflow = function(e) {
		var target = e.srcElement ? e.srcElement : e.target;
		var thisUL = target;
		var menuX = 0;
		var browserWidth = 0;
		var branchWidth = 0;
		
		while ((thisUL.tagName != 'UL') && (thisUL.tagName != 'BODY')) {
			thisUL = thisUL.parentNode;
		}
		
		if (thisUL.id == this._menuDomId) {
			thisUL = target.parentNode.parentNode.childNodes[1];
			
			branchWidth = this._hoverDetectWidestBranch(thisUL);
			menuX = this._getElementXPosRelativeToScreen(thisUL);
			browserWidth = (window.innerWidth ? window.innerWidth : document.body.scrollWidth) - 20;
		
			if ((menuX + branchWidth) > browserWidth) {
				thisUL.className = ' leftAlign';
			} else if (thisUL) {
				//this may cause problems - check in future sites
				//thisUL.className = ' DDMhover';
			}
		}
	}
	
	this._hoverDetectWidestBranch = function(HTMLObj){
		if (!HTMLObj) {
			return 0;
		}
		
		var widestBranchWidth = HTMLObj.offsetWidth;
		var thisBranchesWidth = 0;
		
		this._menuULs = Array();
		this._hoverGetULsAtEndOfBranchesRC(HTMLObj);
		
		for(var i=0; i<this._menuULs.length; i++) {
			thisBranchesWidth = this._hoverDetectThisBranchesWidth(HTMLObj, this._menuULs[i]);
			if (thisBranchesWidth > widestBranchWidth) {
				widestBranchWidth = thisBranchesWidth;
			}
		}
		
		return widestBranchWidth;
	}
	
	this._hoverDetectThisBranchesWidth = function (topULObj, ULObj) {
		var width = topULObj.offsetWidth;
		
		while (ULObj != topULObj) {
			if (ULObj.tagName == 'UL') {
				width = width + ULObj.offsetWidth;
			}
			ULObj = ULObj.parentNode;
		}
		
		return width;
	}
	
	
	this._hoverGetULsAtEndOfBranchesRC = function(HTMLObj) {
		
		for (var i=0; i<HTMLObj.childNodes.length; i++) {
			var child = HTMLObj.childNodes[i];
			if (child.tagName == 'UL') {
				
				this._menuULs.push(child);
			}
			
			this._hoverGetULsAtEndOfBranchesRC(child);
		}
	}
	

	this._getElementXPosRelativeToScreen = function(elementObj) {
		var x = 0;
		
		if (!elementObj) {
			return x;
		}
		
		var element = elementObj.parentNode;
				
		while (element.tagName != 'BODY') {
			x = x + element.offsetLeft;
			element = element.parentNode;
		}
				
		return x;
	}
}
