2 Copyright (C) 2005 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
21 /* some generic JavaScript code for SOGo */
23 // TODO: replace things with Prototype where applicable
32 var activeAjaxRequests = 0;
35 var allDocumentElements = null;
37 /* a W3C compliant document.all */
38 function getAllScopeElements(scope)
40 var elements = new Array();
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 != '')
47 elements.push(scope.childNodes[i]);
48 var childElements = getAllElements(scope.childNodes[i]);
49 if (childElements.length > 0)
50 elements.push(childElements);
56 function getAllElements(scope)
64 && allDocumentElements != null)
65 elements = allDocumentElements;
68 elements = getAllScopeElements(scope);
69 if (scope == document)
70 allDocumentElements = elements;
77 http://www.robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/ */
78 function getElementsByClassName2(_tag, _class, _scope) {
79 var regexp, classes, elements, element, returnElements;
81 _scope = _scope || document;
83 elements = (!_tag || _tag == "*"
84 ? getAllElements(null)
85 : _scope.getElementsByTagName(_tag));
88 classes = _class.split(/\s+/);
89 regexp = new RegExp("(^|\s+)("+ classes.join("|") +")(\s+|$)","i");
92 for(var i = 0; element = elements[i]; i++) {
93 if (regexp.test(element.className)) {
94 returnElements.push(element);
97 return returnElements;
103 function ml_stripActionInURL(url) {
104 if (url[url.length - 1] != '/') {
107 i = url.lastIndexOf("/");
108 if (i != -1) url = url.substring(0, i);
110 if (url[url.length - 1] != '/') // ensure trailing slash
115 function extractEmailAddress(mailTo) {
119 = /([a-zA-Z0-9]+[a-zA-Z0-9\._-]+[a-zA-Z0-9]+@[a-zA-Z0-9]+[a-zA-Z0-9\._-]+[a-zA-Z0-9]+)/g;
120 if (emailre.test(mailTo)) {
121 emailre.exec(mailTo);
128 function extractEmailName(mailTo) {
131 var emailNamere = /(\w[\w\ _-]+)\ (<|<)/;
132 if (emailNamere.test(mailTo)) {
133 emailNamere.exec(mailTo);
134 emailName = RegExp.$1;
138 function sanitizeMailTo(dirtyMailTo) {
139 var emailName = extractEmailName(dirtyMailTo);
140 var email = "" + extractEmailAddress(dirtyMailTo);
143 if (emailName && emailName.length > 0)
144 mailto = emailName + ' <' + email + '>';
151 function openMailComposeWindow(url) {
152 var w = window.open(url, null,
153 "width=680,height=520,resizable=1,scrollbars=1,toolbar=0,"
154 + "location=0,directories=0,status=0,menubar=0"
161 function openMailTo(senderMailto) {
162 var mailto = sanitizeMailTo(senderMailto);
164 if (mailto.length > 0)
165 openMailComposeWindow(ApplicationBaseURL
166 + "/../Mail/compose?mailto=" + mailto);
168 return false; /* stop following the link */
171 function createHTTPClient() {
172 // http://developer.apple.com/internet/webcontent/xmlhttpreq.html
173 if (typeof XMLHttpRequest != "undefined")
174 return new XMLHttpRequest();
176 try { return new ActiveXObject("Msxml2.XMLHTTP"); }
178 try { return new ActiveXObject("Microsoft.XMLHTTP"); }
184 function triggerAjaxRequest(url, callback, userdata) {
185 var http = createHTTPClient();
187 activeAjaxRequests += 1;
188 document.animTimer = setTimeout("checkAjaxRequestsState();", 200);
191 http.onreadystatechange
193 // log ("state changed (" + http.readyState + "): " + url);
195 if (http.readyState == 4
196 && activeAjaxRequests > 0) {
198 http.callbackData = userdata;
201 activeAjaxRequests -= 1;
202 checkAjaxRequestsState();
206 activeAjaxRequests -= 1;
207 checkAjaxRequestsState();
208 log("AJAX Request, Caught Exception: " + e.name);
213 http.open("GET", url, true);
220 function checkAjaxRequestsState() {
221 var toolbar = document.getElementById("toolbar");
223 if (activeAjaxRequests > 0
224 && !document.busyAnim) {
225 var anim = document.createElement("img");
226 document.busyAnim = anim;
227 anim.id = "progressIndicator";
228 anim.src = ResourcesURL + "/busy.gif";
229 anim.style.visibility = "hidden;";
230 toolbar.appendChild(anim);
231 anim.style.visibility = "visible;";
233 else if (activeAjaxRequests == 0
234 && document.busyAnim) {
235 document.busyAnim.parentNode.removeChild(document.busyAnim);
236 document.busyAnim = null;
241 function resetSelection(win) {
243 if (win && win.getSelection) {
244 t = win.getSelection().toString();
245 win.getSelection().removeAllRanges();
250 function refreshOpener() {
251 if (window.opener && !window.opener.closed) {
252 window.opener.location.reload();
258 function parseQueryString() {
259 var queryArray, queryDict
260 var key, value, s, idx;
261 queryDict.length = 0;
263 queryDict = new Array();
264 queryArray = location.search.substr(1).split('&');
265 for (var i in queryArray) {
266 if (!queryArray[i]) continue ;
268 idx = s.indexOf("=");
274 key = s.substr(0, idx);
275 value = unescape(s.substr(idx + 1));
278 if (typeof queryDict[key] == 'undefined')
281 queryDict[key] = value;
286 function generateQueryString(queryDict) {
288 for (var key in queryDict) {
293 s = s + key + "=" + escape(queryDict[key]);
298 function getQueryParaArray(s) {
299 if (s.charAt(0) == "?") s = s.substr(1, s.length - 1);
303 function getQueryParaValue(s, name) {
306 t = getQueryParaArray(s);
307 for (var i = 0; i < t.length; i++) {
310 if (s.indexOf(name) != 0)
313 s = s.substr(name.length, s.length - name.length);
314 return decodeURIComponent(s);
319 /* opener callback */
321 function triggerOpenerCallback() {
322 /* this code has some issue if the folder has no proper trailing slash! */
323 if (window.opener && !window.opener.closed) {
326 t = getQueryParaValue(window.location.search, "openerurl=");
327 cburl = window.opener.location.href;
328 if (cburl[cburl.length - 1] != "/") {
329 cburl = cburl.substr(0, cburl.lastIndexOf("/") + 1);
332 window.opener.location.href = cburl;
336 /* selection mechanism */
338 function deselectAll(parent) {
339 for (var i = 0; i < parent.childNodes.length; i++) {
340 var node = parent.childNodes.item(i);
341 if (node.nodeType == 1)
346 function isNodeSelected(node) {
347 var classStr = '' + node.getAttribute('class');
348 var position = classStr.indexOf('_selected', 0);
350 return (position > -1);
353 function acceptMultiSelect(node) {
354 var accept = ('' + node.getAttribute('multiselect')).toLowerCase();
356 return (accept == 'yes');
359 function onRowClick(event) {
360 var node = event.target;
362 if (node.tagName == 'TD')
363 node = node.parentNode;
365 var startSelection = node.parentNode.getSelectedNodes();
366 if (event.shiftKey == 1
367 && (acceptMultiSelect(node.parentNode)
368 || acceptMultiSelect(node.parentNode.parentNode))) {
369 if (isNodeSelected(node) == true) {
375 deselectAll(node.parentNode);
379 if (startSelection != node.parentNode.getSelectedNodes()) {
380 var parentNode = node.parentNode;
381 if (parentNode instanceof HTMLTableSectionElement)
382 parentNode = parentNode.parentNode;
383 var onSelectionChangeEvent = document.createEvent("Event");
384 onSelectionChangeEvent.initEvent("selectionchange", true, true);
385 parentNode.dispatchEvent(onSelectionChangeEvent);
391 var bodyOnClick = "";
392 // var acceptClick = false;
394 function onMenuClick(event, menuId) {
395 var node = event.target;
397 if (document.currentPopupMenu)
398 hideMenu(event, document.currentPopupMenu);
400 var popup = document.getElementById(menuId);
402 var menuTop = event.pageY;
403 var menuLeft = event.pageX;
404 var heightDiff = (window.innerHeight
405 - (menuTop + popup.offsetHeight));
407 menuTop += heightDiff;
409 var leftDiff = (window.innerWidth
410 - (menuLeft + popup.offsetWidth));
412 menuLeft -= popup.offsetWidth;
414 popup.style.top = menuTop + "px;";
415 popup.style.left = menuLeft + "px;";
416 popup.style.visibility = "visible;";
417 setupMenuTarget(popup, node);
419 bodyOnClick = "" + document.body.getAttribute("onclick");
420 document.body.setAttribute("onclick", "onBodyClick(event);");
421 document.currentPopupMenu = popup;
423 event.cancelBubble = true;
424 event.returnValue = false;
429 function setupMenuTarget(menu, target) {
430 menu.menuTarget = target;
431 var menus = document.getElementsByClassName("menu", menu);
432 for (var i = 0; i < menus.length; i++) {
433 menus[i].menuTarget = target;
437 function getParentMenu(node) {
438 var currentNode, menuNode;
442 var menure = new RegExp("(^|\s+)menu(\s+|$)", "i");
444 while (menuNode == null
446 if (menure.test(currentNode.className))
447 menuNode = currentNode;
449 currentNode = currentNode.parentNode;
454 function onBodyClick(event) {
455 document.currentPopupMenu.menuTarget = null;
456 hideMenu(event, document.currentPopupMenu);
457 document.body.setAttribute("onclick", bodyOnClick);
462 function hideMenu(event, menuNode) {
465 // log('hiding menu "' + menuNode.getAttribute('id') + '"');
466 if (menuNode.submenu) {
467 hideMenu(event, menuNode.submenu);
468 menuNode.submenu = null;
471 menuNode.style.visibility = "hidden";
472 if (menuNode.parentMenuItem) {
473 menuNode.parentMenuItem.setAttribute('class', 'submenu');
474 menuNode.parentMenuItem = null;
475 menuNode.parentMenu.setAttribute('onmousemove', null);
476 menuNode.parentMenu.submenuItem = null;
477 menuNode.parentMenu.submenu = null;
478 menuNode.parentMenu = null;
481 var onhideEvent = document.createEvent("Event");
482 onhideEvent.initEvent("hideMenu", false, true);
483 menuNode.dispatchEvent(onhideEvent);
486 function onMenuEntryClick(event, menuId) {
487 var node = event.target;
489 id = getParentMenu(node).menuTarget;
490 // log("clicked " + id + "/" + id.tagName);
495 function parseQueryParameters(url) {
496 var parameters = new Array();
498 var params = url.split("?")[1];
500 var pairs = params.split("&");
501 for (var i = 0; i < pairs.length; i++) {
502 var pair = pairs[i].split("=");
503 parameters[pair[0]] = pair[1];
510 function initLogConsole() {
511 var logConsole = $("logConsole");
513 logConsole.addEventListener("dblclick", onLogDblClick, false);
514 logConsole.innerHTML = "";
515 node = document.getElementsByTagName('body')[0];
516 node.addEventListener("keydown", onBodyKeyDown, true);
520 function onBodyKeyDown(event) {
521 if (event.keyCode == 27) {
523 event.cancelBubble = true;
524 event.returnValue = false;
528 function onLogDblClick(event) {
529 var logConsole = $("logConsole");
530 logConsole.innerHTML = "";
533 function toggleLogConsole(event) {
534 var logConsole = $("logConsole");
535 var display = '' + logConsole.style.display;
536 if (display.length == 0) {
537 logConsole.style.display = 'block;';
539 logConsole.style.display = '';
541 event.cancelBubble = true;
542 event.returnValue = false;
543 event.preventDefault();
546 function log(message) {
549 while (logWindow.opener)
550 logWindow = logWindow.opener;
552 var logConsole = logWindow.document.getElementById("logConsole");
554 logConsole.innerHTML += message + '<br />' + "\n";
557 function dropDownSubmenu(event) {
558 var node = event.target;
559 var submenu = node.getAttribute("submenu");
560 if (submenu && submenu != "") {
561 var submenuNode = document.getElementById(submenu);
562 var parentNode = getParentMenu(node);
563 if (parentNode.submenu)
564 hideMenu(event, parentNode.submenu);
565 submenuNode.parentMenuItem = node;
566 submenuNode.parentMenu = parentNode;
567 parentNode.submenuItem = node;
568 parentNode.submenu = submenuNode;
570 var menuTop = (node.offsetTop - 2);
572 var heightDiff = (window.innerHeight
573 - (menuTop + submenuNode.offsetHeight));
575 menuTop += heightDiff;
577 var menuLeft = parentNode.offsetWidth - 3;
578 if (window.innerWidth
579 < (menuLeft + submenuNode.offsetWidth
580 + parentNode.cascadeLeftOffset()))
581 menuLeft = -submenuNode.offsetWidth + 3;
583 parentNode.setAttribute('onmousemove', 'checkDropDown(event);');
584 node.setAttribute('class', 'submenu-selected');
585 submenuNode.style.top = menuTop + "px;";
586 submenuNode.style.left = menuLeft + "px;";
587 submenuNode.style.visibility = "visible;";
591 function checkDropDown(event) {
592 var parentMenu = getParentMenu(event.target);
593 var submenuItem = parentMenu.submenuItem;
595 var menuX = event.clientX - parentMenu.cascadeLeftOffset();
596 var menuY = event.clientY - parentMenu.cascadeTopOffset();
597 var itemX = submenuItem.offsetLeft;
598 var itemY = submenuItem.offsetTop - 75;
601 && menuX < itemX + submenuItem.offsetWidth
603 || menuY > (itemY + submenuItem.offsetHeight))) {
604 hideMenu(event, parentMenu.submenu);
605 parentMenu.submenu = null;
606 parentMenu.submenuItem = null;
607 parentMenu.setAttribute('onmousemove', null);
613 function popupSearchMenu(event, menuId) {
614 var node = event.target;
615 relX = event.pageX - node.cascadeLeftOffset();
616 relY = event.pageY - node.cascadeTopOffset();
618 if (event.button == 0
620 event.cancelBubble = true;
621 event.returnValue = false;
623 if (document.currentPopupMenu)
624 hideMenu(event, document.currentPopupMenu);
626 var popup = document.getElementById(menuId);
627 popup.style.top = node.offsetHeight + "px";
628 popup.style.left = (node.offsetLeft + 3) + "px";
629 popup.style.visibility = "visible";
631 bodyOnClick = "" + document.body.getAttribute("onclick");
632 document.body.setAttribute("onclick", "onBodyClick('" + menuId + "');");
633 document.currentPopupMenu = popup;
637 function setSearchCriteria(event) {
638 searchValue = $("searchValue");
639 searchCriteria = $("searchCriteria");
641 var node = event.target;
642 searchValue.setAttribute("ghost-phrase", node.innerHTML);
643 searchCriteria = node.getAttribute('id');
646 function checkSearchValue(event) {
647 var form = event.target;
648 var searchValue = $("searchValue");
649 var ghostPhrase = searchValue.getAttribute('ghost-phrase');
651 if (searchValue.value == ghostPhrase)
652 searchValue.value = "";
655 function onSearchChange() {
656 log ("onSearchChange()...");
659 function onSearchMouseDown(event, searchValue) {
660 superNode = searchValue.parentNode.parentNode.parentNode;
661 relX = (event.pageX - superNode.offsetLeft - searchValue.offsetLeft);
662 relY = (event.pageY - superNode.offsetTop - searchValue.offsetTop);
665 event.cancelBubble = true;
666 event.returnValue = false;
670 function onSearchFocus(searchValue) {
671 ghostPhrase = searchValue.getAttribute("ghost-phrase");
672 if (searchValue.value == ghostPhrase) {
673 searchValue.value = "";
674 searchValue.setAttribute("modified", "");
676 searchValue.select();
679 searchValue.style.color = "#000";
682 function onSearchBlur(searchValue) {
683 var ghostPhrase = searchValue.getAttribute("ghost-phrase");
684 // log ("search blur: '" + searchValue.value + "'");
685 if (!searchValue.value) {
686 searchValue.setAttribute("modified", "");
687 searchValue.style.color = "#aaa";
688 searchValue.value = ghostPhrase;
689 } else if (searchValue.value == ghostPhrase) {
690 searchValue.setAttribute("modified", "");
691 searchValue.style.color = "#aaa";
693 searchValue.setAttribute("modified", "yes");
694 searchValue.style.color = "#000";
698 function onSearchKeyDown(searchValue) {
699 if (searchValue.timer)
700 clearTimeout(searchValue.timer);
702 searchValue.timer = setTimeout("onSearchFormSubmit()", 1000);
705 function initCriteria() {
706 var searchCriteria = $("searchCriteria");
707 var searchValue = $("searchValue");
710 var searchOptions = $("searchOptions");
712 firstOption = searchOptions.childNodes[1];
713 searchCriteria.value = firstOption.getAttribute('id');
714 searchValue.setAttribute('ghost-phrase', firstOption.innerHTML);
715 if (searchValue.value == '') {
716 searchValue.value = firstOption.innerHTML;
717 searchValue.setAttribute("modified", "");
718 searchValue.style.color = "#aaa";
723 /* contact selector */
725 function onContactAdd(node)
728 var selectorURL = '?popup=YES';
730 selector = node.parentNode.parentNode;
731 selectorURL += ("&selectorId=" + selector.getAttribute("id"));
734 urlstr = ApplicationBaseURL;
735 if (urlstr[urlstr.length-1] != '/')
737 urlstr += ("../../" + UserLogin + "/Contacts/"
738 + contactSelectorAction + selectorURL);
740 var w = window.open(urlstr, "Addressbook",
741 "width=640,height=400,resizable=1,scrollbars=0");
742 w.selector = selector;
749 function onContactRemove(node) {
750 var selector = node.parentNode.parentNode;
751 var selectorId = selector.getAttribute("id");
752 var hasChanged = false;
754 var names = $('uixselector-' + selectorId + '-display');
755 var nodes = names.getSelectedNodes();
756 hasChanged = (nodes.length > 0);
757 for (var i = 0; i < nodes.length; i++) {
758 var currentNode = nodes[i];
759 currentNode.parentNode.removeChild(currentNode);
762 var uids = $('uixselector-' + selectorId + '-uidList');
763 nodes = node.parentNode.childNodes;
764 var ids = new Array();
765 for (var i = 0; i < nodes.length; i++)
766 if (nodes[i] instanceof HTMLLIElement)
767 ids.push(nodes[i].getAttribute("uid"));
768 uids.value = ids.join(",");
770 if (selector.changeNotification && hasChanged)
771 selector.changeNotification("removal");
776 function listRowMouseDownHandler(event) {
777 event.preventDefault();
783 var containers = document.getElementsByClassName("tabsContainer");
784 for (var x = 0; x < containers.length; x++) {
785 var container = containers[x];
786 var nodes = container.childNodes[1].childNodes;
789 for (var i = 0; i < nodes.length; i++) {
790 if (nodes[i] instanceof HTMLLIElement) {
794 nodes[i].addEventListener("mousedown", onTabMouseDown, true);
795 nodes[i].addEventListener("click", onTabClick, true);
799 firstTab.addClassName("first");
800 firstTab.addClassName("active");
801 container.activeTab = firstTab;
803 var target = $(firstTab.getAttribute("target"));
804 target.addClassName("active");
808 function initMenusNamed(menuDivNames) {
809 for (var i = 0; i < menuDivNames.length; i++) {
810 var menuDIV = $(menuDivNames[i]);
814 log("menu named '" + menuDivNames[i] + "' not found");
818 function initMenu(menuDIV) {
819 var lis = menuDIV.childNodesWithTag("ul")[0].childNodesWithTag("li");
820 for (var j = 0; j < lis.length; j++)
821 lis[j].addEventListener("mousedown", listRowMouseDownHandler, false);
822 var subMenus = menuDIV.childNodesWithTag("div");
823 for (var i = 0; i < subMenus.length; i++)
824 initMenu(subMenus[i]);
827 function onTabMouseDown(event) {
828 event.cancelBubble = true;
829 event.preventDefault();
832 function openExternalLink(anchor) {
836 function openAclWindow(url, objectTitle) {
837 var w = window.open(url, "aclWindow",
838 "width=300,height=300,resizable=1,scrollbars=1,toolbar=0,"
839 + "location=0,directories=0,status=0,menubar=0"
842 w.title = "Poil: " + objectTitle;
847 function onTabClick(event) {
848 var node = event.target;
850 var target = node.getAttribute("target");
852 var container = node.parentNode.parentNode;
853 var oldTarget = container.activeTab.getAttribute("target");
854 var content = $(target);
855 var oldContent = $(oldTarget);
857 oldContent.removeClassName("active");
858 container.activeTab.removeClassName("active");
859 container.activeTab = node;
860 container.activeTab.addClassName("active");
861 content.addClassName("active");
866 function enableAnchor(anchor) {
867 var classStr = '' + anchor.getAttribute("class");
868 var position = classStr.indexOf("_disabled", 0);
870 var disabledHref = anchor.getAttribute("disabled-href");
872 anchor.setAttribute("href", disabledHref);
873 var disabledOnclick = anchor.getAttribute("disabled-onclick");
875 anchor.setAttribute("onclick", disabledOnclick);
876 anchor.removeClassName("_disabled");
877 anchor.setAttribute("disabled-href", null);
878 anchor.setAttribute("disabled-onclick", null);
884 function disableAnchor(anchor) {
885 var classStr = '' + anchor.getAttribute("class");
886 var position = classStr.indexOf("_disabled", 0);
888 var href = anchor.getAttribute("href");
890 anchor.setAttribute("disabled-href", href);
891 var onclick = anchor.getAttribute("onclick");
893 anchor.setAttribute("disabled-onclick", onclick);
894 anchor.addClassName("_disabled");
895 anchor.setAttribute("href", "#");
896 anchor.setAttribute("onclick", "return false;");
903 var hD = "0123456789abcdef";
904 var h = hD.substr(d&15,1);
907 h=hD.substr(d&15,1)+h;
912 function indexColor(number) {
918 var colorTable = new Array(1, 1, 1);
920 var currentValue = number;
924 if (currentValue & 1)
933 + d2h((256 / colorTable[2]) - 1)
934 + d2h((256 / colorTable[1]) - 1)
935 + d2h((256 / colorTable[0]) - 1));
941 var onLoadHandler = {
942 handleEvent: function (event) {
943 queryParameters = parseQueryParameters('' + window.location);
944 if (!document.body.hasClassName("popup")) {
950 configureDragHandles();
951 configureSortableTableHeaders();
952 configureLinkBanner();
953 var progressImage = $("progressIndicator");
955 progressImage.parentNode.removeChild(progressImage);
959 function configureSortableTableHeaders() {
960 var headers = document.getElementsByClassName("sortableTableHeader");
961 for (var i = 0; i < headers.length; i++) {
962 var anchor = headers[i].childNodesWithTag("a")[0];
964 anchor.link = anchor.getAttribute("href");
966 anchor.addEventListener("click", onHeaderClick, true);
971 function onLinkBannerClick() {
972 activeAjaxRequests++;
973 checkAjaxRequestsState();
976 function configureLinkBanner() {
977 var linkBanner = $("linkBanner");
979 var anchors = linkBanner.childNodesWithTag("a");
980 for (var i = 0; i < 4; i++) {
981 anchors[i].addEventListener("mousedown", listRowMouseDownHandler,
983 anchors[i].addEventListener("click", onLinkBannerClick, false);
985 if (anchors.length > 5)
986 anchors[5].addEventListener("click", toggleLogConsole, true);
990 window.addEventListener("load", onLoadHandler, false);
993 function configureDragHandles() {
996 function initializeMenus() {
999 function onHeaderClick(event) {
1000 window.alert("generic headerClick");