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