2 +-----------------------------------------------------------------------+
3 | RoundCube Webmail Client Script |
5 | This file is part of the RoundCube Webmail client |
6 | Copyright (C) 2005-2009, RoundCube Dev, - Switzerland |
7 | Licensed under the GNU GPL |
9 +-----------------------------------------------------------------------+
10 | Authors: Thomas Bruederli <roundcube@gmail.com> |
11 | Charles McNulty <charles@charlesmcnulty.com> |
12 +-----------------------------------------------------------------------+
13 | Requires: common.js, list.js |
14 +-----------------------------------------------------------------------+
16 $Id: app.js 2483 2009-05-15 10:22:29Z thomasb $
20 var rcube_webmail_client;
22 function rcube_webmail()
24 this.env = new Object();
25 this.labels = new Object();
26 this.buttons = new Object();
27 this.gui_objects = new Object();
28 this.commands = new Object();
29 this.onloads = new Array();
31 // create protected reference to myself
32 rcube_webmail_client = this;
33 this.ref = 'rcube_webmail_client';
36 // webmail client settings
37 this.dblclick_time = 500;
38 this.message_time = 3000;
40 this.identifier_expr = new RegExp('[^0-9a-z\-_]', 'gi');
42 // mimetypes supported by the browser (default settings)
43 this.mimetypes = new Array('text/plain', 'text/html', 'text/xml',
44 'image/jpeg', 'image/gif', 'image/png',
45 'application/x-javascript', 'application/pdf',
46 'application/x-shockwave-flash');
48 // default environment vars
49 this.env.keep_alive = 60; // seconds
50 this.env.request_timeout = 180; // seconds
51 this.env.draft_autosave = 0; // seconds
52 this.env.comm_path = './';
53 this.env.bin_path = './bin/';
54 this.env.blankpage = 'program/blank.gif';
56 // set environment variable(s)
57 this.set_env = function(p, value)
59 if (p != null && typeof(p) == 'object' && !value)
66 // add a localized label to the client environment
67 this.add_label = function(key, value)
69 this.labels[key] = value;
72 // add a button to the button list
73 this.register_button = function(command, id, type, act, sel, over)
75 if (!this.buttons[command])
76 this.buttons[command] = new Array();
78 var button_prop = {id:id, type:type};
79 if (act) button_prop.act = act;
80 if (sel) button_prop.sel = sel;
81 if (over) button_prop.over = over;
83 this.buttons[command][this.buttons[command].length] = button_prop;
86 // register a specific gui object
87 this.gui_object = function(name, id)
89 this.gui_objects[name] = id;
92 // execute the given script on load
93 this.add_onload = function(f)
95 this.onloads[this.onloads.length] = f;
98 // initialize webmail client
99 this.init = function()
102 this.task = this.env.task;
105 if (!bw.dom || !bw.xmlhttp_test())
107 this.goto_url('error', '_code=0x199');
111 // find all registered gui objects
112 for (var n in this.gui_objects)
113 this.gui_objects[n] = rcube_find_object(this.gui_objects[n]);
115 // tell parent window that this frame is loaded
116 if (this.env.framed && parent.rcmail && parent.rcmail.set_busy)
117 parent.rcmail.set_busy(false);
119 // enable general commands
120 this.enable_command('logout', 'mail', 'addressbook', 'settings', true);
122 if (this.env.permaurl)
123 this.enable_command('permaurl', true);
128 if (this.gui_objects.messagelist)
130 this.message_list = new rcube_list_widget(this.gui_objects.messagelist, {multiselect:true, draggable:true, keyboard:true, dblclick_time:this.dblclick_time});
131 this.message_list.row_init = function(o){ p.init_message_row(o); };
132 this.message_list.addEventListener('dblclick', function(o){ p.msglist_dbl_click(o); });
133 this.message_list.addEventListener('keypress', function(o){ p.msglist_keypress(o); });
134 this.message_list.addEventListener('select', function(o){ p.msglist_select(o); });
135 this.message_list.addEventListener('dragstart', function(o){ p.drag_start(o); });
136 this.message_list.addEventListener('dragmove', function(o, e){ p.drag_move(e); });
137 this.message_list.addEventListener('dragend', function(o){ p.drag_active = false; });
139 this.message_list.init();
140 this.enable_command('toggle_status', 'toggle_flag', true);
142 if (this.gui_objects.mailcontframe)
144 this.gui_objects.mailcontframe.onmousedown = function(e){ return p.click_on_list(e); };
145 document.onmouseup = function(e){ return p.doc_mouse_up(e); };
148 this.message_list.focus();
151 if (this.env.coltypes)
152 this.set_message_coltypes(this.env.coltypes);
154 // enable mail commands
155 this.enable_command('list', 'checkmail', 'compose', 'add-contact', 'search', 'reset-search', 'collapse-folder', true);
157 if (this.env.search_text != null && document.getElementById('quicksearchbox') != null)
158 document.getElementById('quicksearchbox').value = this.env.search_text;
160 if (this.env.action=='show' || this.env.action=='preview')
162 this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 'mark', 'viewsource', 'print', 'load-attachment', 'load-headers', true);
163 if (this.env.next_uid)
165 this.enable_command('nextmessage', true);
166 this.enable_command('lastmessage', true);
168 if (this.env.prev_uid)
170 this.enable_command('previousmessage', true);
171 this.enable_command('firstmessage', true);
175 if (this.env.trash_mailbox && this.env.mailbox != this.env.trash_mailbox)
176 this.set_alttext('delete', 'movemessagetotrash');
178 // make preview/message frame visible
179 if (this.env.action == 'preview' && this.env.framed && parent.rcmail)
181 this.enable_command('compose', 'add-contact', false);
182 parent.rcmail.show_contentframe(true);
185 if ((this.env.action=='show' || this.env.action=='preview') && this.env.blockedobjects)
187 if (this.gui_objects.remoteobjectsmsg)
188 this.gui_objects.remoteobjectsmsg.style.display = 'block';
189 this.enable_command('load-images', 'always-load', true);
192 if (this.env.action=='compose')
194 this.enable_command('add-attachment', 'send-attachment', 'remove-attachment', 'send', true);
195 if (this.env.spellcheck)
197 this.env.spellcheck.spelling_state_observer = function(s){ ref.set_spellcheck_state(s); };
198 this.set_spellcheck_state('ready');
199 if (rcube_find_object('_is_html').value == '1')
200 this.display_spellcheck_controls(false);
202 if (this.env.drafts_mailbox)
203 this.enable_command('savedraft', true);
205 document.onmouseup = function(e){ return p.doc_mouse_up(e); };
208 if (this.env.messagecount)
209 this.enable_command('select-all', 'select-none', 'expunge', true);
211 if (this.purge_mailbox_test())
212 this.enable_command('purge', true);
214 this.set_page_buttons();
216 // init message compose form
217 if (this.env.action=='compose')
218 this.init_messageform();
220 // show printing dialog
221 if (this.env.action=='print')
224 // get unread count for each mailbox
225 if (this.gui_objects.mailboxlist)
227 this.env.unread_counts = {};
228 this.gui_objects.folderlist = this.gui_objects.mailboxlist;
229 this.http_request('getunread', '');
232 // ask user to send MDN
233 if (this.env.mdn_request && this.env.uid)
235 var mdnurl = '_uid='+this.env.uid+'&_mbox='+urlencode(this.env.mailbox);
236 if (confirm(this.get_label('mdnrequest')))
237 this.http_post('sendmdn', mdnurl);
239 this.http_post('mark', mdnurl+'&_flag=mdnsent');
246 if (this.gui_objects.contactslist)
248 this.contact_list = new rcube_list_widget(this.gui_objects.contactslist, {multiselect:true, draggable:true, keyboard:true});
249 this.contact_list.addEventListener('keypress', function(o){ p.contactlist_keypress(o); });
250 this.contact_list.addEventListener('select', function(o){ p.contactlist_select(o); });
251 this.contact_list.addEventListener('dragstart', function(o){ p.drag_start(o); });
252 this.contact_list.addEventListener('dragmove', function(o, e){ p.drag_move(e); });
253 this.contact_list.addEventListener('dragend', function(o){ p.drag_active = false; });
254 this.contact_list.init();
257 this.contact_list.highlight_row(this.env.cid);
259 if (this.gui_objects.contactslist.parentNode)
261 this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return p.click_on_list(e); };
262 document.onmouseup = function(e){ return p.doc_mouse_up(e); };
265 this.contact_list.focus();
268 this.set_page_buttons();
270 if (this.env.address_sources && this.env.address_sources[this.env.source] && !this.env.address_sources[this.env.source].readonly)
271 this.enable_command('add', true);
274 this.enable_command('show', 'edit', true);
276 if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform)
277 this.enable_command('save', true);
279 this.enable_command('search', 'reset-search', 'moveto', 'import', true);
281 if (this.contact_list && this.contact_list.rowcount > 0)
282 this.enable_command('export', true);
284 this.enable_command('list', true);
289 this.enable_command('preferences', 'identities', 'save', 'folders', true);
291 if (this.env.action=='identities' || this.env.action=='edit-identity' || this.env.action=='add-identity') {
292 this.enable_command('add', this.env.identities_level < 2);
293 this.enable_command('delete', 'edit', true);
296 if (this.env.action=='edit-identity' || this.env.action=='add-identity')
297 this.enable_command('save', true);
299 if (this.env.action=='folders')
300 this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', true);
302 if (this.gui_objects.identitieslist)
304 this.identity_list = new rcube_list_widget(this.gui_objects.identitieslist, {multiselect:false, draggable:false, keyboard:false});
305 this.identity_list.addEventListener('select', function(o){ p.identity_select(o); });
306 this.identity_list.init();
307 this.identity_list.focus();
310 this.identity_list.highlight_row(this.env.iid);
313 if (this.gui_objects.subscriptionlist)
314 this.init_subscription_list();
319 var input_user = rcube_find_object('rcmloginuser');
320 var input_pass = rcube_find_object('rcmloginpwd');
321 var input_tz = rcube_find_object('rcmlogintz');
324 input_user.onkeyup = function(e){ return rcmail.login_user_keyup(e); };
325 if (input_user && input_user.value=='')
330 // detect client timezone
332 input_tz.value = new Date().getTimezoneOffset() / -60;
334 this.enable_command('login', true);
341 // enable basic commands
342 this.enable_command('logout', true);
344 // flag object as complete
348 if (this.pending_message)
349 this.display_message(this.pending_message[0], this.pending_message[1]);
351 // start keep-alive interval
352 this.start_keepalive();
354 // execute all foreign onload scripts
355 for (var i=0; i<this.onloads.length; i++)
357 if (typeof(this.onloads[i]) == 'string')
358 eval(this.onloads[i]);
359 else if (typeof(this.onloads[i]) == 'function')
364 // start interval for keep-alive/recent_check signal
365 this.start_keepalive = function()
367 if (this.env.keep_alive && !this.env.framed && this.task=='mail' && this.gui_objects.mailboxlist)
368 this._int = setInterval(function(){ ref.check_for_recent(false); }, this.env.keep_alive * 1000);
369 else if (this.env.keep_alive && !this.env.framed && this.task!='login')
370 this._int = setInterval(function(){ ref.send_keep_alive(); }, this.env.keep_alive * 1000);
373 this.init_message_row = function(row)
376 if (uid && this.env.messages[uid])
378 row.deleted = this.env.messages[uid].deleted ? true : false;
379 row.unread = this.env.messages[uid].unread ? true : false;
380 row.replied = this.env.messages[uid].replied ? true : false;
381 row.flagged = this.env.messages[uid].flagged ? true : false;
382 row.forwarded = this.env.messages[uid].forwarded ? true : false;
385 // set eventhandler to message icon
386 if ((row.icon = row.obj.cells[0].childNodes[0]) && row.icon.nodeName=='IMG')
389 row.icon.id = 'msgicn_'+row.uid;
390 row.icon._row = row.obj;
391 row.icon.onmousedown = function(e) { p.command('toggle_status', this); };
394 // global variable 'flagged_col' may be not defined yet
395 if (!this.env.flagged_col && this.env.coltypes)
398 if((found = find_in_array('flag', this.env.coltypes)) >= 0)
399 this.set_env('flagged_col', found+1);
402 // set eventhandler to flag icon, if icon found
403 if (this.env.flagged_col && (row.flagged_icon = row.obj.cells[this.env.flagged_col].childNodes[0])
404 && row.flagged_icon.nodeName=='IMG')
407 row.flagged_icon.id = 'flaggedicn_'+row.uid;
408 row.flagged_icon._row = row.obj;
409 row.flagged_icon.onmousedown = function(e) { p.command('toggle_flag', this); };
413 // init message compose form: set focus and eventhandlers
414 this.init_messageform = function()
416 if (!this.gui_objects.messageform)
419 //this.messageform = this.gui_objects.messageform;
420 var input_from = rcube_find_object('_from');
421 var input_to = rcube_find_object('_to');
422 var input_cc = rcube_find_object('_cc');
423 var input_bcc = rcube_find_object('_bcc');
424 var input_replyto = rcube_find_object('_replyto');
425 var input_subject = rcube_find_object('_subject');
426 var input_message = rcube_find_object('_message');
427 var draftid = rcube_find_object('_draft_saveid');
429 // init live search events
431 this.init_address_input_events(input_to);
433 this.init_address_input_events(input_cc);
435 this.init_address_input_events(input_bcc);
437 // add signature according to selected identity
438 if (input_from && input_from.type=='select-one' && (!draftid || draftid.value=='')
439 // if we have HTML editor, signature is added in callback
440 && rcube_find_object('_is_html').value != '1')
442 this.change_identity(input_from);
445 if (input_to && input_to.value=='')
447 else if (input_subject && input_subject.value=='')
448 input_subject.focus();
449 else if (input_message)
450 this.set_caret2start(input_message);
452 // get summary of all field values
453 this.compose_field_hash(true);
455 // start the auto-save timer
456 this.auto_save_start();
459 this.init_address_input_events = function(obj)
461 var handler = function(e){ return ref.ksearch_keypress(e,this); };
463 if (obj.addEventListener)
464 obj.addEventListener(bw.safari ? 'keydown' : 'keypress', handler, false);
466 obj.onkeydown = handler;
468 obj.setAttribute('autocomplete', 'off');
472 /*********************************************************/
473 /********* client command interface *********/
474 /*********************************************************/
476 // execute a specific command on the web client
477 this.command = function(command, props, obj)
485 // command not supported or allowed
486 if (!this.commands[command])
488 // pass command to parent window
489 if (this.env.framed && parent.rcmail && parent.rcmail.command)
490 parent.rcmail.command(command, props);
495 // check input before leaving compose step
496 if (this.task=='mail' && this.env.action=='compose' && (command=='list' || command=='mail' || command=='addressbook' || command=='settings'))
498 if (this.cmp_hash != this.compose_field_hash() && !confirm(this.get_label('notsentwarning')))
506 if (this.gui_objects.loginform)
507 this.gui_objects.loginform.submit();
511 this.goto_url('logout', '', true);
514 // commands to switch task
518 this.switch_task(command);
522 if (obj && obj.href && obj.target)
524 else if (this.env.permaurl)
525 parent.location.href = this.env.permaurl;
528 // misc list commands
530 if (this.task=='mail')
532 if (this.env.search_request<0 || (props != '' && (this.env.search_request && props != this.env.mailbox)))
533 this.reset_qsearch();
535 this.list_mailbox(props);
537 if (this.env.trash_mailbox)
538 this.set_alttext('delete', this.env.mailbox != this.env.trash_mailbox ? 'movemessagetotrash' : 'deletemessage');
540 else if (this.task=='addressbook')
542 if (this.env.search_request<0 || (this.env.search_request && props != this.env.source))
543 this.reset_qsearch();
545 this.list_contacts(props);
546 this.enable_command('add', (this.env.address_sources && !this.env.address_sources[props].readonly));
552 this.load_headers(obj);
557 // get the type of sorting
558 var a_sort = props.split('_');
559 var sort_col = a_sort[0];
560 var sort_order = a_sort[1] ? a_sort[1].toUpperCase() : null;
563 // no sort order specified: toggle
564 if (sort_order==null)
566 if (this.env.sort_col==sort_col)
567 sort_order = this.env.sort_order=='ASC' ? 'DESC' : 'ASC';
569 sort_order = this.env.sort_order;
572 if (this.env.sort_col==sort_col && this.env.sort_order==sort_order)
575 // set table header class
576 if (header = document.getElementById('rcm'+this.env.sort_col))
577 this.set_classname(header, 'sorted'+(this.env.sort_order.toUpperCase()), false);
578 if (header = document.getElementById('rcm'+sort_col))
579 this.set_classname(header, 'sorted'+sort_order, true);
581 // save new sort properties
582 this.env.sort_col = sort_col;
583 this.env.sort_order = sort_order;
585 // reload message list
586 this.list_mailbox('', '', sort_col+'_'+sort_order);
590 this.list_page('next');
594 this.list_page('last');
598 this.list_page('prev');
602 this.list_page('first');
606 if (this.env.messagecount)
607 this.expunge_mailbox(this.env.mailbox);
611 case 'empty-mailbox':
612 if (this.env.messagecount)
613 this.purge_mailbox(this.env.mailbox);
617 // common commands used in multiple tasks
619 if (this.task=='mail')
621 var uid = this.get_single_uid();
622 if (uid && (!this.env.uid || uid != this.env.uid))
624 if (this.env.mailbox == this.env.drafts_mailbox)
625 this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true);
627 this.show_message(uid);
630 else if (this.task=='addressbook')
632 var cid = props ? props : this.get_single_cid();
633 if (cid && !(this.env.action=='show' && cid==this.env.cid))
634 this.load_contact(cid, 'show');
639 if (this.task=='addressbook')
640 this.load_contact(0, 'add');
641 else if (this.task=='settings')
643 this.identity_list.clear_selection();
644 this.load_identity(0, 'add-identity');
650 if (this.task=='addressbook' && (cid = this.get_single_cid()))
651 this.load_contact(cid, 'edit');
652 else if (this.task=='settings' && props)
653 this.load_identity(props, 'edit-identity');
656 case 'save-identity':
658 if (this.gui_objects.editform)
660 var input_pagesize = rcube_find_object('_pagesize');
661 var input_name = rcube_find_object('_name');
662 var input_email = rcube_find_object('_email');
665 if (input_pagesize && isNaN(parseInt(input_pagesize.value)))
667 alert(this.get_label('nopagesizewarning'));
668 input_pagesize.focus();
671 // contacts/identities
674 if (input_name && input_name.value == '')
676 alert(this.get_label('nonamewarning'));
680 else if (input_email && !rcube_check_email(input_email.value))
682 alert(this.get_label('noemailwarning'));
688 this.gui_objects.editform.submit();
694 if (this.task=='mail')
695 this.delete_messages();
697 else if (this.task=='addressbook')
698 this.delete_contacts();
699 // user settings task
700 else if (this.task=='settings')
701 this.delete_identity();
705 // mail task commands
708 if (this.task == 'mail')
709 this.move_messages(props);
710 else if (this.task == 'addressbook' && this.drag_active)
711 this.copy_contact(null, props);
716 this.mark_message(props);
719 case 'toggle_status':
720 if (props && !props._row)
728 uid = props._row.uid;
730 // toggle read/unread
731 if (this.message_list.rows[uid].deleted) {
733 } else if (!this.message_list.rows[uid].unread)
737 this.mark_message(flag, uid);
741 if (props && !props._row)
745 var flag = 'flagged';
749 uid = props._row.uid;
750 // toggle flagged/unflagged
751 if (this.message_list.rows[uid].flagged)
754 this.mark_message(flag, uid);
758 if (this.env.uid && this.env.sender) {
759 this.add_contact(urlencode(this.env.sender));
760 window.setTimeout(function(){ ref.command('load-images'); }, 300);
766 this.show_message(this.env.uid, true, this.env.action=='preview');
769 case 'load-attachment':
770 var qstring = '_mbox='+urlencode(this.env.mailbox)+'&_uid='+this.env.uid+'&_part='+props.part;
772 // open attachment in frame if it's of a supported mimetype
773 if (this.env.uid && props.mimetype && find_in_array(props.mimetype, this.mimetypes)>=0)
775 if (props.mimetype == 'text/html')
776 qstring += '&_safe=1';
777 this.attachment_win = window.open(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1', 'rcubemailattachment');
778 if (this.attachment_win)
780 window.setTimeout(function(){ ref.attachment_win.focus(); }, 10);
785 this.goto_url('get', qstring+'&_download=1', false);
789 this.message_list.select_all(props);
793 this.message_list.clear_selection();
797 if (this.env.next_uid)
798 this.show_message(this.env.next_uid, false, this.env.action=='preview');
802 if (this.env.last_uid)
803 this.show_message(this.env.last_uid);
806 case 'previousmessage':
807 if (this.env.prev_uid)
808 this.show_message(this.env.prev_uid, false, this.env.action=='preview');
812 if (this.env.first_uid)
813 this.show_message(this.env.first_uid);
817 this.check_for_recent(true);
821 var url = this.env.comm_path+'&_action=compose';
823 if (this.task=='mail')
825 url += '&_mbox='+urlencode(this.env.mailbox);
827 if (this.env.mailbox==this.env.drafts_mailbox)
830 if (uid = this.get_single_uid())
831 url += '&_draft_uid='+uid;
834 url += '&_to='+urlencode(props);
836 // modify url if we're in addressbook
837 else if (this.task=='addressbook')
839 // switch to mail compose step directly
840 if (props && props.indexOf('@') > 0)
842 url = this.get_task_url('mail', url);
843 this.redirect(url + '&_to='+urlencode(props));
847 // use contact_id passed as command parameter
848 var a_cids = new Array();
850 a_cids[a_cids.length] = props;
851 // get selected contacts
852 else if (this.contact_list)
854 var selection = this.contact_list.get_selection();
855 for (var n=0; n<selection.length; n++)
856 a_cids[a_cids.length] = selection[n];
860 this.http_request('mailto', '_cid='+urlencode(a_cids.join(','))+'&_source='+urlencode(this.env.source), true);
865 // don't know if this is necessary...
866 url = url.replace(/&_framed=1/, "");
872 if (window.tinyMCE && tinyMCE.get('compose-body')) {
873 tinyMCE.execCommand('mceSpellCheck', true);
875 else if (this.env.spellcheck && this.env.spellcheck.spellCheck && this.spellcheck_ready) {
876 this.env.spellcheck.spellCheck(this.env.spellcheck.check_link);
877 this.set_spellcheck_state('checking');
882 // Reset the auto-save timer
883 self.clearTimeout(this.save_timer);
885 if (!this.gui_objects.messageform)
888 // if saving Drafts is disabled in main.inc.php
889 // or if compose form did not change
890 if (!this.env.drafts_mailbox || this.cmp_hash == this.compose_field_hash())
893 this.set_busy(true, 'savingmessage');
894 var form = this.gui_objects.messageform;
895 form.target = "savetarget";
896 form._draft.value = '1';
901 if (!this.gui_objects.messageform)
904 if (!this.check_compose_input())
907 // Reset the auto-save timer
908 self.clearTimeout(this.save_timer);
910 // all checks passed, send message
911 this.set_busy(true, 'sendingmessage');
912 var form = this.gui_objects.messageform;
913 form.target = "savetarget";
914 form._draft.value = '';
917 // clear timeout (sending could take longer)
918 clearTimeout(this.request_timer);
921 case 'add-attachment':
922 this.show_attachment_form(true);
924 case 'send-attachment':
925 // Reset the auto-save timer
926 self.clearTimeout(this.save_timer);
928 this.upload_file(props)
931 case 'remove-attachment':
932 this.remove_attachment(props);
938 if (uid = this.get_single_uid())
939 this.goto_url('compose', '_reply_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(command=='reply-all' ? '&_all=1' : ''), true);
944 if (uid = this.get_single_uid())
945 this.goto_url('compose', '_forward_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true);
950 if (uid = this.get_single_uid())
952 ref.printwin = window.open(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(this.env.safemode ? '&_safe=1' : ''));
955 window.setTimeout(function(){ ref.printwin.focus(); }, 20);
956 if (this.env.action != 'show')
957 this.mark_message('read', uid);
964 if (uid = this.get_single_uid())
966 ref.sourcewin = window.open(this.env.comm_path+'&_action=viewsource&_uid='+this.env.uid+'&_mbox='+urlencode(this.env.mailbox));
968 window.setTimeout(function(){ ref.sourcewin.focus(); }, 20);
973 this.add_contact(props);
978 if (!props && this.gui_objects.qsearchbox)
979 props = this.gui_objects.qsearchbox.value;
988 var s = this.env.search_request;
989 this.reset_qsearch();
991 if (s && this.env.mailbox)
992 this.list_mailbox(this.env.mailbox);
993 else if (s && this.task == 'addressbook')
994 this.list_contacts(this.env.source);
998 if (this.env.action == 'import' && this.gui_objects.importform) {
999 var file = document.getElementById('rcmimportfile');
1000 if (file && !file.value) {
1001 alert(this.get_label('selectimportfile'));
1004 this.gui_objects.importform.submit();
1005 this.set_busy(true, 'importwait');
1006 this.lock_form(this.gui_objects.importform, true);
1009 this.goto_url('import');
1013 if (this.contact_list.rowcount > 0) {
1014 var add_url = (this.env.source ? '_source='+urlencode(this.env.source)+'&' : '');
1015 if (this.env.search_request)
1016 add_url += '_search='+this.env.search_request;
1018 this.goto_url('export', add_url);
1022 // collapse/expand folder
1023 case 'collapse-folder':
1025 this.collapse_folder(props);
1028 // user settings commands
1034 this.goto_url('identities');
1037 case 'delete-identity':
1038 this.delete_identity();
1041 this.goto_url('folders');
1045 this.subscribe_folder(props);
1049 this.unsubscribe_folder(props);
1052 case 'create-folder':
1053 this.create_folder(props);
1056 case 'rename-folder':
1057 this.rename_folder(props);
1060 case 'delete-folder':
1061 this.delete_folder(props);
1066 return obj ? false : true;
1069 // set command enabled or disabled
1070 this.enable_command = function()
1072 var args = arguments;
1073 if(!args.length) return -1;
1076 var enable = args[args.length-1];
1078 for(var n=0; n<args.length-1; n++)
1081 this.commands[command] = enable;
1082 this.set_button(command, (enable ? 'act' : 'pas'));
1087 // lock/unlock interface
1088 this.set_busy = function(a, message)
1092 var msg = this.get_label(message);
1096 this.display_message(msg, 'loading', true);
1099 this.hide_message();
1102 //document.body.style.cursor = a ? 'wait' : 'default';
1104 if (this.gui_objects.editform)
1105 this.lock_form(this.gui_objects.editform, a);
1107 // clear pending timer
1108 if (this.request_timer)
1109 clearTimeout(this.request_timer);
1111 // set timer for requests
1112 if (a && this.env.request_timeout)
1113 this.request_timer = window.setTimeout(function(){ ref.request_timed_out(); }, this.env.request_timeout * 1000);
1116 // return a localized string
1117 this.get_label = function(name)
1119 if (this.labels[name])
1120 return this.labels[name];
1125 // switch to another application task
1126 this.switch_task = function(task)
1128 if (this.task===task && task!='mail')
1131 var url = this.get_task_url(task);
1133 url += '&_mbox=INBOX';
1138 this.get_task_url = function(task, url)
1141 url = this.env.comm_path;
1143 return url.replace(/_task=[a-z]+/, '_task='+task);
1146 // called when a request timed out
1147 this.request_timed_out = function()
1149 this.set_busy(false);
1150 this.display_message('Request timed out!', 'error');
1154 /*********************************************************/
1155 /********* event handling methods *********/
1156 /*********************************************************/
1158 this.doc_mouse_up = function(e)
1162 if (this.message_list) {
1163 if (!rcube_mouse_is_over(e, this.message_list.list))
1164 this.message_list.blur();
1165 model = this.env.mailboxes;
1167 else if (this.contact_list) {
1168 if (!rcube_mouse_is_over(e, this.contact_list.list))
1169 this.contact_list.blur();
1170 model = this.env.address_sources;
1172 else if (this.ksearch_value) {
1173 this.ksearch_blur();
1176 // handle mouse release when dragging
1177 if (this.drag_active && model && this.env.last_folder_target) {
1178 this.set_classname(this.get_folder_li(this.env.last_folder_target), 'droptarget', false);
1179 this.command('moveto', model[this.env.last_folder_target].id);
1180 this.env.last_folder_target = null;
1184 this.drag_start = function(list)
1186 this.initialBodyScrollTop = bw.ie ? 0 : window.pageYOffset;
1187 this.initialMailBoxScrollTop = document.getElementById("mailboxlist-container").scrollTop;
1189 var model = this.task == 'mail' ? this.env.mailboxes : this.env.address_sources;
1191 this.drag_active = true;
1192 if (this.preview_timer)
1193 clearTimeout(this.preview_timer);
1195 // save folderlist and folders location/sizes for droptarget calculation in drag_move()
1196 if (this.gui_objects.folderlist && model)
1198 var li, pos, list, height;
1199 list = rcube_find_object(this.task == 'mail' ? 'mailboxlist' : 'directorylist');
1200 pos = rcube_get_object_pos(list);
1201 this.env.folderlist_coords = {x1:pos.x, y1:pos.y, x2:pos.x + list.offsetWidth, y2:pos.y + list.offsetHeight};
1203 this.env.folder_coords = new Array();
1204 for (var k in model) {
1205 if (li = this.get_folder_li(k))
1207 pos = rcube_get_object_pos(li.firstChild);
1208 // only visible folders
1209 if (height = li.firstChild.offsetHeight)
1210 this.env.folder_coords[k] = {x1:pos.x, y1:pos.y, x2:pos.x + li.firstChild.offsetWidth, y2:pos.y + height};
1216 this.drag_move = function(e)
1218 if (this.gui_objects.folderlist && this.env.folder_coords)
1220 // offsets to compensate for scrolling while dragging a message
1221 var boffset = bw.ie ? -document.documentElement.scrollTop : this.initialBodyScrollTop;
1222 var moffset = this.initialMailBoxScrollTop-document.getElementById('mailboxlist-container').scrollTop;
1223 var toffset = -moffset-boffset;
1226 mouse = rcube_event.get_mouse_pos(e);
1227 pos = this.env.folderlist_coords;
1231 // if mouse pointer is outside of folderlist
1232 if (mouse.x < pos.x1 || mouse.x >= pos.x2
1233 || mouse.y < pos.y1 || mouse.y >= pos.y2)
1235 if (this.env.last_folder_target) {
1236 this.set_classname(this.get_folder_li(this.env.last_folder_target), 'droptarget', false);
1237 this.env.last_folder_target = null;
1243 for (var k in this.env.folder_coords)
1245 pos = this.env.folder_coords[k];
1246 if (this.check_droptarget(k) && ((mouse.x >= pos.x1) && (mouse.x < pos.x2)
1247 && (mouse.y >= pos.y1) && (mouse.y < pos.y2)))
1249 this.set_classname(this.get_folder_li(k), 'droptarget', true);
1250 this.env.last_folder_target = k;
1253 this.set_classname(this.get_folder_li(k), 'droptarget', false);
1258 this.collapse_folder = function(id)
1261 if ((li = this.get_folder_li(id)) &&
1262 (div = li.getElementsByTagName("div")[0]) &&
1263 (div.className.match(/collapsed/) || div.className.match(/expanded/)))
1265 var ul = li.getElementsByTagName("ul")[0];
1266 if (div.className.match(/collapsed/))
1268 ul.style.display = '';
1269 this.set_classname(div, 'collapsed', false);
1270 this.set_classname(div, 'expanded', true);
1271 var reg = new RegExp('&'+urlencode(id)+'&');
1272 this.set_env('collapsed_folders', this.env.collapsed_folders.replace(reg, ''));
1276 ul.style.display = 'none';
1277 this.set_classname(div, 'expanded', false);
1278 this.set_classname(div, 'collapsed', true);
1279 this.set_env('collapsed_folders', this.env.collapsed_folders+'&'+urlencode(id)+'&');
1281 // select parent folder if one of its childs is currently selected
1282 if (this.env.mailbox.indexOf(id + this.env.delimiter) == 0)
1283 this.command('list', id);
1286 // Work around a bug in IE6 and IE7, see #1485309
1287 if ((bw.ie6 || bw.ie7) &&
1289 (li.nextSibling.getElementsByTagName("ul").length>0) &&
1290 li.nextSibling.getElementsByTagName("ul")[0].style &&
1291 (li.nextSibling.getElementsByTagName("ul")[0].style.display!='none'))
1293 li.nextSibling.getElementsByTagName("ul")[0].style.display = 'none';
1294 li.nextSibling.getElementsByTagName("ul")[0].style.display = '';
1297 this.http_post('save-pref', '_name=collapsed_folders&_value='+urlencode(this.env.collapsed_folders));
1298 this.set_unread_count_display(id, false);
1302 this.click_on_list = function(e)
1304 if (this.gui_objects.qsearchbox)
1305 this.gui_objects.qsearchbox.blur();
1307 if (this.message_list)
1308 this.message_list.focus();
1309 else if (this.contact_list)
1310 this.contact_list.focus();
1313 if (mbox_li = this.get_folder_li())
1314 this.set_classname(mbox_li, 'unfocused', true);
1316 return rcube_event.get_button(e) == 2 ? true : rcube_event.cancel(e);
1319 this.msglist_select = function(list)
1321 if (this.preview_timer)
1322 clearTimeout(this.preview_timer);
1324 var selected = list.selection.length==1;
1326 // Hide certain command buttons when Drafts folder is selected
1327 if (this.env.mailbox == this.env.drafts_mailbox)
1329 this.enable_command('reply', 'reply-all', 'forward', false);
1330 this.enable_command('show', 'print', selected);
1331 this.enable_command('delete', 'moveto', 'mark', (list.selection.length > 0 ? true : false));
1335 this.enable_command('show', 'reply', 'reply-all', 'forward', 'print', selected);
1336 this.enable_command('delete', 'moveto', 'mark', (list.selection.length > 0 ? true : false));
1339 // start timer for message preview (wait for double click)
1340 if (selected && this.env.contentframe && !list.multi_selecting)
1341 this.preview_timer = window.setTimeout(function(){ ref.msglist_get_preview(); }, 200);
1342 else if (this.env.contentframe)
1343 this.show_contentframe(false);
1346 this.msglist_dbl_click = function(list)
1348 if (this.preview_timer)
1349 clearTimeout(this.preview_timer);
1351 var uid = list.get_single_selection();
1352 if (uid && this.env.mailbox == this.env.drafts_mailbox)
1353 this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true);
1355 this.show_message(uid, false, false);
1358 this.msglist_keypress = function(list)
1360 if (list.key_pressed == list.ENTER_KEY)
1361 this.command('show');
1362 else if (list.key_pressed == list.DELETE_KEY)
1363 this.command('delete');
1364 else if (list.key_pressed == list.BACKSPACE_KEY)
1365 this.command('delete');
1367 list.shiftkey = false;
1370 this.msglist_get_preview = function()
1372 var uid = this.get_single_uid();
1373 if (uid && this.env.contentframe && !this.drag_active)
1374 this.show_message(uid, false, true);
1375 else if (this.env.contentframe)
1376 this.show_contentframe(false);
1379 this.check_droptarget = function(id)
1381 if (this.task == 'mail')
1382 return (this.env.mailboxes[id] && this.env.mailboxes[id].id != this.env.mailbox && !this.env.mailboxes[id].virtual);
1383 else if (this.task == 'addressbook')
1384 return (id != this.env.source && this.env.address_sources[id] && !this.env.address_sources[id].readonly);
1385 else if (this.task == 'settings')
1386 return (id != this.env.folder);
1390 /*********************************************************/
1391 /********* (message) list functionality *********/
1392 /*********************************************************/
1394 // when user doble-clicks on a row
1395 this.show_message = function(id, safe, preview)
1400 var action = preview ? 'preview': 'show';
1401 var target = window;
1403 if (preview && this.env.contentframe && window.frames && window.frames[this.env.contentframe])
1405 target = window.frames[this.env.contentframe];
1406 add_url = '&_framed=1';
1410 add_url = '&_safe=1';
1412 // also send search request to get the right messages
1413 if (this.env.search_request)
1414 add_url += '&_search='+this.env.search_request;
1415 var url = '&_action='+action+'&_uid='+id+'&_mbox='+urlencode(this.env.mailbox)+add_url;
1416 if (action == 'preview' && String(target.location.href).indexOf(url) >= 0)
1417 this.show_contentframe(true);
1420 this.set_busy(true, 'loading');
1421 target.location.href = this.env.comm_path+url;
1423 // mark as read and change mbox unread counter
1424 if (action == 'preview' && this.message_list && this.message_list.rows[id] && this.message_list.rows[id].unread)
1426 this.set_message(id, 'unread', false);
1427 if (this.env.unread_counts[this.env.mailbox])
1429 this.env.unread_counts[this.env.mailbox] -= 1;
1430 this.set_unread_count(this.env.mailbox, this.env.unread_counts[this.env.mailbox], this.env.mailbox == 'INBOX');
1436 this.show_contentframe = function(show)
1439 if (this.env.contentframe && (frm = rcube_find_object(this.env.contentframe)))
1441 if (!show && window.frames[this.env.contentframe])
1443 if (window.frames[this.env.contentframe].location.href.indexOf(this.env.blankpage)<0)
1444 window.frames[this.env.contentframe].location.href = this.env.blankpage;
1446 else if (!bw.safari && !bw.konq)
1447 frm.style.display = show ? 'block' : 'none';
1450 if (!show && this.busy)
1451 this.set_busy(false);
1454 // list a specific page
1455 this.list_page = function(page)
1458 page = this.env.current_page+1;
1460 page = this.env.pagecount;
1461 if (page=='prev' && this.env.current_page>1)
1462 page = this.env.current_page-1;
1463 if (page=='first' && this.env.current_page>1)
1466 if (page > 0 && page <= this.env.pagecount)
1468 this.env.current_page = page;
1470 if (this.task=='mail')
1471 this.list_mailbox(this.env.mailbox, page);
1472 else if (this.task=='addressbook')
1473 this.list_contacts(this.env.source, page);
1477 // list messages of a specific mailbox using filter
1478 this.filter_mailbox = function(filter)
1481 if (this.gui_objects.qsearchbox)
1482 search = this.gui_objects.qsearchbox.value;
1484 this.message_list.clear();
1487 this.env.current_page = 1;
1488 this.set_busy(true, 'searching');
1489 this.http_request('search', '_filter='+filter
1490 + (search ? '&_q='+urlencode(search) : '')
1491 + (this.env.mailbox ? '&_mbox='+urlencode(this.env.mailbox) : ''), true);
1495 // list messages of a specific mailbox
1496 this.list_mailbox = function(mbox, page, sort)
1498 this.last_selected = 0;
1500 var target = window;
1503 mbox = this.env.mailbox;
1505 // add sort to url if set
1507 add_url += '&_sort=' + sort;
1509 // also send search request to get the right messages
1510 if (this.env.search_request)
1511 add_url += '&_search='+this.env.search_request;
1513 // set page=1 if changeing to another mailbox
1514 if (!page && mbox != this.env.mailbox)
1517 this.env.current_page = page;
1518 if (this.message_list)
1519 this.message_list.clear_selection();
1520 this.show_contentframe(false);
1523 if (mbox != this.env.mailbox || (mbox == this.env.mailbox && !page && !sort))
1524 add_url += '&_refresh=1';
1526 this.select_folder(mbox, this.env.mailbox);
1527 this.env.mailbox = mbox;
1529 // load message list remotely
1530 if (this.gui_objects.messagelist)
1532 this.list_mailbox_remote(mbox, page, add_url);
1536 if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
1538 target = window.frames[this.env.contentframe];
1539 add_url += '&_framed=1';
1542 // load message list to target frame/window
1545 this.set_busy(true, 'loading');
1546 target.location.href = this.env.comm_path+'&_mbox='+urlencode(mbox)+(page ? '&_page='+page : '')+add_url;
1550 // send remote request to load message list
1551 this.list_mailbox_remote = function(mbox, page, add_url)
1553 // clear message list first
1554 this.message_list.clear();
1556 // send request to server
1557 var url = '_mbox='+urlencode(mbox)+(page ? '&_page='+page : '');
1558 this.set_busy(true, 'loading');
1559 this.http_request('list', url+add_url, true);
1562 this.expunge_mailbox = function(mbox)
1567 // lock interface if it's the active mailbox
1568 if (mbox == this.env.mailbox)
1571 this.set_busy(true, 'loading');
1572 add_url = '&_reload=1';
1575 // send request to server
1576 var url = '_mbox='+urlencode(mbox);
1577 this.http_post('expunge', url+add_url, lock);
1580 this.purge_mailbox = function(mbox)
1585 if (!confirm(this.get_label('purgefolderconfirm')))
1588 // lock interface if it's the active mailbox
1589 if (mbox == this.env.mailbox)
1592 this.set_busy(true, 'loading');
1593 add_url = '&_reload=1';
1596 // send request to server
1597 var url = '_mbox='+urlencode(mbox);
1598 this.http_post('purge', url+add_url, lock);
1602 // test if purge command is allowed
1603 this.purge_mailbox_test = function()
1605 return (this.env.messagecount && (this.env.mailbox == this.env.trash_mailbox || this.env.mailbox == this.env.junk_mailbox
1606 || this.env.mailbox.match('^' + RegExp.escape(this.env.trash_mailbox) + RegExp.escape(this.env.delimiter))
1607 || this.env.mailbox.match('^' + RegExp.escape(this.env.junk_mailbox) + RegExp.escape(this.env.delimiter))));
1611 this.set_message_icon = function(uid)
1614 var rows = this.message_list.rows;
1619 if (rows[uid].deleted && this.env.deletedicon)
1620 icn_src = this.env.deletedicon;
1621 else if (rows[uid].replied && this.env.repliedicon)
1623 if (rows[uid].forwarded && this.env.forwardedrepliedicon)
1624 icn_src = this.env.forwardedrepliedicon;
1626 icn_src = this.env.repliedicon;
1628 else if (rows[uid].forwarded && this.env.forwardedicon)
1629 icn_src = this.env.forwardedicon;
1630 else if (rows[uid].unread && this.env.unreadicon)
1631 icn_src = this.env.unreadicon;
1632 else if (this.env.messageicon)
1633 icn_src = this.env.messageicon;
1635 if (icn_src && rows[uid].icon)
1636 rows[uid].icon.src = icn_src;
1640 if (rows[uid].flagged && this.env.flaggedicon)
1641 icn_src = this.env.flaggedicon;
1642 else if (!rows[uid].flagged && this.env.unflaggedicon)
1643 icn_src = this.env.unflaggedicon;
1645 if (rows[uid].flagged_icon && icn_src)
1646 rows[uid].flagged_icon.src = icn_src;
1649 // set message status
1650 this.set_message_status = function(uid, flag, status)
1652 var rows = this.message_list.rows;
1654 if (!rows[uid]) return false;
1656 if (flag == 'unread')
1657 rows[uid].unread = status;
1658 else if(flag == 'deleted')
1659 rows[uid].deleted = status;
1660 else if (flag == 'replied')
1661 rows[uid].replied = status;
1662 else if (flag == 'forwarded')
1663 rows[uid].forwarded = status;
1664 else if (flag == 'flagged')
1665 rows[uid].flagged = status;
1667 this.env.messages[uid] = rows[uid];
1670 // set message row status, class and icon
1671 this.set_message = function(uid, flag, status)
1673 var rows = this.message_list.rows;
1675 if (!rows[uid]) return false;
1678 this.set_message_status(uid, flag, status);
1680 if (rows[uid].unread && rows[uid].classname.indexOf('unread')<0)
1682 rows[uid].classname += ' unread';
1683 this.set_classname(rows[uid].obj, 'unread', true);
1685 else if (!rows[uid].unread && rows[uid].classname.indexOf('unread')>=0)
1687 rows[uid].classname = rows[uid].classname.replace(/\s*unread/, '');
1688 this.set_classname(rows[uid].obj, 'unread', false);
1691 if (rows[uid].deleted && rows[uid].classname.indexOf('deleted')<0)
1693 rows[uid].classname += ' deleted';
1694 this.set_classname(rows[uid].obj, 'deleted', true);
1696 else if (!rows[uid].deleted && rows[uid].classname.indexOf('deleted')>=0)
1698 rows[uid].classname = rows[uid].classname.replace(/\s*deleted/, '');
1699 this.set_classname(rows[uid].obj, 'deleted', false);
1702 if (rows[uid].flagged && rows[uid].classname.indexOf('flagged')<0)
1704 rows[uid].classname += ' flagged';
1705 this.set_classname(rows[uid].obj, 'flagged', true);
1707 else if (!rows[uid].flagged && rows[uid].classname.indexOf('flagged')>=0)
1709 rows[uid].classname = rows[uid].classname.replace(/\s*flagged/, '');
1710 this.set_classname(rows[uid].obj, 'flagged', false);
1713 this.set_message_icon(uid);
1716 // move selected messages to the specified mailbox
1717 this.move_messages = function(mbox)
1719 // exit if current or no mailbox specified or if selection is empty
1720 if (!mbox || mbox == this.env.mailbox || (!this.env.uid && (!this.message_list || !this.message_list.get_selection().length)))
1724 var add_url = '&_target_mbox='+urlencode(mbox)+'&_from='+(this.env.action ? this.env.action : '');
1726 // show wait message
1727 if (this.env.action=='show')
1730 this.set_busy(true, 'movingmessage');
1732 else if (!this.env.flag_for_deletion)
1733 this.show_contentframe(false);
1735 // Hide message command buttons until a message is selected
1736 this.enable_command('reply', 'reply-all', 'forward', 'delete', 'mark', 'print', false);
1738 this._with_selected_messages('moveto', lock, add_url, (this.env.flag_for_deletion ? false : true));
1741 // delete selected messages from the current mailbox
1742 this.delete_messages = function()
1744 var selection = this.message_list ? this.message_list.get_selection() : new Array();
1746 // exit if no mailbox specified or if selection is empty
1747 if (!this.env.uid && !selection.length)
1750 // if there is a trash mailbox defined and we're not currently in it:
1751 if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase() != String(this.env.trash_mailbox).toLowerCase())
1753 // if shift was pressed delete it immediately
1754 if (this.message_list && this.message_list.shiftkey)
1756 if (confirm(this.get_label('deletemessagesconfirm')))
1757 this.permanently_remove_messages();
1760 this.move_messages(this.env.trash_mailbox);
1762 // if there is a trash mailbox defined but we *are* in it:
1763 else if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase() == String(this.env.trash_mailbox).toLowerCase())
1764 this.permanently_remove_messages();
1765 // if there isn't a defined trash mailbox and the config is set to flag for deletion
1766 else if (!this.env.trash_mailbox && this.env.flag_for_deletion)
1768 this.mark_message('delete');
1769 if(this.env.action=="show")
1770 this.command('nextmessage','',this);
1771 else if (selection.length == 1)
1772 this.message_list.select_next();
1774 // if there isn't a defined trash mailbox and the config is set NOT to flag for deletion
1775 else if (!this.env.trash_mailbox)
1776 this.permanently_remove_messages();
1779 // delete the selected messages permanently
1780 this.permanently_remove_messages = function()
1782 // exit if no mailbox specified or if selection is empty
1783 if (!this.env.uid && (!this.message_list || !this.message_list.get_selection().length))
1786 this.show_contentframe(false);
1787 this._with_selected_messages('delete', false, '&_from='+(this.env.action ? this.env.action : ''), true);
1790 // Send a specifc request with UIDs of all selected messages
1792 this._with_selected_messages = function(action, lock, add_url, remove)
1794 var a_uids = new Array();
1797 a_uids[0] = this.env.uid;
1800 var selection = this.message_list.get_selection();
1801 var rows = this.message_list.rows;
1803 for (var n=0; n<selection.length; n++)
1806 a_uids[a_uids.length] = id;
1809 this.message_list.remove_row(id, (n == selection.length-1));
1812 this.set_message_status(id, 'deleted', true);
1813 if (this.env.read_when_deleted)
1814 this.set_message_status(id, 'unread', false);
1815 this.set_message(id);
1820 // also send search request to get the right messages
1821 if (this.env.search_request)
1822 add_url += '&_search='+this.env.search_request;
1824 // send request to server
1825 this.http_post(action, '_uid='+a_uids.join(',')+'&_mbox='+urlencode(this.env.mailbox)+add_url, lock);
1828 // set a specific flag to one or more messages
1829 this.mark_message = function(flag, uid)
1831 var a_uids = new Array();
1832 var r_uids = new Array();
1833 var selection = this.message_list ? this.message_list.get_selection() : new Array();
1837 else if (this.env.uid)
1838 a_uids[0] = this.env.uid;
1839 else if (this.message_list)
1841 for (var n=0; n<selection.length; n++)
1843 a_uids[a_uids.length] = selection[n];
1847 if (!this.message_list)
1850 for (var id, n=0; n<a_uids.length; n++)
1853 if ((flag=='read' && this.message_list.rows[id].unread)
1854 || (flag=='unread' && !this.message_list.rows[id].unread)
1855 || (flag=='delete' && !this.message_list.rows[id].deleted)
1856 || (flag=='undelete' && this.message_list.rows[id].deleted)
1857 || (flag=='flagged' && !this.message_list.rows[id].flagged)
1858 || (flag=='unflagged' && this.message_list.rows[id].flagged))
1860 r_uids[r_uids.length] = id;
1872 this.toggle_read_status(flag, r_uids);
1876 this.toggle_delete_status(r_uids);
1880 this.toggle_flagged_status(flag, a_uids);
1885 // set class to read/unread
1886 this.toggle_read_status = function(flag, a_uids)
1888 // mark all message rows as read/unread
1889 for (var i=0; i<a_uids.length; i++)
1890 this.set_message(a_uids[i], 'unread', (flag=='unread' ? true : false));
1892 this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag='+flag);
1895 // set image to flagged or unflagged
1896 this.toggle_flagged_status = function(flag, a_uids)
1898 // mark all message rows as flagged/unflagged
1899 for (var i=0; i<a_uids.length; i++)
1900 this.set_message(a_uids[i], 'flagged', (flag=='flagged' ? true : false));
1902 this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag='+flag);
1905 // mark all message rows as deleted/undeleted
1906 this.toggle_delete_status = function(a_uids)
1908 var rows = this.message_list ? this.message_list.rows : new Array();
1910 if (a_uids.length==1)
1912 if (!rows.length || (rows[a_uids[0]] && !rows[a_uids[0]].deleted))
1913 this.flag_as_deleted(a_uids);
1915 this.flag_as_undeleted(a_uids);
1920 var all_deleted = true;
1921 for (var i=0; i<a_uids.length; i++)
1925 if (!rows[uid].deleted)
1927 all_deleted = false;
1934 this.flag_as_undeleted(a_uids);
1936 this.flag_as_deleted(a_uids);
1941 this.flag_as_undeleted = function(a_uids)
1943 for (var i=0; i<a_uids.length; i++)
1944 this.set_message(a_uids[i], 'deleted', false);
1946 this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag=undelete');
1950 this.flag_as_deleted = function(a_uids)
1953 var r_uids = new Array();
1954 var rows = this.message_list ? this.message_list.rows : new Array();
1956 for (var i=0; i<a_uids.length; i++)
1961 this.set_message(uid, 'deleted', true);
1962 if (rows[uid].unread)
1963 r_uids[r_uids.length] = uid;
1968 add_url = '&_ruid='+r_uids.join(',');
1970 this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag=delete'+add_url);
1974 // flag as read without mark request (called from backend)
1975 // argument should be a coma-separated list of uids
1976 this.flag_deleted_as_read = function(uids)
1979 var rows = this.message_list ? this.message_list.rows : new Array();
1980 var str = String(uids);
1981 var a_uids = new Array();
1983 a_uids = str.split(',');
1985 for (var uid, i=0; i<a_uids.length; i++)
1989 this.set_message(uid, 'unread', false);
1994 /*********************************************************/
1995 /********* login form methods *********/
1996 /*********************************************************/
1998 // handler for keyboard events on the _user field
1999 this.login_user_keyup = function(e)
2001 var key = rcube_event.get_keycode(e);
2005 if ((key==13) && (elm = rcube_find_object('_pass')))
2013 /*********************************************************/
2014 /********* message compose methods *********/
2015 /*********************************************************/
2017 // checks the input fields before sending a message
2018 this.check_compose_input = function()
2020 // check input fields
2021 var input_to = rcube_find_object('_to');
2022 var input_cc = rcube_find_object('_cc');
2023 var input_bcc = rcube_find_object('_bcc');
2024 var input_from = rcube_find_object('_from');
2025 var input_subject = rcube_find_object('_subject');
2026 var input_message = rcube_find_object('_message');
2028 // check sender (if have no identities)
2029 if (input_from.type == 'text' && !rcube_check_email(input_from.value, true))
2031 alert(this.get_label('nosenderwarning'));
2036 // check for empty recipient
2037 var recipients = input_to.value ? input_to.value : (input_cc.value ? input_cc.value : input_bcc.value);
2038 if (!rcube_check_email(recipients.replace(/^\s+/, '').replace(/[\s,;]+$/, ''), true))
2040 alert(this.get_label('norecipientwarning'));
2045 // display localized warning for missing subject
2046 if (input_subject && input_subject.value == '')
2048 var subject = prompt(this.get_label('nosubjectwarning'), this.get_label('nosubject'));
2050 // user hit cancel, so don't send
2051 if (!subject && subject !== '')
2053 input_subject.focus();
2058 input_subject.value = subject ? subject : this.get_label('nosubject');
2062 // check for empty body
2063 if ((!window.tinyMCE || !tinyMCE.get('compose-body')) && input_message.value == '' && !confirm(this.get_label('nobodywarning')))
2065 input_message.focus();
2068 else if (window.tinyMCE && tinyMCE.get('compose-body') && !tinyMCE.get('compose-body').getContent() && !confirm(this.get_label('nobodywarning')))
2070 tinyMCE.get('compose-body').focus();
2074 // Apply spellcheck changes if spell checker is active
2075 this.stop_spellchecking();
2080 this.stop_spellchecking = function()
2082 if (this.env.spellcheck && !this.spellcheck_ready) {
2083 exec_event(this.env.spellcheck.check_link, 'click');
2084 this.set_spellcheck_state('ready');
2088 this.display_spellcheck_controls = function(vis)
2090 if (this.env.spellcheck) {
2091 // stop spellchecking process
2093 this.stop_spellchecking();
2095 this.env.spellcheck.check_link.style.visibility = vis ? 'visible' : 'hidden';
2096 this.env.spellcheck.switch_lan_pic.style.visibility = vis ? 'visible' : 'hidden';
2100 this.set_spellcheck_state = function(s)
2102 this.spellcheck_ready = (s=='check_spelling' || s=='ready');
2103 this.enable_command('spellcheck', this.spellcheck_ready);
2106 this.set_draft_id = function(id)
2109 if (f = rcube_find_object('_draft_saveid'))
2113 this.auto_save_start = function()
2115 if (this.env.draft_autosave)
2116 this.save_timer = self.setTimeout(function(){ ref.command("savedraft"); }, this.env.draft_autosave * 1000);
2118 // Unlock interface now that saving is complete
2122 this.compose_field_hash = function(save)
2124 // check input fields
2125 var input_to = rcube_find_object('_to');
2126 var input_cc = rcube_find_object('_cc');
2127 var input_bcc = rcube_find_object('_bcc');
2128 var input_subject = rcube_find_object('_subject');
2129 var editor, input_message;
2132 if (input_to && input_to.value)
2133 str += input_to.value+':';
2134 if (input_cc && input_cc.value)
2135 str += input_cc.value+':';
2136 if (input_bcc && input_bcc.value)
2137 str += input_bcc.value+':';
2138 if (input_subject && input_subject.value)
2139 str += input_subject.value+':';
2141 if (editor = tinyMCE.get('compose-body'))
2142 str += editor.getContent();
2145 input_message = rcube_find_object('_message');
2146 str += input_message.value;
2150 this.cmp_hash = str;
2155 this.change_identity = function(obj)
2157 if (!obj || !obj.options)
2160 var id = obj.options[obj.selectedIndex].value;
2161 var input_message = rcube_find_object('_message');
2162 var message = input_message ? input_message.value : '';
2163 var is_html = (rcube_find_object('_is_html').value == '1');
2166 if (!this.env.identity)
2167 this.env.identity = id
2171 // remove the 'old' signature
2172 if (this.env.identity && this.env.signatures && this.env.signatures[this.env.identity])
2174 if (this.env.signatures[this.env.identity]['is_html'])
2175 sig = this.env.signatures[this.env.identity]['plain_text'];
2177 sig = this.env.signatures[this.env.identity]['text'];
2179 if (sig.indexOf('-- ')!=0)
2182 p = message.lastIndexOf(sig);
2184 message = message.substring(0, p-1) + message.substring(p+sig.length, message.length);
2187 message = message.replace(/[\r\n]+$/, '');
2189 // add the new signature string
2190 if (this.env.signatures && this.env.signatures[id])
2192 sig = this.env.signatures[id]['text'];
2193 if (this.env.signatures[id]['is_html'])
2195 sig = this.env.signatures[id]['plain_text'];
2197 if (sig.indexOf('-- ')!=0)
2199 message += '\n\n'+sig;
2204 var editor = tinyMCE.get('compose-body');
2206 if (this.env.signatures)
2208 // Append the signature as a div within the body
2209 var sigElem = editor.dom.get('_rc_sig');
2215 // add empty line before signature on IE
2217 editor.getBody().appendChild(editor.getDoc().createElement('br'));
2219 sigElem = editor.getDoc().createElement('div');
2220 sigElem.setAttribute('id', '_rc_sig');
2221 editor.getBody().appendChild(sigElem);
2224 if (this.env.signatures[id])
2226 newsig = this.env.signatures[id]['text'];
2227 htmlsig = this.env.signatures[id]['is_html'];
2230 if (htmlsig && this.env.signatures[id]['plain_text'].indexOf('-- ')!=0)
2231 newsig = '<p>-- </p>' + newsig;
2232 else if (!htmlsig && newsig.indexOf('-- ')!=0)
2233 newsig = '-- \n' + newsig;
2238 sigElem.innerHTML = newsig;
2240 sigElem.innerHTML = '<pre>' + newsig + '</pre>';
2245 input_message.value = message;
2247 this.env.identity = id;
2251 this.show_attachment_form = function(a)
2253 if (!this.gui_objects.uploadbox)
2257 if (elm = this.gui_objects.uploadbox)
2259 if (a && (list = this.gui_objects.attachmentlist))
2261 var pos = rcube_get_object_pos(list);
2263 var top = pos.y + list.offsetHeight + 10;
2265 elm.style.top = top+'px';
2266 elm.style.left = left+'px';
2269 elm.style.visibility = a ? 'visible' : 'hidden';
2272 // clear upload form
2274 if (!a && this.gui_objects.attachmentform != this.gui_objects.messageform)
2275 this.gui_objects.attachmentform.reset();
2277 catch(e){} // ignore errors
2282 // upload attachment file
2283 this.upload_file = function(form)
2288 // get file input fields
2290 for (var n=0; n<form.elements.length; n++)
2291 if (form.elements[n].type=='file' && form.elements[n].value)
2297 // create hidden iframe and post upload form
2300 var ts = new Date().getTime();
2301 var frame_name = 'rcmupload'+ts;
2303 // have to do it this way for IE
2304 // otherwise the form will be posted to a new window
2307 var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
2308 document.body.insertAdjacentHTML('BeforeEnd',html);
2310 else // for standards-compilant browsers
2312 var frame = document.createElement('IFRAME');
2313 frame.name = frame_name;
2314 frame.style.border = 'none';
2315 frame.style.width = 0;
2316 frame.style.height = 0;
2317 frame.style.visibility = 'hidden';
2318 document.body.appendChild(frame);
2321 form.target = frame_name;
2322 form.action = this.env.comm_path+'&_action=upload';
2323 form.setAttribute('enctype', 'multipart/form-data');
2327 // set reference to the form object
2328 this.gui_objects.attachmentform = form;
2332 // add file name to attachment list
2333 // called from upload page
2334 this.add2attachment_list = function(name, content)
2336 if (!this.gui_objects.attachmentlist)
2339 var li = document.createElement('LI');
2341 li.innerHTML = content;
2342 this.gui_objects.attachmentlist.appendChild(li);
2346 this.remove_from_attachment_list = function(name)
2348 if (!this.gui_objects.attachmentlist)
2351 var list = this.gui_objects.attachmentlist.getElementsByTagName("li");
2352 for (i=0;i<list.length;i++)
2353 if (list[i].id == name)
2354 this.gui_objects.attachmentlist.removeChild(list[i]);
2357 this.remove_attachment = function(name)
2360 this.http_post('remove-attachment', '_file='+urlencode(name));
2365 // send remote request to add a new contact
2366 this.add_contact = function(value)
2369 this.http_post('addcontact', '_address='+value);
2374 // send remote request to search mail or contacts
2375 this.qsearch = function(value, addurl)
2379 if (this.message_list)
2380 this.message_list.clear();
2381 else if (this.contact_list) {
2382 this.contact_list.clear(true);
2383 this.show_contentframe(false);
2386 if (this.gui_objects.search_filter)
2387 addurl = '&_filter=' + this.gui_objects.search_filter.value;
2390 this.env.current_page = 1;
2391 this.set_busy(true, 'searching');
2392 this.http_request('search', '_q='+urlencode(value)
2393 + (this.env.mailbox ? '&_mbox='+urlencode(this.env.mailbox) : '')
2394 + (this.env.source ? '&_source='+urlencode(this.env.source) : '')
2395 + (addurl ? addurl : ''), true);
2400 // reset quick-search form
2401 this.reset_qsearch = function()
2403 if (this.gui_objects.qsearchbox)
2404 this.gui_objects.qsearchbox.value = '';
2406 this.env.search_request = null;
2410 this.sent_successfully = function(type, msg)
2412 this.list_mailbox();
2413 this.display_message(msg, type, true);
2417 /*********************************************************/
2418 /********* keyboard live-search methods *********/
2419 /*********************************************************/
2421 // handler for keyboard events on address-fields
2422 this.ksearch_keypress = function(e, obj)
2424 if (this.ksearch_timer)
2425 clearTimeout(this.ksearch_timer);
2428 var key = rcube_event.get_keycode(e);
2429 var mod = rcube_event.get_modifier(e);
2434 case 40: // key down
2435 if (!this.ksearch_pane)
2438 var dir = key==38 ? 1 : 0;
2440 highlight = document.getElementById('rcmksearchSelected');
2442 highlight = this.ksearch_pane.ul.firstChild;
2445 this.ksearch_select(dir ? highlight.previousSibling : highlight.nextSibling);
2447 return rcube_event.cancel(e);
2450 if(mod == SHIFT_KEY)
2454 if (this.ksearch_selected===null || !this.ksearch_input || !this.ksearch_value)
2457 // insert selected address and hide ksearch pane
2458 this.insert_recipient(this.ksearch_selected);
2459 this.ksearch_hide();
2461 return rcube_event.cancel(e);
2464 this.ksearch_hide();
2469 if (mod != SHIFT_KEY)
2474 this.ksearch_timer = window.setTimeout(function(){ ref.ksearch_get_results(); }, 200);
2475 this.ksearch_input = obj;
2480 this.ksearch_select = function(node)
2482 var current = document.getElementById('rcmksearchSelected');
2483 if (current && node) {
2484 current.removeAttribute('id');
2485 this.set_classname(current, 'selected', false);
2489 node.setAttribute('id', 'rcmksearchSelected');
2490 this.set_classname(node, 'selected', true);
2491 this.ksearch_selected = node._rcm_id;
2495 this.insert_recipient = function(id)
2497 if (!this.env.contacts[id] || !this.ksearch_input)
2501 var inp_value = this.ksearch_input.value.toLowerCase();
2502 var cpos = this.get_caret_pos(this.ksearch_input);
2503 var p = inp_value.lastIndexOf(this.ksearch_value, cpos);
2505 // replace search string with full address
2506 var pre = this.ksearch_input.value.substring(0, p);
2507 var end = this.ksearch_input.value.substring(p+this.ksearch_value.length, this.ksearch_input.value.length);
2508 var insert = this.env.contacts[id]+', ';
2509 this.ksearch_input.value = pre + insert + end;
2511 // set caret to insert pos
2512 cpos = p+insert.length;
2513 if (this.ksearch_input.setSelectionRange)
2514 this.ksearch_input.setSelectionRange(cpos, cpos);
2517 // address search processor
2518 this.ksearch_get_results = function()
2520 var inp_value = this.ksearch_input ? this.ksearch_input.value : null;
2521 if (inp_value === null)
2524 if (this.ksearch_pane && this.ksearch_pane.visible)
2525 this.ksearch_pane.show(0);
2527 // get string from current cursor pos to last comma
2528 var cpos = this.get_caret_pos(this.ksearch_input);
2529 var p = inp_value.lastIndexOf(',', cpos-1);
2530 var q = inp_value.substring(p+1, cpos);
2532 // trim query string
2533 q = q.replace(/(^\s+|\s+$)/g, '').toLowerCase();
2535 // Don't (re-)search if string is empty or if the last results are still active
2536 if (!q.length || q == this.ksearch_value)
2539 this.ksearch_value = q;
2541 this.display_message(this.get_label('searching'), 'loading', true);
2542 this.http_post('autocomplete', '_search='+q);
2545 this.ksearch_query_results = function(results, search)
2547 // ignore this outdated search response
2548 if (search != this.ksearch_value)
2551 this.hide_message();
2552 this.env.contacts = results ? results : [];
2553 this.ksearch_display_results(this.env.contacts);
2556 this.ksearch_display_results = function (a_results)
2558 // display search results
2559 if (a_results.length && this.ksearch_input) {
2562 // create results pane if not present
2563 if (!this.ksearch_pane) {
2564 ul = document.createElement('UL');
2565 this.ksearch_pane = new rcube_layer('rcmKSearchpane', {vis:0, zindex:30000});
2566 this.ksearch_pane.elm.appendChild(ul);
2567 this.ksearch_pane.ul = ul;
2570 ul = this.ksearch_pane.ul;
2572 // remove all search results
2575 // add each result line to list
2576 for (i=0; i<a_results.length; i++) {
2577 li = document.createElement('LI');
2578 li.innerHTML = a_results[i].replace(new RegExp('('+this.ksearch_value+')', 'ig'), '##$1%%').replace(/</g, '<').replace(/>/g, '>').replace(/##([^%]+)%%/g, '<b>$1</b>');
2579 li.onmouseover = function(){ ref.ksearch_select(this); };
2580 li.onmouseup = function(){ ref.ksearch_click(this) };
2586 ul.firstChild.setAttribute('id', 'rcmksearchSelected');
2587 this.set_classname(ul.firstChild, 'selected', true);
2588 this.ksearch_selected = 0;
2590 // move the results pane right under the input box and make it visible
2591 var pos = rcube_get_object_pos(this.ksearch_input);
2592 this.ksearch_pane.move(pos.x, pos.y+this.ksearch_input.offsetHeight);
2593 this.ksearch_pane.show(1);
2595 // hide results pane
2597 this.ksearch_hide();
2600 this.ksearch_click = function(node)
2602 if (this.ksearch_input)
2603 this.ksearch_input.focus();
2605 this.insert_recipient(node._rcm_id);
2606 this.ksearch_hide();
2609 this.ksearch_blur = function()
2611 if (this.ksearch_timer)
2612 clearTimeout(this.ksearch_timer);
2614 this.ksearch_value = '';
2615 this.ksearch_input = null;
2617 this.ksearch_hide();
2621 this.ksearch_hide = function()
2623 this.ksearch_selected = null;
2625 if (this.ksearch_pane)
2626 this.ksearch_pane.show(0);
2630 /*********************************************************/
2631 /********* address book methods *********/
2632 /*********************************************************/
2634 this.contactlist_keypress = function(list)
2636 if (list.key_pressed == list.DELETE_KEY)
2637 this.command('delete');
2640 this.contactlist_select = function(list)
2642 if (this.preview_timer)
2643 clearTimeout(this.preview_timer);
2645 var id, frame, ref = this;
2646 if (id = list.get_single_selection())
2647 this.preview_timer = window.setTimeout(function(){ ref.load_contact(id, 'show'); }, 200);
2648 else if (this.env.contentframe)
2649 this.show_contentframe(false);
2651 this.enable_command('compose', list.selection.length > 0);
2652 this.enable_command('edit', (id && this.env.address_sources && !this.env.address_sources[this.env.source].readonly) ? true : false);
2653 this.enable_command('delete', list.selection.length && this.env.address_sources && !this.env.address_sources[this.env.source].readonly);
2658 this.list_contacts = function(src, page)
2661 var target = window;
2664 src = this.env.source;
2666 if (page && this.current_page==page && src == this.env.source)
2669 if (src != this.env.source)
2672 this.env.current_page = page;
2673 this.reset_qsearch();
2676 this.select_folder(src, this.env.source);
2677 this.env.source = src;
2679 // load contacts remotely
2680 if (this.gui_objects.contactslist)
2682 this.list_contacts_remote(src, page);
2686 if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
2688 target = window.frames[this.env.contentframe];
2689 add_url = '&_framed=1';
2692 // also send search request to get the correct listing
2693 if (this.env.search_request)
2694 add_url += '&_search='+this.env.search_request;
2696 this.set_busy(true, 'loading');
2697 target.location.href = this.env.comm_path+(src ? '&_source='+urlencode(src) : '')+(page ? '&_page='+page : '')+add_url;
2700 // send remote request to load contacts list
2701 this.list_contacts_remote = function(src, page)
2703 // clear message list first
2704 this.contact_list.clear(true);
2705 this.show_contentframe(false);
2706 this.enable_command('delete', 'compose', false);
2708 // send request to server
2709 var url = (src ? '_source='+urlencode(src) : '') + (page ? (src?'&':'') + '_page='+page : '');
2710 this.env.source = src;
2712 // also send search request to get the right messages
2713 if (this.env.search_request)
2714 url += '&_search='+this.env.search_request;
2716 this.set_busy(true, 'loading');
2717 this.http_request('list', url, true);
2720 // load contact record
2721 this.load_contact = function(cid, action, framed)
2724 var target = window;
2725 if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
2727 add_url = '&_framed=1';
2728 target = window.frames[this.env.contentframe];
2729 this.show_contentframe(true);
2734 if (action && (cid || action=='add') && !this.drag_active)
2736 this.set_busy(true);
2737 target.location.href = this.env.comm_path+'&_action='+action+'&_source='+urlencode(this.env.source)+'&_cid='+urlencode(cid) + add_url;
2742 // copy a contact to the specified target (group or directory)
2743 this.copy_contact = function(cid, to)
2746 cid = this.contact_list.get_selection().join(',');
2748 if (to != this.env.source && cid && this.env.address_sources[to] && !this.env.address_sources[to].readonly)
2749 this.http_post('copy', '_cid='+urlencode(cid)+'&_source='+urlencode(this.env.source)+'&_to='+urlencode(to));
2753 this.delete_contacts = function()
2755 // exit if no mailbox specified or if selection is empty
2756 var selection = this.contact_list.get_selection();
2757 if (!(selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm')))
2760 var a_cids = new Array();
2764 a_cids[a_cids.length] = this.env.cid;
2768 for (var n=0; n<selection.length; n++)
2771 a_cids[a_cids.length] = id;
2772 this.contact_list.remove_row(id, (n == selection.length-1));
2775 // hide content frame if we delete the currently displayed contact
2776 if (selection.length == 1)
2777 this.show_contentframe(false);
2780 // also send search request to get the right records from the next page
2781 if (this.env.search_request)
2782 qs += '&_search='+this.env.search_request;
2784 // send request to server
2785 this.http_post('delete', '_cid='+urlencode(a_cids.join(','))+'&_source='+urlencode(this.env.source)+'&_from='+(this.env.action ? this.env.action : '')+qs);
2789 // update a contact record in the list
2790 this.update_contact_row = function(cid, cols_arr)
2793 if (this.contact_list.rows[cid] && (row = this.contact_list.rows[cid].obj))
2795 for (var c=0; c<cols_arr.length; c++)
2797 row.cells[c].innerHTML = cols_arr[c];
2806 /*********************************************************/
2807 /********* user settings methods *********/
2808 /*********************************************************/
2810 this.init_subscription_list = function()
2813 this.subscription_list = new rcube_list_widget(this.gui_objects.subscriptionlist, {multiselect:false, draggable:true, keyboard:false, toggleselect:true});
2814 this.subscription_list.addEventListener('select', function(o){ p.subscription_select(o); });
2815 this.subscription_list.addEventListener('dragstart', function(o){ p.drag_active = true; });
2816 this.subscription_list.addEventListener('dragend', function(o){ p.subscription_move_folder(o); });
2817 this.subscription_list.row_init = function (row)
2819 var anchors = row.obj.getElementsByTagName('A');
2821 anchors[0].onclick = function() { p.rename_folder(row.id); return false; };
2823 anchors[1].onclick = function() { p.delete_folder(row.id); return false; };
2824 row.obj.onmouseover = function() { p.focus_subscription(row.id); };
2825 row.obj.onmouseout = function() { p.unfocus_subscription(row.id); };
2827 this.subscription_list.init();
2830 this.identity_select = function(list)
2833 if (id = list.get_single_selection())
2834 this.load_identity(id, 'edit-identity');
2837 // load contact record
2838 this.load_identity = function(id, action)
2840 if (action=='edit-identity' && (!id || id==this.env.iid))
2844 var target = window;
2845 if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
2847 add_url = '&_framed=1';
2848 target = window.frames[this.env.contentframe];
2849 document.getElementById(this.env.contentframe).style.visibility = 'inherit';
2852 if (action && (id || action=='add-identity'))
2854 this.set_busy(true);
2855 target.location.href = this.env.comm_path+'&_action='+action+'&_iid='+id+add_url;
2860 this.delete_identity = function(id)
2862 // exit if no mailbox specified or if selection is empty
2863 var selection = this.identity_list.get_selection();
2864 if (!(selection.length || this.env.iid))
2868 id = this.env.iid ? this.env.iid : selection[0];
2870 // if (this.env.framed && id)
2871 this.goto_url('delete-identity', '_iid='+id, true);
2875 this.focus_subscription = function(id)
2878 var reg = RegExp('['+RegExp.escape(this.env.delimiter)+']?[^'+RegExp.escape(this.env.delimiter)+']+$');
2880 if (this.drag_active && this.env.folder && (row = document.getElementById(id)))
2881 if (this.env.subscriptionrows[id] &&
2882 (folder = this.env.subscriptionrows[id][0]))
2884 if (this.check_droptarget(folder) &&
2885 !this.env.subscriptionrows[this.get_folder_row_id(this.env.folder)][2] &&
2886 (folder != this.env.folder.replace(reg, '')) &&
2887 (!folder.match(new RegExp('^'+RegExp.escape(this.env.folder+this.env.delimiter)))))
2889 this.set_env('dstfolder', folder);
2890 this.set_classname(row, 'droptarget', true);
2893 else if (this.env.folder.match(new RegExp(RegExp.escape(this.env.delimiter))))
2895 this.set_env('dstfolder', this.env.delimiter);
2896 this.set_classname(this.subscription_list.frame, 'droptarget', true);
2900 this.unfocus_subscription = function(id)
2903 this.set_env('dstfolder', null);
2904 if (this.env.subscriptionrows[id] &&
2905 (row = document.getElementById(id)))
2906 this.set_classname(row, 'droptarget', false);
2908 this.set_classname(this.subscription_list.frame, 'droptarget', false);
2911 this.subscription_select = function(list)
2914 if ((id = list.get_single_selection()) &&
2915 this.env.subscriptionrows['rcmrow'+id] &&
2916 (folder = this.env.subscriptionrows['rcmrow'+id][0]))
2917 this.set_env('folder', folder);
2919 this.set_env('folder', null);
2921 if (this.gui_objects.createfolderhint)
2922 this.gui_objects.createfolderhint.innerHTML = this.env.folder ? this.get_label('addsubfolderhint') : '';
2925 this.subscription_move_folder = function(list)
2927 var reg = RegExp('['+RegExp.escape(this.env.delimiter)+']?[^'+RegExp.escape(this.env.delimiter)+']+$');
2928 if (this.env.folder && this.env.dstfolder && (this.env.dstfolder != this.env.folder) &&
2929 (this.env.dstfolder != this.env.folder.replace(reg, '')))
2931 var reg = new RegExp('[^'+RegExp.escape(this.env.delimiter)+']*['+RegExp.escape(this.env.delimiter)+']', 'g');
2932 var basename = this.env.folder.replace(reg, '');
2933 var newname = this.env.dstfolder==this.env.delimiter ? basename : this.env.dstfolder+this.env.delimiter+basename;
2935 this.set_busy(true, 'foldermoving');
2936 this.http_post('rename-folder', '_folder_oldname='+urlencode(this.env.folder)+'&_folder_newname='+urlencode(newname), true);
2938 this.drag_active = false;
2939 this.unfocus_subscription(this.get_folder_row_id(this.env.dstfolder));
2942 // tell server to create and subscribe a new mailbox
2943 this.create_folder = function(name)
2945 if (this.edit_folder)
2946 this.reset_folder_rename();
2949 if ((form = this.gui_objects.editform) && form.elements['_folder_name'])
2951 name = form.elements['_folder_name'].value;
2953 if (name.indexOf(this.env.delimiter)>=0)
2955 alert(this.get_label('forbiddencharacter')+' ('+this.env.delimiter+')');
2959 if (this.env.folder && name != '')
2960 name = this.env.folder+this.env.delimiter+name;
2962 this.set_busy(true, 'foldercreating');
2963 this.http_post('create-folder', '_name='+urlencode(name), true);
2965 else if (form.elements['_folder_name'])
2966 form.elements['_folder_name'].focus();
2969 // start renaming the mailbox name.
2970 // this will replace the name string with an input field
2971 this.rename_folder = function(id)
2973 var temp, row, form;
2975 // reset current renaming
2976 if (temp = this.edit_folder)
2978 this.reset_folder_rename();
2983 if (id && this.env.subscriptionrows[id] && (row = document.getElementById(id)))
2985 var reg = new RegExp('.*['+RegExp.escape(this.env.delimiter)+']');
2986 this.name_input = document.createElement('INPUT');
2987 this.name_input.value = this.env.subscriptionrows[id][0].replace(reg, '');
2988 this.name_input.style.width = '100%';
2990 reg = new RegExp('['+RegExp.escape(this.env.delimiter)+']?[^'+RegExp.escape(this.env.delimiter)+']+$');
2991 this.name_input.__parent = this.env.subscriptionrows[id][0].replace(reg, '');
2992 this.name_input.onkeypress = function(e){ rcmail.name_input_keypress(e); };
2994 row.cells[0].replaceChild(this.name_input, row.cells[0].firstChild);
2995 this.edit_folder = id;
2996 this.name_input.select();
2998 if (form = this.gui_objects.editform)
2999 form.onsubmit = function(){ return false; };
3003 // remove the input field and write the current mailbox name to the table cell
3004 this.reset_folder_rename = function()
3006 var cell = this.name_input ? this.name_input.parentNode : null;
3008 if (cell && this.edit_folder && this.env.subscriptionrows[this.edit_folder])
3009 cell.innerHTML = this.env.subscriptionrows[this.edit_folder][1];
3011 this.edit_folder = null;
3014 // handler for keyboard events on the input field
3015 this.name_input_keypress = function(e)
3017 var key = rcube_event.get_keycode(e);
3022 var newname = this.name_input ? this.name_input.value : null;
3023 if (this.edit_folder && newname)
3025 if (newname.indexOf(this.env.delimiter)>=0)
3027 alert(this.get_label('forbiddencharacter')+' ('+this.env.delimiter+')');
3031 if (this.name_input.__parent)
3032 newname = this.name_input.__parent + this.env.delimiter + newname;
3034 this.set_busy(true, 'folderrenaming');
3035 this.http_post('rename-folder', '_folder_oldname='+urlencode(this.env.subscriptionrows[this.edit_folder][0])+'&_folder_newname='+urlencode(newname), true);
3040 this.reset_folder_rename();
3043 // delete a specific mailbox with all its messages
3044 this.delete_folder = function(id)
3046 var folder = this.env.subscriptionrows[id][0];
3048 if (this.edit_folder)
3049 this.reset_folder_rename();
3051 if (folder && confirm(this.get_label('deletefolderconfirm')))
3053 this.set_busy(true, 'folderdeleting');
3054 this.http_post('delete-folder', '_mboxes='+urlencode(folder), true);
3055 this.set_env('folder', null);
3057 if (this.gui_objects.createfolderhint)
3058 this.gui_objects.createfolderhint.innerHTML = '';
3062 // add a new folder to the subscription list by cloning a folder row
3063 this.add_folder_row = function(name, display_name, replace, before)
3065 if (!this.gui_objects.subscriptionlist)
3068 // find not protected folder
3069 for (var refid in this.env.subscriptionrows)
3070 if (this.env.subscriptionrows[refid]!=null && !this.env.subscriptionrows[refid][2])
3074 var tbody = this.gui_objects.subscriptionlist.tBodies[0];
3075 var id = 'rcmrow'+(tbody.childNodes.length+1);
3076 var selection = this.subscription_list.get_single_selection();
3078 if (replace && replace.id)
3084 if (!id || !(refrow = document.getElementById(refid)))
3086 // Refresh page if we don't have a table row to clone
3087 this.goto_url('folders');
3091 // clone a table row if there are existing rows
3092 var row = this.clone_table_row(refrow);
3095 if (before && (before = this.get_folder_row_id(before)))
3096 tbody.insertBefore(row, document.getElementById(before));
3098 tbody.appendChild(row);
3101 tbody.removeChild(replace);
3104 // add to folder/row-ID map
3105 this.env.subscriptionrows[row.id] = [name, display_name, 0];
3108 row.cells[0].innerHTML = display_name;
3110 // set messages count to zero
3112 row.cells[1].innerHTML = '*';
3114 if (!replace && row.cells[2] && row.cells[2].firstChild.tagName=='INPUT')
3116 row.cells[2].firstChild.value = name;
3117 row.cells[2].firstChild.checked = true;
3120 // add new folder to rename-folder list and clear input field
3121 if (!replace && (form = this.gui_objects.editform))
3123 if (form.elements['_folder_oldname'])
3124 form.elements['_folder_oldname'].options[form.elements['_folder_oldname'].options.length] = new Option(name,name);
3125 if (form.elements['_folder_name'])
3126 form.elements['_folder_name'].value = '';
3129 this.init_subscription_list();
3130 if (selection && document.getElementById('rcmrow'+selection))
3131 this.subscription_list.select_row(selection);
3133 if (document.getElementById(id).scrollIntoView)
3134 document.getElementById(id).scrollIntoView();
3137 // replace an existing table row with a new folder line
3138 this.replace_folder_row = function(oldfolder, newfolder, display_name, before)
3140 var id = this.get_folder_row_id(oldfolder);
3141 var row = document.getElementById(id);
3143 // replace an existing table row (if found)
3144 this.add_folder_row(newfolder, display_name, row, before);
3146 // rename folder in rename-folder dropdown
3148 if ((form = this.gui_objects.editform) && (elm = form.elements['_folder_oldname']))
3150 for (var i=0;i<elm.options.length;i++)
3152 if (elm.options[i].value == oldfolder)
3154 elm.options[i].text = display_name;
3155 elm.options[i].value = newfolder;
3160 form.elements['_folder_newname'].value = '';
3164 // remove the table row of a specific mailbox from the table
3165 // (the row will not be removed, just hidden)
3166 this.remove_folder_row = function(folder)
3169 var id = this.get_folder_row_id(folder);
3170 if (id && (row = document.getElementById(id)))
3171 row.style.display = 'none';
3173 // remove folder from rename-folder list
3175 if ((form = this.gui_objects.editform) && form.elements['_folder_oldname'])
3177 for (var i=0;i<form.elements['_folder_oldname'].options.length;i++)
3179 if (form.elements['_folder_oldname'].options[i].value == folder)
3181 form.elements['_folder_oldname'].options[i] = null;
3187 if (form && form.elements['_folder_newname'])
3188 form.elements['_folder_newname'].value = '';
3191 this.subscribe_folder = function(folder)
3194 this.http_post('subscribe', '_mbox='+urlencode(folder));
3197 this.unsubscribe_folder = function(folder)
3200 this.http_post('unsubscribe', '_mbox='+urlencode(folder));
3203 // helper method to find a specific mailbox row ID
3204 this.get_folder_row_id = function(folder)
3206 for (var id in this.env.subscriptionrows)
3207 if (this.env.subscriptionrows[id] && this.env.subscriptionrows[id][0] == folder)
3213 // duplicate a specific table row
3214 this.clone_table_row = function(row)
3217 var new_row = document.createElement('TR');
3218 for(var n=0; n<row.cells.length; n++)
3220 cell = row.cells[n];
3221 td = document.createElement('TD');
3224 td.className = cell.className;
3226 td.setAttribute('align', cell.align);
3228 td.innerHTML = cell.innerHTML;
3229 new_row.appendChild(td);
3236 /*********************************************************/
3237 /********* GUI functionality *********/
3238 /*********************************************************/
3240 // eable/disable buttons for page shifting
3241 this.set_page_buttons = function()
3243 this.enable_command('nextpage', (this.env.pagecount > this.env.current_page));
3244 this.enable_command('lastpage', (this.env.pagecount > this.env.current_page));
3245 this.enable_command('previouspage', (this.env.current_page > 1));
3246 this.enable_command('firstpage', (this.env.current_page > 1));
3249 // set button to a specific state
3250 this.set_button = function(command, state)
3252 var a_buttons = this.buttons[command];
3255 if(!a_buttons || !a_buttons.length)
3258 for(var n=0; n<a_buttons.length; n++)
3260 button = a_buttons[n];
3261 obj = document.getElementById(button.id);
3263 // get default/passive setting of the button
3264 if (obj && button.type=='image' && !button.status) {
3265 button.pas = obj._original_src ? obj._original_src : obj.src;
3266 // respect PNG fix on IE browsers
3267 if (obj.runtimeStyle && obj.runtimeStyle.filter && obj.runtimeStyle.filter.match(/src=['"]([^'"]+)['"]/))
3268 button.pas = RegExp.$1;
3270 else if (obj && !button.status)
3271 button.pas = String(obj.className);
3273 // set image according to button state
3274 if (obj && button.type=='image' && button[state])
3276 button.status = state;
3277 obj.src = button[state];
3279 // set class name according to button state
3280 else if (obj && typeof(button[state])!='undefined')
3282 button.status = state;
3283 obj.className = button[state];
3285 // disable/enable input buttons
3286 if (obj && button.type=='input')
3288 button.status = state;
3289 obj.disabled = !state;
3294 // display a specific alttext
3295 this.set_alttext = function(command, label)
3297 if (!this.buttons[command] || !this.buttons[command].length)
3300 var button, obj, link;
3301 for (var n=0; n<this.buttons[command].length; n++)
3303 button = this.buttons[command][n];
3304 obj = document.getElementById(button.id);
3306 if (button.type=='image' && obj)
3308 obj.setAttribute('alt', this.get_label(label));
3309 if ((link = obj.parentNode) && link.tagName == 'A')
3310 link.setAttribute('title', this.get_label(label));
3313 obj.setAttribute('title', this.get_label(label));
3317 // mouse over button
3318 this.button_over = function(command, id)
3320 var a_buttons = this.buttons[command];
3323 if(!a_buttons || !a_buttons.length)
3326 for(var n=0; n<a_buttons.length; n++)
3328 button = a_buttons[n];
3329 if(button.id==id && button.status=='act')
3331 img = document.getElementById(button.id);
3332 if (img && button.over)
3333 img.src = button.over;
3339 // mouse down on button
3340 this.button_sel = function(command, id)
3342 var a_buttons = this.buttons[command];
3345 if(!a_buttons || !a_buttons.length)
3348 for(var n=0; n<a_buttons.length; n++)
3350 button = a_buttons[n];
3351 if(button.id==id && button.status=='act')
3353 img = document.getElementById(button.id);
3354 if (img && button.sel)
3355 img.src = button.sel;
3360 // mouse out of button
3361 this.button_out = function(command, id)
3363 var a_buttons = this.buttons[command];
3366 if(!a_buttons || !a_buttons.length)
3369 for(var n=0; n<a_buttons.length; n++)
3371 button = a_buttons[n];
3372 if(button.id==id && button.status=='act')
3374 img = document.getElementById(button.id);
3375 if (img && button.act)
3376 img.src = button.act;
3381 // set/unset a specific class name
3382 this.set_classname = function(obj, classname, set)
3384 var reg = new RegExp('\s*'+classname, 'i');
3385 if (!set && obj.className.match(reg))
3386 obj.className = obj.className.replace(reg, '');
3387 else if (set && !obj.className.match(reg))
3388 obj.className += ' '+classname;
3391 // write to the document/window title
3392 this.set_pagetitle = function(title)
3394 if (title && document.title)
3395 document.title = title;
3398 // display a system message
3399 this.display_message = function(msg, type, hold)
3401 if (!this.loaded) // save message in order to display after page loaded
3403 this.pending_message = new Array(msg, type);
3407 // pass command to parent window
3408 if (this.env.framed && parent.rcmail)
3409 return parent.rcmail.display_message(msg, type, hold);
3411 if (!this.gui_objects.message)
3414 if (this.message_timer)
3415 clearTimeout(this.message_timer);
3419 cont = '<div class="'+type+'">'+cont+'</div>';
3422 this.gui_objects.message.innerHTML = cont;
3423 this.gui_objects.message.style.display = 'block';
3425 if (type!='loading')
3426 this.gui_objects.message.onmousedown = function(){ _rcube.hide_message(); return true; };
3429 this.message_timer = window.setTimeout(function(){ ref.hide_message(); }, this.message_time);
3432 // make a message row disapear
3433 this.hide_message = function()
3435 if (this.gui_objects.message)
3437 this.gui_objects.message.style.display = 'none';
3438 this.gui_objects.message.onmousedown = null;
3442 // mark a mailbox as selected and set environment variable
3443 this.select_folder = function(name, old)
3445 if (this.gui_objects.folderlist)
3447 var current_li, target_li;
3449 if ((current_li = this.get_folder_li(old)))
3451 this.set_classname(current_li, 'selected', false);
3452 this.set_classname(current_li, 'unfocused', false);
3455 if ((target_li = this.get_folder_li(name)))
3457 this.set_classname(target_li, 'unfocused', false);
3458 this.set_classname(target_li, 'selected', true);
3463 // helper method to find a folder list item
3464 this.get_folder_li = function(name)
3466 if (this.gui_objects.folderlist)
3468 name = String(name).replace(this.identifier_expr, '');
3469 return document.getElementById('rcmli'+name);
3475 // for reordering column array, Konqueror workaround
3476 this.set_message_coltypes = function(coltypes)
3478 this.coltypes = coltypes;
3480 // set correct list titles
3482 var thead = this.gui_objects.messagelist ? this.gui_objects.messagelist.tHead : null;
3483 for (var n=0; thead && n<this.coltypes.length; n++)
3485 col = this.coltypes[n];
3486 if ((cell = thead.rows[0].cells[n+1]) && (col=='from' || col=='to'))
3488 // if we have links for sorting, it's a bit more complicated...
3489 if (cell.firstChild && cell.firstChild.tagName=='A')
3491 cell.firstChild.innerHTML = this.get_label(this.coltypes[n]);
3492 cell.firstChild.onclick = function(){ return rcmail.command('sort', this.__col, this); };
3493 cell.firstChild.__col = col;
3496 cell.innerHTML = this.get_label(this.coltypes[n]);
3498 cell.id = 'rcm'+col;
3500 else if (col == 'subject' && this.message_list)
3501 this.message_list.subject_col = n+1;
3505 // create a table row in the message list
3506 this.add_message_row = function(uid, cols, flags, attachment, attop)
3508 if (!this.gui_objects.messagelist || !this.message_list)
3511 var tbody = this.gui_objects.messagelist.tBodies[0];
3512 var rowcount = tbody.rows.length;
3513 var even = rowcount%2;
3515 this.env.messages[uid] = {deleted:flags.deleted?1:0,
3516 replied:flags.replied?1:0,
3517 unread:flags.unread?1:0,
3518 forwarded:flags.forwarded?1:0,
3519 flagged:flags.flagged?1:0};
3521 var row = document.createElement('TR');
3522 row.id = 'rcmrow'+uid;
3523 row.className = 'message'
3524 + (even ? ' even' : ' odd')
3525 + (flags.unread ? ' unread' : '')
3526 + (flags.deleted ? ' deleted' : '')
3527 + (flags.flagged ? ' flagged' : '');
3529 if (this.message_list.in_selection(uid))
3530 row.className += ' selected';
3532 var icon = this.env.messageicon;
3533 if (flags.deleted && this.env.deletedicon)
3534 icon = this.env.deletedicon;
3535 else if (flags.replied && this.env.repliedicon)
3537 if (flags.forwarded && this.env.forwardedrepliedicon)
3538 icon = this.env.forwardedrepliedicon;
3540 icon = this.env.repliedicon;
3542 else if (flags.forwarded && this.env.forwardedicon)
3543 icon = this.env.forwardedicon;
3544 else if(flags.unread && this.env.unreadicon)
3545 icon = this.env.unreadicon;
3547 var col = document.createElement('TD');
3548 col.className = 'icon';
3549 col.innerHTML = icon ? '<img src="'+icon+'" alt="" />' : '';
3550 row.appendChild(col);
3552 // add each submitted col
3553 for (var n = 0; n < this.coltypes.length; n++)
3555 var c = this.coltypes[n];
3556 col = document.createElement('TD');
3557 col.className = String(c).toLowerCase();
3561 if (flags.flagged && this.env.flaggedicon)
3562 col.innerHTML = '<img src="'+this.env.flaggedicon+'" alt="" />';
3563 else if(!flags.flagged && this.env.unflaggedicon)
3564 col.innerHTML = '<img src="'+this.env.unflaggedicon+'" alt="" />';
3566 else if (c=='attachment')
3567 col.innerHTML = attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" />' : ' ';
3569 col.innerHTML = cols[c];
3571 row.appendChild(col);
3574 this.message_list.insert_row(row, attop);
3577 if (attop && this.env.pagesize && this.message_list.rowcount > this.env.pagesize)
3579 var uid = this.message_list.get_last_row();
3580 this.message_list.remove_row(uid);
3581 this.message_list.clear_selection(uid);
3585 // replace content of row count display
3586 this.set_rowcount = function(text)
3588 if (this.gui_objects.countdisplay)
3589 this.gui_objects.countdisplay.innerHTML = text;
3591 // update page navigation buttons
3592 this.set_page_buttons();
3595 // replace content of mailboxname display
3596 this.set_mailboxname = function(content)
3598 if (this.gui_objects.mailboxname && content)
3599 this.gui_objects.mailboxname.innerHTML = content;
3602 // replace content of quota display
3603 this.set_quota = function(content)
3605 if (this.gui_objects.quotadisplay && content)
3606 this.gui_objects.quotadisplay.innerHTML = content;
3609 // update the mailboxlist
3610 this.set_unread_count = function(mbox, count, set_title)
3612 if (!this.gui_objects.mailboxlist)
3615 this.env.unread_counts[mbox] = count;
3616 this.set_unread_count_display(mbox, set_title);
3619 // update the mailbox count display
3620 this.set_unread_count_display = function(mbox, set_title)
3622 var reg, text_obj, item, mycount, childcount, div;
3623 if (item = this.get_folder_li(mbox))
3625 mycount = this.env.unread_counts[mbox] ? this.env.unread_counts[mbox] : 0;
3626 text_obj = item.getElementsByTagName('a')[0];
3627 reg = /\s+\([0-9]+\)$/i;
3630 if ((div = item.getElementsByTagName('div')[0]) &&
3631 div.className.match(/collapsed/))
3633 // add children's counters
3634 for (var k in this.env.unread_counts)
3635 if (k.indexOf(mbox + this.env.delimiter) == 0) {
3636 childcount += this.env.unread_counts[k];
3640 if (mycount && text_obj.innerHTML.match(reg))
3641 text_obj.innerHTML = text_obj.innerHTML.replace(reg, ' ('+mycount+')');
3643 text_obj.innerHTML += ' ('+mycount+')';
3645 text_obj.innerHTML = text_obj.innerHTML.replace(reg, '');
3647 // set parent's display
3648 reg = new RegExp(RegExp.escape(this.env.delimiter) + '[^' + RegExp.escape(this.env.delimiter) + ']+$');
3649 if (mbox.match(reg))
3650 this.set_unread_count_display(mbox.replace(reg, ''), false);
3652 // set the right classes
3653 this.set_classname(item, 'unread', (mycount+childcount)>0 ? true : false);
3656 // set unread count to window title
3657 reg = /^\([0-9]+\)\s+/i;
3658 if (set_title && document.title)
3660 var doc_title = String(document.title);
3663 if (mycount && doc_title.match(reg))
3664 new_title = doc_title.replace(reg, '('+mycount+') ');
3666 new_title = '('+mycount+') '+doc_title;
3668 new_title = doc_title.replace(reg, '');
3670 this.set_pagetitle(new_title);
3674 // notifies that a new message(s) has hit the mailbox
3675 this.new_message_focus = function()
3677 // focus main window
3678 if (this.env.framed && window.parent)
3679 window.parent.focus();
3684 // add row to contacts list
3685 this.add_contact_row = function(cid, cols, select)
3687 if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0])
3690 var tbody = this.gui_objects.contactslist.tBodies[0];
3691 var rowcount = tbody.rows.length;
3692 var even = rowcount%2;
3694 var row = document.createElement('TR');
3695 row.id = 'rcmrow'+cid;
3696 row.className = 'contact '+(even ? 'even' : 'odd');
3698 if (this.contact_list.in_selection(cid))
3699 row.className += ' selected';
3701 // add each submitted col
3704 col = document.createElement('TD');
3705 col.className = String(c).toLowerCase();
3706 col.innerHTML = cols[c];
3707 row.appendChild(col);
3710 this.contact_list.insert_row(row);
3711 this.enable_command('export', (this.contact_list.rowcount > 0));
3714 this.toggle_prefer_html = function(checkbox)
3716 var addrbook_show_images;
3717 if (addrbook_show_images = document.getElementById('rcmfd_addrbook_show_images'))
3718 addrbook_show_images.disabled = !checkbox.checked;
3721 // display fetched raw headers
3722 this.set_headers = function(content)
3724 if (this.gui_objects.all_headers_row && this.gui_objects.all_headers_box && content)
3726 var box = this.gui_objects.all_headers_box;
3727 box.innerHTML = content;
3728 box.style.display = 'block';
3730 if (this.env.framed && parent.rcmail)
3731 parent.rcmail.set_busy(false);
3733 this.set_busy(false);
3737 // display all-headers row and fetch raw message headers
3738 this.load_headers = function(elem)
3740 if (!this.gui_objects.all_headers_row || !this.gui_objects.all_headers_box || !this.env.uid)
3743 this.set_classname(elem, 'show-headers', false);
3744 this.set_classname(elem, 'hide-headers', true);
3745 this.gui_objects.all_headers_row.style.display = bw.ie ? 'block' : 'table-row';
3746 elem.onclick = function() { rcmail.hide_headers(elem); }
3748 // fetch headers only once
3749 if (!this.gui_objects.all_headers_box.innerHTML)
3751 this.display_message(this.get_label('loading'), 'loading', true);
3752 this.http_post('headers', '_uid='+this.env.uid);
3756 // hide all-headers row
3757 this.hide_headers = function(elem)
3759 if (!this.gui_objects.all_headers_row || !this.gui_objects.all_headers_box)
3762 this.set_classname(elem, 'hide-headers', false);
3763 this.set_classname(elem, 'show-headers', true);
3764 this.gui_objects.all_headers_row.style.display = 'none';
3765 elem.onclick = function() { rcmail.load_headers(elem); }
3769 /********************************************************/
3770 /********* html to text conversion functions *********/
3771 /********************************************************/
3773 this.html2plain = function(htmlText, id)
3775 var http_request = new rcube_http_request();
3776 var url = this.env.bin_path+'html2text.php';
3779 this.set_busy(true, 'converting');
3780 console.log('HTTP POST: '+url);
3782 http_request.onerror = function(o) { rcmail.http_error(o); };
3783 http_request.oncomplete = function(o) { rcmail.set_text_value(o, id); };
3784 http_request.POST(url, htmlText, 'application/octet-stream');
3787 this.set_text_value = function(httpRequest, id)
3789 this.set_busy(false);
3790 document.getElementById(id).value = httpRequest.get_text();
3791 console.log(httpRequest.get_text());
3795 /********************************************************/
3796 /********* remote request methods *********/
3797 /********************************************************/
3799 this.redirect = function(url, lock)
3801 if (lock || lock === null)
3802 this.set_busy(true);
3804 if (this.env.framed && window.parent)
3805 parent.location.href = url;
3807 location.href = url;
3810 this.goto_url = function(action, query, lock)
3812 var querystring = query ? '&'+query : '';
3813 this.redirect(this.env.comm_path+'&_action='+action+querystring, lock);
3816 this.http_sockets = new Array();
3818 // find a non-busy socket or create a new one
3819 this.get_request_obj = function()
3821 for (var n=0; n<this.http_sockets.length; n++)
3823 if (!this.http_sockets[n].busy)
3824 return this.http_sockets[n];
3827 // create a new XMLHTTP object
3828 var i = this.http_sockets.length;
3829 this.http_sockets[i] = new rcube_http_request();
3831 return this.http_sockets[i];
3834 // send a http request to the server
3835 this.http_request = function(action, querystring, lock)
3837 var request_obj = this.get_request_obj();
3838 querystring += (querystring ? '&' : '') + '_remote=1';
3840 // add timestamp to request url to avoid cacheing problems in Safari
3842 querystring += '&_ts='+(new Date().getTime());
3847 console.log('HTTP request: '+this.env.comm_path+'&_action='+action+'&'+querystring);
3850 this.set_busy(true);
3853 request_obj.__lock = lock ? true : false;
3854 request_obj.__action = action;
3855 request_obj.onerror = function(o){ ref.http_error(o); };
3856 request_obj.oncomplete = function(o){ ref.http_response(o); };
3857 request_obj.GET(this.env.comm_path+'&_action='+action+'&'+querystring);
3861 // send a http POST request to the server
3862 this.http_post = function(action, postdata, lock)
3865 if (postdata && typeof(postdata) == 'object')
3866 postdata._remote = 1;
3868 postdata += (postdata ? '&' : '') + '_remote=1';
3871 if (request_obj = this.get_request_obj())
3873 console.log('HTTP POST: '+this.env.comm_path+'&_action='+action);
3876 this.set_busy(true);
3879 request_obj.__lock = lock ? true : false;
3880 request_obj.__action = action;
3881 request_obj.onerror = function(o){ rcm.http_error(o); };
3882 request_obj.oncomplete = function(o){ rcm.http_response(o); };
3883 request_obj.POST(this.env.comm_path+'&_action='+action, postdata);
3887 // handle HTTP response
3888 this.http_response = function(request_obj)
3890 var ctype = request_obj.get_header('Content-Type');
3893 ctype = String(ctype).toLowerCase();
3894 var ctype_array=ctype.split(";");
3895 ctype = ctype_array[0];
3898 if (request_obj.__lock)
3899 this.set_busy(false);
3901 console.log(request_obj.get_text());
3903 // if we get javascript code from server -> execute it
3904 if (request_obj.get_text() && (ctype=='text/javascript' || ctype=='application/x-javascript'))
3905 eval(request_obj.get_text());
3907 // process the response data according to the sent action
3908 switch (request_obj.__action) {
3910 if (this.task == 'addressbook') {
3911 var uid = this.contact_list.get_selection();
3912 this.enable_command('compose', (uid && this.contact_list.rows[uid]));
3913 this.enable_command('delete', 'edit', (uid && this.contact_list.rows[uid] && this.env.address_sources && !this.env.address_sources[this.env.source].readonly));
3914 this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0));
3918 if (this.env.action == 'show')
3919 this.command('list');
3920 else if (this.message_list)
3921 this.message_list.init();
3926 if (!this.env.messagecount && this.task == 'mail') {
3927 // clear preview pane content
3928 if (this.env.contentframe)
3929 this.show_contentframe(false);
3930 // disable commands useless when mailbox is empty
3931 this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 'mark', 'viewsource',
3932 'print', 'load-attachment', 'purge', 'expunge', 'select-all', 'select-none', 'sort', false);
3936 case 'check-recent':
3939 if (this.task == 'mail') {
3940 if (this.message_list && request_obj.__action == 'list')
3941 this.msglist_select(this.message_list);
3942 this.enable_command('show', 'expunge', 'select-all', 'select-none', 'sort', (this.env.messagecount > 0));
3943 this.enable_command('purge', this.purge_mailbox_test());
3945 else if (this.task == 'addressbook')
3946 this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0));
3951 request_obj.reset();
3954 // handle HTTP request errors
3955 this.http_error = function(request_obj)
3957 //alert('Error sending request: '+request_obj.url+' => HTTP '+request_obj.xmlhttp.status);
3958 if (request_obj.__lock)
3959 this.set_busy(false);
3961 request_obj.reset();
3962 request_obj.__lock = false;
3963 this.display_message('Unknown Server Error!', 'error');
3966 // use an image to send a keep-alive siganl to the server
3967 this.send_keep_alive = function()
3970 this.http_request('keep-alive', '_t='+d.getTime());
3973 // send periodic request to check for recent messages
3974 this.check_for_recent = function(setbusy)
3980 this.set_busy(true, 'checkingmail');
3982 var addurl = '_t=' + (new Date().getTime());
3984 if (this.gui_objects.messagelist)
3985 addurl += '&_list=1';
3986 if (this.gui_objects.quotadisplay)
3987 addurl += '&_quota=1';
3988 if (this.env.search_request)
3989 addurl += '&_search=' + this.env.search_request;
3991 this.http_request('check-recent', addurl, true);
3995 /********************************************************/
3996 /********* helper methods *********/
3997 /********************************************************/
3999 // check if we're in show mode or if we have a unique selection
4000 // and return the message uid
4001 this.get_single_uid = function()
4003 return this.env.uid ? this.env.uid : (this.message_list ? this.message_list.get_single_selection() : null);
4006 // same as above but for contacts
4007 this.get_single_cid = function()
4009 return this.env.cid ? this.env.cid : (this.contact_list ? this.contact_list.get_single_selection() : null);
4013 this.get_caret_pos = function(obj)
4015 if (typeof(obj.selectionEnd)!='undefined')
4016 return obj.selectionEnd;
4017 else if (document.selection && document.selection.createRange)
4019 var range = document.selection.createRange();
4020 if (range.parentElement()!=obj)
4023 var gm = range.duplicate();
4024 if (obj.tagName=='TEXTAREA')
4025 gm.moveToElementText(obj);
4027 gm.expand('textedit');
4029 gm.setEndPoint('EndToStart', range);
4030 var p = gm.text.length;
4032 return p<=obj.value.length ? p : -1;
4035 return obj.value.length;
4038 this.set_caret2start = function(obj)
4040 if (obj.createTextRange)
4042 var range = obj.createTextRange();
4043 range.collapse(true);
4046 else if (obj.setSelectionRange)
4047 obj.setSelectionRange(0,0);
4052 // set all fields of a form disabled
4053 this.lock_form = function(form, lock)
4055 if (!form || !form.elements)
4059 for (var n=0; n<form.elements.length; n++)
4061 type = form.elements[n];
4065 form.elements[n].disabled = lock;
4069 } // end object rcube_webmail
4073 * Class for sending HTTP requests
4076 function rcube_http_request()
4080 this.xmlhttp = null;
4082 // reset object properties
4083 this.reset = function()
4085 // set unassigned event handlers
4086 this.onloading = function(){ };
4087 this.onloaded = function(){ };
4088 this.oninteractive = function(){ };
4089 this.oncomplete = function(){ };
4090 this.onabort = function(){ };
4091 this.onerror = function(){ };
4095 this.xmlhttp = null;
4098 // create HTMLHTTP object
4099 this.build = function()
4101 if (window.XMLHttpRequest)
4102 this.xmlhttp = new XMLHttpRequest();
4103 else if (window.ActiveXObject)
4105 try { this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }
4106 catch(e) { this.xmlhttp = null; }
4115 this.GET = function(url)
4129 this.xmlhttp.onreadystatechange = function(){ _ref.xmlhttp_onreadystatechange(); };
4130 this.xmlhttp.open('GET', url, true);
4131 this.xmlhttp.setRequestHeader('X-RoundCube-Referer', bw.get_cookie('roundcube_sessid'));
4132 this.xmlhttp.send(null);
4135 this.POST = function(url, body, contentType)
4137 // default value for contentType if not provided
4138 if (typeof(contentType) == 'undefined')
4139 contentType = 'application/x-www-form-urlencoded';
4149 var req_body = body;
4150 if (typeof(body) == 'object')
4154 req_body += (req_body ? '&' : '') + p+'='+urlencode(body[p]);
4161 this.xmlhttp.onreadystatechange = function() { ref.xmlhttp_onreadystatechange(); };
4162 this.xmlhttp.open('POST', url, true);
4163 this.xmlhttp.setRequestHeader('Content-Type', contentType);
4164 this.xmlhttp.setRequestHeader('X-RoundCube-Referer', bw.get_cookie('roundcube_sessid'));
4165 this.xmlhttp.send(req_body);
4168 // handle onreadystatechange event
4169 this.xmlhttp_onreadystatechange = function()
4171 if(this.xmlhttp.readyState == 1)
4172 this.onloading(this);
4174 else if(this.xmlhttp.readyState == 2)
4175 this.onloaded(this);
4177 else if(this.xmlhttp.readyState == 3)
4178 this.oninteractive(this);
4180 else if(this.xmlhttp.readyState == 4)
4183 if (this.xmlhttp.status == 0)
4185 else if(this.xmlhttp.status == 200)
4186 this.oncomplete(this);
4200 // getter method for HTTP headers
4201 this.get_header = function(name)
4203 return this.xmlhttp.getResponseHeader(name);
4206 this.get_text = function()
4208 return this.xmlhttp.responseText;
4211 this.get_xml = function()
4213 return this.xmlhttp.responseXML;
4218 } // end class rcube_http_request
4220 // helper function to call the init method with a delay
4221 function call_init(o)
4223 window.setTimeout('if (window[\''+o+'\'] && window[\''+o+'\'].init) { '+o+'.init(); }',
4224 bw.win ? 500 : 200);