<public:component>
<public:property name="nodeXmlSrc" internalname="m_strXmlSrc" />
<public:property name="nodeXslSrc" internalname="m_strXslSrc" />
<public:property name="selectedNode" get="fnGetSelectedNode" put="fnSetSelectedNode" />
<public:method name="sync" internalname="fnSync" />
<public:method name="reload" internalname="fnReload" />
<public:method name="getParent" internalname="fnGetParent" />
<public:method name="getChildren" internalname="fnGetChildren" />
<public:method name="getFirstChild" internalname="fnGetFirstChild" />
<public:method name="getLastChild" internalname="fnGetLastChild" />
<public:method name="getPreviousSibling" internalname="fnGetPreviousSibling" />
<public:method name="getNextSibling" internalname="fnGetNextSibling" />
<public:method name="buildLayoutXml" internalname="fnBuildLayoutXml" />
<public:method name="moveSelectedNodeUp" internalname="fnMoveSelectedNodeUp" />
<public:method name="moveSelectedNodeDown" internalname="fnMoveSelectedNodeDown" />
<public:attach event="ondocumentready" onevent="fnInitialize()" />
<public:attach event="onmouseover" onevent="fnOnMouseOver()" />
<public:attach event="onmouseout" onevent="fnOnMouseOut()" />
<public:attach event="onmousedown" onevent="fnOnMouseDown()" />
<public:attach event="onmouseup" onevent="fnOnMouseUp()" />
<public:attach event="onclick" onevent="fnOnClick()" />
<public:attach event="onselectstart" onevent="fnOnSelectStart()" />
<public:event name="onselectednodechanged" id="m_evtOnSelectedNodeChanged" />
<public:event name="onloadnodecontent" id="m_evtOnLoadNodeContent" />
<public:event name="onready" id="m_evtOnReady" />

<script language="JScript">

var m_objSelectedNode = null;
var m_bReady = false;

var m_strTempNodeStart = "<div type='leaf'><span class='clsSpace' type='img'><span class='clsLeaf'>.</span></span><span class='clsLabel' type='label'><span class='clsUnavailable'>";
var m_strTempNodeEnd = "</span></span></div>";

var m_strUnavailableText = "Unavailable";
var m_strLoadingText = "Loading Tree...";

var m_strXmlSrc;
var m_strXslSrc;
var m_xslDoc = new ActiveXObject("Microsoft.XMLDOM");

//-----------------------------------------------
//          ASYNCRONOUS LOAD HANDLERS
//-----------------------------------------------

function CallObject(xmlDoc, strUrl, objElement, objSync) {
	this.m_xmlDoc = xmlDoc;
	this.m_strUrl = strUrl;
	this.m_objElement = objElement;
	this.m_objSync = objSync;
}

function SyncObject(nlContext) {
	this.m_nlContext = nlContext;
    this.m_iCurrentIdx = 0;
    this.m_objNode = null;
}

var m_iMaxCalls = 1024;
var m_iLastCall = 0;
var m_arrCalls = new Array();

function fnAllocCall() {
	for (var i = 0; i < m_iMaxCalls; i++)
		if (m_arrCalls[m_iLastCall] == null)
			return m_iLastCall;
		else
			m_iLastCall = (m_iLastCall + 1) % m_iMaxCalls;
	return null;
}

function fnDeAllocCall(iCallIdx) { m_arrCalls[iCallIdx] = null; }

function fnLoadComplete(iCallIdx) {
	var objCall;
	try {
		if (iCallIdx == null
			|| (objCall = m_arrCalls[iCallIdx]) == null
			|| objCall.m_xmlDoc == null
			|| objCall.m_xmlDoc.readyState != 4) return;
	} catch(e) {
		return;
	}
	fnFixGzip(objCall.m_xmlDoc, objCall.m_strUrl);
	var strHtml;
	if (m_xslDoc.xml == ""
		|| (strHtml = objCall.m_xmlDoc.transformNode(m_xslDoc)) == null
		|| strHtml == "") strHtml = m_strTempNodeStart + m_strUnavailableText + m_strTempNodeEnd;
	objCall.m_objElement.innerHTML = strHtml;
	fnSyncAsync(objCall.m_objSync);
	fnDeAllocCall(iCallIdx);
	if (!m_bReady) {
		m_bReady = true;
		m_evtOnReady.fire();
	}
}

