//***********************************************
//* 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 defaultStates = new Array(); 
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);
}

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;
}

