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