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