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