* which_div_expanded is now persistent across page loads
[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
11 if (document.layers) {browserType = "nn4"}
12 if (document.all) {browserType = "ie"}
13 if (window.navigator.userAgent.toLowerCase().match("gecko")) {
14         browserType= "gecko"
15 }
16
17 var ns6=document.getElementById&&!document.all;
18
19
20 //
21 // This code handles the popups for instant messages.
22 //
23
24
25 function hide_page_popup() {
26         if (browserType == "gecko" )
27                 document.poppedLayer = eval('document.getElementById(\'page_popup\')');
28         else if (browserType == "ie")
29                 document.poppedLayer = eval('document.all[\'page_popup\']');
30         else
31                 document.poppedLayer = eval('document.layers[\'`page_popup\']');
32
33         document.poppedLayer.style.visibility = "hidden";
34 }
35
36 function hide_imsg_popup() {
37         if (browserType == "gecko" )
38                 document.poppedLayer = eval('document.getElementById(\'important_message\')');
39         else if (browserType == "ie")
40                 document.poppedLayer = eval('document.all[\'important_message\']');
41         else
42                 document.poppedLayer = eval('document.layers[\'`important_message\']');
43
44         document.poppedLayer.style.visibility = "hidden";
45 }
46
47 // This function activates the ajax-powered recipient autocompleters on the message entry screen.
48 function activate_entmsg_autocompleters() {
49         new Ajax.Autocompleter('cc_id', 'cc_name_choices', 'cc_autocomplete', {} );
50         new Ajax.Autocompleter('bcc_id', 'bcc_name_choices', 'bcc_autocomplete', {} );
51         new Ajax.Autocompleter('recp_id', 'recp_name_choices', 'recp_autocomplete', {} );
52 }
53
54
55
56 // Toggle the icon bar between menu/roomlist...
57 var which_div_expanded = null;
58 var num_drop_targets = 0;
59 var drop_targets_elements = new Array();
60 var drop_targets_roomnames = new Array();
61
62 function switch_to_room_list() {
63         $('iconbar').innerHTML = $('iconbar').innerHTML.substr(0, $('iconbar').innerHTML.indexOf('switch'));
64         new Ajax.Updater('iconbar', 'iconbar_ajax_rooms', { method: 'get' } );
65 }
66
67 function expand_floor(floor_div) {
68         if (which_div_expanded != null) {
69                 if ($(which_div_expanded) != null) {
70                         $(which_div_expanded).style.display = 'none' ;
71                 }
72         }
73         if (which_div_expanded == floor_div) {
74                 which_div_expanded = null;
75                 return true;
76         }
77         $(floor_div).style.display = 'block';
78         which_div_expanded = floor_div;
79
80         // notify the server of what we did
81         new Ajax.Request(
82                 'set_floordiv_expanded/'+floor_div, {
83                         method: 'post',
84                 }
85         );
86 }
87
88 function switch_to_menu_buttons() {
89         which_div_expanded = null;
90         num_drop_targets = 0;
91         new Ajax.Updater('iconbar', 'iconbar_ajax_menu', { method: 'get' } );
92 }
93
94
95 // Static variables for mailbox view...
96 //
97 var CtdlNumMsgsSelected = 0;
98 var CtdlMsgsSelected = new Array();
99
100 // This gets called when you single click on a message in the mailbox view.
101 // We know that the element id of the table row will be the letter 'm' plus the message number.
102 //
103 function CtdlSingleClickMsg(evt, msgnum) {
104
105         // Clear the preview pane until we load the new message
106         $('preview_pane').innerHTML = '';
107
108         // De-select any messages that were already selected, *unless* the Ctrl key
109         // is being pressed, in which case the user wants multi select.
110         if (!evt.ctrlKey) {
111                 if (CtdlNumMsgsSelected > 0) {
112                         for (i=0; i<CtdlNumMsgsSelected; ++i) {
113                                 $('m'+CtdlMsgsSelected[i]).style.backgroundColor = '#fff';
114                                 $('m'+CtdlMsgsSelected[i]).style.color = '#000';
115                         }
116                         CtdlNumMsgsSelected = 0;
117                 }
118         }
119
120         // For multi select ... is the message being clicked already selected?
121         already_selected = 0;
122         if ( (evt.ctrlKey) && (CtdlNumMsgsSelected > 0) ) {
123                 for (i=0; i<CtdlNumMsgsSelected; ++i) {
124                         if (CtdlMsgsSelected[i] == msgnum) {
125                                 already_selected = 1;
126                         }
127                 }
128         }
129
130         // Now select (or de-select) the message
131         if ( (evt.ctrlKey) && (already_selected == 1) ) {
132                 $('m'+msgnum).style.backgroundColor = '#fff';
133                 $('m'+msgnum).style.color = '#000';
134         }
135         else {
136                 $('m'+msgnum).style.backgroundColor='#69aaff';
137                 $('m'+msgnum).style.color='#fff';
138                 CtdlNumMsgsSelected = CtdlNumMsgsSelected + 1;
139                 CtdlMsgsSelected[CtdlNumMsgsSelected-1] = msgnum;
140         }
141
142         // Update the preview pane
143         new Ajax.Updater('preview_pane', 'msg/'+msgnum, { method: 'get' } );
144
145         // Mark the message as read
146         new Ajax.Request(
147                 'ajax_servcmd', {
148                         method: 'post',
149                         parameters: 'g_cmd=SEEN '+msgnum+'|1',
150                         onComplete: CtdlRemoveTheUnseenBold(msgnum)
151                 }
152         );
153
154         return false;           // try to defeat the default click behavior
155 }
156
157 // Delete selected messages.
158 function CtdlDeleteSelectedMessages(evt) {
159         
160         if (CtdlNumMsgsSelected < 1) {
161                 // Nothing to delete, so exit silently.
162                 return false;
163         }
164         for (i=0; i<CtdlNumMsgsSelected; ++i) {
165                 new Ajax.Request(
166                         'ajax_servcmd', {
167                                 method: 'post',
168                                 parameters: 'g_cmd=MOVE ' + CtdlMsgsSelected[i] + '|_TRASH_|0',
169                                 onComplete: CtdlClearDeletedMsg(CtdlMsgsSelected[i])
170                         }
171                 );
172         }
173         CtdlNumMsgsSelected = 0;
174
175         // Clear the preview pane too.
176         $('preview_pane').innerHTML = '';
177 }
178
179
180 // Move selected messages.
181 function CtdlMoveSelectedMessages(evt, target_roomname) {
182         
183         if (CtdlNumMsgsSelected < 1) {
184                 // Nothing to delete, so exit silently.
185                 return false;
186         }
187         for (i=0; i<CtdlNumMsgsSelected; ++i) {
188                 new Ajax.Request(
189                         'ajax_servcmd', {
190                                 method:'post',
191                                 parameters:'g_cmd=MOVE ' + CtdlMsgsSelected[i] + '|' + target_roomname + '|0',
192                                 onComplete:CtdlClearDeletedMsg(CtdlMsgsSelected[i])
193                         }
194                 );
195         }
196         CtdlNumMsgsSelected = 0;
197
198         // Clear the preview pane too.
199         $('preview_pane').innerHTML = '';
200 }
201
202
203
204 // This gets called when the user touches the keyboard after selecting messages...
205 function CtdlMsgListKeyPress(evt) {
206         if(document.all) {                              // aIEeee
207                 var whichKey = window.event.keyCode;
208         }
209         else {                                          // non-sux0r browsers
210                 var whichKey = evt.which;
211         }
212         if (whichKey == 46) {                           // DELETE key
213                 CtdlDeleteSelectedMessages(evt);
214         }
215         return true;
216 }
217
218 // Take the boldface away from a message to indicate that it has been seen.
219 function CtdlRemoveTheUnseenBold(msgnum) {
220         $('m'+msgnum).style.fontWeight='normal';
221 }
222
223 // A message has been deleted, so yank it from the list.
224 // (IE barfs on m9999.innerHTML='' so we use a script.aculo.us effect instead.)
225 function CtdlClearDeletedMsg(msgnum) {
226         new Effect.Squish('m'+msgnum);
227 }
228
229
230 // These functions called when the user down-clicks on the message list resizer bar
231
232 var saved_x = 0;
233 var saved_y = 0;
234
235 function CtdlResizeMsgListMouseUp(evt) {
236         document.onmouseup = null;
237         document.onmousemove = null;
238         if (document.layers) {
239                 document.releaseEvents(Event.MOUSEUP | Event.MOUSEMOVE);
240         }
241         return true;
242 }
243
244 function CtdlResizeMsgListMouseMove(evt) {
245         y = (ns6 ? evt.clientY : event.clientY);
246         increment = y - saved_y;
247
248         // First move the bottom of the message list...
249         d = $('message_list');
250         if (d.offsetHeight){
251                 divHeight = d.offsetHeight;
252         }
253         else if (d.style.pixelHeight) {
254                 divHeight = d.style.pixelHeight;
255         }
256         d.style.height = (divHeight + increment) + 'px';
257
258         // Then move the top of the preview pane...
259         d = $('preview_pane');
260         if (d.offsetTop){
261                 divTop = d.offsetTop;
262         }
263         else if (d.style.pixelTop) {
264                 divTop = d.style.pixelTop;
265         }
266         d.style.top = (divTop + increment) + 'px';
267
268         // Resize the bottom of the preview pane...
269         d = $('preview_pane');
270         if (d.offsetHeight){
271                 divHeight = d.offsetHeight;
272         }
273         else if (d.style.pixelHeight) {
274                 divHeight = d.style.pixelHeight;
275         }
276         d.style.height = (divHeight - increment) + 'px';
277
278         // Then move the top of the slider bar.
279         d = $('resize_msglist');
280         if (d.offsetTop){
281                 divTop = d.offsetTop;
282         }
283         else if (d.style.pixelTop) {
284                 divTop = d.style.pixelTop;
285         }
286         d.style.top = (divTop + increment) + 'px';
287
288         saved_y = y;
289         return true;
290 }
291
292 function CtdlResizeMsgListMouseDown(evt) {
293         saved_y = (ns6 ? evt.clientY : event.clientY);
294         document.onmouseup = CtdlResizeMsgListMouseUp;
295         document.onmousemove = CtdlResizeMsgListMouseMove;
296         if (document.layers) {
297                 document.captureEvents(Event.MOUSEUP | Event.MOUSEMOVE);
298         }
299         return false;           // disable the default action
300 }
301
302
303
304 // These functions handle drag and drop message moving
305
306 var mm_div = null;
307
308 function CtdlMoveMsgMouseDown(evt, msgnum) {
309
310         // do the highlight first
311         CtdlSingleClickMsg(evt, msgnum);
312
313         // Now handle the possibility of dragging
314         saved_x = (ns6 ? evt.clientX : event.clientX);
315         saved_y = (ns6 ? evt.clientY : event.clientY);
316         document.onmouseup = CtdlMoveMsgMouseUp;
317         document.onmousemove = CtdlMoveMsgMouseMove;
318         if (document.layers) {
319                 document.captureEvents(Event.MOUSEUP | Event.MOUSEMOVE);
320         }
321
322         return false;
323 }
324
325 function CtdlMoveMsgMouseMove(evt) {
326         x = (ns6 ? evt.clientX : event.clientX);
327         y = (ns6 ? evt.clientY : event.clientY);
328
329         if ( (x == saved_x) && (y == saved_y) ) {
330                 return true;
331         }
332
333         if (CtdlNumMsgsSelected < 1) { 
334                 return true;
335         }
336
337         if (!mm_div) {
338
339
340                 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>";
341                 for (i=0; i<CtdlNumMsgsSelected; ++i) {
342                         drag_o_text = drag_o_text + 
343                                 ctdl_ts_getInnerText(
344                                         $('m'+CtdlMsgsSelected[i]).cells[0]
345                                 ) + '<br>';
346                 }
347                 drag_o_text = drag_o_text + "<div>";
348
349                 mm_div = document.createElement("DIV");
350                 mm_div.style.position='absolute';
351                 mm_div.style.top = y + 'px';
352                 mm_div.style.left = x + 'px';
353                 mm_div.style.pixelHeight = '300';
354                 mm_div.style.pixelWidth = '300';
355                 mm_div.innerHTML = drag_o_text;
356                 document.body.appendChild(mm_div);
357         }
358         else {
359                 mm_div.style.top = y + 'px';
360                 mm_div.style.left = x + 'px';
361         }
362
363         return false;   // prevent the default mouse action from happening?
364 }
365
366 function CtdlMoveMsgMouseUp(evt) {
367         document.onmouseup = null;
368         document.onmousemove = null;
369         if (document.layers) {
370                 document.releaseEvents(Event.MOUSEUP | Event.MOUSEMOVE);
371         }
372
373         if (mm_div) {
374                 document.body.removeChild(mm_div);      
375                 mm_div = null;
376         }
377
378         if (num_drop_targets < 1) {     // nowhere to drop
379                 return true;
380         }
381
382         // Did we release the mouse button while hovering over a drop target?
383         // NOTE: this only works cross-browser because the iconbar div is always
384         //      positioned at 0,0.  Browsers differ in whether the 'offset'
385         //      functions return pos relative to the document or parent.
386
387         for (i=0; i<num_drop_targets; ++i) {
388
389                 x = (ns6 ? evt.clientX : event.clientX);
390                 y = (ns6 ? evt.clientY : event.clientY);
391
392                 l = parseInt(drop_targets_elements[i].offsetLeft);
393                 t = parseInt(drop_targets_elements[i].offsetTop);
394                 r = parseInt(drop_targets_elements[i].offsetLeft)
395                   + parseInt(drop_targets_elements[i].offsetWidth);
396                 b = parseInt(drop_targets_elements[i].offsetTop)
397                   + parseInt(drop_targets_elements[i].offsetHeight);
398
399                 /* alert('Offsets are: ' + l + ' ' + t + ' ' + r + ' ' + b + '.'); */
400         
401                 if ( (x >= l) && (x <= r) && (y >= t) && (y <= b) ) {
402                         // Yes, we dropped it on a hotspot.
403                         CtdlMoveSelectedMessages(evt, drop_targets_roomnames[i]);
404                         return true;
405                 }
406         }
407
408         return true;
409 }
410
411
412 function ctdl_ts_getInnerText(el) {
413         if (typeof el == "string") return el;
414         if (typeof el == "undefined") { return el };
415         if (el.innerText) return el.innerText;  //Not needed but it is faster
416         var str = "";
417         
418         var cs = el.childNodes;
419         var l = cs.length;
420         for (var i = 0; i < l; i++) {
421                 switch (cs[i].nodeType) {
422                         case 1: //ELEMENT_NODE
423                                 str += ts_getInnerText(cs[i]);
424                                 break;
425                         case 3: //TEXT_NODE
426                                 str += cs[i].nodeValue;
427                                 break;
428                 }
429         }
430         return str;
431 }
432
433