]> err.no Git - scalable-opengroupware.org/blob - UI/WebServerResources/generic.js
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1009 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / WebServerResources / generic.js
1 /*
2   Copyright (C) 2005 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
6   OGo is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21 /* some generic JavaScript code for SOGo */
22
23 // TODO: replace things with Prototype where applicable
24
25 /* generic stuff */
26
27 var logConsole;
28 var logWindow = null;
29
30 var queryParameters;
31
32 var activeAjaxRequests = 0;
33
34 // logArea = null;
35 var allDocumentElements = null;
36
37 /* a W3C compliant document.all */
38 function getAllScopeElements(scope)
39 {
40   var elements = new Array();
41
42   for (var i = 0; i < scope.childNodes.length; i++)
43     if (typeof(scope.childNodes[i]) == "object"
44         && scope.childNodes[i].tagName
45         && scope.childNodes[i].tagName != '')
46       {
47         elements.push(scope.childNodes[i]);
48         var childElements = getAllElements(scope.childNodes[i]);
49         if (childElements.length > 0)
50           elements.push(childElements);
51       }
52
53   return elements;
54 }
55
56 function getAllElements(scope)
57 {
58   var elements;
59
60   if (scope == null)
61     scope = document;
62
63   if (scope == document
64       && allDocumentElements != null)
65     elements = allDocumentElements;
66   else
67     {
68       elements = getAllScopeElements(scope);
69       if (scope == document)
70         allDocumentElements = elements;
71     }
72
73   return elements;
74 }
75
76 /* from
77    http://www.robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/ */
78 function getElementsByClassName2(_tag, _class, _scope) {
79   var regexp, classes, elements, element, returnElements;
80
81   _scope = _scope || document;
82
83   elements = (!_tag || _tag == "*"
84               ? getAllElements(null)
85               : _scope.getElementsByTagName(_tag));
86   returnElements = [];
87
88   classes = _class.split(/\s+/);
89   regexp = new RegExp("(^|\s+)("+ classes.join("|") +")(\s+|$)","i");
90
91   if (_class) {
92     for(var i = 0; element = elements[i]; i++) {
93       if (regexp.test(element.className)) {
94         returnElements.push(element);
95       }
96     }
97     return returnElements;
98   } else {
99     return elements;
100   }
101 }
102
103 function ml_stripActionInURL(url) {
104   if (url[url.length - 1] != '/') {
105     var i;
106     
107     i = url.lastIndexOf("/");
108     if (i != -1) url = url.substring(0, i);
109   }
110   if (url[url.length - 1] != '/') // ensure trailing slash
111     url = url + "/";
112   return url;
113 }
114
115 function extractEmailAddress(mailTo) {
116   var email = "";
117
118   var emailre
119     = /([a-zA-Z0-9]+[a-zA-Z0-9\._-]+[a-zA-Z0-9]+@[a-zA-Z0-9]+[a-zA-Z0-9\._-]+[a-zA-Z0-9]+)/g;
120   if (emailre.test(mailTo)) {
121     emailre.exec(mailTo);
122     email = RegExp.$1;
123   }
124
125   return email;
126 }
127
128 function extractEmailName(mailTo) {
129   var emailName = "";
130
131   var emailNamere = /(\w[\w\ _-]+)\ (&lt;|<)/;
132   if (emailNamere.test(mailTo)) {
133     emailNamere.exec(mailTo);
134     emailName = RegExp.$1;
135   }
136 }
137
138 function sanitizeMailTo(dirtyMailTo) {
139   var emailName = extractEmailName(dirtyMailTo);
140   var email = "" + extractEmailAddress(dirtyMailTo);
141
142   var mailto = "";
143   if (emailName && emailName.length > 0)
144     mailto = emailName + ' <' + email + '>';
145   else
146     mailto = email;
147
148   return mailto;
149 }
150
151 function openMailComposeWindow(url) {
152   var w = window.open(url, null,
153                       "width=680,height=520,resizable=1,scrollbars=1,toolbar=0,"
154                       + "location=0,directories=0,status=0,menubar=0"
155                       + ",copyhistory=0");
156   w.focus();
157
158   return w;
159 }
160
161 function openMailTo(senderMailto) {
162   var mailto = sanitizeMailTo(senderMailto);
163
164   if (mailto.length > 0)
165     openMailComposeWindow(ApplicationBaseURL
166                           + "/../Mail/compose?mailto=" + mailto);
167
168   return false; /* stop following the link */
169 }
170
171 function createHTTPClient() {
172   // http://developer.apple.com/internet/webcontent/xmlhttpreq.html
173   if (typeof XMLHttpRequest != "undefined")
174     return new XMLHttpRequest();
175   
176   try { return new ActiveXObject("Msxml2.XMLHTTP"); } 
177   catch (e) { }
178   try { return new ActiveXObject("Microsoft.XMLHTTP"); } 
179   catch (e) { }
180
181   return null;
182 }
183
184 function triggerAjaxRequest(url, callback, userdata) {
185   var http = createHTTPClient();
186
187   activeAjaxRequests += 1;
188   document.animTimer = setTimeout("checkAjaxRequestsState();", 200);
189
190   if (http) {
191     http.onreadystatechange
192       = function() {
193 //         log ("state changed (" + http.readyState + "): " + url);
194         try {
195           if (http.readyState == 4
196               && activeAjaxRequests > 0) {
197                 if (!http.aborted) {
198                   http.callbackData = userdata;
199                   callback(http);
200                 }
201                 activeAjaxRequests -= 1;
202                 checkAjaxRequestsState();
203               }
204         }
205         catch( e ) {
206           activeAjaxRequests -= 1;
207           checkAjaxRequestsState();
208           log("AJAX Request, Caught Exception: " + e.name);
209           log(e.message);
210         }
211       };
212     http.url = url;
213     http.open("GET", url, true);
214     http.send("");
215   }
216
217   return http;
218 }
219
220 function checkAjaxRequestsState() {
221   var toolbar = document.getElementById("toolbar");
222   if (toolbar) {
223     if (activeAjaxRequests > 0
224         && !document.busyAnim) {
225       var anim = document.createElement("img");
226       document.busyAnim = anim;
227       anim.id = "progressIndicator";
228       anim.src = ResourcesURL + "/busy.gif";
229       anim.style.visibility = "hidden;";
230       toolbar.appendChild(anim);
231       anim.style.visibility = "visible;";
232     }
233     else if (activeAjaxRequests == 0
234              && document.busyAnim) {
235       document.busyAnim.parentNode.removeChild(document.busyAnim);
236       document.busyAnim = null;
237     }
238   }
239 }
240
241 function resetSelection(win) {
242   var t = "";
243   if (win && win.getSelection) {
244     t = win.getSelection().toString();
245     win.getSelection().removeAllRanges();
246   }
247   return t;
248 }
249
250 function refreshOpener() {
251   if (window.opener && !window.opener.closed) {
252     window.opener.location.reload();
253   }
254 }
255
256 /* query string */
257
258 function parseQueryString() {
259   var queryArray, queryDict
260   var key, value, s, idx;
261   queryDict.length = 0;
262   
263   queryDict  = new Array();
264   queryArray = location.search.substr(1).split('&');
265   for (var i in queryArray) {
266     if (!queryArray[i]) continue ;
267     s   = queryArray[i];
268     idx = s.indexOf("=");
269     if (idx == -1) {
270       key   = s;
271       value = "";
272     }
273     else {
274       key   = s.substr(0, idx);
275       value = unescape(s.substr(idx + 1));
276     }
277     
278     if (typeof queryDict[key] == 'undefined')
279       queryDict.length++;
280     
281     queryDict[key] = value;
282   }
283   return queryDict;
284 }
285
286 function generateQueryString(queryDict) {
287   var s = "";
288   for (var key in queryDict) {
289     if (s.length == 0)
290       s = "?";
291     else
292       s = s + "&";
293     s = s + key + "=" + escape(queryDict[key]);
294   }
295   return s;
296 }
297
298 function getQueryParaArray(s) {
299   if (s.charAt(0) == "?") s = s.substr(1, s.length - 1);
300   return s.split("&");
301 }
302
303 function getQueryParaValue(s, name) {
304   var t;
305   
306   t = getQueryParaArray(s);
307   for (var i = 0; i < t.length; i++) {
308     var s = t[i];
309     
310     if (s.indexOf(name) != 0)
311       continue;
312     
313     s = s.substr(name.length, s.length - name.length);
314     return decodeURIComponent(s);
315   }
316   return null;
317 }
318
319 /* opener callback */
320
321 function triggerOpenerCallback() {
322   /* this code has some issue if the folder has no proper trailing slash! */
323   if (window.opener && !window.opener.closed) {
324     var t, cburl;
325     
326     t = getQueryParaValue(window.location.search, "openerurl=");
327     cburl = window.opener.location.href;
328     if (cburl[cburl.length - 1] != "/") {
329       cburl = cburl.substr(0, cburl.lastIndexOf("/") + 1);
330     }
331     cburl = cburl + t;
332     window.opener.location.href = cburl;
333   }
334 }
335
336 /* selection mechanism */
337
338 function deselectAll(parent) {
339   for (var i = 0; i < parent.childNodes.length; i++) {
340     var node = parent.childNodes.item(i);
341     if (node.nodeType == 1)
342       node.deselect();
343   }
344 }
345
346 function isNodeSelected(node) {
347   var classStr = '' + node.getAttribute('class');
348   var position = classStr.indexOf('_selected', 0);
349
350   return (position > -1);
351 }
352
353 function acceptMultiSelect(node) {
354   var accept = ('' + node.getAttribute('multiselect')).toLowerCase();
355
356   return (accept == 'yes');
357 }
358
359 function onRowClick(event) {
360   var node = event.target;
361
362   if (node.tagName == 'TD')
363     node = node.parentNode;
364
365   var startSelection = node.parentNode.getSelectedNodes();
366   if (event.shiftKey == 1
367       && (acceptMultiSelect(node.parentNode)
368           || acceptMultiSelect(node.parentNode.parentNode))) {
369     if (isNodeSelected(node) == true) {
370       node.deselect();
371     } else {
372       node.select();
373     }
374   } else {
375     deselectAll(node.parentNode);
376     node.select();
377   }
378
379   if (startSelection != node.parentNode.getSelectedNodes()) {
380     var parentNode = node.parentNode;
381     if (parentNode instanceof HTMLTableSectionElement)
382       parentNode = parentNode.parentNode;
383     var onSelectionChangeEvent = document.createEvent("Event");
384     onSelectionChangeEvent.initEvent("selectionchange", true, true);
385     parentNode.dispatchEvent(onSelectionChangeEvent);
386   }
387 }
388
389 /* popup menus */
390
391 var bodyOnClick = "";
392 // var acceptClick = false;
393
394 function onMenuClick(event, menuId) {
395   var node = event.target;
396
397   if (document.currentPopupMenu)
398     hideMenu(event, document.currentPopupMenu);
399
400   var popup = document.getElementById(menuId);
401
402   var menuTop = event.pageY;
403   var menuLeft = event.pageX;
404   var heightDiff = (window.innerHeight
405                     - (menuTop + popup.offsetHeight));
406   if (heightDiff < 0)
407     menuTop += heightDiff;
408
409   var leftDiff = (window.innerWidth
410                   - (menuLeft + popup.offsetWidth));
411   if (leftDiff < 0)
412     menuLeft -= popup.offsetWidth;
413
414   popup.style.top = menuTop + "px;";
415   popup.style.left = menuLeft + "px;";
416   popup.style.visibility = "visible;";
417   setupMenuTarget(popup, node);
418
419   bodyOnClick = "" + document.body.getAttribute("onclick");
420   document.body.setAttribute("onclick", "onBodyClick(event);");
421   document.currentPopupMenu = popup;
422
423   event.cancelBubble = true;
424   event.returnValue = false;
425
426   return false;
427 }
428
429 function setupMenuTarget(menu, target) {
430   menu.menuTarget = target;
431   var menus = document.getElementsByClassName("menu", menu);
432   for (var i = 0; i < menus.length; i++) {
433     menus[i].menuTarget = target;
434   }
435 }
436
437 function getParentMenu(node) {
438   var currentNode, menuNode;
439
440   menuNode = null;
441   currentNode = node;
442   var menure = new RegExp("(^|\s+)menu(\s+|$)", "i");
443
444   while (menuNode == null
445          && currentNode)
446     if (menure.test(currentNode.className))
447       menuNode = currentNode;
448     else
449       currentNode = currentNode.parentNode;
450
451   return menuNode;
452 }
453
454 function onBodyClick(event) {
455   document.currentPopupMenu.menuTarget = null;
456   hideMenu(event, document.currentPopupMenu);
457   document.body.setAttribute("onclick", bodyOnClick);
458
459   return false;
460 }
461
462 function hideMenu(event, menuNode) {
463   var onHide;
464
465 //   log('hiding menu "' + menuNode.getAttribute('id') + '"');
466   if (menuNode.submenu) {
467     hideMenu(event, menuNode.submenu);
468     menuNode.submenu = null;
469   }
470
471   menuNode.style.visibility = "hidden";
472   if (menuNode.parentMenuItem) {
473     menuNode.parentMenuItem.setAttribute('class', 'submenu');
474     menuNode.parentMenuItem = null;
475     menuNode.parentMenu.setAttribute('onmousemove', null);
476     menuNode.parentMenu.submenuItem = null;
477     menuNode.parentMenu.submenu = null;
478     menuNode.parentMenu = null;
479   }
480
481   var onhideEvent = document.createEvent("Event");
482   onhideEvent.initEvent("hideMenu", false, true);
483   menuNode.dispatchEvent(onhideEvent);
484 }
485
486 function onMenuEntryClick(event, menuId) {
487   var node = event.target;
488
489   id = getParentMenu(node).menuTarget;
490 //   log("clicked " + id + "/" + id.tagName);
491
492   return false;
493 }
494
495 function parseQueryParameters(url) {
496   var parameters = new Array();
497
498   var params = url.split("?")[1];
499   if (params) {
500     var pairs = params.split("&");
501     for (var i = 0; i < pairs.length; i++) {
502       var pair = pairs[i].split("=");
503       parameters[pair[0]] = pair[1];
504     }
505   }
506
507   return parameters;
508 }
509
510 function initLogConsole() {
511   var logConsole = $("logConsole");
512   if (logConsole) {
513     logConsole.addEventListener("dblclick", onLogDblClick, false);
514     logConsole.innerHTML = "";
515     node = document.getElementsByTagName('body')[0];
516     node.addEventListener("keydown", onBodyKeyDown, true);
517   }
518 }
519
520 function onBodyKeyDown(event) {
521   if (event.keyCode == 27) {
522     toggleLogConsole();
523     event.cancelBubble = true;
524     event.returnValue = false;
525   }
526 }
527
528 function onLogDblClick(event) {
529   var logConsole = $("logConsole");
530   logConsole.innerHTML = "";
531 }
532
533 function toggleLogConsole(event) {
534   var logConsole = $("logConsole");
535   var display = '' + logConsole.style.display;
536   if (display.length == 0) {
537     logConsole.style.display = 'block;';
538   } else {
539     logConsole.style.display = '';
540   }
541   event.cancelBubble = true;
542   event.returnValue = false;
543   event.preventDefault();
544 }
545
546 function log(message) {
547   if (!logWindow) {
548     logWindow = window;
549     while (logWindow.opener)
550       logWindow = logWindow.opener;
551   }
552   var logConsole = logWindow.document.getElementById("logConsole");
553   if (logConsole)
554     logConsole.innerHTML += message + '<br />' + "\n";
555 }
556
557 function dropDownSubmenu(event) {
558   var node = event.target;
559   var submenu = node.getAttribute("submenu");
560   if (submenu && submenu != "") {
561     var submenuNode = document.getElementById(submenu);
562     var parentNode = getParentMenu(node);
563     if (parentNode.submenu)
564       hideMenu(event, parentNode.submenu);
565     submenuNode.parentMenuItem = node;
566     submenuNode.parentMenu = parentNode;
567     parentNode.submenuItem = node;
568     parentNode.submenu = submenuNode;
569     
570     var menuTop = (node.offsetTop - 2);
571     
572     var heightDiff = (window.innerHeight
573                       - (menuTop + submenuNode.offsetHeight));
574     if (heightDiff < 0)
575       menuTop += heightDiff;
576     
577     var menuLeft = parentNode.offsetWidth - 3;
578     if (window.innerWidth
579         < (menuLeft + submenuNode.offsetWidth
580            + parentNode.cascadeLeftOffset()))
581         menuLeft = -submenuNode.offsetWidth + 3;
582     
583     parentNode.setAttribute('onmousemove', 'checkDropDown(event);');
584     node.setAttribute('class', 'submenu-selected');
585     submenuNode.style.top = menuTop + "px;";
586     submenuNode.style.left = menuLeft + "px;";
587     submenuNode.style.visibility = "visible;";
588   }
589 }
590
591 function checkDropDown(event) {
592   var parentMenu = getParentMenu(event.target);
593   var submenuItem = parentMenu.submenuItem;
594   if (submenuItem) {
595     var menuX = event.clientX - parentMenu.cascadeLeftOffset();
596     var menuY = event.clientY - parentMenu.cascadeTopOffset();
597     var itemX = submenuItem.offsetLeft;
598     var itemY = submenuItem.offsetTop - 75;
599
600     if (menuX >= itemX
601         && menuX < itemX + submenuItem.offsetWidth
602         && (menuY < itemY
603             || menuY > (itemY + submenuItem.offsetHeight))) {
604       hideMenu(event, parentMenu.submenu);
605       parentMenu.submenu = null;
606       parentMenu.submenuItem = null;
607       parentMenu.setAttribute('onmousemove', null);
608     }
609   }
610 }
611
612 /* search field */
613 function popupSearchMenu(event, menuId) {
614   var node = event.target;
615   relX = event.pageX - node.cascadeLeftOffset();
616   relY = event.pageY - node.cascadeTopOffset();
617
618   if (event.button == 0
619       && relX < 24) {
620     event.cancelBubble = true;
621     event.returnValue = false;
622
623     if (document.currentPopupMenu)
624       hideMenu(event, document.currentPopupMenu);
625
626     var popup = document.getElementById(menuId);
627     popup.style.top = node.offsetHeight + "px";
628     popup.style.left = (node.offsetLeft + 3) + "px";
629     popup.style.visibility = "visible";
630   
631     bodyOnClick = "" + document.body.getAttribute("onclick");
632     document.body.setAttribute("onclick", "onBodyClick('" + menuId + "');");
633     document.currentPopupMenu = popup;
634   }
635 }
636
637 function setSearchCriteria(event) {
638   searchValue = $("searchValue");
639   searchCriteria = $("searchCriteria");
640   
641   var node = event.target;
642   searchValue.setAttribute("ghost-phrase", node.innerHTML);
643   searchCriteria = node.getAttribute('id');
644 }
645
646 function checkSearchValue(event) {
647   var form = event.target;
648   var searchValue = $("searchValue");
649   var ghostPhrase = searchValue.getAttribute('ghost-phrase');
650
651   if (searchValue.value == ghostPhrase)
652     searchValue.value = "";
653 }
654
655 function onSearchChange() {
656   log ("onSearchChange()...");
657 }
658
659 function onSearchMouseDown(event, searchValue) {
660   superNode = searchValue.parentNode.parentNode.parentNode;
661   relX = (event.pageX - superNode.offsetLeft - searchValue.offsetLeft);
662   relY = (event.pageY - superNode.offsetTop - searchValue.offsetTop);
663
664   if (relY < 24) {
665     event.cancelBubble = true;
666     event.returnValue = false;
667   }
668 }
669
670 function onSearchFocus(searchValue) {
671   ghostPhrase = searchValue.getAttribute("ghost-phrase");
672   if (searchValue.value == ghostPhrase) {
673     searchValue.value = "";
674     searchValue.setAttribute("modified", "");
675   } else {
676     searchValue.select();
677   }
678
679   searchValue.style.color = "#000";
680 }
681
682 function onSearchBlur(searchValue) {
683   var ghostPhrase = searchValue.getAttribute("ghost-phrase");
684 //   log ("search blur: '" + searchValue.value + "'");
685   if (!searchValue.value) {
686     searchValue.setAttribute("modified", "");
687     searchValue.style.color = "#aaa";
688     searchValue.value = ghostPhrase;
689   } else if (searchValue.value == ghostPhrase) {
690     searchValue.setAttribute("modified", "");
691     searchValue.style.color = "#aaa";
692   } else {
693     searchValue.setAttribute("modified", "yes");
694     searchValue.style.color = "#000";
695   }
696 }
697
698 function onSearchKeyDown(searchValue) {
699   if (searchValue.timer)
700     clearTimeout(searchValue.timer);
701
702   searchValue.timer = setTimeout("onSearchFormSubmit()", 1000);
703 }
704
705 function initCriteria() {
706   var searchCriteria = $("searchCriteria");
707   var searchValue = $("searchValue");
708   var firstOption;
709  
710   var searchOptions = $("searchOptions");
711   if (searchOptions) {
712     firstOption = searchOptions.childNodes[1];
713     searchCriteria.value = firstOption.getAttribute('id');
714     searchValue.setAttribute('ghost-phrase', firstOption.innerHTML);
715     if (searchValue.value == '') {
716       searchValue.value = firstOption.innerHTML;
717       searchValue.setAttribute("modified", "");
718       searchValue.style.color = "#aaa";
719     }
720   }
721 }
722
723 /* contact selector */
724
725 function onContactAdd(node)
726 {
727   var selector = null;
728   var selectorURL = '?popup=YES';
729   if (node) {
730     selector = node.parentNode.parentNode;
731     selectorURL += ("&selectorId=" + selector.getAttribute("id"));
732   }
733
734   urlstr = ApplicationBaseURL;
735   if (urlstr[urlstr.length-1] != '/')
736     urlstr += '/';
737   urlstr += ("../../" + UserLogin + "/Contacts/"
738              + contactSelectorAction + selectorURL);
739 //   log (urlstr);
740   var w = window.open(urlstr, "Addressbook",
741                       "width=640,height=400,resizable=1,scrollbars=0");
742   w.selector = selector;
743   w.opener = this;
744   w.focus();
745
746   return false;
747 }
748
749 function onContactRemove(node) {
750   var selector = node.parentNode.parentNode;
751   var selectorId = selector.getAttribute("id");
752   var hasChanged = false;
753
754   var names = $('uixselector-' + selectorId + '-display');
755   var nodes = names.getSelectedNodes();
756   hasChanged = (nodes.length > 0);
757   for (var i = 0; i < nodes.length; i++) {
758     var currentNode = nodes[i];
759     currentNode.parentNode.removeChild(currentNode);
760   }
761
762   var uids = $('uixselector-' + selectorId + '-uidList');
763   nodes = node.parentNode.childNodes;
764   var ids = new Array();
765   for (var i = 0; i < nodes.length; i++)
766     if (nodes[i] instanceof HTMLLIElement)
767       ids.push(nodes[i].getAttribute("uid"));
768   uids.value = ids.join(",");
769
770   if (selector.changeNotification && hasChanged)
771     selector.changeNotification("removal");
772
773   return false;
774 }
775
776 function listRowMouseDownHandler(event) {
777   event.preventDefault();
778 }
779
780 /* tabs */
781 function initTabs()
782 {
783   var containers = document.getElementsByClassName("tabsContainer");
784   for (var x = 0; x < containers.length; x++) {
785     var container = containers[x];
786     var nodes = container.childNodes[1].childNodes;
787
788     var firstTab;
789     for (var i = 0; i < nodes.length; i++) {
790       if (nodes[i] instanceof HTMLLIElement) {
791         if (!firstTab) {
792           firstTab = nodes[i];
793         }
794         nodes[i].addEventListener("mousedown", onTabMouseDown, true);
795         nodes[i].addEventListener("click", onTabClick, true);
796       }
797     }
798
799     firstTab.addClassName("first");
800     firstTab.addClassName("active");
801     container.activeTab = firstTab;
802
803     var target = $(firstTab.getAttribute("target"));
804     target.addClassName("active");
805   }
806 }
807
808 function initMenusNamed(menuDivNames) {
809   for (var i = 0; i < menuDivNames.length; i++) {
810     var menuDIV = $(menuDivNames[i]);
811     if (menuDIV)
812       initMenu(menuDIV);
813     else
814       log("menu named '" + menuDivNames[i] + "' not found");
815   }
816 }
817
818 function initMenu(menuDIV) {
819   var lis = menuDIV.childNodesWithTag("ul")[0].childNodesWithTag("li");
820   for (var j = 0; j < lis.length; j++)
821     lis[j].addEventListener("mousedown", listRowMouseDownHandler, false);
822   var subMenus = menuDIV.childNodesWithTag("div");
823   for (var i = 0; i < subMenus.length; i++)
824     initMenu(subMenus[i]);
825 }
826
827 function onTabMouseDown(event) {
828   event.cancelBubble = true;
829   event.preventDefault();
830 }
831
832 function openExternalLink(anchor) {
833   return false;
834 }
835
836 function openAclWindow(url, objectTitle) {
837   var w = window.open(url, "aclWindow",
838                       "width=300,height=300,resizable=1,scrollbars=1,toolbar=0,"
839                       + "location=0,directories=0,status=0,menubar=0"
840                       + ",copyhistory=0");
841   w.focus();
842   w.title = "Poil: " + objectTitle;
843
844   return w;
845 }
846
847 function onTabClick(event) {
848   var node = event.target;
849
850   var target = node.getAttribute("target");
851
852   var container = node.parentNode.parentNode;
853   var oldTarget = container.activeTab.getAttribute("target");
854   var content = $(target);
855   var oldContent = $(oldTarget);
856
857   oldContent.removeClassName("active");
858   container.activeTab.removeClassName("active");
859   container.activeTab = node;
860   container.activeTab.addClassName("active");
861   content.addClassName("active");
862
863   return false;
864 }
865
866 function enableAnchor(anchor) {
867   var classStr = '' + anchor.getAttribute("class");
868   var position = classStr.indexOf("_disabled", 0);
869   if (position > -1) {
870     var disabledHref = anchor.getAttribute("disabled-href");
871     if (disabledHref)
872       anchor.setAttribute("href", disabledHref);
873     var disabledOnclick = anchor.getAttribute("disabled-onclick");
874     if (disabledOnclick)
875       anchor.setAttribute("onclick", disabledOnclick);
876     anchor.removeClassName("_disabled");
877     anchor.setAttribute("disabled-href", null);
878     anchor.setAttribute("disabled-onclick", null);
879     anchor.disabled = 0;
880     anchor.enabled = 1;
881   }
882 }
883
884 function disableAnchor(anchor) {
885   var classStr = '' + anchor.getAttribute("class");
886   var position = classStr.indexOf("_disabled", 0);
887   if (position < 0) {
888     var href = anchor.getAttribute("href");
889     if (href)
890       anchor.setAttribute("disabled-href", href);
891     var onclick = anchor.getAttribute("onclick");
892     if (onclick)
893       anchor.setAttribute("disabled-onclick", onclick);
894     anchor.addClassName("_disabled");
895     anchor.setAttribute("href", "#");
896     anchor.setAttribute("onclick", "return false;");
897     anchor.disabled = 1;
898     anchor.enabled = 0;
899   }
900 }
901
902 function d2h(d) {
903   var hD = "0123456789abcdef";
904   var h = hD.substr(d&15,1);
905   while (d>15) {
906     d>>=4;
907     h=hD.substr(d&15,1)+h;
908   }
909   return h;
910 }
911
912 function indexColor(number) {
913   var color;
914
915   if (number == 0)
916     color = "#ccf";
917   else {
918     var colorTable = new Array(1, 1, 1);
919     
920     var currentValue = number;
921     var index = 0;
922     while (currentValue)
923       {
924         if (currentValue & 1)
925           colorTable[index]++;
926       if (index == 3)
927         index = 0;
928       currentValue >>= 1;
929       index++;
930       }
931     
932     color = ("#"
933              + d2h((256 / colorTable[2]) - 1)
934              + d2h((256 / colorTable[1]) - 1)
935              + d2h((256 / colorTable[0]) - 1));
936   }
937
938   return color;
939 }
940
941 var onLoadHandler = {
942   handleEvent: function (event) {
943     queryParameters = parseQueryParameters('' + window.location);
944     if (!document.body.hasClassName("popup")) {
945       initLogConsole();
946       initializeMenus();
947       initCriteria();
948     }
949     initTabs();
950     configureDragHandles();
951     configureSortableTableHeaders();
952     configureLinkBanner();
953     var progressImage = $("progressIndicator");
954     if (progressImage)
955       progressImage.parentNode.removeChild(progressImage);
956   }
957 }
958
959 function configureSortableTableHeaders() {
960   var headers = document.getElementsByClassName("sortableTableHeader");
961   for (var i = 0; i < headers.length; i++) {
962     var anchor = headers[i].childNodesWithTag("a")[0];
963     if (!anchor.link) {
964       anchor.link = anchor.getAttribute("href");
965       anchor.href = "#";
966       anchor.addEventListener("click", onHeaderClick, true);
967     }
968   }
969 }
970
971 function onLinkBannerClick() {
972   activeAjaxRequests++;
973   checkAjaxRequestsState();
974 }
975
976 function configureLinkBanner() {
977   var linkBanner = $("linkBanner");
978   if (linkBanner) {
979     var anchors = linkBanner.childNodesWithTag("a");
980     for (var i = 0; i < 4; i++) {
981       anchors[i].addEventListener("mousedown", listRowMouseDownHandler,
982                                   false);
983       anchors[i].addEventListener("click", onLinkBannerClick, false);
984     }
985     if (anchors.length > 5)
986       anchors[5].addEventListener("click", toggleLogConsole, true);
987   }
988 }
989
990 window.addEventListener("load", onLoadHandler, false);
991
992 /* stubs */
993 function configureDragHandles() {
994 }
995
996 function initializeMenus() {
997 }
998
999 function onHeaderClick(event) {
1000   window.alert("generic headerClick");
1001 }