function getOffset(element, offset) { if (!offset) offset = {left: 0, top: 0}; do { offset.left += parseInt(element.offsetLeft, 10); offset.top += parseInt(element.offsetTop, 10); } while (element = element.offsetParent); return offset; } function hasClassName(element, className) { if (className == '') return false; var classNames = element.className.split(' '); for (var i = 0; i < classNames.length; i++) { if (classNames[i] == className) return true; } return false; } function addClassName(element, className) { if (!hasClassName(element, className)) { element.className += (element.className ? ' ' : '') + className; } } function removeClassName(element, className) { var classNames = element.className.split(' '); var newClassNames = ''; for (var i = 0; i < classNames.length; i++) { if (classNames[i] != className) { if (newClassNames.length > 0) { newClassNames += ' '; } newClassNames += classNames[i]; } } element.className = newClassNames; } function addHandler(target, eventName, handlerName, handler) { if (handler) target[handlerName] = handler; if (target.addEventListener) { target.addEventListener(eventName, function(e) { target[handlerName](e); }, false); } else if (target.attachEvent) { target.attachEvent('on' + eventName, function(e) { target[handlerName](e); }); } else { var origHandler = target['on' + eventName]; if (origHandler) { target['on' + eventName] = function(e) { origHandler(e); target[handlerName](e); }; } else { target['on' + eventName] = target[handlerName]; } } } function ScoutMenu(elementId, options) { this.isIE50 = (navigator.userAgent.indexOf('MSIE 5.0') > 0); this.element = window.document.getElementById(elementId); this.offset = {left: 0, top: 0}; this.emptySubMenu = null; this.controlObscurer = null; this.options = new ScoutMenuOptions(); // merge empty SearchMenuOptions with given options if (options) { for (prop in options) { this.options[prop] = options[prop]; } } this.topLevelItems = []; this.lastSelectedTopLevelItem = -1; this.hideTimeout = false; } ScoutMenu.prototype = { getOptions: function() { return this.options; }, initialize: function() { if (!this.element) return; this.offset = getOffset(this.element); this.initializeSpecialItems(); this.addHandlers(); if (this.options.initialTopMenuItem >= 0 && this.options.initialTopMenuItem < this.topLevelItems.length) { this.topLevelItems[this.options.initialTopMenuItem].scoutMenu_highlightItem(null); } }, initializeSpecialItems: function() { if (this.options.specialItemsClassName) { var emptySubmenuFound = (this.options.emptySubmenuClassName == ''); var controlObscurerFound = (this.isIE50 || this.options.controlObscurerClassName == ''); for (var i = 0; i < this.element.childNodes.length && (!emptySubmenuFound || !controlObscurerFound); i++) { var child = this.element.childNodes[i]; if (child.nodeType == 1 && hasClassName(child, this.options.specialItemsClassName)) { for (var j = 0; j < child.childNodes.length && (!emptySubmenuFound || !controlObscurerFound); j++) { var specialNode = child.childNodes[j]; if (specialNode.nodeType == 1) { if (!controlObscurerFound && hasClassName(specialNode, 'control-obscurer')) { this.element.insertBefore(specialNode, child); this.controlObscurer = specialNode; controlObscurerFound = true; j--; } } } this.element.removeChild(child); } } } }, addHandlers: function() { var itemIndex = 0; for (var i = 0; i < this.element.childNodes.length; i++) { var child = this.element.childNodes[i]; if (child.nodeType == 1 && child.nodeName == 'LI' && !hasClassName(child, this.options.specialItemsClassName)) { if (itemIndex == 0 && this.isIE50 && this.emptySubMenu) { this.emptySubMenu.style.top = (parseInt(this.emptySubMenu.style.top) + child.offsetHeight) + 'px'; } this.topLevelItems[itemIndex] = child; var me = this; child.scoutMenu_isRightMenu = hasClassName(child, this.options.rightMenuClassName); child.scoutMenu_itemIndex = itemIndex; child.scoutMenu_highlightItem = function(event) { me.mouseOver(event, this.scoutMenu_itemIndex); }; child.scoutMenu_unhighlightItem = function(event, resetting) { if (!me.options.showOtherSubmenus) { if (!resetting || (me.options.initialTopMenuItem != this.scoutMenu_itemIndex)) { if (this.scoutMenu_isRightMenu) removeClassName(this, me.options.rightMenuHighlightClassName); else removeClassName(this, me.options.highlightClassName); } } else { if (!resetting || (me.options.initialTopMenuItem != this.scoutMenu_itemIndex)) { if (this.scoutMenu_isRightMenu) removeClassName(this, me.options.rightMenuActiveClassName); else removeClassName(this, me.options.activeClassName); } } if (me.options.showOtherSubmenus && this.scoutMenu_subMenu && (!resetting || (this.scoutMenu_itemIndex != me.options.initialTopMenuItem))) { this.scoutMenu_subMenu.style.visibility = ''; } } addHandler(child, 'mouseover', 'scoutMenu_highlightItem'); addHandler(child, 'mouseout', 'scoutMenu_mouseOut', function(evt) { me.mouseOut(evt, this.scoutMenu_itemIndex); }); this.addSubmenuHandlers(child); itemIndex++; } } }, addSubmenuHandlers: function(element) { var childUL = null; for (var i = 0; i < element.childNodes.length; i++) { var child = element.childNodes[i]; if (child.nodeType == 1 && child.nodeName == 'UL') { if (!childUL) childUL = child; else { childUL = null; break; } } } if (childUL) { element.scoutMenu_subMenu = childUL; childUL.style.left = (parseInt(childUL.offsetLeft, 10) + this.offset.left) + 'px'; childUL.style.top = (parseInt(childUL.offsetTop, 10) + this.offset.top + 1 + (this.isIE50 ? element.offsetHeight : 0)) + 'px'; var isFirstChild = true; for (var i = 0; i < childUL.childNodes.length; i++) { var child = childUL.childNodes[i]; if (child.nodeType == 1 && child.nodeName == 'LI') { child.scoutMenu_isRightMenu = element.scoutMenu_isRightMenu; if (this.options.separator) { if (!isFirstChild) { var sepElem = document.createElement('SPAN'); sepElem.appendChild(document.createTextNode(this.options.separator)); sepElem.className = this.options.separatorClassName; child.parentNode.insertBefore(sepElem, child); i++; } else { isFirstChild = false; } } var me = this; child.scoutMenu_highlightItem = function(event) { if (this.scoutMenu_isRightMenu) { if (this.scoutMenu_subMenu) addClassName(this, me.options.rightSubmenuActiveWithPopupClassName); else addClassName(this, me.options.rightSubmenuActiveClassName); } else { if (this.scoutMenu_subMenu) addClassName(this, me.options.submenuActiveWithPopupClassName); else addClassName(this, me.options.submenuActiveClassName); } me.showPopupMenu(this); }; child.scoutMenu_unhighlightItem = function(event) { if (this.scoutMenu_isRightMenu) { if (this.scoutMenu_subMenu) removeClassName(this, me.options.rightSubmenuActiveWithPopupClassName); else removeClassName(this, me.options.rightSubmenuActiveClassName); } else { if (this.scoutMenu_subMenu) removeClassName(this, me.options.submenuActiveWithPopupClassName); else removeClassName(this, me.options.submenuActiveClassName); } me.hidePopupMenu(this); }; addHandler(child, 'mouseover', 'scoutMenu_highlightItem'); addHandler(child, 'mouseout', 'scoutMenu_unhighlightItem'); this.addPopupMenuHandlers(child) } } } }, addPopupMenuHandlers: function(element) { var childUL = null; for (var i = 0; i < element.childNodes.length; i++) { var child = element.childNodes[i]; if (child.nodeType == 1 && child.nodeName == 'UL') { if (!childUL) childUL = child; else { childUL = null; break; } } } if (childUL) { element.scoutMenu_subMenu = childUL; var offset = getOffset(element); childUL.style.left = (offset.left + parseInt(childUL.offsetLeft) - 2) + 'px'; childUL.style.top = (element.offsetHeight+1) + 'px'; } }, mouseOver: function(event, itemIndex) { if (this.hideTimeout) { clearTimeout(this.hideTimeout); this.hideTimeout = false; } if (this.lastSelectedTopLevelItem == itemIndex) return; if (this.lastSelectedTopLevelItem >= 0) { this.topLevelItems[this.lastSelectedTopLevelItem].scoutMenu_unhighlightItem(event); } var item = this.topLevelItems[itemIndex]; if (!this.options.showOtherSubmenus && this.options.initialTopMenuItem != itemIndex) { if (item.scoutMenu_isRightMenu) addClassName(item, this.options.rightMenuHighlightClassName); else addClassName(item, this.options.highlightClassName); } else { if (item.scoutMenu_isRightMenu) addClassName(item, this.options.rightMenuActiveClassName); else { // if no submenu then have no active entry var showActive = false; if(item.lastChild != null) { var subItem = item.lastChild; // submenu is nested in unordered list if(subItem.nodeName.toLowerCase() == "ul") { // check if menu empty if(subItem.childNodes.length > 1) { showActive = true; } else if(subItem.childNodes.length == 1 && subItem.firstChild.nodeName != "#text") { showActive = true; } } } if(showActive) { addClassName(item, this.options.activeClassName); } } } this.lastSelectedTopLevelItem = itemIndex; if ((this.options.showOtherSubmenus || (this.options.initialTopMenuItem == itemIndex)) && item.scoutMenu_subMenu) { item.scoutMenu_subMenu.style.visibility = 'visible'; } }, mouseOut: function(event, itemIndex) { if (this.hideTimeout) return; var me = this; if (this.options.showOtherSubmenus) this.hideTimeout = setTimeout(function() { me.resetMenu(); }, this.options.hideTime ? this.options.hideTime : 100); else this.resetMenu(); }, resetMenu: function() { if (this.lastSelectedTopLevelItem >= 0) { this.topLevelItems[this.lastSelectedTopLevelItem].scoutMenu_unhighlightItem(null, true); } if (this.options.initialTopMenuItem >= 0) { this.topLevelItems[this.options.initialTopMenuItem].scoutMenu_highlightItem(); } }, showPopupMenu: function(element) { if (element.scoutMenu_subMenu) { element.scoutMenu_subMenu.style.visibility = 'visible'; if (this.controlObscurer) { var offset = getOffset(element.scoutMenu_subMenu); this.controlObscurer.style.left = (offset.left) + 'px'; this.controlObscurer.style.top = (offset.top) + 'px'; this.controlObscurer.style.width = (element.scoutMenu_subMenu.offsetWidth) + 'px'; this.controlObscurer.style.height = (element.scoutMenu_subMenu.offsetHeight) + 'px'; this.controlObscurer.style.display = 'block'; } } }, hidePopupMenu: function(element) { if (element.scoutMenu_subMenu) { element.scoutMenu_subMenu.style.visibility = 'hidden'; if (this.controlObscurer) { this.controlObscurer.style.display = 'none'; } } } } function ScoutMenuOptions() { this.hideTime = 3000; this.initialTopMenuItem = 0; this.separator = '|'; this.separatorClassName = 'scoutmenu-separator'; this.showOtherSubmenus = false; this.specialItemsClassName = 'special-items'; this.emptySubmenuClassName = 'empty-submenu'; this.controlObscurerClassName = 'control-obscurer'; this.highlightClassName = 'highlight'; this.activeClassName = 'active'; this.rightMenuClassName = 'rightmenu'; this.rightMenuHighlightClassName = 'rightmenu-highlight'; this.rightMenuActiveClassName = 'rightmenu-active'; this.submenuActiveClassName = 'subactive'; this.submenuActiveWithPopupClassName = 'subactive-withpopup'; this.rightSubmenuActiveClassName = 'rightmenu-subactive'; this.rightSubmenuActiveWithPopupClassName = 'rightmenu-subactive-withpopup'; }