// ****************************
// CSS Menu Lib - RTG 6/21/2001, 12/17/2001
// ****************************
//
// This library allows your page to have dynamically
// collapsable menus for some 4+ browsers. All other
// browsers that do not support this feature will
// see fully expanded windows, and shouldn't encounter
// any annoying errors.
//
// Public Functions:
// ----------------
//
// canShowAndHideMenus() 
//
//     Returns true if this library's functions are
//     supported by this browser. Don't use the rest
//     of the functions of this library without first
//     checking that this function returns true.
//
// Public Objects:
// --------------
// 
// NavMenu(cookieName, bHideOthers); 
//    Creates and returns a menu which stores its state
//    in cookie <cookieName>. If bHideOthers is true, nodes
//    will automatically collapse whenever another node is
//    opened.
//
// NavMenu::activate();
//    Activates the menu by setting the nodes to the states
//    saved in the cookie, and inserting links in the node
//    headers.
//
// NavMenu::addNode(nodeObj);
//    Adds a MenuNode <nodeObj> to the collection of nodes in
//    a particular menu. You should not add the same MenuNode
//    to more than one NavMenu on a page.
//
// MenuNode(linkTextID, showHideID);
//    Creates and returns a menu node where the node header
//    is encapsulated in element ID <linkTextID>, and the
//    body [to be shown/hidden] is found in element ID
//    <showHideID>. 
//

// ############ GENERAL LIBRARY FUNCS 


function canShowAndHideMenus() {
    // Returns nonzero value if this library will be able to run
    // with the current browser.

    // Must be using a 4+ browser with access to the following properties
    // in order to even be a contender:
    
    return parseFloat(navigator.appVersion) >= 4 &&
           document.getElementById &&        // More portable than document.all
           document.body &&                  // M
           document.body.innerHTML &&        // Make sure we can write to this later.
           document.body.style != null &&    // Same for CSS/visibility
           document.body.style.display != null;
}


// ############ class MenuNode 


function MenuNode(linkTextID, showHideID,    strJumpPageURL) {
    //
    // Make sure the two IDs exist. strJumpPageURL is *optional*.
    //

    if(!(this.linktextobj = document.getElementById(linkTextID))) {
        alert("menu_activateLink Error: linkTextId '" + linkTextID + "' was not found.");
        return;
    }

    if(!(this.showhideobj = document.getElementById(showHideID))) {
        alert("menu_activateLink Error: linkTextId '" + linkTextID + "' was not found.");
        return;
    }

    // Do the same check for style.
    if(!this.showhideobj.style) {
        alert("menu_activateLink Error: 'style' object for linkTextId '" + linkTextID + "' was not found. Perhaps you have more than one object of this ID?");
        return;
    }

    // ...and hook them up.
    this.shcss     = this.showhideobj.style;
    this.shid      = showHideID;
    this.jumpPageURL = strJumpPageURL;
	
    // Set my menu to null for now. This will be
    // changed once I am added to an actual NavMenu object.
    this.menu      = null;
    this.nodeid    = null;

    // Lastly, set up the methods.
    this.hide      = menunode_hide;
    this.show      = menunode_show;
    this.toggleVis = menunode_toggleVis;
    this.isVis     = menunode_isVis;
    this.activate  = menunode_activate;

    return this;
}

function menunode_activate() {
    // Hide the detail part of the menu.
    this.hide();

    // Encapsulate the text of the linkText object with
    // <A> </A>
    this.linktextobj.innerHTML = '<A HREF="#" class="NavMenu" onClick="return gAllMenusOnPage[' + this.menu.menuid +
                                 '].nodes[' + this.nodeid + '].toggleVis()" onDoubleClick="return gAllMenusOnPage[' + this.menu.menuid +
                                 '].nodes[' + this.nodeid + '].toggleVis()" >' +
                                 this.linktextobj.innerHTML + '</A>';
}

// Syntax: menu.toggleVis([bShow])
// Toggles showing/hiding the details of a given node.
// if bShow is null, toggles. if 
function menunode_toggleVis(bShow) {

    if (bShow == null) bShow = !this.isVis();

    // Change visibility
    if (bShow)
        this.show();
	else if (this.jumpPageURL != null && document.location)
        document.location = this.jumpPageURL;
    else
        this.hide();
        
    // Update preferences (in Mr. Cookie)
    this.menu.updatePrefs();

    return false;
}


// Hides the details of this node.
// -- PRIVATE FUNCTION --
function menunode_hide() {
    this.shcss.display = 'none';
}

// Shows the details of this node.
// -- PRIVATE FUNCTION --
function menunode_show() {
    if(this.menu.hideothers) { this.menu.hideAll(); }

    this.shcss.display = '';
}

// Returns true if the details of this node are visible.
function menunode_isVis() {
    return this.shcss.display == "none"?false:true;
}


// ############ class NavMenu

// NavMenu(cookieName, bHideOthers); 
//
//     Creates, registers and returns a NavMenu object.
//
//     ARGUMENTS:
//
//     string   cookieName - The name of the cookie which
//                           stores the menu's state (assuming
//                           that the client enables cookies).
//
//     boolean bHideOthers - True if when one menu node is
//                           expanded, the others are collapsed.
//

