/*

------------
Installation
------------

Call jaxTreeRegister() during your document's onLoad() event to add a tree view
control, appended to the element specified by you, and bearing dimensions + look 
and feel as described by the jaxtree.css file.  Some steps you'll need to follow 
to get it working:

	1) include the jaxtree.js file in your html (via <script> tag),
	2) include common/jax-xmlhttprequest.js  in your html (via <script> tag),
	3) include the jaxtree.css file in your html (via <link> tag),
	4) in the body of your html have a tag with an id (preferably a <div> tag),
	5) call jaxTreeRegister() on document load,
	6) write a server-side response script that gathers stored content (like
	   from a database) and returns it.

Parameter descriptions for jaxTreeRegister():

	contentURL: url that returns an xml feed to populate the tree
	id:         id of the element (preferably div) to append tree to

Additional functionality - you can gain additional functionality by calling the
following functions prior to the jaxTreeRegister() function:

	1) jaxTreeSetFileViewURL  < the url to send a 'get' with id, allows user to view the file
	2) jaxTreeShowAdd         < the url to send a 'get' with id, allows user to add file/folder
	3) jaxTreeShowEdit        < the url to send a 'get' with id, allows user to edit file/folder
	4) jaxTreeShowDelete      < the url to send a 'get' with id, allows user to delete file/folder
	5) jaxTreeShowFoldersOnly < call this to enable view folders only

-------
Credits
-------

Authored by Dan Juliano on August 27th, 2005.

*/

var jtNodeArray = null;
var jtParentsArray = null;
var jtNodeList = null;

var jtFileViewURL = null;
var jtFolderAddURL = null;
var jtFolderEditURL = null;
var jtFolderMoveUpURL = null;
var jtFolderMoveDownURL = null;
var jtFolderDeleteURL = null;
var jtShowFoldersOnly = false;
var jtOpenRoot = false;
var jtDisableEditTopLevelNonLeafs = false;
var jtBlockEvents = false;

var jtNodeClick = null;

// The function below is actually a class constructor - takes in a 
// record from the xml feed and converts it to a node, a set of 
// which will form into a linked tree hierarchy.
function jaxTreeNode(xmlArrayRecord) {

	this.id = xmlArrayRecord[0];
	this.parent_id = xmlArrayRecord[1];
	this.is_leaf = xmlArrayRecord[2];
	this.title = xmlArrayRecord[3];
	this.path = xmlArrayRecord[4];
	
	// The 'children' array is how we create a node structure.
	this.children = new Array();
}

function jaxTreeRegister(contentURL, id) {
	var html = "";
	html += "<div id=\"tree_loading\">Loading</div>";
	html += "<div id=\"tree_container\"></div>";
//	alert(contentURL + ", " + id);
	document.getElementById(id).innerHTML = html;
	jaxCommonLoadingMessageReset("tree_loading", "tree_container");
	
	xmlRequest(contentURL, null, jaxTreeBuild);
}

function jaxTreeBuild(xml) {

	// Check to see if relevant xml data is present.
	jaxCommonLoadingMessageHide("tree_loading");
	if (xml.getElementsByTagName("record").length == 0) {
		document.getElementById("tree_container").innerHTML = "<p>No records to display</p>";
		return;
	}

	// Convert the xml to a two dimensional array, then 
	// create a linked list, then generate an html table
	// and spit it out to the page.
	jaxTreeConvertXMLToArray(xml);
	jaxTreeConvertArrayToTree();
	document.getElementById("tree_container").innerHTML = jaxTreeBuildHtml(jtNodeList, "", 0, 1);
	document.getElementById("tree_container").childNodes[0].className += " tree_root";
	
	jaxTreeInitializeJavascriptEvents();
}

function jaxTreeConvertXMLToArray(xml) {
	
	jtNodeArray = new Array();
	jtParentsArray = new Array();
	
	var children = xml.getElementsByTagName("record");
	var child = null;
	var counter = -1;
	var record = new Array();
	var hasPath = false;
	var node = null;
	
	var depth = 0;
	var depthParents = new Array();
	
	// Loop through all records in the xml and move them to a flexible node tree.
	for (var i = 0; i < children.length; i++) {
		
		// Dig the data out of the xml and place it in a simple array.
		child = children[i];
		record[0] = child.getElementsByTagName("id")[0].childNodes[0].nodeValue;
		record[1] = child.getElementsByTagName("parent_id")[0].childNodes[0].nodeValue;
		record[2] = child.getElementsByTagName("is_leaf")[0].childNodes[0].nodeValue;
		record[3] = child.getElementsByTagName("title")[0].childNodes[0].nodeValue;
		
		if (i == 0) {
			if (child.getElementsByTagName("path").length > 0) {
				hasPath = true;
			}
		}
		if (hasPath) {
			record[4] = child.getElementsByTagName("path")[0].childNodes[0].nodeValue;
		} else {
			record[4] = "";
		}
		
		node = new jaxTreeNode(record);
		jtNodeArray[++counter] = node;
		if (node.is_leaf != "Y") jtParentsArray[jtParentsArray.length] = node;
	}
}