function fnLoadXml(strXmlSrc, objElement, objSync) {
	var iCallIdx = fnAllocCall();
	with (m_arrCalls[iCallIdx] = new CallObject(new ActiveXObject("Microsoft.XMLDOM"), strXmlSrc, objElement, objSync)) {
		m_xmlDoc.async = true;
		m_xmlDoc.onreadystatechange = Function("fnLoadComplete(" + iCallIdx + ");");
		m_xmlDoc.load(strXmlSrc);
	}
	return iCallIdx;
}

function fnSync(xmlDoc) {
	fnSyncAsync(new SyncObject(xmlDoc.selectNodes("//Item[@ID]")));
}

function fnSyncAsync(objSync) {
	if (objSync == null
		|| objSync.m_nlContext == null)
		return false;
    objSync.m_objNode = window.document.all(objSync.m_nlContext.item(objSync.m_iCurrentIdx).getAttribute("ID"));
	if ((++objSync.m_iCurrentIdx) == objSync.m_nlContext.length) {
		fnExpandNode(objSync.m_objNode, null);
		fnSelectNode(objSync.m_objNode, false, true);
	} else
		fnExpandNode(objSync.m_objNode, objSync);
	return objSync.m_objNode != null;
}

//------ END OF ASYNCRONOUS LOAD HANDLERS -------


//-----------------------------------------------
//                EVENT HANDLERS
//-----------------------------------------------

function fnInitialize() {
	element.type = "container";
	m_xslDoc.async = false;
	m_xslDoc.load(m_strXslSrc);
	fnFixGzip(m_xslDoc, m_strXslSrc);
	fnReload();
    window.document.body.attachEvent("onkeydown", fnKeyDown);
}

function fnOnMouseDown() { fnSetStyle("clsMouseDown"); }
function fnOnMouseUp() { fnSetStyle("clsMouseOver"); }
function fnOnMouseOver() { fnSetStyle("clsMouseOver"); }
function fnOnMouseOut() { fnSetStyle(null); }

function fnOnClick() {
	window.event.cancelBubble = true;
	window.event.returnValue = false;
	window.document.body.focus();
	var objUnknown = window.event.srcElement;
	var objNode;
 	if (objUnknown.tagName.toUpperCase() == "A") {
		fnSelectNode(objNode = objUnknown.parentElement.parentElement, false, false);
		fnToggleNode(objNode);
 	} else if (objUnknown.parentElement != null && objUnknown.parentElement.type == "img") {
 		if ((objNode = objUnknown.parentElement.parentElement).type == "leaf")
 			fnSelectNode(objNode, false, false);
 		else
 			fnToggleNode(objNode);
 	} else if (objUnknown.type == "img") {
 		if ((objNode = objUnknown.parentElement).type == "leaf")
 			fnSelectNode(objNode, false, false);
 		else
 			fnToggleNode(objNode);
 	} else if (objUnknown.type == "label") {
		fnSelectNode(objNode = objUnknown.parentElement, false, false);
		fnToggleNode(objNode);
 	} else
		fnSelectNode(m_objSelectedNode, true, false);
}

function fnKeyDown() {
	with (window.event) {
		if (ctrlKey || altKey) {
			cancelBubble = false;
			returnValue = true;
			return;
		} else {
			cancelBubble = true;
			returnValue = false;
		}
		switch (keyCode) {
			case 9: // tab key
				if (shiftKey)
					fnMovePrevious();
				else
					fnMoveNext();
				break;
			case 13: // enter key
				if (m_objSelectedNode != null)
					fnLoadNodeContent(m_objSelectedNode);
				break;
			case 37: // left key
				fnMoveLeft();
				break;
			case 38: // up key
				fnMoveUp();
				break;
			case 39: // right key
				fnMoveRight();
				break;
			case 40: // down key
				fnMoveDown();
				break;
			case 188: // "<" key
				fnMovePrevious();
				break;
			case 190: // ">" key
				fnMoveNext();
				break;
			default:
				cancelBubble = false;
				returnValue = true;
				break;
		}
	}
}

