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