// ===================================================================
// Author: François BACCONNET <js@tchoa.com>
// http://www.tchoa.com/
//
// NOTICE: You may use this code for any purpose, commercial or
// private, without any further permission from the author. You may
// remove this notice from your final code if you wish, however it is
// appreciated by the author if at least my web site address is kept.
//
// You may *NOT* re-distribute this code in any way except through its
// use. That means, you can include it in your product, or your web
// site, or any other form where the code is actually being used. You
// may not put the plain javascript up on your site for download or
// include it in your javascript libraries for download. 
// ===================================================================

/*
This code is inspired by and extended from :
		Matt Kruse <matt@mattkruse.com> : http://www.mattkruse.com/
		Stuart Langridge's aqlist code: http://www.kryogenix.org/code/browser/aqlists/
		Stuart Langridge, November 2002 : sil@kryogenix.org
		Inspired by Aaron's labels.js (http://youngpup.net/demos/labels/) 
		Dave Lindquist's menuDropDown.js (http://www.gazingus.org/dhtml/?id=109)
		AND (of course) THE X lib (http://www.cross-browser.com)

New features :
		some parameters :
			AUTOCLOSE
			AUTODEPTH
			AUTOCLOSETREE
			BULLETFILL
		LIs may have classes that stay
		open tree down to a level
		object-oriented code
		
DOTOs :
		1- Finish function unload()
		2- Give every tree its own BULLETFILL (tricky , may be with a 'beforeBuild' function ? or acess xMkTree from ourside so ...)
		3- Prevent trees from allready made
*/

// DEFAULT PARAMETERS

var AUTOCLOSE = true;	// true=yes | false=no
var AUTODEPTH = 1;	// 1 means only one top level branch can be open at one time 
var AUTOCLOSETREE = true; // usage : allso close open branches that the clicker is not in
var BULLETFILL = '\u00A0'; // '\u00A0' = '&nbsp;'
var treeClass = "mktree"; // class to put on the ULs you wanna turn into trees
var nodeClosedClass = "liClosed"; // CSS settings
var nodeOpenClass = "liOpen";
var nodeBulletClass = "liBullet";
var nodeLinkClass = "bullet";

var Trees = []; // tree colection
																		
// loader & unloader
xAddEventListener(window, 'load',
  function(){
		if (!document.createElement) { alert("Upgrade your browser ! so that code can work"); return; } // Without createElement, we can't do anything
		xEach( xGetElementsByClassName(treeClass,document,'UL'), function (ulTreeCl,i) { Trees[i] = new xMkTree(ulTreeCl); }); // Make a tree of all ul w/ treeClass class
		if(typeof(afterTreeBuild)!="undefined" && Trees.length>0) afterTreeBuild(); // do afterTreeBuild if defined
  }, false
);
xAddEventListener(window, 'unload', function(){xEach(Trees, function(Tree) {Tree.unload()});}, false);

