first part of ACL and re-skinning work and some other small stuff
[freeside.git] / httemplate / elements / xmenu.js
diff --git a/httemplate/elements/xmenu.js b/httemplate/elements/xmenu.js
new file mode 100644 (file)
index 0000000..134265f
--- /dev/null
@@ -0,0 +1,668 @@
+//<script>
+/*
+ * This script was created by Erik Arvidsson (erik@eae.net)
+ * for WebFX (http://webfx.eae.net)
+ * Copyright 2001
+ * 
+ * For usage see license at http://webfx.eae.net/license.html  
+ *
+ * Created:            2001-01-12
+ * Updates:            2001-11-20      Added hover mode support and removed Opera focus hacks
+ *                             2001-12-20      Added auto positioning and some properties to support this
+ *                             2002-08-13      toString used ' for attributes. Changed to " to allow in args
+ */
+// check browsers
+var ua = navigator.userAgent;
+var opera = /opera [56789]|opera\/[56789]/i.test(ua);
+var ie = !opera && /MSIE/.test(ua);
+var ie50 = ie && /MSIE 5\.[01234]/.test(ua);
+var ie6 = ie && /MSIE [6789]/.test(ua);
+var ieBox = ie && (document.compatMode == null || document.compatMode != "CSS1Compat");
+var moz = !opera && /gecko/i.test(ua);
+var nn6 = !opera && /netscape.*6\./i.test(ua);
+var khtml = /KHTML/i.test(ua);
+
+// define the default values
+
+webfxMenuDefaultWidth                  = 154;
+
+webfxMenuDefaultBorderLeft             = 1;
+webfxMenuDefaultBorderRight            = 1;
+webfxMenuDefaultBorderTop              = 1;
+webfxMenuDefaultBorderBottom   = 1;
+
+webfxMenuDefaultPaddingLeft            = 1;
+webfxMenuDefaultPaddingRight   = 1;
+webfxMenuDefaultPaddingTop             = 1;
+webfxMenuDefaultPaddingBottom  = 1;
+
+webfxMenuDefaultShadowLeft             = 0;
+webfxMenuDefaultShadowRight            = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 :0;
+webfxMenuDefaultShadowTop              = 0;
+webfxMenuDefaultShadowBottom   = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 : 0;
+
+
+webfxMenuItemDefaultHeight             = 18;
+webfxMenuItemDefaultText               = "Untitled";
+webfxMenuItemDefaultHref               = "javascript:void(0)";
+
+webfxMenuSeparatorDefaultHeight        = 6;
+
+webfxMenuDefaultEmptyText              = "Empty";
+
+webfxMenuDefaultUseAutoPosition        = nn6 ? false : true;
+
+
+
+// other global constants
+
+webfxMenuImagePath                             = "";
+
+webfxMenuUseHover                              = opera ? true : false;
+webfxMenuHideTime                              = 500;
+webfxMenuShowTime                              = 200;
+
+
+
+var webFXMenuHandler = {
+       idCounter               :       0,
+       idPrefix                :       "webfx-menu-object-",
+       all                             :       {},
+       getId                   :       function () { return this.idPrefix + this.idCounter++; },
+       overMenuItem    :       function (oItem) {
+               if (this.showTimeout != null)
+                       window.clearTimeout(this.showTimeout);
+               if (this.hideTimeout != null)
+                       window.clearTimeout(this.hideTimeout);
+               var jsItem = this.all[oItem.id];
+               if (webfxMenuShowTime <= 0)
+                       this._over(jsItem);
+               else if ( jsItem )
+                       //this.showTimeout = window.setTimeout(function () { webFXMenuHandler._over(jsItem) ; }, webfxMenuShowTime);
+                       // I hate IE5.0 because the piece of shit crashes when using setTimeout with a function object
+                       this.showTimeout = window.setTimeout("webFXMenuHandler._over(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuShowTime);
+       },
+       outMenuItem     :       function (oItem) {
+               if (this.showTimeout != null)
+                       window.clearTimeout(this.showTimeout);
+               if (this.hideTimeout != null)
+                       window.clearTimeout(this.hideTimeout);
+               var jsItem = this.all[oItem.id];
+               if (webfxMenuHideTime <= 0)
+                       this._out(jsItem);
+               else if ( jsItem ) 
+                       //this.hideTimeout = window.setTimeout(function () { webFXMenuHandler._out(jsItem) ; }, webfxMenuHideTime);
+                       this.hideTimeout = window.setTimeout("webFXMenuHandler._out(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuHideTime);
+       },
+       blurMenu                :       function (oMenuItem) {
+               window.setTimeout("webFXMenuHandler.all[\"" + oMenuItem.id + "\"].subMenu.hide();", webfxMenuHideTime);
+       },
+       _over   :       function (jsItem) {
+               if (jsItem.subMenu) {
+                       jsItem.parentMenu.hideAllSubs();
+                       jsItem.subMenu.show();
+               }
+               else
+                       jsItem.parentMenu.hideAllSubs();
+       },
+       _out    :       function (jsItem) {
+               // find top most menu
+               var root = jsItem;
+               var m;
+               if (root instanceof WebFXMenuButton)
+                       m = root.subMenu;
+               else {
+                       m = jsItem.parentMenu;
+                       while (m.parentMenu != null && !(m.parentMenu instanceof WebFXMenuBar))
+                               m = m.parentMenu;
+               }
+               if (m != null)  
+                       m.hide();       
+       },
+       hideMenu        :       function (menu) {
+               if (this.showTimeout != null)
+                       window.clearTimeout(this.showTimeout);
+               if (this.hideTimeout != null)
+                       window.clearTimeout(this.hideTimeout);
+
+               this.hideTimeout = window.setTimeout("webFXMenuHandler.all['" + menu.id + "'].hide()", webfxMenuHideTime);
+       },
+       showMenu        :       function (menu, src, dir) {
+               if (this.showTimeout != null)
+                       window.clearTimeout(this.showTimeout);
+               if (this.hideTimeout != null)
+                       window.clearTimeout(this.hideTimeout);
+
+               if (arguments.length < 3)
+                       dir = "vertical";
+               
+               menu.show(src, dir);
+       }
+};
+
+function WebFXMenu() {
+       this._menuItems = [];
+       this._subMenus  = [];
+       this.id                 = webFXMenuHandler.getId();
+       this.top                = 0;
+       this.left               = 0;
+       this.shown              = false;
+       this.parentMenu = null;
+       webFXMenuHandler.all[this.id] = this;
+}
+
+WebFXMenu.prototype.width                      = webfxMenuDefaultWidth;
+WebFXMenu.prototype.emptyText          = webfxMenuDefaultEmptyText;
+WebFXMenu.prototype.useAutoPosition    = webfxMenuDefaultUseAutoPosition;
+
+WebFXMenu.prototype.borderLeft         = webfxMenuDefaultBorderLeft;
+WebFXMenu.prototype.borderRight                = webfxMenuDefaultBorderRight;
+WebFXMenu.prototype.borderTop          = webfxMenuDefaultBorderTop;
+WebFXMenu.prototype.borderBottom       = webfxMenuDefaultBorderBottom;
+
+WebFXMenu.prototype.paddingLeft                = webfxMenuDefaultPaddingLeft;
+WebFXMenu.prototype.paddingRight       = webfxMenuDefaultPaddingRight;
+WebFXMenu.prototype.paddingTop         = webfxMenuDefaultPaddingTop;
+WebFXMenu.prototype.paddingBottom      = webfxMenuDefaultPaddingBottom;
+
+WebFXMenu.prototype.shadowLeft         = webfxMenuDefaultShadowLeft;
+WebFXMenu.prototype.shadowRight                = webfxMenuDefaultShadowRight;
+WebFXMenu.prototype.shadowTop          = webfxMenuDefaultShadowTop;
+WebFXMenu.prototype.shadowBottom       = webfxMenuDefaultShadowBottom;
+
+
+
+WebFXMenu.prototype.add = function (menuItem) {
+       this._menuItems[this._menuItems.length] = menuItem;
+       if (menuItem.subMenu) {
+               this._subMenus[this._subMenus.length] = menuItem.subMenu;
+               menuItem.subMenu.parentMenu = this;
+       }
+       
+       menuItem.parentMenu = this;
+};
+
+WebFXMenu.prototype.show = function (relObj, sDir) {
+       if (this.useAutoPosition)
+               this.position(relObj, sDir);
+
+       var divElement = document.getElementById(this.id);
+       if ( divElement ) {
+
+         divElement.style.left = opera ? this.left : this.left + "px";
+         divElement.style.top = opera ? this.top : this.top + "px";
+         divElement.style.visibility = "visible";
+
+         if ( ie ) {
+           var shimElement = document.getElementById(this.id + "Shim");
+           if ( shimElement ) {
+             shimElement.style.width = divElement.offsetWidth;
+             shimElement.style.height = divElement.offsetHeight;
+             shimElement.style.top = divElement.style.top;
+             shimElement.style.left = divElement.style.left;
+             /*shimElement.style.zIndex = divElement.style.zIndex - 1; */
+             shimElement.style.display = "block";
+             shimElement.style.filter='progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
+           }
+         }
+
+       }
+
+       this.shown = true;
+
+       if (this.parentMenu)
+               this.parentMenu.show();
+};
+
+WebFXMenu.prototype.hide = function () {
+       this.hideAllSubs();
+       var divElement = document.getElementById(this.id);
+       if ( divElement ) {
+         divElement.style.visibility = "hidden";
+         if ( ie ) {
+           var shimElement = document.getElementById(this.id + "Shim");
+           if ( shimElement ) {
+             shimElement.style.display = "none";
+           }
+         }
+       }
+
+       this.shown = false;
+};
+
+WebFXMenu.prototype.hideAllSubs = function () {
+       for (var i = 0; i < this._subMenus.length; i++) {
+               if (this._subMenus[i].shown)
+                       this._subMenus[i].hide();
+       }
+};
+
+WebFXMenu.prototype.toString = function () {
+       var top = this.top + this.borderTop + this.paddingTop;
+       var str = "<div id='" + this.id + "' class='webfx-menu' style='" + 
+       "width:" + (!ieBox  ?
+               this.width - this.borderLeft - this.paddingLeft - this.borderRight - this.paddingRight  : 
+               this.width) + "px;" +
+       (this.useAutoPosition ?
+               "left:" + this.left + "px;" + "top:" + this.top + "px;" :
+               "") +
+       (ie50 ? "filter: none;" : "") +
+       "'>";
+
+       if (this._menuItems.length == 0) {
+               str +=  "<span class='webfx-menu-empty'>" + this.emptyText + "</span>";
+       }
+       else {  
+               str += '<span class="webfx-menu-title" onmouseover="webFXMenuHandler.overMenuItem(this)"' +
+                       (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") +
+                        '>' + this.emptyText + '</span>';
+               // str += '<div id="' + this.id + '-title">' + this.emptyText + '</div>';
+               // loop through all menuItems
+               for (var i = 0; i < this._menuItems.length; i++) {
+                       var mi = this._menuItems[i];
+                       str += mi;
+                       if (!this.useAutoPosition) {
+                               if (mi.subMenu && !mi.subMenu.useAutoPosition)
+                                       mi.subMenu.top = top - mi.subMenu.borderTop - mi.subMenu.paddingTop;
+                               top += mi.height;
+                       }
+               }
+
+       }
+       
+       str += "</div>";
+
+       if ( ie ) {
+          str += "<iframe id='" + this.id + "Shim' src='javascript:false;' scrolling='no' frameBorder='0' style='position:absolute; top:0px; left: 0px; display:none;'></iframe>";
+       }
+       
+       for (var i = 0; i < this._subMenus.length; i++) {
+               this._subMenus[i].left = this.left + this.width - this._subMenus[i].borderLeft;
+               str += this._subMenus[i];
+       }
+       
+       return str;
+};
+// WebFXMenu.prototype.position defined later
+
+function WebFXMenuItem(sText, sHref, sToolTip, oSubMenu) {
+       this.text = sText || webfxMenuItemDefaultText;
+       this.href = (sHref == null || sHref == "") ? webfxMenuItemDefaultHref : sHref;
+       this.subMenu = oSubMenu;
+       if (oSubMenu)
+               oSubMenu.parentMenuItem = this;
+       this.toolTip = sToolTip;
+       this.id = webFXMenuHandler.getId();
+       webFXMenuHandler.all[this.id] = this;
+};
+WebFXMenuItem.prototype.height = webfxMenuItemDefaultHeight;
+WebFXMenuItem.prototype.toString = function () {
+       return  "<a" +
+                       " id='" + this.id + "'" +
+                       " href=\"" + this.href + "\"" +
+                       (this.toolTip ? " title=\"" + this.toolTip + "\"" : "") +
+                       " onmouseover='webFXMenuHandler.overMenuItem(this)'" +
+                       (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") +
+                       (this.subMenu ? " unselectable='on' tabindex='-1'" : "") +
+                       ">" +
+                       (this.subMenu ? "<img class='arrow' src=\"" + webfxMenuImagePath + "arrow.right.black.png\">" : "") +
+                       this.text + 
+                       "</a>";
+};
+
+
+function WebFXMenuSeparator() {
+       this.id = webFXMenuHandler.getId();
+       webFXMenuHandler.all[this.id] = this;
+};
+WebFXMenuSeparator.prototype.height = webfxMenuSeparatorDefaultHeight;
+WebFXMenuSeparator.prototype.toString = function () {
+       return  "<div" +
+                       " id='" + this.id + "'" +
+                       (webfxMenuUseHover ? 
+                       " onmouseover='webFXMenuHandler.overMenuItem(this)'" +
+                       " onmouseout='webFXMenuHandler.outMenuItem(this)'"
+                       :
+                       "") +
+                       "></div>"
+};
+
+function WebFXMenuBar() {
+       this._parentConstructor = WebFXMenu;
+       this._parentConstructor();
+}
+WebFXMenuBar.prototype = new WebFXMenu;
+WebFXMenuBar.prototype.toString = function () {
+       var str = "<div id='" + this.id + "' class='webfx-menu-bar'>";
+       
+       // loop through all menuButtons
+       for (var i = 0; i < this._menuItems.length; i++)
+               str += this._menuItems[i];
+       
+       str += "</div>";
+
+       for (var i = 0; i < this._subMenus.length; i++)
+               str += this._subMenus[i];
+       
+       return str;
+};
+
+function WebFXMenuButton(sText, sHref, sToolTip, oSubMenu) {
+       this._parentConstructor = WebFXMenuItem;
+       this._parentConstructor(sText, sHref, sToolTip, oSubMenu);
+}
+WebFXMenuButton.prototype = new WebFXMenuItem;
+WebFXMenuButton.prototype.toString = function () {
+       return  "<a" +
+                       " id='" + this.id + "'" +
+                       " href='" + this.href + "'" +
+                       (this.toolTip ? " title='" + this.toolTip + "'" : "") +
+                       (webfxMenuUseHover ?
+                               (" onmouseover='webFXMenuHandler.overMenuItem(this)'" +
+                               " onmouseout='webFXMenuHandler.outMenuItem(this)'") :
+                               (
+                                       " onfocus='webFXMenuHandler.overMenuItem(this)'" +
+                                       (this.subMenu ?
+                                               " onblur='webFXMenuHandler.blurMenu(this)'" :
+                                               ""
+                                       )
+                               )) +
+                       ">" +
+                       (this.subMenu ? "<img class='arrow' src='" + webfxMenuImagePath + "arrow.right.png'>" : "") +                           
+                       this.text + 
+                       "</a>";
+};
+
+
+
+
+
+/* Position functions */
+
+
+function getInnerLeft(el) {
+
+       if (el == null) return 0;
+
+       if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
+
+       return parseInt( getLeft(el) + parseInt(getBorderLeft(el)) );
+
+}
+
+
+
+function getLeft(el, debug) {
+
+       if (el == null) return 0;
+
+        //if ( debug )
+       //  alert ( el.offsetLeft + ' - ' + getInnerLeft(el.offsetParent) );
+
+       return parseInt( el.offsetLeft + parseInt(getInnerLeft(el.offsetParent)) );
+
+}
+
+
+
+function getInnerTop(el) {
+
+       if (el == null) return 0;
+
+       if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
+
+       return parseInt( getTop(el) + parseInt(getBorderTop(el)) );
+
+}
+
+
+
+function getTop(el) {
+
+       if (el == null) return 0;
+
+       return parseInt( el.offsetTop + parseInt(getInnerTop(el.offsetParent)) );
+
+}
+
+
+
+function getBorderLeft(el) {
+
+       return ie ?
+
+               el.clientLeft :
+
+               ( khtml 
+                   ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
+                   : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-left-width")) 
+               );
+
+}
+
+
+
+function getBorderTop(el) {
+
+       return ie ?
+
+               el.clientTop :
+
+               ( khtml 
+                   ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
+                   : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-top-width"))
+               );
+
+}
+
+
+
+function opera_getLeft(el) {
+
+       if (el == null) return 0;
+
+       return el.offsetLeft + opera_getLeft(el.offsetParent);
+
+}
+
+
+
+function opera_getTop(el) {
+
+       if (el == null) return 0;
+
+       return el.offsetTop + opera_getTop(el.offsetParent);
+
+}
+
+
+
+function getOuterRect(el, debug) {
+
+       return {
+
+               left:   (opera ? opera_getLeft(el) : getLeft(el, debug)),
+
+               top:    (opera ? opera_getTop(el) : getTop(el)),
+
+               width:  el.offsetWidth,
+
+               height: el.offsetHeight
+
+       };
+
+}
+
+
+
+// mozilla bug! scrollbars not included in innerWidth/height
+
+function getDocumentRect(el) {
+
+       return {
+
+               left:   0,
+
+               top:    0,
+
+               width:  (ie ?
+
+                                       (ieBox ? document.body.clientWidth : document.documentElement.clientWidth) :
+
+                                       window.innerWidth
+
+                               ),
+
+               height: (ie ?
+
+                                       (ieBox ? document.body.clientHeight : document.documentElement.clientHeight) :
+
+                                       window.innerHeight
+
+                               )
+
+       };
+
+}
+
+
+
+function getScrollPos(el) {
+
+       return {
+
+               left:   (ie ?
+
+                                       (ieBox ? document.body.scrollLeft : document.documentElement.scrollLeft) :
+
+                                       window.pageXOffset
+
+                               ),
+
+               top:    (ie ?
+
+                                       (ieBox ? document.body.scrollTop : document.documentElement.scrollTop) :
+
+                                       window.pageYOffset
+
+                               )
+
+       };
+
+}
+
+
+/* end position functions */
+
+WebFXMenu.prototype.position = function (relEl, sDir) {
+       var dir = sDir;
+       // find parent item rectangle, piRect
+       var piRect;
+       if (!relEl) {
+               var pi = this.parentMenuItem;
+               if (!this.parentMenuItem)
+                       return;
+               
+               relEl = document.getElementById(pi.id);
+               if (dir == null)
+                       dir = pi instanceof WebFXMenuButton ? "vertical" : "horizontal";
+               //alert('created RelEl from parent: ' + pi.id);
+               piRect = getOuterRect(relEl, 1);
+       }
+       else if (relEl.left != null && relEl.top != null && relEl.width != null && relEl.height != null) {      // got a rect
+               //alert('passed a Rect as RelEl: ' + typeof(relEl));
+
+               piRect = relEl;
+       }
+       else {
+               //alert('passed an element as RelEl: ' + typeof(relEl));
+               piRect = getOuterRect(relEl);
+       }
+
+       var menuEl = document.getElementById(this.id);
+       var menuRect = getOuterRect(menuEl);
+       var docRect = getDocumentRect();
+       var scrollPos = getScrollPos();
+       var pMenu = this.parentMenu;
+       
+       if (dir == "vertical") {
+               if (piRect.left + menuRect.width - scrollPos.left <= docRect.width) {
+                       //alert('piRect.left: ' + piRect.left);
+                       this.left = piRect.left;
+                       if ( ! ie )
+                         this.left = this.left + 138;
+               } else if (docRect.width >= menuRect.width) {
+                       //konq (not safari though) winds up here by accident and positions the menus all weird
+                       //alert('docRect.width + scrollPos.left - menuRect.width');
+
+                       this.left = docRect.width + scrollPos.left - menuRect.width;
+               } else {
+                       //alert('scrollPos.left: ' + scrollPos.left);
+                       this.left = scrollPos.left;
+               }
+                       
+               if (piRect.top + piRect.height + menuRect.height <= docRect.height + scrollPos.top)
+
+                       this.top = piRect.top + piRect.height;
+
+               else if (piRect.top - menuRect.height >= scrollPos.top)
+
+                       this.top = piRect.top - menuRect.height;
+
+               else if (docRect.height >= menuRect.height)
+
+                       this.top = docRect.height + scrollPos.top - menuRect.height;
+
+               else
+
+                       this.top = scrollPos.top;
+       }
+       else {
+               if (piRect.top + menuRect.height - this.borderTop - this.paddingTop <= docRect.height + scrollPos.top)
+
+                       this.top = piRect.top - this.borderTop - this.paddingTop;
+
+               else if (piRect.top + piRect.height - menuRect.height + this.borderTop + this.paddingTop >= 0)
+
+                       this.top = piRect.top + piRect.height - menuRect.height + this.borderBottom + this.paddingBottom + this.shadowBottom;
+
+               else if (docRect.height >= menuRect.height)
+
+                       this.top = docRect.height + scrollPos.top - menuRect.height;
+
+               else
+
+                       this.top = scrollPos.top;
+
+
+
+               var pMenuPaddingLeft = pMenu ? pMenu.paddingLeft : 0;
+
+               var pMenuBorderLeft = pMenu ? pMenu.borderLeft : 0;
+
+               var pMenuPaddingRight = pMenu ? pMenu.paddingRight : 0;
+
+               var pMenuBorderRight = pMenu ? pMenu.borderRight : 0;
+
+               
+
+               if (piRect.left + piRect.width + menuRect.width + pMenuPaddingRight +
+
+                       pMenuBorderRight - this.borderLeft + this.shadowRight <= docRect.width + scrollPos.left)
+
+                       this.left = piRect.left + piRect.width + pMenuPaddingRight + pMenuBorderRight - this.borderLeft;
+
+               else if (piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight >= 0)
+
+                       this.left = piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight;
+
+               else if (docRect.width >= menuRect.width)
+
+                       this.left = docRect.width  + scrollPos.left - menuRect.width;
+
+               else
+
+                       this.left = scrollPos.left;
+       }
+};