//TODO List:
//- make toolbar work better
//- add images!
//- allow Tree.render() to start somewhere other than the root
//- make it work for non-DHTML browsers
//- allow nodes to be added in any order? - probs. with Tree.collapse(), TreeNode()
//- fix IE "close" button error?

/**
 * One individual node on the tree.
 *
 * parent   a pointer to node's parent
 * id       unique ID (string)
 * text     text to display in the browser
 * img      optional URL of an image to display; shows up to the left of the node's text
 */
function TreeNode(parent, id, text, img)
{
  this.parent   = parent;
  this.id       = id;
  this.text     = text;
  this.img      = img;
  this.expanded = false;
  this.childNum = this.parent ? this.parent.children.length : 0;
  this.level    = this.parent ? this.parent.level + 1 : -1;
  this.children = new Array();
}

TreeNode.prototype.setExpanded = TreeNode_setExpanded;
TreeNode.prototype.getExpanded = TreeNode_getExpanded;
TreeNode.prototype.next        = TreeNode_next;
TreeNode.prototype.go          = TreeNode_go;
TreeNode.prototype.toString    = TreeNode_toString;
TreeNode.prototype.getHTML     = TreeNode_getHTML;

/**
 * Changes node's plus/minus graphic.
 *
 * @name   setExpanded(expanded)
 * @parent TreeNode
 * @type   Method
 *
 * @param  expanded  if true sets icon to
 */
function TreeNode_setExpanded(expanded)
{
	if (this.children.length != 0)
	{
	  this.expanded = expanded;
		if (Tree.ie)
		{
			var image = document.all[this.id + '-img'];
		}
		else if (Tree.ns6)
		{
			var image = document.getElementById(this.id + '-img');
		}
		else if (Tree.ns4)
		{
			var image = Tree.getElementStyle(this.id + '-pm', this.id).document.images[0];
		}
		image.src = this.expanded ? Tree.minusURL : Tree.plusURL;		
	}
}

function TreeNode_getExpanded()
{
  return this.expanded;
}

function TreeNode_next()
{
  if (this.children.length != 0)
  {
    return this.children[0];
  }
  else
  {
    var currNode = this;
    var parentNode = currNode.parent;
    while (parentNode != null)
    {
      if (currNode.childNum < parentNode.children.length-1)
      {
        return parentNode.children[currNode.childNum + 1];
      }
      currNode = parentNode;
      parentNode = currNode.parent;
    }
    //if we get here then we're at the last node so return null
    return null;
  }
}

function TreeNode_go()
{
  if (this.id.indexOf("$") != 0)
  {
  //alert('HERE');
    top.frames[2].location.href = this.id;
  }
  else
  {
  //alert("go:" + this.id);
  t.swap(this.id);
  
  }
  /*
  var d = top.frames[1].document;
  d.open();
  d.write('<html><head></head><body>');
  d.write('<h1>' + this.text + '</h1>');
  d.write('
  d.write('<table border="1">');
  d.write('<tr>');
  d.write('  <td>ID:</td>');
  d.write('  <td>' + this.id + '</td>');
  d.write('</tr>');
  d.write('<tr>');
  d.write('  <td>Parent ID:</td>');
  d.write('  <td>' + this.parent.id + '</td>');
  d.write('</tr>');
  d.write('</table>');
  d.write('</body></html>');
  d.close();
  */
}

function TreeNode_toString()
{
  return '<a href="javascript:t.nodeHash[\'' + this.id + '\'].go();">' + this.text + '</a>';
  //return "<a target='mainframe' href='" + this.id + "'>"+this.text+"</a>";
}

