Version: 1.0
Type: Full Script
Category: HTML
License: GNU General Public License
Description: GMenu is a highly customizable multilevel drop down menu. GMenu is written in JavaScript and is customized using CSS style sheets. Using GMenu, you can quickly create a dynamic drop down menu that: 1. Organizes your website hierarchy 2. Is stylish and interactive 3. Works in IE, Firefox, and Netscape 4. Can even drop down over form fields in IE and much more!
/*
** v1.0.20050304
**
** gmenu is a customizable collection of javascript routines which can be used
** to quickly create platform independent drop down menus.
**
** gmenu has been tested on IE 6.0, Netscape 7.1, and FireFox 1.0
**
** Copyright (C) 2004-2005 Geoffrey Derek Cox (formfields.com, info@formfields.com)
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
** See http://www.gnu.org/ for an up-to-date copy of the GPL.
*/
// ENHANCEMENTS:
// 1. GMenu blinks when web browser caching is disabled. This could be fixed
// by optimizing some of the GMenu highlighting routines.
var isIe = (document.all ? true : false);
// Needed because the mouse coordinates in IE and NN are not exact
var TOLERANCE_TOP = (isIe ? 2 : -2);
var TOLERANCE_RIGHT = (isIe ? 2 : 0);
var TOLERANCE_BOTTOM = (isIe ? 2 : 1);
var TOLERANCE_LEFT = (isIe ? 2 : 0);
// The number of pixels to offset the top-left corner of the top menu from a
// root menu item.
var TOP_MENU_LEFT_OFFSET = 0;
// The maximum depth of sub menus (not including the root menu)
var MAX_MENUS = 5;
function stripPx(x) {
if (x.length <= 2)
return x;
return eval(x.substr(0,x.length-2));
}
function addDimensions(x1, x2) {
return (stripPx(x1) + stripPx(x2)) + 'px';
}
function getHighlightedClass(curClass) {
if (curClass.substr(0, 12) == 'subMenuItemL')
return 'subMenuItemLeafHighlight';
else
return 'subMenuItemHighlight';
}
function getNotHighlightedClass(curClass) {
if (curClass.substr(0, 12) == 'subMenuItemL')
return 'subMenuItemLeaf';
else
return 'subMenuItem';
}
// Highlight current item and "unhighlight" other items in current menu
function setHighlighting(selectedNode) {
//alert('start setHighlighting');
for (i = 0; i < (selectedNode.parentNode).childNodes.length; i++) {
var curNode = (selectedNode.parentNode).childNodes[i];
if (curNode.id == selectedNode.id) {
curNode.className = getHighlightedClass(curNode.className);
} else {
curNode.className = getNotHighlightedClass(curNode.className);
}
}
//alert('end setHighlighting');
}
function createLink(text, url, cssClass) {
if (url == null)
return text;
return '<a class="' + cssClass + '" href="' + url + '" style="height:100%;width:100%;">' + text + '</a>';
}
// This is needed so that a user doesn't cause a js error by doing a mouse over on the menu
// before the menu has completely loaded.
function enableRootMenus() {
var i = 1;
while ( (rootMenu = document.getElementById("rootMenu" + (i++))) ) {
rootMenu.disabled = false;
}
}
// Prepare the menus
// Note: IE doesn't allow one to cover a form field with a div, however it does permit
// an iframe to cover a form field. Therefore, we place an iframe under each menu div.
function initGMenu() {
html = '';
for (i = 1; i <= MAX_MENUS; i++) {
html += '<div id="menuBase' + i + '" class="subMenuBase" style="position:absolute;top:0px;left:0px;visibility:hidden;">'
+ '<iframe frameborder="0" width="100%"></iframe></div>';
html += '<div id="menu' + i + '" class="subMenu" style="position:absolute;top:0px;left:0px;visibility:hidden;" onMouseOut="hideMenus(event);">'
+ '</div>';
}
document.body.innerHTML += html;
enableRootMenus();
}
// Fill in the sub menus. This is done every time a menu pops up.
function drawSubMenu(menuId, menuArray) {
var menu = document.getElementById(menuId);
var html = '';
for (i = 0; i < menuArray.length; i++) {
html += '<div id="' + menuId + "-" + i + '" class="' + (menuArray[i][2] == null ? 'subMenuItemLeaf' : 'subMenuItem"') + '"'
+ ' onMouseOver="' +(menuArray[i][2] == null ? '' : 'showMenu(\'' + menuId + '\', ' + menuArray[i][2] + ', this);') + 'setHighlighting(this);">'
+ createLink(menuArray[i][0], menuArray[i][1], 'subMenuItem')
+ '</div>';
}
menu.innerHTML = html;
return menu;
}
// Shows a level 1 menu, right below the root menus.
// Had to adjust the top location for netscape?????????
function showTopMenu(curMenu, menuArray) {
curMenu.className = curMenu.id + 'Highlight';
var subMenu = drawSubMenu('menu1', menuArray);
subMenu.style.top = curMenu.offsetParent.offsetTop + curMenu.offsetParent.offsetHeight;
subMenu.style.left = curMenu.offsetParent.offsetLeft + curMenu.offsetLeft + TOP_MENU_LEFT_OFFSET;
//subMenu.style.top = curMenu.offsetTop + curMenu.offsetHeight;
//subMenu.style.left = curMenu.offsetLeft + TOP_MENU_LEFT_OFFSET;
var subMenuBase = document.getElementById('menuBase1');
subMenuBase.childNodes[0].style.height = subMenu.offsetHeight;
subMenuBase.style.top = subMenu.style.top;
subMenuBase.style.left = subMenu.style.left;
subMenu.style.visibility = 'visible';
subMenuBase.style.visibility = 'visible';
}
// Shows a level2..leveln menu, directly to the right of the selected item.
function showMenu(curMenuId, menuArray, curItem) {
var curMenu = document.getElementById(curMenuId);
var subMenuId = 'menu' + (eval((curMenuId).substr(4,1)) + 1);
var subMenuBaseId = 'menuBase' + (eval((curMenuId).substr(4,1)) + 1);
var subMenuBase = document.getElementById(subMenuBaseId);
var subMenu = drawSubMenu(subMenuId, menuArray);
subMenu.style.top = curItem.offsetParent.offsetTop + curItem.offsetTop; //stripPx(curMenu.style.top);
subMenu.style.left = stripPx(curMenu.style.left) + curMenu.offsetWidth - 4;
subMenuBase.style.top = subMenu.style.top;
subMenuBase.childNodes[0].style.height = subMenu.offsetHeight;
subMenuBase.style.left = subMenu.style.left;
subMenu.style.visibility = 'visible';
subMenuBase.style.visibility = 'visible';
}
function unhighlightRootMenus() {
var i = 1;
while ( (rootMenu = document.getElementById("rootMenu" + (i++))) ) {
rootMenu.className = rootMenu.id;
}
}
// Recursively check and then hide all menus arropriately. This is done
// during a mouseOut event.
// ENHANCEMENT: Ideally the menus should be hidden a second after the mouse leaves
// the menu hierarchy. Unfortunately, I was unable to implement this
// using setTimeout because setTimeout doesn't play nicely with mouse coordinates.
function hideMenus(e) {
if (hideMenu('menu1', e)) {
unhighlightRootMenus();
}
}
// Recursively check all sub menus of the current menu.
// If the mouse has left a menu and all its sub menus then hide that menu.
function hideMenu(curMenuId, e) {
var curMenu = document.getElementById(curMenuId);
if (isIe) { // using IE?
//xMousePos = window.event.x + document.body.scrollLeft + stripPx(curMenu.style.left); // for IE
xMousePos = event.clientX + document.body.scrollLeft;
//yMousePos = window.event.y + document.body.scrollTop + stripPx(curMenu.style.top);
yMousePos = event.clientY + document.body.scrollTop;
} else {
xMousePos = e.clientX;
yMousePos = e.clientY;
}
var hideChild = true;
var childId = 'menu' + (eval((curMenu.id).substr(4,1)) + 1);
var curMenuBaseId = 'menuBase' + (eval((curMenu.id).substr(4,1)));
var curMenuBase = document.getElementById(curMenuBaseId);
var child = document.getElementById(childId);
if (child != null) {
hideChild = hideMenu(child.id, e);
}
//alert(curMenu.style.left + " " + curMenu.offsetWidth + " " + curMenu.offsetLeft + " " + xMousePos);
//alert(curMenu.style.left + " " + curMenu.offsetWidth + " " + curMenu.offsetLeft + " " + xMousePos);
if (xMousePos < stripPx(curMenu.style.left) + TOLERANCE_LEFT
|| xMousePos > stripPx(curMenu.style.left) + curMenu.offsetWidth - TOLERANCE_RIGHT
|| yMousePos < stripPx(curMenu.style.top) + TOLERANCE_TOP
|| yMousePos > stripPx(curMenu.style.top) + curMenu.offsetHeight - TOLERANCE_BOTTOM
|| curMenu.style.visibility == 'hidden') {
if (hideChild == true) {
curMenu.style.visibility = 'hidden';
curMenuBase.style.visibility = 'hidden';
return true;
}
return false;
} else {
return false;
}
}