//***********************************************
//* Slashdot Menu script- By DimX
//* Submitted to Dynamic Drive DHTML code library: http://www.dynamicdrive.com
//* Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code
//***********************************************

//***********************************************
//* May 2007: Modified by Ictinus for nested submenus and wrapping menu items.
//***********************************************
//* Note: rarely but reproducably a submenu can appear to be too long for the links 
//* within it, a slight reduction in width will usually make a link wrap to fill up the space.
//* The browser just isn't calculating the height correctly.
//***********************************************

var remember = true; //Remember menu states, and restore them on next visit.
//defaultStates - An array of zeros and ones (0,1,1,0) that represent open (0) and closed (1) menus.
//if the array is empty, no default state of menu will be loaded. if the array has values, but not as many as there are menus, you will be alerted.
var defaultStates = new Array(0,0,0,0,0,0,1,0,1,0,1,0,1,1,1,1,1); 
var contractall_default = true; //Should all submenus be contracted by default? (true or false)

//NOTE: order or priority for menu states is 'remember' (ie. use cookie), defaultStates (if array not empty), 'contractall_default'

var bypixels = 2;   //Basicly it's speed, 
					//but if the (number of submenu elements * bypixels) is larger than the submenu height
					//the menu will change height by ~50% each step.
var collapse_lastmenu = false; //if true, close the last menu that was opened if it was not a parent or child menu.
var collapse_topmenus_only = true; //if true, collapse top level menus only; requires collapse_lastmenu to be true.
var bInstantMenus = false; //if you want the menus to open instantly

var redraw_timeout = 30; //milliseconds to menu redraw incase of window or font resize.

var bRefreshMenu = true; //if true, the submenus will be refreshed if needed at the redraw_timeout interval.
						 //this allows the menu to work with font and window resizing.

var iSubmenuIndent = 10; //number of pixels for submenu menu title indents.
var iSubmenuItemIndent = 10; //number of pixels for submenu item indent level.

//================= should be no need to configure anything below this line, but feel free to be adventurous ===================================
var menu, titles, titletext, submenus, bypixels;
var submenu_haschildren = new Array();
var menuWidth = 0;
var menuHeight = 0;
var lastMenu = 0;
var refreshdelay;
var q = new Array();
var qOpen;
var qClose;
var submenuState = new Array();

function forceRedraw() {
	if ((q[0] == false) && (q[1] == false)) { 
		if ( ( (menuWidth != menu.offsetWidth) || (menuHeight != menu.offsetHeight) ) ) {
			restoreFromCookie();
		}
	}
}

function refreshmenu() {

	if (q[0] == true) {
		(bInstantMenus) ? hidemenunow(qClose, true) : hidemenu(qClose, true);
	}
	if (q[1] == true) {
		(bInstantMenus) ? showmenunow(qOpen, true, true) : showmenu(qOpen, true);
	}
	// only redraw the menu if we aren't closing or opening a submenu
	if ((q[0] == false) && (q[1] == false)) {
		//and only do it if the menu dimensions have changed
		if ( ( (menuWidth != menu.offsetWidth) || (menuHeight != menu.offsetHeight) ) ) { 
			if (bRefreshMenu) restoreFromMemory();
		}
	}
}

function slash_expandall(bStore){
	if (typeof menu!="undefined"){
		for(var i=submenus.length-1; i >= 0; i--){
			if (submenu_haschildren[i] == true)
				showmenunow(i, bStore, false);
		}
	}
}

function slash_contractall(bStore){
	if (typeof menu!="undefined"){
		for(var i=0; i<submenus.length; i++){
			hidemenunow(i, bStore);
		}
	}
}