function TreeNode_getHTML(name)
{
  var str = "";
  var tempstr = "";
  var currNode = this;
	var leftIndex = this.level;

	if (Tree.ie || Tree.ns6)
	{
		str = str
			+ '\n\t<table border="0" cellpadding="0" cellspacing="0" height="' + Tree.lineHeight + '">'
			+ '\n\t<tr>';			
	}
	else
	{
		str = str
			+ '\n\t<table border="0" cellpadding="0" cellspacing="0">'
			+ '\n\t<tr>'
			+ '\n\t\t<td width="' + Tree.hLineWidth * (this.level+1) + '" nowrap></td>'
			+ '\n\t\t<td align="center" valign="center" nowrap>' + this + '</td>'
			+ '\n\t</tr>'
			+ '\n\t</table>';
	}

	//build lines, blank spaces, etc. (from right to left)
	while (currNode.parent != null)
	{
		//figure out if we want |-, L, or inverted L right before text
		if (currNode == this)
		{
			//last child of parent -> L
			if (this.childNum == this.parent.children.length - 1)
			{
				tempstr += TreeNode.L(leftIndex);
			}
			//first node in the tree -> inverted L
			else if (this.parent.parent == null)
			{
				tempstr += TreeNode.invertedL(leftIndex);
			}
			//not first or last -> |-
			else
			{
				tempstr += TreeNode.plus(leftIndex);
			}
		}
		//last child of parent -> blank
		else if (currNode.childNum == currNode.parent.children.length-1)
		{
			tempstr = TreeNode.blank(leftIndex) + tempstr;
		}
		//otherwise -> |
		else
		{
			tempstr = TreeNode.vLine(leftIndex) + tempstr;
		}
		currNode = currNode.parent;
		leftIndex--;
	}

	str = str + tempstr;

	if (Tree.ie || Tree.ns6)
	{
		if (this.img)
		{
			str += '\n\t\t<td rowspan="2"><img src="' + this.img + '"></td>'
			str += '\n\t\t<td width="2"></td>';
		}
		str = str
			+ '\n\t\t<td rowspan="2" nowrap>' + this + '</td>'
			+ '\n\t</tr>';
		//last child of parent -> write blank part of L
		if (this.childNum == this.parent.children.length - 1)
		{
			str += TreeNode.L2();
		}
		//first node in the tree -> write the vertical line part of inverted L
		else if (this.parent.parent == null)
		{
			str += TreeNode.invertedL2();
		}
		str += '\n\t</table>';
    if (this.children.length != 0)
    {
      str += '\n\t<div'
        + ' id="' + this.id + '-pm"'
        + ' style="'
        + ' position: absolute;'
        + ' top: ' + (Tree.lineHeight / 2 - 7) + 'px;'
        + ' left: ' + (this.level * Tree.hLineWidth + 3) + 'px;"'
        + ' onclick="javascript:' + name + '.swap(\'' + this.id + '\')">'
        + '<img id="' + this.id + '-img" border="0" src="' + Tree.plusURL + '">'
        + '</div>';      
    }
	}
  //add plus or minus sign
	if (Tree.ns4 && this.children.length != 0)
	{
		str += '\n\t<div'
			+ ' id="' + this.id + '-pm"'
			+ ' style="'
			+ ' position: absolute;'
			+ ' top: ' + (Tree.lineHeight / 2 - 7) + 'px;'
			+ ' left: ' + (this.level * Tree.hLineWidth + 3) + 'px;">'
			+ '<a href="javascript:' + name + '.swap(\'' + this.id + '\')">'
			+ '<img id="' + this.id + '-img" border="0" src="' + Tree.plusURL + '">'
			+ '</a>'
			+ '</div>';
	}

  return str;
}

////////////////////////////////// Tree CLASS //////////////////////////////////

function Tree(name, y)
{
	this.name  = name;
	this.y     = y;

  //should be a pretty unique identifier
  this.ROOT_ID = (new Date()).toString().replace(/ /g, "");
  this.ROOT = new TreeNode(null, this.ROOT_ID, '', '');

  this.nodeHash = new Array();
  this.nodeHash[this.ROOT_ID] = this.ROOT;

  this.nodeArray = new Array();

  //calculate line height
  if (Tree.ie || Tree.ns6)
  {
    document.write('<div id="' + this.ROOT_ID + '-lhcalc" style="visibility: hidden"><a href="">&nbsp;</a></div>');
    Tree.lineHeight = Tree.getElementById(this.ROOT_ID + '-lhcalc').offsetHeight;
  }
  else if (Tree.ns4)
  {
    document.write('<layer id="' + this.ROOT_ID + '-lhcalc" visibility="hide"><a href="">&nbsp;</a></layer>');
    Tree.lineHeight = Tree.getElementById(this.ROOT_ID + '-lhcalc').clip.height;
  }
  Tree.lineHeight = Math.max(Tree.lineHeight, 16);
  //increase Tree.lineHeight so that it is divisible by 4
  Tree.lineHeight += Tree.lineHeight % 4;
}

