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