// JScript File
//static variables
var m_lastCtx;
var m_ie5 = (window.navigator.appName.toLowerCase().indexOf("microsoft")>=0);
var m_trees;
var m_plusImg = 'TreeLineImages/plus.gif';
var m_minusImg = 'TreeLineImages/minus.gif';
var m_dragObj = null;
var m_dragObjTreeId = null;
var m_lastMouseover = null;
var m_lastParent = null;

//Tree object
function Tree(id)
{
	var m_nodes = new Array();
	var m_lastCtxNode;
	var m_activeElement;
	var m_dragDropCallback;
	var m_selectCallBack;
	var m_nodeStyles;
	var m_nodesMoved = false;
	var m_containerId;
	
	this.Id = id;
	this.ContainerId = m_containerId; //Optional getter/setter
	this.ShowCtxForActiveOnly = false;
	this.AddNode = function(node)
	{
		m_nodes.push(node);
	}	
	this.GetNodeCount = function(){return m_nodes.length;}
	this.GetNodes = function(){return m_nodes;}
	this.GetNode = function(index)
	{   
		return (m_nodes.length > index) ? m_nodes[index] : null;
	}    
	this.FindNode = function(id)
	{
		var node = null;
		for (var i=0;i<m_nodes.length;i++)
		{
			node = m_nodes[i].FindNode(id);
			if (node != null)
				break;
		}
		return node;
	}
	this.GetHtml = function()
	{
		var s = "";
		m_firstTbl = true;
		if (m_nodes != null)
		{
			for (var i=0;i<m_nodes.length;i++)
				s += m_nodes[i].GetHtml();
		}
		//Replace first table with
		if (! m_ie5)
			s = s.replace(/<table /,"<table class='tdt' ");

		return s;
	}
	this.RefreshHtml = function()
	{
		var s = "";
		m_firstTbl = true;
		if (m_nodes != null)
		{
			for (var i=0;i<m_nodes.length;i++)
				s += m_nodes[i].RefreshHtml();
		}
		//Replace first table with
		if (! m_ie5)
			s = s.replace(/<table /,"<table class='tdt' ");

		return s;
	}
	this.GetCheckedNodes = function()
	{
		var checkedNodes = new Array();
		for (var i=0;i<m_nodes.length;i++)
			m_nodes[i].SetCheckedNodes(checkedNodes);
		return checkedNodes;
	}
	this.UpdateChecked = function()
	{
		for (var i=0;i<m_nodes.length;i++)
			m_nodes[i].UpdateChecked();	
	}
	this.GetExpandedNodes = function()
	{
		var expStates = new Array();
		for (var i=0;i<m_nodes.length;i++)
			m_nodes[i].SetExpandState(expStates,true,false);
		return expStates;
	}
	this.GetCollapseddNodes = function()
	{
		var collapseStates = new Array();
		for (var i=0;i<m_nodes.length;i++)
			m_nodes[i].SetExpandState(collapseStates,false,false);
		return collapseStates;
	}
	this.SetInitialExpand = function()
	{
		for (var i=0;i<m_nodes.length;i++)
			m_nodes[i].SetInitialExpand();
	}
	this.ExpandNode = function(id){this.ExpandCollapseNode(id,true);}
	this.CollapseNode = function(id){this.ExpandCollapseNode(id,false);}
	this.ExpandCollapseNode = function(id,expand)
	{
		var img = document.getElementById("i" + id);
		var div = document.getElementById("d" + id);
		if (img != null && div != null)
			_setDisplay(img, div, expand);
	}
	this.SetLastCtxNode = function(node){m_lastCtxNode = node;}
	this.GetLastCtxNode = function(){return m_lastCtxNode;}
	this.ExpandAll = function()
	{
		for (var i=0;i<m_nodes.length;i++)
			m_nodes[i].ExpandAll();
	}
	this.SetActiveNode = function(nodeId,cssClass)
	{  
		var ele = document.getElementById(nodeId);
		if (ele != null)
		{
			if (m_activeElement != null) //reset class of current active node
			{
				try{m_activeElement.className = this.FindNode(m_activeElement.id).GetCss();}catch(e){alert('ExceptionHandler::SetActiveNode:' + e.message);}
			}
			ele.className = cssClass;
			//set new active node
			m_activeElement = ele;
		}
	}
	this.GetActiveElement = function(){return m_activeElement;}
	this.RegisterDragDrop = function (callback){m_dragDropCallback = callback;}
	this.UnregisterDragDrop = function(){m_dragDropCallback = null;}
	this.RegisterSelect = function(callback){m_selectCallBack = callback;}
	this.UnregisterSelect = function(){m_selectCallBack = null;}		
	this.RaiseDragDrop = function(draggedId,droppedId)
	{
		if (m_dragDropCallback)
			m_dragDropCallback(this, this.FindNode(draggedId), this.FindNode(droppedId));
	}
	this.RaiseSelect = function(selectedId)
	{
		if (m_selectCallBack)
			m_selectCallBack(this, this.FindNode(selectedId));
	}
	this.RemoveNode = function (nodeId)
	{   
		for (var i=0;i<m_nodes.length;i++)
		{
			if (m_nodes[i].Id == nodeId)
			{
				m_nodes.splice(i,1);
				break;
			}
			else if (m_nodes[i].RemoveChild(nodeId))
				break;
		}		
		if (m_activeElement != null && m_activeElement.id == nodeId)
		    m_activeElement = null;
	}
	this.MoveNode = function(nodeId,insertBeforeNodeId)
	{
		var moved = false;
		if (nodeId == insertBeforeNodeId)
			return moved;

		var node = this.FindNode(nodeId);
		if (node != null)
		{
			var tmp = m_activeElement;//remember active element. RemoveNode will reset active element node being removed is active
			this.RemoveNode(nodeId);
			m_activeElement = tmp;
			for (var i=0;i<m_nodes.length;i++)
			{
				if (m_nodes[i].Id == insertBeforeNodeId)
				{
					m_nodes.splice(i,0,node);
					moved = true;
					break;
				}
			}
			if (!moved)
			{
				for (var i=0;i<m_nodes.length;i++)
				{
					if (m_nodes[i].InsertChild(node,insertBeforeNodeId))
					{
						moved = true;
						break;
					}
				}
			}
		}
		m_nodesMoved = moved;
		return moved;
	}
	this.GetParentNode = function(nodeId)
	{
		m_lastParent = null;
		for (var i=0;i<m_nodes.length;i++)
		{
			if (m_nodes[i].Id == nodeId)
				break;
			else if (m_nodes[i].FindParent(nodeId))
				break;
		} 
		return m_lastParent;
	}
	this.GetNodesMovedStatus = function(){return m_nodesMoved;}
	this.ResetNodesMovedStatus = function(){m_nodesMoved = false;}
	this.SetStyles = function(styles){m_nodeStyles = styles;}
	this.GetStyles = function()
	{
		if (m_nodeStyles == null)
			m_nodeStyles = new NodeStyles('','','','','');
		return m_nodeStyles
	}	
	this.ToString = function(){return "Id:" + this.Id + ",ContainerId:" + this.ContainerId + ",Node Count(root):" + m_nodes.length;}
}
//Node object
function Node(id,treeId,text,tooltip,showCheck,checked,expanded,ntype,cmenu,css,draggable)
{
	var m_childNodes = null;
	var m_checked = checked;
	var m_expanded = expanded;
	var m_nodeCss = css;
	var m_draggable = draggable;

	this.Id = id;
	this.TreeId = treeId;
	this.Text = text;
	this.Tooltip = tooltip;
	this.ShowCheck = showCheck;
	this.NodeType = ntype;
	this.ContextMenu = cmenu;    
	this.Children = m_childNodes;
	this.GetChildCount = function(){return (m_childNodes == null) ? 0 : m_childNodes.length;}
	this.AddChild = function(node)
	{
		if (m_childNodes == null)
		{
			m_childNodes = new Array();
			this.Children = m_childNodes;
		}
		m_childNodes.push(node);
	}
	this.GetChild = function(index)
	{
		return (m_childNodes.length > index) ? m_childNodes[index] : null;
	}	 
	this.FindNode = function(id)
	{
		var node = null;
		if (this.Id == id)
		{
			node = this;
		}
		else if (m_childNodes != null)
		{
			for (var i=0;i<m_childNodes.length;i++)
			{
				node = m_childNodes[i].FindNode(id);
				if (node != null)
					break;
			}
		}
		return node;
	}
	this.GetHtml = function()
	{
		return this._GetHtml(true, (m_checked ? " checked " : ""),false);
	}    
	this.RefreshHtml = function()
	{	
		return this._GetHtml(this.IsExpanded(), (this.IsChecked() ? " checked " : ""),true);	
	}
	this._GetHtml = function(expanded,checkedState,refreshMode)
	{
		var s = "<table cellpadding=0 cellspacing=1><tr>";
		var trId = this.TreeId;
		if (m_childNodes != null)
		{
			//add +/- button
			if (expanded)			
				s += "<td><img id='i" + this.Id + "' src='" + m_minusImg + "' onclick='_tgl(this, \"d" + this.Id + "\");' /></td>";
			else
				s += "<td><img id='i" + this.Id + "' src='" + m_plusImg + "' onclick='_tgl(this, \"d" + this.Id + "\");' /></td>";
		}		
		if (trId != null)
			s += "<td nowrap parent='" + trId + "' id='" + this.Id + "' unselectable='on'";
		else
			s += "<td nowrap";
		if (this.Tooltip != null)
		{
			s += " title='" + this.Tooltip + "'";
		}	    
		//hook drag-drop event
		if (this.IsDraggable())		
			s += " onclick='_nclick(this,\"" + trId + "\");' onmousedown='_mdown(event,this,\"" + trId + "\");'";
		else
			s += " onclick='_nclick(this,\"" + trId + "\");'";		

		s += " class='" + m_nodeCss + "'>"; 
		if (this.ShowCheck)
		{
			s += "<input type=checkbox id='ck" + this.Id + "'" + checkedState + "/>";
		}

		s += this.Text + "</td></tr>";
	   
		if (m_childNodes != null)
		{
			s += "<tr><td></td><td><div id='d" + this.Id + "'>";
			for (var i=0;i<m_childNodes.length;i++)
			{
				s += (refreshMode) ? m_childNodes[i].RefreshHtml() : m_childNodes[i].GetHtml();
			}
			s += "</div></td></tr>";
		}
		s += "</table>";
		return s;
	}
	this.Expand = function()
	{
		var img = document.getElementById("i" + this.Id);
		var div = document.getElementById("d" + this.Id); 
		if (img != null && div != null)
		{
			_setDisplay(img, div, true);
		}
	}
	this.Collapse = function()
	{
		var img = document.getElementById("i" + this.Id);
		var div = document.getElementById("d" + this.Id); 
		if (img != null && div != null)
		{ 
			_setDisplay(img, div, false);
		}
	}
	this.IsExpanded = function()
	{   
		var div = document.getElementById("d" + this.Id);
		return (div != null) && (div.style.display == '' || div.style.display == 'block');
	}
	this.ExpandAll = function()
	{
		this.Expand();
		if (m_childNodes != null)
		{
			for (var i=0;i<m_childNodes.length;i++)
				m_childNodes[i].ExpandAll();
		}
	}
	this.SetInitialExpand = function()
	{
		if (! m_expanded)
			this.Collapse();
		if (m_childNodes != null)
		{
			for (var i=0;i<m_childNodes.length;i++)
				m_childNodes[i].SetInitialExpand();
		}
	}
	this.IsChecked = function()
	{
		var chk = false;
		if (this.ShowCheck)
		{
			var ele = document.getElementById("ck" + this.Id);
			chk = (ele != null && ele.checked);
		}
		return chk;
	}
	this.UpdateChecked = function()
	{
		m_checked = this.IsChecked();
		if (m_childNodes != null)
		{    
			for (var i=0;i<m_childNodes.length;i++)			
				m_childNodes[i].UpdateChecked();			
		}
	}
	this.SetCheckedNodes = function(checkedNodesArray)
	{
		if (this.IsChecked())
			checkedNodesArray.push(this);

		if (m_childNodes != null)
		{    
			for (var i=0;i<m_childNodes.length;i++)
			{
				m_childNodes[i].SetCheckedNodes(checkedNodesArray);
			}
		} 
	}
	this.CheckNode = function(checked, checkChildren)
	{
		var ele = document.getElementById("ck" + this.Id)
		if (ele != null)
			ele.checked = checked;
		if (checkChildren && m_childNodes != null)
		{
			for (var i=0;i<m_childNodes.length;i++)
			{
				m_childNodes[i].CheckNode(checked, checkChildren);
			}
		}		
	}
	this.SetExpandState = function(idArray,getExpandedNodes,includeLegend)
	{
		if (this.NodeType != "LG" || includeLegend)
		{   
			var expanded = this.IsExpanded();
			if (expanded && getExpandedNodes)			
				idArray.push(this.Id);			
		}
		if (m_childNodes != null)
		{
		    if(!expanded && !getExpandedNodes)/*add collapsed state only if this node has children*/
				idArray.push(this.Id);
				
			for (var i=0;i<m_childNodes.length;i++)
			{
				m_childNodes[i].SetExpandState(idArray,getExpandedNodes,includeLegend);
			}
		}
	}
	this.SetCss = function(css){m_nodeCss=css;}
	this.GetCss = function(){return m_nodeCss;}
	this.IsDraggable = function(){return m_draggable;}
	this.RemoveChild = function (nodeId)
	{
		var removed = false;
		if (m_childNodes != null)
		{
			for (var i=0;i<m_childNodes.length;i++)
			{
				if (m_childNodes[i].Id == nodeId)
				{
					m_childNodes.splice(i,1);
					removed = true;
					break;
				}
				else if (m_childNodes[i].RemoveChild(nodeId))
				{
					removed = true;
					break;
				}
			}
			if (m_childNodes.length == 0)
			    m_childNodes = null;			
		}
		return removed;
	}
	this.InsertChild = function(node,insertBeforeNodeId)
	{
		var inserted = false;
		if (m_childNodes != null)
		{
			for (var i=0;i<m_childNodes.length;i++)
			{
				if (m_childNodes[i].Id == insertBeforeNodeId)
				{
					m_childNodes.splice(i,0,node);
					inserted = true;
					break;
				}
				else if (m_childNodes[i].InsertChild(node,insertBeforeNodeId))
				{
					inserted = true;
					break;
				}
			}
		}
		return inserted;
	}
	this.FindParent = function(nodeId)
	{	
		if (m_childNodes != null)
		{
			for (var i=0;i<m_childNodes.length;i++)
			{
				if (m_childNodes[i].Id == nodeId)
				{
					m_lastParent = this;					
					break;
				}
				else if (m_childNodes[i].FindParent(nodeId))
					break;				
			}
		}
		return (m_lastParent != null);	
	}
	/* does not update html, only nodes to tree/node object are added */
	this.InsertNewChildNodes = function(childXml, clearExistingChild)
	{
		if (childXml == null)
			return;

		childXml = _unescapeXml(childXml);
		
		var xmlDoc = _createXmlDoc(childXml);		
		if (xmlDoc.childNodes.length > 0)
		{
			try 
			{
				if (clearExistingChild) m_childNodes = null;
			}
			catch(e){}
			var treeId = this.TreeId;		
			var childNodes = xmlDoc.childNodes[0].childNodes;
			//Update tree
			for (var i=0;i<childNodes.length;i++)			
				_AddChildNode(this,childNodes[i],treeId);
		}	
	}
	this.UpdateId = function(newId)
	{
        var ele = document.getElementById(this.Id);
        if (ele != null)
            ele.id = newId;
        this.Id = newId;
	}
	this.ToString = function() 
	{ return "Id:" + this.Id + ",Text:" + this.Text + ",Tooltip:" + this.Tooltip + ",ShowCheck:" + this.ShowCheck + ",Expanded:" + this.IsExpanded() + ",NodeType:" + this.NodeType + ",ContextMenu:" + this.ContextMenu + ",ChildCount:" + this.GetChildCount() + ",Drggable:" + this.IsDraggable();}

}
//Tree Styles
function NodeStyles(regular,active,greyed,group,rootLevel)
{
	this.Regular = regular;
	this.Active = active;
	this.Greyed = greyed;
	this.Group = group;
	this.RootLevel = rootLevel;
	this.None = '';
	this.ToString = function()
	{ return "Regular:" + this.Regular + ",Active:" + this.Active + ",Greyed:" + this.Greyed + ",Group:" + this.Group;}
}
//standalone functions (_ are internal)
function _tgl(img, divId)
{
	var ele = document.getElementById(divId);
	if (ele)
	{
		if (ele.style.display == '' || ele.style.display == 'block')
		{
			_setDisplay(img, ele, false);
		}
		else
		{
			_setDisplay(img, ele, true);
		}
	}
}
function _setDisplay(img, div, show)
{
	div.style.display = show ? 'block' : 'none';
	img.src = show ? m_minusImg : m_plusImg;  
}

