Bw.Widgets.Tree =
{
	superclassName: "Bw.Widgets.Widget",
	selfclassName: "Bw.Widgets.Tree",
	
	create: function ()
	{
		var obj = document.createElement ('div');
		
		Bw.Core.bootstrap (obj, "Bw.Widgets.Tree");
		
		return obj;
	},
	
	initialize: function ()
	{
		Bw.Widgets.Widget.initialize.call (this);
		
		this.selection = null;
		this.columns = [];
		
		this.draw();
		
		this.rootPath = this.getAttribute ("rootPath");
		this.treeNodeName = this.getAttribute ("treeNodeName");
		this.treeLeafName = this.getAttribute ("treeLeafName");
		this.rowValue = this.getAttribute ("rowValue");
				
		var att = this.getAttribute ("canToggleRoot");
		this.canToggleRoot = (typeof att != "undefined" && att == 'true');
		
		var self = this;
		
		this.onselect = this.getAttribute ("onselect");
		this.onaction = this.getAttribute ("onaction");
		this.onupdate = this.getAttribute ("onupdate");

		this.onkeydown = function (e) { return self.keyPressed (Bw.getEvent (e)); };
		this.onkeypress = function (e) { return false; };
		
		this.initColumns();
		
		this.src = this.getAttribute ("src");
		if (this.src && !this.getFirstNode())
		{
			this.appendXML (this, this.src, null);
		}
		
		return false;
	},
	
	draw: function ()
	{
		with (this.style)
		{
			overflow = "auto";
			cursor = "default";
			MozUserInput = "enabled";
			MozUserFocus = "normal";
		}
		
		this.tabIndex = 0;	
	},
	
	initColumns: function ()
	{
		var c = this.firstChild;
		var n;
		
		while (c != null)
		{
			n = c.nextSibling;
				
			if (c.className == "Bw.Widgets.List.Column")
			{
				var col = Bw.Widgets.List.Column.create (this);
				
				col.setName (c.getAttribute ("name"));
				col.setAlign (c.getAttribute ("align"));
				col.loadRenderCode (c.getAttribute ("render"));
				
				var s = c.getAttribute ("size");
				if (typeof s == "string")
				{
					col.setSize (c.getAttribute ("size"));
				}
				
				this.columns.push (col);
				
				this.removeChild (c);
			}
			
			c = n;
		}
	},
	
	appendXML: function (parent, src, cb)
	{
		var m = src;
		if (typeof m == "string")
		{
			var q = Bw.IO.Query.create ();
			q.setNoCache();
			
			var self = this;
			var uri = (parent != this) ? src + parent.value : src; 
			q.get (uri, function () { self._asyncAppend (parent, q.getMessage(), cb); } );
		}
		else
		{
			this._asyncAppend (parent, m, cb);
		}
	},
	
	_asyncAppend: function (parent, m, cb)
	{
		var root = m.getNode (this.rootPath);
		if (root)
		{
			var recs = root.childNodes;
			var divs = this._append (parent, recs, 0);
		
			for (var i = 0; i < divs.length; i++)
			{
				Bw.Core.bootstrap (divs[i], "Bw.Widgets.Tree.Node");
			}
		}
			
		if (cb) cb();
	},
	
	_append: function (parent, recs, depth)
	{
		var divs = [];
		
		for (var i = 0; i < recs.length; i++)
		{
			var r = recs[i];
			var isLeaf = (r.nodeName == this.treeLeafName);
			var isNode = (r.nodeName == this.treeNodeName);
			
			if (!isLeaf && !isNode) continue; 
			
			var text = r.getAttribute ("label");
			var code = r.getAttribute ("value");
			var subs = (isNode) ? r.childNodes : null;
			
			var d = Bw.Widgets.Tree.Node.create (parent, r, isNode);
			divs.push (d);

			if (isNode)
			{
				this._append (d, subs, depth + 1);
			}			
		}
		
		return divs;
	},	
	
	cellFocused: function (widget)
	{
		var node = widget.parentNode.parentNode.parentNode;
		node.select();
	},

	cellUpdated: function (widget)
	{
		var node = widget.parentNode.parentNode.parentNode;
		var i = widget.cellIndex;
			
		if (this.onupdate)
		{
			this.updatedRow = node;
			this.updatedCellIndex = i;
			Bw.Util.call (this, this.onupdate);
		}
	},
		
	getSelection: function ()
	{
		return this.selection;
	},

	getCellValue: function (node, colIndex)
	{
		var c = this.getCellWidget (node, colIndex);
		return (c) ? c.getValue() : null;
	},
	
	setCellValue: function (node, colIndex, value)
	{
		var c = this.getCellWidget (node, colIndex);
		if (c) c.setValue (value);
	},
	
	getCellWidget: function (node, colIndex)
	{
		var l = node.content.childNodes;
		return (colIndex >= 0 && colIndex < l.length) ? l[colIndex] : null;
	},

	clear: function ()
	{
		var c;	
		while ((c = this.firstChild) != null)
		{
			this.removeChild (c);
		}
	
		this.selection = null;
	},

	getValue: function ()
	{
		var s = this.selection;
		return (!s)	? null : s.value;
	},
	
	getFirstNode: function ()
	{
		var c = this.firstChild;
		while (c != null)
		{
			if (Bw.instanceOf (c, Bw.Widgets.Tree.Node)) break;
			c = c.nextSibling;
		}
		return c;
	},
	
	keyPressed: function (e)
	{
		var s = this.selection;
		if (s == null)
		{
			s = this.getFirstNode();
		}
		
		switch (e.keyCode)
		{
			case 38:
				if (s.isFirst ())
				{
					s = s.getParentNode ();
				} 
				else
				{
					s = s.getPreviousNode();
					while (s.opened)
					{
						s = s.getLastNode();
					}
				}
				break;
			
			case 40:
				if (s.opened)
				{
					s = s.getFirstNode ();
				}
				else if (!s.isLast())
				{
					s = s.getNextNode ();
				}
				else
				{
					while (s != null && s.isLast())
					{
						s = s.getParentNode ();
					}
					
					if (s != null)
					s = s.getNextNode();
				}
				break;
			
			case 37:
			case 39:
				if (this.canToggleRoot || !s.isRoot())
				{
					s.toggle();
				}
				break;
			
			case 13:
				Bw.Util.call (this, this.onaction);
				return true;
				
			default:
				return true;
		}

		if (s != null)
		{
			s.select ();
			this.scrollToNode (s);
		}
		
		return false;
	},
	
	scrollToNode: function (n)
	{
		n = n.leaf;
		if (n.offsetTop < this.scrollTop)
		{
			this.scrollTop = n.offsetTop;
		}
		else if ((n.offsetTop + n.offsetHeight) > (this.scrollTop + this.clientHeight))
		{
			this.scrollTop = n.offsetTop - this.clientHeight + n.offsetHeight;
		}
	}
};