function fnOnSelectStart() {
	window.event.cancelBubble = true;
	window.event.returnValue = false;
}

//------------ END OF EVENT HANDLERS ------------


//-----------------------------------------------
//                PUBLIC METHODS
//-----------------------------------------------

function fnMoveSelectedNodeUp() {
	if (m_objSelectedNode != null && m_objSelectedNode.previousSibling != null)
		m_objSelectedNode.swapNode(m_objSelectedNode.previousSibling);
	fnSelectNode(m_objSelectedNode, true, true);
}

function fnMoveSelectedNodeDown() {
	if (m_objSelectedNode != null && m_objSelectedNode.nextSibling != null)
		m_objSelectedNode.swapNode(m_objSelectedNode.nextSibling);
	fnSelectNode(m_objSelectedNode, true, true);
}

function fnBuildLayoutXml(objNode, strNodeType, xmlDoc) {
	if (xmlDoc == null) {
		xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
		xmlDoc.appendChild(xmlDoc.createElement("Root"));
	}
	if (objNode == null)
		objNode = element;
	var children = fnGetChildren(objNode);
	for (var i = 0; i < children.length; i++) {
		objNode = children[i];
		if (objNode.nodeType == strNodeType) {
			var xmlElemItem = xmlDoc.createElement("Item");
			xmlElemItem.setAttribute("ID", objNode.nodeData);
			xmlElemItem.setAttribute("Order", i);
			xmlDoc.documentElement.appendChild(xmlElemItem);
			fnBuildLayoutXml(objNode, strNodeType, xmlDoc);
		}
	}
	return xmlDoc;
}

function fnReload() {
	m_bReady = false;
	fnLoadXml(m_strXmlSrc, element);
}

function fnGetParent(objNode) {
	var objParent;
	if (objNode == null
		|| objNode.parentElement == null
		|| (objParent = objNode.parentElement.parentElement) == null
		|| objParent.type != "parent")
		return null;
	return objParent;
}

function fnGetChildren(objNode) {
	var objContainer = objNode == element ? element : fnGetHtmlElement(objNode, "DIV" , "container");
	return objContainer != null ? objContainer.children : null;
}

function fnGetFirstChild(objNode) {
	var arrChildren = fnGetChildren(objNode);
	return arrChildren == null || arrChildren.length == 0 ? null : arrChildren[0];
}

function fnGetLastChild(objNode) {
	var arrChildren = fnGetChildren(objNode);
	return arrChildren == null || arrChildren.length == 0 ? null : arrChildren[arrChildren.length - 1];
}

function fnGetPreviousSibling(objNode) {
	return objNode == null ? null : objNode.previousSibling;
}

function fnGetNextSibling(objNode) {
	return objNode == null ? null : objNode.nextSibling;
}

//------------ END OF PUBLIC METHODS ------------


//-----------------------------------------------
//                PRIVATE METHODS
//-----------------------------------------------

function fnFixGzip(xmlDoc, strUrl) {
	if (xmlDoc.xml == "")
		with (new ActiveXObject("MSXML2.XMLHTTP")) {
			open("GET", strUrl, false);
			send();
			xmlDoc.loadXML(responseXml.xml);
		}
}

function fnSetStyle(strStyle) {
	var objUnknown = window.event.srcElement;
	if (objUnknown.tagName.toUpperCase() == "A")
		objUnknown = objUnknown.parentElement;
	if (objUnknown.type == "label")
		objUnknown.className = strStyle ? strStyle : objUnknown.classType;
}

