Generating lists from XML file with javascript and HTTPRequest

See Just the result

The php/html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<script type="text/javascript" language="javascript1.4" src="menus.js"></script>
	<link rel="stylesheet" type="text/css" href="menus.css" />
	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
	<title>XML Sample</title>
</head>

<body onload="init()">
	<div id="navigation"></div>
	<div id="header"></div>
	<div id="content"></div>
</body>
</html>

The Javascript:

function init()
{
	var xmlMenu = new XmlMenu();
}
function XmlMenu()
{
//User Editable Variables
	var menuDiv   = "navigation";
	var headerDiv = "header";
	var mainDiv   = "content";
	var useHTTPRequest = true;				//Load new page or Load into the content div?
	var rememberSubmenuLocation = false;//This is broken for the moment.
	var xmlFile = 'menus.xml';//"./include_js/menus.xml"; // path/to/xml file of Menu content this is relative to the html/php file calling it
	
	var menu_div = document.getElementById(menuDiv);
	var header   = document.getElementById(headerDiv);
	var main_div = document.getElementById(mainDiv);
	var rootMenu = null;

	xmlRequest(xmlFile, onLoad);
	function onLoad(resultXML)
	{
		resultXML.setAttribute('title', 'ROOT');
		rootMenu = new Menu(resultXML, null, 0, 0);
		loadMenuItem(rootMenu);	
		openMenuPath(useHTTPRequest);
	}

	function loadMenuItem(menuItem)
	{
		var parentUL = (menuItem.parent != null) ? menuItem.parent.getULcontainer() : null;
		while(menu_div.lastChild != parentUL)
			menu_div.removeChild(menu_div.lastChild);
	
		menu_div.appendChild(menuItem.getULcontainer());
		menuItem.clearCurrent();
	
		if (rememberSubmenuLocation) 
			menuItem.loadSubMenu();
	}
	function loadPage(url)
	{
	// the next line would make the pages reload and change openMenuPath(true); in loadMenus function to openMenuPath(false);
		if (useHTTPRequest) 
		{
			main_div.innerHTML = '';
			if(url != null)
			{
				var loadingMessage = new LoadingMessage('Loading '+url);
				main_div.appendChild(loadingMessage.getDOM());
				function onLoad(htmlStr) { main_div.innerHTML = htmlStr; }; 
				HttpRequest(url, '', onLoad);
			}
		} 
	}
	function openMenuPath(clickLast)
	{
		var hash = window.location.hash || '#menu=0';
		var start = hash.indexOf('menu=', 0);
		if(start > -1)
		{
			start += 5;
			var end   = hash.indexOf('&', start);
			var bob   = (end > -1) ? hash.substr(start, end - start) : hash.substr(start);
			var menus = explode(bob, ',');
			
			var parent = rootMenu;
			for(var n = 0; n < menus.length; n++)
			{
				var next = parent.getSubMenu(menus[n]);
				if(n + 1 < menus.length || clickLast == false)
				{
					parent.current_li = parent.getUL().childNodes[menus[n]];
					parent.current_li.className = 'current_li';
						
					loadMenuItem(next);
					var parent = next;
				}
				else parent.getUL().childNodes[menus[n]].firstChild.onclick();
			}
		} 
	}

	function Menu(resultXML, parent, number, depth)
	{
		var ref = this;
	//	this.hasChildren = function() { return (subs.length > 0); };
		
		this.parent = parent;
		this.depth = depth;
		this.number = number;
		var title = resultXML.getAttribute('title');
		var url = resultXML.getAttribute('url');
		var subs = new Array();
		
		this.current_li = null;
		
		var counter = 0;
		for(var n = 0; n < resultXML.childNodes.length; n++)
			if(resultXML.childNodes[n].tagName == 'Menu')
				subs.push(new Menu(resultXML.childNodes[n], ref, counter++, depth + 1));
		
		var ul = null;
		var li = null;
		var ul_container = null; //Used to add encapsulate UL's in menu items.
		
		this.loadSubMenu = function()
		{
			if(ref.current_li != null)
			{
				var temp = ref.current_li;
				ref.current_li = null;
				temp.firstChild.onclick();
			}
		}
		
		this.clearCurrent = function()
		{
			if(this.current_li != null)
			{
				this.current_li.className = 'menu_li';
				this.current_li = null;
			}
		}
		
		this.getSubMenu = function(index) { return subs[index]; };
		
		this.getUL = function()
		{
			if (ul == null)
				getULcontainer();
			return ul;
		}
		
		this.getULcontainer = function () 
		{
			if(ul == null)
			{
				ul_container = document.createElement('div'); ul_container.className = 'menu_ul_container';
				ul = document.createElement('ul'); ul.className = 'menu_ul';
				ul_container.appendChild(ul);
				for(var n = 0; n < subs.length; n++)
					ul.appendChild(subs[n].getLI());
			}
	
			return ul_container;	
		}
		
		this.getLI = function()
		{
			if(li == null)
			{
				var current = ref;
				var menuPath = current.number;
				while((current = current.parent) && current.parent != null)
					menuPath = current.number+','+menuPath;
					
				var a = document.createElement('a');
				a.className = 'menu_a';
				a.href = (useHTTPRequest) ? '#menu='+menuPath : ((url) ? url+'#menu='+menuPath : 'javascript:;');
				
				a.appendChild(document.createTextNode(title));
				a.onclick = function() 
				{
	//				if(ref.parent.current_li != li)
	//				{
						if(ref.parent.current_li != null)
							ref.parent.current_li.className = 'menu_li';
						
						li.className = 'current_li';
						ref.parent.current_li = li;
						
							
						if(useHTTPRequest)
						{
							loadPage(url);
							loadMenuItem(ref);
						}
						else if(url == null)
							loadMenuItem(ref);
	//					if(url != null)
	//						loadPage(url);
	//				}
				};
	
				li = document.createElement('li'); li.className = 'menu_li'; 
				li.appendChild(a);
				if(subs.length > 0)
				{
					var span = document.createElement('span'); span.className='arrow';
					li.appendChild(span);
				}
			}
			return li;
		}
	}

	function LoadingMessage(message)
	{
		var content_div = document.createElement('div');
		content_div.appendChild(document.createTextNode(message));
		
		var timer = new Timer(500, 30000, onIncrement, onTimeout);
		timer.start();
		var counter = 0;
		
		this.getDOM = function() { return content_div; };
		this.remove = function() { if(content_div.parentNode != null) content_div.parentNode.remove(content_div); };
		
		function onIncrement()
		{
			if(content_div.parentNode == null)
				timer.stop();
			if((++counter % 5) == 0)
				while(content_div.firstChild != content_div.lastChild)
					content_div.removeChild(content_div.lastChild);
			else content_div.appendChild(document.createTextNode('.'));
		}
		function onTimeout()
		{
			alert("The operation has timed out, if this problem persists please report it to an administrator");
		}
	}
	function Timer(increment, lifeTime, onIncrement, onComplete)
	{
		var running = false;
		var elapsed = null;
		var timerID = null;
		
		this.stop = function()
		{
			if(running)
				window.clearTimeout(timerID);
			running = false;
		}
		
		this.start = function()
		{
			elapsed = 0;
			running = true;
			timerID = window.setTimeout(onTimeout, increment);
	
			function onTimeout()
			{
				elapsed += increment;
				if(elapsed < lifeTime)
				{
					if(running)
						timerID = window.setTimeout(onTimeout, increment);
					onIncrement();
				}
				else if(running)
				{
					running = false;
					onComplete();
				}
			}
		}
	}
	// url     : relative or absolute url of the server script or xml file
	// handler : function that will process the XML document, must take the responseXML.documentElement as it's argument
	function HttpRequest(url, data, handler)
	{
		if(window.XMLHttpRequest || window.ActiveXObject)
		{
			var request = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
			request.onreadystatechange = function() { send_onStateChange(request, handler) } ;
			request.open('POST', url, true);
			request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			request.setRequestHeader('Content-Length', data.length);
			request.send(data);
		}
		else alert('XML request not supported, get a new browser!!!');
		
		function send_onStateChange(request, handler)
		{
			if(request.readyState == 4) // 4 means done loading
			{
				if(request.status == 200)
					handler(request.responseText);
				else alert('Load Failed: status: '+request.status+": "+request.statusText);
			}
		}
	}
	
	function xmlRequest(url, handler)
	{
		if(window.XMLHttpRequest || window.ActiveXObject)
		{
			var request = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
			request.onreadystatechange = function() { load_onStateChange(request, handler) } ;
			request.open('GET', url, true);
			request.send(null);
		}
		else alert('XML request not supported, get a new browser!!!');
		
		function load_onStateChange(request, handler)
		{
			if(request.readyState == 4) // 4 means done loading
			{
				if(request.status == 200)
				{
					if(request.responseXML)
					{
						var result = request.responseXML.documentElement;
						if(result.nodeName != 'parsererror')
							handler(result);
						else alert('Parser Error: '+result.textContent);
					}
					else alert('Script Error: '+request.responseText);
				}
				else alert('Load Failed: status: '+request.status+": "+request.statusText);
			}
		}
	}
	
	function explode(str, sep)
	{
		var peices = Array();
		var strlen = str.length;
		var seplen = sep.length;
		var start = 0;
		
		for(var n = 0; n < strlen; n++)
		{
			var match = true;
			var x;
			for(x = 0; x < seplen; x++)
			{
				if(n + x >= strlen || str.charAt(n + x) != sep.charAt(x))
				{
					match = false;
					break;
				}
			}
			
			if(match)
			{
				peices.push(str.substring(start, n));
				start = n + x;
			}
		}
		if(start < strlen)
			peices.push(str.substring(start, strlen));
			
		return peices;
	}
}

