]> err.no Git - scalable-opengroupware.org/blobdiff - UI/WebServerResources/SchedulerUI.js
Install libs to /usr/lib
[scalable-opengroupware.org] / UI / WebServerResources / SchedulerUI.js
index e1b86a0ab4e77a906bd7fbe00377f069355f1cfe..012688b25f724c8a2993891a7d1335f284a57d35 100644 (file)
@@ -1,11 +1,10 @@
 /* JavaScript for SOGoCalendar */
 
-var sortOrder = '';
-var sortKey = '';
 var listFilter = 'view_today';
 
 var listOfSelection = null;
 var selectedCalendarCell;
+var calendarColorIndex = null;
 
 var showCompletedTasks = 0;
 
@@ -23,14 +22,18 @@ var usersRightsWindowHeight = 250;
 var usersRightsWindowWidth = 502;
 
 function newEvent(sender, type) {
-   var day = sender.day;
+   var day = sender.readAttribute("day");
    if (!day)
       day = currentDay;
-
-   var hour = sender.hour;
-   if (!hour)
-      hour = sender.getAttribute("hour");
-   var folderID = getSelectedFolder();
+   var hour = sender.readAttribute("hour");
+   var folder = getSelectedFolder();
+   var folderID = folder.readAttribute("id");
+   var roles = folder.readAttribute("roles");
+   if (roles) {
+     roles = roles.split(",")
+       if ($(roles).indexOf("PublicModifier") < 0)
+        folderID = "/personal";
+   }
    var urlstr = ApplicationBaseURL + folderID + "/new" + type;
    var params = new Array();
    if (day)
@@ -39,7 +42,6 @@ function newEvent(sender, type) {
       params.push("hm=" + hour);
    if (params.length > 0)
       urlstr += "?" + params.join("&");
-   
    window.open(urlstr, "", "width=490,height=470,resizable=0");
    
    return false; /* stop following the link */
@@ -47,12 +49,12 @@ function newEvent(sender, type) {
 
 function getSelectedFolder() {
   var folder;
-
-  var nodes = $("calendarList").getSelectedRows();
+  var list = $("calendarList");
+  var nodes = list.getSelectedRows();
   if (nodes.length > 0)
-    folder = nodes[0].getAttribute("id");
+    folder = nodes[0];
   else
-    folder = "/personal";
+    folder = list.down("li");
 
   return folder;
 }
@@ -70,19 +72,27 @@ function _editEventId(id, calendar) {
   var targetname = "SOGo_edit_" + id;
   var win = window.open(urlstr, "_blank",
                         "width=490,height=470,resizable=0");
-  win.focus();
+  if (win)
+    win.focus();
 }
 
 function editEvent() {
   if (listOfSelection) {
     var nodes = listOfSelection.getSelectedRows();
 
+    if (nodes.length == 0) {
+      window.alert(labels["Please select an event or a task."]);
+      return false;
+    }
+
     for (var i = 0; i < nodes.length; i++)
       _editEventId(nodes[i].getAttribute("id"),
                    nodes[i].calendar);
   } else if (selectedCalendarCell) {
-      _editEventId(selectedCalendarCell[0].cname,
-                   selectedCalendarCell[0].calendar);
+    _editEventId(selectedCalendarCell[0].cname,
+                 selectedCalendarCell[0].calendar);
+  } else {
+    window.alert(labels["Please select an event or a task."]);
   }
 
   return false; /* stop following the link */
@@ -131,6 +141,8 @@ function deleteEvent() {
         }
         _batchDeleteEvents();
       }
+    } else {
+      window.alert(labels["Please select an event or a task."]);
     }
   }
   else if (selectedCalendarCell) {
@@ -146,7 +158,7 @@ function deleteEvent() {
      }
   }
   else
-    window.alert("no selection");
+    window.alert(labels["Please select an event or a task."]);
 
   return false;
 }
