]> err.no Git - scalable-opengroupware.org/blob - UI/WebServerResources/SchedulerUI.js
6aa1bfb3f8209199933ec90ebbae651ec246109b
[scalable-opengroupware.org] / UI / WebServerResources / SchedulerUI.js
1 var sortOrder = '';
2 var sortKey = '';
3 var listFilter = 'view_today';
4
5 var listOfSelection = null;
6 var selectedCalendarCell;
7
8 var hideCompletedTasks = 0;
9
10 var currentDay = '';
11 var currentView = "dayview";
12
13 var cachedDateSelectors = new Array();
14
15 var contactSelectorAction = 'calendars-contacts';
16
17 var eventsToDelete = new Array();
18 var ownersOfEventsToDelete = new Array();
19
20 function newEvent(sender, type) {
21   var day = sender.getAttribute("day");
22   if (!day)
23     day = currentDay;
24
25   var user = UserLogin;
26   if (currentView == "multicolumndayview" && type == "event")
27      user = sender.parentNode.parentNode.getAttribute("user");
28
29   var hour = sender.getAttribute("hour");
30   var urlstr = UserFolderURL + "../" + user + "/Calendar/new" + type;
31   var params = new Array();
32   if (day)
33     params.push("day=" + day);
34   if (hour)
35     params.push("hm=" + hour);
36   if (params.length > 0)
37     urlstr += "?" + params.join("&");
38
39   window.open(urlstr, "", "width=620,height=600,resizable=0");
40
41   return false; /* stop following the link */
42 }
43
44 function _editEventId(id, owner) {
45   var urlBase;
46   if (owner)
47     urlBase = UserFolderURL + "../" + owner + "/";
48   urlBase += "Calendar/"
49
50   var urlstr = urlBase + id + "/edit";
51
52   var win = window.open(urlstr, "SOGo_edit_" + id,
53                         "width=620,height=600,resizable=0,scrollbars=0,toolbar=0," +
54                         "location=0,directories=0,status=0,menubar=0,copyhistory=0");
55   win.focus();
56 }
57
58 function editEvent() {
59   if (listOfSelection) {
60     var nodes = listOfSelection.getSelectedRows();
61
62     for (var i = 0; i < nodes.length; i++)
63       _editEventId(nodes[i].getAttribute("id"),
64                    nodes[i].getAttribute("owner"));
65   } else if (selectedCalendarCell) {
66       _editEventId(selectedCalendarCell.getAttribute("aptCName"),
67                    selectedCalendarCell.getAttribute("owner"));
68   }
69
70   return false; /* stop following the link */
71 }
72
73 function _batchDeleteEvents() {
74   var events = eventsToDelete.shift();
75   var owner = ownersOfEventsToDelete.shift();
76   var urlstr = (UserFolderURL + "../" + owner + "/Calendar/batchDelete?ids="
77                 + events.join('/'));
78   document.deleteEventAjaxRequest = triggerAjaxRequest(urlstr,
79                                                        deleteEventCallback,
80                                                        events);
81 }
82
83 function deleteEvent()
84 {
85   if (listOfSelection) {
86     var nodes = listOfSelection.getSelectedRows();
87
88     if (nodes.length > 0) {
89       var label = "";
90       if (listOfSelection == $("tasksList"))
91         label = labels["taskDeleteConfirmation"].decodeEntities();
92       else
93         label = labels["appointmentDeleteConfirmation"].decodeEntities();
94       
95       if (confirm(label)) {
96         if (document.deleteEventAjaxRequest) {
97           document.deleteEventAjaxRequest.aborted = true;
98           document.deleteEventAjaxRequest.abort();
99         }
100         var sortedNodes = new Array();
101         var owners = new Array();
102
103         for (var i = 0; i < nodes.length; i++) {
104           var owner = nodes[i].getAttribute("owner");
105           if (!sortedNodes[owner]) {
106               sortedNodes[owner] = new Array();
107               owners.push(owner);
108           }
109           sortedNodes[owner].push(nodes[i].getAttribute("id"));
110         }
111         for (var i = 0; i < owners.length; i++) {
112           ownersOfEventsToDelete.push(owners[i]);
113           eventsToDelete.push(sortedNodes[owners[i]]);
114         }
115         _batchDeleteEvents();
116       }
117     }
118   }
119   else if (selectedCalendarCell) {
120      var label = labels["appointmentDeleteConfirmation"].decodeEntities();
121      if (confirm(label)) {
122         if (document.deleteEventAjaxRequest) {
123            document.deleteEventAjaxRequest.aborted = true;
124            document.deleteEventAjaxRequest.abort();
125         }
126         eventsToDelete.push([selectedCalendarCell.getAttribute("aptCName")]);
127         ownersOfEventsToDelete.push(selectedCalendarCell.getAttribute("owner"));
128         _batchDeleteEvents();
129      }
130   }
131   else
132     window.alert("no selection");
133
134   return false;
135 }
136
137 function modifyEvent(sender, modification) {
138   var currentLocation = '' + window.location;
139   var arr = currentLocation.split("/");
140   arr[arr.length-1] = modification;
141
142   document.modifyEventAjaxRequest = triggerAjaxRequest(arr.join("/"),
143                                                        modifyEventCallback,
144                                                        modification);
145
146   return false;
147 }
148
149 function closeInvitationWindow() {
150   var closeDiv = document.createElement("div");
151   closeDiv.addClassName("javascriptPopupBackground");
152   var closePseudoWin = document.createElement("div");
153   closePseudoWin.addClassName("javascriptMessagePseudoWindow");
154   closePseudoWin.style.top = "0px;";
155   closePseudoWin.style.left = "0px;";
156   closePseudoWin.style.right = "0px;";
157   closePseudoWin.appendChild(document.createTextNode(labels["closeThisWindowMessage"].decodeEntities()));
158   document.body.appendChild(closeDiv);
159   document.body.appendChild(closePseudoWin);
160 }
161
162 function modifyEventCallback(http) {
163   if (http.readyState == 4) {
164     if (http.status == 200) {
165       log("closing window...?");
166       if (queryParameters["mail-invitation"] == "yes")
167         closeInvitationWindow();
168       else {
169         window.opener.setTimeout("refreshAppointmentsAndDisplay();", 100);
170         window.setTimeout("window.close();", 100);
171       }
172     }
173     else {
174       log("showing alert...");
175       window.alert(labels["eventPartStatModificationError"]);
176     }
177     document.modifyEventAjaxRequest = null;
178   }
179 }
180
181 function deleteEventCallback(http)
182 {
183   if (http.readyState == 4
184       && http.status == 200) {
185     var nodes = http.callbackData;
186     for (var i = 0; i < nodes.length; i++) {
187       var node = $(nodes[i]);
188       if (node)
189         node.parentNode.removeChild(node);
190     }
191     if (eventsToDelete.length)
192       _batchDeleteEvents();
193     else {
194       document.deleteEventAjaxRequest = null;
195       refreshAppointments();
196       refreshTasks();
197       changeCalendarDisplay();
198     }
199   }
200   else
201     log ("ajax fuckage");
202 }
203
204 function editDoubleClickedEvent(node)
205 {
206   _editEventId(node.getAttribute("id"),
207                node.getAttribute("owner"));
208   
209   return false;
210 }
211
212 function onSelectAll() {
213   var list = $("appointmentsList");
214   list.selectRowsMatchingClass("appointmentRow");
215
216   return false;
217 }
218
219 function displayAppointment(event) {
220   _editEventId(this.getAttribute("aptCName"),
221                this.getAttribute("owner"));
222
223   event.preventDefault();
224   event.stopPropagation();
225   event.cancelBubble = true;
226   event.returnValue = false;
227 }
228
229 function onDaySelect(node)
230 {
231   var day = node.getAttribute("day");
232   var needRefresh = (listFilter == 'view_selectedday'
233                      && day != currentDay);
234
235   var td = node.getParentWithTagName("td");
236   var table = td.getParentWithTagName("table");
237
238 //   log ("table.selected: " + table.selected);
239
240   if (document.selectedDate)
241     document.selectedDate.deselect();
242
243   td.select();
244   document.selectedDate = td;
245
246   changeCalendarDisplay( { "day": day } );
247   if (needRefresh)
248     refreshAppointments();
249
250   return false;
251 }
252
253 function onDateSelectorGotoMonth(node)
254 {
255   var day = node.getAttribute("date");
256
257   changeDateSelectorDisplay(day, true);
258
259   return false;
260 }
261
262 function onCalendarGotoDay(node)
263 {
264   var day = node.getAttribute("date");
265
266   changeDateSelectorDisplay(day);
267   changeCalendarDisplay( { "day": day } );
268
269   return false;
270 }
271
272 function gotoToday()
273 {
274   changeDateSelectorDisplay('');
275   changeCalendarDisplay();
276
277   return false;
278 }
279
280 function setDateSelectorContent(content)
281 {
282   var div = $("dateSelectorView");
283
284   div.innerHTML = content;
285   if (currentDay.length > 0)
286     restoreCurrentDaySelection(div);
287 }
288
289 function dateSelectorCallback(http)
290 {
291   if (http.readyState == 4
292       && http.status == 200) {
293     document.dateSelectorAjaxRequest = null;
294     var content = http.responseText;
295     setDateSelectorContent(content);
296     cachedDateSelectors[http.callbackData] = content;
297   }
298   else
299     log ("ajax fuckage");
300 }
301
302 function appointmentsListCallback(http)
303 {
304   var div = $("appointmentsListView");
305
306   if (http.readyState == 4
307       && http.status == 200) {
308     document.appointmentsListAjaxRequest = null;
309     div.innerHTML = http.responseText;
310     var params = parseQueryParameters(http.callbackData);
311     sortKey = params["sort"];
312     sortOrder = params["desc"];
313     var list = $("appointmentsList");
314     list.addEventListener("selectionchange",
315                           onAppointmentsSelectionChange, true);
316     configureSortableTableHeaders();
317   }
318   else
319     log ("ajax fuckage");
320 }
321
322 function tasksListCallback(http)
323 {
324   var div = $("tasksListView");
325
326   if (http.readyState == 4
327       && http.status == 200) {
328     document.tasksListAjaxRequest = null;
329     var list = $("tasksList");
330     var scroll = list.scrollTop;
331     div.innerHTML = http.responseText;
332     list = $("tasksList");
333     list.addEventListener("selectionchange",
334                           onTasksSelectionChange, true);
335     list.scrollTop = scroll;
336     if (http.callbackData) {
337       var selectedNodesId = http.callbackData;
338       for (var i = 0; i < selectedNodesId.length; i++)
339         $(selectedNodesId[i]).select();
340     }
341   }
342   else
343     log ("ajax fuckage");
344 }
345
346 function restoreCurrentDaySelection(div)
347 {
348   var elements = div.getElementsByTagName("a");
349   var day = null;
350   var i = 9;
351   while (!day && i < elements.length)
352     {
353       day = elements[i].getAttribute("day");
354       i++;
355     }
356
357   if (day
358       && day.substr(0, 6) == currentDay.substr(0, 6)) {
359       for (i = 0; i < elements.length; i++) {
360         day = elements[i].getAttribute("day");
361         if (day && day == currentDay) {
362           var td = elements[i].getParentWithTagName("td");
363           if (document.selectedDate)
364             document.selectedDate.deselect();
365           td.select();
366           document.selectedDate = td;
367         }
368       }
369     }
370 }
371
372 function changeDateSelectorDisplay(day, keepCurrentDay)
373 {
374   var url = ApplicationBaseURL + "dateselector";
375   if (day)
376     url += "?day=" + day;
377
378   if (day != currentDay) {
379     if (!keepCurrentDay)
380       currentDay = day;
381
382     log (backtrace());
383     var month = day.substr(0, 6);
384     if (cachedDateSelectors[month]) {
385 //       log ("restoring cached selector for month: " + month);
386       setDateSelectorContent(cachedDateSelectors[month]);
387     }
388     else {
389 //       log ("loading selector for month: " + month);
390       if (document.dateSelectorAjaxRequest) {
391         document.dateSelectorAjaxRequest.aborted = true;
392         document.dateSelectorAjaxRequest.abort();
393       }
394       document.dateSelectorAjaxRequest
395         = triggerAjaxRequest(url,
396                              dateSelectorCallback,
397                              month);
398     }
399   }
400 }
401
402 function changeCalendarDisplay(time, newView)
403 {
404   var url = ApplicationBaseURL + ((newView) ? newView : currentView);
405
406   selectedCalendarCell = null;
407
408   var day = null;
409   var hour = null;
410   if (time) {
411     day = time['day'];
412     hour = time['hour'];
413   }
414
415   if (!day)
416     day = currentDay;
417   if (day)
418     url += "?day=" + day;
419
420 //   if (newView)
421 //     log ("switching to view: " + newView);
422 //   log ("changeCalendarDisplay: " + url);
423
424   if (document.dayDisplayAjaxRequest) {
425 //     log ("aborting day ajaxrq");
426     document.dayDisplayAjaxRequest.aborted = true;
427     document.dayDisplayAjaxRequest.abort();
428   }
429   document.dayDisplayAjaxRequest = triggerAjaxRequest(url,
430                                                       calendarDisplayCallback,
431                                                       { "view": newView,
432                                                         "day": day,
433                                                         "hour": hour });
434
435   return false;
436 }
437
438 function _ensureView(view) {
439   if (currentView != view)
440     changeCalendarDisplay(null, view);
441
442   return false;
443 }
444
445 function onDayOverview()
446 {
447   return _ensureView("dayview");
448 }
449
450 function onMulticolumnDayOverview()
451 {
452   return _ensureView("multicolumndayview");
453 }
454
455 function onWeekOverview()
456 {
457   return _ensureView("weekview");
458 }
459
460 function onMonthOverview()
461 {
462   return _ensureView("monthview");
463 }
464
465 function scrollDayView(hour)
466 {
467   var rowNumber;
468   if (hour) {
469     if (hour.length == 3)
470       rowNumber = parseInt(hour.substr(0, 1));
471     else {
472       if (hour.substr(0, 1) == "0")
473         rowNumber = parseInt(hour.substr(1, 1));
474       else
475         rowNumber = parseInt(hour.substr(0, 2));
476     }
477   } else
478     rowNumber = 8;
479
480   var daysView = $("daysView");
481   var hours = daysView.childNodesWithTag("div")[0].childNodesWithTag("div");
482   if (hours.length > 0)
483     daysView.parentNode.scrollTop = hours[rowNumber + 1].offsetTop;
484 }
485
486 function onClickableCellsDblClick(event) {
487   newEvent(this, 'event');
488
489   event.cancelBubble = true;
490   event.returnValue = false;
491 }
492
493 function calendarDisplayCallback(http)
494 {
495   var div = $("calendarView");
496
497 //   log ("calendardisplaycallback: " + div);
498   if (http.readyState == 4
499       && http.status == 200) {
500     document.dayDisplayAjaxRequest = null;
501     div.innerHTML = http.responseText;
502     if (http.callbackData["view"])
503       currentView = http.callbackData["view"];
504     if (http.callbackData["day"])
505       currentDay = http.callbackData["day"];
506     var hour = null;
507     if (http.callbackData["hour"])
508       hour = http.callbackData["hour"];
509     var contentView;
510     if (currentView == "monthview")
511       contentView = $("calendarContent");
512     else {
513       scrollDayView(hour);
514 //       log("cbtest1");
515       contentView = $("daysView");
516     }
517     var appointments = document.getElementsByClassName("appointment", contentView);
518     for (var i = 0; i < appointments.length; i++) {
519       appointments[i].addEventListener("mousedown", listRowMouseDownHandler, true);
520       appointments[i].addEventListener("click", onCalendarSelectAppointment, false);
521       appointments[i].addEventListener("dblclick", displayAppointment, true);
522     }
523     var days = document.getElementsByClassName("day", contentView);
524     if (currentView == "monthview")
525       for (var i = 0; i < days.length; i++) {
526         days[i].addEventListener("click", onCalendarSelectDay, true);
527         days[i].addEventListener("dblclick", onClickableCellsDblClick, false);
528       }
529     else
530       for (var i = 0; i < days.length; i++) {
531         days[i].addEventListener("click", onCalendarSelectDay, false);
532         var clickableCells = document.getElementsByClassName("clickableHourCell",
533                                                              days[i]);
534         for (var j = 0; j < clickableCells.length; j++)
535           clickableCells[j].addEventListener("dblclick",
536                                              onClickableCellsDblClick, false);
537       }
538 //     log("cbtest1");
539   }
540   else
541     log ("ajax fuckage");
542 }
543
544 function assignCalendar(name)
545 {
546   var node = $(name);
547
548   node.calendar = new skycalendar(node);
549   node.calendar.setCalendarPage(ResourcesURL + "/skycalendar.html");
550   var dateFormat = node.getAttribute("dateFormat");
551   if (dateFormat)
552     node.calendar.setDateFormat(dateFormat);
553 }
554
555 function popupCalendar(node)
556 {
557   var nodeId = node.getAttribute("inputId");
558   var input = $(nodeId);
559   input.calendar.popup();
560
561   return false;
562 }
563
564 function onAppointmentContextMenu(event, element)
565 {
566   var topNode = $("appointmentsList");
567 //   log(topNode);
568
569   var menu = $("appointmentsListMenu");
570
571   menu.addEventListener("hideMenu", onAppointmentContextMenuHide, false);
572   onMenuClick(event, "appointmentsListMenu");
573
574   var topNode = $("appointmentsList");
575   var selectedNodes = topNode.getSelectedRows();
576   topNode.menuSelectedRows = selectedNodes;
577   for (var i = 0; i < selectedNodes.length; i++)
578     selectedNodes[i].deselect();
579
580   topNode.menuSelectedEntry = element;
581   element.select();
582 }
583
584 function onAppointmentContextMenuHide(event)
585 {
586   var topNode = $("appointmentsList");
587
588   if (topNode.menuSelectedEntry) {
589     topNode.menuSelectedEntry.deselect();
590     topNode.menuSelectedEntry = null;
591   }
592   if (topNode.menuSelectedRows) {
593     var nodeIds = topNode.menuSelectedRows;
594     for (var i = 0; i < nodeIds.length; i++) {
595       var node = $(nodeIds[i]);
596       node.select();
597     }
598     topNode.menuSelectedRows = null;
599   }
600 }
601
602 function onAppointmentsSelectionChange() {
603   listOfSelection = this;
604   this.removeClassName("_unfocused");
605   $("tasksList").addClassName("_unfocused");
606 }
607
608 function onTasksSelectionChange() {
609   listOfSelection = this;
610   this.removeClassName("_unfocused");
611   $("appointmentsList").addClassName("_unfocused");
612 }
613
614 function _loadAppointmentHref(href) {
615   if (document.appointmentsListAjaxRequest) {
616     document.appointmentsListAjaxRequest.aborted = true;
617     document.appointmentsListAjaxRequest.abort();
618   }
619   var url = ApplicationBaseURL + href;
620   document.appointmentsListAjaxRequest
621     = triggerAjaxRequest(url, appointmentsListCallback, href);
622
623   return false;
624 }
625
626 function _loadTasksHref(href) {
627   if (document.tasksListAjaxRequest) {
628     document.tasksListAjaxRequest.aborted = true;
629     document.tasksListAjaxRequest.abort();
630   }
631   url = ApplicationBaseURL + href;
632
633   var selectedIds = $("tasksList").getSelectedNodesId();
634   document.tasksListAjaxRequest
635     = triggerAjaxRequest(url, tasksListCallback, selectedIds);
636
637   return false;
638 }
639
640 function onHeaderClick(event) {
641 //   log("onHeaderClick: " + this.link);
642   _loadAppointmentHref(this.link);
643
644   event.preventDefault();
645 }
646
647 function refreshAppointments() {
648   return _loadAppointmentHref("aptlist?desc=" + sortOrder
649                               + "&sort=" + sortKey
650                               + "&day=" + currentDay
651                               + "&filterpopup=" + listFilter);
652 }
653
654 function refreshTasks() {
655   return _loadTasksHref("taskslist?hide-completed=" + hideCompletedTasks);
656 }
657
658 function refreshAppointmentsAndDisplay()
659 {
660   refreshAppointments();
661   changeCalendarDisplay();
662 }
663
664 function onListFilterChange() {
665   var node = $("filterpopup");
666
667   listFilter = node.value;
668 //   log ("listFilter = " + listFilter);
669
670   return refreshAppointments();
671 }
672
673 function onAppointmentClick(event)
674 {
675   var node = event.target.getParentWithTagName("tr");
676   var day = node.getAttribute("day");
677   var hour = node.getAttribute("hour");
678
679   changeCalendarDisplay( { "day": day, "hour": hour} );
680   changeDateSelectorDisplay(day);
681
682   return onRowClick(event);
683 }
684
685 function selectMonthInMenu(menu, month)
686 {
687   var entries = menu.childNodes[1].childNodesWithTag("LI");
688   for (i = 0; i < entries.length; i++) {
689     var entry = entries[i];
690     var entryMonth = entry.getAttribute("month");
691     if (entryMonth == month)
692       entry.addClassName("currentMonth");
693     else
694       entry.removeClassName("currentMonth");
695   }
696 }
697
698 function selectYearInMenu(menu, month)
699 {
700   var entries = menu.childNodes[1].childNodes;
701   for (i = 0; i < entries.length; i++) {
702     var entry = entries[i];
703     if (entry instanceof HTMLLIElement) {
704       var entryMonth = entry.innerHTML;
705       if (entryMonth == month)
706         entry.addClassName("currentMonth");
707       else
708         entry.removeClassName("currentMonth");
709     }
710   }
711 }
712
713 function popupMonthMenu(event, menuId)
714 {
715   var node = event.target;
716
717   if (event.button == 0) {
718     event.cancelBubble = true;
719     event.returnValue = false;
720
721     if (document.currentPopupMenu)
722       hideMenu(event, document.currentPopupMenu);
723
724     var popup = $(menuId);
725     var id = node.getAttribute("id");
726     if (id == "monthLabel")
727       selectMonthInMenu(popup, node.getAttribute("month"));
728     else
729       selectYearInMenu(popup, node.innerHTML);
730
731     var diff = (popup.offsetWidth - node.offsetWidth) /2;
732
733     popup.style.top = (node.offsetTop + 95) + "px";
734     popup.style.left = (node.offsetLeft - diff) + "px";
735     popup.style.visibility = "visible";
736
737     bodyOnClick = "" + document.body.getAttribute("onclick");
738     document.body.setAttribute("onclick", "onBodyClick('" + menuId + "');");
739     document.currentPopupMenu = popup;
740   }
741 }
742
743 function onMonthMenuItemClick(node)
744 {
745   var month = '' + node.getAttribute("month");
746   var year = '' + $("yearLabel").innerHTML;
747   
748   changeDateSelectorDisplay(year+month+"01", true);
749
750   return false;
751 }
752
753 function onYearMenuItemClick(node) {
754   var month = '' + $("monthLabel").getAttribute("month");;
755   var year = '' + node.innerHTML;
756
757   changeDateSelectorDisplay(year+month+"01", true);
758
759   return false;
760 }
761
762 function onSearchFormSubmit() {
763   log ("search not implemented");
764
765   return false;
766 }
767
768 function onCalendarSelectAppointment() {
769   var list = $("appointmentsList");
770   list.deselectAll();
771
772   var aptCName = this.getAttribute("aptCName");
773   if (selectedCalendarCell)
774     selectedCalendarCell.deselect();
775   this.select();
776   selectedCalendarCell = this;
777   var row = $(aptCName);
778   if (row) {
779     var div = row.parentNode.parentNode.parentNode;
780     div.scrollTop = row.offsetTop - (div.offsetHeight / 2);
781     row.select();
782   }
783 }
784
785 function onCalendarSelectDay(event) {
786   var day;
787   if (currentView == "multicolumndayview")
788      day = this.parentNode.getAttribute("day");
789   else
790      day = this.getAttribute("day");
791   var needRefresh = (listFilter == 'view_selectedday'
792                      && day != currentDay);
793
794   if (currentView == 'weekview')
795     changeWeekCalendarDisplayOfSelectedDay(this);
796   else if (currentView == 'monthview')
797     changeMonthCalendarDisplayOfSelectedDay(this);
798   changeDateSelectorDisplay(day);
799
800   if (listOfSelection) {
801     listOfSelection.addClassName("_unfocused");
802     listOfSelection = null;
803   }
804
805   if (needRefresh)
806     refreshAppointments();
807 }
808
809 function changeWeekCalendarDisplayOfSelectedDay(node) {
810   var days = document.getElementsByClassName("day", node.parentNode);
811
812   for (var i = 0; i < days.length; i++)
813     if (days[i] != node)
814       days[i].removeClassName("selectedDay");
815
816   node.addClassName("selectedDay");
817 }
818
819 function findMonthCalendarSelectedCell(table) {
820   var tbody = table.tBodies[0];
821   var rows = tbody.rows;
822
823   var i = 1;
824   while (i < rows.length && !table.selectedCell) {
825     var cells = rows[i].cells;
826     var j = 0;
827     while (j < cells.length && !table.selectedCell) {
828       if (cells[j].hasClassName("selectedDay"))
829         table.selectedCell = cells[j];
830       else
831         j++;
832     }
833     i++;
834   }
835 }
836
837 function changeMonthCalendarDisplayOfSelectedDay(node)
838 {
839   var tr = node.parentNode;
840   var table = tr.parentNode.parentNode;
841
842   if (!table.selectedCell)
843     findMonthCalendarSelectedCell(table);
844
845   if (table.selectedCell)
846     table.selectedCell.removeClassName("selectedDay");
847   table.selectedCell = node;
848   node.addClassName("selectedDay");
849 }
850
851 function onHideCompletedTasks(node)
852 {
853   hideCompletedTasks = (node.checked ? 1 : 0);
854
855   return refreshTasks();
856 }
857
858 function updateTaskStatus(node)
859 {
860   var taskId = node.parentNode.getAttribute("id");
861   var taskOwner = node.parentNode.getAttribute("owner");
862   var newStatus = (node.checked ? 1 : 0);
863 //   log ("update task status: " + taskId);
864
865   var http = createHTTPClient();
866
867   url = (UserFolderURL + "../" + taskOwner + "/Calendar/"
868          + taskId + "/changeStatus?status=" + newStatus);
869
870   if (http) {
871 //     log ("url: " + url);
872     // TODO: add parameter to signal that we are only interested in OK
873     http.url = url;
874     http.open("GET", url, false /* not async */);
875     http.send("");
876     if (http.status == 200)
877       refreshTasks();
878   } else
879     log ("no http client?");
880
881   return false;
882 }
883
884 function updateCalendarStatus()
885 {
886   var list = new Array();
887
888   var clist = $("calendarsList");
889   var nodes = clist.childNodesWithTag("ul")[0].childNodesWithTag("li");
890   for (var i = 0; i < nodes.length; i++) {
891     var input = nodes[i].childNodesWithTag("input")[0];
892     if (input.checked)
893       list.push(nodes[i].getAttribute("uid"));
894   }
895
896   if (!list.length) {
897     list.push(nodes[0].getAttribute("uid"));
898     nodes[0].childNodesWithTag("input")[0].checked = true;
899   }
900 //   ApplicationBaseURL = (UserFolderURL + "Groups/_custom_"
901 //                      + list.join(",") + "/Calendar/");
902
903   updateCalendarsList();
904   refreshAppointments();
905   refreshTasks();
906   changeCalendarDisplay();
907
908   return false;
909 }
910
911 function calendarUidsList()
912 {
913   var list = "";
914
915   var nodes = $("uixselector-calendarsList-display").childNodesWithTag("li");
916   for (var i = 0; i < nodes.length; i++) {
917     var currentNode = nodes[i];
918     var input = currentNode.childNodesWithTag("input")[0];
919     if (!input.checked)
920       list += "-";
921     list += currentNode.getAttribute("uid") + ",";
922   }
923
924   return list.substr(0, list.length - 1);
925 }
926
927 // function updateCalendarContacts(contacts)
928 // {
929 //   var list = contacts.split(",");
930
931 //   var clist = $("calendarsList");
932 //   var nodes = clist.childNodes[5].childNodes;
933 //   for (var i = 0; i < nodes.length; i++) {
934 //     var currentNode = nodes[i];
935 //     if (currentNode instanceof HTMLLIElement) {
936 //       var input = currentNode.childNodes[3];
937 //       if (!input.checked)
938 //         list += "-";
939 //       list += currentNode.getAttribute("uid") + ",";
940 //     }
941 //   }
942 // }
943
944 function inhibitMyCalendarEntry()
945 {
946   var clist = $("calendarsList");
947   var nodes = clist.childNodes[5].childNodes;
948   var done = false;
949
950   var i = 0;
951   while (!done && i < nodes.length) {
952     var currentNode = nodes[i];
953     if (currentNode instanceof HTMLLIElement) {
954       var input = currentNode.childNodes[3];
955       if (currentNode.getAttribute("uid") == UserLogin) {
956         done = true;
957 //         currentNode.style.color = "#999;";
958         currentNode.style.fontWeight = "bold;";
959 //         currentNode.setAttribute("onclick", "");
960       }
961     }
962     i++;
963   }
964 }
965
966 function userCalendarEntry(user, color) {
967   var li = document.createElement("li");
968   li.setAttribute("uid", user);
969   li.addEventListener("mousedown", listRowMouseDownHandler, false);
970   li.addEventListener("click", onRowClick, false);
971   var colorBox = document.createElement("span");
972   colorBox.addClassName("colorBox");
973   if (color) {
974     log("color:  " + color);
975     colorBox.style.backgroundColor = color + ";";
976   }
977   li.appendChild(colorBox);
978   var checkBox = document.createElement("input");
979   checkBox.addClassName("checkBox");
980   checkBox.type = "checkbox";
981   checkBox.addEventListener("change", updateCalendarStatus, false);
982   li.appendChild(checkBox);
983   var text = document.createTextNode(" " + user);
984   li.appendChild(text);
985
986   return li;
987 }
988
989 function ensureSelfIfPresent() {
990   var ul = $("uixselector-calendarsList-display");
991   var list = ul.childNodesWithTag("li");
992   var selfEntry = userCalendarEntry(UserLogin, indexColor(0));
993   selfEntry.style.fontWeight = "bold;";
994   if (list.length < 1) {
995     ul.appendChild(selfEntry);
996   } else if (list[0].getAttribute("uid") != UserLogin) {
997     ul.insertBefore(selfEntry, list[0]);
998   }
999 }
1000
1001 function updateCalendarsList(method)
1002 {
1003   ensureSelfIfPresent();
1004   var url = (ApplicationBaseURL + "updateCalendars?ids="
1005              + calendarUidsList());
1006   if (document.calendarsListAjaxRequest) {
1007     document.calendarsListAjaxRequest.aborted = true;
1008     document.calendarsListAjaxRequest.abort();
1009   }
1010   var http = createHTTPClient();
1011   if (http) {
1012     http.url = url;
1013     http.open("GET", url, false);
1014     http.send("");
1015
1016     if (method == "removal")
1017       updateCalendarStatus();
1018
1019     http = createHTTPClient();
1020     http.url = ApplicationBaseURL + "checkRights";
1021     http.open("GET", http.url, false /* not async */);
1022     http.send("");
1023     if (http.status == 200
1024         && http.responseText.length > 0) {
1025       rights = http.responseText.split(",");
1026       var list = $("uixselector-calendarsList-display").childNodesWithTag("li");
1027       for (var i = 0; i < list.length; i++) {
1028         var input = list[i].childNodesWithTag("input")[0];
1029         if (rights[i] == "1") {
1030           list[i].removeClassName("denied");
1031           input.disabled = false;
1032         }
1033         else {
1034           input.checked = false;
1035           input.disabled = true;
1036           list[i].addClassName("denied");
1037         }
1038       }
1039     }
1040   }
1041 }
1042
1043 function addContact(tag, fullContactName, contactId, contactName, contactEmail)
1044 {
1045   var uids = $("uixselector-calendarsList-uidList");
1046 //   log("addContact");
1047   if (contactId)
1048     {
1049       var re = new RegExp("(^|,)" + contactId + "($|,)");
1050
1051       if (!re.test(uids.value))
1052         {
1053           if (uids.value.length > 0)
1054             uids.value += ',' + contactId;
1055           else
1056             uids.value = contactId;
1057           var names = $("uixselector-calendarsList-display");
1058           var listElems = names.childNodesWithTag("li");
1059           var colorDef = indexColor(listElems.length);
1060           names.appendChild(userCalendarEntry(contactId, colorDef));
1061
1062           var styles = document.getElementsByTagName("style");
1063           styles[0].innerHTML += ('.ownerIs' + contactId + ' {'
1064                                   + ' background-color: '
1065                                   + colorDef
1066                                   + ' !important; }');
1067         }
1068     }
1069
1070   return false;
1071 }
1072
1073 function onChangeCalendar(list) {
1074    var form = document.forms.editform;
1075    var urlElems = form.getAttribute("action").split("/");
1076    urlElems[urlElems.length-4]
1077       = list.childNodesWithTag("option")[list.value].innerHTML;
1078    form.setAttribute("action", urlElems.join("/"));
1079 }
1080
1081 function validateBrowseURL(input) {
1082   var button = $("browseURLBtn");
1083
1084   if (input.value.length) {
1085     if (!button.enabled)
1086       enableAnchor(button);
1087   } else if (!button.disabled)
1088     disableAnchor(button);
1089 }
1090
1091 function browseURL(anchor, event) {
1092   if (event.button == 0) {
1093     var input = $("url");
1094     var url = input.value;
1095     if (url.length)
1096       window.open(url, '_blank');
1097   }
1098
1099   return false;
1100 }
1101
1102 function initializeMenus() {
1103   var menus = new Array("monthListMenu", "yearListMenu",
1104                         "appointmentsListMenu", "calendarsMenu", "searchMenu");
1105   initMenusNamed(menus);
1106
1107   $("calendarsList").attachMenu("calendarsMenu");
1108
1109   var accessRightsMenuEntry = $("accessRightsMenuEntry");
1110   accessRightsMenuEntry.addEventListener("mouseup",
1111                                          onAccessRightsMenuEntryMouseUp,
1112                                          false);
1113 }
1114
1115 function onAccessRightsMenuEntryMouseUp(event) {
1116   var folders = $("uixselector-calendarsList-display");
1117   var selected = folders.getSelectedNodes()[0];
1118   var uid = selected.getAttribute("uid");
1119   log("application base url: " + ApplicationBaseURL);
1120   if (uid == UserLogin)
1121     url = ApplicationBaseURL + "acls";
1122   else
1123     url = UserFolderURL + "../" + uid + "/Calendar/acls";
1124
1125   openAclWindow(url, uid);
1126 }
1127
1128 function configureDragHandles() {
1129   var handle = $("verticalDragHandle");
1130   if (handle) {
1131     handle.addInterface(SOGoDragHandlesInterface);
1132     handle.leftBlock=$("leftPanel");
1133     handle.rightBlock=$("rightPanel");
1134   }
1135
1136   handle = $("rightDragHandle");
1137   if (handle) {
1138     handle.addInterface(SOGoDragHandlesInterface);
1139     handle.upperBlock=$("appointmentsListView");
1140     handle.lowerBlock=$("calendarView");
1141   }
1142 }
1143
1144 function initCalendarContactsSelector() {
1145   var selector = $("calendarsList");
1146   inhibitMyCalendarEntry();
1147   updateCalendarStatus();
1148   selector.changeNotification = updateCalendarsList;
1149
1150   var list = $("uixselector-calendarsList-display").childNodesWithTag("li");
1151   for (var i = 0; i < list.length; i++) {
1152     var input = list[i].childNodesWithTag("input")[0];
1153     input.addEventListener("change", updateCalendarStatus, false);
1154   }
1155 }
1156
1157 function initCalendars() {
1158   if (!document.body.hasClassName("popup"))
1159     initCalendarContactsSelector();
1160 }
1161
1162 window.addEventListener("load", initCalendars, false);