/************************************************************************ 
Author: Eric Simmons
Contact: info AT jswitch DOT com
Website: http://www.jswitch.com
Version: 2.0 12/2005
Browser Target: Mozilla 6+/FireFox Netscape 6+, IE 5.0+
Type: XP Style Menus ver 2

DISCLAIMER:
THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT
ANY EXPRESS OR IMPLIED WARRANTIES, JSWITCH.COM
IS NOT RESPONSIBLE FOR ANY ADVERSE AFFECTS TO
YOUR COMPUTER OR SYSTEMS RUNNING THIS SCRIPT.

LICENSE:
YOU ARE GRANTED THE RIGHT TO USE THE SCRIPT
PERSONALLY OR COMMERCIALLY. THE AUTHOR, WEBSITE LINKS 
AND LICENSE INFORMATION IN THE HEADER OF THIS SCRIPT
MUST NOT BE MODIFIED OR REMOVED. IF PORTIONS OF THE 
CODE ARE TRANSFERED TO ANOTHER SCRIPT THE AUTHORS NAME
AND CONTACT INFORMATION MUST BE STATED IN THE NEW SCRIPT
BY THE PORTIONS CODE THAT HAVE BEEN TRANSFERED.


v 2.0
-W3C HTML 4.01 Transitional compliant
-Support for initially open menu
-New fading capability
-Cookie menu persistence
-Redesigned code


Notes:

If you want a menu to be open initially set the style display to
"inline" in the subMenu class div like this...
<div class="subMenu" style="display:inline;">


The fading effect can be disabled by setting the 
doFading var to false. I have only tested this feature on 
ie 6, Netscape 1.7, firefox 1 & 1.5

If the user has cookies enabled the script will bake a cookie ;) on
the browser to track the state of the menus. This is good news for those
of you who don't use frames, you can now have persistent menu states

In version 2's example I have also added some wintery, <- is that even a word,
themes to the menus, You can see how I did by looking at the html and the 
style sheet.

Make sure you put the java script include tag after all the menus otherwise
it will not be able find and initialize them. If you have questions, or comments
about the XpStyle Menu v2 send me an email.

And lastly a plug for myself, If you need some professional help with technical
web development please drop me an email, I do contracts for a competitive price. :)



v 1.0
XP style sliding Menu Bar

***********************************************************************/

/*
Modified throughout by Grant Paton-Simpson
Enables setting of extra length for menu to cater to double lines
Clearer naming of key variables e.g. dropMenu
Broekn into more discrete functions
Renamed all css
Put wrapper div (class='sf-wrapper') around topItem so it can effectively have 
	two background images - one for the arrow and another for the underline
Subitem height is now a combination of the height and padding-top and -bottom settings
	for better layout
Top items are also links so everything works even if JS is disabled
Block not inline for subitems and other misc formatting changes plus documentation code
In PHP - Removed div wrapping set of menus and incorporated dropmenus into a menu structure
PHP interface to facilitate rapid reuse of dropmenu in other projects
Use CSS a:hover etc for hover formatting of subitems rather than JS
*/

var cookieDropMenuHeightsArray = new Array();

var dropMenusArray = new Array();
var subMenusArray = new Array();
var numSubItemsArray = new Array();
var mainMenusArray = new Array();
var dropMenuHeightsArray = new Array();
var setIntervalsArray = new Array();
var visibilityDropMenusArray = new Array(); // booleans - display set to block or none
var topItemsArray = new Array();
var menuExtensionArray = new Array();

var timerSlide = null;
var slideDelay = 15; //milliseconds so no gains going any smaller
var subItemHeight = 28;
var moveSlidePix = 15; //how much it moves per cycle
var isLocked = false;
var doFading = true;


//get any existing menu settings from the cookie (if present)
cookieDropMenuHeightsArray = GetUserCookie("xpMenuCookv2").split(","); //alert(cookieDropMenuHeightsArray.toString());


//process drop menus
processDropMenus();


