/*
Nornix TreeMenu -- treemenu structure, style and behavior -- version 1.75 -- 2006-04-24
Copyright (C) 2006
Anders Nawroth (http://www.anders.nawroth.com/)
Eric Jexén (http://eric.jexen.se/)
Released under LGPL, see http://treemenu.nornix.com/license
Usage instructions at http://treemenu.nornix.com/info/usage
*/

/*
For information on public, privileged and private members and the "this" bug, see:
http://www.crockford.com/javascript/private.html
*/

// TreeMenu class
function TreeMenu(inMenuId, inAllowWhitespace)
{
	// workaround for the "this" bug in ECMAscript
	var self = this;
	// private vars
	var menuId = inMenuId ? inMenuId : "menu";
	var allowWhitespace = (inAllowWhitespace === false) ? false : true;

	/* Methods accessible from outside the class */

	/* privileged */ this.start = function()
	{
		if (document.getElementById(menuId))
		{
			init();
		}
		else
		{
			setTimeout(self.start, 10);
		}
	}

	/* privileged */ this.save = function()
	{
		var s = "";
		loopFolders(function (li)
			{
				if (li.className.search(self.openPattern) != -1)
				{
					s += "-";
				}
				else
				{
					s += "+";
				}
			}
		)
		createCookie("treemenu", s);
	}

	/* Event handlers, accessible from outside the class */

	/* privileged */ this.toggleClick = function()
	{
		toggle(this.parentNode);
	}

	/* privileged */ this.toggleClickLink = function(e)
	{
		toggle(this.parentNode);
		cancelEvent(e);
		return false;
	}

	/* privileged */ this.checkKeypress = function(e)
	{
		var keyCode;
		if (e.keyCode)
		{
			keyCode = e.keyCode;
		}
		else
		{
			keyCode = e.which;
		}
		if (keyCode == 32 || (keyCode == 13 && isHrefEmpty(this)))
		{
			toggle(this.parentNode);
			cancelEvent(e);
			return false;
		}
		return true;
	}

	/* privileged */ this.closeTree = function()
	{
		loopFolders(function(li)
			{
				makeClosed(li);
			}
		)
		ieFix();
	}

	/* privileged */ this.openTree = function()
	{
		loopFolders(function(li)
			{
				makeOpen(li);
			}
		)
		ieFix();
	}

	/* Methods accessible only from inside the class */

	/* private */ function init()
	{
		if (!document.getElementById || !document.createElement) return;
		self.menu = document.getElementById(menuId);
		self.menuElements = self.menu.getElementsByTagName("li");
		// regex patterns
		self.openPattern = /(^| )open( |$)/;
		self.closedPattern = /(^| )closed( |$)/;
		// set classes, if not set in the HTML
		if (self.dynamicClasses)
		{
			setClasses();
		}
		if (self.openCloseAll)
		{
			createOpenCloseAllIcons();
		}
		// add open/close nodes for every folder, using an empty <span> element
		prepareFolderTree();
		// re-render menu now if IE
		ieFix();
		// add unload handler to save cookie
		addEvent(window, "unload", self.save);
		// preload images for faster rendering
		for (var imgNo in self.preloadImages)
		{
			(new Image()).src = self.preloadImages[imgNo]; // no need to keep the images!
		}
	}

	/* private */ function setClasses()
	// Set folder/document/open/closed/last classes on elements.
	// Make folders from the current element/page and "upwards" open
	{
		var page = window.location;
		// set up root node
		// - find an <a> element that is a child of the menu element
		for (var i = 0, a; a = self.menu.childNodes[i]; i++)
		{
			if (a.nodeName && a.nodeName.toLowerCase() == "a")
			{
				a.className += a.className ? " root" : "root";
				if (a.href == page)
				{
					a.removeAttribute("href");
				}
				break;
			}
		}
		// loop list items
		for (var i = 0, li; li = self.menuElements[i]; i++)
		{
			if (isFolder(li))
			{
				li.className = "folder closed"; // default is closed
			}
			else
			{
				li.className = "document"; // only one childNode (a link)
			}
			if (li == li.parentNode.lastChild)
			{
				li.className += " last";
			}
			if (li.firstChild.href == page)
			{
				// current page
				li.firstChild.removeAttribute("href");
				if (isFolder(li)) // only open if li is folder
				{
					li.className = li.className.replace("closed", "open");
				}
				// trace upwards in the tree to open the "path" to the current page
				var node = li.parentNode.parentNode;
				while (node && node != self.menu)
				{
					makeOpen(node);
					node = node.parentNode.parentNode;
				}
			}
		}
	}

	/* private */ function isHrefEmpty(node)
	{
		if (node.href && (node.href == window.location+"#" || node.href == "javascript:;"))
		{
			return true;
		}
		return false;
	}

	if (document.all && document.opera === undefined)
	{
		// IE bugfix, forces IE to re-render the menu
		// and sets focus on elements that have fired an event
		/* private */ var ieFix = function()
		{
			self.menu.style.display = "none";
			self.menu.style.display = "block";
			if (window.event && window.event.srcElement) window.event.srcElement.focus();
		}
	}
	else
	{
		/* private */ var ieFix = function(){}
	}


	/* private */ function isFolder(li)
	{
		if (allowWhitespace)
		{
			for (var i=0, child; child = li.childNodes[i]; i++)
			{
				if (child.nodeName && child.nodeName.toLowerCase() == "ul")
				{
					return true;
				}
			}
			return false;
		}
		else
		{
			// this is enough when there are no comments, whitespace or extra text nodes
			return li.childNodes.length > 1;
		}
	}

	/* private */ function loopFolders(func)
	{
		for (var i = 0, li; li = self.menuFolders[i]; i++)
		{
			func(li);
		}
	}

	/* private */ function toggle(node)
	{
		if (node.className.search(self.openPattern) == -1)
		{
			makeOpen(node);
		}
		else
		{
			makeClosed(node);
		}
		ieFix();
	}

	/* private */ function makeOpen(li)
	{
		swapClasses(li, "open", self.openPattern, self.closedPattern, self.closeFolderTitle);
	}

	/* private */ function makeClosed(li)
	{
		swapClasses(li, "closed", self.closedPattern, self.openPattern, self.openFolderTitle);
	}

	/* private */ function swapClasses(li, newClass, newClassPattern, oldClassPattern, title)
	{
		if (li.className.search(newClassPattern) == -1)
		{
			if (li.className.search(oldClassPattern) == -1)
			{
				li.className += " "+newClass;
			}
			else
			{
				li.className = li.className.replace(oldClassPattern, "$1"+newClass+"$2");
			}
		}
		li.firstChild.title = title;
	}

	/* private */ function createOpenCloseAllIcons()
	{
		var orgA = document.createElement("a");
		orgA.href = "javascript:;";
		var a = orgA.cloneNode(false); // new <a> tag
		a.className = "closeTree";
		a.title = self.closeTreeTitle;
		addEvent(a, "click", self.closeTree);
		self.menu.insertBefore(a, self.menu.firstChild.nextSibling);
		a = orgA.cloneNode(false); // new <a> tag
		a.className = "openTree";
		a.title = self.openTreeTitle;
		addEvent(a, "click", self.openTree);
		self.menu.insertBefore(a, self.menu.firstChild.nextSibling.nextSibling);
	}

	/* private */ function prepareFolderTree()
	{
		// setup list of folder elements, used by loopFolders()
		self.menuFolders = new Array();
		for (var i = 0, li; li = self.menuElements[i]; i++)
		{
			if (isFolder(li))
			{
				self.menuFolders.push(li);
			}
		}
		var oldTree = readCookie("treemenu");
		var orgSpan = document.createElement("span");
		orgSpan.title = self.openFolderTitle; // default value is closed folder
		var i=0;
		loopFolders(function(li)
			{
				// open/Contraers with the space bar or enter key or mouse click
				if (isHrefEmpty(li.firstChild))
				{
					addEvent(li.firstChild, "click", self.toggleClickLink);
				}
				addEvent(li.firstChild, "keypress", self.checkKeypress);
				var span = orgSpan.cloneNode(false);
				if (self.dynamicClasses && li.className.search(self.openPattern) != -1)
				{
					// folder opened in setClasses() !
					span.title = self.closeFolderTitle;
				}
				li.insertBefore(span, li.firstChild);
				addEvent(span, "click", self.toggleClick);
				var chr = oldTree.charAt(i);
				if (chr && chr == "-")
				{
					makeOpen(li);
				}
				i++;
			}
		)
	}
}

// CONFIG: set public members to default values
TreeMenu.prototype.dynamicClasses = true;
TreeMenu.prototype.openCloseAll = true;
TreeMenu.prototype.preloadImages =
	[
		"/style/nornix-home-icon.png",
		"/style/nornix-close-icon.png",
		"/style/nornix-open-icon.png",
		"/style/nornix-plus-node.png",
		"/style/nornix-minus-node.png",
		"/style/nornix-folder-closed-icon.png",
		"/style/nornix-doc-node-icon.png",
		"/style/nornix-folder-open-icon.png",
		"/style/nornix-treemenu-line.png"
	];
// text to use: change according to your needs, here or
// from an other JS-file or from the HTML
TreeMenu.prototype.closeTreeTitle = "Contraer todo";
TreeMenu.prototype.openTreeTitle = "Expandir todo";
TreeMenu.prototype.closeFolderTitle = "Contraer";
TreeMenu.prototype.openFolderTitle = "Expandir";

// start the script
var treemenu = new TreeMenu("menu");
treemenu.start();

