/*	
	(c) Remy@Smart, pour Smart Agence, 2007 - Global object managing a sitemap with (un)foldable levels
	New version : full object-oriented
*/
/* Main object [constructor] */
function SiteMapManager(sID) {
	// Members
	this.moduleName = "SiteMap Manager";
	this.moduleVersion = "2.0";
	this.sSiteMapID = sID;
	this.olToggleSwitch = { isUsed: false, sContainerID: "" };
	this.olTitleFolding = { isUsed: false, sTag: ""};
	this.olTreeStyles = { sTree: "treeview", sNormal: "aq3bullet", sClosed: "aq3closed", sOpened: "aq3open" };
	this.aTitleTargets = [];
	this.aTargets = [];
	this.isDOM = document.createElement && document.getElementById;
	this.isOk = sID ? (document.getElementById(sID) ? true : false) : false;
	this.oDOMUtils = DOMUtils;
	this.bHideAllByDefault = true;
	
	this.toString = function() {
		return this.moduleName + ' ' + this.moduleVersion;
	};
	
	this.setHideAllByDefault = function(bValue) {
		this.bHideAllByDefault = bValue;
		return this;
	};
	
	this.setToggleSwitch = function(isUsed, sContainerID) {
		if(isUsed) {
			this.olToggleSwitch.isUsed = isUsed;
		}
		
		if(sContainerID !== "") {
			this.olToggleSwitch.sContainerID = sContainerID;
		}
		
		return this;
	};
	
	this.setTitleFolding = function(isUsed, sTag) {
		if(isUsed) {
			this.olTitleFolding.isUsed = isUsed;
		}
		
		if(sTag !== "") {
			this.olTitleFolding.sTag = sTag;
		}
		
		return this;
	};
	
	this.setTreeStyles = function(olParams) {
		this.olTreeStyles = olParams;
		
		return this;
	};
	
	return this;
}	// SiteMapManager class definition + constructor


/* Extention via prototyping */
SiteMapManager.prototype.apply = function() {
	if(this.isDOM && this.isOk) {
		this.makeTreesClickable();
		this.addToggleSwitch();
	}
	else if(!this.isDOM) {
		alert("Your browser seems not to support DOM enough. Script execution halted.");
	}
	else {
		alert("Sitemap container #" + this.sSiteMapID + " not found ! Script execution halted.");
	}
};	// apply

/*
	Notice : the 3 next methods are initially derived from aqtree3clickable.js. Converts an unordered list to an explorer-style tree, with clickable icons.
	See http://www.kryogenix.org/code/browser/aqlists/ for more details.
	This is, here, an integration to our SiteMapManager object.
*/
SiteMapManager.prototype.makeTreesClickable = function() {
	var aUL = document.getElementById(this.sSiteMapID).getElementsByTagName("UL");
	var index = 0;
	if(this.olTitleFolding.isUsed) {
		this.applyTitleFolding();
	}
	for (index=0;index<aUL.length;index++) {
		aUL[index].className = (aUL[index].className != "" ? aUL[index].className +" "+this.olTreeStyles.sTree : this.olTreeStyles.sTree);
		this.processList(aUL[index]);
	}
};	// makeTreesClickable

SiteMapManager.prototype.processList = function(oUL) {
	if (oUL.childNodes && oUL.childNodes.length > 0) {
		// Iterate LIs
		for (var index=0; index < oUL.childNodes.length; index++) {
			var oItem = oUL.childNodes[index];
			if (oItem.nodeName == "LI") {
				// Iterate things in this LI		
				/* Note :
				   We MUST initialize these vars to "null", otherwise a strange bug accurs : if a <li> element, not containing any <ul> sublist, follows a <li> containing a <ul>, it's treated as having a sub-list.
				   Setting these vars to "null" helps resolving the bug.
				*/
				var oLink = null;
				var oChildUL = null;
			
				for(var index2=0;index2<oItem.childNodes.length;index2++) {
					var oSubItem = oItem.childNodes[index2];
					switch (oSubItem.nodeName) {
						case "A":
							oLink = oSubItem; 
						break;
						case "UL":
							/*in case of a UL list, we store it in an associative array principle : aTarget[parentID] = theSubList; */
							oChildUL = oSubItem;
							oChildUL.parentNode.setAttribute("id",this.getRandomID("smartMap_",2));
							if(this.bHideAllByDefault) {
								this.aTargets[oItem.id] = oItem.removeChild(oChildUL);
							}
							// recursive traversing
							this.processList(oChildUL);
						break;
					}
				}
				/*
				  if list => associate link and list and set active classes to the link's parent
				  if link and no list => add normal class to the link				
				*/
				if(oChildUL) {
					this.associateLinkToList(oLink);
				}
				else {
					oLink.parentNode.className = this.olTreeStyles.sNormal;
				}
			}
		}
	}
};	// processUL

SiteMapManager.prototype.associateLinkToList = function(oLink) {
	if (oLink.parentNode.className.indexOf(this.olTreeStyles.sNormal)==-1){
		if(this.bHideAllByDefault) {
			oLink.parentNode.className = this.olTreeStyles.sClosed;
		}
		else {
			oLink.parentNode.className = this.olTreeStyles.sOpened;
		}
		this.triggerFolding(oLink.parentNode);
	}
};	// associateLinkToList