//Identify dropmenu divs and process them one by one
function processDropMenus(){
	
	var divs, numDivs, menuCounter, i, topItem, wrapper, mainMenu, dropMenu, extension, 
		menuExtension, subMenu;
	
	divs = document.getElementsByTagName("DIV");

	numDivs = divs.length;
	
	menuCounter = 0; //initialise
	
	for(i=0; i < numDivs; i++){
		
		if(String(divs.item(i).className) == "sf-top-item"){
			
			//identify the components of the dropmenu we need to work with
			topItem = divs.item(i);
			wrapper = topItem.parentNode;
			mainMenu = wrapper.parentNode;
			dropMenu = mainMenu.getElementsByTagName("DIV").item(3); //alert(dropMenu.className); //debugging //NB childNodes includes new line characters in W3C!
			extension = mainMenu.getElementsByTagName("DIV").item(5); 
			menuExtension = parseInt(extension.style.height); //alert(menuExtension); //debugging
			subMenu = dropMenu.getElementsByTagName("DIV").item(0); //alert(subMenu.className); //debugging			

			/*add the details and objects of this particular menu to the relevant arrays.
			Some details have to be added after processing the dropMenu*/
			dropMenusArray[menuCounter] = dropMenu;
			subMenusArray[menuCounter] = subMenu;
			mainMenusArray[menuCounter] = mainMenu;
			topItemsArray[menuCounter] = topItem;
			menuExtensionArray[menuCounter] = menuExtension;
				
			//process dropmenu
			processDropMenu(menuCounter, mainMenu, topItem, dropMenu, subMenu, menuExtension);
			
			//add to dropmenus counter
			menuCounter++;
		}
	}
}


//Process individual dropmenu
function processDropMenu(menuCounter, mainMenu, topItem, dropMenu, subMenu, menuExtension){
	
	var styleRules, rawHeight, paddingTop, paddingBottom, dropMenuHeight, hideMenu=true;
	
	//if a cookie set, use that to determine the height for this particular submenu
	if(cookieDropMenuHeightsArray != 0 && menuCounter < cookieDropMenuHeightsArray.length){
		subMenu.style.height = cookieDropMenuHeightsArray[menuCounter] + "px"; //alert("Starting height of subMenu is: " + subMenu.style.height); //starting height
	}

    
    //count subitems in dropmenu
    numSubItemsArray[menuCounter] = countSubItems(subMenu); //alert(numSubItemsArray[menuCounter]);

    
	//get subItem height
	subItemHeight = getSubItemHeight(subMenu); //alert("subItemHeight is: " + subItemHeight);

	
	//respond to lock setting (lock open and skip setting response to clicking topItem)
	if(isLocked){
		
	    //show menu
	    hideMenu = false;
		
	} else {
		
	    //hide or display dropMenu
		if(cookieDropMenuHeightsArray != 0){
			if(parseInt(cookieDropMenuHeightsArray[menuCounter]) != 0){
				hideMenu = false; //default is true
			}
		}
			
	    //set action in response to topItem being clicked
	    topItem.onclick = setSlide; //setSlide cannot receive parameters so need to use 'this' to identify index number **************************
	}
	
	//hide or display menus
    
    setSubMenuVisibility(subMenu, hideMenu);
    setDropMenuHeightAndOpacity(hideMenu, dropMenu, menuCounter, subItemHeight, menuExtension);
}


//Get subItem height - currently implemented by reading CSS using document.styleSheets which fails with Opera
function getSubItemHeight(subMenu){

	var firstSubItemOffset, i, j, styleRules, tmpStr1, tmpStr2, 
		rawHeight, paddingTop, paddingBottom, subItemHeight;
	
	
	firstSubItemOffset = getFirstSubItem(subMenu);
	
	for(i=0; i < document.styleSheets.length; i++){	

		if(-1 != String(document.styleSheets[i].href).indexOf("css/sf.css")){
			break;
		}
	}
	
	if(!document.styleSheets[i].rules){
		styleRules = document.styleSheets[i].cssRules;
	} else {
		styleRules = document.styleSheets[i].rules;
	}
	
	for(j=0; j < styleRules.length; j++){
		
		tmpStr1 = String(styleRules[j].selectorText);
		tmpStr2 = String("." + subMenu.childNodes.item(firstSubItemOffset).className);
		
		if(tmpStr1 == tmpStr2){
			
			if(NaN != parseInt(styleRules[j].style.height)){
				
				rawHeight = parseInt(styleRules[j].style.height);
				paddingTop = parseInt(styleRules[j].style.paddingTop);
				paddingBottom = parseInt(styleRules[j].style.paddingBottom);
				subItemHeight = rawHeight + paddingTop + paddingBottom;
				return subItemHeight;
			}
		}
	}
}


