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 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";
164 switch_to_menu_buttons();
165 setTextContent(target, _switchToRoomList);
166 target.ctdlSwitchIconBarTo = "rooms";
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);
175 roomlist.className = roomlist.className.replace("hidden","");
176 summary.className += " hidden";
179 function switch_to_menu_buttons() {
180 if (roomlist != null) {
181 roomlist.className += "hidden";
183 var iconbar = document.getElementById("iconbar_menu");
184 iconbar.className = iconbar.className.replace("hidden","");
185 var roomlist = document.getElementById("roomlist");
186 roomlist.className += " hidden";
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));
210 if (currentExpanded != null && currentExpanded == "Mailbox") {
211 expandFloor(mailboxSPAN);
213 for(var a=0; a<floors.length; a++) {
214 var floor = floors[a];
215 var floornum = floor[0];
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));
231 if (currentExpanded != null && currentExpanded == name) {
232 expandFloor(floorSPAN);
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";
262 className += "room-chat";
265 className += " room-newmsgs";
267 roomLI.setAttribute("class", className);
268 roomA.dropTarget = true;
269 roomA.dropHandler = roomListDropHandler;
273 function roomListDropHandler(target, dropped) {
274 if (dropped.ctdlMsgId) {
275 var room = getTextContent(target);
277 for(msgId in currentlyMarkedRows) { //defined in summaryview.js
280 var mvCommand = "g_cmd=MOVE " + msgIds + "|"+room+"|0";
281 new Ajax.Request('ajax_servcmd', {
283 parameters: mvCommand,
284 onComplete: deleteAllMarkedRows()});
287 function expandFloorEvent(event) {
288 expandFloor(event.target);
290 function expandFloor(target) {
291 if (target.nodeName.toLowerCase() != "span") {
292 return; // ignore clicks on child UL
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","");
299 parentUL.className = parentUL.className + " floor-expanded";
300 currentlyExpandedFloor = parentUL;
303 // These functions handle moving sticky notes around the screen by dragging them
305 var uid_of_note_being_dragged = 0;
306 var saved_cursor_style = 'default';
307 var note_was_dragged = 0;
309 function NotesDragMouseUp(evt) {
310 document.onmouseup = null;
311 document.onmousemove = null;
312 if (document.layers) {
313 document.releaseEvents(Event.MOUSEUP | Event.MOUSEMOVE);
316 d = $('note-' + uid_of_note_being_dragged);
317 d.style.cursor = saved_cursor_style;
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();
334 uid_of_note_being_dragged = '';
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;
345 d = $('note-' + uid_of_note_being_dragged);
347 divTop = parseInt(d.style.top);
348 divLeft = parseInt(d.style.left);
350 d.style.top = (divTop + y_increment) + 'px';
351 d.style.left = (divLeft + x_increment) + 'px';
355 note_was_dragged = 1;
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);
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
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.
379 function NotesClickPalette(evt, uid) {
380 uid_of_note_being_colored = uid;
381 d = $('palette-' + uid_of_note_being_colored);
383 if (d.style.display) {
384 if (d.style.display == 'none') {
385 d.style.display = 'block';
388 d.style.display = 'none';
392 d.style.display = 'block';
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.
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);
408 // alert('FIXME red=' + red + ' green=' + green + ' blue=' + blue);
410 note_div.style.backgroundColor = notecolor;
411 titlebar_div.style.backgroundColor = titlecolor;
412 palette_button.style.display = 'none';
414 // submit an ajax http call to record it to the server
415 p = 'note_uid=' + uid_of_note_being_colored
419 + '&r=' + CtdlRandomString();
432 // These functions handle resizing sticky notes by dragging the resize handle
434 var uid_of_note_being_resized = 0;
435 var saved_cursor_style = 'default';
436 var note_was_resized = 0;
438 function NotesResizeMouseUp(evt) {
439 document.onmouseup = null;
440 document.onmousemove = null;
441 if (document.layers) {
442 document.releaseEvents(Event.MOUSEUP | Event.MOUSEMOVE);
445 d = $('note-' + uid_of_note_being_resized);
446 d.style.cursor = saved_cursor_style;
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();
463 uid_of_note_being_resized = '';
464 return false; // disable the default action
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;
474 d = $('note-' + uid_of_note_being_resized);
476 divTop = parseInt(d.style.height);
477 divLeft = parseInt(d.style.width);
479 d.style.height = (divTop + y_increment) + 'px';
480 d.style.width = (divLeft + x_increment) + 'px';
484 note_was_resized = 1;
485 return false; // disable the default action
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);
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
505 function DeleteStickyNote(evt, uid, confirmation_prompt) {
506 uid_of_note_being_deleted = uid;
507 d = $('note-' + uid_of_note_being_deleted);
509 if (confirm(confirmation_prompt)) {
512 // submit an ajax http call to delete it on the server
513 p = 'note_uid=' + uid_of_note_being_deleted
515 + '&r=' + CtdlRandomString();
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
532 var cs = el.childNodes;
534 for (var i = 0; i < l; i++) {
535 switch (cs[i].nodeType) {
536 case 1: //ELEMENT_NODE
537 str += ts_getInnerText(cs[i]);
540 str += cs[i].nodeValue;
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\"> Loading....</font></td></tr></table><br /></div>";
556 // Pop open the address book (target_input is the INPUT field to populate)
558 function PopOpenAddressBook(target_input) {
559 $('address_book_popup').style.display = 'block';
560 p = 'target_input=' + target_input + '&r=' + CtdlRandomString();
562 'address_book_popup_middle_div',
563 'display_address_book_middle_div',
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\"> Loading....</font></td></tr></table><br /></div>";
574 p = 'which_addr_book=' + which_addr_book
575 + '&target_input=' + target_input
576 + '&r=' + CtdlRandomString();
578 'address_book_inner_div',
579 'display_address_book_inner_div',
587 // What happens when a contact is selected from the address book popup
588 // (populate the specified target)
590 function AddContactsToTarget(target, whichaddr) {
591 while (whichaddr.selectedIndex != -1) {
592 if (target.value.length > 0) {
593 target.value = target.value + ', ';
595 target.value = target.value + whichaddr.value;
596 whichaddr.options[whichaddr.selectedIndex].selected = false;
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 });
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 });
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";
624 dtStart.disabled = false;
625 dtStart.style.textDecoration = "";
626 if (dtStart.value.length == 0)
627 dtStart.dpck._initCurrentDate();
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";
637 dueField.disabled = false;
638 dueField.style.textDecoration = "";
639 if (dueField.value.length == 0)
640 dueField.dpck._initCurrentDate();
643 function ToggleTaskDateOrNoDateActivate(event) {
644 var dtstart = document.getElementById("nodtstart");
645 if (dtstart != null) {
646 toggleTaskDtStart(null);
648 $('nodtstart').observe('click', toggleTaskDtStart);
649 $('nodue').observe('click', toggleTaskDue);
652 function TaskViewGatherCategoriesFromTable() {
653 var table = $('taskview');
656 function attachDatePicker(relative) {
657 var dpck = new DatePicker({
659 language: 'en', //wclang.substr(0,2),
660 disableFutureDate: false,
661 dateFormat: [ ["yyyy", "mm", "dd"], "-"],
664 document.getElementById(relative).dpck = dpck; // attach a ref to it
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";
673 //dtend_day.disabled = false;
674 dtend.style.textDecoration = "";
678 // Functions which handle show/hide of various elements in the recurrence editor
680 function RecurrenceShowHide() {
682 if ($('is_recur').checked) {
683 $('rrule_div').style.display = 'block';
686 $('rrule_div').style.display = 'none';
689 if ($('freq_selector').selectedIndex == 4) {
690 $('weekday_selector').style.display = 'block';
693 $('weekday_selector').style.display = 'none';
696 if ($('freq_selector').selectedIndex == 5) {
697 $('monthday_selector').style.display = 'block';
700 $('monthday_selector').style.display = 'none';
703 if ($('rrend_count').checked) {
704 $('rrcount').disabled = false;
707 $('rrcount').disabled = true;
710 if ($('rrend_until').checked) {
711 $('rruntil').disabled = false;
714 $('rruntil').disabled = true;
717 if ($('rrmonthtype_mday').checked) {
718 $('rrmday').disabled = false;
721 $('rrmday').disabled = true;
724 if ($('rrmonthtype_wday').checked) {
725 $('rrmweek').disabled = false;
726 $('rrmweekday').disabled = false;
729 $('rrmweek').disabled = true;
730 $('rrmweekday').disabled = true;
733 if ($('freq_selector').selectedIndex == 6) {
734 $('yearday_selector').style.display = 'block';
737 $('yearday_selector').style.display = 'none';
740 $('ymday').innerHTML = 'XXXX-' + $('dtstart').value.substr(5);
741 $('rrmday').innerHTML = $('dtstart').value.substr(8);
743 if ($('rryeartype_ywday').checked) {
744 $('rrymweek').disabled = false;
745 $('rrymweekday').disabled = false;
746 $('rrymonth').disabled = false;
749 $('rrymweek').disabled = true;
750 $('rrymweekday').disabled = true;
751 $('rrymonth').disabled = true;
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');
759 function WCLog(msg) {
760 if (!!window.console && !!console.log) {
762 } else if (!!window.opera && !!opera.postError) {
763 opera.postError(msg);