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