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