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