/** Animated TreeMenu script by Garrett Smith.
 *
 *  URL: http://dhtmlkitchen.com/
 *  email: admin@dhtmlkitchen.com
 *
 *  Usage: see url below (subject to change)
 *  http://dhtmlkitchen.com/dhtml/ui/menutree/
 *
 */

TreeGlobals = {

	// You may add to the browser properties,
	// but do not change existing properties.
	browser : new function(){
		var ua = navigator.userAgent;
		this.OPERA	= ua.indexOf("Opera") > 0;
		this.NS4	= typeof document.layers != "undefined";
		this.ICAB	= ua.toLowerCase().indexOf("icab") > 0;
		this.IE5	= ua.indexOf("MSIE 5") > 0 && !this.OPERA;
		this.MAC	= navigator.platform.indexOf("PPC") > 0;
		this.MAC_IE5= this.MAC > 0 && this.IE5;
	},
	
// don't change these
	inited : false
};

if(typeof window.TreeParams == "undefined")
TreeParams = {

	// You will probably have to change these.

/** TreeParams:
*	OPEN_MULTIPLE_MENUS	--	Boolean.
*						if true, more than one menu can
*						be open at a time. Otherwise,
*						opening a new menu closes any
*						open menu.
*
*
*
*	TIME_DELAY				--	int.
*						How slowly a menuNode collapses
*						in milliseconds. (0 to 100).
*
*	OPEN_WHILE_CLOSING		--	Boolean.
*						If either OPEN_MULTIPLE_MENUS or
*						OPEN_WHILE_CLOSING will simultaneously
*						open a new menu while closing the
*						currently open menu.
*/

	OPEN_MULTIPLE_MENUS	: false,
	
	TIME_DELAY			: TreeGlobals.browser.MAC_IE5 ? 20 : 30,
	TIME_DELAY_OPEN		: this.TIME_DELAY,
	TIME_DELAY_CLOSE	: this.TIME_DELAY,
	OPEN_WHILE_CLOSING	: true,

	OPEN_MENU_ICON		: "img/open-menu-blue.gif",
	CLOSED_MENU_ICON	: "img/closed-menu-blue.gif"
	
};

// Unless you really know what you are doing,
// don't change anything below this line!
//_________________________________________________________________________
///////////////////////////////////////////////////////////////////////////


function toggleMenu(htmlElement) {
	
	if(TreeGlobals.browser.OPERA || TreeGlobals.browser.NS4) return; 
	
	var label = TreeFunctions.getLabel(htmlElement);
	
	if(label.menu.blocked) return;
		
	if(label.menu.container.menuToOpen != null) return;
		
	if(label.isDepressed) {
		if (TreeParams.OPEN_MULTIPLE_MENUS || label.menu.container.activeMenu == label.menu) {
			
			TreeFunctions.closeMenu(label.menu);
			label.menu.container.activeMenu = null;
		}
	}
	else{ // push it in.
			
		if(label.icon != null)
			label.icon.src = TreeParams.OPEN_MENU_ICON;

		label.menu.container.menuToOpen = label.menu;
		if(TreeParams.OPEN_MULTIPLE_MENUS || label.menu.container.activeMenu == null ) {
			TreeFunctions.openMenu(label.menu);
		}
			
		else {
			TreeFunctions.closeMenu(label.menu.container.activeMenu);
			
			if(TreeParams.OPEN_WHILE_CLOSING)
				TreeFunctions.openMenu(label.menu);
			else
				label.menu.container.activeMenu.menuInCue = label.menu;
		}
		label.menu.container.activeMenu = label.menu;
			
	}
}

function activateMenu(sButtonId){
	if(!window.toggleMenu)
		return;
		
	var button = document.getElementById(sButtonId);
	if(!button) return;
	
	toggleMenu(button.getElementsByTagName("span")[0]);
}

function buttonOver(htmlLabel){
	
	window.status = htmlLabel.parentNode.id;
	
	label = TreeFunctions.getLabel(htmlLabel);
	if(new RegExp("labelHover").test(label.htmlElement.className))
		return;
	
	label.htmlElement.className += " labelHover";
}

function buttonOff(label){
	window.status = window.defaultStatus;
	TreeUtils.removeClass(label, "labelHover");
}

if(typeof document.getElementsByTagName == "undefined"
  || TreeGlobals.browser.OPERA)
	buttonOver = buttonOff = function(){};


Button = function(htmlElement, category){
	
	this.htmlElement = htmlElement;
	
	this.category = category;
	this.menu = new Menu(document.getElementById(this.category +"Menu"), this);
	
	var icons = htmlElement.getElementsByTagName("img");
	this.icon = (icons.length > 0) ?
				icons[0] : null;
	
	this.isIcon = false;
	if(htmlElement.tagName == "IMG"){
		this.isIcon = true;
		this.icon = htmlElement;
	}
	
	this.isDepressed = false;
};


