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