/*
TITLE:   Dynamic Site Map Generator
AUTHOR:  Matthew Sant
VERSION: 1.0
FILES:   sitemap.xml, sitemap.js, sample.html, 
         webfolder.gif, webpage.gif, readme.txt

Javascript code for the generation of dynamic site maps.
This code is freely distributable, but the author would 
be happy if you give him credit for his work by not 
deleting this section and if you distributed the code 
as is without modification. The author would be even more 
delighted if you sent a donation of $10 made payable to 
Matthew Sant to the following address if you found the 
code useful:

'Mittenwald', 36,
Sturnell Street,
Kappara 
MALTA SGN02

If you are wondering, Malta is a small island in the 
middle of the Mediterranean sea lying between Sicily and
Libya. The weather is nice all year round, the beaches 
are great, the night life is wild and the people are 
friendly. So the author strongly recommends you go and 
visit it some time :-)
*/

// VARIABLES LOADED FROM SETTINGS FILE
var ColumnCount;
var ColumnWidth;
var TargetNode;
var ConnectionStyle;
var ConnectionColor;
var MajorGroupStyle;
var MinorGroupStyle;
var SiteLinkStyle;
var MajorGroupImage;
var MinorGroupImage;
var SiteLinkImage;
var BackgroundColor;

// GLOBAL VARIABLES
var ParentNode;
var SiteMapNodesHTML;
var innerHTMLString = "";
var SectionIndex;
var SiblingIndex = 0;
var PreviousDepth = 0;
var RunningSiblingIndex = 0;
var ChildCount = 0;

// HTML RENDERING VARIABLES
var SiteLinkSymbol = "<div style='z-index:10;background:url(%SITELINKIMAGE%) no-repeat;position:absolute;overflow:hidden;width:16px;height:18px;top:%TOP%px;left:%LEFT%px'></div>"
var MinorGroupSymbol = "<div style='z-index:10;background:url(%MINORGROUPIMAGE%) no-repeat;position:absolute;overflow:hidden;width:16px;height:18px;top:%TOP%px;left:%LEFT%px'></div>"
var MajorGroupSymbol = "<div style='z-index:10;background:url(%MAJORGROUPIMAGE%) no-repeat;position:absolute;overflow:hidden;width:16px;height:18px;top:%TOP%px;left:%LEFT%px'></div>"
var ParentWrapper = "<div style='position:absolute;overflow:hidden;height:18px;width:%COLUMNWIDTH%px;top:%TOP%px;left:%LEFT%px;%MAJORGROUPSTYLE%'>%TITLE%</div>"
var GroupWrapper = "<div style='position:absolute;overflow:hidden;height:18px;width:%COLUMNWIDTH%px;top:%TOP%px;left:%LEFT%px;%MINORGROUPSTYLE%'>%TITLE%</div>"
var LinkWrapper = "<div style='position:absolute;overflow:hidden;height:18px;width:%COLUMNWIDTH%px;top:%TOP%px;left:%LEFT%px;%SITELINKSTYLE%'>%TITLE%</div>"
var ConnectionLayer = "<div style='z-index:0;position:absolute;overflow:hidden;height:%HEIGHT%px;width:%WIDTH%px;top:%TOP%px;left:%LEFT%px;border-left-style:%CONNECTIONSTYLE%;border-bottom-style:%CONNECTIONSTYLE%;border-width:1px;border-color:%CONNECTIONCOLOR%'></div>"