Menu = function(htmlElement, label) {
	
	this.ownerButton = label;
	this.id = label.category; // a short-cut reference to this.ownerButton.category.
	this.htmlElement = htmlElement;
	this.items = TreeUtils.getChildNodesWithClass(this.htmlElement, "div", "menuNode");
	this.allItems = TreeUtils.getElementsWithClass(this.htmlElement, "div", "menuNode");
	this.cur = 0;
	this.blocked = false;
	
	this._root = null;
	
	// look for ancestor with class menu.
	// get that element's id minus "Menu"
	this.container = this.getContainer();	
	this.menuToOpen = null;
	this.activeMenu = null;
	this.menuInCue = null;
	
};

function debug(s, obj){
	var rv = "";
	for(var x in obj)
		rv += s+"."+x+ " = "+ obj[x];
	alert(rv);
}

Menu.prototype = {

	open : function(){
	
		this.itemsToOpen[this.cur].style.display = "block";
		
		if(++this.cur == this.itemsToOpen.length)
			this.performActionEnd("block");
	},
	close : function(){
		this.itemsToClose[this.cur].style.display = "none";
		
		if(++this.cur == this.itemsToClose.length)
			this.performActionEnd("none");
	},
	
	performActionEnd : function(sDisplay) {
		
		this.htmlElement.style.display = sDisplay;
		this.performActionTimer = clearInterval(this.performActionTimer);
		
		if(sDisplay=='block')
			this.container.menuToOpen = null;
			
		else {
			TreeFunctions.setDefaultLabel(this.ownerButton);
			
			if(!TreeGlobals.OPEN_WHILE_CLOSING
				&& this.menuInCue != null)
				TreeFunctions.openMenu(this.menuInCue);
				this.menuInCue = null;
		}
		
		setTimeout("TreeUtils.repaintFix(TreeList[\""+this._root.id+"\"])", 50);
		this.blocked = false;
	},
	
	root : function(){
		
		if(this._root == null) {
			
			var rt = TreeUtils.findAncestorWithClass(this.htmlElement, "AnimTree");
			
			if(rt == null) 
				rt = document.body;
				
			if(!rt.id)
				rt.id = "AnimTree_"+ Math.round(Math.random() * 100);
				
			if(TreeList[rt.id] != null){
				this._root = TreeList[rt.id];
				this._root.menus[this.id] = this;
			}
			else
				this._root = new Tree(rt, this);
		}
		return this._root;
	},
	
	getContainer : function(){
		
		var root = this.root();
		var parentMenuElement = TreeUtils.findAncestorWithClass(this.htmlElement,"menu");
		if(parentMenuElement != null)
			return root.menus[parentMenuElement.id.replace(/Menu$/,"")];
		return root;
	
	}
};


Tree = function(htmlElement, menu) {
	this.htmlElement = htmlElement;
	this.activeMenu = null;
	this.menus[menu.id] = menu;
	this.menuToOpen = null;
	this.id = htmlElement.id;
	
	TreeList[this.id] = this;
 };

Tree.prototype.menus = new Object();

TreeList = {};


TreeFunctions = {

/** TreeFunctions:
*	getLabel(htmlLabel)	-- called the very first time a buttonlabel is clicked or moused-over.
*	initMenu			-- sets .menu and .menuNode to "display : none".
*	openMenu			-- opens a menu.
*	closeMenu			-- closes a menu.
*	setDefaultLabel		-- sets the label to normal state. Called by toggleButton -> 
*                         closeMenu -> menu.performActionEnd -> setDefaultLabel
*
*/

getLabel : function(htmlLabel) {
	
	var menuName = TreeUtils.findAncestorWithClass(htmlLabel,"button").id;
	var b;
	
	for(var tree in TreeList) 
		if(TreeList[tree].menus[menuName] != null)
			return TreeList[tree].menus[menuName].ownerButton;
		
	
	return new Button(htmlLabel, menuName);
	
},

initMenu : function(){
	if(document.getElementById && !TreeGlobals.browser.OPERA && !TreeGlobals.inited){
		
		document.writeln("<style type='text/css'>");
		document.writeln("/* <![CDATA[ */");
		document.write(".menu, .menuNode{display: none;}");
		document.writeln("/* ]]> */");
		document.write("<"+"/style>");
		
		TreeGlobals.inited = true;
	}
},

openMenu : function(menu){ // because menuToOpen may change,
	
	menu.blocked = true;
	menu.cur = 0;
	menu.itemsToOpen = new Array();
	
	
	
	menu.htmlElement.style.display = "block";
	
	// if itemsToClose does not exist, the menu has not been closed.
	if(menu.itemsToClose && menu.itemsToClose.length > menu.items.length)
			menu.itemsToOpen = menu.itemsToClose.reverse();
	
	else
		menu.itemsToOpen = menu.items;
	
	if(!menu.ownerButton.isIcon)
		menu.ownerButton.htmlElement.className += " labelDown";
		
		
	menu.performActionTimer = setInterval(
								"TreeList."+menu.root().id+".menus." + menu.id +".open()", 
								TreeParams.TIME_DELAY);
				
				
	menu.ownerButton.isDepressed = true;
	
},

closeMenu : function(menu) {
	
	menu.blocked = true;
	menu.cur = 0;
	menu.itemsToClose = new Array();
	
	for(var i = menu.allItems.length-1,counter = 0; i > 0; i--)
	
		if(menu.allItems[i].style.display == "block")
			menu.itemsToClose[counter++] = menu.allItems[i];
			
			
	menu.itemsToClose[menu.itemsToClose.length] = menu.htmlElement;
	
	
	menu.performActionTimer = setInterval(
									"TreeList."+menu.root().id+".menus." + menu.id +".close()", 
									TreeParams.TIME_DELAY);
					
	menu.ownerButton.isDepressed = false;
	
},

setDefaultLabel : function(button){
	
	if(button.isIcon)
		return void( button.icon.src = TreeParams.CLOSED_MENU_ICON);
	
	TreeUtils.removeClass(button.htmlElement, "labelHover");
	TreeUtils.removeClass(button.htmlElement, "labelDown");
	
	if(button.icon != null)
		button.icon.src = TreeParams.CLOSED_MENU_ICON;
}

};

