]> err.no Git - scalable-opengroupware.org/blobdiff - UI/WebServerResources/SchedulerUI.js
Install libs to /usr/lib
[scalable-opengroupware.org] / UI / WebServerResources / SchedulerUI.js
index 5ca05b1820d19e9562698821705b0f5903c4e5b4..012688b25f724c8a2993891a7d1335f284a57d35 100644 (file)
@@ -1,39 +1,40 @@
 /* JavaScript for SOGoCalendar */
 
-var sortOrder = '';
-var sortKey = '';
 var listFilter = 'view_today';
 
 var listOfSelection = null;
 var selectedCalendarCell;
+var calendarColorIndex = null;
 
 var showCompletedTasks = 0;
 
 var currentDay = '';
-var currentView = "dayview";
+var currentView = "weekview";
 
 var cachedDateSelectors = new Array();
 
 var contactSelectorAction = 'calendars-contacts';
 
 var eventsToDelete = new Array();
-var ownersOfEventsToDelete = new Array();
+var calendarsOfEventsToDelete = new Array();
 
 var usersRightsWindowHeight = 250;
 var usersRightsWindowWidth = 502;
 
 function newEvent(sender, type) {
-   var day = sender.getAttribute("day");
+   var day = sender.readAttribute("day");
    if (!day)
       day = currentDay;
-
-   var user = UserLogin;
-   if (sender.parentNode.getAttribute("id") != "toolbar"
-       && currentView == "multicolumndayview" && type == "event")
-      user = sender.parentNode.parentNode.getAttribute("user");
-
-   var hour = sender.getAttribute("hour");
-   var urlstr = UserFolderURL + "../" + user + "/Calendar/new" + type;
+   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)
       params.push("day=" + day);
@@ -41,12 +42,23 @@ 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 */
 }
 
+function getSelectedFolder() {
+  var folder;
+  var list = $("calendarList");
+  var nodes = list.getSelectedRows();
+  if (nodes.length > 0)
+    folder = nodes[0];
+  else
+    folder = list.down("li");
+
+  return folder;
+}
+
 function onMenuNewEventClick(event) {
    newEvent(this, "event");
 }
@@ -55,29 +67,32 @@ function onMenuNewTaskClick(event) {
    newEvent(this, "task");
 }
 
-function _editEventId(id, owner) {
-  var urlBase;
-  if (owner)
-    urlBase = UserFolderURL + "../" + owner + "/";
-  urlBase += "Calendar/"
-
-  var urlstr = urlBase + id + "/edit";
-
-  var win = window.open(urlstr, "SOGo_edit_" + id,
+function _editEventId(id, calendar) {
+  var urlstr = ApplicationBaseURL + "/" + calendar + "/" + id + "/edit";
+  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].getAttribute("owner"));
+                   nodes[i].calendar);
   } else if (selectedCalendarCell) {
-      _editEventId(selectedCalendarCell.getAttribute("aptCName"),
-                   selectedCalendarCell.getAttribute("owner"));
+    _editEventId(selectedCalendarCell[0].cname,
+                 selectedCalendarCell[0].calendar);
+  } else {
+    window.alert(labels["Please select an event or a task."]);
   }
 
   return false; /* stop following the link */
