/program/js/app.js
https://bitbucket.org/gencer/roundcubemail · JavaScript · 8051 lines · 6584 code · 922 blank · 545 comment · 1733 complexity · f366bf2993d9b6e49680930ff75594b8 MD5 · raw file
Large files are truncated click here to view the full file
- /**
- * Roundcube Webmail Client Script
- *
- * This file is part of the Roundcube Webmail client
- *
- * @licstart The following is the entire license notice for the
- * JavaScript code in this file.
- *
- * Copyright (C) 2005-2014, The Roundcube Dev Team
- * Copyright (C) 2011-2014, Kolab Systems AG
- *
- * The JavaScript code in this page is free software: you can
- * redistribute it and/or modify it under the terms of the GNU
- * General Public License (GNU GPL) as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option)
- * any later version. The code is distributed WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
- *
- * As additional permission under GNU GPL version 3 section 7, you
- * may distribute non-source (e.g., minimized or compacted) forms of
- * that code without the copy of the GNU GPL normally required by
- * section 4, provided you include this license notice and a URL
- * through which recipients can access the Corresponding Source.
- *
- * @licend The above is the entire license notice
- * for the JavaScript code in this file.
- *
- * @author Thomas Bruederli <roundcube@gmail.com>
- * @author Aleksander 'A.L.E.C' Machniak <alec@alec.pl>
- * @author Charles McNulty <charles@charlesmcnulty.com>
- *
- * @requires jquery.js, common.js, list.js
- */
- function rcube_webmail()
- {
- this.labels = {};
- this.buttons = {};
- this.buttons_sel = {};
- this.gui_objects = {};
- this.gui_containers = {};
- this.commands = {};
- this.command_handlers = {};
- this.onloads = [];
- this.messages = {};
- this.group2expand = {};
- this.http_request_jobs = {};
- this.menu_stack = [];
- // webmail client settings
- this.dblclick_time = 500;
- this.message_time = 5000;
- this.identifier_expr = /[^0-9a-z_-]/gi;
- // environment defaults
- this.env = {
- request_timeout: 180, // seconds
- draft_autosave: 0, // seconds
- comm_path: './',
- blankpage: 'program/resources/blank.gif',
- recipients_separator: ',',
- recipients_delimiter: ', ',
- popup_width: 1150,
- popup_width_small: 900
- };
- // create protected reference to myself
- this.ref = 'rcmail';
- var ref = this;
- // set jQuery ajax options
- $.ajaxSetup({
- cache: false,
- timeout: this.env.request_timeout * 1000,
- error: function(request, status, err){ ref.http_error(request, status, err); },
- beforeSend: function(xmlhttp){ xmlhttp.setRequestHeader('X-Roundcube-Request', ref.env.request_token); }
- });
- // unload fix
- $(window).bind('beforeunload', function() { ref.unload = true; });
- // 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(p, value)
- {
- if (typeof p == 'string')
- this.labels[p] = value;
- else if (typeof p == 'object')
- $.extend(this.labels, p);
- };
- // add a button to the button list
- this.register_button = function(command, id, type, act, sel, over)
- {
- 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;
- if (!this.buttons[command])
- this.buttons[command] = [];
- this.buttons[command].push(button_prop);
- if (this.loaded)
- init_button(command, button_prop);
- };
- // register a specific gui object
- this.gui_object = function(name, id)
- {
- this.gui_objects[name] = this.loaded ? rcube_find_object(id) : id;
- };
- // register a container object
- this.gui_container = function(name, id)
- {
- this.gui_containers[name] = id;
- };
- // add a GUI element (html node) to a specified container
- this.add_element = function(elm, container)
- {
- if (this.gui_containers[container] && this.gui_containers[container].jquery)
- this.gui_containers[container].append(elm);
- };
- // register an external handler for a certain command
- this.register_command = function(command, callback, enable)
- {
- this.command_handlers[command] = callback;
- if (enable)
- this.enable_command(command, true);
- };
- // execute the given script on load
- this.add_onload = function(f)
- {
- this.onloads.push(f);
- };
- // initialize webmail client
- this.init = function()
- {
- var n;
- this.task = this.env.task;
- // check browser
- if (this.env.server_error != 409 && (!bw.dom || !bw.xmlhttp_test() || (bw.mz && bw.vendver < 1.9) || (bw.ie && bw.vendver < 7))) {
- this.goto_url('error', '_code=0x199');
- return;
- }
- // find all registered gui containers
- for (n in this.gui_containers)
- this.gui_containers[n] = $('#'+this.gui_containers[n]);
- // find all registered gui objects
- for (n in this.gui_objects)
- this.gui_objects[n] = rcube_find_object(this.gui_objects[n]);
- // clickjacking protection
- if (this.env.x_frame_options) {
- try {
- // bust frame if not allowed
- if (this.env.x_frame_options == 'deny' && top.location.href != self.location.href)
- top.location.href = self.location.href;
- else if (top.location.hostname != self.location.hostname)
- throw 1;
- } catch (e) {
- // possible clickjacking attack: disable all form elements
- $('form').each(function(){ ref.lock_form(this, true); });
- this.display_message("Blocked: possible clickjacking attack!", 'error');
- return;
- }
- }
- // init registered buttons
- this.init_buttons();
- // tell parent window that this frame is loaded
- if (this.is_framed()) {
- parent.rcmail.set_busy(false, null, parent.rcmail.env.frame_lock);
- parent.rcmail.env.frame_lock = null;
- }
- // enable general commands
- this.enable_command('close', 'logout', 'mail', 'addressbook', 'settings', 'save-pref',
- 'compose', 'undo', 'about', 'switch-task', 'menu-open', 'menu-close', 'menu-save', true);
- // set active task button
- this.set_button(this.task, 'sel');
- if (this.env.permaurl)
- this.enable_command('permaurl', 'extwin', true);
- switch (this.task) {
- case 'mail':
- // enable mail commands
- this.enable_command('list', 'checkmail', 'add-contact', 'search', 'reset-search', 'collapse-folder', 'import-messages', true);
- if (this.gui_objects.messagelist) {
- this.message_list = new rcube_list_widget(this.gui_objects.messagelist, {
- multiselect:true, multiexpand:true, draggable:true, keyboard:true,
- column_movable:this.env.col_movable, dblclick_time:this.dblclick_time
- });
- this.message_list
- .addEventListener('initrow', function(o) { ref.init_message_row(o); })
- .addEventListener('dblclick', function(o) { ref.msglist_dbl_click(o); })
- .addEventListener('click', function(o) { ref.msglist_click(o); })
- .addEventListener('keypress', function(o) { ref.msglist_keypress(o); })
- .addEventListener('select', function(o) { ref.msglist_select(o); })
- .addEventListener('dragstart', function(o) { ref.drag_start(o); })
- .addEventListener('dragmove', function(e) { ref.drag_move(e); })
- .addEventListener('dragend', function(e) { ref.drag_end(e); })
- .addEventListener('expandcollapse', function(o) { ref.msglist_expand(o); })
- .addEventListener('column_replace', function(o) { ref.msglist_set_coltypes(o); })
- .addEventListener('listupdate', function(o) { ref.triggerEvent('listupdate', o); })
- .init();
- // TODO: this should go into the list-widget code
- $(this.message_list.thead).on('click', 'a.sortcol', function(e){
- return ref.command('sort', $(this).attr('rel'), this);
- });
- this.enable_command('toggle_status', 'toggle_flag', 'sort', true);
- this.enable_command('set-listmode', this.env.threads && !this.is_multifolder_listing());
- // load messages
- this.command('list');
- $(this.gui_objects.qsearchbox).val(this.env.search_text).focusin(function() { ref.message_list.blur(); });
- }
- this.set_button_titles();
- this.env.message_commands = ['show', 'reply', 'reply-all', 'reply-list',
- 'move', 'copy', 'delete', 'open', 'mark', 'edit', 'viewsource',
- 'print', 'load-attachment', 'download-attachment', 'show-headers', 'hide-headers', 'download',
- 'forward', 'forward-inline', 'forward-attachment', 'change-format'];
- if (this.env.action == 'show' || this.env.action == 'preview') {
- this.enable_command(this.env.message_commands, this.env.uid);
- this.enable_command('reply-list', this.env.list_post);
- if (this.env.action == 'show') {
- this.http_request('pagenav', {_uid: this.env.uid, _mbox: this.env.mailbox, _search: this.env.search_request},
- this.display_message('', 'loading'));
- }
- if (this.env.blockedobjects) {
- if (this.gui_objects.remoteobjectsmsg)
- this.gui_objects.remoteobjectsmsg.style.display = 'block';
- this.enable_command('load-images', 'always-load', true);
- }
- // make preview/message frame visible
- if (this.env.action == 'preview' && this.is_framed()) {
- this.enable_command('compose', 'add-contact', false);
- parent.rcmail.show_contentframe(true);
- }
- }
- else if (this.env.action == 'compose') {
- this.env.address_group_stack = [];
- this.env.compose_commands = ['send-attachment', 'remove-attachment', 'send', 'cancel',
- 'toggle-editor', 'list-adresses', 'pushgroup', 'search', 'reset-search', 'extwin',
- 'insert-response', 'save-response', 'menu-open', 'menu-close'];
- if (this.env.drafts_mailbox)
- this.env.compose_commands.push('savedraft')
- this.enable_command(this.env.compose_commands, 'identities', 'responses', true);
- // add more commands (not enabled)
- $.merge(this.env.compose_commands, ['add-recipient', 'firstpage', 'previouspage', 'nextpage', 'lastpage']);
- if (window.googie) {
- this.env.editor_config.spellchecker = googie;
- this.env.editor_config.spellcheck_observer = function(s) { ref.spellcheck_state(); };
- this.env.compose_commands.push('spellcheck')
- this.enable_command('spellcheck', true);
- }
- // initialize HTML editor
- this.editor_init(this.env.editor_config, this.env.composebody);
- // init canned response functions
- if (this.gui_objects.responseslist) {
- $('a.insertresponse', this.gui_objects.responseslist)
- .attr('unselectable', 'on')
- .mousedown(function(e){ return rcube_event.cancel(e); })
- .bind('mouseup keypress', function(e){
- if (e.type == 'mouseup' || rcube_event.get_keycode(e) == 13) {
- ref.command('insert-response', $(this).attr('rel'));
- $(document.body).trigger('mouseup'); // hides the menu
- return rcube_event.cancel(e);
- }
- });
- // avoid textarea loosing focus when hitting the save-response button/link
- $.each(this.buttons['save-response'] || [], function (i, v) {
- $('#' + v.id).mousedown(function(e){ return rcube_event.cancel(e); })
- });
- }
- // init message compose form
- this.init_messageform();
- }
- else if (this.env.action == 'get')
- this.enable_command('download', 'print', true);
- // show printing dialog
- else if (this.env.action == 'print' && this.env.uid) {
- if (bw.safari)
- setTimeout('window.print()', 10);
- else
- window.print();
- }
- // get unread count for each mailbox
- if (this.gui_objects.mailboxlist) {
- this.env.unread_counts = {};
- this.gui_objects.folderlist = this.gui_objects.mailboxlist;
- this.http_request('getunread');
- }
- // init address book widget
- if (this.gui_objects.contactslist) {
- this.contact_list = new rcube_list_widget(this.gui_objects.contactslist,
- { multiselect:true, draggable:false, keyboard:true });
- this.contact_list
- .addEventListener('initrow', function(o) { ref.triggerEvent('insertrow', { cid:o.uid, row:o }); })
- .addEventListener('select', function(o) { ref.compose_recipient_select(o); })
- .addEventListener('dblclick', function(o) { ref.compose_add_recipient(); })
- .addEventListener('keypress', function(o) {
- if (o.key_pressed == o.ENTER_KEY) {
- if (!ref.compose_add_recipient()) {
- // execute link action on <enter> if not a recipient entry
- if (o.last_selected && String(o.last_selected).charAt(0) == 'G') {
- $(o.rows[o.last_selected].obj).find('a').first().click();
- }
- }
- }
- })
- .init();
- // remember last focused address field
- $('#_to,#_cc,#_bcc').focus(function() { ref.env.focused_field = this; });
- }
- if (this.gui_objects.addressbookslist) {
- this.gui_objects.folderlist = this.gui_objects.addressbookslist;
- this.enable_command('list-adresses', true);
- }
- // ask user to send MDN
- if (this.env.mdn_request && this.env.uid) {
- var postact = 'sendmdn',
- postdata = {_uid: this.env.uid, _mbox: this.env.mailbox};
- if (!confirm(this.get_label('mdnrequest'))) {
- postdata._flag = 'mdnsent';
- postact = 'mark';
- }
- this.http_post(postact, postdata);
- }
- // detect browser capabilities
- if (!this.is_framed() && !this.env.extwin)
- this.browser_capabilities_check();
- break;
- case 'addressbook':
- this.env.address_group_stack = [];
- if (this.gui_objects.folderlist)
- this.env.contactfolders = $.extend($.extend({}, this.env.address_sources), this.env.contactgroups);
- this.enable_command('add', 'import', this.env.writable_source);
- this.enable_command('list', 'listgroup', 'pushgroup', 'popgroup', 'listsearch', 'search', 'reset-search', 'advanced-search', true);
- if (this.gui_objects.contactslist) {
- this.contact_list = new rcube_list_widget(this.gui_objects.contactslist,
- {multiselect:true, draggable:this.gui_objects.folderlist?true:false, keyboard:true});
- this.contact_list
- .addEventListener('initrow', function(o) { ref.triggerEvent('insertrow', { cid:o.uid, row:o }); })
- .addEventListener('keypress', function(o) { ref.contactlist_keypress(o); })
- .addEventListener('select', function(o) { ref.contactlist_select(o); })
- .addEventListener('dragstart', function(o) { ref.drag_start(o); })
- .addEventListener('dragmove', function(e) { ref.drag_move(e); })
- .addEventListener('dragend', function(e) { ref.drag_end(e); })
- .init();
- $(this.gui_objects.qsearchbox).focusin(function() { ref.contact_list.blur(); });
- this.update_group_commands();
- this.command('list');
- }
- if (this.gui_objects.savedsearchlist) {
- this.savedsearchlist = new rcube_treelist_widget(this.gui_objects.savedsearchlist, {
- id_prefix: 'rcmli',
- id_encode: this.html_identifier_encode,
- id_decode: this.html_identifier_decode
- });
- this.savedsearchlist.addEventListener('select', function(node) {
- ref.triggerEvent('selectfolder', { folder:node.id, prefix:'rcmli' }); });
- }
- this.set_page_buttons();
- if (this.env.cid) {
- this.enable_command('show', 'edit', true);
- // register handlers for group assignment via checkboxes
- if (this.gui_objects.editform) {
- $('input.groupmember').change(function() {
- ref.group_member_change(this.checked ? 'add' : 'del', ref.env.cid, ref.env.source, this.value);
- });
- }
- }
- if (this.gui_objects.editform) {
- this.enable_command('save', true);
- if (this.env.action == 'add' || this.env.action == 'edit' || this.env.action == 'search')
- this.init_contact_form();
- }
- break;
- case 'settings':
- this.enable_command('preferences', 'identities', 'responses', 'save', 'folders', true);
- if (this.env.action == 'identities') {
- this.enable_command('add', this.env.identities_level < 2);
- }
- else if (this.env.action == 'edit-identity' || this.env.action == 'add-identity') {
- this.enable_command('save', 'edit', 'toggle-editor', true);
- this.enable_command('delete', this.env.identities_level < 2);
- // initialize HTML editor
- this.editor_init(this.env.editor_config, 'rcmfd_signature');
- }
- else if (this.env.action == 'folders') {
- this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', true);
- }
- else if (this.env.action == 'edit-folder' && this.gui_objects.editform) {
- this.enable_command('save', 'folder-size', true);
- parent.rcmail.env.exists = this.env.messagecount;
- parent.rcmail.enable_command('purge', this.env.messagecount);
- }
- else if (this.env.action == 'responses') {
- this.enable_command('add', true);
- }
- if (this.gui_objects.identitieslist) {
- this.identity_list = new rcube_list_widget(this.gui_objects.identitieslist,
- {multiselect:false, draggable:false, keyboard:true});
- this.identity_list
- .addEventListener('select', function(o) { ref.identity_select(o); })
- .addEventListener('keypress', function(o) {
- if (o.key_pressed == o.ENTER_KEY) {
- ref.identity_select(o);
- }
- })
- .init()
- .focus();
- }
- else if (this.gui_objects.sectionslist) {
- this.sections_list = new rcube_list_widget(this.gui_objects.sectionslist, {multiselect:false, draggable:false, keyboard:true});
- this.sections_list
- .addEventListener('select', function(o) { ref.section_select(o); })
- .addEventListener('keypress', function(o) { if (o.key_pressed == o.ENTER_KEY) ref.section_select(o); })
- .init()
- .focus();
- }
- else if (this.gui_objects.subscriptionlist) {
- this.init_subscription_list();
- }
- else if (this.gui_objects.responseslist) {
- this.responses_list = new rcube_list_widget(this.gui_objects.responseslist, {multiselect:false, draggable:false, keyboard:true});
- this.responses_list
- .addEventListener('select', function(list) {
- var win, id = list.get_single_selection();
- ref.enable_command('delete', !!id && $.inArray(id, ref.env.readonly_responses) < 0);
- if (id && (win = ref.get_frame_window(ref.env.contentframe))) {
- ref.set_busy(true);
- ref.location_href({ _action:'edit-response', _key:id, _framed:1 }, win);
- }
- })
- .init()
- .focus();
- }
- break;
- case 'login':
- var input_user = $('#rcmloginuser');
- input_user.bind('keyup', function(e){ return ref.login_user_keyup(e); });
- if (input_user.val() == '')
- input_user.focus();
- else
- $('#rcmloginpwd').focus();
- // detect client timezone
- if (window.jstz) {
- var timezone = jstz.determine();
- if (timezone.name())
- $('#rcmlogintz').val(timezone.name());
- }
- else {
- $('#rcmlogintz').val(new Date().getStdTimezoneOffset() / -60);
- }
- // display 'loading' message on form submit, lock submit button
- $('form').submit(function () {
- $('input[type=submit]', this).prop('disabled', true);
- ref.clear_messages();
- ref.display_message('', 'loading');
- });
- this.enable_command('login', true);
- break;
- }
- // select first input field in an edit form
- if (this.gui_objects.editform)
- $("input,select,textarea", this.gui_objects.editform)
- .not(':hidden').not(':disabled').first().select().focus();
- // unset contentframe variable if preview_pane is enabled
- if (this.env.contentframe && !$('#' + this.env.contentframe).is(':visible'))
- this.env.contentframe = null;
- // prevent from form submit with Enter key in file input fields
- if (bw.ie)
- $('input[type=file]').keydown(function(e) { if (e.keyCode == '13') e.preventDefault(); });
- // flag object as complete
- this.loaded = true;
- this.env.lastrefresh = new Date();
- // show message
- if (this.pending_message)
- this.display_message(this.pending_message[0], this.pending_message[1], this.pending_message[2]);
- // init treelist widget
- if (this.gui_objects.folderlist && window.rcube_treelist_widget) {
- this.treelist = new rcube_treelist_widget(this.gui_objects.folderlist, {
- selectable: true,
- id_prefix: 'rcmli',
- id_encode: this.html_identifier_encode,
- id_decode: this.html_identifier_decode,
- check_droptarget: function(node) { return !node.virtual && ref.check_droptarget(node.id) }
- });
- this.treelist
- .addEventListener('collapse', function(node) { ref.folder_collapsed(node) })
- .addEventListener('expand', function(node) { ref.folder_collapsed(node) })
- .addEventListener('select', function(node) { ref.triggerEvent('selectfolder', { folder:node.id, prefix:'rcmli' }) });
- }
- // activate html5 file drop feature (if browser supports it and if configured)
- if (this.gui_objects.filedrop && this.env.filedrop && ((window.XMLHttpRequest && XMLHttpRequest.prototype && XMLHttpRequest.prototype.sendAsBinary) || window.FormData)) {
- $(document.body).bind('dragover dragleave drop', function(e){ return ref.document_drag_hover(e, e.type == 'dragover'); });
- $(this.gui_objects.filedrop).addClass('droptarget')
- .bind('dragover dragleave', function(e){ return ref.file_drag_hover(e, e.type == 'dragover'); })
- .get(0).addEventListener('drop', function(e){ return ref.file_dropped(e); }, false);
- }
- // catch document (and iframe) mouse clicks
- var body_mouseup = function(e){ return ref.doc_mouse_up(e); };
- $(document.body)
- .bind('mouseup', body_mouseup)
- .bind('keydown', function(e){ return ref.doc_keypress(e); });
- $('iframe').load(function(e) {
- try { $(this.contentDocument || this.contentWindow).on('mouseup', body_mouseup); }
- catch (e) {/* catch possible "Permission denied" error in IE */ }
- })
- .contents().on('mouseup', body_mouseup);
- // trigger init event hook
- this.triggerEvent('init', { task:this.task, action:this.env.action });
- // execute all foreign onload scripts
- // @deprecated
- for (n in this.onloads) {
- if (typeof this.onloads[n] === 'string')
- eval(this.onloads[n]);
- else if (typeof this.onloads[n] === 'function')
- this.onloads[n]();
- }
- // start keep-alive and refresh intervals
- this.start_refresh();
- this.start_keepalive();
- };
- this.log = function(msg)
- {
- if (window.console && console.log)
- console.log(msg);
- };
- /*********************************************************/
- /********* client command interface *********/
- /*********************************************************/
- // execute a specific command on the web client
- this.command = function(command, props, obj, event)
- {
- var ret, uid, cid, url, flag, aborted = false;
- if (obj && obj.blur && !(event && rcube_event.is_keyboard(event)))
- obj.blur();
- // do nothing if interface is locked by other command (with exception for searching reset)
- if (this.busy && !(command == 'reset-search' && this.last_command == 'search'))
- return false;
- // let the browser handle this click (shift/ctrl usually opens the link in a new window/tab)
- if ((obj && obj.href && String(obj.href).indexOf('#') < 0) && rcube_event.get_modifier(event)) {
- return true;
- }
- // command not supported or allowed
- if (!this.commands[command]) {
- // pass command to parent window
- if (this.is_framed())
- parent.rcmail.command(command, props);
- return false;
- }
- // check input before leaving compose step
- if (this.task == 'mail' && this.env.action == 'compose' && $.inArray(command, this.env.compose_commands) < 0 && !this.env.server_error) {
- if (this.cmp_hash != this.compose_field_hash() && !confirm(this.get_label('notsentwarning')))
- return false;
- // remove copy from local storage if compose screen is left intentionally
- this.remove_compose_data(this.env.compose_id);
- this.compose_skip_unsavedcheck = true;
- }
- this.last_command = command;
- // process external commands
- if (typeof this.command_handlers[command] === 'function') {
- ret = this.command_handlers[command](props, obj, event);
- return ret !== undefined ? ret : (obj ? false : true);
- }
- else if (typeof this.command_handlers[command] === 'string') {
- ret = window[this.command_handlers[command]](props, obj, event);
- return ret !== undefined ? ret : (obj ? false : true);
- }
- // trigger plugin hooks
- this.triggerEvent('actionbefore', {props:props, action:command, originalEvent:event});
- ret = this.triggerEvent('before'+command, props || event);
- if (ret !== undefined) {
- // abort if one of the handlers returned false
- if (ret === false)
- return false;
- else
- props = ret;
- }
- ret = undefined;
- // process internal command
- switch (command) {
- case 'login':
- if (this.gui_objects.loginform)
- this.gui_objects.loginform.submit();
- break;
- // commands to switch task
- case 'logout':
- case 'mail':
- case 'addressbook':
- case 'settings':
- this.switch_task(command);
- break;
- case 'about':
- this.redirect('?_task=settings&_action=about', false);
- break;
- case 'permaurl':
- if (obj && obj.href && obj.target)
- return true;
- else if (this.env.permaurl)
- parent.location.href = this.env.permaurl;
- break;
- case 'extwin':
- if (this.env.action == 'compose') {
- var form = this.gui_objects.messageform,
- win = this.open_window('');
- if (win) {
- this.save_compose_form_local();
- this.compose_skip_unsavedcheck = true;
- $("input[name='_action']", form).val('compose');
- form.action = this.url('mail/compose', { _id: this.env.compose_id, _extwin: 1 });
- form.target = win.name;
- form.submit();
- }
- }
- else {
- this.open_window(this.env.permaurl, true);
- }
- break;
- case 'change-format':
- url = this.env.permaurl + '&_format=' + props;
- if (this.env.action == 'preview')
- url = url.replace(/_action=show/, '_action=preview') + '&_framed=1';
- if (this.env.extwin)
- url += '&_extwin=1';
- location.href = url;
- break;
- case 'menu-open':
- if (props && props.menu == 'attachmentmenu') {
- var mimetype = this.env.attachments[props.id];
- this.enable_command('open-attachment', mimetype && this.env.mimetypes && $.inArray(mimetype, this.env.mimetypes) >= 0);
- }
- this.show_menu(props, props.show || undefined, event);
- break;
- case 'menu-close':
- this.hide_menu(props, event);
- break;
- case 'menu-save':
- this.triggerEvent(command, {props:props, originalEvent:event});
- return false;
- case 'open':
- if (uid = this.get_single_uid()) {
- obj.href = this.url('show', {_mbox: this.get_message_mailbox(uid), _uid: uid});
- return true;
- }
- break;
- case 'close':
- if (this.env.extwin)
- window.close();
- break;
- case 'list':
- if (props && props != '') {
- this.reset_qsearch();
- }
- if (this.env.action == 'compose' && this.env.extwin) {
- window.close();
- }
- else if (this.task == 'mail') {
- this.list_mailbox(props);
- this.set_button_titles();
- }
- else if (this.task == 'addressbook')
- this.list_contacts(props);
- break;
- case 'set-listmode':
- this.set_list_options(null, undefined, undefined, props == 'threads' ? 1 : 0);
- break;
- case 'sort':
- var sort_order = this.env.sort_order,
- sort_col = !this.env.disabled_sort_col ? props : this.env.sort_col;
- if (!this.env.disabled_sort_order)
- sort_order = this.env.sort_col == sort_col && sort_order == 'ASC' ? 'DESC' : 'ASC';
- // set table header and update env
- this.set_list_sorting(sort_col, sort_order);
- // reload message list
- this.list_mailbox('', '', sort_col+'_'+sort_order);
- break;
- case 'nextpage':
- this.list_page('next');
- break;
- case 'lastpage':
- this.list_page('last');
- break;
- case 'previouspage':
- this.list_page('prev');
- break;
- case 'firstpage':
- this.list_page('first');
- break;
- case 'expunge':
- if (this.env.exists)
- this.expunge_mailbox(this.env.mailbox);
- break;
- case 'purge':
- case 'empty-mailbox':
- if (this.env.exists)
- this.purge_mailbox(this.env.mailbox);
- break;
- // common commands used in multiple tasks
- case 'show':
- if (this.task == 'mail') {
- uid = this.get_single_uid();
- if (uid && (!this.env.uid || uid != this.env.uid)) {
- if (this.env.mailbox == this.env.drafts_mailbox)
- this.open_compose_step({ _draft_uid: uid, _mbox: this.env.mailbox });
- else
- this.show_message(uid);
- }
- }
- else if (this.task == 'addressbook') {
- cid = props ? props : this.get_single_cid();
- if (cid && !(this.env.action == 'show' && cid == this.env.cid))
- this.load_contact(cid, 'show');
- }
- break;
- case 'add':
- if (this.task == 'addressbook')
- this.load_contact(0, 'add');
- else if (this.task == 'settings' && this.env.action == 'responses') {
- var frame;
- if ((frame = this.get_frame_window(this.env.contentframe))) {
- this.set_busy(true);
- this.location_href({ _action:'add-response', _framed:1 }, frame);
- }
- }
- else if (this.task == 'settings') {
- this.identity_list.clear_selection();
- this.load_identity(0, 'add-identity');
- }
- break;
- case 'edit':
- if (this.task == 'addressbook' && (cid = this.get_single_cid()))
- this.load_contact(cid, 'edit');
- else if (this.task == 'settings' && props)
- this.load_identity(props, 'edit-identity');
- else if (this.task == 'mail' && (uid = this.get_single_uid())) {
- url = { _mbox: this.get_message_mailbox(uid) };
- url[this.env.mailbox == this.env.drafts_mailbox && props != 'new' ? '_draft_uid' : '_uid'] = uid;
- this.open_compose_step(url);
- }
- break;
- case 'save':
- var input, form = this.gui_objects.editform;
- if (form) {
- // adv. search
- if (this.env.action == 'search') {
- }
- // user prefs
- else if ((input = $("input[name='_pagesize']", form)) && input.length && isNaN(parseInt(input.val()))) {
- alert(this.get_label('nopagesizewarning'));
- input.focus();
- break;
- }
- // contacts/identities
- else {
- // reload form
- if (props == 'reload') {
- form.action += '?_reload=1';
- }
- else if (this.task == 'settings' && (this.env.identities_level % 2) == 0 &&
- (input = $("input[name='_email']", form)) && input.length && !rcube_check_email(input.val())
- ) {
- alert(this.get_label('noemailwarning'));
- input.focus();
- break;
- }
- // clear empty input fields
- $('input.placeholder').each(function(){ if (this.value == this._placeholder) this.value = ''; });
- }
- // add selected source (on the list)
- if (parent.rcmail && parent.rcmail.env.source)
- form.action = this.add_url(form.action, '_orig_source', parent.rcmail.env.source);
- form.submit();
- }
- break;
- case 'delete':
- // mail task
- if (this.task == 'mail')
- this.delete_messages(event);
- // addressbook task
- else if (this.task == 'addressbook')
- this.delete_contacts();
- // settings: canned response
- else if (this.task == 'settings' && this.env.action == 'responses')
- this.delete_response();
- // settings: user identities
- else if (this.task == 'settings')
- this.delete_identity();
- break;
- // mail task commands
- case 'move':
- case 'moveto': // deprecated
- if (this.task == 'mail')
- this.move_messages(props, event);
- else if (this.task == 'addressbook')
- this.move_contacts(props);
- break;
- case 'copy':
- if (this.task == 'mail')
- this.copy_messages(props, event);
- else if (this.task == 'addressbook')
- this.copy_contacts(props);
- break;
- case 'mark':
- if (props)
- this.mark_message(props);
- break;
- case 'toggle_status':
- case 'toggle_flag':
- flag = command == 'toggle_flag' ? 'flagged' : 'read';
- if (uid = props) {
- // toggle flagged/unflagged
- if (flag == 'flagged') {
- if (this.message_list.rows[uid].flagged)
- flag = 'unflagged';
- }
- // toggle read/unread
- else if (this.message_list.rows[uid].deleted)
- flag = 'undelete';
- else if (!this.message_list.rows[uid].unread)
- flag = 'unread';
- this.mark_message(flag, uid);
- }
- break;
- case 'always-load':
- if (this.env.uid && this.env.sender) {
- this.add_contact(this.env.sender);
- setTimeout(function(){ ref.command('load-images'); }, 300);
- break;
- }
- case 'load-images':
- if (this.env.uid)
- this.show_message(this.env.uid, true, this.env.action=='preview');
- break;
- case 'load-attachment':
- case 'open-attachment':
- case 'download-attachment':
- var qstring = '_mbox='+urlencode(this.env.mailbox)+'&_uid='+this.env.uid+'&_part='+props,
- mimetype = this.env.attachments[props];
- // open attachment in frame if it's of a supported mimetype
- if (command != 'download-attachment' && mimetype && this.env.mimetypes && $.inArray(mimetype, this.env.mimetypes) >= 0) {
- if (this.open_window(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1'))
- break;
- }
- this.goto_url('get', qstring+'&_download=1', false);
- break;
- case 'select-all':
- this.select_all_mode = props ? false : true;
- this.dummy_select = true; // prevent msg opening if there's only one msg on the list
- if (props == 'invert')
- this.message_list.invert_selection();
- else
- this.message_list.select_all(props == 'page' ? '' : props);
- this.dummy_select = null;
- break;
- case 'select-none':
- this.select_all_mode = false;
- this.message_list.clear_selection();
- break;
- case 'expand-all':
- this.env.autoexpand_threads = 1;
- this.message_list.expand_all();
- break;
- case 'expand-unread':
- this.env.autoexpand_threads = 2;
- this.message_list.collapse_all();
- this.expand_unread();
- break;
- case 'collapse-all':
- this.env.autoexpand_threads = 0;
- this.message_list.collapse_all();
- 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 'compose':
- url = {};
- if (this.task == 'mail') {
- url._mbox = this.env.mailbox;
- if (props)
- url._to = props;
- // also send search request so we can go back to search result after message is sent
- if (this.env.search_request)
- url._search = this.env.search_request;
- }
- // modify url if we're in addressbook
- else if (this.task == 'addressbook') {
- // switch to mail compose step directly
- if (props && props.indexOf('@') > 0) {
- url._to = props;
- }
- else {
- var a_cids = [];
- // use contact id passed as command parameter
- if (props)
- a_cids.push(props);
- // get selected contacts
- else if (this.contact_list)
- a_cids = this.contact_list.get_selection();
- if (a_cids.length)
- this.http_post('mailto', { _cid: a_cids.join(','), _source: this.env.source }, true);
- else if (this.env.group)
- this.http_post('mailto', { _gid: this.env.group, _source: this.env.source }, true);
- break;
- }
- }
- else if (props && typeof props == 'string') {
- url._to = props;
- }
- else if (props && typeof props == 'object') {
- $.extend(url, props);
- }
- this.open_compose_step(url);
- break;
- case 'spellcheck':
- if (this.spellcheck_state()) {
- this.editor.spellcheck_stop();
- }
- else {
- this.editor.spellcheck_start();
- }
- break;
- case 'savedraft':
- // Reset the auto-save timer
- clearTimeout(this.save_timer);
- // compose form did not change (and draft wasn't saved already)
- if (this.env.draft_id && this.cmp_hash == this.compose_field_hash()) {
- this.auto_save_start();
- break;
- }
- this.submit_messageform(true);
- break;
- case 'send':
- if (!props.nocheck && !this.check_compose_input(command))
- break;
- // Reset the auto-save timer
- clearTimeout(this.save_timer);
- this.submit_messageform();
- break;
- case 'send-attachment':
- // Reset the auto-save timer
- clearTimeout(this.save_timer);
- if (!(flag = this.upload_file(props || this.gui_objects.uploadform, 'upload'))) {
- if (flag !== false)
- alert(this.get_label('selectimportfile'));
- aborted = true;
- }
- break;
- case 'insert-sig':
- this.change_identity($("[name='_from']")[0], true);
- break;
- case 'list-adresses':
- this.list_contacts(props);
- this.enable_command('add-recipient', false);
- break;
- case 'add-recipient':
- this.compose_add_recipient(props);
- break;
- case 'reply-all':
- case 'reply-list':
- case 'reply':
- if (uid = this.get_single_uid()) {
- url = {_reply_uid: uid, _mbox: this.get_message_mailbox(uid)};
- if (command == 'reply-all')
- // do reply-list, when list is detected and popup menu wasn't used
- url._all = (!props && this.env.reply_all_mode == 1 && this.commands['reply-list'] ? 'list' : 'all');
- else if (command == 'reply-list')
- url._all = 'list';
- this.open_compose_step(url);
- }
- break;
- case 'forward-attachment':
- case 'forward-inline':
- case 'forward':
- var uids = this.env.uid ? [this.env.uid] : (this.message_list ? this.message_list.get_selection() : []);
- if (uids.length) {
- url = { _forward_uid: this.uids_to_list(uids), _mbox: this.env.mailbox, _search: this.env.search_request };
- if (command == 'forward-attachment' || (!props && this.env.forward_attachment) || uids.length > 1)
- url._attachment = 1;
- this.open_compose_step(url);
- }
- break;
- case 'print':
- if (this.env.action == 'get') {
- this.gui_objects.messagepartframe.contentWindow.print();
- }
- else if (uid = this.get_single_uid()) {
- url = '&_action=print&_uid='+uid+'&_mbox='+urlencode(this.get_message_mailbox(uid))+(this.env.safemode ? '&_safe=1' : '');
- if (this.open_window(this.env.comm_path + url, true, true)) {
- if (this.env.action != 'show')
- this.mark_message('read', uid);
- }
- }
- break;
- case 'viewsource':
- if (uid = this.get_single_uid())
- this.open_window(this.env.comm_path+'&_action=viewsource&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true, true);
- break;
- case 'download':
- if (this.env.action == 'get') {
- location.href = location.href.replace(/_frame=/, '_download=');
- }
- else if (uid = this.get_single_uid()) {
- this.goto_url('viewsource', { _uid: uid, _mbox: this.get_message_mailbox(uid), _save: 1 });
- }
- break;
- // quicksearch
- case 'search':
- if (!props && this.gui_objects.qsearchbox)
- props = this.gui_objects.qsearchbox.value;
- if (props) {
- this.qsearch(props);
- break;
- }
- // reset quicksearch
- case 'reset-search':
- var n, s = this.env.search_request || this.env.qsearch;
- this.reset_qsearch();
- this.select_all_mode = false;
- if (s && this.env.action == 'compose') {
- if (this.contact_list)
- this.list_contacts_clear();
- }
- else if (s && this.env.mailbox) {
- this.list_mailbox(this.env.mailbox, 1);
- }
- else if (s && this.task == 'addressbook') {
- if (this.env.source == '') {
- for (n in this.env.address_sources) break;
- this.env.source = n;
- this.env.group = '';
- }
- this.list_contacts(this.env.source, this.env.group, 1);
- }
- break;
- case 'pushgroup':
- // add group ID to stack
- this.env.address_group_stack.push(props.id);
- if (obj && event)
- rcube_event.cancel(event);
- case 'listgroup':
- this.reset_qsearch();
- this.list_contacts(props.source, props.id);
- break;
- case 'popgroup':
- if (this.env.address_group_stack.length > 1) {
- this.env.address_group_stack.pop();
- this.reset_qsearch();
- this.list_contacts(props.source, this.env.address_group_stack[this.env.address_group_stack.length-1]);
- }
- break;
- case 'import-messages':
- var form = props || this.gui_objects.importform,
- importlock = this.set_busy(true, 'importwait');
- $('input[name="_unlock"]', form).val(importlock);
- if (!(flag = this.upload_file(form, 'import'))) {
- this.set_busy(false, null, importlock);
- if (flag !== false)
- alert(this.get_label('selectimportfile'));
- aborted = true;
- }
- break;
- case 'import':
- if (this.env.action == 'import' && this.gui_objects.importform) {
- var file = document.getElementById('rcmimportfile');
- if (file && !file.value) {
- alert(this.get_label('selectimportfile'));
- aborted = true;
- break;
- }
- this.gui_objects.importform.submit();
- this.set_busy(true, 'importwait');
- this.lock_form(this.gui_objects.importform, true);
- }
- else
- this.goto_url('import', (this.env.source ? '_target='+urlencode(this.env.source)+'&' : ''));
- break;
- case 'export':
- if (this.contact_list.rowcount > 0) {
- this.goto_url('export', { _source: this.env.source, _gid: this.env.group, _search: this.env.search_request });
- }
- break;
- case 'export-selected':
- if (this.contact_list.rowcount > 0) {
- this.goto_url('export', { _source: this.env.source, _gid: this.env.group, _cid: this.contact_list.get_selection().join(',') });
- }
- break;
- case 'upload-photo':
- this.upload_contact_photo(props || this.gui_objects.uploadform);
- break;
- case 'delete-photo':
- this.replace_contact_photo('-del-');
- break;
- // user settings commands
- case 'preferences':
- case 'identities':
- case 'responses':
- case 'folders':
- this.goto_url('settings/' + command);
- break;
- case 'undo':
- this.http_request('undo', '', this.display_message('', 'loading'));
- break;
- // unified command call (command name == function name)
- default:
- var func = command.replace(/-/g, '_');
- if (this[func] && typeof this[func] === 'function') {
- ret = this[func](props, obj, event);
- }
- break;
- }
- if (!aborted && this.triggerEvent('after'+command, props) === false)
- ret = false;
- this.triggerEvent('actionafter', { props:props, action:command, aborted:aborted });
- return ret === false ? false : obj ? false : true;
- };
- // set command(s) enabled or disabled
- this.enable_command = function()
- {
- var i, n, args = Array.prototype.slice.call(arguments),
- enable = args.pop(), cmd;
- for (n=0; n<args.length; n++) {
- cmd = args[n];
- // argument of type array
- if (typeof cmd === 'string') {
- this.commands[cmd] = enable;
- this.set_button(cmd, (enable ? 'act' : 'pas'));
- this.triggerEvent('enable-command', {command: cmd, status: enable});
- }
- // push array elements into commands array
- else {
- for (i in cmd)
- args.push(cmd[i]);
- }
- }
- };
- this.command_enabled = function(cmd)
- {
- return this.commands[cmd];
- };
- // lock/unlock interface
- this.set_busy = function(a, message, id)
- {
- if (a && message) {
- var msg = this.get_label(message);
- if (msg == message)
- msg = 'Loading...';
- id = this.display_message(msg, 'loading');
- }
- else if (!a && id) {
- this.hide_message(id);
- }
- this.busy = a;
- //document.body.style.cursor = a ? 'wait' : 'default';
- if (this.gui_objects.editform)
- this.lock_form(this.gui_objects.editform, a);
- return id;
- };
- // return a localized string
- this.get_label = function(name, domain)
- {
- if (domain && this.labels[domain+'.'+name])
- return this.labels[domain+'.'+name];
- else if (this.labels[name])
- return this.labels[name];
- else
- return name;
- };
- // alias for convenience reasons
- this.gettext = this.get_label;
- // switch to another application task
- this.switch_task = function(task)
- {
- if (this.task === task && task != 'mail')
- return;
- var url = this.get_task_url(task);
- if (task == 'mail')
- url += '&_mbox=INBOX';
- else if (task == 'logout' && !this.env.server_error)
- this.clear_compose_data();
- this.redirect(url);
- };
- this.get_task_url = function(task, url)
- {
- if (!url)
- url = this.env.comm_path;
- return url.replace(/_task=[a-z0-9_-]+/i, '_task='+task);
- };
- this.reload = function(delay)
- {
- if (this.is_framed())
- parent.rcmail.reload(delay);
- else if (delay)
- setTimeout(function() { ref.reload(); }, delay);
- else if (window.location)
- location.href = this.env.comm_path + (this.env.action ? '&_action='+this.env.action : '');
- };
- // Add variable to GET string, replace old value if exists
- this.add_url = function(url, name, value)
- {
- value = urlencode(value);
- if (/(\?.*)$/.test(url)) {
- var urldata = RegExp.$1,
- datax = RegExp('((\\?|&)'+RegExp.escape(name)+'=[^&]*)');
- if (datax.test(urldata)) {
- urldata = urldata.replace(datax, RegExp.$2 + name + '=' + value);
- }
- else
- urldata += '&' + name + '=' + value
- return url.replace(/(\?.*)$/, urldata);
- }
- return url + '?' + name + '=' + value;
- };
- this.is_framed = function()
- {
- return (this.env.framed && parent.rcmail && parent.rcmail != this && parent.rcmail.command);
- };
- this.save_pref = function(prop)
- {
- var request = {_name: prop.name, _value: prop.value};
- if (prop.session)
- request._session = prop.session;
- if (prop.env)
- this.env[prop.env] = prop.value;
- this.http_post('save-pref', req…