// Can take this out later...
function get_XMLHTTPRequestObject() {
	var xmlhro = false;
	if (window.XMLHttpRequest) {
		xmlhro = new XMLHttpRequest();
	} else if (window.ActiveXObject) {
		xmlhro = new ActiveXObject("Microsoft.XMLHTTP");
	}
	//xmlhro.overrideMimeType("text/xml");
	if (!xmlhro) {
		alert("Your browser is not supported by GCensus. Browsers that are known to work are: Mozilla Firefox v1.0 or greater; MS Internet Explorer 6");
		throw "Unsupported Browser";
	}
	return xmlhro;
}

// The TreeNode class stores state for each node
function TreeNode(type,domid) {
	this.type = type;
	this.domid = domid;
}
TreeNode.prototype.internalnode = 1;
TreeNode.prototype.leafnode = 2;

DynamicTree.prototype.treecounter=0;
DynamicTree.prototype.nodeOpenClass = 'liOpen';
DynamicTree.prototype.nodeClosedClass = 'liClosed';
DynamicTree.prototype.nodeLeafClass= 'liBullet';
DynamicTree.prototype.treeClass = 'dtree';
DynamicTree.prototype.nodeLinkClass = 'bullet';

// The DynamicTree class represents a DHTML/CSS tree that dynamically
// loads its contents using AJAX as internal nodes are clicked

