* Now attempting to fix the final few rendering problems in Internet Explorer. Testi...
[citadel.git] / webcit / static / wclib.js
1 /*
2  * $Id$
3  * Copyright 2005 - 2009 The Citadel Team
4  * Licensed under the GPL V3
5  *
6  * JavaScript function library for WebCit.
7  *
8  */
9
10
11 var browserType;
12 var room_is_trash = 0;
13
14 var currentlyExpandedFloor = null;
15 var roomlist = null;
16
17 var _switchToRoomList = "switch to room list";
18 var _switchToMenu = "switch to menu";
19
20 var currentDropTarget = null;
21
22 var supportsAddEventListener = (!!document.addEventListener);
23 var today = new Date();
24
25 var wc_log = "";
26 var is_ie6 = false;
27 if (document.all) {browserType = "ie"}
28 if (window.navigator.userAgent.toLowerCase().match("gecko")) {
29         browserType= "gecko";
30 }
31 var ns6=document.getElementById&&!document.all;
32 Event.observe(window, 'load', ToggleTaskDateOrNoDateActivate);
33 Event.observe(window, 'load', taskViewActivate);
34 Event.observe(window, 'load', fixbanner);
35 //document.observe("dom:loaded", setupPrefEngine);
36 document.observe("dom:loaded", setupIconBar);
37 function CtdlRandomString()  {
38         return((Math.random()+'').substr(3));
39 }
40 function strcmp ( str1, str2 ) {
41     // http://kevin.vanzonneveld.net
42     // +   original by: Waldo Malqui Silva
43     // +      input by: Steve Hilder
44     // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
45     // +    revised by: gorthaur
46     // *     example 1: strcmp( 'waldo', 'owald' );
47     // *     returns 1: 1
48     // *     example 2: strcmp( 'owald', 'waldo' );
49     // *     returns 2: -1
50  
51     return ( ( str1 == str2 ) ? 0 : ( ( str1 > str2 ) ? 1 : -1 ) );
52 }
53
54 function emptyElement(element) {
55   childNodes = element.childNodes;
56   for(var i=0; i<childNodes.length; i++) {
57     try {
58     element.removeChild(childNodes[i]);
59     } catch (e) {
60       WCLog(e+"|"+e.description);
61     }
62   }
63 }
64 /** Implements superior internet explorer 'extract all child text from element' feature'. Falls back on buggy, patent violating standardized method */
65 function getTextContent(element) {
66   if (element.textContent == undefined) {
67     return element.innerText;
68   }
69   return element.textContent;
70 }
71 /** Same reasons as above */
72 function setTextContent(element, textContent) {
73   if(element.textContent == undefined) {
74     element.innerText = textContent;
75   } else {
76   element.textContent = textContent;
77   }
78 }
79
80 // We love string tokenizers.
81 function extract_token(source_string, token_num, delimiter) {
82         var i = 0;
83         var extracted_string = source_string;
84
85         if (token_num > 0) {
86                 for (i=0; i<token_num; ++i) {
87                         var j = extracted_string.indexOf(delimiter);
88                         if (j >= 0) {
89                                 extracted_string = extracted_string.substr(j+1);
90                         }
91                 }
92         }
93
94         j = extracted_string.indexOf(delimiter);
95         if (j >= 0) {
96                 extracted_string = extracted_string.substr(0, j);
97         }
98
99         return extracted_string;
100 }
101
102 function CtdlSpawnContextMenu(event, source) {
103   // remove any existing menus
104   disintergrateContextMenus(null);
105   var x = event.clientX-10; // cut a few pixels out so our mouseout works right
106   var y = event.clientY-10;
107   var contextDIV = document.createElement("div");
108   contextDIV.setAttribute("id", "ctdlContextMenu");
109   document.body.appendChild(contextDIV);
110   var sourceChildren = source.childNodes;
111   for(var j=0; j<sourceChildren.length; j++) {
112     contextDIV.appendChild(sourceChildren[j].cloneNode(true));
113   }
114   var leftRule = "left: "+x+"px;";
115   contextDIV.setAttribute("style", leftRule);
116   contextDIV.setAttribute("actual", leftRule);
117   contextDIV.style.top = y+"px";
118   contextDIV.style.display = "block";
119   $(contextDIV).observe('mouseout',disintergrateContextMenus);
120 }
121 function disintergrateContextMenus(event) {
122   var contextMenu = document.getElementById("ctdlContextMenu");
123   if (contextMenu) {
124     contextMenu.parentNode.removeChild(contextMenu);
125   }
126   Event.stopObserving(document,'click',disintergrateContextMenus);
127 }
128 // This code handles the popups for important-messages.
129 function hide_imsg_popup() {
130         if (browserType == "gecko") {
131                 document.poppedLayer = eval('document.getElementById(\'important_message\')');
132         }
133         else if (browserType == "ie") {
134                 document.poppedLayer = eval('document.all[\'important_message\']');
135         }
136         else {
137                 document.poppedLayer = eval('document.layers[\'`important_message\']');
138         }
139
140         document.poppedLayer.style.visibility = "hidden";
141 }
142
143
144 // This function activates the ajax-powered recipient autocompleters on the message entry screen.
145 function activate_entmsg_autocompleters() {
146         new Ajax.Autocompleter('cc_id', 'cc_name_choices', 'cc_autocomplete', {} );
147         new Ajax.Autocompleter('bcc_id', 'bcc_name_choices', 'bcc_autocomplete', {} );
148         new Ajax.Autocompleter('recp_id', 'recp_name_choices', 'recp_autocomplete', {} );
149 }
150
151 function setupIconBar() {
152   if (!document.getElementById("switch")) {
153       return;
154     }
155   _switchToRoomList = getTextContent(document.getElementById("rmlist_template"));
156   _switchToMenu = getTextContent(document.getElementById("mnlist_template"));
157   var switchSpan = document.getElementById("switch").firstChild;
158   if (switchSpan != null) {
159     setTextContent(switchSpan, _switchToRoomList);
160     $(switchSpan).observe('click', changeIconBarEvent);
161     var currentView = ctdlLocalPrefs.readPref("iconbar_view");
162     if (currentView != null) {
163       switchSpan.ctdlSwitchIconBarTo = currentView;
164       changeIconBar(switchSpan);
165     } else {
166       switchSpan.ctdlSwitchIconBarTo = "rooms";
167     }
168   }
169   var online_users = document.getElementById("online_users");
170   if (online_users.offsetParent != null && online_users.offsetTop > 0) {
171     new Ajax.PeriodicalUpdater('online_users', 'do_template?template=who_iconbar', {method: 'get', frequency: 30});
172   }
173 }
174 function changeIconBarEvent(event) {
175   changeIconBar(event.target);
176 }
177 function changeIconBar(target) {
178   var switchTo = target.ctdlSwitchIconBarTo;
179   WCLog("Changing to: " + switchTo);
180   ctdlLocalPrefs.setPref("iconbar_view", target.ctdlSwitchIconBarTo);  
181   if (switchTo == "rooms") {
182     switch_to_room_list();
183     setTextContent(target, _switchToMenu);
184     target.ctdlSwitchIconBarTo = "menu";
185   } else {
186     switch_to_menu_buttons();
187     setTextContent(target, _switchToRoomList);
188     target.ctdlSwitchIconBarTo = "rooms";
189   }
190 }
191 function switch_to_room_list() {
192   var roomlist = document.getElementById("roomlist");
193   var summary = document.getElementById("iconbar_menu");
194   if (!rooms || !floors || !roomlist) {
195     FillRooms(IconBarRoomList);
196   }
197   roomlist.className = roomlist.className.replace("hidden","");
198   summary.className += " hidden";
199 }
200
201 function switch_to_menu_buttons() {
202   if (roomlist != null) {
203     roomlist.className += "hidden";
204   }
205   var iconbar = document.getElementById("iconbar_menu");
206   iconbar.className = iconbar.className.replace("hidden","");
207   var roomlist = document.getElementById("roomlist");
208   roomlist.className += " hidden";
209 }
210
211 function GenericTreeRoomList(roomlist) {
212   var currentExpanded = ctdlLocalPrefs.readPref("rooms_expanded");
213   var curRoomName = "";
214   if (document.getElementById("rmname")) {
215     curRoomName = getTextContent(document.getElementById("rmname"));
216   }
217   currentDropTargets = new Array();
218   var iconbar = document.getElementById("iconbar");
219   var ul = document.createElement("ul");
220   roomlist.appendChild(ul);
221   // Add mailbox, because they are special
222   var mailboxLI = document.createElement("li");
223   ul.appendChild(mailboxLI);
224   var mailboxSPAN = document.createElement("span");
225   var _mailbox = getTextContent(document.getElementById("mbox_template"));
226   mailboxSPAN.appendChild(document.createTextNode(_mailbox));
227   $(mailboxSPAN).observe('click', expandFloorEvent);
228   mailboxLI.appendChild(mailboxSPAN);
229   mailboxLI.className = "floor";
230   var mailboxUL = document.createElement("ul");
231   mailboxLI.appendChild(mailboxUL);
232   var mailboxRooms = GetMailboxRooms();
233   for(var i=0; i<mailboxRooms.length; i++) {
234           var room = mailboxRooms[i];
235           currentDropTargets.push(addRoomToList(mailboxUL, room, curRoomName));
236   }
237   if (currentExpanded != null && currentExpanded == _mailbox ) {
238           expandFloor(mailboxSPAN);
239   }
240   for(var a=0; a<floors.length; a++) {
241           var floor = floors[a];
242           var floornum = floor[0];
243     
244           if (floornum != -1)
245           {
246
247                   var name = floor[1];
248                   var floorLI = document.createElement("li");
249                   ul.appendChild(floorLI);
250                   var floorSPAN = document.createElement("span");
251                   floorSPAN.appendChild(document.createTextNode(name));
252                   $(floorSPAN).observe('click', expandFloorEvent);
253                   floorLI.appendChild(floorSPAN);
254                   floorLI.className = "floor";
255                   var floorUL = document.createElement("ul");
256                   floorLI.appendChild(floorUL);
257                   var roomsForFloor = GetRoomsByFloorNum(floornum);
258                   for(var b=0; b<roomsForFloor.length; b++) {
259                           var room = roomsForFloor[b];
260                           currentDropTargets.push(addRoomToList(floorUL, room, curRoomName));
261                   }
262                   if (currentExpanded != null && currentExpanded == name) {
263                           expandFloor(floorSPAN);
264                   }
265     }
266   }
267 }
268 function IconBarRoomList() {
269   roomlist = document.getElementById("roomlist");
270   GenericTreeRoomList(roomlist);
271 }
272 function KNRoomsRoomList() {
273   roomlist = document.getElementById("roomlist_knrooms");
274   GenericTreeRoomList(roomlist);
275 }
276
277 function addRoomToList(floorUL,room, roomToEmphasize) {
278   var roomName = room[RN_ROOM_NAME];
279   var flag = room[RN_ROOM_FLAG];
280   var curView = room[RN_CUR_VIEW];
281   var view = room[RN_DEF_VIEW];
282   var raflags = room[RN_RAFLAGS];
283   var isMailBox = ((flag & QR_MAILBOX) == QR_MAILBOX);
284   var hasNewMsgs = ((raflags & UA_HASNEWMSGS) == UA_HASNEWMSGS);
285   var roomLI = document.createElement("li");
286   var roomA = document.createElement("a");
287   roomA.setAttribute("href","dotgoto?room="+roomName);
288   roomA.appendChild(document.createTextNode(roomName));
289   roomLI.appendChild(roomA);
290   floorUL.appendChild(roomLI);
291   var className = "room ";
292   if (view == VIEW_MAILBOX) {
293     className += "room-private"
294   } else if (view == VIEW_ADDRESSBOOK) {
295     className += "room-addr";
296   } else if (view == VIEW_CALENDAR || view == VIEW_CALBRIEF) {
297     className += "room-cal";
298   } else if (view == VIEW_TASKS) {
299     className += "room-tasks";
300   } else if (view == VIEW_NOTES) {
301     className += "room-notes";
302   } else {
303     className += "room-chat";
304   }
305   if (hasNewMsgs) {
306     className += " room-newmsgs";
307   }
308   if (roomName == roomToEmphasize) {
309     className += " room-emphasized";
310   }
311   roomLI.setAttribute("class", className);
312   roomA.dropTarget = true;
313   roomA.dropHandler = roomListDropHandler;
314   return roomLI;
315 }
316
317 function roomListDropHandler(target, dropped) {
318   if (dropped.getAttribute("citadel:msgid")) {
319     var room = getTextContent(target);
320     var msgIds = "";
321     for(msgId in currentlyMarkedRows) { //defined in summaryview.js
322       msgIds += ","+msgId;
323     }
324     var mvCommand = "g_cmd=MOVE " + msgIds + "|"+room+"|0";
325     new Ajax.Request('ajax_servcmd', {
326       method: 'post',
327           parameters: mvCommand,
328           onComplete: deleteAllMarkedRows()});
329     } 
330 }
331 function expandFloorEvent(event) {
332   expandFloor(event.target);
333 }
334 function expandFloor(target) {
335   if (target.nodeName.toLowerCase() != "span") {
336     return; // ignore clicks on child UL
337   }
338   ctdlLocalPrefs.setPref("rooms_expanded", target.firstChild.nodeValue);
339   var parentUL = target.parentNode;
340   if (currentlyExpandedFloor != null) {
341     currentlyExpandedFloor.className = currentlyExpandedFloor.className.replace("floor-expanded","");
342   }
343   parentUL.className = parentUL.className + " floor-expanded";
344   currentlyExpandedFloor = parentUL;
345 }
346
347 // These functions handle moving sticky notes around the screen by dragging them
348
349 var uid_of_note_being_dragged = 0;
350 var saved_cursor_style = 'default';
351 var note_was_dragged = 0;
352
353 function NotesDragMouseUp(evt) {
354         document.onmouseup = null;
355         document.onmousemove = null;
356         if (document.layers) {
357                 document.releaseEvents(Event.MOUSEUP | Event.MOUSEMOVE);
358         }
359
360         d = $('note-' + uid_of_note_being_dragged);
361         d.style.cursor = saved_cursor_style;
362
363         // If any motion actually occurred, submit an ajax http call to record it to the server
364         if (note_was_dragged > 0) {
365                 p = 'note_uid=' + uid_of_note_being_dragged
366                         + '&left=' + d.style.left
367                         + '&top=' + d.style.top
368                         + '&r=' + CtdlRandomString();
369                 new Ajax.Request(
370                         'ajax_update_note',
371                         {
372                                 method: 'post',
373                                 parameters: p
374                         }
375                 );
376         }
377
378         uid_of_note_being_dragged = '';
379         return true;
380 }
381
382 function NotesDragMouseMove(evt) {
383         x = (ns6 ? evt.clientX : event.clientX);
384         x_increment = x - saved_x;
385         y = (ns6 ? evt.clientY : event.clientY);
386         y_increment = y - saved_y;
387
388         // Move the div
389         d = $('note-' + uid_of_note_being_dragged);
390
391         divTop = parseInt(d.style.top);
392         divLeft = parseInt(d.style.left);
393
394         d.style.top = (divTop + y_increment) + 'px';
395         d.style.left = (divLeft + x_increment) + 'px';
396
397         saved_x = x;
398         saved_y = y;
399         note_was_dragged = 1;
400         return true;
401 }
402
403
404 function NotesDragMouseDown(evt, uid) {
405         saved_x = (ns6 ? evt.clientX : event.clientX);
406         saved_y = (ns6 ? evt.clientY : event.clientY);
407         document.onmouseup = NotesDragMouseUp;
408         document.onmousemove = NotesDragMouseMove;
409         if (document.layers) {
410                 document.captureEvents(Event.MOUSEUP | Event.MOUSEMOVE);
411         }
412         uid_of_note_being_dragged = uid;
413         d = $('note-' + uid_of_note_being_dragged);
414         saved_cursor_style = d.style.cursor;
415         d.style.cursor = 'move';
416         return false;           // disable the default action
417 }
418
419
420 // Called when the user clicks on the palette icon of a sticky note to change its color.
421 // It toggles the color selector visible or invisible.
422
423 function NotesClickPalette(evt, uid) {
424         uid_of_note_being_colored = uid;
425         d = $('palette-' + uid_of_note_being_colored);
426
427         if (d.style.display) {
428                 if (d.style.display == 'none') {
429                         d.style.display = 'block';
430                 }
431                 else {
432                         d.style.display = 'none';
433                 }
434         }
435         else {
436                 d.style.display = 'block';
437         }
438
439         return true;
440 }
441
442
443 // Called when the user clicks on one of the colors in an open color selector.
444 // Sets the desired color and then closes the color selector.
445
446 function NotesClickColor(evt, uid, red, green, blue, notecolor, titlecolor) {
447         uid_of_note_being_colored = uid;
448         palette_button = $('palette-' + uid_of_note_being_colored);
449         note_div = $('note-' + uid_of_note_being_colored);
450         titlebar_div = $('titlebar-' + uid_of_note_being_colored);
451
452         // alert('FIXME red=' + red + ' green=' + green + ' blue=' + blue);
453
454         note_div.style.backgroundColor = notecolor;
455         titlebar_div.style.backgroundColor = titlecolor;
456         palette_button.style.display = 'none';
457
458         // submit an ajax http call to record it to the server
459         p = 'note_uid=' + uid_of_note_being_colored
460                 + '&red=' + red
461                 + '&green=' + green
462                 + '&blue=' + blue
463                 + '&r=' + CtdlRandomString();
464         new Ajax.Request(
465                 'ajax_update_note',
466                 {
467                         method: 'post',
468                         parameters: p
469                 }
470         );
471 }
472
473
474
475
476 // These functions handle resizing sticky notes by dragging the resize handle
477
478 var uid_of_note_being_resized = 0;
479 var saved_cursor_style = 'default';
480 var note_was_resized = 0;
481
482 function NotesResizeMouseUp(evt) {
483         document.onmouseup = null;
484         document.onmousemove = null;
485         if (document.layers) {
486                 document.releaseEvents(Event.MOUSEUP | Event.MOUSEMOVE);
487         }
488
489         d = $('note-' + uid_of_note_being_resized);
490         d.style.cursor = saved_cursor_style;
491
492         // If any motion actually occurred, submit an ajax http call to record it to the server
493         if (note_was_resized > 0) {
494                 p = 'note_uid=' + uid_of_note_being_resized
495                         + '&width=' + d.style.width
496                         + '&height=' + d.style.height
497                         + '&r=' + CtdlRandomString();
498                 new Ajax.Request(
499                         'ajax_update_note',
500                         {
501                                 method: 'post',
502                                 parameters: p
503                         }
504                 );
505         }
506
507         uid_of_note_being_resized = '';
508         return false;           // disable the default action
509 }
510
511 function NotesResizeMouseMove(evt) {
512         x = (ns6 ? evt.clientX : event.clientX);
513         x_increment = x - saved_x;
514         y = (ns6 ? evt.clientY : event.clientY);
515         y_increment = y - saved_y;
516
517         // Move the div
518         d = $('note-' + uid_of_note_being_resized);
519
520         divTop = parseInt(d.style.height);
521         divLeft = parseInt(d.style.width);
522
523         newHeight = divTop + y_increment;
524         if (newHeight < 50) newHeight = 50;
525
526         newWidth = divLeft + x_increment;
527         if (newWidth < 50) newWidth = 50;
528
529         d.style.height = newHeight + 'px';
530         d.style.width = newWidth + 'px';
531
532         saved_x = x;
533         saved_y = y;
534         note_was_resized = 1;
535         return false;           // disable the default action
536 }
537
538
539 function NotesResizeMouseDown(evt, uid) {
540         saved_x = (ns6 ? evt.clientX : event.clientX);
541         saved_y = (ns6 ? evt.clientY : event.clientY);
542         document.onmouseup = NotesResizeMouseUp;
543         document.onmousemove = NotesResizeMouseMove;
544         if (document.layers) {
545                 document.captureEvents(Event.MOUSEUP | Event.MOUSEMOVE);
546         }
547         uid_of_note_being_resized = uid;
548         d = $('note-' + uid_of_note_being_resized);
549         saved_cursor_style = d.style.cursor;
550         d.style.cursor = 'move';
551         return false;           // disable the default action
552 }
553
554
555 function DeleteStickyNote(evt, uid, confirmation_prompt) {
556         uid_of_note_being_deleted = uid;
557         d = $('note-' + uid_of_note_being_deleted);
558
559         if (confirm(confirmation_prompt)) {
560                 new Effect.Puff(d);
561
562                 // submit an ajax http call to delete it on the server
563                 p = 'note_uid=' + uid_of_note_being_deleted
564                         + '&deletenote=yes'
565                         + '&r=' + CtdlRandomString();
566                 new Ajax.Request(
567                         'ajax_update_note',
568                         {
569                                 method: 'post',
570                                 parameters: p
571                         }
572                 );
573         }
574 }
575
576 function ctdl_ts_getInnerText(el) {
577         if (typeof el == "string") return el;
578         if (typeof el == "undefined") { return el };
579         if (el.innerText) return el.innerText;  //Not needed but it is faster
580         var str = "";
581         
582         var cs = el.childNodes;
583         var l = cs.length;
584         for (var i = 0; i < l; i++) {
585                 switch (cs[i].nodeType) {
586                         case 1: //ELEMENT_NODE
587                                 str += ts_getInnerText(cs[i]);
588                                 break;
589                         case 3: //TEXT_NODE
590                                 str += cs[i].nodeValue;
591                                 break;
592                 }
593         }
594         return str;
595 }
596
597
598 // Place a gradient loadscreen on an element, e.g to use before Ajax.updater
599 function CtdlLoadScreen(elementid) {
600 var elem = document.getElementById(elementid);
601 elem.innerHTML = "<div align=center><br><table border=0 cellpadding=10 bgcolor=\"#ffffff\"><tr><td><img src=\"static/throbber.gif\" /><font color=\"#AAAAAA\">&nbsp;&nbsp;Loading....</font></td></tr></table><br /></div>";
602 }
603
604
605
606 // Pop open the address book (target_input is the INPUT field to populate)
607
608 function PopOpenAddressBook(target_input) {
609         $('address_book_popup').style.display = 'block';
610         p = 'target_input=' + target_input + '&r=' + CtdlRandomString();
611         new Ajax.Updater(
612                 'address_book_popup_middle_div',
613                 'display_address_book_middle_div',
614                 {
615                         method: 'get',
616                         parameters: p,
617                         evalScripts: true
618                 }
619         );
620 }
621
622 function PopulateAddressBookInnerDiv(which_addr_book, target_input) {
623         $('address_book_inner_div').innerHTML = "<div align=center><br><table border=0 cellpadding=10 bgcolor=\"#ffffff\"><tr><td><img src=\"static/throbber.gif\" /><font color=\"#AAAAAA\">&nbsp;&nbsp;Loading....</font></td></tr></table><br /></div>";
624         p = 'which_addr_book=' + which_addr_book
625           + '&target_input=' + target_input
626           + '&r=' + CtdlRandomString();
627         new Ajax.Updater(
628                 'address_book_inner_div',
629                 'display_address_book_inner_div',
630                 {
631                         method: 'get',
632                         parameters: p
633                 }
634         );
635 }
636
637 // What happens when a contact is selected from the address book popup
638 // (populate the specified target)
639
640 function AddContactsToTarget(target, whichaddr) {
641         while (whichaddr.selectedIndex != -1) {
642                 if (target.value.length > 0) {
643                         target.value = target.value + ', ';
644                 }
645                 target.value = target.value + whichaddr.value;
646                 whichaddr.options[whichaddr.selectedIndex].selected = false;
647         }
648 }
649
650 // Respond to a meeting invitation
651 function RespondToInvitation(question_divname, title_divname, msgnum, cal_partnum, sc) {
652         p = 'msgnum=' + msgnum + '&cal_partnum=' + cal_partnum + '&sc=' + sc ;
653         new Ajax.Updater(title_divname, 'respond_to_request', { method: 'post', parameters: p } );
654         Effect.Fade(question_divname, { duration: 0.5 });
655 }
656
657 // Handle a received RSVP
658 function HandleRSVP(question_divname, title_divname, msgnum, cal_partnum, sc) {
659         p = 'msgnum=' + msgnum + '&cal_partnum=' + cal_partnum + '&sc=' + sc ;
660         new Ajax.Updater(title_divname, 'handle_rsvp', { method: 'post', parameters: p } );
661         Effect.Fade(question_divname, { duration: 0.5 });
662 }
663 /* var fakeMouse = document.createEvent("MouseEvents");
664  fakeMouse.initMouseEvent("click", true, true, window, 
665    0,0,0,0,0, false, false, false, false, 0, null); */
666 // TODO: Collapse into one function
667 function toggleTaskDtStart(event) {
668         var checkBox = $('nodtstart');
669         var checkBoxTime = $('dtstart_time_assoc');
670         var dtstart = document.getElementById("dtstart");
671         var dtstart_date = document.getElementById("dtstart_date");
672         var dtstart_time = document.getElementById("dtstart_time");
673         if (checkBox.checked) {
674                 dtstart_date.style.visibility = "hidden";
675                 dtstart_time.style.visibility = "hidden";
676         } else {
677                 if (checkBoxTime.checked) {
678                         dtstart_time.style.visibility = "visible";
679                 } else {
680                         dtstart_time.style.visibility = "hidden";
681                 }
682                 dtstart_date.style.visibility = "visible";
683                 if (dtstart.value.length == 0)
684                         dtstart.dpck._initCurrentDate();
685         }
686 }
687 function toggleTaskDue(event) {
688         var checkBox = $('nodue');
689         var checkBoxTime = $('due_time_assoc');
690         var due = document.getElementById("due");
691         var due_date = document.getElementById("due_date");
692         var due_time = document.getElementById("due_time");
693         if (checkBox.checked) {
694                 due_date.style.visibility = "hidden";
695                 due_time.style.visibility = "hidden";
696         } else {
697                 if (checkBoxTime.checked) {
698                         due_time.style.visibility = "visible";
699                 } else {
700                         due_time.style.visibility = "hidden";
701                 }
702                 due_date.style.visibility = "visible";
703                 if (due.value.length == 0)
704                         due.dpck._initCurrentDate();
705         }
706 }
707 function ToggleTaskDateOrNoDateActivate(event) {
708         var dtstart = document.getElementById("nodtstart");
709         if (dtstart != null) {
710                 toggleTaskDtStart(null);
711                 toggleTaskDue(null);
712                 $('nodtstart').observe('click', toggleTaskDtStart);
713                 $('dtstart_time_assoc').observe('click', toggleTaskDtStart);
714                 $('nodue').observe('click', toggleTaskDue);
715                 $('due_time_assoc').observe('click', toggleTaskDue);
716         } 
717 }
718 function TaskViewGatherCategoriesFromTable() {
719         var table = $('taskview');
720         
721 }
722 function attachDatePicker(relative) {
723         var dpck = new DatePicker({
724         relative: relative,
725               language: 'en', //wclang.substr(0,2),
726               disableFutureDate: false,
727               dateFormat: [ ["yyyy", "mm", "dd"], "-"],
728               showDuration: 0.2
729         });
730         document.getElementById(relative).dpck = dpck; // attach a ref to it
731 }
732 function eventEditAllDay() {
733         var allDayCheck = document.getElementById("alldayevent");
734         var dtend_time = document.getElementById("dtend_time");
735         var dtstart_time = document.getElementById("dtstart_time");
736         if(allDayCheck.checked) {
737                 dtstart_time.style.visibility = "hidden";
738                 dtend_time.style.visibility = "hidden";
739         } else {
740                 dtstart_time.style.visibility = "visible";
741                 dtend_time.style.visibility = "visible";
742         }
743 }
744
745 // Functions which handle show/hide of various elements in the recurrence editor
746
747 function RecurrenceShowHide() {
748
749         if ($('is_recur').checked) {
750                 $('rrule_div').style.display = 'block';
751         }
752         else {
753                 $('rrule_div').style.display = 'none';
754         }
755
756         if ($('freq_selector').selectedIndex == 4) {
757                 $('weekday_selector').style.display = 'block';
758         }
759         else {
760                 $('weekday_selector').style.display = 'none';
761         }
762
763         if ($('freq_selector').selectedIndex == 5) {
764                 $('monthday_selector').style.display = 'block';
765         }
766         else {
767                 $('monthday_selector').style.display = 'none';
768         }
769
770         if ($('rrend_count').checked) {
771                 $('rrcount').disabled = false;
772         }
773         else {
774                 $('rrcount').disabled = true;
775         }
776
777         if ($('rrend_until').checked) {
778                 $('rruntil').disabled = false;
779         }
780         else {
781                 $('rruntil').disabled = true;
782         }
783
784         if ($('rrmonthtype_mday').checked) {
785                 $('rrmday').disabled = false;
786         }
787         else {
788                 $('rrmday').disabled = true;
789         }
790
791         if ($('rrmonthtype_wday').checked) {
792                 $('rrmweek').disabled = false;
793                 $('rrmweekday').disabled = false;
794         }
795         else {
796                 $('rrmweek').disabled = true;
797                 $('rrmweekday').disabled = true;
798         }
799
800         if ($('freq_selector').selectedIndex == 6) {
801                 $('yearday_selector').style.display = 'block';
802         }
803         else {
804                 $('yearday_selector').style.display = 'none';
805         }
806
807         $('ymday').innerHTML = 'XXXX-' + $('dtstart').value.substr(5);
808         $('rrmday').innerHTML = $('dtstart').value.substr(8);
809
810         if ($('rryeartype_ywday').checked) {
811                 $('rrymweek').disabled = false;
812                 $('rrymweekday').disabled = false;
813                 $('rrymonth').disabled = false;
814         }
815         else {
816                 $('rrymweek').disabled = true;
817                 $('rrymweekday').disabled = true;
818                 $('rrymonth').disabled = true;
819         }
820
821 }
822
823
824 // Enable or disable the 'check attendee availability' button depending on whether
825 // the attendees list is empty
826 function EnableOrDisableCheckButton()
827 {
828         if ($('attendees_box').value.length == 0) {
829                 $('check_button').disabled = true;
830         }
831         else {
832                 $('check_button').disabled = false;
833         }
834 }
835
836
837
838
839 function launchChat(event) {
840 window.open('chat', 'ctdl_chat_window', 'toolbar=no,location=no,directories=no,copyhistory=no,status=no,scrollbars=yes,resizable=yes');
841 }
842 // logger
843 function WCLog(msg) {
844   if (!!window.console && !!console.log) {
845     console.log(msg);
846   } else if (!!window.opera && !!opera.postError) {
847     opera.postError(msg);
848   } else {
849     wc_log += msg + "\r\n";
850   }
851 }
852
853 function fixMissingCSSTable(elems) {
854  if (elems[0] == null || elems[1] == null) {
855     return;
856   }
857   if (elems[0].getStyle("display") != "table-cell") {
858     var parentNode = elems[0].parentNode;
859     var table = document.createElement("table");
860     table.style.width = "100%";
861     var tbody = document.createElement("tbody");
862     table.appendChild(tbody);
863     var tr = document.createElement("tr");
864     tbody.appendChild(tr);
865     parentNode.appendChild(table);
866     for(var i=0; i<elems.length; i++) {
867       parentNode.removeChild(elems[i]);
868       var td = document.createElement("td");
869       td.appendChild(elems[i]);
870       tr.appendChild(td);
871     }
872   }
873 }
874 function fixbanner() {
875   // Use prototype api methods here
876   var elems = [$('room_banner'),$('actiondiv')];
877   fixMissingCSSTable(elems);
878   if (!is_ie6) {
879     Event.observe(window, 'resize', makeContentScrollable);
880     makeContentScrollable();
881   }
882 }
883 function makeContentScrollable() {
884 if (document.getElementById("banner")
885       && document.getElementById("content") 
886       && !document.getElementById("summary_view")) {
887   WCLog("makeContentScrollable");
888     document.body.style.overflowY="hidden";
889     var global = $("global");
890     global.className += "scrollable";
891     var content = document.getElementById("content");
892     var banner = document.getElementById("banner");
893     var bannerHeight = banner.offsetHeight;
894     banner.style.width="100%";
895     var bannerPercent = (bannerHeight/document.viewport.getHeight())*100;
896     //banner.style.height=bannerPercent+"%";
897     content.style.overflowY="scroll";
898     //content.style.top=bannerPercent+"%";
899     content.style.height=(100-bannerPercent)+"%";
900     content.style.right="0px";
901   }
902 }
903 function fixOffsetBanner() {
904   var banner = document.getElementById("banner");
905   if (banner.offsetLeft > 0) {
906     var viewportWidth = document.viewport.getWidth();
907     var iconbarWidth = document.getElementById("iconbar").offsetWidth;
908     var contentDiv = document.getElementById("content");
909     var newContentWidth = viewportWidth-iconbarWidth;
910     contentDiv.style.width = newContentWidth+"px";
911   }
912 }
913
914 function RefreshSMTPqueueDisplay() {
915         new Ajax.Updater('smtpqueue_inner_div',
916         'display_smtpqueue_inner_div', { method: 'get',
917                 parameters: Math.random() } );
918 }
919
920 function DeleteSMTPqueueMsg(msgnum1, msgnum2) {
921         new Ajax.Request(
922                 'ajax_servcmd', {
923                         method: 'post',
924                         parameters: 'g_cmd=DELE ' + msgnum1 + ',' + msgnum2,
925                         onComplete: RefreshSMTPqueueDisplay()
926                 }
927         );
928 }