@@ -204,19 +216,25 @@ function modifyEventCallback(http) {
 function deleteEventCallback(http) {
   if (http.readyState == 4) {
     if (isHttpStatus204(http.status)) {
+      var isTask = false;
       var nodes = http.callbackData;
       for (var i = 0; i < nodes.length; i++) {
        var node = $(nodes[i]);
-       if (node)
+       if (node) {
+         isTask = isTask || (node.parentNode.id == 'tasksList');
          node.parentNode.removeChild(node);
+       }
       }
       if (eventsToDelete.length)
        _batchDeleteEvents();
       else {
        document.deleteEventAjaxRequest = null;
-       refreshEvents();
-       refreshTasks();
-       changeCalendarDisplay();
+       if (isTask)
+         refreshTasks();
+       else {
+         refreshEvents();
+         changeCalendarDisplay();
+       }
       }
     }
     else
@@ -255,6 +273,7 @@ function onDaySelect(node) {
   document.selectedDate = td;
 
   changeCalendarDisplay( { "day": day } );
+  currentDay = day;
   if (needRefresh)
     refreshEvents();
 
@@ -271,9 +290,13 @@ function onDateSelectorGotoMonth(event) {
 
 function onCalendarGotoDay(node) {
    var day = node.getAttribute("date");
+   var needRefresh = (listFilter == 'view_selectedday'
+                     && day != currentDay);
    
    changeDateSelectorDisplay(day);
    changeCalendarDisplay( { "day": day } );
+   if (needRefresh)
+     refreshEvents();
   
    return false;
 }
@@ -314,50 +337,67 @@ function eventsListCallback(http) {
 
     document.eventsListAjaxRequest = null;
     var table = $("eventsList");
-    var params = parseQueryParameters(http.callbackData);
-    sortKey = params["sort"];
-    sortOrder = params["desc"];
-    lastClickedRow = null; // from generic.js
-
-    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));
-
-      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, "mousedown", onRowClick);
+       Event.observe(row, "selectstart", listRowMouseDownHandler);
+       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][10]));
+
+       td = document.createElement("td");
+       row.appendChild(td);
+       Event.observe(td, "mousedown", listRowMouseDownHandler, true);
+       td.appendChild(document.createTextNode(data[i][11]));
+      
+       td = document.createElement("td");
+       row.appendChild(td);
+       Event.observe(td, "mousedown", listRowMouseDownHandler, true);
+       td.appendChild(document.createTextNode(data[i][6]));
+      }
+
+      if (sorting["attribute"] && sorting["attribute"].length > 0) {
+       var sortHeader = $(sorting["attribute"] + "Header");
       
-      td = document.createElement("td");
-      row.appendChild(td);
-      Event.observe(td, "mousedown", listRowMouseDownHandler, true);
-      td.appendChild(document.createTextNode(data[i][6]));
+       if (sortHeader) {
+         var sortImages = $(table.tHead).getElementsByClassName("sortImage");
+         $(sortImages).each(function(item) {
+             item.remove();
+           });
+
+         var sortImage = createElement("img", "messageSortImage", "sortImage");
+         sortHeader.insertBefore(sortImage, sortHeader.firstChild);
+         if (sorting["ascending"])
+           sortImage.src = ResourcesURL + "/title_sortdown_12x12.png";
+         else
+           sortImage.src = ResourcesURL + "/title_sortup_12x12.png";
+       }
+      }
     }
   }
   else
@@ -371,42 +411,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);
-      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++) {
+       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).addClassName(data[i][6]);
+       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");
@@ -438,7 +482,7 @@ function restoreCurrentDaySelection(div) {
 }
 
 function changeDateSelectorDisplay(day, keepCurrentDay) {
-  var url = ApplicationBaseURL + "/dateselector";
+  var url = ApplicationBaseURL + "dateselector";
   if (day)
     url += "?day=" + day;
 
@@ -465,27 +509,62 @@ function changeDateSelectorDisplay(day, keepCurrentDay) {
   }
 }
 
