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