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