/* +-----------------------------------------------------------------------+ | RoundCube Webmail Client Script | | | | This file is part of the RoundCube Webmail client | | Copyright (C) 2005-2007, RoundCube Dev, - Switzerland | | Licensed under the GNU GPL | | | +-----------------------------------------------------------------------+ | Authors: Thomas Bruederli | | Charles McNulty | +-----------------------------------------------------------------------+ | Requires: common.js, list.js | +-----------------------------------------------------------------------+ $Id$ */ var rcube_webmail_client; function rcube_webmail() { this.env = new Object(); this.labels = new Object(); this.buttons = new Object(); this.gui_objects = new Object(); this.commands = new Object(); this.onloads = new Array(); // create protected reference to myself rcube_webmail_client = this; this.ref = 'rcube_webmail_client'; var ref = this; // webmail client settings this.dblclick_time = 500; this.message_time = 5000; this.identifier_expr = new RegExp('[^0-9a-z\-_]', 'gi'); // mimetypes supported by the browser (default settings) this.mimetypes = new Array('text/plain', 'text/html', 'text/xml', 'image/jpeg', 'image/gif', 'image/png', 'application/x-javascript', 'application/pdf', 'application/x-shockwave-flash'); // default environment vars this.env.keep_alive = 60; // seconds this.env.request_timeout = 180; // seconds this.env.draft_autosave = 0; // seconds this.env.comm_path = './'; this.env.bin_path = './bin/'; this.env.blankpage = 'program/blank.gif'; // set environment variable(s) this.set_env = function(p, value) { if (p != null && typeof(p) == 'object' && !value) for (var n in p) this.env[n] = p[n]; else this.env[p] = value; }; // add a localized label to the client environment this.add_label = function(key, value) { this.labels[key] = value; }; // add a button to the button list this.register_button = function(command, id, type, act, sel, over) { if (!this.buttons[command]) this.buttons[command] = new Array(); var button_prop = {id:id, type:type}; if (act) button_prop.act = act; if (sel) button_prop.sel = sel; if (over) button_prop.over = over; this.buttons[command][this.buttons[command].length] = button_prop; }; // register a specific gui object this.gui_object = function(name, id) { this.gui_objects[name] = id; }; // execute the given script on load this.add_onload = function(f) { this.onloads[this.onloads.length] = f; }; // initialize webmail client this.init = function() { var p = this; this.task = this.env.task; // check browser if (!bw.dom || !bw.xmlhttp_test()) { this.goto_url('error', '_code=0x199'); return; } // find all registered gui objects for (var n in this.gui_objects) this.gui_objects[n] = rcube_find_object(this.gui_objects[n]); // tell parent window that this frame is loaded if (this.env.framed && parent.rcmail && parent.rcmail.set_busy) parent.rcmail.set_busy(false); // enable general commands this.enable_command('logout', 'mail', 'addressbook', 'settings', true); switch (this.task) { case 'mail': if (this.gui_objects.messagelist) { this.message_list = new rcube_list_widget(this.gui_objects.messagelist, {multiselect:true, draggable:true, keyboard:true, dblclick_time:this.dblclick_time}); this.message_list.row_init = function(o){ p.init_message_row(o); }; this.message_list.addEventListener('dblclick', function(o){ p.msglist_dbl_click(o); }); this.message_list.addEventListener('keypress', function(o){ p.msglist_keypress(o); }); this.message_list.addEventListener('select', function(o){ p.msglist_select(o); }); this.message_list.addEventListener('dragstart', function(o){ p.drag_active = true; }); this.message_list.addEventListener('dragend', function(o){ p.drag_active = false; }); this.message_list.init(); this.enable_command('toggle_status', true); if (this.gui_objects.mailcontframe) { this.gui_objects.mailcontframe.onmousedown = function(e){ return p.click_on_list(e); }; document.onmouseup = function(e){ return p.doc_mouse_up(e); }; } else this.message_list.focus(); } // enable mail commands this.enable_command('list', 'checkmail', 'compose', 'add-contact', 'search', 'reset-search', true); if (this.env.action=='show' || this.env.action=='preview') { this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 'viewsource', 'print', 'load-attachment', true); if (this.env.next_uid) { this.enable_command('nextmessage', true); this.enable_command('lastmessage', true); } if (this.env.prev_uid) { this.enable_command('previousmessage', true); this.enable_command('firstmessage', true); } } // make preview/message frame visible if (this.env.action == 'preview' && this.env.framed && parent.rcmail) { this.enable_command('compose', 'add-contact', false); parent.rcmail.show_contentframe(true); parent.rcmail.mark_message('read', this.env.uid); } if ((this.env.action=='show' || this.env.action=='preview') && this.env.blockedobjects) { if (this.gui_objects.remoteobjectsmsg) this.gui_objects.remoteobjectsmsg.style.display = 'block'; this.enable_command('load-images', true); } if (this.env.action=='compose') { this.enable_command('add-attachment', 'send-attachment', 'remove-attachment', 'send', true); if (this.env.spellcheck) { this.env.spellcheck.spelling_state_observer = function(s){ ref.set_spellcheck_state(s); }; this.set_spellcheck_state('ready'); } if (this.env.drafts_mailbox) this.enable_command('savedraft', true); } if (this.env.messagecount) this.enable_command('select-all', 'select-none', 'sort', 'expunge', true); if (this.env.messagecount && (this.env.mailbox==this.env.trash_mailbox || this.env.mailbox==this.env.junk_mailbox)) this.enable_command('purge', true); this.set_page_buttons(); // focus this window window.focus(); // init message compose form if (this.env.action=='compose') this.init_messageform(); // show printing dialog if (this.env.action=='print') window.print(); // get unread count for each mailbox if (this.gui_objects.mailboxlist) { this.gui_objects.folderlist = this.gui_objects.mailboxlist; this.http_request('getunread', ''); } break; case 'addressbook': if (this.gui_objects.contactslist) { this.contact_list = new rcube_list_widget(this.gui_objects.contactslist, {multiselect:true, draggable:true, keyboard:true}); this.contact_list.addEventListener('keypress', function(o){ p.contactlist_keypress(o); }); this.contact_list.addEventListener('select', function(o){ p.contactlist_select(o); }); this.contact_list.addEventListener('dragstart', function(o){ p.drag_active = true; }); this.contact_list.addEventListener('dragend', function(o){ p.drag_active = false; }); this.contact_list.init(); if (this.env.cid) this.contact_list.highlight_row(this.env.cid); if (this.gui_objects.contactslist.parentNode) { this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return p.click_on_list(e); }; document.onmouseup = function(e){ return p.doc_mouse_up(e); }; } else this.contact_list.focus(); } this.set_page_buttons(); if (this.env.address_sources && !this.env.address_sources[this.env.source].readonly) this.enable_command('add', true); if (this.env.cid) this.enable_command('show', 'edit', true); if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform) this.enable_command('save', true); else this.enable_command('search', 'reset-search', 'moveto', true); this.enable_command('list', true); break; case 'settings': this.enable_command('preferences', 'identities', 'save', 'folders', true); if (this.env.action=='identities' || this.env.action=='edit-identity' || this.env.action=='add-identity') this.enable_command('edit', 'add', 'delete', true); if (this.env.action=='edit-identity' || this.env.action=='add-identity') this.enable_command('save', true); if (this.env.action=='folders') this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', true); if (this.gui_objects.identitieslist) { this.identity_list = new rcube_list_widget(this.gui_objects.identitieslist, {multiselect:false, draggable:false, keyboard:false}); this.identity_list.addEventListener('select', function(o){ p.identity_select(o); }); this.identity_list.init(); this.identity_list.focus(); if (this.env.iid) this.identity_list.highlight_row(this.env.iid); } break; case 'login': var input_user = rcube_find_object('_user'); var input_pass = rcube_find_object('_pass'); if (input_user) input_user.onkeypress = function(e){ return rcmail.login_user_keypress(e); }; if (input_user && input_user.value=='') input_user.focus(); else if (input_pass) input_pass.focus(); this.enable_command('login', true); break; default: break; } // enable basic commands this.enable_command('logout', true); // flag object as complete this.loaded = true; // show message if (this.pending_message) this.display_message(this.pending_message[0], this.pending_message[1]); // start keep-alive interval this.start_keepalive(); // execute all foreign onload scripts for (var i=0; i=0) { if (props.mimetype == 'text/html') qstring += '&_safe=1'; this.attachment_win = window.open(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1', 'rcubemailattachment'); if (this.attachment_win) { setTimeout(function(){ ref.attachment_win.focus(); }, 10); break; } } this.goto_url('get', qstring+'&_download=1'); break; case 'select-all': this.message_list.select_all(props); break; case 'select-none': this.message_list.clear_selection(); break; case 'nextmessage': if (this.env.next_uid) this.show_message(this.env.next_uid, false, this.env.action=='preview'); break; case 'lastmessage': if (this.env.last_uid) this.show_message(this.env.last_uid); break; case 'previousmessage': if (this.env.prev_uid) this.show_message(this.env.prev_uid, false, this.env.action=='preview'); break; case 'firstmessage': if (this.env.first_uid) this.show_message(this.env.first_uid); break; case 'checkmail': this.check_for_recent(); break; case 'compose': var url = this.env.comm_path+'&_action=compose'; if (this.task=='mail' && this.env.mailbox==this.env.drafts_mailbox) { var uid; if (uid = this.get_single_uid()) url += '&_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox); } // modify url if we're in addressbook else if (this.task=='addressbook') { // switch to mail compose step directly if (props && props.indexOf('@') > 0) { url = this.get_task_url('mail', url); this.redirect(url + '&_to='+urlencode(props)); break; } // use contact_id passed as command parameter var a_cids = new Array(); if (props) a_cids[a_cids.length] = props; // get selected contacts else if (this.contact_list) { var selection = this.contact_list.get_selection(); for (var n=0; n0 ? true : false); } else { this.enable_command('show', 'reply', 'reply-all', 'forward', 'print', selected); this.enable_command('delete', 'moveto', list.selection.length>0 ? true : false); } // start timer for message preview (wait for double click) if (selected && this.env.contentframe) this.preview_timer = setTimeout(function(){ ref.msglist_get_preview(); }, this.dblclick_time + 10); else if (this.env.contentframe) this.show_contentframe(false); }; this.msglist_dbl_click = function(list) { if (this.preview_timer) clearTimeout(this.preview_timer); var uid = list.get_single_selection(); if (uid && this.env.mailbox == this.env.drafts_mailbox) this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true); else if (uid) this.show_message(uid, false, false); }; this.msglist_keypress = function(list) { if (list.key_pressed == list.ENTER_KEY) this.command('show'); else if (list.key_pressed == list.DELETE_KEY) this.command('delete'); }; this.msglist_get_preview = function() { var uid = this.get_single_uid(); if (uid && this.env.contentframe && !this.drag_active) this.show_message(uid, false, true); else if (this.env.contentframe) this.show_contentframe(false); }; this.check_droptarget = function(id) { if (this.task == 'mail') return (id != this.env.mailbox); else if (this.task == 'addressbook') return (id != this.env.source && this.env.address_sources[id] && !this.env.address_sources[id].readonly); }; /*********************************************************/ /********* (message) list functionality *********/ /*********************************************************/ // when user doble-clicks on a row this.show_message = function(id, safe, preview) { var add_url = ''; var action = preview ? 'preview': 'show'; var target = window; if (preview && this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { target = window.frames[this.env.contentframe]; add_url = '&_framed=1'; } if (safe) add_url = '&_safe=1'; if (id) { var url = '&_action='+action+'&_uid='+id+'&_mbox='+urlencode(this.env.mailbox)+add_url; if (action == 'preview' && String(target.location.href).indexOf(url) >= 0) this.show_contentframe(true); else { this.set_busy(true, 'loading'); target.location.href = this.env.comm_path+url; } } }; this.show_contentframe = function(show) { var frm; if (this.env.contentframe && (frm = rcube_find_object(this.env.contentframe))) { if (!show && window.frames[this.env.contentframe] && frames[this.env.contentframe].location.href.indexOf(this.env.blankpage)<0) frames[this.env.contentframe].location.href = this.env.blankpage; if (!bw.safari) frm.style.display = show ? 'block' : 'none'; } if (!show && this.busy) this.set_busy(false); }; // list a specific page this.list_page = function(page) { if (page=='next') page = this.env.current_page+1; if (page=='last') page = this.env.pagecount; if (page=='prev' && this.env.current_page>1) page = this.env.current_page-1; if (page=='first' && this.env.current_page>1) page = 1; if (page > 0 && page <= this.env.pagecount) { this.env.current_page = page; if (this.task=='mail') this.list_mailbox(this.env.mailbox, page); else if (this.task=='addressbook') this.list_contacts(this.env.source, page); } }; // list messages of a specific mailbox this.list_mailbox = function(mbox, page, sort) { this.last_selected = 0; var add_url = ''; var target = window; if (!mbox) mbox = this.env.mailbox; // add sort to url if set if (sort) add_url += '&_sort=' + sort; // also send search request to get the right messages if (this.env.search_request) add_url += '&_search='+this.env.search_request; // set page=1 if changeing to another mailbox if (!page && mbox != this.env.mailbox) { page = 1; this.env.current_page = page; if (this.message_list) this.message_list.clear_selection(); this.show_contentframe(false); } if (mbox != this.env.mailbox || (mbox == this.env.mailbox && !page && !sort)) add_url += '&_refresh=1'; this.select_folder(mbox, this.env.mailbox); this.env.mailbox = mbox; // load message list remotely if (this.gui_objects.messagelist) { this.list_mailbox_remote(mbox, page, add_url); return; } if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { target = window.frames[this.env.contentframe]; add_url += '&_framed=1'; } // load message list to target frame/window if (mbox) { this.set_busy(true, 'loading'); target.location.href = this.env.comm_path+'&_mbox='+urlencode(mbox)+(page ? '&_page='+page : '')+add_url; } }; // send remote request to load message list this.list_mailbox_remote = function(mbox, page, add_url) { // clear message list first this.message_list.clear(); // send request to server var url = '_mbox='+urlencode(mbox)+(page ? '&_page='+page : ''); this.set_busy(true, 'loading'); this.http_request('list', url+add_url, true); }; this.expunge_mailbox = function(mbox) { var lock = false; var add_url = ''; // lock interface if it's the active mailbox if (mbox == this.env.mailbox) { lock = true; this.set_busy(true, 'loading'); add_url = '&_reload=1'; } // send request to server var url = '_mbox='+urlencode(mbox); this.http_post('expunge', url+add_url, lock); }; this.purge_mailbox = function(mbox) { var lock = false; var add_url = ''; if (!confirm(this.get_label('purgefolderconfirm'))) return false; // lock interface if it's the active mailbox if (mbox == this.env.mailbox) { lock = true; this.set_busy(true, 'loading'); add_url = '&_reload=1'; } // send request to server var url = '_mbox='+urlencode(mbox); this.http_post('purge', url+add_url, lock); return true; }; // move selected messages to the specified mailbox this.move_messages = function(mbox) { // exit if current or no mailbox specified or if selection is empty if (!mbox || !this.env.uid || mbox==this.env.mailbox) { if (!this.message_list || !this.message_list.get_selection().length) return; } var lock = false; var add_url = '&_target_mbox='+urlencode(mbox)+'&_from='+(this.env.action ? this.env.action : ''); // show wait message if (this.env.action=='show') { lock = true; this.set_busy(true, 'movingmessage'); } else this.show_contentframe(false); this._with_selected_messages('moveto', lock, add_url); }; // delete selected messages from the current mailbox this.delete_messages = function() { var selection = this.message_list ? this.message_list.get_selection() : new Array(); // exit if no mailbox specified or if selection is empty if (!this.env.uid && !selection.length) return; // if there is a trash mailbox defined and we're not currently in it: if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase() != String(this.env.trash_mailbox).toLowerCase()) { // if shift was pressed delete it immediately if (this.message_list && this.message_list.shiftkey) { if (confirm(this.get_label('deletemessagesconfirm'))) this.permanently_remove_messages(); } else this.move_messages(this.env.trash_mailbox); } // if there is a trash mailbox defined but we *are* in it: else if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase() == String(this.env.trash_mailbox).toLowerCase()) this.permanently_remove_messages(); // if there isn't a defined trash mailbox and the config is set to flag for deletion else if (!this.env.trash_mailbox && this.env.flag_for_deletion) { this.mark_message('delete'); if(this.env.action=="show") this.command('nextmessage','',this); else if (selection.length == 1) this.message_list.select_next(); } // if there isn't a defined trash mailbox and the config is set NOT to flag for deletion else if (!this.env.trash_mailbox) this.permanently_remove_messages(); }; // delete the selected messages permanently this.permanently_remove_messages = function() { // exit if no mailbox specified or if selection is empty if (!this.env.uid && (!this.message_list || !this.message_list.get_selection().length)) return; this.show_contentframe(false); this._with_selected_messages('delete', false, '&_from='+(this.env.action ? this.env.action : '')); }; // Send a specifc request with UIDs of all selected messages // @private this._with_selected_messages = function(action, lock, add_url) { var a_uids = new Array(); if (this.env.uid) a_uids[a_uids.length] = this.env.uid; else { var selection = this.message_list.get_selection(); var id; for (var n=0; n 0) { rows[uid].classname = rows[uid].classname.replace(/\s*deleted/, ''); this.set_classname(rows[uid].obj, 'deleted', false); } if (rows[uid].unread && this.env.unreadicon) icn_src = this.env.unreadicon; else if (rows[uid].replied && this.env.repliedicon) icn_src = this.env.repliedicon; else if (this.env.messageicon) icn_src = this.env.messageicon; if (rows[uid].icon && icn_src) rows[uid].icon.src = icn_src; } } this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag=undelete'); return true; }; this.flag_as_deleted = function(a_uids) { // if deleting message from "view message" don't bother with delete icon if (this.env.action == "show") return false; var rows = this.message_list.rows; for (var i=0; i=0) message = message.substring(0, p-1) + message.substring(p+sig.length, message.length); } // add the new signature string if (this.env.signatures && this.env.signatures[id]) { sig = this.env.signatures[id]['text']; if (this.env.signatures[id]['is_html']) { sig = this.env.signatures[id]['plain_text']; } if (sig.indexOf('-- ')!=0) sig = '-- \n'+sig; message += '\n'+sig; } } else { var eid = tinyMCE.getEditorId('_message'); // editor is a TinyMCE_Control object var editor = tinyMCE.getInstanceById(eid); var msgDoc = editor.getDoc(); var msgBody = msgDoc.body; if (this.env.signatures && this.env.signatures[id]) { // Append the signature as a span within the body var sigElem = msgDoc.getElementById("_rc_sig"); if (!sigElem) { sigElem = msgDoc.createElement("span"); sigElem.setAttribute("id", "_rc_sig"); msgBody.appendChild(sigElem); } if (this.env.signatures[id]['is_html']) { sigElem.innerHTML = this.env.signatures[id]['text']; } else { sigElem.innerHTML = '
' + this.env.signatures[id]['text'] + '
'; } } } if (input_message) input_message.value = message; this.env.identity = id; return true; }; this.show_attachment_form = function(a) { if (!this.gui_objects.uploadbox) return false; var elm, list; if (elm = this.gui_objects.uploadbox) { if (a && (list = this.gui_objects.attachmentlist)) { var pos = rcube_get_object_pos(list); var left = pos.x; var top = pos.y + list.offsetHeight + 10; elm.style.top = top+'px'; elm.style.left = left+'px'; } elm.style.visibility = a ? 'visible' : 'hidden'; } // clear upload form if (!a && this.gui_objects.attachmentform && this.gui_objects.attachmentform!=this.gui_objects.messageform) this.gui_objects.attachmentform.reset(); return true; }; // upload attachment file this.upload_file = function(form) { if (!form) return false; // get file input fields var send = false; for (var n=0; n