function getColumnWidth()
{
    return ColumnWidth;
}
// Loads the settings from the XML file and stores them in the allotted variables
function loadSettings()
{
	var Settings = xmlDoc.getElementsByTagName('settings');
	for (i=0;i<Settings[0].childNodes.length;i++)
	{
		if (Settings[0].childNodes[i].nodeType == 1) 
		{			
			switch (Settings[0].childNodes[i].nodeName)
			{
				case('columnCount') :
				{
					ColumnCount = parseInt(Settings[0].childNodes[i].firstChild.nodeValue);break;
				}
				case('columnWidth') :
				{
					ColumnWidth = parseInt(Settings[0].childNodes[i].firstChild.nodeValue);break;
				}
				case('targetLayerId') :
				{
					TargetNodeId = Settings[0].childNodes[i].firstChild.nodeValue;
					TargetNode = document.getElementById(TargetNodeId);					
					break;
				}				
				case('connectionStyle') :
				{
					ConnectionStyle = Settings[0].childNodes[i].firstChild.nodeValue;					
					break;
				}	
				case('connectionColor') :
				{
					ConnectionColor = Settings[0].childNodes[i].firstChild.nodeValue;					
					break;
				}	
				case('majorGroupStyle') :
				{
					MajorGroupStyle = Settings[0].childNodes[i].firstChild.nodeValue;					
					break;
				}	
				case('minorGroupStyle') :
				{
					MinorGroupStyle = Settings[0].childNodes[i].firstChild.nodeValue;					
					break;
				}	
				case('siteLinkStyle') :
				{
					SiteLinkStyle = Settings[0].childNodes[i].firstChild.nodeValue;					
					break;
				}	
				case('majorGroupImage') :
				{
					MajorGroupImage = Settings[0].childNodes[i].firstChild.nodeValue;					
					break;
				}	
				case('minorGroupImage') :
				{
					MinorGroupImage = Settings[0].childNodes[i].firstChild.nodeValue;					
					break;
				}	
				case('siteLinkImage') :
				{
					SiteLinkImage = Settings[0].childNodes[i].firstChild.nodeValue;					
					break;
				}	
				case('backgroundColor') :
				{
					BackgroundColor = Settings[0].childNodes[i].firstChild.nodeValue;					
					break;
				}	
			}			
		}
	}
} 

// Main function that loads site map
function loadSiteMap()
{
	// Read XML file for Mozilla clients
	if (document.implementation && document.implementation.createDocument)
	{
		xmlDoc = document.implementation.createDocument("","",null);
		xmlDoc.onload=drawMap;
	}
	// Read XML file for Internet Explorer clients
	else if (window.ActiveXObject)
	{
		xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
		xmlDoc.onreadystatechange = function()
		{
			if (xmlDoc.readyState == 4) drawMap()
		};
	}
	else
	{
		//Browser does not support XML handling
		return;
	}
	xmlDoc.load("sitemap.xml");
}

// Function to obtain a node's depth within the site tree
function getDepth(Node)
{
	Depth = 0;
	CurrentNode = Node;
	while (CurrentNode != ParentNode)
	{
		CurrentNode = CurrentNode.parentNode;
		Depth++;
	}
	return Depth;
}

// Function to obtain a node's visual depth within the site tree
function getExpandedPosition(Node)
{
	if (Node.nodeType == 1) 
	{
  	var PrecedingSiblings = 0;
  	var CurrentNodeParent = Node.parentNode;  	
  	for (m=0;m<CurrentNodeParent.childNodes.length;m++)
  	{				  	  		
  		if (CurrentNodeParent.childNodes[m].nodeType == 1)
  		{    			
  			if (CurrentNodeParent.childNodes[m] == Node)
  				break;
  			ChildCount = 0;
  			
  			if (CurrentNodeParent.childNodes[m].hasChildNodes())
  				getTotalChildCount(CurrentNodeParent.childNodes[m]);
  			
  			PrecedingSiblings+=ChildCount + 1;
  		}
  	}
	
		return PrecedingSiblings;
	}
	else
		return 0;
}

// Gets the number of children a node has recursing children-bearing child nodes
function getTotalChildCount(Node)
{		
	if (Node.hasChildNodes() == false)
	  return;	
	if (Node.nodeType == 1) 
	{
  	if (Node.hasChildNodes() && Node.getAttribute('title') != null) 
  	{			  	
  		ChildCount+=getProperNodeCount(Node);
  		for (j=0;j<Node.childNodes.length;j++)
  		{
  			if (Node.childNodes[j].nodeType == 1)
  			{			  				
  				if (Node.childNodes[j].hasChildNodes() && Node.childNodes[j].getAttribute('title') != null)
  					getTotalChildCount(Node.childNodes[j]);
  			}
  		}
  	}	
	}
	else 
		return;
}