//Probably don't work but you never know
Tree.prototype.getIndex    = Tree_getIndex;
Tree.prototype.getTextTree = Tree_getTextTree;

Tree.prototype.add         = Tree_add;
Tree.prototype.toString    = Tree_toString;

Tree.prototype.swap        = Tree_swap;
Tree.prototype.expand      = Tree_expand;
Tree.prototype.collapse    = Tree_collapse;
Tree.prototype.expandAll   = Tree_expandAll;
Tree.prototype.collapseAll = Tree_collapseAll;

Tree.prototype.render      = Tree_render;
Tree.prototype.showTree    = Tree_showTree;
Tree.prototype.showIndex   = Tree_showIndex;

function Tree_getIndex()
{
  var str = '';

  this.nodeArray.sort(Tree.sortFunc);

	if (Tree.ie || Tree.ns6)
	{
		str = '<div'
			+ ' id="' + this.ROOT_ID + '-index"'
			+ ' style="'
			+ ' position: absolute;'
			+ ' left: 5;'
			+ ' top: ' + this.y + ';'
			+ ' visibility: hidden;">';
	}
	else if (Tree.ns4)
	{
		str = '<layer'
			+ ' id="' + this.ROOT_ID + '-index"'
			+ ' left="5"'
			+ ' top="' + this.y + '"'
			+ ' visibility="hide">';
	}

	for (var i=0; i<this.nodeArray.length; i++)
  {
    str += this.nodeArray[i] + '<br>\n';
  }

	if (Tree.ie || Tree.ns6)
	{
		str += '</div>';
	}
	else if (Tree.ns4)
	{
		str += '</layer>';
	}

  return str;
}

/**
 * Prints out a plain text representation of the tree
 *
 * @param level     The current level of the tree (used to calculate
 *                  indentation)
 * @param currItem  The current TreeNode we are examining
 */
function Tree_getTextTree(level, currItem)
{
  var i, str = '';
  //fill in missing parameters
  if (level == null)
  {
    var level = -1;
  }
  if (currItem == null)
  {
    var currItem = this.ROOT;
  }

  //write out the necessary indentation
  for (i=0; i<level; i++)
  {
    str += '  ';
  }
  if (currItem != this.ROOT)
  {
    str += currItem.text + '\n';    
  }
  i = 0;
  while (i < currItem.children.length)
  {
    str += this.getTextTree(level + 1, currItem.children[i]);
    i++;
  }
  return str;
}

function Tree_add(parentId, id, text, img)
{
	if (this.nodeHash[parentId] == null) {
		this.nodeHash[parentId] = new TreeNode(null, parentId, null, null);
	}
	if (this.nodeHash[id] == null) {
		this.nodeHash[id] = new TreeNode(this.nodeHash[parentId], id, text, img);
	}
	else
	{
		this.nodeHash[id].parent = this.nodeHash[parentId];
		this.nodeHash[id].text = text;
		this.nodeHash[id].img = img;
	}

  this.nodeHash[parentId].children[this.nodeHash[parentId].children.length] = this.nodeHash[id];
  this.nodeArray[this.nodeArray.length] = this.nodeHash[id];
}