function initmenu(){
	var sublvls;

    menu = getElementsByClassName("sdmenu", "div", document)[0];
    titles = getElementsByClassName("title", "span", menu);
    submenus = getElementsByClassName("submenu", "div", menu);
    titletext = getElementsByClassName("tt", "span", menu);

	//if ((defaultStates.length > 0) && (defaultStates.length != submenus.length)) { alert('The number of default states is ' + defaultStates.length + ', but the number of menus is ' +submenus.length) }

    for(var i=0; i<submenus.length; i++) {
        titles[i].onclick = gomenu;
        titletext[i].onclick = gomenu;
        submenus[i].style.height = submenuHeight(submenus[i])+"px";
		submenu_haschildren[i] = (submenus[i].getElementsByTagName("*").length > 0)
    }
	setSubmenuMargins(menu, 0);

	//set the menu to the appropriate collapse/expand state
    (remember) ? restoreFromCookie() : restoreStates(defaultStates);
	
	refreshdelay = setInterval("refreshmenu()", redraw_timeout);

	q[0] = false; q[1] = false; //set no current menu activity
}

function setSubmenuMargins(oElm, currentMargin) {
	var oElmCurrent = oElm.firstChild;
	while (oElmCurrent) {
		if (oElmCurrent.className == "submenu") {
			setSubmenuMargins(oElmCurrent, parseInt(currentMargin) + parseInt(iSubmenuIndent));
		} else if (oElmCurrent.nodeType == 1) {//nodeType test to skip non-HTML elements, i.e. text nodes
			if (oElmCurrent.nodeName == "A") {
				if ((oElmCurrent.firstChild.className == "title") || (oElmCurrent.firstChild.className == "titlehidden")) {//submenu title link
					oElmCurrent.firstChild.firstChild.style.marginLeft = String(currentMargin) + "px" ;
				} else {//normal menu link
					oElmCurrent.firstChild.style.marginLeft = String(parseInt(currentMargin)+(parseInt(iSubmenuItemIndent))) + "px" ;
				}
			} else if ((oElmCurrent.className == "title") || (oElmCurrent.className == "titlehidden")) {
				oElmCurrent.firstChild.style.marginLeft = String(currentMargin) + "px" ;
			}
		}
		oElmCurrent = oElmCurrent.nextSibling;
	}
}

function restoreStates(arrStates) {
	if (submenus.length == arrStates.length) {
		for (var i=arrStates.length-1; i>=0; i--) {
			if (arrStates[i] == 1 || submenu_haschildren[i] == false) {
				hidemenunow(i, true);
			} else {
				showmenunow(i, true, false);
			}
   		} 
	} else {
		if (submenus.length == defaultStates.length) {
			restoreStates(defaultStates);
		} else if (contractall_default == true) {
			slash_contractall(true);
		} else {
			slash_expandall(true);
		}
	}
}

function restoreFromMemory() {

	for (var i=submenuState.length-1; i>=0; i--) {
		if (submenuState[i] == 1) {
			if (parseInt(submenus[i].style.height) != 0 || submenu_haschildren[i] == false) {
				hidemenunow(i, false); // no need to store cookie info, it will be the same in the end
			}
		} else {
			if (parseInt(submenus[i].style.height) != submenuHeight(submenus[i])) {
				showmenunow(i, false, false); // no need to store cookie info, it will be the same in the end
			}
		}
   	}
}

function restoreFromCookie() {
    if (getcookie("menu") != null) {
        submenuState = getcookie("menu").split(",");
		restoreStates(submenuState);
    } else {
		restoreStates(defaultStates);
	}
	menuWidth = menu.offsetWidth;
	menuHeight = menu.offsetHeight;
}

function gomenu(e) {
	// if we don't allow multiple menus to be manipulated at the same time, check to see if one is being manipulated
   	if ((q[0] == true || q[1] == true) && collapse_lastmenu == false) return;

    if (!e) var e = window.event;
    var ce = (e.target) ? e.target : e.srcElement;
    var sm;

	i = 0; sm = -1;
	while (i < titles.length && sm == -1) {
        if(titles[i] == ce || titletext[i] == ce) sm = i;
		i++;
    }

    if (submenu_haschildren[sm] == true) { //only expand the menu if it has sub elements
 		// do not allow the event to bubble up to containing span
		// keep anti-bubble code within "if submenu_haschildren[sm] == true"
		if( e.preventDefault ) { e.preventDefault(); }
		e.returnValue = false;
		if( e.stopPropagation ) { e.stopPropagation(); }
		e.cancelBubble = true;
		
    	if(parseInt(submenus[sm].style.height) > 0) {
				qClose = sm;
				q[0] = true;//action taken in function refreshMenus				
    	} else if (parseInt(submenus[sm].style.height) == 0) { //
				if (collapse_lastmenu == true) {
					//don't collapse lastmenu when it is related... menu 1/submenu 1.1/submenu 1.1.1
					if (isAncestor(submenus[sm], submenus[lastMenu]) != true) {
						if (isAncestor(submenus[lastMenu], submenus[sm]) != true) {
							qClose = lastMenu;
							q[0] = true; //action taken in function refreshMenus
						}
					}
		     	}
				qOpen = sm;
				q[1] = true; //action taken in function refreshMenus
    	}
	}
}

