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