-function changeCalendarDisplay(time, newView) {
-  var url = ApplicationBaseURL + "/" + ((newView) ? newView : currentView);
-
-  selectedCalendarCell = null;
-
+function changeCalendarDisplay(data, newView) {
+  var url = ApplicationBaseURL + ((newView) ? newView : currentView);
   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)
     day = currentDay;
-  if (day)
-    url += "?day=" + day;
 
+  if (day) {
+    if (data) {
+      var divs = $$('div.day[day='+day+']');
+      if (divs.length) {
+       // Don't reload the view if the event is present in current view
+       
+       // Deselect previous day
+       var selectedDivs = $$('div.day.selectedDay');
+       selectedDivs.each(function(div) {
+           div.removeClassName('selectedDay');
+         });
+       
+       // Select new day
+       divs.each(function(div) {
+           div.addClassName('selectedDay');
+         });
+       
+       // Deselect day in date selector
+       if (document.selectedDate)
+         document.selectedDate.deselect();
+       
+       // Select day in date selector
+       var selectedLink = $$('table#dateSelectorTable a[day='+day+']');
+       if (selectedLink.length > 0) {
+         selectedCell = selectedLink[0].up(1);
+         selectedCell.select();
+         document.selectedDate = selectedCell;
+       }
+       
+       // Scroll to event
+       scrollDayView(scrollEvent);     
+
+       return false;
+      }
+    }
+    url += "?day=" + day;
+  }
 //   if (newView)
 //     log ("switching to view: " + newView);
 //   log ("changeCalendarDisplay: " + url);
 
+  selectedCalendarCell = null;
+
   if (document.dayDisplayAjaxRequest) {
 //     log ("aborting day ajaxrq");
     document.dayDisplayAjaxRequest.aborted = true;
@@ -493,7 +572,9 @@ function changeCalendarDisplay(time, newView) {
   }
   document.dayDisplayAjaxRequest
      = triggerAjaxRequest(url, calendarDisplayCallback,
-                         { "view": newView, "day": day, "hour": hour });
+                         { "view": newView,
+                           "day": day,
+                           "scrollEvent": scrollEvent });
 
   return false;
 }
@@ -521,25 +602,39 @@ 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 divs;
 
+  // Select event in calendar view
+  if (scrollEvent) {
+    divs = $$("div#calendarContent div." + eventClass(scrollEvent));
+    selectCalendarEvent(divs[0]);
+  }
+  
+  // Don't scroll if in month view
+  if (currentView == "monthview")
+    return;
+
+  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) {
+    divs = $$("div#calendarContent div." + eventClass(scrollEvent));
+    var classes = $w(divs[0].className);
+    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
+    // Scroll to 8 AM
+    offset = hours[8].offsetTop;
+
+  daysView.scrollTop = offset - 5;
 }
 
 function onClickableCellsDblClick(event) {
@@ -549,7 +644,7 @@ function onClickableCellsDblClick(event) {
   event.returnValue = false;
 }
 
-function refreshCalendarEvents() {
+function refreshCalendarEvents(scrollEvent) {
    var todayDate = new Date();
    var sd;
    var ed;
@@ -592,21 +687,26 @@ function refreshCalendarEvents() {
       document.refreshCalendarEventsAjaxRequest.aborted = true;
       document.refreshCalendarEventsAjaxRequest.abort();
    }
-   var url = ApplicationBaseURL + "/eventslist?sd=" + sd + "&ed=" + ed;
+   var url = ApplicationBaseURL + "eventslist?sd=" + sd + "&ed=" + ed;
    document.refreshCalendarEventsAjaxRequest
       = triggerAjaxRequest(url, refreshCalendarEventsCallback,
-                          {"startDate": sd, "endDate": ed});
+                          {"startDate": sd, "endDate": ed,
+                           "scrollEvent": scrollEvent});
 }
 
 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"]);
+    }
+    scrollDayView(http.callbackData["scrollEvent"]);
   }
   else
      log("AJAX error when refreshing calendar events");