// Gets the number of child nodes that are of type element
function getProperNodeCount(Node)
{
	var ProperNodeCount = 0;
	for (i=0;i<Node.childNodes.length;i++)
  {
  	if (Node.childNodes[i].nodeType == 1)
  	  ProperNodeCount++;
  }
  return ProperNodeCount;
}

// Gets the total amount of children for a group
function getGroupChildCounts(Node)
{	
	var ChildCounts = new Array(getProperNodeCount(Node));	
	var ProperNodeIndex = 0;
	for (k=0;k<Node.childNodes.length;k++)
	{
		if (Node.childNodes[k].nodeType == 1) 
		{
  		if (Node.childNodes[k].getAttribute('title') != null) 
  		{						
  			ChildCount = 0;
  			getTotalChildCount(Node.childNodes[k]);
  			ChildCounts[ProperNodeIndex] = ChildCount;					
  			ProperNodeIndex++;
  		}
		}
	}
	return ChildCounts;
}

// Renders the HTML for the site map and stores the major group nodes in an array
function drawSiteMapNode(CurrentTreeNode, CurrentDisplayNode)
{	
	if (CurrentTreeNode.parentNode == ParentNode)
		SiblingIndex = 0;
	else
		SiblingIndex = getExpandedPosition(CurrentTreeNode);

	if (CurrentTreeNode != ParentNode) 
	{		
		var TopOffset	= ((1 + SiblingIndex) * 18);
		var ImageTopOffset	= 0;
		var LeftOffset = 16;
		var ImageLeftOffset = 0;
		var LinkTopOffset = 2;
		var LinkLeftOffset = 20;
		var CurrentSymbol;
		var CurrentLinkWrapper;
		
		if (CurrentTreeNode.parentNode != ParentNode)
		{
		    var LinkTop = (((CurrentTreeNode.parentNode.parentNode != ParentNode) ? (getExpandedPosition(CurrentTreeNode.parentNode) + 0) : 1) * 18) - 8;
		    var LinkLeft = (((CurrentTreeNode.parentNode.parentNode != ParentNode) ? (getDepth(CurrentTreeNode) - 3) : 0) * 16) + 8;
		    var LinkWidth = 16;
		    var LinkHeight = (getExpandedPosition(CurrentTreeNode) + 1) * 18;
		    innerHTMLString += ConnectionLayer.replace("%TOP%",LinkTop).replace("%LEFT%",LinkLeft).replace("%WIDTH%",LinkWidth).replace("%HEIGHT%",LinkHeight).replace("%CONNECTIONSTYLE%",ConnectionStyle).replace("%CONNECTIONSTYLE%",ConnectionStyle).replace("%CONNECTIONCOLOR%",ConnectionColor);
		}
		
		if (CurrentTreeNode.parentNode == ParentNode) 
		{
			CurrentSymbol = MajorGroupSymbol.replace('%TOP%', ImageTopOffset).replace('%LEFT%',ImageLeftOffset).replace("%MAJORGROUPIMAGE%", MajorGroupImage);
			if (CurrentTreeNode.getAttribute('url') == null)
				CurrentLinkWrapper = ParentWrapper.replace('%TOP%', LinkTopOffset).replace('%LEFT%', LinkLeftOffset).replace('%TITLE%', CurrentTreeNode.getAttribute('title')).replace("%MAJORGROUPSTYLE%",MajorGroupStyle).replace("%COLUMNWIDTH%",(ColumnWidth - 20));
			else
				CurrentLinkWrapper = ParentWrapper.replace('%TOP%', LinkTopOffset).replace('%LEFT%', LinkLeftOffset).replace('%TITLE%', "<a href=\"" + CurrentTreeNode.getAttribute('url') + "\" style=\"" + MajorGroupStyle + "\">" + CurrentTreeNode.getAttribute('title') + "</a>").replace("%MAJORGROUPSTYLE%",MajorGroupStyle).replace("%COLUMNWIDTH%",(ColumnWidth - 20));
		}
		else if (CurrentTreeNode.hasChildNodes() == false)
		{
			CurrentSymbol = SiteLinkSymbol.replace('%TOP%', ImageTopOffset).replace('%LEFT%',ImageLeftOffset).replace("%SITELINKIMAGE%", SiteLinkImage);
			if (CurrentTreeNode.getAttribute('url') == null)
				CurrentLinkWrapper = LinkWrapper.replace('%TOP%', LinkTopOffset).replace('%LEFT%', LinkLeftOffset).replace('%TITLE%', CurrentTreeNode.getAttribute('title')).replace("%SITELINKSTYLE%",SiteLinkStyle).replace("%COLUMNWIDTH%",(ColumnWidth - 20));
			else
				CurrentLinkWrapper = LinkWrapper.replace('%TOP%', LinkTopOffset).replace('%LEFT%', LinkLeftOffset).replace('%TITLE%', "<a href=\"" + CurrentTreeNode.getAttribute('url') + "\" style=\"" + SiteLinkStyle + "\">" + CurrentTreeNode.getAttribute('title') + "</a>").replace("%SITELINKSTYLE%",SiteLinkStyle).replace("%COLUMNWIDTH%",(ColumnWidth - 20));
		}
		else
		{
			CurrentSymbol = MinorGroupSymbol.replace('%TOP%', ImageTopOffset).replace('%LEFT%',ImageLeftOffset).replace("%MINORGROUPIMAGE%", MinorGroupImage);		
			if (CurrentTreeNode.getAttribute('url') == null)
				CurrentLinkWrapper = GroupWrapper.replace('%TOP%', LinkTopOffset).replace('%LEFT%', LinkLeftOffset).replace('%TITLE%', CurrentTreeNode.getAttribute('title')).replace("%MINORGROUPSTYLE%",MinorGroupStyle).replace("%COLUMNWIDTH%",(ColumnWidth - 20)).replace("%COLUMNWIDTH%",(ColumnWidth - 20));
			else
				CurrentLinkWrapper = GroupWrapper.replace('%TOP%', LinkTopOffset).replace('%LEFT%', LinkLeftOffset).replace('%TITLE%', "<a href=\"" + CurrentTreeNode.getAttribute('url') + "\" style=\"" + MinorGroupStyle + "\">" + CurrentTreeNode.getAttribute('title') + "</a>").replace("%MINORGROUPSTYLE%",MinorGroupStyle);
		}
		 
		if (CurrentTreeNode.parentNode == ParentNode)
			innerHTMLString += "<div" + " style='font-family:arial;font-size:11px;position:absolute;display:inline;height:20px;top:" + 
			"%TOP%" + "px" + ";left:" + "%LEFT%" + "px'>" + CurrentSymbol + CurrentLinkWrapper;
		else
			innerHTMLString += "<div" + " style='font-family:arial;font-size:11px;position:absolute;display:inline;height:20px;top:" + 
			TopOffset + "px" + ";left:" + LeftOffset + "px'>" + CurrentSymbol + CurrentLinkWrapper;										
			
		SiblingIndex++;
		RunningSiblingIndex++;
	}
	
	if (CurrentTreeNode.hasChildNodes()) 
	{
  	var i = 0;
  	for (i=0;i<CurrentTreeNode.childNodes.length;i++)
  	{
  		if (CurrentTreeNode.childNodes[i].nodeType == 1) 
  		drawSiteMapNode(CurrentTreeNode.childNodes[i], CurrentDisplayNode);
  	}
	}
	
	if (CurrentTreeNode != ParentNode)
		innerHTMLString += "</div>";
	if (CurrentTreeNode.parentNode == ParentNode) 
	{
		SiteMapNodesHTML[SectionIndex] = innerHTMLString;
		innerHTMLString="";
		SectionIndex++;
		SiblingIndex = 0;
		RunningSiblingIndex = 0;
	}	  
}