function fnHighlightNode(objNode) {
	var objSpan = fnGetHtmlElement(objNode, "SPAN", "label");
	if (objSpan != null) {
		if (objSpan.classType != "clsUnavailable") {
			objSpan.classType = objSpan.className = "clsCurrentHasFocus";
			if (objNode != m_objSelectedNode) {
				if (m_objSelectedNode != null) {
					objSpan = fnGetHtmlElement(m_objSelectedNode, "SPAN", "label");
					if (objSpan != null) {
						objSpan.className = "";
						objSpan.classType = "";
					}
				}
			}
		}
		m_objSelectedNode = objNode;
	} else
		m_objSelectedNode = null;
}

function fnScrollIntoView(objNode, bScroll) {
	if (bScroll) {
		var iY = objNode.offsetTop - element.parentElement.clientHeight / 2;
		var iX = objNode.offsetLeft - 15;
		if (element.currentStyle.overflow == "auto") {
			element.scrollTop = iY;
			element.scrollLeft = iX;
		} else
			window.scrollTo(iX , iY);
	}
}

function fnGetHtmlElement(objParent, strChildTagName, strType) {
	var objChildren = objParent != null ? objParent.children : null;
	for (var i = 0; objChildren != null && i < objChildren.length; i++) {
		var objElement = objChildren[i];
		if (objElement.tagName.toUpperCase() == strChildTagName.toUpperCase()
			&& (strType == null || objElement.type == strType))
			return objElement;
	}
	return null;
}

function fnIsChild(objChildNode, objParentNode) {
	var objNode = fnGetParent(objChildNode);
	while (objNode != null)
		if (objNode == objParentNode)
			return true;
		else
			objNode = fnGetParent(objNode);
	return false;
}

function fnExpandAncestors(objNode) {
	while (objNode = fnGetParent(objNode))
		if (objNode.state != "shown")
			fnExpandNode(objNode, null);
}

function fnExpandNode(objNode, objSync) {
	var objContainer = fnGetHtmlElement(objNode, "DIV", "container");
	if (objContainer != null) {
		var objImg = fnGetHtmlElement(objNode, "SPAN", "img");
		if (objImg != null) {
			var objSpan = fnGetHtmlElement(objNode, "SPAN", "label");
			if (objSpan != null && objSpan.parentElement.type != "leaf") {
				objContainer.className = "shown";
				objImg.innerHTML = "<span class='clsExpand'>-</span>";
				objNode.state = "shown";
				return fnLoadChildren(objNode, objSync);
			}
		}
	}
	return false;
}

function fnCollapseNode(objNode) {
	var objContainer = fnGetHtmlElement(objNode, "DIV", "container");
	if (objContainer != null) {
		objImg = fnGetHtmlElement(objNode, "SPAN", "img");
		if (objContainer != null) {
			objNode.state = "hidden";
			objContainer.className = "hide";
			objImg.innerHTML = "<span class='clsCollapse'>+</span>";
			if (fnIsChild(m_objSelectedNode, objNode))
				fnSelectNode(objNode, false, false);
		}
	}
}

function fnToggleNode(objNode) {
	if (objNode.type == "parent")
		if (objNode.state == "shown")
			fnCollapseNode(objNode);
		else
			fnExpandNode(objNode, null);
}

function fnSelectNode(objNode, bSupressLink, bForceIntoView) {
	if (objNode == null)
		objNode = fnGetHtmlElement(element, "DIV");
	if (!bSupressLink)
		fnLoadNodeContent(objNode);
	fnHighlightNode(objNode);
	fnExpandAncestors(objNode);
	fnScrollIntoView(objNode, bForceIntoView);
	var evt = window.document.createEventObject();
	evt.node = objNode;
	m_evtOnSelectedNodeChanged.fire(evt);
}

function fnLoadChildren(objNode, objSync) {
	var objContainer = fnGetHtmlElement(objNode, "DIV", "container");
	if (objContainer != null) {
		var objSpan = fnGetHtmlElement(objNode, "SPAN", "label")
		if (objContainer.children.length == 0) {
			objContainer.innerHTML = m_strTempNodeStart + m_strLoadingText + m_strTempNodeEnd;
			if (objSpan != null) {
				fnLoadXml(objSpan.xmlsrc, objContainer, objSync);
				return true; // Indicate that an asyncronous load is taking place
			}
		} else
			fnSyncAsync(objSync);
	}
	return false;
}

