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