@@ -617,22 +717,14 @@ function drawCalendarEvent(eventData, sd, ed) {
    var viewEndDate = ed.asDate();
 
    var startDate = new Date();
+   startDate.setTime(eventData[4] * 1000);
    var endDate = new Date();
-   if (eventData[7] == 0) {
-     startDate.setTime(eventData[4] * 1000 + (1000 * UTCOffset));
-     endDate.setTime(eventData[5] * 1000 + (1000 * UTCOffset));
-   }
-   else {
-     startDate.setTime(eventData[4] * 1000);
-     endDate.setTime(eventData[5] * 1000);
-   }
+   endDate.setTime(eventData[5] * 1000);
 
-//    log ("s: " + startDate+ "; e: " + endDate);
+//    log ("s: " + startDate + "; e: " + endDate);
 
    var days = startDate.daysUpTo(endDate);
 
-   var divs = new Array();
-
    var title;
    if (currentView == "monthview"
        && (eventData[7] == 0))
@@ -644,7 +736,7 @@ function drawCalendarEvent(eventData, sd, ed) {
 //    log("viewS: " + viewStartDate);
    var startHour = null;
    var endHour = null;
-
+   
    var siblings = new Array();
    for (var i = 0; i < days.length; i++)
       if (days[i].earlierDate(viewStartDate) == viewStartDate
@@ -679,6 +771,8 @@ function drawCalendarEvent(eventData, sd, ed) {
                                    null, null, title);
         siblings.push(eventDiv);
         eventDiv.siblings = siblings;
+        if (eventData[9].length > 0)
+          eventDiv.addClassName(eventData[9]);
         var dayString = days[i].getDayString();
 //      log("day: " + dayString);
         var parentDiv = null;
@@ -719,16 +813,22 @@ function drawCalendarEvent(eventData, sd, ed) {
            }
         }
         if (parentDiv)
-           parentDiv.appendChild(eventDiv);
+          parentDiv.appendChild(eventDiv);
       }
 }
 