// Main constructor & Classes
function xMkTree (ul) {
  // Public Property
	ul.xtree = this;
	this.ele = ul;
	this.autoclose = AUTOCLOSE ;
	this.autodepth = AUTODEPTH ;
	this.autoclosetree = AUTOCLOSETREE ;
	this.x_expandCollapseList = xf_expandCollapseList;

	// Constructor Code
	x_processList(ul); // call recursive
	
	function x_processList(ul) {
		var li = xFirstChild(ul,'li');
		while (li) { // for each li in given ul
			var subLists = false; // assuming its not a node
			var subUl = xFirstChild(li,'ul');
			while (subUl) { // for each ul in this li
				subLists = true; // fathers li is a node
				x_processList(subUl);
				subUl = xNextSib(subUl,'ul');
			}
			var s= xCreateElement("SPAN"); // create the span to add
			var t= BULLETFILL; // prepare its content
			xAddClass(s, nodeLinkClass); // give it its class
			if (subLists) { // This LI has UL's in it, so it's a +/- node
				xAddClass(li, nodeClosedClass);
				if (li.firstChild.nodeName=="#text") { // If it's just text,
					t = t+li.firstChild.nodeValue; // make the text work as the link also
					li.removeChild(li.firstChild);
				}
				xAddEventListener(s,"click",x_onclick,false); // add dynamik to span
			}
			else xAddClass(li, nodeBulletClass); // No sublists, so it's just a bullet node
			s.appendChild(document.createTextNode(t)); // put the content in the span
			li.insertBefore(s,li.firstChild); // & insert it into the li node
			li = xNextSib(li,'li');
		} // end of while (li)
	}

	function x_onclick(e) {
		var ev = new xEvent(e);
		var liT = xParentNode( ev.target, 1 );
		if (xHasClass(liT,nodeOpenClass)) { // is it an open node ?
			xRemoveClass(liT,nodeOpenClass); // it's open : anyway we close
			xAddClass(liT,nodeClosedClass);
		}	else { // it's closed, we shall open, but we may have to close some
			var cLevel = 1, prts = xParentNode(liT,1) ; // find what level clicked
			while (prts!=null && prts.className!=treeClass) {
				cLevel += 1;
				prts = xParentNode(prts,1) ;
			}
			if (prts && prts.xtree.autoclose) { // get into autoclose , principle : close all nodes on same level of the clicker
				if (cLevel==(prts.xtree.autodepth*2)-1) { // check if its the good one
					if (prts.xtree.autoclosetree) {
						xrec_expandCollapseList(prts,nodeClosedClass,null,null); // close all
						var origId = liT.id;
						if (origId=="") { liT.id="autocloseid"; } // if not allready set, give it an ID to chase it
						xrec_expandCollapseList(prts,null,(origId=="")?"autocloseid":origId,null); // open upto clicker
						liT.id = origId; // put id in original state
					} else xCloseLevel(prts,prts.xtree.autodepth-1); // close all nodes at that level but dont touch any thing else
				}
			}
			xRemoveClass(liT,nodeClosedClass); // and we open
			xAddClass(liT,nodeOpenClass);			
		}
	}
	
	function xCloseLevel (ul,level) {
		var li = xFirstChild(ul,'li');
		while (li && level>=0) {
			var node = false;
			var ulc = xFirstChild(li,'ul');
			while (ulc) {
				node = true;
				xCloseLevel (ulc,level-1);
				ulc = xNextSib(ulc,'ul');
			}
			if (node && level==0) {
				xRemoveClass(li,nodeOpenClass);
				xAddClass(li,nodeClosedClass);
			}
			li = xNextSib(li,'li');
		}
	}

	// Performs 3 functions:
	// a) Expand or collapse all nodes depending on cName set to nodeOpenClass or nodeClosedClass
	// b) Expand all nodes to reach a certain ID (that ID MUST NOT BE on a UL)
	// c) Expand all nodes to a certain number of levels (cName don't care, itemId MUST BE null)
	function xf_expandCollapseList(cName,itemId,nbLevel) {
		xrec_expandCollapseList(this.ele,cName,itemId,nbLevel); // call recursive on actual element
	}
	
	function xrec_expandCollapseList(ul,cName,itemId,nbLevel) {
		var li = xFirstChild(ul);
		while (li) { // Iterate things into given ul
			if (itemId!=null && li.id==itemId) { return true; } // ID found, leave
			if (li.nodeName == "LI") { // this is a li :: DOTO : can only be li, remove condition, make simplier
				var node = false; // assuming not a node
				var ulc = xFirstChild(li,'ul');
				while (ulc) { // walk through sub-uls
					node = true; // so its a node
					var ret = xrec_expandCollapseList(ulc,cName,itemId,nbLevel-1); // process it
					if (itemId!=null && ret) { // itemId search & found, returning
						xRemoveClass(li,nodeClosedClass);
						xAddClass(li,nodeOpenClass); // open the node
						return true; // & leave
					}
					ulc = xNextSib(ulc,'ul');
				}
				if (node && itemId==null) { // if bullet-like node & not looking for an ID
					xRemoveClass(li,(xHasClass(li,nodeClosedClass))?nodeClosedClass:nodeOpenClass); // Remove xMkTree nodeClass
					if ( nbLevel!=null && cName==null ) xAddClass(li,( nbLevel==0 )?nodeClosedClass:nodeOpenClass); //  do we care about the level ?
					else xAddClass(li,cName); // or just openning or closing all tree ?
				}
			} // Ends if this is a li
			li = xNextSib(li);
		}
	}

} // End of constructor

