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