function fnLoadNodeContent(objNode) {
	var evt = window.document.createEventObject();
	evt.node = objNode;
	m_evtOnLoadNodeContent.fire(evt);
}

function fnMoveNext() {
	if (m_objSelectedNode == null
		&& (m_objSelectedNode = fnGetHtmlElement(element , "DIV")) == null) return;
	var objChildNode = null;
	if (m_objSelectedNode.state == "shown")
		objChildNode = fnGetFirstChild(m_objSelectedNode);
	else if (m_objSelectedNode.type != "leaf")
		if (fnExpandNode(m_objSelectedNode, null))
			m_objSelectedNode.selectChild = true;
		else
			objChildNode = fnGetFirstChild(m_objSelectedNode);
	else {
		var objNode = m_objSelectedNode;
		objChildNode = fnGetNextSibling(m_objSelectedNode);
		while (objChildNode == null) {
			objNode = fnGetParent(objNode);
			if (objNode == null)
				objChildNode = m_objSelectedNode;
			else
				objChildNode = fnGetNextSibling(objNode);
		}
	}
	if (objChildNode != null)
		fnSelectNode(objChildNode, false, true);
}

function fnMovePrevious() {
	if (m_objSelectedNode == null
		&& (m_objSelectedNode = fnGetHtmlElement(element , "DIV")) == null) return;
	var objChildNode = fnGetPreviousSibling(m_objSelectedNode);
	if (objChildNode == null)
		objChildNode = fnGetParent(m_objSelectedNode);
	if (objChildNode != null)
		fnSelectNode(objChildNode, false, true);
}

function fnMoveLeft() {
	if (m_objSelectedNode == null
		&& (m_objSelectedNode = fnGetHtmlElement(element , "DIV")) == null) return;
	if (m_objSelectedNode.state == "shown")
		fnCollapseNode(m_objSelectedNode);
	else {
		var objChildNode = fnGetParent(m_objSelectedNode);
		if (objChildNode != null)
			fnSelectNode(objChildNode, false, true);
	}
}

function fnMoveRight() {
	if (m_objSelectedNode == null
		&& (m_objSelectedNode = fnGetHtmlElement(element , "DIV")) == null) return;
	if (m_objSelectedNode.state == "shown") {
		var objChildNode = fnGetFirstChild(m_objSelectedNode);
		if (objChildNode != null)
			fnSelectNode(objChildNode, false, true);
	}
	else if (m_objSelectedNode.type != "leaf")
		fnExpandNode(m_objSelectedNode, null);
}

function fnMoveUp() {
	if (m_objSelectedNode == null
		&& (m_objSelectedNode = fnGetHtmlElement(element , "DIV")) == null) return;
	var objChildNode = fnGetPreviousSibling(m_objSelectedNode);
	if (objChildNode != null)
		while (objChildNode.state == "shown")
			objChildNode = fnGetLastChild(objChildNode);
	else
		objChildNode = fnGetParent(m_objSelectedNode);
	if (objChildNode != null)
		fnSelectNode(objChildNode, false, true);
}

function fnMoveDown() {
	if (m_objSelectedNode == null
		&& (m_objSelectedNode = fnGetHtmlElement(element , "DIV")) == null) return;
	var objChildNode;
	if (m_objSelectedNode.state == "shown")
		objChildNode = fnGetFirstChild(m_objSelectedNode);
	else {
		var objNode = m_objSelectedNode;
		objChildNode = fnGetNextSibling(objNode);
		while (objChildNode == null) {
			objNode = fnGetParent(objNode);
			if (objNode == null)
				objChildNode = m_objSelectedNode;
			else
				objChildNode = fnGetNextSibling(objNode);
		}
	}
	if (objChildNode != null)
		fnSelectNode(objChildNode, false, true);
}

function fnGetSelectedNode() { return m_objSelectedNode; }
function fnSetSelectedNode(objNode) { fnSelectNode(objNode, false, true); }

//----------- END OF PRIVATE METHODS ------------

</script>

</public:component>