function isAncestor(oElm, oElmTest) {

	if (oElm.className != 'sdmenu') {
		if (oElm == oElmTest) {
			return (true);
		} else {
			return (isAncestor(oElm.parentNode, oElmTest));
		}
	} else {
		return (false);
	}	
}

function expandChildren(oElm) {
	//recursively expand child submenus (that are not hidden) of the given menu element
	//I don't believe this should be needed, but without it the auto-resize/restore function 
	//prevented submenus from being drawn as menus expand.

	var oElmCurrent = oElm.firstChild;

	while (oElmCurrent) {
		if (oElmCurrent.className == "submenu") {
			if (oElmCurrent.style.display != "none") {
				oElmCurrent.style.height = submenuHeight(oElmCurrent)+"px"; 
				expandChildren(oElmCurrent);
			}
		}	
		oElmCurrent = oElmCurrent.nextSibling;
	}
}

function changeHeight(oElm, iDelta) {
	//recursively change the height of the object oElm and its parent until the parent object is 'sdmenu' or the parent not displayed
	//Note: if iDelta is negative, the menu height will be decreased, if it is positive, the menu height will increase.
	var newHeight;

	expandChildren(oElm); //ensure submenus that should be displayed are displayed.
	while ((oElm.className != "sdmenu") && (oElm.style.display != "none")) {
		newHeight = parseInt(oElm.style.height) + iDelta;
		if (newHeight <= 0) {
			oElm.style.height = "0px";
		} else {
    		oElm.style.height = newHeight+"px";
   		}
		var lastElm = oElm;
		oElm = oElm.parentNode;
	}

}

function hidemenu(sm, bStore) {
	var iDelta = submenuChildCount(submenus[sm])*bypixels;

	if (iDelta >= parseInt(submenus[sm].style.height))
		iDelta = Math.floor((parseInt(submenus[sm].style.height)+1)/2);
	
	changeHeight(submenus[sm], -iDelta);

    if(parseInt(submenus[sm].style.height) == 0) {
        titles[sm].className = "titlehidden";
        titletext[sm].className = "tthidden";
        submenus[sm].style.display = "none";
 		if (bStore == true) { store(); }
		//must set q[0] here because this is the only time we know the hiding has finished.
		q[0] = false;
	}
}

function hidemenunow(sm, bStore) {
        //whatever the height is, reduce the height by this much to make it 0, make the same height change to parent elements
        changeHeight(submenus[sm], -parseInt(submenus[sm].style.height));
	    titles[sm].className = "titlehidden";
        titletext[sm].className = "tthidden";
        submenus[sm].style.display = "none";
 		if (bStore == true) store();
		//must set q[0] here because this is the only time we know hidemenu has finished.
		q[0] = false;
}


function submenuChildCount(oElm) {
	var subMenuCC = 0;
	var oElmCurrent = oElm.firstChild;

	while (oElmCurrent) {
		if (oElmCurrent.className == "submenu") {
			if (oElmCurrent.style.display != "none") {
				subMenuCC = subMenuCC + submenuChildCount(oElmCurrent);
			}
		} else if (oElmCurrent.nodeType == 1) { //not submenu so add height of element...
			//nodeType test to skip non-HTML elements, i.e. text nodes
			subMenuCC = subMenuCC + 1;
		}	
		oElmCurrent = oElmCurrent.nextSibling;
	}
	return subMenuCC;
}