+function eventClass(cname) {
+  return  escape(cname.replace(".", "-"));
+}
+
+
 function newEventDIV(cname, calendar, starts, lasts,
                     startHour, endHour, title) {
    var eventDiv = document.createElement("div");
    eventDiv.cname = escape(cname);
    eventDiv.calendar = calendar;
    $(eventDiv).addClassName("event");
+   $(eventDiv).addClassName(eventClass(cname));
    $(eventDiv).addClassName("starts" + starts);
    $(eventDiv).addClassName("lasts" + lasts);
    for (var i = 1; i < 5; i++) {
@@ -783,22 +883,21 @@ function calendarDisplayCallback(http) {
     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);
+    else
       contentView = $("daysView");
-    }
-    refreshCalendarEvents();
+    
+    refreshCalendarEvents(http.callbackData.scrollEvent);
+    
     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"); 
@@ -838,7 +937,7 @@ function assignCalendar(name) {
 }
 
 function popupCalendar(node) {
-   var nodeId = node.getAttribute("inputId");
+   var nodeId = $(node).readAttribute("inputId");
    var input = $(nodeId);
    input.calendar.popup();
 
@@ -847,21 +946,10 @@ function popupCalendar(node) {
 
 function onEventContextMenu(event) {
   var topNode = $("eventsList");
-//   log(topNode);
-
   var menu = $("eventsListMenu");
 
   Event.observe(menu, "hideMenu",  onEventContextMenuHide);
   popupMenu(event, "eventsListMenu", this);
-
-  var topNode = $("eventsList");
-  var selectedNodes = topNode.getSelectedRows();
-  topNode.menuSelectedRows = selectedNodes;
-  for (var i = 0; i < selectedNodes.length; i++)
-    selectedNodes[i].deselect();
-
-  topNode.menuSelectedEntry = this;
-  this.select();
 }
 
 function onEventContextMenuHide(event) {
@@ -871,20 +959,20 @@ function onEventContextMenuHide(event) {
     topNode.menuSelectedEntry.deselect();
     topNode.menuSelectedEntry = null;
   }
-  if (topNode.menuSelectedRows) {
-    var nodeIds = topNode.menuSelectedRows;
-    for (var i = 0; i < nodeIds.length; i++) {
-      var node = $(nodeIds[i]);
-      node.select();
-    }
-    topNode.menuSelectedRows = null;
-  }
 }
 
 function onEventsSelectionChange() {
   listOfSelection = this;
   this.removeClassName("_unfocused");
   $("tasksList").addClassName("_unfocused");
+
+  var rows = this.tBodies[0].getSelectedNodes();
+  if (rows.length == 1) {
+    var row = rows[0];
+    changeCalendarDisplay( { "day": row.day,
+         "scrollEvent": row.getAttribute("id") } );
+    changeDateSelectorDisplay(row.day);
+  }
 }
 
 function onTasksSelectionChange() {
@@ -914,7 +1002,7 @@ function _loadTasksHref(href) {
     document.tasksListAjaxRequest.aborted = true;
     document.tasksListAjaxRequest.abort();
   }
-  url = ApplicationBaseURL + "/" + href;
+  url = ApplicationBaseURL + href;
 
   var tasksList = $("tasksList");
   var selectedIds;
@@ -933,17 +1021,47 @@ function _loadTasksHref(href) {
 }
 
 function onHeaderClick(event) {
-  //log("onHeaderClick: " + this.link);
-  //_loadEventHref(this.link);
+   var headerId = this.getAttribute("id");
+  var newSortAttribute;
+  if (headerId == "titleHeader")
+    newSortAttribute = "title";
+  else if (headerId == "startHeader")
+    newSortAttribute = "start";
+  else if (headerId == "endHeader")
+    newSortAttribute = "end";
+  else if (headerId == "locationHeader")
+    newSortAttribute = "location";
+  else
+    newSortAttribute = "start";
+  
+   if (sorting["attribute"] == newSortAttribute)
+    sorting["ascending"] = !sorting["ascending"];
+  else {
+    sorting["attribute"] = newSortAttribute;
+    sorting["ascending"] = true;
+  }
+   refreshEvents();
 
-  preventDefault(event);
+  Event.stop(event);
+}
+
+function refreshCurrentFolder() {
+  refreshEvents();
 }
 
 function refreshEvents() {
-   return _loadEventHref("eventslist?desc=" + sortOrder
-                        + "&sort=" + sortKey
-                        + "&day=" + currentDay
-                        + "&filterpopup=" + listFilter);
+  var titleSearch;
+  var value = search["value"];
+  if (value && value.length)
+    titleSearch = "&search=" + value;
+  else
+    titleSearch = "";
+  return _loadEventHref("eventslist?asc=" + sorting["ascending"]
+                       + "&sort=" + sorting["attribute"]
+                       + "&day=" + currentDay
+                       + titleSearch
+                       + "&filterpopup=" + listFilter);
 }
 
 function refreshTasks() {
@@ -964,16 +1082,6 @@ function onListFilterChange() {
   return refreshEvents();
 }
 
-function onEventClick(event) { log ("onEventClick");
-  var day = this.day;
-  var hour = this.hour;
-
-  changeCalendarDisplay( { "day": day, "hour": hour} );
-  changeDateSelectorDisplay(day);
-
-  return onRowClick(event);
-}
-
 function selectMonthInMenu(menu, month) {
    var entries = menu.childNodes[1].childNodesWithTag("LI");
    for (i = 0; i < entries.length; i++) {
@@ -1033,23 +1141,25 @@ function onYearMenuItemClick(event) {
   changeDateSelectorDisplay(year + month + "01", true);
 }
 
-function onSearchFormSubmit() {
-  log ("search not implemented");
+function selectCalendarEvent(div) {
+  // Select event in calendar view
+  if (selectedCalendarCell)
+     for (var i = 0; i < selectedCalendarCell.length; i++)
+       selectedCalendarCell[i].deselect();
 
-  return false;
+  for (var i = 0; i < div.siblings.length; i++)
+     div.siblings[i].select();
+
+  selectedCalendarCell = div.siblings;
 }
 
 function onCalendarSelectEvent() {
   var list = $("eventsList");
-  list.tBodies[0].deselectAll();
 
-  if (selectedCalendarCell)
-     for (var i = 0; i < selectedCalendarCell.length; i++)
-       selectedCalendarCell[i].deselect();
+  selectCalendarEvent(this);
 
-  for (var i = 0; i < this.siblings.length; i++)
-     this.siblings[i].select();
-  selectedCalendarCell = this.siblings;
+  // Select event in events list
+  $(list.tBodies[0]).deselectAll();
   var row = $(this.cname);
   if (row) {
     var div = row.parentNode.parentNode.parentNode;
@@ -1143,7 +1253,7 @@ function updateTaskStatus(event) {
     newStatus = (newStatus ? 0 : 1);
   }
   
-  url = (ApplicationBaseURL + "/" + this.parentNode.calendar
+  url = (ApplicationBaseURL + this.parentNode.calendar
         + "/" + taskId + "/changeStatus?status=" + newStatus);
 
   if (http) {
@@ -1151,7 +1261,7 @@ function updateTaskStatus(event) {
     http.open("POST", url, false /* not async */);
     http.url = url;
     http.send("");
-    if (http.status == 200)
+    if (isHttpStatus204(http.status))
       refreshTasks();
   } else
     log ("no http client?");
@@ -1291,6 +1401,21 @@ function browseURL(anchor, event) {
   return false;
 }
 
+function onCalendarsMenuPrepareVisibility() {
+  var folders = $("calendarList");
+  var selected = folders.getSelectedNodes();  
+
+  if (selected.length > 0) {
+    var folderOwner = selected[0].getAttribute("owner");
+    var sharingOption = $(this).down("ul").childElements().last();
+    // Disable the "Sharing" option when calendar is not owned by user
+    if (folderOwner == UserLogin || IsSuperUser)
+      sharingOption.removeClassName("disabled");
+    else
+      sharingOption.addClassName("disabled");
+  }
+}
+
 function getMenus() {
    var menus = {};
 
@@ -1316,13 +1441,20 @@ function getMenus() {
                                      null, "-", onMenuSharing);
    menus["searchMenu"] = new Array(setSearchCriteria);
 
+   var calendarsMenu = $("calendarsMenu");
+   if (calendarsMenu)
+     calendarsMenu.prepareVisibility = onCalendarsMenuPrepareVisibility;
+
    return menus;
 }
 
 function onMenuSharing(event) {
+  if ($(this).hasClassName("disabled"))
+    return;
+
   var folders = $("calendarList");
   var selected = folders.getSelectedNodes()[0];
-  /* FIXME: activation of the context menu should preferable select the entry
+  /* FIXME: activation of the context menu should preferably select the entry
               above which the event has occured */
   if (selected) {
      var folderID = selected.getAttribute("id");
@@ -1375,9 +1507,9 @@ function onMenuModify(event) {
   var selected = folders.getSelectedNodes()[0];
 
   if (UserLogin == selected.getAttribute("owner")) {
-    var node = selected.childNodes[4];
+    var node = selected.childNodes[selected.childNodes.length - 1];
     var currentName = node.nodeValue.trim();
-    var newName = window.prompt(labels["Address Book Name"],
+    var newName = window.prompt(labels["Name of the Calendar"],
                                currentName);
     if (newName && newName.length > 0
        && newName != currentName) {
@@ -1410,41 +1542,79 @@ function onCalendarAdd(event) {
   preventDefault(event);
 }
 
-function appendCalendar(folderName, folder) {
-  if (folder)
-    folder = accessToSubscribedFolder(folder);
+function appendCalendar(folderName, folderPath) {
+  var owner;
+
+  if (folderPath) {
+    owner = getSubscribedFolderOwner(folderPath);
+    folderPath = accessToSubscribedFolder(folderPath);
+  }
   else
-    folder = "/" + folderName;
+    folderPath = "/" + folderName;
+
+  if (!owner)
+    owner = UserLogin;
 
-//   log ("append: " + folderName + "; folder: " + folder);
+  //log ("append name: " + folderName + "; path: " + folderPath + "; owner: " + owner);
 
-  if ($(folder))
+  if ($(folderPath))
     window.alert(clabels["You have already subscribed to that folder!"]);
   else {
     var calendarList = $("calendarList");
-    var lis = calendarList.childNodesWithTag("li");
-    var color = indexColor(lis.length + 100);
-    //log ("color: " + color);
-
+    var items = calendarList.childNodesWithTag("li");
     var li = document.createElement("li");
-    calendarList.appendChild(li);
-    li.setAttribute("id", folder);
-
+    
+    // Add the calendar to the proper place
+    var i = getListIndexForFolder(items, owner, folderName);
+    if (i != items.length) // User is subscribed to other calendars of the same owner
+      calendarList.insertBefore(li, items[i]);
+    else 
+      calendarList.appendChild(li);
+    
+    li.setAttribute("id", folderPath);
+    li.setAttribute("owner", owner);
+
+    // Generate new color
+    if (calendarColorIndex == null)
+      calendarColorIndex = items.length;
+    calendarColorIndex++;
+    var colorTable = [1, 1, 1];
+    var color;
+    var currentValue = calendarColorIndex;
+    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");
+    if (owner == UserLogin)
+      checkBox.checked = 1;
 
     var colorBox = document.createElement("div");
     li.appendChild(colorBox);
-    li.appendChild(document.createTextNode(" " + folderName));
+    li.appendChild(document.createTextNode(folderName));
     colorBox.appendChild(document.createTextNode("OO"));
 
     $(colorBox).addClassName("colorBox");
-    if (color)
-      $(colorBox).setStyle({color: color,
-                           backgroundColor: color});
+    $(colorBox).addClassName('calendarFolder' + folderPath.substr(1));
 
     // Register events (doesn't work with Safari)
     Event.observe(li, "mousedown",  listRowMouseDownHandler);
@@ -1453,24 +1623,28 @@ function appendCalendar(folderName, folder) {
     Event.observe(checkBox, "click",
                  updateCalendarStatus.bindAsEventListener(checkBox));
 
-    var url = URLForFolderID(folder) + "/canAccessContent";
-    triggerAjaxRequest(url, calendarEntryCallback, folder);
+    var url = URLForFolderID(folderPath) + "/canAccessContent";
+    triggerAjaxRequest(url, calendarEntryCallback, folderPath);
     
+    // Update CSS for events color
     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; }');
-    }
+    
+    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);
   }
 }
 
@@ -1483,40 +1657,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);
   }
 }
 
@@ -1536,11 +1709,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);
@@ -1558,13 +1733,10 @@ function configureLists() {
 
    list = $("eventsList");
    list.multiselect = true;
-   //configureSortableTableHeaders(list);
+   configureSortableTableHeaders(list);
    TableKit.Resizable.init(list, {'trueResize' : true, 'keepWidth' : true});
    Event.observe(list, "mousedown",
                 onEventsSelectionChange.bindAsEventListener(list));
-   var div = list.parentNode;
-   Event.observe(div, "contextmenu",
-                onEventContextMenu.bindAsEventListener(div));
 }
 
 function initDateSelectorEvents() {
@@ -1584,6 +1756,9 @@ function initDateSelectorEvents() {
 }
 
 function initCalendars() {
+   sorting["attribute"] = "start";
+   sorting["ascending"] = true;
+  
    if (!document.body.hasClassName("popup")) {
       initDateSelectorEvents();
       initCalendarSelector();
@@ -1595,4 +1770,4 @@ function initCalendars() {
    }
 }
 
-addEvent(window, 'load', initCalendars);
+FastInit.addOnLoad(initCalendars);