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