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