
// 20091215: nodeCaptionToString()
// 20091211: toggle()
// 20091211: clipToString()
// 20091205: dTree.add()

function escapeHTML (str)
{
	var div = document.createElement("DIV");
	var text = document.createTextNode(str);
	div.appendChild(text);
	return div.innerHTML;
}

function unescapeHTML (html)
{
	var htmlNode = document.createElement("DIV");
	htmlNode.innerHTML = html;
	if (htmlNode.innerText)
		return htmlNode.innerText; // IE
	else
		return htmlNode.textContent; // FF
}

function attr_id (id)
{
	if (id == null) return '';
	return ' id="' + String(id) + '"';
}

function attr_class (classes)
{
	if (classes == null) return '';
	return ' class="' + classes.join(' ') + '"';
}

function attr_style (styles)
{
	if (styles == null) return '';
	var s1 = '';
	for (var style in styles) s1 += (s1.length?' ':'') + String(style) + ':' + String(styles[style]) + ';';
	return ' style="' + s1 + '"';
}

function elem0_div (id, classes, styles, html)
{
	return '<DIV' + attr_id(id) + attr_class(classes) + attr_style(styles) + '>' + html + '</DIV>\n';
}

function attrs_elem (attrs)
{
	if (attrs == null) return '';
	var a1 = [];
	for (var attr in attrs) if (attrs[attr]!=null) a1.push(attr + '="' + String(attrs[attr]) + '"');
	return ' ' + a1.join(' ');
}

function elem2_div (attrs, html)
{
	return '<DIV' + attrs_elem(attrs) + '>' + html + '</DIV>';
}

function elem2_a (attrs, html)
{
	return '<A' + attrs_elem(attrs) + '>' + html + '</A>';
}

function elem1_img (attrs)
{
	return '<IMG' + attrs_elem(attrs) + '>';
}