//get offset of first subItem
function getFirstSubItem(subMenu){
	
	var i;
    for (i=0; i < subMenu.childNodes.length; i++){
    	
        if (subMenu.childNodes.item(i).tagName == "SPAN"){
            break;
        }
    }
    return i;
}


//set visibility of subMenu
function setSubMenuVisibility(subMenu, hideMenu){
	
	if(hideMenu){
		subMenu.style.display = 'none';
	} else {
		subMenu.style.display = 'block';
	}
}


//set dropMenu height and opacity (NB not same as subMenu height)
function setDropMenuHeightAndOpacity(hideMenu, dropMenu, menuCounter, subItemHeight, menuExtension){
	
	var numMenuItem, dropMenuHeight;
	
	
	numMenuItem = numSubItemsArray[menuCounter];
	
    if(!hideMenu){
    	
     	dropMenuHeight = parseInt(numMenuItem * subItemHeight) + parseInt(menuExtension);
        
        //initial height on open
        dropMenu.style.height = dropMenuHeight + "px";
        
        dropMenuHeightsArray[menuCounter] = dropMenuHeight;
        visibilityDropMenusArray[menuCounter] = true;
        
        if(doFading){
			if (dropMenu.filters){
				dropMenu.filters.alpha.opacity = 100;
			} else {
				dropMenu.style.opacity = 1;
			}
		}
        
    } else { //none i.e. hidden - starting display
    	
		topItemsArray[menuCounter].className = topItemsArray[menuCounter].className+"-over"; /*topItem*/
        
		dropMenuHeight = 0;
        
		dropMenu.style.height = dropMenuHeight + "px"; //height when not displayed
        
		dropMenuHeightsArray[menuCounter] = dropMenuHeight;
		visibilityDropMenusArray[menuCounter] = false;
        
        if(doFading){
			if (dropMenu.filters){
				dropMenu.filters.alpha.opacity = 0;
			} else {
				dropMenu.style.opacity = .0;
			}
		}
    }	
}


//count subItems in subMenu
function countSubItems(subMenu){
	
	var i, numMenuItem = 0;
	
	
    for (i=0; i < subMenu.childNodes.length; i++){
    	
        if (subMenu.childNodes.item(i).tagName == "SPAN"){
            numMenuItem ++;
        }
    }
    return numMenuItem;
}


//sets the interval for the runSlide function (which turns the interval off when finished)
function setSlide(){
	
	var i;
	
	
	//identify item number of the topItem which triggered the onClick event i.e. this function
    for (i=0; i < dropMenusArray.length; i++){
    	
        if (topItemsArray[i] == this){ //this refers to the clicked object
            if (setIntervalsArray[i] == null)
                setIntervalsArray[i] = setInterval("RunSlide(" + i + ")", slideDelay);
            break;
        }
    }
}


//is repeatedly triggered by SetSlide which is the OnClick event response for topItem
function RunSlide(menuCounter){
	
    if (!visibilityDropMenusArray[menuCounter]){ 
		expandMenu(menuCounter);  //invisible (and expanding)             
    } else {
    	contractMenu(menuCounter); //visible (and retracting)
    }
}


