4 // JavaScript function library for WebCit.
10 var room_is_trash = 0;
12 var currentlyExpandedFloor = null;
15 var _switchToRoomList = "switch to room list";
16 var _switchToMenu = "switch to menu";
18 var currentDropTarget = null;
20 var supportsAddEventListener = (!!document.addEventListener);
21 var today = new Date();
23 if (document.all) {browserType = "ie"}
24 if (window.navigator.userAgent.toLowerCase().match("gecko")) {
27 var ns6=document.getElementById&&!document.all;
28 Event.observe(window, 'load', ToggleTaskDateOrNoDateActivate);
29 Event.observe(window, 'load', taskViewActivate);
30 //document.observe("dom:loaded", setupPrefEngine);
31 document.observe("dom:loaded", setupIconBar);
32 document.observe('dom:loaded', function() { if (!!document.getElementById("ib_chat_launch")) { $('ib_chat_launch').observe('click', launchChat); } });
33 function CtdlRandomString() {
34 return((Math.random()+'').substr(3));
37 function emptyElement(element) {
38 childNodes = element.childNodes;
39 for(var i=0; i<childNodes.length; i++) {
40 element.removeChild(childNodes[i]);
43 /** Implements superior internet explorer 'extract all child text from element' feature'. Falls back on buggy, patent violating standardized method */
44 function getTextContent(element) {
45 if (element.textContent == undefined) {
46 return element.innerText;
48 return element.textContent;
50 /** Same reasons as above */
51 function setTextContent(element, textContent) {
52 if(element.textContent == undefined) {
53 element.innerText = textContent;
55 element.textContent = textContent;
59 // We love string tokenizers.
60 function extract_token(source_string, token_num, delimiter) {
62 var extracted_string = source_string;
65 for (i=0; i<token_num; ++i) {
66 var j = extracted_string.indexOf(delimiter);
68 extracted_string = extracted_string.substr(j+1);
73 j = extracted_string.indexOf(delimiter);
75 extracted_string = extracted_string.substr(0, j);
78 return extracted_string;
81 function CtdlSpawnContextMenu(event, source) {
82 // remove any existing menus
83 disintergrateContextMenus(null);
84 var x = event.clientX-10; // cut a few pixels out so our mouseout works right
85 var y = event.clientY-10;
86 var contextDIV = document.createElement("div");
87 contextDIV.setAttribute("id", "ctdlContextMenu");
88 document.body.appendChild(contextDIV);
89 var sourceChildren = source.childNodes;
90 for(var j=0; j<sourceChildren.length; j++) {
91 contextDIV.appendChild(sourceChildren[j].cloneNode(true));
93 var leftRule = "left: "+x+"px;";
94 contextDIV.setAttribute("style", leftRule);
95 contextDIV.setAttribute("actual", leftRule);
96 contextDIV.style.top = y+"px";
97 contextDIV.style.display = "block";
98 $(contextDIV).observe('mouseout',disintergrateContextMenus);
100 function disintergrateContextMenus(event) {
101 var contextMenu = document.getElementById("ctdlContextMenu");
103 contextMenu.parentNode.removeChild(contextMenu);
105 Event.stopObserving(document,'click',disintergrateContextMenus);
107 // This code handles the popups for important-messages.
108 function hide_imsg_popup() {
109 if (browserType == "gecko") {
110 document.poppedLayer = eval('document.getElementById(\'important_message\')');
112 else if (browserType == "ie") {
113 document.poppedLayer = eval('document.all[\'important_message\']');
116 document.poppedLayer = eval('document.layers[\'`important_message\']');
119 document.poppedLayer.style.visibility = "hidden";
123 // This function activates the ajax-powered recipient autocompleters on the message entry screen.
124 function activate_entmsg_autocompleters() {
125 new Ajax.Autocompleter('cc_id', 'cc_name_choices', 'cc_autocomplete', {} );
126 new Ajax.Autocompleter('bcc_id', 'bcc_name_choices', 'bcc_autocomplete', {} );
127 new Ajax.Autocompleter('recp_id', 'recp_name_choices', 'recp_autocomplete', {} );
130 function setupIconBar() {
131 if (!document.getElementById("switch")) {
134 var switchSpan = document.getElementById("switch").firstChild;
135 if (switchSpan != null) {
136 setTextContent(switchSpan, _switchToRoomList);
137 $(switchSpan).observe('click', changeIconBarEvent);
138 var currentView = ctdlLocalPrefs.readPref("iconbar_view");
139 if (currentView != null) {
140 switchSpan.ctdlSwitchIconBarTo = currentView;
141 changeIconBar(switchSpan);
143 switchSpan.ctdlSwitchIconBarTo = "rooms";
146 var online_users = document.getElementById("online_users");
147 var ou_displayAs = online_users.style.display;
148 if (ou_displayAs != "none") {
149 new Ajax.PeriodicalUpdater('online_users', 'do_template?template=wholist_section', {method: 'get', frequency: 30});
152 function changeIconBarEvent(event) {
153 changeIconBar(event.target);
155 function changeIconBar(target) {
156 var switchTo = target.ctdlSwitchIconBarTo;
157 if (!!window.console) {
158 console.log("Changing to: " + switchTo);
160 ctdlLocalPrefs.setPref("iconbar_view", target.ctdlSwitchIconBarTo);
161 if (switchTo == "rooms") {
162 switch_to_room_list();
163 setTextContent(target, _switchToMenu);
164 target.ctdlSwitchIconBarTo = "menu";
166 switch_to_menu_buttons();
167 setTextContent(target, _switchToRoomList);
168 target.ctdlSwitchIconBarTo = "rooms";
171 function switch_to_room_list() {
172 var roomlist = document.getElementById("roomlist");
173 var summary = document.getElementById("iconbar_menu");
174 if (!rooms || !floors || !roomlist) {
175 FillRooms(IconBarRoomList);
177 roomlist.className = roomlist.className.replace("hidden","");
178 summary.className += " hidden";
181 function switch_to_menu_buttons() {
182 if (roomlist != null) {
183 roomlist.className += "hidden";
185 var iconbar = document.getElementById("iconbar_menu");
186 iconbar.className = iconbar.className.replace("hidden","");
187 var roomlist = document.getElementById("roomlist");
188 roomlist.className += " hidden";
190 function IconBarRoomList() {
191 var currentExpanded = ctdlLocalPrefs.readPref("rooms_expanded");
192 currentDropTargets = new Array();
193 var iconbar = document.getElementById("iconbar");
194 roomlist = document.getElementById("roomlist");
195 var ul = document.createElement("ul");
196 roomlist.appendChild(ul);
197 // Add mailbox, because they are special
198 var mailboxLI = document.createElement("li");
199 ul.appendChild(mailboxLI);
200 var mailboxSPAN = document.createElement("span");
201 mailboxSPAN.appendChild(document.createTextNode("Mailbox"));
202 $(mailboxSPAN).observe('click', expandFloorEvent);
203 mailboxLI.appendChild(mailboxSPAN);
204 mailboxLI.setAttribute("class", "floor");
205 var mailboxUL = document.createElement("ul");
206 mailboxLI.appendChild(mailboxUL);
207 var mailboxRooms = GetMailboxRooms();
208 for(var i=0; i<mailboxRooms.length; i++) {
209 var room = mailboxRooms[i];
210 currentDropTargets.push(addRoomToList(mailboxUL, room));
212 if (currentExpanded != null && currentExpanded == "Mailbox") {
213 expandFloor(mailboxSPAN);
215 for(var a=0; a<floors.length; a++) {
216 var floor = floors[a];
217 var floornum = floor[0];
219 var floorLI = document.createElement("li");
220 ul.appendChild(floorLI);
221 var floorSPAN = document.createElement("span");
222 floorSPAN.appendChild(document.createTextNode(name));
223 $(floorSPAN).observe('click', expandFloorEvent);
224 floorLI.appendChild(floorSPAN);
225 floorLI.setAttribute("class", "floor");
226 var floorUL = document.createElement("ul");
227 floorLI.appendChild(floorUL);
228 var roomsForFloor = GetRoomsByFloorNum(floornum);
229 for(var b=0; b<roomsForFloor.length; b++) {
230 var room = roomsForFloor[b];
231 currentDropTargets.push(addRoomToList(floorUL, room));
233 if (currentExpanded != null && currentExpanded == name) {
234 expandFloor(floorSPAN);
239 function addRoomToList(floorUL,room) {
240 var roomName = room[RN_ROOM_NAME];
241 var flag = room[RN_ROOM_FLAG];
242 var curView = room[RN_CUR_VIEW];
243 var view = room[RN_DEF_VIEW];
244 var isMailBox = ((flag & QR_MAILBOX) == QR_MAILBOX);
245 var hasNewMsgs = ((curView & UA_HASNEWMSGS) == UA_HASNEWMSGS);
246 var roomLI = document.createElement("li");
247 var roomA = document.createElement("a");
248 roomA.setAttribute("href","dotgoto?room="+roomName);
249 roomA.appendChild(document.createTextNode(roomName));
250 roomLI.appendChild(roomA);
251 floorUL.appendChild(roomLI);
252 var className = "room ";
253 if (view == VIEW_MAILBOX) {
254 className += "room-private"
255 } else if (view == VIEW_ADDRESSBOOK) {
256 className += "room-addr";
257 } else if (view == VIEW_CALENDAR || view == VIEW_CALBRIEF) {
258 className += "room-cal";
259 } else if (view == VIEW_TASKS) {
260 className += "room-tasks";
261 } else if (view == VIEW_NOTES) {
262 className += "room-notes";
264 className += "room-chat";
267 className += " room-newmsgs";
269 roomLI.setAttribute("class", className);
270 roomA.dropTarget = true;
271 roomA.dropHandler = roomListDropHandler;
275 function roomListDropHandler(target, dropped) {
276 if (dropped.ctdlMsgId) {
277 var room = getTextContent(target);
279 for(msgId in currentlyMarkedRows) { //defined in summaryview.js
282 var mvCommand = "g_cmd=MOVE " + msgIds + "|"+room+"|0";
283 new Ajax.Request('ajax_servcmd', {
285 parameters: mvCommand,
286 onComplete: deleteAllMarkedRows()});
289 function expandFloorEvent(event) {
290 expandFloor(event.target);
292 function expandFloor(target) {
293 if (target.nodeName.toLowerCase() != "span") {
294 return; // ignore clicks on child UL
296 ctdlLocalPrefs.setPref("rooms_expanded", target.firstChild.nodeValue);
297 var parentUL = target.parentNode;
298 if (currentlyExpandedFloor != null) {
299 currentlyExpandedFloor.className = currentlyExpandedFloor.className.replace("floor-expanded","");
301 parentUL.className = parentUL.className + " floor-expanded";
302 currentlyExpandedFloor = parentUL;
305 // These functions handle moving sticky notes around the screen by dragging them
307 var uid_of_note_being_dragged = 0;
308 var saved_cursor_style = 'default';
309 var note_was_dragged = 0;
311 function NotesDragMouseUp(evt) {
312 document.onmouseup = null;
313 document.onmousemove = null;
314 if (document.layers) {
315 document.releaseEvents(Event.MOUSEUP | Event.MOUSEMOVE);
318 d = $('note-' + uid_of_note_being_dragged);
319 d.style.cursor = saved_cursor_style;
321 // If any motion actually occurred, submit an ajax http call to record it to the server
322 if (note_was_dragged > 0) {
323 p = 'note_uid=' + uid_of_note_being_dragged
324 + '&left=' + d.style.left
325 + '&top=' + d.style.top
326 + '&r=' + CtdlRandomString();
336 uid_of_note_being_dragged = '';
340 function NotesDragMouseMove(evt) {
341 x = (ns6 ? evt.clientX : event.clientX);
342 x_increment = x - saved_x;
343 y = (ns6 ? evt.clientY : event.clientY);
344 y_increment = y - saved_y;
347 d = $('note-' + uid_of_note_being_dragged);
349 divTop = parseInt(d.style.top);
350 divLeft = parseInt(d.style.left);
352 d.style.top = (divTop + y_increment) + 'px';
353 d.style.left = (divLeft + x_increment) + 'px';
357 note_was_dragged = 1;
362 function NotesDragMouseDown(evt, uid) {
363 saved_x = (ns6 ? evt.clientX : event.clientX);
364 saved_y = (ns6 ? evt.clientY : event.clientY);
365 document.onmouseup = NotesDragMouseUp;
366 document.onmousemove = NotesDragMouseMove;
367 if (document.layers) {
368 document.captureEvents(Event.MOUSEUP | Event.MOUSEMOVE);
370 uid_of_note_being_dragged = uid;
371 d = $('note-' + uid_of_note_being_dragged);
372 saved_cursor_style = d.style.cursor;
373 d.style.cursor = 'move';
374 return false; // disable the default action
378 // Called when the user clicks on the palette icon of a sticky note to change its color.
379 // It toggles the color selector visible or invisible.
381 function NotesClickPalette(evt, uid) {
382 uid_of_note_being_colored = uid;
383 d = $('palette-' + uid_of_note_being_colored);
385 if (d.style.display) {
386 if (d.style.display == 'none') {
387 d.style.display = 'block';
390 d.style.display = 'none';
394 d.style.display = 'block';
401 // Called when the user clicks on one of the colors in an open color selector.
402 // Sets the desired color and then closes the color selector.
404 function NotesClickColor(evt, uid, red, green, blue, notecolor, titlecolor) {
405 uid_of_note_being_colored = uid;
406 palette_button = $('palette-' + uid_of_note_being_colored);
407 note_div = $('note-' + uid_of_note_being_colored);
408 titlebar_div = $('titlebar-' + uid_of_note_being_colored);
410 // alert('FIXME red=' + red + ' green=' + green + ' blue=' + blue);
412 note_div.style.backgroundColor = notecolor;
413 titlebar_div.style.backgroundColor = titlecolor;
414 palette_button.style.display = 'none';
416 // submit an ajax http call to record it to the server
417 p = 'note_uid=' + uid_of_note_being_colored
421 + '&r=' + CtdlRandomString();
434 // These functions handle resizing sticky notes by dragging the resize handle
436 var uid_of_note_being_resized = 0;
437 var saved_cursor_style = 'default';
438 var note_was_resized = 0;
440 function NotesResizeMouseUp(evt) {
441 document.onmouseup = null;
442 document.onmousemove = null;
443 if (document.layers) {
444 document.releaseEvents(Event.MOUSEUP | Event.MOUSEMOVE);
447 d = $('note-' + uid_of_note_being_resized);
448 d.style.cursor = saved_cursor_style;
450 // If any motion actually occurred, submit an ajax http call to record it to the server
451 if (note_was_resized > 0) {
452 p = 'note_uid=' + uid_of_note_being_resized
453 + '&width=' + d.style.width
454 + '&height=' + d.style.height
455 + '&r=' + CtdlRandomString();
465 uid_of_note_being_resized = '';
466 return false; // disable the default action
469 function NotesResizeMouseMove(evt) {
470 x = (ns6 ? evt.clientX : event.clientX);
471 x_increment = x - saved_x;
472 y = (ns6 ? evt.clientY : event.clientY);
473 y_increment = y - saved_y;
476 d = $('note-' + uid_of_note_being_resized);
478 divTop = parseInt(d.style.height);
479 divLeft = parseInt(d.style.width);
481 d.style.height = (divTop + y_increment) + 'px';
482 d.style.width = (divLeft + x_increment) + 'px';
486 note_was_resized = 1;
487 return false; // disable the default action
491 function NotesResizeMouseDown(evt, uid) {
492 saved_x = (ns6 ? evt.clientX : event.clientX);
493 saved_y = (ns6 ? evt.clientY : event.clientY);
494 document.onmouseup = NotesResizeMouseUp;
495 document.onmousemove = NotesResizeMouseMove;
496 if (document.layers) {
497 document.captureEvents(Event.MOUSEUP | Event.MOUSEMOVE);
499 uid_of_note_being_resized = uid;
500 d = $('note-' + uid_of_note_being_resized);
501 saved_cursor_style = d.style.cursor;
502 d.style.cursor = 'move';
503 return false; // disable the default action
507 function DeleteStickyNote(evt, uid, confirmation_prompt) {
508 uid_of_note_being_deleted = uid;
509 d = $('note-' + uid_of_note_being_deleted);
511 if (confirm(confirmation_prompt)) {
514 // submit an ajax http call to delete it on the server
515 p = 'note_uid=' + uid_of_note_being_deleted
517 + '&r=' + CtdlRandomString();
528 function ctdl_ts_getInnerText(el) {
529 if (typeof el == "string") return el;
530 if (typeof el == "undefined") { return el };
531 if (el.innerText) return el.innerText; //Not needed but it is faster
534 var cs = el.childNodes;
536 for (var i = 0; i < l; i++) {
537 switch (cs[i].nodeType) {
538 case 1: //ELEMENT_NODE
539 str += ts_getInnerText(cs[i]);
542 str += cs[i].nodeValue;
550 // Place a gradient loadscreen on an element, e.g to use before Ajax.updater
551 function CtdlLoadScreen(elementid) {
552 var elem = document.getElementById(elementid);
553 elem.innerHTML = "<div align=center><br><table border=0 cellpadding=10 bgcolor=\"#ffffff\"><tr><td><img src=\"static/throbber.gif\" /><font color=\"#AAAAAA\"> Loading....</font></td></tr></table><br /></div>";
558 // Pop open the address book (target_input is the INPUT field to populate)
560 function PopOpenAddressBook(target_input) {
561 $('address_book_popup').style.display = 'block';
562 p = 'target_input=' + target_input + '&r=' + CtdlRandomString();
564 'address_book_popup_middle_div',
565 'display_address_book_middle_div',
574 function PopulateAddressBookInnerDiv(which_addr_book, target_input) {
575 $('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\"> Loading....</font></td></tr></table><br /></div>";
576 p = 'which_addr_book=' + which_addr_book
577 + '&target_input=' + target_input
578 + '&r=' + CtdlRandomString();
580 'address_book_inner_div',
581 'display_address_book_inner_div',
589 // What happens when a contact is selected from the address book popup
590 // (populate the specified target)
592 function AddContactsToTarget(target, whichaddr) {
593 while (whichaddr.selectedIndex != -1) {
594 if (target.value.length > 0) {
595 target.value = target.value + ', ';
597 target.value = target.value + whichaddr.value;
598 whichaddr.options[whichaddr.selectedIndex].selected = false;
602 // Respond to a meeting invitation
603 function RespondToInvitation(question_divname, title_divname, msgnum, cal_partnum, sc) {
604 p = 'msgnum=' + msgnum + '&cal_partnum=' + cal_partnum + '&sc=' + sc ;
605 new Ajax.Updater(title_divname, 'respond_to_request', { method: 'post', parameters: p } );
606 Effect.Fade(question_divname, { duration: 0.5 });
609 // Handle a received RSVP
610 function HandleRSVP(question_divname, title_divname, msgnum, cal_partnum, sc) {
611 p = 'msgnum=' + msgnum + '&cal_partnum=' + cal_partnum + '&sc=' + sc ;
612 new Ajax.Updater(title_divname, 'handle_rsvp', { method: 'post', parameters: p } );
613 Effect.Fade(question_divname, { duration: 0.5 });
615 /* var fakeMouse = document.createEvent("MouseEvents");
616 fakeMouse.initMouseEvent("click", true, true, window,
617 0,0,0,0,0, false, false, false, false, 0, null); */
618 // TODO: Collapse into one function
619 function toggleTaskDtStart(event) {
620 var checkBox = $('nodtstart');
621 dtStart = document.getElementById("dtstart");
622 if (checkBox.checked) {
623 dtStart.disabled = true;
624 dtStart.style.textDecoration = "line-through";
626 dtStart.disabled = false;
627 dtStart.style.textDecoration = "";
628 if (dtStart.value.length == 0)
629 dtStart.dpck._initCurrentDate();
632 function toggleTaskDue(event) {
633 var checkBox = $('nodue');
634 dueField = document.getElementById("due");
635 if (checkBox.checked) {
636 dueField.disabled = true;
637 dueField.style.textDecoration = "line-through";
639 dueField.disabled = false;
640 dueField.style.textDecoration = "";
641 if (dueField.value.length == 0)
642 dueField.dpck._initCurrentDate();
645 function ToggleTaskDateOrNoDateActivate(event) {
646 var dtstart = document.getElementById("nodtstart");
647 if (dtstart != null) {
648 toggleTaskDtStart(null);
650 $('nodtstart').observe('click', toggleTaskDtStart);
651 $('nodue').observe('click', toggleTaskDue);
654 function TaskViewGatherCategoriesFromTable() {
655 var table = $('taskview');
658 function attachDatePicker(relative) {
659 var dpck = new DatePicker({
661 language: 'en', //wclang.substr(0,2),
662 disableFutureDate: false,
663 dateFormat: [ ["yyyy", "mm", "dd"], "-"],
666 document.getElementById(relative).dpck = dpck; // attach a ref to it
668 function eventEditAllDay() {
669 var allDayCheck = document.getElementById("alldayevent");
670 var dtend= document.getElementById("dtendcell");
671 if(allDayCheck.checked) {
672 //dtend.disabled = true;
673 dtend.style.textDecoration = "line-through";
675 //dtend_day.disabled = false;
676 dtend.style.textDecoration = "";
683 // Functions which handle show/hide of various elements in the recurrence editor
685 function RecurrenceShowHide() {
687 if ($('is_recur').checked) {
688 $('rrule_div').style.display = 'block';
691 $('rrule_div').style.display = 'none';
694 if ($('freq_selector').selectedIndex == 4) {
695 $('weekday_selector').style.display = 'block';
698 $('weekday_selector').style.display = 'none';
701 if ($('freq_selector').selectedIndex == 5) {
702 $('monthday_selector').style.display = 'block';
705 $('monthday_selector').style.display = 'none';
708 if ($('rrend_count').checked) {
709 $('rrcount').disabled = false;
712 $('rrcount').disabled = true;
715 if ($('rrend_until').checked) {
716 $('rruntil').disabled = false;
719 $('rruntil').disabled = true;
722 if ($('rrmonthtype_mday').checked) {
723 $('rrmday').disabled = false;
726 $('rrmday').disabled = true;
729 if ($('rrmonthtype_wday').checked) {
730 $('rrmweek').disabled = false;
731 $('rrmweekday').disabled = false;
734 $('rrmweek').disabled = true;
735 $('rrmweekday').disabled = true;
738 if ($('freq_selector').selectedIndex == 6) {
739 $('yearday_selector').style.display = 'block';
742 $('yearday_selector').style.display = 'none';
745 $('ymday').innerHTML = 'XXXX-' + $('dtstart').value.substr(5);
746 $('rrmday').innerHTML = $('dtstart').value.substr(8);
748 if ($('rryeartype_ywday').checked) {
749 $('rrymweek').disabled = false;
750 $('rrymweekday').disabled = false;
751 $('rrymonth').disabled = false;
754 $('rrymweek').disabled = true;
755 $('rrymweekday').disabled = true;
756 $('rrymonth').disabled = true;
760 function launchChat(event) {
761 window.open('chat', 'ctdl_chat_window', 'toolbar=no,location=no,directories=no,copyhistory=no,status=no,scrollbars=yes,resizable=yes');