]> err.no Git - scalable-opengroupware.org/blob - UI/WebServerResources/generic.js
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1080 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 /* generic stuff */
24
25 var logConsole;
26 var logWindow = null;
27
28 var queryParameters;
29
30 var activeAjaxRequests = 0;
31 var menus = new Array();
32
33 var weekStartIsMonday = true;
34
35 // logArea = null;
36 var allDocumentElements = null;
37
38 /* a W3C compliant document.all */
39 function getAllScopeElements(scope) {
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   var elements;
58
59   if (scope == null)
60     scope = document;
61
62   if (scope == document
63       && allDocumentElements != null)
64     elements = allDocumentElements;
65   else
66     {
67       elements = getAllScopeElements(scope);
68       if (scope == document)
69         allDocumentElements = elements;
70     }
71
72   return elements;
73 }
74
75 /* from
76    http://www.robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/ */
77 function getElementsByClassName2(_tag, _class, _scope) {
78   var regexp, classes, elements, element, returnElements;
79
80   _scope = _scope || document;
81
82   elements = (!_tag || _tag == "*"
83               ? getAllElements(null)
84               : _scope.getElementsByTagName(_tag));
85   returnElements = [];
86
87   classes = _class.split(/\s+/);
88   regexp = new RegExp("(^|\s+)("+ classes.join("|") +")(\s+|$)","i");
89
90   if (_class) {
91     for(var i = 0; element = elements[i]; i++) {
92       if (regexp.test(element.className)) {
93         returnElements.push(element);
94       }
95     }
96     return returnElements;
97   } else {
98     return elements;
99   }
100 }
101
102 function ml_stripActionInURL(url) {
103   if (url[url.length - 1] != '/') {
104     var i;
105     
106     i = url.lastIndexOf("/");
107     if (i != -1) url = url.substring(0, i);
108   }
109   if (url[url.length - 1] != '/') // ensure trailing slash
110     url = url + "/";
111   return url;
112 }
113
114 function URLForFolderID(folderID) {
115    var folderInfos = folderID.split(":");
116    var url;
117    if (folderInfos.length > 1) {
118       url = UserFolderURL + "../" + folderInfos[0];
119       if (folderInfos[1][0] != '/')
120         url += '/';
121       url += folderInfos[1];
122    }
123    else
124       url = ApplicationBaseURL + folderInfos[0];
125    
126    if (url[url.length-1] == '/')
127       url = url.substr(0, url.length-1);
128
129    return url;
130 }
131
132 function extractEmailAddress(mailTo) {
133   var email = "";
134
135   var emailre
136     = /([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;
137   if (emailre.test(mailTo)) {
138     emailre.exec(mailTo);
139     email = RegExp.$1;
140   }
141
142   return email;
143 }
144
145 function extractEmailName(mailTo) {
146   var emailName = "";
147
148    var tmpMailTo = mailTo.replace("&lt;", "<");
149    tmpMailTo = tmpMailTo.replace("&gt;", ">");
150
151    var emailNamere = /([        ]+)?(.+)\ </;
152    if (emailNamere.test(tmpMailTo)) {
153       emailNamere.exec(tmpMailTo);
154       emailName = RegExp.$2;
155    }
156    
157    return emailName;
158 }
159
160 function sanitizeMailTo(dirtyMailTo) {
161    var emailName = extractEmailName(dirtyMailTo);
162    var email = "" + extractEmailAddress(dirtyMailTo);
163    
164    var mailto = "";
165    if (emailName && emailName.length > 0)
166       mailto = emailName + ' <' + email + '>';
167    else
168       mailto = email;
169
170    return mailto;
171 }
172
173 function openUserFolderSelector(callback, type) {
174    var urlstr = ApplicationBaseURL;
175    if (urlstr[urlstr.length-1] != '/')
176       urlstr += '/';
177    urlstr += ("../../" + UserLogin + "/Contacts/userFolders");
178    var w = window.open(urlstr, "User Selector",
179                        "width=322,height=250,resizable=1,scrollbars=0");
180    w.opener = window;
181    w.userFolderCallback = callback;
182    w.userFolderType = type;
183    w.focus();
184 }
185
186 function openMailComposeWindow(url) {
187   var w = window.open(url, null,
188                       "width=680,height=520,resizable=1,scrollbars=1,toolbar=0,"
189                       + "location=0,directories=0,status=0,menubar=0"
190                       + ",copyhistory=0");
191   w.focus();
192
193   return w;
194 }
195
196 function openMailTo(senderMailTo) {
197    var mailto = sanitizeMailTo(senderMailTo);
198    if (mailto.length > 0)
199       openMailComposeWindow(ApplicationBaseURL
200                             + "/../Mail/compose?mailto=" + mailto);
201
202   return false; /* stop following the link */
203 }
204
205 function createHTTPClient() {
206   // http://developer.apple.com/internet/webcontent/xmlhttpreq.html
207   if (typeof XMLHttpRequest != "undefined")
208     return new XMLHttpRequest();
209   
210   try { return new ActiveXObject("Msxml2.XMLHTTP"); } 
211   catch (e) { }
212   try { return new ActiveXObject("Microsoft.XMLHTTP"); } 
213   catch (e) { }
214
215   return null;
216 }
217
218 function appendDifferentiator(url) {
219   var url_nocache = url;
220   var position = url.indexOf('?', 0);
221   if (position < 0)
222     url_nocache += '?';
223   else
224     url_nocache += '&';
225   url_nocache += 'differentiator=' + Math.floor(Math.random()*50000);
226
227   return url_nocache;
228 }
229
230 function triggerAjaxRequest(url, callback, userdata) {
231   var http = createHTTPClient();
232
233   activeAjaxRequests += 1;
234   document.animTimer = setTimeout("checkAjaxRequestsState();", 200);
235   //url = appendDifferentiator(url);
236
237   if (http) {
238     http.open("POST", url, true);
239     http.url = url;
240     http.onreadystatechange
241       = function() {
242         //log ("state changed (" + http.readyState + "): " + url);
243         try {
244           if (http.readyState == 4
245               && activeAjaxRequests > 0) {
246                 if (!http.aborted) {
247                   http.callbackData = userdata;
248                   callback(http);
249                 }
250                 activeAjaxRequests -= 1;
251                 checkAjaxRequestsState();
252               }
253         }
254         catch( e ) {
255           activeAjaxRequests -= 1;
256           checkAjaxRequestsState();
257           log("AJAX Request, Caught Exception: " + e.name);
258           log(e.message);
259           log(backtrace());
260         }
261       };
262     http.send(null);
263   }
264   else {
265     log("triggerAjaxRequest: error creating HTTP Client!");
266   }
267
268   return http;
269 }
270
271 function checkAjaxRequestsState() {
272   var toolbar = document.getElementById("toolbar");
273   if (toolbar) {
274     if (activeAjaxRequests > 0
275         && !document.busyAnim) {
276       var anim = document.createElement("img");
277       anim = $(anim);
278       document.busyAnim = anim;
279       anim.id = "progressIndicator";
280       anim.src = ResourcesURL + "/busy.gif";
281       anim.setStyle({ visibility: "hidden" });
282       toolbar.appendChild(anim);
283       anim.setStyle({ visibility: "visible" });
284     }
285     else if (activeAjaxRequests == 0
286              && document.busyAnim
287              && document.busyAnim.parentNode) {
288       document.busyAnim.parentNode.removeChild(document.busyAnim);
289       document.busyAnim = null;
290     }
291   }
292 }
293
294 function isSafari() {
295   //var agt = navigator.userAgent.toLowerCase();
296   //var is_safari = ((agt.indexOf('safari')!=-1)&&(agt.indexOf('mac')!=-1))?true:false;
297
298   return (navigator.vendor == "Apple Computer, Inc.");
299 }
300
301 function isHttpStatus204(status) {
302   return (status == 204 ||                                  // Firefox
303           (isSafari() && typeof(status) == 'undefined') ||  // Safari
304           status == 1223);                                  // IE
305 }
306
307 function getTarget(event) {
308   event = event || window.event;
309   if (event.target)
310     return event.target; // W3C DOM
311   else
312     return event.srcElement; // IE
313 }
314
315 function preventDefault(event) {
316   if (event.preventDefault)
317     event.preventDefault(); // W3C DOM
318
319   event.returnValue = false; // IE
320 }
321
322 function resetSelection(win) {
323   var t = "";
324   if (win && win.getSelection) {
325     t = win.getSelection().toString();
326     win.getSelection().removeAllRanges();
327   }
328   return t;
329 }
330
331 function refreshOpener() {
332   if (window.opener && !window.opener.closed) {
333     window.opener.location.reload();
334   }
335 }
336
337 /* query string */
338
339 function parseQueryString() {
340   var queryArray, queryDict
341   var key, value, s, idx;
342   queryDict.length = 0;
343   
344   queryDict  = new Array();
345   queryArray = location.search.substr(1).split('&');
346   for (var i in queryArray) {
347     if (!queryArray[i]) continue ;
348     s   = queryArray[i];
349     idx = s.indexOf("=");
350     if (idx == -1) {
351       key   = s;
352       value = "";
353     }
354     else {
355       key   = s.substr(0, idx);
356       value = unescape(s.substr(idx + 1));
357     }
358     
359     if (typeof queryDict[key] == 'undefined')
360       queryDict.length++;
361     
362     queryDict[key] = value;
363   }
364   return queryDict;
365 }
366
367 function generateQueryString(queryDict) {
368   var s = "";
369   for (var key in queryDict) {
370     if (s.length == 0)
371       s = "?";
372     else
373       s = s + "&";
374     s = s + key + "=" + escape(queryDict[key]);
375   }
376   return s;
377 }
378
379 function getQueryParaArray(s) {
380   if (s.charAt(0) == "?") s = s.substr(1, s.length - 1);
381   return s.split("&");
382 }
383
384 function getQueryParaValue(s, name) {
385   var t;
386   
387   t = getQueryParaArray(s);
388   for (var i = 0; i < t.length; i++) {
389     var s = t[i];
390     
391     if (s.indexOf(name) != 0)
392       continue;
393     
394     s = s.substr(name.length, s.length - name.length);
395     return decodeURIComponent(s);
396   }
397   return null;
398 }
399
400 /* opener callback */
401
402 function triggerOpenerCallback() {
403   /* this code has some issue if the folder has no proper trailing slash! */
404   if (window.opener && !window.opener.closed) {
405     var t, cburl;
406     
407     t = getQueryParaValue(window.location.search, "openerurl=");
408     cburl = window.opener.location.href;
409     if (cburl[cburl.length - 1] != "/") {
410       cburl = cburl.substr(0, cburl.lastIndexOf("/") + 1);
411     }
412     cburl = cburl + t;
413     window.opener.location.href = cburl;
414   }
415 }
416
417 /* selection mechanism */
418
419 function deselectAll(parent) {
420   for (var i = 0; i < parent.childNodes.length; i++) {
421     var node = parent.childNodes.item(i);
422     if (node.nodeType == 1)
423       $(node).deselect();
424   }
425 }
426
427 function isNodeSelected(node) {
428   return $(node).hasClassName('_selected');
429 }
430
431 function acceptMultiSelect(node) {
432    var response = false;
433    var attribute = node.getAttribute('multiselect');
434    if (attribute) {
435       log("node '" + node.getAttribute("id")
436           + "' is still using old-stylemultiselect!");
437       response = (attribute.toLowerCase() == 'yes');
438    }
439    else
440       response = node.multiselect;
441
442    return response;
443 }
444
445 function onRowClick(event) {
446   var node = getTarget(event);
447
448   if (node.tagName == 'TD')
449     node = node.parentNode;
450   var startSelection = $(node.parentNode).getSelectedNodes();
451   if (event.shiftKey == 1
452       && (acceptMultiSelect(node.parentNode)
453           || acceptMultiSelect(node.parentNode.parentNode))) {
454     if (isNodeSelected(node) == true) {
455       $(node).deselect();
456     } else {
457       $(node).select();
458     }
459   } else {
460     $(node.parentNode).deselectAll();
461     $(node).select();
462   }
463
464   if (startSelection != $(node.parentNode).getSelectedNodes()) {
465     var parentNode = node.parentNode;
466     if (parentNode.tagName == 'TBODY')
467       parentNode = parentNode.parentNode;
468     //log("onRowClick: parentNode = " + parentNode.tagName);
469     // parentNode is UL or TABLE
470     if (document.createEvent) {
471       var onSelectionChangeEvent;
472       if (isSafari())
473         onSelectionChangeEvent = document.createEvent("UIEvents");
474       else
475         onSelectionChangeEvent = document.createEvent("Events");
476       onSelectionChangeEvent.initEvent("mousedown", true, true);
477       parentNode.dispatchEvent(onSelectionChangeEvent);
478     }
479     else if (document.createEventObject) {
480       parentNode.fireEvent("onmousedown");
481     }
482   }
483
484   return true;
485 }
486
487 /* popup menus */
488
489 // var acceptClick = false;
490
491 function popupMenu(event, menuId, target) {
492    document.menuTarget = target;
493
494    if (document.currentPopupMenu)
495        hideMenu(event, document.currentPopupMenu);
496
497    var popup = $(menuId);
498    var menuTop = event.pageY;
499    var menuLeft = event.pageX;
500    var heightDiff = (window.innerHeight
501                      - (menuTop + popup.offsetHeight));
502    if (heightDiff < 0)
503       menuTop += heightDiff;
504   
505    var leftDiff = (window.innerWidth
506                    - (menuLeft + popup.offsetWidth));
507    if (leftDiff < 0)
508       menuLeft -= popup.offsetWidth;
509
510    popup.setStyle({ top: menuTop + "px",
511                     left: menuLeft + "px",
512                     visibility: "visible" });
513
514    document.currentPopupMenu = popup;
515    Event.observe(document.body, "click", onBodyClickMenuHandler);
516
517    preventDefault(event);
518 }
519
520 function getParentMenu(node) {
521   var currentNode, menuNode;
522
523   menuNode = null;
524   currentNode = node;
525   var menure = new RegExp("(^|\s+)menu(\s+|$)", "i");
526
527   while (menuNode == null
528          && currentNode)
529     if (menure.test(currentNode.className))
530       menuNode = currentNode;
531     else
532       currentNode = currentNode.parentNode;
533
534   return menuNode;
535 }
536
537 function onBodyClickMenuHandler(event) {
538    document.body.menuTarget = null;
539    hideMenu(event, document.currentPopupMenu);
540    Event.stopObserving(document.body, "click", onBodyClickMenuHandler);
541
542    preventDefault(event);
543 }
544
545 function hideMenu(event, menuNode) {
546   var onHide;
547
548 //   log('hiding menu "' + menuNode.getAttribute('id') + '"');
549   if (menuNode.submenu) {
550     hideMenu(event, menuNode.submenu);
551     menuNode.submenu = null;
552   }
553
554   menuNode.setStyle({ visibility: "hidden" });
555   //   menuNode.hide();
556   if (menuNode.parentMenuItem) {
557     menuNode.parentMenuItem.setAttribute('class', 'submenu');
558     menuNode.parentMenuItem = null;
559     menuNode.parentMenu.setAttribute('onmousemove', null);
560     menuNode.parentMenu.submenuItem = null;
561     menuNode.parentMenu.submenu = null;
562     menuNode.parentMenu = null;
563   }
564
565   if (document.initEvent) {
566     var onhideEvent = document.createEvent("UIEvents");
567     onhideEvent.initEvent("hideMenu", false, true);
568     menuNode.dispatchEvent(onhideEvent);
569   }
570   else if (document.createEventObject) {
571     // TODO: add support for IE
572   }
573 }
574
575 function onMenuEntryClick(event) {
576   var node = event.target;
577
578   id = getParentMenu(node).menuTarget;
579 //   log("clicked " + id + "/" + id.tagName);
580
581   return false;
582 }
583
584 function parseQueryParameters(url) {
585   var parameters = new Array();
586
587   var params = url.split("?")[1];
588   if (params) {
589     var pairs = params.split("&");
590     for (var i = 0; i < pairs.length; i++) {
591       var pair = pairs[i].split("=");
592       parameters[pair[0]] = pair[1];
593     }
594   }
595
596   return parameters;
597 }
598
599 function initLogConsole() {
600     var logConsole = $("logConsole");
601     if (logConsole) {
602         logConsole.highlighted = false;
603         Event.observe(logConsole, "dblclick", onLogDblClick, false);
604         logConsole.innerHTML = "";
605         Event.observe(window, "keydown", onBodyKeyDown);
606     }
607 }
608
609 function onBodyKeyDown(event) {
610     if (event.keyCode == 27) {
611         toggleLogConsole();
612         preventDefault(event);
613     }
614 }
615
616 function onLogDblClick(event) {
617   var logConsole = $("logConsole");
618   logConsole.innerHTML = "";
619 }
620
621 function toggleLogConsole(event) {
622   var logConsole = $("logConsole");
623   var display = '' + logConsole.style.display;
624   if (display.length == 0) {
625     logConsole.setStyle({ display: 'block' });
626   } else {
627     logConsole.setStyle({ display: '' });
628   }
629   if (event)
630       preventDefault(event);
631 }
632
633 function log(message) {
634   if (!logWindow) {
635     logWindow = window;
636     while (logWindow.opener)
637       logWindow = logWindow.opener;
638   }
639   var logConsole = logWindow.document.getElementById("logConsole");
640   if (logConsole) {
641       logConsole.highlighted = !logConsole.highlighted;
642       var logMessage = message.replace("<", "&lt;", "g");
643       logMessage = logMessage.replace(" ", "&nbsp;", "g");
644       logMessage = logMessage.replace("\r\n", "<br />\n", "g");
645       logMessage = logMessage.replace("\n", "<br />\n", "g");
646       logMessage += '<br />' + "\n";
647       if (logConsole.highlighted)
648           logMessage = '<div class="highlighted">' + logMessage + '</div>';
649       logConsole.innerHTML += logMessage;
650   }
651 }
652
653 function backtrace() {
654    var func = backtrace.caller;
655    var str = "backtrace:\n";
656
657    while (func)
658    {
659       if (func.name)
660       {
661          str += "  " + func.name;
662          if (this)
663             str += " (" + this + ")";
664       }
665       else
666          str += "[anonymous]\n";
667
668       str += "\n";
669       func = func.caller;
670    }
671    str += "--\n";
672
673    return str;
674 }
675
676 function dropDownSubmenu(event) {
677    var node = this;
678    if (this.submenu && this.submenu != "") {
679       log ("submenu: " + this.submenu);
680       var submenuNode = $(this.submenu);
681       var parentNode = getParentMenu(node);
682       if (parentNode.submenu)
683          hideMenu(event, parentNode.submenu);
684       submenuNode.parentMenuItem = node;
685       submenuNode.parentMenu = parentNode;
686       parentNode.submenuItem = node;
687       parentNode.submenu = submenuNode;
688       
689       var menuTop = (node.offsetTop - 2);
690       
691       var heightDiff = (window.innerHeight
692                         - (menuTop + submenuNode.offsetHeight));
693       if (heightDiff < 0)
694          menuTop += heightDiff;
695       
696       var menuLeft = parentNode.offsetWidth - 3;
697       if (window.innerWidth
698           < (menuLeft + submenuNode.offsetWidth
699              + parentNode.cascadeLeftOffset()))
700          menuLeft = - submenuNode.offsetWidth + 3;
701       
702       parentNode.setAttribute('onmousemove', 'checkDropDown(event);');
703       node.setAttribute('class', 'submenu-selected');
704       submenuNode.setStyle({ top: menuTop + "px",
705                                      left: menuLeft + "px",
706                                      visibility: "visible" });
707    }
708 }
709
710 function checkDropDown(event) {
711   var parentMenu = getParentMenu(event.target);
712   var submenuItem = parentMenu.submenuItem;
713   if (submenuItem) {
714     var menuX = event.clientX - parentMenu.cascadeLeftOffset();
715     var menuY = event.clientY - parentMenu.cascadeTopOffset();
716     var itemX = submenuItem.offsetLeft;
717     var itemY = submenuItem.offsetTop - 75;
718
719     if (menuX >= itemX
720         && menuX < itemX + submenuItem.offsetWidth
721         && (menuY < itemY
722             || menuY > (itemY + submenuItem.offsetHeight))) {
723       hideMenu(event, parentMenu.submenu);
724       parentMenu.submenu = null;
725       parentMenu.submenuItem = null;
726       parentMenu.setAttribute('onmousemove', null);
727     }
728   }
729 }
730
731 /* search field */
732 function popupSearchMenu(event) {
733   var node = getTarget(event);
734
735   var menuId = this.getAttribute("menuid");
736   relX = event.pageX - node.cascadeLeftOffset();
737   relY = event.pageY - node.cascadeTopOffset();
738
739   if (event.button == 0
740       && relX < 24) {
741     event.cancelBubble = true;
742     event.returnValue = false;
743
744     if (document.currentPopupMenu)
745       hideMenu(event, document.currentPopupMenu);
746
747     var popup = document.getElementById(menuId);
748     popup.setStyle({ top: node.offsetHeight + "px",
749                      left: (node.offsetLeft + 3) + "px",
750                             visibility: "visible" });
751   
752     document.currentPopupMenu = popup;
753     Event.observe(document.body, "click", onBodyClickMenuHandler);
754   }
755 }
756
757 function setSearchCriteria(event) {
758   searchValue = $("searchValue");
759   searchCriteria = $("searchCriteria");
760
761   searchValue.setAttribute("ghost-phrase", this.innerHTML);
762 //   searchCriteria = this.getAttribute('id');
763 }
764
765 function checkSearchValue(event) {
766   var form = event.target;
767   var searchValue = $("searchValue");
768   var ghostPhrase = searchValue.getAttribute('ghost-phrase');
769
770   if (searchValue.value == ghostPhrase)
771     searchValue.value = "";
772 }
773
774 function onSearchChange() {
775   log ("onSearchChange()...");
776 }
777
778 function onSearchMouseDown(event) {
779    var superNode = this.parentNode.parentNode.parentNode;
780    relX = (event.pageX - superNode.offsetLeft - this.offsetLeft);
781    relY = (event.pageY - superNode.offsetTop - this.offsetTop);
782
783    if (relY < 24) {
784       event.cancelBubble = true;
785       event.returnValue = false;
786    }
787 }
788
789 function onSearchFocus() {
790   ghostPhrase = this.getAttribute("ghost-phrase");
791   if (this.value == ghostPhrase) {
792     this.value = "";
793     this.setAttribute("modified", "");
794   } else {
795     this.select();
796   }
797
798   this.setStyle({ color: "#000" });
799 }
800
801 function onSearchBlur(event) {
802   var ghostPhrase = this.getAttribute("ghost-phrase");
803 //   log ("search blur: '" + this.value + "'");
804   if (!this.value) {
805     this.setAttribute("modified", "");
806     this.setStyle({ color: "#aaa" });
807     this.value = ghostPhrase;
808   } else if (this.value == ghostPhrase) {
809     this.setAttribute("modified", "");
810     this.setStyle({ color: "#aaa" });
811   } else {
812     this.setAttribute("modified", "yes");
813     this.setStyle({ color: "#000" });
814   }
815 }
816
817 function onSearchKeyDown(event) {
818   if (this.timer)
819     clearTimeout(this.timer);
820
821   this.timer = setTimeout("onSearchFormSubmit()", 1000);
822 }
823
824 function initCriteria() {
825   var searchCriteria = $("searchCriteria");
826   var searchValue = $("searchValue");
827   var firstOption;
828  
829   var searchOptions = $("searchOptions");
830   if (searchOptions) {
831     var firstOption;
832     $A(searchOptions.childNodes).each(function (item) {
833       if (item.tagName == 'LI') {
834         firstOption = item;
835       }
836     });
837     searchCriteria.value = firstOption.getAttribute('id');
838     searchValue.setAttribute('ghost-phrase', firstOption.innerHTML);
839     if (searchValue.value == '') {
840       searchValue.value = firstOption.innerHTML;
841       searchValue.setAttribute("modified", "");
842       searchValue.setStyle({ color: "#aaa" });
843     }
844   }
845 }
846
847 /* toolbar buttons */
848 function popupToolbarMenu(event, menuId) {
849    var toolbar = $("toolbar");
850    var node = getTarget(event);
851    if (node.tagName != 'A')
852       node = node.getParentWithTagName("a");
853    node = node.childNodesWithTag("span")[0];
854
855    if (event.button == 0) {
856       event.cancelBubble = true;
857       event.returnValue = false;
858       
859       if (document.currentPopupMenu)
860          hideMenu(event, document.currentPopupMenu);
861       
862       var popup = document.getElementById(menuId);
863       var top = node.offsetTop + node.offsetHeight - 2;
864       popup.setStyle({ top: top + "px",
865                        left: node.cascadeLeftOffset() + "px",
866                        visibility: "visible" });
867
868       document.currentPopupMenu = popup;
869       Event.observe(document.body, "click", onBodyClickMenuHandler);
870    }
871 }
872
873 /* contact selector */
874
875 function folderSubscriptionCallback(http) {
876    if (http.readyState == 4) {
877       if (http.status == 204) {
878          if (http.callbackData)
879             http.callbackData["method"](http.callbackData["data"]);
880       }
881       else
882          window.alert(labels["Unable to subscribe to that folder!"].decodeEntities());
883       document.subscriptionAjaxRequest = null;
884    }
885    else
886       log ("ajax fuckage");
887 }
888
889 function subscribeToFolder(refreshCallback, refreshCallbackData) {
890    var folderData = refreshCallbackData["folder"].split(":");
891    var username = folderData[0];
892    var folderPath = folderData[1];
893    if (username != UserLogin) {
894       var url = (UserFolderURL + "../" + username
895                  + folderPath + "/subscribe");
896       if (document.subscriptionAjaxRequest) {
897          document.subscriptionAjaxRequest.aborted = true;
898          document.subscriptionAjaxRequest.abort();
899       }
900       var rfCbData = { method: refreshCallback, data: refreshCallbackData };
901       document.subscriptionAjaxRequest = triggerAjaxRequest(url,
902                                                             folderSubscriptionCallback,
903                                                             rfCbData);
904    }
905    else
906       window.alert(labels["You cannot subscribe to a folder that you own!"]
907                    .decodeEntities());
908 }
909
910 function folderUnsubscriptionCallback(http) {
911    if (http.readyState == 4) {
912       if (http.status == 204) {
913          if (http.callbackData)
914             http.callbackData["method"](http.callbackData["data"]);
915       }
916       else
917          window.alert(labels["Unable to unsubscribe from that folder!"].decodeEntities());
918       document.unsubscriptionAjaxRequest = null;
919    }
920 }
921
922 function unsubscribeFromFolder(folder, refreshCallback, refreshCallbackData) {
923    if (document.body.hasClassName("popup")) {
924       window.opener.unsubscribeFromFolder(folder, refreshCallback,
925                                           refreshCallbackData);
926    }
927    else {
928       var folderData = folder.split(":");
929       var username = folderData[0];
930       var folderPath = folderData[1];
931       if (username != UserLogin) {
932          var url = (UserFolderURL + "../" + username
933                     + "/" + folderPath + "/unsubscribe");
934          if (document.unsubscriptionAjaxRequest) {
935             document.unsubscriptionAjaxRequest.aborted = true;
936             document.unsubscriptionAjaxRequest.abort();
937          }
938          var rfCbData = { method: refreshCallback, data: refreshCallbackData };
939          document.unsubscriptionAjaxRequest
940             = triggerAjaxRequest(url, folderUnsubscriptionCallback,
941                                  rfCbData);
942       }
943       else
944          window.alert(labels["You cannot unsubscribe from a folder that you own!"].decodeEntities());
945    }
946 }
947
948 function listRowMouseDownHandler(event) {
949   preventDefault(event);
950 }
951
952 /* tabs */
953 function initTabs() {
954   var containers = document.getElementsByClassName("tabsContainer");
955   for (var x = 0; x < containers.length; x++) {
956     var container = containers[x];
957     var firstTab = null;
958     for (var i = 0; i < container.childNodes.length; i++) {
959       if (container.childNodes[i].tagName == 'UL') {
960         if (!firstTab)
961           firstTab = i;
962       }
963     }
964     var nodes = container.childNodes[firstTab].childNodes;
965     
966     firstTab = null;
967     for (var i = 0; i < nodes.length; i++) {
968         var currentNode = nodes[i];
969         if (currentNode.tagName == 'LI') {
970             if (!firstTab)
971                 firstTab = i;
972             Event.observe(currentNode, "mousedown",
973                           onTabMouseDown.bindAsEventListener(currentNode));
974             Event.observe(currentNode, "click",
975                           onTabClick.bindAsEventListener(currentNode));
976             //$(currentNode.getAttribute("target")).hide();
977         }
978     }
979
980     nodes[firstTab].addClassName("first");
981     nodes[firstTab].addClassName("active");
982     container.activeTab = nodes[firstTab];
983
984     var target = $(nodes[firstTab].getAttribute("target"));
985     target.addClassName("active");
986     //target.show();
987   }
988 }
989
990 function initMenus() {
991    var menus = getMenus();
992    if (menus) {
993       for (var menuID in menus) {
994          var menuDIV = $(menuID);
995          if (menuDIV)
996             initMenu(menuDIV, menus[menuID]);
997       }
998    }
999 }
1000
1001 function initMenu(menuDIV, callbacks) {
1002    var lis = $(menuDIV.childNodesWithTag("ul")[0]).childNodesWithTag("li");
1003    for (var j = 0; j < lis.length; j++) {
1004       var node = lis[j];
1005       Event.observe(node, "mousedown", listRowMouseDownHandler, false);
1006       var callback = callbacks[j];
1007       if (callback) {
1008          if (typeof(callback) == "string") {
1009             if (callback == "-")
1010                node.addClassName("separator");
1011             else {
1012                node.submenu = callback;
1013                node.addClassName("submenu");
1014                Event.observe(node, "mouseover", dropDownSubmenu);
1015             }
1016          }
1017          else
1018             Event.observe(node, "mouseup",
1019                           $(callback).bindAsEventListener(node));
1020       }
1021       else
1022          node.addClassName("disabled");
1023    }
1024 }
1025
1026 function onTabMouseDown(event) {
1027   event.cancelBubble = true;
1028   preventDefault(event);
1029 }
1030
1031 function openExternalLink(anchor) {
1032   return false;
1033 }
1034
1035 function openAclWindow(url) {
1036   var w = window.open(url, "aclWindow",
1037                       "width=420,height=300,resizable=1,scrollbars=1,toolbar=0,"
1038                       + "location=0,directories=0,status=0,menubar=0"
1039                       + ",copyhistory=0");
1040   w.opener = window;
1041   w.focus();
1042
1043   return w;
1044 }
1045
1046 function getUsersRightsWindowHeight() {
1047    return usersRightsWindowHeight;
1048 }
1049
1050 function getUsersRightsWindowWidth() {
1051    return usersRightsWindowWidth;
1052 }
1053
1054 function getTopWindow() {
1055    var topWindow = null;
1056    var currentWindow = window;
1057    while (!topWindow) {
1058       if (currentWindow.document.body.hasClassName("popup")
1059           && currentWindow.opener)
1060          currentWindow = currentWindow.opener;
1061       else
1062          topWindow = currentWindow;
1063    }
1064
1065    return topWindow;
1066 }
1067
1068 function onTabClick(event) {
1069   var node = getTarget(event); // LI element
1070
1071   var target = node.getAttribute("target");
1072
1073   var container = node.parentNode.parentNode;
1074   var oldTarget = container.activeTab.getAttribute("target");
1075   var content = $(target);
1076   var oldContent = $(oldTarget);
1077
1078   oldContent.removeClassName("active");
1079   container.activeTab.removeClassName("active"); // previous LI
1080   container.activeTab = node;
1081   container.activeTab.addClassName("active"); // current LI
1082   content.addClassName("active");
1083   
1084   // Prototype alternative
1085
1086   //oldContent.removeClassName("active");
1087   //container.activeTab.removeClassName("active"); // previous LI
1088   //container.activeTab = node;
1089   //container.activeTab.addClassName("active"); // current LI
1090
1091   //container.activeTab.hide();
1092   //oldContent.hide();
1093   //content.show();
1094
1095   //container.activeTab = node;
1096   //container.activeTab.show();
1097
1098   return false;
1099 }
1100
1101 function enableAnchor(anchor) {
1102   var classStr = '' + anchor.getAttribute("class");
1103   var position = classStr.indexOf("_disabled", 0);
1104   if (position > -1) {
1105     var disabledHref = anchor.getAttribute("disabled-href");
1106     if (disabledHref)
1107       anchor.setAttribute("href", disabledHref);
1108     var disabledOnclick = anchor.getAttribute("disabled-onclick");
1109     if (disabledOnclick)
1110       anchor.setAttribute("onclick", disabledOnclick);
1111     anchor.removeClassName("_disabled");
1112     anchor.setAttribute("disabled-href", null);
1113     anchor.setAttribute("disabled-onclick", null);
1114     anchor.disabled = 0;
1115     anchor.enabled = 1;
1116   }
1117 }
1118
1119 function disableAnchor(anchor) {
1120   var classStr = '' + anchor.getAttribute("class");
1121   var position = classStr.indexOf("_disabled", 0);
1122   if (position < 0) {
1123     var href = anchor.getAttribute("href");
1124     if (href)
1125       anchor.setAttribute("disabled-href", href);
1126     var onclick = anchor.getAttribute("onclick");
1127     if (onclick)
1128       anchor.setAttribute("disabled-onclick", onclick);
1129     anchor.addClassName("_disabled");
1130     anchor.setAttribute("href", "#");
1131     anchor.setAttribute("onclick", "return false;");
1132     anchor.disabled = 1;
1133     anchor.enabled = 0;
1134   }
1135 }
1136
1137 function d2h(d) {
1138   var hD = "0123456789abcdef";
1139   var h = hD.substr(d & 15, 1);
1140
1141   while (d > 15) {
1142     d >>= 4;
1143     h = hD.substr(d & 15, 1) + h;
1144   }
1145
1146   return h;
1147 }
1148
1149 function indexColor(number) {
1150   var color;
1151
1152   if (number == 0)
1153     color = "#ccf";
1154   else {
1155     var colorTable = new Array(1, 1, 1);
1156     
1157     var currentValue = number;
1158     var index = 0;
1159     while (currentValue) {
1160        if (currentValue & 1)
1161           colorTable[index]++;
1162        if (index == 3)
1163           index = 0;
1164        currentValue >>= 1;
1165        index++;
1166     }
1167     
1168     color = ("#"
1169              + d2h((256 / colorTable[2]) - 1)
1170              + d2h((256 / colorTable[1]) - 1)
1171              + d2h((256 / colorTable[0]) - 1));
1172   }
1173
1174   return color;
1175 }
1176
1177 function onLoadHandler(event) {
1178     queryParameters = parseQueryParameters('' + window.location);
1179     if (!$(document.body).hasClassName("popup")) {
1180         initLogConsole();
1181         initCriteria();
1182     }
1183     initMenus();
1184     initTabs();
1185     configureDragHandles();
1186     configureSortableTableHeaders();
1187     configureLinkBanner();
1188     var progressImage = $("progressIndicator");
1189     if (progressImage)
1190         progressImage.parentNode.removeChild(progressImage);
1191     Event.observe(document.body, "contextmenu", onBodyClickContextMenu);
1192 }
1193
1194 function onBodyClickContextMenu(event) {
1195   preventDefault(event);
1196 }
1197
1198 function configureSortableTableHeaders() {
1199   var headers = document.getElementsByClassName("sortableTableHeader");
1200   for (var i = 0; i < headers.length; i++) {
1201      var header = headers[i];
1202      var anchor = $(header).childNodesWithTag("a")[0];
1203      if (anchor)
1204         Event.observe(anchor, "click",
1205                       onHeaderClick.bindAsEventListener(anchor));
1206   }
1207 }
1208
1209 function onLinkBannerClick() {
1210   activeAjaxRequests++;
1211   checkAjaxRequestsState();
1212 }
1213
1214 function configureLinkBanner() {
1215   var linkBanner = $("linkBanner");
1216   if (linkBanner) {
1217     var anchors = linkBanner.childNodesWithTag("a");
1218     for (var i = 0; i < 2; i++) {
1219        Event.observe(anchors[i], "mousedown", listRowMouseDownHandler);
1220        Event.observe(anchors[i], "click", onLinkBannerClick);
1221     }
1222     if (anchors.length > 3)
1223        Event.observe(anchors[3], "click", toggleLogConsole);
1224   }
1225 }
1226
1227 addEvent(window, 'load', onLoadHandler);
1228
1229 /* stubs */
1230 function configureDragHandles() {
1231 }
1232
1233 function getMenus() {
1234 }
1235
1236 function onHeaderClick(event) {
1237    window.alert("generic headerClick");
1238 }
1239
1240 function parent$(element) {
1241    return this.opener.document.getElementById(element);
1242 }