function NavMenu(cookieName, bHideOthers) {
    // Create list of MenuNode objects.
    this.nodes = new Array();

    // Store prefs
    this.cookiename  = cookieName  || null;
    this.hideothers  = bHideOthers || false;

    // Attach methods.
    this.addNode     = navmenu_addNode;
	this.getNode     = navmenu_getNode;
    this.hideAll     = navmenu_hideAll;
    this.autoHide    = navmenu_autoHide;
    this.activate    = navmenu_activate;
    this.updatePrefs = navmenu_updatePrefCookie;

    // Add to list of all menus on page and link up.
    this.menuid      = gAllMenusOnPage.length;
    gAllMenusOnPage[gAllMenusOnPage.length] = this;
    

    return this;
}

// Automatically hides all items of this menu
// when this '<a>' link has been clicked. <linkID> is 
// the ID of this link.
//
// You might call this method directly in your code if you have a static
// link in a menu which you want to cause the menu to auto-collapse when clicked.
//
// For example, some normal menus would call:
// 		mainMenu.addNode(new MenuNode('menu_head_Safety', 'menu_body_Safety'));
// Yet other "menus" with no subcontent might call:
//  	mainMenu.autoHide('menu_head_MyProfile');


function navmenu_autoHide(linkID) {
    
    if(!document.getElementById(linkID)) {
        alert("navmenu_autoHide Error: linkID '" + linkID + "' was not found in this document.");
        return;
    }
    
    var link = document.getElementById(linkID);

    if(link.attachEvent)               // IE
        link.attachEvent("onclick", navmenu_autoHideEventOnClick);
    else if (link.addEventListener)    // Mozilla
        link.addEventListener("click", navmenu_autoHideEventOnClick, false);

    if (!link.setAttribute) return;
    link.setAttribute("navmenu_id", this.menuid);
    
}


function navmenu_autoHideEventOnClick(e)
{
    // Internal function - triggered when user has clicked on
    // an object that we have attached our autoHide event to.
    // This will close all the pieces of our menu when triggered.
    
    e = e || window.event || window.Event;
    if(!e) return;
    
    var currele = e.currentTarget || e.target || e.srcElement;
   
    while(currele && currele.getAttribute) {
        var navmenu_id = currele.getAttribute("navmenu_id");
        if (currele.id && typeof navmenu_id != 'undefined' && gAllMenusOnPage[navmenu_id]) {
            // hide all menus on next load
            gAllMenusOnPage[navmenu_id].updatePrefs(1); 
            return;
        }
        
        currele = currele.parentNode; // Move up in the tree
    }
}


// Collapses all menu nodes.
function navmenu_hideAll() {
    for(var i=0; i<this.nodes.length; i++)
        this.nodes[i].hide();
     
    return true; // success, always!
}

// Associates a MenuNode object with a menu
// (so that cookies and collapsing can work
//  properly).
function navmenu_addNode(nodeObj) {
    // Patch the object in.
    nodeObj.nodeid = this.nodes.length;
    this.nodes[this.nodes.length] = nodeObj;

    // Let the object point to me too.
    nodeObj.menu = this;
}

// Performs a linear search for, and returns,
// a MenuNode object matching the showHideID (the ID of the
// thing being shown or hidden). Case sensitive.
//
// If error or not found, returns null
function navmenu_getNode(showHideID) {
    // Patch the object in.
    for (var i=0; i<this.nodes.length; ++i) {
	    if (this.nodes[i].shid == showHideID)
		    return this.nodes[i];
	}
	return null;
}
//
// Activates menu after all the MenuNodes have been
// assigned.
//
function navmenu_activate() {
    var openMenus = this.cookiename == null?"":getCookie(this.cookiename);

    for(var i=0; i<this.nodes.length; i++)
    {
        // Activate the links of the object (so user sees <A></A> tags)
        this.nodes[i].activate();

        // Show/Hide the details of that menu nodeer.
        if(openMenus && openMenus.charAt(i)=="1") { this.nodes[i].show(); }
        else { this.nodes[i].hide(); }
    }
}

// Checks the showing/hidden status of each node in the
// menu, builds a cookie value and updates the browser's
// cookie. THIS ONLY STAYS IN MEMORY FOR THE SESSION --
// when the user closes his browser, the cookie is long
// gone.
function navmenu_updatePrefCookie(allhidden) {
    if(this.cookiename != null) {
        var newCookieStr = "";
        for(var i=0; i<this.nodes.length; i++)
            newCookieStr += this.nodes[i].isVis()&&!allhidden?"1":"0";
        setCookie(this.cookiename, newCookieStr);
    }
}

// ############ GENERAL COOKIE MANIP

// Sets cookie values. Expiration date is optional
function setCookie(name, value, expire) {   
    document.cookie = name + "=" + escape(value)   +
        (expire ? ("; path=/; expires=" + expire.toGMTString()) : "; path=/; ");
}

// Gets cookie values.
function getCookie(Name) {
    // (from http://developer.netscape.com/docs/manuals/communicator/jsguide4/advtopic.htm#1013101)
    var search = Name + "="
    if (document.cookie.length > 0) { 
        // if there are any cookies     
        offset = document.cookie.indexOf(search)       
        if (offset != -1) { 
            // if cookie exists          
            offset += search.length          
            // set index of beginning of value         
            end = document.cookie.indexOf(";", offset)          
            // set index of end of cookie value         
            if (end == -1)             
            end = document.cookie.length         
            return unescape(document.cookie.substring(offset, end))      
        }    
    }
}

// ############ GLOBAL VARS

var gAllMenusOnPage = new Array(); // Holds all menus on the page, used internally.
var loadedMenuLib   = true;        // True if this library has loaded properly 
