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