The XML file:

<?xml version="1.0" encoding="iso-8859-1"?>

<Root>
    <Menu title="Menu 1">
        <Menu title="sub 1" url="root_pages.php?id=1">
            <Menu title="sub 1" url="pages.php?id=1"/>
            <Menu title="sub 2" url="pages.php?id=2"/>
            <Menu title="sub 3" url="pages.php?id=3"/>
            <Menu title="sub 4" url="pages.php?id=4"/>
            <Menu title="sub 5" url="pages.php?id=5"/>
        </Menu>
        <Menu title="sub 2" url="root_pages.php?id=2">
            <Menu title="sub 6" url="pages.php?id=6"/>
            <Menu title="sub 7" url="pages.php?id=7"/>
            <Menu title="sub 8" url="pages.php?id=8"/>
            <Menu title="sub 9" url="pages.php?id=9"/>
            <Menu title="sub 10" url="pages.php?id=10"/>
        </Menu>
        <Menu title="sub 3" url="root_pages.php?id=3">
            <Menu title="sub 11" url="pages.php?id=11"/>
            <Menu title="sub 12" url="pages.php?id=12"/>
            <Menu title="sub 13" url="pages.php?id=13"/>
            <Menu title="sub 14" url="pages.php?id=14"/>
            <Menu title="sub 15" url="pages.php?id=15"/>
        </Menu>
    </Menu>
    <Menu title="Menu 2">
        <Menu title="sub2 1" url="root_pages.php?id=4">
            <Menu title="sub 16" url="pages.php?id=16"/>
            <Menu title="sub 17" url="pages.php?id=17"/>
            <Menu title="sub 18" url="pages.php?id=18"/>
            <Menu title="sub 18" url="pages.php?id=19"/>
            <Menu title="sub 20" url="pages.php?id=20"/>
        </Menu>
        <Menu title="sub2 2" url="root_pages.php?id=5">
            <Menu title="sub 21" url="pages.php?id=21"/>
            <Menu title="sub 22" url="pages.php?id=22"/>
            <Menu title="sub 23" url="pages.php?id=23"/>
            <Menu title="sub 24" url="pages.php?id=24"/>
            <Menu title="sub 25" url="pages.php?id=25"/>
        </Menu>
        <Menu title="sub2 3" url="root_pages.php?id=6">
            <Menu title="sub 26" url="pages.php?id=26"/>
            <Menu title="sub 27" url="pages.php?id=27"/>
            <Menu title="sub 28" url="pages.php?id=28"/>
            <Menu title="sub 28" url="pages.php?id=29"/>
            <Menu title="sub 30" url="pages.php?id=30"/>
        </Menu>
    </Menu>
    <Menu title="Menu 3">
        <Menu title="sub3 1" url="root_pages.php?id=7">
            <Menu title="sub 31" url="pages.php?id=31"/>
            <Menu title="sub 32" url="pages.php?id=32"/>
            <Menu title="sub 33" url="pages.php?id=33"/>
            <Menu title="sub 34" url="pages.php?id=34"/>
            <Menu title="sub 35" url="pages.php?id=35"/>
        </Menu>
        <Menu title="sub3 2" url="root_pages.php?id=8">
            <Menu title="sub 36" url="pages.php?id=36"/>
            <Menu title="sub 37" url="pages.php?id=37"/>
            <Menu title="sub 38" url="pages.php?id=38"/>
            <Menu title="sub 39" url="pages.php?id=39"/>
            <Menu title="sub 40" url="pages.php?id=40"/>
        </Menu>
        <Menu title="sub3 3" url="root_pages.php?id=9">
            <Menu title="sub 41" url="pages.php?id=41"/>
            <Menu title="sub 42" url="pages.php?id=42"/>
            <Menu title="sub 43" url="pages.php?id=43"/>
            <Menu title="sub 44" url="pages.php?id=44"/>
            <Menu title="sub 45" url="pages.php?id=45"/>
        </Menu>
    </Menu>
</Root>