@@ -85,9 +100,9 @@ function editEvent() {
 
 function _batchDeleteEvents() {
   var events = eventsToDelete.shift();
-  var owner = ownersOfEventsToDelete.shift();
-  var urlstr = (UserFolderURL + "../" + owner + "/Calendar/batchDelete?ids="
-                + events.join('/'));
+  var calendar = calendarsOfEventsToDelete.shift();
+  var urlstr = (ApplicationBaseURL + "/" + calendar
+               + "/batchDelete?ids=" + events.join('/'));
   document.deleteEventAjaxRequest = triggerAjaxRequest(urlstr,
                                                        deleteEventCallback,
                                                        events);
@@ -100,9 +115,9 @@ function deleteEvent() {
     if (nodes.length > 0) {
       var label = "";
       if (listOfSelection == $("tasksList"))
-        label = labels["taskDeleteConfirmation"].decodeEntities();
+        label = labels["taskDeleteConfirmation"];
       else
-        label = labels["appointmentDeleteConfirmation"].decodeEntities();
+        label = labels["eventDeleteConfirmation"];
       
       if (confirm(label)) {
         if (document.deleteEventAjaxRequest) {
@@ -110,38 +125,40 @@ function deleteEvent() {
           document.deleteEventAjaxRequest.abort();
         }
         var sortedNodes = new Array();
-        var owners = new Array();
+        var calendars = new Array();
 
         for (var i = 0; i < nodes.length; i++) {
-          var owner = nodes[i].getAttribute("owner");
-          if (!sortedNodes[owner]) {
-              sortedNodes[owner] = new Array();
-              owners.push(owner);
+          var calendar = nodes[i].calendar;
+          if (!sortedNodes[calendar]) {
+           sortedNodes[calendar] = new Array();
+           calendars.push(calendar);
           }
-          sortedNodes[owner].push(nodes[i].getAttribute("id"));
+          sortedNodes[calendar].push(nodes[i].cname);
         }
-        for (var i = 0; i < owners.length; i++) {
-          ownersOfEventsToDelete.push(owners[i]);
-          eventsToDelete.push(sortedNodes[owners[i]]);
+        for (var i = 0; i < calendars.length; i++) {
+          calendarsOfEventsToDelete.push(calendars[i]);
+          eventsToDelete.push(sortedNodes[calendars[i]]);
         }
         _batchDeleteEvents();
       }
+    } else {
+      window.alert(labels["Please select an event or a task."]);
     }
   }
   else if (selectedCalendarCell) {
-     var label = labels["appointmentDeleteConfirmation"].decodeEntities();
+     var label = labels["eventDeleteConfirmation"];
      if (confirm(label)) {
         if (document.deleteEventAjaxRequest) {
            document.deleteEventAjaxRequest.aborted = true;
            document.deleteEventAjaxRequest.abort();
         }
-        eventsToDelete.push([selectedCalendarCell.getAttribute("aptCName")]);
-        ownersOfEventsToDelete.push(selectedCalendarCell.getAttribute("owner"));
+        eventsToDelete.push([selectedCalendarCell[0].cname]);
+        calendarsOfEventsToDelete.push(selectedCalendarCell[0].calendar);
         _batchDeleteEvents();
      }
   }
   else
-    window.alert("no selection");
+    window.alert(labels["Please select an event or a task."]);
 
   return false;
 }
@@ -160,88 +177,92 @@ 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"].decodeEntities()));
-  document.body.appendChild(closeDiv);
-  document.body.appendChild(closePseudoWin);
+  closePseudoWin.appendChild(document.createTextNode(labels["closeThisWindowMessage"]));
+
+  var calLink = document.createElement("a");
+  closePseudoWin.appendChild(calLink);
+  calLink.href = ApplicationBaseURL;
+  calLink.appendChild(document.createTextNode(labels["Calendar"].toLowerCase()));
 }
 
 function modifyEventCallback(http) {
-   if (http.readyState == 4) {
-      if (http.status == 200) {
-        if (queryParameters["mail-invitation"].toLowerCase() == "yes")
-           closeInvitationWindow();
-        else {
-           window.opener.setTimeout("refreshAppointmentsAndDisplay();", 100);
-           window.setTimeout("window.close();", 100);
-        }
-      }
+  if (http.readyState == 4) {
+    if (http.status == 200) {
+      var mailInvitation = queryParameters["mail-invitation"];
+      if (mailInvitation && mailInvitation.toLowerCase() == "yes")
+       closeInvitationWindow();
       else {
-        log("showing alert...");
-        window.alert(labels["eventPartStatModificationError"]);
+       window.opener.setTimeout("refreshEventsAndDisplay();", 100);
+       window.setTimeout("window.close();", 100);
       }
-      document.modifyEventAjaxRequest = null;
-   }
-}
-
-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;
-      refreshAppointments();
-      refreshTasks();
-      changeCalendarDisplay();
+//      log("showing alert...");
+      window.alert(labels["eventPartStatModificationError"]);
     }
+    document.modifyEventAjaxRequest = null;
   }
-  else
-    log ("ajax fuckage");
 }
 
-function editDoubleClickedEvent(node) {
-  _editEventId(node.getAttribute("id"),
-               node.getAttribute("owner"));
-  
-  return false;
+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) {
+         isTask = isTask || (node.parentNode.id == 'tasksList');
+         node.parentNode.removeChild(node);
+       }
+      }
+      if (eventsToDelete.length)
+       _batchDeleteEvents();
+      else {
+       document.deleteEventAjaxRequest = null;
+       if (isTask)
+         refreshTasks();
+       else {
+         refreshEvents();
+         changeCalendarDisplay();
+       }
+      }
+    }
+    else
+      log ("deleteEventCallback Ajax error");
+  }
 }
 