// Public Methods
xMkTree.prototype.unload = function() { // Should release memory on this node
	// alert("unload");
	return true;
};
xMkTree.prototype.expandTree =  function() { // xGetElementById('ID').xtree.expandTree() : Full expands a tree with a given ID
	this.x_expandCollapseList(nodeOpenClass,null,null);
	return true;
};
xMkTree.prototype.collapseTree = function() { // xGetElementById('ID').xtree.collapseTree() : Fully collapses a tree with a given ID
	this.x_expandCollapseList(nodeClosedClass,null,null);
	return true;
};
xMkTree.prototype.expandToItem = function(itemId) { // xGetElementById('ID').xtree.expandToItem(itemId) : Expands enough nodes to expose an LI with a given ID (dont put on UL)
	this.x_expandCollapseList(null,itemId,null);
	return true;
};
xMkTree.prototype.expandToLevel = function(nbLevel) { // xGetElementById('ID').xtree.expandToLevel(nbLevel) : Expands enough nodes to expose nbLevel levels
	this.x_expandCollapseList(null,null,nbLevel-1);
	return true;
};

/* Compiled from X 4.06 with XC 1.0 on 30Jan07 */
function xAddClass(e,c){e=xGetElementById(e);if(!e)return false;if(!xHasClass(e,c))e.className+=' '+c;return true;};function xAddEventListener(e,eT,eL,cap){if(!(e=xGetElementById(e)))return;eT=eT.toLowerCase();if(e==window&&!e.opera&&!document.all){if(eT=='resize'){e.xPCW=xClientWidth();e.xPCH=xClientHeight();e.xREL=eL;xResizeEvent();return;}if(eT=='scroll'){e.xPSL=xScrollLeft();e.xPST=xScrollTop();e.xSEL=eL;xScrollEvent();return;}}if(e.addEventListener)e.addEventListener(eT,eL,cap);else if(e.attachEvent)e.attachEvent('on'+eT,eL);else e['on'+eT]=eL;}function xResizeEvent(){if(window.xREL)setTimeout('xResizeEvent()',250);var w=window,cw=xClientWidth(),ch=xClientHeight();if(w.xPCW!=cw||w.xPCH!=ch){w.xPCW=cw;w.xPCH=ch;if(w.xREL)w.xREL();}}function xScrollEvent(){if(window.xSEL)setTimeout('xScrollEvent()',250);var w=window,sl=xScrollLeft(),st=xScrollTop();if(w.xPSL!=sl||w.xPST!=st){w.xPSL=sl;w.xPST=st;if(w.xSEL)w.xSEL();}}function xClientHeight(){var v=0,d=document,w=window;if(d.compatMode=='CSS1Compat'&&!w.opera&&d.documentElement&&d.documentElement.clientHeight){v=d.documentElement.clientHeight;}else if(d.body&&d.body.clientHeight){v=d.body.clientHeight;}else if(xDef(w.innerWidth,w.innerHeight,d.width)){v=w.innerHeight;if(d.width>w.innerWidth)v-=16;}return v;}function xClientWidth(){var v=0,d=document,w=window;if(d.compatMode=='CSS1Compat'&&!w.opera&&d.documentElement&&d.documentElement.clientWidth){v=d.documentElement.clientWidth;}else if(d.body&&d.body.clientWidth){v=d.body.clientWidth;}else if(xDef(w.innerWidth,w.innerHeight,d.height)){v=w.innerWidth;if(d.height>w.innerHeight)v-=16;}return v;}function xCreateElement(sTag){if(document.createElement)return document.createElement(sTag);else return null;}function xDef(){for(var i=0;i<arguments.length;++i){if(typeof(arguments[i])=='undefined')return false;}return true;}function xEach(c,f,s){var l=c.length;for(var i=(s||0);i<l;i++){f(c[i],i,l);}};function xEvent(evt){var e=evt||window.event;if(!e)return;if(e.type)this.type=e.type;if(e.target)this.target=e.target;else if(e.srcElement)this.target=e.srcElement;if(e.relatedTarget)this.relatedTarget=e.relatedTarget;else if(e.type=='mouseover'&&e.fromElement)this.relatedTarget=e.fromElement;else if(e.type=='mouseout')this.relatedTarget=e.toElement;if(xDef(e.pageX,e.pageY)){this.pageX=e.pageX;this.pageY=e.pageY;}else if(xDef(e.clientX,e.clientY)){this.pageX=e.clientX+xScrollLeft();this.pageY=e.clientY+xScrollTop();}if(xDef(e.offsetX,e.offsetY)){this.offsetX=e.offsetX;this.offsetY=e.offsetY;}else if(xDef(e.layerX,e.layerY)){this.offsetX=e.layerX;this.offsetY=e.layerY;}else{this.offsetX=this.pageX-xPageX(this.target);this.offsetY=this.pageY-xPageY(this.target);}this.keyCode=e.keyCode||e.which||0;this.shiftKey=e.shiftKey;this.ctrlKey=e.ctrlKey;this.altKey=e.altKey;}function xFirstChild(e,t){if(!(e=xGetElementById(e)))return;var c=e?e.firstChild:null;if(t)while(c&&c.nodeName.toLowerCase()!=t.toLowerCase()){c=c.nextSibling;}else while(c&&c.nodeType!=1){c=c.nextSibling;}return c;}function xGetElementById(e){if(typeof(e)=='string'){if(document.getElementById)e=document.getElementById(e);else if(document.all)e=document.all[e];else e=null;}return e;}function xGetElementsByClassName(c,p,t,f){var found=new Array();var re=new RegExp('\\b'+c+'\\b','i');var list=xGetElementsByTagName(t,p);for(var i=0;i<list.length;++i){if(list[i].className&&list[i].className.search(re)!=-1){found[found.length]=list[i];if(f)f(list[i]);}}return found;}function xGetElementsByTagName(t,p){var list=null;t=t||'*';p=p||document;if(p.getElementsByTagName){list=p.getElementsByTagName(t);if(t=='*'&&(!list||!list.length))list=p.all;}else{if(t=='*')list=p.all;else if(p.all&&p.all.tags)list=p.all.tags(t);}return list||new Array();}function xHasClass(e,c){e=xGetElementById(e);if(!e||!e.className)return false;return(e.className==c)||e.className.match(new RegExp('\\b'+c+'\\b'));};xLibrary={version:'4.06',license:'GNU LGPL',url:'http://cross-browser.com/'};function xNextSib(e,t){if(!(e=xGetElementById(e)))return;var s=e?e.nextSibling:null;if(t)while(s&&s.nodeName.toLowerCase()!=t.toLowerCase()){s=s.nextSibling;}else while(s&&s.nodeType!=1){s=s.nextSibling;}return s;}function xNum(){for(var i=0;i<arguments.length;++i){if(isNaN(arguments[i])||typeof(arguments[i])!='number')return false;}return true;}function xPageX(e){if(!(e=xGetElementById(e)))return 0;var x=0;while(e){if(xDef(e.offsetLeft))x+=e.offsetLeft;e=xDef(e.offsetParent)?e.offsetParent:null;}return x;}function xPageY(e){if(!(e=xGetElementById(e)))return 0;var y=0;while(e){if(xDef(e.offsetTop))y+=e.offsetTop;e=xDef(e.offsetParent)?e.offsetParent:null;}return y;}function xParentNode(ele,n){while(ele&&n--){ele=ele.parentNode;}return ele;}function xRemoveClass(e,c){e=xGetElementById(e);if(!e)return false;if(xHasClass(e,c))e.className=e.className.replace(new RegExp('(^| )'+c+'($| )','g'),'');return true;};function xScrollLeft(e,bWin){var offset=0;if(!xDef(e)||bWin||e==document||e.tagName.toLowerCase()=='html'||e.tagName.toLowerCase()=='body'){var w=window;if(bWin&&e)w=e;if(w.document.documentElement&&w.document.documentElement.scrollLeft)offset=w.document.documentElement.scrollLeft;else if(w.document.body&&xDef(w.document.body.scrollLeft))offset=w.document.body.scrollLeft;}else{e=xGetElementById(e);if(e&&xNum(e.scrollLeft))offset=e.scrollLeft;}return offset;}function xScrollTop(e,bWin){var offset=0;if(!xDef(e)||bWin||e==document||e.tagName.toLowerCase()=='html'||e.tagName.toLowerCase()=='body'){var w=window;if(bWin&&e)w=e;if(w.document.documentElement&&w.document.documentElement.scrollTop)offset=w.document.documentElement.scrollTop;else if(w.document.body&&xDef(w.document.body.scrollTop))offset=w.document.body.scrollTop;}else{e=xGetElementById(e);if(e&&xNum(e.scrollTop))offset=e.scrollTop;}return offset;}