function Tree_toString()
{
  var currNode = this.ROOT;
  var str = "";

	//Tree portion
	while ((currNode = currNode.next()) != null)
  {
    if (Tree.ie || Tree.ns6)
    {
      str += '<div'
        + ' id="' + currNode.id + '"'
        + ' style="'
        + ' position: absolute;'
        + ' left: 0;'
        + ' visibility: hidden;">'
        + currNode.getHTML(this.name)
        + '\n</div>\n';
    }
    else if (Tree.ns4)
    {
      str += '<layer'
        + ' id="' + currNode.id + '"'
        + ' left="0"'
        + ' visibility="hide">'
        + currNode.getHTML(this.name)
        + '\n</layer>\n';
    }
  }


	//Index portion
  this.nodeArray.sort(Tree.sortFunc);
	if (Tree.ie || Tree.ns6)
	{
		str += '<div'
			+ ' id="' + this.ROOT_ID + '-index"'
			+ ' style="'
			+ ' position: absolute;'
			+ ' left: 5;'
			+ ' top: ' + this.y + ';'
			+ ' visibility: hidden;">';
	}
	else if (Tree.ns4)
	{
		str += '<layer'
			+ ' id="' + this.ROOT_ID + '-index"'
			+ ' left="5"'
			+ ' top="' + this.y + '"'
			+ ' visibility="hide">';
	}
	for (var i=0; i<this.nodeArray.length; i++)
  {
    str += '<nobr>' + this.nodeArray[i] + '<br>\n';
  }
	if (Tree.ie || Tree.ns6)
	{
		str += '</div>';
	}
	else if (Tree.ns4)
	{
		str += '</layer>';
	}


	return str;
}

function Tree_swap(nodeId)
{
//alert(nodeId);
//alert(this.nodeHash[nodeId]);
  this.nodeHash[nodeId].getExpanded() ? this.collapse(nodeId) : this.expand(nodeId);
}

function Tree_expand(nodeId, skipRender)
{

  var node = this.nodeHash[nodeId];
  node.setExpanded(true);
  for (var i=0; i<node.children.length; i++)
  {
    Tree.getElementStyle(node.children[i].id).visibility = Tree.visible;
    if (node.children[i].getExpanded())
    {
      this.expand(node.children[i].id, true);
    }
  }
	if (!skipRender)
	{
		t.render();		
	}
}

function Tree_collapse(nodeId)
{
  var start = this.nodeHash[nodeId];
  var currNode = start.next();
  start.setExpanded(false);
  while (currNode != null && currNode.level > start.level)
  {
    Tree.getElementStyle(currNode.id).visibility = Tree.hidden;
    currNode = currNode.next();
  }
	t.render();
}

function Tree_expandAll()
{
  for (var i=0; i<this.nodeArray.length; i++)
  {
    if (this.nodeArray[i].children.length != 0)
    {
			this.nodeArray[i].setExpanded(true);
    }
    Tree.getElementStyle(this.nodeArray[i].id).visibility = Tree.visible;
  }
  this.render();
}

function Tree_collapseAll()
{
  for (var i=0; i<this.nodeArray.length; i++)
  {
    if (this.nodeArray[i].parent == this.ROOT)
    {
      Tree.getElementStyle(this.nodeArray[i].id).visibility = Tree.visible;
    }
    else
    {
      Tree.getElementStyle(this.nodeArray[i].id).visibility = Tree.hidden;
    }
		this.nodeArray[i].setExpanded(false);
  }
  this.render();
}

function Tree_render()
{
  var visibleCount = 0;
  var currNode = this.ROOT;

  while ((currNode = currNode.next()) != null)
  {
    if (Tree.getElementStyle(currNode.id).visibility == Tree.visible)
    {
      Tree.getElementStyle(currNode.id).top = this.y + Tree.lineHeight * (visibleCount++);
    }
  }
}

function Tree_showTree()
{
	Tree.getElementStyle(this.ROOT_ID + '-index').visibility = Tree.hidden;
	this.collapseAll();
}

function Tree_showIndex()
{
	for (var i=0; i<this.nodeArray.length; i++)
	{
		Tree.getElementStyle(this.nodeArray[i].id).visibility = Tree.hidden;
	}
	Tree.getElementStyle(this.ROOT_ID + '-index').visibility = Tree.visible;
}

//////////////////////// STATIC PROPERTIES AND METHODS /////////////////////////

