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