function TreeJSObj() {
	this.parent = new Array();
	this.nodes = new Array();
	this.cacheParent = new Array();
	
	this.addNode = function(aId, aParentId, aName) {
		this.nodes[aId] = new TreeJSNode(aId, aParentId, aName);
		if (aParentId == 0) {
			this.parent[this.parent.length] = aId
		} else {
			//Add this child to the parent if we can
			if (typeof(this.nodes[aParentId]) == "undefined") {
				var tArray = new Array(aId)
				//Add this to the cache
				if (typeof(this.cacheParent[aParentId]) == "undefined") {
					this.cacheParent[aParentId] = new Array()
				}
				this.cacheParent[aParentId][this.cacheParent[aParentId].length] = aId
			} else {
				this.nodes[aParentId].addChild(aId)
			}
		}
		//Has anything been cached against this node?
		if (typeof(this.cacheParent[aId]) != "undefined") {
			for (var i=0; i < this.cacheParent[aId].length; i++) {
				this.nodes[aId].addChild(this.cacheParent[aId][i])
			}
		}
	}
}
function TreeJSNode(aId, aParentId, aName) {
	this.id = aId;
	this.parent = aParentId;
	this.name = aName;
	this.child = new Array();
	
	this.addChild = function(aId) {
		this.child[this.child.length] = aId
	}
}
function TreeTextOptimizedObject () {
	if (!Array.prototype.push) {
		function array_push() {
			for(var i = 0; i < arguments.length; i++)
				this[this.length] = arguments[i];
			return this.length;
		}
		Array.prototype.push = array_push;
	}
	if (!Array.prototype.pop) {
		function array_pop(){
			lastElement = this[this.length-1];
			this.length = Math.max(this.length-1, 0);
			return lastElement;
		}
		Array.prototype.pop = array_pop;
	}
	
	this.negativeIsLink = false
	this.processedNoLinks = new Array()
	this.noLinksUncached = true
	this.nodesNoLinksRecursive = false
	
	//var tDebug = new Array()
	this.createTree = function (aImageArray, aImagePath, tTree, aPlacementId, aNavLink, startNode, openNode, nodesNoLinks, disableActive, aNavLinkHi, aOpenAllNodes, aNoLinks, aShowCheckBox, aCheckedNodes, aHomePageId, aLinkPrefix, aLinkSuffix, aPassNameParam) {
		var time1 = new Date()
		
		//Set member configuration variables
		this.myName			= 'treeObj';
		this.icons			= aImageArray;
		this.iconsPath	 	= aImagePath;
		this.tree			= tTree;
		this.cssCurNavStyle = aNavLink;
		this.cssNavLink 	= aNavLink;
		this.cssNavLinkHi 	= aNavLinkHi;
		this.startNode 		= startNode;
		this.openNode 		= openNode;
		this.nodeNoLinks	= nodesNoLinks;
		this.disableActive 	= disableActive;
		this.checkedNodes	= aCheckedNodes;
		this.homePageId		= aHomePageId;
		
		//Analyse the icons array to determine if we are using spacers
		this.useSpacers = (typeof(this.icons["spacerclosed"]) == "string" && typeof(this.icons["spaceropen"]) == "string")
		
		//test the openallnodes and nolinks params
		if (typeof(aOpenAllNodes) == "boolean") {
			this.openall = aOpenAllNodes
		} else {
			this.openall = false
		}
		if (typeof(aNoLinks) == "boolean") {
			this.nolinks = aNoLinks
		} else {
			this.nolinks = false
		}
		// should simple wizard be showing the "select page" checkboxes
		if (typeof(aShowCheckBox) == "boolean" && aShowCheckBox == true) {
			this.showCheckBox	= true
		} else {
			this.showCheckBox	= false;
		}
		if (typeof(aLinkPrefix) == "string") {
			this.linkPrefix = aLinkPrefix
		}
		if (typeof(aLinkSuffix) == "string") {
			this.linkSuffix = aLinkSuffix
		} else {
			this.linkSuffix = ""
		}
		if (typeof(aPassNameParam) == "boolean") {
			this.passNameParam = aPassNameParam
		} else {
			this.passNameParam = false
		}
		
		this.myId = aPlacementId
		this.myName += aPlacementId
	
		//Now recursivly draw all the nodes
		for (var i=0; i < this.tree.parent.length; i++) {
			//Add the parent node then recurse for all the children!!
			this.addNode(this.tree.nodes[this.tree.parent[i]], true, (i == this.tree.parent.length - 1), new Array())
		}

		//alert("CWM Enhanced Time taken:" + (new Date()-time1) + "ms\n" + tDebug.join("\n"))
		//alert("CWM Enhanced Time taken:" + (new Date()-time1) + "ms")
	}
	
	this.addNode = function(aNodeObj, aIsParent, aIsLastNode, aRecursedList) {
		//If parent add the table script
		//if (aIsParent) {
			document.write('<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr><td align="left" valign="top" width="1%" nowrap>');
		//}
		
		//Draw this node, but first get some information about the node
		//var tNodeId = this.cleanseId(aNodeObj.id)
		var tNodeId = aNodeObj.id
		var tNodeHasChildren = (aNodeObj.child.length > 0)
		var tNodeIsOpen = this.isNodeOpen(tNodeId)
		var tHTML = new Array()
		
		//Set mouse states
		var tMouseOverTxt = "window.status='" + aNodeObj.name.replace(/\'/g, "\\'").replace(/\"/g, "&#34;") + "';return true;";
		var tMouseOutTxt = "window.status=' ';return true;";
		
		//Add tree starting images based on the recursion level
		this.addTreeLines(aRecursedList, aNodeObj.name)
		
		//Add to the recursion list identifiers so that we will know what lines, etc to draw when adding the children
		if (aIsLastNode) {
			aRecursedList.push(0);		//0 indicates that this node is the last in the line, and that the line should not be added
		} else {
			aRecursedList.push(1);		//1 indicates that this node has more child items to come, so the line should be added
		}
		
		//Add tree controls
		this.addTreeControls(tNodeId, aNodeObj.name, tNodeHasChildren, aIsLastNode, tNodeIsOpen, tMouseOverTxt, tMouseOutTxt);
		
		//Add the link text for this item
		this.addTreeLink(tNodeId, aNodeObj.name, tMouseOverTxt, tMouseOutTxt, aIsLastNode);
		
		//Add any children this node has
		this.addChildren(tNodeId, aNodeObj, tNodeHasChildren, tNodeIsOpen, aRecursedList)
		
		//Remove the last added element from the recursion list
		aRecursedList.pop()
		
		//If parent draw the table close script
		//if (aIsParent) {
			document.write('</td></tr></table>');
		//}
	}
	
	this.cleanseId = function(aVal) {
		return aVal.substr(1);
	}
	
	this.createCheckBoxText	= function(aNode) {
		var tCheckBoxText = ""
		if (this.showCheckBox) {
			var tItemName = "selectpage_" + aNode;
			if (this.homePageId == aNode) {
				tCheckBoxText	= "<input type=\"hidden\" name=" + tItemName + ">";
			} else {
				//See if we are currently checked
				var tChecked = false;
				for (var i in this.checkedNodes) {
					if (this.checkedNodes[i] == aNode) {
						tChecked = true;
						break;
					}
				}
				//Add checkbox
				tCheckBoxText = "&nbsp;<input type=checkbox name=" + tItemName + " value=1 onClick='javascript: doSelectPages(\"" + tItemName + "\")'" + ((tChecked)?"checked":"") + ">&nbsp;";
			}
		} else {
			//Do not do anything, just return the empty string
		}
		return tCheckBoxText;
	}
	
	this.isNodeOpen = function(aNodeId) {
		if (this.openall) {
			return true
		} else {
			if (typeof(this.openNode[aNodeId]) == "undefined") {
				//Get the children for this node
				var tChildren = this.tree.nodes[aNodeId].child
				var tFoundChild = false
				for (var i = 0; i < tChildren.length; i++) {
					//Use recursion
					tFoundChild = this.isNodeOpen(tChildren[i])
					//Did we find someone
					if (tFoundChild) {
						break;
					}
				}
				return tFoundChild;
			} else {
				//We found it open already
				return true
			}
		}
	}
	
	this.getIconPath = function(aFilename) {
		if (aFilename.charAt(0) == "/") {
			//Forward slash means relative path
			return aFilename
		} else {
			//No leading slash indicates that we should add the source folder to the filename
			return this.iconsPath + aFilename
		}
	}
	
	this.addTreeLines = function(aRecursedList, aName) {
		//tDebug[tDebug.length] = (aName + ":" + aRecursedList.join(", "))
		for (var i = 0; i < aRecursedList.length; i++) {
			if (aRecursedList[i] == 1) {
				if (this.icons["line"] != "") {
					document.write('<img src="' + this.getIconPath(this.icons["line"]) + '" align="absbottom" border="0" alt="" >');
				}
			} else {
				if (this.icons["empty"] != "") {
					document.write('<img src="' + this.getIconPath(this.icons["empty"]) + '" align="absbottom" border="0" alt="" >');
				}
			}
		}
	}
	
	this.addTreeControls = function(aNodeId, aName, aNodeHasChildren, aIsLastNode, aNodeIsOpen, aMouseOverText, aMouseOutText) {
		//Firstly we want to add the links and images for the left of the tree
		var tImageSrc = ""
		var tBottom = 1
		//Check which image to draw firstly testing if we have any children
		if (aNodeHasChildren) {
			//Are we the last node in the list?
			if (aIsLastNode) {
				//We are the last node, finally checked if we are open or not
				if (aNodeIsOpen) {
					tImageSrc = this.icons["closedbottom"]
				} else {
					tImageSrc = this.icons["openbottom"]
				}
			} else {
				//We are not the last node, finally checked if we are open or not
				if (aNodeIsOpen) {
					tImageSrc = this.icons["closed"]
				} else {
					tImageSrc = this.icons["open"]
				}
				//Set the bottom value to 0
				tBottom = 0
			}
			//Now draw the image if we found a value that is not blank
			if (tImageSrc != "") {
				//Add link information if required
				if (!this.nolinks) {
					document.write('<a href="javascript: ' + this.myName + '.oc(' + aNodeId + ', ' + tBottom + ');">');
				}
				document.write('<img id="' + this.myId + 'join' + aNodeId + '" src="' + this.getIconPath(tImageSrc) + '" border="0" align="absbottom" alt="Locked ' + aName + '" >'); 
				if (this.useSpacers) {
					if (aNodeIsOpen) {
						document.write('<img id="' + this.myId + 'spacer' + aNodeId + '" src="' + this.getIconPath(this.icons["spaceropen"]) + '" border="0" align="absbottom" alt="">');
					} else {
						document.write('<img id="' + this.myId + 'spacer' + aNodeId + '"src="' + this.getIconPath(this.icons["spacerclosed"]) + '" border="0" align="absbottom" alt="">');
					}
				}
				//Add link information if required
				if (!this.nolinks) {
					document.write('</a>');
				}
			}
		} else {
			//This node has no children, are we the last node in the list?
			if (aIsLastNode) {
				//We are the last node
				tImageSrc = this.icons["joinbottom"]
				//tDebug[tDebug.length] = "added join bottom:" + tImageSrc + ":" + aName
			} else {
				//We are not the last node
				tImageSrc = this.icons["joined"]
				//tDebug[tDebug.length] = "added join:" + tImageSrc + ":" + aName
			}
			//Now draw the image if we found a value that is not blank
			if (tImageSrc != "") {
				document.write('<img src="' + this.getIconPath(tImageSrc) + '" border="0" align="absbottom" alt="">');
			}
			if (this.useSpacers) {
				if (aNodeIsOpen) {
					document.write('<img id="' + this.myId + 'spacer' + aNodeId + '" src="' + this.getIconPath(this.icons["spaceropen"]) + '" border="0" align="absbottom" alt="">');
				} else {
					document.write('<img id="' + this.myId + 'spacer' + aNodeId + '"src="' + this.getIconPath(this.icons["spacerclosed"]) + '" border="0" align="absbottom" alt="">');
				}
			}
		}
	}
	
	this.addTreeLink = function(aNodeId, aName, aMouseOverText, aMouseOutText, aIsLastNode) {
		//Is this an open node?
		if ((this.disableActive && this.isNodeOpen(aNodeId)) || (this.isLinkDisabled(aNodeId))) {
			document.write('<span class="' + this.cssCurNavStyle + '" onmouseover="' + aMouseOverText + '" onmouseout="' + aMouseOutText + '">' + this.createCheckBoxText(aNodeId) + aName + ' </span>');	// Write out node name
		} else {
			//Draw the link only if we are displaying links and if the id is greater than 0, -1 are used for placeholder
			if ((!this.nolinks) && (this.negativeIsLink || (aNodeId > 0))) {
				//Draw the name as a link testing configuration
				
				document.write('<a href="' + this.linkPrefix + aNodeId)
				//Should we be passing the link name as a parameter before the suffix?
				if (this.passNameParam && this.linkSuffix != "") {
					document.write(",'" + aName.replace(/\'/g, "\\\'").replace(/\"/g, "&#34;") + "'," + ((aIsLastNode)?"1":"0"));
				}
				document.write(this.linkSuffix + '"');
				
				if (this.linkTarget != null) {
					document.write(' target="' + this.linkTarget + '"');
				}
				document.write(' class="' + this.cssCurNavStyle + '" onmouseover="' + aMouseOverText + '" onmouseout="' + aMouseOutText + '">' + this.createCheckBoxText(aNodeId) + aName + "</a>");
			} else {
				//Draw just the name
				document.write(this.createCheckBoxText(aNodeId) + aName);
			}
		}
		document.write("<BR>");
	}
	
	this.isLinkDisabled = function(aNodeId) {
		//Do we need to generate the list of disabled links?
		if (this.noLinksUncached) {
			//Signal we have cached them
			this.noLinksUncached = false
			
			//Is it recursive or not?
			if (this.nodesNoLinksRecursive) {
				//Read them all and recurse that the parent and all children can't be selected
				for (var i=0; i < this.nodeNoLinks.length; i++) {
					//Set that this node's parent can't be chosen
					this.processedNoLinks[this.tree.nodes[this.nodeNoLinks[i]].parent] = ""
					
					//Call the recursive to add for this item and its children
					this.processNoLinksForNode(this.nodeNoLinks[i])
				}
			} else {
				//Simply add the ones in the no links array
				for (var i=0; i < this.nodeNoLinks.length; i++) {
					//Set that this node's parent can't be chosen
					this.processedNoLinks[this.nodeNoLinks[i]] = ""
				}
			}
		}
		return (typeof(this.processedNoLinks[aNodeId]) != "undefined")
	}
	
	this.processNoLinksForNode = function(aNodeId) {
		//Add this node
		this.processedNoLinks[aNodeId] = ""
		var tChildren = this.tree.nodes[aNodeId].child
		for (var i = 0; i < tChildren.length; i++) {
			//Use recursion to add them and add their children
			this.processNoLinksForNode(tChildren[i])
		}
	}
	
	this.addChildren = function(aNodeId, aNodeObj, aNodeHasChildren, aNodeIsOpen, aRecursedList) {
		//Now add children recursivly
		if (aNodeHasChildren) {
			//Write out the div layer for the children so we can show/hide children
			document.write("<div id=\"" + this.myId+  "div" + aNodeId + "\"");
			//If this node is currently not open, set the style to hidden
			if (!aNodeIsOpen) {
				document.write(" style=\"display: none;\"");
			}
			document.write(">");
			
			//Now draw all the children
			for (var i = 0; i < aNodeObj.child.length; i++) {
				this.addNode(this.tree.nodes[aNodeObj.child[i]], false, (i == aNodeObj.child.length - 1), aRecursedList)
			}
			
			//Close div layer
			document.write("</div>");
		}
	}
	
	this.oc = function (node, bottom, aOnlyOpen) {
		var theDiv = (document.getElementById) ? document.getElementById(this.myId + "div" + node) : document.all[this.myId + "div" + node]
		var theJoin	= (document.getElementById) ? document.getElementById(this.myId + "join" + node) : document.all[this.myId + "join" + node]
		var theIcon = (document.getElementById) ? document.getElementById(this.myId + "icon" + node) : document.all[this.myId + "icon" + node]
		
		//Did we find an object
		if (!((theDiv == null) || (typeof(theDiv) != "object"))) {
			//What exactly are we doing
			if (theDiv.style.display == 'none') {
				//Opening
				if (bottom == 1) {
					theJoin.src = this.getIconPath(this.icons["closedbottom"]);
				} else {
					theJoin.src = this.getIconPath(this.icons["closed"]);
				}
				//Spacer?
				if (this.useSpacers) {
					var theSpacer = (document.getElementById) ? document.getElementById(this.myId + "spacer" + node) : document.all[this.myId + "spacer" + node]
					if (theSpacer != 'null' && theSpacer != null) {
						theSpacer.src = this.getIconPath(this.icons["spaceropen"]);
					}
				}
				if (theIcon != 'null' && theIcon != null) {
					theIcon.src = this.getIconPath(this.icons["joined"]);
				}
				theDiv.style.display = '';
			} else if (typeof(aOnlyOpen) == "undefined" || !aOnlyOpen) {
				//Closing, and not only opening
				if (bottom == 1) {
					theJoin.src = this.getIconPath(this.icons["openbottom"]);
				} else {
					theJoin.src = this.getIconPath(this.icons["open"]);
				}
				//Spacer?
				if (this.useSpacers) {
					var theSpacer = (document.getElementById) ? document.getElementById(this.myId + "spacer" + node) : document.all[this.myId + "spacer" + node]
					if (theSpacer != 'null' && theSpacer != null) {
						theSpacer.src = this.getIconPath(this.icons["spacerclosed"]);
					}
				}
				if (theIcon != 'null' && theIcon != null) {
					theIcon.src = this.getIconPath(this.icons["empty"]);
				}
				theDiv.style.display = 'none';
			}
		} else {
			//alert("theDiv:" + theDiv + ":" + typeof(theDiv) + "\n" + "theJoin:" + theJoin + ":" + typeof(theJoin) + "\n" + "theIcon:" + theIcon + ":" + typeof(theIcon) + "\n")
		}
	}
	
	//Method for opening all the nodes in the tree
	this.openTree = function(aOpen) {
		if (aOpen) {
			for (var i = 0; i < this.tree.parent.length; i++) {
				this.recurseOpen(this.tree.nodes[this.tree.parent[i]])
			}
		} else {
			for (var i = 0; i < this.tree.parent.length; i++) {
				this.recurseClose(this.tree.nodes[this.tree.parent[i]])
			}
		}
	}
	
	this.recurseOpen = function(aNodeObj) {
		//Open the node
		var theDiv = (document.getElementById) ? document.getElementById(this.myId + "div" + aNodeObj.id) : document.all[this.myId + "div" + aNodeObj.id]
		if (typeof(theDiv) == "object" && theDiv != null) {
			if (theDiv.style.display == 'none') {
				var theJoin	= (document.getElementById) ? document.getElementById(this.myId + "join" + aNodeObj.id) : document.all[this.myId + "join" + aNodeObj.id]
				var theIcon = (document.getElementById) ? document.getElementById(this.myId + "icon" + aNodeObj.id) : document.all[this.myId + "icon" + aNodeObj.id]
				if (aNodeObj.child.length > 0) {
					theJoin.src = this.getIconPath(this.icons["closedbottom"]);
				} else {
					theJoin.src = this.getIconPath(this.icons["closed"]);
				}
				//Spacer?
				if (this.useSpacers) {
					var theSpacer = (document.getElementById) ? document.getElementById(this.myId + "spacer" + aNodeObj.id) : document.all[this.myId + "spacer" + aNodeObj.id]
					if (theSpacer != 'null' && theSpacer != null) {
						theSpacer.src = this.getIconPath(this.icons["spaceropen"]);
					}
				}
				if (theIcon != 'null' && theIcon != null) {
					theIcon.src = this.getIconPath(this.icons["joined"]);
				}
				theDiv.style.display = '';
			}
		}
		//Get the children of this node and recurse
		for (var i = 0; i < aNodeObj.child.length; i++) {
			this.recurseOpen(this.tree.nodes[aNodeObj.child[i]])
		}
	}
	
	this.recurseClose = function(aNodeObj) {
		//Close the node
		var theDiv = (document.getElementById) ? document.getElementById(this.myId + "div" + aNodeObj.id) : document.all[this.myId + "div" + aNodeObj.id]
		if (typeof(theDiv) == "object" && theDiv != null) {
			if (theDiv.style.display != 'none') {
				var theJoin	= (document.getElementById) ? document.getElementById(this.myId + "join" + aNodeObj.id) : document.all[this.myId + "join" + aNodeObj.id]
				var theIcon = (document.getElementById) ? document.getElementById(this.myId + "icon" + aNodeObj.id) : document.all[this.myId + "icon" + aNodeObj.id]
				//Closing, and not only opening
				if (aNodeObj.child.length > 0) {
					theJoin.src = this.getIconPath(this.icons["openbottom"]);
				} else {
					theJoin.src = this.getIconPath(this.icons["open"]);
				}
				//Spacer?
				if (this.useSpacers) {
					var theSpacer = (document.getElementById) ? document.getElementById(this.myId + "spacer" + aNodeObj.id) : document.all[this.myId + "spacer" + aNodeObj.id]
					if (theSpacer != 'null' && theSpacer != null) {
						theSpacer.src = this.getIconPath(this.icons["spacerclosed"]);
					}
				}
				if (theIcon != 'null' && theIcon != null) {
					theIcon.src = this.getIconPath(this.icons["empty"]);
				}
				theDiv.style.display = 'none';
			}
		}
		//Get the children of this node and recurse
		for (var i = 0; i < aNodeObj.child.length; i++) {
			this.recurseClose(this.tree.nodes[aNodeObj.child[i]])
		}
	}
}