Tree.preloadImages   = sTree_preloadImages;
Tree.getElementById  = sTree_getElementById;
Tree.getElementStyle = sTree_getElementStyle;
Tree.sortFunc        = sTree_sortFunc;
Tree.ie  = (navigator.appName == 'Microsoft Internet Explorer' && document.all != null);
Tree.ns4 = (navigator.appName == 'Netscape' && document.layers != null);
Tree.ns6 = (navigator.appName == 'Netscape' && navigator.appVersion.substring(0, 1) == '5');
Tree.clearPixelURL = 'images/clearpixel.gif';
Tree.hLineURL      = 'images/hline.gif';
Tree.vLineURL      = 'images/vline.gif';
Tree.plusURL       = 'images/plus.gif';
Tree.minusURL      = 'images/minus.gif';
Tree.hLineWidth    = 19;

if (Tree.ie || Tree.ns6)
{
  Tree.visible = 'visible';
  Tree.hidden = 'hidden';
}
else if (Tree.ns4)
{
  Tree.visible = 'show';
  Tree.hidden = 'hide';
}
Tree.preloadImages(Tree.clearPixelURL, Tree.hLineURL, Tree.vLineURL, Tree.plusURL, Tree.minusURL);

function sTree_preloadImages()
{
	var tmpImage = new Image();
	for (var i=0; i<arguments.length; i++)
	{
		tmpImage.src = arguments[i];
	}
}

function sTree_getElementById(id, parentId)
{
  if (Tree.ie)
  {
    return document.all[id];
  }
  else if (Tree.ns4)
  {
		if (parentId)
		{
			return document.layers[parentId].layers[id];
		}
		else
		{
	    return document.layers[id];
		}
  }
  else if (Tree.ns6)
  {
    return document.getElementById(id);
  }
}

function sTree_getElementStyle(id, parentId)
{
	if (Tree.ie || Tree.ns6)
	{
		return Tree.getElementById(id, parentId).style;
	}
	else if (Tree.ns4)
	{
		return Tree.getElementById(id, parentId);
	}
}

function sTree_sortFunc(a, b)
{
  if (a.text.toLowerCase() < b.text.toLowerCase())
  {
    return -1;
  }
  else if (a.text.toLowerCase() == b.text.toLowerCase())
  {
    return 0;
  }
  else
  {
    return 1;
  }
}

/////////////// STATIC TreeNode METHODS USED BY TreeNode.getHTML ///////////////

