summaryrefslogtreecommitdiff
path: root/program/js/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'program/js/app.js')
-rw-r--r--program/js/app.js1268
1 files changed, 774 insertions, 494 deletions
diff --git a/program/js/app.js b/program/js/app.js
index b88ed28c3..2a6b9ef78 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -1,23 +1,37 @@
-/*
- +-----------------------------------------------------------------------+
- | Roundcube Webmail Client Script |
- | |
- | This file is part of the Roundcube Webmail client |
- | Copyright (C) 2005-2013, The Roundcube Dev Team |
- | Copyright (C) 2011-2013, Kolab Systems AG |
- | |
- | Licensed under the GNU General Public License version 3 or |
- | any later version with exceptions for skins & plugins. |
- | See the README file for a full license statement. |
- | |
- +-----------------------------------------------------------------------+
- | Authors: Thomas Bruederli <roundcube@gmail.com> |
- | Aleksander 'A.L.E.C' Machniak <alec@alec.pl> |
- | Charles McNulty <charles@charlesmcnulty.com> |
- +-----------------------------------------------------------------------+
- | Requires: jquery.js, common.js, list.js |
- +-----------------------------------------------------------------------+
-*/
+/**
+ * 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()
{
@@ -31,6 +45,7 @@ function rcube_webmail()
this.onloads = [];
this.messages = {};
this.group2expand = {};
+ this.http_request_jobs = {};
// webmail client settings
this.dblclick_time = 500;
@@ -62,7 +77,7 @@ function rcube_webmail()
});
// unload fix
- $(window).bind('beforeunload', function() { rcmail.unload = true; });
+ $(window).bind('beforeunload', function() { ref.unload = true; });
// set environment variable(s)
this.set_env = function(p, value)
@@ -138,11 +153,11 @@ function rcube_webmail()
// initialize webmail client
this.init = function()
{
- var n, p = this;
+ var n;
this.task = this.env.task;
// check browser
- if (!bw.dom || !bw.xmlhttp_test() || (bw.mz && bw.vendver < 1.9)) {
+ 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;
}
@@ -199,28 +214,34 @@ function rcube_webmail()
column_movable:this.env.col_movable, dblclick_time:this.dblclick_time
});
this.message_list
- .addEventListener('initrow', function(o) { p.init_message_row(o); })
- .addEventListener('dblclick', function(o) { p.msglist_dbl_click(o); })
- .addEventListener('click', function(o) { p.msglist_click(o); })
- .addEventListener('keypress', function(o) { p.msglist_keypress(o); })
- .addEventListener('select', function(o) { p.msglist_select(o); })
- .addEventListener('dragstart', function(o) { p.drag_start(o); })
- .addEventListener('dragmove', function(e) { p.drag_move(e); })
- .addEventListener('dragend', function(e) { p.drag_end(e); })
- .addEventListener('expandcollapse', function(o) { p.msglist_expand(o); })
- .addEventListener('column_replace', function(o) { p.msglist_set_coltypes(o); })
- .addEventListener('listupdate', function(o) { p.triggerEvent('listupdate', o); })
+ .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();
- document.onmouseup = function(e){ return p.doc_mouse_up(e); };
- this.gui_objects.messagelist.parentNode.onmousedown = function(e){ return p.click_on_list(e); };
+ // 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);
+ });
+
+ document.onmouseup = function(e){ return ref.doc_mouse_up(e); };
+ this.gui_objects.messagelist.parentNode.onmousedown = function(e){ return ref.click_on_list(e); };
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() { rcmail.message_list.blur(); });
+ $(this.gui_objects.qsearchbox).val(this.env.search_text).focusin(function() { ref.message_list.blur(); });
}
this.set_button_titles();
@@ -288,7 +309,7 @@ function rcube_webmail()
}
}
- document.onmouseup = function(e){ return p.doc_mouse_up(e); };
+ document.onmouseup = function(e){ return ref.doc_mouse_up(e); };
// init message compose form
this.init_messageform();
@@ -315,7 +336,7 @@ function rcube_webmail()
this.contact_list = new rcube_list_widget(this.gui_objects.contactslist,
{ multiselect:true, draggable:false, keyboard:false });
this.contact_list
- .addEventListener('initrow', function(o) { p.triggerEvent('insertrow', { cid:o.uid, row:o }); })
+ .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('to'); })
.init();
@@ -356,21 +377,21 @@ function rcube_webmail()
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) { p.triggerEvent('insertrow', { cid:o.uid, row:o }); })
- .addEventListener('keypress', function(o) { p.contactlist_keypress(o); })
- .addEventListener('select', function(o) { p.contactlist_select(o); })
- .addEventListener('dragstart', function(o) { p.drag_start(o); })
- .addEventListener('dragmove', function(e) { p.drag_move(e); })
- .addEventListener('dragend', function(e) { p.drag_end(e); })
+ .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();
if (this.env.cid)
this.contact_list.highlight_row(this.env.cid);
- this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return p.click_on_list(e); };
- document.onmouseup = function(e){ return p.doc_mouse_up(e); };
+ this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return ref.click_on_list(e); };
+ document.onmouseup = function(e){ return ref.doc_mouse_up(e); };
- $(this.gui_objects.qsearchbox).focusin(function() { rcmail.contact_list.blur(); });
+ $(this.gui_objects.qsearchbox).focusin(function() { ref.contact_list.blur(); });
this.update_group_commands();
this.command('list');
@@ -422,7 +443,7 @@ function rcube_webmail()
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); })
+ .addEventListener('select', function(o) { ref.identity_select(o); })
.init()
.focus();
@@ -432,7 +453,7 @@ function rcube_webmail()
else if (this.gui_objects.sectionslist) {
this.sections_list = new rcube_list_widget(this.gui_objects.sectionslist, {multiselect:false, draggable:false, keyboard:false});
this.sections_list
- .addEventListener('select', function(o) { p.section_select(o); })
+ .addEventListener('select', function(o) { ref.section_select(o); })
.init()
.focus();
}
@@ -444,10 +465,10 @@ function rcube_webmail()
this.responses_list
.addEventListener('select', function(list) {
var win, id = list.get_single_selection();
- p.enable_command('delete', !!id && $.inArray(id, p.env.readonly_responses) < 0);
- if (id && (win = p.get_frame_window(p.env.contentframe))) {
- p.set_busy(true);
- p.location_href({ _action:'edit-response', _key:id, _framed:1 }, win);
+ 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()
@@ -458,7 +479,7 @@ function rcube_webmail()
case 'login':
var input_user = $('#rcmloginuser');
- input_user.bind('keyup', function(e){ return rcmail.login_user_keyup(e); });
+ input_user.bind('keyup', function(e){ return ref.login_user_keyup(e); });
if (input_user.val() == '')
input_user.focus();
@@ -466,7 +487,7 @@ function rcube_webmail()
$('#rcmloginpwd').focus();
// detect client timezone
- if (window.jstz && !bw.ie6) {
+ if (window.jstz) {
var timezone = jstz.determine();
if (timezone.name())
$('#rcmlogintz').val(timezone.name());
@@ -478,8 +499,8 @@ function rcube_webmail()
// display 'loading' message on form submit, lock submit button
$('form').submit(function () {
$('input[type=submit]', this).prop('disabled', true);
- rcmail.clear_messages();
- rcmail.display_message('', 'loading');
+ ref.clear_messages();
+ ref.display_message('', 'loading');
});
this.enable_command('login', true);
@@ -507,23 +528,19 @@ function rcube_webmail()
if (this.pending_message)
this.display_message(this.pending_message[0], this.pending_message[1], this.pending_message[2]);
- // map implicit containers
- if (this.gui_objects.folderlist) {
- this.gui_containers.foldertray = $(this.gui_objects.folderlist);
-
- // init treelist widget
- if (window.rcube_treelist_widget) {
- this.treelist = new rcube_treelist_widget(this.gui_objects.folderlist, {
+ // init treelist widget
+ if (this.gui_objects.folderlist && window.rcube_treelist_widget) {
+ this.treelist = new rcube_treelist_widget(this.gui_objects.folderlist, {
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' }) });
- }
+ });
+
+ 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)
@@ -564,12 +581,13 @@ function rcube_webmail()
// execute a specific command on the web client
this.command = function(command, props, obj, event)
{
- var ret, uid, cid, url, flag;
+ var ret, uid, cid, url, flag, aborted = false;
if (obj && obj.blur)
obj.blur();
- if (this.busy)
+ // 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)
@@ -595,6 +613,8 @@ function rcube_webmail()
this.remove_compose_data(this.env.compose_id);
}
+ this.last_command = command;
+
// process external commands
if (typeof this.command_handlers[command] === 'function') {
ret = this.command_handlers[command](props, obj);
@@ -650,11 +670,16 @@ function rcube_webmail()
var form = this.gui_objects.messageform,
win = this.open_window('');
- this.save_compose_form_local();
- $("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();
+ if (win) {
+ this.save_compose_form_local();
+ $("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.display_message(this.get_label('windowopenerror'), 'error');
+ }
}
else {
this.open_window(this.env.permaurl, true);
@@ -684,7 +709,7 @@ function rcube_webmail()
case 'open':
if (uid = this.get_single_uid()) {
- obj.href = this.url('show', {_mbox: this.env.mailbox, _uid: uid});
+ obj.href = this.url('show', {_mbox: this.get_message_mailbox(uid), _uid: uid});
return true;
}
break;
@@ -695,10 +720,12 @@ function rcube_webmail()
break;
case 'list':
- if (props && props != '')
+ if (props && props != '') {
this.reset_qsearch();
- if (this.env.action == 'compose' && this.env.extwin)
+ }
+ if (this.env.action == 'compose' && this.env.extwin) {
window.close();
+ }
else if (this.task == 'mail') {
this.list_mailbox(props);
this.set_button_titles();
@@ -707,6 +734,10 @@ function rcube_webmail()
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;
@@ -787,9 +818,9 @@ function rcube_webmail()
this.load_contact(cid, 'edit');
else if (this.task == 'settings' && props)
this.load_identity(props, 'edit-identity');
- else if (this.task == 'mail' && (cid = this.get_single_uid())) {
- url = { _mbox: this.env.mailbox };
- url[this.env.mailbox == this.env.drafts_mailbox && props != 'new' ? '_draft_uid' : '_uid'] = cid;
+ 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;
@@ -1049,7 +1080,11 @@ function rcube_webmail()
// Reset the auto-save timer
clearTimeout(this.save_timer);
- this.upload_file(props || this.gui_objects.uploadform, 'upload');
+ 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':
@@ -1069,7 +1104,7 @@ function rcube_webmail()
case 'reply-list':
case 'reply':
if (uid = this.get_single_uid()) {
- url = {_reply_uid: uid, _mbox: this.env.mailbox};
+ 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');
@@ -1085,7 +1120,7 @@ function rcube_webmail()
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 };
+ 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);
@@ -1097,7 +1132,7 @@ function rcube_webmail()
this.gui_objects.messagepartframe.contentWindow.print();
}
else if (uid = this.get_single_uid()) {
- ref.printwin = this.open_window(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(this.env.safemode ? '&_safe=1' : ''), true, true);
+ this.printwin = this.open_window(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+urlencode(this.get_message_mailbox(uid))+(this.env.safemode ? '&_safe=1' : ''), true, true);
if (this.printwin) {
if (this.env.action != 'show')
this.mark_message('read', uid);
@@ -1114,8 +1149,9 @@ function rcube_webmail()
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.env.mailbox, _save: 1 });
+ else if (uid = this.get_single_uid()) {
+ this.goto_url('viewsource', { _uid: uid, _mbox: this.get_message_mailbox(uid), _save: 1 });
+ }
break;
// quicksearch
@@ -1171,9 +1207,17 @@ function rcube_webmail()
break;
case 'import-messages':
- var form = props || this.gui_objects.importform;
- $('input[name="_unlock"]', form).val(this.set_busy(true, 'importwait'));
- this.upload_file(form, 'import');
+ 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':
@@ -1181,6 +1225,7 @@ function rcube_webmail()
var file = document.getElementById('rcmimportfile');
if (file && !file.value) {
alert(this.get_label('selectimportfile'));
+ aborted = true;
break;
}
this.gui_objects.importform.submit();
@@ -1232,9 +1277,9 @@ function rcube_webmail()
break;
}
- if (this.triggerEvent('after'+command, props) === false)
+ if (!aborted && this.triggerEvent('after'+command, props) === false)
ret = false;
- this.triggerEvent('actionafter', {props:props, action:command});
+ this.triggerEvent('actionafter', { props:props, action:command, aborted:aborted });
return ret === false ? false : obj ? false : true;
};
@@ -1261,6 +1306,11 @@ function rcube_webmail()
}
};
+ this.command_enabled = function(cmd)
+ {
+ return this.commands[cmd];
+ }
+
// lock/unlock interface
this.set_busy = function(a, message, id)
{
@@ -1326,7 +1376,7 @@ function rcube_webmail()
if (this.is_framed())
parent.rcmail.reload(delay);
else if (delay)
- setTimeout(function(){ rcmail.reload(); }, delay);
+ setTimeout(function() { ref.reload(); }, delay);
else if (window.location)
location.href = this.env.comm_path + (this.env.action ? '&_action='+this.env.action : '');
};
@@ -1434,11 +1484,31 @@ function rcube_webmail()
this.drag_end = function(e)
{
- this.drag_active = false;
- this.env.last_folder_target = null;
+ var list, model;
if (this.treelist)
this.treelist.drag_end();
+
+ // execute drag & drop action when mouse was released
+ if (list = this.message_list)
+ model = this.env.mailboxes;
+ else if (list = this.contact_list)
+ model = this.env.contactfolders;
+
+ if (this.drag_active && model && this.env.last_folder_target) {
+ var target = model[this.env.last_folder_target];
+ list.draglayer.hide();
+
+ if (this.contact_list) {
+ if (!this.contacts_drag_menu(e, target))
+ this.command('move', target);
+ }
+ else if (!this.drag_menu(e, target))
+ this.command('move', target);
+ }
+
+ this.drag_active = false;
+ this.env.last_folder_target = null;
};
this.drag_move = function(e)
@@ -1499,38 +1569,16 @@ function rcube_webmail()
this.doc_mouse_up = function(e)
{
- var model, list, id;
+ var list, id;
// ignore event if jquery UI dialog is open
if ($(rcube_event.get_target(e)).closest('.ui-dialog, .ui-widget-overlay').length)
return;
- if (list = this.message_list)
- model = this.env.mailboxes;
- else if (list = this.contact_list)
- model = this.env.contactfolders;
- else if (this.ksearch_value)
- this.ksearch_blur();
-
+ list = this.message_list || this.contact_list;
if (list && !rcube_mouse_is_over(e, list.list.parentNode))
list.blur();
- // handle mouse release when dragging
- if (this.drag_active && model && this.env.last_folder_target) {
- var target = model[this.env.last_folder_target];
-
- this.env.last_folder_target = null;
- list.draglayer.hide();
- this.drag_end(e);
-
- if (this.contact_list) {
- if (!this.contacts_drag_menu(e, target))
- this.command('move', target);
- }
- else if (!this.drag_menu(e, target))
- this.command('move', target);
- }
-
// reset 'pressed' buttons
if (this.buttons_sel) {
for (id in this.buttons_sel)
@@ -1618,7 +1666,7 @@ function rcube_webmail()
var uid = list.get_single_selection();
- if (uid && this.env.mailbox == this.env.drafts_mailbox)
+ if (uid && (this.env.messages[uid].mbox || this.env.mailbox) == this.env.drafts_mailbox)
this.open_compose_step({ _draft_uid: uid, _mbox: this.env.mailbox });
else if (uid)
this.show_message(uid, false, false);
@@ -1659,28 +1707,30 @@ function rcube_webmail()
{
var i, found, name, cols = list.thead.rows[0].cells;
- this.env.coltypes = [];
+ this.env.listcols = [];
for (i=0; i<cols.length; i++)
if (cols[i].id && cols[i].id.startsWith('rcm')) {
name = cols[i].id.slice(3);
- this.env.coltypes.push(name);
+ this.env.listcols.push(name);
}
- if ((found = $.inArray('flag', this.env.coltypes)) >= 0)
+ if ((found = $.inArray('flag', this.env.listcols)) >= 0)
this.env.flagged_col = found;
- if ((found = $.inArray('subject', this.env.coltypes)) >= 0)
+ if ((found = $.inArray('subject', this.env.listcols)) >= 0)
this.env.subject_col = found;
- this.command('save-pref', { name: 'list_cols', value: this.env.coltypes, session: 'list_attrib/columns' });
+ this.command('save-pref', { name: 'list_cols', value: this.env.listcols, session: 'list_attrib/columns' });
};
this.check_droptarget = function(id)
{
switch (this.task) {
case 'mail':
- return (this.env.mailboxes[id] && this.env.mailboxes[id].id != this.env.mailbox && !this.env.mailboxes[id].virtual) ? 1 : 0;
+ return (this.env.mailboxes[id]
+ && !this.env.mailboxes[id].virtual
+ && (this.env.mailboxes[id].id != this.env.mailbox || this.is_multifolder_listing())) ? 1 : 0;
case 'settings':
return id != this.env.mailbox ? 1 : 0;
@@ -1737,7 +1787,7 @@ function rcube_webmail()
this.triggerEvent('openwindow', { url:url, handle:extwin });
// focus window, delayed to bring to front
- window.setTimeout(function() { extwin && extwin.focus(); }, 10);
+ setTimeout(function() { extwin && extwin.focus(); }, 10);
return extwin;
};
@@ -1749,31 +1799,31 @@ function rcube_webmail()
this.init_message_row = function(row)
{
- var i, fn = {}, self = this, uid = row.uid,
- status_icon = (this.env.status_col != null ? 'status' : 'msg') + 'icn' + row.uid;
+ var i, fn = {}, uid = row.uid,
+ status_icon = (this.env.status_col != null ? 'status' : 'msg') + 'icn' + row.id;
if (uid && this.env.messages[uid])
$.extend(row, this.env.messages[uid]);
// set eventhandler to status icon
if (row.icon = document.getElementById(status_icon)) {
- fn.icon = function(e) { self.command('toggle_status', uid); };
+ fn.icon = function(e) { ref.command('toggle_status', uid); };
}
// save message icon position too
if (this.env.status_col != null)
- row.msgicon = document.getElementById('msgicn'+row.uid);
+ row.msgicon = document.getElementById('msgicn'+row.id);
else
row.msgicon = row.icon;
// set eventhandler to flag icon
- if (this.env.flagged_col != null && (row.flagicon = document.getElementById('flagicn'+row.uid))) {
- fn.flagicon = function(e) { self.command('toggle_flag', uid); };
+ if (this.env.flagged_col != null && (row.flagicon = document.getElementById('flagicn'+row.id))) {
+ fn.flagicon = function(e) { ref.command('toggle_flag', uid); };
}
// set event handler to thread expand/collapse icon
- if (!row.depth && row.has_children && (row.expando = document.getElementById('rcmexpando'+row.uid))) {
- fn.expando = function(e) { self.expand_message_row(e, uid); };
+ if (!row.depth && row.has_children && (row.expando = document.getElementById('rcmexpando'+row.id))) {
+ fn.expando = function(e) { ref.expand_message_row(e, uid); };
}
// attach events
@@ -1819,6 +1869,7 @@ function rcube_webmail()
selected: this.select_all_mode || this.message_list.in_selection(uid),
ml: flags.ml?1:0,
ctype: flags.ctype,
+ mbox: flags.mbox,
// flags from plugins
flags: flags.extra_flags
});
@@ -1828,12 +1879,13 @@ function rcube_webmail()
list = this.message_list,
rows = list.rows,
message = this.env.messages[uid],
+ msg_id = this.html_identifier(uid,true),
row_class = 'message'
+ (!flags.seen ? ' unread' : '')
+ (flags.deleted ? ' deleted' : '')
+ (flags.flagged ? ' flagged' : '')
+ (message.selected ? ' selected' : ''),
- row = { cols:[], style:{}, id:'rcmrow'+uid };
+ row = { cols:[], style:{}, id:'rcmrow'+msg_id, uid:uid };
// message status icons
css_class = 'msgicon';
@@ -1859,7 +1911,7 @@ function rcube_webmail()
if (this.env.threading) {
if (message.depth) {
// This assumes that div width is hardcoded to 15px,
- tree += '<span id="rcmtab' + uid + '" class="branch" style="width:' + (message.depth * 15) + 'px;">&nbsp;&nbsp;</span>';
+ tree += '<span id="rcmtab' + msg_id + '" class="branch" style="width:' + (message.depth * 15) + 'px;">&nbsp;&nbsp;</span>';
if ((rows[message.parent_uid] && rows[message.parent_uid].expanded === false)
|| ((this.env.autoexpand_threads == 0 || this.env.autoexpand_threads == 2) &&
@@ -1878,7 +1930,7 @@ function rcube_webmail()
message.expanded = true;
}
- expando = '<div id="rcmexpando' + uid + '" class="' + (message.expanded ? 'expanded' : 'collapsed') + '">&nbsp;&nbsp;</div>';
+ expando = '<div id="rcmexpando' + row.id + '" class="' + (message.expanded ? 'expanded' : 'collapsed') + '">&nbsp;&nbsp;</div>';
row_class += ' thread' + (message.expanded? ' expanded' : '');
}
@@ -1886,28 +1938,34 @@ function rcube_webmail()
row_class += ' unroot';
}
- tree += '<span id="msgicn'+uid+'" class="'+css_class+'">&nbsp;</span>';
+ tree += '<span id="msgicn'+row.id+'" class="'+css_class+'">&nbsp;</span>';
row.className = row_class;
- // build subject link
- if (!bw.ie && cols.subject) {
+ // build subject link
+ if (cols.subject) {
var action = flags.mbox == this.env.drafts_mailbox ? 'compose' : 'show';
var uid_param = flags.mbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid';
- cols.subject = '<a href="./?_task=mail&_action='+action+'&_mbox='+urlencode(flags.mbox)+'&'+uid_param+'='+uid+'"'+
- ' onclick="return rcube_event.cancel(event)" onmouseover="rcube_webmail.long_subject_title(this,'+(message.depth+1)+')">'+cols.subject+'</a>';
+ cols.subject = '<a href="./?_task=mail&_action='+action+'&_mbox='+urlencode(flags.mbox)+'&'+uid_param+'='+urlencode(uid)+'"'+
+ ' onclick="return rcube_event.cancel(event)" onmouseover="rcube_webmail.long_subject_title(this,'+(message.depth+1)+')"><span>'+cols.subject+'</span></a>';
}
// add each submitted col
- for (n in this.env.coltypes) {
- c = this.env.coltypes[n];
- col = { className: String(c).toLowerCase() };
+ for (n in this.env.listcols) {
+ c = this.env.listcols[n];
+ col = {className: String(c).toLowerCase(), events:{}};
+
+ if (this.env.coltypes[c] && this.env.coltypes[c].hidden) {
+ col.className += ' hidden';
+ }
if (c == 'flag') {
css_class = (flags.flagged ? 'flagged' : 'unflagged');
- html = '<span id="flagicn'+uid+'" class="'+css_class+'">&nbsp;</span>';
+ html = '<span id="flagicn'+row.id+'" class="'+css_class+'">&nbsp;</span>';
}
else if (c == 'attachment') {
- if (/application\/|multipart\/(m|signed)/.test(flags.ctype))
+ if (flags.attachmentClass)
+ html = '<span class="'+flags.attachmentClass+'">&nbsp;</span>';
+ else if (/application\/|multipart\/(m|signed)/.test(flags.ctype))
html = '<span class="attachment">&nbsp;</span>';
else if (/multipart\/report/.test(flags.ctype))
html = '<span class="report">&nbsp;</span>';
@@ -1923,16 +1981,13 @@ function rcube_webmail()
css_class = 'unreadchildren';
else
css_class = 'msgicon';
- html = '<span id="statusicn'+uid+'" class="'+css_class+'">&nbsp;</span>';
+ html = '<span id="statusicn'+row.id+'" class="'+css_class+'">&nbsp;</span>';
}
else if (c == 'threads')
html = expando;
else if (c == 'subject') {
- if (bw.ie) {
- col.onmouseover = function() { rcube_webmail.long_subject_title_ex(this, message.depth+1); };
- if (bw.ie8)
- tree = '<span></span>' + tree; // #1487821
- }
+ if (bw.ie)
+ col.events.mouseover = function() { rcube_webmail.long_subject_title_ex(this); };
html = tree + cols[c];
}
else if (c == 'priority') {
@@ -1941,6 +1996,9 @@ function rcube_webmail()
else
html = '&nbsp;';
}
+ else if (c == 'folder') {
+ html = '<span onmouseover="rcube_webmail.long_subject_title(this)">' + cols[c] + '<span>';
+ }
else
html = cols[c];
@@ -1990,7 +2048,7 @@ function rcube_webmail()
if (cols && cols.length) {
// make sure new columns are added at the end of the list
- var i, idx, name, newcols = [], oldcols = this.env.coltypes;
+ var i, idx, name, newcols = [], oldcols = this.env.listcols;
for (i=0; i<oldcols.length; i++) {
name = oldcols[i];
idx = $.inArray(name, cols);
@@ -2021,7 +2079,7 @@ function rcube_webmail()
var win, target = window,
action = preview ? 'preview': 'show',
- url = '&_action='+action+'&_uid='+id+'&_mbox='+urlencode(this.env.mailbox);
+ url = '&_action='+action+'&_uid='+id+'&_mbox='+urlencode(this.get_message_mailbox(id));
if (preview && (win = this.get_frame_window(this.env.contentframe))) {
target = win;
@@ -2138,7 +2196,7 @@ function rcube_webmail()
var lock = this.set_busy(true, 'checkingmail'),
params = this.check_recent_params();
- this.http_request('check-recent', params, lock);
+ this.http_post('check-recent', params, lock);
};
// list messages of a specific mailbox using filter
@@ -2150,11 +2208,20 @@ function rcube_webmail()
// reset vars
this.env.current_page = 1;
+ this.env.search_filter = filter;
this.http_request('search', this.search_params(false, filter), lock);
};
+ // reload the current message listing
+ this.refresh_list = function()
+ {
+ this.list_mailbox(this.env.mailbox, this.env.current_page || 1, null, { _clear:1 }, true);
+ if (this.message_list)
+ this.message_list.clear_selection();
+ };
+
// list messages of a specific mailbox
- this.list_mailbox = function(mbox, page, sort, url)
+ this.list_mailbox = function(mbox, page, sort, url, update_only)
{
var win, target = window;
@@ -2179,15 +2246,17 @@ function rcube_webmail()
this.select_all_mode = false;
}
- // unselect selected messages and clear the list and message data
- this.clear_message_list();
+ if (!update_only) {
+ // unselect selected messages and clear the list and message data
+ this.clear_message_list();
- if (mbox != this.env.mailbox || (mbox == this.env.mailbox && !page && !sort))
- url._refresh = 1;
+ if (mbox != this.env.mailbox || (mbox == this.env.mailbox && !page && !sort))
+ url._refresh = 1;
- this.select_folder(mbox, '', true);
- this.unmark_folder(mbox, 'recent', '', true);
- this.env.mailbox = mbox;
+ this.select_folder(mbox, '', true);
+ this.unmark_folder(mbox, 'recent', '', true);
+ this.env.mailbox = mbox;
+ }
// load message list remotely
if (this.gui_objects.messagelist) {
@@ -2221,20 +2290,17 @@ function rcube_webmail()
};
// send remote request to load message list
- this.list_mailbox_remote = function(mbox, page, post_data)
+ this.list_mailbox_remote = function(mbox, page, url)
{
- // clear message list first
- this.message_list.clear();
-
var lock = this.set_busy(true, 'loading');
- if (typeof post_data != 'object')
- post_data = {};
- post_data._mbox = mbox;
+ if (typeof url != 'object')
+ url = {};
+ url._mbox = mbox;
if (page)
- post_data._page = page;
+ url._page = page;
- this.http_request('list', post_data, lock);
+ this.http_request('list', url, lock);
};
// removes messages that doesn't exists from list selection array
@@ -2386,7 +2452,7 @@ function rcube_webmail()
}
if (html)
- $('#rcmtab'+uid).html(html);
+ $('#rcmtab'+this.html_identifier(uid, true)).html(html);
};
// update parent in a thread
@@ -2450,17 +2516,17 @@ function rcube_webmail()
r.depth--; // move left
// reset width and clear the content of a tab, icons will be added later
- $('#rcmtab'+r.uid).width(r.depth * 15).html('');
+ $('#rcmtab'+r.id).width(r.depth * 15).html('');
if (!r.depth) { // a new root
count++; // increase roots count
r.parent_uid = 0;
if (r.has_children) {
// replace 'leaf' with 'collapsed'
- $('#rcmrow'+r.uid+' '+'.leaf:first')
- .attr('id', 'rcmexpando' + r.uid)
+ $('#'+r.id+' .leaf:first')
+ .attr('id', 'rcmexpando' + r.id)
.attr('class', (r.obj.style.display != 'none' ? 'expanded' : 'collapsed'))
- .bind('mousedown', {uid:r.uid, p:this},
- function(e) { return e.data.p.expand_message_row(e, e.data.uid); });
+ .bind('mousedown', {uid: r.uid},
+ function(e) { return ref.expand_message_row(e, e.data.uid); });
r.unread_children = 0;
roots.push(r);
@@ -2651,7 +2717,7 @@ function rcube_webmail()
return this.folder_selector(obj, function(folder) { ref.command('move', folder); });
// exit if current or no mailbox specified
- if (!mbox || mbox == this.env.mailbox)
+ if (!mbox || (mbox == this.env.mailbox && !this.is_multifolder_listing()))
return;
var lock = false, post_data = this.selection_post_data({_target_mbox: mbox});
@@ -2719,7 +2785,8 @@ function rcube_webmail()
// @private
this._with_selected_messages = function(action, post_data, lock)
{
- var count = 0, msg;
+ var count = 0, msg,
+ remove = (action == 'delete' || !this.is_multifolder_listing());
// update the list (remove rows, clear selection)
if (this.message_list) {
@@ -2736,10 +2803,11 @@ function rcube_webmail()
roots.push(root);
}
}
- this.message_list.remove_row(id, (this.env.display_next && n == selection.length-1));
+ if (remove)
+ this.message_list.remove_row(id, (this.env.display_next && n == selection.length-1));
}
// make sure there are no selected rows
- if (!this.env.display_next)
+ if (!this.env.display_next && remove)
this.message_list.clear_selection();
// update thread tree icons
for (n=0, len=roots.length; n<len; n++) {
@@ -2750,9 +2818,12 @@ function rcube_webmail()
if (count < 0)
post_data._count = (count*-1);
// remove threads from the end of the list
- else if (count > 0)
+ else if (count > 0 && remove)
this.delete_excessive_thread_rows();
+ if (!remove)
+ post_data._refresh = 1;
+
if (!lock) {
msg = action == 'move' ? 'movingmessage' : 'deletingmessage';
lock = this.display_message(this.get_label(msg), 'loading');
@@ -2962,7 +3033,8 @@ function rcube_webmail()
var icn_src, uid, i, len,
rows = this.message_list ? this.message_list.rows : {};
- uids = String(uids).split(',');
+ if (typeof uids == 'string')
+ uids = uids.split(',');
for (i=0, len=uids.length; i<len; i++) {
uid = uids[i];
@@ -2975,7 +3047,7 @@ function rcube_webmail()
// with select_all mode checking
this.uids_to_list = function(uids)
{
- return this.select_all_mode ? '*' : uids.join(',');
+ return this.select_all_mode ? '*' : (uids.length <= 1 ? uids.join(',') : uids);
};
// Sets title of the delete button
@@ -3048,8 +3120,8 @@ function rcube_webmail()
// handler for keyboard events on the _user field
this.login_user_keyup = function(e)
{
- var key = rcube_event.get_keycode(e);
- var passwd = $('#rcmloginpwd');
+ var key = rcube_event.get_keycode(e),
+ passwd = $('#rcmloginpwd');
// enter
if (key == 13 && passwd.length && !passwd.val()) {
@@ -3096,7 +3168,12 @@ function rcube_webmail()
// close compose step in opener
if (opener_rc && opener_rc.env.action == 'compose') {
- setTimeout(function(){ opener.history.back(); }, 100);
+ setTimeout(function(){
+ if (opener.history.length > 1)
+ opener.history.back();
+ else
+ opener_rc.redirect(opener_rc.get_task_url('mail'));
+ }, 100);
this.env.opened_extwin = true;
}
@@ -3141,6 +3218,10 @@ function rcube_webmail()
if (this.env.draft_id && formdata.draft_id && formdata.draft_id != this.env.draft_id) {
continue;
}
+ // skip records on reply
+ if (this.env.reply_msgid && formdata.reply_msgid != this.env.reply_msgid) {
+ continue;
+ }
// show dialog asking to restore the message
if (formdata.changed && formdata.session != this.env.session_id) {
this.show_popup_dialog(
@@ -3358,33 +3439,72 @@ function rcube_webmail()
{
this.stop_spellchecking();
- var flag = $('[name="_is_html"]');
+ var ed, curr, content, result,
+ // these non-printable chars are not removed on text2html and html2text
+ // we can use them as temp signature replacement
+ sig_mark = "\u0002\u0003",
+ input = $('#' + props.id),
+ signature = this.env.identity ? this.env.signatures[this.env.identity] : null,
+ is_sig = signature && signature.text && signature.text.length > 1;
if (props.mode == 'html') {
- this.plain2html($('#'+props.id).val(), props.id);
- flag.val(1);
- tinymce.execCommand('mceAddEditor', false, props.id);
+ content = input.val();
+
+ // replace current text signature with temp mark
+ if (is_sig)
+ content = content.replace(signature.text, sig_mark);
- if (this.env.default_font)
- setTimeout(function() {
- $(tinymce.get(props.id).getBody()).css('font-family', rcmail.env.default_font);
- }, 500);
+ // convert to html
+ result = this.plain2html(content, function(data) {
+ // replace signature mark with html version of the signature
+ if (is_sig)
+ data = data.replace(sig_mark, '<div id="_rc_sig">' + signature.html + '</div>');
+
+ input.val(data);
+ tinyMCE.execCommand('mceAddEditor', false, props.id);
+
+ if (ref.env.default_font)
+ setTimeout(function() {
+ $(tinyMCE.get(props.id).getBody()).css('font-family', ref.env.default_font);
+ }, 500);
+ });
}
else {
- var thisMCE = tinymce.get(props.id), existingHtml;
-
- if (existingHtml = thisMCE.getContent()) {
- if (!confirm(this.get_label('editorwarning'))) {
- return false;
- }
- this.html2plain(existingHtml, props.id);
+ ed = tinyMCE.get(props.id);
+
+ if (is_sig) {
+ // get current version of signature, we'll need it in
+ // case of html2text conversion abort
+ if (curr = ed.dom.get('_rc_sig'))
+ curr = curr.innerHTML;
+
+ // replace current signature with some non-printable characters
+ // we use non-printable characters, because this replacement
+ // is visible to the user
+ // doing this after getContent() would be hard
+ ed.dom.setHTML('_rc_sig', sig_mark);
}
- flag.val(0);
- tinymce.execCommand('mceRemoveEditor', false, props.id);
+ // get html content
+ content = ed.getContent();
+
+ // convert html to text
+ result = this.html2plain(content, function(data) {
+ tinyMCE.execCommand('mceRemoveEditor', false, props.id);
+
+ // replace signture mark with text version of the signature
+ if (is_sig)
+ data = data.replace(sig_mark, "\n" + signature.text);
+
+ input.val(data);
+ });
+
+ // bring back current signature
+ if (!result && curr)
+ ed.dom.setHTML('_rc_sig', curr);
}
- return true;
+ return result;
};
this.insert_response = function(key)
@@ -3397,7 +3517,7 @@ function rcube_webmail()
if ($("input[name='_is_html']").val() == '1') {
var editor = tinymce.get(this.env.composebody);
editor.getWin().focus(); // correct focus in IE & Chrome
- editor.selection.setContent(insert, { format:'text' });
+ editor.selection.setContent(this.quote_html(insert).replace(/\r?\n/g, '<br/>'), { format:'text' });
}
// replace selection in compose textarea
else {
@@ -3560,8 +3680,8 @@ function rcube_webmail()
else if ((ed = this.env.spellcheck) && ed.state)
active = ed.state != 'ready' && ed.state != 'no_error_found';
- if (rcmail.buttons.spellcheck)
- $('#'+rcmail.buttons.spellcheck[0].id)[active ? 'addClass' : 'removeClass']('selected');
+ if (this.buttons.spellcheck)
+ $('#'+this.buttons.spellcheck[0].id)[active ? 'addClass' : 'removeClass']('selected');
return active;
};
@@ -3619,14 +3739,28 @@ function rcube_webmail()
this.env.draft_id = id;
$("input[name='_draft_saveid']").val(id);
- this.remove_compose_data(this.env.compose_id);
+ // reset history of hidden iframe used for saving draft (#1489643)
+ // but don't do this on timer-triggered draft-autosaving (#1489789)
+ if (window.frames['savetarget'] && window.frames['savetarget'].history && !this.draft_autosave_submit) {
+ window.frames['savetarget'].history.back();
+ }
+
+ this.draft_autosave_submit = false;
}
+
+ // always remove local copy upon saving as draft
+ this.remove_compose_data(this.env.compose_id);
};
this.auto_save_start = function()
{
- if (this.env.draft_autosave)
- this.save_timer = setTimeout(function(){ ref.command("savedraft"); }, this.env.draft_autosave * 1000);
+ if (this.env.draft_autosave) {
+ this.draft_autosave_submit = false;
+ this.save_timer = setTimeout(function(){
+ ref.draft_autosave_submit = true; // set auto-saved flag (#1489789)
+ ref.command("savedraft");
+ }, this.env.draft_autosave * 1000);
+ }
// save compose form content to local storage every 5 seconds
if (!this.local_save_timer && window.localStorage) {
@@ -3649,7 +3783,7 @@ function rcube_webmail()
this.compose_field_hash = function(save)
{
// check input fields
- var ed, i, val, str = '', hash_fields = ['to', 'cc', 'bcc', 'subject'];
+ var ed, i, id, val, str = '', hash_fields = ['to', 'cc', 'bcc', 'subject'];
for (i=0; i<hash_fields.length; i++)
if (val = $('[name="_' + hash_fields[i] + '"]').val())
@@ -3661,8 +3795,8 @@ function rcube_webmail()
str += $("[name='_message']").val();
if (this.env.attachments)
- for (var upload_id in this.env.attachments)
- str += upload_id;
+ for (id in this.env.attachments)
+ str += id;
if (save)
this.cmp_hash = str;
@@ -3684,6 +3818,9 @@ function rcube_webmail()
if (this.env.draft_id) {
formdata.draft_id = this.env.draft_id;
}
+ if (this.env.reply_msgid) {
+ formdata.reply_msgid = this.env.reply_msgid;
+ }
$('input, select, textarea', this.gui_objects.messageform).each(function(i, elem) {
switch (elem.tagName.toLowerCase()) {
@@ -3769,9 +3906,9 @@ function rcube_webmail()
this.clear_compose_data = function()
{
if (window.localStorage) {
- var index = this.local_storage_get_item('compose.index', []);
+ var i, index = this.local_storage_get_item('compose.index', []);
- for (var i=0; i < index.length; i++) {
+ for (i=0; i < index.length; i++) {
this.local_storage_remove_item('compose.' + index[i]);
}
this.local_storage_remove_item('compose.index');
@@ -3942,7 +4079,7 @@ function rcube_webmail()
this.upload_file = function(form, action)
{
if (!form)
- return false;
+ return;
// count files and size on capable browser
var size = 0, numfiles = 0;
@@ -3963,7 +4100,7 @@ function rcube_webmail()
if (numfiles) {
if (this.env.max_filesize && this.env.filesizeerror && size > this.env.max_filesize) {
this.display_message(this.env.filesizeerror, 'error');
- return;
+ return false;
}
var frame_name = this.async_upload_form(form, action || 'upload', function(e) {
@@ -3974,17 +4111,17 @@ function rcube_webmail()
} else if (this.contentWindow) {
d = this.contentWindow.document;
}
- content = d.childNodes[0].innerHTML;
+ content = d.childNodes[1].innerHTML;
} catch (err) {}
- if (!content.match(/add2attachment/) && (!bw.opera || (rcmail.env.uploadframe && rcmail.env.uploadframe == e.data.ts))) {
+ if (!content.match(/add2attachment/) && (!bw.opera || (ref.env.uploadframe && ref.env.uploadframe == e.data.ts))) {
if (!content.match(/display_message/))
- rcmail.display_message(rcmail.get_label('fileuploaderror'), 'error');
- rcmail.remove_from_attachment_list(e.data.ts);
+ ref.display_message(ref.get_label('fileuploaderror'), 'error');
+ ref.remove_from_attachment_list(e.data.ts);
}
// Opera hack: handle double onload
if (bw.opera)
- rcmail.env.uploadframe = e.data.ts;
+ ref.env.uploadframe = e.data.ts;
});
// display upload indicator and cancel button
@@ -3997,11 +4134,11 @@ function rcube_webmail()
if (this.env.upload_progress_time) {
this.upload_progress_start('upload', ts);
}
- }
- // set reference to the form object
- this.gui_objects.attachmentform = form;
- return true;
+ // set reference to the form object
+ this.gui_objects.attachmentform = form;
+ return true;
+ }
};
// add file name to attachment list
@@ -4014,8 +4151,8 @@ function rcube_webmail()
if (!this.gui_objects.attachmentlist)
return false;
- if (!att.complete && ref.env.loadingicon)
- att.html = '<img src="'+ref.env.loadingicon+'" alt="" class="uploading" />' + att.html;
+ if (!att.complete && this.env.loadingicon)
+ att.html = '<img src="'+this.env.loadingicon+'" alt="" class="uploading" />' + att.html;
if (!att.complete && att.frame)
att.html = '<a title="'+this.get_label('cancel')+'" onclick="return rcmail.cancel_attachment_upload(\''+name+'\', \''+att.frame+'\');" href="#cancelupload" class="cancelupload">'
@@ -4026,7 +4163,7 @@ function rcube_webmail()
li.attr('id', name)
.addClass(att.classname)
.html(att.html)
- .on('mouseover', function() { rcube_webmail.long_subject_title_ex(this, 0); });
+ .on('mouseover', function() { rcube_webmail.long_subject_title_ex(this); });
// replace indicator's li
if (upload_id && (indicator = document.getElementById(upload_id))) {
@@ -4046,8 +4183,10 @@ function rcube_webmail()
this.remove_from_attachment_list = function(name)
{
- delete this.env.attachments[name];
- $('#'+name).remove();
+ if (this.env.attachments) {
+ delete this.env.attachments[name];
+ $('#'+name).remove();
+ }
};
this.remove_attachment = function(name)
@@ -4070,7 +4209,7 @@ function rcube_webmail()
this.upload_progress_start = function(action, name)
{
- setTimeout(function() { rcmail.http_request(action, {_progress: name}); },
+ setTimeout(function() { ref.http_request(action, {_progress: name}); },
this.env.upload_progress_time * 1000);
};
@@ -4101,7 +4240,8 @@ function rcube_webmail()
{
if (value != '') {
var r, lock = this.set_busy(true, 'searching'),
- url = this.search_params(value);
+ url = this.search_params(value),
+ action = this.env.action == 'compose' && this.contact_list ? 'search-contacts' : 'search';
if (this.message_list)
this.clear_message_list();
@@ -4116,11 +4256,26 @@ function rcube_webmail()
// reset vars
this.env.current_page = 1;
- var action = this.env.action == 'compose' && this.contact_list ? 'search-contacts' : 'search';
r = this.http_request(action, url, lock);
this.env.qsearch = {lock: lock, request: r};
+ this.enable_command('set-listmode', this.env.threads && (this.env.search_scope || 'base') == 'base');
+
+ return true;
}
+
+ return false;
+ };
+
+ this.continue_search = function(request_id)
+ {
+ var lock = this.set_busy(true, 'stillsearching');
+
+ setTimeout(function() {
+ var url = ref.search_params();
+ url._continue = request_id;
+ ref.env.qsearch = { lock: lock, request: ref.http_request('search', url, lock) };
+ }, 100);
};
// build URL params for search
@@ -4128,7 +4283,8 @@ function rcube_webmail()
{
var n, url = {}, mods_arr = [],
mods = this.env.search_mods,
- mbox = this.env.mailbox;
+ scope = this.env.search_scope || 'base',
+ mbox = scope == 'all' ? '*' : this.env.mailbox;
if (!filter && this.gui_objects.search_filter)
filter = this.gui_objects.search_filter.value;
@@ -4143,7 +4299,7 @@ function rcube_webmail()
url._q = search;
if (mods && this.message_list)
- mods = mods[mbox] ? mods[mbox] : mods['*'];
+ mods = mods[mbox] || mods['*'];
if (mods) {
for (n in mods)
@@ -4152,7 +4308,9 @@ function rcube_webmail()
}
}
- if (mbox)
+ if (scope)
+ url._scope = scope;
+ if (mbox && scope != 'all')
url._mbox = mbox;
return url;
@@ -4170,6 +4328,43 @@ function rcube_webmail()
this.env.qsearch = null;
this.env.search_request = null;
this.env.search_id = null;
+
+ this.enable_command('set-listmode', this.env.threads);
+ };
+
+ this.set_searchscope = function(scope)
+ {
+ var old = this.env.search_scope;
+ this.env.search_scope = scope;
+
+ // re-send search query with new scope
+ if (scope != old && this.env.search_request) {
+ if (!this.qsearch(this.gui_objects.qsearchbox.value) && this.env.search_filter && this.env.search_filter != 'ALL')
+ this.filter_mailbox(this.env.search_filter);
+ if (scope != 'all')
+ this.select_folder(this.env.mailbox, '', true);
+ }
+ };
+
+ this.set_searchmods = function(mods)
+ {
+ var mbox = this.env.mailbox,
+ scope = this.env.search_scope || 'base';
+
+ if (scope == 'all')
+ mbox = '*';
+
+ if (!this.env.search_mods)
+ this.env.search_mods = {};
+
+ if (mbox)
+ this.env.search_mods[mbox] = mods;
+ };
+
+ this.is_multifolder_listing = function()
+ {
+ return this.env.multifolder_listing !== undefined ? this.env.multifolder_listing :
+ (this.env.search_request && (this.env.search_scope || 'base') != 'base');
};
this.sent_successfully = function(type, msg, folders)
@@ -4261,7 +4456,7 @@ function rcube_webmail()
this.ksearch_visible = function()
{
- return (this.ksearch_selected !== null && this.ksearch_selected !== undefined && this.ksearch_value);
+ return this.ksearch_selected !== null && this.ksearch_selected !== undefined && this.ksearch_value;
};
this.ksearch_select = function(node)
@@ -4295,11 +4490,15 @@ function rcube_webmail()
this.ksearch_destroy();
// insert all members of a group
- if (typeof this.env.contacts[id] === 'object' && this.env.contacts[id].id) {
+ if (typeof this.env.contacts[id] === 'object' && this.env.contacts[id].type == 'group') {
insert += this.env.contacts[id].name + this.env.recipients_delimiter;
this.group2expand[this.env.contacts[id].id] = $.extend({ input: this.ksearch_input }, this.env.contacts[id]);
this.http_request('mail/group-expand', {_source: this.env.contacts[id].source, _gid: this.env.contacts[id].id}, false);
}
+ else if (typeof this.env.contacts[id] === 'object' && this.env.contacts[id].name) {
+ insert = this.env.contacts[id].name + this.env.recipients_delimiter;
+ trigger = true;
+ }
else if (typeof this.env.contacts[id] === 'string') {
insert = this.env.contacts[id] + this.env.recipients_delimiter;
trigger = true;
@@ -4308,12 +4507,10 @@ function rcube_webmail()
this.ksearch_input.value = pre + insert + end;
// set caret to insert pos
- cpos = p+insert.length;
- if (this.ksearch_input.setSelectionRange)
- this.ksearch_input.setSelectionRange(cpos, cpos);
+ this.set_caret_pos(this.ksearch_input, p + insert.length);
if (trigger) {
- this.triggerEvent('autocomplete_insert', { field:this.ksearch_input, insert:insert });
+ this.triggerEvent('autocomplete_insert', { field:this.ksearch_input, insert:insert, data:this.env.contacts[id] });
this.compose_type_activity++;
}
};
@@ -4344,7 +4541,7 @@ function rcube_webmail()
p = inp_value.lastIndexOf(this.env.recipients_separator, cpos-1),
q = inp_value.substring(p+1, cpos),
min = this.env.autocomplete_min_length,
- ac = this.ksearch_data;
+ data = this.ksearch_data;
// trim query string
q = $.trim(q);
@@ -4371,34 +4568,26 @@ function rcube_webmail()
return;
// ...new search value contains old one and previous search was not finished or its result was empty
- if (old_value && old_value.length && q.startsWith(old_value) && (!ac || ac.num <= 0) && this.env.contacts && !this.env.contacts.length)
+ if (old_value && old_value.length && q.startsWith(old_value) && (!data || data.num <= 0) && this.env.contacts && !this.env.contacts.length)
return;
- var i, lock, source, xhr, reqid = new Date().getTime(),
- post_data = {_search: q, _id: reqid},
- threads = props && props.threads ? props.threads : 1,
- sources = props && props.sources ? props.sources : [],
- action = props && props.action ? props.action : 'mail/autocomplete';
-
- this.ksearch_data = {id: reqid, sources: sources.slice(), action: action,
- locks: [], requests: [], num: sources.length};
-
- for (i=0; i<threads; i++) {
- source = this.ksearch_data.sources.shift();
- if (threads > 1 && source === undefined)
- break;
-
- post_data._source = source ? source : '';
- lock = this.display_message(this.get_label('searching'), 'loading');
- xhr = this.http_post(action, post_data, lock);
+ var sources = props && props.sources ? props.sources : [''];
+ var reqid = this.multi_thread_http_request({
+ items: sources,
+ threads: props && props.threads ? props.threads : 1,
+ action: props && props.action ? props.action : 'mail/autocomplete',
+ postdata: { _search:q, _source:'%s' },
+ lock: this.display_message(this.get_label('searching'), 'loading')
+ });
- this.ksearch_data.locks.push(lock);
- this.ksearch_data.requests.push(xhr);
- }
+ this.ksearch_data = { id:reqid, sources:sources.slice(), num:sources.length };
};
this.ksearch_query_results = function(results, search, reqid)
{
+ // trigger multi-thread http response callback
+ this.multi_thread_http_response(results, reqid);
+
// search stopped in meantime?
if (!this.ksearch_value)
return;
@@ -4408,9 +4597,8 @@ function rcube_webmail()
return;
// display search results
- var i, len, ul, li, text, init,
+ var i, len, ul, li, text, type, init,
value = this.ksearch_value,
- data = this.ksearch_data,
maxlen = this.env.autocomplete_max ? this.env.autocomplete_max : 15;
// create results pane if not present
@@ -4442,11 +4630,13 @@ function rcube_webmail()
if (results && (len = results.length)) {
for (i=0; i < len && maxlen > 0; i++) {
text = typeof results[i] === 'object' ? results[i].name : results[i];
+ type = typeof results[i] === 'object' ? results[i].type : '';
li = document.createElement('LI');
li.innerHTML = text.replace(new RegExp('('+RegExp.escape(value)+')', 'ig'), '##$1%%').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/##([^%]+)%%/g, '<b>$1</b>');
li.onmouseover = function(){ ref.ksearch_select(this); };
li.onmouseup = function(){ ref.ksearch_click(this) };
li._rcm_id = this.env.contacts.length + i;
+ if (type) li.className = type;
ul.appendChild(li);
maxlen -= 1;
}
@@ -4464,27 +4654,8 @@ function rcube_webmail()
if (len)
this.env.contacts = this.env.contacts.concat(results);
- // run next parallel search
- if (data.id == reqid) {
- data.num--;
- if (maxlen > 0 && data.sources.length) {
- var lock, xhr, source = data.sources.shift(), post_data;
- if (source) {
- post_data = {_search: value, _id: reqid, _source: source};
- lock = this.display_message(this.get_label('searching'), 'loading');
- xhr = this.http_post(data.action, post_data, lock);
-
- this.ksearch_data.locks.push(lock);
- this.ksearch_data.requests.push(xhr);
- }
- }
- else if (!maxlen) {
- if (!this.ksearch_msg)
- this.ksearch_msg = this.display_message(this.get_label('autocompletemore'));
- // abort pending searches
- this.ksearch_abort();
- }
- }
+ if (this.ksearch_data.id == reqid)
+ this.ksearch_data.num--;
};
this.ksearch_click = function(node)
@@ -4519,7 +4690,8 @@ function rcube_webmail()
// Clears autocomplete data/requests
this.ksearch_destroy = function()
{
- this.ksearch_abort();
+ if (this.ksearch_data)
+ this.multi_thread_request_abort(this.ksearch_data.id);
if (this.ksearch_info)
this.hide_message(this.ksearch_info);
@@ -4530,18 +4702,6 @@ function rcube_webmail()
this.ksearch_data = null;
this.ksearch_info = null;
this.ksearch_msg = null;
- }
-
- // Aborts pending autocomplete requests
- this.ksearch_abort = function()
- {
- var i, len, ac = this.ksearch_data;
-
- if (!ac)
- return;
-
- for (i=0, len=ac.locks.length; i<len; i++)
- this.abort_request({request: ac.requests[i], lock: ac.locks[i]});
};
@@ -4560,7 +4720,7 @@ function rcube_webmail()
if (this.preview_timer)
clearTimeout(this.preview_timer);
- var n, id, sid, contact, ref = this, writable = false,
+ var n, id, sid, contact, writable = false,
source = this.env.source ? this.env.address_sources[this.env.source] : null;
// we don't have dblclick handler here, so use 200 instead of this.dblclick_time
@@ -4977,7 +5137,7 @@ function rcube_webmail()
this.init_contact_form = function()
{
- var ref = this, col;
+ var col;
if (this.env.coltypes) {
this.set_photo_actions($('#ff_photo').val());
@@ -5028,7 +5188,7 @@ function rcube_webmail()
if (!this.name_input) {
this.enable_command('list', 'listgroup', false);
this.name_input = $('<input>').attr('type', 'text').val(this.env.contactgroups['G'+this.env.source+this.env.group].name);
- this.name_input.bind('keydown', function(e){ return rcmail.add_input_keydown(e); });
+ this.name_input.bind('keydown', function(e) { return ref.add_input_keydown(e); });
this.env.group_renaming = true;
var link, li = this.get_folder_li('G'+this.env.source+this.env.group,'',true);
@@ -5069,7 +5229,7 @@ function rcube_webmail()
if (!this.name_input) {
this.name_input = $('<input>').attr('type', 'text').data('tt', type);
- this.name_input.bind('keydown', function(e){ return rcmail.add_input_keydown(e); });
+ this.name_input.bind('keydown', function(e) { return ref.add_input_keydown(e); });
this.name_input_li = $('<li>').addClass(type).append(this.name_input);
var ul, li;
@@ -5096,21 +5256,21 @@ function rcube_webmail()
//remove selected contacts from current active group
this.group_remove_selected = function()
{
- ref.http_post('group-delmembers', {_cid: this.contact_list.selection,
+ this.http_post('group-delmembers', {_cid: this.contact_list.selection,
_source: this.env.source, _gid: this.env.group});
};
//callback after deleting contact(s) from current group
this.remove_group_contacts = function(props)
{
- if('undefined' != typeof this.env.group && (this.env.group === props.gid)){
+ if (this.env.group !== undefined && (this.env.group === props.gid)) {
var n, selection = this.contact_list.get_selection();
for (n=0; n<selection.length; n++) {
id = selection[n];
this.contact_list.remove_row(id, (n == selection.length-1));
}
}
- }
+ };
// handler for keyboard events on the input field
this.add_input_keydown = function(e)
@@ -5172,7 +5332,7 @@ function rcube_webmail()
var key = 'G'+prop.source+prop.id,
link = $('<a>').attr('href', '#')
.attr('rel', prop.source+':'+prop.id)
- .click(function() { return rcmail.command('listgroup', prop, this); })
+ .click(function() { return ref.command('listgroup', prop, this); })
.html(prop.name);
this.env.contactfolders[key] = this.env.contactgroups[key] = prop;
@@ -5207,7 +5367,7 @@ function rcube_webmail()
newnode.id = newkey;
newnode.html = $('<a>').attr('href', '#')
.attr('rel', prop.source+':'+prop.newid)
- .click(function() { return rcmail.command('listgroup', newprop, this); })
+ .click(function() { return ref.command('listgroup', newprop, this); })
.html(prop.name);
}
// update displayed group name
@@ -5384,7 +5544,7 @@ function rcube_webmail()
{
if (form && form.elements._photo.value) {
this.async_upload_form(form, 'upload-photo', function(e) {
- rcmail.set_busy(false, null, rcmail.file_upload_id);
+ ref.set_busy(false, null, ref.file_upload_id);
});
// display upload indicator
@@ -5449,7 +5609,7 @@ function rcube_webmail()
var key = 'S'+id,
link = $('<a>').attr('href', '#')
.attr('rel', id)
- .click(function() { return rcmail.command('listsearch', id, this); })
+ .click(function() { return ref.command('listsearch', id, this); })
.html(name),
prop = { name:name, id:id };
@@ -5625,25 +5785,25 @@ function rcube_webmail()
this.init_subscription_list = function()
{
- var p = this, delim = RegExp.escape(this.env.delimiter);
+ var delim = RegExp.escape(this.env.delimiter);
this.last_sub_rx = RegExp('['+delim+']?[^'+delim+']+$');
this.subscription_list = new rcube_list_widget(this.gui_objects.subscriptionlist,
{multiselect:false, draggable:true, keyboard:false, toggleselect:true});
this.subscription_list
- .addEventListener('select', function(o){ p.subscription_select(o); })
- .addEventListener('dragstart', function(o){ p.drag_active = true; })
- .addEventListener('dragend', function(o){ p.subscription_move_folder(o); })
+ .addEventListener('select', function(o){ ref.subscription_select(o); })
+ .addEventListener('dragstart', function(o){ ref.drag_active = true; })
+ .addEventListener('dragend', function(o){ ref.subscription_move_folder(o); })
.addEventListener('initrow', function (row) {
- row.obj.onmouseover = function() { p.focus_subscription(row.id); };
- row.obj.onmouseout = function() { p.unfocus_subscription(row.id); };
+ row.obj.onmouseover = function() { ref.focus_subscription(row.id); };
+ row.obj.onmouseout = function() { ref.unfocus_subscription(row.id); };
})
.init();
$('#mailboxroot')
- .mouseover(function(){ p.focus_subscription(this.id); })
- .mouseout(function(){ p.unfocus_subscription(this.id); })
+ .mouseover(function(){ ref.focus_subscription(this.id); })
+ .mouseout(function(){ ref.unfocus_subscription(this.id); })
};
this.focus_subscription = function(id)
@@ -5739,7 +5899,8 @@ function rcube_webmail()
if (!this.gui_objects.subscriptionlist)
return false;
- var row, n, i, tmp, tmp_name, rowid, folders = [], list = [], slist = [],
+ var row, n, i, tmp, tmp_name, rowid, collator,
+ folders = [], list = [], slist = [],
tbody = this.gui_objects.subscriptionlist.tBodies[0],
refrow = $('tr', tbody).get(1),
id = 'rcmrow'+((new Date).getTime());
@@ -5766,24 +5927,32 @@ function rcube_webmail()
// add to folder/row-ID map
this.env.subscriptionrows[id] = [name, display_name, false];
- // sort folders (to find a place where to insert the row)
- // replace delimiter with \0 character to fix sorting
- // issue where 'Abc Abc' would be placed before 'Abc/def'
- var replace_from = RegExp(RegExp.escape(this.env.delimiter), 'g'),
- replace_to = String.fromCharCode(0);
-
- $.each(this.env.subscriptionrows, function(k,v) {
- if (v.length < 4) {
- var n = v[0];
- n = n.replace(replace_from, replace_to);
- v.push(n);
- }
- folders.push(v);
- });
+ // copy folders data to an array for sorting
+ $.each(this.env.subscriptionrows, function(k, v) { folders.push(v); });
+
+ try {
+ // use collator if supported (FF29, IE11, Opera15, Chrome24)
+ collator = new Intl.Collator(this.env.locale.replace('_', '-'));
+ }
+ catch (e) {};
+ // sort folders
folders.sort(function(a, b) {
- var len = a.length - 1; n1 = a[len], n2 = b[len];
- return n1 < n2 ? -1 : 1;
+ var i, f1, f2,
+ path1 = a[0].split(ref.env.delimiter),
+ path2 = b[0].split(ref.env.delimiter);
+
+ for (i=0; i<path1.length; i++) {
+ f1 = path1[i];
+ f2 = path2[i];
+
+ if (f1 !== f2) {
+ if (collator)
+ return collator.compare(f1, f2);
+ else
+ return f1 < f2 ? -1 : 1;
+ }
+ }
});
for (n in folders) {
@@ -6033,14 +6202,14 @@ function rcube_webmail()
elm._command = cmd;
elm._id = prop.id;
if (prop.sel) {
- elm.onmousedown = function(e){ return rcmail.button_sel(this._command, this._id); };
- elm.onmouseup = function(e){ return rcmail.button_out(this._command, this._id); };
+ elm.onmousedown = function(e) { return ref.button_sel(this._command, this._id); };
+ elm.onmouseup = function(e) { return ref.button_out(this._command, this._id); };
if (preload)
new Image().src = prop.sel;
}
if (prop.over) {
- elm.onmouseover = function(e){ return rcmail.button_over(this._command, this._id); };
- elm.onmouseout = function(e){ return rcmail.button_out(this._command, this._id); };
+ elm.onmouseover = function(e) { return ref.button_over(this._command, this._id); };
+ elm.onmouseout = function(e) { return ref.button_out(this._command, this._id); };
if (preload)
new Image().src = prop.over;
}
@@ -6072,7 +6241,7 @@ function rcube_webmail()
button = a_buttons[n];
obj = document.getElementById(button.id);
- if (!obj)
+ if (!obj || button.status === state)
continue;
// get default/passive setting of the button
@@ -6085,20 +6254,22 @@ function rcube_webmail()
else if (!button.status)
button.pas = String(obj.className);
+ button.status = state;
+
// set image according to button state
if (button.type == 'image' && button[state]) {
- button.status = state;
obj.src = button[state];
}
// set class name according to button state
else if (button[state] !== undefined) {
- button.status = state;
obj.className = button[state];
}
// disable/enable input buttons
if (button.type == 'input') {
- button.status = state;
- obj.disabled = !state;
+ obj.disabled = state == 'pas';
+ }
+ else if (button.type == 'uibutton') {
+ $(obj).button('option', 'disabled', state == 'pas');
}
}
};
@@ -6184,8 +6355,7 @@ function rcube_webmail()
type = type ? type : 'notice';
- var ref = this,
- key = this.html_identifier(msg),
+ var key = this.html_identifier(msg),
date = new Date(),
id = type + date.getTime();
@@ -6314,7 +6484,7 @@ function rcube_webmail()
{
// forward call to parent window
if (this.is_framed()) {
- return parent.rcmail.show_popup_dialog(html, title, buttons);
+ return parent.rcmail.show_popup_dialog(html, title, buttons, options);
}
var popup = $('<div class="popup">')
@@ -6334,7 +6504,7 @@ function rcube_webmail()
popup.dialog('option', {
height: Math.min(h - 40, height + 75 + (buttons ? 50 : 0)),
- width: Math.min(w - 20, width + 20)
+ width: Math.min(w - 20, width + 36)
});
return popup;
@@ -6368,12 +6538,14 @@ function rcube_webmail()
this.mark_folder = function(name, class_name, prefix, encode)
{
$(this.get_folder_li(name, prefix, encode)).addClass(class_name);
+ this.triggerEvent('markfolder', {folder: name, mark: class_name, status: true});
};
// adds a class to selected folder
this.unmark_folder = function(name, class_name, prefix, encode)
{
$(this.get_folder_li(name, prefix, encode)).removeClass(class_name);
+ this.triggerEvent('markfolder', {folder: name, mark: class_name, status: false});
};
// helper method to find a folder list item
@@ -6390,18 +6562,18 @@ function rcube_webmail()
// for reordering column array (Konqueror workaround)
// and for setting some message list global variables
- this.set_message_coltypes = function(coltypes, repl, smart_col)
+ this.set_message_coltypes = function(listcols, repl, smart_col)
{
var list = this.message_list,
thead = list ? list.thead : null,
- cell, col, n, len, th, tr;
+ repl, cell, col, n, len, tr;
- this.env.coltypes = coltypes;
+ this.env.listcols = listcols;
// replace old column headers
if (thead) {
if (repl) {
- th = document.createElement('thead');
+ thead.innerHTML = '';
tr = document.createElement('tr');
for (c=0, len=repl.length; c < len; c++) {
@@ -6411,20 +6583,13 @@ function rcube_webmail()
if (repl[c].className) cell.className = repl[c].className;
tr.appendChild(cell);
}
- th.appendChild(tr);
- thead.parentNode.replaceChild(th, thead);
- list.thead = thead = th;
+ thead.appendChild(tr);
}
- for (n=0, len=this.env.coltypes.length; n<len; n++) {
- col = this.env.coltypes[n];
+ for (n=0, len=this.env.listcols.length; n<len; n++) {
+ col = this.env.listcols[n];
if ((cell = thead.rows[0].cells[n]) && (col == 'from' || col == 'to' || col == 'fromto')) {
- cell.id = 'rcm'+col;
- $('span,a', cell).text(this.get_label(col == 'fromto' ? smart_col : col));
- // if we have links for sorting, it's a bit more complicated...
- $('a', cell).click(function(){
- return rcmail.command('sort', this.id.replace(/^rcm/, ''), this);
- });
+ $(cell).attr('rel', col).find('span,a').text(this.get_label(col == 'fromto' ? smart_col : col));
}
}
}
@@ -6433,18 +6598,23 @@ function rcube_webmail()
this.env.flagged_col = null;
this.env.status_col = null;
- if ((n = $.inArray('subject', this.env.coltypes)) >= 0) {
+ if (this.env.coltypes.folder)
+ this.env.coltypes.folder.hidden = !(this.env.search_request || this.env.search_id) || this.env.search_scope == 'base';
+
+ if ((n = $.inArray('subject', this.env.listcols)) >= 0) {
this.env.subject_col = n;
if (list)
list.subject_col = n;
}
- if ((n = $.inArray('flag', this.env.coltypes)) >= 0)
+ if ((n = $.inArray('flag', this.env.listcols)) >= 0)
this.env.flagged_col = n;
- if ((n = $.inArray('status', this.env.coltypes)) >= 0)
+ if ((n = $.inArray('status', this.env.listcols)) >= 0)
this.env.status_col = n;
- if (list)
+ if (list) {
+ list.hide_column('folder', (this.env.coltypes.folder && this.env.coltypes.folder.hidden) || $.inArray('folder', this.env.listcols) < 0);
list.init_header();
+ }
};
// replace content of row count display
@@ -6569,12 +6739,13 @@ function rcube_webmail()
$(elem).removeClass('show-headers').addClass('hide-headers');
$(this.gui_objects.all_headers_row).show();
- elem.onclick = function() { rcmail.command('hide-headers', '', elem); };
+ elem.onclick = function() { ref.command('hide-headers', '', elem); };
// fetch headers only once
if (!this.gui_objects.all_headers_box.innerHTML) {
- var lock = this.display_message(this.get_label('loading'), 'loading');
- this.http_post('headers', {_uid: this.env.uid}, lock);
+ this.http_post('headers', {_uid: this.env.uid, _mbox: this.env.mailbox},
+ this.display_message(this.get_label('loading'), 'loading')
+ );
}
};
@@ -6586,7 +6757,7 @@ function rcube_webmail()
$(elem).removeClass('hide-headers').addClass('show-headers');
$(this.gui_objects.all_headers_row).hide();
- elem.onclick = function() { rcmail.command('show-headers', '', elem); };
+ elem.onclick = function() { ref.command('show-headers', '', elem); };
};
// create folder selector popup, position and display it
@@ -6667,8 +6838,9 @@ function rcube_webmail()
this.element_position = function(element, obj)
{
var obj = $(obj), win = $(window),
- width = obj.width(),
- height = obj.height(),
+ width = obj.outerWidth(),
+ height = obj.outerHeight(),
+ menu_pos = obj.data('menu-pos'),
win_height = win.height(),
elem_height = $(element).height(),
elem_width = $(element).width(),
@@ -6676,6 +6848,13 @@ function rcube_webmail()
top = pos.top,
left = pos.left + width;
+ if (menu_pos == 'bottom') {
+ top += height;
+ left -= width;
+ }
+ else
+ left -= 5;
+
if (top + elem_height > win_height) {
top -= elem_height - height;
if (top < 0)
@@ -6693,28 +6872,50 @@ function rcube_webmail()
/********* html to text conversion functions *********/
/********************************************************/
- this.html2plain = function(htmlText, id)
+ this.html2plain = function(html, func)
{
- var rcmail = this,
- url = '?_task=utils&_action=html2text',
- lock = this.set_busy(true, 'converting');
-
- this.log('HTTP POST: ' + url);
+ return this.format_converter(html, 'html', func);
+ };
- $.ajax({ type: 'POST', url: url, data: htmlText, contentType: 'application/octet-stream',
- error: function(o, status, err) { rcmail.http_error(o, status, err, lock); },
- success: function(data) { rcmail.set_busy(false, null, lock); $('#'+id).val(data); rcmail.log(data); }
- });
+ this.plain2html = function(plain, func)
+ {
+ return this.format_converter(plain, 'plain', func);
};
- this.plain2html = function(plain, id)
+ this.format_converter = function(text, format, func)
{
- var lock = this.set_busy(true, 'converting');
+ // warn the user (if converted content is not empty)
+ if (!text
+ || (format == 'html' && !(text.replace(/<[^>]+>|&nbsp;|\xC2\xA0|\s/g, '')).length)
+ || (format != 'html' && !(text.replace(/\xC2\xA0|\s/g, '')).length)
+ ) {
+ // without setTimeout() here, textarea is filled with initial (onload) content
+ if (func)
+ setTimeout(function() { func(''); }, 50);
+ return true;
+ }
- plain = plain.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
- $('#'+id).val(plain ? '<pre>'+plain+'</pre>' : '');
+ var confirmed = this.env.editor_warned || confirm(this.get_label('editorwarning'));
- this.set_busy(false, null, lock);
+ this.env.editor_warned = true;
+
+ if (!confirmed)
+ return false;
+
+ var url = '?_task=utils&_action=' + (format == 'html' ? 'html2text' : 'text2html'),
+ lock = this.set_busy(true, 'converting');
+
+ this.log('HTTP POST: ' + url);
+
+ $.ajax({ type: 'POST', url: url, data: text, contentType: 'application/octet-stream',
+ error: function(o, status, err) { ref.http_error(o, status, err, lock); },
+ success: function(data) {
+ ref.set_busy(false, null, lock);
+ if (func) func(data);
+ }
+ });
+
+ return true;
};
@@ -6734,13 +6935,13 @@ function rcube_webmail()
if (action)
query._action = action;
- else
+ else if (this.env.action)
query._action = this.env.action;
var base = this.env.comm_path, k, param = {};
// overwrite task name
- if (query._action.match(/([a-z0-9_-]+)\/([a-z0-9-_.]+)/)) {
+ if (action && action.match(/([a-z0-9_-]+)\/([a-z0-9-_.]+)/)) {
query._action = RegExp.$2;
base = base.replace(/\_task=[a-z0-9_-]+/, '_task='+RegExp.$1);
}
@@ -6751,7 +6952,7 @@ function rcube_webmail()
param[k] = query[k];
}
- return base + '&' + $.param(param) + querystring;
+ return base + (base.indexOf('?') > -1 ? '&' : '?') + $.param(param) + querystring;
};
this.redirect = function(url, lock)
@@ -6775,7 +6976,7 @@ function rcube_webmail()
this.goto_url = function(action, query, lock)
{
- this.redirect(this.url(action, query));
+ this.redirect(this.url(action, query), lock);
};
this.location_href = function(url, target, frame)
@@ -6972,12 +7173,15 @@ function rcube_webmail()
this.env.qsearch = null;
case 'list':
if (this.task == 'mail') {
+ var is_multifolder = this.is_multifolder_listing();
this.enable_command('show', 'select-all', 'select-none', this.env.messagecount > 0);
- this.enable_command('expunge', this.env.exists);
- this.enable_command('purge', this.purge_mailbox_test());
- this.enable_command('expand-all', 'expand-unread', 'collapse-all', this.env.threading && this.env.messagecount);
+ this.enable_command('expunge', this.env.exists && !is_multifolder);
+ this.enable_command('purge', this.purge_mailbox_test() && !is_multifolder);
+ this.enable_command('import-messages', !is_multifolder);
+ this.enable_command('expand-all', 'expand-unread', 'collapse-all', this.env.threading && this.env.messagecount && !is_multifolder);
if ((response.action == 'list' || response.action == 'search') && this.message_list) {
+ this.enable_command('set-listmode', this.env.threads && !is_multifolder);
this.msglist_select(this.message_list);
this.triggerEvent('listupdate', { folder:this.env.mailbox, rowcount:this.message_list.rowcount });
}
@@ -7022,7 +7226,7 @@ function rcube_webmail()
else if (status == 'timeout')
this.display_message(this.get_label('requesttimedout'), 'error');
else if (request.status == 0 && status != 'abort')
- this.display_message(this.get_label('servererror') + ' (No connection)', 'error');
+ this.display_message(this.get_label('connerror'), 'error');
// redirect to url specified in location header if not empty
var location_url = request.getResponseHeader("Location");
@@ -7052,7 +7256,7 @@ function rcube_webmail()
this.save_compose_form_local();
}
else if (redirect_url) {
- window.setTimeout(function(){ ref.redirect(redirect_url, true); }, 2000);
+ setTimeout(function(){ ref.redirect(redirect_url, true); }, 2000);
}
};
@@ -7065,6 +7269,130 @@ function rcube_webmail()
clearTimeout(this.submit_timer);
};
+ /**
+ Send multi-threaded parallel HTTP requests to the server for a list if items.
+ The string '%' in either a GET query or POST parameters will be replaced with the respective item value.
+ This is the argument object expected: {
+ items: ['foo','bar','gna'], // list of items to send requests for
+ action: 'task/some-action', // Roudncube action to call
+ query: { q:'%s' }, // GET query parameters
+ postdata: { source:'%s' }, // POST data (sends a POST request if present)
+ threads: 3, // max. number of concurrent requests
+ onresponse: function(data){ }, // Callback function called for every response received from server
+ whendone: function(alldata){ } // Callback function called when all requests have been sent
+ }
+ */
+ this.multi_thread_http_request = function(prop)
+ {
+ var i, item, reqid = new Date().getTime(),
+ threads = prop.threads || 1;
+
+ prop.reqid = reqid;
+ prop.running = 0;
+ prop.requests = [];
+ prop.result = [];
+ prop._items = $.extend([], prop.items); // copy items
+
+ if (!prop.lock)
+ prop.lock = this.display_message(this.get_label('loading'), 'loading');
+
+ // add the request arguments to the jobs pool
+ this.http_request_jobs[reqid] = prop;
+
+ // start n threads
+ for (i=0; i < threads; i++) {
+ item = prop._items.shift();
+ if (item === undefined)
+ break;
+
+ prop.running++;
+ prop.requests.push(this.multi_thread_send_request(prop, item));
+ }
+
+ return reqid;
+ };
+
+ // helper method to send an HTTP request with the given iterator value
+ this.multi_thread_send_request = function(prop, item)
+ {
+ var k, postdata, query;
+
+ // replace %s in post data
+ if (prop.postdata) {
+ postdata = {};
+ for (k in prop.postdata) {
+ postdata[k] = String(prop.postdata[k]).replace('%s', item);
+ }
+ postdata._reqid = prop.reqid;
+ }
+ // replace %s in query
+ else if (typeof prop.query == 'string') {
+ query = prop.query.replace('%s', item);
+ query += '&_reqid=' + prop.reqid;
+ }
+ else if (typeof prop.query == 'object' && prop.query) {
+ query = {};
+ for (k in prop.query) {
+ query[k] = String(prop.query[k]).replace('%s', item);
+ }
+ query._reqid = prop.reqid;
+ }
+
+ // send HTTP GET or POST request
+ return postdata ? this.http_post(prop.action, postdata) : this.http_request(prop.action, query);
+ };
+
+ // callback function for multi-threaded http responses
+ this.multi_thread_http_response = function(data, reqid)
+ {
+ var prop = this.http_request_jobs[reqid];
+ if (!prop || prop.running <= 0 || prop.cancelled)
+ return;
+
+ prop.running--;
+
+ // trigger response callback
+ if (prop.onresponse && typeof prop.onresponse == 'function') {
+ prop.onresponse(data);
+ }
+
+ prop.result = $.extend(prop.result, data);
+
+ // send next request if prop.items is not yet empty
+ var item = prop._items.shift();
+ if (item !== undefined) {
+ prop.running++;
+ prop.requests.push(this.multi_thread_send_request(prop, item));
+ }
+ // trigger whendone callback and mark this request as done
+ else if (prop.running == 0) {
+ if (prop.whendone && typeof prop.whendone == 'function') {
+ prop.whendone(prop.result);
+ }
+
+ this.set_busy(false, '', prop.lock);
+
+ // remove from this.http_request_jobs pool
+ delete this.http_request_jobs[reqid];
+ }
+ };
+
+ // abort a running multi-thread request with the given identifier
+ this.multi_thread_request_abort = function(reqid)
+ {
+ var prop = this.http_request_jobs[reqid];
+ if (prop) {
+ for (var i=0; prop.running > 0 && i < prop.requests.length; i++) {
+ if (prop.requests[i].abort)
+ prop.requests[i].abort();
+ }
+
+ prop.running = 0;
+ prop.cancelled = true;
+ this.set_busy(false, '', prop.lock);
+ }
+ };
+
// post the given form to a hidden iframe
this.async_upload_form = function(form, action, onload)
{
@@ -7115,14 +7443,14 @@ function rcube_webmail()
this.document_drag_hover = function(e, over)
{
e.preventDefault();
- $(ref.gui_objects.filedrop)[(over?'addClass':'removeClass')]('active');
+ $(this.gui_objects.filedrop)[(over?'addClass':'removeClass')]('active');
};
this.file_drag_hover = function(e, over)
{
e.preventDefault();
e.stopPropagation();
- $(ref.gui_objects.filedrop)[(over?'addClass':'removeClass')]('hover');
+ $(this.gui_objects.filedrop)[(over?'addClass':'removeClass')]('hover');
};
// handler when files are dropped to a designated area.
@@ -7280,7 +7608,7 @@ function rcube_webmail()
this.env.lastrefresh = new Date();
// plugins should bind to 'requestrefresh' event to add own params
- this.http_request('refresh', params, lock);
+ this.http_post('refresh', params, lock);
};
// returns check-recent request parameters
@@ -7342,95 +7670,44 @@ function rcube_webmail()
return this.env.cid ? this.env.cid : (this.contact_list ? this.contact_list.get_single_selection() : null);
};
+ // get the IMP mailbox of the message with the given UID
+ this.get_message_mailbox = function(uid)
+ {
+ var msg = this.env.messages ? this.env.messages[uid] : {};
+ return msg.mbox || this.env.mailbox;
+ };
+
// gets cursor position
this.get_caret_pos = function(obj)
{
if (obj.selectionEnd !== undefined)
return obj.selectionEnd;
- if (document.selection && document.selection.createRange) {
- var range = document.selection.createRange();
- if (range.parentElement() != obj)
- return 0;
-
- var gm = range.duplicate();
- if (obj.tagName == 'TEXTAREA')
- gm.moveToElementText(obj);
- else
- gm.expand('textedit');
-
- gm.setEndPoint('EndToStart', range);
- var p = gm.text.length;
-
- return p <= obj.value.length ? p : -1;
- }
-
return obj.value.length;
};
// moves cursor to specified position
this.set_caret_pos = function(obj, pos)
{
- if (obj.setSelectionRange)
- obj.setSelectionRange(pos, pos);
- else if (obj.createTextRange) {
- var range = obj.createTextRange();
- range.collapse(true);
- range.moveEnd('character', pos);
- range.moveStart('character', pos);
- range.select();
+ try {
+ if (obj.setSelectionRange)
+ obj.setSelectionRange(pos, pos);
}
+ catch(e) {} // catch Firefox exception if obj is hidden
};
// get selected text from an input field
- // http://stackoverflow.com/questions/7186586/how-to-get-the-selected-text-in-textarea-using-jquery-in-internet-explorer-7
this.get_input_selection = function(obj)
{
- var start = 0, end = 0,
- normalizedValue, range,
- textInputRange, len, endRange;
+ var start = 0, end = 0, normalizedValue = '';
if (typeof obj.selectionStart == "number" && typeof obj.selectionEnd == "number") {
normalizedValue = obj.value;
start = obj.selectionStart;
end = obj.selectionEnd;
}
- else {
- range = document.selection.createRange();
-
- if (range && range.parentElement() == obj) {
- len = obj.value.length;
- normalizedValue = obj.value; //.replace(/\r\n/g, "\n");
-
- // create a working TextRange that lives only in the input
- textInputRange = obj.createTextRange();
- textInputRange.moveToBookmark(range.getBookmark());
-
- // Check if the start and end of the selection are at the very end
- // of the input, since moveStart/moveEnd doesn't return what we want
- // in those cases
- endRange = obj.createTextRange();
- endRange.collapse(false);
-
- if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
- start = end = len;
- }
- else {
- start = -textInputRange.moveStart("character", -len);
- start += normalizedValue.slice(0, start).split("\n").length - 1;
-
- if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
- end = len;
- }
- else {
- end = -textInputRange.moveEnd("character", -len);
- end += normalizedValue.slice(0, end).split("\n").length - 1;
- }
- }
- }
- }
- return { start:start, end:end, text:normalizedValue.substr(start, end-start) };
+ return {start: start, end: end, text: normalizedValue.substr(start, end-start)};
};
// disable/enable all fields of a form
@@ -7452,9 +7729,7 @@ function rcube_webmail()
// remember which elem was disabled before lock
if (lock && elm.disabled)
this.disabled_form_elements.push(elm);
- // check this.disabled_form_elements before inArray() as a workaround for FF5 bug
- // http://bugs.jquery.com/ticket/9873
- else if (lock || (this.disabled_form_elements && $.inArray(elm, this.disabled_form_elements)<0))
+ else if (lock || $.inArray(elm, this.disabled_form_elements) < 0)
elm.disabled = lock;
}
};
@@ -7469,20 +7744,26 @@ function rcube_webmail()
try {
window.navigator.registerProtocolHandler('mailto', this.mailto_handler_uri(), name);
}
- catch(e) {};
+ catch(e) {
+ this.display_message(String(e), 'error');
+ }
};
this.check_protocol_handler = function(name, elem)
{
var nav = window.navigator;
- if (!nav
- || (typeof nav.registerProtocolHandler != 'function')
- || ((typeof nav.isProtocolHandlerRegistered == 'function')
- && nav.isProtocolHandlerRegistered('mailto', this.mailto_handler_uri()) == 'registered')
- )
- $(elem).addClass('disabled');
- else
- $(elem).click(function() { rcmail.register_protocol_handler(name); return false; });
+
+ if (!nav || (typeof nav.registerProtocolHandler != 'function')) {
+ $(elem).addClass('disabled').click(function(){ return false; });
+ }
+ else if (typeof nav.isProtocolHandlerRegistered == 'function') {
+ var status = nav.isProtocolHandlerRegistered('mailto', this.mailto_handler_uri());
+ if (status)
+ $(elem).parent().find('.mailtoprotohandler-status').html(status);
+ }
+ else {
+ $(elem).click(function() { ref.register_protocol_handler(name); return false; });
+ }
};
// Checks browser capabilities eg. PDF support, TIF support
@@ -7519,8 +7800,8 @@ function rcube_webmail()
{
var img = new Image();
- img.onload = function() { rcmail.env.browser_capabilities.tif = 1; };
- img.onerror = function() { rcmail.env.browser_capabilities.tif = 0; };
+ img.onload = function() { ref.env.browser_capabilities.tif = 1; };
+ img.onerror = function() { ref.env.browser_capabilities.tif = 0; };
img.src = 'program/resources/blank.tif';
};
@@ -7536,12 +7817,12 @@ function rcube_webmail()
if (window.ActiveXObject) {
try {
- if (axObj = new ActiveXObject("AcroPDF.PDF"))
+ if (plugin = new ActiveXObject("AcroPDF.PDF"))
return 1;
}
catch (e) {}
try {
- if (axObj = new ActiveXObject("PDF.PdfCtrl"))
+ if (plugin = new ActiveXObject("PDF.PdfCtrl"))
return 1;
}
catch (e) {}
@@ -7569,7 +7850,7 @@ function rcube_webmail()
if (window.ActiveXObject) {
try {
- if (axObj = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"))
+ if (plugin = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"))
return 1;
}
catch (e) {}
@@ -7595,7 +7876,6 @@ function rcube_webmail()
// wrapper for localStorage.getItem(key)
this.local_storage_get_item = function(key, deflt, encrypted)
{
-
// TODO: add encryption
var item = localStorage.getItem(this.get_local_storage_prefix() + key);
return item !== null ? JSON.parse(item) : (deflt || null);
@@ -7622,12 +7902,12 @@ rcube_webmail.long_subject_title = function(elem, indent)
{
if (!elem.title) {
var $elem = $(elem);
- if ($elem.width() + indent * 15 > $elem.parent().width())
+ if ($elem.width() + (indent || 0) * 15 > $elem.parent().width())
elem.title = $elem.text();
}
};
-rcube_webmail.long_subject_title_ex = function(elem, indent)
+rcube_webmail.long_subject_title_ex = function(elem)
{
if (!elem.title) {
var $elem = $(elem),
@@ -7639,7 +7919,7 @@ rcube_webmail.long_subject_title_ex = function(elem, indent)
w = tmp.width();
tmp.remove();
- if (w + indent * 15 > $elem.width())
+ if (w + $('span.branch', $elem).width() * 15 > $elem.width())
elem.title = txt;
}
};