/** * Class for the dynamic menu. * * Requires Prototype JS version 1.5.0 or greater. * (Also supports version 1.6.0.*, avoiding all deprecated methods) * http://www.prototypejs.org/ * * Copyright 2007-2008 Charming Design, Niek Kouwenberg * http://www.charmingdesign.net/ * * Special thanks to CARE Internet Services B.V. * http://www.care.nl/ */ Menu = { /* Version of the Menu class */ Version: "1.1", /* * CONSTANTS */ /* Constant for a horizontal menu */ HORIZONTAL: 1, /* Constant for a vertical menu */ VERTICAL: 2, /* * MENU */ /* Hold the ID of the menu */ _menuId: null, /* Hold the menu UL node */ _menuNode: null, /* * TEMPORARY VARIABLES */ /* Hold the hide timer */ _hideTimeout: null, /* Will hold the active A element */ _activeLink: null, /* Will hold the active UL element */ _activeSubMenu: null, /* * OPTIONS */ /* Orientation of the menu (horizontal or vertical) */ _orientation: 1, /* Time in milliseconds before hiding the sub menu */ _hidePause: 1000, /* Opacity (0 = transparent, 1 = opaque) */ _opacity: 1, /* * METHODS */ /** * Sets the time to wait before hiding the sub menu. * * @param int secs * * @deprecated Please use the options argument for Menu.init() instead. */ setHidePause: function(secs) { Menu._hidePause = secs * 1000; /* deprecation warning */ alert("Deprecated method Menu.setHidePause() used: Please use the options argument for Menu.init()."); }, // function setHidePause /** * Initializes the dynamic menu. * * @param string menuId * @param object options * * Available options: * - orientation (int, Menu.HORIZONTAL or Menu.VERTICAL) * - hidePause (float, seconds) * - opacity (float; 0 = transparent, 1 = opaque, transparency of the sub menu) * * Example usage: * * * This method can be called after document load, but it is preferred to be * called directly from your page (HTML , before document load). This * way the menu loads faster and can be interacted with much sooner. */ init: function(menuId, options) { /* Save menu ID (fall back to the default ID "menu") */ Menu._menuId = (typeof menuId == "string") ? menuId : "menu"; /* Save options */ if (options) { /* Orientation */ if (options.orientation != undefined) { Menu._orientation = options.orientation; } /* Hide timeout in seconds */ if (options.hidePause != undefined) { Menu._hidePause = options.hidePause * 1000; } /* Sub menu opacity */ if (options.opacity != undefined) { Menu._opacity = options.opacity; } } /* Check if the document is already loaded. Prototype 1.6.0 introduces * the document.loaded boolean, for 1.5*, check if we can retrieve an * element from the DOM (fails when document is not loaded). */ if (document.loaded === true || $(Menu._menuId)) { Menu._doInit(); } /* This is how it should work (init called before document load) */ else { /* Do the actual initialization on document load. The "dom:loaded" * event is prefered, but only available since 1.6.0 (with * document.observe construction). Fallback to the window onload * when not available. */ if (document.observe) { document.observe("dom:loaded", Menu._doInit); } else { Element.observe(window, "load", Menu._doInit); } } }, // function init /** * Initializes the drop down menu. * * Should be called on page load. */ _doInit: function() { /* After the DOM is loaded, save the menu node */ Menu._menuNode = $(Menu._menuId); /* Find all first level menu nodes */ var elements = (Element.select) ? Menu._menuNode.select("li") : Menu._menuNode.getElementsBySelector("li"); for (var i = 0; i < elements.length; i++) { /* Check if it has a sub menu */ if (((Element.select) ? elements[i].select("ul").length : elements[i].getElementsBySelector("ul").length) > 0) { /* Add expand/collapse listeners to the node link */ var a = (Element.select) ? elements[i].select("a")[0] : elements[i].getElementsBySelector("a")[0]; Element.observe(elements[i], "mouseover", Menu._showSubMenu.bind(elements[i])); Element.observe(elements[i], "mouseout", Menu._hideSubMenu.bind(elements[i])); /* Add "submenu" class for this node link */ Element.addClassName(a, "submenu"); } /* No sub menu */ else { /* Only hide any expanded sub menu when hovering this node */ Element.observe(elements[i], "mouseover", Menu._quickHideSubMenu); } } }, // function _doInit /** * Shows the sub menu. */ _showSubMenu: function(e) { /* Get node link and sub menu */ var a = (Element.select) ? this.select("a")[0] : this.getElementsBySelector("a")[0]; var subMenu = (Element.select) ? this.select("ul")[0] : this.getElementsBySelector("ul")[0]; /* Hide previous opened sub menu */ Menu._quickHideSubMenu(); /* Keep hover style as long as opened */ Element.addClassName(a, "menu_open"); /* Show sub menu */ Element.setStyle(subMenu, {"visibility": "visible", "position": "absolute"}); /* Set correct position */ var pos = (Element.positionedOffset) ? this.positionedOffset() : Position.positionedOffset(this); if (Menu._orientation == Menu.HORIZONTAL) { subMenu.setStyle({ "left": pos[0] + "px", "top": (pos[1] + this.getHeight()) + "px" }); } else if (Menu._orientation == Menu.VERTICAL) { subMenu.setStyle({ "left": (pos[0] + this.getWidth()) + "px", "top": pos[1] + "px" }); } /* Apply opacity if not fully opaque */ if (Menu._opacity > 0 && Menu._opacity < 1) { subMenu.setOpacity(Menu._opacity); } /* Save submenu */ Menu._activeLink = a; Menu._activeSubMenu = subMenu; }, // function _showSubMenu /** * Immediately hides the active menu. */ _quickHideSubMenu: function() { /* Clear possible timeout */ if (Menu._hideTimeout) { window.clearTimeout(Menu._hideTimeout); } /* And hide the menu */ Menu._doHideSubMenu(Menu._activeLink, Menu._activeSubMenu); }, // function _quickHideSubMenu /** * Method for hiding the sub menu, takes */ _hideSubMenu: function() { /* Get node link and submenu */ var a = (Element.select) ? this.select("a")[0] : this.getElementsBySelector("a")[0]; var subMenu = (Element.select) ? this.select("ul")[0] : this.getElementsBySelector("ul")[0]; /* No pause? Don't use the timeout */ if (Menu._hidePause <= 0) { /* Hide the menu */ Menu._doHideSubMenu(a, subMenu); } else { /* Hide in x (milli)seconds */ Menu._hideTimeout = window.setTimeout(function() { Menu._doHideSubMenu(a, subMenu); }, Menu._hidePause); } }, // function _hideSubMenu /** * Does the actual hiding the the sub menu. * * @param node a (A) * @param node subMenu (UL) */ _doHideSubMenu: function(a, subMenu) { /* Remove hover style */ if (a) { Element.removeClassName(a, "menu_open"); } /* Hide sub menu */ if (subMenu) { Element.setStyle(subMenu, {"visibility": "hidden"}); } } // function _doHideSubMenu }; // class Menu