-function onSelectAll() {
-  var list = $("appointmentsList");
-  list.selectRowsMatchingClass("appointmentRow");
+function editDoubleClickedEvent(event) {
+  _editEventId(this.cname, this.calendar);
 
-  return false;
+  preventDefault(event);
+  event.cancelBubble = true;
 }
 
-function displayAppointment(event) {
-  _editEventId(this.getAttribute("aptCName"),
-               this.getAttribute("owner"));
+function onSelectAll() {
+  var list = $("eventsList");
+  list.selectRowsMatchingClass("eventRow");
 
-  preventDefault(event);
-  event.stopPropagation();
-  event.cancelBubble = true;
-  event.returnValue = false;
+  return false;
 }
 
 function onDaySelect(node) {
-  var day = node.getAttribute("day");
+  var day = node.getAttribute('day');
   var needRefresh = (listFilter == 'view_selectedday'
                      && day != currentDay);
 
-  var td = node.getParentWithTagName("td");
-  var table = td.getParentWithTagName("table");
+  var td = $(node).getParentWithTagName("td");
+  var table = $(td).getParentWithTagName("table");
 
 //   log ("table.selected: " + table.selected);
 
@@ -252,27 +273,32 @@ function onDaySelect(node) {
   document.selectedDate = td;
 
   changeCalendarDisplay( { "day": day } );
+  currentDay = day;
   if (needRefresh)
-    refreshAppointments();
+    refreshEvents();
 
   return false;
 }
 
-function onDateSelectorGotoMonth(node) {
-  var day = node.getAttribute("date");
+function onDateSelectorGotoMonth(event) {
+   var day = this.getAttribute("date");
 
-  changeDateSelectorDisplay(day, true);
+   changeDateSelectorDisplay(day, true);
 
-  return false;
+   Event.stop(event);
 }
 
 function onCalendarGotoDay(node) {
-  var day = node.getAttribute("date");
-
-  changeDateSelectorDisplay(day);
-  changeCalendarDisplay( { "day": day } );
-
-  return false;
+   var day = node.getAttribute("date");
+   var needRefresh = (listFilter == 'view_selectedday'
+                     && day != currentDay);
+   
+   changeDateSelectorDisplay(day);
+   changeCalendarDisplay( { "day": day } );
+   if (needRefresh)
+     refreshEvents();
+  
+   return false;
 }
 
 function gotoToday() {
@@ -288,36 +314,94 @@ function setDateSelectorContent(content) {
   div.innerHTML = content;
   if (currentDay.length > 0)
     restoreCurrentDaySelection(div);
+
+  initDateSelectorEvents();
 }
 
 function dateSelectorCallback(http) {
   if (http.readyState == 4
       && http.status == 200) {
-    document.dateSelectorAjaxRequest = null;
-    var content = http.responseText;
-    setDateSelectorContent(content);
-    cachedDateSelectors[http.callbackData] = content;
+     document.dateSelectorAjaxRequest = null;
+     var content = http.responseText;
+     setDateSelectorContent(content);
+     cachedDateSelectors[http.callbackData] = content;
   }
   else
-    log ("ajax fuckage");
+    log ("dateSelectorCallback Ajax error");
 }
 
-function appointmentsListCallback(http) {
-  var div = $("appointmentsListView");
-
+function eventsListCallback(http) {
   if (http.readyState == 4
       && http.status == 200) {
-    document.appointmentsListAjaxRequest = null;
-    div.innerHTML = http.responseText;
-    var params = parseQueryParameters(http.callbackData);
-    sortKey = params["sort"];
-    sortOrder = params["desc"];
-    var list = $("appointmentsList");
-    Event.observe(list, "selectionchange", onAppointmentsSelectionChange.bindAsEventListener(list), true);
-    configureSortableTableHeaders();
+     var div = $("eventsListView");
+
+    document.eventsListAjaxRequest = null;
+    var table = $("eventsList");
+    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");
+      
+       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
-    log ("ajax fuckage");
+    log ("eventsListCallback Ajax error");
 }
 
 function tasksListCallback(http) {
@@ -327,40 +411,70 @@ function tasksListCallback(http) {
       && http.status == 200) {
     document.tasksListAjaxRequest = null;
     var list = $("tasksList");
-    var scroll = list.scrollTop;
-    div.innerHTML = http.responseText;
-    list = $("tasksList");
-    Event.observe(list, "selectionchange", onTasksSelectionChange.bindAsEventListener(list), true);
-    list.scrollTop = scroll;
-    if (http.callbackData) {
-      var selectedNodesId = http.callbackData;
-      for (var i = 0; i < selectedNodesId.length; i++)
-        $(selectedNodesId[i]).select();
+    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;
+
+      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 ("ajax fuckage");
+    log ("tasksListCallback Ajax error");
 }
 
 function restoreCurrentDaySelection(div) {
-  var elements = div.getElementsByTagName("a");
+  var elements = $(div).getElementsByTagName("a");
   var day = null;
   var i = 9;
   while (!day && i < elements.length)
     {
-      day = elements[i].getAttribute("day");
+      day = elements[i].day;
       i++;
     }
 
   if (day
       && day.substr(0, 6) == currentDay.substr(0, 6)) {
       for (i = 0; i < elements.length; i++) {
-        day = elements[i].getAttribute("day");
+        day = elements[i].day;
         if (day && day == currentDay) {
-          var td = elements[i].getParentWithTagName("td");
+          var td = $(elements[i]).getParentWithTagName("td");
           if (document.selectedDate)
             document.selectedDate.deselect();
-          td.select();
+          $(td).select();
           document.selectedDate = td;
         }
       }
@@ -395,37 +509,72 @@ 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)
     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;
     document.dayDisplayAjaxRequest.abort();
   }
-  document.dayDisplayAjaxRequest = triggerAjaxRequest(url,
-                                                      calendarDisplayCallback,
-                                                      { "view": newView,
-                                                        "day": day,
-                                                        "hour": hour });
+  document.dayDisplayAjaxRequest
+     = triggerAjaxRequest(url, calendarDisplayCallback,
+                         { "view": newView,
+                           "day": day,
+                           "scrollEvent": scrollEvent });
 
   return false;
 }
@@ -453,24 +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.parentNode.scrollTop = hours[rowNumber + 1].offsetTop;
+  var hours =
+    $(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) {
@@ -480,130 +644,355 @@ function onClickableCellsDblClick(event) {
   event.returnValue = false;
 }
 
+function refreshCalendarEvents(scrollEvent) {
+   var todayDate = new Date();
+   var sd;
+   var ed;
+   if (currentView == "dayview") {
+      if (currentDay)
+        sd = currentDay;
+      else
+        sd = todayDate.getDayString();
+      ed = sd;
+   }
+   else if (currentView == "weekview") {
+      var startDate;
+      if (currentDay)
+        startDate = currentDay.asDate();
+      else
+        startDate = todayDate;
+      startDate = startDate.beginOfWeek();
+      sd = startDate.getDayString();
+      var endDate = new Date();
+      endDate.setTime(startDate.getTime());
+      endDate.addDays(6);
+      ed = endDate.getDayString();
+   }
+   else {
+      var monthDate;
+      if (currentDay)
+        monthDate = currentDay.asDate();
+      else
+        monthDate = todayDate;
+      monthDate.setDate(1);
+      sd = monthDate.beginOfWeek().getDayString();
+
+      var lastMonthDate = new Date();
+      lastMonthDate.setTime(monthDate.getTime());
+      lastMonthDate.setMonth(monthDate.getMonth() + 1);
+      lastMonthDate.addDays(-1);
+      ed = lastMonthDate.endOfWeek().getDayString();
+   }
+   if (document.refreshCalendarEventsAjaxRequest) {
+      document.refreshCalendarEventsAjaxRequest.aborted = true;
+      document.refreshCalendarEventsAjaxRequest.abort();
+   }
+   var url = ApplicationBaseURL + "eventslist?sd=" + sd + "&ed=" + ed;
+   document.refreshCalendarEventsAjaxRequest
+      = triggerAjaxRequest(url, refreshCalendarEventsCallback,
+                          {"startDate": sd, "endDate": ed,
+                           "scrollEvent": scrollEvent});
+}
+
+function refreshCalendarEventsCallback(http) {
+  if (http.readyState == 4
+      && http.status == 200) {
+
+    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++)
+       drawCalendarEvent(data[i],
+                         http.callbackData["startDate"],
+                         http.callbackData["endDate"]);
+    }
+    scrollDayView(http.callbackData["scrollEvent"]);
+  }
+  else
+     log("AJAX error when refreshing calendar events");
+}
+
+function drawCalendarEvent(eventData, sd, ed) {
+   var viewStartDate = sd.asDate();
+   var viewEndDate = ed.asDate();
+
+   var startDate = new Date();
+   startDate.setTime(eventData[4] * 1000);
+   var endDate = new Date();
+   endDate.setTime(eventData[5] * 1000);
+
+//    log ("s: " + startDate + "; e: " + endDate);
+
+   var days = startDate.daysUpTo(endDate);
+
+   var title;
+   if (currentView == "monthview"
+       && (eventData[7] == 0))
+      title = startDate.getDisplayHoursString() + " " + eventData[3];
+   else
+      title = eventData[3];
+
+//    log("title: " + title); 
+//    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
+         && days[i].laterDate(viewEndDate) == viewEndDate) {
+        var starts;
+
+//      log("day: " + days[i]);
+        if (i == 0) {
+           var quarters = (startDate.getUTCHours() * 4
+                           + Math.floor(startDate.getUTCMinutes() / 15));
+           starts = quarters;
+           startHour = startDate.getDisplayHoursString();
+           endHour = endDate.getDisplayHoursString();
+        }
+        else
+           starts = 0;
+        
+        var ends;
+        var lasts;
+        if (i == days.length - 1) {
+           var quarters = (endDate.getUTCHours() * 4
+                           + Math.ceil(endDate.getUTCMinutes() / 15));
+           ends = quarters;
+        }
+        else
+           ends = 96;
+        lasts = ends - starts;
+        if (!lasts)
+           lasts = 1;
+
+        var eventDiv = newEventDIV(eventData[0], eventData[1], starts, lasts,
+                                   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;
+        if (currentView == "monthview") {
+           var dayDivs = $("monthDaysView").childNodesWithTag("div");
+           var j = 0; 
+           while (!parentDiv && j < dayDivs.length) {
+              if (dayDivs[j].getAttribute("day") == dayString)
+                 parentDiv = dayDivs[j];
+              else
+                 j++;
+           }
+        }
+        else {
+           if (eventData[7] == 0) {
+              var daysView = $("daysView");
+              var eventsDiv = $(daysView).childNodesWithTag("div")[1];
+              var dayDivs = $(eventsDiv).childNodesWithTag("div");
+              var j = 0; 
+              while (!parentDiv && j < dayDivs.length) {
+                 if (dayDivs[j].getAttribute("day") == dayString)
+                    parentDiv = dayDivs[j].childNodesWithTag("div")[0];
+                 else
+                    j++;
+              }
+           }
+           else {
+              var header = $("calendarHeader");
+              var daysDiv = $(header).childNodesWithTag("div")[1];
+              var dayDivs = $(daysDiv).childNodesWithTag("div");
+              var j = 0; 
+              while (!parentDiv && j < dayDivs.length) {
+                 if (dayDivs[j].getAttribute("day") == dayString)
+                    parentDiv = dayDivs[j];
+                 else
+                    j++;
+              }
+           }
+        }
+        if (parentDiv)
+          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++) {
+      var shadowDiv = document.createElement("div");
+      eventDiv.appendChild(shadowDiv);
+      $(shadowDiv).addClassName("shadow");
+      $(shadowDiv).addClassName("shadow" + i);
+   }
+   var innerDiv = document.createElement("div");
+   eventDiv.appendChild(innerDiv);
+   $(innerDiv).addClassName("eventInside");
+   $(innerDiv).addClassName("calendarFolder" + calendar);
+
+   var gradientDiv = document.createElement("div");
+   innerDiv.appendChild(gradientDiv);
+   $(gradientDiv).addClassName("gradient");
+   var gradientImg = document.createElement("img");
+   gradientDiv.appendChild(gradientImg);
+   gradientImg.src = ResourcesURL + "/event-gradient.png";
+
+   var textDiv = document.createElement("div");
+   innerDiv.appendChild(textDiv);
+   $(textDiv).addClassName("text");
+   if (startHour) {
+      var headerSpan = document.createElement("span");
+      textDiv.appendChild(headerSpan);
+      $(headerSpan).addClassName("eventHeader");
+      headerSpan.appendChild(document.createTextNode(startHour + " - "
+                                                    + endHour));
+      textDiv.appendChild(document.createElement("br"));
+   }
+   textDiv.appendChild(document.createTextNode(title));
+
+   Event.observe(eventDiv, "mousedown", listRowMouseDownHandler);
+   Event.observe(eventDiv, "click",
+                onCalendarSelectEvent.bindAsEventListener(eventDiv));
+   Event.observe(eventDiv, "dblclick",
+                editDoubleClickedEvent.bindAsEventListener(eventDiv));
+
+   return eventDiv;
+}
+
 function calendarDisplayCallback(http) {
   var div = $("calendarView");
 
-  //log ("calendarDisplayCallback: " + div);
   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);
+    else
       contentView = $("daysView");
-    }
-    var appointments = document.getElementsByClassName("appointment", contentView);
-    for (var i = 0; i < appointments.length; i++) {
-      Event.observe(appointments[i], "mousedown",  listRowMouseDownHandler);
-      Event.observe(appointments[i], "click",  onCalendarSelectAppointment.bindAsEventListener(appointments[i]));
-      Event.observe(appointments[i], "dblclick", displayAppointment.bindAsEventListener(appointments[i]));
-    }
+    
+    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
-      for (var i = 0; i < days.length; i++) {
-        Event.observe(days[i], "click",  onCalendarSelectDay.bindAsEventListener(days[i]));
-        var clickableCells = document.getElementsByClassName("clickableHourCell",
-                                                             days[i]);
-        for (var j = 0; j < clickableCells.length; j++)
-          Event.observe(clickableCells[j], "dblclick", onClickableCellsDblClick.bindAsEventListener(clickableCells[j]));
-      }
-//     log("cbtest1");
+    else {
+       var headerDivs = $("calendarHeader").childNodesWithTag("div"); 
+       var headerDaysLabels = document.getElementsByClassName("day", headerDivs[0]);
+       var headerDays = document.getElementsByClassName("day", headerDivs[1]);
+       for (var i = 0; i < days.length; i++) {
+         headerDays[i].hour = "allday";
+         Event.observe(headerDaysLabels[i], "mousedown", listRowMouseDownHandler);
+         Event.observe(headerDays[i], "click",
+                       onCalendarSelectDay.bindAsEventListener(days[i]));
+         Event.observe(headerDays[i], "dblclick",
+                       onClickableCellsDblClick.bindAsEventListener(headerDays[i]));
+         Event.observe(days[i], "click",
+                       onCalendarSelectDay.bindAsEventListener(days[i]));
+         var clickableCells = document.getElementsByClassName("clickableHourCell",
+                                                              days[i]);
+         for (var j = 0; j < clickableCells.length; j++)
+            Event.observe(clickableCells[j], "dblclick",
+                          onClickableCellsDblClick.bindAsEventListener(clickableCells[j]));
+       }
+    }
   }
   else
-    log ("ajax fuckage");
+    log ("calendarDisplayCallback Ajax error (" + http.readyState + "/" + http.status + ")");
 }
 
 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) {
-   var nodeId = node.getAttribute("inputId");
+   var nodeId = $(node).readAttribute("inputId");
    var input = $(nodeId);
    input.calendar.popup();
 
    return false;
 }
 
-function onAppointmentContextMenu(event, element) {
-  var topNode = $("appointmentsList");
-//   log(topNode);
-
-  var menu = $("appointmentsListMenu");
+function onEventContextMenu(event) {
+  var topNode = $("eventsList");
+  var menu = $("eventsListMenu");
 
-  Event.observe(menu, "hideMenu",  onAppointmentContextMenuHide);
-  popupMenu(event, "appointmentsListMenu", element);
-
-  var topNode = $("appointmentsList");
-  var selectedNodes = topNode.getSelectedRows();
-  topNode.menuSelectedRows = selectedNodes;
-  for (var i = 0; i < selectedNodes.length; i++)
-    selectedNodes[i].deselect();
-
-  topNode.menuSelectedEntry = element;
-  element.select();
+  Event.observe(menu, "hideMenu",  onEventContextMenuHide);
+  popupMenu(event, "eventsListMenu", this);
 }
 
-function onAppointmentContextMenuHide(event) {
-  var topNode = $("appointmentsList");
+function onEventContextMenuHide(event) {
+  var topNode = $("eventsList");
 
   if (topNode.menuSelectedEntry) {
     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 onAppointmentsSelectionChange() {
+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() {
   listOfSelection = this;
   this.removeClassName("_unfocused");
-  $("appointmentsList").addClassName("_unfocused");
+  $("eventsList").addClassName("_unfocused");
 }
 
-function _loadAppointmentHref(href) {
-  if (document.appointmentsListAjaxRequest) {
-    document.appointmentsListAjaxRequest.aborted = true;
-    document.appointmentsListAjaxRequest.abort();
+function _loadEventHref(href) {
+  if (document.eventsListAjaxRequest) {
+    document.eventsListAjaxRequest.aborted = true;
+    document.eventsListAjaxRequest.abort();
   }
-  var url = ApplicationBaseURL + href;
-  document.appointmentsListAjaxRequest
-    = triggerAjaxRequest(url, appointmentsListCallback, href);
+  var url = ApplicationBaseURL + "/" + href;
+  document.eventsListAjaxRequest
+    = triggerAjaxRequest(url, eventsListCallback, href);
+
+  var table = $("eventsList").tBodies[0];
+  while (table.rows.length > 0)
+     table.removeChild(table.rows[0]);
 
   return false;
 }
@@ -615,33 +1004,72 @@ function _loadTasksHref(href) {
   }
   url = ApplicationBaseURL + href;
 
-  var selectedIds = $("tasksList").getSelectedNodesId();
+  var tasksList = $("tasksList");
+  var selectedIds;
+  if (tasksList)
+     selectedIds = tasksList.getSelectedNodesId();
+  else
+     selectedIds = null;
   document.tasksListAjaxRequest
     = triggerAjaxRequest(url, tasksListCallback, selectedIds);
 
-  return false;
+  tasksList.previousScroll = tasksList.scrollTop;
+  while (tasksList.childNodes.length)
+     tasksList.removeChild(tasksList.childNodes[0]);
+
+  return true;
 }
 
 function onHeaderClick(event) {
-//   log("onHeaderClick: " + this.link);
-  _loadAppointmentHref(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 refreshAppointments() {
-  return _loadAppointmentHref("aptlist?desc=" + sortOrder
-                              + "&sort=" + sortKey
-                              + "&day=" + currentDay
-                              + "&filterpopup=" + listFilter);
+function refreshCurrentFolder() {
+  refreshEvents();
+}
+
+function refreshEvents() {
+  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() {
   return _loadTasksHref("taskslist?show-completed=" + showCompletedTasks);
 }
 
-function refreshAppointmentsAndDisplay() {
-  refreshAppointments();
+function refreshEventsAndDisplay() {
+  refreshEvents();
   changeCalendarDisplay();
 }
 
@@ -651,38 +1079,26 @@ function onListFilterChange() {
   listFilter = node.value;
 //   log ("listFilter = " + listFilter);
 
-  return refreshAppointments();
-}
-
-function onAppointmentClick(event) {
-  var target = getTarget(event);
-  var node = target.getParentWithTagName("tr");
-  var day = node.getAttribute("day");
-  var hour = node.getAttribute("hour");
-
-  changeCalendarDisplay( { "day": day, "hour": hour} );
-  changeDateSelectorDisplay(day);
-
-  return onRowClick(event);
+  return refreshEvents();
 }
 
 function selectMonthInMenu(menu, month) {
-  var entries = menu.childNodes[1].childNodesWithTag("LI");
-  for (i = 0; i < entries.length; i++) {
-    var entry = entries[i];
-    var entryMonth = entry.getAttribute("month");
-    if (entryMonth == month)
-      entry.addClassName("currentMonth");
-    else
-      entry.removeClassName("currentMonth");
-  }
+   var entries = menu.childNodes[1].childNodesWithTag("LI");
+   for (i = 0; i < entries.length; i++) {
+      var entry = entries[i];
+      var entryMonth = entry.getAttribute("month");
+      if (entryMonth == month)
+        entry.addClassName("currentMonth");
+      else
+        entry.removeClassName("currentMonth");
+   }
 }
 
 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");
@@ -692,32 +1108,22 @@ function selectYearInMenu(menu, month) {
   }
 }
 
-function popupMonthMenu(event, menuId) {
-  var node = event.target;
-
+function popupMonthMenu(event) {
   if (event.button == 0) {
-    event.cancelBubble = true;
-    event.returnValue = false;
-
-    if (document.currentPopupMenu)
-      hideMenu(event, document.currentPopupMenu);
+    var id = this.getAttribute("id");
+    if (id == "monthLabel")
+       menuId = "monthListMenu";
+    else
+       menuId = "yearListMenu";
 
     var popup = $(menuId);
-    var id = node.getAttribute("id");
     if (id == "monthLabel")
-      selectMonthInMenu(popup, node.getAttribute("month"));
+      selectMonthInMenu(popup, this.getAttribute("month"));
     else
-      selectYearInMenu(popup, node.innerHTML);
+      selectYearInMenu(popup, this.innerHTML);
 
-    var diff = (popup.offsetWidth - node.offsetWidth) /2;
-
-    popup.style.top = (node.offsetTop + 95) + "px";
-    popup.style.left = (node.offsetLeft - diff) + "px";
-    popup.style.visibility = "visible";
-
-    bodyOnClick = "" + document.body.getAttribute("onclick");
-    document.body.setAttribute("onclick", "onBodyClick('" + menuId + "');");
-    document.currentPopupMenu = popup;
+    popupToolbarMenu(this, menuId);
+    Event.stop(event);
   }
 }
 
@@ -726,8 +1132,6 @@ function onMonthMenuItemClick(event) {
   var year = '' + $("yearLabel").innerHTML;
 
   changeDateSelectorDisplay(year + month + "01", true);
-
-//   event.cancelBubble();
 }
 
 function onYearMenuItemClick(event) {
@@ -737,22 +1141,26 @@ 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();
+
+  for (var i = 0; i < div.siblings.length; i++)
+     div.siblings[i].select();
 
-  return false;
+  selectedCalendarCell = div.siblings;
 }
 
-function onCalendarSelectAppointment() {
-  var list = $("appointmentsList");
-  list.deselectAll();
+function onCalendarSelectEvent() {
+  var list = $("eventsList");
 
-  var aptCName = this.getAttribute("aptCName");
-  if (selectedCalendarCell)
-    selectedCalendarCell.deselect();
-  this.select();
-  selectedCalendarCell = this;
-  var row = $(aptCName);
+  selectCalendarEvent(this);
+
+  // Select event in events list
+  $(list.tBodies[0]).deselectAll();
+  var row = $(this.cname);
   if (row) {
     var div = row.parentNode.parentNode.parentNode;
     div.scrollTop = row.offsetTop - (div.offsetHeight / 2);
@@ -763,7 +1171,7 @@ function onCalendarSelectAppointment() {
 function onCalendarSelectDay(event) {
   var day;
   if (currentView == "multicolumndayview")
-     day = this.parentNode.getAttribute("day");
+     day = this.getAttribute("day");
   else
      day = this.getAttribute("day");
   var needRefresh = (listFilter == 'view_selectedday'
@@ -781,17 +1189,26 @@ function onCalendarSelectDay(event) {
   }
 
   if (needRefresh)
-    refreshAppointments();
+    refreshEvents();
 }
 
 function changeWeekCalendarDisplayOfSelectedDay(node) {
   var days = document.getElementsByClassName("day", node.parentNode);
+  var headerDiv = $("calendarHeader").childNodesWithTag("div")[1];
+  var headerDays = document.getElementsByClassName("day", headerDiv);
 
+//   log ("days: " + days.length + "; headerDays: " + headerDays.length);
   for (var i = 0; i < days.length; i++)
-    if (days[i] != node)
-      days[i].removeClassName("selectedDay");
-
-  node.addClassName("selectedDay");
+     if (days[i] != node) {
+//     log("unselect day : " + i);
+       headerDays[i].removeClassName("selectedDay");
+       days[i].removeClassName("selectedDay");
+     }
+     else {
+//     log("selected day : " + i);
+       headerDays[i].addClassName("selectedDay");
+       days[i].addClassName("selectedDay");
+     }
 }
 
 function findMonthCalendarSelectedCell(daysContainer) {
@@ -800,7 +1217,7 @@ function findMonthCalendarSelectedCell(daysContainer) {
 
    while (!found && i < daysContainer.childNodes.length) {
       var currentNode = daysContainer.childNodes[i];
-      if (currentNode instanceof HTMLDivElement
+      if (currentNode.tagName == 'DIV'
           && currentNode.hasClassName("selectedDay")) {
          daysContainer.selectedCell = currentNode;
          found = true;
@@ -821,30 +1238,30 @@ function changeMonthCalendarDisplayOfSelectedDay(node) {
    node.addClassName("selectedDay");
 }
 
-function onShowCompletedTasks(node) {
-  showCompletedTasks = (node.checked ? 1 : 0);
+function onShowCompletedTasks(event) {
+   showCompletedTasks = (this.checked ? 1 : 0);
 
-  return refreshTasks();
+   return refreshTasks();
 }
 
-function updateTaskStatus(node) {
-  var taskId = node.parentNode.getAttribute("id");
-  var taskOwner = node.parentNode.getAttribute("owner");
-  var newStatus = (node.checked ? 1 : 0);
-//   log ("update task status: " + taskId);
-
+function updateTaskStatus(event) {
+  var taskId = this.parentNode.getAttribute("id");
+  var newStatus = (this.checked ? 1 : 0);
   var http = createHTTPClient();
 
-  url = (UserFolderURL + "../" + taskOwner + "/Calendar/"
-         + taskId + "/changeStatus?status=" + newStatus);
+  if (isSafari() && !isSafari3()) {
+    newStatus = (newStatus ? 0 : 1);
+  }
+  
+  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;
-    http.open("GET", url, false /* not async */);
     http.send("");
-    if (http.status == 200)
+    if (isHttpStatus204(http.status))
       refreshTasks();
   } else
     log ("no http client?");
@@ -854,6 +1271,12 @@ function updateTaskStatus(node) {
 
 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++) {
@@ -868,25 +1291,27 @@ function updateCalendarStatus(event) {
     }
   }
 
-  if (!list.length) {
-     list.push(UserLogin);
-     nodes[0].childNodesWithTag("input")[0].checked = true;
-  }
+//   if (!list.length) {
+//      list.push(UserLogin);
+//      nodes[0].childNodesWithTag("input")[0].checked = true;
+//   }
+
 //   ApplicationBaseURL = (UserFolderURL + "Groups/_custom_"
 //                     + list.join(",") + "/Calendar/");
 
   if (event) {
      var folderID = this.parentNode.getAttribute("id");
      var urlstr = URLForFolderID(folderID);
-     if (this.checked)
-       urlstr += "/activateFolder";
+     if (newStatus)
+       urlstr += "/activateFolder";
      else
-       urlstr += "/deactivateFolder";
+       urlstr += "/deactivateFolder";
+     //log("updateCalendarStatus: ajax request = " + urlstr + ", folderID = " + folderID);
      triggerAjaxRequest(urlstr, calendarStatusCallback, folderID);
   }
   else {
      updateCalendarsList();
-     refreshAppointments();
+     refreshEvents();
      refreshTasks();
      changeCalendarDisplay();
   }
@@ -895,23 +1320,25 @@ function updateCalendarStatus(event) {
 }
 
 function calendarStatusCallback(http) {
-   if (http.readyState == 4) {
-      if (http.status == 204) {
-         refreshAppointments();
-         refreshTasks();
-         changeCalendarDisplay();
-      }
-      else {
-         var folder = $(http.callbackData);
-         var input = folder.childNodesWithTag("input")[0];
-         input.checked = (!input.checked);
-      }
-   }
+  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");
 }
 
 function calendarEntryCallback(http) {
-   if (http.readyState == 4) { 
-      var denied = (http.status != 204)
+   if (http.readyState == 4) {
+      var denied = !isHttpStatus204(http.status);
       var entry = $(http.callbackData);
       if (denied)
         entry.addClassName("denied");
@@ -974,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 = {};
 
@@ -987,22 +1429,32 @@ function getMenus() {
       dateMenu.push(onYearMenuItemClick);
    menus["yearListMenu"] = dateMenu;
 
-   menus["appointmentsListMenu"] = new Array(onMenuNewEventClick, "-",
-                                            onMenuNewTaskClick,
-                                            editEvent, deleteEvent, "-",
-                                            onSelectAll, "-",
-                                            null, null);
-   menus["calendarsMenu"] = new Array(null, null, "-", null, null, "-",
+   menus["eventsListMenu"] = new Array(onMenuNewEventClick, "-",
+                                      onMenuNewTaskClick,
+                                      editEvent, deleteEvent, "-",
+                                      onSelectAll, "-",
+                                      null, null);
+   menus["calendarsMenu"] = new Array(onMenuModify,
+                                     "-",
+                                     onCalendarNew, onCalendarRemove,
+                                     "-", null, null, "-",
                                      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");
@@ -1023,7 +1475,7 @@ function configureDragHandles() {
   handle = $("rightDragHandle");
   if (handle) {
     handle.addInterface(SOGoDragHandlesInterface);
-    handle.upperBlock=$("appointmentsListView");
+    handle.upperBlock=$("eventsListView");
     handle.lowerBlock=$("calendarView");
   }
 }
@@ -1033,107 +1485,289 @@ 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, "change", updateCalendarStatus.bindAsEventListener(input));
-    Event.observe(list[i], "mousedown", listRowMouseDownHandler);
-    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");
-  Event.observe(links[0], "click",  onCalendarAdd);
-  Event.observe(links[1], "click",  onCalendarRemove);
+  Event.observe(links[0], "click",  onCalendarNew);
+  Event.observe(links[1], "click",  onCalendarAdd);
+  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[selected.childNodes.length - 1];
+    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);
+  preventDefault(event);
 }
 
 function onCalendarAdd(event) {
-   openUserFolderSelector(onFolderSubscribeCB, "calendar");
-
-   preventDefault(event);
-}
-
-function appendCalendar(folderName, folder) {
-   var calendarList = $("calendarList");
-   var lis = calendarList.childNodesWithTag("li");
-   var color = indexColor(lis.length);
-   log ("color: " + color);
-   var li = document.createElement("li");
-   li.setAttribute("id", folder);
-   Event.observe(li, "mousedown",  listRowMouseDownHandler);
-   Event.observe(li, "click",  onRowClick);
-   var checkBox = document.createElement("input");
-   checkBox.addClassName("checkBox");
-   checkBox.type = "checkbox";
-   Event.observe(checkBox, "change",  updateCalendarStatus);
-   li.appendChild(checkBox);
-   li.appendChild(document.createTextNode(" "));
-   var colorBox = document.createElement("div");
-   colorBox.appendChild(document.createTextNode("OO"));
-   colorBox.addClassName("colorBox");
-   if (color) {
-     colorBox.setStyle({ color: color,
-                        backgroundColor: color });
-   }
-   li.appendChild(colorBox);
-   li.appendChild(document.createTextNode(" " + folderName));
+  openUserFolderSelector(onFolderSubscribeCB, "calendar");
+  preventDefault(event);
+}
 
-   calendarList.appendChild(li);
+function appendCalendar(folderName, folderPath) {
+  var owner;
 
-   var contactId = folder.split(":")[0];
-   var styles = document.getElementsByTagName("style");
+  if (folderPath) {
+    owner = getSubscribedFolderOwner(folderPath);
+    folderPath = accessToSubscribedFolder(folderPath);
+  }
+  else
+    folderPath = "/" + folderName;
 
-   var url = URLForFolderID(folder) + "/canAccessContent";
-   triggerAjaxRequest(url, calendarEntryCallback, folder);
+  if (!owner)
+    owner = UserLogin;
 
-   styles[0].innerHTML += ('.ownerIs' + contactId + ' {'
-                          + ' background-color: '
-                          + color
-                          + ' !important; }');
+  //log ("append name: " + folderName + "; path: " + folderPath + "; owner: " + owner);
+
+  if ($(folderPath))
+    window.alert(clabels["You have already subscribed to that folder!"]);
+  else {
+    var calendarList = $("calendarList");
+    var items = calendarList.childNodesWithTag("li");
+    var li = document.createElement("li");
+    
+    // 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));
+    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"]);
+     appendCalendar(folderData["folderName"], folderData["folder"]);
 }
 
 function onFolderUnsubscribeCB(folderId) {
-   var node = $(folderId);
-   node.parentNode.removeChild(node);
+  var node = $(folderId);
+  node.parentNode.removeChild(node);
+  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) {
+  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 configureSearchField() {
-   var searchValue = $("searchValue");
+function deletePersonalCalendar(folderElement) {
+  var folderId = folderElement.substr(1);
+  var label
+    = labels["Are you sure you want to delete the calendar \"%{0}\"?"].formatted($(folderElement).lastChild.nodeValue.strip());
+  if (window.confirm(label)) {
+    removeFolderRequestCount++;
+    var url = ApplicationBaseURL + "/" + folderId + "/deleteFolder";
+    triggerAjaxRequest(url, deletePersonalCalendarCallback, folderId);
+  }
+}
 
-   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 deletePersonalCalendarCallback(http) {
+  if (http.readyState == 4) {
+    if (isHttpStatus204(http.status)) {
+      var ul = $("calendarList");
+      var children = ul.childNodesWithTag("li");
+      var i = 0;
+      var done = false;
+      while (!done && i < children.length) {
+       var currentFolderId = children[i].getAttribute("id").substr(1);
+       if (currentFolderId == http.callbackData) {
+         ul.removeChild(children[i]);
+         done = true;
+       }
+       else
+         i++;
+      }
+      removeFolderRequestCount--;
+      if (removeFolderRequestCount == 0) {
+       refreshEvents();
+       refreshTasks();
+       changeCalendarDisplay();
+      }
+    }
+  }
+  else
+    log ("ajax problem 5: " + http.status);
+}
+
+function configureLists() {
+   var list = $("tasksList");
+   list.multiselect = true;
+   Event.observe(list, "mousedown",
+                onTasksSelectionChange.bindAsEventListener(list));
+
+   var input = $("showHideCompletedTasks");
+   Event.observe(input, "click",
+                onShowCompletedTasks.bindAsEventListener(input));
+
+   list = $("eventsList");
+   list.multiselect = true;
+   configureSortableTableHeaders(list);
+   TableKit.Resizable.init(list, {'trueResize' : true, 'keepWidth' : true});
+   Event.observe(list, "mousedown",
+                onEventsSelectionChange.bindAsEventListener(list));
+}
+
+function initDateSelectorEvents() {
+   var arrow = $("rightArrow");
+   Event.observe(arrow, "click",
+                onDateSelectorGotoMonth.bindAsEventListener(arrow));
+   arrow = $("leftArrow");
+   Event.observe(arrow, "click",
+                onDateSelectorGotoMonth.bindAsEventListener(arrow));
+   
+   var menuButton = $("monthLabel");
+   Event.observe(menuButton, "click",
+                popupMonthMenu.bindAsEventListener(menuButton));
+   menuButton = $("yearLabel");
+   Event.observe(menuButton, "click",
+                popupMonthMenu.bindAsEventListener(menuButton));
 }
 
 function initCalendars() {
+   sorting["attribute"] = "start";
+   sorting["ascending"] = true;
+  
    if (!document.body.hasClassName("popup")) {
+      initDateSelectorEvents();
       initCalendarSelector();
       configureSearchField();
+      configureLists();
       var selector = $("calendarSelector");
       if (selector)
         selector.attachMenu("calendarsMenu");
    }
 }
 
-addEvent(window, 'load', initCalendars);
+FastInit.addOnLoad(initCalendars);