
//-------------------------------------------------------------------------------
// MenubarControl JavaScript object
//
// Created by: dmd 081508
//
// Purpose:
//
// This object represents a windows-style menubar control.
//
// Updates:
//
// - dmd 090608: added the prototype Javascript library.
//
// Usage:
//
// - Add a DIV tag to the HTML and give it a valid "id" attribute.
// - Instantiate the menubar object as follows:
//
//      var menubar = new MenubarControl("menubar");   // Where "menubar" is the id attribute of the menubar DIV.
//     
//  - To add a menu to the menubar (for instance, adding a "home" menu that links to "home.asp"):
// 
//  menubar.addMenu("menu_home", "Home", "30", "home.asp");
//
//  In this example, "menu_home" is (what will be) the unique DOM id attribute of the menu, "Home" is
//  the text that will be displayed on the menubar, "30" is the number of pixels that "Home" will be
//  offset from the left of the menubar, and "home.asp" is the link that is navigated to if the user
//  clicks on "Home".
//
//
//  CSS Classes used by this object:
//
//  - Menubar_MenubarClass
//  - Menubar_MenuClass
//  - Menubar_SelectedMenuNameClass
//  - Menubar_MenuNameClass
//  - Menubar_VisibleBodyClass
//  - Menubar_HiddenBodyClass
//  - Menubar_SelectedOptionClass
//  - Menubar_OptionClass
//  - Menubar_EmptyOptionClass
//  - Menubar_OptionGroupClass
//
//-------------------------------------------------------------------------------

