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