function submenuHeight(oElm) {
var th = 0;
var oElmCurrent;

	oElmCurrent = oElm.firstChild;

	while (oElmCurrent) {
		if (oElmCurrent.className == "submenu") {
			if (oElmCurrent.style.display != "none") {
				th = th + submenuHeight(oElmCurrent);
			}
		} else if (oElmCurrent.nodeType == 1) { //not submenu so add height of element...
			//nodeType test to skip non-HTML elements, i.e. text nodes
			th = th + oElmCurrent.offsetHeight;
		}	
		oElmCurrent = oElmCurrent.nextSibling;
	}
	return th;
}

function showmenu(sm, bStore, bRememberLastMenu) {
	
	var iDelta;
	var iChildCount = submenuChildCount(submenus[sm]);

    submenus[sm].style.display = "";
    titles[sm].className = "title";
   	titletext[sm].className = "tt";
	var i = 0;

	var submenuContentHeight = submenuHeight(submenus[sm]);
	iDelta = iChildCount*bypixels;

	if (iDelta >= submenuContentHeight - parseInt(submenus[sm].style.height)) {
		iDelta = Math.floor((submenuContentHeight - parseInt(submenus[sm].style.height)+1)/2);
	}
	changeHeight(submenus[sm], iDelta);
	
    if (parseInt(submenus[sm].style.height) == submenuContentHeight) {
		if ((collapse_topmenus_only != true) || (submenus[sm].parentNode == menu)) { lastMenu = sm; } //remember the appropriate lastmenu
	 	if (bStore == true) { store(); }
		//must set q[1] here because this is the only time we know showmenu has finished.
		q[1] = false;
	}
}

function showmenunow(sm, bStore, bRememberLastMenu) {
    submenus[sm].style.display = "";
    titles[sm].className = "title";
   	titletext[sm].className = "tt";

	//calculate the last iDelta, ie. the difference between the submenu div height and the submenu content height
    var iDelta = submenuHeight(submenus[sm]) - parseInt(submenus[sm].style.height);
    changeHeight(submenus[sm], iDelta);
	if (bRememberLastMenu && ((collapse_topmenus_only != true) || (submenus[sm].parentNode == menu))) { lastMenu = sm; } //remember the appropriate lastmenu
	if (bStore == true) store();
	//must set q[1] here because this is the only time we know the hiding has finished.
	q[1] = false;
}

function store() {
    submenuState = new Array();

    for (var i=0; i<submenus.length; i++) {
		if (submenus[i].style.display == "none" || submenu_haschildren[i] == false) {
			submenuState.push(1);  //collapsed
		} else {
            submenuState.push(0);  //expanded
		}
    }
    putcookie("menu", submenuState.join(","), 30);
}

function getElementsByClassName(strClassName, strTagName, oElm){
    var arrElements = (strTagName == "*" && document.all) ? document.all : oElm.getElementsByTagName(strTagName);
    var arrReturnElements = new Array();
    strClassName = strClassName.replace(/\-/g, "\\-");
    var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
    var oElement;
    for(var i=0; i<arrElements.length; i++){
        oElement = arrElements[i];      
        if(oRegExp.test(oElement.className)){
            arrReturnElements.push(oElement);
        }
    }
    return (arrReturnElements)
}

function putcookie(c_name,value,expiredays) {
    var exdate=new Date();
    exdate.setDate(exdate.getDate()+expiredays);
    document.cookie = c_name + "=" + escape(value) + ((expiredays==null) ? "" : ";expires="+exdate + "; path=/");
}

function getcookie(c_name) {
    if(document.cookie.length > 0) {
        var c_start = document.cookie.indexOf(c_name + "=");
        if(c_start != -1) {
            c_start = c_start + c_name.length + 1;
            var c_end = document.cookie.indexOf(";",c_start);
            if(c_end == -1)
                c_end = document.cookie.length;
            return unescape(document.cookie.substring(c_start, c_end));
        }
    }
    return null;
}
