/////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2007, 2008, Oracle. All rights reserved.
// Function : SiteMapTree
// Comments : 
/////////////////////////////////////////////////////////////////////////////

function SiteMapTree (strInstanceName, strCgiPath, strAssetsPath, strTheme, strShowHome)
{
	this.m_InstanceName = strInstanceName;
	this.m_CgiPath		= strCgiPath;
	this.m_Theme		= 'explorer1';
	this.m_ShowHome	    = false;
	
	if (strShowHome != null && strShowHome == 'true')
		this.m_ShowHome = true;
	
	this.m_AssetsPath	= strAssetsPath;

	if (strTheme != null && strTheme != '')
		this.m_Theme = strTheme;
	
	if (this.m_Theme != '')
		 this.m_AssetsPath += this.m_Theme + '/';

	this.m_navItemTable = 'SiteMapTree_' + this.m_Theme + '_navItemTable';
	this.m_navItemText  = 'SiteMapTree_' + this.m_Theme + '_navItemText';
	this.m_navItemLink  = 'SiteMapTree_' + this.m_Theme + '_navItemLink';

	this.m_NavPath      = g_navNode_Path;

	/* Config variable: changing to the name of a different directory will change which
		icon images are displayed in the tree. Current options are bookshelf, explorer1, and explorer2 */

	/* Declare variables containing src values for icon images. */
	
	this.src_I = this.m_AssetsPath + "tree_I.gif";
	this.src_Icon_ColClosed = this.m_AssetsPath + "tree_icon_collection_closed.gif";
	this.src_Icon_ColOpen = this.m_AssetsPath + "tree_icon_collection_open.gif";
	this.src_Icon_Item = this.m_AssetsPath + "tree_icon_item.gif";
	this.src_L_ColClosed = this.m_AssetsPath + "tree_L_collection_closed.gif";
	this.src_L_ColOpen = this.m_AssetsPath + "tree_L_collection_open.gif";
	this.src_T_ColClosed = this.m_AssetsPath + "tree_T_collection_closed.gif";
	this.src_T_ColOpen = this.m_AssetsPath + "tree_T_collection_open.gif";
	this.src_L_Item = this.m_AssetsPath + "tree_L_item.gif";	
	this.src_T_Item = this.m_AssetsPath + "tree_T_item.gif";
	this.src_F_Item = this.m_AssetsPath + "tree_F_item.gif";
	this.src_O_Item = this.m_AssetsPath + "tree_O_item.gif";
	this.src_Space = this.m_AssetsPath + "tree_space.gif";
	this.src_Top_ColClosed = this.m_AssetsPath + "tree_top_collection_closed.gif";
	this.src_Top_ColOpen = this.m_AssetsPath + "tree_top_collection_open.gif";
	this.src_Only_ColClosed = this.m_AssetsPath + "tree_only_collection_closed.gif";
	this.src_Only_ColOpen = this.m_AssetsPath + "tree_only_collection_open.gif";

	/* Declare variables containing height and width values for icon images. */
	
	this.iconWidth_Col = "16";
	this.iconHeight_Col = "16";
	this.iconWidth_Item = "16";
	this.iconHeight_Item = "16";
	this.connectorWidth = "12";
	this.connectorHeight = "20";

	this.m_ds = new Array();
	this.m_di = 0;

	SiteMapTree.prototype.Display = SiteMapTree_Display;
	SiteMapTree.prototype.DisplayNode = SiteMapTree_DisplayNode;
	
	SiteMapTree.prototype.Display = SiteMapTree_Display;

	SiteMapTree.prototype.addHtmlForChild = SiteMapTree_addHtmlForChild;
	SiteMapTree.prototype.hasNextSiblingElement = SiteMapTree_hasNextSiblingElement;
	SiteMapTree.prototype.hasPreviousSiblingElement = SiteMapTree_hasPreviousSiblingElement;
	SiteMapTree.prototype.imageHtml = SiteMapTree_imageHtml;
	SiteMapTree.prototype.toggleDisplay = SiteMapTree_toggleDisplay;
}

// ========================= document generation code ====================

function SiteMapTree_Display (node)
{
	/* Call the function that builds the navigation code. Note: this function contains
		a recursive looping mechanism that causes it to drill down through all the clildren
		beneath the passed-in element; this process builds the html code by appending markup
		for each level to the m_ds[m_di++] variable. */

	this.DisplayNode(node);

//	alert(this.m_ds.join(''));

	document.write(this.m_ds.join(''));	
}