function _showmenu(e)
{   
	_hidemenu(e);
	var firingEle = (m_ie5) ? event.srcElement : e.target;
	var tid = firingEle.id;

	var isChild = false;  
	var parentAttr = firingEle.attributes["parent"];
	var parentTree = null;
	if (parentAttr != null)
	{   
		parentTree = _FindTree(parentAttr.value);
		isChild = (parentTree != null);
		if (isChild && parentTree.ShowCtxForActiveOnly && parentTree.GetActiveElement() != null && parentTree.GetActiveElement().id != tid)
			isChild = false;
	}

	if (isChild)
	{   
		//get firing element node
		var firingNode = parentTree.FindNode(tid);
		if (firingNode != null)
		{
			parentTree.SetLastCtxNode(firingNode);
			//get context menu for this node
			m_lastCtx = document.getElementById(firingNode.ContextMenu);
			if (m_lastCtx != null)
			{
				//set its position
				m_lastCtx.style.left = (m_ie5 ? document.documentElement.scrollLeft + event.clientX + 'px' : document.documentElement.scrollLeft + e.clientX + 'px');
				m_lastCtx.style.top = (m_ie5 ? document.documentElement.scrollTop + event.clientY + 'px' : document.documentElement.scrollTop + e.clientY + 'px');
				m_lastCtx.style.display = 'block';
				m_lastCtx.style.zIndex = 100;
			}
		}

		if (! m_ie5)
			e.stopPropagation();

		return false;
	}
}
function _hidemenu(e)
{
	if (m_lastCtx)
		m_lastCtx.style.display="none";
}
function _loadTree(xml,treeId,containerId)
{   
	if (xml == null)
		return;

	var xmlDoc = _createXmlDoc(_unescapeXml(xml));
	    
	if (xmlDoc.childNodes.length > 0)
	{
		var treeNode = xmlDoc.childNodes[0];
		if (treeId == null)
		{   
			try {treeId = treeNode.attributes.getNamedItem ("id").value;} catch(e){}
		}
		if (containerId == null)
		{
			try{containerId = treeNode.attributes.getNamedItem("containerid").value;} catch(e){}
		}
		var container = document.getElementById(containerId)
		if (container == null)
		{ 
			if (m_ie5)
				window.status = 'invalid container:' + containerId + ' for treeview';
			return;
		}
		if (m_trees == null)
		{
			m_trees = new Array();
		}
		else
		{
			//remove duplicates ?
			_RemoveTree(treeId);
		}

		var tree = new Tree(treeId);		
		tree.ContainerId = containerId;
		m_trees.push(tree);

		var rootNodes = treeNode.childNodes;
		for (var i=0;i<rootNodes.length;i++)
		{
			var rootNode = rootNodes[i];
			var val = rootNode.attributes[0].value;
			var rootTreeNode = _CreateTreeNode(val,treeId);

			tree.AddNode(rootTreeNode);

			var rootChildren = rootNode.childNodes; 
			for (var j=0;j<rootChildren.length;j++)
			{
				_AddChildNode(rootTreeNode,rootChildren[j],treeId);
			}
		}
		
		//Set styles
		try
		{
			var cssstyles = treeNode.attributes.getNamedItem("cssstyles").value.split("|");
			var styles = new NodeStyles(cssstyles[0],cssstyles[1],cssstyles[2],cssstyles[3],'');
			if (cssstyles.length > 4)
				styles.RootLevel = cssstyles[4];
			tree.SetStyles(styles);
		}
		catch (e){}
		container.innerHTML = tree.GetHtml();
		tree.SetInitialExpand();
		try
		{
			var activeNodeId = treeNode.attributes.getNamedItem("activenode").value;
			if (activeNodeId != null)
				tree.SetActiveNode(activeNodeId,tree.GetStyles().Active);
		}
		catch(e){}				
	}
}