// Node object
function Node (_index, parent, name, caption, url, tooltip, target, title, icon0, icon1, open) 
{
	this._index 		= _index;
	this.parent			= parent;
	this.name 			= name;
	this.caption 		= caption;
	this.url 			= url;
	this.tooltip 		= tooltip;
	this.target 		= target;
	this.title			= title;
	this.icon0 			= icon0;		// icon 'closed'
	this.icon1 			= icon1;		// icon 'opened'
	this.open 			= open;			// icon state, 'closed' or 'opened'
	this.selected 		= false;		// icon highlighted
	this.children		= null;			// array of nodes (submenu)

	this.dtree			= null;			// parent object (the dtree)
	
	this.isLastSibling = function()
	{
		if (this.parent == null)
			return true;
		else
			return this.parent.children[this.parent.children.length - 1] == this;
	}
	

	// select or unselect the node
	this.selectOrUnselect = function (selected) 
	{
		if (this.dtree.document)
		{
			var elem_highlighted1 = this.dtree.getElementBySubnameAndIndex ("href", this._index);
			elem_highlighted1.className = selected ? "node1" : "node0";
		}
		
		this.selected = selected;
		
		this.dtree.shownNode = selected ? this : null;
	}
	
	
	// open or close the node
	this.openOrClose = function (open) 
	{
		if (this.dtree.document)
		{
			if (this.dtree.config.useIcons) 
			{
				if (!this.icon0) 
					this.icon0 = 
						this.dtree.iconsFolder +
						(
							this.parent == null ? 
								this.dtree.icons.root : 
								this.children != null ? this.dtree.icons.folder : this.dtree.icons.node
						);
				if (!this.icon1) 
					this.icon1 = 
						this.dtree.iconsFolder + 
						(
							this.parent == null ? 
								this.dtree.icons.root : 
								this.children != null ? this.dtree.icons.folderOpen : this.dtree.icons.node
						);

				var elem_icon = this.dtree.getElementBySubnameAndIndex ("icon", this._index);
//alert (this._index);//(open ? this.icon1 : this.icon0);
				elem_icon.src = open ? this.icon1 : this.icon0;
			}
			
			if (this._index)
			{
				var elem_branch	= this.dtree.getElementBySubnameAndIndex ("branch", this._index);
//if (elem_branch == null) alert(this._index);
				elem_branch.src = 
					this.dtree.iconsFolder + 
					(
						this.dtree.config.useLines ?
							open ? 
								this.isLastSibling() ? this.dtree.icons.minusBottom : this.dtree.icons.minus : 
								this.isLastSibling() ? this.dtree.icons.plusBottom : this.dtree.icons.plus :
							open ? this.dtree.icons.nlMinus : this.dtree.icons.nlPlus
					);
			}
			
			var elem_clip	= this.dtree.getElementBySubnameAndIndex ("clip", this._index);
			elem_clip.style.display = open ? 'block' : 'none';
		}
		
		this.open = open;
	}
	

	// close children nodes
	this.closeChildren = function (skip) 
	{
		for (var n=0; this.children && n<this.children.length; n++) 
		{
			var node = this.children[n];
			if (node != skip) 
			{
				if (node.open)
				{
					node.closeChildren (null);
					node.openOrClose (false);
				}
			}
		}
	}
	
	
	// closes nodes on the same level 
	this.closeLevel = function (skip) 
	{
		if (this.parent) this.parent.closeChildren (skip) 
	}
	
	
	this.captionString = function()
	{
		if (this.caption != null) 
			return this.caption;
		else
			return escapeHTML(this.name != null ? this.name : this._index);
	}
	
	
	this.tooltipString = function()
	{
		if (this.tooltip != null) 
			return this.tooltip;
		else 
			return this.captionString().replace(/\<[^\>]*\>/g,"");
	}
	

	this.jsCallString = function (subname)
	{
		return "javascript:" + this.dtree.getFnBySubnameAndIndex (subname, this._index) + ';';
	}
	

	this.idNodeString = function (subname)
	{
		return this.dtree.getIdBySubnameAndIndex (subname, this._index);
	}
	
	
	

	this.nodeParentToString = function(sitemap) 
	{
		// add 'empty' and/or 'line' icons...
		if (this.parent) 
		{
			var html2 = '';
			
			var indents1 = [];
			for (var node1=this.parent; node1.parent!=null; node1=node1.parent) indents1.push (node1);
			for (var j=indents1.length-1; j>=0; j--)
				html2 += 
					elem1_img (
						{
							src: this.dtree.iconsFolder + (!indents1[j].isLastSibling() && this.dtree.config.useLines ? this.dtree.icons.line : this.dtree.icons.empty),
							alt: ""
						}
					);
			
			if (this.children != null) 
			{
				var html1 = 
					elem1_img (
						{
							id: sitemap ? null : this.idNodeString("branch"),
							src: 
								this.dtree.iconsFolder +
								(
									this.dtree.config.useLines ?
										(sitemap || this.open) ? 
											this.isLastSibling() ? this.dtree.icons.minusBottom : this.dtree.icons.minus : 
											this.isLastSibling() ? this.dtree.icons.plusBottom : this.dtree.icons.plus :
										(sitemap || this.open) ? this.dtree.icons.nlMinus : this.dtree.icons.nlPlus
								),
							alt: ""
						}
					);
				html2 += 
					elem2_a (
						{
							href: this.jsCallString("toogle")
						},
						html1
					);
			} 
			else 
			{
				html2 += 
					elem1_img (
						{
							src: 
								this.dtree.iconsFolder + 
								(
									this.dtree.config.useLines ? 
										this.isLastSibling() ? this.dtree.icons.joinBottom : this.dtree.icons.join : 
										this.dtree.icons.empty
								),
							alt: ""
						}
					);
			}
		
			return html2; // + '\n';
		}
		else
		{
			return '';
		}
	}


	this.nodeIconToString = function(sitemap) 
	{
		// add icon of the this...
		if (this.dtree.config.useIcons) 
		{
			var html2 = '';
			
			if (!this.icon0) 
				this.icon0 = 
					this.dtree.iconsFolder +
					(
						this.parent == null ? 
							this.dtree.icons.root : 
							this.children != null ? this.dtree.icons.folder : this.dtree.icons.node
					);
			if (!this.icon1) 
				this.icon1 = 
					this.dtree.iconsFolder + 
					(
						this.parent == null ? 
							this.dtree.icons.root : 
							this.children != null ? this.dtree.icons.folderOpen : this.dtree.icons.node
					);
			
			html2 +=
				elem1_img (
					{
						id: sitemap ? null : this.idNodeString("icon"),
						src: (sitemap || this.open) ? this.icon1 : this.icon0,
						alt: ""
					}
				);
			
			return html2; // + '\n';
		}
		else
		{
			return '';
		}
	}
	
	
	this.nodeCaptionToString = function(sitemap)
	{
		var html2 = '';
		
		if (this.url) 
		{
			html2 += 
				elem2_a (
					{
						id: sitemap ? null : this.idNodeString("href"),
						"class": !sitemap && this.dtree.config.useSelection && this.selected ? "node1" : "node0", //20091215
						href: this.url,
						target: this.target!=null ? this.target : this.dtree.config.target,
						title: this.tooltipString(),
						onclick: (this.url.substr(0,11) != 'javascript:') && this.dtree.config.useSelection ? this.jsCallString("highlight") : null
					},
					this.captionString()
				);
		}
		else
		{
			if (this.children) 
			{
				html2 += 
					elem2_a (
						{
							href: this.jsCallString("toogle"),
							"class": "node0",
							title: this.tooltipString()
						},
						this.captionString()
					);
			}
			else
			{
				html2 += this.captionString();
			}
		}
		
		return html2; // + '\n';
	}


	// create 'icon', 'url', etc... of the node...
	this.nodeToString = function(sitemap) 
	{
		if (this.name == null) return '';

		var html2 = '';
		
		// add 'empty' and/or 'line' icons...
		html2 += this.nodeParentToString(sitemap);
		
		// add icon of the this...
		html2 += this.nodeIconToString(sitemap);
		
		// add caption and url of the this...
		html2 += this.nodeCaptionToString(sitemap);
		
		var html1 = 
			elem2_div (
				{
					"class": "node"
				},
				html2 //'\n' + html2
			);
		return html1 + '\n';
	}
	

	this.clipToString = function (sitemap) //20091211: (subname)????
	{
		// add children encapsulated in a 'clip' DIV element...
		if (this.children) 
		{
			// create the submenu structure...
			var html2 = "";
			for (var n = 0; n < this.children.length; n++) 
			{
				html2 += this.children[n].toString(sitemap);
			}
			var html1 = 
				elem2_div (
					{
						id: sitemap ? null : this.idNodeString("clip"),
						"class": "clip",
						style: "display:" + ((this.parent == null || (sitemap || this.open)) ? 'block' : 'none') + ';'
					},
					'\n' + html2
				);
			return html1 + '\n';
		}
		else
		{
			return '';
		}
	}
	

	// create 'icon', 'url', etc...
	this.toString = function(sitemap) 
	{
		return this.nodeToString(sitemap) + this.clipToString(sitemap);
	}

}