if (Tree.ie || Tree.ns6)
{
	TreeNode.vLine = function()
	{
		return '\n\t\t<td'
			+ ' rowspan="2"'
			+ ' background="' + Tree.vLineURL + '"'
			+ ' width="' + Tree.hLineWidth + '"'
			+ ' nowrap>'
      + '<img src="' + Tree.clearPixelURL + '">'
			+ '</td>';
	}

	TreeNode.blank = function()
	{
		return '\n\t\t<td'
			+ ' rowspan="2"'
			+ ' width="' + Tree.hLineWidth + '"'
			+ ' nowrap>'
			+ '</td>';
	}

	TreeNode.plus = function()
	{
		return '\n\t\t<td'
			+ ' rowspan="2"'
			+ ' background="' + Tree.vLineURL + '"'
			+ ' width="' + Tree.hLineWidth + '"'
			+ ' valign="center">'
			+ '<img src="' + Tree.hLineURL + '">'
			+ '</td>';
	}

	TreeNode.L = function()
	{
		return '\n\t\t<td'
			+ ' background="' + Tree.vLineURL + '"'
			+ ' width="' + Tree.hLineWidth + '"'
			+ ' height="' + Tree.lineHeight/2 + '"'
			+ ' valign="bottom">'
			+ '<img src="' + Tree.hLineURL + '">'
			+ '</td>';
	}

	TreeNode.L2 = function()
	{
		return '\n\t<tr>'
			+ '\n\t\t<td'
			+ ' width="' + Tree.hLineWidth + '"'
			+ ' height="' + Tree.lineHeight/2 + '">'
			+ '</td>'
			+ '\n\t</tr>';
	}
	
	TreeNode.invertedL = function()
	{
		return '\n\t\t<td'
			+ ' width="' + Tree.hLineWidth + '"'
			+ ' height="' + Tree.lineHeight/2 + '"'
			+ ' valign="bottom">'
			+ '<img src="' + Tree.hLineURL + '">'
			+ '</td>';
	}

	TreeNode.invertedL2 = function()
	{
		return '\n\t<tr>'
			+ '\n\t\t<td'
			+ ' background="' + Tree.vLineURL + '"'
			+ ' width="' + Tree.hLineWidth + '"'
			+ ' height="' + Tree.lineHeight/2 + '">'
      + '<img src="' + Tree.clearPixelURL + '">'
			+ '</td>'
			+ '\n\t</tr>';
	}
}
else if (Tree.ns4)
{
	TreeNode.vLine = function (leftIndex)
	{
		return '\n\t<layer'
		+ ' top="0"'
		+ ' left="' + leftIndex * Tree.hLineWidth + '"'
		+ ' height="' + Tree.lineHeight + '"'
		+ ' width="' + Tree.hLineWidth + '"'
		+ ' background="' + Tree.vLineURL + '">'
    + '<img src="' + Tree.clearPixelURL + '">'
		+ '\n\t</layer>';
	}

	TreeNode.blank = function (leftIndex)
	{
		return '';
	}

	TreeNode.plus = function (leftIndex)
	{
		return '\n\t<layer'
		+ ' top="0"'
		+ ' left="' + leftIndex * Tree.hLineWidth + '"'
		+ ' height="' + Tree.lineHeight + '"'
		+ ' width="' + Tree.hLineWidth + '"'
		+ ' background="' + Tree.vLineURL + '">'
		+ '\n\t\t<table border="0" cellpadding="0" cellspacing="0" width="100%" height="' + Tree.lineHeight + '">'
		+ '\n\t\t<tr>'
		+ '\n\t\t\t<td valign="center" align="right"><img src="' + Tree.hLineURL + '"></td>'
		+ '\n\t\t</tr>'
		+ '\n\t\t</table>'
		+ '\n\t</layer>';
	}

	TreeNode.L = function (leftIndex)
	{
		return '\n\t<layer'
		+ ' top="0"'
		+ ' left="' + leftIndex * Tree.hLineWidth + '"'
		+ ' height="' + Tree.lineHeight/2 + '"'
		+ ' width="' + Tree.hLineWidth + '"'
		+ ' background="' + Tree.vLineURL + '">'
		+ '\n\t\t<table border="0" cellpadding="0" cellspacing="0" width="100%" height="' + Tree.lineHeight/2 + '">'
		+ '\n\t\t<tr>'
		+ '\n\t\t\t<td valign="bottom" align="right"><img src="' + Tree.hLineURL + '"></td>'
		+ '\n\t\t</tr>'
		+ '\n\t\t</table>'
		+ '\n\t</layer>';
	}
	
	TreeNode.invertedL = function (leftIndex)
	{
		return '\n\t<layer'
		+ ' top="' + (Tree.lineHeight/2-2) + '"'
		+ ' left="' + leftIndex * Tree.hLineWidth + '"'
		+ ' height="' + (Tree.lineHeight/2+2) + '"'
		+ ' width="' + Tree.hLineWidth + '"'
		+ ' background="' + Tree.vLineURL + '">'
		+ '\n\t\t<table border="0" cellpadding="0" cellspacing="0" width="100%" height="2">'
		+ '\n\t\t<tr>'
		+ '\n\t\t\t<td><img src="' + Tree.clearPixelURL + '" width="1" height="1"></td>'
		+ '\n\t\t</tr>'
		+ '\n\t\t<tr>'
		+ '\n\t\t\t<td><img src="' + Tree.hLineURL + '"></td>'
		+ '\n\t\t</tr>'
		+ '\n\t\t</table>'
		+ '\n\t</layer>';
	}
}