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