3 * This script was created by Erik Arvidsson (erik@eae.net)
4 * for WebFX (http://webfx.eae.net)
7 * For usage see license at http://webfx.eae.net/license.html
10 * Updates: 2001-11-20 Added hover mode support and removed Opera focus hacks
11 * 2001-12-20 Added auto positioning and some properties to support this
12 * 2002-08-13 toString used ' for attributes. Changed to " to allow in args
16 var ua = navigator.userAgent;
17 // "window.opera" exists in Opera 10+, and needs to be preserved for FCKeditor
19 var opera = window.opera || /opera [56789]|opera\/[56789]/i.test(ua);
20 var ie = !opera && /MSIE/.test(ua);
21 var ie50 = ie && /MSIE 5\.[01234]/.test(ua);
22 var ie6 = ie && /MSIE [6789]/.test(ua);
23 var ieBox = ie && (document.compatMode == null || document.compatMode != "CSS1Compat");
24 var moz = !opera && /gecko/i.test(ua);
25 var nn6 = !opera && /netscape.*6\./i.test(ua);
26 var khtml = /KHTML/i.test(ua);
28 // define the default values
30 webfxMenuDefaultWidth = 154;
32 webfxMenuDefaultBorderLeft = 1;
33 webfxMenuDefaultBorderRight = 1;
34 webfxMenuDefaultBorderTop = 1;
35 webfxMenuDefaultBorderBottom = 1;
37 webfxMenuDefaultPaddingLeft = 1;
38 webfxMenuDefaultPaddingRight = 1;
39 webfxMenuDefaultPaddingTop = 1;
40 webfxMenuDefaultPaddingBottom = 1;
42 webfxMenuDefaultShadowLeft = 0;
43 webfxMenuDefaultShadowRight = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 :0;
44 webfxMenuDefaultShadowTop = 0;
45 webfxMenuDefaultShadowBottom = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 : 0;
48 webfxMenuItemDefaultHeight = 18;
49 webfxMenuItemDefaultText = "Untitled";
50 webfxMenuItemDefaultHref = "javascript:void(0)";
52 webfxMenuSeparatorDefaultHeight = 6;
54 webfxMenuDefaultEmptyText = "Empty";
56 webfxMenuDefaultUseAutoPosition = nn6 ? false : true;
60 // other global constants
62 webfxMenuImagePath = "";
64 webfxMenuUseHover = opera ? true : false;
65 webfxMenuHideTime = 500;
66 webfxMenuShowTime = 200;
70 var webFXMenuHandler = {
72 idPrefix : "webfx-menu-object-",
74 getId : function () { return this.idPrefix + this.idCounter++; },
75 overMenuItem : function (oItem) {
76 if (this.showTimeout != null)
77 window.clearTimeout(this.showTimeout);
78 if (this.hideTimeout != null)
79 window.clearTimeout(this.hideTimeout);
80 var jsItem = this.all[oItem.id];
81 if (webfxMenuShowTime <= 0)
84 //this.showTimeout = window.setTimeout(function () { webFXMenuHandler._over(jsItem) ; }, webfxMenuShowTime);
85 // I hate IE5.0 because the piece of shit crashes when using setTimeout with a function object
86 this.showTimeout = window.setTimeout("webFXMenuHandler._over(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuShowTime);
88 outMenuItem : function (oItem) {
89 if (this.showTimeout != null)
90 window.clearTimeout(this.showTimeout);
91 if (this.hideTimeout != null)
92 window.clearTimeout(this.hideTimeout);
93 var jsItem = this.all[oItem.id];
94 if (webfxMenuHideTime <= 0)
97 //this.hideTimeout = window.setTimeout(function () { webFXMenuHandler._out(jsItem) ; }, webfxMenuHideTime);
98 this.hideTimeout = window.setTimeout("webFXMenuHandler._out(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuHideTime);
100 blurMenu : function (oMenuItem) {
101 window.setTimeout("webFXMenuHandler.all[\"" + oMenuItem.id + "\"].subMenu.hide();", webfxMenuHideTime);
103 _over : function (jsItem) {
104 if (jsItem.subMenu) {
105 jsItem.parentMenu.hideAllSubs();
106 jsItem.subMenu.show();
109 jsItem.parentMenu.hideAllSubs();
111 _out : function (jsItem) {
112 // find top most menu
115 if (root instanceof WebFXMenuButton)
118 m = jsItem.parentMenu;
119 while (m.parentMenu != null && !(m.parentMenu instanceof WebFXMenuBar))
125 hideMenu : function (menu) {
126 if (this.showTimeout != null)
127 window.clearTimeout(this.showTimeout);
128 if (this.hideTimeout != null)
129 window.clearTimeout(this.hideTimeout);
131 this.hideTimeout = window.setTimeout("webFXMenuHandler.all['" + menu.id + "'].hide()", webfxMenuHideTime);
133 showMenu : function (menu, src, dir) {
134 if (this.showTimeout != null)
135 window.clearTimeout(this.showTimeout);
136 if (this.hideTimeout != null)
137 window.clearTimeout(this.hideTimeout);
139 if (arguments.length < 3)
146 function WebFXMenu() {
147 this._menuItems = [];
149 this.id = webFXMenuHandler.getId();
153 this.parentMenu = null;
154 webFXMenuHandler.all[this.id] = this;
157 WebFXMenu.prototype.width = webfxMenuDefaultWidth;
158 WebFXMenu.prototype.emptyText = webfxMenuDefaultEmptyText;
159 WebFXMenu.prototype.useAutoPosition = webfxMenuDefaultUseAutoPosition;
161 WebFXMenu.prototype.borderLeft = webfxMenuDefaultBorderLeft;
162 WebFXMenu.prototype.borderRight = webfxMenuDefaultBorderRight;
163 WebFXMenu.prototype.borderTop = webfxMenuDefaultBorderTop;
164 WebFXMenu.prototype.borderBottom = webfxMenuDefaultBorderBottom;
166 WebFXMenu.prototype.paddingLeft = webfxMenuDefaultPaddingLeft;
167 WebFXMenu.prototype.paddingRight = webfxMenuDefaultPaddingRight;
168 WebFXMenu.prototype.paddingTop = webfxMenuDefaultPaddingTop;
169 WebFXMenu.prototype.paddingBottom = webfxMenuDefaultPaddingBottom;
171 WebFXMenu.prototype.shadowLeft = webfxMenuDefaultShadowLeft;
172 WebFXMenu.prototype.shadowRight = webfxMenuDefaultShadowRight;
173 WebFXMenu.prototype.shadowTop = webfxMenuDefaultShadowTop;
174 WebFXMenu.prototype.shadowBottom = webfxMenuDefaultShadowBottom;
178 WebFXMenu.prototype.add = function (menuItem) {
179 this._menuItems[this._menuItems.length] = menuItem;
180 if (menuItem.subMenu) {
181 this._subMenus[this._subMenus.length] = menuItem.subMenu;
182 menuItem.subMenu.parentMenu = this;
185 menuItem.parentMenu = this;
188 WebFXMenu.prototype.show = function (relObj, sDir) {
189 if (this.useAutoPosition)
190 this.position(relObj, sDir);
192 var divElement = document.getElementById(this.id);
195 //divElement.style.left = opera ? this.left : this.left + "px";
196 //divElement.style.top = opera ? this.top : this.top + "px";
197 divElement.style.left = this.left + "px";
198 divElement.style.top = this.top + "px";
199 divElement.style.visibility = "visible";
202 var shimElement = document.getElementById(this.id + "Shim");
204 shimElement.style.width = divElement.offsetWidth;
205 shimElement.style.height = divElement.offsetHeight;
206 shimElement.style.top = divElement.style.top;
207 shimElement.style.left = divElement.style.left;
208 /*shimElement.style.zIndex = divElement.style.zIndex - 1; */
209 shimElement.style.display = "block";
210 shimElement.style.filter='progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
219 this.parentMenu.show();
222 WebFXMenu.prototype.hide = function () {
224 var divElement = document.getElementById(this.id);
226 divElement.style.visibility = "hidden";
228 var shimElement = document.getElementById(this.id + "Shim");
230 shimElement.style.display = "none";
238 WebFXMenu.prototype.hideAllSubs = function () {
239 for (var i = 0; i < this._subMenus.length; i++) {
240 if (this._subMenus[i].shown)
241 this._subMenus[i].hide();
245 WebFXMenu.prototype.toString = function () {
246 var top = this.top + this.borderTop + this.paddingTop;
247 var str = "<div id='" + this.id + "' class='webfx-menu' style='" +
249 this.width - this.borderLeft - this.paddingLeft - this.borderRight - this.paddingRight :
250 this.width) + "px;" +
251 (this.useAutoPosition ?
252 "left:" + this.left + "px;" + "top:" + this.top + "px;" :
254 (ie50 ? "filter: none;" : "") +
257 if (this._menuItems.length == 0) {
258 str += "<span class='webfx-menu-empty'>" + this.emptyText + "</span>";
261 str += '<span class="webfx-menu-title" onmouseover="webFXMenuHandler.overMenuItem(this)"' +
262 (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") +
263 '>' + this.emptyText + '</span>';
264 // str += '<div id="' + this.id + '-title">' + this.emptyText + '</div>';
265 // loop through all menuItems
266 for (var i = 0; i < this._menuItems.length; i++) {
267 var mi = this._menuItems[i];
269 if (!this.useAutoPosition) {
270 if (mi.subMenu && !mi.subMenu.useAutoPosition)
271 mi.subMenu.top = top - mi.subMenu.borderTop - mi.subMenu.paddingTop;
281 str += "<iframe id='" + this.id + "Shim' src='javascript:false;' scrolling='no' frameBorder='0' style='position:absolute; top:0px; left: 0px; display:none;'></iframe>";
284 for (var i = 0; i < this._subMenus.length; i++) {
285 this._subMenus[i].left = this.left + this.width - this._subMenus[i].borderLeft;
286 str += this._subMenus[i];
291 // WebFXMenu.prototype.position defined later
293 function WebFXMenuItem(sText, sHref, sToolTip, oSubMenu) {
294 this.text = sText || webfxMenuItemDefaultText;
295 this.href = (sHref == null || sHref == "") ? webfxMenuItemDefaultHref : sHref;
296 this.subMenu = oSubMenu;
298 oSubMenu.parentMenuItem = this;
299 this.toolTip = sToolTip;
300 this.id = webFXMenuHandler.getId();
301 webFXMenuHandler.all[this.id] = this;
303 WebFXMenuItem.prototype.height = webfxMenuItemDefaultHeight;
304 WebFXMenuItem.prototype.toString = function () {
306 " id='" + this.id + "'" +
307 " href=\"" + this.href + "\"" +
308 (this.toolTip ? " title=\"" + this.toolTip + "\"" : "") +
309 " onmouseover='webFXMenuHandler.overMenuItem(this)'" +
310 (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") +
311 (this.subMenu ? " unselectable='on' tabindex='-1'" : "") +
313 (this.subMenu ? "<img class='arrow' src=\"" + webfxMenuImagePath + "arrow.right.black.png\">" : "") +
319 function WebFXMenuSeparator() {
320 this.id = webFXMenuHandler.getId();
321 webFXMenuHandler.all[this.id] = this;
323 WebFXMenuSeparator.prototype.height = webfxMenuSeparatorDefaultHeight;
324 WebFXMenuSeparator.prototype.toString = function () {
326 " id='" + this.id + "'" +
328 " onmouseover='webFXMenuHandler.overMenuItem(this)'" +
329 " onmouseout='webFXMenuHandler.outMenuItem(this)'"
335 function WebFXMenuBar() {
336 this._parentConstructor = WebFXMenu;
337 this._parentConstructor();
339 WebFXMenuBar.prototype = new WebFXMenu;
340 WebFXMenuBar.prototype.toString = function () {
341 var str = "<div id='" + this.id + "' class='webfx-menu-bar'>";
343 // loop through all menuButtons
344 for (var i = 0; i < this._menuItems.length; i++)
345 str += this._menuItems[i];
349 for (var i = 0; i < this._subMenus.length; i++)
350 str += this._subMenus[i];
355 function WebFXMenuButton(sText, sHref, sToolTip, oSubMenu) {
356 this._parentConstructor = WebFXMenuItem;
357 this._parentConstructor(sText, sHref, sToolTip, oSubMenu);
359 WebFXMenuButton.prototype = new WebFXMenuItem;
360 WebFXMenuButton.prototype.toString = function () {
362 " id='" + this.id + "'" +
363 " href='" + this.href + "'" +
364 (this.toolTip ? " title='" + this.toolTip + "'" : "") +
366 (" onmouseover='webFXMenuHandler.overMenuItem(this)'" +
367 " onmouseout='webFXMenuHandler.outMenuItem(this)'") :
369 " onfocus='webFXMenuHandler.overMenuItem(this)'" +
371 " onblur='webFXMenuHandler.blurMenu(this)'" :
377 (this.subMenu ? "<img class='arrow' src='" + webfxMenuImagePath + "arrow.down.black.png'>" : "") +
385 /* Position functions */
388 function getInnerLeft(el, debug) {
390 if (el == null) return 0;
392 if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
395 // alert ( 'getInnerLeft: ' + getLeft(el) + ' - ' + getBorderLeft(el) );
397 return parseInt( getLeft(el) + parseInt(getBorderLeft(el)) );
403 function getLeft(el, debug) {
405 if (el == null) return 0;
408 // alert ( el + ': ' + el.offsetLeft + ' - ' + getInnerLeft(el.offsetParent) );
410 return parseInt( el.offsetLeft + parseInt(getInnerLeft(el.offsetParent)) );
416 function getInnerTop(el) {
418 if (el == null) return 0;
420 if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
422 return parseInt( getTop(el) + parseInt(getBorderTop(el)) );
428 function getTop(el) {
430 if (el == null) return 0;
432 return parseInt( el.offsetTop + parseInt(getInnerTop(el.offsetParent)) );
438 function getBorderLeft(el) {
445 ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
446 : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-left-width"))
453 function getBorderTop(el) {
460 ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
461 : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-top-width"))
468 function opera_getLeft(el) {
470 if (el == null) return 0;
472 return el.offsetLeft + opera_getLeft(el.offsetParent);
478 function opera_getTop(el) {
480 if (el == null) return 0;
482 return el.offsetTop + opera_getTop(el.offsetParent);
488 function getOuterRect(el, debug) {
492 left: (opera ? opera_getLeft(el) : getLeft(el, debug)),
494 top: (opera ? opera_getTop(el) : getTop(el)),
496 width: el.offsetWidth,
498 height: el.offsetHeight
506 // mozilla bug! scrollbars not included in innerWidth/height
508 function getDocumentRect(el) {
518 (ieBox ? document.body.clientWidth : document.documentElement.clientWidth) :
526 (ieBox ? document.body.clientHeight : document.documentElement.clientHeight) :
538 function getScrollPos(el) {
544 (ieBox ? document.body.scrollLeft : document.documentElement.scrollLeft) :
552 (ieBox ? document.body.scrollTop : document.documentElement.scrollTop) :
563 /* end position functions */
565 WebFXMenu.prototype.position = function (relEl, sDir) {
567 // find parent item rectangle, piRect
570 var pi = this.parentMenuItem;
571 if (!this.parentMenuItem)
574 relEl = document.getElementById(pi.id);
576 dir = pi instanceof WebFXMenuButton ? "vertical" : "horizontal";
577 //alert('created RelEl from parent: ' + pi.id);
578 piRect = getOuterRect(relEl, 1);
580 else if (relEl.left != null && relEl.top != null && relEl.width != null && relEl.height != null) { // got a rect
581 //alert('passed a Rect as RelEl: ' + typeof(relEl));
586 //alert('passed an element as RelEl: ' + typeof(relEl));
587 piRect = getOuterRect(relEl);
590 var menuEl = document.getElementById(this.id);
591 var menuRect = getOuterRect(menuEl);
592 var docRect = getDocumentRect();
593 var scrollPos = getScrollPos();
594 var pMenu = this.parentMenu;
596 if (dir == "vertical") {
597 if (piRect.left + menuRect.width - scrollPos.left <= docRect.width) {
598 //alert('piRect.left: ' + piRect.left);
599 this.left = piRect.left;
601 // this.left = this.left + 138;
602 } else if (docRect.width >= menuRect.width) {
603 //konq (not safari though) winds up here by accident and positions the menus all weird
604 //alert('docRect.width + scrollPos.left - menuRect.width');
606 this.left = docRect.width + scrollPos.left - menuRect.width;
608 //alert('scrollPos.left: ' + scrollPos.left);
609 this.left = scrollPos.left;
612 if (piRect.top + piRect.height + menuRect.height <= docRect.height + scrollPos.top)
614 this.top = piRect.top + piRect.height;
616 else if (piRect.top - menuRect.height >= scrollPos.top)
618 this.top = piRect.top - menuRect.height;
620 else if (docRect.height >= menuRect.height)
622 this.top = docRect.height + scrollPos.top - menuRect.height;
626 this.top = scrollPos.top;
629 if (piRect.top + menuRect.height - this.borderTop - this.paddingTop <= docRect.height + scrollPos.top)
631 this.top = piRect.top - this.borderTop - this.paddingTop;
633 else if (piRect.top + piRect.height - menuRect.height + this.borderTop + this.paddingTop >= 0)
635 this.top = piRect.top + piRect.height - menuRect.height + this.borderBottom + this.paddingBottom + this.shadowBottom;
637 else if (docRect.height >= menuRect.height)
639 this.top = docRect.height + scrollPos.top - menuRect.height;
643 this.top = scrollPos.top;
647 var pMenuPaddingLeft = pMenu ? pMenu.paddingLeft : 0;
649 var pMenuBorderLeft = pMenu ? pMenu.borderLeft : 0;
651 var pMenuPaddingRight = pMenu ? pMenu.paddingRight : 0;
653 var pMenuBorderRight = pMenu ? pMenu.borderRight : 0;
657 if (piRect.left + piRect.width + menuRect.width + pMenuPaddingRight +
659 pMenuBorderRight - this.borderLeft + this.shadowRight <= docRect.width + scrollPos.left)
661 this.left = piRect.left + piRect.width + pMenuPaddingRight + pMenuBorderRight - this.borderLeft;
663 else if (piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight >= 0)
665 this.left = piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight;
667 else if (docRect.width >= menuRect.width)
669 this.left = docRect.width + scrollPos.left - menuRect.width;
673 this.left = scrollPos.left;