]> code.citadel.org Git - citadel.git/blob - webcit/static/wclib.js
Found the cause of Bug #173 and added a comment to the
[citadel.git] / webcit / static / wclib.js
1 //
2 // $Id: wclib.js,v 625.2 2005/09/18 04:04:32 ajc Exp $
3 //
4 // JavaScript function library for WebCit.
5 //
6 //
7
8
9 var browserType;
10 var room_is_trash = 0;
11
12 if (document.layers) {browserType = "nn4"}
13 if (document.all) {browserType = "ie"}
14 if (window.navigator.userAgent.toLowerCase().match("gecko")) {
15         browserType= "gecko"
16 }
17
18 var ns6=document.getElementById&&!document.all;
19
20
21
22 // We love string tokenizers.
23 function extract_token(source_string, token_num, delimiter) {
24         var i = 0;
25         var extracted_string = source_string;
26
27         if (token_num > 0) {
28                 for (i=0; i<token_num; ++i) {
29                         var j = extracted_string.indexOf(delimiter);
30                         if (j >= 0) {
31                                 extracted_string = extracted_string.substr(j+1);
32                         }
33                 }
34         }
35
36         j = extracted_string.indexOf(delimiter);
37         if (j >= 0) {
38                 extracted_string = extracted_string.substr(0, j);
39         }
40
41         return extracted_string;
42 }
43
44
45
46 // This code handles the popups for important-messages.
47 function hide_imsg_popup() {
48         if (browserType == "gecko" )
49                 document.poppedLayer = eval('document.getElementById(\'important_message\')');
50         else if (browserType == "ie")
51                 document.poppedLayer = eval('document.all[\'important_message\']');
52         else
53                 document.poppedLayer = eval('document.layers[\'`important_message\']');
54
55         document.poppedLayer.style.visibility = "hidden";
56 }
57
58
59 // This function activates the ajax-powered recipient autocompleters on the message entry screen.
60 function activate_entmsg_autocompleters() {
61         new Ajax.Autocompleter('cc_id', 'cc_name_choices', 'cc_autocomplete', {} );
62         new Ajax.Autocompleter('bcc_id', 'bcc_name_choices', 'bcc_autocomplete', {} );
63         new Ajax.Autocompleter('recp_id', 'recp_name_choices', 'recp_autocomplete', {} );
64 }
65
66
67
68 // Toggle the icon bar between menu/roomlist...
69 var which_div_expanded = null;
70 var num_drop_targets = 0;
71 var drop_targets_elements = new Array();
72 var drop_targets_roomnames = new Array();
73
74 function switch_to_room_list() {
75         $('iconbar').innerHTML = $('iconbar').innerHTML.substr(0, $('iconbar').innerHTML.indexOf('switch'));
76         new Ajax.Updater('iconbar', 'iconbar_ajax_rooms', { method: 'get' } );
77 }
78
79 function expand_floor(floor_div) {
80         if (which_div_expanded != null) {
81                 if ($(which_div_expanded) != null) {
82                         $(which_div_expanded).style.display = 'none' ;
83                 }
84         }
85
86         // clicking on the already-expanded floor causes the whole list to collapse
87         if (which_div_expanded == floor_div) {
88                 which_div_expanded = null;
89
90                 // notify the server that no floors are expanded
91                 new Ajax.Request(
92                         'set_floordiv_expanded/-1', {
93                                 method: 'post'
94                         }
95                 );
96                 return true;
97         }
98
99         // expand the requested floor
100         $(floor_div).style.display = 'block';
101         which_div_expanded = floor_div;
102
103         // notify the server of which floor is expanded
104         new Ajax.Request(
105                 'set_floordiv_expanded/'+floor_div, {
106                         method: 'post'
107                 }
108         );
109 }
110
111 function switch_to_menu_buttons() {
112         which_div_expanded = null;
113         num_drop_targets = 0;
114         new Ajax.Updater('iconbar', 'iconbar_ajax_menu', { method: 'get' } );
115 }
116
117
118 // Static variables for mailbox view...
119 //
120 var CtdlNumMsgsSelected = 0;
121 var CtdlMsgsSelected = new Array();
122
123 // This gets called when you single click on a message in the mailbox view.
124 // We know that the element id of the table row will be the letter 'm' plus the message number.
125 //
126 function CtdlSingleClickMsg(evt, msgnum) {
127
128         // Clear the preview pane until we load the new message
129         $('preview_pane').innerHTML = '';
130
131         // De-select any messages that were already selected, *unless* the Ctrl key
132         // is being pressed, in which case the user wants multi select.
133         if (!evt.ctrlKey) {
134                 if (CtdlNumMsgsSelected > 0) {
135                         for (i=0; i<CtdlNumMsgsSelected; ++i) {
136                                 $('m'+CtdlMsgsSelected[i]).style.backgroundColor = '#fff';
137                                 $('m'+CtdlMsgsSelected[i]).style.color = '#000';
138                         }
139                         CtdlNumMsgsSelected = 0;
140                 }
141         }
142
143         // For multi select ... is the message being clicked already selected?
144         already_selected = 0;
145         if ( (evt.ctrlKey) && (CtdlNumMsgsSelected > 0) ) {
146                 for (i=0; i<CtdlNumMsgsSelected; ++i) {
147                         if (CtdlMsgsSelected[i] == msgnum) {
148                                 already_selected = 1;
149                         }
150                 }
151         }
152
153         // Now select (or de-select) the message
154         if ( (evt.ctrlKey) && (already_selected == 1) ) {
155                 $('m'+msgnum).style.backgroundColor = '#fff';
156                 $('m'+msgnum).style.color = '#000';
157                 // FIXME pull the message out of the selected list here, stupid.
158                 // this will fix Bugzilla #173
159         }
160         else {
161                 $('m'+msgnum).style.backgroundColor='#69aaff';
162                 $('m'+msgnum).style.color='#fff';
163                 CtdlNumMsgsSelected = CtdlNumMsgsSelected + 1;
164                 CtdlMsgsSelected[CtdlNumMsgsSelected-1] = msgnum;
165         }
166
167         // Update the preview pane
168         new Ajax.Updater('preview_pane', 'msg/'+msgnum, { method: 'get' } );
169
170         // Mark the message as read
171         new Ajax.Request(
172                 'ajax_servcmd', {
173                         method: 'post',
174                         parameters: 'g_cmd=SEEN '+msgnum+'|1',
175                         onComplete: CtdlRemoveTheUnseenBold(msgnum)
176                 }
177         );
178
179         return false;           // try to defeat the default click behavior
180 }
181
182 // Delete selected messages.
183 function CtdlDeleteSelectedMessages(evt) {
184         
185         if (CtdlNumMsgsSelected < 1) {
186                 // Nothing to delete, so exit silently.
187                 return false;
188         }
189         for (i=0; i<CtdlNumMsgsSelected; ++i) {
190                 if (parseInt(room_is_trash) > 0) {
191                         new Ajax.Request(
192                                 'ajax_servcmd', {
193                                         method: 'post',
194                                         parameters: 'g_cmd=DELE ' + CtdlMsgsSelected[i],
195                                         onComplete: CtdlClearDeletedMsg(CtdlMsgsSelected[i])
196                                 }
197                         );
198                 }
199                 else {
200                         new Ajax.Request(
201                                 'ajax_servcmd', {
202                                         method: 'post',
203                                         parameters: 'g_cmd=MOVE ' + CtdlMsgsSelected[i] + '|_TRASH_|0',
204                                         onComplete: CtdlClearDeletedMsg(CtdlMsgsSelected[i])
205                                 }
206                         );
207                 }
208         }
209         CtdlNumMsgsSelected = 0;
210
211         // Clear the preview pane too.
212         $('preview_pane').innerHTML = '';
213 }
214
215
216 // Move selected messages.
217 function CtdlMoveSelectedMessages(evt, target_roomname) {
218         
219         if (CtdlNumMsgsSelected < 1) {
220                 // Nothing to delete, so exit silently.
221                 return false;
222         }
223         for (i=0; i<CtdlNumMsgsSelected; ++i) {
224                 new Ajax.Request(
225                         'ajax_servcmd', {
226                                 method:'post',
227                                 parameters:'g_cmd=MOVE ' + CtdlMsgsSelected[i] + '|' + target_roomname + '|0',
228                                 onComplete:CtdlClearDeletedMsg(CtdlMsgsSelected[i])
229                         }
230                 );
231         }
232         CtdlNumMsgsSelected = 0;
233
234         // Clear the preview pane too.
235         $('preview_pane').innerHTML = '';
236 }
237
238
239
240 // This gets called when the user touches the keyboard after selecting messages...
241 function CtdlMsgListKeyPress(evt) {
242         if(document.all) {                              // aIEeee
243                 var whichKey = window.event.keyCode;
244         }
245         else {                                          // non-sux0r browsers
246                 var whichKey = evt.which;
247         }
248         if (whichKey == 46) {                           // DELETE key
249                 CtdlDeleteSelectedMessages(evt);
250         }
251         return true;
252 }
253
254 // Take the boldface away from a message to indicate that it has been seen.
255 function CtdlRemoveTheUnseenBold(msgnum) {
256         $('m'+msgnum).style.fontWeight='normal';
257 }
258
259 // A message has been deleted, so yank it from the list.
260 function CtdlClearDeletedMsg(msgnum) {
261
262
263         // Traverse the table looking for a row whose ID contains the desired msgnum
264         var table = $('summary_headers');
265         if (table) {
266                 for (var r = 0; r < table.rows.length; r++) {
267                         var thename = table.rows[r].id;
268                         if (thename.substr(1) == msgnum) {
269                                 try {
270                                         table.deleteRow(r);
271                                 }
272                                 catch(e) {
273                                         alert('error: browser failed to clear row ' + r);
274                                 }
275                         }
276                 }
277         }
278         else {                                          // if we can't delete it,
279                 new Effect.Squish('m'+msgnum);          // just hide it.
280         }
281
282
283 }
284
285 // These functions called when the user down-clicks on the message list resizer bar
286
287 var saved_x = 0;
288 var saved_y = 0;
289
290 function CtdlResizeMsgListMouseUp(evt) {
291         document.onmouseup = null;
292         document.onmousemove = null;
293         if (document.layers) {
294                 document.releaseEvents(Event.MOUSEUP | Event.MOUSEMOVE);
295         }
296         return true;
297 }
298
299 function CtdlResizeMsgListMouseMove(evt) {
300         y = (ns6 ? evt.clientY : event.clientY);
301         increment = y - saved_y;
302
303         // First move the bottom of the message list...
304         d = $('message_list');
305         if (d.offsetHeight){
306                 divHeight = d.offsetHeight;
307         }
308         else if (d.style.pixelHeight) {
309                 divHeight = d.style.pixelHeight;
310         }
311         d.style.height = (divHeight + increment) + 'px';
312
313         // Then move the top of the preview pane...
314         d = $('preview_pane');
315         if (d.offsetTop){
316                 divTop = d.offsetTop;
317         }
318         else if (d.style.pixelTop) {
319                 divTop = d.style.pixelTop;
320         }
321         d.style.top = (divTop + increment) + 'px';
322
323         // Resize the bottom of the preview pane...
324         d = $('preview_pane');
325         if (d.offsetHeight){
326                 divHeight = d.offsetHeight;
327         }
328         else if (d.style.pixelHeight) {
329                 divHeight = d.style.pixelHeight;
330         }
331         d.style.height = (divHeight - increment) + 'px';
332
333         // Then move the top of the slider bar.
334         d = $('resize_msglist');
335         if (d.offsetTop){
336                 divTop = d.offsetTop;
337         }
338         else if (d.style.pixelTop) {
339                 divTop = d.style.pixelTop;
340         }
341         d.style.top = (divTop + increment) + 'px';
342
343         saved_y = y;
344         return true;
345 }
346
347 function CtdlResizeMsgListMouseDown(evt) {
348         saved_y = (ns6 ? evt.clientY : event.clientY);
349         document.onmouseup = CtdlResizeMsgListMouseUp;
350         document.onmousemove = CtdlResizeMsgListMouseMove;
351         if (document.layers) {
352                 document.captureEvents(Event.MOUSEUP | Event.MOUSEMOVE);
353         }
354         return false;           // disable the default action
355 }
356
357
358
359 // These functions handle drag and drop message moving
360
361 var mm_div = null;
362
363 function CtdlMoveMsgMouseDown(evt, msgnum) {
364
365         // do the highlight first
366         CtdlSingleClickMsg(evt, msgnum);
367
368         // Now handle the possibility of dragging
369         saved_x = (ns6 ? evt.clientX : event.clientX);
370         saved_y = (ns6 ? evt.clientY : event.clientY);
371         document.onmouseup = CtdlMoveMsgMouseUp;
372         document.onmousemove = CtdlMoveMsgMouseMove;
373         if (document.layers) {
374                 document.captureEvents(Event.MOUSEUP | Event.MOUSEMOVE);
375         }
376
377         return false;
378 }
379
380 function CtdlMoveMsgMouseMove(evt) {
381         x = (ns6 ? evt.clientX : event.clientX);
382         y = (ns6 ? evt.clientY : event.clientY);
383
384         if ( (x == saved_x) && (y == saved_y) ) {
385                 return true;
386         }
387
388         if (CtdlNumMsgsSelected < 1) { 
389                 return true;
390         }
391
392         if (!mm_div) {
393
394
395                 drag_o_text = "<div style=\"overflow:none; background-color:#fff; color:#000; border: 1px solid black; filter:alpha(opacity=75); -moz-opacity:.75; opacity:.75;\"><tr><td>";
396                 for (i=0; i<CtdlNumMsgsSelected; ++i) {
397                         drag_o_text = drag_o_text + 
398                                 ctdl_ts_getInnerText(
399                                         $('m'+CtdlMsgsSelected[i]).cells[0]
400                                 ) + '<br>';
401                 }
402                 drag_o_text = drag_o_text + "<div>";
403
404                 mm_div = document.createElement("DIV");
405                 mm_div.style.position='absolute';
406                 mm_div.style.top = y + 'px';
407                 mm_div.style.left = x + 'px';
408                 mm_div.style.pixelHeight = '300';
409                 mm_div.style.pixelWidth = '300';
410                 mm_div.innerHTML = drag_o_text;
411                 document.body.appendChild(mm_div);
412         }
413         else {
414                 mm_div.style.top = y + 'px';
415                 mm_div.style.left = x + 'px';
416         }
417
418         return false;   // prevent the default mouse action from happening?
419 }
420
421 function CtdlMoveMsgMouseUp(evt) {
422         document.onmouseup = null;
423         document.onmousemove = null;
424         if (document.layers) {
425                 document.releaseEvents(Event.MOUSEUP | Event.MOUSEMOVE);
426         }
427
428         if (mm_div) {
429                 document.body.removeChild(mm_div);      
430                 mm_div = null;
431         }
432
433         if (num_drop_targets < 1) {     // nowhere to drop
434                 return true;
435         }
436
437         // Did we release the mouse button while hovering over a drop target?
438         // NOTE: this only works cross-browser because the iconbar div is always
439         //      positioned at 0,0.  Browsers differ in whether the 'offset'
440         //      functions return pos relative to the document or parent.
441
442         for (i=0; i<num_drop_targets; ++i) {
443
444                 x = (ns6 ? evt.clientX : event.clientX);
445                 y = (ns6 ? evt.clientY : event.clientY);
446
447                 l = parseInt(drop_targets_elements[i].offsetLeft);
448                 t = parseInt(drop_targets_elements[i].offsetTop);
449                 r = parseInt(drop_targets_elements[i].offsetLeft)
450                   + parseInt(drop_targets_elements[i].offsetWidth);
451                 b = parseInt(drop_targets_elements[i].offsetTop)
452                   + parseInt(drop_targets_elements[i].offsetHeight);
453
454                 /* alert('Offsets are: ' + l + ' ' + t + ' ' + r + ' ' + b + '.'); */
455         
456                 if ( (x >= l) && (x <= r) && (y >= t) && (y <= b) ) {
457                         // Yes, we dropped it on a hotspot.
458                         CtdlMoveSelectedMessages(evt, drop_targets_roomnames[i]);
459                         return true;
460                 }
461         }
462
463         return true;
464 }
465
466
467 function ctdl_ts_getInnerText(el) {
468         if (typeof el == "string") return el;
469         if (typeof el == "undefined") { return el };
470         if (el.innerText) return el.innerText;  //Not needed but it is faster
471         var str = "";
472         
473         var cs = el.childNodes;
474         var l = cs.length;
475         for (var i = 0; i < l; i++) {
476                 switch (cs[i].nodeType) {
477                         case 1: //ELEMENT_NODE
478                                 str += ts_getInnerText(cs[i]);
479                                 break;
480                         case 3: //TEXT_NODE
481                                 str += cs[i].nodeValue;
482                                 break;
483                 }
484         }
485         return str;
486 }
487
488
489
490 // This function handles the creation of new notes in the "Notes" view.
491 //
492 function add_new_note() {
493
494         new_eid = Math.random() + '';
495         new_eid = new_eid.substr(3);
496
497         $('new_notes_here').innerHTML = $('new_notes_here').innerHTML
498                 + '<IMG ALIGN=MIDDLE src=\"static/storenotes_48x.gif\">'
499                 + '<span id=\"note' + new_eid + '\">' + Date() + '</span><br />'
500         ;
501
502         new Ajax.InPlaceEditor('note' + new_eid,
503                 'updatenote?eid=' + new_eid , {rows:5,cols:72});
504 }