TreeFunctions.initMenu();


/* <>--<>--<>--<>--<>--<>-- UTILITY FUNCTIONS --<>--<>--<>--<>--<>--<> */

TreeUtils = {

/** TreeUtils: Utility functions for tree menu.
*
*	getChildNodesWithClass(parent, tagName, className)
*
*	getElementsWithClass(parent, tagName, className)
*
*	hasAncestorWithClass(el, klass)
*
*	removeClass(el, name)
*
*/

/** returns an array of all childNodes
*	who have a className that matches the className
*	parameter.
*	Side effect: if the element does not have an id,
*	an unique id will be assigned to it.
*
*	Nested elements are not returned, only
*	direct descendants (i.e. childNodes).
*/
getChildNodesWithClass : function(parent, tagName, klass){
	
	var collection;
	var returnedCollection = [];
	var collection = parent.childNodes;
	
	for(var i = 0, counter = 0; i < collection.length; i++){
		if(!collection[i].className
		|| collection[i].tagName.toUpperCase() != tagName.toUpperCase())
			continue;
		
		if( collection[i].className.test(klass, " "))
			returnedCollection[counter++] = collection[i];
	}
	return returnedCollection;
},

/** Obtains a NodeList of all descendant elements
*	who have a className that matches the className
*	parameter. This method differs from objectGetChildNodesWithClass
*	because it returns ALL descendants (deep).
*/
getElementsWithClass : function(parent, tagName, klass){
	var collection;
	var returnedCollection = [];
	
	if(parent.all && tagName == "*") collection = parent.all;
	else collection = parent.getElementsByTagName(tagName);
	for(var i = 0, counter = 0; i < collection.length; i++){
		
		if(collection[i].className != null
			&& collection[i].className.test(klass, " "))
			returnedCollection[counter++] = collection[i];
	}
	return returnedCollection;
},

findAncestorWithClass : function(el, klass){
	
	for(var parent = el.parentNode;parent != null;){
	
		if( parent.className != null && parent.className.test(klass, " "))
			return parent;
			
		parent = parent.parentNode;
	}
	return null;
},

removeClass : function(el, klass){
	
	var newClass = "";
	var list = el.className.split(" ");
	
	for(var i = 0; i < list.length; i++)
		if(list[i] != klass)
			newClass += list[i] + " ";
	el.className = newClass.normalize();
	
},

repaintFix : function(tree){
	
	if(!tree.activeMenu) return;
	
	tree.activeMenu.htmlElement.style.visibility = "hidden";
	tree.activeMenu.htmlElement.style.visibility = "visible";
}

};

String.prototype.trim = function(){
		return this.replace(/^\s+|\s+$/g, "");
};
String.prototype.normalize = function(){
		return this.trim().replace(/\s\s+/g, " ");
};

String.prototype.test = function(inp, delim){
	var exps = getTokenizedExps(inp, delim);
	return( exps.global.test(this) || exps.ends.test(this) );
};
/** getTokenizedExps is used by String.test.
*/
function getTokenizedExps(inp, delim) {

	return {
		global : new RegExp(delim+inp+delim, "\g"),
		ends : new RegExp("^"+inp+delim+"|^"+inp+"$|"+delim+inp+"$", "\g")
	};
}