function MenubarControl (menubarID) {

	//----------------------------------------------------
	// Define member variables.
	//----------------------------------------------------
	
	// "Self" fixes loss-of-scope problem in inner functions.		
	var self = this; 
	
	// The ID of the currently selected menu/option.
	this.currentlySelectedMenuID = "";
	this.currentlySelectedOptionID = "";
	
	// This text is prepended to CSS class names assigned below (to associate related class names and
	// try to keep them distinct from class names specified anywhere else).
	this.classPrefix = "Menubar";
	
	// A counter used to generate ID's.
	this.idIndex = 0;
	
	// ID values.
	this.menubarID = "";
		
	this.menubarElement = null;
	
	// Number of menus available from the menubar.
	this.menuCount = 0;
	
	// A collection of menu ID's.
	this.menuIdList = new Array();
	
    // Formatting-related variables (note: don't append "px" to end!)
    this.format_currentLeftOffset = 0;
    this.format_leftOffset = 40;
    this.format_menuSpacing = 100;
    this.format_useSeparators = false;
    
    // These variables are used when setting and clearing the "window.timeout" while
	// showing/hiding menus .
	this.timeOutID;
	this.timeOutInMilliseconds = 250;
    
    // Different option types.
    this.TYPE_OPTION = "0";
    this.TYPE_EMPTYLINE = "1";
    this.TYPE_OPTIONGROUP = "2";
  
	//----------------------------------------------------
	// Define member methods.
	//----------------------------------------------------

    // Add a menu to the menubar. Note that the id parameter is REQUIRED and must be a valid, unique 
    // DOM id (the menu options under this menu use this id as a "parent ID" and allows them to be 
    // correctly associated with the parent menu).
    this.addMenu = function (id, displayText, leftPosition, location) {
    
        var menuElement;
        var menuNameElement;
        var menuBodyElement;
        
        // Validate input.
        if (isEmpty(id)) { return self.displayError("Invalid id","addMenu"); }
        if (isEmpty(displayText)) { return self.displayError("Invalid displayText","addMenu"); }
            
        // Generate the DOM Elements.
        menuElement = document.createElement("div");
        menuNameElement = document.createElement("div");
        menuBodyElement = document.createElement("div");
        
        // Set the attributes.
        menuElement.setAttribute("id", id);
        menuElement.setAttribute(self.MENU_ATTR_HASOPTIONS, "false");
        menuElement.className = self.classPrefix + "_MenuClass";
        
        // Calculate the menu's left-side position.
        if (isEmpty(leftPosition)) {
            // Use defaults.
            leftPosition = parseInt(self.format_leftOffset) + parseInt(self.format_currentLeftOffset) + 
                (parseInt(self.format_menuSpacing) * parseInt(self.menuCount));
        } 
        
        menuElement.style.left = String(leftPosition) + "px";
        
        menuNameElement.setAttribute("id", id + "__Name");
        menuNameElement.className = self.classPrefix + "_MenuNameClass";
        menuNameElement.innerHTML = displayText;
        
        // Only add the onclick handler if there's a non-empty location value.
        if (!isEmpty(location)) {
            eval("menuNameElement.onclick = function () {top.location = '" + location + "';}");
        }
        menuNameElement.onmouseover = function () {self.handleMouseOver(id, "");}
        menuNameElement.onmouseout = function () {self.handleMouseOut(id);}
        menuBodyElement.setAttribute("id", id + "__Body");
        menuBodyElement.className = self.classPrefix + "_HiddenBodyClass";
        
        menuElement.appendChild(menuNameElement);
        menuElement.appendChild(menuBodyElement);
        
        // Append the menu under the menubar Element.
        self.menubarElement.appendChild(menuElement);
        
        // Update the list of menu ID's.
        self.menuIdList[self.menuIdList.length] = id;
        self.menuCount++;
    };
    
    
    // Add a new group/heading under an existing menu in the menubar.
    this.addMenuGroup = function (displayText, location, menuID) {
    
        var id;
        var menuBodyElement;
        var optionElement;
        
        // Validate input.
        if (isEmpty(displayText)) {displayText = "&nbsp;";}
        if (isEmpty(location)) { return self.displayError("Invalid location","addMenuGroup"); }
        if (isEmpty(menuID)) { return self.displayError("Invalid menuID","addMenuGroup"); }
        
        // Generate a unique id using the menu ID and idIndex.
        id = menuID + "__" + self.idIndex;
        self.idIndex++;
        
        // Get a reference to the menu body Element (this menu group will be appended under it).
        menuBodyElement = $(menuID + "__Body");
        if (menuBodyElement == null) { return self.displayError("Invalid menuBodyElement","addMenuGroup"); } 
   
        // Create the option Element.
        optionElement = document.createElement("div");
        optionElement.setAttribute("id", id);
        optionElement.setAttribute(self.MENU_ATTR_TYPE, self.TYPE_OPTIONGROUP);
        optionElement.onmouseover = function () {self.handleMouseOver(menuID, id);}
        optionElement.onmouseout = function () {self.handleMouseOut(menuID);}
        optionElement.className = self.classPrefix + "_OptionGroupClass";
        eval("optionElement.onclick = function () {top.location = '" + location + "';}");
        optionElement.innerHTML = displayText;
        
        // Append the new option (group) under the parent menu.
        menuBodyElement.appendChild(optionElement);
    };
    
    
    // Add an empty line/row under an existing menu in the menubar.
    this.addEmptyLine = function (menuID) {
    
        var id;
        var menuBodyElement;
        var optionElement;
        
        // Validate input.
        if (isEmpty(menuID)) { return self.displayError("Invalid menuID","addEmptyLine"); }
        
        // Generate a unique id using the menu ID and idIndex.
        id = menuID + "__" + self.idIndex;
        self.idIndex++;
        
        // Get a reference to the menu body Element (this empty line will be appended under it).
        menuBodyElement = $(menuID + "__Body");
        if (menuBodyElement == null) { return self.displayError("Invalid menuBodyElement","addEmptyLine"); } 
   
        // Create the option Element.
        optionElement = document.createElement("div");
        optionElement.setAttribute("id", id);
        optionElement.setAttribute(self.MENU_ATTR_TYPE, self.TYPE_EMPTYLINE);
        optionElement.onmouseover = function () {self.handleMouseOver(menuID, id);}
        optionElement.onmouseout = function () {self.handleMouseOut(menuID);}
        optionElement.className = self.classPrefix + "_EmptyOptionClass";
        eval("optionElement.onclick = function () {top.location = '" + location + "';}");
        optionElement.innerHTML = "&nbsp;";
        
        // Append the new empty line under the parent menu.
        menuBodyElement.appendChild(optionElement);
    };
    
    
    // Add a new option under an existing menu in the menubar.
    this.addMenuOption = function (displayText, location, menuID) {
    
        var id;
        var menuBodyElement;
        var optionElement;
        
        // Validate input.
        if (isEmpty(displayText)) {displayText = "&nbsp;";}
        if (isEmpty(location)) { return self.displayError("Invalid location","addMenuOption"); }
        if (isEmpty(menuID)) { return self.displayError("Invalid menuID","addMenuOption"); }
        
        // Generate a unique id using the menu ID and idIndex.
        id = menuID + "__" + self.idIndex;
        self.idIndex++;
        
        // Get a reference to the menu body Element (this menu option will be appended under it).
        menuBodyElement = $(menuID + "__Body");
        if (menuBodyElement == null) { return self.displayError("Invalid menuBodyElement","addMenuOption"); } 
   
        // Create the option Element.
        optionElement = document.createElement("div");
        optionElement.setAttribute("id", id);
        optionElement.setAttribute(self.MENU_ATTR_TYPE, self.TYPE_OPTION);
        optionElement.onmouseover = function () {self.handleMouseOver(menuID, id);}
        optionElement.onmouseout = function () {self.handleMouseOut(menuID);}
        optionElement.className = self.classPrefix + "_OptionClass";
        
        // dmd 052009
        // Only add the onclick handler if there's a non-empty location value.
        if (!isEmpty(location)) {
        
            // If the URL begins with "http" open it in a new window, otherwise navigate to the URL.
            if (location.substr(0,4) == "http") {
                eval("optionElement.onclick = function () {window.open('" + location + "','_blank');}");
            } else {
                eval("optionElement.onclick = function () {top.location = '" + location + "';}");
            }
        }
        optionElement.innerHTML = displayText;
        
        // Append the new option under the parent menu.
        menuBodyElement.appendChild(optionElement);
    };
    
    
    // Call the global displayError() function.
    this.displayError = function (message, functionName) {
        return global_displayError(message, functionName, "MenubarControl");
    };
    
    
    // The mouseOut and mouseOver functions are used to keep track of selections
	// and determine when to stop displaying a menu.
	this.handleMouseOut = function (menuID) {
		// The function provided as the first parameter to window.setTimeout will be called 
		// after the specified delay (timeOutInMilliseconds) unless the time-out is cleared.
		self.timeOutID = window.setTimeout(function () {self.deselectMenu(menuID);}, self.timeOutInMilliseconds);
	};
	
	this.handleMouseOver = function (menuID, optionID) {
		
		// Clear the time-out set by window.setTimeout in function mouseOut.
		window.clearTimeout(self.timeOutID);
		
		// Update the menu's display.
		self.updateMenu(menuID, optionID);
	};
	
	
	this.deselectMenu = function (menuID) {
	    
	    var menuBodyElement;
	    var menuNameElement;
	    var optionElement;
	    
	    if (isEmpty(menuID)) {return self.displayError("Invalid menuID", "deselectMenu");}
	    
	    if (!isEmpty(self.currentlySelectedOptionID)) {
	    
	        // Deselect the currently selected option.
	        optionElement = $(self.currentlySelectedOptionID);
	        if (optionElement == null) {return self.displayError("Invalid selected optionID", "deselectMenu");}
	        
	        optionElement.className = self.classPrefix + "_OptionClass";
	        
	        self.currentlySelectedOptionID = "";
	    }
	    
	    menuBodyElement = $(menuID + "__Body");
	    if (menuBodyElement == null)  {return self.displayError("Invalid menuBodyElement", "deselectMenu");}
	    
	    menuNameElement = $(menuID + "__Name");
	    if (menuNameElement == null)  {return self.displayError("Invalid menuNameElement", "deselectMenu");}
	    
	    menuBodyElement.className = self.classPrefix + "_HiddenBodyClass";
	    menuNameElement.className = self.classPrefix + "_MenuNameClass";
	    
	    self.currentlySelectedMenuID = "";
	};
	
	
	this.deselectOption = function (optionID) {
	    
	    var className;
	    var optionElement;
	    var type;
	    
	    if (isEmpty(optionID)) {return self.displayError("Invalid optionID", "deselectOption");}

        optionElement = $(optionID);
        if (optionElement == null) {return self.displayError("Invalid optionElement", "deselectOption");}
        
        
        // Determine class name based on type.
        type = optionElement.getAttribute(self.MENU_ATTR_TYPE);
        if (isEmpty(type)) {type = self.TYPE_OPTION;}
        
        if (type == self.TYPE_OPTION) {className = self.classPrefix + "_OptionClass";}
        if (type == self.TYPE_EMPTYLINE) {className = self.classPrefix + "_EmptyOptionClass";}
        if (type == self.TYPE_OPTIONGROUP) {className = self.classPrefix + "_OptionGroupClass";}
        
        optionElement.className = className;
        
        self.currentlySelectedOptionID = "";
	};
	
	
	this.selectMenu = function (menuID) {
	    
	    var menuBodyElement;
	    var menuNameElement;
	    
	    if (isEmpty(menuID)) {return self.displayError("Invalid menuID", "selectMenu");}
	    
	    menuBodyElement = $(menuID + "__Body");
	    if (menuBodyElement == null)  {return self.displayError("Invalid menuBodyElement", "selectMenu");}
	    
	    menuNameElement = $(menuID + "__Name");
	    if (menuNameElement == null)  {return self.displayError("Invalid menuNameElement", "selectMenu");}
	    
	    if (menuBodyElement.childNodes != null && menuBodyElement.childNodes.length > 0) {
	        menuBodyElement.className = self.classPrefix + "_VisibleBodyClass";
	    } else {
	        menuBodyElement.className = self.classPrefix + "_HiddenBodyClass";
	    }
	    menuNameElement.className = self.classPrefix + "_SelectedMenuNameClass";
	    
	    self.currentlySelectedMenuID = menuID;
	};
	
	this.selectOption = function (optionID) {
	    
	    var optionElement;
	    
	    if (isEmpty(optionID)) {return true; } 
	    
	    optionElement = $(optionID);
	    if (optionElement == null)  {return self.displayError("Invalid optionElement", "selectOption");}
	    optionElement.className = self.classPrefix + "_SelectedOptionClass";
	
	    self.currentlySelectedOptionID = optionID;
	};
	
	this.updateMenu = function (menuID, optionID) {

        // This menu needs to be selected.
        if (!isEmpty(self.currentlySelectedMenuID) && self.currentlySelectedMenuID != menuID) {
            // Another menu was previously selected (and implicitly, its options).
            self.deselectMenu(self.currentlySelectedMenuID);
        }
        
        if (isEmpty(self.currentlySelectedMenuID)) { self.selectMenu(menuID); }
        
        if (!isEmpty(self.currentlySelectedOptionID) && self.currentlySelectedOptionID != optionID) {
            // Another option was previously selected.
            self.deselectOption(self.currentlySelectedOptionID);
        }
        
        if (!isEmpty(optionID)) { self.selectOption(optionID);}
	};
	
	
	//---------------------------------------------------------------------------
    // This is executed when the object is instantiated.
    //---------------------------------------------------------------------------
	
	// Validate the input parameter.
	if (isEmpty(menubarID)) {return self.displayError("Invalid menubarID", ""); }

    self.menubarID = menubarID;
    
    // Get a reference to the Element specified by the menubar ID parameter.
    self.menubarElement = $(self.menubarID);
	if (self.menubarElement == null) {return self.displayError("Invalid menubarElement", ""); }
	
	// Set the menubar's class name.
	self.menubarElement.className = self.classPrefix + "_MenubarClass";
};	
	