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