//expands the drop menu
function expandMenu(menuCounter){
	
	if(doFading){
		
		if(dropMenusArray[menuCounter].filters){
			dropMenusArray[menuCounter].filters.alpha.opacity += 
				100/ ( ( (parseInt(numSubItemsArray[menuCounter] * subItemHeight) + parseInt(menuExtensionArray[menuCounter]))/ moveSlidePix) +1);
		} else {
			opcVal = parseFloat(dropMenusArray[menuCounter].style.opacity);
			opcVal += 
				.9/((parseInt(numSubItemsArray[menuCounter] * subItemHeight) + parseInt(menuExtensionArray[menuCounter])) / moveSlidePix);
			dropMenusArray[menuCounter].style.opacity = opcVal;
		}
	}
	
    dropMenuHeightsArray[menuCounter] +=  moveSlidePix; //growing drop menu
    
    //if still not reached full height
    if (dropMenuHeightsArray[menuCounter] < (parseInt(numSubItemsArray[menuCounter] * subItemHeight) + parseInt(menuExtensionArray[menuCounter]))){
        
    	dropMenusArray[menuCounter].style.height = dropMenuHeightsArray[menuCounter] + "px"; //starting height when expanding
    
    } else { //reached full height - tidy up and display as fully open

    	//make dropMenu opaque	
		if(doFading){

			if(dropMenusArray[menuCounter].filters){
				
				dropMenusArray[menuCounter].filters.alpha.opacity = 100;

			} else {

				dropMenusArray[menuCounter].style.opacity = 1;
			}
		}
		
		strClassName = String(topItemsArray[menuCounter].className);
		topItemsArray[menuCounter].className = strClassName.substring(0, strClassName.length - 5);      
		dropMenuHeightsArray[menuCounter] = parseInt(numSubItemsArray[menuCounter] * subItemHeight) + parseInt(menuExtensionArray[menuCounter]);
		dropMenusArray[menuCounter].style.height = (parseInt(numSubItemsArray[menuCounter] * subItemHeight) + parseInt(menuExtensionArray[menuCounter])) + "px"; //final extended height
        
		subMenusArray[menuCounter].style.display = 'block';
        
		clearInterval(setIntervalsArray[menuCounter]);
        setIntervalsArray[menuCounter] = null;
        
        visibilityDropMenusArray[menuCounter] = true;
        
        updateUserCookie();
    }
}


//contracts the drop menu
function contractMenu(menuCounter){
	
	if(doFading){
		
		if(dropMenusArray[menuCounter].filters){
			dropMenusArray[menuCounter].filters.alpha.opacity -= 100/ (((parseInt(numSubItemsArray[menuCounter] * subItemHeight) + parseInt(menuExtensionArray[menuCounter])) / moveSlidePix) +1);
		} else {
			dropMenusArray[menuCounter].style.opacity -= .9/(((parseInt(numSubItemsArray[menuCounter] * subItemHeight) + parseInt(menuExtensionArray[menuCounter])) / moveSlidePix)+1);
		}
	}
	
    subMenusArray[menuCounter].style.display = 'none';
    dropMenuHeightsArray[menuCounter] -=  moveSlidePix; //shorten height
    
    if (dropMenuHeightsArray[menuCounter] > 0){ //if still some height left to retract ...
        
    	dropMenusArray[menuCounter].style.height = dropMenuHeightsArray[menuCounter] + "px"; //alert(dropMenusArray[objIndex].style.height); //height when shrinking
        
    } else { //tidy up and change to display as closed
    	
        if(doFading){
        	
			if(dropMenusArray[menuCounter].filters){
				dropMenusArray[menuCounter].filters.alpha.opacity = 0;
			} else {
				dropMenusArray[menuCounter].style.opacity = 0;
			}
		}
	
		topItemsArray[menuCounter].className = topItemsArray[menuCounter].className + "-over";
		
        dropMenuHeightsArray[menuCounter] = 0;
        dropMenusArray[menuCounter].style.height = 0 + "px";
        
        subMenusArray[menuCounter].style.display = 'none';
        
        clearInterval(setIntervalsArray[menuCounter]);
        setIntervalsArray[menuCounter] = null;
        
        visibilityDropMenusArray[menuCounter] = false;
        
        updateUserCookie();
    }
}


//save the menu heights to a cookie as a serialised array
function updateUserCookie(){
	
	var date, cookieContent;
	
	
    date = new Date();
    date.setTime(date.getTime() + (1000 * 60 * 60 * 24 * 30));
    //alert(dropMenuHeightsArray.toString());
    cookieContent = "xpMenuCookv2" + "=" + 
    	escape(dropMenuHeightsArray.toString()) + 
    	"; expires=" + date.toGMTString(); //alert(cookieContent);
    document.cookie = cookieContent;
}


//gets the cookie and returns an array
function GetUserCookie(crumbName){
	
	var colCookie, i;
	
	
    colCookie = document.cookie.split("; ");
    
    for (i=0; i < colCookie.length; i++){
    	
        colCrumb = colCookie[i].split("=");                    
        if(colCrumb[0] == crumbName){
            return unescape(colCrumb[1]);
        }
    }
    return "";
}


//toggles from Over variant of style to style without Over
function ChangeStyle(){
	
	var className;
	
	
    className = String(this.className);
    
    if (className.substring(className.length - 5, className.length) == "-over"){
        this.className = className.substring(0, className.length - 5);
    } else {
        this.className = this.className + "-over";
    }    
}