/*----------------------------------------------------------------------------\ | DHTML Menu 4.28 | |-----------------------------------------------------------------------------| | Created by Erik Arvidsson | | (http://webfx.eae.net/contact.html#erik) | | For WebFX (http://webfx.eae.net/) | |-----------------------------------------------------------------------------| | A menu system for Internet Explorer 5.5+ Win32 that allows menus to extend | | outside the browser window limits. | |-----------------------------------------------------------------------------| | Copyright (c) 1999 - 2003 Erik Arvidsson | |-----------------------------------------------------------------------------| | This software is provided "as is", without warranty of any kind, express or | | implied, including but not limited to the warranties of merchantability, | | fitness for a particular purpose and noninfringement. In no event shall the | | authors or copyright holders be liable for any claim, damages or other | | liability, whether in an action of contract, tort or otherwise, arising | | from, out of or in connection with the software or the use or other | | dealings in the software. | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | This software is available under the three different licenses mentioned | | below. To use this software you must chose, and qualify, for one of those. | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | The WebFX Non-Commercial License http://webfx.eae.net/license.html | | Permits anyone the right to use the software in a non-commercial context | | free of charge. | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | The WebFX Commercial license http://webfx.eae.net/commercial.html | | Permits the license holder the right to use the software in a commercial | | context. Such license must be specifically obtained, however it's valid for | | any number of implementations of the licensed software. | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | GPL - The GNU General Public License http://www.gnu.org/licenses/gpl.txt | | Permits anyone the right to use and modify the software without limitations | | as long as proper credits are given and the original and modified source | | code are included. Requires that the final product, software derivate from | | the original source or any software utilizing a GPL component, such as | | this, is also licensed under the GPL license. | |-----------------------------------------------------------------------------| | 2002-05-28 | First version | | 2002-06-07 | Updated default cssFile value to "skins/winclassic.css" | | | instead of "winclassic.css" | | 2002-06-10 | (4.1) Lots of changes. Rewrote measuring and positioning | | | routines to prevent screen flicker. As well as general code | | | optimization. | | 2002-07-03 | getInsetRight and getInsetBottom broke in the last update. | | | Radio and Check box did not check disabled state correctly. | | 2002-07-25 | Created a work around for a weird bug that did not show first | | | menu. Disabled browser keyboard shortcuts when menus are open. | | | Added workaround for buggy dual monitor drivers. | | 2002-09-05 | Fixed cases where the caching of the CSS failed and caused the | | | cached menu size to be incorrect. | | 2002-09-05 | Insets were ignored for vertical menus. | | 2002-09-06 | Some properties have been moved to the prototype to make | | | customizing easier. | | 2002-09-24 | Minor changes to prevent size errors. | | 2002-10-22 | Added second argument to Menu add. | | | Added support for Menu cssText. | | 2002-10-29 | (4.2) Lots of work to work around IE memory bugs. | | 2002-11-03 | Typo in MenuBar goToNextMenuItem | | 2002-11-23 | The height and width were not correctly limited in show. | | 2002-12-04 | Changed to use onunload instead of onbeforeunload. | | | Onbeforeunload was causing troubles with certain links. | | 2003-03-07 | Fixed bug in MenuButton toHtml and added MenuBar invalidate | | | also created a clone extension (menu4.clone.js) | | 2003-04-01 | added document arguments to MenuBar create and write. | | | Better mnemonic handling when HTML is used | | | onclose, onshow and onbeforeshow | | 2003-09-12 | Updated mnemonic code and fixed an itemIndex bug when adding | | | items. | | 2003-09-23 | The scrollbutton.js still used onbeforeunload | | 2003-10-15 | Add support for keyboardAccelKey2 (defaults to F10). Also | | | fixed so that Esc on last menu correctly goes to the menu bar. | | 2003-11-24 | Changed the MenuButton constructor to not fail if sub menu is | | | left out. This allows you to set the sub menu later. A sub | | | menu is still needed! | |-----------------------------------------------------------------------------| | Dependencies: poslib.js Used to find positions of elements | | scrollbutton.js Used for the buttnos that allows the menu | | to be scrollable | |-----------------------------------------------------------------------------| | Created 2002-05-28 | All changes are in the log above. | Updated 2003-11-24 | \----------------------------------------------------------------------------*/ //////////////////////////////////////////////////////////////////////////////////// // menuCache // var menuCache = { _count: 0, _idPrefix: "-menu-cache-", getId: function () { return this._idPrefix + this._count++; }, remove: function ( o ) { delete this[ o.id ]; } }; //////////////////////////////////////////////////////////////////////////////////// // Menu // function Menu() { this.items = []; this.parentMenu = null; this.parentMenuItem = null; this.popup = null; this.shownSubMenu = null; this._aboutToShowSubMenu = false; this.selectedIndex = -1; this._drawn = false; this._scrollingMode = false; this._showTimer = null; this._closeTimer = null; this._onCloseInterval = null; this._closed = true; this._closedAt = 0; this._cachedSizes = {}; this._measureInvalid = true; this.id = menuCache.getId(); menuCache[ this.id ] = this; } Menu.prototype.cssFile = "skins/winclassic.css"; Menu.prototype.cssText = null; Menu.prototype.mouseHoverDisabled = true; Menu.prototype.showTimeout = 250; Menu.prototype.closeTimeout = 250; Menu.keyboardAccelKey = 27; // the keyCode for the key tp activate Menu.keyboardAccelKey2 = 121; // the menubar Menu.keyboardAccelProperty = "ctrlKey"; // when this property is true default // actions will be canceled on a menu // Use -1 to disable keyboard invoke of the menubar // Use "" to allow all normal keyboard commands inside the menus Menu.prototype.add = function ( mi, beforeMi ) { if ( beforeMi != null ) { var items = this.items; var l = items.length; var i = 0; for ( ; i < l; i++ ) { if ( items[i] == beforeMi ) break; } this.items = items.slice( 0, i ).concat( mi ).concat( items.slice( i, l ) ); // update itemIndex for (var j = i; j < l + 1; j++) this.items[j].itemIndex = j; } else { this.items.push( mi ); mi.itemIndex = this.items.length - 1; } mi.parentMenu = this; if ( mi.subMenu ) { mi.subMenu.parentMenu = this; mi.subMenu.parentMenuItem = mi; } return mi; }; Menu.prototype.remove = function ( mi ) { var res = []; var items = this.items; var l = items.length; for (var i = 0; i < l; i++) { if ( items[i] != mi ) { res.push( items[i] ); items[i].itemIndex = res.length - 1; } } this.items = res; mi.parentMenu = null; return mi; }; Menu.prototype.toHtml = function () { var items = this.items; var l = items.length var itemsHtml = new Array( l ); for (var i = 0; i < l; i++) itemsHtml[i] = items[i].toHtml(); return "" + (this.cssText == null ? "" : "") + "" + "
" + "" + "
" + "" + "5" + "" + "
" + "
" + "" + itemsHtml.join( "" ) + "
" + "
" + "" + "
" + "" + "6" + "" + "
" + "
" + ""; }; Menu.prototype.createPopup = function () { var w; var pm = this.parentMenu; if ( pm == null ) w = window; else w = pm.getDocument().parentWindow; this.popup = w.createPopup(); }; Menu.prototype.getMeasureDocument = function () { if ( this.isShown() && this._drawn ) return this.getDocument(); var mf = Menu._measureFrame; if ( mf == null ) { // should be top document mf = Menu._measureFrame = document.createElement("IFRAME"); var mfs = mf.style; mfs.position = "absolute"; mfs.visibility = "hidden"; mfs.left = "-100px"; mfs.top = "-100px"; mfs.width = "10px"; mfs.height = "10px"; mf.frameBorder = 0; document.body.appendChild( mf ); } var d = mf.contentWindow.document if ( Menu._measureMenu == this && !this._measureInvalid ) return d; d.open( "text/html", "replace" ); d.write( this.toHtml() ); d.close(); Menu._measureMenu = this; this._measureInvalid = false; return d; }; Menu.prototype.getDocument = function () { if ( this.popup ) return this.popup.document; else return null; }; Menu.prototype.getPopup = function () { if ( this.popup == null ) this.createPopup(); return this.popup; }; Menu.prototype.invalidate = function () { if ( this._drawn ) { // do some memory cleanup if ( this._scrollUpButton ) this._scrollUpButton.destroy(); if ( this._scrollDownButton ) this._scrollDownButton.destroy(); var items = this.items; var l = items.length; var mi; for ( var i = 0; i < l; i++ ) { mi = items[i]; mi._htmlElement_menuItem = null; mi._htmlElement = null; } this.detachEvents(); } this._drawn = false; this.resetSizeCache(); this._measureInvalid = true; }; Menu.prototype.redrawMenu = function () { this.invalidate(); this.drawMenu(); }; Menu.prototype.drawMenu = function () { if ( this._drawn ) return; this.getPopup(); var d = this.getDocument(); d.open( "text/html", "replace" ); d.write( this.toHtml() ); d.close(); this._drawn = true; // set up scroll buttons var up = d.getElementById( "scroll-up-item" ); var down = d.getElementById( "scroll-down-item" ); var scrollContainer = d.getElementById( "scroll-container" ); new ScrollButton( up, scrollContainer, 8 ); new ScrollButton( down, scrollContainer, 2 ); // bind menu items to the table rows var rows = scrollContainer.firstChild.tBodies[0].rows; var items = this.items; var l = rows.length; var mi; for ( var i = 0; i < l; i++ ) { mi = items[i]; rows[i]._menuItem = mi; mi._htmlElement = rows[i]; } // hook up mouse this.hookupMenu( d ); }; Menu.prototype.show = function ( left, top, w, h ) { var pm = this.parentMenu; if ( pm ) pm.closeAllSubs( this ); var wasShown = this.isShown(); if ( typeof this.onbeforeshow == "function" && !wasShown ) this.onbeforeshow(); this.drawMenu(); if ( left == null ) left = 0; if ( top == null ) top = 0; w = w || Math.min( window.screen.width, this.getPreferredWidth() ); h = h || Math.min( window.screen.height, this.getPreferredHeight() ); this.popup.show( left, top, w, h ); // work around a bug that sometimes occured with large pages when // opening the first menu if ( this.getPreferredWidth() == 0 ) { this.invalidate(); this.show( left, top, w, h ); return; } this.fixScrollButtons(); this.fixScrollEnabledState(); // clear selected item if ( this.selectedIndex != -1 ) { if ( this.items[ this.selectedIndex ] ) this.items[ this.selectedIndex ].setSelected( false ); } if ( pm ) { pm.shownSubMenu = this; pm._aboutToShowSubMenu = false; } window.clearTimeout( this._showTimer ); window.clearTimeout( this._closeTimer ); this._closed = false; this._startClosePoll(); if ( typeof this.onshow == "function" && !wasShown && this.isShown() ) this.onshow(); }; Menu.prototype.isShown = function () { this._checkCloseState(); return this.popup != null && this.popup.isOpen; }; Menu.prototype.fixSize = function () { var w = Math.min( window.screen.width, this.getPreferredWidth() ); var h = Math.min( window.screen.height, this.getPreferredHeight() ); var l = Math.max( 0, this.getLeft() ); var t = Math.max( 0, this.getTop() ); this.popup.show( l, t, w, h ); }; Menu.prototype.getWidth = function () { var d = this.getDocument(); if ( d != null ) return d.body.offsetWidth; else return 0; }; Menu.prototype.getHeight = function () { var d = this.getDocument(); if ( d != null ) return d.body.offsetHeight; else return 0; }; Menu.prototype.getPreferredWidth = function () { this.updateSizeCache(); return this._cachedSizes.preferredWidth; }; Menu.prototype.getPreferredHeight = function () { this.updateSizeCache(); return this._cachedSizes.preferredHeight; }; Menu.prototype.getLeft = function () { var d = this.getDocument(); if ( d != null ) return d.parentWindow.screenLeft; else return 0; }; Menu.prototype.getTop = function () { var d = this.getDocument(); if ( d != null ) return d.parentWindow.screenTop; else return 0; }; // Depreciated. Use show instead Menu.prototype.setLeft = function ( l ) { throw new Error("Depreciated. Use show instead"); //var t = this.getTop(); //this.setLocation( l, t ); }; // Depreciated. Use show instead Menu.prototype.setTop = function ( t ) { throw new Error("Depreciated. Use show instead"); //var l = this.getLeft(); //this.setLocation( l, t ); }; // Depreciated. Use show instead Menu.prototype.setLocation = function ( l, t ) { throw new Error("Depreciated. Use show instead"); //var w = this.getWidth(); //var h = this.getHeight(); //this.popup.show( l, t, w, h ); }; // Depreciated. Use show instead Menu.prototype.setRect = function ( l, t, w, h ) { throw new Error("Depreciated. Use show instead"); //this.popup.show( l, t, w, h ); }; Menu.prototype.getInsetLeft = function () { this.updateSizeCache(); return this._cachedSizes.insetLeft; }; Menu.prototype.getInsetRight = function () { this.updateSizeCache(); return this._cachedSizes.insetRight; }; Menu.prototype.getInsetTop = function () { this.updateSizeCache(); return this._cachedSizes.insetTop; }; Menu.prototype.getInsetBottom = function () { this.updateSizeCache(); return this._cachedSizes.insetBottom; }; Menu.prototype.areSizesCached = function () { var cs = this._cachedSizes; return this._drawn && "preferredWidth" in cs && "preferredHeight" in cs && "insetLeft" in cs && "insetRight" in cs && "insetTop" in cs && "insetBottom" in cs; }; // depreciated Menu.prototype.cacheSizes = function ( bForce ) { return updateSizeCache( bForce ); }; Menu.prototype.resetSizeCache = function () { this._cachedSizes = {}; }; Menu.prototype.updateSizeCache = function ( bForce ) { if ( this.areSizesCached() && !bForce ) return; var d = this.getMeasureDocument(); var body = d.body; var cs = this._cachedSizes = {}; // reset var scrollContainer = d.getElementById( "scroll-container" ); // preferred width cs.preferredWidth = d.body.scrollWidth; // preferred height scrollContainer.style.overflow = "visible"; cs.preferredHeight = body.firstChild.offsetHeight; //body.scrollHeight; scrollContainer.style.overflow = "hidden"; // inset left cs.insetLeft = posLib.getLeft( scrollContainer ); // inset right cs.insetRight = body.scrollWidth - posLib.getLeft( scrollContainer ) - scrollContainer.offsetWidth; // inset top var up = d.getElementById( "scroll-up-item" ); if ( up.currentStyle.display == "none" ) cs.insetTop = posLib.getTop( scrollContainer ); else cs.insetTop = posLib.getTop( up ); // inset bottom var down = d.getElementById( "scroll-down-item" ); if ( down.currentStyle.display == "none" ) { cs.insetBottom = body.scrollHeight - posLib.getTop( scrollContainer ) - scrollContainer.offsetHeight; } else { cs.insetBottom = body.scrollHeight - posLib.getTop( down ) - down.offsetHeight; } }; Menu.prototype.fixScrollButtons = function () { var d = this.getDocument(); var up = d.getElementById( "scroll-up-item" ); var down = d.getElementById( "scroll-down-item" ); var scrollContainer = d.getElementById( "scroll-container" ); var scs = scrollContainer.style; if ( scrollContainer.scrollHeight > this.getHeight() ) { up.style.display = ""; down.style.display = ""; scs.height = ""; scs.overflow = "visible"; scs.height = Math.max( 0, this.getHeight() - ( d.body.scrollHeight - scrollContainer.offsetHeight ) ) + "px"; scs.overflow = "hidden"; this._scrollingMode = true; } else { up.style.display = "none"; down.style.display = "none"; scs.overflow = "visible"; scs.height = ""; this._scrollingMode = false; } }; Menu.prototype.fixScrollEnabledState = function () { var d = this.getDocument(); var up = d.getElementById( "scroll-up-item" ); var down = d.getElementById( "scroll-down-item" ); var scrollContainer = d.getElementById( "scroll-container" ); var tr; tr = up.rows[0]; if ( scrollContainer.scrollTop == 0 ) { if ( tr.className == "hover" || tr.className == "disabled-hover" ) tr.className = "disabled-hover"; else tr.className = "disabled"; } else { if ( tr.className == "disabled-hover" || tr.className == "hover" ) tr.className = "hover"; else tr.className = ""; } tr = down.rows[0]; if ( scrollContainer.scrollHeight - scrollContainer.clientHeight <= scrollContainer.scrollTop ) { if ( tr.className == "hover" || tr.className == "disabled-hover" ) tr.className = "disabled-hover"; else tr.className = "disabled"; } else { if ( tr.className == "disabled-hover" || tr.className == "hover" ) tr.className = "hover"; else tr.className = ""; } }; Menu.prototype.closeAllMenus = function () { if ( this.parentMenu ) this.parentMenu.closeAllMenus(); else this.close(); }; Menu.prototype.close = function () { this.closeAllSubs(); window.clearTimeout( this._showTimer ); window.clearTimeout( this._closeTimer ); if ( this.popup ) this.popup.hide(); var pm = this.parentMenu; if ( pm && pm.shownSubMenu == this ) pm.shownSubMenu = null; this.setSelectedIndex( -1 ); this._checkCloseState(); }; Menu.prototype.closeAllSubs = function ( oNotThisSub) { // go through items and check for sub menus var items = this.items; var l = items.length; for (var i = 0; i < l; i++) { if ( items[i].subMenu != null && items[i].subMenu != oNotThisSub ) items[i].subMenu.close(); } }; Menu.prototype.getSelectedIndex = function () { return this.selectedIndex; }; Menu.prototype.setSelectedIndex = function ( nIndex ) { if ( this.selectedIndex == nIndex ) return; if ( nIndex >= this.items.length ) nIndex = -1; var mi; // deselect old if ( this.selectedIndex != -1 ) { mi = this.items[ this.selectedIndex ]; mi.setSelected( false ); } this.selectedIndex = nIndex; mi = this.items[ this.selectedIndex ]; if ( mi != null ) mi.setSelected( true ); }; Menu.prototype.goToNextMenuItem = function () { var i = 0; var items = this.items; var length = items.length; var index = this.getSelectedIndex(); var tmp; do { if ( index == -1 || index >= length ) index = 0; else index++; i++; tmp = items[index] } while ( !( tmp != null && tmp instanceof MenuItem && !(tmp instanceof MenuSeparator) || i >= length ) ) if ( tmp != null ) this.setSelectedIndex( index ); }; Menu.prototype.goToPreviousMenuItem = function () { var i = 0; var items = this.items; var length = items.length; var index = this.getSelectedIndex(); var tmp; do { if ( index == -1 || index >= length ) index = length - 1; else index--; i++; tmp = items[index] } while ( !( tmp != null && tmp instanceof MenuItem && !(tmp instanceof MenuSeparator) || i >= length ) ) if ( tmp != null ) this.setSelectedIndex( index ); }; Menu.prototype.goToNextMenu = function () { var index = this.getSelectedIndex(); var mi = this.items[ index ]; if ( mi && mi.subMenu && !mi.disabled ) { mi.subMenu.setSelectedIndex( 0 ); mi.showSubMenu( false ); } else { // go up to root and select next var mb = this.getMenuBar(); if ( mb != null ) mb.goToNextMenuItem(); } }; Menu.prototype.goToPreviousMenu = function () { if ( this.parentMenuItem && this.parentMenuItem instanceof MenuButton ) { this.parentMenu.goToPreviousMenuItem(); } else if ( this.parentMenuItem ) { this.close(); } }; Menu.prototype.getMenuBar = function () { if ( this.parentMenu == null ) return null; return this.parentMenu.getMenuBar(); }; Menu.prototype.makeEventListeners = function () { if ( this.eventListeners != null ) return; this.eventListeners = { onscroll: new Function( "eventListeners.menu.onscroll(\"" + this.id + "\")" ), onmouseover: new Function( "eventListeners.menu.onmouseover(\"" + this.id + "\")" ), onmouseout: new Function( "eventListeners.menu.onmouseout(\"" + this.id + "\")" ), onmouseup: new Function( "eventListeners.menu.onmouseup(\"" + this.id + "\")" ), onmousewheel: new Function( "eventListeners.menu.onmousewheel(\"" + this.id + "\")" ), onreadystatechange: new Function( "eventListeners.menu.onreadystatechange(\"" + this.id + "\")" ), onkeydown: new Function( "eventListeners.menu.onkeydown(\"" + this.id + "\")" ), oncontextmenu: new Function( "eventListeners.menu.oncontextmenu(\"" + this.id + "\")" ), onunload: new Function( "eventListeners.menu.onunload(\"" + this.id + "\")" ) }; }; Menu.prototype.detachEvents = function () { if ( this.eventListeners == null ) return; var d = this.getDocument(); var w = d.parentWindow; var scrollContainer = d.getElementById("scroll-container"); scrollContainer.detachEvent( "onscroll", this.eventListeners.onscroll ); d.detachEvent( "onmouseover", this.eventListeners.onmouseover ); d.detachEvent( "onmouseout", this.eventListeners.onmouseout ); d.detachEvent( "onmouseup", this.eventListeners.onmouseup ); d.detachEvent( "onmousewheel", this.eventListeners.onmousewheel ); if (this.cssText == null) { var linkEl = d.getElementsByTagName("LINK")[0]; linkEl.detachEvent( "onreadystatechange", this.eventListeners.onreadystatechange ); } d.detachEvent( "onkeydown", this.eventListeners.onkeydown ); d.detachEvent( "oncontextmenu", this.eventListeners.oncontextmenu ); // prevent IE to keep menu open when navigating away window.detachEvent( "onunload", this.eventListeners.onunload ); } Menu.prototype.hookupMenu = function ( d ) { this.detachEvents(); this.makeEventListeners(); var oThis = this; var d = this.getDocument(); var w = d.parentWindow; var scrollContainer = d.getElementById("scroll-container"); // listen to the onscroll scrollContainer.attachEvent( "onscroll", this.eventListeners.onscroll ); d.attachEvent( "onmouseover", this.eventListeners.onmouseover ); d.attachEvent( "onmouseout", this.eventListeners.onmouseout ); d.attachEvent( "onmouseup", this.eventListeners.onmouseup ); d.attachEvent( "onmousewheel", this.eventListeners.onmousewheel ); // if css file is not loaded we need to wait for it to load. // Once loaded fix the size if (this.cssText == null) { var linkEl = d.getElementsByTagName("LINK")[0]; if ( linkEl.readyState != "complete") { linkEl.attachEvent( "onreadystatechange", this.eventListeners.onreadystatechange ); } } d.attachEvent( "onkeydown", this.eventListeners.onkeydown ); d.attachEvent( "oncontextmenu", this.eventListeners.oncontextmenu ); // prevent IE to keep menu open when navigating away window.attachEvent( "onunload", this.eventListeners.onunload ); var all = d.all; var l = all.length; for ( var i = 0; i < l; i++ ) all[i].unselectable = "on"; }; Menu.prototype.handleKeyEvent = function ( oEvent ) { if ( this.shownSubMenu ) // sub menu handles key event return; var nKeyCode = oEvent.keyCode; switch ( nKeyCode ) { case 40: // down this.goToNextMenuItem(); break; case 38: // up this.goToPreviousMenuItem(); break; case 39: // right this.goToNextMenu(); break; case 37: // left this.goToPreviousMenu(); break; case 13: // enter var mi = this.items[ this.getSelectedIndex() ]; if ( mi ) mi.dispatchAction(); break; case 27: // esc this.close(); // should close menu and go to parent menu item break; case Menu.keyboardAccelKey: case Menu.keyboardAccelKey2: this.closeAllMenus(); break; default: // find any mnemonic that matches var c = String.fromCharCode( nKeyCode ).toLowerCase(); var items = this.items; var l = items.length; for ( var i = 0; i < l; i++ ) { if ( items[i].mnemonic == c ) { items[i].dispatchAction(); break; } } } // cancel default action oEvent.returnValue = false; oEvent.keyCode = 0; }; // poll close state and when closed call _onclose Menu.prototype._startClosePoll = function () { var oThis = this; window.clearInterval( this._onCloseInterval ); this._onCloseInterval = window.setInterval( "eventListeners.menu.oncloseinterval(\"" + this.id + "\")", 100 ); }; Menu.prototype._checkCloseState = function () { var closed = this.popup == null || !this.popup.isOpen; if ( closed && this._closed != closed ) { this._closed = closed; this._closedAt = new Date().valueOf(); window.clearInterval( this._onCloseInterval ); if ( typeof this._onclose == "function" ) { var e = this.getDocument().parentWindow.event; if ( e != null && e.keyCode == 27 ) this._closeReason = "escape"; else this._closeReason = "unknown"; this._onclose(); } if ( typeof this.onclose == "function" ) this.onclose(); } }; Menu.prototype._isCssFileLoaded = function () { if (this.cssText != null) return true; var d = this.getMeasureDocument(); var l = d.getElementsByTagName("LINK")[0]; return l.readyState == "complete"; }; Menu.prototype.destroy = function () { var l = this.items.length; for ( var i = l -1; i >= 0; i-- ) this.items[i].destroy(); this.detachEvents(); this.items = []; this.parentMenu = null; this.parentMenuItem = null; this.shownSubMenu = null; this._cachedSizes = null; this.eventListeners = null; if ( this.popup != null ) { var d = this.popup.document; d.open("text/plain", "replace"); d.write(""); d.close(); this.popup = null; } if ( Menu._measureMenu == this ) { Menu._measureMenu = null; var d = Menu._measureFrame.contentWindow.document; d.open("text/plain", "replace"); d.write(""); d.close(); Menu._measureFrame.parentNode.removeChild(Menu._measureFrame); Menu._measureFrame = null; } menuCache.remove( this ); }; //////////////////////////////////////////////////////////////////////////////////// // MenuItem // function MenuItem( sLabelText, fAction, sIconSrc, oSubMenu ) { // public this.icon = sIconSrc || ""; this.text = sLabelText; this.action = fAction; this.subMenu = oSubMenu; this.parentMenu = null; // private this._selected = false; this._useInsets = true; // should insets be taken into account when showing sub menu this.id = menuCache.getId(); menuCache[ this.id ] = this; } MenuItem.prototype.subMenuDirection = "horizontal"; MenuItem.prototype.disabled = false; MenuItem.prototype.mnemonic = null; MenuItem.prototype.shortcut = null; MenuItem.prototype.toolTip = ""; MenuItem.prototype.target = null; MenuItem.prototype.visible = true; MenuItem.prototype.toHtml = function () { var cssClass = this.getCssClass(); var toolTip = this.getToolTip(); return "" + this.getIconCellHtml() + this.getTextCellHtml() + this.getShortcutCellHtml() + this.getSubMenuArrowCellHtml() + ""; }; MenuItem.prototype.getTextHtml = function () { var s = this.text; if ( !s || !this.mnemonic ) return s; // replace character with character // /^(((<([^>]|MNEMONIC)+>)|[^MNEMONIC])*)(MNEMONIC)/i var re = new RegExp( "^(((<([^>]|" + this.mnemonic + ")+>)|[^<" + this.mnemonic + "])*)(" + this.mnemonic + ")", "i" ); re.exec( s ); if ( RegExp.index != -1 && RegExp.$5 != "" ) return RegExp.$1 + "" + RegExp.$5 + "" + RegExp.rightContext; else return s; }; MenuItem.prototype.getIconHtml = function () { return this.icon != "" ? "" : " "; }; MenuItem.prototype.getTextCellHtml = function () { return "" + this.makeDisabledContainer( this.getTextHtml() ) + ""; }; MenuItem.prototype.getIconCellHtml = function () { return "" + this.makeDisabledContainer( this.getIconHtml() ) + ""; }; MenuItem.prototype.getCssClass = function () { if ( this.disabled && this._selected ) return "disabled-hover"; else if ( this.disabled ) return "disabled"; else if ( this._selected ) return "hover"; return ""; }; MenuItem.prototype.getToolTip = function () { return this.toolTip; }; MenuItem.prototype.getShortcutHtml = function () { if ( this.shortcut == null ) return " "; return this.shortcut; }; MenuItem.prototype.getShortcutCellHtml = function () { return "" + this.makeDisabledContainer( this.getShortcutHtml() ) + ""; }; MenuItem.prototype.getSubMenuArrowHtml = function () { if ( this.subMenu == null ) return " "; return 4; // right arrow using the marlett (or webdings) font }; MenuItem.prototype.getSubMenuArrowCellHtml = function () { return "" + this.makeDisabledContainer( this.getSubMenuArrowHtml() ) + ""; }; MenuItem.prototype.makeDisabledContainer = function ( s ) { if ( this.disabled ) return "" + s + ""; return s; }; MenuItem.prototype.dispatchAction = function () { if ( this.disabled ) return; this.setSelected( true ); if ( this.subMenu ) { if ( !this.subMenu.isShown() ) this.showSubMenu( false ); return; } if ( typeof this.action == "function" ) { this.setSelected( false ); this.parentMenu.closeAllMenus(); this.action(); } else if ( typeof this.action == "string" ) { // href this.setSelected( false ); this.parentMenu.closeAllMenus(); if ( this.target != null ) { var date = new Date(); var url = this.action; if (url.indexOf("?") > -1) { url = url + "&random="; } else { url = url + "?random="; } window.open( url + date.valueOf(), this.target ); } else document.location.href = this.action; } }; MenuItem.prototype.setSelected = function ( bSelected ) { if ( this._selected == bSelected ) return; this._selected = Boolean( bSelected ); var tr = this._htmlElement; if ( tr ) tr.className = this.getCssClass(); if ( !this._selected ) this.closeSubMenu( true ); var pm = this.parentMenu; if ( bSelected ) { pm.setSelectedIndex( this.itemIndex ); this.scrollIntoView(); // select item in parent menu as well if ( pm.parentMenuItem ) pm.parentMenuItem.setSelected( true ); } else pm.setSelectedIndex( -1 ); if ( this._selected ) { // clear timers for parent menu window.clearTimeout( pm._closeTimer ); } }; MenuItem.prototype.getSelected = function () { return this.itemIndex == this.parentMenu.selectedIndex; }; MenuItem.prototype.showSubMenu = function ( bDelayed ) { var sm = this.subMenu; var pm = this.parentMenu; if ( sm && !this.disabled ) { pm._aboutToShowSubMenu = true; window.clearTimeout( sm._showTimer ); window.clearTimeout( sm._closeTimer ); var showTimeout = bDelayed ? sm.showTimeout : 0; var oThis = this; sm._showTimer = window.setTimeout( "eventListeners.menuItem.onshowtimer(\"" + this.id + "\")", showTimeout ); } }; MenuItem.prototype.closeSubMenu = function ( bDelay ) { var sm = this.subMenu; if ( sm ) { window.clearTimeout( sm._showTimer ); window.clearTimeout( sm._closeTimer ); if ( sm.popup ) { if ( !bDelay ) sm.close(); else { var oThis = this; sm._closeTimer = window.setTimeout( "eventListeners.menuItem.onclosetimer(\"" + this.id + "\")", sm.closeTimeout ); } } } }; MenuItem.prototype.scrollIntoView = function () { if ( this.parentMenu._scrollingMode ) { var d = this.parentMenu.getDocument(); var sc = d.getElementById( "scroll-container" ); var scrollTop = sc.scrollTop; var clientHeight = sc.clientHeight; var offsetTop = this._htmlElement.offsetTop; var offsetHeight = this._htmlElement.offsetHeight; if ( offsetTop < scrollTop ) sc.scrollTop = offsetTop; else if ( offsetTop + offsetHeight > scrollTop + clientHeight ) sc.scrollTop = offsetTop + offsetHeight - clientHeight; } }; MenuItem.prototype.positionSubMenu = function () { var dir = this.subMenuDirection; var el = this._htmlElement; var useInsets = this._useInsets; var sm = this.subMenu; var oThis = this; if ( !sm._isCssFileLoaded() ) { window.setTimeout( "eventListeners.menuItem.onpositionsubmenutimer(\"" + this.id + "\")", 1 ); return; } // find parent item rectangle var rect = { left: posLib.getScreenLeft( el ), top: posLib.getScreenTop( el ), width: el.offsetWidth, height: el.offsetHeight }; var menuRect = { left: sm.getLeft(), top: sm.getTop(), width: sm.getPreferredWidth(), height: sm.getPreferredHeight(), insetLeft: useInsets ? sm.getInsetLeft() : 0, insetRight: useInsets ? sm.getInsetRight() : 0, insetTop: useInsets ? sm.getInsetTop() : 0, insetBottom: useInsets ? sm.getInsetBottom() : 0 }; // work around for buggy graphics drivers that screw up the screen.left var screenWidth = screen.width; var screenHeight = screen.height; while ( rect.left > screenWidth ) screenWidth += screen.width; while ( rect.top > screenHeight ) screenHeight += screen.height; var left, top, width = menuRect.width, height = menuRect.height; if ( dir == "vertical" ) { if ( rect.left + menuRect.width - menuRect.insetLeft <= screenWidth ) left = rect.left - menuRect.insetLeft; else if ( screenWidth >= menuRect.width ) left = screenWidth - menuRect.width; else left = 0; if ( rect.top + rect.height + menuRect.height - menuRect.insetTop <= screenHeight ) top = rect.top + rect.height - menuRect.insetTop; else if ( rect.top - menuRect.height + menuRect.insetBottom >= 0 ) top = rect.top - menuRect.height + menuRect.insetBottom; else { // use largest and resize var sizeAbove = rect.top + menuRect.insetBottom; var sizeBelow = screenHeight - rect.top - rect.height + menuRect.insetTop; if ( sizeBelow >= sizeAbove ) { top = rect.top + rect.height - menuRect.insetTop; height = sizeBelow; } else { top = 0; height = sizeAbove; } } } else { if ( rect.top + menuRect.height - menuRect.insetTop <= screenHeight ) top = rect.top - menuRect.insetTop; else if ( rect.top + rect.height - menuRect.height + menuRect.insetBottom >= 0) top = rect.top + rect.height - menuRect.height + menuRect.insetBottom; else if ( screenHeight >= menuRect.height ) top = screenHeight - menuRect.height; else { top = 0; height = screenHeight } if ( rect.left + rect.width + menuRect.width - menuRect.insetLeft <= screenWidth ) left = rect.left + rect.width - menuRect.insetLeft; else if ( rect.left - menuRect.width + menuRect.insetRight >= 0 ) left = rect.left - menuRect.width + menuRect.insetRight; else if ( screenWidth >= menuRect.width ) left = screenWidth - menuRect.width; else left = 0; } var scrollBefore = sm._scrollingMode; sm.show( left, top, width, height ); if ( sm._scrollingMode != scrollBefore ) this.positionSubMenu(); }; MenuItem.prototype.destroy = function () { if ( this.subMenu != null ) this.subMenu.destroy(); this.subMenu = null; this.parentMenu = null; var el = this._htmlElement if ( el != null ) el._menuItem = null; this._htmlElement = null; menuCache.remove( this ); }; /////////////////////////////////////////////////////////////////////////////// // CheckBoxMenuItem extends MenuItem // function CheckBoxMenuItem( sLabelText, bChecked, fAction, oSubMenu ) { this.MenuItem = MenuItem; this.MenuItem( sLabelText, fAction, null, oSubMenu); // public this.checked = bChecked; } CheckBoxMenuItem.prototype = new MenuItem; CheckBoxMenuItem.prototype.getIconHtml = function () { return "" + (this.checked ? "a" : " ") + ""; }; CheckBoxMenuItem.prototype.getIconCellHtml = function () { return "" + this.makeDisabledContainer( this.getIconHtml() ) + ""; }; CheckBoxMenuItem.prototype.getCssClass = function () { var s = (this.checked ? " checked" : ""); if ( this.disabled && this._selected ) return "disabled-hover" + s; else if ( this.disabled ) return "disabled" + s; else if ( this._selected ) return "hover" + s; return s; }; CheckBoxMenuItem.prototype._menuItem_dispatchAction = MenuItem.prototype.dispatchAction; CheckBoxMenuItem.prototype.dispatchAction = function () { if (!this.disabled) { this.checked = !this.checked; this._menuItem_dispatchAction(); this.parentMenu.invalidate(); this.parentMenu.closeAllMenus(); } }; /////////////////////////////////////////////////////////////////////////////// // RadioButtonMenuItem extends MenuItem // function RadioButtonMenuItem( sLabelText, bChecked, sRadioGroupName, fAction, oSubMenu ) { this.MenuItem = MenuItem; this.MenuItem( sLabelText, fAction, null, oSubMenu ); // public this.checked = bChecked; this.radioGroupName = sRadioGroupName; } RadioButtonMenuItem.prototype = new MenuItem; RadioButtonMenuItem.prototype.getIconHtml = function () { return "" + (this.checked ? "n" : " ") + ""; }; RadioButtonMenuItem.prototype.getIconCellHtml = function () { return "" + this.makeDisabledContainer( this.getIconHtml() ) + ""; }; RadioButtonMenuItem.prototype.getCssClass = function () { var s = (this.checked ? " checked" : ""); if ( this.disabled && this._selected ) return "disabled-hover" + s; else if ( this.disabled ) return "disabled" + s; else if ( this._selected ) return "hover" + s; return s; }; RadioButtonMenuItem.prototype._menuItem_dispatchAction = MenuItem.prototype.dispatchAction; RadioButtonMenuItem.prototype.dispatchAction = function () { if (!this.disabled) { if ( !this.checked ) { // loop through items in parent menu var items = this.parentMenu.items; var l = items.length; for ( var i = 0; i < l; i++ ) { if ( items[i] instanceof RadioButtonMenuItem ) { if ( items[i].radioGroupName == this.radioGroupName ) { items[i].checked = items[i] == this; } } } this.parentMenu.invalidate(); } this._menuItem_dispatchAction(); this.parentMenu.closeAllMenus(); } }; /////////////////////////////////////////////////////////////////////////////// // MenuSeparator extends MenuItem // function MenuSeparator() { this.MenuItem = MenuItem; this.MenuItem(); } MenuSeparator.prototype = new MenuItem; MenuSeparator.prototype.toHtml = function () { return "" + "
" + ""; }; MenuSeparator.prototype.getCssClass = function () { return "separator"; }; //////////////////////////////////////////////////////////////////////////////////// // MenuBar extends Menu // function MenuBar() { this.items = []; this.parentMenu = null; this.parentMenuItem = null; this.shownSubMenu = null; this._aboutToShowSubMenu = false; this.active = false; this.id = menuCache.getId(); menuCache[ this.id ] = this; } MenuBar.prototype = new Menu; MenuBar.prototype._document = null; MenuBar.leftMouseButton = 1; MenuBar.prototype.toHtml = function () { var items = this.items; var l = items.length; var itemsHtml = new Array( l ); for (var i = 0; i < l; i++ ) itemsHtml[i] = items[i].toHtml(); return "
" + itemsHtml.join( "" ) + "
"; }; MenuBar.prototype.invalidate = function () { if (this._htmlElement) { this.detachEvents(); var oldEl = this._htmlElement; var newEl = this.create(this._document); oldEl.parentNode.replaceChild(newEl, oldEl); } }; MenuBar.prototype.createPopup = function () {}; MenuBar.prototype.getPopup= function () {}; MenuBar.prototype.drawMenu = function () {}; MenuBar.prototype.getDocument = function () { return this._document; }; MenuBar.prototype.show = function ( left, top, w, h ) {}; MenuBar.prototype.isShown = function () { return true; }; MenuBar.prototype.fixSize = function () {} MenuBar.prototype.getWidth = function () { return this._htmlElement.offsetWidth; }; MenuBar.prototype.getHeight = function () { return this._htmlElement.offsetHeight; }; MenuBar.prototype.getPreferredWidth = function () { var el = this._htmlElement; el.runtimStyle.whiteSpace = "nowrap"; var sw = el.scrollWidth; el.runtimStyle.whiteSpace = ""; return sw + parseInt( el.currentStyle.borderLeftWidth ) + parseInt( el.currentStyle.borderRightWidth ); }; MenuBar.prototype.getPreferredHeight = function () { var el = this._htmlElement; el.runtimStyle.whiteSpace = "nowrap"; var sw = el.scrollHeight; el.runtimStyle.whiteSpace = ""; return sw + parseInt( el.currentStyle.borderTopWidth ) + parseInt( el.currentStyle.borderBottomWidth ); }; MenuBar.prototype.getLeft = function () { return posLib.getScreenLeft( this._htmlElement ); }; MenuBar.prototype.getTop = function () { return posLib.getScreenLeft( this._htmlElement ); }; MenuBar.prototype.setLeft = function ( l ) {}; MenuBar.prototype.setTop = function ( t ) {}; MenuBar.prototype.setLocation = function ( l, t ) {}; MenuBar.prototype.setRect = function ( l, t, w, h ) {}; MenuBar.prototype.getInsetLeft = function () { return parseInt( this._htmlElement.currentStyle.borderLeftWidth ); }; MenuBar.prototype.getInsetRight = function () { return parseInt( this._htmlElement.currentStyle.borderRightWidth ); }; MenuBar.prototype.getInsetTop = function () { return parseInt( this._htmlElement.currentStyle.borderTopWidth ); }; MenuBar.prototype.getInsetBottom = function () { return parseInt( this._htmlElement.currentStyle.borderBottomWidth ); }; MenuBar.prototype.fixScrollButtons = function () {}; MenuBar.prototype.fixScrollEnabledState = function () {}; MenuBar.prototype.makeEventListeners = function () { if ( this.eventListeners != null ) return; this.eventListeners = { onmouseover: new Function( "eventListeners.menuBar.onmouseover(\"" + this.id + "\")" ), onmouseout: new Function( "eventListeners.menuBar.onmouseout(\"" + this.id + "\")" ), onmousedown: new Function( "eventListeners.menuBar.onmousedown(\"" + this.id + "\")" ), onkeydown: new Function( "eventListeners.menuBar.onkeydown(\"" + this.id + "\")" ), onunload: new Function( "eventListeners.menuBar.onunload(\"" + this.id + "\")" ) }; }; MenuBar.prototype.detachEvents = function () { if ( this.eventListeners == null ) return; this._htmlElement.detachEvent( "onmouseover", this.eventListeners.onmouseover ); this._htmlElement.detachEvent( "onmouseout", this.eventListeners.onmouseout ); this._htmlElement.detachEvent( "onmousedown", this.eventListeners.onmousedown ); this._document.detachEvent( "onkeydown", this.eventListeners.onkeydown ); window.detachEvent( "onunload", this.eventListeners.onunload ); } MenuBar.prototype.hookupMenu = function ( element ) { if ( !this._document ) this._document = element.document; this.detachEvents(); this.makeEventListeners(); // create shortcut to html element this._htmlElement = element; element.unselectable = "on"; // and same for menu buttons var cs = element.childNodes; var items = this.items; var l = cs.length; for ( var i = 0; i < l; i++ ) { items[i]._htmlElement = cs[i]; cs[i]._menuItem = items[i]; } // hook up events element.attachEvent( "onmouseover", this.eventListeners.onmouseover ); element.attachEvent( "onmouseout", this.eventListeners.onmouseout ); element.attachEvent( "onmousedown", this.eventListeners.onmousedown ); this._document.attachEvent( "onkeydown", this.eventListeners.onkeydown ); window.attachEvent( "onunload", this.eventListeners.onunload ); }; function getMenuItemElement( el ) { while ( el != null && el._menuItem == null) el = el.parentNode; return el; } function getTrElement( el ) { while ( el != null && el.tagName != "TR" ) el = el.parentNode; return el; } MenuBar.prototype.write = function (oDocument) { this._document = oDocument || document; this._document.write( this.toHtml() ); var el = this._document.getElementById( this.id ); this.hookupMenu( el ); }; MenuBar.prototype.create = function (oDocument) { this._document = oDocument || document; var dummyDiv = this._document.createElement( "DIV" ); dummyDiv.innerHTML = this.toHtml(); var el = dummyDiv.removeChild( dummyDiv.firstChild ); this.hookupMenu( el ); return el; }; MenuBar.prototype.handleKeyEvent = function ( e ) { if ( this.getActiveState() == "open" ) return; var nKeyCode = e.keyCode; if ( this.active && e[ Menu.keyboardAccelProperty ] ) { e.returnValue = false; e.keyCode = 0; } if ( nKeyCode == Menu.keyboardAccelKey || nKeyCode == Menu.keyboardAccelKey2 ) { if ( !e.repeat ) { this.toggleActive(); } e.returnValue = false; e.keyCode = 0; return; } if ( !this.active ) { // do not set return value to true return; } switch ( nKeyCode ) { case 39: // right this.goToNextMenuItem(); e.returnValue = false; break; case 37: // left this.goToPreviousMenuItem(); e.returnValue = false; break; case 40: // down case 38: // up case 13: // enter var mi = this.items[ this.getSelectedIndex() ]; if ( mi ) { mi.dispatchAction(); if ( mi.subMenu ) mi.subMenu.setSelectedIndex( 0 ); } e.returnValue = false; break; case 27: // esc // we need to make sure that the menu bar looses its current // keyboard activation state this.setActive( false ); e.returnValue = false; break; default: // find any mnemonic that matches var c = String.fromCharCode( nKeyCode ).toLowerCase(); var items = this.items; var l = items.length; for ( var i = 0; i < l; i++ ) { if ( items[i].mnemonic == c ) { items[i].dispatchAction(); e.returnValue = false; break; } } } }; MenuBar.prototype.getMenuBar = function () { return this; }; MenuBar.prototype._menu_goToNextMenuItem = Menu.prototype.goToNextMenuItem; MenuBar.prototype.goToNextMenuItem = function () { var expand = this.getActiveState() == "open"; this._menu_goToNextMenuItem(); var mi = this.items[ this.getSelectedIndex() ]; if ( expand && mi != null ) { window.setTimeout( "eventListeners.menuBar.ongotonextmenuitem(\"" + this.id + "\")", 1 ); } }; MenuBar.prototype._menu_goToPreviousMenuItem = Menu.prototype.goToPreviousMenuItem; MenuBar.prototype.goToPreviousMenuItem = function () { var expand = this.getActiveState() == "open"; this._menu_goToPreviousMenuItem(); var mi = this.items[ this.getSelectedIndex() ]; if ( expand && mi != null ) { window.setTimeout( "eventListeners.menuBar.ongotopreviousmenuitem(\"" + this.id + "\")", 1 ); } }; MenuBar.prototype._menu_setSelectedIndex = Menu.prototype.setSelectedIndex; MenuBar.prototype.setSelectedIndex = function ( nIndex ) { this._menu_setSelectedIndex( nIndex ); this.active = nIndex != -1; }; MenuBar.prototype.setActive = function ( bActive ) { if ( this.active != bActive ) { this.active = Boolean( bActive ); if ( this.active ) { this.setSelectedIndex( 0 ); this.backupFocused(); window.focus(); } else { this.setSelectedIndex( -1 ); this.restoreFocused(); } } }; MenuBar.prototype.toggleActive = function () { if ( this.getActiveState() == "active" ) this.setActive( false ); else if ( this.getActiveState() == "inactive" ) this.setActive( true ); }; // returns active, inactive or open MenuBar.prototype.getActiveState = function () { if ( this.shownSubMenu != null || this._aboutToShowSubMenu) return "open"; else if ( this.active ) return "active"; else return "inactive"; }; MenuBar.prototype.backupFocused = function () { this._activeElement = this._document.activeElement; }; MenuBar.prototype.restoreFocused = function () { try { this._activeElement.focus(); } catch (ex) {} delete this._activeElement; }; MenuBar.prototype.destroy = function () { var l = this.items.length; for ( var i = l -1; i >= 0; i-- ) this.items[i].destroy(); this.detachEvents(); this._activeElement = null; this._htmlElement = null; this._document = null; this.items = []; this.shownSubMenu = null; this.eventListeners = null; menuCache.remove( this ); }; //////////////////////////////////////////////////////////////////////////////////// // MenuButton extends MenuItem // function MenuButton( sLabelText, oSubMenu ) { this.MenuItem = MenuItem; this.MenuItem( sLabelText, null, null, oSubMenu ); // private this._hover = false; this._useInsets = false; // should insets be taken into account when showing sub menu } MenuButton.prototype = new MenuItem; MenuButton.prototype.subMenuDirection = "vertical"; MenuButton.prototype.scrollIntoView = function () {}; MenuButton.prototype.toHtml = function () { var cssClass = this.getCssClass(); var toolTip = this.getToolTip(); if ( this.subMenu && !this.subMenu._onclose ) this.subMenu._onclose = new Function( "eventListeners.menuButton.onclose(\"" + this.id + "\")" ); return "" + "" + this.getTextHtml() + "" + "" + ""; }; MenuButton.prototype.getCssClass = function () { if ( this.disabled && this._selected ) return "menu-button disabled-hover"; else if ( this.disabled ) return "menu-button disabled"; else if ( this._selected ) { if ( this.parentMenu.getActiveState() == "open" ) { return "menu-button active"; } else return "menu-button hover"; } else if ( this._hover ) return "menu-button hover"; return "menu-button "; }; MenuButton.prototype.subMenuClosed = function () { if ( this.subMenu._closeReason == "escape" ) this.setSelected( true ); else this.setSelected( false ); if ( this.parentMenu.getActiveState() == "inactive" ) this.parentMenu.restoreFocused(); }; MenuButton.prototype.setSelected = function ( bSelected ) { var oldSelected = this._selected; this._selected = Boolean( bSelected ); var tr = this._htmlElement; if ( tr ) tr.className = this.getCssClass(); if ( this._selected == oldSelected ) return; if ( !this._selected ) this.closeSubMenu( true ); if ( bSelected ) { this.parentMenu.setSelectedIndex( this.itemIndex ); this.scrollIntoView(); } else this.parentMenu.setSelectedIndex( -1 ); }; //////////////////////////////////////////////////////////////////////////////////// // event listener // var eventListeners = { menu: { onkeydown: function ( id ) { var oThis = menuCache[id]; var w = oThis.getDocument().parentWindow; oThis.handleKeyEvent( w.event ); }, onunload: function ( id ) { if (id in menuCache) { menuCache[id].closeAllMenus(); menuCache[id].destroy(); } // else already destroyed }, oncontextmenu: function ( id ) { var oThis = menuCache[id]; var w = oThis.getDocument().parentWindow; w.event.returnValue = false; }, onscroll: function ( id ) { menuCache[id].fixScrollEnabledState(); }, onmouseover: function ( id ) { var oThis = menuCache[id]; var w = oThis.getDocument().parentWindow; var fromEl = getTrElement( w.event.fromElement ); var toEl = getTrElement( w.event.toElement ); if ( toEl != null && toEl != fromEl ) { var mi = toEl._menuItem; if ( mi ) { if ( !mi.disabled || oThis.mouseHoverDisabled ) { mi.setSelected( true ); mi.showSubMenu( true ); } } else { // scroll button if (toEl.className == "disabled" || toEl.className == "disabled-hover" ) toEl.className = "disabled-hover"; else toEl.className = "hover"; oThis.selectedIndex = -1; } } }, onmouseout: function ( id ) { var oThis = menuCache[id]; var w = oThis.getDocument().parentWindow; var fromEl = getTrElement( w.event.fromElement ); var toEl = getTrElement( w.event.toElement ); if ( fromEl != null && toEl != fromEl ) { var id = fromEl.parentNode.parentNode.id; var mi = fromEl._menuItem; if ( id == "scroll-up-item" || id == "scroll-down-item" ) { if (fromEl.className == "disabled-hover" || fromEl.className == "disabled" ) fromEl.className = "disabled"; else fromEl.className = ""; oThis.selectedIndex = -1; } else if ( mi && ( toEl != null || mi.subMenu == null || mi.disabled ) ) { mi.setSelected( false ); } } }, onmouseup: function ( id ) { var oThis = menuCache[id]; var w = oThis.getDocument().parentWindow; var srcEl = getMenuItemElement( w.event.srcElement ); if ( srcEl != null ) { var id = srcEl.parentNode.parentNode.id; if ( id == "scroll-up-item" || id == "scroll-down-item" ) { return; } oThis.selectedIndex = srcEl.rowIndex; var menuItem = oThis.items[ oThis.selectedIndex ]; menuItem.dispatchAction(); } }, onmousewheel: function ( id ) { var oThis = menuCache[id]; var d = oThis.getDocument(); var w = d.parentWindow; var scrollContainer = d.getElementById("scroll-container"); scrollContainer.scrollTop -= 3 * w.event.wheelDelta / 120 * ScrollButton.scrollAmount; }, onreadystatechange: function ( id ) { var oThis = menuCache[id]; var d = oThis.getDocument(); var linkEl = d.getElementsByTagName("LINK")[0]; if ( linkEl.readyState == "complete" ) { oThis.resetSizeCache(); // reset sizes oThis.fixSize(); oThis.fixScrollButtons(); } }, oncloseinterval: function ( id ) { menuCache[id]._checkCloseState(); } }, menuItem: { onshowtimer: function ( id ) { var oThis = menuCache[id]; var sm = oThis.subMenu; var pm = oThis.parentMenu; var selectedIndex = sm.getSelectedIndex(); pm.closeAllSubs( sm ); window.setTimeout( "eventListeners.menuItem.onshowtimer2(\"" + id + "\")", 1); }, onshowtimer2: function ( id ) { var oThis = menuCache[id]; var sm = oThis.subMenu; var selectedIndex = sm.getSelectedIndex(); oThis.positionSubMenu(); sm.setSelectedIndex( selectedIndex ); oThis.setSelected( true ); }, onclosetimer: function ( id ) { var oThis = menuCache[id]; var sm = oThis.subMenu; sm.close(); }, onpositionsubmenutimer: function ( id ) { var oThis = menuCache[id]; var sm = oThis.subMenu; sm.resetSizeCache(); // reset sizes oThis.positionSubMenu(); sm.setSelectedIndex( 0 ); } }, menuBar: { onmouseover: function ( id ) { var oThis = menuCache[id]; var e = oThis.getDocument().parentWindow.event; var fromEl = getMenuItemElement( e.fromElement ); var toEl = getMenuItemElement( e.toElement ); if ( toEl != null && toEl != fromEl ) { var mb = toEl._menuItem; var m = mb.parentMenu; if ( m.getActiveState() == "open" ) { window.setTimeout( function () { mb.dispatchAction(); }, 1); } else if ( m.getActiveState() == "active" ) { mb.setSelected( true ); } else { mb._hover = true; toEl.className = mb.getCssClass(); } } }, onmouseout: function ( id ) { var oThis = menuCache[id]; var e = oThis.getDocument().parentWindow.event; var fromEl = getMenuItemElement( e.fromElement ); var toEl = getMenuItemElement( e.toElement ); if ( fromEl != null && toEl != fromEl ) { var mb = fromEl._menuItem; mb._hover = false; fromEl.className = mb.getCssClass(); } }, onmousedown: function ( id ) { var oThis = menuCache[id]; var e = oThis.getDocument().parentWindow.event; if ( e.button != MenuBar.leftMouseButton ) return; var el = getMenuItemElement( e.srcElement ); if ( el != null ) { var mb = el._menuItem; if ( mb.subMenu ) { mb.subMenu._checkCloseState(); if ( new Date() - mb.subMenu._closedAt > 100 ) { // longer than the time to // do the hide mb.dispatchAction(); } else { mb._hover = true; mb._htmlElement.className = mb.getCssClass(); } } } }, onkeydown: function ( id ) { var oThis = menuCache[id]; var e = oThis.getDocument().parentWindow.event; oThis.handleKeyEvent( e ); }, onunload: function ( id ) { menuCache[id].destroy(); }, ongotonextmenuitem: function ( id ) { var oThis = menuCache[id]; var mi = oThis.items[ oThis.getSelectedIndex() ]; mi.dispatchAction(); if ( mi.subMenu ) mi.subMenu.setSelectedIndex( 0 ); }, ongotopreviousmenuitem: function ( id ) { var oThis = menuCache[id]; var mi = oThis.items[ oThis.getSelectedIndex() ]; mi.dispatchAction(); if ( mi.subMenu ) mi.subMenu.setSelectedIndex( 0 ); } }, menuButton: { onclose: function ( id ) { menuCache[id].subMenuClosed(); } } };