Bw.Widgets.Tree.Node =
{
	superclassName: "Bw.Widgets.Widget",
	selfclassName: "Bw.Widgets.Tree.Node",
	
	create: function (parent, xmlRec, hasChild)
	{
		var obj = document.createElement ('div');
		obj.xmlRec = xmlRec;
		
		if (hasChild)
		{
			obj.setAttribute ("hasChild", true);
		}
		
		obj.className = "Bw.Widgets.Tree.Node";
		
		parent.appendChild (obj);
		
		return obj;
	},
	
	initialize: function ()
	{
		Bw.Widgets.Widget.initialize.call (this);
		
		this.hasChild = this.getAttribute ("hasChild");
		
		this.opened = this.hasChildren();
		
		this.draw (this.xmlRec);
		
		this.xmlRec = null;
		
		return true;
	},
	
	draw: function (xmlRec)
	{
		var tree = this.getContainer (Bw.Widgets.Tree);

		var leaf = document.createElement ("div");
		leaf.className = "leaf";
		with (leaf.style)
		{
			verticalAlign = "middle";		
			overflow = "hidden";
			whiteSpace = "nowrap";
		}
		
		var self = this;
		leaf.onclick = function (e) { self.leafClicked (Bw.getEvent (e)); }
		leaf.ondblclick = function (e) { self.leafDoubleClicked (Bw.getEvent (e)); }
		
		if (tree.canToggleRoot || this.getParentNode())
		{
			var icon = document.createElement ("img");
			icon.style.verticalAlign = "middle";
			icon.src = this.getIconSrc ();			
			leaf.appendChild (icon);
			
			var i = icon;
			var p = this.getParentNode();
			
			while (p != null && (tree.canToggleRoot || !p.isRoot()))
			{
				var pi = document.createElement ("img");
				pi.style.verticalAlign = "middle";
				pi.src = (p.isLast()) ? Bw.themePath + "icons/tree/N.gif" : Bw.themePath + "icons/tree/I.gif";

				leaf.insertBefore (pi, i);
				i = pi;
				p = p.getParentNode();
			}
		}
		
		var c = document.createElement ("div");
		with (c.style)
		{
			display = "inline";
			verticalAlign = "middle";
		}
		leaf.appendChild (c);
		
		var l = tree.columns.length;
		for (var i = 0; i < l; i++)
		{
			var col = tree.columns[i];
			var w = col.cellContent (xmlRec);
			w.cellIndex = i;
			
			with (w.style)
			{
				display = "inline";
				if (col.size) width = col.size;
			}
			c.appendChild (w);
		}
		
		if (tree.rowValue)
		{
			var v = Bw.Xml.Helpers.getNode (xmlRec, tree.rowValue);
			this.value = Bw.Xml.Helpers.getNodeValue (v);
		}
		
		this.leaf = leaf;
		this.icon = icon;
		this.content = c;
		
		if (this.firstChild)
		{
			this.insertBefore (leaf, this.firstChild);
		}
		else
		{
			this.appendChild (leaf);
		}
	},
	
	getIconSrc: function ()
	{
		var s = this.isLast() ? "L" : "T";
		
		if (this.hasChildren() || this.hasChild)
		{
			s += this.opened ? "minus" : "plus";
		}
		
		return Bw.themePath + "icons/tree/" + s + ".gif";
	},
	
	hasChildren: function ()
	{
		return (this.getFirstNode() != null);
	},
	
	isRoot: function ()
	{
		return (this.getParentNode() == null);
	},
	
	isLast: function ()
	{
		return (this.getNextNode() == null);
	},
	
	isFirst: function ()
	{
		return (this.getPreviousNode() == null);
	},
	
	getFirstNode: function ()
	{
		return this._next (this.firstChild);
	},
	
	getLastNode: function ()
	{
		return this._prev (this.lastChild);
	},
	
	getNextNode: function ()
	{
		return this._next (this.nextSibling);
	},
	
	getPreviousNode: function ()
	{
		return this._prev (this.previousSibling);
	},
	
	getParentNode: function ()
	{
		var p = this.parentNode;
		if (!Bw.instanceOf (p, Bw.Widgets.Tree.Node))
		{
			return null;
		}
		return p;
	},
	
	_next: function (from)
	{
		var c = from;
		while (c != null)
		{
			if (Bw.instanceOf (c, Bw.Widgets.Tree.Node)) break;
			c = c.nextSibling;
		}
		return c;
	},
	
	_prev: function (from)
	{
		var c = from;
		while (c != null)
		{
			if (Bw.instanceOf (c, Bw.Widgets.Tree.Node)) break;
			c = c.previousSibling;
		}
		return c;
	},
	
	leafClicked: function (e)
	{
		if (e.target == this.icon && (this.hasChildren() || this.hasChild))
		{
			this.toggle();
		}
		else
		{
			this.select();
		}
	},
	
	leafDoubleClicked: function (e)
	{
		if (e.target == this.icon) return;
		
		var tree = this.getContainer (Bw.Widgets.Tree);
		Bw.Util.call (tree, tree.onaction);
	},
	
	select: function ()
	{
		var tree = this.getContainer (Bw.Widgets.Tree);
		var s = tree.selection;
		if (s)
		{
			var f = s.firstChild;
			f.className = "leaf";
		}
		
		var f = this.firstChild;
		f.className = "leaf selected";
		
		tree.selection = this;
		
		Bw.Util.call (tree, tree.onselect);
	},
	
	toggle: function (cb)
	{
		if (this.hasChildren())
		{
			var n = this.leaf.nextSibling;
			var d;
			while (n != null)
			{
				var s = n.style;
				if (s)
				{
					if (!d)
					{
						d = (s.display == "none") ? "block" : "none";
					}
					s.display = d;
				}
				n = n.nextSibling;
			}
			
			if (cb) cb();
		}
		else if (this.hasChild)
		{
			if (!this.opened)
			{
				var tree = this.getContainer (Bw.Widgets.Tree);
				tree.appendXML (this, tree.src, cb);				
			}
		}
		
		this.opened = !this.opened;
		this.icon.src = this.getIconSrc();		
	}
};