function _AddChildNode(parentNode,child,treeId)
{
	var val = child.attributes[0].value;  
	var treeNode = _CreateTreeNode(val,treeId);
	parentNode.AddChild(treeNode);
	var children = child.childNodes; 
	if (children != null && children.length > 0)
	{
		for (var i=0;i<children.length;i++)
		{
			_AddChildNode(treeNode, children[i], treeId);
		}
	}
}
function _CreateTreeNode(val,treeId)
{
	var nodeInfo = val.split("~");

	var node = new Node(nodeInfo[0],treeId,nodeInfo[1],nodeInfo[2],
				(nodeInfo[3] == "1") ? true : false,(nodeInfo[4] == "1") ? true : false,
				(nodeInfo[5] == "1") ? true : false,
				nodeInfo[6],nodeInfo[7],nodeInfo[8],
				(nodeInfo[9] == "1") ? true : false);
	return node;
}
function _FindTree(treeId)
{
	var tree = null;
	if (m_trees != null)
	{
		for (var i=0;i<m_trees.length;i++)
		{
			if (m_trees[i].Id == treeId)
			{
				tree = m_trees[i];
				break;
			}
		}
	}
	return tree;
}
function _GetFirstTree()
{
	if (m_trees != null && m_trees.length > 0)
		return m_trees[0];
}
function _RemoveTree(treeId)
{
	if (m_trees != null)
	{
		for (var i=0;i<m_trees.length;i++)
		{
			if (m_trees[i].Id == treeId)
			{
				m_trees.splice(i,1);
				break;
			}
		}
	}
}