function DynamicTree(divid,callback) {
	// Constructor
		// Increment a class-global counter to get a tree id
		DynamicTree.prototype.treecounter++;
		this.treeid = "DTREE"+DynamicTree.prototype.treecounter;
		
		// nodes indexes from node IDs to TreeNodes
		this.nodes = new Array();

		// When a leaf is clicked, leaf_callback is called with its data
		this.leaf_callback = callback;

		// Maintain internal counters to label nodes
		this.internalnodectr = 0;
		this.leafnodectr = 0;

		// Store the id of the div holding this tree
		this.div_elt = document.getElementById(divid);
		// Nuke the div contents and replace with an empty UL
		this.div_elt.innerHTML = "";
		this.root_ul = document.createElement("ul");
		this.root_ul.className = this.treeClass;
		this.div_elt.appendChild(this.root_ul);
	// --- end ctor ---

	
	// Member functions
	this.addInternalNode = function(parent,text,nextpage) {
		this.internalnodectr++;
		var nodeid = this.treeid+"_I"+this.internalnodectr;
		var node = new TreeNode(TreeNode.internalnode,nodeid);
		this.nodes[nodeid] = node;
		
		// Add the node into the HTML UL
		var parent_ul;
		if (parent == null) {parent_ul = this.root_ul;}
		else {parent_ul = document.getElementById(parent+"ul");}
		
		// Create the LI node and set its ID
		var intnode = document.createElement("li");
		intnode.setAttribute("id",nodeid);
		intnode.className = this.nodeClosedClass;
		
		// Create the span for the internal node's clickable text
		var nodespan = document.createElement("span");
		nodespan.className = this.nodeLinkClass;
		var nodetext = '\u00A0' + text;
		nodespan.appendChild(document.createTextNode(nodetext));
		
		// Create the UL holding subitems of this node
		var intnode_ul = document.createElement("ul");
		intnode_ul.className = this.treeClass;
		intnode_ul.setAttribute("id",nodeid+"ul");

		// Create the callback function to handle expansion/contraction
		// as well as dynamic loading
		// ...here's where the dynamic magic comes in...
		var loaded_data = false;
		var theTree = this;
		nodespan.onclick = function() {
			// Check for existing entries using closure on loaded_data
			if (loaded_data) {
				theTree.toggleIntNodeVisibility(intnode);
			} else {
				// Asynchronously load the data
				var xmlhro = get_XMLHTTPRequestObject();
				xmlhro.open("GET",nextpage,true);
				
				// Add a "loading" indicator and make node visible
				var loading_li = document.createElement("li");
				var italic = document.createElement("i");
				loading_li.appendChild(italic);
				italic.appendChild(document.createTextNode("Loading..."));
				intnode_ul.appendChild(loading_li);
				intnode.className = theTree.nodeOpenClass;
				
				// On loading, delete loading indicator and add the nodes (already visible) 
				xmlhro.onreadystatechange = function () {
					if (xmlhro.readyState == 4 && xmlhro.status == 200) {
						intnode_ul.removeChild(loading_li);
						theTree.addXMLNodes(nodeid,xmlhro.responseXML);
						loaded_data = true;
					}
				}

				// Issue async load
				xmlhro.send(null);
			}
		}						
		
		intnode.appendChild(nodespan);
		intnode.appendChild(intnode_ul);

		parent_ul.appendChild(intnode);
		return nodeid;
	}

	this.toggleIntNodeVisibility = function (intnode) {
		if (intnode.className == this.nodeClosedClass) {
			intnode.className = this.nodeOpenClass;
		} else {
			intnode.className = this.nodeClosedClass;
		}
	}
		

	this.addLeafNode = function(parent,text,data) {
		this.leafnodectr++;
		var nodeid = this.treeid + "_L" + this.leafnodectr;
		var node = new TreeNode(TreeNode.leafnode,nodeid);
		this.nodes[nodeid] = node;
		// Add the node into the HTML UL
		var parent_ul;
		if (parent == null) {
			// Adding as terminal root node
			parent_ul = this.root_ul;
		} else {
			parent_ul = document.getElementById(parent+"ul");
		}
		
		// Put the text and callback handler in a clickable span
		var leafspan = document.createElement("span");
		leafspan.className = this.nodeLinkClass;
		
		// Create this variable because "this" doesn't get wrapped inside the closure
		var cb = this.leaf_callback;
		// ...but "data" does.
		leafspan.onclick = function () {return cb(data);}
		
		var leaftext = '\u00A0'; // &nbsp;
		leaftext += text;
		leafspan.appendChild(document.createTextNode(leaftext));
		
		// Add the span to a list item
		var leafnode = document.createElement("li");
		leafnode.setAttribute("id",nodeid);
		leafnode.appendChild(leafspan);
		leafnode.className = this.nodeLeafClass;

		// Stick the LI into the parent list
		parent_ul.appendChild(leafnode);
		return nodeid;
	}

	this.addTerminatingNode = function(parent,text) {
		var parent_ul;
		if (parent == null) {
			// Adding as terminal root node
			parent_ul = this.root_ul;
		} else {
			parent_ul = document.getElementById(parent+"ul");
		}
		// Generate the node data
		var nodedata = document.createTextNode(text);
		var termnode = document.createElement("li");
		termnode.appendChild(nodedata);

		parent_ul.appendChild(termnode);
	}


	this.addXMLNodes = function(parent,xmldoc) {
		var treedata = xmldoc.getElementsByTagName("treedata");
		if (treedata.length == 1) {
			treedata = treedata[0];
		} else if (treedata.length > 1) {
			alert("Malformed treedata document");
		} else {
			this.addTerminatingNode(parent,"No further options for root treedata");
		}
		var xmlnodes = treedata.getElementsByTagName("node");
		for (var i = 0;i < xmlnodes.length; i++) {
			var curnode = xmlnodes[i];
			var nodetype = curnode.getAttribute("type");
			if (nodetype == "internal") {
				var nextpage,text;
				var elts = curnode.getElementsByTagName("nextpage");
				if (elts.length != 1 ) {alert("Malformed internal node");}
				nextpage = elts[0].firstChild.nodeValue;
				elts = curnode.getElementsByTagName("text");
				if (elts.length != 1 ) {alert("Malformed internal node");}
				text = elts[0].firstChild.nodeValue;
				
				this.addInternalNode(parent,text,nextpage);
			} else if (nodetype == "leaf") {
				var kvpairs = new Array();
				var text;
				
				var elts = curnode.getElementsByTagName("text");
				if (elts.length != 1 ) {alert("Malformed leaf node");}
				text = elts[0].firstChild.nodeValue;

				elts = curnode.getElementsByTagName("kvpair");
				var leafdata = new Array();
				for (var j = 0;j < elts.length; j++) {
					var key = elts[j].getElementsByTagName("key")[0].firstChild.nodeValue;
					var value = elts[j].getElementsByTagName("value")[0].firstChild.nodeValue;
					leafdata[key] = value;
				}
				this.addLeafNode(parent,text,leafdata);

			} else {
				alert("Malformed tree - unknown node type");
			}
		}
			
	}
}



