svncommit
2006-09-23 6649b1f0a5db6160d197a13ca79cfd67fbb02d77
commit | author | age
e0ed97 1 /*
4e17e6 2  +-----------------------------------------------------------------------+
T 3  | RoundCube Webmail Client Script                                       |
4  |                                                                       |
5  | This file is part of the RoundCube Webmail client                     |
6  | Copyright (C) 2005, RoundCube Dev, - Switzerland                      |
30233b 7  | Licensed under the GNU GPL                                            |
4e17e6 8  |                                                                       |
T 9  +-----------------------------------------------------------------------+
8c2e58 10  | Authors: Thomas Bruederli <roundcube@gmail.com>                       |
T 11  |          Charles McNulty <charles@charlesmcnulty.com>                 |
4e17e6 12  +-----------------------------------------------------------------------+
15a9d1 13  
T 14   $Id$
4e17e6 15 */
24053e 16
b11a00 17 // Constants
T 18 var CONTROL_KEY = 1;
19 var SHIFT_KEY = 2;
20 var CONTROL_SHIFT_KEY = 3;
4e17e6 21
T 22 var rcube_webmail_client;
23
24 function rcube_webmail()
25   {
26   this.env = new Object();
10a699 27   this.labels = new Object();
4e17e6 28   this.buttons = new Object();
T 29   this.gui_objects = new Object();
30   this.commands = new Object();
31   this.selection = new Array();
e8adc7 32   this.last_selected = 0;
1c5853 33   this.in_message_list = false;
4e17e6 34
T 35   // create public reference to myself
36   rcube_webmail_client = this;
37   this.ref = 'rcube_webmail_client';
38  
39   // webmail client settings
40   this.dblclick_time = 600;
41   this.message_time = 5000;
9a5261 42   
4e17e6 43   this.mbox_expression = new RegExp('[^0-9a-z\-_]', 'gi');
T 44   
45   // mimetypes supported by the browser (default settings)
46   this.mimetypes = new Array('text/plain', 'text/html', 'text/xml',
47                              'image/jpeg', 'image/gif', 'image/png',
48                              'application/x-javascript', 'application/pdf',
49                              'application/x-shockwave-flash');
50
9a5261 51   // default environment vars
7139e3 52   this.env.keep_alive = 60;        // seconds
9a5261 53   this.env.request_timeout = 180;  // seconds
e170b4 54   this.env.draft_autosave = 0;     // seconds
9a5261 55
4e17e6 56
T 57   // set environment variable
58   this.set_env = function(name, value)
59     {
9a5261 60     this.env[name] = value;
4e17e6 61     };
10a699 62
T 63
64   // add a localized label to the client environment
65   this.add_label = function(key, value)
66     {
67     this.labels[key] = value;
68     };
69
4e17e6 70
T 71   // add a button to the button list
72   this.register_button = function(command, id, type, act, sel, over)
73     {
74     if (!this.buttons[command])
75       this.buttons[command] = new Array();
76       
77     var button_prop = {id:id, type:type};
78     if (act) button_prop.act = act;
79     if (sel) button_prop.sel = sel;
80     if (over) button_prop.over = over;
81
82     this.buttons[command][this.buttons[command].length] = button_prop;    
83     };
84
85
86   // register a specific gui object
87   this.gui_object = function(name, id)
88     {
89     this.gui_objects[name] = id;
90     };
91
92
93   // initialize webmail client
94   this.init = function()
95     {
96     this.task = this.env.task;
97     
98     // check browser
a95e0e 99     if (!bw.dom || !bw.xmlhttp_test())
4e17e6 100       {
T 101       location.href = this.env.comm_path+'&_action=error&_code=0x199';
102       return;
103       }
104     
105     // find all registered gui objects
106     for (var n in this.gui_objects)
107       this.gui_objects[n] = rcube_find_object(this.gui_objects[n]);
108       
109     // tell parent window that this frame is loaded
110     if (this.env.framed && parent.rcmail && parent.rcmail.set_busy)
111       parent.rcmail.set_busy(false);
112
113     // enable general commands
114     this.enable_command('logout', 'mail', 'addressbook', 'settings', true);
115     
116     switch (this.task)
117       {
118       case 'mail':
d58c69 119         var msg_list_frame = this.gui_objects.mailcontframe;
4e17e6 120         var msg_list = this.gui_objects.messagelist;
T 121         if (msg_list)
122           {
d58c69 123           msg_list_frame.onmousedown = function(e){return rcube_webmail_client.click_on_list(e);};
4e17e6 124           this.init_messagelist(msg_list);
857a38 125           this.enable_command('toggle_status', true);
4e17e6 126           }
T 127
128         // enable mail commands
c8c1e0 129         this.enable_command('list', 'checkmail', 'compose', 'add-contact', 'search', 'reset-search', true);
4e17e6 130         
T 131         if (this.env.action=='show')
132           {
583f1c 133           this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 'viewsource', 'print', 'load-attachment', true);
4e17e6 134           if (this.env.next_uid)
T 135             this.enable_command('nextmessage', true);
136           if (this.env.prev_uid)
137             this.enable_command('previousmessage', true);
138           }
139
140         if (this.env.action=='show' && this.env.blockedobjects)
141           {
142           if (this.gui_objects.remoteobjectsmsg)
143             this.gui_objects.remoteobjectsmsg.style.display = 'block';
144           this.enable_command('load-images', true);
a0109c 145           }
4e17e6 146
T 147         if (this.env.action=='compose')
ed5d29 148           {
a894ba 149           this.enable_command('add-attachment', 'send-attachment', 'remove-attachment', 'send', true);
ed5d29 150           if (this.env.spellcheck)
e170b4 151             {
T 152             this.env.spellcheck.spelling_state_observer = function(s){ rcube_webmail_client.set_spellcheck_state(s); };
153             this.set_spellcheck_state('ready');
154             }
41fa0b 155           if (this.env.drafts_mailbox)
T 156             this.enable_command('savedraft', true);
ed5d29 157           }
a0109c 158
4e17e6 159         if (this.env.messagecount)
15a9d1 160           this.enable_command('select-all', 'select-none', 'sort', 'expunge', true);
4e17e6 161
b4b081 162         if (this.env.messagecount && (this.env.mailbox==this.env.trash_mailbox || this.env.mailbox==this.env.junk_mailbox))
5e3512 163           this.enable_command('purge', true);
T 164
4e17e6 165         this.set_page_buttons();
T 166
167         // focus this window
168         window.focus();
169
170         // init message compose form
171         if (this.env.action=='compose')
172           this.init_messageform();
173
174         // show printing dialog
175         if (this.env.action=='print')
176           window.print();
a0109c 177
15a9d1 178         // get unread count for each mailbox
T 179         if (this.gui_objects.mailboxlist)
180           this.http_request('getunread', '');
4e17e6 181
T 182         break;
183
184
185       case 'addressbook':
d1d2c4 186         var contacts_list      = this.gui_objects.contactslist;
S 187         var ldap_contacts_list = this.gui_objects.ldapcontactslist;
188
4e17e6 189         if (contacts_list)
T 190           this.init_contactslist(contacts_list);
191       
d1d2c4 192         if (ldap_contacts_list)
S 193           this.init_ldapsearchlist(ldap_contacts_list);
194
4e17e6 195         this.set_page_buttons();
T 196           
197         if (this.env.cid)
198           this.enable_command('show', 'edit', true);
199
200         if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform)
201           this.enable_command('save', true);
202       
c0da98 203         this.enable_command('list', 'add', true);
S 204
205         this.enable_command('ldappublicsearch', this.env.ldappublicsearch);
206
4e17e6 207         break;
T 208
209
210       case 'settings':
211         this.enable_command('preferences', 'identities', 'save', 'folders', true);
212         
213         if (this.env.action=='identities' || this.env.action=='edit-identity' || this.env.action=='add-identity')
214           this.enable_command('edit', 'add', 'delete', true);
215
216         if (this.env.action=='edit-identity' || this.env.action=='add-identity')
217           this.enable_command('save', true);
218           
219         if (this.env.action=='folders')
c8c1e0 220           this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', true);
4e17e6 221           
T 222         var identities_list = this.gui_objects.identitieslist;
223         if (identities_list)
224           this.init_identitieslist(identities_list);
225
226         break;
227
228       case 'login':
229         var input_user = rcube_find_object('_user');
230         var input_pass = rcube_find_object('_pass');
231         if (input_user && input_user.value=='')
232           input_user.focus();
233         else if (input_pass)
234           input_pass.focus();
235           
236         this.enable_command('login', true);
237         break;
238       
239       default:
240         break;
241       }
242
243
244     // enable basic commands
245     this.enable_command('logout', true);
246
247     // disable browser's contextmenus
8c2e58 248     // document.oncontextmenu = function(){ return false; }
4e17e6 249
d58c69 250     // load body click event
S 251     document.onmousedown = function(){ return rcube_webmail_client.reset_click(); };
356b6e 252     document.onkeydown   = function(e){ return rcube_webmail_client.key_pressed(e, msg_list_frame); };
d58c69 253     
4e17e6 254     // flag object as complete
T 255     this.loaded = true;
9a5261 256
4e17e6 257     // show message
T 258     if (this.pending_message)
259       this.display_message(this.pending_message[0], this.pending_message[1]);
9a5261 260
T 261     // start keep-alive interval
262     this.start_keepalive();
4e17e6 263     };
9a5261 264
T 265
266   // start interval for keep-alive/recent_check signal
267   this.start_keepalive = function()
268     {
269     if (this.env.keep_alive && this.task=='mail' && this.gui_objects.messagelist)
270       this._int = setInterval(this.ref+'.check_for_recent()', this.env.keep_alive * 1000);
271     else if (this.env.keep_alive && this.task!='login')
272       this._int = setInterval(this.ref+'.send_keep_alive()', this.env.keep_alive * 1000);    
273     }
274
4e17e6 275
d58c69 276   // reset last clicked if user clicks on anything other than the message table
24053e 277   this.reset_click = function()
T 278     {
279     var id;
1c5853 280     this.in_message_list = false;
24053e 281     for (var n=0; n<this.selection.length; n++)
T 282       {
952860 283       id = this.selection[n];
24053e 284       if (this.list_rows[id] && this.list_rows[id].obj)
T 285         {
952860 286         this.set_classname(this.list_rows[id].obj, 'selected', false);
S 287         this.set_classname(this.list_rows[id].obj, 'unfocused', true);
24053e 288         }
T 289       }
290     };
d58c69 291     
S 292   this.click_on_list = function(e)
293     {
294     if (!e)
295       e = window.event;
952860 296
fe79b1 297     for (var n=0; n<this.selection.length; n++)
T 298       {
952860 299       id = this.selection[n];
fe79b1 300       if (this.list_rows[id].obj)
T 301         {
952860 302         this.set_classname(this.list_rows[id].obj, 'selected', true);
S 303         this.set_classname(this.list_rows[id].obj, 'unfocused', false);
fe79b1 304         }
T 305       }
d58c69 306
fe79b1 307     var mbox_li;
T 308     if (mbox_li = this.get_mailbox_li()) 
309       this.set_classname(mbox_li, 'unfocused', true);
310     
d58c69 311     this.in_message_list = true;
S 312     e.cancelBubble = true;
313     };
314
356b6e 315   this.key_pressed = function(e, msg_list_frame) {
d58c69 316     if (this.in_message_list != true) 
S 317       return true;
318     var keyCode = document.layers ? e.which : document.all ? event.keyCode : document.getElementById ? e.keyCode : 0;
319     var mod_key = this.get_modifier(e);
356b6e 320     switch (keyCode) {
ff9107 321       case 13:
S 322         this.command('show','',this);
323         break;
356b6e 324       case 40:
S 325       case 38: 
326         return this.use_arrow_key(keyCode, mod_key, msg_list_frame);
327         break;
328       case 46:
329         return this.use_delete_key(keyCode, mod_key, msg_list_frame);
330         break;
331       default:
332         return true;
333     }
ff9107 334     return true;
356b6e 335   }
S 336
337   this.use_arrow_key = function(keyCode, mod_key, msg_list_frame) {
d58c69 338     var scroll_to = 0;
S 339     if (keyCode == 40) { // down arrow key pressed
e8adc7 340       new_row = this.get_next_row();
d58c69 341       if (!new_row) return false;
S 342       scroll_to = (Number(new_row.offsetTop) + Number(new_row.offsetHeight)) - Number(msg_list_frame.offsetHeight);
343     } else if (keyCode == 38) { // up arrow key pressed
e8adc7 344       new_row = this.get_prev_row();
d58c69 345       if (!new_row) return false;
S 346       scroll_to = new_row.offsetTop;
f108b9 347     } else {return true;}
d58c69 348     
e8adc7 349     this.select_row(new_row.uid,mod_key,true);
d58c69 350
S 351     if (((Number(new_row.offsetTop)) < (Number(msg_list_frame.scrollTop))) || 
352        ((Number(new_row.offsetTop) + Number(new_row.offsetHeight)) > (Number(msg_list_frame.scrollTop) + Number(msg_list_frame.offsetHeight)))) {
353       msg_list_frame.scrollTop = scroll_to;
354     }
355     return false;
356   };
356b6e 357   
S 358   this.use_delete_key = function(keyCode, mod_key, msg_list_frame){
359     this.command('delete','',this);
360     return false;
361   }
4e17e6 362
T 363   // get all message rows from HTML table and init each row
364   this.init_messagelist = function(msg_list)
365     {
366     if (msg_list && msg_list.tBodies[0])
367       {
d58c69 368           
4e17e6 369       this.message_rows = new Array();
T 370
371       var row;
372       for(var r=0; r<msg_list.tBodies[0].childNodes.length; r++)
373         {
374         row = msg_list.tBodies[0].childNodes[r];
1c5853 375         while (row && (row.nodeType != 1 || row.style.display == 'none')) {
S 376           row = row.nextSibling;
377           r++;
378         }
4e17e6 379         //row = msg_list.tBodies[0].rows[r];
1c5853 380         if (row) this.init_message_row(row);
4e17e6 381         }
T 382       }
383       
384     // alias to common rows array
385     this.list_rows = this.message_rows;
386     };
387     
388     
389   // make references in internal array and set event handlers
390   this.init_message_row = function(row)
391     {
392     var uid, msg_icon;
393     
394     if (String(row.id).match(/rcmrow([0-9]+)/))
395       {
396       uid = RegExp.$1;
397       row.uid = uid;
398               
399       this.message_rows[uid] = {id:row.id, obj:row,
400                                 classname:row.className,
857a38 401                                 deleted:this.env.messages[uid] ? this.env.messages[uid].deleted : null,
4e17e6 402                                 unread:this.env.messages[uid] ? this.env.messages[uid].unread : null,
T 403                                 replied:this.env.messages[uid] ? this.env.messages[uid].replied : null};
404               
405       // set eventhandlers to table row
406       row.onmousedown = function(e){ return rcube_webmail_client.drag_row(e, this.uid); };
407       row.onmouseup = function(e){ return rcube_webmail_client.click_row(e, this.uid); };
d58c69 408
b11a00 409       if (document.all)
T 410         row.onselectstart = function() { return false; };
411
4e17e6 412       // set eventhandler to message icon
T 413       if ((msg_icon = row.cells[0].childNodes[0]) && row.cells[0].childNodes[0].nodeName=='IMG')
414         {                
415         msg_icon.id = 'msgicn_'+uid;
416         msg_icon._row = row;
857a38 417         msg_icon.onmousedown = function(e) { rcube_webmail_client.command('toggle_status', this); };
4e17e6 418                 
T 419         // get message icon and save original icon src
420         this.message_rows[uid].icon = msg_icon;
421         }
422       }
423     };
424
425
426   // init message compose form: set focus and eventhandlers
427   this.init_messageform = function()
428     {
429     if (!this.gui_objects.messageform)
430       return false;
431     
432     //this.messageform = this.gui_objects.messageform;
1cded8 433     var input_from = rcube_find_object('_from');
4e17e6 434     var input_to = rcube_find_object('_to');
T 435     var input_cc = rcube_find_object('_cc');
436     var input_bcc = rcube_find_object('_bcc');
437     var input_replyto = rcube_find_object('_replyto');
438     var input_subject = rcube_find_object('_subject');
439     var input_message = rcube_find_object('_message');
a0109c 440
4e17e6 441     // init live search events
T 442     if (input_to)
443       this.init_address_input_events(input_to);
444     if (input_cc)
445       this.init_address_input_events(input_cc);
446     if (input_bcc)
447       this.init_address_input_events(input_bcc);
1cded8 448       
T 449     // add signature according to selected identity
450     if (input_from && input_from.type=='select-one')
451       this.change_identity(input_from);
4e17e6 452
T 453     if (input_to && input_to.value=='')
454       input_to.focus();
455     else if (input_subject && input_subject.value=='')
456       input_subject.focus();
457     else if (input_message)
458       this.set_caret2start(input_message); // input_message.focus();
a0109c 459
977a29 460     // get summary of all field values
T 461     this.cmp_hash = this.compose_field_hash();
f0f98f 462  
S 463     // start the auto-save timer
464     this.auto_save_start();
4e17e6 465     };
T 466
467   this.init_address_input_events = function(obj)
468     {
469     var handler = function(e){ return rcube_webmail_client.ksearch_keypress(e,this); };
470     var handler2 = function(e){ return rcube_webmail_client.ksearch_blur(e,this); };
471     
472     if (bw.safari)
473       {
474       obj.addEventListener('keydown', handler, false);
475       // obj.addEventListener('blur', handler2, false);
476       }
477     else if (bw.mz)
478       {
479       obj.addEventListener('keypress', handler, false);
480       obj.addEventListener('blur', handler2, false);
481       }
482     else if (bw.ie)
483       {
484       obj.onkeydown = handler;
485       //obj.attachEvent('onkeydown', handler);
486       // obj.attachEvent('onblur', handler2, false);
487       }
488     
489     obj.setAttribute('autocomplete', 'off');       
490     };
491
492
493
494   // get all contact rows from HTML table and init each row
495   this.init_contactslist = function(contacts_list)
496     {
497     if (contacts_list && contacts_list.tBodies[0])
498       {
499       this.contact_rows = new Array();
500
501       var row;
502       for(var r=0; r<contacts_list.tBodies[0].childNodes.length; r++)
503         {
504         row = contacts_list.tBodies[0].childNodes[r];
505         this.init_table_row(row, 'contact_rows');
506         }
507       }
508
509     // alias to common rows array
510     this.list_rows = this.contact_rows;
511     
512     if (this.env.cid)
2a1e18 513       this.highlight_row(this.env.cid);
4e17e6 514     };
T 515
516
d1d2c4 517   // get all contact rows from HTML table and init each row
S 518   this.init_ldapsearchlist = function(ldap_contacts_list)
519     {
520     if (ldap_contacts_list && ldap_contacts_list.tBodies[0])
521       {
522       this.ldap_contact_rows = new Array();
523
524       var row;
525       for(var r=0; r<ldap_contacts_list.tBodies[0].childNodes.length; r++)
526         {
527         row = ldap_contacts_list.tBodies[0].childNodes[r];
528         this.init_table_row(row, 'ldap_contact_rows');
529         }
530       }
531
532     // alias to common rows array
533     this.list_rows = this.ldap_contact_rows;
534     };
535
536
4e17e6 537   // make references in internal array and set event handlers
T 538   this.init_table_row = function(row, array_name)
539     {
540     var cid;
541     
542     if (String(row.id).match(/rcmrow([0-9]+)/))
543       {
544       cid = RegExp.$1;
545       row.cid = cid;
546
547       this[array_name][cid] = {id:row.id,
548                                obj:row,
549                                classname:row.className};
550
551       // set eventhandlers to table row
552       row.onmousedown = function(e) { rcube_webmail_client.in_selection_before=this.cid; return false; };  // fake for drag handler
553       row.onmouseup = function(e){ return rcube_webmail_client.click_row(e, this.cid); };
554       }
555     };
556
557
558   // get all contact rows from HTML table and init each row
559   this.init_identitieslist = function(identities_list)
560     {
561     if (identities_list && identities_list.tBodies[0])
562       {
563       this.identity_rows = new Array();
564
565       var row;
566       for(var r=0; r<identities_list.tBodies[0].childNodes.length; r++)
567         {
568         row = identities_list.tBodies[0].childNodes[r];
569         this.init_table_row(row, 'identity_rows');
570         }
571       }
572
573     // alias to common rows array
574     this.list_rows = this.identity_rows;
575     
576     if (this.env.iid)
2a1e18 577       this.highlight_row(this.env.iid);    
4e17e6 578     };
T 579     
580
581
582   /*********************************************************/
583   /*********       client command interface        *********/
584   /*********************************************************/
585
586
587   // execute a specific command on the web client
588   this.command = function(command, props, obj)
589     {
590     if (obj && obj.blur)
591       obj.blur();
592
593     if (this.busy)
594       return false;
595
596     // command not supported or allowed
597     if (!this.commands[command])
598       {
599       // pass command to parent window
600       if (this.env.framed && parent.rcmail && parent.rcmail.command)
601         parent.rcmail.command(command, props);
602
603       return false;
604       }
15a9d1 605       
T 606       
607    // check input before leaving compose step
608    if (this.task=='mail' && this.env.action=='compose' && (command=='list' || command=='mail' || command=='addressbook' || command=='settings'))
609      {
610      if (this.cmp_hash != this.compose_field_hash() && !confirm(this.get_label('notsentwarning')))
611         return false;
612      }
613
4e17e6 614
T 615     // process command
616     switch (command)
617       {
618       case 'login':
619         if (this.gui_objects.loginform)
620           this.gui_objects.loginform.submit();
621         break;
622
623       case 'logout':
624         location.href = this.env.comm_path+'&_action=logout';
625         break;      
626
627       // commands to switch task
628       case 'mail':
629       case 'addressbook':
630       case 'settings':
631         this.switch_task(command);
632         break;
633
634
635       // misc list commands
636       case 'list':
637         if (this.task=='mail')
4647e1 638           {
ac6b87 639           if (this.env.search_request<0 || (this.env.search_request && props != this.env.mailbox))
4647e1 640             this.reset_qsearch();
c8c1e0 641
24053e 642           // Reset message list header, unless returning from compose/read/etc
T 643           // don't know what this is good for (thomasb, 2006/07/25)
644           //if (this.env.mailbox != props && this.message_rows)
645           //  this.clear_message_list_header();
c8c1e0 646
4e17e6 647           this.list_mailbox(props);
4647e1 648           }
4e17e6 649         else if (this.task=='addressbook')
T 650           this.list_contacts();
f3b659 651         break;
c8c1e0 652
f3b659 653
T 654       case 'sort':
655         // get the type of sorting
b076a4 656         var a_sort = props.split('_');
T 657         var sort_col = a_sort[0];
1cded8 658         var sort_order = a_sort[1] ? a_sort[1].toUpperCase() : null;
b076a4 659         var header;
1cded8 660         
T 661         // no sort order specified: toggle
662         if (sort_order==null)
663           {
664           if (this.env.sort_col==sort_col)
665             sort_order = this.env.sort_order=='ASC' ? 'DESC' : 'ASC';
666           else
667             sort_order = this.env.sort_order;
668           }
24053e 669
b076a4 670         if (this.env.sort_col==sort_col && this.env.sort_order==sort_order)
T 671           break;
672
673         // set table header class
674         if (header = document.getElementById('rcmHead'+this.env.sort_col))
675           this.set_classname(header, 'sorted'+(this.env.sort_order.toUpperCase()), false);
676         if (header = document.getElementById('rcmHead'+sort_col))
677           this.set_classname(header, 'sorted'+sort_order, true);
678
679         // save new sort properties
680         this.env.sort_col = sort_col;
681         this.env.sort_order = sort_order;
682
683         // reload message list
1cded8 684         this.list_mailbox('', '', sort_col+'_'+sort_order);
4e17e6 685         break;
T 686
687       case 'nextpage':
688         this.list_page('next');
689         break;
690
691       case 'previouspage':
692         this.list_page('prev');
15a9d1 693         break;
T 694
695       case 'expunge':
696         if (this.env.messagecount)
697           this.expunge_mailbox(this.env.mailbox);
698         break;
699
5e3512 700       case 'purge':
T 701       case 'empty-mailbox':
702         if (this.env.messagecount)
703           this.purge_mailbox(this.env.mailbox);
4e17e6 704         break;
T 705
706
707       // common commands used in multiple tasks
708       case 'show':
709         if (this.task=='mail')
710           {
711           var uid = this.get_single_uid();
712           if (uid && (!this.env.uid || uid != this.env.uid))
41fa0b 713             {
1966c5 714             if (this.env.mailbox==this.env.drafts_mailbox)
S 715               {
716               this.set_busy(true);
4d4264 717               location.href = this.env.comm_path+'&_action=compose&_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox);
1966c5 718               }
S 719             else
720               this.show_message(uid);
41fa0b 721             }
4e17e6 722           }
T 723         else if (this.task=='addressbook')
724           {
725           var cid = props ? props : this.get_single_cid();
726           if (cid && !(this.env.action=='show' && cid==this.env.cid))
727             this.load_contact(cid, 'show');
728           }
729         break;
730
731       case 'add':
732         if (this.task=='addressbook')
d1d2c4 733           if (!window.frames[this.env.contentframe].rcmail)
S 734             this.load_contact(0, 'add');
735           else
736             {
737             if (window.frames[this.env.contentframe].rcmail.selection.length)
738               this.add_ldap_contacts();
739             else
740               this.load_contact(0, 'add');
741             }
4e17e6 742         else if (this.task=='settings')
T 743           {
744           this.clear_selection();
745           this.load_identity(0, 'add-identity');
746           }
747         break;
748
749       case 'edit':
750         var cid;
751         if (this.task=='addressbook' && (cid = this.get_single_cid()))
752           this.load_contact(cid, 'edit');
753         else if (this.task=='settings' && props)
754           this.load_identity(props, 'edit-identity');
755         break;
756
757       case 'save-identity':
758       case 'save':
759         if (this.gui_objects.editform)
10a699 760           {
T 761           var input_pagesize = rcube_find_object('_pagesize');
762           var input_name  = rcube_find_object('_name');
763           var input_email = rcube_find_object('_email');
764
765           // user prefs
e66f5b 766           if (input_pagesize && isNaN(input_pagesize.value))
10a699 767             {
T 768             alert(this.get_label('nopagesizewarning'));
769             input_pagesize.focus();
770             break;
771             }
772           // contacts/identities
773           else
774             {
775             if (input_name && input_name.value == '')
776               {
777               alert(this.get_label('nonamewarning'));
778               input_name.focus();
779               break;
780               }
781             else if (input_email && !rcube_check_email(input_email.value))
782               {
783               alert(this.get_label('noemailwarning'));
784               input_email.focus();
785               break;
786               }
787             }
788
4e17e6 789           this.gui_objects.editform.submit();
10a699 790           }
4e17e6 791         break;
T 792
793       case 'delete':
794         // mail task
857a38 795         if (this.task=='mail')
4e17e6 796           this.delete_messages();
T 797         // addressbook task
798         else if (this.task=='addressbook')
799           this.delete_contacts();
800         // user settings task
801         else if (this.task=='settings')
802           this.delete_identity();
803         break;
804
805
806       // mail task commands
807       case 'move':
808       case 'moveto':
809         this.move_messages(props);
810         break;
811         
857a38 812       case 'toggle_status':
4e17e6 813         if (props && !props._row)
T 814           break;
815         
816         var uid;
817         var flag = 'read';
818         
819         if (props._row.uid)
820           {
821           uid = props._row.uid;
822           this.dont_select = true;
823           // toggle read/unread
857a38 824           if (this.message_rows[uid].deleted) {
S 825               flag = 'undelete';
826           } else if (!this.message_rows[uid].unread)
4e17e6 827             flag = 'unread';
T 828           }
829           
830         this.mark_message(flag, uid);
831         break;
832         
833       case 'load-images':
834         if (this.env.uid)
835           this.show_message(this.env.uid, true);
836         break;
837
838       case 'load-attachment':
839         var url = this.env.comm_path+'&_action=get&_mbox='+this.env.mailbox+'&_uid='+this.env.uid+'&_part='+props.part;
840         
841         // open attachment in frame if it's of a supported mimetype
842         if (this.env.uid && props.mimetype && find_in_array(props.mimetype, this.mimetypes)>=0)
843           {
844           this.attachment_win = window.open(url+'&_frame=1', 'rcubemailattachment');
845           if (this.attachment_win)
846             {
847             setTimeout(this.ref+'.attachment_win.focus()', 10);
848             break;
849             }
850           }
851
852         location.href = url;
853         break;
854         
855       case 'select-all':
856         this.select_all(props);
857         break;
858
859       case 'select-none':
860         this.clear_selection();
861         break;
862
863       case 'nextmessage':
864         if (this.env.next_uid)
09941e 865           this.show_message(this.env.next_uid);
T 866           //location.href = this.env.comm_path+'&_action=show&_uid='+this.env.next_uid+'&_mbox='+this.env.mailbox;
4e17e6 867         break;
T 868
869       case 'previousmessage':
870         if (this.env.prev_uid)
09941e 871           this.show_message(this.env.prev_uid);
T 872           //location.href = this.env.comm_path+'&_action=show&_uid='+this.env.prev_uid+'&_mbox='+this.env.mailbox;
4e17e6 873         break;
d1d2c4 874       
c8c1e0 875       case 'checkmail':
S 876         this.check_for_recent();
877         break;
d1d2c4 878       
4e17e6 879       case 'compose':
T 880         var url = this.env.comm_path+'&_action=compose';
1966c5 881        
S 882         if (this.task=='mail' && this.env.mailbox==this.env.drafts_mailbox)
883           {
aade7b 884           var uid;
T 885           if (uid = this.get_single_uid())
4d4264 886             url += '&_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox);
1966c5 887           } 
4e17e6 888         // modify url if we're in addressbook
1966c5 889         else if (this.task=='addressbook')
4e17e6 890           {
T 891           url = this.get_task_url('mail', url);            
892           var a_cids = new Array();
893           
894           // use contact_id passed as command parameter
895           if (props)
896             a_cids[a_cids.length] = props;
897             
898           // get selected contacts
899           else
900             {
d1d2c4 901             if (!window.frames[this.env.contentframe].rcmail.selection.length)
S 902               {
903               for (var n=0; n<this.selection.length; n++)
904                 a_cids[a_cids.length] = this.selection[n];
905               }
906             else
907               {
908               var frameRcmail = window.frames[this.env.contentframe].rcmail;
909               // get the email address(es)
910               for (var n=0; n<frameRcmail.selection.length; n++)
911                 a_cids[a_cids.length] = frameRcmail.ldap_contact_rows[frameRcmail.selection[n]].obj.cells[1].innerHTML;
912               }
4e17e6 913             }
T 914           if (a_cids.length)
915             url += '&_to='+a_cids.join(',');
916           else
917             break;
d1d2c4 918             
4e17e6 919           }
T 920         else if (props)
b4b081 921            url += '&_to='+encodeURIComponent(props);
bc8dc6 922
d1d2c4 923         // don't know if this is necessary...
S 924         url = url.replace(/&_framed=1/, "");
4e17e6 925
T 926         this.set_busy(true);
d1d2c4 927
S 928         // need parent in case we are coming from the contact frame
bc8dc6 929         if (this.env.framed)
T 930           parent.location.href = url;
931         else
932           location.href = url;
ed5d29 933         break;
T 934         
935       case 'spellcheck':
e170b4 936         if (this.env.spellcheck && this.env.spellcheck.spellCheck && this.spellcheck_ready)
T 937           {
ed5d29 938           this.env.spellcheck.spellCheck(this.env.spellcheck.check_link);
e170b4 939           this.set_spellcheck_state('checking');
T 940           }
ed5d29 941         break;
4e17e6 942
1966c5 943       case 'savedraft':
41fa0b 944         // Reset the auto-save timer
T 945         self.clearTimeout(this.save_timer);
f0f98f 946
1966c5 947         if (!this.gui_objects.messageform)
S 948           break;
f0f98f 949
41fa0b 950         // if saving Drafts is disabled in main.inc.php
e170b4 951         // or if compose form did not change
T 952         if (!this.env.drafts_mailbox || this.cmp_hash == this.compose_field_hash())
41fa0b 953           break;
f0f98f 954
1966c5 955         this.set_busy(true, 'savingmessage');
S 956         var form = this.gui_objects.messageform;
41fa0b 957         form.target = "savetarget";
1966c5 958         form.submit();
S 959         break;
960
4e17e6 961       case 'send':
T 962         if (!this.gui_objects.messageform)
963           break;
41fa0b 964
977a29 965         if (!this.check_compose_input())
10a699 966           break;
9a5261 967           
T 968         // Reset the auto-save timer
969         self.clearTimeout(this.save_timer);
10a699 970
T 971         // all checks passed, send message
972         this.set_busy(true, 'sendingmessage');
973         var form = this.gui_objects.messageform;
41fa0b 974         form.target = "savetarget";     
T 975         form._draft.value = '';
10a699 976         form.submit();
9a5261 977         
T 978         // clear timeout (sending could take longer)
979         clearTimeout(this.request_timer);
4e17e6 980         break;
T 981
982       case 'add-attachment':
983         this.show_attachment_form(true);
984         
985       case 'send-attachment':
f0f98f 986         // Reset the auto-save timer
41fa0b 987         self.clearTimeout(this.save_timer);
f0f98f 988
4e17e6 989         this.upload_file(props)      
T 990         break;
a894ba 991       
S 992       case 'remove-attachment':
993         this.remove_attachment(props);
994         break;
4e17e6 995
583f1c 996       case 'reply-all':
4e17e6 997       case 'reply':
T 998         var uid;
999         if (uid = this.get_single_uid())
1000           {
1001           this.set_busy(true);
4d4264 1002           location.href = this.env.comm_path+'&_action=compose&_reply_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(command=='reply-all' ? '&_all=1' : '');
4e17e6 1003           }
T 1004         break;      
1005
1006       case 'forward':
1007         var uid;
1008         if (uid = this.get_single_uid())
1009           {
1010           this.set_busy(true);
4d4264 1011           location.href = this.env.comm_path+'&_action=compose&_forward_uid='+uid+'&_mbox='+urlencode(this.env.mailbox);
4e17e6 1012           }
T 1013         break;
1014         
1015       case 'print':
1016         var uid;
1017         if (uid = this.get_single_uid())
1018           {
4d4264 1019           this.printwin = window.open(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(this.env.safemode ? '&_safe=1' : ''));
4e17e6 1020           if (this.printwin)
T 1021             setTimeout(this.ref+'.printwin.focus()', 20);
1022           }
1023         break;
1024
1025       case 'viewsource':
1026         var uid;
1027         if (uid = this.get_single_uid())
1028           {          
4d4264 1029           this.sourcewin = window.open(this.env.comm_path+'&_action=viewsource&_uid='+this.env.uid+'&_mbox='+urlencode(this.env.mailbox));
4e17e6 1030           if (this.sourcewin)
T 1031             setTimeout(this.ref+'.sourcewin.focus()', 20);
1032           }
1033         break;
1034
1035       case 'add-contact':
1036         this.add_contact(props);
1037         break;
d1d2c4 1038       
4647e1 1039       // mail quicksearch
T 1040       case 'search':
1041         if (!props && this.gui_objects.qsearchbox)
1042           props = this.gui_objects.qsearchbox.value;
1043         if (props)
4d4264 1044           this.qsearch(urlencode(props), this.env.mailbox);
4647e1 1045         break;
T 1046
1047       // reset quicksearch        
1048       case 'reset-search':
1049         var s = this.env.search_request;
1050         this.reset_qsearch();
1051         
1052         if (s)
1053           this.list_mailbox(this.env.mailbox);
1054         break;
d1d2c4 1055
S 1056       // ldap search
1057       case 'ldappublicsearch':
1058         if (this.gui_objects.ldappublicsearchform) 
1059           this.gui_objects.ldappublicsearchform.submit();
1060         else 
1061           this.ldappublicsearch(command);
1062         break; 
4e17e6 1063
T 1064
1065       // user settings commands
1066       case 'preferences':
1067         location.href = this.env.comm_path;
1068         break;
1069
1070       case 'identities':
1071         location.href = this.env.comm_path+'&_action=identities';
1072         break;
1073           
1074       case 'delete-identity':
1075         this.delete_identity();
1076         
1077       case 'folders':
1078         location.href = this.env.comm_path+'&_action=folders';
1079         break;
1080
1081       case 'subscribe':
1082         this.subscribe_folder(props);
1083         break;
1084
1085       case 'unsubscribe':
1086         this.unsubscribe_folder(props);
1087         break;
1088         
1089       case 'create-folder':
1090         this.create_folder(props);
c8c1e0 1091         break;
S 1092
1093       case 'rename-folder':
1094         this.rename_folder(props);
4e17e6 1095         break;
T 1096
1097       case 'delete-folder':
1cded8 1098         if (confirm(this.get_label('deletefolderconfirm')))
4e17e6 1099           this.delete_folder(props);
T 1100         break;
1101
1102       }
1103
1104     return obj ? false : true;
1105     };
1106
1107
1108   // set command enabled or disabled
1109   this.enable_command = function()
1110     {
1c5853 1111     var args = arguments;
4e17e6 1112     if(!args.length) return -1;
T 1113
1114     var command;
1115     var enable = args[args.length-1];
1116     
1117     for(var n=0; n<args.length-1; n++)
1118       {
1119       command = args[n];
1120       this.commands[command] = enable;
1121       this.set_button(command, (enable ? 'act' : 'pas'));
1122       }
1c5853 1123       return true;
4e17e6 1124     };
T 1125
1126
a95e0e 1127   // lock/unlock interface
4e17e6 1128   this.set_busy = function(a, message)
T 1129     {
1130     if (a && message)
10a699 1131       {
T 1132       var msg = this.get_label(message);
1133       if (msg==message)        
1134         msg = 'Loading...';
1135
1136       this.display_message(msg, 'loading', true);
1137       }
4e17e6 1138     else if (!a && this.busy)
T 1139       this.hide_message();
1140
1141     this.busy = a;
1142     //document.body.style.cursor = a ? 'wait' : 'default';
1143     
1144     if (this.gui_objects.editform)
1145       this.lock_form(this.gui_objects.editform, a);
a95e0e 1146       
T 1147     // clear pending timer
1148     if (this.request_timer)
1149       clearTimeout(this.request_timer);
1150
1151     // set timer for requests
9a5261 1152     if (a && this.env.request_timeout)
T 1153       this.request_timer = setTimeout(this.ref+'.request_timed_out()', this.env.request_timeout * 1000);
4e17e6 1154     };
T 1155
1156
10a699 1157   // return a localized string
T 1158   this.get_label = function(name)
1159     {
1160     if (this.labels[name])
1161       return this.labels[name];
1162     else
1163       return name;
1164     };
1165
1166
1167   // switch to another application task
4e17e6 1168   this.switch_task = function(task)
T 1169     {
01bb03 1170     if (this.task===task && task!='mail')
4e17e6 1171       return;
T 1172
01bb03 1173     var url = this.get_task_url(task);
T 1174     if (task=='mail')
1175       url += '&_mbox=INBOX';
1176
4e17e6 1177     this.set_busy(true);
01bb03 1178     location.href = url;
4e17e6 1179     };
T 1180
1181
1182   this.get_task_url = function(task, url)
1183     {
1184     if (!url)
1185       url = this.env.comm_path;
1186
1187     return url.replace(/_task=[a-z]+/, '_task='+task);
a95e0e 1188     };
T 1189     
1190   
1191   // called when a request timed out
1192   this.request_timed_out = function()
1193     {
1194     this.set_busy(false);
1195     this.display_message('Request timed out!', 'error');
4e17e6 1196     };
T 1197
1198
1199   /*********************************************************/
1200   /*********        event handling methods         *********/
1201   /*********************************************************/
1202
1203
1204   // onmouseup handler for mailboxlist item
1205   this.mbox_mouse_up = function(mbox)
1206     {
1207     if (this.drag_active)
fe79b1 1208       {
T 1209       this.unfocus_mailbox(mbox);
4e17e6 1210       this.command('moveto', mbox);
fe79b1 1211       }
4e17e6 1212     else
T 1213       this.command('list', mbox);
fe79b1 1214   
4e17e6 1215     return false;
T 1216     };
1217
1218
1219   // onmousedown-handler of message list row
1220   this.drag_row = function(e, id)
1221     {
1222     this.in_selection_before = this.in_selection(id) ? id : false;
1223
1224     // don't do anything (another action processed before)
1225     if (this.dont_select)
1226       return false;
1227
b11a00 1228     // selects currently unselected row
ff9107 1229     if (!this.in_selection_before && !this.list_rows[id].clicked)
b11a00 1230     {
d58c69 1231       var mod_key = this.get_modifier(e);
e8adc7 1232       this.select_row(id,mod_key,false);
b11a00 1233     }
4e17e6 1234     
T 1235     if (this.selection.length)
1236       {
1237       this.drag_start = true;
1238       document.onmousemove = function(e){ return rcube_webmail_client.drag_mouse_move(e); };
1239       document.onmouseup = function(e){ return rcube_webmail_client.drag_mouse_up(e); };
1240       }
1241
1242     return false;
1243     };
1244
1245
1246   // onmouseup-handler of message list row
1247   this.click_row = function(e, id)
1248     {
8c2e58 1249     var mod_key = this.get_modifier(e);
d58c69 1250
4e17e6 1251     // don't do anything (another action processed before)
T 1252     if (this.dont_select)
1253       {
1254       this.dont_select = false;
1255       return false;
1256       }
1257     
b11a00 1258     // unselects currently selected row    
ff9107 1259     if (!this.drag_active && this.in_selection_before==id && !this.list_rows[id].clicked)
e8adc7 1260       this.select_row(id,mod_key,false);
8c2e58 1261
4e17e6 1262     this.drag_start = false;
T 1263     this.in_selection_before = false;
1264         
1265     // row was double clicked
ff9107 1266     if (this.task=='mail' && this.list_rows && this.list_rows[id].clicked && this.in_selection(id))
4e17e6 1267       {
1966c5 1268       if (this.env.mailbox==this.env.drafts_mailbox)
S 1269         {
1270         this.set_busy(true);
4d4264 1271         location.href = this.env.comm_path+'&_action=compose&_draft_uid='+id+'&_mbox='+urlencode(this.env.mailbox);
1966c5 1272         }
S 1273       else
1274         {
1275         this.show_message(id);
1276         }
4e17e6 1277       return false;
T 1278       }
1279     else if (this.task=='addressbook')
1280       {
d1d2c4 1281       if (this.contact_rows && this.selection.length==1)
S 1282         {
4e17e6 1283         this.load_contact(this.selection[0], 'show', true);
d1d2c4 1284         // change the text for the add contact button
S 1285         var links = parent.document.getElementById('abooktoolbar').getElementsByTagName('A');
1286         for (i = 0; i < links.length; i++)
1287           {
1288           var onclickstring = new String(links[i].onclick);
1289           if (onclickstring.search('\"add\"') != -1)
1290             links[i].title = this.env.newcontact;
1291           }
1292         }
1293       else if (this.contact_rows && this.contact_rows[id].clicked)
4e17e6 1294         {
T 1295         this.load_contact(id, 'show');
1296         return false;
1297         }
d1d2c4 1298       else if (this.ldap_contact_rows && !this.ldap_contact_rows[id].clicked)
S 1299         {
1300         // clear selection
1301         parent.rcmail.clear_selection();
1302
1303         // disable delete
1304         parent.rcmail.set_button('delete', 'pas');
1305
1306         // change the text for the add contact button
1307         var links = parent.document.getElementById('abooktoolbar').getElementsByTagName('A');
1308         for (i = 0; i < links.length; i++)
1309           {
1310           var onclickstring = new String(links[i].onclick);
1311           if (onclickstring.search('\"add\"') != -1)
1312             links[i].title = this.env.addcontact;
1313           }
1314         }
1315       // handle double click event
1316       else if (this.ldap_contact_rows && this.selection.length==1 && this.ldap_contact_rows[id].clicked)
1317         this.command('compose', this.ldap_contact_rows[id].obj.cells[1].innerHTML);
4e17e6 1318       else if (this.env.contentframe)
T 1319         {
1320         var elm = document.getElementById(this.env.contentframe);
1321         elm.style.visibility = 'hidden';
1322         }
1323       }
1324     else if (this.task=='settings')
1325       {
1326       if (this.selection.length==1)
1327         this.command('edit', this.selection[0]);
1328       }
1329
1330     this.list_rows[id].clicked = true;
1331     setTimeout(this.ref+'.list_rows['+id+'].clicked=false;', this.dblclick_time);
1332       
1333     return false;
1334     };
1335
1336
1337
1338   /*********************************************************/
1339   /*********     (message) list functionality      *********/
1340   /*********************************************************/
1341
e8adc7 1342   // get next and previous rows that are not hidden
S 1343   this.get_next_row = function(){
dab065 1344       if (!this.list_rows) return false;
e8adc7 1345     var last_selected_row = this.list_rows[this.last_selected];
S 1346     var new_row = last_selected_row.obj.nextSibling;
1347     while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none')) {
1348       new_row = new_row.nextSibling;
1349     }
1350     return new_row;
1351   }
1352   
1353   this.get_prev_row = function(){
dab065 1354     if (!this.list_rows) return false;
e8adc7 1355     var last_selected_row = this.list_rows[this.last_selected];
S 1356     var new_row = last_selected_row.obj.previousSibling;
1357     while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none')) {
1358       new_row = new_row.previousSibling;
1359     }
1360     return new_row;
1361   }
1362   
d58c69 1363   // highlight/unhighlight a row
S 1364   this.highlight_row = function(id, multiple)
4e17e6 1365     {
T 1366     var selected = false
1367     
1368     if (this.list_rows[id] && !multiple)
1369       {
1370       this.clear_selection();
1371       this.selection[0] = id;
1372       this.list_rows[id].obj.className += ' selected';
1373       selected = true;
1374       }
1375     
1376     else if (this.list_rows[id])
1377       {
1378       if (!this.in_selection(id))  // select row
1379         {
1380         this.selection[this.selection.length] = id;
952860 1381         this.set_classname(this.list_rows[id].obj, 'selected', true);
4e17e6 1382         }
T 1383       else  // unselect row
1384         {
1385         var p = find_in_array(id, this.selection);
1386         var a_pre = this.selection.slice(0, p);
1387         var a_post = this.selection.slice(p+1, this.selection.length);
1388         this.selection = a_pre.concat(a_post);
1389         this.set_classname(this.list_rows[id].obj, 'selected', false);
952860 1390         this.set_classname(this.list_rows[id].obj, 'unfocused', false);
4e17e6 1391         }
T 1392       selected = (this.selection.length==1);
1393       }
1394
1395     // enable/disable commands for message
1396     if (this.task=='mail')
1397       {
1966c5 1398       if (this.env.mailbox==this.env.drafts_mailbox)
S 1399     {
1400     this.enable_command('show', selected);
b4b081 1401     this.enable_command('delete', 'moveto', this.selection.length>0 ? true : false);
1966c5 1402         }
S 1403       else
1404         {
1405         this.enable_command('show', 'reply', 'reply-all', 'forward', 'print', selected);
1406         this.enable_command('delete', 'moveto', this.selection.length>0 ? true : false);
1407         }
4e17e6 1408       }
T 1409     else if (this.task=='addressbook')
1410       {
1411       this.enable_command('edit', /*'print',*/ selected);
1412       this.enable_command('delete', 'compose', this.selection.length>0 ? true : false);
1413       }
1414     };
1415
b11a00 1416
d58c69 1417 // selects or unselects the proper row depending on the modifier key pressed
857a38 1418   this.select_row = function(id,mod_key,with_mouse)  { 
d58c69 1419       if (!mod_key) {
S 1420       this.shift_start = id;
1421         this.highlight_row(id, false);
1422     } else {
1423       switch (mod_key) {
1424         case SHIFT_KEY: { 
1425           this.shift_select(id,false); 
1426           break; }
1427         case CONTROL_KEY: { 
1428           this.shift_start = id;
857a38 1429           if (!with_mouse)
e8adc7 1430             this.highlight_row(id, true); 
d58c69 1431           break; 
S 1432           }
1433         case CONTROL_SHIFT_KEY: { 
1434           this.shift_select(id,true);
1435           break;
1436           }
1437         default: {
1438           this.highlight_row(id, false); 
1439           break;
1440           }
1441       }
1442     }
24053e 1443     if (this.last_selected != 0 && this.list_rows[this.last_selected])
T 1444       this.set_classname(this.list_rows[this.last_selected].obj, 'focused', false);
1445
e8adc7 1446     this.last_selected = id;
cbd62d 1447     this.set_classname(this.list_rows[id].obj, 'focused', true);        
d58c69 1448   };
S 1449
1450   this.shift_select = function(id, control) {
1451     var from_rowIndex = this.list_rows[this.shift_start].obj.rowIndex;
b11a00 1452     var to_rowIndex = this.list_rows[id].obj.rowIndex;
T 1453         
1454     var i = ((from_rowIndex < to_rowIndex)? from_rowIndex : to_rowIndex);
1455     var j = ((from_rowIndex > to_rowIndex)? from_rowIndex : to_rowIndex);
1456     
1457     // iterate through the entire message list
1458     for (var n in this.list_rows) {
1459       if ((this.list_rows[n].obj.rowIndex >= i) && (this.list_rows[n].obj.rowIndex <= j)) {
1460         if (!this.in_selection(n))
d58c69 1461           this.highlight_row(n, true);
b11a00 1462       } else {
T 1463         if  (this.in_selection(n) && !control)
d58c69 1464           this.highlight_row(n, true);
b11a00 1465       }
T 1466     }
1467   };
1468   
4e17e6 1469
T 1470   this.clear_selection = function()
1471     {
1472     for(var n=0; n<this.selection.length; n++)
952860 1473       if (this.list_rows[this.selection[n]]) {
4e17e6 1474         this.set_classname(this.list_rows[this.selection[n]].obj, 'selected', false);
952860 1475         this.set_classname(this.list_rows[this.selection[n]].obj, 'unfocused', false);
S 1476       }
4e17e6 1477     this.selection = new Array();    
T 1478     };
1479
1480
1481   // check if given id is part of the current selection
1482   this.in_selection = function(id)
1483     {
1484     for(var n in this.selection)
1485       if (this.selection[n]==id)
1486         return true;
1487
1488     return false;    
1489     };
1490
1491
1492   // select each row in list
1493   this.select_all = function(filter)
1494     {
1495     if (!this.list_rows || !this.list_rows.length)
1496       return false;
1497       
1498     // reset selection first
1499     this.clear_selection();
1500     
f07394 1501     for (var n in this.list_rows)
T 1502       {
4e17e6 1503       if (!filter || this.list_rows[n][filter]==true)
f07394 1504         {
25d8ba 1505         this.last_selected = n;
1c5853 1506         this.highlight_row(n, true);
f07394 1507         }
T 1508       }
1509
1c5853 1510     return true;  
4e17e6 1511     };
T 1512     
1513
1514   // when user doble-clicks on a row
1515   this.show_message = function(id, safe)
1516     {
1517     var add_url = '';
1518     var target = window;
1519     if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
1520       {
1521       target = window.frames[this.env.contentframe];
1522       add_url = '&_framed=1';
1523       }
1524       
1525     if (safe)
1526       add_url = '&_safe=1';
1527
1528     if (id)
1529       {
09941e 1530       this.set_busy(true, 'loading');
4d4264 1531       target.location.href = this.env.comm_path+'&_action=show&_uid='+id+'&_mbox='+urlencode(this.env.mailbox)+add_url;
4e17e6 1532       }
T 1533     };
1534
1535
1536
1537   // list a specific page
1538   this.list_page = function(page)
1539     {
1540     if (page=='next')
1541       page = this.env.current_page+1;
1542     if (page=='prev' && this.env.current_page>1)
1543       page = this.env.current_page-1;
1544       
1545     if (page > 0 && page <= this.env.pagecount)
1546       {
1547       this.env.current_page = page;
1548       
1549       if (this.task=='mail')
1550         this.list_mailbox(this.env.mailbox, page);
1551       else if (this.task=='addressbook')
1552         this.list_contacts(page);
1553       }
1554     };
1555
1556
1557   // list messages of a specific mailbox
f3b659 1558   this.list_mailbox = function(mbox, page, sort)
4e17e6 1559     {
cbd62d 1560     this.last_selected = 0;
4e17e6 1561     var add_url = '';
T 1562     var target = window;
1563
1564     if (!mbox)
1565       mbox = this.env.mailbox;
1566
f3b659 1567     // add sort to url if set
T 1568     if (sort)
1569       add_url += '&_sort=' + sort;
1570       
4e17e6 1571     // set page=1 if changeing to another mailbox
T 1572     if (!page && mbox != this.env.mailbox)
1573       {
1574       page = 1;
9fee0e 1575       add_url += '&_refresh=1';
4e17e6 1576       this.env.current_page = page;
T 1577       this.clear_selection();
1578       }
4647e1 1579     
T 1580     // also send search request to get the right messages
1581     if (this.env.search_request)
1582       add_url += '&_search='+this.env.search_request;
4e17e6 1583       
fe79b1 1584     this.select_mailbox(mbox);
4e17e6 1585
T 1586     // load message list remotely
1587     if (this.gui_objects.messagelist)
1588       {
9fee0e 1589       this.list_mailbox_remote(mbox, page, add_url);
4e17e6 1590       return;
T 1591       }
1592     
1593     if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
1594       {
1595       target = window.frames[this.env.contentframe];
9fee0e 1596       add_url += '&_framed=1';
4e17e6 1597       }
T 1598
1599     // load message list to target frame/window
1600     if (mbox)
1601       {
1602       this.set_busy(true, 'loading');
4d4264 1603       target.location.href = this.env.comm_path+'&_mbox='+urlencode(mbox)+(page ? '&_page='+page : '')+add_url;
4e17e6 1604       }
T 1605     };
1606
1607
1608   // send remote request to load message list
9fee0e 1609   this.list_mailbox_remote = function(mbox, page, add_url)
4e17e6 1610     {
15a9d1 1611     // clear message list first
T 1612     this.clear_message_list();
1613
1614     // send request to server
4d4264 1615     var url = '_mbox='+urlencode(mbox)+(page ? '&_page='+page : '');
15a9d1 1616     this.set_busy(true, 'loading');
T 1617     this.http_request('list', url+add_url, true);
1618     };
1619
1620
1621   this.clear_message_list = function()
1622     {
4e17e6 1623     var table = this.gui_objects.messagelist;
c8c1e0 1624    
4e17e6 1625     var tbody = document.createElement('TBODY');
T 1626     table.insertBefore(tbody, table.tBodies[0]);
1627     table.removeChild(table.tBodies[1]);
1628     
1629     this.message_rows = new Array();
1630     this.list_rows = this.message_rows;
15a9d1 1631     
T 1632     };
1633
24053e 1634
c8c1e0 1635   this.clear_message_list_header = function()
S 1636     {
24053e 1637     var table;
T 1638     if (table = this.gui_objects.messagelist)
1639       {
1640       if (table.colgroup)
1641         table.removeChild(table.colgroup);
1642       if (table.tHead)
1643         table.removeChild(table.tHead);
c8c1e0 1644
24053e 1645       var colgroup = document.createElement('COLGROUP');
T 1646       var thead = document.createElement('THEAD');
1647       table.insertBefore(colgroup, table.tBodies[0]);
1648       table.insertBefore(thead, table.tBodies[0]);
1649       }
c8c1e0 1650     };
24053e 1651
15a9d1 1652
T 1653   this.expunge_mailbox = function(mbox)
1654     {
1655     var lock = false;
1656     var add_url = '';
1657     
1658     // lock interface if it's the active mailbox
1659     if (mbox == this.env.mailbox)
1660        {
1661        lock = true;
1662        this.set_busy(true, 'loading');
1663        add_url = '&_reload=1';
1664        }
4e17e6 1665
T 1666     // send request to server
4d4264 1667     var url = '_mbox='+urlencode(mbox);
15a9d1 1668     this.http_request('expunge', url+add_url, lock);
4e17e6 1669     };
T 1670
1671
5e3512 1672   this.purge_mailbox = function(mbox)
T 1673     {
1674     var lock = false;
1675     var add_url = '';
1676     
1677     if (!confirm(this.get_label('purgefolderconfirm')))
1678       return false;
1679     
1680     // lock interface if it's the active mailbox
1681     if (mbox == this.env.mailbox)
1682        {
1683        lock = true;
1684        this.set_busy(true, 'loading');
1685        add_url = '&_reload=1';
1686        }
1687
1688     // send request to server
4d4264 1689     var url = '_mbox='+urlencode(mbox);
5e3512 1690     this.http_request('purge', url+add_url, lock);
1c5853 1691     return true;
5e3512 1692     };
T 1693     
fe79b1 1694   this.focus_mailbox = function(mbox)
T 1695     {
1696     var mbox_li;
1697       if (this.drag_active && mbox != this.env.mailbox && (mbox_li = this.get_mailbox_li(mbox)))
1698       this.set_classname(mbox_li, 'droptarget', true);
1699     }
1700     
1701   this.unfocus_mailbox = function(mbox)
1702     {
1703     var mbox_li;
1704       if (this.drag_active && (mbox_li = this.get_mailbox_li(mbox)))
1705       this.set_classname(mbox_li, 'droptarget', false);
1706     }
1707   
4e17e6 1708   // move selected messages to the specified mailbox
T 1709   this.move_messages = function(mbox)
1710     {
1711     // exit if no mailbox specified or if selection is empty
1712     if (!mbox || !(this.selection.length || this.env.uid) || mbox==this.env.mailbox)
1713       return;
1714     
1715     var a_uids = new Array();
1716
1717     if (this.env.uid)
1718       a_uids[a_uids.length] = this.env.uid;
1719     else
1720       {
1721       var id;
1722       for (var n=0; n<this.selection.length; n++)
1723         {
1724         id = this.selection[n];
1725         a_uids[a_uids.length] = id;
1726       
1727         // 'remove' message row from list (just hide it)
1728         if (this.message_rows[id].obj)
1729           this.message_rows[id].obj.style.display = 'none';
1730         }
cbd62d 1731       next_row = this.get_next_row();
S 1732       prev_row = this.get_prev_row();
1733       new_row = (next_row) ? next_row : prev_row;
dab065 1734       if (new_row) this.select_row(new_row.uid,false,false);
4e17e6 1735       }
ecf759 1736       
T 1737     var lock = false;
4e17e6 1738
T 1739     // show wait message
1740     if (this.env.action=='show')
ecf759 1741       {
T 1742       lock = true;
4e17e6 1743       this.set_busy(true, 'movingmessage');
ecf759 1744       }
4e17e6 1745     // send request to server
4d4264 1746     this.http_request('moveto', '_uid='+a_uids.join(',')+'&_mbox='+urlencode(this.env.mailbox)+'&_target_mbox='+urlencode(mbox)+'&_from='+(this.env.action ? this.env.action : ''), lock);
4e17e6 1747     };
T 1748
857a38 1749   this.permanently_remove_messages = function() {
4e17e6 1750     // exit if no mailbox specified or if selection is empty
T 1751     if (!(this.selection.length || this.env.uid))
1752       return;
1753     
1754     var a_uids = new Array();
1755
1756     if (this.env.uid)
1757       a_uids[a_uids.length] = this.env.uid;
1758     else
1759       {
1760       var id;
1761       for (var n=0; n<this.selection.length; n++)
1762         {
1763         id = this.selection[n];
1764         a_uids[a_uids.length] = id;
1765       
1766         // 'remove' message row from list (just hide it)
1767         if (this.message_rows[id].obj)
1768           this.message_rows[id].obj.style.display = 'none';
1769         }
1770       }
cbd62d 1771       next_row = this.get_next_row();
S 1772       prev_row = this.get_prev_row();
1773       new_row = (next_row) ? next_row : prev_row;
dab065 1774       if (new_row) this.select_row(new_row.uid,false,false);
4e17e6 1775
T 1776     // send request to server
4d4264 1777     this.http_request('delete', '_uid='+a_uids.join(',')+'&_mbox='+urlencode(this.env.mailbox)+'&_from='+(this.env.action ? this.env.action : ''));
857a38 1778   }
S 1779     
1780     
1781   // delete selected messages from the current mailbox
1782   this.delete_messages = function()
1783     {
1784     // exit if no mailbox specified or if selection is empty
1785     if (!(this.selection.length || this.env.uid))
1786       return;
1787     // if there is a trash mailbox defined and we're not currently in it:
1788     if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase()!=String(this.env.trash_mailbox).toLowerCase())
1789       this.move_messages(this.env.trash_mailbox);
1790     // if there is a trash mailbox defined but we *are* in it:
1791     else if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase() == String(this.env.trash_mailbox).toLowerCase())
1792       this.permanently_remove_messages();
1793     // if there isn't a defined trash mailbox and the config is set to flag for deletion
1794     else if (!this.env.trash_mailbox && this.env.flag_for_deletion) {
1c5853 1795       flag = 'delete';
S 1796       this.mark_message(flag);
dab065 1797       if(this.env.action=="show"){
S 1798         this.command('nextmessage','',this);
1c5853 1799       } else if (this.selection.length == 1) {
dab065 1800         next_row = this.get_next_row();
S 1801         prev_row = this.get_prev_row();
1802         new_row = (next_row) ? next_row : prev_row;
1803         if (new_row) this.select_row(new_row.uid,false,false);
1804       }
857a38 1805     // if there isn't a defined trash mailbox and the config is set NOT to flag for deletion
S 1806     }else if (!this.env.trash_mailbox && !this.env.flag_for_deletion) {
1807       this.permanently_remove_messages();
1808     }
1809     return;
1810   };
4e17e6 1811
T 1812
1813   // set a specific flag to one or more messages
1814   this.mark_message = function(flag, uid)
1815     {
1816     var a_uids = new Array();
1817     
1818     if (uid)
1819       a_uids[0] = uid;
1820     else if (this.env.uid)
1821       a_uids[0] = this.env.uid;
1822     else
1823       {
1824       var id;
1825       for (var n=0; n<this.selection.length; n++)
1826         {
1827         id = this.selection[n];
1828         a_uids[a_uids.length] = id;
1829         }
1830       }
857a38 1831       switch (flag) {
S 1832         case 'read':
1833         case 'unread':
1834           this.toggle_read_status(flag,a_uids);
1835           break;
1836         case 'delete':
1837         case 'undelete':
1c5853 1838           this.toggle_delete_status(a_uids);
857a38 1839           break;
S 1840       }
1841     };
4e17e6 1842
857a38 1843   // set class to read/unread
S 1844   this.toggle_read_status = function(flag, a_uids) {
4e17e6 1845     // mark all message rows as read/unread
T 1846     var icn_src;
1847     for (var i=0; i<a_uids.length; i++)
1848       {
1849       uid = a_uids[i];
1850       if (this.message_rows[uid])
1851         {
1852         this.message_rows[uid].unread = (flag=='unread' ? true : false);
1853         
1854         if (this.message_rows[uid].classname.indexOf('unread')<0 && this.message_rows[uid].unread)
1855           {
1856           this.message_rows[uid].classname += ' unread';
fd660a 1857           this.set_classname(this.message_rows[uid].obj, 'unread', true);
T 1858
4e17e6 1859           if (this.env.unreadicon)
T 1860             icn_src = this.env.unreadicon;
1861           }
1862         else if (!this.message_rows[uid].unread)
1863           {
1864           this.message_rows[uid].classname = this.message_rows[uid].classname.replace(/\s*unread/, '');
fd660a 1865           this.set_classname(this.message_rows[uid].obj, 'unread', false);
4e17e6 1866
T 1867           if (this.message_rows[uid].replied && this.env.repliedicon)
1868             icn_src = this.env.repliedicon;
1869           else if (this.env.messageicon)
1870             icn_src = this.env.messageicon;
1871           }
1872
1873         if (this.message_rows[uid].icon && icn_src)
1874           this.message_rows[uid].icon.src = icn_src;
1875         }
1876       }
1c5853 1877       this.http_request('mark', '_uid='+a_uids.join(',')+'&_flag='+flag);
857a38 1878   }
S 1879   
1880   // mark all message rows as deleted/undeleted
1c5853 1881   this.toggle_delete_status = function(a_uids) {
857a38 1882     if (this.env.read_when_deleted) {
S 1883       this.toggle_read_status('read',a_uids);
1884     }
dab065 1885     // if deleting message from "view message" don't bother with delete icon
S 1886     if (this.env.action == "show")
1887       return false;
4e17e6 1888
1c5853 1889     if (a_uids.length==1){
717169 1890       if(this.message_rows[a_uids[0]].classname.indexOf('deleted') < 0 ){
1c5853 1891           this.flag_as_deleted(a_uids)
S 1892       } else {
1893           this.flag_as_undeleted(a_uids)
1894       }
1895       return true;
1896     }
1897     
1898     var all_deleted = true;
1899     
1900     for (var i=0; i<a_uids.length; i++) {
857a38 1901       uid = a_uids[i];
1c5853 1902       if (this.message_rows[uid]) {
S 1903         if (this.message_rows[uid].classname.indexOf('deleted')<0) {
1904           all_deleted = false;
1905           break;
857a38 1906         }
S 1907       }
1c5853 1908     }
S 1909     
1910     if (all_deleted)
1911       this.flag_as_undeleted(a_uids);
1912     else
1913       this.flag_as_deleted(a_uids);
1914     
1915     return true;
857a38 1916   }
4e17e6 1917
1c5853 1918   this.flag_as_undeleted = function(a_uids){
S 1919     // if deleting message from "view message" don't bother with delete icon
1920     if (this.env.action == "show")
1921       return false;
1922
1923     var icn_src;
1924       
1925     for (var i=0; i<a_uids.length; i++) {
1926       uid = a_uids[i];
1927       if (this.message_rows[uid]) {
1928         this.message_rows[uid].deleted = false;
1929         
1930         if (this.message_rows[uid].classname.indexOf('deleted') > 0) {
1931           this.message_rows[uid].classname = this.message_rows[uid].classname.replace(/\s*deleted/, '');
1932           this.set_classname(this.message_rows[uid].obj, 'deleted', false);
1933         }
1934         if (this.message_rows[uid].unread && this.env.unreadicon)
1935           icn_src = this.env.unreadicon;
1936         else if (this.message_rows[uid].replied && this.env.repliedicon)
1937           icn_src = this.env.repliedicon;
1938         else if (this.env.messageicon)
1939           icn_src = this.env.messageicon;
1940         if (this.message_rows[uid].icon && icn_src)
1941           this.message_rows[uid].icon.src = icn_src;
1942       }
1943     }
1944     this.http_request('mark', '_uid='+a_uids.join(',')+'&_flag=undelete');
1945     return true;
1946   }
1947   
1948   this.flag_as_deleted = function(a_uids) {
1949     // if deleting message from "view message" don't bother with delete icon
1950     if (this.env.action == "show")
1951       return false;
1952
1953     for (var i=0; i<a_uids.length; i++) {
1954       uid = a_uids[i];
1955       if (this.message_rows[uid]) {
1956         this.message_rows[uid].deleted = true;
1957         
1958         if (this.message_rows[uid].classname.indexOf('deleted')<0) {
1959           this.message_rows[uid].classname += ' deleted';
1960           this.set_classname(this.message_rows[uid].obj, 'deleted', true);
1961         }
1962         if (this.message_rows[uid].icon && this.env.deletedicon)
1963           this.message_rows[uid].icon.src = this.env.deletedicon;
1964       }
1965     }
1966     this.http_request('mark', '_uid='+a_uids.join(',')+'&_flag=delete');
1967     return true;  
1968   }
fe79b1 1969
T 1970
1971   this.get_mailbox_li = function(mbox)
1972     {
1973     if (this.gui_objects.mailboxlist)
1974       {
1975       mbox = String((mbox ? mbox : this.env.mailbox)).toLowerCase().replace(this.mbox_expression, '');
1976       return document.getElementById('rcmbx'+mbox);
1977       }
1978     
1979     return null;
1980     };
1981     
4e17e6 1982
T 1983   /*********************************************************/
1984   /*********        message compose methods        *********/
1985   /*********************************************************/
1cded8 1986   
T 1987   
977a29 1988   // checks the input fields before sending a message
T 1989   this.check_compose_input = function()
1990     {
1991     // check input fields
1992     var input_to = rcube_find_object('_to');
1993     var input_subject = rcube_find_object('_subject');
1994     var input_message = rcube_find_object('_message');
1995
1996     // check for empty recipient
1997     if (input_to && !rcube_check_email(input_to.value, true))
1998       {
1999       alert(this.get_label('norecipientwarning'));
2000       input_to.focus();
2001       return false;
2002       }
2003
2004     // display localized warning for missing subject
2005     if (input_subject && input_subject.value == '')
2006       {
2007       var subject = prompt(this.get_label('nosubjectwarning'), this.get_label('nosubject'));
2008
2009       // user hit cancel, so don't send
2010       if (!subject && subject !== '')
2011         {
2012         input_subject.focus();
2013         return false;
2014         }
2015       else
2016         {
2017         input_subject.value = subject ? subject : this.get_label('nosubject');            
2018         }
2019       }
2020
2021     // check for empty body
a0109c 2022     if ((input_message.value=='')&&(tinyMCE.getContent()==''))
977a29 2023       {
T 2024       if (!confirm(this.get_label('nobodywarning')))
2025         {
2026         input_message.focus();
2027         return false;
2028         }
2029       }
2030
2031     return true;
2032     };
41fa0b 2033
T 2034
e170b4 2035   this.set_spellcheck_state = function(s)
T 2036     {
2037     this.spellcheck_ready = (s=='check_spelling' || s=='ready');
2038     this.enable_command('spellcheck', this.spellcheck_ready);
2039     };
2040
2041
f0f98f 2042   this.auto_save_start = function()
S 2043     {
9a5261 2044     if (this.env.draft_autosave)
T 2045       this.save_timer = self.setTimeout(this.ref+'.command("savedraft")', this.env.draft_autosave * 1000);
41fa0b 2046     };
T 2047
2048
977a29 2049   this.compose_field_hash = function()
T 2050     {
2051     // check input fields
2052     var input_to = rcube_find_object('_to');
2053     var input_cc = rcube_find_object('_to');
2054     var input_bcc = rcube_find_object('_to');
2055     var input_subject = rcube_find_object('_subject');
2056     var input_message = rcube_find_object('_message');
2057     
2058     var str = '';
2059     if (input_to && input_to.value)
2060       str += input_to.value+':';
2061     if (input_cc && input_cc.value)
2062       str += input_cc.value+':';
2063     if (input_bcc && input_bcc.value)
2064       str += input_bcc.value+':';
2065     if (input_subject && input_subject.value)
2066       str += input_subject.value+':';
2067     if (input_message && input_message.value)
2068       str += input_message.value;
2069
2070     return str;
2071     };
2072     
2073   
1cded8 2074   this.change_identity = function(obj)
T 2075     {
2076     if (!obj || !obj.options)
2077       return false;
2078
2079     var id = obj.options[obj.selectedIndex].value;
2080     var input_message = rcube_find_object('_message');
2081     var message = input_message ? input_message.value : '';
a0109c 2082     var is_html = (rcube_find_object('_is_html').value == '1');
749b07 2083     var sig, p;
1cded8 2084
1966c5 2085     if (!this.env.identity)
S 2086       this.env.identity = id
a0109c 2087   
S 2088     if (!is_html)
1cded8 2089       {
a0109c 2090       // remove the 'old' signature
S 2091       if (this.env.identity && this.env.signatures && this.env.signatures[this.env.identity])
2092         {
2093         sig = this.env.signatures[this.env.identity]['text'];
2094         if (sig.indexOf('--')!=0)
2095           sig = '--\n'+sig;
2096   
2097         p = message.lastIndexOf(sig);
2098         if (p>=0)
2099           message = message.substring(0, p-1) + message.substring(p+sig.length, message.length);
2100         }
2101   
2102       // add the new signature string
2103       if (this.env.signatures && this.env.signatures[id])
2104         {
2105         sig = this.env.signatures[id]['text'];
2106         if (sig.indexOf('--')!=0)
2107           sig = '--\n'+sig;
2108         message += '\n'+sig;
2109         }
1cded8 2110       }
a0109c 2111     else
1cded8 2112       {
a0109c 2113         var eid = tinyMCE.getEditorId('_message');
S 2114         // editor is a TinyMCE_Control object
2115         var editor = tinyMCE.getInstanceById(eid);
2116         var msgDoc = editor.getDoc();
2117         var msgBody = msgDoc.body;
2118
2119         if (this.env.signatures && this.env.signatures[id])
2120           {
2121           // Append the signature as a span within the body
2122           var sigElem = msgDoc.getElementById("_rc_sig");
2123           if (!sigElem)
2124             {
2125             sigElem = msgDoc.createElement("span");
2126             sigElem.setAttribute("id", "_rc_sig");
2127             msgBody.appendChild(sigElem);
2128             }
2129           if (this.env.signatures[id]['is_html'])
2130             {
2131             sigElem.innerHTML = this.env.signatures[id]['text'];
2132             }
2133           else
2134             {
2135             sigElem.innerHTML = '<pre>' + this.env.signatures[id]['text'] + '</pre>';
2136             }
2137           }
1cded8 2138       }
T 2139
749b07 2140     if (input_message)
1cded8 2141       input_message.value = message;
a0109c 2142
1cded8 2143     this.env.identity = id;
1c5853 2144     return true;
1cded8 2145     };
4e17e6 2146
T 2147
2148   this.show_attachment_form = function(a)
2149     {
2150     if (!this.gui_objects.uploadbox)
2151       return false;
2152       
2153     var elm, list;
2154     if (elm = this.gui_objects.uploadbox)
2155       {
2156       if (a &&  (list = this.gui_objects.attachmentlist))
2157         {
2158         var pos = rcube_get_object_pos(list);
2159         var left = pos.x;
2160         var top = pos.y + list.offsetHeight + 10;
2161       
2162         elm.style.top = top+'px';
2163         elm.style.left = left+'px';
2164         }
2165       
2166       elm.style.visibility = a ? 'visible' : 'hidden';
2167       }
2168       
2169     // clear upload form
2170     if (!a && this.gui_objects.attachmentform && this.gui_objects.attachmentform!=this.gui_objects.messageform)
2171       this.gui_objects.attachmentform.reset();
1c5853 2172     
S 2173     return true;  
4e17e6 2174     };
T 2175
2176
2177   // upload attachment file
2178   this.upload_file = function(form)
2179     {
f0f98f 2180     
4e17e6 2181     if (!form)
T 2182       return false;
2183       
2184     // get file input fields
2185     var send = false;
2186     for (var n=0; n<form.elements.length; n++)
2187       if (form.elements[n].type=='file' && form.elements[n].value)
2188         {
2189         send = true;
2190         break;
2191         }
2192     
2193     // create hidden iframe and post upload form
2194     if (send)
2195       {
2196       var ts = new Date().getTime();
2197       var frame_name = 'rcmupload'+ts;
2198
2199       // have to do it this way for IE
2200       // otherwise the form will be posted to a new window
2201       if(document.all && !window.opera)
2202         {
2203         var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
2204         document.body.insertAdjacentHTML('BeforeEnd',html);
2205         }
2206       else  // for standards-compilant browsers
2207         {
2208         var frame = document.createElement('IFRAME');
2209         frame.name = frame_name;
2210         frame.width = 10;
2211         frame.height = 10;
2212         frame.style.visibility = 'hidden';
2213         document.body.appendChild(frame);
2214         }
2215
2216       form.target = frame_name;
2217       form.action = this.env.comm_path+'&_action=upload';
2218       form.setAttribute('enctype', 'multipart/form-data');
2219       form.submit();
2220       }
2221     
2222     // set reference to the form object
2223     this.gui_objects.attachmentform = form;
1c5853 2224     return true;
4e17e6 2225     };
T 2226
2227
2228   // add file name to attachment list
2229   // called from upload page
9a5261 2230   this.add2attachment_list = function(name, content)
4e17e6 2231     {
T 2232     if (!this.gui_objects.attachmentlist)
2233       return false;
aade7b 2234       
4e17e6 2235     var li = document.createElement('LI');
a894ba 2236     li.id = name;
S 2237     li.innerHTML = content;
4e17e6 2238     this.gui_objects.attachmentlist.appendChild(li);
1c5853 2239     return true;
4e17e6 2240     };
T 2241
a894ba 2242   this.remove_from_attachment_list = function(name)
S 2243     {
2244     if (!this.gui_objects.attachmentlist)
2245       return false;
2246
2247     var list = this.gui_objects.attachmentlist.getElementsByTagName("li");
2248     for (i=0;i<list.length;i++)
2249       if (list[i].id == name)
9a5261 2250         this.gui_objects.attachmentlist.removeChild(list[i]);
41fa0b 2251     };
a894ba 2252
S 2253   this.remove_attachment = function(name)
2254     {
2255     if (name)
4d4264 2256       this.http_request('remove-attachment', '_file='+urlencode(name));
a894ba 2257
S 2258     return true;
41fa0b 2259     };
4e17e6 2260
T 2261   // send remote request to add a new contact
2262   this.add_contact = function(value)
2263     {
2264     if (value)
2265       this.http_request('addcontact', '_address='+value);
1c5853 2266     
S 2267     return true;
4e17e6 2268     };
T 2269
4647e1 2270   // send remote request to search mail
T 2271   this.qsearch = function(value, mbox)
2272     {
2273     if (value && mbox)
2274       {
2275       this.clear_message_list();
2276       this.set_busy(true, 'searching');
2277       this.http_request('search', '_search='+value+'&_mbox='+mbox, true);
2278       }
1c5853 2279     return true;
4647e1 2280     };
T 2281
2282   // reset quick-search form
2283   this.reset_qsearch = function()
2284     {
2285     if (this.gui_objects.qsearchbox)
2286       this.gui_objects.qsearchbox.value = '';
2287       
2288     this.env.search_request = null;
1c5853 2289     return true;
4647e1 2290     };
41fa0b 2291
T 2292
2293   this.sent_successfully = function(msg)
2294     {
2295     this.list_mailbox();
2296     this.display_message(msg, 'confirmation', true);
2297     }
2298
4e17e6 2299
T 2300   /*********************************************************/
2301   /*********     keyboard live-search methods      *********/
2302   /*********************************************************/
2303
2304
2305   // handler for keyboard events on address-fields
2306   this.ksearch_keypress = function(e, obj)
2307     {
2308     if (typeof(this.env.contacts)!='object' || !this.env.contacts.length)
2309       return true;
2310
2311     if (this.ksearch_timer)
2312       clearTimeout(this.ksearch_timer);
2313
2314     if (!e)
2315       e = window.event;
2316       
2317     var highlight;
2318     var key = e.keyCode ? e.keyCode : e.which;
2319
2320     switch (key)
2321       {
2322       case 38:  // key up
2323       case 40:  // key down
2324         if (!this.ksearch_pane)
2325           break;
2326           
2327         var dir = key==38 ? 1 : 0;
2328         var next;
2329         
2330         highlight = document.getElementById('rcmksearchSelected');
2331         if (!highlight)
2332           highlight = this.ksearch_pane.ul.firstChild;
2333         
2334         if (highlight && (next = dir ? highlight.previousSibling : highlight.nextSibling))
2335           {
2336           highlight.removeAttribute('id');
2337           //highlight.removeAttribute('class');
2338           this.set_classname(highlight, 'selected', false);
2339           }
2340
2341         if (next)
2342           {
2343           next.setAttribute('id', 'rcmksearchSelected');
2344           this.set_classname(next, 'selected', true);
2345           this.ksearch_selected = next._rcm_id;
2346           }
2347
2348         if (e.preventDefault)
2349           e.preventDefault();
2350         return false;
2351
2352       case 9:  // tab
2353         if(e.shiftKey)
2354           break;
2355
2356       case 13:  // enter     
2357         if (this.ksearch_selected===null || !this.ksearch_input || !this.ksearch_value)
2358           break;
2359
2360         // get cursor pos
2361         var inp_value = this.ksearch_input.value.toLowerCase();
2362         var cpos = this.get_caret_pos(this.ksearch_input);
2363         var p = inp_value.lastIndexOf(this.ksearch_value, cpos);
2364         
2365         // replace search string with full address
2366         var pre = this.ksearch_input.value.substring(0, p);
2367         var end = this.ksearch_input.value.substring(p+this.ksearch_value.length, this.ksearch_input.value.length);
2368         var insert = this.env.contacts[this.ksearch_selected]+', ';
2369         this.ksearch_input.value = pre + insert + end;
2370         
2371         //this.ksearch_input.value = this.ksearch_input.value.substring(0, p)+insert;
2372         
2373         // set caret to insert pos
2374         cpos = p+insert.length;
2375         if (this.ksearch_input.setSelectionRange)
2376           this.ksearch_input.setSelectionRange(cpos, cpos);
2377         
2378         // hide ksearch pane
2379         this.ksearch_hide();
2380       
2381         if (e.preventDefault)
2382           e.preventDefault();
2383         return false;
2384
2385       case 27:  // escape
2386         this.ksearch_hide();
2387         break;
2388
2389       }
2390
2391     // start timer
2392     this.ksearch_timer = setTimeout(this.ref+'.ksearch_get_results()', 200);      
2393     this.ksearch_input = obj;
2394     
2395     return true;
2396     };
2397
2398
2399   // address search processor
2400   this.ksearch_get_results = function()
2401     {
2402     var inp_value = this.ksearch_input ? this.ksearch_input.value : null;
2403     if (inp_value===null)
2404       return;
2405
2406     // get string from current cursor pos to last comma
2407     var cpos = this.get_caret_pos(this.ksearch_input);
2408     var p = inp_value.lastIndexOf(',', cpos-1);
2409     var q = inp_value.substring(p+1, cpos);
2410
2411     // trim query string
2412     q = q.replace(/(^\s+|\s+$)/g, '').toLowerCase();
2413
2414     if (!q.length || q==this.ksearch_value)
2415       {
2416       if (!q.length && this.ksearch_pane && this.ksearch_pane.visible)
2417         this.ksearch_pane.show(0);
2418
2419       return;
2420       }
2421
2422     this.ksearch_value = q;
2423     
2424     // start searching the contact list
2425     var a_results = new Array();
2426     var a_result_ids = new Array();
2427     var c=0;
2428     for (var i=0; i<this.env.contacts.length; i++)
2429       {
2430       if (this.env.contacts[i].toLowerCase().indexOf(q)>=0)
2431         {
2432         a_results[c] = this.env.contacts[i];
2433         a_result_ids[c++] = i;
2434         
2435         if (c==15)  // limit search results
2436           break;
2437         }
2438       }
2439
2440     // display search results
2441     if (c && a_results.length)
2442       {
2443       var p, ul, li;
2444       
2445       // create results pane if not present
2446       if (!this.ksearch_pane)
2447         {
2448         ul = document.createElement('UL');
2449         this.ksearch_pane = new rcube_layer('rcmKSearchpane', {vis:0, zindex:30000});
2450         this.ksearch_pane.elm.appendChild(ul);
2451         this.ksearch_pane.ul = ul;
2452         }
2453       else
2454         ul = this.ksearch_pane.ul;
2455
2456       // remove all search results
2457       ul.innerHTML = '';
2458             
2459       // add each result line to list
2460       for (i=0; i<a_results.length; i++)
2461         {
2462         li = document.createElement('LI');
2463         li.innerHTML = a_results[i].replace(/</, '&lt;').replace(/>/, '&gt;');
2464         li._rcm_id = a_result_ids[i];
2465         ul.appendChild(li);
2466         }
2467
2468       // check if last selected item is still in result list
2469       if (this.ksearch_selected!==null)
2470         {
2471         p = find_in_array(this.ksearch_selected, a_result_ids);
2472         if (p>=0 && ul.childNodes)
2473           {
2474           ul.childNodes[p].setAttribute('id', 'rcmksearchSelected');
2475           this.set_classname(ul.childNodes[p], 'selected', true);
2476           }
2477         else
2478           this.ksearch_selected = null;
2479         }
2480       
2481       // if no item selected, select the first one
2482       if (this.ksearch_selected===null)
2483         {
2484         ul.firstChild.setAttribute('id', 'rcmksearchSelected');
2485         this.set_classname(ul.firstChild, 'selected', true);
2486         this.ksearch_selected = a_result_ids[0];
2487         }
2488
2489       // resize the containing layer to fit the list
2490       //this.ksearch_pane.resize(ul.offsetWidth, ul.offsetHeight);
2491     
2492       // move the results pane right under the input box and make it visible
2493       var pos = rcube_get_object_pos(this.ksearch_input);
2494       this.ksearch_pane.move(pos.x, pos.y+this.ksearch_input.offsetHeight);
2495       this.ksearch_pane.show(1); 
2496       }
2497     // hide results pane
2498     else
2499       this.ksearch_hide();
2500     };
2501
2502
2503   this.ksearch_blur = function(e, obj)
2504     {
2505     if (this.ksearch_timer)
2506       clearTimeout(this.ksearch_timer);
2507
2508     this.ksearch_value = '';      
2509     this.ksearch_input = null;
2510     
2511     this.ksearch_hide();
2512     };
2513
2514
2515   this.ksearch_hide = function()
2516     {
2517     this.ksearch_selected = null;
2518     
2519     if (this.ksearch_pane)
2520       this.ksearch_pane.show(0);    
2521     };
2522
2523
2524
2525   /*********************************************************/
2526   /*********         address book methods          *********/
2527   /*********************************************************/
2528
2529
2530   this.list_contacts = function(page)
2531     {
2532     var add_url = '';
2533     var target = window;
2534     
2535     if (page && this.current_page==page)
2536       return false;
2537
2538     // load contacts remotely
2539     if (this.gui_objects.contactslist)
2540       {
2541       this.list_contacts_remote(page);
2542       return;
2543       }
2544
2545     if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
2546       {
2547       target = window.frames[this.env.contentframe];
2548       add_url = '&_framed=1';
2549       }
2550
2551     this.set_busy(true, 'loading');
2552     location.href = this.env.comm_path+(page ? '&_page='+page : '')+add_url;
2553     };
2554
2555
2556   // send remote request to load contacts list
2557   this.list_contacts_remote = function(page)
2558     {
2559     // clear list
2560     var table = this.gui_objects.contactslist;
2561     var tbody = document.createElement('TBODY');
2562     table.insertBefore(tbody, table.tBodies[0]);
2563     table.tBodies[1].style.display = 'none';
2564     
2565     this.contact_rows = new Array();
2566     this.list_rows = this.contact_rows;
2567
2568     // send request to server
2569     var url = page ? '&_page='+page : '';
2570     this.set_busy(true, 'loading');
ecf759 2571     this.http_request('list', url, true);
4e17e6 2572     };
T 2573
2574
2575   // load contact record
2576   this.load_contact = function(cid, action, framed)
2577     {
2578     var add_url = '';
2579     var target = window;
2580     if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
2581       {
2582       add_url = '&_framed=1';
2583       target = window.frames[this.env.contentframe];
2584       document.getElementById(this.env.contentframe).style.visibility = 'inherit';
2585       }
2586     else if (framed)
2587       return false;
2588       
2589     //if (this.env.framed && add_url=='')
5e3512 2590     
4e17e6 2591     //  add_url = '&_framed=1';
T 2592     
2593     if (action && (cid || action=='add'))
2594       {
2595       this.set_busy(true);
2596       target.location.href = this.env.comm_path+'&_action='+action+'&_cid='+cid+add_url;
2597       }
1c5853 2598     return true;
4e17e6 2599     };
T 2600
2601
2602   this.delete_contacts = function()
2603     {
2604     // exit if no mailbox specified or if selection is empty
5e3512 2605     if (!(this.selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm')))
4e17e6 2606       return;
5e3512 2607       
4e17e6 2608     var a_cids = new Array();
T 2609
2610     if (this.env.cid)
2611       a_cids[a_cids.length] = this.env.cid;
2612     else
2613       {
2614       var id;
2615       for (var n=0; n<this.selection.length; n++)
2616         {
2617         id = this.selection[n];
2618         a_cids[a_cids.length] = id;
2619       
2620         // 'remove' row from list (just hide it)
2621         if (this.contact_rows[id].obj)
2622           this.contact_rows[id].obj.style.display = 'none';
2623         }
2624
2625       // hide content frame if we delete the currently displayed contact
2626       if (this.selection.length==1 && this.env.contentframe)
2627         {
2628         var elm = document.getElementById(this.env.contentframe);
2629         elm.style.visibility = 'hidden';
2630         }
2631       }
2632
2633     // send request to server
2634     this.http_request('delete', '_cid='+a_cids.join(',')+'&_from='+(this.env.action ? this.env.action : ''));
1c5853 2635     return true;
4e17e6 2636     };
T 2637
2638
2639   // update a contact record in the list
2640   this.update_contact_row = function(cid, cols_arr)
2641     {
2642     if (!this.contact_rows[cid] || !this.contact_rows[cid].obj)
2643       return false;
2644       
2645     var row = this.contact_rows[cid].obj;
1c5853 2646     for (var c=0; c<cols_arr.length; c++){
4e17e6 2647       if (row.cells[c])
T 2648         row.cells[c].innerHTML = cols_arr[c];
1c5853 2649     }
S 2650     return true;
4e17e6 2651     };
T 2652   
d1d2c4 2653   
S 2654   // load ldap search form
2655   this.ldappublicsearch = function(action)
2656     {
2657     var add_url = '';
2658     var target = window;
2659     if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
2660       {
2661       add_url = '&_framed=1';
2662       target = window.frames[this.env.contentframe];
2663       document.getElementById(this.env.contentframe).style.visibility = 'inherit';
2664       }
2665     else
2666       return false; 
2667
2668
2669     if (action == 'ldappublicsearch')
2670       target.location.href = this.env.comm_path+'&_action='+action+add_url;
1c5853 2671       
S 2672     return true;
d1d2c4 2673     };
S 2674  
2675   // add ldap contacts to address book
2676   this.add_ldap_contacts = function()
2677     {
2678     if (window.frames[this.env.contentframe].rcmail)
2679       {
2680       var frame = window.frames[this.env.contentframe];
2681
2682       // build the url
2683       var url    = '&_framed=1';
2684       var emails = '&_emails=';
2685       var names  = '&_names=';
2686       var end    = '';
2687       for (var n=0; n<frame.rcmail.selection.length; n++)
2688         {
2689         end = n < frame.rcmail.selection.length - 1 ? ',' : '';
2690         emails += frame.rcmail.ldap_contact_rows[frame.rcmail.selection[n]].obj.cells[1].innerHTML + end;
2691         names  += frame.rcmail.ldap_contact_rows[frame.rcmail.selection[n]].obj.cells[0].innerHTML + end;
2692         }
2693        
2694       frame.location.href = this.env.comm_path + '&_action=save&_framed=1' + emails + names;
2695       }
2696     return false;
2697     }
2698   
4e17e6 2699
T 2700
2701   /*********************************************************/
2702   /*********        user settings methods          *********/
2703   /*********************************************************/
2704
2705
2706   // load contact record
2707   this.load_identity = function(id, action)
2708     {
2709     if (action=='edit-identity' && (!id || id==this.env.iid))
1c5853 2710       return false;
4e17e6 2711
T 2712     var add_url = '';
2713     var target = window;
2714     if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
2715       {
2716       add_url = '&_framed=1';
2717       target = window.frames[this.env.contentframe];
2718       document.getElementById(this.env.contentframe).style.visibility = 'inherit';
2719       }
2720
2721     if (action && (id || action=='add-identity'))
2722       {
2723       this.set_busy(true);
2724       target.location.href = this.env.comm_path+'&_action='+action+'&_iid='+id+add_url;
2725       }
1c5853 2726     return true;
4e17e6 2727     };
T 2728
2729
2730
2731   this.delete_identity = function(id)
2732     {
2733     // exit if no mailbox specified or if selection is empty
2734     if (!(this.selection.length || this.env.iid))
2735       return;
2736     
2737     if (!id)
2738       id = this.env.iid ? this.env.iid : this.selection[0];
2739
2740 /*
2741     // 'remove' row from list (just hide it)
2742     if (this.identity_rows && this.identity_rows[id].obj)
2743       {
2744       this.clear_selection();
2745       this.identity_rows[id].obj.style.display = 'none';
2746       }
2747 */
2748
2749     // if (this.env.framed && id)
2750       this.set_busy(true);
1c5853 2751       location.href = this.env.comm_path+'&_action=delete-identity&_iid='+id;     
4e17e6 2752     // else if (id)
T 2753     //  this.http_request('delete-identity', '_iid='+id);
1c5853 2754     return true;
4e17e6 2755     };
T 2756
2757
24053e 2758   // tell server to create and subscribe a new mailbox
4e17e6 2759   this.create_folder = function(name)
T 2760     {
24053e 2761     if (this.edit_folder)
T 2762       this.reset_folder_rename();
2763
4e17e6 2764     var form;
T 2765     if ((form = this.gui_objects.editform) && form.elements['_folder_name'])
2766       name = form.elements['_folder_name'].value;
2767
2768     if (name)
4d4264 2769       this.http_request('create-folder', '_name='+urlencode(name), true);
4e17e6 2770     else if (form.elements['_folder_name'])
T 2771       form.elements['_folder_name'].focus();
2772     };
2773
24053e 2774
T 2775   // entry point for folder renaming
c8c1e0 2776   this.rename_folder = function(props)
S 2777     {
24053e 2778     var form, oldname, newname;
T 2779     
2780     // rename a specific mailbox
2781     if (props)
2782       this.edit_foldername(props);
2783
2784     // use a dropdown and input field (old behavior)
2785     else if ((form = this.gui_objects.editform) && form.elements['_folder_oldname'] && form.elements['_folder_newname'])
2786       {
c8c1e0 2787       oldname = form.elements['_folder_oldname'].value;
S 2788       newname = form.elements['_folder_newname'].value;
2789       }
2790
2791     if (oldname && newname)
4d4264 2792       this.http_request('rename-folder', '_folder_oldname='+urlencode(oldname)+'&_folder_newname='+urlencode(newname));
c8c1e0 2793     };
S 2794
4e17e6 2795
24053e 2796   // start editing the mailbox name.
T 2797   // this will replace the name string with an input field
2798   this.edit_foldername = function(folder)
4e17e6 2799     {
24053e 2800     var temp, row, form;
T 2801     var id = this.get_folder_row_id(folder);
2802
2803     // reset current renaming
2804     if (temp = this.edit_folder)
2805       {
2806       this.reset_folder_rename();
2807       if (temp == id)
2808         return;
2809       }
2810
2811     if (id && (row = document.getElementById(id)))
4e17e6 2812       {
24053e 2813       this.name_input = document.createElement('INPUT');
4d4264 2814       this.name_input.value = this.env.subscriptionrows[id][1];
24053e 2815       this.name_input.style.width = '100%';
T 2816       this.name_input.onkeypress = function(e){ rcmail.name_input_keypress(e); };
2817       
2818       row.cells[0].replaceChild(this.name_input, row.cells[0].firstChild);
2819       this.edit_folder = id;
2820       this.name_input.select();
2821       
2822       if (form = this.gui_objects.editform)
2823         form.onsubmit = function(){ return false; };
4e17e6 2824       }
1cded8 2825     };
T 2826
2827
24053e 2828   // remove the input field and write the current mailbox name to the table cell
T 2829   this.reset_folder_rename = function()
1cded8 2830     {
24053e 2831     var cell = this.name_input ? this.name_input.parentNode : null;
e170b4 2832     if (cell && this.edit_folder && this.env.subscriptionrows[this.edit_folder])
4d4264 2833       cell.innerHTML = this.env.subscriptionrows[this.edit_folder][1];
24053e 2834       
T 2835     this.edit_folder = null;
2836     };
2837
2838
2839   // handler for keyboard events on the input field
2840   this.name_input_keypress = function(e)
2841     {
2842     var key = document.all ? event.keyCode : document.getElementById ? e.keyCode : 0;
2843
2844     // enter
2845     if (key==13)
2846       {
2847       var newname = this.name_input ? this.name_input.value : null;
2848       if (this.edit_folder && newname)
4d4264 2849         this.http_request('rename-folder', '_folder_oldname='+urlencode(this.env.subscriptionrows[this.edit_folder][0])+'&_folder_newname='+urlencode(newname));        
24053e 2850       }
T 2851     // escape
2852     else if (key==27)
2853       this.reset_folder_rename();
2854     };
2855
2856
2857   // delete a specific mailbox with all its messages
2858   this.delete_folder = function(folder)
2859     {
2860     if (this.edit_folder)
2861       this.reset_folder_rename();
2862     
2863     if (folder)
4d4264 2864       this.http_request('delete-folder', '_mboxes='+urlencode(folder));
24053e 2865     };
T 2866
2867
2868   // add a new folder to the subscription list by cloning a folder row
4d4264 2869   this.add_folder_row = function(name, display_name, replace)
24053e 2870     {
T 2871     name = name.replace('\\',"");
2872     if (!this.gui_objects.subscriptionlist)
2873       return false;
2874
2875     for (var refid in this.env.subscriptionrows)
2876       if (this.env.subscriptionrows[refid]!=null)
1cded8 2877         break;
T 2878
24053e 2879     var refrow, form;
T 2880     var tbody = this.gui_objects.subscriptionlist.tBodies[0];
2881     var id = replace && replace.id ? replace.id : tbody.childNodes.length+1;
2882
2883     if (!id || !(refrow = document.getElementById(refid)))
2884       {
2885       // Refresh page if we don't have a table row to clone
2886       location.href = this.env.comm_path+'&_action=folders';
2887       }
2888     else
2889       {
2890       // clone a table row if there are existing rows
2891       var row = this.clone_table_row(refrow);
2892       row.id = 'rcmrow'+id;
2893       if (replace)
2894         tbody.replaceChild(row, replace);
2895       else
2896         tbody.appendChild(row);
2897       }
2898
2899     // add to folder/row-ID map
4d4264 2900     this.env.subscriptionrows[row.id] = [name, display_name];
24053e 2901
T 2902     // set folder name
4d4264 2903     row.cells[0].innerHTML = display_name;
24053e 2904     if (row.cells[1] && row.cells[1].firstChild.tagName=='INPUT')
T 2905       {
2906       row.cells[1].firstChild.value = name;
2907       row.cells[1].firstChild.checked = true;
2908       }
2909        
2910     if (row.cells[2] && row.cells[2].firstChild.tagName=='A')
2911       row.cells[2].firstChild.onclick = new Function(this.ref+".command('rename-folder','"+name.replace('\'','\\\'')+"')");
2912     if (row.cells[3] && row.cells[3].firstChild.tagName=='A')
2913       row.cells[3].firstChild.onclick = new Function(this.ref+".command('delete-folder','"+name.replace('\'','\\\'')+"')");
2914
2915     // add new folder to rename-folder list and clear input field
f9c107 2916     if (!replace && (form = this.gui_objects.editform))
24053e 2917       {
f9c107 2918       if (form.elements['_folder_oldname'])
T 2919         form.elements['_folder_oldname'].options[form.elements['_folder_oldname'].options.length] = new Option(name,name);
2920       if (form.elements['_folder_name'])
2921         form.elements['_folder_name'].value = ''; 
24053e 2922       }
T 2923
2924     };
2925
2926
2927   // replace an existing table row with a new folder line
4d4264 2928   this.replace_folder_row = function(oldfolder, newfolder, display_name)
24053e 2929     {
T 2930     var id = this.get_folder_row_id(oldfolder);
2931     var row = document.getElementById(id);
2932     
2933     // replace an existing table row (if found)
4d4264 2934     this.add_folder_row(newfolder, display_name, row);
24053e 2935     this.env.subscriptionrows[id] = null;
T 2936     
2937     // rename folder in rename-folder dropdown
2938     var form, elm;
2939     if ((form = this.gui_objects.editform) && (elm = form.elements['_folder_oldname']))
2940       {
2941       for (var i=0;i<elm.options.length;i++)
2942         {
2943         if (elm.options[i].value == oldfolder)
2944           {
4d4264 2945           elm.options[i].text = display_name;
24053e 2946           elm.options[i].value = newfolder;
T 2947           break;
2948           }
2949         }
2950
2951       form.elements['_folder_newname'].value = '';
2952       }
2953     };
2954     
2955
2956   // remove the table row of a specific mailbox from the table
2957   // (the row will not be removed, just hidden)
2958   this.remove_folder_row = function(folder)
2959     {
1cded8 2960     var row;
24053e 2961     var id = this.get_folder_row_id(folder);
1cded8 2962     if (id && (row = document.getElementById(id)))
T 2963       row.style.display = 'none';    
c8c1e0 2964
S 2965     // remove folder from rename-folder list
2966     var form;
2967     if ((form = this.gui_objects.editform) && form.elements['_folder_oldname'])
2968       {
2969       for (var i=0;i<form.elements['_folder_oldname'].options.length;i++)
2970         {
2971         if (form.elements['_folder_oldname'].options[i].value == folder) 
24053e 2972           {
c8c1e0 2973           form.elements['_folder_oldname'].options[i] = null;
24053e 2974           break;
c8c1e0 2975           }
S 2976         }
2977       }
24053e 2978     
f9c107 2979     if (form && form.elements['_folder_newname'])
T 2980       form.elements['_folder_newname'].value = '';
4e17e6 2981     };
T 2982
2983
2984   this.subscribe_folder = function(folder)
2985     {
2986     var form;
2987     if ((form = this.gui_objects.editform) && form.elements['_unsubscribed'])
2988       this.change_subscription('_unsubscribed', '_subscribed', 'subscribe');
2989     else if (folder)
4d4264 2990       this.http_request('subscribe', '_mboxes='+urlencode(folder));
4e17e6 2991     };
T 2992
2993
2994   this.unsubscribe_folder = function(folder)
2995     {
2996     var form;
2997     if ((form = this.gui_objects.editform) && form.elements['_subscribed'])
2998       this.change_subscription('_subscribed', '_unsubscribed', 'unsubscribe');
2999     else if (folder)
4d4264 3000       this.http_request('unsubscribe', '_mboxes='+urlencode(folder));
4e17e6 3001     };
T 3002     
3003
3004   this.change_subscription = function(from, to, action)
3005     {
3006     var form;
3007     if (form = this.gui_objects.editform)
3008       {
3009       var a_folders = new Array();
3010       var list_from = form.elements[from];
3011
3012       for (var i=0; list_from && i<list_from.options.length; i++)
3013         {
3014         if (list_from.options[i] && list_from.options[i].selected)
3015           {
3016           a_folders[a_folders.length] = list_from.options[i].value;
3017           list_from[i] = null;
3018           i--;
3019           }
3020         }
3021
3022       // yes, we have some folders selected
3023       if (a_folders.length)
3024         {
3025         var list_to = form.elements[to];
3026         var index;
3027         
3028         for (var n=0; n<a_folders.length; n++)
3029           {
3030           index = list_to.options.length;
3031           list_to[index] = new Option(a_folders[n]);
3032           }
3033           
4d4264 3034         this.http_request(action, '_mboxes='+urlencode(a_folders.join(',')));
4e17e6 3035         }
T 3036       }
3037       
3038     };
3039
24053e 3040   // helper method to find a specific mailbox row ID
T 3041   this.get_folder_row_id = function(folder)
3042     {
3043     for (var id in this.env.subscriptionrows)
4d4264 3044       if (this.env.subscriptionrows[id] && this.env.subscriptionrows[id][0] == folder)
24053e 3045         break;
T 3046         
3047     return id;
3048     };
4e17e6 3049
T 3050   // duplicate a specific table row
3051   this.clone_table_row = function(row)
3052     {
3053     var cell, td;
3054     var new_row = document.createElement('TR');
3055     for(var n=0; n<row.childNodes.length; n++)
3056       {
3057       cell = row.childNodes[n];
3058       td = document.createElement('TD');
3059
3060       if (cell.className)
3061         td.className = cell.className;
3062       if (cell.align)
3063         td.setAttribute('align', cell.align);
3064         
3065       td.innerHTML = cell.innerHTML;
3066       new_row.appendChild(td);
3067       }
3068     
3069     return new_row;
3070     };
3071
3072
3073   /*********************************************************/
3074   /*********           GUI functionality           *********/
3075   /*********************************************************/
3076
3077
3078   // eable/disable buttons for page shifting
3079   this.set_page_buttons = function()
3080     {
3081     this.enable_command('nextpage', (this.env.pagecount > this.env.current_page));
3082     this.enable_command('previouspage', (this.env.current_page > 1));
3083     }
3084
3085
3086   // set button to a specific state
3087   this.set_button = function(command, state)
3088     {
3089     var a_buttons = this.buttons[command];
3090     var button, obj;
3091
3092     if(!a_buttons || !a_buttons.length)
3093       return;
3094
3095     for(var n=0; n<a_buttons.length; n++)
3096       {
3097       button = a_buttons[n];
3098       obj = document.getElementById(button.id);
3099
3100       // get default/passive setting of the button
3101       if (obj && button.type=='image' && !button.status)
3102         button.pas = obj._original_src ? obj._original_src : obj.src;
3103       else if (obj && !button.status)
3104         button.pas = String(obj.className);
3105
3106       // set image according to button state
3107       if (obj && button.type=='image' && button[state])
3108         {
3109         button.status = state;        
3110         obj.src = button[state];
3111         }
3112       // set class name according to button state
3113       else if (obj && typeof(button[state])!='undefined')
3114         {
3115         button.status = state;        
3116         obj.className = button[state];        
3117         }
3118       // disable/enable input buttons
3119       if (obj && button.type=='input')
3120         {
3121         button.status = state;
3122         obj.disabled = !state;
3123         }
3124       }
3125     };
3126
3127
3128   // mouse over button
3129   this.button_over = function(command, id)
3130     {
3131     var a_buttons = this.buttons[command];
3132     var button, img;
3133
3134     if(!a_buttons || !a_buttons.length)
3135       return;
3136
3137     for(var n=0; n<a_buttons.length; n++)
3138       {
3139       button = a_buttons[n];
3140       if(button.id==id && button.status=='act')
3141         {
3142         img = document.getElementById(button.id);
3143         if (img && button.over)
3144           img.src = button.over;
3145         }
3146       }
3147     };
3148
c8c1e0 3149   // mouse down on button
S 3150   this.button_sel = function(command, id)
3151     {
3152     var a_buttons = this.buttons[command];
3153     var button, img;
3154
3155     if(!a_buttons || !a_buttons.length)
3156       return;
3157
3158     for(var n=0; n<a_buttons.length; n++)
3159       {
3160       button = a_buttons[n];
3161       if(button.id==id && button.status=='act')
3162         {
3163         img = document.getElementById(button.id);
3164         if (img && button.sel)
3165           img.src = button.sel;
3166         }
3167       }
3168     };
4e17e6 3169
T 3170   // mouse out of button
3171   this.button_out = function(command, id)
3172     {
3173     var a_buttons = this.buttons[command];
3174     var button, img;
3175
3176     if(!a_buttons || !a_buttons.length)
3177       return;
3178
3179     for(var n=0; n<a_buttons.length; n++)
3180       {
3181       button = a_buttons[n];
3182       if(button.id==id && button.status=='act')
3183         {
3184         img = document.getElementById(button.id);
3185         if (img && button.act)
3186           img.src = button.act;
3187         }
3188       }
3189     };
3190
3191
3192   // set/unset a specific class name
3193   this.set_classname = function(obj, classname, set)
3194     {
3195     var reg = new RegExp('\s*'+classname, 'i');
3196     if (!set && obj.className.match(reg))
3197       obj.className = obj.className.replace(reg, '');
3198     else if (set && !obj.className.match(reg))
3199       obj.className += ' '+classname;
3200     };
3201
3202
3203   // display a specific alttext
3204   this.alttext = function(text)
3205     {
3206     
3207     };
3208
3209
3210   // display a system message
3211   this.display_message = function(msg, type, hold)
3212     {
f0f98f 3213     this.set_busy(false);
4e17e6 3214     if (!this.loaded)  // save message in order to display after page loaded
T 3215       {
3216       this.pending_message = new Array(msg, type);
3217       return true;
3218       }
f0f98f 3219   
4e17e6 3220     if (!this.gui_objects.message)
T 3221       return false;
f9c107 3222
4e17e6 3223     if (this.message_timer)
T 3224       clearTimeout(this.message_timer);
3225     
3226     var cont = msg;
3227     if (type)
3228       cont = '<div class="'+type+'">'+cont+'</div>';
a95e0e 3229
T 3230     this.gui_objects.message._rcube = this;
4e17e6 3231     this.gui_objects.message.innerHTML = cont;
T 3232     this.gui_objects.message.style.display = 'block';
f0f98f 3233  
a95e0e 3234     if (type!='loading')
T 3235       this.gui_objects.message.onmousedown = function(){ this._rcube.hide_message(); return true; };
4e17e6 3236     
T 3237     if (!hold)
3238       this.message_timer = setTimeout(this.ref+'.hide_message()', this.message_time);
3239     };
3240
3241
3242   // make a message row disapear
3243   this.hide_message = function()
3244     {
3245     if (this.gui_objects.message)
a95e0e 3246       {
4e17e6 3247       this.gui_objects.message.style.display = 'none';
a95e0e 3248       this.gui_objects.message.onmousedown = null;
T 3249       }
4e17e6 3250     };
T 3251
3252
3253   // mark a mailbox as selected and set environment variable
3254   this.select_mailbox = function(mbox)
3255     {
fe79b1 3256     if (this.gui_objects.mailboxlist )
4e17e6 3257       {
fe79b1 3258       var item, reg, text_obj;      
T 3259       var current_li = this.get_mailbox_li();
3260       var mbox_li = this.get_mailbox_li(mbox);
597170 3261       
fe79b1 3262       if (current_li)
T 3263         {
6a35c8 3264         this.set_classname(current_li, 'selected', false);
952860 3265         this.set_classname(current_li, 'unfocused', false);
S 3266         }
fe79b1 3267
T 3268       if (mbox_li || this.env.mailbox == mbox)
3269         {
3270         this.set_classname(mbox_li, 'unfocused', false);
6a35c8 3271         this.set_classname(mbox_li, 'selected', true);
fe79b1 3272         }
4e17e6 3273       }
fa4cd2 3274       
T 3275     // also update mailbox name in window title
3276     if (document.title)
3277       {
3278       var doc_title = String(document.title);
3279       var reg = new RegExp(this.env.mailbox.toLowerCase(), 'i');
3280       if (this.env.mailbox && doc_title.match(reg))
3281         document.title = doc_title.replace(reg, mbox).replace(/^\([0-9]+\)\s+/i, '');
3282       }
4e17e6 3283     
T 3284     this.env.mailbox = mbox;
3285     };
3286
24053e 3287
25d8ba 3288   // for reordering column array, Konqueror workaround
S 3289   this.set_message_coltypes = function(coltypes) 
3290   { 
24053e 3291     this.coltypes = coltypes;
T 3292     
3293     // set correct list titles
3294     var cell, col;
3295     var thead = this.gui_objects.messagelist ? this.gui_objects.messagelist.tHead : null;
3296     for (var n=0; thead && n<this.coltypes.length; n++) 
3297       {
3298       col = this.coltypes[n];
3299       if ((cell = thead.rows[0].cells[n+1]) && (col=='from' || col=='to'))
3300         {
3301         // if we have links for sorting, it's a bit more complicated...
3302         if (cell.firstChild && cell.firstChild.tagName=='A')
3303           {
3304           cell.firstChild.innerHTML = this.get_label(this.coltypes[n]);
3305           cell.firstChild.onclick = function(){ return rcmail.command('sort', this.__col, this); };
3306           cell.firstChild.__col = col;
3307           }
3308         else
3309           cell.innerHTML = this.get_label(this.coltypes[n]);
3310
3311         cell.id = 'rcmHead'+col;
3312         }
3313       }
3314
3315   };
4e17e6 3316
T 3317   // create a table row in the message list
15a9d1 3318   this.add_message_row = function(uid, cols, flags, attachment, attop)
4e17e6 3319     {
T 3320     if (!this.gui_objects.messagelist || !this.gui_objects.messagelist.tBodies[0])
3321       return false;
3322     
3323     var tbody = this.gui_objects.messagelist.tBodies[0];
3324     var rowcount = tbody.rows.length;
3325     var even = rowcount%2;
3326     
857a38 3327     this.env.messages[uid] = {deleted:flags.deleted?1:0,
S 3328                               replied:flags.replied?1:0,
4e17e6 3329                               unread:flags.unread?1:0};
T 3330     
3331     var row = document.createElement('TR');
3332     row.id = 'rcmrow'+uid;
15a9d1 3333     row.className = 'message '+(even ? 'even' : 'odd')+(flags.unread ? ' unread' : '')+(flags.deleted ? ' deleted' : '');
T 3334     
4e17e6 3335     if (this.in_selection(uid))
T 3336       row.className += ' selected';
3337
857a38 3338     var icon = flags.deleted && this.env.deletedicon ? this.env.deletedicon:
S 3339                (flags.unread && this.env.unreadicon ? this.env.unreadicon :
3340                (flags.replied && this.env.repliedicon ? this.env.repliedicon : this.env.messageicon));
4e17e6 3341
T 3342     var col = document.createElement('TD');
3343     col.className = 'icon';
3344     col.innerHTML = icon ? '<img src="'+icon+'" alt="" border="0" />' : '';
3345     row.appendChild(col);
3346
3347     // add each submitted col
25d8ba 3348     for (var n = 0; n < this.coltypes.length; n++) 
S 3349       { 
3350       var c = this.coltypes[n];
4e17e6 3351       col = document.createElement('TD');
T 3352       col.className = String(c).toLowerCase();
3353       col.innerHTML = cols[c];
3354       row.appendChild(col);
3355       }
3356
3357     col = document.createElement('TD');
3358     col.className = 'icon';
3359     col.innerHTML = attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" border="0" />' : '';
3360     row.appendChild(col);
3361     
15a9d1 3362     if (attop && tbody.rows.length)
T 3363       tbody.insertBefore(row, tbody.firstChild);
3364     else
3365       tbody.appendChild(row);
3366       
4e17e6 3367     this.init_message_row(row);
T 3368     };
3369
3370
3371   // replace content of row count display
3372   this.set_rowcount = function(text)
3373     {
3374     if (this.gui_objects.countdisplay)
3375       this.gui_objects.countdisplay.innerHTML = text;
3376
3377     // update page navigation buttons
3378     this.set_page_buttons();
3379     };
3380
58e360 3381   // replace content of quota display
T 3382    this.set_quota = function(text)
3383      {
3384      if (this.gui_objects.quotadisplay)
3385        this.gui_objects.quotadisplay.innerHTML = text;
3386      };
3387                  
4e17e6 3388
T 3389   // update the mailboxlist
15a9d1 3390   this.set_unread_count = function(mbox, count, set_title)
4e17e6 3391     {
T 3392     if (!this.gui_objects.mailboxlist)
3393       return false;
25d8ba 3394
S 3395     if (mbox==this.env.mailbox)
3396       set_title = true;
3397
fe79b1 3398     var reg, text_obj;
T 3399     var item = this.get_mailbox_li(mbox);
15a9d1 3400     mbox = String(mbox).toLowerCase().replace(this.mbox_expression, '');
T 3401
3402     if (item && item.className && item.className.indexOf('mailbox '+mbox)>=0)
4e17e6 3403       {
15a9d1 3404       // set new text
T 3405       text_obj = item.firstChild;
3406       reg = /\s+\([0-9]+\)$/i;
4e17e6 3407
15a9d1 3408       if (count && text_obj.innerHTML.match(reg))
T 3409         text_obj.innerHTML = text_obj.innerHTML.replace(reg, ' ('+count+')');
3410       else if (count)
3411         text_obj.innerHTML += ' ('+count+')';
3412       else
3413         text_obj.innerHTML = text_obj.innerHTML.replace(reg, '');
25d8ba 3414
15a9d1 3415       // set the right classes
T 3416       this.set_classname(item, 'unread', count>0 ? true : false);
3417       }
3418
3419     // set unread count to window title
01c86f 3420     reg = /^\([0-9]+\)\s+/i;
25d8ba 3421     if (set_title && document.title)
15a9d1 3422       {
T 3423       var doc_title = String(document.title);
3424
3425       if (count && doc_title.match(reg))
3426         document.title = doc_title.replace(reg, '('+count+') ');
3427       else if (count)
3428         document.title = '('+count+') '+doc_title;
3429       else
3430         document.title = doc_title.replace(reg, '');
01c86f 3431       }
4e17e6 3432     };
T 3433
3434
3435   // add row to contacts list
3436   this.add_contact_row = function(cid, cols)
3437     {
3438     if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0])
3439       return false;
3440     
3441     var tbody = this.gui_objects.contactslist.tBodies[0];
3442     var rowcount = tbody.rows.length;
3443     var even = rowcount%2;
3444     
3445     var row = document.createElement('TR');
3446     row.id = 'rcmrow'+cid;
3447     row.className = 'contact '+(even ? 'even' : 'odd');
3448     
3449     if (this.in_selection(cid))
3450       row.className += ' selected';
3451
3452     // add each submitted col
3453     for (var c in cols)
3454       {
3455       col = document.createElement('TD');
3456       col.className = String(c).toLowerCase();
3457       col.innerHTML = cols[c];
3458       row.appendChild(col);
3459       }
3460     
3461     tbody.appendChild(row);
3462     this.init_table_row(row, 'contact_rows');
3463     };
3464
3465
a0109c 3466   this.toggle_editor = function(checkbox, textElementName)
S 3467     {
3468     var ischecked = checkbox.checked;
3469     if (ischecked)
3470       {
3471         tinyMCE.execCommand('mceAddControl', true, textElementName);
3472       }
3473     else
3474       {
3475         tinyMCE.execCommand('mceRemoveControl', true, textElementName);
3476       }
3477     }
4e17e6 3478
T 3479   /********************************************************/
3480   /*********          drag & drop methods         *********/
3481   /********************************************************/
3482
3483
3484   this.drag_mouse_move = function(e)
3485     {
3486     if (this.drag_start)
3487       {
3488       if (!this.draglayer)
3489         this.draglayer = new rcube_layer('rcmdraglayer', {x:0, y:0, width:300, vis:0, zindex:2000});
3490       
3491       // get subjects of selectedd messages
3492       var names = '';
3493       var c, subject, obj;
3494       for(var n=0; n<this.selection.length; n++)
3495         {
3496         if (n>12)  // only show 12 lines
3497           {
3498           names += '...';
3499           break;
3500           }
3501
3502         if (this.message_rows[this.selection[n]].obj)
3503           {
3504           obj = this.message_rows[this.selection[n]].obj;
3505           subject = '';
3506
3507           for(c=0; c<obj.childNodes.length; c++)
3508             if (!subject && obj.childNodes[c].nodeName=='TD' && obj.childNodes[c].firstChild && obj.childNodes[c].firstChild.nodeType==3)
3509               {
3510               subject = obj.childNodes[c].firstChild.data;
3511               names += (subject.length > 50 ? subject.substring(0, 50)+'...' : subject) + '<br />';
3512               }
3513           }
3514         }
3515         
3516       this.draglayer.write(names);
3517       this.draglayer.show(1);
3518       }
3519
3520     var pos = this.get_mouse_pos(e);
3521     this.draglayer.move(pos.x+20, pos.y-5);
3522     
3523     this.drag_start = false;
3524     this.drag_active = true;
3525     
3526     return false;
3527     };
3528
3529
3530   this.drag_mouse_up = function()
3531     {
3532     document.onmousemove = null;
3533     
3534     if (this.draglayer && this.draglayer.visible)
3535       this.draglayer.show(0);
3536       
3537     this.drag_active = false;
3538     
3539     return false;
3540     };
3541
3542
3543
3544   /********************************************************/
3545   /*********        remote request methods        *********/
3546   /********************************************************/
3547
3548
ecf759 3549   this.http_sockets = new Array();
T 3550   
3551   // find a non-busy socket or create a new one
3552   this.get_request_obj = function()
4e17e6 3553     {
ecf759 3554     for (var n=0; n<this.http_sockets.length; n++)
4e17e6 3555       {
ecf759 3556       if (!this.http_sockets[n].busy)
T 3557         return this.http_sockets[n];
4e17e6 3558       }
ecf759 3559     
T 3560     // create a new XMLHTTP object
3561     var i = this.http_sockets.length;
3562     this.http_sockets[i] = new rcube_http_request();
4e17e6 3563
ecf759 3564     return this.http_sockets[i];
T 3565     };
3566   
3567
3568   // send a http request to the server
3569   this.http_request = function(action, querystring, lock)
3570     {
3571     var request_obj = this.get_request_obj();
4e17e6 3572     querystring += '&_remote=1';
T 3573     
3574     // add timestamp to request url to avoid cacheing problems in Safari
3575     if (bw.safari)
3576       querystring += '&_ts='+(new Date().getTime());
3577
3578     // send request
ecf759 3579     if (request_obj)
4e17e6 3580       {
4d4264 3581       // prompt('request', this.env.comm_path+'&_action='+urlencode(action)+'&'+querystring);
T 3582       console('HTTP request: '+this.env.comm_path+'&_action='+action+'&'+querystring);
ecf759 3583
T 3584       if (lock)
3585         this.set_busy(true);
3586
3587       request_obj.__lock = lock ? true : false;
3588       request_obj.__action = action;
3589       request_obj.onerror = function(o){ rcube_webmail_client.http_error(o); };
3590       request_obj.oncomplete = function(o){ rcube_webmail_client.http_response(o); };
4d4264 3591       request_obj.GET(this.env.comm_path+'&_action='+action+'&'+querystring);
4e17e6 3592       }
T 3593     };
3594
3595
ecf759 3596   // handle HTTP response
T 3597   this.http_response = function(request_obj)
4e17e6 3598     {
ecf759 3599     var ctype = request_obj.get_header('Content-Type');
e1cf7c 3600     if (ctype){
ecf759 3601       ctype = String(ctype).toLowerCase();
e1cf7c 3602       var ctype_array=ctype.split(";");
S 3603       ctype = ctype_array[0];
3604     }
4e17e6 3605
c8c1e0 3606     this.set_busy(false);
4e17e6 3607
5e3512 3608   console(request_obj.get_text());
4e17e6 3609
ecf759 3610     // if we get javascript code from server -> execute it
c03095 3611     if (request_obj.get_text() && (ctype=='text/javascript' || ctype=='application/x-javascript'))
T 3612       eval(request_obj.get_text());
4e17e6 3613
ecf759 3614     // process the response data according to the sent action
T 3615     switch (request_obj.__action)
3616       {
3617       case 'delete':
3618       case 'moveto':
3619         if (this.env.action=='show')
3620           this.command('list');
3621         break;
15a9d1 3622
ecf759 3623       case 'list':
5e3512 3624         if (this.env.messagecount)
T 3625           this.enable_command('purge', (this.env.mailbox==this.env.trash_mailbox));
3626
15a9d1 3627       case 'expunge':
T 3628         this.enable_command('select-all', 'select-none', 'expunge', this.env.messagecount ? true : false);
e170b4 3629         break;
4e17e6 3630       }
ecf759 3631
T 3632     request_obj.reset();
4e17e6 3633     };
ecf759 3634
T 3635
3636   // handle HTTP request errors
3637   this.http_error = function(request_obj)
3638     {
9a5261 3639     //alert('Error sending request: '+request_obj.url);
ecf759 3640
T 3641     if (request_obj.__lock)
3642       this.set_busy(false);
3643
3644     request_obj.reset();
3645     request_obj.__lock = false;
3646     };
3647
3648
3649   // use an image to send a keep-alive siganl to the server
3650   this.send_keep_alive = function()
3651     {
3652     var d = new Date();
3653     this.http_request('keep-alive', '_t='+d.getTime());
3654     };
15a9d1 3655
ecf759 3656     
15a9d1 3657   // send periodic request to check for recent messages
T 3658   this.check_for_recent = function()
3659     {
aade7b 3660     if (this.busy)
T 3661       {
3662       this.send_keep_alive();
3663       return;
3664       }
3665
c8c1e0 3666     this.set_busy(true, 'checkingmail');
15a9d1 3667     var d = new Date();
T 3668     this.http_request('check-recent', '_t='+d.getTime());
3669     };
4e17e6 3670
T 3671
3672   /********************************************************/
3673   /*********            helper methods            *********/
3674   /********************************************************/
3675   
3676   // check if we're in show mode or if we have a unique selection
3677   // and return the message uid
3678   this.get_single_uid = function()
3679     {
3680     return this.env.uid ? this.env.uid : (this.selection.length==1 ? this.selection[0] : null);
3681     };
3682
3683   // same as above but for contacts
3684   this.get_single_cid = function()
3685     {
3686     return this.env.cid ? this.env.cid : (this.selection.length==1 ? this.selection[0] : null);
3687     };
3688
3689
8c2e58 3690 /* deprecated methods
T 3691
4e17e6 3692   // check if Shift-key is pressed on event
T 3693   this.check_shiftkey = function(e)
3694     {
3695     if(!e && window.event)
3696       e = window.event;
3697
3698     if(bw.linux && bw.ns4 && e.modifiers)
3699       return true;
3700     else if((bw.ns4 && e.modifiers & Event.SHIFT_MASK) || (e && e.shiftKey))
3701       return true;
3702     else
3703       return false;
3704     }
3705
1cded8 3706   // check if Shift-key is pressed on event
T 3707   this.check_ctrlkey = function(e)
3708     {
3709     if(!e && window.event)
3710       e = window.event;
3711
3712     if(bw.linux && bw.ns4 && e.modifiers)
3713       return true;
3714    else if (bw.mac)
3715        return this.check_shiftkey(e);
3716     else if((bw.ns4 && e.modifiers & Event.CTRL_MASK) || (e && e.ctrlKey))
3717       return true;
3718     else
3719       return false;
3720     }
8c2e58 3721 */
4e17e6 3722
8c2e58 3723   // returns modifier key (constants defined at top of file)
b11a00 3724   this.get_modifier = function(e)
T 3725     {
3726     var opcode = 0;
8c2e58 3727     e = e || window.event;
T 3728
3729     if (bw.mac && e)
3730       {
3731       opcode += (e.metaKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
3732       return opcode;    
3733       }
3734     if (e)
3735       {
b11a00 3736       opcode += (e.ctrlKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
8c2e58 3737       return opcode;
T 3738       }
b11a00 3739     if (e.cancelBubble)
8c2e58 3740       {
b11a00 3741       e.cancelBubble = true;
T 3742       e.returnValue = false;
8c2e58 3743       }
b11a00 3744     else if (e.preventDefault)
T 3745       e.preventDefault();
3746   }
3747
3748
4e17e6 3749   this.get_mouse_pos = function(e)
T 3750     {
3751     if(!e) e = window.event;
3752     var mX = (e.pageX) ? e.pageX : e.clientX;
3753     var mY = (e.pageY) ? e.pageY : e.clientY;
3754
3755     if(document.body && document.all)
3756       {
3757       mX += document.body.scrollLeft;
3758       mY += document.body.scrollTop;
3759       }
3760
3761     return { x:mX, y:mY };
3762     };
3763     
3764   
3765   this.get_caret_pos = function(obj)
3766     {
3767     if (typeof(obj.selectionEnd)!='undefined')
3768       return obj.selectionEnd;
3769
3770     else if (document.selection && document.selection.createRange)
3771       {
3772       var range = document.selection.createRange();
3773       if (range.parentElement()!=obj)
3774         return 0;
3775
3776       var gm = range.duplicate();
3777       if (obj.tagName=='TEXTAREA')
3778         gm.moveToElementText(obj);
3779       else
3780         gm.expand('textedit');
3781       
3782       gm.setEndPoint('EndToStart', range);
3783       var p = gm.text.length;
3784
3785       return p<=obj.value.length ? p : -1;
3786       }
3787
3788     else
3789       return obj.value.length;
3790     };
3791
3792
3793   this.set_caret2start = function(obj)
3794     {
3795     if (obj.createTextRange)
3796       {
3797       var range = obj.createTextRange();
3798       range.collapse(true);
3799       range.select();
3800       }
3801     else if (obj.setSelectionRange)
3802       obj.setSelectionRange(0,0);
3803
3804     obj.focus();
3805     };
3806
3807
3808   // set all fields of a form disabled
3809   this.lock_form = function(form, lock)
3810     {
3811     if (!form || !form.elements)
3812       return;
3813     
3814     var type;
3815     for (var n=0; n<form.elements.length; n++)
3816       {
3817       type = form.elements[n];
3818       if (type=='hidden')
3819         continue;
3820         
3821       form.elements[n].disabled = lock;
3822       }
3823     };
3824     
3825   }  // end object rcube_webmail
3826
3827
3828
ecf759 3829 // class for HTTP requests
T 3830 function rcube_http_request()
3831   {
3832   this.url = '';
3833   this.busy = false;
3834   this.xmlhttp = null;
3835
3836
3837   // reset object properties
3838   this.reset = function()
3839     {
3840     // set unassigned event handlers
3841     this.onloading = function(){ };
3842     this.onloaded = function(){ };
3843     this.oninteractive = function(){ };
3844     this.oncomplete = function(){ };
3845     this.onabort = function(){ };
3846     this.onerror = function(){ };
3847     
3848     this.url = '';
3849     this.busy = false;
3850     this.xmlhttp = null;
3851     }
3852
3853
3854   // create HTMLHTTP object
3855   this.build = function()
3856     {
3857     if (window.XMLHttpRequest)
3858       this.xmlhttp = new XMLHttpRequest();
3859     else if (window.ActiveXObject)
3860       this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
3861     else
3862       {
3863       
3864       }
3865     }
3866
a0109c 3867   // send GET request
ecf759 3868   this.GET = function(url)
T 3869     {
3870     this.build();
3871
3872     if (!this.xmlhttp)
3873       {
3874       this.onerror(this);
3875       return false;
3876       }
3877
3878     var ref = this;
3879     this.url = url;
3880     this.busy = true;
3881
3882     this.xmlhttp.onreadystatechange = function(){ ref.xmlhttp_onreadystatechange(); };
3883     this.xmlhttp.open('GET', url);
3884     this.xmlhttp.send(null);
3885     };
3886
3887
a0109c 3888   this.POST = function(url, body, contentType)
ecf759 3889     {
a0109c 3890     // default value for contentType if not provided
S 3891     contentType = typeof(contentType) != 'undefined' ?
3892         contentType : 'application/x-www-form-urlencoded';
3893
3894     this.build();
3895     
3896     if (!this.xmlhttp)
3897     {
3898        this.onerror(this);
3899        return false;
3900     }
3901
3902     var ref=this;
3903     this.url = url;
3904     this.busy = true;
3905     
3906     this.xmlhttp.onreadystatechange = function() { ref.xmlhttp_onreadystatechange(); };
3907     this.xmlhttp.open('POST', url, true);
3908     this.xmlhttp.setRequestHeader('Content-Type', contentType);
3909     this.xmlhttp.send(body);
ecf759 3910     };
T 3911
3912
3913   // handle onreadystatechange event
3914   this.xmlhttp_onreadystatechange = function()
3915     {
3916     if(this.xmlhttp.readyState == 1)
3917       this.onloading(this);
3918
3919     else if(this.xmlhttp.readyState == 2)
3920       this.onloaded(this);
3921
3922     else if(this.xmlhttp.readyState == 3)
3923       this.oninteractive(this);
3924
3925     else if(this.xmlhttp.readyState == 4)
3926       {
9a5261 3927       try {
T 3928         if (this.xmlhttp.status == 0)
3929           this.onabort(this);
3930         else if(this.xmlhttp.status == 200)
3931           this.oncomplete(this);
3932         else
3933           this.onerror(this);
3934
3935         this.busy = false;
3936         }
3937       catch(err)
3938         {
ecf759 3939         this.onerror(this);
9a5261 3940         this.busy = false;
T 3941         }
ecf759 3942       }
T 3943     }
3944
3945   // getter method for HTTP headers
3946   this.get_header = function(name)
3947     {
3948     return this.xmlhttp.getResponseHeader(name);
3949     };
3950
c03095 3951   this.get_text = function()
T 3952     {
3953     return this.xmlhttp.responseText;
3954     };
3955
3956   this.get_xml = function()
3957     {
3958     return this.xmlhttp.responseXML;
3959     };
ecf759 3960
T 3961   this.reset();
3962   
3963   }  // end class rcube_http_request
3964
3965
e170b4 3966 // helper function to call the init method with a delay
T 3967 function call_init(o)
3968   {
3969   if (window[o] && window[o].init)
3970     setTimeout(o+'.init()', 200);
3971   }
4e17e6 3972
T 3973 function console(str)
3974   {
3975   if (document.debugform && document.debugform.console)
3976     document.debugform.console.value += str+'\n--------------------------------------\n';
3977   }
3978