/*

xDHTML/CSS/JS menus
2-levels deep (heading + 1 dropdown)
Accessible, cross-browser compatible, semantically correct xHTML
Full keyboard support
Full CSS style support

Limitations:
    javascript MUST be enabled on the browser
    can only have 1 menu per page (js needs to know the root <ul>'s id)
    only supports 1 level of dropdown
    added IE6 iframe hack from http://www.brainjar.com/dhtml/menubar/default11.asp  

FIXME!!
    rounded corners at bottom of dropdowns (like cubiks.com)?
    to add <h3> around heading items? http://www.udm4.com/manual/depth/indicators/
    to gracefully degrade when no CSS (originally JS would hide onmouseover!)
        http://www.filamentgroup.com/lab/delivering_the_right_experience_to_the_right_device/
        http://ejohn.org/blog/progressive-css-enhancement/

Useful links:
    http://ejohn.org/blog/how-javascript-timers-work/ -- setInterval() vs setTimeout() 
    http://www.htmldog.com/ptg/archives/000050.php -- no keyboard support
    http://www.onlinetools.org/tools/yadm/reldropdown.html -- limited keyboard support
    http://www.softcomplex.com/products/tigra_menu_gold/demo/selects/ -- costs £, still no keyboard support
    http://www.udm4.com/ -- probably the best, completely overkill, costs ££££

*/

// A BrandingSet can specify its own dropdown positioning code
// (it ma want vertical menus for example). The site.master should define the following
// javascript function:
//
//      repositionDropDownMenu(
//          menu,       the object that should have its style.left and style.top reset
//          item)       the <a> heading menu item that the menu should be positioned against
//      {menu.style.top = item.offsetTop + "px"; menu.style.left = item.offsetLeft + "px";}

// globals
var CM_currentMenu = null;
var CM_timer = null;
var CM_timerOn = false;
var CM_closeTimeout = 500;              // ms delay before dropdown automatically closes after it loses focus
var CM_menuRoot = "menuRoot";           // must match the id given to the <ul> at the very root of the menu HTML
var CM_iframeID = "menuRootIFrame";     // .id given to dynamically generated IFRAME (only for IE)



function initialiseMenuOnLoad()
{
	var root = document.getElementById(CM_menuRoot);
	if(!root)
	    return;

    // build the menu items
	initialiseMenu(root, root);

    // if required, add the iframe hack
    iframeBuild(root);
}

// called by window.onload(), initialise the whole menu structure, recursive
function initialiseMenu(item, root)
{
    var menuHeading, menuItem, menuRow;
    
    // iterate through all menu headings
    for(var x=0; x<item.childNodes.length; x++)
    {
        // all the child nodes should be <li>, but just incase
        if(item.childNodes[x].nodeName.toLowerCase()=="li")
        {
            // is there a drop-down menu?
            menuItem = item.childNodes[x].getElementsByTagName("ul");
            if(menuItem.length>0)
            {
                // yes, so initialise that
                menuHeading = item.childNodes[x].getElementsByTagName("a").item(0);
                menuItem = menuItem.item(0);
                initialiseMenu(menuItem, root);
                initialiseMenuBody(menuItem, menuHeading, root);
            }
            else
            {
                // no drop-down children
                menuRow = item.childNodes[x].getElementsByTagName("a");
                if(menuRow.length>0)
                    initialiseMenuItem(menuRow.item(0));
            }
        }
    }
}

// called by inisialiseMenu() to add event handlers to each menu 'item' that doesn't have any children.
// item is either Heading (that has no drop-down) or an item in a dropdown list, it will always be an <a>
function initialiseMenuItem(item)
{
	item.onmouseover = function()
	{
	    // get the menu <ul> that this item belongs to
		hideChildMenus(this.parentNode.parentNode);
	}
	
	item.onfocus = function()
	{
		item.onmouseover();
	}
}