// Tree object
function dTree (name)
{
	this.name = name;

	this.firstNode = null;		// 1st node on which is based the tree
	this.shownNode = null;		// shown node and displayed page (highlighted menu)

	this._indexes = [];
	this._urls = [];
	
	// Settings
	this.config = {
		target				: null,
		//folderLinks		: true,
		useSelection		: true,
		useLines			: true,
		useIcons			: false, //true,
		//useStatusText		: true,
		closeSameLevel		: true //false
	};
	
	// Icons
	this.icons = {
		root				: "base.gif",
		folder				: "folder.gif",
		folderOpen			: "folderopen.gif",
		node				: "page.gif",
		empty				: "empty.gif",
		line				: "line.gif",
		join				: "join.gif",
		joinBottom			: "joinbottom.gif",
		plus				: "plus.gif",
		plusBottom			: "plusbottom.gif",
		minus				: "minus.gif",
		minusBottom			: "minusbottom.gif",
		nlPlus				: "nolines_plus.gif",
		nlMinus				: "nolines_minus.gif"
	};
	this.iconsFolder = "/dtree/img/";
	
	this.window = null;
	this.document = null;
	



	this.getFnBySubnameAndIndex = function (subname, index)
	{
		return this.name + "." + subname + "(" + index + ")";
	}
	
	
	this.getIdBySubnameAndIndex = function (subname, index)
	{
		return this.name + "." + subname + "." + index;
	}


	this.getElementBySubnameAndIndex = function (subname, index)
	{
		return this.document.getElementById (this.getIdBySubnameAndIndex (subname, index) );
	}


	// output the 'html' tree...
	this.toString = function(sitemap) 
	{
		return elem2_div ({"class":"dTree"}, '\n' + this.firstNode.toString(sitemap)) + '\n';
	}
	

	//// output the 'html' sitemap...
	//this.sitemap = function() 
	//{
	//	return elem2_div ({"class":"dTree"}, '\n' + this.firstNode.sitemap()) + '\n';
	//}


	// output the 'html' tree...
	this.shownNodeToString = function()
	{
		if (this.shownNode == null) return '';
		
		var node = this.shownNode;
		var html = '';
		
		while (node)
		{
			html = node.nodeCaptionToString(true) + (html.length ? '::'+html : '');
			node = node.parent;
		}
		
		return html + '\n';
	}
	
	
	
	
	// create a new node and add it to the tree...
	this.add = function (parent, name, caption, url, tooltip, target, title, icon0, icon1, open) 
	{
		var _index = this._indexes.length;
		var node = new Node (_index, parent, name, caption, url, tooltip, target, title, icon0, icon1, open);
		node.dtree = this;
		
		this._indexes.push (node);
		
		if (parent == null) 
		{
			this.firstNode = node;
		}
		else
		{
			if (parent.children == null) parent.children = [];
			parent.children.push (node);
		}
		
		this._indexes[name] = node;
		
		if (url != null && url.split("?") != null && url.split("?").length==2)
		{
			var url1 = url.split("?")[1];
			if (url1.substring(0,8) == "gallery=") this._indexes[url1.substring(8)] = node;
		}
		
		//this._urls[url] = node;
		if (this._urls[url] == null) this._urls[url] = node;
		
		return node;
	}
	
	
	
	
	// highlight the indexed node
	this.highlight = function (n) 
	{
		if (this.config.useSelection) 
		{
			if (this.shownNode) this.shownNode.selectOrUnselect (false);
			
			(this.shownNode = this._indexes[n]).selectOrUnselect (true);
		}
	}
	
	
	// toggle (open or close) the indexed node
	this.toogle = function (n) 
	{
		var node = this._indexes[n];

		if (this.config.closeSameLevel) node.closeLevel (node);
		
		if (node.open) node.closeChildren (null);
		node.openOrClose (!node.open);
		
		if (node.open) while (node.parent && !node.parent.open) {node = node.parent; node.openOrClose(true);} //20091211
	}
	
	
	
	
	// open the tree to a specific node
	this.openNode = function (node, selected) 
	{
		if (node == null) return;
		
		if (node.children) node.openOrClose(true);
		if (selected) this.highlight(node._index);
		
		this.openNode (node.parent, false);
	}
	
	
	// open or close all nodes
	this.openOrCloseAllNodes = function (state) 
	{
		for (var n=0; n<this._indexes.length; n++) 
		{
			var node = this._indexes[n];
			
			if (node.children && node.parent) 
			{
				node.openOrClose (state);
			}
		}
	}
		
	
	// open all nodes
	this.openAllNodes = function() 
	{
		this.openOrCloseAllNodes (true);
	}
	
	
	// close all nodes
	this.closeAllNodes = function() 
	{
		this.openOrCloseAllNodes (false);
	}
	
};

