RT# 81830 Critical log event for corrupted invoice data
[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 // "window.opera" exists in Opera 10+, and needs to be preserved for FCKeditor 
18 // to work.
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);
27
28 // define the default values
29
30 webfxMenuDefaultWidth                   = 154;
31
32 webfxMenuDefaultBorderLeft              = 1;
33 webfxMenuDefaultBorderRight             = 1;
34 webfxMenuDefaultBorderTop               = 1;
35 webfxMenuDefaultBorderBottom    = 1;
36
37 webfxMenuDefaultPaddingLeft             = 1;
38 webfxMenuDefaultPaddingRight    = 1;
39 webfxMenuDefaultPaddingTop              = 1;
40 webfxMenuDefaultPaddingBottom   = 1;
41
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;
46
47
48 webfxMenuItemDefaultHeight              = 18;
49 webfxMenuItemDefaultText                = "Untitled";
50 webfxMenuItemDefaultHref                = "javascript:void(0)";
51
52 webfxMenuSeparatorDefaultHeight = 6;
53
54 webfxMenuDefaultEmptyText               = "Empty";
55
56 webfxMenuDefaultUseAutoPosition = nn6 ? false : true;
57
58
59
60 // other global constants
61
62 webfxMenuImagePath                              = "";
63
64 webfxMenuUseHover                               = opera ? true : false;
65 webfxMenuHideTime                               = 500;
66 webfxMenuShowTime                               = 200;
67
68
69
70 var webFXMenuHandler = {
71         idCounter               :       0,
72         idPrefix                :       "webfx-menu-object-",
73         all                             :       {},
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)
82                         this._over(jsItem);
83                 else if ( jsItem )
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);
87         },
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)
95                         this._out(jsItem);
96                 else if ( jsItem ) 
97                         //this.hideTimeout = window.setTimeout(function () { webFXMenuHandler._out(jsItem) ; }, webfxMenuHideTime);
98                         this.hideTimeout = window.setTimeout("webFXMenuHandler._out(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuHideTime);
99         },
100         blurMenu                :       function (oMenuItem) {
101                 window.setTimeout("webFXMenuHandler.all[\"" + oMenuItem.id + "\"].subMenu.hide();", webfxMenuHideTime);
102         },
103         _over   :       function (jsItem) {
104                 if (jsItem.subMenu) {
105                         jsItem.parentMenu.hideAllSubs();
106                         jsItem.subMenu.show();
107                 }
108                 else
109                         jsItem.parentMenu.hideAllSubs();
110         },
111         _out    :       function (jsItem) {
112                 // find top most menu
113                 var root = jsItem;
114                 var m;
115                 if (root instanceof WebFXMenuButton)
116                         m = root.subMenu;
117                 else {
118                         m = jsItem.parentMenu;
119                         while (m.parentMenu != null && !(m.parentMenu instanceof WebFXMenuBar))
120                                 m = m.parentMenu;
121                 }
122                 if (m != null)  
123                         m.hide();       
124         },
125         hideMenu        :       function (menu) {
126                 if (this.showTimeout != null)
127                         window.clearTimeout(this.showTimeout);
128                 if (this.hideTimeout != null)
129                         window.clearTimeout(this.hideTimeout);
130
131                 this.hideTimeout = window.setTimeout("webFXMenuHandler.all['" + menu.id + "'].hide()", webfxMenuHideTime);
132         },
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);
138
139                 if (arguments.length < 3)
140                         dir = "vertical";
141                 
142                 menu.show(src, dir);
143         }
144 };
145
146 function WebFXMenu() {
147         this._menuItems = [];
148         this._subMenus  = [];
149         this.id                 = webFXMenuHandler.getId();
150         this.top                = 0;
151         this.left               = 0;
152         this.shown              = false;
153         this.parentMenu = null;
154         webFXMenuHandler.all[this.id] = this;
155 }
156
157 WebFXMenu.prototype.width                       = webfxMenuDefaultWidth;
158 WebFXMenu.prototype.emptyText           = webfxMenuDefaultEmptyText;
159 WebFXMenu.prototype.useAutoPosition     = webfxMenuDefaultUseAutoPosition;
160
161 WebFXMenu.prototype.borderLeft          = webfxMenuDefaultBorderLeft;
162 WebFXMenu.prototype.borderRight         = webfxMenuDefaultBorderRight;
163 WebFXMenu.prototype.borderTop           = webfxMenuDefaultBorderTop;
164 WebFXMenu.prototype.borderBottom        = webfxMenuDefaultBorderBottom;
165
166 WebFXMenu.prototype.paddingLeft         = webfxMenuDefaultPaddingLeft;
167 WebFXMenu.prototype.paddingRight        = webfxMenuDefaultPaddingRight;
168 WebFXMenu.prototype.paddingTop          = webfxMenuDefaultPaddingTop;
169 WebFXMenu.prototype.paddingBottom       = webfxMenuDefaultPaddingBottom;
170
171 WebFXMenu.prototype.shadowLeft          = webfxMenuDefaultShadowLeft;
172 WebFXMenu.prototype.shadowRight         = webfxMenuDefaultShadowRight;
173 WebFXMenu.prototype.shadowTop           = webfxMenuDefaultShadowTop;
174 WebFXMenu.prototype.shadowBottom        = webfxMenuDefaultShadowBottom;
175
176
177
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;
183         }
184         
185         menuItem.parentMenu = this;
186 };
187
188 WebFXMenu.prototype.show = function (relObj, sDir) {
189         if (this.useAutoPosition)
190                 this.position(relObj, sDir);
191
192         var divElement = document.getElementById(this.id);
193         if ( divElement ) {
194
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";
200
201           if ( ie ) {
202             var shimElement = document.getElementById(this.id + "Shim");
203             if ( shimElement ) {
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)';
211             }
212           }
213
214         }
215
216         this.shown = true;
217
218         if (this.parentMenu)
219                 this.parentMenu.show();
220 };
221
222 WebFXMenu.prototype.hide = function () {
223         this.hideAllSubs();
224         var divElement = document.getElementById(this.id);
225         if ( divElement ) {
226           divElement.style.visibility = "hidden";
227           if ( ie ) {
228             var shimElement = document.getElementById(this.id + "Shim");
229             if ( shimElement ) {
230               shimElement.style.display = "none";
231             }
232           }
233         }
234
235         this.shown = false;
236 };
237
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();
242         }
243 };
244
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='" + 
248         "width:" + (!ieBox  ?
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;" :
253                 "") +
254         (ie50 ? "filter: none;" : "") +
255         "'>";
256
257         if (this._menuItems.length == 0) {
258                 str +=  "<span class='webfx-menu-empty'>" + this.emptyText + "</span>";
259         }
260         else {  
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];
268                         str += mi;
269                         if (!this.useAutoPosition) {
270                                 if (mi.subMenu && !mi.subMenu.useAutoPosition)
271                                         mi.subMenu.top = top - mi.subMenu.borderTop - mi.subMenu.paddingTop;
272                                 top += mi.height;
273                         }
274                 }
275
276         }
277         
278         str += "</div>";
279
280         if ( ie ) {
281           str += "<iframe id='" + this.id + "Shim' src='javascript:false;' scrolling='no' frameBorder='0' style='position:absolute; top:0px; left: 0px; display:none;'></iframe>";
282         }
283         
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];
287         }
288         
289         return str;
290 };
291 // WebFXMenu.prototype.position defined later
292
293 function WebFXMenuItem(sText, sHref, sToolTip, oSubMenu) {
294         this.text = sText || webfxMenuItemDefaultText;
295         this.href = (sHref == null || sHref == "") ? webfxMenuItemDefaultHref : sHref;
296         this.subMenu = oSubMenu;
297         if (oSubMenu)
298                 oSubMenu.parentMenuItem = this;
299         this.toolTip = sToolTip;
300         this.id = webFXMenuHandler.getId();
301         webFXMenuHandler.all[this.id] = this;
302 };
303 WebFXMenuItem.prototype.height = webfxMenuItemDefaultHeight;
304 WebFXMenuItem.prototype.toString = function () {
305         return  "<a" +
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'" : "") +
312                         ">" +
313                         (this.subMenu ? "<img class='arrow' src=\"" + webfxMenuImagePath + "arrow.right.black.png\">" : "") +
314                         this.text + 
315                         "</a>";
316 };
317
318
319 function WebFXMenuSeparator() {
320         this.id = webFXMenuHandler.getId();
321         webFXMenuHandler.all[this.id] = this;
322 };
323 WebFXMenuSeparator.prototype.height = webfxMenuSeparatorDefaultHeight;
324 WebFXMenuSeparator.prototype.toString = function () {
325         return  "<div" +
326                         " id='" + this.id + "'" +
327                         (webfxMenuUseHover ? 
328                         " onmouseover='webFXMenuHandler.overMenuItem(this)'" +
329                         " onmouseout='webFXMenuHandler.outMenuItem(this)'"
330                         :
331                         "") +
332                         "></div>"
333 };
334
335 function WebFXMenuBar() {
336         this._parentConstructor = WebFXMenu;
337         this._parentConstructor();
338 }
339 WebFXMenuBar.prototype = new WebFXMenu;
340 WebFXMenuBar.prototype.toString = function () {
341         var str = "<div id='" + this.id + "' class='webfx-menu-bar'>";
342         
343         // loop through all menuButtons
344         for (var i = 0; i < this._menuItems.length; i++)
345                 str += this._menuItems[i];
346         
347         str += "</div>";
348
349         for (var i = 0; i < this._subMenus.length; i++)
350                 str += this._subMenus[i];
351         
352         return str;
353 };
354
355 function WebFXMenuButton(sText, sHref, sToolTip, oSubMenu) {
356         this._parentConstructor = WebFXMenuItem;
357         this._parentConstructor(sText, sHref, sToolTip, oSubMenu);
358 }
359 WebFXMenuButton.prototype = new WebFXMenuItem;
360 WebFXMenuButton.prototype.toString = function () {
361         return  "<a" +
362                         " id='" + this.id + "'" +
363                         " href='" + this.href + "'" +
364                         (this.toolTip ? " title='" + this.toolTip + "'" : "") +
365                         (webfxMenuUseHover ?
366                                 (" onmouseover='webFXMenuHandler.overMenuItem(this)'" +
367                                 " onmouseout='webFXMenuHandler.outMenuItem(this)'") :
368                                 (
369                                         " onfocus='webFXMenuHandler.overMenuItem(this)'" +
370                                         (this.subMenu ?
371                                                 " onblur='webFXMenuHandler.blurMenu(this)'" :
372                                                 ""
373                                         )
374                                 )) +
375                         ">" +
376                         this.text + 
377                         (this.subMenu ? "<img class='arrow' src='" + webfxMenuImagePath + "arrow.down.black.png'>" : "") +                              
378                         "</a>";
379 };
380
381
382
383
384
385 /* Position functions */
386
387
388 function getInnerLeft(el, debug) {
389
390         if (el == null) return 0;
391
392         if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
393
394         //if ( debug ) 
395         //  alert ( 'getInnerLeft: ' + getLeft(el) + ' - ' + getBorderLeft(el) );
396
397         return parseInt( getLeft(el) + parseInt(getBorderLeft(el)) );
398
399 }
400
401
402
403 function getLeft(el, debug) {
404
405         if (el == null) return 0;
406
407         //if ( debug )
408         //  alert ( el + ': ' + el.offsetLeft + ' - ' + getInnerLeft(el.offsetParent) );
409
410         return parseInt( el.offsetLeft + parseInt(getInnerLeft(el.offsetParent)) );
411
412 }
413
414
415
416 function getInnerTop(el) {
417
418         if (el == null) return 0;
419
420         if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
421
422         return parseInt( getTop(el) + parseInt(getBorderTop(el)) );
423
424 }
425
426
427
428 function getTop(el) {
429
430         if (el == null) return 0;
431
432         return parseInt( el.offsetTop + parseInt(getInnerTop(el.offsetParent)) );
433
434 }
435
436
437
438 function getBorderLeft(el) {
439
440         return ie ?
441
442                 el.clientLeft :
443
444                 ( khtml 
445                     ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
446                     : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-left-width")) 
447                 );
448
449 }
450
451
452
453 function getBorderTop(el) {
454
455         return ie ?
456
457                 el.clientTop :
458
459                 ( khtml 
460                     ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
461                     : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-top-width"))
462                 );
463
464 }
465
466
467
468 function opera_getLeft(el) {
469
470         if (el == null) return 0;
471
472         return el.offsetLeft + opera_getLeft(el.offsetParent);
473
474 }
475
476
477
478 function opera_getTop(el) {
479
480         if (el == null) return 0;
481
482         return el.offsetTop + opera_getTop(el.offsetParent);
483
484 }
485
486
487
488 function getOuterRect(el, debug) {
489
490         return {
491
492                 left:   (opera ? opera_getLeft(el) : getLeft(el, debug)),
493
494                 top:    (opera ? opera_getTop(el) : getTop(el)),
495
496                 width:  el.offsetWidth,
497
498                 height: el.offsetHeight
499
500         };
501
502 }
503
504
505
506 // mozilla bug! scrollbars not included in innerWidth/height
507
508 function getDocumentRect(el) {
509
510         return {
511
512                 left:   0,
513
514                 top:    0,
515
516                 width:  (ie ?
517
518                                         (ieBox ? document.body.clientWidth : document.documentElement.clientWidth) :
519
520                                         window.innerWidth
521
522                                 ),
523
524                 height: (ie ?
525
526                                         (ieBox ? document.body.clientHeight : document.documentElement.clientHeight) :
527
528                                         window.innerHeight
529
530                                 )
531
532         };
533
534 }
535
536
537
538 function getScrollPos(el) {
539
540         return {
541
542                 left:   (ie ?
543
544                                         (ieBox ? document.body.scrollLeft : document.documentElement.scrollLeft) :
545
546                                         window.pageXOffset
547
548                                 ),
549
550                 top:    (ie ?
551
552                                         (ieBox ? document.body.scrollTop : document.documentElement.scrollTop) :
553
554                                         window.pageYOffset
555
556                                 )
557
558         };
559
560 }
561
562
563 /* end position functions */
564
565 WebFXMenu.prototype.position = function (relEl, sDir) {
566         var dir = sDir;
567         // find parent item rectangle, piRect
568         var piRect;
569         if (!relEl) {
570                 var pi = this.parentMenuItem;
571                 if (!this.parentMenuItem)
572                         return;
573                 
574                 relEl = document.getElementById(pi.id);
575                 if (dir == null)
576                         dir = pi instanceof WebFXMenuButton ? "vertical" : "horizontal";
577                 //alert('created RelEl from parent: ' + pi.id);
578                 piRect = getOuterRect(relEl, 1);
579         }
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));
582
583                 piRect = relEl;
584         }
585         else {
586                 //alert('passed an element as RelEl: ' + typeof(relEl));
587                 piRect = getOuterRect(relEl);
588         }
589
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;
595         
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;
600 //                      if ( ! ie )
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');
605
606                         this.left = docRect.width + scrollPos.left - menuRect.width;
607                 } else {
608                         //alert('scrollPos.left: ' + scrollPos.left);
609                         this.left = scrollPos.left;
610                 }
611                         
612                 if (piRect.top + piRect.height + menuRect.height <= docRect.height + scrollPos.top)
613
614                         this.top = piRect.top + piRect.height;
615
616                 else if (piRect.top - menuRect.height >= scrollPos.top)
617
618                         this.top = piRect.top - menuRect.height;
619
620                 else if (docRect.height >= menuRect.height)
621
622                         this.top = docRect.height + scrollPos.top - menuRect.height;
623
624                 else
625
626                         this.top = scrollPos.top;
627         }
628         else {
629                 if (piRect.top + menuRect.height - this.borderTop - this.paddingTop <= docRect.height + scrollPos.top)
630
631                         this.top = piRect.top - this.borderTop - this.paddingTop;
632
633                 else if (piRect.top + piRect.height - menuRect.height + this.borderTop + this.paddingTop >= 0)
634
635                         this.top = piRect.top + piRect.height - menuRect.height + this.borderBottom + this.paddingBottom + this.shadowBottom;
636
637                 else if (docRect.height >= menuRect.height)
638
639                         this.top = docRect.height + scrollPos.top - menuRect.height;
640
641                 else
642
643                         this.top = scrollPos.top;
644
645
646
647                 var pMenuPaddingLeft = pMenu ? pMenu.paddingLeft : 0;
648
649                 var pMenuBorderLeft = pMenu ? pMenu.borderLeft : 0;
650
651                 var pMenuPaddingRight = pMenu ? pMenu.paddingRight : 0;
652
653                 var pMenuBorderRight = pMenu ? pMenu.borderRight : 0;
654
655                 
656
657                 if (piRect.left + piRect.width + menuRect.width + pMenuPaddingRight +
658
659                         pMenuBorderRight - this.borderLeft + this.shadowRight <= docRect.width + scrollPos.left)
660
661                         this.left = piRect.left + piRect.width + pMenuPaddingRight + pMenuBorderRight - this.borderLeft;
662
663                 else if (piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight >= 0)
664
665                         this.left = piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight;
666
667                 else if (docRect.width >= menuRect.width)
668
669                         this.left = docRect.width  + scrollPos.left - menuRect.width;
670
671                 else
672
673                         this.left = scrollPos.left;
674         }
675 };