]> err.no Git - scalable-opengroupware.org/blob - UI/WebServerResources/SchedulerUI.js
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1279 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / WebServerResources / SchedulerUI.js
1 /* JavaScript for SOGoCalendar */
2
3 var sortOrder = '';
4 var sortKey = '';
5 var listFilter = 'view_today';
6
7 var listOfSelection = null;
8 var selectedCalendarCell;
9 var calendarColorIndex = null;
10
11 var showCompletedTasks = 0;
12
13 var currentDay = '';
14 var currentView = "weekview";
15
16 var cachedDateSelectors = new Array();
17
18 var contactSelectorAction = 'calendars-contacts';
19
20 var eventsToDelete = new Array();
21 var calendarsOfEventsToDelete = new Array();
22
23 var usersRightsWindowHeight = 250;
24 var usersRightsWindowWidth = 502;
25
26 function newEvent(sender, type) {
27    var day = sender.readAttribute("day");
28    if (!day)
29       day = currentDay;
30    var hour = sender.readAttribute("hour");
31    var folder = getSelectedFolder();
32    var folderID = folder.readAttribute("id");
33    var roles = folder.readAttribute("roles");
34    if (roles) {
35      roles = roles.split(",")
36        if ($(roles).indexOf("PublicModifier") < 0)
37          folderID = "/personal";
38    }
39    var urlstr = ApplicationBaseURL + folderID + "/new" + type;
40    var params = new Array();
41    if (day)
42       params.push("day=" + day);
43    if (hour)
44       params.push("hm=" + hour);
45    if (params.length > 0)
46       urlstr += "?" + params.join("&");
47    window.open(urlstr, "", "width=490,height=470,resizable=0");
48    
49    return false; /* stop following the link */
50 }
51
52 function getSelectedFolder() {
53   var folder;
54   var list = $("calendarList");
55   var nodes = list.getSelectedRows();
56   if (nodes.length > 0)
57     folder = nodes[0];
58   else
59     folder = list.down("li");
60
61   return folder;
62 }
63
64 function onMenuNewEventClick(event) {
65    newEvent(this, "event");
66 }
67
68 function onMenuNewTaskClick(event) {
69    newEvent(this, "task");
70 }
71
72 function _editEventId(id, calendar) {
73   var urlstr = ApplicationBaseURL + "/" + calendar + "/" + id + "/edit";
74   var targetname = "SOGo_edit_" + id;
75   var win = window.open(urlstr, "_blank",
76                         "width=490,height=470,resizable=0");
77   win.focus();
78 }
79
80 function editEvent() {
81   if (listOfSelection) {
82     var nodes = listOfSelection.getSelectedRows();
83
84     for (var i = 0; i < nodes.length; i++)
85       _editEventId(nodes[i].getAttribute("id"),
86                    nodes[i].calendar);
87   } else if (selectedCalendarCell) {
88       _editEventId(selectedCalendarCell[0].cname,
89                    selectedCalendarCell[0].calendar);
90   }
91
92   return false; /* stop following the link */
93 }
94
95 function _batchDeleteEvents() {
96   var events = eventsToDelete.shift();
97   var calendar = calendarsOfEventsToDelete.shift();
98   var urlstr = (ApplicationBaseURL + "/" + calendar
99                 + "/batchDelete?ids=" + events.join('/'));
100   document.deleteEventAjaxRequest = triggerAjaxRequest(urlstr,
101                                                        deleteEventCallback,
102                                                        events);
103 }
104
105 function deleteEvent() {
106   if (listOfSelection) {
107     var nodes = listOfSelection.getSelectedRows();
108
109     if (nodes.length > 0) {
110       var label = "";
111       if (listOfSelection == $("tasksList"))
112         label = labels["taskDeleteConfirmation"];
113       else
114         label = labels["eventDeleteConfirmation"];
115       
116       if (confirm(label)) {
117         if (document.deleteEventAjaxRequest) {
118           document.deleteEventAjaxRequest.aborted = true;
119           document.deleteEventAjaxRequest.abort();
120         }
121         var sortedNodes = new Array();
122         var calendars = new Array();
123
124         for (var i = 0; i < nodes.length; i++) {
125           var calendar = nodes[i].calendar;
126           if (!sortedNodes[calendar]) {
127             sortedNodes[calendar] = new Array();
128             calendars.push(calendar);
129           }
130           sortedNodes[calendar].push(nodes[i].cname);
131         }
132         for (var i = 0; i < calendars.length; i++) {
133           calendarsOfEventsToDelete.push(calendars[i]);
134           eventsToDelete.push(sortedNodes[calendars[i]]);
135         }
136         _batchDeleteEvents();
137       }
138     }
139   }
140   else if (selectedCalendarCell) {
141      var label = labels["eventDeleteConfirmation"];
142      if (confirm(label)) {
143         if (document.deleteEventAjaxRequest) {
144            document.deleteEventAjaxRequest.aborted = true;
145            document.deleteEventAjaxRequest.abort();
146         }
147         eventsToDelete.push([selectedCalendarCell[0].cname]);
148         calendarsOfEventsToDelete.push(selectedCalendarCell[0].calendar);
149         _batchDeleteEvents();
150      }
151   }
152   else
153     window.alert("no selection");
154
155   return false;
156 }
157
158 function modifyEvent(sender, modification) {
159   var currentLocation = '' + window.location;
160   var arr = currentLocation.split("/");
161   arr[arr.length-1] = modification;
162
163   document.modifyEventAjaxRequest = triggerAjaxRequest(arr.join("/"),
164                                                        modifyEventCallback,
165                                                        modification);
166
167   return false;
168 }
169
170 function closeInvitationWindow() {
171   var closeDiv = document.createElement("div");
172   document.body.appendChild(closeDiv);
173   closeDiv.addClassName("javascriptPopupBackground");
174
175   var closePseudoWin = document.createElement("div");
176   document.body.appendChild(closePseudoWin);
177   closePseudoWin.addClassName("javascriptMessagePseudoTopWindow");
178   closePseudoWin.style.top = "0px;";
179   closePseudoWin.style.left = "0px;";
180   closePseudoWin.style.right = "0px;";
181   closePseudoWin.appendChild(document.createTextNode(labels["closeThisWindowMessage"]));
182
183   var calLink = document.createElement("a");
184   closePseudoWin.appendChild(calLink);
185   calLink.href = ApplicationBaseURL;
186   calLink.appendChild(document.createTextNode(labels["Calendar"].toLowerCase()));
187 }
188
189 function modifyEventCallback(http) {
190   if (http.readyState == 4) {
191     if (http.status == 200) {
192       var mailInvitation = queryParameters["mail-invitation"];
193       if (mailInvitation && mailInvitation.toLowerCase() == "yes")
194         closeInvitationWindow();
195       else {
196         window.opener.setTimeout("refreshEventsAndDisplay();", 100);
197         window.setTimeout("window.close();", 100);
198       }
199     }
200     else {
201 //       log("showing alert...");
202       window.alert(labels["eventPartStatModificationError"]);
203     }
204     document.modifyEventAjaxRequest = null;
205   }
206 }
207
208 function deleteEventCallback(http) {
209   if (http.readyState == 4) {
210     if (isHttpStatus204(http.status)) {
211       var nodes = http.callbackData;
212       for (var i = 0; i < nodes.length; i++) {
213         var node = $(nodes[i]);
214         if (node)
215           node.parentNode.removeChild(node);
216       }
217       if (eventsToDelete.length)
218         _batchDeleteEvents();
219       else {
220         document.deleteEventAjaxRequest = null;
221         refreshEvents();
222         refreshTasks();
223         changeCalendarDisplay();
224       }
225     }
226     else
227       log ("deleteEventCallback Ajax error");
228   }
229 }
230
231 function editDoubleClickedEvent(event) {
232   _editEventId(this.cname, this.calendar);
233
234   preventDefault(event);
235   event.cancelBubble = true;
236 }
237
238 function onSelectAll() {
239   var list = $("eventsList");
240   list.selectRowsMatchingClass("eventRow");
241
242   return false;
243 }
244
245 function onDaySelect(node) {
246   var day = node.getAttribute('day');
247   var needRefresh = (listFilter == 'view_selectedday'
248                      && day != currentDay);
249
250   var td = $(node).getParentWithTagName("td");
251   var table = $(td).getParentWithTagName("table");
252
253 //   log ("table.selected: " + table.selected);
254
255   if (document.selectedDate)
256     document.selectedDate.deselect();
257
258   td.select();
259   document.selectedDate = td;
260
261   changeCalendarDisplay( { "day": day } );
262   if (needRefresh)
263     refreshEvents();
264
265   return false;
266 }
267
268 function onDateSelectorGotoMonth(event) {
269    var day = this.getAttribute("date");
270
271    changeDateSelectorDisplay(day, true);
272
273    Event.stop(event);
274 }
275
276 function onCalendarGotoDay(node) {
277    var day = node.getAttribute("date");
278    
279    changeDateSelectorDisplay(day);
280    changeCalendarDisplay( { "day": day } );
281   
282    return false;
283 }
284
285 function gotoToday() {
286   changeDateSelectorDisplay('');
287   changeCalendarDisplay();
288
289   return false;
290 }
291
292 function setDateSelectorContent(content) {
293   var div = $("dateSelectorView");
294
295   div.innerHTML = content;
296   if (currentDay.length > 0)
297     restoreCurrentDaySelection(div);
298
299   initDateSelectorEvents();
300 }
301
302 function dateSelectorCallback(http) {
303   if (http.readyState == 4
304       && http.status == 200) {
305      document.dateSelectorAjaxRequest = null;
306      var content = http.responseText;
307      setDateSelectorContent(content);
308      cachedDateSelectors[http.callbackData] = content;
309   }
310   else
311     log ("dateSelectorCallback Ajax error");
312 }
313
314 function eventsListCallback(http) {
315   if (http.readyState == 4
316       && http.status == 200) {
317      var div = $("eventsListView");
318
319     document.eventsListAjaxRequest = null;
320     var table = $("eventsList");
321     var params = parseQueryParameters(http.callbackData);
322     sortKey = params["sort"];
323     sortOrder = params["desc"];
324     lastClickedRow = -1; // from generic.js
325
326     if (http.responseText.length > 0) {
327       var data = http.responseText.evalJSON(true);
328       for (var i = 0; i < data.length; i++) {
329         var row = document.createElement("tr");
330         table.tBodies[0].appendChild(row);
331         $(row).addClassName("eventRow");
332         row.setAttribute("id", escape(data[i][0]));
333         row.cname = escape(data[i][0]);
334         row.calendar = data[i][1];
335
336         var startDate = new Date();
337         startDate.setTime(data[i][4] * 1000);
338         row.day = startDate.getDayString();
339         row.hour = startDate.getHourString();
340         Event.observe(row, "click",
341                       onEventClick.bindAsEventListener(row));
342         Event.observe(row, "dblclick",
343                       editDoubleClickedEvent.bindAsEventListener(row));
344         Event.observe(row, "contextmenu",
345                       onEventContextMenu.bindAsEventListener(row));
346       
347         var td = document.createElement("td");
348         row.appendChild(td);
349         Event.observe(td, "mousedown", listRowMouseDownHandler, true);
350         td.appendChild(document.createTextNode(data[i][3]));
351
352         td = document.createElement("td");
353         row.appendChild(td);
354         Event.observe(td, "mousedown", listRowMouseDownHandler, true);
355         td.appendChild(document.createTextNode(data[i][9]));
356
357         td = document.createElement("td");
358         row.appendChild(td);
359         Event.observe(td, "mousedown", listRowMouseDownHandler, true);
360         td.appendChild(document.createTextNode(data[i][10]));
361       
362         td = document.createElement("td");
363         row.appendChild(td);
364         Event.observe(td, "mousedown", listRowMouseDownHandler, true);
365         td.appendChild(document.createTextNode(data[i][6]));
366       }
367     }
368   }
369   else
370     log ("eventsListCallback Ajax error");
371 }
372
373 function tasksListCallback(http) {
374   var div = $("tasksListView");
375
376   if (http.readyState == 4
377       && http.status == 200) {
378     document.tasksListAjaxRequest = null;
379     var list = $("tasksList");
380  
381     if (http.responseText.length > 0) {
382       var data = http.responseText.evalJSON(true);
383
384       for (var i = 0; i < data.length; i++) {
385         //log(i + " = " + data[i][3]);
386         var listItem = document.createElement("li");
387         list.appendChild(listItem);
388         Event.observe(listItem, "mousedown", listRowMouseDownHandler);
389         Event.observe(listItem, "click", onRowClick);
390         Event.observe(listItem, "dblclick",
391                       editDoubleClickedEvent.bindAsEventListener(listItem));
392         listItem.setAttribute("id", data[i][0]);
393         $(listItem).addClassName(data[i][5]);
394         listItem.calendar = data[i][1];
395         $(listItem).addClassName("calendarFolder" + data[i][1]);
396         listItem.cname = escape(data[i][0]);
397         var input = document.createElement("input");
398         input.setAttribute("type", "checkbox");
399         listItem.appendChild(input);
400         Event.observe(input, "click", updateTaskStatus.bindAsEventListener(input), true);
401         input.setAttribute("value", "1");
402         if (data[i][2] == 1)
403           input.setAttribute("checked", "checked");
404         $(input).addClassName("checkBox");
405         listItem.appendChild(document.createTextNode(data[i][3]));
406       }
407
408       list.scrollTop = list.previousScroll;
409
410       if (http.callbackData) {
411         var selectedNodesId = http.callbackData;
412         for (var i = 0; i < selectedNodesId.length; i++) {
413           //    log(selectedNodesId[i] + " (" + i + ") is selected");
414           $(selectedNodesId[i]).select();
415         }
416       }
417       else
418         log ("tasksListCallback: no data");
419     }
420   }
421   else
422     log ("tasksListCallback Ajax error");
423 }
424
425 function restoreCurrentDaySelection(div) {
426   var elements = $(div).getElementsByTagName("a");
427   var day = null;
428   var i = 9;
429   while (!day && i < elements.length)
430     {
431       day = elements[i].day;
432       i++;
433     }
434
435   if (day
436       && day.substr(0, 6) == currentDay.substr(0, 6)) {
437       for (i = 0; i < elements.length; i++) {
438         day = elements[i].day;
439         if (day && day == currentDay) {
440           var td = $(elements[i]).getParentWithTagName("td");
441           if (document.selectedDate)
442             document.selectedDate.deselect();
443           $(td).select();
444           document.selectedDate = td;
445         }
446       }
447     }
448 }
449
450 function changeDateSelectorDisplay(day, keepCurrentDay) {
451   var url = ApplicationBaseURL + "dateselector";
452   if (day)
453     url += "?day=" + day;
454
455   if (day != currentDay) {
456     if (!keepCurrentDay)
457       currentDay = day;
458
459     var month = day.substr(0, 6);
460     if (cachedDateSelectors[month]) {
461 //       log ("restoring cached selector for month: " + month);
462       setDateSelectorContent(cachedDateSelectors[month]);
463     }
464     else {
465 //       log ("loading selector for month: " + month);
466       if (document.dateSelectorAjaxRequest) {
467         document.dateSelectorAjaxRequest.aborted = true;
468         document.dateSelectorAjaxRequest.abort();
469       }
470       document.dateSelectorAjaxRequest
471         = triggerAjaxRequest(url,
472                              dateSelectorCallback,
473                              month);
474     }
475   }
476 }
477
478 function changeCalendarDisplay(data, newView) {
479   var url = ApplicationBaseURL + ((newView) ? newView : currentView);
480   var day = null;
481   var scrollEvent = null;
482
483   if (data) {
484     day = data['day'];
485     scrollEvent = data['scrollEvent'];
486   }
487
488   if (!day)
489     day = currentDay;
490
491   if (day) {
492     if (data) {
493       var divs = $$('div.day[day='+day+']');
494       if (divs.length) {
495         // Don't reload the view if the event is present in current view
496         
497         // Deselect previous day
498         var selectedDivs = $$('div.day.selectedDay');
499         selectedDivs.each(function(div) {
500             div.removeClassName('selectedDay');
501           });
502         
503         // Select new day
504         divs.each(function(div) {
505             div.addClassName('selectedDay');
506           });
507         
508         // Deselect day in date selector
509         if (document.selectedDate)
510           document.selectedDate.deselect();
511         
512         // Select day in date selector
513         var selectedLink = $$('table#dateSelectorTable a[day='+day+']');
514         if (selectedLink.length > 0) {
515           selectedCell = selectedLink[0].up(1);
516           selectedCell.select();
517           document.selectedDate = selectedCell;
518         }
519         
520         // Scroll to event
521         scrollDayView(scrollEvent);     
522
523         return false;
524       }
525     }
526     url += "?day=" + day;
527   }
528 //   if (newView)
529 //     log ("switching to view: " + newView);
530 //   log ("changeCalendarDisplay: " + url);
531
532   selectedCalendarCell = null;
533
534   if (document.dayDisplayAjaxRequest) {
535 //     log ("aborting day ajaxrq");
536     document.dayDisplayAjaxRequest.aborted = true;
537     document.dayDisplayAjaxRequest.abort();
538   }
539   document.dayDisplayAjaxRequest
540      = triggerAjaxRequest(url, calendarDisplayCallback,
541                           { "view": newView,
542                             "day": day,
543                             "scrollEvent": scrollEvent });
544
545   return false;
546 }
547
548 function _ensureView(view) {
549   if (currentView != view)
550     changeCalendarDisplay(null, view);
551
552   return false;
553 }
554
555 function onDayOverview() {
556   return _ensureView("dayview");
557 }
558
559 function onMulticolumnDayOverview() {
560   return _ensureView("multicolumndayview");
561 }
562
563 function onWeekOverview() {
564   return _ensureView("weekview");
565 }
566
567 function onMonthOverview() {
568   return _ensureView("monthview");
569 }
570
571 function scrollDayView(scrollEvent) {
572   var divs;
573
574   // Select event in calendar view
575   if (scrollEvent) {
576     divs = $$("div#calendarContent div." + eventClass(scrollEvent));
577     selectCalendarEvent(divs[0]);
578   }
579   
580   // Don't scroll if in month view
581   if (currentView == "monthview")
582     return;
583
584   var offset = 0;
585   var daysView = $("daysView");
586   var hours =
587     $(daysView.childNodesWithTag("div")[0]).childNodesWithTag("div");
588
589   if (scrollEvent) {
590     divs = $$("div#calendarContent div." + eventClass(scrollEvent));
591     var classes = $w(divs[0].className);
592     for (var i = 0; i < classes.length; i++) {
593       if (classes[i].startsWith("starts")) {
594         var starts = Math.floor(parseInt(classes[i].substr(6)) / 4);
595         offset = hours[starts].offsetTop;
596       }
597     }
598   }
599   else
600     // Scroll to 8 AM
601     offset = hours[8].offsetTop;
602
603   daysView.scrollTop = offset - 5;
604 }
605
606 function onClickableCellsDblClick(event) {
607   newEvent(this, 'event');
608
609   event.cancelBubble = true;
610   event.returnValue = false;
611 }
612
613 function refreshCalendarEvents(scrollEvent) {
614    var todayDate = new Date();
615    var sd;
616    var ed;
617    if (currentView == "dayview") {
618       if (currentDay)
619          sd = currentDay;
620       else
621          sd = todayDate.getDayString();
622       ed = sd;
623    }
624    else if (currentView == "weekview") {
625       var startDate;
626       if (currentDay)
627          startDate = currentDay.asDate();
628       else
629          startDate = todayDate;
630       startDate = startDate.beginOfWeek();
631       sd = startDate.getDayString();
632       var endDate = new Date();
633       endDate.setTime(startDate.getTime());
634       endDate.addDays(6);
635       ed = endDate.getDayString();
636    }
637    else {
638       var monthDate;
639       if (currentDay)
640          monthDate = currentDay.asDate();
641       else
642          monthDate = todayDate;
643       monthDate.setDate(1);
644       sd = monthDate.beginOfWeek().getDayString();
645
646       var lastMonthDate = new Date();
647       lastMonthDate.setTime(monthDate.getTime());
648       lastMonthDate.setMonth(monthDate.getMonth() + 1);
649       lastMonthDate.addDays(-1);
650       ed = lastMonthDate.endOfWeek().getDayString();
651    }
652    if (document.refreshCalendarEventsAjaxRequest) {
653       document.refreshCalendarEventsAjaxRequest.aborted = true;
654       document.refreshCalendarEventsAjaxRequest.abort();
655    }
656    var url = ApplicationBaseURL + "eventslist?sd=" + sd + "&ed=" + ed;
657    document.refreshCalendarEventsAjaxRequest
658       = triggerAjaxRequest(url, refreshCalendarEventsCallback,
659                            {"startDate": sd, "endDate": ed,
660                             "scrollEvent": scrollEvent});
661 }
662
663 function refreshCalendarEventsCallback(http) {
664   if (http.readyState == 4
665       && http.status == 200) {
666
667     if (http.responseText.length > 0) {
668       var data = http.responseText.evalJSON(true);
669 //      log("refresh calendar events: " + data.length);
670       for (var i = 0; i < data.length; i++)
671         drawCalendarEvent(data[i],
672                           http.callbackData["startDate"],
673                           http.callbackData["endDate"]);
674     }
675     scrollDayView(http.callbackData["scrollEvent"]);
676   }
677   else
678      log("AJAX error when refreshing calendar events");
679 }
680
681 function drawCalendarEvent(eventData, sd, ed) {
682    var viewStartDate = sd.asDate();
683    var viewEndDate = ed.asDate();
684
685    var startDate = new Date();
686    startDate.setTime(eventData[4] * 1000);
687    var endDate = new Date();
688    endDate.setTime(eventData[5] * 1000);
689
690 //    log ("s: " + startDate + "; e: " + endDate);
691
692    var days = startDate.daysUpTo(endDate);
693
694    var title;
695    if (currentView == "monthview"
696        && (eventData[7] == 0))
697       title = startDate.getDisplayHoursString() + " " + eventData[3];
698    else
699       title = eventData[3];
700
701 //    log("title: " + title); 
702 //    log("viewS: " + viewStartDate);
703    var startHour = null;
704    var endHour = null;
705    
706    var siblings = new Array();
707    for (var i = 0; i < days.length; i++)
708       if (days[i].earlierDate(viewStartDate) == viewStartDate
709           && days[i].laterDate(viewEndDate) == viewEndDate) {
710          var starts;
711
712 //       log("day: " + days[i]);
713          if (i == 0) {
714             var quarters = (startDate.getUTCHours() * 4
715                             + Math.floor(startDate.getUTCMinutes() / 15));
716             starts = quarters;
717             startHour = startDate.getDisplayHoursString();
718             endHour = endDate.getDisplayHoursString();
719          }
720          else
721             starts = 0;
722          
723          var ends;
724          var lasts;
725          if (i == days.length - 1) {
726             var quarters = (endDate.getUTCHours() * 4
727                             + Math.ceil(endDate.getUTCMinutes() / 15));
728             ends = quarters;
729          }
730          else
731             ends = 96;
732          lasts = ends - starts;
733          if (!lasts)
734             lasts = 1;
735
736          var eventDiv = newEventDIV(eventData[0], eventData[1], starts, lasts,
737                                     null, null, title);
738          siblings.push(eventDiv);
739          eventDiv.siblings = siblings;
740          var dayString = days[i].getDayString();
741 //       log("day: " + dayString);
742          var parentDiv = null;
743          if (currentView == "monthview") {
744             var dayDivs = $("monthDaysView").childNodesWithTag("div");
745             var j = 0; 
746             while (!parentDiv && j < dayDivs.length) {
747                if (dayDivs[j].getAttribute("day") == dayString)
748                   parentDiv = dayDivs[j];
749                else
750                   j++;
751             }
752          }
753          else {
754             if (eventData[7] == 0) {
755                var daysView = $("daysView");
756                var eventsDiv = $(daysView).childNodesWithTag("div")[1];
757                var dayDivs = $(eventsDiv).childNodesWithTag("div");
758                var j = 0; 
759                while (!parentDiv && j < dayDivs.length) {
760                   if (dayDivs[j].getAttribute("day") == dayString)
761                      parentDiv = dayDivs[j].childNodesWithTag("div")[0];
762                   else
763                      j++;
764                }
765             }
766             else {
767                var header = $("calendarHeader");
768                var daysDiv = $(header).childNodesWithTag("div")[1];
769                var dayDivs = $(daysDiv).childNodesWithTag("div");
770                var j = 0; 
771                while (!parentDiv && j < dayDivs.length) {
772                   if (dayDivs[j].getAttribute("day") == dayString)
773                      parentDiv = dayDivs[j];
774                   else
775                      j++;
776                }
777             }
778          }
779          if (parentDiv)
780            parentDiv.appendChild(eventDiv);
781       }
782 }
783
784 function eventClass(cname) {
785   return  escape(cname.replace(".", "-"));
786 }
787
788
789 function newEventDIV(cname, calendar, starts, lasts,
790                      startHour, endHour, title) {
791    var eventDiv = document.createElement("div");
792    eventDiv.cname = escape(cname);
793    eventDiv.calendar = calendar;
794    $(eventDiv).addClassName("event");
795    $(eventDiv).addClassName(eventClass(cname));
796    $(eventDiv).addClassName("starts" + starts);
797    $(eventDiv).addClassName("lasts" + lasts);
798    for (var i = 1; i < 5; i++) {
799       var shadowDiv = document.createElement("div");
800       eventDiv.appendChild(shadowDiv);
801       $(shadowDiv).addClassName("shadow");
802       $(shadowDiv).addClassName("shadow" + i);
803    }
804    var innerDiv = document.createElement("div");
805    eventDiv.appendChild(innerDiv);
806    $(innerDiv).addClassName("eventInside");
807    $(innerDiv).addClassName("calendarFolder" + calendar);
808
809    var gradientDiv = document.createElement("div");
810    innerDiv.appendChild(gradientDiv);
811    $(gradientDiv).addClassName("gradient");
812    var gradientImg = document.createElement("img");
813    gradientDiv.appendChild(gradientImg);
814    gradientImg.src = ResourcesURL + "/event-gradient.png";
815
816    var textDiv = document.createElement("div");
817    innerDiv.appendChild(textDiv);
818    $(textDiv).addClassName("text");
819    if (startHour) {
820       var headerSpan = document.createElement("span");
821       textDiv.appendChild(headerSpan);
822       $(headerSpan).addClassName("eventHeader");
823       headerSpan.appendChild(document.createTextNode(startHour + " - "
824                                                      + endHour));
825       textDiv.appendChild(document.createElement("br"));
826    }
827    textDiv.appendChild(document.createTextNode(title));
828
829    Event.observe(eventDiv, "mousedown", listRowMouseDownHandler);
830    Event.observe(eventDiv, "click",
831                  onCalendarSelectEvent.bindAsEventListener(eventDiv));
832    Event.observe(eventDiv, "dblclick",
833                  editDoubleClickedEvent.bindAsEventListener(eventDiv));
834
835    return eventDiv;
836 }
837
838 function calendarDisplayCallback(http) {
839   var div = $("calendarView");
840
841   if (http.readyState == 4
842       && http.status == 200) {
843     document.dayDisplayAjaxRequest = null;
844     div.update(http.responseText);
845     if (http.callbackData["view"])
846       currentView = http.callbackData["view"];
847     if (http.callbackData["day"])
848       currentDay = http.callbackData["day"];
849
850     var contentView;
851     if (currentView == "monthview")
852       contentView = $("calendarContent");
853     else
854       contentView = $("daysView");
855     
856     refreshCalendarEvents(http.callbackData.scrollEvent);
857     
858     var days = document.getElementsByClassName("day", contentView);
859     if (currentView == "monthview")
860       for (var i = 0; i < days.length; i++) {
861         Event.observe(days[i], "click",
862                       onCalendarSelectDay.bindAsEventListener(days[i]));
863         Event.observe(days[i], "dblclick",
864                       onClickableCellsDblClick.bindAsEventListener(days[i]));
865       }
866     else {
867        var headerDivs = $("calendarHeader").childNodesWithTag("div"); 
868        var headerDaysLabels = document.getElementsByClassName("day", headerDivs[0]);
869        var headerDays = document.getElementsByClassName("day", headerDivs[1]);
870        for (var i = 0; i < days.length; i++) {
871           headerDays[i].hour = "allday";
872           Event.observe(headerDaysLabels[i], "mousedown", listRowMouseDownHandler);
873           Event.observe(headerDays[i], "click",
874                         onCalendarSelectDay.bindAsEventListener(days[i]));
875           Event.observe(headerDays[i], "dblclick",
876                         onClickableCellsDblClick.bindAsEventListener(headerDays[i]));
877           Event.observe(days[i], "click",
878                         onCalendarSelectDay.bindAsEventListener(days[i]));
879           var clickableCells = document.getElementsByClassName("clickableHourCell",
880                                                                days[i]);
881           for (var j = 0; j < clickableCells.length; j++)
882              Event.observe(clickableCells[j], "dblclick",
883                            onClickableCellsDblClick.bindAsEventListener(clickableCells[j]));
884        }
885     }
886   }
887   else
888     log ("calendarDisplayCallback Ajax error (" + http.readyState + "/" + http.status + ")");
889 }
890
891 function assignCalendar(name) {
892   if (typeof(skycalendar) != "undefined") {
893     var node = $(name);
894       
895     node.calendar = new skycalendar(node);
896     node.calendar.setCalendarPage(ResourcesURL + "/skycalendar.html");
897     var dateFormat = node.getAttribute("dateFormat");
898     if (dateFormat)
899       node.calendar.setDateFormat(dateFormat);
900   }
901 }
902
903 function popupCalendar(node) {
904    var nodeId = $(node).readAttribute("inputId");
905    var input = $(nodeId);
906    input.calendar.popup();
907
908    return false;
909 }
910
911 function onEventContextMenu(event) {
912   var topNode = $("eventsList");
913 //   log(topNode);
914
915   var menu = $("eventsListMenu");
916
917   Event.observe(menu, "hideMenu",  onEventContextMenuHide);
918   popupMenu(event, "eventsListMenu", this);
919
920   var topNode = $("eventsList");
921   var selectedNodes = topNode.getSelectedRows();
922   topNode.menuSelectedRows = selectedNodes;
923   for (var i = 0; i < selectedNodes.length; i++)
924     selectedNodes[i].deselect();
925
926   topNode.menuSelectedEntry = this;
927   this.select();
928 }
929
930 function onEventContextMenuHide(event) {
931   var topNode = $("eventsList");
932
933   if (topNode.menuSelectedEntry) {
934     topNode.menuSelectedEntry.deselect();
935     topNode.menuSelectedEntry = null;
936   }
937   if (topNode.menuSelectedRows) {
938     var nodeIds = topNode.menuSelectedRows;
939     for (var i = 0; i < nodeIds.length; i++) {
940       var node = $(nodeIds[i]);
941       node.select();
942     }
943     topNode.menuSelectedRows = null;
944   }
945 }
946
947 function onEventsSelectionChange() {
948   listOfSelection = this;
949   this.removeClassName("_unfocused");
950   $("tasksList").addClassName("_unfocused");
951 }
952
953 function onTasksSelectionChange() {
954   listOfSelection = this;
955   this.removeClassName("_unfocused");
956   $("eventsList").addClassName("_unfocused");
957 }
958
959 function _loadEventHref(href) {
960   if (document.eventsListAjaxRequest) {
961     document.eventsListAjaxRequest.aborted = true;
962     document.eventsListAjaxRequest.abort();
963   }
964   var url = ApplicationBaseURL + "/" + href;
965   document.eventsListAjaxRequest
966     = triggerAjaxRequest(url, eventsListCallback, href);
967
968   var table = $("eventsList").tBodies[0];
969   while (table.rows.length > 0)
970      table.removeChild(table.rows[0]);
971
972   return false;
973 }
974
975 function _loadTasksHref(href) {
976   if (document.tasksListAjaxRequest) {
977     document.tasksListAjaxRequest.aborted = true;
978     document.tasksListAjaxRequest.abort();
979   }
980   url = ApplicationBaseURL + href;
981
982   var tasksList = $("tasksList");
983   var selectedIds;
984   if (tasksList)
985      selectedIds = tasksList.getSelectedNodesId();
986   else
987      selectedIds = null;
988   document.tasksListAjaxRequest
989     = triggerAjaxRequest(url, tasksListCallback, selectedIds);
990
991   tasksList.previousScroll = tasksList.scrollTop;
992   while (tasksList.childNodes.length)
993      tasksList.removeChild(tasksList.childNodes[0]);
994
995   return true;
996 }
997
998 function onHeaderClick(event) {
999   //log("onHeaderClick: " + this.link);
1000   //_loadEventHref(this.link);
1001
1002   preventDefault(event);
1003 }
1004
1005 function refreshCurrentFolder() {
1006   refreshEvents();
1007 }
1008
1009 function refreshEvents() {
1010   var titleSearch;
1011   var value = search["value"];
1012   if (value && value.length)
1013     titleSearch = "&search=" + value;
1014   else
1015     titleSearch = "";
1016
1017   return _loadEventHref("eventslist?desc=" + sortOrder
1018                         + "&sort=" + sortKey
1019                         + "&day=" + currentDay
1020                         + titleSearch
1021                         + "&filterpopup=" + listFilter);
1022 }
1023
1024 function refreshTasks() {
1025   return _loadTasksHref("taskslist?show-completed=" + showCompletedTasks);
1026 }
1027
1028 function refreshEventsAndDisplay() {
1029   refreshEvents();
1030   changeCalendarDisplay();
1031 }
1032
1033 function onListFilterChange() {
1034   var node = $("filterpopup");
1035
1036   listFilter = node.value;
1037 //   log ("listFilter = " + listFilter);
1038
1039   return refreshEvents();
1040 }
1041
1042 function onEventClick(event) {
1043   changeCalendarDisplay( { "day": this.day,
1044         "scrollEvent": this.getAttribute("id") } );
1045   changeDateSelectorDisplay(this.day);
1046   
1047   return onRowClick(event);
1048 }
1049
1050 function selectMonthInMenu(menu, month) {
1051    var entries = menu.childNodes[1].childNodesWithTag("LI");
1052    for (i = 0; i < entries.length; i++) {
1053       var entry = entries[i];
1054       var entryMonth = entry.getAttribute("month");
1055       if (entryMonth == month)
1056          entry.addClassName("currentMonth");
1057       else
1058          entry.removeClassName("currentMonth");
1059    }
1060 }
1061
1062 function selectYearInMenu(menu, month) {
1063   var entries = menu.childNodes[1].childNodes;
1064   for (i = 0; i < entries.length; i++) {
1065     var entry = entries[i];
1066     if (entry.tagName == "LI") {
1067       var entryMonth = entry.innerHTML;
1068       if (entryMonth == month)
1069         entry.addClassName("currentMonth");
1070       else
1071         entry.removeClassName("currentMonth");
1072     }
1073   }
1074 }
1075
1076 function popupMonthMenu(event) {
1077   if (event.button == 0) {
1078     var id = this.getAttribute("id");
1079     if (id == "monthLabel")
1080        menuId = "monthListMenu";
1081     else
1082        menuId = "yearListMenu";
1083
1084     var popup = $(menuId);
1085     if (id == "monthLabel")
1086       selectMonthInMenu(popup, this.getAttribute("month"));
1087     else
1088       selectYearInMenu(popup, this.innerHTML);
1089
1090     popupToolbarMenu(this, menuId);
1091     Event.stop(event);
1092   }
1093 }
1094
1095 function onMonthMenuItemClick(event) {
1096   var month = '' + this.getAttribute("month");
1097   var year = '' + $("yearLabel").innerHTML;
1098
1099   changeDateSelectorDisplay(year + month + "01", true);
1100 }
1101
1102 function onYearMenuItemClick(event) {
1103   var month = '' + $("monthLabel").getAttribute("month");;
1104   var year = '' + this.innerHTML;
1105
1106   changeDateSelectorDisplay(year + month + "01", true);
1107 }
1108
1109 function selectCalendarEvent(div) {
1110   // Select event in calendar view
1111   if (selectedCalendarCell)
1112      for (var i = 0; i < selectedCalendarCell.length; i++)
1113         selectedCalendarCell[i].deselect();
1114
1115   for (var i = 0; i < div.siblings.length; i++)
1116      div.siblings[i].select();
1117
1118   selectedCalendarCell = div.siblings;
1119 }
1120
1121 function onCalendarSelectEvent() {
1122   var list = $("eventsList");
1123
1124   selectCalendarEvent(this);
1125
1126   // Select event in events list
1127   $(list.tBodies[0]).deselectAll();
1128   var row = $(this.cname);
1129   if (row) {
1130     var div = row.parentNode.parentNode.parentNode;
1131     div.scrollTop = row.offsetTop - (div.offsetHeight / 2);
1132     row.select();
1133   }
1134 }
1135
1136 function onCalendarSelectDay(event) {
1137   var day;
1138   if (currentView == "multicolumndayview")
1139      day = this.getAttribute("day");
1140   else
1141      day = this.getAttribute("day");
1142   var needRefresh = (listFilter == 'view_selectedday'
1143                      && day != currentDay);
1144
1145   if (currentView == 'weekview')
1146     changeWeekCalendarDisplayOfSelectedDay(this);
1147   else if (currentView == 'monthview')
1148     changeMonthCalendarDisplayOfSelectedDay(this);
1149   changeDateSelectorDisplay(day);
1150
1151   if (listOfSelection) {
1152     listOfSelection.addClassName("_unfocused");
1153     listOfSelection = null;
1154   }
1155
1156   if (needRefresh)
1157     refreshEvents();
1158 }
1159
1160 function changeWeekCalendarDisplayOfSelectedDay(node) {
1161   var days = document.getElementsByClassName("day", node.parentNode);
1162   var headerDiv = $("calendarHeader").childNodesWithTag("div")[1];
1163   var headerDays = document.getElementsByClassName("day", headerDiv);
1164
1165 //   log ("days: " + days.length + "; headerDays: " + headerDays.length);
1166   for (var i = 0; i < days.length; i++)
1167      if (days[i] != node) {
1168 //      log("unselect day : " + i);
1169         headerDays[i].removeClassName("selectedDay");
1170         days[i].removeClassName("selectedDay");
1171      }
1172      else {
1173 //      log("selected day : " + i);
1174         headerDays[i].addClassName("selectedDay");
1175         days[i].addClassName("selectedDay");
1176      }
1177 }
1178
1179 function findMonthCalendarSelectedCell(daysContainer) {
1180    var found = false;
1181    var i = 0;
1182
1183    while (!found && i < daysContainer.childNodes.length) {
1184       var currentNode = daysContainer.childNodes[i];
1185       if (currentNode.tagName == 'DIV'
1186           && currentNode.hasClassName("selectedDay")) {
1187          daysContainer.selectedCell = currentNode;
1188          found = true;
1189       }
1190       else
1191          i++;
1192    }
1193 }
1194
1195 function changeMonthCalendarDisplayOfSelectedDay(node) {
1196    var daysContainer = node.parentNode;
1197    if (!daysContainer.selectedCell)
1198       findMonthCalendarSelectedCell(daysContainer);
1199    
1200    if (daysContainer.selectedCell)
1201       daysContainer.selectedCell.removeClassName("selectedDay");
1202    daysContainer.selectedCell = node;
1203    node.addClassName("selectedDay");
1204 }
1205
1206 function onShowCompletedTasks(event) {
1207    showCompletedTasks = (this.checked ? 1 : 0);
1208
1209    return refreshTasks();
1210 }
1211
1212 function updateTaskStatus(event) {
1213   var taskId = this.parentNode.getAttribute("id");
1214   var newStatus = (this.checked ? 1 : 0);
1215   var http = createHTTPClient();
1216
1217   if (isSafari() && !isSafari3()) {
1218     newStatus = (newStatus ? 0 : 1);
1219   }
1220   
1221   url = (ApplicationBaseURL + this.parentNode.calendar
1222          + "/" + taskId + "/changeStatus?status=" + newStatus);
1223
1224   if (http) {
1225     // TODO: add parameter to signal that we are only interested in OK
1226     http.open("POST", url, false /* not async */);
1227     http.url = url;
1228     http.send("");
1229     if (isHttpStatus204(http.status))
1230       refreshTasks();
1231   } else
1232     log ("no http client?");
1233
1234   return false;
1235 }
1236
1237 function updateCalendarStatus(event) {
1238   var list = new Array();
1239   var newStatus = (this.checked ? 1 : 0);
1240   
1241   if (isSafari() && !isSafari3()) {
1242     newStatus = (newStatus ? 0 : 1);
1243     this.checked = newStatus;
1244   }
1245
1246   var nodes = $("calendarList").childNodesWithTag("li");
1247   for (var i = 0; i < nodes.length; i++) {
1248     var input = $(nodes[i]).childNodesWithTag("input")[0];
1249     if (input.checked) {
1250        var folderId = nodes[i].getAttribute("id");
1251        var elems = folderId.split(":");
1252        if (elems.length > 1)
1253           list.push(elems[0]);
1254        else
1255           list.push(UserLogin);
1256     }
1257   }
1258
1259 //   if (!list.length) {
1260 //      list.push(UserLogin);
1261 //      nodes[0].childNodesWithTag("input")[0].checked = true;
1262 //   }
1263
1264 //   ApplicationBaseURL = (UserFolderURL + "Groups/_custom_"
1265 //                      + list.join(",") + "/Calendar/");
1266
1267   if (event) {
1268      var folderID = this.parentNode.getAttribute("id");
1269      var urlstr = URLForFolderID(folderID);
1270      if (newStatus)
1271        urlstr += "/activateFolder";
1272      else
1273        urlstr += "/deactivateFolder";
1274      //log("updateCalendarStatus: ajax request = " + urlstr + ", folderID = " + folderID);
1275      triggerAjaxRequest(urlstr, calendarStatusCallback, folderID);
1276   }
1277   else {
1278      updateCalendarsList();
1279      refreshEvents();
1280      refreshTasks();
1281      changeCalendarDisplay();
1282   }
1283
1284   return false;
1285 }
1286
1287 function calendarStatusCallback(http) {
1288   if (http.readyState == 4) {
1289     if (isHttpStatus204(http.status)) {
1290       refreshEvents();
1291       refreshTasks();
1292       changeCalendarDisplay();
1293     }
1294     else {
1295       var folder = $(http.callbackData);
1296       var input = folder.childNodesWithTag("input")[0];
1297       input.checked = (!input.checked);
1298     }
1299   }
1300   else
1301     log("calendarStatusCallback Ajax error");
1302 }
1303
1304 function calendarEntryCallback(http) {
1305    if (http.readyState == 4) {
1306       var denied = !isHttpStatus204(http.status);
1307       var entry = $(http.callbackData);
1308       if (denied)
1309          entry.addClassName("denied");
1310       else
1311          entry.removeClassName("denied");
1312    }
1313 }
1314
1315 function updateCalendarsList(method) {
1316   var list = $("calendarList").childNodesWithTag("li");
1317   for (var i = 0; i < list.length; i++) {
1318      var folderID = list[i].getAttribute("id");
1319      var url = URLForFolderID(folderID) + "/canAccessContent";
1320      triggerAjaxRequest(url, calendarEntryCallback, folderID);
1321   }
1322 }
1323
1324 function addContact(tag, fullContactName, contactId, contactName, contactEmail) {
1325   var uids = $("uixselector-calendarsList-uidList");
1326 //   log("addContact");
1327   if (contactId)
1328     {
1329       var re = new RegExp("(^|,)" + contactId + "($|,)");
1330
1331       if (!re.test(uids.value))
1332         {
1333           if (uids.value.length > 0)
1334             uids.value += ',' + contactId;
1335           else
1336             uids.value = contactId;
1337           var names = $("calendarList");
1338           var listElems = names.childNodesWithTag("li");
1339           var colorDef = indexColor(listElems.length);
1340           names.appendChild(userCalendarEntry(contactId, colorDef));
1341
1342         }
1343     }
1344
1345   return false;
1346 }
1347
1348 function validateBrowseURL(input) {
1349   var button = $("browseURLBtn");
1350
1351   if (input.value.length) {
1352     if (!button.enabled)
1353       enableAnchor(button);
1354   } else if (!button.disabled)
1355     disableAnchor(button);
1356 }
1357
1358 function browseURL(anchor, event) {
1359   if (event.button == 0) {
1360     var input = $("url");
1361     var url = input.value;
1362     if (url.length)
1363       window.open(url, '_blank');
1364   }
1365
1366   return false;
1367 }
1368
1369 function onCalendarsMenuPrepareVisibility() {
1370   var folders = $("calendarList");
1371   var selected = folders.getSelectedNodes();  
1372
1373   if (selected.length > 0) {
1374     var folderOwner = selected[0].getAttribute("owner");
1375     var sharingOption = $(this).down("ul").childElements().last();
1376     // Disable the "Sharing" option when calendar is not owned by user
1377     if (folderOwner == UserLogin || IsSuperUser)
1378       sharingOption.removeClassName("disabled");
1379     else
1380       sharingOption.addClassName("disabled");
1381   }
1382 }
1383
1384 function getMenus() {
1385    var menus = {};
1386
1387    var dateMenu = new Array();
1388    for (var i = 0; i < 12; i++)
1389       dateMenu.push(onMonthMenuItemClick);
1390    menus["monthListMenu"] = dateMenu;
1391
1392    dateMenu = new Array();
1393    for (var i = 0; i < 11; i++)
1394       dateMenu.push(onYearMenuItemClick);
1395    menus["yearListMenu"] = dateMenu;
1396
1397    menus["eventsListMenu"] = new Array(onMenuNewEventClick, "-",
1398                                        onMenuNewTaskClick,
1399                                        editEvent, deleteEvent, "-",
1400                                        onSelectAll, "-",
1401                                        null, null);
1402    menus["calendarsMenu"] = new Array(onMenuModify,
1403                                       "-",
1404                                       onCalendarNew, onCalendarRemove,
1405                                       "-", null, null, "-",
1406                                       null, "-", onMenuSharing);
1407    menus["searchMenu"] = new Array(setSearchCriteria);
1408
1409    var calendarsMenu = $("calendarsMenu");
1410    if (calendarsMenu)
1411      calendarsMenu.prepareVisibility = onCalendarsMenuPrepareVisibility;
1412
1413    return menus;
1414 }
1415
1416 function onMenuSharing(event) {
1417   if ($(this).hasClassName("disabled"))
1418     return;
1419
1420   var folders = $("calendarList");
1421   var selected = folders.getSelectedNodes()[0];
1422   /* FIXME: activation of the context menu should preferably select the entry
1423                above which the event has occured */
1424   if (selected) {
1425      var folderID = selected.getAttribute("id");
1426      var urlstr = URLForFolderID(folderID) + "/acls";
1427
1428      openAclWindow(urlstr);
1429   }
1430 }
1431
1432 function configureDragHandles() {
1433   var handle = $("verticalDragHandle");
1434   if (handle) {
1435     handle.addInterface(SOGoDragHandlesInterface);
1436     handle.leftBlock=$("leftPanel");
1437     handle.rightBlock=$("rightPanel");
1438   }
1439
1440   handle = $("rightDragHandle");
1441   if (handle) {
1442     handle.addInterface(SOGoDragHandlesInterface);
1443     handle.upperBlock=$("eventsListView");
1444     handle.lowerBlock=$("calendarView");
1445   }
1446 }
1447
1448 function initCalendarSelector() {
1449   var selector = $("calendarSelector");
1450   updateCalendarStatus();
1451   selector.changeNotification = updateCalendarsList;
1452
1453   var list = $("calendarList");
1454   list.multiselect = true;
1455   var items = list.childNodesWithTag("li");
1456   for (var i = 0; i < items.length; i++) {
1457     var input = items[i].childNodesWithTag("input")[0];
1458     Event.observe(input, "click", updateCalendarStatus.bindAsEventListener(input));
1459     Event.observe(items[i], "mousedown", listRowMouseDownHandler);
1460     Event.observe(items[i], "selectstart", listRowMouseDownHandler);
1461     Event.observe(items[i], "click", onRowClick);
1462   }
1463
1464   var links = $("calendarSelectorButtons").childNodesWithTag("a");
1465   Event.observe(links[0], "click",  onCalendarNew);
1466   Event.observe(links[1], "click",  onCalendarAdd);
1467   Event.observe(links[2], "click",  onCalendarRemove);
1468 }
1469
1470 function onMenuModify(event) {
1471   var folders = $("calendarList");
1472   var selected = folders.getSelectedNodes()[0];
1473
1474   if (UserLogin == selected.getAttribute("owner")) {
1475     var node = selected.childNodes[selected.childNodes.length - 1];
1476     var currentName = node.nodeValue.trim();
1477     var newName = window.prompt(labels["Name of the Calendar"],
1478                                 currentName);
1479     if (newName && newName.length > 0
1480         && newName != currentName) {
1481       var url = (URLForFolderID(selected.getAttribute("id"))
1482                  + "/renameFolder?name=" + escape(newName.utf8encode()));
1483       triggerAjaxRequest(url, folderRenameCallback,
1484                          {node: node, name: " " + newName});
1485     }
1486   } else
1487     window.alert(clabels["Unable to rename that folder!"]);
1488 }
1489
1490 function folderRenameCallback(http) {
1491   if (http.readyState == 4) {
1492     if (isHttpStatus204(http.status)) {
1493       var dict = http.callbackData;
1494       dict["node"].nodeValue = dict["name"];
1495     }
1496   }
1497 }
1498
1499 function onCalendarNew(event) {
1500   createFolder(window.prompt(labels["Name of the Calendar"]),
1501                appendCalendar);
1502   preventDefault(event);
1503 }
1504
1505 function onCalendarAdd(event) {
1506   openUserFolderSelector(onFolderSubscribeCB, "calendar");
1507   preventDefault(event);
1508 }
1509
1510 function appendCalendar(folderName, folderPath) {
1511   var owner;
1512
1513   if (folderPath) {
1514     owner = getSubscribedFolderOwner(folderPath);
1515     folderPath = accessToSubscribedFolder(folderPath);
1516   }
1517   else
1518     folderPath = "/" + folderName;
1519
1520   if (!owner)
1521     owner = UserLogin;
1522
1523   //log ("append name: " + folderName + "; path: " + folderPath + "; owner: " + owner);
1524
1525   if ($(folderPath))
1526     window.alert(clabels["You have already subscribed to that folder!"]);
1527   else {
1528     var calendarList = $("calendarList");
1529     var lis = calendarList.childNodesWithTag("li");
1530     var li = document.createElement("li");
1531     
1532     // Add the calendar to the proper place
1533     var previousOwner = null;
1534     for (var i = 0; i < lis.length; i++) {
1535       var currentFolderName = lis[i].lastChild.nodeValue.strip();
1536       var currentOwner = lis[i].readAttribute('owner');
1537       if (currentOwner == owner) {
1538         previousOwner = currentOwner;
1539         if (currentFolderName > folderName)
1540           break;
1541       }
1542       else if (previousOwner || 
1543                (currentOwner != UserLogin && currentOwner > owner))
1544         break;
1545     }
1546     if (i != lis.length) // User is subscribed to other calendars of the same owner
1547       calendarList.insertBefore(li, lis[i]);
1548     else 
1549       calendarList.appendChild(li);
1550     
1551     li.setAttribute("id", folderPath);
1552     li.setAttribute("owner", owner);
1553
1554     // Generate new color
1555     if (calendarColorIndex == null)
1556       calendarColorIndex = lis.length;
1557     calendarColorIndex++;
1558     var colorTable = [1, 1, 1];
1559     var color;
1560     var currentValue = calendarColorIndex;
1561     var index = 0;
1562     while (currentValue) {
1563       if (currentValue & 1)
1564         colorTable[index]++;
1565       if (index == 3)
1566         index = 0;
1567       currentValue >>= 1;
1568       index++;
1569     }
1570     colorTable[0] = parseInt(255 / colorTable[0]) - 1;
1571     colorTable[1] = parseInt(255 / colorTable[1]) - 1;
1572     colorTable[2] = parseInt(255 / colorTable[2]) - 1;
1573
1574     color = "#"
1575       + colorTable[2].toString(16)
1576       + colorTable[1].toString(16)
1577       + colorTable[0].toString(16);
1578     //log ("color = " + color);
1579     
1580     var checkBox = document.createElement("input");
1581     checkBox.setAttribute("type", "checkbox");
1582     li.appendChild(checkBox);
1583     li.appendChild(document.createTextNode(" "));
1584     $(checkBox).addClassName("checkBox");
1585
1586     var colorBox = document.createElement("div");
1587     li.appendChild(colorBox);
1588     li.appendChild(document.createTextNode(folderName));
1589     colorBox.appendChild(document.createTextNode("OO"));
1590
1591     $(colorBox).addClassName("colorBox");
1592     $(colorBox).addClassName('calendarFolder' + folderPath.substr(1));
1593
1594     // Register events (doesn't work with Safari)
1595     Event.observe(li, "mousedown",  listRowMouseDownHandler);
1596     Event.observe(li, "selectstart", listRowMouseDownHandler);
1597     Event.observe(li, "click",  onRowClick);
1598     Event.observe(checkBox, "click",
1599                   updateCalendarStatus.bindAsEventListener(checkBox));
1600
1601     var url = URLForFolderID(folderPath) + "/canAccessContent";
1602     triggerAjaxRequest(url, calendarEntryCallback, folderPath);
1603     
1604     // Update CSS for events color
1605     if (!document.styleSheets) return;
1606     
1607     var styleElement = document.createElement("style");
1608     styleElement.type = "text/css";
1609     var selectors = [
1610                      '.calendarFolder' + folderPath.substr(1),
1611                      'div.colorBox.calendarFolder' + folderPath.substr(1)
1612                      ];
1613     var rules = [
1614                  ' { background-color: ' + color + ' !important; }',
1615                  ' { color: ' + color + ' !important; }'
1616                  ];
1617     for (var i = 0; i < rules.length; i++)
1618       if (styleElement.styleSheet && styleElement.styleSheet.addRule)
1619         styleElement.styleSheet.addRule(selectors[i], rules[i]); // IE
1620       else
1621         styleElement.appendChild(document.createTextNode(selectors[i] + rules[i])); // Mozilla _+ Safari
1622     document.getElementsByTagName("head")[0].appendChild(styleElement);
1623   }
1624 }
1625
1626 function onFolderSubscribeCB(folderData) {
1627   var folder = $(folderData["folder"]);
1628    if (!folder)
1629      appendCalendar(folderData["folderName"], folderData["folder"]);
1630 }
1631
1632 function onFolderUnsubscribeCB(folderId) {
1633   var node = $(folderId);
1634   node.parentNode.removeChild(node);
1635   if (removeFolderRequestCount == 0) {
1636     refreshEvents();
1637     refreshTasks();
1638     changeCalendarDisplay();
1639   }
1640 }
1641
1642 function onCalendarRemove(event) {
1643   if (removeFolderRequestCount == 0) {
1644     var nodes = $("calendarList").getSelectedNodes();
1645     for (var i = 0; i < nodes.length; i++) {
1646       nodes[i].deselect();
1647       var folderId = nodes[i].getAttribute("id");
1648       var folderIdElements = folderId.split("_");
1649       if (folderIdElements.length > 1) {
1650         unsubscribeFromFolder(folderId, onFolderUnsubscribeCB, folderId);
1651       }
1652       else
1653         deletePersonalCalendar(folderIdElements[0]);
1654     }
1655   }
1656   
1657   preventDefault(event);
1658 }
1659
1660 function deletePersonalCalendar(folderElement) {
1661   var folderId = folderElement.substr(1);
1662   var label
1663     = labels["Are you sure you want to delete the calendar \"%{0}\"?"].formatted($(folderElement).lastChild.nodeValue.strip());
1664   if (window.confirm(label)) {
1665     removeFolderRequestCount++;
1666     var url = ApplicationBaseURL + "/" + folderId + "/deleteFolder";
1667     triggerAjaxRequest(url, deletePersonalCalendarCallback, folderId);
1668   }
1669 }
1670
1671 function deletePersonalCalendarCallback(http) {
1672   if (http.readyState == 4) {
1673     if (isHttpStatus204(http.status)) {
1674       var ul = $("calendarList");
1675       var children = ul.childNodesWithTag("li");
1676       var i = 0;
1677       var done = false;
1678       while (!done && i < children.length) {
1679         var currentFolderId = children[i].getAttribute("id").substr(1);
1680         if (currentFolderId == http.callbackData) {
1681           ul.removeChild(children[i]);
1682           done = true;
1683         }
1684         else
1685           i++;
1686       }
1687       removeFolderRequestCount--;
1688       if (removeFolderRequestCount == 0) {
1689         refreshEvents();
1690         refreshTasks();
1691         changeCalendarDisplay();
1692       }
1693     }
1694   }
1695   else
1696     log ("ajax problem 5: " + http.status);
1697 }
1698
1699 function configureLists() {
1700    var list = $("tasksList");
1701    list.multiselect = true;
1702    Event.observe(list, "mousedown",
1703                  onTasksSelectionChange.bindAsEventListener(list));
1704
1705    var input = $("showHideCompletedTasks");
1706    Event.observe(input, "click",
1707                  onShowCompletedTasks.bindAsEventListener(input));
1708
1709    list = $("eventsList");
1710    list.multiselect = true;
1711    //configureSortableTableHeaders(list);
1712    TableKit.Resizable.init(list, {'trueResize' : true, 'keepWidth' : true});
1713    Event.observe(list, "mousedown",
1714                  onEventsSelectionChange.bindAsEventListener(list));
1715    var div = list.parentNode;
1716    Event.observe(div, "contextmenu",
1717                  onEventContextMenu.bindAsEventListener(div));
1718 }
1719
1720 function initDateSelectorEvents() {
1721    var arrow = $("rightArrow");
1722    Event.observe(arrow, "click",
1723                  onDateSelectorGotoMonth.bindAsEventListener(arrow));
1724    arrow = $("leftArrow");
1725    Event.observe(arrow, "click",
1726                  onDateSelectorGotoMonth.bindAsEventListener(arrow));
1727    
1728    var menuButton = $("monthLabel");
1729    Event.observe(menuButton, "click",
1730                  popupMonthMenu.bindAsEventListener(menuButton));
1731    menuButton = $("yearLabel");
1732    Event.observe(menuButton, "click",
1733                  popupMonthMenu.bindAsEventListener(menuButton));
1734 }
1735
1736 function initCalendars() {
1737    if (!document.body.hasClassName("popup")) {
1738       initDateSelectorEvents();
1739       initCalendarSelector();
1740       configureSearchField();
1741       configureLists();
1742       var selector = $("calendarSelector");
1743       if (selector)
1744          selector.attachMenu("calendarsMenu");
1745    }
1746 }
1747
1748 FastInit.addOnLoad(initCalendars);