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