/* ---------------------- Next methods are custom ones ---------------------- */

SiteMapManager.prototype.triggerFolding = function(oItem) {
	// HTML string of the element enabling folding/unfolding
	var sOpenClose='<a class="open_close" href="" title="Afficher le sous niveau"><span class="fx">[Afficher le sous-niveau]</span></a> '+ oItem.innerHTML;
	var self = this; // keeping a reference to the caller SMM object. We need this because we'll have to call some of its methods during event firing
	oItem.innerHTML = sOpenClose;
	
	if (oItem.firstChild && oItem.firstChild.className=="open_close") {
		oItem.firstChild.onclick = oItem.firstChild.onkeypress = function(event) {
			// keyboard
			var oEvent = (window.event ? window.event : event);
			var keyBoardUsed = (oEvent.type == "keypress" ? true : false);
			var iTouche = -1;
			var oFXSpan = null;
			
			if(keyBoardUsed) {
				iTouche = (window.event ? window.event.keyCode : oEvent.which);
			}
				
			// mouse used, or keyboard with "space" key ?
			if(keyBoardUsed === false || (keyBoardUsed === true && iTouche == 32)) {
				// getting the span.fx element
				oFXSpan = self.oDOMUtils.getFirstChild(oItem).firstChild;
				var oLastChild = self.oDOMUtils.getLastChild(oItem);
				if(oLastChild.nodeName == "UL") {
					// Folding : removing the sublist from the DOM
					if(!self.bHideAllByDefault) {
						self.aTargets[oItem.id] = oItem.removeChild(oLastChild);
					}
					else {
						oItem.removeChild(oLastChild);
					}
					oItem.className = self.olTreeStyles.sClosed;
					self.oDOMUtils.getFirstChild(oItem).setAttribute('title','Afficher le sous niveau');
				}
				else {
					// Unfolding : inserting the related sublist as last child of its parent
					oItem.appendChild(self.aTargets[oItem.id]);
					oItem.className = self.olTreeStyles.sOpened;
					self.oDOMUtils.getFirstChild(oItem).setAttribute('title','Masquer le sous niveau');
				}
				oFXSpan.innerHTML = (oItem.className == self.olTreeStyles.sOpened ? "[Masquer le sous-niveau]":"[Afficher le sous-niveau]");
			}
			
			return false;
		};
	}
};	// triggerFolding

SiteMapManager.prototype.applyTitleFolding = function() {
	var aTitles = document.getElementById(this.sSiteMapID).getElementsByTagName(this.olTitleFolding.sTag);
	var oLink = null;
	var self = this;
	var oNext = null;
	for( var i = 0; i < aTitles.length; i++) {
		oNext = this.oDOMUtils.getNextSibling(aTitles[i]);

		if(oNext && oNext.nodeName.toLowerCase() == "ul") {
			aTitles[i].innerHTML = '<a class="open_close" title="Masquer le niveau" href=""><span class="fx">[Masquer le niveau]</span></a> ' + aTitles[i].innerHTML;
			oLink = aTitles[i].firstChild;
			oNext.setAttribute("id", this.getRandomID("smartMap_",2));
			aTitles[i].setAttribute("rel",oNext.id);
	//		oLink.nextSibling.nextSibling.innerHTML += "<sup>[rel = " + aTitles[i].getAttribute("rel") + "]</sup>";
			aTitles[i].className = this.olTreeStyles.sOpened;
		}
		
		oLink.onclick = oLink.onkeypress = function(event) {
			// keyboard
			var oEvent = window.event ? window.event : event;
			var keyBoardUsed = (oEvent.type == "keypress" ? true : false);
			var iTouche = -1;
			var oTitle = null;
			var oFXSpan = null;
			
			if(keyBoardUsed) {
				iTouche = window.event ? window.event.keyCode : oEvent.which;
			}
			
			// mouse used, or keyboard with "space" key ?
			if(!keyBoardUsed || (keyBoardUsed && iTouche == 32)) {
				// getting the span.fx element
				oFXSpan = this.firstChild;
				oTitle = this.parentNode;
				if(oTitle.className == self.olTreeStyles.sOpened) {	// folding => removing UL from the tree
					self.aTitleTargets[oTitle.getAttribute("rel")] = oTitle.parentNode.removeChild(self.oDOMUtils.getNextSibling(oTitle));
					oTitle.className = self.olTreeStyles.sClosed;
					oTitle.firstChild.setAttribute('title','Afficher le niveau');
					oFXSpan.innerHTML = "[Afficher le niveau]";
				}
				else {
					// unfolding : inserting the related sublist as next sibling
					oTitle.parentNode.insertBefore(self.aTitleTargets[oTitle.getAttribute("rel")],oTitle.nextSibling);
					oTitle.className = self.olTreeStyles.sOpened;
					oTitle.firstChild.setAttribute('title','Masquer le niveau');
					oFXSpan.innerHTML = "[Masquer le niveau]";
				}
			}	// if
			
			return false;
		};	// onclick = onkeypress = function()
	}	// for
};	// applyTitleFolding

