combine ticket notification scrips, #15353
[freeside.git] / httemplate / elements / xmenu.top.js
1 //<script>
2 /*
3  * This script was created by Erik Arvidsson (erik@eae.net)
4  * for WebFX (http://webfx.eae.net)
5  * Copyright 2001
6  * 
7  * For usage see license at http://webfx.eae.net/license.html   
8  *
9  * Created:             2001-01-12
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
13  */
14  
15 // check browsers
16 var ua = navigator.userAgent;
17 var opera = /opera [56789]|opera\/[56789]/i.test(ua);
18 var ie = !opera && /MSIE/.test(ua);
19 var ie50 = ie && /MSIE 5\.[01234]/.test(ua);
20 var ie6 = ie && /MSIE [6789]/.test(ua);
21 var ieBox = ie && (document.compatMode == null || document.compatMode != "CSS1Compat");
22 var moz = !opera && /gecko/i.test(ua);
23 var nn6 = !opera && /netscape.*6\./i.test(ua);
24 var khtml = /KHTML/i.test(ua);
25
26 // define the default values
27
28 webfxMenuDefaultWidth                   = 154;
29
30 webfxMenuDefaultBorderLeft              = 1;
31 webfxMenuDefaultBorderRight             = 1;
32 webfxMenuDefaultBorderTop               = 1;
33 webfxMenuDefaultBorderBottom    = 1;
34
35 webfxMenuDefaultPaddingLeft             = 1;
36 webfxMenuDefaultPaddingRight    = 1;
37 webfxMenuDefaultPaddingTop              = 1;
38 webfxMenuDefaultPaddingBottom   = 1;
39
40 webfxMenuDefaultShadowLeft              = 0;
41 webfxMenuDefaultShadowRight             = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 :0;
42 webfxMenuDefaultShadowTop               = 0;
43 webfxMenuDefaultShadowBottom    = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 : 0;
44
45
46 webfxMenuItemDefaultHeight              = 18;
47 webfxMenuItemDefaultText                = "Untitled";
48 webfxMenuItemDefaultHref                = "javascript:void(0)";
49
50 webfxMenuSeparatorDefaultHeight = 6;
51
52 webfxMenuDefaultEmptyText               = "Empty";
53
54 webfxMenuDefaultUseAutoPosition = nn6 ? false : true;
55
56
57
58 // other global constants
59
60 webfxMenuImagePath                              = "";
61
62 webfxMenuUseHover                               = opera ? true : false;
63 webfxMenuHideTime                               = 500;
64 webfxMenuShowTime                               = 200;
65
66
67
68 var webFXMenuHandler = {
69         idCounter               :       0,
70         idPrefix                :       "webfx-menu-object-",
71         all                             :       {},
72         getId                   :       function () { return this.idPrefix + this.idCounter++; },
73         overMenuItem    :       function (oItem) {
74                 if (this.showTimeout != null)
75                         window.clearTimeout(this.showTimeout);
76                 if (this.hideTimeout != null)
77                         window.clearTimeout(this.hideTimeout);
78                 var jsItem = this.all[oItem.id];
79                 if (webfxMenuShowTime <= 0)
80                         this._over(jsItem);
81                 else if ( jsItem )
82                         //this.showTimeout = window.setTimeout(function () { webFXMenuHandler._over(jsItem) ; }, webfxMenuShowTime);
83                         // I hate IE5.0 because the piece of shit crashes when using setTimeout with a function object
84                         this.showTimeout = window.setTimeout("webFXMenuHandler._over(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuShowTime);
85         },
86         outMenuItem     :       function (oItem) {
87                 if (this.showTimeout != null)
88                         window.clearTimeout(this.showTimeout);
89                 if (this.hideTimeout != null)
90                         window.clearTimeout(this.hideTimeout);
91                 var jsItem = this.all[oItem.id];
92                 if (webfxMenuHideTime <= 0)
93                         this._out(jsItem);
94                 else if ( jsItem ) 
95                         //this.hideTimeout = window.setTimeout(function () { webFXMenuHandler._out(jsItem) ; }, webfxMenuHideTime);
96                         this.hideTimeout = window.setTimeout("webFXMenuHandler._out(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuHideTime);
97         },
98         blurMenu                :       function (oMenuItem) {
99                 window.setTimeout("webFXMenuHandler.all[\"" + oMenuItem.id + "\"].subMenu.hide();", webfxMenuHideTime);
100         },
101         _over   :       function (jsItem) {
102                 if (jsItem.subMenu) {
103                         jsItem.parentMenu.hideAllSubs();
104                         jsItem.subMenu.show();
105                 }
106                 else
107                         jsItem.parentMenu.hideAllSubs();
108         },
109         _out    :       function (jsItem) {
110                 // find top most menu
111                 var root = jsItem;
112                 var m;
113                 if (root instanceof WebFXMenuButton)
114                         m = root.subMenu;
115                 else {
116                         m = jsItem.parentMenu;
117                         while (m.parentMenu != null && !(m.parentMenu instanceof WebFXMenuBar))
118                                 m = m.parentMenu;
119                 }
120                 if (m != null)  
121                         m.hide();       
122         },
123         hideMenu        :       function (menu) {
124                 if (this.showTimeout != null)
125                         window.clearTimeout(this.showTimeout);
126                 if (this.hideTimeout != null)
127                         window.clearTimeout(this.hideTimeout);
128
129                 this.hideTimeout = window.setTimeout("webFXMenuHandler.all['" + menu.id + "'].hide()", webfxMenuHideTime);
130         },
131         showMenu        :       function (menu, src, dir) {
132                 if (this.showTimeout != null)
133                         window.clearTimeout(this.showTimeout);
134                 if (this.hideTimeout != null)
135                         window.clearTimeout(this.hideTimeout);
136
137                 if (arguments.length < 3)
138                         dir = "vertical";
139                 
140                 menu.show(src, dir);
141         }
142 };
143
144 function WebFXMenu() {
145         this._menuItems = [];
146         this._subMenus  = [];
147         this.id                 = webFXMenuHandler.getId();
148         this.top                = 0;
149         this.left               = 0;
150         this.shown              = false;
151         this.parentMenu = null;
152         webFXMenuHandler.all[this.id] = this;
153 }
154
155 WebFXMenu.prototype.width                       = webfxMenuDefaultWidth;
156 WebFXMenu.prototype.emptyText           = webfxMenuDefaultEmptyText;
157 WebFXMenu.prototype.useAutoPosition     = webfxMenuDefaultUseAutoPosition;
158
159 WebFXMenu.prototype.borderLeft          = webfxMenuDefaultBorderLeft;
160 WebFXMenu.prototype.borderRight         = webfxMenuDefaultBorderRight;
161 WebFXMenu.prototype.borderTop           = webfxMenuDefaultBorderTop;
162 WebFXMenu.prototype.borderBottom        = webfxMenuDefaultBorderBottom;
163
164 WebFXMenu.prototype.paddingLeft         = webfxMenuDefaultPaddingLeft;
165 WebFXMenu.prototype.paddingRight        = webfxMenuDefaultPaddingRight;
166 WebFXMenu.prototype.paddingTop          = webfxMenuDefaultPaddingTop;
167 WebFXMenu.prototype.paddingBottom       = webfxMenuDefaultPaddingBottom;
168
169 WebFXMenu.prototype.shadowLeft          = webfxMenuDefaultShadowLeft;
170 WebFXMenu.prototype.shadowRight         = webfxMenuDefaultShadowRight;
171 WebFXMenu.prototype.shadowTop           = webfxMenuDefaultShadowTop;
172 WebFXMenu.prototype.shadowBottom        = webfxMenuDefaultShadowBottom;
173
174
175
176 WebFXMenu.prototype.add = function (menuItem) {
177         this._menuItems[this._menuItems.length] = menuItem;
178         if (menuItem.subMenu) {
179                 this._subMenus[this._subMenus.length] = menuItem.subMenu;
180                 menuItem.subMenu.parentMenu = this;
181         }
182         
183         menuItem.parentMenu = this;
184 };
185
186 WebFXMenu.prototype.show = function (relObj, sDir) {
187         if (this.useAutoPosition)
188                 this.position(relObj, sDir);
189
190         var divElement = document.getElementById(this.id);
191         if ( divElement ) {
192
193           divElement.style.left = opera ? this.left : this.left + "px";
194           divElement.style.top = opera ? this.top : this.top + "px";
195           divElement.style.visibility = "visible";
196
197           if ( ie ) {
198             var shimElement = document.getElementById(this.id + "Shim");
199             if ( shimElement ) {
200               shimElement.style.width = divElement.offsetWidth;
201               shimElement.style.height = divElement.offsetHeight;
202               shimElement.style.top = divElement.style.top;
203               shimElement.style.left = divElement.style.left;
204               /*shimElement.style.zIndex = divElement.style.zIndex - 1; */
205               shimElement.style.display = "block";
206               shimElement.style.filter='progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
207             }
208           }
209
210         }
211
212         this.shown = true;
213
214         if (this.parentMenu)
215                 this.parentMenu.show();
216 };
217
218 WebFXMenu.prototype.hide = function () {
219         this.hideAllSubs();
220         var divElement = document.getElementById(this.id);
221         if ( divElement ) {
222           divElement.style.visibility = "hidden";
223           if ( ie ) {
224             var shimElement = document.getElementById(this.id + "Shim");
225             if ( shimElement ) {
226               shimElement.style.display = "none";
227             }
228           }
229         }
230
231         this.shown = false;
232 };
233
234 WebFXMenu.prototype.hideAllSubs = function () {
235         for (var i = 0; i < this._subMenus.length; i++) {
236                 if (this._subMenus[i].shown)
237                         this._subMenus[i].hide();
238         }
239 };
240
241 WebFXMenu.prototype.toString = function () {
242         var top = this.top + this.borderTop + this.paddingTop;
243         var str = "<div id='" + this.id + "' class='webfx-menu' style='" + 
244         "width:" + (!ieBox  ?
245                 this.width - this.borderLeft - this.paddingLeft - this.borderRight - this.paddingRight  : 
246                 this.width) + "px;" +
247         (this.useAutoPosition ?
248                 "left:" + this.left + "px;" + "top:" + this.top + "px;" :
249                 "") +
250         (ie50 ? "filter: none;" : "") +
251         "'>";
252
253         if (this._menuItems.length == 0) {
254                 str +=  "<span class='webfx-menu-empty'>" + this.emptyText + "</span>";
255         }
256         else {  
257                 str += '<span class="webfx-menu-title" onmouseover="webFXMenuHandler.overMenuItem(this)"' +
258                         (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") +
259                          '>' + this.emptyText + '</span>';
260                 // str += '<div id="' + this.id + '-title">' + this.emptyText + '</div>';
261                 // loop through all menuItems
262                 for (var i = 0; i < this._menuItems.length; i++) {
263                         var mi = this._menuItems[i];
264                         str += mi;
265                         if (!this.useAutoPosition) {
266                                 if (mi.subMenu && !mi.subMenu.useAutoPosition)
267                                         mi.subMenu.top = top - mi.subMenu.borderTop - mi.subMenu.paddingTop;
268                                 top += mi.height;
269                         }
270                 }
271
272         }
273         
274         str += "</div>";
275
276         if ( ie ) {
277           str += "<iframe id='" + this.id + "Shim' src='javascript:false;' scrolling='no' frameBorder='0' style='position:absolute; top:0px; left: 0px; display:none;'></iframe>";
278         }
279         
280         for (var i = 0; i < this._subMenus.length; i++) {
281                 this._subMenus[i].left = this.left + this.width - this._subMenus[i].borderLeft;
282                 str += this._subMenus[i];
283         }
284         
285         return str;
286 };
287 // WebFXMenu.prototype.position defined later
288
289 function WebFXMenuItem(sText, sHref, sToolTip, oSubMenu) {
290         this.text = sText || webfxMenuItemDefaultText;
291         this.href = (sHref == null || sHref == "") ? webfxMenuItemDefaultHref : sHref;
292         this.subMenu = oSubMenu;
293         if (oSubMenu)
294                 oSubMenu.parentMenuItem = this;
295         this.toolTip = sToolTip;
296         this.id = webFXMenuHandler.getId();
297         webFXMenuHandler.all[this.id] = this;
298 };
299 WebFXMenuItem.prototype.height = webfxMenuItemDefaultHeight;
300 WebFXMenuItem.prototype.toString = function () {
301         return  "<a" +
302                         " id='" + this.id + "'" +
303                         " href=\"" + this.href + "\"" +
304                         (this.toolTip ? " title=\"" + this.toolTip + "\"" : "") +
305                         " onmouseover='webFXMenuHandler.overMenuItem(this)'" +
306                         (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") +
307                         (this.subMenu ? " unselectable='on' tabindex='-1'" : "") +
308                         ">" +
309                         (this.subMenu ? "<img class='arrow' src=\"" + webfxMenuImagePath + "arrow.right.black.png\">" : "") +
310                         this.text + 
311                         "</a>";
312 };
313
314
315 function WebFXMenuSeparator() {
316         this.id = webFXMenuHandler.getId();
317         webFXMenuHandler.all[this.id] = this;
318 };
319 WebFXMenuSeparator.prototype.height = webfxMenuSeparatorDefaultHeight;
320 WebFXMenuSeparator.prototype.toString = function () {
321         return  "<div" +
322                         " id='" + this.id + "'" +
323                         (webfxMenuUseHover ? 
324                         " onmouseover='webFXMenuHandler.overMenuItem(this)'" +
325                         " onmouseout='webFXMenuHandler.outMenuItem(this)'"
326                         :
327                         "") +
328                         "></div>"
329 };
330
331 function WebFXMenuBar() {
332         this._parentConstructor = WebFXMenu;
333         this._parentConstructor();
334 }
335 WebFXMenuBar.prototype = new WebFXMenu;
336 WebFXMenuBar.prototype.toString = function () {
337         var str = "<div id='" + this.id + "' class='webfx-menu-bar'>";
338         
339         // loop through all menuButtons
340         for (var i = 0; i < this._menuItems.length; i++)
341                 str += this._menuItems[i];
342         
343         str += "</div>";
344
345         for (var i = 0; i < this._subMenus.length; i++)
346                 str += this._subMenus[i];
347         
348         return str;
349 };
350
351 function WebFXMenuButton(sText, sHref, sToolTip, oSubMenu) {
352         this._parentConstructor = WebFXMenuItem;
353         this._parentConstructor(sText, sHref, sToolTip, oSubMenu);
354 }
355 WebFXMenuButton.prototype = new WebFXMenuItem;
356 WebFXMenuButton.prototype.toString = function () {
357         return  "<a" +
358                         " id='" + this.id + "'" +
359                         " href='" + this.href + "'" +
360                         (this.toolTip ? " title='" + this.toolTip + "'" : "") +
361                         (webfxMenuUseHover ?
362                                 (" onmouseover='webFXMenuHandler.overMenuItem(this)'" +
363                                 " onmouseout='webFXMenuHandler.outMenuItem(this)'") :
364                                 (
365                                         " onfocus='webFXMenuHandler.overMenuItem(this)'" +
366                                         (this.subMenu ?
367                                                 " onblur='webFXMenuHandler.blurMenu(this)'" :
368                                                 ""
369                                         )
370                                 )) +
371                         ">" +
372                         this.text + 
373                         (this.subMenu ? "<img class='arrow' src='" + webfxMenuImagePath + "arrow.down.black.png'>" : "") +                              
374                         "</a>";
375 };
376
377
378
379
380
381 /* Position functions */
382
383
384 function getInnerLeft(el, debug) {
385
386         if (el == null) return 0;
387
388         if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
389
390         //if ( debug ) 
391         //  alert ( 'getInnerLeft: ' + getLeft(el) + ' - ' + getBorderLeft(el) );
392
393         return parseInt( getLeft(el) + parseInt(getBorderLeft(el)) );
394
395 }
396
397
398
399 function getLeft(el, debug) {
400
401         if (el == null) return 0;
402
403         //if ( debug )
404         //  alert ( el + ': ' + el.offsetLeft + ' - ' + getInnerLeft(el.offsetParent) );
405
406         return parseInt( el.offsetLeft + parseInt(getInnerLeft(el.offsetParent)) );
407
408 }
409
410
411
412 function getInnerTop(el) {
413
414         if (el == null) return 0;
415
416         if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
417
418         return parseInt( getTop(el) + parseInt(getBorderTop(el)) );
419
420 }
421
422
423
424 function getTop(el) {
425
426         if (el == null) return 0;
427
428         return parseInt( el.offsetTop + parseInt(getInnerTop(el.offsetParent)) );
429
430 }
431
432
433
434 function getBorderLeft(el) {
435
436         return ie ?
437
438                 el.clientLeft :
439
440                 ( khtml 
441                     ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
442                     : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-left-width")) 
443                 );
444
445 }
446
447
448
449 function getBorderTop(el) {
450
451         return ie ?
452
453                 el.clientTop :
454
455                 ( khtml 
456                     ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
457                     : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-top-width"))
458                 );
459
460 }
461
462
463
464 function opera_getLeft(el) {
465
466         if (el == null) return 0;
467
468         return el.offsetLeft + opera_getLeft(el.offsetParent);
469
470 }
471
472
473
474 function opera_getTop(el) {
475
476         if (el == null) return 0;
477
478         return el.offsetTop + opera_getTop(el.offsetParent);
479
480 }
481
482
483
484 function getOuterRect(el, debug) {
485
486         return {
487
488                 left:   (opera ? opera_getLeft(el) : getLeft(el, debug)),
489
490                 top:    (opera ? opera_getTop(el) : getTop(el)),
491
492                 width:  el.offsetWidth,
493
494                 height: el.offsetHeight
495
496         };
497
498 }
499
500
501
502 // mozilla bug! scrollbars not included in innerWidth/height
503
504 function getDocumentRect(el) {
505
506         return {
507
508                 left:   0,
509
510                 top:    0,
511
512                 width:  (ie ?
513
514                                         (ieBox ? document.body.clientWidth : document.documentElement.clientWidth) :
515
516                                         window.innerWidth
517
518                                 ),
519
520                 height: (ie ?
521
522                                         (ieBox ? document.body.clientHeight : document.documentElement.clientHeight) :
523
524                                         window.innerHeight
525
526                                 )
527
528         };
529
530 }
531
532
533
534 function getScrollPos(el) {
535
536         return {
537
538                 left:   (ie ?
539
540                                         (ieBox ? document.body.scrollLeft : document.documentElement.scrollLeft) :
541
542                                         window.pageXOffset
543
544                                 ),
545
546                 top:    (ie ?
547
548                                         (ieBox ? document.body.scrollTop : document.documentElement.scrollTop) :
549
550                                         window.pageYOffset
551
552                                 )
553
554         };
555
556 }
557
558
559 /* end position functions */
560
561 WebFXMenu.prototype.position = function (relEl, sDir) {
562         var dir = sDir;
563         // find parent item rectangle, piRect
564         var piRect;
565         if (!relEl) {
566                 var pi = this.parentMenuItem;
567                 if (!this.parentMenuItem)
568                         return;
569                 
570                 relEl = document.getElementById(pi.id);
571                 if (dir == null)
572                         dir = pi instanceof WebFXMenuButton ? "vertical" : "horizontal";
573                 //alert('created RelEl from parent: ' + pi.id);
574                 piRect = getOuterRect(relEl, 1);
575         }
576         else if (relEl.left != null && relEl.top != null && relEl.width != null && relEl.height != null) {      // got a rect
577                 //alert('passed a Rect as RelEl: ' + typeof(relEl));
578
579                 piRect = relEl;
580         }
581         else {
582                 //alert('passed an element as RelEl: ' + typeof(relEl));
583                 piRect = getOuterRect(relEl);
584         }
585
586         var menuEl = document.getElementById(this.id);
587         var menuRect = getOuterRect(menuEl);
588         var docRect = getDocumentRect();
589         var scrollPos = getScrollPos();
590         var pMenu = this.parentMenu;
591         
592         if (dir == "vertical") {
593                 if (piRect.left + menuRect.width - scrollPos.left <= docRect.width) {
594                         //alert('piRect.left: ' + piRect.left);
595                         this.left = piRect.left;
596 //                      if ( ! ie )
597 //                        this.left = this.left + 138;
598                 } else if (docRect.width >= menuRect.width) {
599                         //konq (not safari though) winds up here by accident and positions the menus all weird
600                         //alert('docRect.width + scrollPos.left - menuRect.width');
601
602                         this.left = docRect.width + scrollPos.left - menuRect.width;
603                 } else {
604                         //alert('scrollPos.left: ' + scrollPos.left);
605                         this.left = scrollPos.left;
606                 }
607                         
608                 if (piRect.top + piRect.height + menuRect.height <= docRect.height + scrollPos.top)
609
610                         this.top = piRect.top + piRect.height;
611
612                 else if (piRect.top - menuRect.height >= scrollPos.top)
613
614                         this.top = piRect.top - menuRect.height;
615
616                 else if (docRect.height >= menuRect.height)
617
618                         this.top = docRect.height + scrollPos.top - menuRect.height;
619
620                 else
621
622                         this.top = scrollPos.top;
623         }
624         else {
625                 if (piRect.top + menuRect.height - this.borderTop - this.paddingTop <= docRect.height + scrollPos.top)
626
627                         this.top = piRect.top - this.borderTop - this.paddingTop;
628
629                 else if (piRect.top + piRect.height - menuRect.height + this.borderTop + this.paddingTop >= 0)
630
631                         this.top = piRect.top + piRect.height - menuRect.height + this.borderBottom + this.paddingBottom + this.shadowBottom;
632
633                 else if (docRect.height >= menuRect.height)
634
635                         this.top = docRect.height + scrollPos.top - menuRect.height;
636
637                 else
638
639                         this.top = scrollPos.top;
640
641
642
643                 var pMenuPaddingLeft = pMenu ? pMenu.paddingLeft : 0;
644
645                 var pMenuBorderLeft = pMenu ? pMenu.borderLeft : 0;
646
647                 var pMenuPaddingRight = pMenu ? pMenu.paddingRight : 0;
648
649                 var pMenuBorderRight = pMenu ? pMenu.borderRight : 0;
650
651                 
652
653                 if (piRect.left + piRect.width + menuRect.width + pMenuPaddingRight +
654
655                         pMenuBorderRight - this.borderLeft + this.shadowRight <= docRect.width + scrollPos.left)
656
657                         this.left = piRect.left + piRect.width + pMenuPaddingRight + pMenuBorderRight - this.borderLeft;
658
659                 else if (piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight >= 0)
660
661                         this.left = piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight;
662
663                 else if (docRect.width >= menuRect.width)
664
665                         this.left = docRect.width  + scrollPos.left - menuRect.width;
666
667                 else
668
669                         this.left = scrollPos.left;
670         }
671 };