]> err.no Git - scalable-opengroupware.org/blobdiff - UI/WebServerResources/SchedulerUI.js
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1261 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / WebServerResources / SchedulerUI.js
index 7c992039a38d2b073db895b2e7684f7956e69db6..22e7b72abbf63322f37a22be1a63adefd6fa9ba4 100644 (file)
@@ -165,15 +165,21 @@ function modifyEvent(sender, modification) {
 
 function closeInvitationWindow() {
   var closeDiv = document.createElement("div");
+  document.body.appendChild(closeDiv);
   closeDiv.addClassName("javascriptPopupBackground");
+
   var closePseudoWin = document.createElement("div");
+  document.body.appendChild(closePseudoWin);
   closePseudoWin.addClassName("javascriptMessagePseudoTopWindow");
   closePseudoWin.style.top = "0px;";
   closePseudoWin.style.left = "0px;";
   closePseudoWin.style.right = "0px;";
   closePseudoWin.appendChild(document.createTextNode(labels["closeThisWindowMessage"]));
-  document.body.appendChild(closeDiv);
-  document.body.appendChild(closePseudoWin);
+
+  var calLink = document.createElement("a");
+  closePseudoWin.appendChild(calLink);
+  calLink.href = ApplicationBaseURL;
+  calLink.appendChild(document.createTextNode(labels["Calendar"].toLowerCase()));
 }
 
 function modifyEventCallback(http) {
@@ -196,25 +202,26 @@ function modifyEventCallback(http) {
 }
 
 function deleteEventCallback(http) {
-  if (http.readyState == 4
-      && http.status == 200) {
-    var nodes = http.callbackData;
-    for (var i = 0; i < nodes.length; i++) {
-      var node = $(nodes[i]);
-      if (node)
-        node.parentNode.removeChild(node);
-    }
-    if (eventsToDelete.length)
-      _batchDeleteEvents();
-    else {
-      document.deleteEventAjaxRequest = null;
-      refreshEvents();
-      refreshTasks();
-      changeCalendarDisplay();
+  if (http.readyState == 4) {
+    if (isHttpStatus204(http.status)) {
+      var nodes = http.callbackData;
+      for (var i = 0; i < nodes.length; i++) {
+       var node = $(nodes[i]);
+       if (node)
+         node.parentNode.removeChild(node);
+      }
+      if (eventsToDelete.length)
+       _batchDeleteEvents();
+      else {
+       document.deleteEventAjaxRequest = null;
+       refreshEvents();
+       refreshTasks();
+       changeCalendarDisplay();
+      }
     }
+    else
+      log ("deleteEventCallback Ajax error");
   }
-  else
-    log ("deleteEventCallback Ajax error");
 }
 
 function editDoubleClickedEvent(event) {
@@ -306,51 +313,53 @@ function eventsListCallback(http) {
      var div = $("eventsListView");
 
     document.eventsListAjaxRequest = null;
-    var table = $("eventsList").tBodies[0];
+    var table = $("eventsList");
     var params = parseQueryParameters(http.callbackData);
     sortKey = params["sort"];
     sortOrder = params["desc"];
-    configureSortableTableHeaders();
-
-    var data = http.responseText.evalJSON(true);
-    for (var i = 0; i < data.length; i++) {
-      var row = document.createElement("tr");
-      table.appendChild(row);
-      $(row).addClassName("eventRow");
-      row.setAttribute("id", escape(data[i][0]));
-      row.cname = escape(data[i][0]);
-      row.calendar = data[i][1];
-
-      var startDate = new Date();
-      startDate.setTime(data[i][4] * 1000);
-      row.day = startDate.getDayString();
-      row.hour = startDate.getHourString();
-      Event.observe(row, "click",
-                   onEventClick.bindAsEventListener(row));
-      Event.observe(row, "dblclick",
-                   editDoubleClickedEvent.bindAsEventListener(row));
-      Event.observe(row, "contextmenu",
-                   onEventContextMenu.bindAsEventListener(row));
-
-      var td = document.createElement("td");
-      row.appendChild(td);
-      Event.observe(td, "mousedown", listRowMouseDownHandler, true);
-      td.appendChild(document.createTextNode(data[i][3]));
-
-      td = document.createElement("td");
-      row.appendChild(td);
-      Event.observe(td, "mousedown", listRowMouseDownHandler, true);
-      td.appendChild(document.createTextNode(data[i][8]));
-
-      td = document.createElement("td");
-      row.appendChild(td);
-      Event.observe(td, "mousedown", listRowMouseDownHandler, true);
-      td.appendChild(document.createTextNode(data[i][9]));
+    lastClickedRow = -1; // from generic.js
+
+    if (http.responseText.length > 0) {
+      var data = http.responseText.evalJSON(true);
+      for (var i = 0; i < data.length; i++) {
+       var row = document.createElement("tr");
+       table.tBodies[0].appendChild(row);
+       $(row).addClassName("eventRow");
+       row.setAttribute("id", escape(data[i][0]));
+       row.cname = escape(data[i][0]);
+       row.calendar = data[i][1];
+
+       var startDate = new Date();
+       startDate.setTime(data[i][4] * 1000);
+       row.day = startDate.getDayString();
+       row.hour = startDate.getHourString();
+       Event.observe(row, "click",
+                     onEventClick.bindAsEventListener(row));
+       Event.observe(row, "dblclick",
+                     editDoubleClickedEvent.bindAsEventListener(row));
+       Event.observe(row, "contextmenu",
+                     onEventContextMenu.bindAsEventListener(row));
       
-      td = document.createElement("td");
-      row.appendChild(td);
-      Event.observe(td, "mousedown", listRowMouseDownHandler, true);
-      td.appendChild(document.createTextNode(data[i][6]));
+       var td = document.createElement("td");
+       row.appendChild(td);
+       Event.observe(td, "mousedown", listRowMouseDownHandler, true);
+       td.appendChild(document.createTextNode(data[i][3]));
+
+       td = document.createElement("td");
+       row.appendChild(td);
+       Event.observe(td, "mousedown", listRowMouseDownHandler, true);
+       td.appendChild(document.createTextNode(data[i][8]));
+
+       td = document.createElement("td");
+       row.appendChild(td);
+       Event.observe(td, "mousedown", listRowMouseDownHandler, true);
+       td.appendChild(document.createTextNode(data[i][9]));
+      
+       td = document.createElement("td");
+       row.appendChild(td);
+       Event.observe(td, "mousedown", listRowMouseDownHandler, true);
+       td.appendChild(document.createTextNode(data[i][6]));
+      }
     }
   }
   else
@@ -364,42 +373,46 @@ function tasksListCallback(http) {
       && http.status == 200) {
     document.tasksListAjaxRequest = null;
     var list = $("tasksList");
-    var data = http.responseText.evalJSON(true);
-
-    for (var i = 0; i < data.length; i++) {
-      //log(i + " = " + data[i][3]);
-      var listItem = document.createElement("li");
-      list.appendChild(listItem);
-      Event.observe(listItem, "mousedown", listRowMouseDownHandler); // causes problem with Safari
-      Event.observe(listItem, "click", onRowClick);
-      Event.observe(listItem, "dblclick", editDoubleClickedEvent.bindAsEventListener(listItem));
-      listItem.setAttribute("id", data[i][0]);
-      $(listItem).addClassName(data[i][5]);
-      listItem.calendar = data[i][1];
-      $(listItem).addClassName("calendarFolder" + data[i][1]);
-      listItem.cname = escape(data[i][0]);
-      var input = document.createElement("input");
-      input.setAttribute("type", "checkbox");
-      listItem.appendChild(input);
-      Event.observe(input, "click", updateTaskStatus.bindAsEventListener(input), true);
-      input.setAttribute("value", "1");
-      if (data[i][2] == 1)
-       input.setAttribute("checked", "checked");
-      $(input).addClassName("checkBox");
-      listItem.appendChild(document.createTextNode(data[i][3]));
-    }
+    if (http.responseText.length > 0) {
+      var data = http.responseText.evalJSON(true);
+
+      for (var i = 0; i < data.length; i++) {
+       //log(i + " = " + data[i][3]);
+       var listItem = document.createElement("li");
+       list.appendChild(listItem);
+       Event.observe(listItem, "mousedown", listRowMouseDownHandler);
+       Event.observe(listItem, "click", onRowClick);
+       Event.observe(listItem, "dblclick",
+                     editDoubleClickedEvent.bindAsEventListener(listItem));
+       listItem.setAttribute("id", data[i][0]);
+       $(listItem).addClassName(data[i][5]);
+       listItem.calendar = data[i][1];
+       $(listItem).addClassName("calendarFolder" + data[i][1]);
+       listItem.cname = escape(data[i][0]);
+       var input = document.createElement("input");
+       input.setAttribute("type", "checkbox");
+       listItem.appendChild(input);
+       Event.observe(input, "click", updateTaskStatus.bindAsEventListener(input), true);
+       input.setAttribute("value", "1");
+       if (data[i][2] == 1)
+         input.setAttribute("checked", "checked");
+       $(input).addClassName("checkBox");
+       listItem.appendChild(document.createTextNode(data[i][3]));
+      }
 
-    list.scrollTop = list.previousScroll;
+      list.scrollTop = list.previousScroll;
 
-    if (http.callbackData) {
-      var selectedNodesId = http.callbackData;
-      for (var i = 0; i < selectedNodesId.length; i++) {
-//     log(selectedNodesId[i] + " (" + i + ") is selected");
-        $(selectedNodesId[i]).select();
+      if (http.callbackData) {
+       var selectedNodesId = http.callbackData;
+       for (var i = 0; i < selectedNodesId.length; i++) {
+         //    log(selectedNodesId[i] + " (" + i + ") is selected");
+         $(selectedNodesId[i]).select();
+       }
       }
+      else
+       log ("tasksListCallback: no data");
     }
-    else
-      log ("tasksListCallback: no data");
   }
   else
     log ("tasksListCallback Ajax error");
@@ -458,16 +471,16 @@ function changeDateSelectorDisplay(day, keepCurrentDay) {
   }
 }
 
-function changeCalendarDisplay(time, newView) {
+function changeCalendarDisplay(data, newView) {
   var url = ApplicationBaseURL + "/" + ((newView) ? newView : currentView);
 
   selectedCalendarCell = null;
 
   var day = null;
-  var hour = null;
-  if (time) {
-    day = time['day'];
-    hour = time['hour'];
+  var scrollEvent = null;
+  if (data) {
+    day = data['day'];
+    scrollEvent = data['scrollEvent'];
   }
 
   if (!day)
@@ -486,7 +499,9 @@ function changeCalendarDisplay(time, newView) {
   }
   document.dayDisplayAjaxRequest
      = triggerAjaxRequest(url, calendarDisplayCallback,
-                         { "view": newView, "day": day, "hour": hour });
+                         { "view": newView,
+                           "day": day,
+                           "scrollEvent": scrollEvent });
 
   return false;
 }
@@ -514,25 +529,24 @@ function onMonthOverview() {
   return _ensureView("monthview");
 }
 
-function scrollDayView(hour) {
-  var rowNumber;
-  if (hour) {
-    if (hour.length == 3)
-      rowNumber = parseInt(hour.substr(0, 1));
-    else {
-      if (hour.substr(0, 1) == "0")
-        rowNumber = parseInt(hour.substr(1, 1));
-      else
-        rowNumber = parseInt(hour.substr(0, 2));
-    }
-  } else
-    rowNumber = 8;
-
+function scrollDayView(scrollEvent) {
+  var offset = 0;
   var daysView = $("daysView");
   var hours =
-     $(daysView.childNodesWithTag("div")[0]).childNodesWithTag("div");
-  if (hours.length > 0)
-    daysView.scrollTop = hours[rowNumber].offsetTop;
+    $(daysView.childNodesWithTag("div")[0]).childNodesWithTag("div");
+
+  if (scrollEvent && scrollEvent.siblings) {
+    var classes = scrollEvent.siblings[0].getAttribute("class").split(" ");
+    for (var i = 0; i < classes.length; i++)
+      if (classes[i].startsWith("starts")) {
+       var starts = Math.floor(parseInt(classes[i].substr(6)) / 4);
+       offset = hours[starts].offsetTop;
+      }
+  }
+  else
+    offset = hours[8].offsetTop;
+
+  daysView.scrollTop = offset - 5;
 }
 
 function onClickableCellsDblClick(event) {
@@ -594,12 +608,15 @@ function refreshCalendarEvents() {
 function refreshCalendarEventsCallback(http) {
   if (http.readyState == 4
       && http.status == 200) {
-     var data = http.responseText.evalJSON(true);
+
+    if (http.responseText.length > 0) {
+      var data = http.responseText.evalJSON(true);
 //      log("refresh calendar events: " + data.length);
-     for (var i = 0; i < data.length; i++)
+      for (var i = 0; i < data.length; i++)
        drawCalendarEvent(data[i],
                          http.callbackData["startDate"],
                          http.callbackData["endDate"]);
+    }
   }
   else
      log("AJAX error when refreshing calendar events");
@@ -610,13 +627,13 @@ function drawCalendarEvent(eventData, sd, ed) {
    var viewEndDate = ed.asDate();
 
    var startDate = new Date();
-   startDate.setTime(eventData[4] * 1000 + (1000 * UTCOffset));
+   startDate.setTime(eventData[4] * 1000);
    var endDate = new Date();
-   endDate.setTime(eventData[5] * 1000 + (1000 * UTCOffset));
+   endDate.setTime(eventData[5] * 1000);
 
-   var days = startDate.daysUpTo(endDate);
+//    log ("s: " + startDate + "; e: " + endDate);
 
-   var divs = new Array();
+   var days = startDate.daysUpTo(endDate);
 
    var title;
    if (currentView == "monthview"
@@ -704,8 +721,12 @@ function drawCalendarEvent(eventData, sd, ed) {
            }
         }
         if (parentDiv)
-           parentDiv.appendChild(eventDiv);
+          parentDiv.appendChild(eventDiv);
       }
+
+   var eventTR = $(eventData[0]);
+   if (eventTR)
+     eventTR.siblings = siblings;
 }
 
 function newEventDIV(cname, calendar, starts, lasts,
@@ -762,28 +783,28 @@ function calendarDisplayCallback(http) {
   if (http.readyState == 4
       && http.status == 200) {
     document.dayDisplayAjaxRequest = null;
-    div.innerHTML = http.responseText;
+    div.update(http.responseText);
     if (http.callbackData["view"])
       currentView = http.callbackData["view"];
     if (http.callbackData["day"])
       currentDay = http.callbackData["day"];
 
-    var hour = null;
-    if (http.callbackData["hour"])
-      hour = http.callbackData["hour"];
     var contentView;
     if (currentView == "monthview")
       contentView = $("calendarContent");
     else {
-      scrollDayView(hour);
+      var scrollEvent = http.callbackData.scrollEvent;
+      scrollDayView($(scrollEvent));
       contentView = $("daysView");
     }
     refreshCalendarEvents();
     var days = document.getElementsByClassName("day", contentView);
     if (currentView == "monthview")
       for (var i = 0; i < days.length; i++) {
-        Event.observe(days[i], "click",  onCalendarSelectDay.bindAsEventListener(days[i]));
-        Event.observe(days[i], "dblclick",  onClickableCellsDblClick.bindAsEventListener(days[i]));
+        Event.observe(days[i], "click",
+                     onCalendarSelectDay.bindAsEventListener(days[i]));
+        Event.observe(days[i], "dblclick",
+                     onClickableCellsDblClick.bindAsEventListener(days[i]));
       }
     else {
        var headerDivs = $("calendarHeader").childNodesWithTag("div"); 
@@ -811,15 +832,15 @@ function calendarDisplayCallback(http) {
 }
 
 function assignCalendar(name) {
-   if (typeof(skycalendar) != "undefined") {
-      var node = $(name);
+  if (typeof(skycalendar) != "undefined") {
+    var node = $(name);
       
-      node.calendar = new skycalendar(node);
-      node.calendar.setCalendarPage(ResourcesURL + "/skycalendar.html");
-      var dateFormat = node.getAttribute("dateFormat");
-      if (dateFormat)
-        node.calendar.setDateFormat(dateFormat);
-   }
+    node.calendar = new skycalendar(node);
+    node.calendar.setCalendarPage(ResourcesURL + "/skycalendar.html");
+    var dateFormat = node.getAttribute("dateFormat");
+    if (dateFormat)
+      node.calendar.setDateFormat(dateFormat);
+  }
 }
 
 function popupCalendar(node) {
@@ -888,8 +909,8 @@ function _loadEventHref(href) {
     = triggerAjaxRequest(url, eventsListCallback, href);
 
   var table = $("eventsList").tBodies[0];
-  while (table.rows.length > 1)
-     table.removeChild(table.rows[1]);
+  while (table.rows.length > 0)
+     table.removeChild(table.rows[0]);
 
   return false;
 }
@@ -918,8 +939,8 @@ function _loadTasksHref(href) {
 }
 
 function onHeaderClick(event) {
-//   log("onHeaderClick: " + this.link);
-  _loadEventHref(this.link);
+  //log("onHeaderClick: " + this.link);
+  //_loadEventHref(this.link);
 
   preventDefault(event);
 }
@@ -950,11 +971,9 @@ function onListFilterChange() {
 }
 
 function onEventClick(event) {
-  var day = this.day;
-  var hour = this.hour;
-
-  changeCalendarDisplay( { "day": day, "hour": hour} );
-  changeDateSelectorDisplay(day);
+  changeCalendarDisplay( { "day": this.day,
+                          "scrollEvent": this.getAttribute("id") } );
+  changeDateSelectorDisplay(this.day);
 
   return onRowClick(event);
 }
@@ -975,7 +994,7 @@ function selectYearInMenu(menu, month) {
   var entries = menu.childNodes[1].childNodes;
   for (i = 0; i < entries.length; i++) {
     var entry = entries[i];
-    if (entry instanceof HTMLLIElement) {
+    if (entry.tagName == "LI") {
       var entryMonth = entry.innerHTML;
       if (entryMonth == month)
         entry.addClassName("currentMonth");
@@ -1026,7 +1045,7 @@ function onSearchFormSubmit() {
 
 function onCalendarSelectEvent() {
   var list = $("eventsList");
-  list.deselectAll();
+  $(list.tBodies[0]).deselectAll();
 
   if (selectedCalendarCell)
      for (var i = 0; i < selectedCalendarCell.length; i++)
@@ -1123,17 +1142,15 @@ function updateTaskStatus(event) {
   var taskId = this.parentNode.getAttribute("id");
   var newStatus = (this.checked ? 1 : 0);
   var http = createHTTPClient();
-  
-  if (isSafari())
+
+  if (isSafari() && !isSafari3()) {
     newStatus = (newStatus ? 0 : 1);
-  //log("update task status: " + taskId + " to " + this.checked);
-  event.cancelBubble = true;
+  }
   
   url = (ApplicationBaseURL + "/" + this.parentNode.calendar
         + "/" + taskId + "/changeStatus?status=" + newStatus);
 
   if (http) {
-//     log ("url: " + url);
     // TODO: add parameter to signal that we are only interested in OK
     http.open("POST", url, false /* not async */);
     http.url = url;
@@ -1148,6 +1165,12 @@ function updateTaskStatus(event) {
 
 function updateCalendarStatus(event) {
   var list = new Array();
+  var newStatus = (this.checked ? 1 : 0);
+  
+  if (isSafari() && !isSafari3()) {
+    newStatus = (newStatus ? 0 : 1);
+    this.checked = newStatus;
+  }
 
   var nodes = $("calendarList").childNodesWithTag("li");
   for (var i = 0; i < nodes.length; i++) {
@@ -1173,7 +1196,7 @@ function updateCalendarStatus(event) {
   if (event) {
      var folderID = this.parentNode.getAttribute("id");
      var urlstr = URLForFolderID(folderID);
-     if (this.checked)
+     if (newStatus)
        urlstr += "/activateFolder";
      else
        urlstr += "/deactivateFolder";
@@ -1193,18 +1216,18 @@ function updateCalendarStatus(event) {
 function calendarStatusCallback(http) {
   if (http.readyState == 4) {
     if (isHttpStatus204(http.status)) {
-         refreshEvents();
-         refreshTasks();
-         changeCalendarDisplay();
-      }
-      else {
-        var folder = $(http.callbackData);
-         var input = folder.childNodesWithTag("input")[0];
-        input.checked = (!input.checked);
-      }
-   }
-   else
-      log("calendarStatusCallback Ajax error");
+      refreshEvents();
+      refreshTasks();
+      changeCalendarDisplay();
+    }
+    else {
+      var folder = $(http.callbackData);
+      var input = folder.childNodesWithTag("input")[0];
+      input.checked = (!input.checked);
+    }
+  }
+  else
+    log("calendarStatusCallback Ajax error");
 }
 
 function calendarEntryCallback(http) {
@@ -1290,7 +1313,10 @@ function getMenus() {
                                       editEvent, deleteEvent, "-",
                                       onSelectAll, "-",
                                       null, null);
-   menus["calendarsMenu"] = new Array(null, null, "-", null, null, "-",
+   menus["calendarsMenu"] = new Array(onMenuModify,
+                                     "-",
+                                     onCalendarNew, onCalendarRemove,
+                                     "-", null, null, "-",
                                      null, "-", onMenuSharing);
    menus["searchMenu"] = new Array(setSearchCriteria);
 
@@ -1331,12 +1357,15 @@ function initCalendarSelector() {
   updateCalendarStatus();
   selector.changeNotification = updateCalendarsList;
 
-  var list = $("calendarList").childNodesWithTag("li");
-  for (var i = 0; i < list.length; i++) {
-    var input = list[i].childNodesWithTag("input")[0];
-    Event.observe(input, "click", updateCalendarStatus.bindAsEventListener(input)); // not registered in IE?
-    //Event.observe(list[i], "mousedown", listRowMouseDownHandler, true); // problem with Safari
-    Event.observe(list[i], "click", onRowClick);
+  var list = $("calendarList");
+  list.multiselect = true;
+  var items = list.childNodesWithTag("li");
+  for (var i = 0; i < items.length; i++) {
+    var input = items[i].childNodesWithTag("input")[0];
+    Event.observe(input, "click", updateCalendarStatus.bindAsEventListener(input));
+    Event.observe(items[i], "mousedown", listRowMouseDownHandler);
+    Event.observe(items[i], "selectstart", listRowMouseDownHandler);
+    Event.observe(items[i], "click", onRowClick);
   }
 
   var links = $("calendarSelectorButtons").childNodesWithTag("a");
@@ -1345,6 +1374,35 @@ function initCalendarSelector() {
   Event.observe(links[2], "click",  onCalendarRemove);
 }
 
+function onMenuModify(event) {
+  var folders = $("calendarList");
+  var selected = folders.getSelectedNodes()[0];
+
+  if (UserLogin == selected.getAttribute("owner")) {
+    var node = selected.childNodes[4];
+    var currentName = node.nodeValue.trim();
+    var newName = window.prompt(labels["Name of the Calendar"],
+                               currentName);
+    if (newName && newName.length > 0
+       && newName != currentName) {
+      var url = (URLForFolderID(selected.getAttribute("id"))
+                + "/renameFolder?name=" + escape(newName.utf8encode()));
+      triggerAjaxRequest(url, folderRenameCallback,
+                        {node: node, name: " " + newName});
+    }
+  } else
+    window.alert(clabels["Unable to rename that folder!"]);
+}
+
+function folderRenameCallback(http) {
+  if (http.readyState == 4) {
+    if (isHttpStatus204(http.status)) {
+      var dict = http.callbackData;
+      dict["node"].nodeValue = dict["name"];
+    }
+  }
+}
+
 function onCalendarNew(event) {
   createFolder(window.prompt(labels["Name of the Calendar"]),
               appendCalendar);
@@ -1356,68 +1414,121 @@ function onCalendarAdd(event) {
   preventDefault(event);
 }
 
-function appendCalendar(folderName, folder) {
-  if (folder)
-    folder = accessToSubscribedFolder(folder);
-  else
-    folder = "/" + folderName;
+function appendCalendar(folderName, folderPath) {
+  var owner;
 
-//   log ("append: " + folderName + "; folder: " + folder);
+  if (folderPath) {
+    owner = getSubscribedFolderOwner(folderPath);
+    folderPath = accessToSubscribedFolder(folderPath);
+  }
+  else
+    folderPath = "/" + folderName;
 
-  var calendarList = $("calendarList");
-  var lis = calendarList.childNodesWithTag("li");
-  var color = indexColor(lis.length + 100);
-  //log ("color: " + color);
+  if (!owner)
+    owner = UserLogin;
 
-  var li = document.createElement("li");
-  calendarList.appendChild(li);
+  //log ("append name: " + folderName + "; path: " + folderPath + "; owner: " + owner);
 
-  var checkBox = document.createElement("input");
-  checkBox.setAttribute("type", "checkbox");
-  li.appendChild(checkBox);
-   
-  li.appendChild(document.createTextNode(" "));
-
-  var colorBox = document.createElement("div");
-  li.appendChild(colorBox);
-  li.appendChild(document.createTextNode(" " + folderName));
-  colorBox.appendChild(document.createTextNode("OO"));
-
-  li.setAttribute("id", folder);
-  Event.observe(li, "mousedown",  listRowMouseDownHandler);
-  Event.observe(li, "click",  onRowClick);
-  $(checkBox).addClassName("checkBox");
-
-  Event.observe(checkBox, "click",
-               updateCalendarStatus.bindAsEventListener(checkBox));
-
-  $(colorBox).addClassName("colorBox");
-  if (color)
-    $(colorBox).setStyle({color: color,
-                         backgroundColor: color});
-
-  var url = URLForFolderID(folder) + "/canAccessContent";
-  triggerAjaxRequest(url, calendarEntryCallback, folder);
-
-  if (!document.styleSheets) return;
-  var theRules = new Array();
-  var lastSheet = document.styleSheets[document.styleSheets.length - 1];
-  if (lastSheet.insertRule) { // Mozilla
-    lastSheet.insertRule('.calendarFolder' + folder.substr(1) + ' {'
-                        + ' background-color: '
-                        + color
-                        + ' !important; }', 0);
-  }
-  else { // IE
-    lastSheet.addRule('.calendarFolder' + folder.substr(1),
-                     ' background-color: '
-                     + color
-                     + ' !important; }');
+  if ($(folderPath))
+    window.alert(clabels["You have already subscribed to that folder!"]);
+  else {
+    var calendarList = $("calendarList");
+    var lis = calendarList.childNodesWithTag("li");
+    var li = document.createElement("li");
+    
+    // Add the calendar to the proper place
+    var previousOwner = null;
+    for (var i = 0; i < lis.length; i++) {
+      var currentFolderName = lis[i].lastChild.nodeValue.strip();
+      var currentOwner = lis[i].readAttribute('owner');
+      if (currentOwner == owner) {
+       previousOwner = currentOwner;
+       if (currentFolderName > folderName)
+         break;
+      }
+      else if (previousOwner || 
+              (currentOwner != UserLogin && currentOwner > owner))
+       break;
+    }
+    if (i != lis.length) // User is subscribed to other calendars of the same owner
+      calendarList.insertBefore(li, lis[i]);
+    else 
+      calendarList.appendChild(li);
+    
+    li.setAttribute("id", folderPath);
+    li.setAttribute("owner", owner);
+
+    // Generate new color
+    var colorTable = [1, 1, 1];
+    var color;
+    var currentValue = lis.length + 1;
+    var index = 0;
+    while (currentValue) {
+      if (currentValue & 1)
+       colorTable[index]++;
+      if (index == 3)
+       index = 0;
+      currentValue >>= 1;
+      index++;
+    }
+    colorTable[0] = parseInt(255 / colorTable[0]) - 1;
+    colorTable[1] = parseInt(255 / colorTable[1]) - 1;
+    colorTable[2] = parseInt(255 / colorTable[2]) - 1;
+
+    color = "#"
+      + colorTable[2].toString(16)
+      + colorTable[1].toString(16)
+      + colorTable[0].toString(16);
+    //log ("color = " + color);
+    
+    var checkBox = document.createElement("input");
+    checkBox.setAttribute("type", "checkbox");
+    li.appendChild(checkBox);
+    li.appendChild(document.createTextNode(" "));
+    $(checkBox).addClassName("checkBox");
+
+    var colorBox = document.createElement("div");
+    li.appendChild(colorBox);
+    li.appendChild(document.createTextNode(folderName));
+    colorBox.appendChild(document.createTextNode("OO"));
+
+    $(colorBox).addClassName("colorBox");
+    $(colorBox).addClassName('calendarFolder' + folderPath.substr(1));
+
+    // Register events (doesn't work with Safari)
+    Event.observe(li, "mousedown",  listRowMouseDownHandler);
+    Event.observe(li, "selectstart", listRowMouseDownHandler);
+    Event.observe(li, "click",  onRowClick);
+    Event.observe(checkBox, "click",
+                 updateCalendarStatus.bindAsEventListener(checkBox));
+
+    var url = URLForFolderID(folderPath) + "/canAccessContent";
+    triggerAjaxRequest(url, calendarEntryCallback, folderPath);
+    
+    // Update CSS for events color
+    if (!document.styleSheets) return;
+    
+    var styleElement = document.createElement("style");
+    styleElement.type = "text/css";
+    var selectors = [
+                    '.calendarFolder' + folderPath.substr(1),
+                    'div.colorBox.calendarFolder' + folderPath.substr(1)
+                    ];
+    var rules = [
+                ' { background-color: ' + color + ' !important; }',
+                ' { color: ' + color + ' !important; }'
+                ];
+    for (var i = 0; i < rules.length; i++)
+      if (styleElement.styleSheet && styleElement.styleSheet.addRule)
+       styleElement.styleSheet.addRule(selectors[i], rules[i]); // IE
+      else
+       styleElement.appendChild(document.createTextNode(selectors[i] + rules[i])); // Mozilla _+ Safari
+    document.getElementsByTagName("head")[0].appendChild(styleElement);
   }
 }
 
 function onFolderSubscribeCB(folderData) {
-   var folder = $(folderData["folder"]);
+  var folder = $(folderData["folder"]);
    if (!folder)
      appendCalendar(folderData["folderName"], folderData["folder"]);
 }
@@ -1425,40 +1536,39 @@ function onFolderSubscribeCB(folderData) {
 function onFolderUnsubscribeCB(folderId) {
   var node = $(folderId);
   node.parentNode.removeChild(node);
-  refreshEvents();
-  refreshTasks();
-  changeCalendarDisplay();
+  if (removeFolderRequestCount == 0) {
+    refreshEvents();
+    refreshTasks();
+    changeCalendarDisplay();
+  }
 }
 
 function onCalendarRemove(event) {
-  var nodes = $("calendarList").getSelectedNodes();
-  if (nodes.length > 0) { 
-     nodes[0].deselect();
-     var folderId = nodes[0].getAttribute("id");
-     var folderIdElements = folderId.split("_");
-     if (folderIdElements.length > 1) {
-       unsubscribeFromFolder(folderId, onFolderUnsubscribeCB, folderId);
-     }
-     else {
-       var calId = folderIdElements[0].substr(1);
-       deletePersonalCalendar(calId);
-     }
+  if (removeFolderRequestCount == 0) {
+    var nodes = $("calendarList").getSelectedNodes();
+    for (var i = 0; i < nodes.length; i++) {
+      nodes[i].deselect();
+      var folderId = nodes[i].getAttribute("id");
+      var folderIdElements = folderId.split("_");
+      if (folderIdElements.length > 1) {
+       unsubscribeFromFolder(folderId, onFolderUnsubscribeCB, folderId);
+      }
+      else
+       deletePersonalCalendar(folderIdElements[0]);
+    }
   }
-
+  
   preventDefault(event);
 }
 
-function deletePersonalCalendar(folderId) {
+function deletePersonalCalendar(folderElement) {
+  var folderId = folderElement.substr(1);
   var label
-    = labels["Are you sure you want to delete the selected calendar?"];
+    = labels["Are you sure you want to delete the calendar \"%{0}\"?"].formatted($(folderElement).lastChild.nodeValue.strip());
   if (window.confirm(label)) {
-    if (document.deletePersonalCalendarAjaxRequest) {
-      document.deletePersonalCalendarAjaxRequest.aborted = true;
-      document.deletePersonalCalendarAjaxRequest.abort();
-    }
+    removeFolderRequestCount++;
     var url = ApplicationBaseURL + "/" + folderId + "/deleteFolder";
-    document.deletePersonalCalendarAjaxRequest
-      = triggerAjaxRequest(url, deletePersonalCalendarCallback, folderId);
+    triggerAjaxRequest(url, deletePersonalCalendarCallback, folderId);
   }
 }
 
@@ -1478,11 +1588,13 @@ function deletePersonalCalendarCallback(http) {
        else
          i++;
       }
-      refreshEvents();
-      refreshTasks();
-      changeCalendarDisplay();
+      removeFolderRequestCount--;
+      if (removeFolderRequestCount == 0) {
+       refreshEvents();
+       refreshTasks();
+       changeCalendarDisplay();
+      }
     }
-    document.deletePersonalCalendarAjaxRequest = null;
   }
   else
     log ("ajax problem 5: " + http.status);
@@ -1500,6 +1612,8 @@ function configureLists() {
 
    list = $("eventsList");
    list.multiselect = true;
+   //configureSortableTableHeaders(list);
+   TableKit.Resizable.init(list, {'trueResize' : true, 'keepWidth' : true});
    Event.observe(list, "mousedown",
                 onEventsSelectionChange.bindAsEventListener(list));
    var div = list.parentNode;