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