function _nclick(ele,treeId)
{	
	_FindTree(treeId).RaiseSelect(ele.id);
}
function _mdown(ev,ele,treeId)
{   
	if ((window.event || ev).button == 2)	//return if right click
		return;
		
	m_dragObj = ele;
	m_dragObjTreeId = treeId;
	document.body.style.cursor = 'move';

	if (m_ie5)
		document.attachEvent('onmousemove',_mmove);
	else
		document.addEventListener('mousemove',_mmove,false); 
	    
	return false;
}
function _mup(ev)
{
	document.body.style.cursor = 'auto';

	if (m_dragObj != null && m_lastMouseover != null)
	{
		//find tree
		//if callback
		var tree = _FindTree(m_dragObjTreeId);
		if (tree != null)
			tree.RaiseDragDrop(m_dragObj.id, m_lastMouseover);
	}

	//cleanup
	if (m_ie5)
		document.detachEvent('onmousemove',_mmove);
	else
		document.removeEventListener('mousemove',_mmove,false);
	    
	m_dragObjTreeId = null;
	m_dragObj = null;
	m_lastMouseover = null;
}

function _mmove(ev)
{
	var target  = (m_ie5) ? window.event.srcElement : ev.target;
	m_lastMouseover = target.id;
}

function _unescapeXml(xml)
{
	xml = xml.replace(/_{/g,"&lt;");
	xml = xml.replace(/}_/g,"&gt;");
	xml = xml.replace(/{/g,"<");
	xml = xml.replace(/}/g,">");
	xml = xml.replace(/\^/g,"\"");
	return xml;
}
function _createXmlDoc(xml)
{
	var xmlDoc = null;
	if (m_ie5 != true )
	{
		//create new document from string
		xmlDoc = new DOMParser().parseFromString(xml, "text/xml");
	}
	else
	{
		xmlDoc = new ActiveXObject("Msxml2.DOMDocument");
		xmlDoc.loadXML(xml);
	}
	return xmlDoc;
}
//register context menu and other mouse events
if (m_ie5)
{
	document.attachEvent('oncontextmenu',_showmenu);
	document.attachEvent('onclick',_hidemenu);
	document.attachEvent('onmouseup',_mup);
}
else
{
	document.addEventListener('contextmenu',_showmenu,false);
	document.addEventListener('click',_hidemenu,false);
	document.addEventListener('mouseup',_mup,false);
}