// initialise a whole menu, gets called once for each menu heading
function initialiseMenuBody(menu, item, root)
{
    // menu = <ul>
    // item = <a>
    // root = should always be the <ul> with .id=CM_menuRoot (the top of the menu structure)

    if(menu == null || item == null)
        return;
	    
	CM_currentMenu = menu;

	item.setMenuPosition = function()
	{
	    // find the real [X,Y] position of the this item.
	    // code should work correctly in both FF and IE (IE6 offsetTop is broken).
	    var posX = 0, posY = 0;
	    if (typeof (this.offsetParent) != 'undefined')
	    {
	        for (var m = this; m; m = m.offsetParent)
	        {
	            posX += m.offsetLeft;
	            posY += m.offsetTop;
	        }
	    }

	    // make sure the menu is at least as wide as this item heading
	    if (menu.offsetWidth < this.offsetWidth)
	        menu.style.width = this.offsetWidth + "px";

	    // set the position of menu
	    // use branding-specific function if possible
	    if (typeof (repositionDropDownMenu) == 'function')
	    {
	        repositionDropDownMenu(menu, this);
	    }
	    else
	    {
	        // no branding specific function found, use default positioning
	        menu.style.left = posX + "px";
	        menu.style.top = (posY + this.offsetHeight) + "px";
	    }
	}

	item.showMenu = function()
	{
	    // this 'item' is actually the heading
	    this.setMenuPosition();

	    // show the menu
	    menu.style.visibility = "visible";
	    CM_currentMenu = menu;

	    // iframe for IE<=6, this ensures the menu appears above 'windowed' controls, eg SELECT
	    iframeMove(menu.style.left, menu.style.top, menu.offsetWidth + "px", menu.offsetHeight + "px");
	    iframeShow();
	}

    item.onmouseover = function()
    {
        if(CM_currentMenu)
        {
            // is the CM_currentMenu this item's parent?
            if(this.parentNode.parentNode != CM_currentMenu)
            {
                // nope, so hide the current menu
                hideChildMenus(CM_currentMenu);
                CM_currentMenu.style.visibility = "hidden";
                iframeHide();
            }

            // now show the menu that this item belongs to
            CM_currentMenu = null;
            killTimer();        // stop any timer that will hide our menus
            this.showMenu();
        }
    }
    item.onfocus = function()       {this.onmouseover();}
			
    item.onmouseout = function()
    {
        menu.hideMenu();        
    }

    menu.hideMenu = function()
    {
        if(!CM_timerOn)
        {
            CM_timer = setTimeout("hideAllMenus();", CM_closeTimeout);
            CM_timerOn = true;
        }
    }

    menu.onmouseover = function()
    {
        if(CM_currentMenu)
        {
            this.style.visibility = "visible";
            CM_currentMenu = this;
            killTimer();    // don't let the timer hide this menu
        }
    }	
    menu.onfocus = function()           {this.onmouseover();}
    menu.onmouseout = function(event)   {this.hideMenu();}
    menu.onblur = function(event)       {this.hideMenu();}
}
    
function killTimer()
{
    // stop any timer if there is one
    if(CM_timer)
    {
        clearTimeout(CM_timer);
        CM_timer = null;
    }
    CM_timerOn = false;
}

function hideAllMenus()
{
    // hide ALL drop-down menus
    hideChildMenus(document.getElementById(CM_menuRoot));
    iframeHide();
    killTimer();
}

function hideChildMenus(menu)
{
    // menu = the <ul> whose child menus should be hidden (the <ul> itself is not hidden)
    for(var x=0;x<menu.childNodes.length;x++)
    {
        if(menu.childNodes[x].nodeName.toLowerCase()=="li")
        {
            var uls = menu.childNodes[x].getElementsByTagName("ul");
            if(uls.length>0)
            {
                uls.item(0).style.visibility = "hidden";
                hideChildMenus(uls.item(0));
            }
        }
    }
}


// yuck! IE<=6 needs an iframe to ensure that the menu appears above 'windowed' controls, eg SELECT
// http://www.brainjar.com/dhtml/menubar/default11.asp
function iframeBuild(root)
{
    // don't create the iframe twice
    var iframeIE = document.getElementById(CM_iframeID);
    if (iframeIE)
        return;

    var iframeIE = document.createElement("iframe");
    iframeIE.id = CM_iframeID;
    iframeIE.frameBorder = 0;
    iframeIE.src = "javascript:false;";
    iframeIE.style.display = "none";
    iframeIE.style.visibility = "hidden";
    iframeIE.style.position = "absolute";
    iframeIE.style.left = "-2000px";    // hide off screen
    iframeIE.style.width = "1px";
    iframeIE.style.height = "1px";
    iframeIE.style.filter = "progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)";

    // nice hack so that the iframe only gets added to IE<=6
    // spec says .insertBefore() should take 2 parameters, MS thinks the 2nd parameter is optional.
    // so standards-compliant browsers (which don't need the iframe) throw an exception to this - which we ignore.
    try {root.parentNode.insertBefore(iframeIE);}   catch(e){}
}

function iframeMove(left, top, width, height)
{
    var iframeIE = document.getElementById(CM_iframeID);
    if (iframeIE)
    {
        // place the iframe under the menu
        iframeIE.style.left = left;
        iframeIE.style.top = top;
        iframeIE.style.width = width;
        iframeIE.style.height = height;
    }
}

function iframeShow()
{
    var iframeIE = document.getElementById(CM_iframeID);
    if(iframeIE)
    {
        iframeIE.style.visibility = "visible";
        iframeIE.style.display = "block";
    }
}

function iframeHide()
{
    var iframeIE = document.getElementById(CM_iframeID);
    if(iframeIE)
    {
        iframeIE.style.visibility = "hidden";
        iframeIE.style.display = "none";
    }
}

// initialise the menu on DOM load
window.onload = addWindowOnLoadDelegate(window.onload, initialiseMenuOnLoad);

// Decrease count of pages left to load, so that we know when to hide the hourglass and show the mainpage
if (typeof (jsScriptCount) != "undefined")
    jsScriptCount--;
