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