function jaxTreeConvertArrayToTree() {
	
	var node = null;
	var parent = null;
	var counter = null;
	
	// ASSUME the first node is always the master parent (bad idea ... ?)
	jtNodeList = jtNodeArray[0];
	
	// Loop through each node in the array.
	for (var i = 1; i < jtNodeArray.length; i++) {
		node = jtNodeArray[i];

		// Loop through the parents array to find a matching parent.
		for (var j = 0; j < jtParentsArray.length; j++) {
			if (node.parent_id == jtParentsArray[j].id) {
				parent = jtParentsArray[j];
				parent.children[parent.children.length] = node;
				break;
			}
		}
	}
}

function jaxTreeBuildHtml(node, tabs, index, siblings) {
	
	// Leafs do not have children, process them first.
	if (node.is_leaf == 'Y') {
		if (!jtShowFoldersOnly) {
			return jaxTreeBuildHtmlRow(node, tabs, index, siblings);
		}
		return "";
	}
	
	// Generate html for folder.
	var html = "";
	html += tabs + "<div class=\"tree\">\n";
	html += jaxTreeBuildHtmlRow(node, tabs, index, siblings);
	
	// Then dig into the folder's children.
	var length = node.children.length;
	for (var i = 0; i < length; i++) {
		html += jaxTreeBuildHtml(node.children[i], tabs + "\t", i, length);
	}
	html += tabs + "</div>\n\n";
	
	return html;
}

function jaxTreeBuildHtmlRow(node, tabs, index, siblings) {

	var className = null;
	var id = null;
	
	var displayAdd = false;
	var displayEdit = false;
	var displayMoveUp = false;
	var displayMoveDown = false;
	var displayDelete = false;
	var haveDisabledEdit = false;
	
	// Has leaves.
	if (node.is_leaf != "Y") {
		className = "tree_open";
		id = "tree_" + node.id;
		
		if (jtFolderAddURL != null) displayAdd = true;
		if (jtFolderEditURL != null) displayEdit = true;
		if (jtFolderMoveUpURL != null && index != 0) displayMoveUp = true;
		if (jtFolderMoveDownURL != null && index != siblings - 1) displayMoveDown = true;
		if (jtFolderDeleteURL != null) {
			if (node.children.length == 0 && node.parent_id != 0) {
				displayDelete = true;
			}
		}
		if ((!node.parent_id) || (node.parent_id == 0) && jtDisableEditTopLevelNonLeafs) displayEdit = false;
		
	// Is a leaf, or end of a branch.
	} else {
		className = "tree_node";
		id = "tree_node_" + node.id;
		
		if (jtFolderEditURL != null) displayEdit = true;
		if (jtFolderMoveUpURL != null && index != 0) displayMoveUp = true;
		if (jtFolderMoveDownURL != null && index != siblings - 1) displayMoveDown = true;
		if (jtFolderDeleteURL != null) displayDelete = true;
	}
	
	var html = "\n";
	html += tabs + "\t<div class=\"" + className + "\" id=\"" + id + "\">\n";
	html += tabs + "\t\t<table>\n";
	html += tabs + "\t\t\t<tr>\n";
	html += tabs + "\t\t\t\t<td class=\"tree_td_text\">" + node.title + "</td>\n";
	html += tabs + "\t\t\t\t<td class=\"tree_td_anchor\">";
	html += jaxTreeBuildButton(displayAdd, jtFolderAddURL, "add", node.id, "Add New Node", "tree_add", "+");
	html += jaxTreeBuildButton(displayEdit, jtFolderEditURL, "edit", node.id, "Edit Node", "tree_edit", "e");
	html += jaxTreeBuildButton(displayMoveUp, jtFolderMoveUpURL, "moveup", node.id, "Move Node Up", "tree_move", "u");
	html += jaxTreeBuildButton(displayMoveDown, jtFolderMoveDownURL, "movedown", node.id, "Move Node Down", "tree_move", "d");
	html += jaxTreeBuildButton(displayDelete, jtFolderDeleteURL, "delete", node.id, "Delete Node", "tree_delete", "x");
	html += "</td>\n";
	html += tabs + "\t\t\t</tr>\n";
	html += tabs + "\t\t</table>\n";
	html += tabs + "\t</div>\n";
	return html;
}

function jaxTreeBuildButton(display, url, request, id, title, className, sign) {

	if (url == null) return "";
	if (!display) return " <span class=\"" + className + "\">" + sign + "</span>";
	
	var anchorHref = " href='" + url;
	anchorHref += (url.indexOf('?') == -1) ? "?" : "&";
	anchorHref += "request=" + request + "&id=" + id;
	anchorHref += "'";
	
	var anchorTitle   = " title='" + title + "'";
	var anchorCSS     = " class='" + className + "'";
	var anchorOnClick = " onClick='jaxTreeBlockEvents();'";
	
	return " <a" + anchorHref + anchorTitle + anchorCSS + anchorOnClick + ">" + sign + "</a>";
}