SiteMapManager.prototype.getRandomID = function(sPrefix,iInLength) {
	var sChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
	var sRandomID = "";
	var iLength = (iInLength > 0 ? iInLength : 1);
	do {
		for(var x=0;x<iLength;x++) {
			var i = Math.floor(Math.random() * 62);
			sRandomID += sChars.charAt(i);
		}
		sRandomID = sPrefix+sRandomID;
	} while (document.getElementById(sRandomID));
	return sRandomID;
};	// getRandomID

SiteMapManager.prototype.addToggleSwitch = function() {
	// nothing special : DOM manipulation on elements
	var oP = document.createElement("P");
	var oLink = document.createElement("A");
	var oLink_content = document.createTextNode("Tout afficher");
	var oParent = document.getElementById(this.olToggleSwitch.sContainerID);
	var self = this;
	// make sure all elements do exist in the document
	if(oParent){
		// Setting Paragraph attributes
		oP.setAttribute("id","toggle");
		
		// Setting Link attributes
		oLink.href="";
		oLink.className="toggleShowAll";
		// we also manage keyboard to show/hide lists (space-key used)
		oLink.onclick = oLink.onkeypress = function(event){
			// keyboard
			var oEvent = (window.event ? window.event : event);
			var keyBoardUsed = (oEvent.type == "keypress" ? true : false);
			var iTouche = -1;
			
			if(keyBoardUsed) {
				iTouche = (window.event ? window.event.keyCode : oEvent.which);
			}
			
			// mouse used, or keyboard with "space" key ?
			if(!keyBoardUsed || (keyBoardUsed && iTouche == 32)) {
				self.toggleLists(this);
				// référence au LIEN
				switch(this.className) {
					case "toggleHideAll" :
						this.innerHTML = "Tout afficher";
						this.className = "toggleShowAll";
					break;
					case "toggleShowAll" :
						this.innerHTML = "Tout masquer";
						this.className = "toggleHideAll";
					break;
				}
			}
			
			return false;
		};
		// inserting the toggle element
		oLink.appendChild(oLink_content);
		oP.appendChild(oLink);
		oParent.insertBefore(oP,oParent.firstChild);
	}
};	// addToggleSwitch

SiteMapManager.prototype.toggleLists = function(oToggleLink) {
	var oPlanSite = document.getElementById(this.sSiteMapID);
	var aTitles = [];
	var aListElements = oPlanSite.getElementsByTagName("LI");
	var oFXSpan = null;
	var i = 0;
	var oNext = null;
	
	// if using title folding, we must process them first
	if(this.olTitleFolding.isUsed) {
		aTitles = oPlanSite.getElementsByTagName(this.olTitleFolding.sTag);
		for(i = 0; i < aTitles.length; i++) {
			oFXSpan = aTitles[i].firstChild.firstChild;
			oNext = this.oDOMUtils.getNextSibling(aTitles[i]);
			switch(oToggleLink.className) {
				case "toggleHideAll" :	// hide level
					if(oNext.nodeType != 3 && oNext.nodeName.toLowerCase() === "ul") {	// removing UL from the tree
						this.aTitleTargets[aTitles[i].getAttribute("rel")] = aTitles[i].parentNode.removeChild(oNext);
						aTitles[i].className = this.olTreeStyles.sClosed;
						aTitles[i].firstChild.setAttribute('title','Afficher le niveau');
						oFXSpan.innerHTML = "[Afficher le niveau]";
					}
				break;
				case "toggleShowAll" :	// show level
					// inserting the related sublist as next sibling
					if(aTitles[i].className === this.olTreeStyles.sClosed) {
						aTitles[i].parentNode.insertBefore(this.aTitleTargets[aTitles[i].getAttribute("rel")], aTitles[i].nextSibling);
						aTitles[i].className = this.olTreeStyles.sOpened;
						aTitles[i].firstChild.setAttribute('title','Masquer le niveau');
						oFXSpan.innerHTML = "[Masquer le niveau]";
					}
				break;
			}	// switch
		}	// for
	}	// if
	
	// whatever we use title folding (or not), we process sub-lists
	for(i = 0; i < aListElements.length; i++) {
		if(aListElements[i].id) {
			// (re)plier (fold)
			switch(oToggleLink.className) {
				case "toggleHideAll":
					if(aListElements[i].lastChild.nodeName == "UL") {
						aListElements[i].firstChild.firstChild.innerHTML = "[Afficher le sous-niveau]";
						aListElements[i].removeChild(aListElements[i].lastChild);
						aListElements[i].className = this.olTreeStyles.sClosed;
					}
				break;
				case "toggleShowAll":
					aListElements[i].firstChild.firstChild.innerHTML = "[Masquer le sous-niveau]";
					aListElements[i].appendChild(this.aTargets[aListElements[i].id]);
					aListElements[i].className = this.olTreeStyles.sOpened;
				break;
			}
		}
	}
};	// toggleLists