function isUndefined(o) {
	return (typeof(o)=="undefined");
}
function isEmpty(o) {
	return isUndefined(o) || o==null || (typeof(o)=="string" && o.length==0);
}
function getObj(id) {
	var o;
	if (isUndefined(document.all)) {
		o = document.getElementById(id);
	} else {
		o = document.all[id];
	}
	if (isUndefined(o))
		o = null;
	return o;
}
function argumentsToArray(args,fromIdx) {
	var a = new Array();
	for(var i=fromIdx; i<args.length; i++) {
		a.push( args[i] );
	}
	return a;
}
function escapeHtml(s) {
	s = s.replace(new RegExp("<","g"),"&lt;");
	s = s.replace(new RegExp(">","g"),"&gt;");
	s = s.replace(new RegExp('"',"g"),"&quot;");
	return s;
}
/**

*/
function TreeNode(expanded,nodeClass,cols) {	
	if (typeof(cols)=="string") {
		cols = argumentsToArray(arguments,2);
	}
	this.TreeNode(null,cols,expanded,nodeClass);
}
TreeNode.prototype.TreeNode = function(id,columns,expanded,nodeClass) {
	this.id = id;
	this.columns = columns;
	this.expanded = (isUndefined(expanded)?false:expanded);
	this.children = new Array();
	this.parent = null;
	this.nodeClass = nodeClass;
}
TreeNode.prototype.hasChildren = function() {
	return (!isUndefined(this.children) && this.children.length>0);
}
TreeNode.prototype.isExpanded = function() {
	return this.expanded;
}
TreeNode.prototype.setExpanded = function(expanded) {
	if (this.expanded==expanded)
		return;
	this.expanded = expanded;
	var img = this.getExpander();
	if (img!=null) {
		var root = this.getRoot();
		img.src = (this.expanded?root.getSrcMinus():root.getSrcPlus());
		this.showChildren( this.expanded );
	}
}
TreeNode.prototype.isVisible = function() {
	var p = this.getParent();
	if (p==null)
		return true;
	return p.isExpanded() && p.isVisible();
}
TreeNode.prototype.addNode = function(node) {
	this.children.push( node );
	node.parent = this;
	var root = this.getRoot();
	if (root!=null)
		root.addNodeToMap( node );
	return this;
}
TreeNode.prototype.show = function(show) {
	var row = this.getRow();
	if (row==null) {
		return;
	}
	var display = (show?"":"none");
	row.style.display = display;	
	//row.height = 1;
	this.showChildren( show && this.expanded );
}
TreeNode.prototype.showChildren = function(show) {
	if (!this.hasChildren())
		return;
	for(var i=0; i<this.children.length; i++) {
		var child = this.children[i];
		child.show(show);
	}
}
TreeNode.prototype.getParent = function() {
	return this.parent;
}
TreeNode.prototype.getRoot = function() {
	var p = this.parent;
	if (p!=null)
		return p.getRoot();
	return null;
}
TreeNode.prototype.getExpander = function() {
	var id = this.getId()+"te";
	return getObj(id);
}
TreeNode.prototype.getRow = function() {
	var id = this.getId()+"tr";
	return getObj(id);
}
TreeNode.prototype.getId = function() {
	if (isUndefined(this.id) || this.id==null)
		this.id = this.getRoot().getNextId();
	return this.id;
}
TreeNode.prototype.toHtml = function() {
	var i, col, root = this.getRoot();
	var level = this.getLevel();
	var rootdeep = root.getDeep();
	var pmwidth = root.getPlusMinusWidth();
	var s = "<tr id='"+this.getId()+"tr'"+(isEmpty(this.nodeClass)?"":" class='"+this.nodeClass+"'")+(this.isVisible()?"":" style='display:none'")+">";
		for(i=0; i<this.columns.length; i++) {
			col = this.columns[i];
			var colClass = root.getColClass(i);
			if (i==0) {
				s+= "<td>";
				s += "<table width=100% cellspacing=0 cellpadding=0><tr><td width="+(pmwidth*level)+"px nowrap class='treeexp'>";
				var indent = pmwidth*(level-1);
				if (this.hasChildren()) {
					s += "<img width="+indent+" height=1>";
					s += "<img class='treeexp' id='"+this.getId()+"te' src='"+(this.isExpanded()?root.getSrcMinus():root.getSrcPlus())+"' onclick='"+root.getId()+".switchNode(\""+this.getId()+"\")'>";
				} else {
					indent += pmwidth;
					s += "<img width="+indent+" height=1>";
				}
				s += "</td><td width=99% class='"+colClass+"'>"+col+"</td></tr></table>";
				s += "</td>";
			} else {
				s += "<td class='"+colClass+"'>"+col+"</td>";
			}
		}
	s += "</tr>";
	if (this.hasChildren()) {
		for(i=0; i<this.children.length; i++) {
			var child = this.children[i];
			s += child.toHtml();
		}
	}
	return s;
}
TreeNode.prototype.getDeep = function() {
	var i;
	if (isUndefined(this._deep)) {
		this._deep = 0;
		if (this.hasChildren()) {
			for(i=0; i<this.children.length; i++) {
				var deep = this.children[i].getDeep();
				this._deep = Math.max(this._deep,deep);
			}
			this._deep++;
		}
	}
	return this._deep;
}
TreeNode.prototype.getLevel = function() {
	var i;
	if (isUndefined(this._level)) {
		this._level = 0;
		var parent = this.getParent();
		if (parent!=null) {
			this._level = parent.getLevel();
			this._level++;
		}
	}
	return this._level;
}