function jaxTreeInitializeJavascriptEvents() {
	
	// Find all the tree tags and append events to them.
	var divs = document.getElementsByTagName("DIV");
	for (var i = 0; i < divs.length; i++) {
		if (divs[i].className == "tree_open" || divs[i].className == "tree_close") {
			divs[i].style.cursor = "pointer";
			divs[i].onmouseover = function(event) { jaxTreeHighlight(this, true); };
			divs[i].onmouseout = function(event) { jaxTreeHighlight(this, false); };
//			divs[i].onmousedown = function(event) { return false; };
			divs[i].onselectstart = function(event) { return false; };
			divs[i].onclick = function(event) { jaxTreeExpand(this); }; //return false; };
			jaxTreeExpand(divs[i]);
			
		} else if (divs[i].className == "tree_node") {
			divs[i].onmouseover = function(event) { jaxTreeHighlight(this, true); };
			divs[i].onmouseout = function(event) { jaxTreeHighlight(this, false); };
			if (jtNodeClick) {
				divs[i].onclick = function(event) { jtNodeClick(jaxTreeGetFileTitle(this), jaxTreeGetRelativeFolder(this), jaxTreeGetFilePath(this)); };
				divs[i].style.cursor = "pointer";
			}
		}
	}
	
	// Open up the last-opened node.
	if (document.getElementById("tree_" + jtNodeList.id)) {
		jaxTreeExpand(document.getElementById("tree_" + jtNodeList.id));
	}
}

function jaxTreeExpand(element) {
	
	if (jtBlockEvents) return;
	
	var display = "";
	var state = element.className;
	if (state.indexOf("tree_open") > -1) display = "none";
	var found = false;
	
	var children = element.parentNode.childNodes;
	for (var i = 0; i < children.length; i++) {
		if (children[i].nodeType == 1 && children[i].nodeName == "DIV") {
			if (children[i].className != state) {
				children[i].style.display = display;
				found = true;
			}
		}
	}
	
	if (!found) {
		element.className = "tree_empty";
		element.style.cursor = "";
		element.onclick = null;
	} else if (state.indexOf("tree_open") > -1) {
		if (state.indexOf("highlight") > -1) {
			element.className = "tree_close_highlight";
		} else {
			element.className = "tree_close";
		}
	} else {
		if (state.indexOf("highlight") > -1) {
			element.className = "tree_open_highlight";
		} else {
			element.className = "tree_open";
		}
	}
	return false;
}
function jaxTreeHighlight(element, highlight) {

	if (jtBlockEvents) return;

	if (element.className == null) return;
	if (highlight) {
		if (element.className.indexOf("tree_open") > -1) element.className = "tree_open_highlight";
		if (element.className.indexOf("tree_close") > -1) element.className = "tree_close_highlight";
		if (element.className.indexOf("tree_empty") > -1) element.className = "tree_empty_highlight";
		if (element.className.indexOf("tree_node") > -1) element.className = "tree_node_highlight";
	} else {
		if (element.className.indexOf("tree_open") > -1) element.className = "tree_open";
		if (element.className.indexOf("tree_close") > -1) element.className = "tree_close";
		if (element.className.indexOf("tree_empty") > -1) element.className = "tree_empty";
		if (element.className.indexOf("tree_node") > -1) element.className = "tree_node";
	}
}
function jaxTreeGetFileTitle(element) {
	var td = element.getElementsByTagName("TD");
	for (var i = 0; i < td.length; i++) {
		if (td[i].className == "tree_td_text") {
			return td[i].innerHTML;
		}
	}
}
function jaxTreeGetFilePath(element) {
	// Go up until you've hit the tree node, then grab the first anchor
	var id = element.id.substring(10);
	for (var i = 0; i < jtNodeArray.length; i++) {
		if (jtNodeArray[i].id == id) {
			return jtNodeArray[i].path;
		}
	}
	return "";
}
function jaxTreeGetRelativeFolder(element) {
	// Grab all folders parent to the file, until the root node is 'hit'.
	var folder = "";
	while(element.id != null && element.id != "tree_container") {
		element = element.parentNode;
		if (element.className == "tree") {
			folder = jaxTreeGetFileTitle(element) + "/" + folder;
			break;
		}
	}
	
	return folder;
}


function jaxTreeSetFileViewURL(url) {
	jtFileViewURL = url;
}
function jaxTreeShowAdd(url) {
	jtFolderAddURL = url;
}
function jaxTreeShowEdit(url) {
	jtFolderEditURL = url;
}
function jaxTreeShowMoveUp(url) {
	jtFolderMoveUpURL = url;
}
function jaxTreeShowMoveDown(url) {
	jtFolderMoveDownURL = url;
}
function jaxTreeShowDelete(url) {
	jtFolderDeleteURL = url;
}
function jaxTreeShowFoldersOnly() {
	jtShowFoldersOnly = true;
}
function jaxTreeOpenRootElement() {
	jtOpenRoot = true;
}
function jaxTreeSetNodeClickEvent(onclick) {
	jtNodeClick = onclick;
}
function jaxTreeDisableEditTopLevelNonLeafs(){
	jtDisableEditTopLevelNonLeafs = true;
}
function jaxTreeBlockEvents(){
	jtBlockEvents = true;
}