// Get Max Column Height
function getMaxColumnHeight(Columns)
{
	if (ColumnCount == 1)
		return Columns[0];
	else
	{
		var MaxColumnHeight = 0;
		for (colIndex = 0; colIndex < Columns.length; colIndex++)
		{
			if (Columns[colIndex] > MaxColumnHeight)
				MaxColumnHeight = Columns[colIndex];
		}
		return MaxColumnHeight;
		
	}	
}

// Centers the site map on the page
function CenterSiteMap() 
{
    SiteMapLeft = (document.body.clientWidth - (ColumnWidth * ColumnCount)) / 2;            
    document.getElementById("SiteMap").style.left = SiteMapLeft + "px";
    document.getElementById("SiteMap").style.top = "50px";
}

// Renders the site map
function drawMap()
{
	// Load Settings
	loadSettings();			
	// Set Target Layer Style
	TargetNode.style.position = 'relative';		
	TargetNode.style.textAlign = 'left';
	TargetNode.style.backgroundColor = BackgroundColor;
	TargetNode.style.width = (ColumnWidth * ColumnCount) + 'px';	
	//TargetNode.style.height = '300px';
  var SiteMapNodes = xmlDoc.getElementsByTagName('siteMapNode'); 
  ParentNode = SiteMapNodes[0]; 
  var ValidNodesCount = 0; 
  for (i=0;i<ParentNode.childNodes.length;i++)
  {
  	if (ParentNode.childNodes[i].nodeType == 1)
  	  ValidNodesCount++;
  }
  
  
  SiteMapNodesHTML = new Array(ValidNodesCount);
  SectionIndex = 0;
  drawSiteMapNode(SiteMapNodes[0], TargetNode);  
  var ItemsPerColumn = 6;//Math.floor(ValidNodesCount / ColumnCount);    
  var ExtraItems = ValidNodesCount % ColumnCount; 

  
  var DIVBody = "";
  var NextColumn = false;
  var ColShift = 0;
  var TopOffset = 4;
  var LeftOffset = 4;  
  var GroupChildCounts = getGroupChildCounts(ParentNode); 
  var ColumnHeights = new Array(ColumnCount);
  var ColumnIndex = 0;
  var ColumnHeightSum = 0;  
  for (i=0;i<SiteMapNodesHTML.length;i++)
  {   	 	
  	if (NextColumn)
  	{
  		LeftOffset += ColumnWidth;
  		TopOffset = 4;
  		ColumnHeights[ColumnIndex] = ColumnHeightSum + 4;  	
  		ColumnIndex++;	
  		ColumnHeightSum = 0;
  		NextColumn = false;  		
  	}  	
  	else if (i>0)
  	{  		  		  	
  		TopOffset += 20 + ((GroupChildCounts[i-1] + 1) * 18);
  	}  	
  	DIVBody += SiteMapNodesHTML[i].replace("%LEFT%",LeftOffset).replace("%TOP%",TopOffset);
  	ColumnHeightSum += (((NextColumn) || (i==0)) ? 4 : 20) + ((GroupChildCounts[i] + 1) * 18); 
  	
  	//if (((i+1+ColShift) % ItemsPerColumn == 0) && (ExtraItems == 0))
  	if (((ColumnIndex == 0) && (i >= 6))||((ColumnIndex == 1) && (i >= 11)))
  		NextColumn = true;
  		
  	else if (ExtraItems > 0)
  	{
  		if ((i+1) % (ItemsPerColumn + 1) == 0)
  		
  		{
  			NextColumn = true;
  			ColShift++;
  			ExtraItems--;
  		}
  	}  	  	
  } 
  ColumnHeights[ColumnIndex] = ColumnHeightSum;  	
  var sColumnHeights = ""; 
  for (i=0;i<ColumnHeights.length;i++)
  {
  	sColumnHeights += ColumnHeights[i] + ',';
  }
  TargetNode.style.height = getMaxColumnHeight(ColumnHeights) + 'px';  
  TargetNode.innerHTML = DIVBody;    
}
