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