2 +-----------------------------------------------------------------------+
3 | RoundCube Webmail Client Script |
5 | This file is part of the RoundCube Webmail client |
6 | Copyright (C) 2005-2008, 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 2203 2008-12-30 15:22:35Z 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';
57 // set environment variable(s)
58 this.set_env = function(p, value)
60 if (p != null && typeof(p) == 'object' && !value)
67 // add a localized label to the client environment
68 this.add_label = function(key, value)
70 this.labels[key] = value;
73 // add a button to the button list
74 this.register_button = function(command, id, type, act, sel, over)
76 if (!this.buttons[command])
77 this.buttons[command] = new Array();
79 var button_prop = {id:id, type:type};
80 if (act) button_prop.act = act;
81 if (sel) button_prop.sel = sel;
82 if (over) button_prop.over = over;
84 this.buttons[command][this.buttons[command].length] = button_prop;
87 // register a specific gui object
88 this.gui_object = function(name, id)
90 this.gui_objects[name] = id;
93 // execute the given script on load
94 this.add_onload = function(f)
96 this.onloads[this.onloads.length] = f;
99 // initialize webmail client
100 this.init = function()
103 this.task = this.env.task;
106 if (!bw.dom || !bw.xmlhttp_test())
108 this.goto_url('error', '_code=0x199');
112 // find all registered gui objects
113 for (var n in this.gui_objects)
114 this.gui_objects[n] = rcube_find_object(this.gui_objects[n]);
116 // tell parent window that this frame is loaded
117 if (this.env.framed && parent.rcmail && parent.rcmail.set_busy)
118 parent.rcmail.set_busy(false);
120 // enable general commands
121 this.enable_command('logout', 'mail', 'addressbook', 'settings', true);
123 if (this.env.permaurl)
124 this.enable_command('permaurl', true);
129 if (this.gui_objects.messagelist)
131 this.message_list = new rcube_list_widget(this.gui_objects.messagelist, {multiselect:true, draggable:true, keyboard:true, dblclick_time:this.dblclick_time});
132 this.message_list.row_init = function(o){ p.init_message_row(o); };
133 this.message_list.addEventListener('dblclick', function(o){ p.msglist_dbl_click(o); });
134 this.message_list.addEventListener('keypress', function(o){ p.msglist_keypress(o); });
135 this.message_list.addEventListener('select', function(o){ p.msglist_select(o); });
136 this.message_list.addEventListener('dragstart', function(o){ p.drag_start(o); });
137 this.message_list.addEventListener('dragmove', function(o, e){ p.drag_move(e); });
138 this.message_list.addEventListener('dragend', function(o){ p.drag_active = false; });
140 this.message_list.init();
141 this.enable_command('toggle_status', 'toggle_flag', true);
143 if (this.gui_objects.mailcontframe)
145 this.gui_objects.mailcontframe.onmousedown = function(e){ return p.click_on_list(e); };
146 document.onmouseup = function(e){ return p.doc_mouse_up(e); };
149 this.message_list.focus();
152 if (this.env.coltypes)
153 this.set_message_coltypes(this.env.coltypes);
155 // enable mail commands
156 this.enable_command('list', 'checkmail', 'compose', 'add-contact', 'search', 'reset-search', 'collapse-folder', true);
158 if (this.env.search_text != null && document.getElementById('quicksearchbox') != null)
159 document.getElementById('quicksearchbox').value = this.env.search_text;
161 if (this.env.action=='show' || this.env.action=='preview')
163 this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 'mark', 'viewsource', 'print', 'load-attachment', 'load-headers', true);
164 if (this.env.next_uid)
166 this.enable_command('nextmessage', true);
167 this.enable_command('lastmessage', true);
169 if (this.env.prev_uid)
171 this.enable_command('previousmessage', true);
172 this.enable_command('firstmessage', true);
176 if (this.env.trash_mailbox && this.env.mailbox != this.env.trash_mailbox)
177 this.set_alttext('delete', 'movemessagetotrash');
179 // make preview/message frame visible
180 if (this.env.action == 'preview' && this.env.framed && parent.rcmail)
182 this.enable_command('compose', 'add-contact', false);
183 parent.rcmail.show_contentframe(true);
186 if ((this.env.action=='show' || this.env.action=='preview') && this.env.blockedobjects)
188 if (this.gui_objects.remoteobjectsmsg)
189 this.gui_objects.remoteobjectsmsg.style.display = 'block';
190 this.enable_command('load-images', 'always-load', true);
193 if (this.env.action=='compose')
195 this.enable_command('add-attachment', 'send-attachment', 'remove-attachment', 'send', true);
196 if (this.env.spellcheck)
198 this.env.spellcheck.spelling_state_observer = function(s){ ref.set_spellcheck_state(s); };
199 this.set_spellcheck_state('ready');
200 if (rcube_find_object('_is_html').value == '1')
201 this.display_spellcheck_controls(false);
203 if (this.env.drafts_mailbox)
204 this.enable_command('savedraft', true);
206 document.onmouseup = function(e){ return p.doc_mouse_up(e); };
209 if (this.env.messagecount)
210 this.enable_command('select-all', 'select-none', 'expunge', true);
212 if (this.purge_mailbox_test())
213 this.enable_command('purge', true);
215 this.set_page_buttons();
217 // init message compose form
218 if (this.env.action=='compose')
219 this.init_messageform();
221 // show printing dialog
222 if (this.env.action=='print')
225 // get unread count for each mailbox
226 if (this.gui_objects.mailboxlist)
228 this.env.unread_counts = {};
229 this.gui_objects.folderlist = this.gui_objects.mailboxlist;
230 this.http_request('getunread', '');
233 // ask user to send MDN
234 if (this.env.mdn_request && this.env.uid)
236 var mdnurl = '_uid='+this.env.uid+'&_mbox='+urlencode(this.env.mailbox);
237 if (confirm(this.get_label('mdnrequest')))
238 this.http_post('sendmdn', mdnurl);
240 this.http_post('mark', mdnurl+'&_flag=mdnsent');
247 if (this.gui_objects.contactslist)
249 this.contact_list = new rcube_list_widget(this.gui_objects.contactslist, {multiselect:true, draggable:true, keyboard:true});
250 this.contact_list.addEventListener('keypress', function(o){ p.contactlist_keypress(o); });
251 this.contact_list.addEventListener('select', function(o){ p.contactlist_select(o); });
252 this.contact_list.addEventListener('dragstart', function(o){ p.drag_start(o); });
253 this.contact_list.addEventListener('dragmove', function(o, e){ p.drag_move(e); });
254 this.contact_list.addEventListener('dragend', function(o){ p.drag_active = false; });
255 this.contact_list.init();
258 this.contact_list.highlight_row(this.env.cid);
260 if (this.gui_objects.contactslist.parentNode)
262 this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return p.click_on_list(e); };
263 document.onmouseup = function(e){ return p.doc_mouse_up(e); };
266 this.contact_list.focus();
269 this.set_page_buttons();
271 if (this.env.address_sources && this.env.address_sources[this.env.source] && !this.env.address_sources[this.env.source].readonly)
272 this.enable_command('add', true);
275 this.enable_command('show', 'edit', true);
277 if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform)
278 this.enable_command('save', true);
280 this.enable_command('search', 'reset-search', 'moveto', 'import', true);
282 if (this.contact_list && this.contact_list.rowcount > 0)
283 this.enable_command('export', true);
285 this.enable_command('list', true);
290 this.enable_command('preferences', 'identities', 'save', 'folders', true);
292 if (this.env.action=='identities' || this.env.action=='edit-identity' || this.env.action=='add-identity') {
293 this.enable_command('add', this.env.identities_level < 2);
294 this.enable_command('delete', 'edit', true);
297 if (this.env.action=='edit-identity' || this.env.action=='add-identity')
298 this.enable_command('save', true);
300 if (this.env.action=='folders')
301 this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', true);
303 if (this.gui_objects.identitieslist)
305 this.identity_list = new rcube_list_widget(this.gui_objects.identitieslist, {multiselect:false, draggable:false, keyboard:false});
306 this.identity_list.addEventListener('select', function(o){ p.identity_select(o); });
307 this.identity_list.init();
308 this.identity_list.focus();
311 this.identity_list.highlight_row(this.env.iid);
314 if (this.gui_objects.subscriptionlist)
315 this.init_subscription_list();
320 var input_user = rcube_find_object('rcmloginuser');
321 var input_pass = rcube_find_object('rcmloginpwd');
322 var input_tz = rcube_find_object('rcmlogintz');
325 input_user.onkeyup = function(e){ return rcmail.login_user_keyup(e); };
326 if (input_user && input_user.value=='')
331 // detect client timezone
333 input_tz.value = new Date().getTimezoneOffset() / -60;
335 this.enable_command('login', true);
342 // enable basic commands
343 this.enable_command('logout', true);
345 // flag object as complete
349 if (this.pending_message)
350 this.display_message(this.pending_message[0], this.pending_message[1]);
352 // start keep-alive interval
353 this.start_keepalive();
355 // execute all foreign onload scripts
356 for (var i=0; i<this.onloads.length; i++)
358 if (typeof(this.onloads[i]) == 'string')
359 eval(this.onloads[i]);
360 else if (typeof(this.onloads[i]) == 'function')
365 // start interval for keep-alive/recent_check signal
366 this.start_keepalive = function()
368 if (this.env.keep_alive && !this.env.framed && this.task=='mail' && this.gui_objects.mailboxlist)
369 this._int = setInterval(function(){ ref.check_for_recent(false); }, this.env.keep_alive * 1000);
370 else if (this.env.keep_alive && !this.env.framed && this.task!='login')
371 this._int = setInterval(function(){ ref.send_keep_alive(); }, this.env.keep_alive * 1000);
374 this.init_message_row = function(row)
377 if (uid && this.env.messages[uid])
379 row.deleted = this.env.messages[uid].deleted ? true : false;
380 row.unread = this.env.messages[uid].unread ? true : false;
381 row.replied = this.env.messages[uid].replied ? true : false;
382 row.flagged = this.env.messages[uid].flagged ? true : false;
383 row.forwarded = this.env.messages[uid].forwarded ? true : false;
386 // set eventhandler to message icon
387 if ((row.icon = row.obj.cells[0].childNodes[0]) && row.icon.nodeName=='IMG')
390 row.icon.id = 'msgicn_'+row.uid;
391 row.icon._row = row.obj;
392 row.icon.onmousedown = function(e) { p.command('toggle_status', this); };
395 // global variable 'flagged_col' may be not defined yet
396 if (!this.env.flagged_col && this.env.coltypes)
399 if((found = find_in_array('flag', this.env.coltypes)) >= 0)
400 this.set_env('flagged_col', found+1);
403 // set eventhandler to flag icon, if icon found
404 if (this.env.flagged_col && (row.flagged_icon = row.obj.cells[this.env.flagged_col].childNodes[0])
405 && row.flagged_icon.nodeName=='IMG')
408 row.flagged_icon.id = 'flaggedicn_'+row.uid;
409 row.flagged_icon._row = row.obj;
410 row.flagged_icon.onmousedown = function(e) { p.command('toggle_flag', this); };
414 // init message compose form: set focus and eventhandlers
415 this.init_messageform = function()
417 if (!this.gui_objects.messageform)
420 //this.messageform = this.gui_objects.messageform;
421 var input_from = rcube_find_object('_from');
422 var input_to = rcube_find_object('_to');
423 var input_cc = rcube_find_object('_cc');
424 var input_bcc = rcube_find_object('_bcc');
425 var input_replyto = rcube_find_object('_replyto');
426 var input_subject = rcube_find_object('_subject');
427 var input_message = rcube_find_object('_message');
428 var draftid = rcube_find_object('_draft_saveid');
430 // init live search events
432 this.init_address_input_events(input_to);
434 this.init_address_input_events(input_cc);
436 this.init_address_input_events(input_bcc);
438 // add signature according to selected identity
439 if (input_from && input_from.type=='select-one' && (!draftid || draftid.value=='')
440 // if we have HTML editor, signature is added in callback
441 && rcube_find_object('_is_html').value != '1')
443 this.change_identity(input_from);
446 if (input_to && input_to.value=='')
448 else if (input_subject && input_subject.value=='')
449 input_subject.focus();
450 else if (input_message)
451 this.set_caret2start(input_message);
453 // get summary of all field values
454 this.compose_field_hash(true);
456 // start the auto-save timer
457 this.auto_save_start();
460 this.init_address_input_events = function(obj)
462 var handler = function(e){ return ref.ksearch_keypress(e,this); };
464 if (obj.addEventListener)
465 obj.addEventListener(bw.safari ? 'keydown' : 'keypress', handler, false);
467 obj.onkeydown = handler;
469 obj.setAttribute('autocomplete', 'off');
473 /*********************************************************/
474 /********* client command interface *********/
475 /*********************************************************/
477 // execute a specific command on the web client
478 this.command = function(command, props, obj)
486 // command not supported or allowed
487 if (!this.commands[command])
489 // pass command to parent window
490 if (this.env.framed && parent.rcmail && parent.rcmail.command)
491 parent.rcmail.command(command, props);
496 // check input before leaving compose step
497 if (this.task=='mail' && this.env.action=='compose' && (command=='list' || command=='mail' || command=='addressbook' || command=='settings'))
499 if (this.cmp_hash != this.compose_field_hash() && !confirm(this.get_label('notsentwarning')))
507 if (this.gui_objects.loginform)
508 this.gui_objects.loginform.submit();
512 this.goto_url('logout', '', true);
515 // commands to switch task
519 this.switch_task(command);
523 if (obj && obj.href && obj.target)
525 else if (this.env.permaurl)
526 parent.location.href = this.env.permaurl;
529 // misc list commands
531 if (this.task=='mail')
533 if (this.env.search_request<0 || (props != '' && (this.env.search_request && props != this.env.mailbox)))
534 this.reset_qsearch();
536 this.list_mailbox(props);
538 if (this.env.trash_mailbox)
539 this.set_alttext('delete', this.env.mailbox != this.env.trash_mailbox ? 'movemessagetotrash' : 'deletemessage');
541 else if (this.task=='addressbook')
543 if (this.env.search_request<0 || (this.env.search_request && props != this.env.source))
544 this.reset_qsearch();
546 this.list_contacts(props);
547 this.enable_command('add', (this.env.address_sources && !this.env.address_sources[props].readonly));
553 this.load_headers(obj);
558 // get the type of sorting
559 var a_sort = props.split('_');
560 var sort_col = a_sort[0];
561 var sort_order = a_sort[1] ? a_sort[1].toUpperCase() : null;
564 // no sort order specified: toggle
565 if (sort_order==null)
567 if (this.env.sort_col==sort_col)
568 sort_order = this.env.sort_order=='ASC' ? 'DESC' : 'ASC';
570 sort_order = this.env.sort_order;
573 if (this.env.sort_col==sort_col && this.env.sort_order==sort_order)
576 // set table header class
577 if (header = document.getElementById('rcm'+this.env.sort_col))
578 this.set_classname(header, 'sorted'+(this.env.sort_order.toUpperCase()), false);
579 if (header = document.getElementById('rcm'+sort_col))
580 this.set_classname(header, 'sorted'+sort_order, true);
582 // save new sort properties
583 this.env.sort_col = sort_col;
584 this.env.sort_order = sort_order;
586 // reload message list
587 this.list_mailbox('', '', sort_col+'_'+sort_order);
591 this.list_page('next');
595 this.list_page('last');
599 this.list_page('prev');
603 this.list_page('first');
607 if (this.env.messagecount)
608 this.expunge_mailbox(this.env.mailbox);
612 case 'empty-mailbox':
613 if (this.env.messagecount)
614 this.purge_mailbox(this.env.mailbox);
618 // common commands used in multiple tasks
620 if (this.task=='mail')
622 var uid = this.get_single_uid();
623 if (uid && (!this.env.uid || uid != this.env.uid))
625 if (this.env.mailbox == this.env.drafts_mailbox)
626 this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true);
628 this.show_message(uid);
631 else if (this.task=='addressbook')
633 var cid = props ? props : this.get_single_cid();
634 if (cid && !(this.env.action=='show' && cid==this.env.cid))
635 this.load_contact(cid, 'show');
640 if (this.task=='addressbook')
641 this.load_contact(0, 'add');
642 else if (this.task=='settings')
644 this.identity_list.clear_selection();
645 this.load_identity(0, 'add-identity');
651 if (this.task=='addressbook' && (cid = this.get_single_cid()))
652 this.load_contact(cid, 'edit');
653 else if (this.task=='settings' && props)
654 this.load_identity(props, 'edit-identity');
657 case 'save-identity':
659 if (this.gui_objects.editform)
661 var input_pagesize = rcube_find_object('_pagesize');
662 var input_name = rcube_find_object('_name');
663 var input_email = rcube_find_object('_email');
666 if (input_pagesize && isNaN(parseInt(input_pagesize.value)))
668 alert(this.get_label('nopagesizewarning'));
669 input_pagesize.focus();
672 // contacts/identities
675 if (input_name && input_name.value == '')
677 alert(this.get_label('nonamewarning'));
681 else if (input_email && !rcube_check_email(input_email.value))
683 alert(this.get_label('noemailwarning'));
689 this.gui_objects.editform.submit();
695 if (this.task=='mail')
696 this.delete_messages();
698 else if (this.task=='addressbook')
699 this.delete_contacts();
700 // user settings task
701 else if (this.task=='settings')
702 this.delete_identity();
706 // mail task commands
709 if (this.task == 'mail')
710 this.move_messages(props);
711 else if (this.task == 'addressbook' && this.drag_active)
712 this.copy_contact(null, props);
717 this.mark_message(props);
720 case 'toggle_status':
721 if (props && !props._row)
729 uid = props._row.uid;
731 // toggle read/unread
732 if (this.message_list.rows[uid].deleted) {
734 } else if (!this.message_list.rows[uid].unread)
738 this.mark_message(flag, uid);
742 if (props && !props._row)
746 var flag = 'flagged';
750 uid = props._row.uid;
751 // toggle flagged/unflagged
752 if (this.message_list.rows[uid].flagged)
755 this.mark_message(flag, uid);
759 if (this.env.uid && this.env.sender) {
760 this.add_contact(urlencode(this.env.sender));
761 window.setTimeout(function(){ ref.command('load-images'); }, 300);
767 this.show_message(this.env.uid, true, this.env.action=='preview');
770 case 'load-attachment':
771 var qstring = '_mbox='+urlencode(this.env.mailbox)+'&_uid='+this.env.uid+'&_part='+props.part;
773 // open attachment in frame if it's of a supported mimetype
774 if (this.env.uid && props.mimetype && find_in_array(props.mimetype, this.mimetypes)>=0)
776 if (props.mimetype == 'text/html')
777 qstring += '&_safe=1';
778 this.attachment_win = window.open(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1', 'rcubemailattachment');
779 if (this.attachment_win)
781 window.setTimeout(function(){ ref.attachment_win.focus(); }, 10);
786 this.goto_url('get', qstring+'&_download=1', false);
790 this.message_list.select_all(props);
794 this.message_list.clear_selection();
798 if (this.env.next_uid)
799 this.show_message(this.env.next_uid, false, this.env.action=='preview');
803 if (this.env.last_uid)
804 this.show_message(this.env.last_uid);
807 case 'previousmessage':
808 if (this.env.prev_uid)
809 this.show_message(this.env.prev_uid, false, this.env.action=='preview');
813 if (this.env.first_uid)
814 this.show_message(this.env.first_uid);
818 this.check_for_recent(true);
822 var url = this.env.comm_path+'&_action=compose';
824 if (this.task=='mail')
826 url += '&_mbox='+urlencode(this.env.mailbox);
828 if (this.env.mailbox==this.env.drafts_mailbox)
831 if (uid = this.get_single_uid())
832 url += '&_draft_uid='+uid;
835 url += '&_to='+urlencode(props);
837 // modify url if we're in addressbook
838 else if (this.task=='addressbook')
840 // switch to mail compose step directly
841 if (props && props.indexOf('@') > 0)
843 url = this.get_task_url('mail', url);
844 this.redirect(url + '&_to='+urlencode(props));
848 // use contact_id passed as command parameter
849 var a_cids = new Array();
851 a_cids[a_cids.length] = props;
852 // get selected contacts
853 else if (this.contact_list)
855 var selection = this.contact_list.get_selection();
856 for (var n=0; n<selection.length; n++)
857 a_cids[a_cids.length] = selection[n];
861 this.http_request('mailto', '_cid='+urlencode(a_cids.join(','))+'&_source='+urlencode(this.env.source), true);
866 // don't know if this is necessary...
867 url = url.replace(/&_framed=1/, "");
873 if (window.tinyMCE && tinyMCE.get('compose-body')) {
874 tinyMCE.execCommand('mceSpellCheck', true);
876 else if (this.env.spellcheck && this.env.spellcheck.spellCheck && this.spellcheck_ready) {
877 this.env.spellcheck.spellCheck(this.env.spellcheck.check_link);
878 this.set_spellcheck_state('checking');
883 // Reset the auto-save timer
884 self.clearTimeout(this.save_timer);
886 if (!this.gui_objects.messageform)
889 // if saving Drafts is disabled in main.inc.php
890 // or if compose form did not change
891 if (!this.env.drafts_mailbox || this.cmp_hash == this.compose_field_hash())
894 this.set_busy(true, 'savingmessage');
895 var form = this.gui_objects.messageform;
896 form.target = "savetarget";
897 form._draft.value = '1';
902 if (!this.gui_objects.messageform)
905 if (!this.check_compose_input())
908 // Reset the auto-save timer
909 self.clearTimeout(this.save_timer);
911 // all checks passed, send message
912 this.set_busy(true, 'sendingmessage');
913 var form = this.gui_objects.messageform;
914 form.target = "savetarget";
915 form._draft.value = '';
918 // clear timeout (sending could take longer)
919 clearTimeout(this.request_timer);
922 case 'add-attachment':
923 this.show_attachment_form(true);
925 case 'send-attachment':
926 // Reset the auto-save timer
927 self.clearTimeout(this.save_timer);
929 this.upload_file(props)
932 case 'remove-attachment':
933 this.remove_attachment(props);
939 if (uid = this.get_single_uid())
940 this.goto_url('compose', '_reply_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(command=='reply-all' ? '&_all=1' : ''), true);
945 if (uid = this.get_single_uid())
946 this.goto_url('compose', '_forward_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true);
951 if (uid = this.get_single_uid())
953 ref.printwin = window.open(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(this.env.safemode ? '&_safe=1' : ''));
956 window.setTimeout(function(){ ref.printwin.focus(); }, 20);
957 if (this.env.action != 'show')
958 this.mark_message('read', uid);
965 if (uid = this.get_single_uid())
967 ref.sourcewin = window.open(this.env.comm_path+'&_action=viewsource&_uid='+this.env.uid+'&_mbox='+urlencode(this.env.mailbox));
969 window.setTimeout(function(){ ref.sourcewin.focus(); }, 20);
974 this.add_contact(props);
979 if (!props && this.gui_objects.qsearchbox)
980 props = this.gui_objects.qsearchbox.value;
989 var s = this.env.search_request;
990 this.reset_qsearch();
992 if (s && this.env.mailbox)
993 this.list_mailbox(this.env.mailbox);
994 else if (s && this.task == 'addressbook')
995 this.list_contacts(this.env.source);
999 if (this.env.action == 'import' && this.gui_objects.importform) {
1000 var file = document.getElementById('rcmimportfile');
1001 if (file && !file.value) {
1002 alert(this.get_label('selectimportfile'));
1005 this.gui_objects.importform.submit();
1006 this.set_busy(true, 'importwait');
1007 this.lock_form(this.gui_objects.importform, true);
1010 this.goto_url('import');
1014 if (this.contact_list.rowcount > 0) {
1015 var add_url = (this.env.source ? '_source='+urlencode(this.env.source)+'&' : '');
1016 if (this.env.search_request)
1017 add_url += '_search='+this.env.search_request;
1019 this.goto_url('export', add_url);
1023 // collapse/expand folder
1024 case 'collapse-folder':
1026 this.collapse_folder(props);
1029 // user settings commands
1035 this.goto_url('identities');
1038 case 'delete-identity':
1039 this.delete_identity();
1042 this.goto_url('folders');
1046 this.subscribe_folder(props);
1050 this.unsubscribe_folder(props);
1053 case 'create-folder':
1054 this.create_folder(props);
1057 case 'rename-folder':
1058 this.rename_folder(props);
1061 case 'delete-folder':
1062 this.delete_folder(props);
1067 return obj ? false : true;
1070 // set command enabled or disabled
1071 this.enable_command = function()
1073 var args = arguments;
1074 if(!args.length) return -1;
1077 var enable = args[args.length-1];
1079 for(var n=0; n<args.length-1; n++)
1082 this.commands[command] = enable;
1083 this.set_button(command, (enable ? 'act' : 'pas'));
1088 // lock/unlock interface
1089 this.set_busy = function(a, message)
1093 var msg = this.get_label(message);
1097 this.display_message(msg, 'loading', true);
1100 this.hide_message();
1103 //document.body.style.cursor = a ? 'wait' : 'default';
1105 if (this.gui_objects.editform)
1106 this.lock_form(this.gui_objects.editform, a);
1108 // clear pending timer
1109 if (this.request_timer)
1110 clearTimeout(this.request_timer);
1112 // set timer for requests
1113 if (a && this.env.request_timeout)
1114 this.request_timer = window.setTimeout(function(){ ref.request_timed_out(); }, this.env.request_timeout * 1000);
1117 // return a localized string
1118 this.get_label = function(name)
1120 if (this.labels[name])
1121 return this.labels[name];
1126 // switch to another application task
1127 this.switch_task = function(task)
1129 if (this.task===task && task!='mail')
1132 var url = this.get_task_url(task);
1134 url += '&_mbox=INBOX';
1139 this.get_task_url = function(task, url)
1142 url = this.env.comm_path;
1144 return url.replace(/_task=[a-z]+/, '_task='+task);
1147 // called when a request timed out
1148 this.request_timed_out = function()
1150 this.set_busy(false);
1151 this.display_message('Request timed out!', 'error');
1155 /*********************************************************/
1156 /********* event handling methods *********/
1157 /*********************************************************/
1159 this.doc_mouse_up = function(e)
1163 if (this.message_list) {
1164 this.message_list.blur();
1165 model = this.env.mailboxes;
1167 else if (this.contact_list) {
1168 this.contact_list.blur();
1169 model = this.env.address_sources;
1171 else if (this.ksearch_value) {
1172 this.ksearch_blur();
1175 // handle mouse release when dragging
1176 if (this.drag_active && model && this.env.last_folder_target) {
1177 this.set_classname(this.get_folder_li(this.env.last_folder_target), 'droptarget', false);
1178 this.command('moveto', model[this.env.last_folder_target].id);
1179 this.env.last_folder_target = null;
1183 this.drag_start = function(list)
1185 var model = this.task == 'mail' ? this.env.mailboxes : this.env.address_sources;
1187 this.drag_active = true;
1188 if (this.preview_timer)
1189 clearTimeout(this.preview_timer);
1191 // save folderlist and folders location/sizes for droptarget calculation in drag_move()
1192 if (this.gui_objects.folderlist && model)
1194 var li, pos, list, height;
1195 list = rcube_find_object(this.task == 'mail' ? 'mailboxlist' : 'directorylist');
1196 pos = rcube_get_object_pos(list);
1197 this.env.folderlist_coords = {x1:pos.x, y1:pos.y, x2:pos.x + list.offsetWidth, y2:pos.y + list.offsetHeight};
1199 this.env.folder_coords = new Array();
1200 for (var k in model) {
1201 if (li = this.get_folder_li(k))
1203 pos = rcube_get_object_pos(li.firstChild);
1204 // only visible folders
1205 if (height = li.firstChild.offsetHeight)
1206 this.env.folder_coords[k] = {x1:pos.x, y1:pos.y, x2:pos.x + li.firstChild.offsetWidth, y2:pos.y + height};
1212 this.drag_move = function(e)
1214 if (this.gui_objects.folderlist && this.env.folder_coords)
1217 mouse = rcube_event.get_mouse_pos(e);
1218 pos = this.env.folderlist_coords;
1220 // if mouse pointer is outside of folderlist
1221 if (mouse.x < pos.x1 || mouse.x >= pos.x2
1222 || mouse.y < pos.y1 || mouse.y >= pos.y2)
1224 if (this.env.last_folder_target) {
1225 this.set_classname(this.get_folder_li(this.env.last_folder_target), 'droptarget', false);
1226 this.env.last_folder_target = null;
1232 for (var k in this.env.folder_coords)
1234 pos = this.env.folder_coords[k];
1235 if (this.check_droptarget(k) && ((mouse.x >= pos.x1) && (mouse.x < pos.x2)
1236 && (mouse.y >= pos.y1) && (mouse.y < pos.y2)))
1238 this.set_classname(this.get_folder_li(k), 'droptarget', true);
1239 this.env.last_folder_target = k;
1242 this.set_classname(this.get_folder_li(k), 'droptarget', false);
1247 this.collapse_folder = function(id)
1250 if ((li = this.get_folder_li(id)) &&
1251 (div = li.getElementsByTagName("div")[0]) &&
1252 (div.className.match(/collapsed/) || div.className.match(/expanded/)))
1254 var ul = li.getElementsByTagName("ul")[0];
1255 if (div.className.match(/collapsed/))
1257 ul.style.display = '';
1258 this.set_classname(div, 'collapsed', false);
1259 this.set_classname(div, 'expanded', true);
1260 var reg = new RegExp('&'+urlencode(id)+'&');
1261 this.set_env('collapsed_folders', this.env.collapsed_folders.replace(reg, ''));
1265 ul.style.display = 'none';
1266 this.set_classname(div, 'expanded', false);
1267 this.set_classname(div, 'collapsed', true);
1268 this.set_env('collapsed_folders', this.env.collapsed_folders+'&'+urlencode(id)+'&');
1270 // select parent folder if one of its childs is currently selected
1271 if (this.env.mailbox.indexOf(id + this.env.delimiter) == 0)
1272 this.command('list', id);
1275 // Work around a bug in IE6 and IE7, see #1485309
1276 if ((bw.ie6 || bw.ie7) &&
1278 (li.nextSibling.getElementsByTagName("ul").length>0) &&
1279 li.nextSibling.getElementsByTagName("ul")[0].style &&
1280 (li.nextSibling.getElementsByTagName("ul")[0].style.display!='none'))
1282 li.nextSibling.getElementsByTagName("ul")[0].style.display = 'none';
1283 li.nextSibling.getElementsByTagName("ul")[0].style.display = '';
1286 this.http_post('save-pref', '_name=collapsed_folders&_value='+urlencode(this.env.collapsed_folders));
1287 this.set_unread_count_display(id, false);
1291 this.click_on_list = function(e)
1293 if (this.message_list)
1294 this.message_list.focus();
1295 else if (this.contact_list)
1296 this.contact_list.focus();
1299 if (mbox_li = this.get_folder_li())
1300 this.set_classname(mbox_li, 'unfocused', true);
1302 return rcube_event.get_button(e) == 2 ? true : rcube_event.cancel(e);
1305 this.msglist_select = function(list)
1307 if (this.preview_timer)
1308 clearTimeout(this.preview_timer);
1310 var selected = list.selection.length==1;
1312 // Hide certain command buttons when Drafts folder is selected
1313 if (this.env.mailbox == this.env.drafts_mailbox)
1315 this.enable_command('reply', 'reply-all', 'forward', false);
1316 this.enable_command('show', selected);
1317 this.enable_command('delete', 'moveto', 'mark', (list.selection.length > 0 ? true : false));
1321 this.enable_command('show', 'reply', 'reply-all', 'forward', 'print', selected);
1322 this.enable_command('delete', 'moveto', 'mark', (list.selection.length > 0 ? true : false));
1325 // start timer for message preview (wait for double click)
1326 if (selected && this.env.contentframe && !list.multi_selecting)
1327 this.preview_timer = window.setTimeout(function(){ ref.msglist_get_preview(); }, 200);
1328 else if (this.env.contentframe)
1329 this.show_contentframe(false);
1332 this.msglist_dbl_click = function(list)
1334 if (this.preview_timer)
1335 clearTimeout(this.preview_timer);
1337 var uid = list.get_single_selection();
1338 if (uid && this.env.mailbox == this.env.drafts_mailbox)
1339 this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true);
1341 this.show_message(uid, false, false);
1344 this.msglist_keypress = function(list)
1346 if (list.key_pressed == list.ENTER_KEY)
1347 this.command('show');
1348 else if (list.key_pressed == list.DELETE_KEY)
1349 this.command('delete');
1350 else if (list.key_pressed == list.BACKSPACE_KEY)
1351 this.command('delete');
1353 list.shiftkey = false;
1356 this.msglist_get_preview = function()
1358 var uid = this.get_single_uid();
1359 if (uid && this.env.contentframe && !this.drag_active)
1360 this.show_message(uid, false, true);
1361 else if (this.env.contentframe)
1362 this.show_contentframe(false);
1365 this.check_droptarget = function(id)
1367 if (this.task == 'mail')
1368 return (this.env.mailboxes[id] && this.env.mailboxes[id].id != this.env.mailbox && !this.env.mailboxes[id].virtual);
1369 else if (this.task == 'addressbook')
1370 return (id != this.env.source && this.env.address_sources[id] && !this.env.address_sources[id].readonly);
1371 else if (this.task == 'settings')
1372 return (id != this.env.folder);
1376 /*********************************************************/
1377 /********* (message) list functionality *********/
1378 /*********************************************************/
1380 // when user doble-clicks on a row
1381 this.show_message = function(id, safe, preview)
1386 var action = preview ? 'preview': 'show';
1387 var target = window;
1389 if (preview && this.env.contentframe && window.frames && window.frames[this.env.contentframe])
1391 target = window.frames[this.env.contentframe];
1392 add_url = '&_framed=1';
1396 add_url = '&_safe=1';
1398 // also send search request to get the right messages
1399 if (this.env.search_request)
1400 add_url += '&_search='+this.env.search_request;
1401 var url = '&_action='+action+'&_uid='+id+'&_mbox='+urlencode(this.env.mailbox)+add_url;
1402 if (action == 'preview' && String(target.location.href).indexOf(url) >= 0)
1403 this.show_contentframe(true);
1406 this.set_busy(true, 'loading');
1407 target.location.href = this.env.comm_path+url;
1408 // mark as read and change mbox unread counter
1409 if (action == 'preview' && this.message_list && this.message_list.rows[id] && this.message_list.rows[id].unread)
1411 this.set_message(id, 'unread', false);
1412 if (this.env.unread_counts[this.env.mailbox])
1414 this.env.unread_counts[this.env.mailbox] -= 1;
1415 this.set_unread_count(this.env.mailbox, this.env.unread_counts[this.env.mailbox], this.env.mailbox == 'INBOX');
1421 this.show_contentframe = function(show)
1424 if (this.env.contentframe && (frm = rcube_find_object(this.env.contentframe)))
1426 if (!show && window.frames[this.env.contentframe])
1428 if (window.frames[this.env.contentframe].location.href.indexOf(this.env.blankpage)<0)
1429 window.frames[this.env.contentframe].location.href = this.env.blankpage;
1431 else if (!bw.safari)
1432 frm.style.display = show ? 'block' : 'none';
1435 if (!show && this.busy)
1436 this.set_busy(false);
1439 // list a specific page
1440 this.list_page = function(page)
1443 page = this.env.current_page+1;
1445 page = this.env.pagecount;
1446 if (page=='prev' && this.env.current_page>1)
1447 page = this.env.current_page-1;
1448 if (page=='first' && this.env.current_page>1)
1451 if (page > 0 && page <= this.env.pagecount)
1453 this.env.current_page = page;
1455 if (this.task=='mail')
1456 this.list_mailbox(this.env.mailbox, page);
1457 else if (this.task=='addressbook')
1458 this.list_contacts(this.env.source, page);
1462 // list messages of a specific mailbox using filter
1463 this.filter_mailbox = function(filter)
1466 if (this.gui_objects.qsearchbox)
1467 search = this.gui_objects.qsearchbox.value;
1469 this.message_list.clear();
1472 this.env.current_page = 1;
1473 this.set_busy(true, 'searching');
1474 this.http_request('search', '_filter='+filter
1475 + (search ? '&_q='+urlencode(search) : '')
1476 + (this.env.mailbox ? '&_mbox='+urlencode(this.env.mailbox) : ''), true);
1480 // list messages of a specific mailbox
1481 this.list_mailbox = function(mbox, page, sort)
1483 this.last_selected = 0;
1485 var target = window;
1488 mbox = this.env.mailbox;
1490 // add sort to url if set
1492 add_url += '&_sort=' + sort;
1494 // also send search request to get the right messages
1495 if (this.env.search_request)
1496 add_url += '&_search='+this.env.search_request;
1498 // set page=1 if changeing to another mailbox
1499 if (!page && mbox != this.env.mailbox)
1502 this.env.current_page = page;
1503 if (this.message_list)
1504 this.message_list.clear_selection();
1505 this.show_contentframe(false);
1508 if (mbox != this.env.mailbox || (mbox == this.env.mailbox && !page && !sort))
1509 add_url += '&_refresh=1';
1511 this.select_folder(mbox, this.env.mailbox);
1512 this.env.mailbox = mbox;
1514 // load message list remotely
1515 if (this.gui_objects.messagelist)
1517 this.list_mailbox_remote(mbox, page, add_url);
1521 if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
1523 target = window.frames[this.env.contentframe];
1524 add_url += '&_framed=1';
1527 // load message list to target frame/window
1530 this.set_busy(true, 'loading');
1531 target.location.href = this.env.comm_path+'&_mbox='+urlencode(mbox)+(page ? '&_page='+page : '')+add_url;
1535 // send remote request to load message list
1536 this.list_mailbox_remote = function(mbox, page, add_url)
1538 // clear message list first
1539 this.message_list.clear();
1541 // send request to server
1542 var url = '_mbox='+urlencode(mbox)+(page ? '&_page='+page : '');
1543 this.set_busy(true, 'loading');
1544 this.http_request('list', url+add_url, true);
1547 this.expunge_mailbox = function(mbox)
1552 // lock interface if it's the active mailbox
1553 if (mbox == this.env.mailbox)
1556 this.set_busy(true, 'loading');
1557 add_url = '&_reload=1';
1560 // send request to server
1561 var url = '_mbox='+urlencode(mbox);
1562 this.http_post('expunge', url+add_url, lock);
1565 this.purge_mailbox = function(mbox)
1570 if (!confirm(this.get_label('purgefolderconfirm')))
1573 // lock interface if it's the active mailbox
1574 if (mbox == this.env.mailbox)
1577 this.set_busy(true, 'loading');
1578 add_url = '&_reload=1';
1581 // send request to server
1582 var url = '_mbox='+urlencode(mbox);
1583 this.http_post('purge', url+add_url, lock);
1587 // test if purge command is allowed
1588 this.purge_mailbox_test = function()
1590 return (this.env.messagecount && (this.env.mailbox == this.env.trash_mailbox || this.env.mailbox == this.env.junk_mailbox
1591 || this.env.mailbox.match('^' + RegExp.escape(this.env.trash_mailbox) + RegExp.escape(this.env.delimiter))
1592 || this.env.mailbox.match('^' + RegExp.escape(this.env.junk_mailbox) + RegExp.escape(this.env.delimiter))));
1596 this.set_message_icon = function(uid)
1599 var rows = this.message_list.rows;
1604 if (rows[uid].deleted && this.env.deletedicon)
1605 icn_src = this.env.deletedicon;
1606 else if (rows[uid].replied && this.env.repliedicon)
1608 if (rows[uid].forwarded && this.env.forwardedrepliedicon)
1609 icn_src = this.env.forwardedrepliedicon;
1611 icn_src = this.env.repliedicon;
1613 else if (rows[uid].forwarded && this.env.forwardedicon)
1614 icn_src = this.env.forwardedicon;
1615 else if (rows[uid].unread && this.env.unreadicon)
1616 icn_src = this.env.unreadicon;
1617 else if (this.env.messageicon)
1618 icn_src = this.env.messageicon;
1620 if (icn_src && rows[uid].icon)
1621 rows[uid].icon.src = icn_src;
1625 if (rows[uid].flagged && this.env.flaggedicon)
1626 icn_src = this.env.flaggedicon;
1627 else if (!rows[uid].flagged && this.env.unflaggedicon)
1628 icn_src = this.env.unflaggedicon;
1630 if (rows[uid].flagged_icon && icn_src)
1631 rows[uid].flagged_icon.src = icn_src;
1634 // set message status
1635 this.set_message_status = function(uid, flag, status)
1637 var rows = this.message_list.rows;
1639 if (!rows[uid]) return false;
1641 if (flag == 'unread')
1642 rows[uid].unread = status;
1643 else if(flag == 'deleted')
1644 rows[uid].deleted = status;
1645 else if (flag == 'replied')
1646 rows[uid].replied = status;
1647 else if (flag == 'forwarded')
1648 rows[uid].forwarded = status;
1649 else if (flag == 'flagged')
1650 rows[uid].flagged = status;
1652 this.env.messages[uid] = rows[uid];
1655 // set message row status, class and icon
1656 this.set_message = function(uid, flag, status)
1658 var rows = this.message_list.rows;
1660 if (!rows[uid]) return false;
1663 this.set_message_status(uid, flag, status);
1665 if (rows[uid].unread && rows[uid].classname.indexOf('unread')<0)
1667 rows[uid].classname += ' unread';
1668 this.set_classname(rows[uid].obj, 'unread', true);
1670 else if (!rows[uid].unread && rows[uid].classname.indexOf('unread')>=0)
1672 rows[uid].classname = rows[uid].classname.replace(/\s*unread/, '');
1673 this.set_classname(rows[uid].obj, 'unread', false);
1676 if (rows[uid].deleted && rows[uid].classname.indexOf('deleted')<0)
1678 rows[uid].classname += ' deleted';
1679 this.set_classname(rows[uid].obj, 'deleted', true);
1681 else if (!rows[uid].deleted && rows[uid].classname.indexOf('deleted')>=0)
1683 rows[uid].classname = rows[uid].classname.replace(/\s*deleted/, '');
1684 this.set_classname(rows[uid].obj, 'deleted', false);
1687 if (rows[uid].flagged && rows[uid].classname.indexOf('flagged')<0)
1689 rows[uid].classname += ' flagged';
1690 this.set_classname(rows[uid].obj, 'flagged', true);
1692 else if (!rows[uid].flagged && rows[uid].classname.indexOf('flagged')>=0)
1694 rows[uid].classname = rows[uid].classname.replace(/\s*flagged/, '');
1695 this.set_classname(rows[uid].obj, 'flagged', false);
1698 this.set_message_icon(uid);
1701 // move selected messages to the specified mailbox
1702 this.move_messages = function(mbox)
1704 // exit if current or no mailbox specified or if selection is empty
1705 if (!mbox || mbox == this.env.mailbox || (!this.env.uid && (!this.message_list || !this.message_list.get_selection().length)))
1709 var add_url = '&_target_mbox='+urlencode(mbox)+'&_from='+(this.env.action ? this.env.action : '');
1711 // show wait message
1712 if (this.env.action=='show')
1715 this.set_busy(true, 'movingmessage');
1717 else if (!this.env.flag_for_deletion)
1718 this.show_contentframe(false);
1720 // Hide message command buttons until a message is selected
1721 this.enable_command('reply', 'reply-all', 'forward', 'delete', 'mark', 'print', false);
1723 this._with_selected_messages('moveto', lock, add_url, (this.env.flag_for_deletion ? false : true));
1726 // delete selected messages from the current mailbox
1727 this.delete_messages = function()
1729 var selection = this.message_list ? this.message_list.get_selection() : new Array();
1731 // exit if no mailbox specified or if selection is empty
1732 if (!this.env.uid && !selection.length)
1735 // if there is a trash mailbox defined and we're not currently in it:
1736 if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase() != String(this.env.trash_mailbox).toLowerCase())
1738 // if shift was pressed delete it immediately
1739 if (this.message_list && this.message_list.shiftkey)
1741 if (confirm(this.get_label('deletemessagesconfirm')))
1742 this.permanently_remove_messages();
1745 this.move_messages(this.env.trash_mailbox);
1747 // if there is a trash mailbox defined but we *are* in it:
1748 else if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase() == String(this.env.trash_mailbox).toLowerCase())
1749 this.permanently_remove_messages();
1750 // if there isn't a defined trash mailbox and the config is set to flag for deletion
1751 else if (!this.env.trash_mailbox && this.env.flag_for_deletion)
1753 this.mark_message('delete');
1754 if(this.env.action=="show")
1755 this.command('nextmessage','',this);
1756 else if (selection.length == 1)
1757 this.message_list.select_next();
1759 // if there isn't a defined trash mailbox and the config is set NOT to flag for deletion
1760 else if (!this.env.trash_mailbox)
1761 this.permanently_remove_messages();
1764 // delete the selected messages permanently
1765 this.permanently_remove_messages = function()
1767 // exit if no mailbox specified or if selection is empty
1768 if (!this.env.uid && (!this.message_list || !this.message_list.get_selection().length))
1771 this.show_contentframe(false);
1772 this._with_selected_messages('delete', false, '&_from='+(this.env.action ? this.env.action : ''), true);
1775 // Send a specifc request with UIDs of all selected messages
1777 this._with_selected_messages = function(action, lock, add_url, remove)
1779 var a_uids = new Array();
1782 a_uids[0] = this.env.uid;
1785 var selection = this.message_list.get_selection();
1786 var rows = this.message_list.rows;
1788 for (var n=0; n<selection.length; n++)
1791 a_uids[a_uids.length] = id;
1794 this.message_list.remove_row(id, (n == selection.length-1));
1797 this.set_message_status(id, 'deleted', true);
1798 if (this.env.read_when_deleted)
1799 this.set_message_status(id, 'unread', false);
1800 this.set_message(id);
1805 // also send search request to get the right messages
1806 if (this.env.search_request)
1807 add_url += '&_search='+this.env.search_request;
1809 // send request to server
1810 this.http_post(action, '_uid='+a_uids.join(',')+'&_mbox='+urlencode(this.env.mailbox)+add_url, lock);
1813 // set a specific flag to one or more messages
1814 this.mark_message = function(flag, uid)
1816 var a_uids = new Array();
1817 var r_uids = new Array();
1818 var selection = this.message_list ? this.message_list.get_selection() : new Array();
1822 else if (this.env.uid)
1823 a_uids[0] = this.env.uid;
1824 else if (this.message_list)
1826 for (var n=0; n<selection.length; n++)
1828 a_uids[a_uids.length] = selection[n];
1832 if (!this.message_list)
1835 for (var id, n=0; n<a_uids.length; n++)
1838 if ((flag=='read' && this.message_list.rows[id].unread)
1839 || (flag=='unread' && !this.message_list.rows[id].unread)
1840 || (flag=='delete' && !this.message_list.rows[id].deleted)
1841 || (flag=='undelete' && this.message_list.rows[id].deleted)
1842 || (flag=='flagged' && !this.message_list.rows[id].flagged)
1843 || (flag=='unflagged' && this.message_list.rows[id].flagged))
1845 r_uids[r_uids.length] = id;
1857 this.toggle_read_status(flag, r_uids);
1861 this.toggle_delete_status(r_uids);
1865 this.toggle_flagged_status(flag, a_uids);
1870 // set class to read/unread
1871 this.toggle_read_status = function(flag, a_uids)
1873 // mark all message rows as read/unread
1874 for (var i=0; i<a_uids.length; i++)
1875 this.set_message(a_uids[i], 'unread', (flag=='unread' ? true : false));
1877 this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag='+flag);
1880 // set image to flagged or unflagged
1881 this.toggle_flagged_status = function(flag, a_uids)
1883 // mark all message rows as flagged/unflagged
1884 for (var i=0; i<a_uids.length; i++)
1885 this.set_message(a_uids[i], 'flagged', (flag=='flagged' ? true : false));
1887 this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag='+flag);
1890 // mark all message rows as deleted/undeleted
1891 this.toggle_delete_status = function(a_uids)
1893 var rows = this.message_list ? this.message_list.rows : new Array();
1895 if (a_uids.length==1)
1897 if (!rows.length || (rows[a_uids[0]] && !rows[a_uids[0]].deleted))
1898 this.flag_as_deleted(a_uids);
1900 this.flag_as_undeleted(a_uids);
1905 var all_deleted = true;
1906 for (var i=0; i<a_uids.length; i++)
1910 if (!rows[uid].deleted)
1912 all_deleted = false;
1919 this.flag_as_undeleted(a_uids);
1921 this.flag_as_deleted(a_uids);
1926 this.flag_as_undeleted = function(a_uids)
1928 for (var i=0; i<a_uids.length; i++)
1929 this.set_message(a_uids[i], 'deleted', false);
1931 this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag=undelete');
1935 this.flag_as_deleted = function(a_uids)
1938 var r_uids = new Array();
1939 var rows = this.message_list ? this.message_list.rows : new Array();
1941 for (var i=0; i<a_uids.length; i++)
1946 this.set_message(uid, 'deleted', true);
1947 if (rows[uid].unread)
1948 r_uids[r_uids.length] = uid;
1953 add_url = '&_ruid='+r_uids.join(',');
1955 this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag=delete'+add_url);
1959 // flag as read without mark request (called from backend)
1960 // argument should be a coma-separated list of uids
1961 this.flag_deleted_as_read = function(uids)
1964 var rows = this.message_list ? this.message_list.rows : new Array();
1965 var str = String(uids);
1966 var a_uids = new Array();
1968 a_uids = str.split(',');
1970 for (var uid, i=0; i<a_uids.length; i++)
1974 this.set_message(uid, 'unread', false);
1979 /*********************************************************/
1980 /********* login form methods *********/
1981 /*********************************************************/
1983 // handler for keyboard events on the _user field
1984 this.login_user_keyup = function(e)
1986 var key = rcube_event.get_keycode(e);
1990 if ((key==13) && (elm = rcube_find_object('_pass')))
1998 /*********************************************************/
1999 /********* message compose methods *********/
2000 /*********************************************************/
2002 // checks the input fields before sending a message
2003 this.check_compose_input = function()
2005 // check input fields
2006 var input_to = rcube_find_object('_to');
2007 var input_cc = rcube_find_object('_cc');
2008 var input_bcc = rcube_find_object('_bcc');
2009 var input_from = rcube_find_object('_from');
2010 var input_subject = rcube_find_object('_subject');
2011 var input_message = rcube_find_object('_message');
2013 // check sender (if have no identities)
2014 if (input_from.type == 'text' && !rcube_check_email(input_from.value, true))
2016 alert(this.get_label('nosenderwarning'));
2021 // check for empty recipient
2022 var recipients = input_to.value ? input_to.value : (input_cc.value ? input_cc.value : input_bcc.value);
2023 if (!rcube_check_email(recipients.replace(/^\s+/, '').replace(/[\s,;]+$/, ''), true))
2025 alert(this.get_label('norecipientwarning'));
2030 // display localized warning for missing subject
2031 if (input_subject && input_subject.value == '')
2033 var subject = prompt(this.get_label('nosubjectwarning'), this.get_label('nosubject'));
2035 // user hit cancel, so don't send
2036 if (!subject && subject !== '')
2038 input_subject.focus();
2043 input_subject.value = subject ? subject : this.get_label('nosubject');
2047 // check for empty body
2048 if ((!window.tinyMCE || !tinyMCE.get('compose-body')) && input_message.value == '' && !confirm(this.get_label('nobodywarning')))
2050 input_message.focus();
2053 else if (window.tinyMCE && tinyMCE.get('compose-body') && !tinyMCE.get('compose-body').getContent() && !confirm(this.get_label('nobodywarning')))
2055 tinyMCE.get('compose-body').focus();
2059 // Apply spellcheck changes if spell checker is active
2060 this.stop_spellchecking();
2065 this.stop_spellchecking = function()
2067 if (this.env.spellcheck && !this.spellcheck_ready) {
2068 exec_event(this.env.spellcheck.check_link, 'click');
2069 this.set_spellcheck_state('ready');
2073 this.display_spellcheck_controls = function(vis)
2075 if (this.env.spellcheck) {
2076 // stop spellchecking process
2078 this.stop_spellchecking();
2080 this.env.spellcheck.check_link.style.visibility = vis ? 'visible' : 'hidden';
2081 this.env.spellcheck.switch_lan_pic.style.visibility = vis ? 'visible' : 'hidden';
2085 this.set_spellcheck_state = function(s)
2087 this.spellcheck_ready = (s=='check_spelling' || s=='ready');
2088 this.enable_command('spellcheck', this.spellcheck_ready);
2091 this.set_draft_id = function(id)
2094 if (f = rcube_find_object('_draft_saveid'))
2098 this.auto_save_start = function()
2100 if (this.env.draft_autosave)
2101 this.save_timer = self.setTimeout(function(){ ref.command("savedraft"); }, this.env.draft_autosave * 1000);
2103 // Unlock interface now that saving is complete
2107 this.compose_field_hash = function(save)
2109 // check input fields
2110 var input_to = rcube_find_object('_to');
2111 var input_cc = rcube_find_object('_cc');
2112 var input_bcc = rcube_find_object('_bcc');
2113 var input_subject = rcube_find_object('_subject');
2114 var editor, input_message;
2117 if (input_to && input_to.value)
2118 str += input_to.value+':';
2119 if (input_cc && input_cc.value)
2120 str += input_cc.value+':';
2121 if (input_bcc && input_bcc.value)
2122 str += input_bcc.value+':';
2123 if (input_subject && input_subject.value)
2124 str += input_subject.value+':';
2126 if (editor = tinyMCE.get('compose-body'))
2127 str += editor.getContent();
2130 input_message = rcube_find_object('_message');
2131 str += input_message.value;
2135 this.cmp_hash = str;
2140 this.change_identity = function(obj)
2142 if (!obj || !obj.options)
2145 var id = obj.options[obj.selectedIndex].value;
2146 var input_message = rcube_find_object('_message');
2147 var message = input_message ? input_message.value : '';
2148 var is_html = (rcube_find_object('_is_html').value == '1');
2151 if (!this.env.identity)
2152 this.env.identity = id
2156 // remove the 'old' signature
2157 if (this.env.identity && this.env.signatures && this.env.signatures[this.env.identity])
2159 if (this.env.signatures[this.env.identity]['is_html'])
2160 sig = this.env.signatures[this.env.identity]['plain_text'];
2162 sig = this.env.signatures[this.env.identity]['text'];
2164 if (sig.indexOf('-- ')!=0)
2167 p = message.lastIndexOf(sig);
2169 message = message.substring(0, p-1) + message.substring(p+sig.length, message.length);
2172 message = message.replace(/[\r\n]+$/, '');
2174 // add the new signature string
2175 if (this.env.signatures && this.env.signatures[id])
2177 sig = this.env.signatures[id]['text'];
2178 if (this.env.signatures[id]['is_html'])
2180 sig = this.env.signatures[id]['plain_text'];
2182 if (sig.indexOf('-- ')!=0)
2184 message += '\n\n'+sig;
2189 var editor = tinyMCE.get('compose-body');
2191 if (this.env.signatures)
2193 // Append the signature as a div within the body
2194 var sigElem = editor.dom.get('_rc_sig');
2200 // add empty line before signature on IE
2202 editor.getBody().appendChild(editor.getDoc().createElement('br'));
2204 sigElem = editor.getDoc().createElement('div');
2205 sigElem.setAttribute('id', '_rc_sig');
2206 editor.getBody().appendChild(sigElem);
2209 if (this.env.signatures[id])
2211 newsig = this.env.signatures[id]['text'];
2212 htmlsig = this.env.signatures[id]['is_html'];
2216 sigElem.innerHTML = newsig;
2218 sigElem.innerHTML = '<pre>' + newsig + '</pre>';
2223 input_message.value = message;
2225 this.env.identity = id;
2229 this.show_attachment_form = function(a)
2231 if (!this.gui_objects.uploadbox)
2235 if (elm = this.gui_objects.uploadbox)
2237 if (a && (list = this.gui_objects.attachmentlist))
2239 var pos = rcube_get_object_pos(list);
2241 var top = pos.y + list.offsetHeight + 10;
2243 elm.style.top = top+'px';
2244 elm.style.left = left+'px';
2247 elm.style.visibility = a ? 'visible' : 'hidden';
2250 // clear upload form
2252 if (!a && this.gui_objects.attachmentform != this.gui_objects.messageform)
2253 this.gui_objects.attachmentform.reset();
2255 catch(e){} // ignore errors
2260 // upload attachment file
2261 this.upload_file = function(form)
2266 // get file input fields
2268 for (var n=0; n<form.elements.length; n++)
2269 if (form.elements[n].type=='file' && form.elements[n].value)
2275 // create hidden iframe and post upload form
2278 var ts = new Date().getTime();
2279 var frame_name = 'rcmupload'+ts;
2281 // have to do it this way for IE
2282 // otherwise the form will be posted to a new window
2285 var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
2286 document.body.insertAdjacentHTML('BeforeEnd',html);
2288 else // for standards-compilant browsers
2290 var frame = document.createElement('IFRAME');
2291 frame.name = frame_name;
2292 frame.style.border = 'none';
2293 frame.style.width = 0;
2294 frame.style.height = 0;
2295 frame.style.visibility = 'hidden';
2296 document.body.appendChild(frame);
2299 form.target = frame_name;
2300 form.action = this.env.comm_path+'&_action=upload';
2301 form.setAttribute('enctype', 'multipart/form-data');
2305 // set reference to the form object
2306 this.gui_objects.attachmentform = form;
2310 // add file name to attachment list
2311 // called from upload page
2312 this.add2attachment_list = function(name, content)
2314 if (!this.gui_objects.attachmentlist)
2317 var li = document.createElement('LI');
2319 li.innerHTML = content;
2320 this.gui_objects.attachmentlist.appendChild(li);
2324 this.remove_from_attachment_list = function(name)
2326 if (!this.gui_objects.attachmentlist)
2329 var list = this.gui_objects.attachmentlist.getElementsByTagName("li");
2330 for (i=0;i<list.length;i++)
2331 if (list[i].id == name)
2332 this.gui_objects.attachmentlist.removeChild(list[i]);
2335 this.remove_attachment = function(name)
2338 this.http_post('remove-attachment', '_file='+urlencode(name));
2343 // send remote request to add a new contact
2344 this.add_contact = function(value)
2347 this.http_post('addcontact', '_address='+value);
2352 // send remote request to search mail or contacts
2353 this.qsearch = function(value, addurl)
2357 if (this.message_list)
2358 this.message_list.clear();
2359 else if (this.contact_list) {
2360 this.contact_list.clear(true);
2361 this.show_contentframe(false);
2364 if (this.gui_objects.search_filter)
2365 addurl = '&_filter=' + this.gui_objects.search_filter.value;
2368 this.env.current_page = 1;
2369 this.set_busy(true, 'searching');
2370 this.http_request('search', '_q='+urlencode(value)
2371 + (this.env.mailbox ? '&_mbox='+urlencode(this.env.mailbox) : '')
2372 + (this.env.source ? '&_source='+urlencode(this.env.source) : '')
2373 + (addurl ? addurl : ''), true);
2378 // reset quick-search form
2379 this.reset_qsearch = function()
2381 if (this.gui_objects.qsearchbox)
2382 this.gui_objects.qsearchbox.value = '';
2384 this.env.search_request = null;
2388 this.sent_successfully = function(type, msg)
2390 this.list_mailbox();
2391 this.display_message(msg, type, true);
2395 /*********************************************************/
2396 /********* keyboard live-search methods *********/
2397 /*********************************************************/
2399 // handler for keyboard events on address-fields
2400 this.ksearch_keypress = function(e, obj)
2402 if (this.ksearch_timer)
2403 clearTimeout(this.ksearch_timer);
2406 var key = rcube_event.get_keycode(e);
2407 var mod = rcube_event.get_modifier(e);
2412 case 40: // key down
2413 if (!this.ksearch_pane)
2416 var dir = key==38 ? 1 : 0;
2418 highlight = document.getElementById('rcmksearchSelected');
2420 highlight = this.ksearch_pane.ul.firstChild;
2423 this.ksearch_select(dir ? highlight.previousSibling : highlight.nextSibling);
2425 return rcube_event.cancel(e);
2428 if(mod == SHIFT_KEY)
2432 if (this.ksearch_selected===null || !this.ksearch_input || !this.ksearch_value)
2435 // insert selected address and hide ksearch pane
2436 this.insert_recipient(this.ksearch_selected);
2437 this.ksearch_hide();
2439 return rcube_event.cancel(e);
2442 this.ksearch_hide();
2448 this.ksearch_timer = window.setTimeout(function(){ ref.ksearch_get_results(); }, 200);
2449 this.ksearch_input = obj;
2454 this.ksearch_select = function(node)
2456 var current = document.getElementById('rcmksearchSelected');
2457 if (current && node) {
2458 current.removeAttribute('id');
2459 this.set_classname(current, 'selected', false);
2463 node.setAttribute('id', 'rcmksearchSelected');
2464 this.set_classname(node, 'selected', true);
2465 this.ksearch_selected = node._rcm_id;
2469 this.insert_recipient = function(id)
2471 if (!this.env.contacts[id] || !this.ksearch_input)
2475 var inp_value = this.ksearch_input.value.toLowerCase();
2476 var cpos = this.get_caret_pos(this.ksearch_input);
2477 var p = inp_value.lastIndexOf(this.ksearch_value, cpos);
2479 // replace search string with full address
2480 var pre = this.ksearch_input.value.substring(0, p);
2481 var end = this.ksearch_input.value.substring(p+this.ksearch_value.length, this.ksearch_input.value.length);
2482 var insert = this.env.contacts[id]+', ';
2483 this.ksearch_input.value = pre + insert + end;
2485 // set caret to insert pos
2486 cpos = p+insert.length;
2487 if (this.ksearch_input.setSelectionRange)
2488 this.ksearch_input.setSelectionRange(cpos, cpos);
2491 // address search processor
2492 this.ksearch_get_results = function()
2494 var inp_value = this.ksearch_input ? this.ksearch_input.value : null;
2495 if (inp_value === null)
2498 if (this.ksearch_pane && this.ksearch_pane.visible)
2499 this.ksearch_pane.show(0);
2501 // get string from current cursor pos to last comma
2502 var cpos = this.get_caret_pos(this.ksearch_input);
2503 var p = inp_value.lastIndexOf(',', cpos-1);
2504 var q = inp_value.substring(p+1, cpos);
2506 // trim query string
2507 q = q.replace(/(^\s+|\s+$)/g, '').toLowerCase();
2509 // Don't (re-)search if string is empty or if the last results are still active
2510 if (!q.length || q == this.ksearch_value)
2513 this.ksearch_value = q;
2515 this.display_message(this.get_label('searching'), 'loading', true);
2516 this.http_post('autocomplete', '_search='+q);
2519 this.ksearch_query_results = function(results)
2521 this.hide_message();
2522 this.env.contacts = results ? results : [];
2524 var result_ids = new Array();
2526 for (var i=0; i < this.env.contacts.length; i++) {
2527 result_ids[c++] = i;
2528 if (c == 15) // limit search results
2532 this.ksearch_display_results(this.env.contacts, result_ids, c);
2535 this.ksearch_display_results = function (a_results, a_result_ids, c)
2537 // display search results
2538 if (c && a_results.length && this.ksearch_input) {
2541 // create results pane if not present
2542 if (!this.ksearch_pane) {
2543 ul = document.createElement('UL');
2544 this.ksearch_pane = new rcube_layer('rcmKSearchpane', {vis:0, zindex:30000});
2545 this.ksearch_pane.elm.appendChild(ul);
2546 this.ksearch_pane.ul = ul;
2549 ul = this.ksearch_pane.ul;
2551 // remove all search results
2554 // add each result line to list
2555 for (i=0; i<a_results.length; i++) {
2556 li = document.createElement('LI');
2557 li.innerHTML = a_results[i].replace(new RegExp('('+this.ksearch_value+')', 'ig'), '##$1%%').replace(/</g, '<').replace(/>/g, '>').replace(/##([^%]+)%%/g, '<b>$1</b>');
2558 li.onmouseover = function(){ ref.ksearch_select(this); };
2559 li.onmouseup = function(){ ref.ksearch_click(this) };
2560 li._rcm_id = a_result_ids[i];
2564 // check if last selected item is still in result list
2565 if (this.ksearch_selected !== null) {
2566 p = find_in_array(this.ksearch_selected, a_result_ids);
2567 if (p >= 0 && ul.childNodes) {
2568 ul.childNodes[p].setAttribute('id', 'rcmksearchSelected');
2569 this.set_classname(ul.childNodes[p], 'selected', true);
2572 this.ksearch_selected = null;
2575 // if no item selected, select the first one
2576 if (this.ksearch_selected === null) {
2577 ul.firstChild.setAttribute('id', 'rcmksearchSelected');
2578 this.set_classname(ul.firstChild, 'selected', true);
2579 this.ksearch_selected = a_result_ids[0];
2582 // move the results pane right under the input box and make it visible
2583 var pos = rcube_get_object_pos(this.ksearch_input);
2584 this.ksearch_pane.move(pos.x, pos.y+this.ksearch_input.offsetHeight);
2585 this.ksearch_pane.show(1);
2587 // hide results pane
2589 this.ksearch_hide();
2592 this.ksearch_click = function(node)
2594 this.insert_recipient(node._rcm_id);
2595 this.ksearch_hide();
2597 if (ref.ksearch_input)
2598 this.ksearch_input.focus();
2601 this.ksearch_blur = function()
2603 if (this.ksearch_timer)
2604 clearTimeout(this.ksearch_timer);
2606 this.ksearch_value = '';
2607 this.ksearch_input = null;
2609 this.ksearch_hide();
2613 this.ksearch_hide = function()
2615 this.ksearch_selected = null;
2617 if (this.ksearch_pane)
2618 this.ksearch_pane.show(0);
2622 /*********************************************************/
2623 /********* address book methods *********/
2624 /*********************************************************/
2626 this.contactlist_keypress = function(list)
2628 if (list.key_pressed == list.DELETE_KEY)
2629 this.command('delete');
2632 this.contactlist_select = function(list)
2634 if (this.preview_timer)
2635 clearTimeout(this.preview_timer);
2637 var id, frame, ref = this;
2638 if (id = list.get_single_selection())
2639 this.preview_timer = window.setTimeout(function(){ ref.load_contact(id, 'show'); }, 200);
2640 else if (this.env.contentframe)
2641 this.show_contentframe(false);
2643 this.enable_command('compose', list.selection.length > 0);
2644 this.enable_command('edit', (id && this.env.address_sources && !this.env.address_sources[this.env.source].readonly) ? true : false);
2645 this.enable_command('delete', list.selection.length && this.env.address_sources && !this.env.address_sources[this.env.source].readonly);
2650 this.list_contacts = function(src, page)
2653 var target = window;
2656 src = this.env.source;
2658 if (page && this.current_page==page && src == this.env.source)
2661 if (src != this.env.source)
2664 this.env.current_page = page;
2665 this.reset_qsearch();
2668 this.select_folder(src, this.env.source);
2669 this.env.source = src;
2671 // load contacts remotely
2672 if (this.gui_objects.contactslist)
2674 this.list_contacts_remote(src, page);
2678 if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
2680 target = window.frames[this.env.contentframe];
2681 add_url = '&_framed=1';
2684 // also send search request to get the correct listing
2685 if (this.env.search_request)
2686 add_url += '&_search='+this.env.search_request;
2688 this.set_busy(true, 'loading');
2689 target.location.href = this.env.comm_path+(src ? '&_source='+urlencode(src) : '')+(page ? '&_page='+page : '')+add_url;
2692 // send remote request to load contacts list
2693 this.list_contacts_remote = function(src, page)
2695 // clear message list first
2696 this.contact_list.clear(true);
2697 this.show_contentframe(false);
2698 this.enable_command('delete', 'compose', false);
2700 // send request to server
2701 var url = (src ? '_source='+urlencode(src) : '') + (page ? (src?'&':'') + '_page='+page : '');
2702 this.env.source = src;
2704 // also send search request to get the right messages
2705 if (this.env.search_request)
2706 url += '&_search='+this.env.search_request;
2708 this.set_busy(true, 'loading');
2709 this.http_request('list', url, true);
2712 // load contact record
2713 this.load_contact = function(cid, action, framed)
2716 var target = window;
2717 if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
2719 add_url = '&_framed=1';
2720 target = window.frames[this.env.contentframe];
2721 this.show_contentframe(true);
2726 if (action && (cid || action=='add') && !this.drag_active)
2728 this.set_busy(true);
2729 target.location.href = this.env.comm_path+'&_action='+action+'&_source='+urlencode(this.env.source)+'&_cid='+urlencode(cid) + add_url;
2734 // copy a contact to the specified target (group or directory)
2735 this.copy_contact = function(cid, to)
2738 cid = this.contact_list.get_selection().join(',');
2740 if (to != this.env.source && cid && this.env.address_sources[to] && !this.env.address_sources[to].readonly)
2741 this.http_post('copy', '_cid='+urlencode(cid)+'&_source='+urlencode(this.env.source)+'&_to='+urlencode(to));
2745 this.delete_contacts = function()
2747 // exit if no mailbox specified or if selection is empty
2748 var selection = this.contact_list.get_selection();
2749 if (!(selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm')))
2752 var a_cids = new Array();
2756 a_cids[a_cids.length] = this.env.cid;
2760 for (var n=0; n<selection.length; n++)
2763 a_cids[a_cids.length] = id;
2764 this.contact_list.remove_row(id, (n == selection.length-1));
2767 // hide content frame if we delete the currently displayed contact
2768 if (selection.length == 1)
2769 this.show_contentframe(false);
2772 // also send search request to get the right records from the next page
2773 if (this.env.search_request)
2774 qs += '&_search='+this.env.search_request;
2776 // send request to server
2777 this.http_post('delete', '_cid='+urlencode(a_cids.join(','))+'&_source='+urlencode(this.env.source)+'&_from='+(this.env.action ? this.env.action : '')+qs);
2781 // update a contact record in the list
2782 this.update_contact_row = function(cid, cols_arr)
2785 if (this.contact_list.rows[cid] && (row = this.contact_list.rows[cid].obj))
2787 for (var c=0; c<cols_arr.length; c++)
2789 row.cells[c].innerHTML = cols_arr[c];
2798 /*********************************************************/
2799 /********* user settings methods *********/
2800 /*********************************************************/
2802 this.init_subscription_list = function()
2805 this.subscription_list = new rcube_list_widget(this.gui_objects.subscriptionlist, {multiselect:false, draggable:true, keyboard:false, toggleselect:true});
2806 this.subscription_list.addEventListener('select', function(o){ p.subscription_select(o); });
2807 this.subscription_list.addEventListener('dragstart', function(o){ p.drag_active = true; });
2808 this.subscription_list.addEventListener('dragend', function(o){ p.subscription_move_folder(o); });
2809 this.subscription_list.row_init = function (row)
2811 var anchors = row.obj.getElementsByTagName('A');
2813 anchors[0].onclick = function() { p.rename_folder(row.id); return false; };
2815 anchors[1].onclick = function() { p.delete_folder(row.id); return false; };
2816 row.obj.onmouseover = function() { p.focus_subscription(row.id); };
2817 row.obj.onmouseout = function() { p.unfocus_subscription(row.id); };
2819 this.subscription_list.init();
2822 this.identity_select = function(list)
2825 if (id = list.get_single_selection())
2826 this.load_identity(id, 'edit-identity');
2829 // load contact record
2830 this.load_identity = function(id, action)
2832 if (action=='edit-identity' && (!id || id==this.env.iid))
2836 var target = window;
2837 if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
2839 add_url = '&_framed=1';
2840 target = window.frames[this.env.contentframe];
2841 document.getElementById(this.env.contentframe).style.visibility = 'inherit';
2844 if (action && (id || action=='add-identity'))
2846 this.set_busy(true);
2847 target.location.href = this.env.comm_path+'&_action='+action+'&_iid='+id+add_url;
2852 this.delete_identity = function(id)
2854 // exit if no mailbox specified or if selection is empty
2855 var selection = this.identity_list.get_selection();
2856 if (!(selection.length || this.env.iid))
2860 id = this.env.iid ? this.env.iid : selection[0];
2862 // if (this.env.framed && id)
2863 this.goto_url('delete-identity', '_iid='+id, true);
2867 this.focus_subscription = function(id)
2870 var reg = RegExp('['+RegExp.escape(this.env.delimiter)+']?[^'+RegExp.escape(this.env.delimiter)+']+$');
2872 if (this.drag_active && this.env.folder && (row = document.getElementById(id)))
2873 if (this.env.subscriptionrows[id] &&
2874 (folder = this.env.subscriptionrows[id][0]))
2876 if (this.check_droptarget(folder) &&
2877 !this.env.subscriptionrows[this.get_folder_row_id(this.env.folder)][2] &&
2878 (folder != this.env.folder.replace(reg, '')) &&
2879 (!folder.match(new RegExp('^'+RegExp.escape(this.env.folder+this.env.delimiter)))))
2881 this.set_env('dstfolder', folder);
2882 this.set_classname(row, 'droptarget', true);
2885 else if (this.env.folder.match(new RegExp(RegExp.escape(this.env.delimiter))))
2887 this.set_env('dstfolder', this.env.delimiter);
2888 this.set_classname(this.subscription_list.frame, 'droptarget', true);
2892 this.unfocus_subscription = function(id)
2895 this.set_env('dstfolder', null);
2896 if (this.env.subscriptionrows[id] &&
2897 (row = document.getElementById(id)))
2898 this.set_classname(row, 'droptarget', false);
2900 this.set_classname(this.subscription_list.frame, 'droptarget', false);
2903 this.subscription_select = function(list)
2906 if ((id = list.get_single_selection()) &&
2907 this.env.subscriptionrows['rcmrow'+id] &&
2908 (folder = this.env.subscriptionrows['rcmrow'+id][0]))
2909 this.set_env('folder', folder);
2911 this.set_env('folder', null);
2913 if (this.gui_objects.createfolderhint)
2914 this.gui_objects.createfolderhint.innerHTML = this.env.folder ? this.get_label('addsubfolderhint') : '';
2917 this.subscription_move_folder = function(list)
2919 var reg = RegExp('['+RegExp.escape(this.env.delimiter)+']?[^'+RegExp.escape(this.env.delimiter)+']+$');
2920 if (this.env.folder && this.env.dstfolder && (this.env.dstfolder != this.env.folder) &&
2921 (this.env.dstfolder != this.env.folder.replace(reg, '')))
2923 var reg = new RegExp('[^'+RegExp.escape(this.env.delimiter)+']*['+RegExp.escape(this.env.delimiter)+']', 'g');
2924 var basename = this.env.folder.replace(reg, '');
2925 var newname = this.env.dstfolder==this.env.delimiter ? basename : this.env.dstfolder+this.env.delimiter+basename;
2927 this.set_busy(true, 'foldermoving');
2928 this.http_post('rename-folder', '_folder_oldname='+urlencode(this.env.folder)+'&_folder_newname='+urlencode(newname), true);
2930 this.drag_active = false;
2931 this.unfocus_subscription(this.get_folder_row_id(this.env.dstfolder));
2934 // tell server to create and subscribe a new mailbox
2935 this.create_folder = function(name)
2937 if (this.edit_folder)
2938 this.reset_folder_rename();
2941 if ((form = this.gui_objects.editform) && form.elements['_folder_name'])
2943 name = form.elements['_folder_name'].value;
2945 if (name.indexOf(this.env.delimiter)>=0)
2947 alert(this.get_label('forbiddencharacter')+' ('+this.env.delimiter+')');
2951 if (this.env.folder && name != '')
2952 name = this.env.folder+this.env.delimiter+name;
2954 this.set_busy(true, 'foldercreating');
2955 this.http_post('create-folder', '_name='+urlencode(name), true);
2957 else if (form.elements['_folder_name'])
2958 form.elements['_folder_name'].focus();
2961 // start renaming the mailbox name.
2962 // this will replace the name string with an input field
2963 this.rename_folder = function(id)
2965 var temp, row, form;
2967 // reset current renaming
2968 if (temp = this.edit_folder)
2970 this.reset_folder_rename();
2975 if (id && this.env.subscriptionrows[id] && (row = document.getElementById(id)))
2977 var reg = new RegExp('.*['+RegExp.escape(this.env.delimiter)+']');
2978 this.name_input = document.createElement('INPUT');
2979 this.name_input.value = this.env.subscriptionrows[id][0].replace(reg, '');
2980 this.name_input.style.width = '100%';
2982 reg = new RegExp('['+RegExp.escape(this.env.delimiter)+']?[^'+RegExp.escape(this.env.delimiter)+']+$');
2983 this.name_input.__parent = this.env.subscriptionrows[id][0].replace(reg, '');
2984 this.name_input.onkeypress = function(e){ rcmail.name_input_keypress(e); };
2986 row.cells[0].replaceChild(this.name_input, row.cells[0].firstChild);
2987 this.edit_folder = id;
2988 this.name_input.select();
2990 if (form = this.gui_objects.editform)
2991 form.onsubmit = function(){ return false; };
2995 // remove the input field and write the current mailbox name to the table cell
2996 this.reset_folder_rename = function()
2998 var cell = this.name_input ? this.name_input.parentNode : null;
3000 if (cell && this.edit_folder && this.env.subscriptionrows[this.edit_folder])
3001 cell.innerHTML = this.env.subscriptionrows[this.edit_folder][1];
3003 this.edit_folder = null;
3006 // handler for keyboard events on the input field
3007 this.name_input_keypress = function(e)
3009 var key = rcube_event.get_keycode(e);
3014 var newname = this.name_input ? this.name_input.value : null;
3015 if (this.edit_folder && newname)
3017 if (newname.indexOf(this.env.delimiter)>=0)
3019 alert(this.get_label('forbiddencharacter')+' ('+this.env.delimiter+')');
3023 if (this.name_input.__parent)
3024 newname = this.name_input.__parent + this.env.delimiter + newname;
3026 this.set_busy(true, 'folderrenaming');
3027 this.http_post('rename-folder', '_folder_oldname='+urlencode(this.env.subscriptionrows[this.edit_folder][0])+'&_folder_newname='+urlencode(newname), true);
3032 this.reset_folder_rename();
3035 // delete a specific mailbox with all its messages
3036 this.delete_folder = function(id)
3038 var folder = this.env.subscriptionrows[id][0];
3040 if (this.edit_folder)
3041 this.reset_folder_rename();
3043 if (folder && confirm(this.get_label('deletefolderconfirm')))
3045 this.set_busy(true, 'folderdeleting');
3046 this.http_post('delete-folder', '_mboxes='+urlencode(folder), true);
3047 this.set_env('folder', null);
3049 if (this.gui_objects.createfolderhint)
3050 this.gui_objects.createfolderhint.innerHTML = '';
3054 // add a new folder to the subscription list by cloning a folder row
3055 this.add_folder_row = function(name, display_name, replace, before)
3057 if (!this.gui_objects.subscriptionlist)
3060 // find not protected folder
3061 for (var refid in this.env.subscriptionrows)
3062 if (this.env.subscriptionrows[refid]!=null && !this.env.subscriptionrows[refid][2])
3066 var tbody = this.gui_objects.subscriptionlist.tBodies[0];
3067 var id = 'rcmrow'+(tbody.childNodes.length+1);
3068 var selection = this.subscription_list.get_single_selection();
3070 if (replace && replace.id)
3076 if (!id || !(refrow = document.getElementById(refid)))
3078 // Refresh page if we don't have a table row to clone
3079 this.goto_url('folders');
3083 // clone a table row if there are existing rows
3084 var row = this.clone_table_row(refrow);
3087 if (before && (before = this.get_folder_row_id(before)))
3088 tbody.insertBefore(row, document.getElementById(before));
3090 tbody.appendChild(row);
3093 tbody.removeChild(replace);
3096 // add to folder/row-ID map
3097 this.env.subscriptionrows[row.id] = [name, display_name, 0];
3100 row.cells[0].innerHTML = display_name;
3102 // set messages count to zero
3104 row.cells[1].innerHTML = '*';
3106 if (!replace && row.cells[2] && row.cells[2].firstChild.tagName=='INPUT')
3108 row.cells[2].firstChild.value = name;
3109 row.cells[2].firstChild.checked = true;
3112 // add new folder to rename-folder list and clear input field
3113 if (!replace && (form = this.gui_objects.editform))
3115 if (form.elements['_folder_oldname'])
3116 form.elements['_folder_oldname'].options[form.elements['_folder_oldname'].options.length] = new Option(name,name);
3117 if (form.elements['_folder_name'])
3118 form.elements['_folder_name'].value = '';
3121 this.init_subscription_list();
3122 if (selection && document.getElementById('rcmrow'+selection))
3123 this.subscription_list.select_row(selection);
3125 if (document.getElementById(id).scrollIntoView)
3126 document.getElementById(id).scrollIntoView();
3129 // replace an existing table row with a new folder line
3130 this.replace_folder_row = function(oldfolder, newfolder, display_name, before)
3132 var id = this.get_folder_row_id(oldfolder);
3133 var row = document.getElementById(id);
3135 // replace an existing table row (if found)
3136 this.add_folder_row(newfolder, display_name, row, before);
3138 // rename folder in rename-folder dropdown
3140 if ((form = this.gui_objects.editform) && (elm = form.elements['_folder_oldname']))
3142 for (var i=0;i<elm.options.length;i++)
3144 if (elm.options[i].value == oldfolder)
3146 elm.options[i].text = display_name;
3147 elm.options[i].value = newfolder;
3152 form.elements['_folder_newname'].value = '';
3156 // remove the table row of a specific mailbox from the table
3157 // (the row will not be removed, just hidden)
3158 this.remove_folder_row = function(folder)
3161 var id = this.get_folder_row_id(folder);
3162 if (id && (row = document.getElementById(id)))
3163 row.style.display = 'none';
3165 // remove folder from rename-folder list
3167 if ((form = this.gui_objects.editform) && form.elements['_folder_oldname'])
3169 for (var i=0;i<form.elements['_folder_oldname'].options.length;i++)
3171 if (form.elements['_folder_oldname'].options[i].value == folder)
3173 form.elements['_folder_oldname'].options[i] = null;
3179 if (form && form.elements['_folder_newname'])
3180 form.elements['_folder_newname'].value = '';
3183 this.subscribe_folder = function(folder)
3186 this.http_post('subscribe', '_mbox='+urlencode(folder));
3189 this.unsubscribe_folder = function(folder)
3192 this.http_post('unsubscribe', '_mbox='+urlencode(folder));
3195 // helper method to find a specific mailbox row ID
3196 this.get_folder_row_id = function(folder)
3198 for (var id in this.env.subscriptionrows)
3199 if (this.env.subscriptionrows[id] && this.env.subscriptionrows[id][0] == folder)
3205 // duplicate a specific table row
3206 this.clone_table_row = function(row)
3209 var new_row = document.createElement('TR');
3210 for(var n=0; n<row.cells.length; n++)
3212 cell = row.cells[n];
3213 td = document.createElement('TD');
3216 td.className = cell.className;
3218 td.setAttribute('align', cell.align);
3220 td.innerHTML = cell.innerHTML;
3221 new_row.appendChild(td);
3228 /*********************************************************/
3229 /********* GUI functionality *********/
3230 /*********************************************************/
3232 // eable/disable buttons for page shifting
3233 this.set_page_buttons = function()
3235 this.enable_command('nextpage', (this.env.pagecount > this.env.current_page));
3236 this.enable_command('lastpage', (this.env.pagecount > this.env.current_page));
3237 this.enable_command('previouspage', (this.env.current_page > 1));
3238 this.enable_command('firstpage', (this.env.current_page > 1));
3241 // set button to a specific state
3242 this.set_button = function(command, state)
3244 var a_buttons = this.buttons[command];
3247 if(!a_buttons || !a_buttons.length)
3250 for(var n=0; n<a_buttons.length; n++)
3252 button = a_buttons[n];
3253 obj = document.getElementById(button.id);
3255 // get default/passive setting of the button
3256 if (obj && button.type=='image' && !button.status) {
3257 button.pas = obj._original_src ? obj._original_src : obj.src;
3258 // respect PNG fix on IE browsers
3259 if (obj.runtimeStyle && obj.runtimeStyle.filter && obj.runtimeStyle.filter.match(/src=['"]([^'"]+)['"]/))
3260 button.pas = RegExp.$1;
3262 else if (obj && !button.status)
3263 button.pas = String(obj.className);
3265 // set image according to button state
3266 if (obj && button.type=='image' && button[state])
3268 button.status = state;
3269 obj.src = button[state];
3271 // set class name according to button state
3272 else if (obj && typeof(button[state])!='undefined')
3274 button.status = state;
3275 obj.className = button[state];
3277 // disable/enable input buttons
3278 if (obj && button.type=='input')
3280 button.status = state;
3281 obj.disabled = !state;
3286 // display a specific alttext
3287 this.set_alttext = function(command, label)
3289 if (!this.buttons[command] || !this.buttons[command].length)
3292 var button, obj, link;
3293 for (var n=0; n<this.buttons[command].length; n++)
3295 button = this.buttons[command][n];
3296 obj = document.getElementById(button.id);
3298 if (button.type=='image' && obj)
3300 obj.setAttribute('alt', this.get_label(label));
3301 if ((link = obj.parentNode) && link.tagName == 'A')
3302 link.setAttribute('title', this.get_label(label));
3305 obj.setAttribute('title', this.get_label(label));
3309 // mouse over button
3310 this.button_over = function(command, id)
3312 var a_buttons = this.buttons[command];
3315 if(!a_buttons || !a_buttons.length)
3318 for(var n=0; n<a_buttons.length; n++)
3320 button = a_buttons[n];
3321 if(button.id==id && button.status=='act')
3323 img = document.getElementById(button.id);
3324 if (img && button.over)
3325 img.src = button.over;
3331 // mouse down on button
3332 this.button_sel = function(command, id)
3334 var a_buttons = this.buttons[command];
3337 if(!a_buttons || !a_buttons.length)
3340 for(var n=0; n<a_buttons.length; n++)
3342 button = a_buttons[n];
3343 if(button.id==id && button.status=='act')
3345 img = document.getElementById(button.id);
3346 if (img && button.sel)
3347 img.src = button.sel;
3352 // mouse out of button
3353 this.button_out = function(command, id)
3355 var a_buttons = this.buttons[command];
3358 if(!a_buttons || !a_buttons.length)
3361 for(var n=0; n<a_buttons.length; n++)
3363 button = a_buttons[n];
3364 if(button.id==id && button.status=='act')
3366 img = document.getElementById(button.id);
3367 if (img && button.act)
3368 img.src = button.act;
3373 // set/unset a specific class name
3374 this.set_classname = function(obj, classname, set)
3376 var reg = new RegExp('\s*'+classname, 'i');
3377 if (!set && obj.className.match(reg))
3378 obj.className = obj.className.replace(reg, '');
3379 else if (set && !obj.className.match(reg))
3380 obj.className += ' '+classname;
3383 // write to the document/window title
3384 this.set_pagetitle = function(title)
3386 if (title && document.title)
3387 document.title = title;
3390 // display a system message
3391 this.display_message = function(msg, type, hold)
3393 if (!this.loaded) // save message in order to display after page loaded
3395 this.pending_message = new Array(msg, type);
3399 // pass command to parent window
3400 if (this.env.framed && parent.rcmail)
3401 return parent.rcmail.display_message(msg, type, hold);
3403 if (!this.gui_objects.message)
3406 if (this.message_timer)
3407 clearTimeout(this.message_timer);
3411 cont = '<div class="'+type+'">'+cont+'</div>';
3414 this.gui_objects.message.innerHTML = cont;
3415 this.gui_objects.message.style.display = 'block';
3417 if (type!='loading')
3418 this.gui_objects.message.onmousedown = function(){ _rcube.hide_message(); return true; };
3421 this.message_timer = window.setTimeout(function(){ ref.hide_message(); }, this.message_time);
3424 // make a message row disapear
3425 this.hide_message = function()
3427 if (this.gui_objects.message)
3429 this.gui_objects.message.style.display = 'none';
3430 this.gui_objects.message.onmousedown = null;
3434 // mark a mailbox as selected and set environment variable
3435 this.select_folder = function(name, old)
3437 if (this.gui_objects.folderlist)
3439 var current_li, target_li;
3441 if ((current_li = this.get_folder_li(old)))
3443 this.set_classname(current_li, 'selected', false);
3444 this.set_classname(current_li, 'unfocused', false);
3447 if ((target_li = this.get_folder_li(name)))
3449 this.set_classname(target_li, 'unfocused', false);
3450 this.set_classname(target_li, 'selected', true);
3455 // helper method to find a folder list item
3456 this.get_folder_li = function(name)
3458 if (this.gui_objects.folderlist)
3460 name = String(name).replace(this.identifier_expr, '');
3461 return document.getElementById('rcmli'+name);
3467 // for reordering column array, Konqueror workaround
3468 this.set_message_coltypes = function(coltypes)
3470 this.coltypes = coltypes;
3472 // set correct list titles
3474 var thead = this.gui_objects.messagelist ? this.gui_objects.messagelist.tHead : null;
3475 for (var n=0; thead && n<this.coltypes.length; n++)
3477 col = this.coltypes[n];
3478 if ((cell = thead.rows[0].cells[n+1]) && (col=='from' || col=='to'))
3480 // if we have links for sorting, it's a bit more complicated...
3481 if (cell.firstChild && cell.firstChild.tagName=='A')
3483 cell.firstChild.innerHTML = this.get_label(this.coltypes[n]);
3484 cell.firstChild.onclick = function(){ return rcmail.command('sort', this.__col, this); };
3485 cell.firstChild.__col = col;
3488 cell.innerHTML = this.get_label(this.coltypes[n]);
3490 cell.id = 'rcm'+col;
3492 else if (col == 'subject' && this.message_list)
3493 this.message_list.subject_col = n+1;
3497 // create a table row in the message list
3498 this.add_message_row = function(uid, cols, flags, attachment, attop)
3500 if (!this.gui_objects.messagelist || !this.message_list)
3503 var tbody = this.gui_objects.messagelist.tBodies[0];
3504 var rowcount = tbody.rows.length;
3505 var even = rowcount%2;
3507 this.env.messages[uid] = {deleted:flags.deleted?1:0,
3508 replied:flags.replied?1:0,
3509 unread:flags.unread?1:0,
3510 forwarded:flags.forwarded?1:0,
3511 flagged:flags.flagged?1:0};
3513 var row = document.createElement('TR');
3514 row.id = 'rcmrow'+uid;
3515 row.className = 'message'
3516 + (even ? ' even' : ' odd')
3517 + (flags.unread ? ' unread' : '')
3518 + (flags.deleted ? ' deleted' : '')
3519 + (flags.flagged ? ' flagged' : '');
3521 if (this.message_list.in_selection(uid))
3522 row.className += ' selected';
3524 var icon = this.env.messageicon;
3525 if (flags.deleted && this.env.deletedicon)
3526 icon = this.env.deletedicon;
3527 else if (flags.replied && this.env.repliedicon)
3529 if (flags.forwarded && this.env.forwardedrepliedicon)
3530 icon = this.env.forwardedrepliedicon;
3532 icon = this.env.repliedicon;
3534 else if (flags.forwarded && this.env.forwardedicon)
3535 icon = this.env.forwardedicon;
3536 else if(flags.unread && this.env.unreadicon)
3537 icon = this.env.unreadicon;
3539 var col = document.createElement('TD');
3540 col.className = 'icon';
3541 col.innerHTML = icon ? '<img src="'+icon+'" alt="" />' : '';
3542 row.appendChild(col);
3544 // add each submitted col
3545 for (var n = 0; n < this.coltypes.length; n++)
3547 var c = this.coltypes[n];
3548 col = document.createElement('TD');
3549 col.className = String(c).toLowerCase();
3553 if (flags.flagged && this.env.flaggedicon)
3554 col.innerHTML = '<img src="'+this.env.flaggedicon+'" alt="" />';
3555 else if(!flags.flagged && this.env.unflaggedicon)
3556 col.innerHTML = '<img src="'+this.env.unflaggedicon+'" alt="" />';
3558 else if (c=='attachment')
3559 col.innerHTML = attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" />' : ' ';
3561 col.innerHTML = cols[c];
3563 row.appendChild(col);
3566 this.message_list.insert_row(row, attop);
3569 if (attop && this.env.pagesize && this.message_list.rowcount > this.env.pagesize)
3571 var uid = this.message_list.get_last_row();
3572 this.message_list.remove_row(uid);
3573 this.message_list.clear_selection(uid);
3577 // replace content of row count display
3578 this.set_rowcount = function(text)
3580 if (this.gui_objects.countdisplay)
3581 this.gui_objects.countdisplay.innerHTML = text;
3583 // update page navigation buttons
3584 this.set_page_buttons();
3587 // replace content of mailboxname display
3588 this.set_mailboxname = function(content)
3590 if (this.gui_objects.mailboxname && content)
3591 this.gui_objects.mailboxname.innerHTML = content;
3594 // replace content of quota display
3595 this.set_quota = function(content)
3597 if (this.gui_objects.quotadisplay && content)
3598 this.gui_objects.quotadisplay.innerHTML = content;
3601 // update the mailboxlist
3602 this.set_unread_count = function(mbox, count, set_title)
3604 if (!this.gui_objects.mailboxlist)
3607 this.env.unread_counts[mbox] = count;
3608 this.set_unread_count_display(mbox, set_title);
3611 // update the mailbox count display
3612 this.set_unread_count_display = function(mbox, set_title)
3614 var reg, text_obj, item, mycount, childcount, div;
3615 if (item = this.get_folder_li(mbox))
3617 mycount = this.env.unread_counts[mbox] ? this.env.unread_counts[mbox] : 0;
3618 text_obj = item.getElementsByTagName('a')[0];
3619 reg = /\s+\([0-9]+\)$/i;
3622 if ((div = item.getElementsByTagName('div')[0]) &&
3623 div.className.match(/collapsed/))
3625 // add children's counters
3626 for (var k in this.env.unread_counts)
3627 if (k.indexOf(mbox + this.env.delimiter) == 0) {
3628 childcount += this.env.unread_counts[k];
3632 if (mycount && text_obj.innerHTML.match(reg))
3633 text_obj.innerHTML = text_obj.innerHTML.replace(reg, ' ('+mycount+')');
3635 text_obj.innerHTML += ' ('+mycount+')';
3637 text_obj.innerHTML = text_obj.innerHTML.replace(reg, '');
3639 // set parent's display
3640 reg = new RegExp(RegExp.escape(this.env.delimiter) + '[^' + RegExp.escape(this.env.delimiter) + ']+$');
3641 if (mbox.match(reg))
3642 this.set_unread_count_display(mbox.replace(reg, ''), false);
3644 // set the right classes
3645 this.set_classname(item, 'unread', (mycount+childcount)>0 ? true : false);
3648 // set unread count to window title
3649 reg = /^\([0-9]+\)\s+/i;
3650 if (set_title && document.title)
3652 var doc_title = String(document.title);
3655 if (mycount && doc_title.match(reg))
3656 new_title = doc_title.replace(reg, '('+mycount+') ');
3658 new_title = '('+mycount+') '+doc_title;
3660 new_title = doc_title.replace(reg, '');
3662 this.set_pagetitle(new_title);
3666 // notifies that a new message(s) has hit the mailbox
3667 this.new_message_focus = function()
3669 // focus main window
3670 if (this.env.framed && window.parent)
3671 window.parent.focus();
3676 // add row to contacts list
3677 this.add_contact_row = function(cid, cols, select)
3679 if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0])
3682 var tbody = this.gui_objects.contactslist.tBodies[0];
3683 var rowcount = tbody.rows.length;
3684 var even = rowcount%2;
3686 var row = document.createElement('TR');
3687 row.id = 'rcmrow'+cid;
3688 row.className = 'contact '+(even ? 'even' : 'odd');
3690 if (this.contact_list.in_selection(cid))
3691 row.className += ' selected';
3693 // add each submitted col
3696 col = document.createElement('TD');
3697 col.className = String(c).toLowerCase();
3698 col.innerHTML = cols[c];
3699 row.appendChild(col);
3702 this.contact_list.insert_row(row);
3703 this.enable_command('export', (this.contact_list.rowcount > 0));
3706 this.toggle_prefer_html = function(checkbox)
3708 var addrbook_show_images;
3709 if (addrbook_show_images = document.getElementById('rcmfd_addrbook_show_images'))
3710 addrbook_show_images.disabled = !checkbox.checked;
3713 // display fetched raw headers
3714 this.set_headers = function(content)
3716 if (this.gui_objects.all_headers_row && this.gui_objects.all_headers_box && content)
3718 var box = this.gui_objects.all_headers_box;
3719 box.innerHTML = content;
3720 box.style.display = 'block';
3722 if (this.env.framed && parent.rcmail)
3723 parent.rcmail.set_busy(false);
3725 this.set_busy(false);
3729 // display all-headers row and fetch raw message headers
3730 this.load_headers = function(elem)
3732 if (!this.gui_objects.all_headers_row || !this.gui_objects.all_headers_box || !this.env.uid)
3735 this.set_classname(elem, 'show-headers', false);
3736 this.set_classname(elem, 'hide-headers', true);
3737 this.gui_objects.all_headers_row.style.display = bw.ie ? 'block' : 'table-row';
3738 elem.onclick = function() { rcmail.hide_headers(elem); }
3740 // fetch headers only once
3741 if (!this.gui_objects.all_headers_box.innerHTML)
3743 this.display_message(this.get_label('loading'), 'loading', true);
3744 this.http_post('headers', '_uid='+this.env.uid);
3748 // hide all-headers row
3749 this.hide_headers = function(elem)
3751 if (!this.gui_objects.all_headers_row || !this.gui_objects.all_headers_box)
3754 this.set_classname(elem, 'hide-headers', false);
3755 this.set_classname(elem, 'show-headers', true);
3756 this.gui_objects.all_headers_row.style.display = 'none';
3757 elem.onclick = function() { rcmail.load_headers(elem); }
3761 /********************************************************/
3762 /********* html to text conversion functions *********/
3763 /********************************************************/
3765 this.html2plain = function(htmlText, id)
3767 var http_request = new rcube_http_request();
3768 var url = this.env.bin_path+'html2text.php';
3771 this.set_busy(true, 'converting');
3772 //console.log('HTTP POST: '+url);
3774 http_request.onerror = function(o) { rcmail.http_error(o); };
3775 http_request.oncomplete = function(o) { rcmail.set_text_value(o, id); };
3776 http_request.POST(url, htmlText, 'application/octet-stream');
3779 this.set_text_value = function(httpRequest, id)
3781 this.set_busy(false);
3782 document.getElementById(id).value = httpRequest.get_text();
3783 //console.log(httpRequest.get_text());
3787 /********************************************************/
3788 /********* remote request methods *********/
3789 /********************************************************/
3791 this.redirect = function(url, lock)
3793 if (lock || lock === null)
3794 this.set_busy(true);
3796 if (this.env.framed && window.parent)
3797 parent.location.href = url;
3799 location.href = url;
3802 this.goto_url = function(action, query, lock)
3804 var querystring = query ? '&'+query : '';
3805 this.redirect(this.env.comm_path+'&_action='+action+querystring, lock);
3808 this.http_sockets = new Array();
3810 // find a non-busy socket or create a new one
3811 this.get_request_obj = function()
3813 for (var n=0; n<this.http_sockets.length; n++)
3815 if (!this.http_sockets[n].busy)
3816 return this.http_sockets[n];
3819 // create a new XMLHTTP object
3820 var i = this.http_sockets.length;
3821 this.http_sockets[i] = new rcube_http_request();
3823 return this.http_sockets[i];
3826 // send a http request to the server
3827 this.http_request = function(action, querystring, lock)
3829 var request_obj = this.get_request_obj();
3830 querystring += (querystring ? '&' : '') + '_remote=1';
3832 // add timestamp to request url to avoid cacheing problems in Safari
3834 querystring += '&_ts='+(new Date().getTime());
3839 //console.log('HTTP request: '+this.env.comm_path+'&_action='+action+'&'+querystring);
3842 this.set_busy(true);
3845 request_obj.__lock = lock ? true : false;
3846 request_obj.__action = action;
3847 request_obj.onerror = function(o){ ref.http_error(o); };
3848 request_obj.oncomplete = function(o){ ref.http_response(o); };
3849 request_obj.GET(this.env.comm_path+'&_action='+action+'&'+querystring);
3853 // send a http POST request to the server
3854 this.http_post = function(action, postdata, lock)
3857 if (postdata && typeof(postdata) == 'object')
3858 postdata._remote = 1;
3860 postdata += (postdata ? '&' : '') + '_remote=1';
3863 if (request_obj = this.get_request_obj())
3865 //console.log('HTTP POST: '+this.env.comm_path+'&_action='+action);
3868 this.set_busy(true);
3871 request_obj.__lock = lock ? true : false;
3872 request_obj.__action = action;
3873 request_obj.onerror = function(o){ rcm.http_error(o); };
3874 request_obj.oncomplete = function(o){ rcm.http_response(o); };
3875 request_obj.POST(this.env.comm_path+'&_action='+action, postdata);
3879 // handle HTTP response
3880 this.http_response = function(request_obj)
3882 var ctype = request_obj.get_header('Content-Type');
3885 ctype = String(ctype).toLowerCase();
3886 var ctype_array=ctype.split(";");
3887 ctype = ctype_array[0];
3890 if (request_obj.__lock)
3891 this.set_busy(false);
3893 //console.log(request_obj.get_text());
3895 // if we get javascript code from server -> execute it
3896 if (request_obj.get_text() && (ctype=='text/javascript' || ctype=='application/x-javascript'))
3897 eval(request_obj.get_text());
3899 // process the response data according to the sent action
3900 switch (request_obj.__action) {
3902 if (this.task == 'addressbook') {
3903 var uid = this.contact_list.get_selection();
3904 this.enable_command('compose', (uid && this.contact_list.rows[uid]));
3905 this.enable_command('delete', 'edit', (uid && this.contact_list.rows[uid] && this.env.address_sources && !this.env.address_sources[this.env.source].readonly));
3906 this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0));
3910 if (this.env.action == 'show')
3911 this.command('list');
3912 else if (this.message_list)
3913 this.message_list.init();
3918 if (!this.env.messagecount && this.task == 'mail') {
3919 // clear preview pane content
3920 if (this.env.contentframe)
3921 this.show_contentframe(false);
3922 // disable commands useless when mailbox is empty
3923 this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 'mark', 'viewsource',
3924 'print', 'load-attachment', 'purge', 'expunge', 'select-all', 'select-none', 'sort', false);
3928 case 'check-recent':
3931 if (this.task == 'mail') {
3932 if (this.message_list && request_obj.__action == 'list')
3933 this.msglist_select(this.message_list);
3934 this.enable_command('show', 'expunge', 'select-all', 'select-none', 'sort', (this.env.messagecount > 0));
3935 this.enable_command('purge', this.purge_mailbox_test());
3937 else if (this.task == 'addressbook')
3938 this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0));
3943 request_obj.reset();
3946 // handle HTTP request errors
3947 this.http_error = function(request_obj)
3949 //alert('Error sending request: '+request_obj.url+' => HTTP '+request_obj.xmlhttp.status);
3950 if (request_obj.__lock)
3951 this.set_busy(false);
3953 request_obj.reset();
3954 request_obj.__lock = false;
3955 this.display_message('Unknown Server Error!', 'error');
3958 // use an image to send a keep-alive siganl to the server
3959 this.send_keep_alive = function()
3962 this.http_request('keep-alive', '_t='+d.getTime());
3965 // send periodic request to check for recent messages
3966 this.check_for_recent = function(setbusy)
3972 this.set_busy(true, 'checkingmail');
3974 this.http_request('check-recent', (this.env.search_request ? '_search='+this.env.search_request+'&' : '') + '_t='+(new Date().getTime()), true);
3978 /********************************************************/
3979 /********* helper methods *********/
3980 /********************************************************/
3982 // check if we're in show mode or if we have a unique selection
3983 // and return the message uid
3984 this.get_single_uid = function()
3986 return this.env.uid ? this.env.uid : (this.message_list ? this.message_list.get_single_selection() : null);
3989 // same as above but for contacts
3990 this.get_single_cid = function()
3992 return this.env.cid ? this.env.cid : (this.contact_list ? this.contact_list.get_single_selection() : null);
3996 this.get_caret_pos = function(obj)
3998 if (typeof(obj.selectionEnd)!='undefined')
3999 return obj.selectionEnd;
4001 else if (document.selection && document.selection.createRange)
4003 var range = document.selection.createRange();
4004 if (range.parentElement()!=obj)
4007 var gm = range.duplicate();
4008 if (obj.tagName=='TEXTAREA')
4009 gm.moveToElementText(obj);
4011 gm.expand('textedit');
4013 gm.setEndPoint('EndToStart', range);
4014 var p = gm.text.length;
4016 return p<=obj.value.length ? p : -1;
4020 return obj.value.length;
4023 this.set_caret2start = function(obj)
4025 if (obj.createTextRange)
4027 var range = obj.createTextRange();
4028 range.collapse(true);
4031 else if (obj.setSelectionRange)
4032 obj.setSelectionRange(0,0);
4037 // set all fields of a form disabled
4038 this.lock_form = function(form, lock)
4040 if (!form || !form.elements)
4044 for (var n=0; n<form.elements.length; n++)
4046 type = form.elements[n];
4050 form.elements[n].disabled = lock;
4054 } // end object rcube_webmail
4058 * Class for sending HTTP requests
4061 function rcube_http_request()
4065 this.xmlhttp = null;
4067 // reset object properties
4068 this.reset = function()
4070 // set unassigned event handlers
4071 this.onloading = function(){ };
4072 this.onloaded = function(){ };
4073 this.oninteractive = function(){ };
4074 this.oncomplete = function(){ };
4075 this.onabort = function(){ };
4076 this.onerror = function(){ };
4080 this.xmlhttp = null;
4083 // create HTMLHTTP object
4084 this.build = function()
4086 if (window.XMLHttpRequest)
4087 this.xmlhttp = new XMLHttpRequest();
4088 else if (window.ActiveXObject)
4090 try { this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }
4091 catch(e) { this.xmlhttp = null; }
4100 this.GET = function(url)
4114 this.xmlhttp.onreadystatechange = function(){ _ref.xmlhttp_onreadystatechange(); };
4115 this.xmlhttp.open('GET', url, true);
4116 this.xmlhttp.setRequestHeader('X-RoundCube-Referer', bw.get_cookie('roundcube_sessid'));
4117 this.xmlhttp.send(null);
4120 this.POST = function(url, body, contentType)
4122 // default value for contentType if not provided
4123 if (typeof(contentType) == 'undefined')
4124 contentType = 'application/x-www-form-urlencoded';
4134 var req_body = body;
4135 if (typeof(body) == 'object')
4139 req_body += (req_body ? '&' : '') + p+'='+urlencode(body[p]);
4146 this.xmlhttp.onreadystatechange = function() { ref.xmlhttp_onreadystatechange(); };
4147 this.xmlhttp.open('POST', url, true);
4148 this.xmlhttp.setRequestHeader('Content-Type', contentType);
4149 this.xmlhttp.setRequestHeader('X-RoundCube-Referer', bw.get_cookie('roundcube_sessid'));
4150 this.xmlhttp.send(req_body);
4153 // handle onreadystatechange event
4154 this.xmlhttp_onreadystatechange = function()
4156 if(this.xmlhttp.readyState == 1)
4157 this.onloading(this);
4159 else if(this.xmlhttp.readyState == 2)
4160 this.onloaded(this);
4162 else if(this.xmlhttp.readyState == 3)
4163 this.oninteractive(this);
4165 else if(this.xmlhttp.readyState == 4)
4168 if (this.xmlhttp.status == 0)
4170 else if(this.xmlhttp.status == 200)
4171 this.oncomplete(this);
4185 // getter method for HTTP headers
4186 this.get_header = function(name)
4188 return this.xmlhttp.getResponseHeader(name);
4191 this.get_text = function()
4193 return this.xmlhttp.responseText;
4196 this.get_xml = function()
4198 return this.xmlhttp.responseXML;
4203 } // end class rcube_http_request
4205 // helper function to call the init method with a delay
4206 function call_init(o)
4208 window.setTimeout('if (window[\''+o+'\'] && window[\''+o+'\'].init) { '+o+'.init(); }',
4209 bw.win ? 500 : 200);