function SiteMapTree_DisplayNode(parentElement)
{
	/* Create a variable containing all children of the passed-in element. */
	var childList = parentElement.m_subNodes;
	
	/* Generate the opening <div> tag for the current collection. As it's currently designed,
		all the collections are closed (e.g. only the top level collections are displayed initially. */	

	var bExpand = false;
			
	if (this.m_NavPath.length > 0 && parentElement.m_level < this.m_NavPath.length)
	{
		if (this.m_NavPath[parentElement.m_level] == parentElement.m_id)
		{
			bExpand = true;
		}
	}
				
	if (bExpand || parentElement.m_level == 0)
	{
		this.m_ds[this.m_di++] = '<div style="display:block">\n';
	}
	else
	{
		this.m_ds[this.m_di++] = '<div style="display:none">\n';
	}
	
	if (parentElement.m_level == 0 && this.m_ShowHome)
		this.addHtmlForChild(parentElement);
			
	/* Loop through the children of the passed-in element. For each child generate the html code
		for that child, and if that child has children of its own, recursively call the function.
		Note: since the standards-based browsers don't ignore whitespace the nodeType value of 
		each node must be checked. */
		
	for (var i=0; i<childList.length; i++)
	{
		var childNode = childList[i];
		
		this.addHtmlForChild(childNode);
		
		if (childNode.m_subNodes.length > 0)
		{
			this.DisplayNode(childNode);
		}
	}
	
	/* Generate closing <div> tag code for current element. */
	this.m_ds[this.m_di++] = '</div>\n';	
}

/* This function is called by the createChildrenForCollection function above. It
	generates the html table code for a single display item on the nav tree. */
function SiteMapTree_addHtmlForChild(childElement)
{
	/* Declare string variables for markup. */
	var iconImageHtml = "";
	var connectorImageHtml = "";
	var offsetImagesHtml = "";		
	var imageCellWidth = "";
	var labelTextHtml = "";		
	
	/* This large block of nested conditionals determines the type of node and sets
		the properties of the icon images accordingly (using global configuration variables
		declared at the top of the page). */
		
	if (childElement.m_subNodes.length == 0 || childElement.m_level == 0)
	{	// "item"
		var iconSrc = this.src_Icon_Item;
		var iconWidth = this.iconWidth_Item;
		var iconHeight = this.iconHeight_Item;		

		if (childElement.m_level <= 1 && !this.hasNextSiblingElement(childElement) && !this.hasPreviousSiblingElement(childElement))
		{
			var connectorSrc = this.src_O_Item;
		}
		if (childElement.m_level <= 1 && !this.hasPreviousSiblingElement(childElement))
		{
			var connectorSrc = this.src_F_Item;
		}
		else
		if (this.hasNextSiblingElement(childElement))
		{
			var connectorSrc = this.src_T_Item;			
		}
		else
		{
			var connectorSrc = this.src_L_Item;				
		}			
	}
	else
	{	// "collection"
		var iconSrc = this.src_Icon_ColClosed;
		var iconWidth = this.iconWidth_Col;
		var iconHeight = this.iconHeight_Col;
	
		if (/*childElement.m_level <= 1 &&*/ !this.hasNextSiblingElement(childElement) && !this.hasPreviousSiblingElement(childElement))
		{
			var connectorSrc = this.src_Only_ColClosed;
		}
		else
		if (/*childElement.m_parent.m_level <=1 &&*/ !this.hasPreviousSiblingElement(childElement))
		{
			var connectorSrc = this.src_Top_ColClosed;
		}			
		else if (this.hasNextSiblingElement(childElement))
		{
			var connectorSrc = this.src_T_ColClosed;				
		}
		else
		{
			var connectorSrc = this.src_L_ColClosed;								
		}							
			
		if (this.m_NavPath.length > 0 && childElement.m_level < this.m_NavPath.length)
		{
			if (this.m_NavPath[childElement.m_level] == childElement.m_id)
			{
				// prepare for this node being expanded, not closed...
				iconSrc = iconSrc.replace(/closed\.gif/, "open.gif");
				connectorSrc = connectorSrc.replace(/closed\.gif/, "open.gif");
			}
		}
	}
	
	/* Generate the html code for the icon and node-connection point graphics using image
		property values determined in the block above. */
	iconImageHtml = this.imageHtml(iconSrc, iconWidth, iconHeight);
	connectorImageHtml = this.imageHtml(connectorSrc, this.connectorWidth, this.connectorHeight);
	
	/* Loop upward throught the ancestors of the current element to generate html code for 
		the offset image graphics (either dotted lines or spaces in current implementation).
		Also keep a count of the number of levels to use in calculating the width of the table
		cell that will contain the images. */
	var parent = childElement.m_parent;
	var offsetCount = 0;
	while (parent != null && parent.m_level > 0)
	{
		if (this.hasNextSiblingElement(parent))
		{
			offsetImagesHtml = this.imageHtml(this.src_I, this.connectorWidth, this.connectorHeight) + offsetImagesHtml;				
		}
		else
		{
			offsetImagesHtml = this.imageHtml(this.src_Space, this.connectorWidth, this.connectorHeight) + offsetImagesHtml;			
		}
		offsetCount++;
		parent = parent.m_parent;
	}
	
	/* Calculate the width of the table cell containing the images. First the total width of all offset
		images is calculated, then the widths of the node point connector and the icon are added. */		
	imageCellWidth = (offsetCount * this.connectorWidth) + parseInt(this.connectorWidth) + parseInt(iconWidth);
	
	/* Generate html code for the node point connector and icon graphics and associated links.
		In the current implementation, the icon for an "item" will be hot and call the url value from the 
		item's xml data; a "collection" will have both the node point connector and icon contained in a common
		<span> tag with an onclick event that calls the function to open the collection folder. */
	if (childElement.m_subNodes.length == 0 || childElement.m_level == 0)
	{
		var nodeImagesHtml = connectorImageHtml + '<a href="' + childElement.m_href + '">' + iconImageHtml + '</a>';
	}
	else
	{
		var nodeImagesHtml = '<span onclick="' + this.m_InstanceName + '.toggleDisplay(this)">' + connectorImageHtml +iconImageHtml + '</span>';
	}
	
	/* Generate the html code for the label text. A link will be included if the xml data contains 
		a value for the "url" atrribute. */
	if (childElement.m_href != null && childElement.m_href != "")
	{
		labelTextHtml = '<a href="' + childElement.m_href + '" class="' + this.m_navItemLink + '">' + childElement.m_label + '</a>';
	}
	else
	{
		labelTextHtml = childElement.m_label;
	}				
	
	/* Append the final html table code for the item to the global html string variable. */
	this.m_ds[this.m_di++] =
		'<table class="' + this.m_navItemTable + '" border="0" cellspacing="0" cellpadding="0">\n' +
		'	<tr>\n' +
		'		<td style="width:' + imageCellWidth.toString() + 'px; text-align:left">' + offsetImagesHtml + nodeImagesHtml + '</td>\n' +
		'		<td class="' + this.m_navItemText + '">' + labelTextHtml + '</td>\n' +
		'	</tr>\n' +
		'</table>\n';				
}

