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