]> err.no Git - scalable-opengroupware.org/blobdiff - UI/WebServerResources/generic.js
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1152 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / WebServerResources / generic.js
index 8efe056e9d0cfefbd17b11885eda6e6d26b5a12e..0e5d7587fd1e7a9dd98085fab32f1d81a5348756 100644 (file)
@@ -20,8 +20,6 @@
 */
 /* some generic JavaScript code for SOGo */
 
-// TODO: replace things with Prototype where applicable
-
 /* generic stuff */
 
 var logConsole;
@@ -30,10 +28,18 @@ var logWindow = null;
 var queryParameters;
 
 var activeAjaxRequests = 0;
+var menus = new Array();
+var search = {};
+var sorting = {};
+
+var weekStartIsMonday = true;
 
 // logArea = null;
 var allDocumentElements = null;
 
+var userDefaults = null;
+var userSettings = null;
+
 /* a W3C compliant document.all */
 function getAllScopeElements(scope) {
   var elements = new Array();
@@ -98,6 +104,30 @@ function getElementsByClassName2(_tag, _class, _scope) {
   }
 }
 
+function createElement(tagName, id, classes, attributes, htmlAttributes,
+                      parentNode) {
+   var newElement = $(document.createElement(tagName));
+   if (id)
+      newElement.setAttribute("id", id);
+   if (classes) {
+      if (typeof(classes) == "string")
+        newElement.addClassName(classes);
+      else
+        for (var i = 0; i < classes.length; i++)
+           newElement.addClassName(classes[i]);
+   }
+   if (attributes)
+      for (var i in attributes)
+        newElement[i] = attributes[i];
+   if (htmlAttributes)
+      for (var i in htmlAttributes)
+        newElement.setAttribute(i, htmlAttributes[i]);
+   if (parentNode)
+      parentNode.appendChild(newElement);
+
+   return $(newElement);
+}
+
 function ml_stripActionInURL(url) {
   if (url[url.length - 1] != '/') {
     var i;
@@ -115,7 +145,7 @@ function URLForFolderID(folderID) {
    var url;
    if (folderInfos.length > 1) {
       url = UserFolderURL + "../" + folderInfos[0];
-      if (folderInfos[1][0] != '/')
+      if (!folderInfos[1].startsWith('/'))
         url += '/';
       url += folderInfos[1];
    }
@@ -144,43 +174,58 @@ function extractEmailAddress(mailTo) {
 function extractEmailName(mailTo) {
   var emailName = "";
 
-  var emailNamere = /(.+)\ </;
-  if (emailNamere.test(mailTo)) {
-    emailNamere.exec(mailTo);
-    emailName = RegExp.$1;
-  }
+   var tmpMailTo = mailTo.replace("&lt;", "<");
+   tmpMailTo = tmpMailTo.replace("&gt;", ">");
 
-  return emailName;
+   var emailNamere = /([       ]+)?(.+)\ </;
+   if (emailNamere.test(tmpMailTo)) {
+      emailNamere.exec(tmpMailTo);
+      emailName = RegExp.$2;
+   }
+   
+   return emailName;
 }
 
 function sanitizeMailTo(dirtyMailTo) {
-  var emailName = extractEmailName(dirtyMailTo);
-  var email = "" + extractEmailAddress(dirtyMailTo);
-
-  var mailto = "";
-  if (emailName && emailName.length > 0)
-    mailto = emailName + ' <' + email + '>';
-  else
-    mailto = email;
+   var emailName = extractEmailName(dirtyMailTo);
+   var email = "" + extractEmailAddress(dirtyMailTo);
+   
+   var mailto = "";
+   if (emailName && emailName.length > 0)
+      mailto = emailName + ' <' + email + '>';
+   else
+      mailto = email;
 
-  return mailto;
+   return mailto;
 }
 
 function openUserFolderSelector(callback, type) {
    var urlstr = ApplicationBaseURL;
-   if (urlstr[urlstr.length-1] != '/')
+   if (! urlstr.endsWith('/'))
       urlstr += '/';
    urlstr += ("../../" + UserLogin + "/Contacts/userFolders");
-   var w = window.open(urlstr, "User Selector",
+   var w = window.open(urlstr, "_blank",
                       "width=322,height=250,resizable=1,scrollbars=0");
    w.opener = window;
-   w.userFolderCallback = callback;
-   w.userFolderType = type;
+   window.userFolderCallback = callback;
+   window.userFolderType = type;
    w.focus();
 }
 
-function openMailComposeWindow(url) {
-  var w = window.open(url, null,
+function openContactWindow(url, wId) {
+  if (!wId)
+    wId = "" + (new Date().getTime());
+  var w = window.open(url, wId,
+                     "width=450,height=600,resizable=0");
+  w.focus();
+
+  return w;
+}
+
+function openMailComposeWindow(url, wId) {
+  if (!wId)
+    wId = "" + (new Date().getTime());
+  var w = window.open(url, wId,
                       "width=680,height=520,resizable=1,scrollbars=1,toolbar=0,"
                       + "location=0,directories=0,status=0,menubar=0"
                       + ",copyhistory=0");
@@ -189,12 +234,11 @@ function openMailComposeWindow(url) {
   return w;
 }
 
-function openMailTo(senderMailto) {
-  var mailto = sanitizeMailTo(senderMailto);
-
-  if (mailto.length > 0)
-    openMailComposeWindow(ApplicationBaseURL
-                          + "/../Mail/compose?mailto=" + mailto);
+function openMailTo(senderMailTo) {
+   var mailto = sanitizeMailTo(senderMailTo);
+   if (mailto.length > 0)
+      openMailComposeWindow(ApplicationBaseURL
+                           + "/../Mail/compose?mailto=" + mailto);
 
   return false; /* stop following the link */
 }
@@ -203,7 +247,7 @@ function createHTTPClient() {
   // http://developer.apple.com/internet/webcontent/xmlhttpreq.html
   if (typeof XMLHttpRequest != "undefined")
     return new XMLHttpRequest();
-  
+
   try { return new ActiveXObject("Msxml2.XMLHTTP"); } 
   catch (e) { }
   try { return new ActiveXObject("Microsoft.XMLHTTP"); } 
@@ -212,16 +256,31 @@ function createHTTPClient() {
   return null;
 }
 
+function appendDifferentiator(url) {
+  var url_nocache = url;
+  var position = url.indexOf('?', 0);
+  if (position < 0)
+    url_nocache += '?';
+  else
+    url_nocache += '&';
+  url_nocache += 'differentiator=' + Math.floor(Math.random()*50000);
+
+  return url_nocache;
+}
+
 function triggerAjaxRequest(url, callback, userdata) {
   var http = createHTTPClient();
 
   activeAjaxRequests += 1;
   document.animTimer = setTimeout("checkAjaxRequestsState();", 200);
+  //url = appendDifferentiator(url);
 
   if (http) {
+    http.open("POST", url, true);
+    http.url = url;
     http.onreadystatechange
       = function() {
-//         log ("state changed (" + http.readyState + "): " + url);
+        //log ("state changed (" + http.readyState + "): " + url);
         try {
           if (http.readyState == 4
               && activeAjaxRequests > 0) {
@@ -238,11 +297,13 @@ function triggerAjaxRequest(url, callback, userdata) {
           checkAjaxRequestsState();
           log("AJAX Request, Caught Exception: " + e.name);
           log(e.message);
+         log(backtrace());
         }
       };
-    http.url = url;
-    http.open("GET", url, true);
-    http.send("");
+    http.send(null);
+  }
+  else {
+    log("triggerAjaxRequest: error creating HTTP Client!");
   }
 
   return http;
@@ -254,21 +315,51 @@ function checkAjaxRequestsState() {
     if (activeAjaxRequests > 0
         && !document.busyAnim) {
       var anim = document.createElement("img");
+      anim = $(anim);
       document.busyAnim = anim;
       anim.id = "progressIndicator";
       anim.src = ResourcesURL + "/busy.gif";
-      anim.style.visibility = "hidden;";
+      anim.setStyle({ visibility: "hidden" });
       toolbar.appendChild(anim);
-      anim.style.visibility = "visible;";
+      anim.setStyle({ visibility: "visible" });
     }
     else if (activeAjaxRequests == 0
-            && document.busyAnim) {
+            && document.busyAnim
+            && document.busyAnim.parentNode) {
       document.busyAnim.parentNode.removeChild(document.busyAnim);
       document.busyAnim = null;
     }
   }
 }
 
+function isSafari() {
+  //var agt = navigator.userAgent.toLowerCase();
+  //var is_safari = ((agt.indexOf('safari')!=-1)&&(agt.indexOf('mac')!=-1))?true:false;
+
+  return (navigator.vendor == "Apple Computer, Inc.");
+}
+
+function isHttpStatus204(status) {
+  return (status == 204 ||                                  // Firefox
+         (isSafari() && typeof(status) == 'undefined') ||  // Safari
+          status == 1223);                                  // IE
+}
+
+function getTarget(event) {
+  event = event || window.event;
+  if (event.target)
+    return event.target; // W3C DOM
+  else
+    return event.srcElement; // IE
+}
+
+function preventDefault(event) {
+  if (event.preventDefault)
+    event.preventDefault(); // W3C DOM
+
+  event.returnValue = false; // IE
+}
+
 function resetSelection(win) {
   var t = "";
   if (win && win.getSelection) {
@@ -370,99 +461,102 @@ function deselectAll(parent) {
   for (var i = 0; i < parent.childNodes.length; i++) {
     var node = parent.childNodes.item(i);
     if (node.nodeType == 1)
-      node.deselect();
+      $(node).deselect();
   }
 }
 
 function isNodeSelected(node) {
-  var classStr = '' + node.getAttribute('class');
-  var position = classStr.indexOf('_selected', 0);
-
-  return (position > -1);
+  return $(node).hasClassName('_selected');
 }
 
 function acceptMultiSelect(node) {
-  var accept = ('' + node.getAttribute('multiselect')).toLowerCase();
+   var response = false;
+   var attribute = node.getAttribute('multiselect');
+   if (attribute) {
+      log("node '" + node.getAttribute("id")
+         + "' is still using old-stylemultiselect!");
+      response = (attribute.toLowerCase() == 'yes');
+   }
+   else
+      response = node.multiselect;
 
-  return (accept == 'yes');
+   return response;
 }
 
 function onRowClick(event) {
-  var node = event.target;
+  var node = getTarget(event);
 
   if (node.tagName == 'TD')
     node = node.parentNode;
-
-  var startSelection = node.parentNode.getSelectedNodes();
+  var startSelection = $(node.parentNode).getSelectedNodes();
   if (event.shiftKey == 1
       && (acceptMultiSelect(node.parentNode)
          || acceptMultiSelect(node.parentNode.parentNode))) {
     if (isNodeSelected(node) == true) {
-      node.deselect();
+      $(node).deselect();
     } else {
-      node.select();
+      $(node).select();
     }
   } else {
-    deselectAll(node.parentNode);
-    node.select();
+    $(node.parentNode).deselectAll();
+    $(node).select();
   }
 
-  if (startSelection != node.parentNode.getSelectedNodes()) {
+  if (startSelection != $(node.parentNode).getSelectedNodes()) {
     var parentNode = node.parentNode;
-    if (parentNode instanceof HTMLTableSectionElement)
+    if (parentNode.tagName == 'TBODY')
       parentNode = parentNode.parentNode;
-    var onSelectionChangeEvent = document.createEvent("Event");
-    onSelectionChangeEvent.initEvent("selectionchange", true, true);
-    parentNode.dispatchEvent(onSelectionChangeEvent);
+    //log("onRowClick: parentNode = " + parentNode.tagName);
+    // parentNode is UL or TABLE
+    if (document.createEvent) {
+      var onSelectionChangeEvent;
+      if (isSafari())
+       onSelectionChangeEvent = document.createEvent("UIEvents");
+      else
+       onSelectionChangeEvent = document.createEvent("Events");
+      onSelectionChangeEvent.initEvent("mousedown", true, true);
+      parentNode.dispatchEvent(onSelectionChangeEvent);
+    }
+    else if (document.createEventObject) {
+      parentNode.fireEvent("onmousedown");
+    }
   }
+
+  return true;
 }
 
 /* popup menus */
 
-var bodyOnClick = "";
 // var acceptClick = false;
 
-function onMenuClick(event, menuId) {
-  var node = event.target;
-
-  if (document.currentPopupMenu)
-    hideMenu(event, document.currentPopupMenu);
-
-  var popup = document.getElementById(menuId);
+function popupMenu(event, menuId, target) {
+   document.menuTarget = target;
 
-  var menuTop = event.pageY;
-  var menuLeft = event.pageX;
-  var heightDiff = (window.innerHeight
-                   - (menuTop + popup.offsetHeight));
-  if (heightDiff < 0)
-    menuTop += heightDiff;
+   if (document.currentPopupMenu)
+      hideMenu(document.currentPopupMenu);
 
-  var leftDiff = (window.innerWidth
-                 - (menuLeft + popup.offsetWidth));
-  if (leftDiff < 0)
-    menuLeft -= popup.offsetWidth;
+   var popup = $(menuId);
+   var menuTop =  Event.pointerY(event);
+   var menuLeft = Event.pointerX(event);
+   var heightDiff = (window.innerHeight
+                    - (menuTop + popup.offsetHeight));
+   if (heightDiff < 0)
+      menuTop += heightDiff;
+  
+   var leftDiff = (window.innerWidth
+                  - (menuLeft + popup.offsetWidth));
+   if (leftDiff < 0)
+      menuLeft -= popup.offsetWidth;
 
-  popup.style.top = menuTop + "px;";
-  popup.style.left = menuLeft + "px;";
-  popup.style.visibility = "visible;";
-  setupMenuTarget(popup, node);
+   popup.setStyle({ top: menuTop + "px",
+                   left: menuLeft + "px",
+                   visibility: "visible" });
 
-  bodyOnClick = "" + document.body.getAttribute("onclick");
-  document.body.setAttribute("onclick", "onBodyClick(event);");
-  document.currentPopupMenu = popup;
+   document.currentPopupMenu = popup;
 
-  event.cancelBubble = true;
-  event.returnValue = false;
+   Event.observe(document.body, "click", onBodyClickMenuHandler);
 
-  return false;
-}
-
-function setupMenuTarget(menu, target) {
-  menu.menuTarget = target;
-  var menus = document.getElementsByClassName("menu", menu);
-  for (var i = 0; i < menus.length; i++) {
-    menus[i].menuTarget = target;
-  }
+   preventDefault(event);
 }
 
 function getParentMenu(node) {
@@ -482,24 +576,24 @@ function getParentMenu(node) {
   return menuNode;
 }
 
-function onBodyClick(event) {
-  document.currentPopupMenu.menuTarget = null;
-  hideMenu(event, document.currentPopupMenu);
-  document.body.setAttribute("onclick", bodyOnClick);
+function onBodyClickMenuHandler(event) {
+   document.body.menuTarget = null;
+   hideMenu(document.currentPopupMenu);
+   Event.stopObserving(document.body, "click", onBodyClickMenuHandler);
 
-  return false;
+   preventDefault(event);
 }
 
-function hideMenu(event, menuNode) {
+function hideMenu(menuNode) { //log ("hideMenu");
   var onHide;
 
-//   log('hiding menu "' + menuNode.getAttribute('id') + '"');
   if (menuNode.submenu) {
-    hideMenu(event, menuNode.submenu);
+    hideMenu(menuNode.submenu);
     menuNode.submenu = null;
   }
 
-  menuNode.style.visibility = "hidden";
+  menuNode.setStyle({ visibility: "hidden" });
+  //   menuNode.hide();
   if (menuNode.parentMenuItem) {
     menuNode.parentMenuItem.setAttribute('class', 'submenu');
     menuNode.parentMenuItem = null;
@@ -509,12 +603,21 @@ function hideMenu(event, menuNode) {
     menuNode.parentMenu = null;
   }
 
-  var onhideEvent = document.createEvent("Event");
-  onhideEvent.initEvent("hideMenu", false, true);
-  menuNode.dispatchEvent(onhideEvent);
+  if (document.createEvent) {
+    var onhideEvent;
+    if (isSafari())
+      onhideEvent = document.createEvent("UIEvents");
+    else // Mozilla
+      onhideEvent = document.createEvent("Events");
+    onhideEvent.initEvent("mousedown", false, true);
+    menuNode.dispatchEvent(onhideEvent);
+  }
+  else if (document.createEventObject) { // IE
+    menuNode.fireEvent("onmousedown");
+  }
 }
 
-function onMenuEntryClick(event, menuId) {
+function onMenuEntryClick(event) {
   var node = event.target;
 
   id = getParentMenu(node).menuTarget;
@@ -539,21 +642,20 @@ function parseQueryParameters(url) {
 }
 
 function initLogConsole() {
-  var logConsole = $("logConsole");
-  if (logConsole) {
-    logConsole.addEventListener("dblclick", onLogDblClick, false);
-    logConsole.innerHTML = "";
-    node = document.getElementsByTagName('body')[0];
-    node.addEventListener("keydown", onBodyKeyDown, true);
-  }
+    var logConsole = $("logConsole");
+    if (logConsole) {
+       logConsole.highlighted = false;
+       Event.observe(logConsole, "dblclick", onLogDblClick, false);
+       logConsole.innerHTML = "";
+       Event.observe(window, "keydown", onBodyKeyDown);
+    }
 }
 
 function onBodyKeyDown(event) {
-  if (event.keyCode == 27) {
-    toggleLogConsole();
-    event.cancelBubble = true;
-    event.returnValue = false;
-  }
+    if (event.keyCode == 27) {
+       toggleLogConsole();
+       preventDefault(event);
+    }
 }
 
 function onLogDblClick(event) {
@@ -565,13 +667,12 @@ function toggleLogConsole(event) {
   var logConsole = $("logConsole");
   var display = '' + logConsole.style.display;
   if (display.length == 0) {
-    logConsole.style.display = 'block;';
+    logConsole.setStyle({ display: 'block' });
   } else {
-    logConsole.style.display = '';
+    logConsole.setStyle({ display: '' });
   }
-  event.cancelBubble = true;
-  event.returnValue = false;
-  event.preventDefault();
+  if (event)
+      preventDefault(event);
 }
 
 function log(message) {
@@ -581,13 +682,22 @@ function log(message) {
       logWindow = logWindow.opener;
   }
   var logConsole = logWindow.document.getElementById("logConsole");
-  if (logConsole)
-    logConsole.innerHTML += message + '<br />' + "\n";
+  if (logConsole) {
+      logConsole.highlighted = !logConsole.highlighted;
+      var logMessage = message.replace("<", "&lt;", "g");
+      logMessage = logMessage.replace(" ", "&nbsp;", "g");
+      logMessage = logMessage.replace("\r\n", "<br />\n", "g");
+      logMessage = logMessage.replace("\n", "<br />\n", "g");
+      logMessage += '<br />' + "\n";
+      if (logConsole.highlighted)
+         logMessage = '<div class="highlighted">' + logMessage + '</div>';
+      logConsole.innerHTML += logMessage;
+  }
 }
 
 function backtrace() {
    var func = backtrace.caller;
-   var str = "backtrace:<br/>";
+   var str = "backtrace:\n";
 
    while (func)
    {
@@ -600,7 +710,7 @@ function backtrace() {
       else
          str += "[anonymous]\n";
 
-      str += "<br/>";
+      str += "\n";
       func = func.caller;
    }
    str += "--\n";
@@ -609,37 +719,37 @@ function backtrace() {
 }
 
 function dropDownSubmenu(event) {
-  var node = event.target;
-  var submenu = node.getAttribute("submenu");
-  if (submenu && submenu != "") {
-    var submenuNode = document.getElementById(submenu);
-    var parentNode = getParentMenu(node);
-    if (parentNode.submenu)
-      hideMenu(event, parentNode.submenu);
-    submenuNode.parentMenuItem = node;
-    submenuNode.parentMenu = parentNode;
-    parentNode.submenuItem = node;
-    parentNode.submenu = submenuNode;
-    
-    var menuTop = (node.offsetTop - 2);
-    
-    var heightDiff = (window.innerHeight
-                      - (menuTop + submenuNode.offsetHeight));
-    if (heightDiff < 0)
-      menuTop += heightDiff;
-    
-    var menuLeft = parentNode.offsetWidth - 3;
-    if (window.innerWidth
-        < (menuLeft + submenuNode.offsetWidth
-           + parentNode.cascadeLeftOffset()))
-       menuLeft = -submenuNode.offsetWidth + 3;
-    
-    parentNode.setAttribute('onmousemove', 'checkDropDown(event);');
-    node.setAttribute('class', 'submenu-selected');
-    submenuNode.style.top = menuTop + "px;";
-    submenuNode.style.left = menuLeft + "px;";
-    submenuNode.style.visibility = "visible;";
-  }
+   var node = this;
+   if (this.submenu && this.submenu != "") {
+      log ("submenu: " + this.submenu);
+      var submenuNode = $(this.submenu);
+      var parentNode = getParentMenu(node);
+      if (parentNode.submenu)
+        hideMenu(parentNode.submenu);
+      submenuNode.parentMenuItem = node;
+      submenuNode.parentMenu = parentNode;
+      parentNode.submenuItem = node;
+      parentNode.submenu = submenuNode;
+      
+      var menuTop = (node.offsetTop - 2);
+      
+      var heightDiff = (window.innerHeight
+                       - (menuTop + submenuNode.offsetHeight));
+      if (heightDiff < 0)
+        menuTop += heightDiff;
+      
+      var menuLeft = parentNode.offsetWidth - 3;
+      if (window.innerWidth
+         < (menuLeft + submenuNode.offsetWidth
+            + parentNode.cascadeLeftOffset()))
+        menuLeft = - submenuNode.offsetWidth + 3;
+      
+      parentNode.setAttribute('onmousemove', 'checkDropDown(event);');
+      node.setAttribute('class', 'submenu-selected');
+      submenuNode.setStyle({ top: menuTop + "px",
+                                    left: menuLeft + "px",
+                                    visibility: "visible" });
+   }
 }
 
 function checkDropDown(event) {
@@ -655,7 +765,7 @@ function checkDropDown(event) {
         && menuX < itemX + submenuItem.offsetWidth
         && (menuY < itemY
             || menuY > (itemY + submenuItem.offsetHeight))) {
-      hideMenu(event, parentMenu.submenu);
+      hideMenu(parentMenu.submenu);
       parentMenu.submenu = null;
       parentMenu.submenuItem = null;
       parentMenu.setAttribute('onmousemove', null);
@@ -665,11 +775,11 @@ function checkDropDown(event) {
 
 /* search field */
 function popupSearchMenu(event) {
-  var node = event.target;
-
   var menuId = this.getAttribute("menuid");
-  relX = event.pageX - node.cascadeLeftOffset();
-  relY = event.pageY - node.cascadeTopOffset();
+  var offset = Position.cumulativeOffset(this);
+
+  relX = Event.pointerX(event) - offset[0];
+  relY = Event.pointerY(event) - offset[1];
 
   if (event.button == 0
       && relX < 24) {
@@ -677,26 +787,25 @@ function popupSearchMenu(event) {
     event.returnValue = false;
 
     if (document.currentPopupMenu)
-      hideMenu(event, document.currentPopupMenu);
+      hideMenu(document.currentPopupMenu);
 
-    var popup = document.getElementById(menuId);
-    popup.style.top = node.offsetHeight + "px";
-    popup.style.left = (node.offsetLeft + 3) + "px";
-    popup.style.visibility = "visible";
+    var popup = $(menuId);
+    offset = Position.positionedOffset(this);
+    popup.setStyle({ top: this.offsetHeight + "px",
+                   left: (offset[0] + 3) + "px",
+                           visibility: "visible" });
   
-    bodyOnClick = "" + document.body.getAttribute("onclick");
-    document.body.setAttribute("onclick", "onBodyClick('" + menuId + "');");
     document.currentPopupMenu = popup;
+    Event.observe(document.body, "click", onBodyClickMenuHandler);
   }
 }
 
 function setSearchCriteria(event) {
-  searchValue = $("searchValue");
-  searchCriteria = $("searchCriteria");
-  
-  var node = event.target;
-  searchValue.setAttribute("ghost-phrase", node.innerHTML);
-  searchCriteria = node.getAttribute('id');
+  var searchValue = $("searchValue");
+  var searchCriteria = $("searchCriteria");
+
+  searchValue.setAttribute("ghost-phrase", this.innerHTML);
+  searchCriteria.value = this.getAttribute('id');
 }
 
 function checkSearchValue(event) {
@@ -712,10 +821,27 @@ function onSearchChange() {
   log ("onSearchChange()...");
 }
 
+function configureSearchField() {
+   var searchValue = $("searchValue");
+
+   if (!searchValue) return;
+
+   Event.observe(searchValue, "mousedown",
+                onSearchMouseDown.bindAsEventListener(searchValue));
+   Event.observe(searchValue, "click",
+                popupSearchMenu.bindAsEventListener(searchValue));
+   Event.observe(searchValue, "blur",
+                onSearchBlur.bindAsEventListener(searchValue));
+   Event.observe(searchValue, "focus",
+                onSearchFocus.bindAsEventListener(searchValue));
+   Event.observe(searchValue, "keydown",
+                onSearchKeyDown.bindAsEventListener(searchValue));
+}
+
 function onSearchMouseDown(event) {
    var superNode = this.parentNode.parentNode.parentNode;
-   relX = (event.pageX - superNode.offsetLeft - this.offsetLeft);
-   relY = (event.pageY - superNode.offsetTop - this.offsetTop);
+   relX = (Event.pointerX(event) - superNode.offsetLeft - this.offsetLeft);
+   relY = (Event.pointerY(event) - superNode.offsetTop - this.offsetTop);
 
    if (relY < 24) {
       event.cancelBubble = true;
@@ -732,22 +858,23 @@ function onSearchFocus() {
     this.select();
   }
 
-  this.style.color = "#000";
+  this.setStyle({ color: "#000" });
 }
 
 function onSearchBlur(event) {
-  var ghostPhrase = this.getAttribute("ghost-phrase");
-//   log ("search blur: '" + this.value + "'");
-  if (!this.value) {
+   var ghostPhrase = this.getAttribute("ghost-phrase");
+   //log ("search blur: '" + this.value + "'");
+   if (!this.value) {
     this.setAttribute("modified", "");
-    this.style.color = "#aaa";
+    this.setStyle({ color: "#aaa" });
     this.value = ghostPhrase;
+    refreshCurrentFolder();
   } else if (this.value == ghostPhrase) {
     this.setAttribute("modified", "");
-    this.style.color = "#aaa";
+    this.setStyle({ color: "#aaa" });
   } else {
     this.setAttribute("modified", "yes");
-    this.style.color = "#000";
+    this.setStyle({ color: "#000" });
   }
 }
 
@@ -758,65 +885,67 @@ function onSearchKeyDown(event) {
   this.timer = setTimeout("onSearchFormSubmit()", 1000);
 }
 
+function onSearchFormSubmit(event) {
+   var searchValue = $("searchValue");
+   var searchCriteria = $("searchCriteria");
+   var ghostPhrase = searchValue.getAttribute('ghost-phrase');
+
+   if (searchValue.value == ghostPhrase) return;
+
+   search["criteria"] = searchCriteria.value;
+   search["value"] = searchValue.value;
+
+   refreshCurrentFolder();
+}
+
 function initCriteria() {
   var searchCriteria = $("searchCriteria");
   var searchValue = $("searchValue");
-  var firstOption;
  
-  var searchOptions = $("searchOptions");
-  if (searchOptions) {
-    firstOption = searchOptions.childNodes[1];
-    searchCriteria.value = firstOption.getAttribute('id');
-    searchValue.setAttribute('ghost-phrase', firstOption.innerHTML);
+  if (!searchValue) return;
+
+  var searchOptions = $("searchOptions").childNodesWithTag("li");
+  if (searchOptions.length > 0) {
+    var firstChild = searchOptions[0];
+    searchCriteria.value = $(firstChild).getAttribute('id');
+    searchValue.setAttribute('ghost-phrase', firstChild.innerHTML);
     if (searchValue.value == '') {
-      searchValue.value = firstOption.innerHTML;
+      searchValue.value = firstChild.innerHTML;
       searchValue.setAttribute("modified", "");
-      searchValue.style.color = "#aaa";
+      searchValue.setStyle({ color: "#aaa" });
     }
   }
 }
 
 /* toolbar buttons */
-function popupToolbarMenu(event, menuId) {
-   var toolbar = $("toolbar");
-   var node = event.target;
-   if (node.tagName != 'A')
-      node = node.getParentWithTagName("a");
-   node = node.childNodesWithTag("span")[0];
-
-   if (event.button == 0) {
-      event.cancelBubble = true;
-      event.returnValue = false;
-      
-      if (document.currentPopupMenu)
-        hideMenu(event, document.currentPopupMenu);
-      
-      var popup = document.getElementById(menuId);
-      var top = node.offsetTop + node.offsetHeight - 2;
-      popup.style.top = top + "px";
-      popup.style.left = node.cascadeLeftOffset() + "px";
-      popup.style.visibility = "visible";
-      
-      bodyOnClick = "" + document.body.getAttribute("onclick");
-      document.body.setAttribute("onclick", "onBodyClick('" + menuId + "');");
-      document.currentPopupMenu = popup;
-   }
+function popupToolbarMenu(node, menuId) {
+   if (document.currentPopupMenu)
+      hideMenu(document.currentPopupMenu);
+
+   var popup = $(menuId);
+   var top = ($(node).getStyle('top') || 0) + node.offsetHeight - 2;
+   popup.setStyle({ top: top + "px",
+                   left: $(node).cascadeLeftOffset() + "px",
+                   visibility: "visible" });
+
+   document.currentPopupMenu = popup;
+   Event.observe(document.body, "click", onBodyClickMenuHandler);
 }
 
 /* contact selector */
 
 function folderSubscriptionCallback(http) {
    if (http.readyState == 4) {
-      if (http.status == 204) {
+      if (isHttpStatus204(http.status)) {
         if (http.callbackData)
            http.callbackData["method"](http.callbackData["data"]);
       }
       else
-        window.alert(labels["Unable to subscribe to that folder!"].decodeEntities());
+        window.alert(clabels["Unable to subscribe to that folder!"].decodeEntities());
       document.subscriptionAjaxRequest = null;
    }
    else
-      log ("ajax fuckage");
+      log ("folderSubscriptionCallback Ajax error");
 }
 
 function subscribeToFolder(refreshCallback, refreshCallbackData) {
@@ -836,18 +965,18 @@ function subscribeToFolder(refreshCallback, refreshCallbackData) {
                                                            rfCbData);
    }
    else
-      window.alert(labels["You cannot subscribe to a folder that you own!"]
+      refreshCallbackData["window"].alert(clabels["You cannot subscribe to a folder that you own!"]
                   .decodeEntities());
 }
 
 function folderUnsubscriptionCallback(http) {
    if (http.readyState == 4) {
-      if (http.status == 204) {
+      if (isHttpStatus204(http.status)) {
         if (http.callbackData)
            http.callbackData["method"](http.callbackData["data"]);
       }
       else
-        window.alert(labels["Unable to unsubscribe from that folder!"].decodeEntities());
+        window.alert(clabels["Unable to unsubscribe from that folder!"].decodeEntities());
       document.unsubscriptionAjaxRequest = null;
    }
 }
@@ -874,12 +1003,12 @@ function unsubscribeFromFolder(folder, refreshCallback, refreshCallbackData) {
                                 rfCbData);
       }
       else
-        window.alert(labels["You cannot unsubscribe from a folder that you own!"].decodeEntities());
+        window.alert(clabels["You cannot unsubscribe from a folder that you own!"].decodeEntities());
    }
 }
 
 function listRowMouseDownHandler(event) {
-  event.preventDefault();
+   preventDefault(event);
 }
 
 /* tabs */
@@ -887,50 +1016,78 @@ function initTabs() {
   var containers = document.getElementsByClassName("tabsContainer");
   for (var x = 0; x < containers.length; x++) {
     var container = containers[x];
-    var nodes = container.childNodes[1].childNodes;
-
-    var firstTab;
-    for (var i = 0; i < nodes.length; i++) {
-      if (nodes[i] instanceof HTMLLIElement) {
-        if (!firstTab) {
-          firstTab = nodes[i];
-        }
-        nodes[i].addEventListener("mousedown", onTabMouseDown, true);
-        nodes[i].addEventListener("click", onTabClick, true);
+    var firstTab = null;
+    for (var i = 0; i < container.childNodes.length; i++) {
+      if (container.childNodes[i].tagName == 'UL') {
+       if (!firstTab)
+         firstTab = i;
       }
     }
+    var nodes = container.childNodes[firstTab].childNodes;
+    
+    firstTab = null;
+    for (var i = 0; i < nodes.length; i++) {
+       var currentNode = nodes[i];
+       if (currentNode.tagName == 'LI') {
+           if (!firstTab)
+               firstTab = i;
+           Event.observe(currentNode, "mousedown",
+                         onTabMouseDown.bindAsEventListener(currentNode));
+           Event.observe(currentNode, "click",
+                         onTabClick.bindAsEventListener(currentNode));
+           //$(currentNode.getAttribute("target")).hide();
+       }
+    }
 
-    firstTab.addClassName("first");
-    firstTab.addClassName("active");
-    container.activeTab = firstTab;
+    nodes[firstTab].addClassName("first");
+    nodes[firstTab].addClassName("active");
+    container.activeTab = nodes[firstTab];
 
-    var target = $(firstTab.getAttribute("target"));
+    var target = $(nodes[firstTab].getAttribute("target"));
     target.addClassName("active");
+    //target.show();
   }
 }
 
-function initMenusNamed(menuDivNames) {
-  for (var i = 0; i < menuDivNames.length; i++) {
-    var menuDIV = $(menuDivNames[i]);
-    if (menuDIV)
-      initMenu(menuDIV);
-    else
-      log("menu named '" + menuDivNames[i] + "' not found");
-  }
+function initMenus() {
+   var menus = getMenus();
+   if (menus) {
+      for (var menuID in menus) {
+        var menuDIV = $(menuID);
+        if (menuDIV)
+           initMenu(menuDIV, menus[menuID]);
+      }
+   }
 }
 
-function initMenu(menuDIV) {
-  var lis = menuDIV.childNodesWithTag("ul")[0].childNodesWithTag("li");
-  for (var j = 0; j < lis.length; j++)
-    lis[j].addEventListener("mousedown", listRowMouseDownHandler, false);
-  var subMenus = menuDIV.childNodesWithTag("div");
-  for (var i = 0; i < subMenus.length; i++)
-    initMenu(subMenus[i]);
+function initMenu(menuDIV, callbacks) {
+   var lis = $(menuDIV.childNodesWithTag("ul")[0]).childNodesWithTag("li");
+   for (var j = 0; j < lis.length; j++) {
+      var node = $(lis[j]);
+      Event.observe(node, "mousedown", listRowMouseDownHandler, false);
+      var callback = callbacks[j];
+      if (callback) {
+        if (typeof(callback) == "string") {
+           if (callback == "-")
+              node.addClassName("separator");
+           else {
+              node.submenu = callback;
+              node.addClassName("submenu");
+              Event.observe(node, "mouseover", dropDownSubmenu);
+           }
+        }
+        else
+           Event.observe(node, "mouseup",
+                         $(callback).bindAsEventListener(node));
+      }
+      else
+        node.addClassName("disabled");
+   }
 }
 
 function onTabMouseDown(event) {
   event.cancelBubble = true;
-  event.preventDefault();
+  preventDefault(event);
 }
 
 function openExternalLink(anchor) {
@@ -939,7 +1096,7 @@ function openExternalLink(anchor) {
 
 function openAclWindow(url) {
   var w = window.open(url, "aclWindow",
-                      "width=300,height=300,resizable=1,scrollbars=1,toolbar=0,"
+                      "width=420,height=300,resizable=1,scrollbars=1,toolbar=0,"
                       + "location=0,directories=0,status=0,menubar=0"
                       + ",copyhistory=0");
   w.opener = window;
@@ -971,7 +1128,7 @@ function getTopWindow() {
 }
 
 function onTabClick(event) {
-  var node = event.target;
+  var node = getTarget(event); // LI element
 
   var target = node.getAttribute("target");
 
@@ -981,10 +1138,24 @@ function onTabClick(event) {
   var oldContent = $(oldTarget);
 
   oldContent.removeClassName("active");
-  container.activeTab.removeClassName("active");
+  container.activeTab.removeClassName("active"); // previous LI
   container.activeTab = node;
-  container.activeTab.addClassName("active");
+  container.activeTab.addClassName("active"); // current LI
   content.addClassName("active");
+  
+  // Prototype alternative
+
+  //oldContent.removeClassName("active");
+  //container.activeTab.removeClassName("active"); // previous LI
+  //container.activeTab = node;
+  //container.activeTab.addClassName("active"); // current LI
+
+  //container.activeTab.hide();
+  //oldContent.hide();
+  //content.show();
+
+  //container.activeTab = node;
+  //container.activeTab.show();
 
   return false;
 }
@@ -1065,34 +1236,53 @@ function indexColor(number) {
   return color;
 }
 
-var onLoadHandler = {
-  handleEvent: function (event) {
-    queryParameters = parseQueryParameters('' + window.location);
-    if (!document.body.hasClassName("popup")) {
+function loadPreferences() {
+   var url = UserFolderURL + "jsonDefaults";
+   var http = createHTTPClient();
+   http.open("GET", url, false);
+   http.send("");
+   if (http.status == 200)
+      userDefaults = http.responseText.evalJSON(true);
+
+   url = UserFolderURL + "jsonSettings";
+   http.open("GET", url, false);
+   http.send("");
+   if (http.status == 200)
+      userSettings = http.responseText.evalJSON(true);
+}
+
+function onLoadHandler(event) {
+   loadPreferences();
+   queryParameters = parseQueryParameters('' + window.location);
+   if (!$(document.body).hasClassName("popup")) {
       initLogConsole();
-      initializeMenus();
-      initCriteria();
-    }
-    initTabs();
-    configureDragHandles();
-    configureSortableTableHeaders();
-    configureLinkBanner();
-    var progressImage = $("progressIndicator");
-    if (progressImage)
+   }
+   initCriteria();
+   configureSearchField();
+   initMenus();
+   initTabs();
+   configureDragHandles();
+   configureSortableTableHeaders();
+   configureLinkBanner();
+   var progressImage = $("progressIndicator");
+   if (progressImage)
       progressImage.parentNode.removeChild(progressImage);
-  }
+   Event.observe(document.body, "contextmenu", onBodyClickContextMenu);
+}
+
+function onBodyClickContextMenu(event) {
+   preventDefault(event);
 }
 
 function configureSortableTableHeaders() {
-  var headers = document.getElementsByClassName("sortableTableHeader");
-  for (var i = 0; i < headers.length; i++) {
-    var anchor = headers[i].childNodesWithTag("a")[0];
-    if (!anchor.link) {
-      anchor.link = anchor.getAttribute("href");
-      anchor.href = "#";
-      anchor.addEventListener("click", onHeaderClick, true);
-    }
-  }
+   var headers = document.getElementsByClassName("sortableTableHeader");
+   for (var i = 0; i < headers.length; i++) {
+      var header = headers[i];
+      var anchor = $(header).childNodesWithTag("a")[0];
+      if (anchor)
+        Event.observe(anchor, "click",
+                      onHeaderClick.bindAsEventListener(anchor));
+   }
 }
 
 function onLinkBannerClick() {
@@ -1100,33 +1290,47 @@ function onLinkBannerClick() {
   checkAjaxRequestsState();
 }
 
+function onPreferencesClick(event) {
+   var urlstr = UserFolderURL + "preferences";
+   var w = window.open(urlstr, "User Preferences",
+                      "width=430,height=250,resizable=0,scrollbars=0");
+   w.opener = window;
+   w.focus();
+
+   preventDefault(event);
+}
+
 function configureLinkBanner() {
   var linkBanner = $("linkBanner");
   if (linkBanner) {
     var anchors = linkBanner.childNodesWithTag("a");
     for (var i = 0; i < 2; i++) {
-      anchors[i].addEventListener("mousedown", listRowMouseDownHandler,
-                                  false);
-      anchors[i].addEventListener("click", onLinkBannerClick, false);
+       Event.observe(anchors[i], "mousedown", listRowMouseDownHandler);
+       Event.observe(anchors[i], "click", onLinkBannerClick);
     }
-    if (anchors.length > 3)
-      anchors[3].addEventListener("click", toggleLogConsole, true);
+    Event.observe(anchors[3], "mousedown", listRowMouseDownHandler);
+    Event.observe(anchors[3], "click", onPreferencesClick);
+    if (anchors.length > 4)
+       Event.observe(anchors[4], "click", toggleLogConsole);
   }
 }
 
-window.addEventListener("load", onLoadHandler, false);
+addEvent(window, 'load', onLoadHandler);
+
+function parent$(element) {
+   return this.opener.document.getElementById(element);
+}
 
 /* stubs */
-function configureDragHandles() {
+function refreshCurrentFolder() {
 }
 
-function initializeMenus() {
+function configureDragHandles() {
 }
 
-function onHeaderClick(event) {
-  window.alert("generic headerClick");
+function getMenus() {
 }
 
-function parent$(element) {
-   return window.opener.document.getElementById(element);
+function onHeaderClick(event) {
+   window.alert("generic headerClick");
 }