function TreeRoot(id,nodeClass,srcPlus,srcMinus,plusMinusWidth) {
	this.TreeRoot(id,nodeClass,null,null,srcPlus,srcMinus,plusMinusWidth);
}
TreeRoot.prototype = new TreeNode();
TreeRoot.prototype.TreeRoot = function(id,nodeClass,columns,colClasses,srcPlus,srcMinus,plusMinusWidth) {
	this.TreeNode( id, columns, true, nodeClass );
	this.colClasses = colClasses;
	this.srcPlus = srcPlus;
	this.srcMinus = srcMinus;
	this.plusMinusWidth = plusMinusWidth;
	this._nodeMap = new Object();
}
TreeRoot.prototype.getRoot = function() {
	return this;
}
TreeRoot.prototype.getSrcPlus = function() {
	return this.srcPlus;
}
TreeRoot.prototype.getSrcMinus = function() {
	return this.srcMinus;
}
TreeRoot.prototype.getPlusMinusWidth = function() {
	return this.plusMinusWidth;
}
TreeRoot.prototype.getNextId = function() {
	if (isUndefined(this._nextId))
		this._nextId = 0;
	else
		this._nextId++;
	return this.id+"n"+this._nextId;
}
TreeRoot.prototype.addNodeToMap = function(node) {
	var nodeId = node.getId();
	this._nodeMap[nodeId] = node;
	if (node.hasChildren()) {
		for(var i=0; i<node.children.length; i++) {
			this.addNodeToMap( node.children[i] );
		}
	}
}
TreeRoot.prototype.switchNode = function(nodeId) {
	var node = this.getNode(nodeId);
	if (node==null) {
		alert("no node for '"+nodeId+"'");
		return;
	}
	node.setExpanded( !node.isExpanded() );
}
TreeRoot.prototype.getNode = function(nodeId) {
	var node = this._nodeMap[nodeId];
	if (isUndefined(node))
		return null;
	return node;
}
TreeRoot.prototype.toHtml = function() {
	var i, s = "";
	s += "<table"+(isEmpty(this.nodeClass)?"":" class='"+this.nodeClass+"'")+" id='"+this.getId()+"'>";
	if (!isUndefined(this.columns) && this.columns!=null) {
		s += "<tr id='"+this.getId()+"' class='treehead'>";
			for(i=0; i<this.columns.length; i++) {
				col = this.columns[i];
				s += "<th>"+col+"</th>";
			}
		s += "</tr>";
	}
	if (this.hasChildren()) {
		for(i=0; i<this.children.length; i++) {
			var child = this.children[i];
			s += child.toHtml();
		}
	}
	s += "</table>";
	return s;
}
TreeRoot.prototype.getColClass = function(colIdx) {
	if (isUndefined(this.colClasses) || this.colClasses==null || colIdx>=this.colClasses.length)
		return "treecol"+colIdx;
	return this.colClasses[colIdx];
}
TreeNode.prototype.setColClasses = function(colClasses) {	
	if (typeof(colClasses)=="string") {
		colClasses = argumentsToArray(arguments,0);
	}
	this.colClasses = colClasses;
}
TreeNode.prototype.setColumns = function(columns) {	
	if (typeof(columns)=="string") {
		columns = argumentsToArray(arguments,0);
	}
	this.columns = columns;
}

function getNodeId(v) {
	var idx = v.lastIndexOf("_");
	if (idx==-1)
		return v;
	return v.substring(0,idx);
}
function getParentId(v) {
	var idx = v.lastIndexOf("_");
	if (idx==-1)
		return null;
	return v.substring(idx+1);
}
function isExpanded(node) {
	var o = getObj(node+"te");
	if (o==null) {
		//alert("cannot find img '"+node+"te' for node "+node);
		return true;
	}
	return o.src.indexOf("tree.minus.")!=-1;
}
function tree(img,node) {
	var src = img.src, expanded;
	if (src.indexOf('tree.plus.')==-1) {
		src = src.replace('tree.minus.','tree.plus.');
		expanded = false;
	} else {
		src = src.replace('tree.plus.','tree.minus.');
		expanded = true;
	}
	img.src = src;
	var row = getObj(node);
	if (row==null) {
		alert("cannot find row '"+node+"'");
		return;
	}
	row.ex = expanded;
	var showChildrenMap = new Object();
	showChildrenMap[getNodeId(node)] = expanded;
	var idx = row.rowIndex;
	//alert("idx "+idx+" node "+node);
	var table = row.parentNode.parentNode;
	var count = 0;
	for(var i=idx+1; i<table.rows.length; i++) {
		var r = table.rows[i];
		var visible, parentNode = getParentId(r.id);
		visible = showChildrenMap[parentNode];
		//alert("r "+r+" parentNode "+parentNode+" visible "+visible);
		if (isUndefined(visible))
			break;
		count++;
		r.style.display = (visible?"":"none");
		expanded = r.ex;
		if (isUndefined(expanded)) {
			expanded = isExpanded(r.id);
			r.ex = expanded;
		}
		showChildrenMap[getNodeId(r.id)] = visible&&(expanded==true);
	}
	//alert("tree processed "+(count)+" nodes");
}