/* Standard function for generating html image tag code. */
function SiteMapTree_imageHtml(src, w, h)
{
	var imgHtml = '<img src="' + src + '" width="' + w + '" height="' + h + '" border="0" align="absmiddle">';
	return imgHtml
}

/* This function had to be used to find the next element-type node in a collection of children 
	since the standards-based browsers interpret whitespace as additional text nodes in the hierarchy. */
function SiteMapTree_hasNextSiblingElement(element)
{
	var parent = element.m_parent;		

	if (parent == null)
	{
		if (this.m_ShowHome && element.m_subNodes.length)
			return true;
			
		return false;
	}
	
	var len = parent.m_subNodes.length;
	
	if (parent.m_subNodes[len-1] == element)
		return false;
		
	return true;
}

/* This function had to be used to find the previous element-type node in a collection of children 
	since the standards-based browsers interpret whitespace as additional text nodes in the hierarchy. */	
function SiteMapTree_hasPreviousSiblingElement(element)
{
	var parent = element.m_parent;		
	
	if (parent == null)
		return false;
		
	if (parent.m_subNodes[0] == element)
	{
		if (parent.m_level == 0 && this.m_ShowHome)
			return true;
			
		return false;
	}	
	return true;
}	

// ========================= runtime support code ====================

/* Funtion that hides or displays the children of a collection on the tree. */
function SiteMapTree_toggleDisplay(linkObject)
{
	/* Declare variables containing the two images that need to be swapped
		ASSUMPTION: The first (and usually only) two children of the linkObject container
		will be the tree connector and the collection/folder icon respectively. */
	var treeConnectorImage = linkObject.childNodes[0];		
	var treeIconImage = linkObject.childNodes[1];		
	
	/* Iterate through ancestors of link object until the parent TABLE object is found*/
	var parentTable = linkObject.parentNode;
	while (parentTable.tagName != "TABLE")
	{
		parentTable = parentTable.parentNode;
	}
	
	/* Iterate through the siblings of the parent TABLE until the associated DIV container is found.
		ASSUMPTION: associated DIV container will be the only sibling of the TABLE object that
		has a nodeType of DIV. */
	var divToToggle = parentTable.nextSibling;
	while (divToToggle.tagName != "DIV")
	{
		divToToggle = divToToggle.nextSibling;
	}						
	
	/* Toggle the display status of the associated DIV container accordingly. */
	if (divToToggle.style.display == "none")
	{
		/* If child DIV container has no contents, don't display it (I don't know if we'll
			be including empty collections/folders, but I want to keep the option open). */
		if (divToToggle.hasChildNodes())
		{
			divToToggle.style.display = "block";
		}
	}
	else
	{
		divToToggle.style.display = "none";
	}
	
	/* Swap the tree graphics to indicate change of display state.
		ASSUMPTION: The file names of the connector and icon graphics will end with either
		"open" or "closed". */
	if (treeConnectorImage.src.indexOf("closed.gif") > -1)
	{
		treeConnectorImage.src = treeConnectorImage.src.replace(/closed\.gif/, "open.gif");
		treeIconImage.src = treeIconImage.src.replace(/closed\.gif/, "open.gif");
	}
	else
	{
		treeConnectorImage.src = treeConnectorImage.src.replace(/open\.gif/, "closed.gif");
		treeIconImage.src = treeIconImage.src.replace(/open\.gif/, "closed.gif");
	}				
}		

// ======================= the end =====================


