- 1 :
/**
- 2 :
* Roundcube Webmail Client Script
- 3 :
*
- 4 :
* This file is part of the Roundcube Webmail client
- 5 :
*
- 6 :
* @licstart The following is the entire license notice for the
- 7 :
* JavaScript code in this file.
- 8 :
*
- 9 :
* Copyright (C) The Roundcube Dev Team
- 10 :
* Copyright (C) Kolab Systems AG
- 11 :
*
- 12 :
* The JavaScript code in this page is free software: you can
- 13 :
* redistribute it and/or modify it under the terms of the GNU
- 14 :
* General Public License (GNU GPL) as published by the Free Software
- 15 :
* Foundation, either version 3 of the License, or (at your option)
- 16 :
* any later version. The code is distributed WITHOUT ANY WARRANTY;
- 17 :
* without even the implied warranty of MERCHANTABILITY or FITNESS
- 18 :
* FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
- 19 :
*
- 20 :
* As additional permission under GNU GPL version 3 section 7, you
- 21 :
* may distribute non-source (e.g., minimized or compacted) forms of
- 22 :
* that code without the copy of the GNU GPL normally required by
- 23 :
* section 4, provided you include this license notice and a URL
- 24 :
* through which recipients can access the Corresponding Source.
- 25 :
*
- 26 :
* @licend The above is the entire license notice
- 27 :
* for the JavaScript code in this file.
- 28 :
*
- 29 :
* @author Thomas Bruederli <roundcube@gmail.com>
- 30 :
* @author Aleksander 'A.L.E.C' Machniak <alec@alec.pl>
- 31 :
* @author Charles McNulty <charles@charlesmcnulty.com>
- 32 :
*
- 33 :
* @requires jquery.js, common.js, list.js
- 34 :
*/
- 35 :
- 36 :
function rcube_webmail()
- 37 :
{
- 38 :
this.labels = {};
- 39 :
this.buttons = {};
- 40 :
this.buttons_sel = {};
- 41 :
this.gui_objects = {};
- 42 :
this.gui_containers = {};
- 43 :
this.commands = {};
- 44 :
this.command_handlers = {};
- 45 :
this.onloads = [];
- 46 :
this.messages = {};
- 47 :
this.group2expand = {};
- 48 :
this.http_request_jobs = {};
- 49 :
this.menu_stack = [];
- 50 :
this.menu_buttons = {};
- 51 :
this.entity_selectors = [];
- 52 :
this.image_style = {};
- 53 :
this.uploads = {};
- 54 :
- 55 :
// webmail client settings
- 56 :
this.dblclick_time = 500;
- 57 :
this.message_time = 5000;
- 58 :
this.preview_delay_select = 400;
- 59 :
this.preview_delay_click = 60;
- 60 :
this.identifier_expr = /[^0-9a-z_-]/gi;
- 61 :
this.uploadTimeout = 0; // 0 = no timeout | ajax call timeout for loading attachment
- 62 :
- 63 :
- 64 :
// environment defaults
- 65 :
this.env = {
- 66 :
attachments: {},
- 67 :
request_timeout: 180, // seconds
- 68 :
draft_autosave: 0, // seconds
- 69 :
comm_path: './',
- 70 :
recipients_separator: ',', // @deprecated
- 71 :
recipients_delimiter: ', ', // @deprecated
- 72 :
popup_width: 1150,
- 73 :
popup_width_small: 900,
- 74 :
thread_padding: '15px'
- 75 :
};
- 76 :
- 77 :
// create protected reference to myself
- 78 :
this.ref = 'rcmail';
- 79 :
var ref = this;
- 80 :
- 81 :
// set jQuery ajax options
- 82 :
$.ajaxSetup({
- 83 :
cache: false,
- 84 :
timeout: this.env.request_timeout * 1000,
- 85 :
error: function(request, status, err){ ref.http_error(request, status, err); },
- 86 :
beforeSend: function(xmlhttp){
- 87 :
//PAMELA
- 88 :
if (window.disable_x_roundcube === true) return;
- 89 :
else xmlhttp.setRequestHeader('X-Roundcube-Request', ref.env.request_token);
- 90 :
}
- 91 :
- 92 :
});
- 93 :
- 94 :
// unload fix
- 95 :
$(window).on('beforeunload', function() { ref.unload = true; });
- 96 :
- 97 :
// set environment variable(s)
- 98 :
this.set_env = function(p, value)
- 99 :
{
- 100 :
if (p != null && typeof p === 'object' && !value)
- 101 :
for (var n in p)
- 102 :
this.env[n] = p[n];
- 103 :
else
- 104 :
this.env[p] = value;
- 105 :
};
- 106 :
- 107 :
// add a localized label to the client environment
- 108 :
this.add_label = function(p, value)
- 109 :
{
- 110 :
if (typeof p == 'string')
- 111 :
this.labels[p] = value;
- 112 :
else if (typeof p == 'object')
- 113 :
$.extend(this.labels, p);
- 114 :
};
- 115 :
- 116 :
// add a button to the button list
- 117 :
this.register_button = function(command, id, type, act, sel, over)
- 118 :
{
- 119 :
var button_prop = {id:id, type:type};
- 120 :
- 121 :
if (act) button_prop.act = act;
- 122 :
if (sel) button_prop.sel = sel;
- 123 :
if (over) button_prop.over = over;
- 124 :
- 125 :
if (!this.buttons[command])
- 126 :
this.buttons[command] = [];
- 127 :
- 128 :
this.buttons[command].push(button_prop);
- 129 :
- 130 :
if (this.loaded) {
- 131 :
this.init_button(command, button_prop);
- 132 :
this.set_button(command, (this.commands[command] ? 'act' : 'pas'));
- 133 :
}
- 134 :
};
- 135 :
- 136 :
// register a button with popup menu, to set its state according to the state of all commands in the menu
- 137 :
this.register_menu_button = function(button, menu_id)
- 138 :
{
- 139 :
if (this.menu_buttons[menu_id]) {
- 140 :
this.menu_buttons[menu_id][0].push(button);
- 141 :
}
- 142 :
else {
- 143 :
var commands = [];
- 144 :
$('#' + menu_id).find('a').each(function() {
- 145 :
var command, link = $(this), onclick = link.attr('onclick');
- 146 :
- 147 :
if (onclick && String(onclick).match(/rcmail\.command\(\'([^']+)/))
- 148 :
command = RegExp.$1;
- 149 :
else
- 150 :
command = function() { return link.is('.active'); };
- 151 :
- 152 :
commands.push(command);
- 153 :
});
- 154 :
- 155 :
if (commands.length)
- 156 :
this.menu_buttons[menu_id] = [[button], commands];
- 157 :
}
- 158 :
- 159 :
this.set_menu_buttons();
- 160 :
};
- 161 :
- 162 :
// set state of a menu button according to state of all menu actions
- 163 :
this.set_menu_buttons = function()
- 164 :
{
- 165 :
// Use timeouts to not block and set menu button states only once
- 166 :
clearTimeout(this.menu_buttons_timeout);
- 167 :
this.menu_buttons_timeout = setTimeout(function() {
- 168 :
$.each(ref.menu_buttons, function() {
- 169 :
var disabled = true;
- 170 :
$.each(this[1], function() {
- 171 :
var is_func = typeof(this) == 'function';
- 172 :
if ((is_func && this()) || (!is_func && ref.commands[this])) {
- 173 :
return disabled = false;
- 174 :
}
- 175 :
});
- 176 :
- 177 :
$(this[0]).add($(this[0]).parent('.dropbutton'))
- 178 :
.addClass(disabled ? 'disabled' : 'active')
- 179 :
.removeClass(disabled ? 'active' : 'disabled');
- 180 :
});
- 181 :
}, 50);
- 182 :
};
- 183 :
- 184 :
// register a specific gui object
- 185 :
this.gui_object = function(name, id)
- 186 :
{
- 187 :
this.gui_objects[name] = this.loaded ? rcube_find_object(id) : id;
- 188 :
};
- 189 :
- 190 :
// register a container object
- 191 :
this.gui_container = function(name, id)
- 192 :
{
- 193 :
this.gui_containers[name] = id;
- 194 :
};
- 195 :
- 196 :
// add a GUI element (html node) to a specified container
- 197 :
this.add_element = function(elm, container)
- 198 :
{
- 199 :
if (this.gui_containers[container] && this.gui_containers[container].jquery)
- 200 :
this.gui_containers[container].append(elm);
- 201 :
};
- 202 :
- 203 :
// register an external handler for a certain command
- 204 :
this.register_command = function(command, callback, enable)
- 205 :
{
- 206 :
this.command_handlers[command] = callback;
- 207 :
- 208 :
if (enable)
- 209 :
this.enable_command(command, true);
- 210 :
};
- 211 :
- 212 :
// execute the given script on load
- 213 :
this.add_onload = function(f)
- 214 :
{
- 215 :
this.onloads.push(f);
- 216 :
};
- 217 :
- 218 :
// initialize webmail client
- 219 :
this.init = function()
- 220 :
{
- 221 :
var n;
- 222 :
this.task = this.env.task;
- 223 :
- 224 :
if (!this.env.blankpage)
- 225 :
this.env.blankpage = 'about:blank';
- 226 :
- 227 :
// find all registered gui containers
- 228 :
for (n in this.gui_containers)
- 229 :
this.gui_containers[n] = $('#'+this.gui_containers[n]);
- 230 :
- 231 :
// find all registered gui objects
- 232 :
for (n in this.gui_objects)
- 233 :
this.gui_objects[n] = rcube_find_object(this.gui_objects[n]);
- 234 :
- 235 :
// init registered buttons
- 236 :
this.init_buttons();
- 237 :
- 238 :
// tell parent window that this frame is loaded
- 239 :
if (this.is_framed()) {
- 240 :
parent.rcmail.unlock_frame();
- 241 :
}
- 242 :
- 243 :
// enable general commands
- 244 :
this.enable_command('close', 'logout', 'mail', 'addressbook', 'settings', 'save-pref',
- 245 :
'compose', 'undo', 'about', 'switch-task', 'menu-open', 'menu-close', 'menu-save', true);
- 246 :
- 247 :
// set active task button
- 248 :
this.set_button(this.task, 'sel');
- 249 :
- 250 :
if (this.env.permaurl)
- 251 :
this.enable_command('permaurl', 'extwin', true);
- 252 :
- 253 :
switch (this.task) {
- 254 :
- 255 :
case 'mail':
- 256 :
// enable mail commands
- 257 :
this.enable_command('list', 'checkmail', 'add-contact', 'search', 'reset-search', 'collapse-folder', 'import-messages', true);
- 258 :
- 259 :
if (this.gui_objects.messagelist) {
- 260 :
// setup message list cols
- 261 :
this.msglist_setup(this.env.layout);
- 262 :
- 263 :
this.env.widescreen_list_template = [
- 264 :
{className: 'threads', cells: ['threads']},
- 265 :
// PAMELA - Ajout de la priorité dans l'affichage des mails
- 266 :
{className: 'subject', cells: ['fromto', 'date', 'size', 'status', 'subject', 'priority']},
- 267 :
{className: 'flags', cells: ['flag', 'attachment']}
- 268 :
];
- 269 :
- 270 :
this.message_list = new rcube_list_widget(this.gui_objects.messagelist, {
- 271 :
multiselect:true, multiexpand:true, draggable:true, keyboard:true,
- 272 :
column_movable:this.env.col_movable, dblclick_time:this.dblclick_time
- 273 :
});
- 274 :
this.message_list
- 275 :
.addEventListener('initrow', function(o) { ref.init_message_row(o); })
- 276 :
.addEventListener('dblclick', function(o) { ref.msglist_dbl_click(o); })
- 277 :
.addEventListener('keypress', function(o) { ref.msglist_keypress(o); })
- 278 :
.addEventListener('select', function(o) { ref.msglist_select(o); })
- 279 :
.addEventListener('dragstart', function(o) { ref.drag_start(o); })
- 280 :
.addEventListener('dragmove', function(e) { ref.drag_move(e); })
- 281 :
.addEventListener('dragend', function(e) { ref.drag_end(e); })
- 282 :
.addEventListener('expandcollapse', function(o) { ref.msglist_expand(o); })
- 283 :
.addEventListener('column_replace', function(o) { ref.msglist_set_coltypes(o); })
- 284 :
.init();
- 285 :
- 286 :
//Pamela - Rendre le drag'n'drop possible pour les messages dans une frame.
- 287 :
if (parent !== window)
- 288 :
{
- 289 :
setTimeout(() => {
- 290 :
this.message_list.draggable = true;
- 291 :
}, 111);
- 292 :
}
- 293 :
- 294 :
// TODO: this should go into the list-widget code
- 295 :
$(this.message_list.thead).on('click', 'a.sortcol', function(e){
- 296 :
return ref.command('sort', $(this).attr('rel'), this);
- 297 :
});
- 298 :
- 299 :
this.enable_command('toggle_status', 'toggle_flag', 'sort', true);
- 300 :
this.enable_command('set-listmode', this.env.threads && !this.is_multifolder_listing());
- 301 :
- 302 :
// load messages
- 303 :
var searchfilter = $(this.gui_objects.search_filter).val();
- 304 :
if (searchfilter && searchfilter != 'ALL')
- 305 :
this.filter_mailbox(searchfilter);
- 306 :
else
- 307 :
this.command('list');
- 308 :
- 309 :
$(this.gui_objects.qsearchbox).val(this.env.search_text).focusin(function() { ref.message_list.blur(); });
- 310 :
}
- 311 :
- 312 :
this.set_button_titles();
- 313 :
- 314 :
this.env.message_commands = ['show', 'reply', 'reply-all', 'reply-list',
- 315 :
'move', 'copy', 'delete', 'open', 'mark', 'edit', 'viewsource', 'bounce',
- 316 :
'print', 'load-attachment', 'download-attachment', 'show-headers', 'hide-headers', 'download',
- 317 :
'forward', 'forward-inline', 'forward-attachment', 'change-format'];
- 318 :
- 319 :
if (this.env.action == 'show' || this.env.action == 'preview') {
- 320 :
this.enable_command(this.env.message_commands, this.env.uid);
- 321 :
this.enable_command('reply-list', this.env.list_post);
- 322 :
- 323 :
if (this.env.action == 'show') {
- 324 :
this.http_request('pagenav', {_uid: this.env.uid, _mbox: this.env.mailbox, _search: this.env.search_request},
- 325 :
this.display_message('', 'loading'));
- 326 :
}
- 327 :
- 328 :
if (this.env.mail_read_time > 0)
- 329 :
setTimeout(function() {
- 330 :
ref.http_post('mark', {_uid: ref.env.uid, _flag: 'read', _mbox: ref.env.mailbox, _quiet: 1});
- 331 :
}, this.env.mail_read_time * 1000);
- 332 :
- 333 :
if (this.env.blockedobjects) {
- 334 :
$(this.gui_objects.remoteobjectsmsg).show();
- 335 :
this.enable_command('load-remote', true);
- 336 :
}
- 337 :
- 338 :
// make preview/message frame visible
- 339 :
if (this.env.action == 'preview' && this.is_framed()) {
- 340 :
this.enable_command('compose', 'add-contact', false);
- 341 :
parent.rcmail.show_contentframe(true);
- 342 :
}
- 343 :
- 344 :
if ($.inArray('flagged', this.env.message_flags) >= 0) {
- 345 :
$(document.body).addClass('status-flagged');
- 346 :
}
- 347 :
- 348 :
// initialize drag-n-drop on attachments, so they can e.g.
- 349 :
// be dropped into mail compose attachments in another window
- 350 :
if (this.gui_objects.attachments)
- 351 :
$('li > a', this.gui_objects.attachments).not('.drop').on('dragstart', function(e) {
- 352 :
var n, href = this.href, dt = e.originalEvent.dataTransfer;
- 353 :
if (dt) {
- 354 :
// inject username to the uri
- 355 :
href = href.replace(/^https?:\/\//, function(m) { return m + urlencode(ref.env.username) + '@'});
- 356 :
// cleanup the node to get filename without the size test
- 357 :
n = $(this).clone();
- 358 :
n.children().remove();
- 359 :
- 360 :
dt.setData('roundcube-uri', href);
- 361 :
dt.setData('roundcube-name', n.text().trim());
- 362 :
}
- 363 :
});
- 364 :
- 365 :
this.check_mailvelope(this.env.action);
- 366 :
}
- 367 :
else if (this.env.action == 'compose') {
- 368 :
this.env.address_group_stack = [];
- 369 :
this.env.compose_commands = ['send-attachment', 'remove-attachment', 'send', 'cancel',
- 370 :
'toggle-editor', 'list-addresses', 'pushgroup', 'search', 'reset-search', 'extwin',
- 371 :
'insert-response', 'save-response', 'menu-open', 'menu-close', 'load-attachment',
- 372 :
'download-attachment', 'open-attachment', 'rename-attachment'];
- 373 :
- 374 :
if (this.env.drafts_mailbox)
- 375 :
this.env.compose_commands.push('savedraft')
- 376 :
- 377 :
this.enable_command(this.env.compose_commands, true);
- 378 :
- 379 :
// add more commands (not enabled)
- 380 :
$.merge(this.env.compose_commands, ['add-recipient', 'firstpage', 'previouspage', 'nextpage', 'lastpage']);
- 381 :
- 382 :
if (window.googie) {
- 383 :
this.env.editor_config.spellchecker = googie;
- 384 :
this.env.editor_config.spellcheck_observer = function(s) { ref.spellcheck_state(); };
- 385 :
- 386 :
this.env.compose_commands.push('spellcheck')
- 387 :
this.enable_command('spellcheck', true);
- 388 :
}
- 389 :
- 390 :
// initialize HTML editor
- 391 :
this.editor_init(this.env.editor_config, this.env.composebody);
- 392 :
- 393 :
// init canned response functions
- 394 :
if (this.gui_objects.responseslist) {
- 395 :
$('a.insertresponse', this.gui_objects.responseslist)
- 396 :
.attr('unselectable', 'on')
- 397 :
.mousedown(function(e) { return rcube_event.cancel(e); })
- 398 :
.on('mouseup keypress', function(e) {
- 399 :
if (e.type == 'mouseup' || rcube_event.get_keycode(e) == 13) {
- 400 :
ref.command('insert-response', $(this).attr('rel'));
- 401 :
$(document.body).trigger('mouseup'); // hides the menu
- 402 :
return rcube_event.cancel(e);
- 403 :
}
- 404 :
});
- 405 :
- 406 :
// avoid textarea loosing focus when hitting the save-response button/link
- 407 :
$.each(this.buttons['save-response'] || [], function (i, v) {
- 408 :
$('#' + v.id).mousedown(function(e){ return rcube_event.cancel(e); })
- 409 :
});
- 410 :
}
- 411 :
- 412 :
// init message compose form
- 413 :
this.init_messageform();
- 414 :
- 415 :
this.check_mailvelope(this.env.action);
- 416 :
}
- 417 :
else if (this.env.action == 'bounce') {
- 418 :
this.init_messageform_inputs();
- 419 :
this.env.compose_commands = [];
- 420 :
}
- 421 :
else if (this.env.action == 'get') {
- 422 :
this.enable_command('download', true);
- 423 :
this.enable_command('image-scale', 'image-rotate', !!/^image\//.test(this.env.mimetype));
- 424 :
- 425 :
// Mozilla's PDF.js viewer does not allow printing from host page (#5125)
- 426 :
// to minimize user confusion we disable the Print button on Firefox < 75
- 427 :
this.enable_command('print', this.env.mimetype != 'application/pdf' || !bw.mz || bw.vendver >= 75);
- 428 :
- 429 :
if (this.env.is_message) {
- 430 :
this.enable_command('reply', 'reply-all', 'edit', 'viewsource',
- 431 :
'forward', 'forward-inline', 'forward-attachment', 'bounce', true);
- 432 :
if (this.env.list_post)
- 433 :
this.enable_command('reply-list', true);
- 434 :
}
- 435 :
- 436 :
// center and scale the image in preview frame
- 437 :
// TODO: Find a better way. Onload is late, also we could use embed.css
- 438 :
if (this.env.mimetype.startsWith('image/'))
- 439 :
$(this.gui_objects.messagepartframe).on('load', function() {
- 440 :
var contents = $(this).contents();
- 441 :
- 442 :
// do not apply styles to an error page (with no image)
- 443 :
if (contents.find('img').length)
- 444 :
contents.find('head').append(
- 445 :
'<style type="text/css">'
- 446 :
+ 'img { max-width:100%; max-height:100%; } ' // scale
- 447 :
+ 'body { display:flex; align-items:center; justify-content:center; height:100%; margin:0; }' // align
- 448 :
+ '</style>'
- 449 :
);
- 450 :
});
- 451 :
}
- 452 :
// show printing dialog unless decryption must be done first
- 453 :
else if (this.env.action == 'print' && this.env.uid) {
- 454 :
this.check_mailvelope(this.env.action);
- 455 :
if (!this.env.is_pgp_content && !this.env.pgp_mime_part) {
- 456 :
this.print_dialog();
- 457 :
}
- 458 :
}
- 459 :
- 460 :
// get unread count for each mailbox
- 461 :
if (this.gui_objects.mailboxlist) {
- 462 :
this.env.unread_counts = {};
- 463 :
this.gui_objects.folderlist = this.gui_objects.mailboxlist;
- 464 :
this.http_request('getunread', {_page: this.env.current_page});
- 465 :
}
- 466 :
- 467 :
// init address book widget
- 468 :
if (this.gui_objects.contactslist) {
- 469 :
this.contact_list = new rcube_list_widget(this.gui_objects.contactslist,
- 470 :
{ multiselect:true, draggable:false, keyboard:true });
- 471 :
this.contact_list
- 472 :
.addEventListener('initrow', function(o) { ref.triggerEvent('insertrow', { cid:o.uid, row:o }); })
- 473 :
.addEventListener('select', function(o) { ref.compose_recipient_select(o); })
- 474 :
.addEventListener('dblclick', function(o) { ref.compose_add_recipient(); })
- 475 :
.addEventListener('keypress', function(o) {
- 476 :
if (o.key_pressed == o.ENTER_KEY) {
- 477 :
if (!ref.compose_add_recipient()) {
- 478 :
// execute link action on <enter> if not a recipient entry
- 479 :
if (o.last_selected && String(o.last_selected).charAt(0) == 'G') {
- 480 :
$(o.rows[o.last_selected].obj).find('a').first().click();
- 481 :
}
- 482 :
}
- 483 :
}
- 484 :
})
- 485 :
.init();
- 486 :
- 487 :
// remember last focused address field
- 488 :
$('#_to,#_cc,#_bcc').focus(function() { ref.env.focused_field = this; });
- 489 :
}
- 490 :
- 491 :
if (this.gui_objects.addressbookslist) {
- 492 :
this.gui_objects.folderlist = this.gui_objects.addressbookslist;
- 493 :
this.enable_command('list-addresses', true);
- 494 :
}
- 495 :
- 496 :
// ask user to send MDN
- 497 :
if (this.env.mdn_request && this.env.uid) {
- 498 :
this.mdn_request_dialog(this.env.uid, this.env.mailbox);
- 499 :
}
- 500 :
- 501 :
// detect browser capabilities
- 502 :
if (!this.is_framed() && !this.env.extwin)
- 503 :
this.browser_capabilities_check();
- 504 :
- 505 :
break;
- 506 :
- 507 :
case 'addressbook':
- 508 :
this.env.address_group_stack = [];
- 509 :
- 510 :
if (this.gui_objects.folderlist)
- 511 :
this.env.contactfolders = $.extend($.extend({}, this.env.address_sources), this.env.contactgroups);
- 512 :
- 513 :
this.enable_command('add', 'import', this.env.writable_source);
- 514 :
this.enable_command('list', 'listgroup', 'pushgroup', 'popgroup', 'listsearch', 'search', 'reset-search', 'advanced-search', true);
- 515 :
- 516 :
if (this.gui_objects.contactslist) {
- 517 :
this.contact_list = new rcube_list_widget(this.gui_objects.contactslist,
- 518 :
{multiselect:true, draggable:this.gui_objects.folderlist?true:false, keyboard:true});
- 519 :
this.contact_list
- 520 :
.addEventListener('initrow', function(o) { ref.triggerEvent('insertrow', { cid:o.uid, row:o }); })
- 521 :
.addEventListener('keypress', function(o) { ref.list_keypress(o); })
- 522 :
.addEventListener('select', function(o) { ref.contactlist_select(o); })
- 523 :
.addEventListener('dragstart', function(o) { ref.drag_start(o); })
- 524 :
.addEventListener('dragmove', function(e) { ref.drag_move(e); })
- 525 :
.addEventListener('dragend', function(e) { ref.drag_end(e); })
- 526 :
.init();
- 527 :
- 528 :
$(this.gui_objects.qsearchbox).focusin(function() { ref.contact_list.blur(); });
- 529 :
- 530 :
this.update_group_commands();
- 531 :
this.command('list');
- 532 :
}
- 533 :
- 534 :
if (this.gui_objects.savedsearchlist) {
- 535 :
this.savedsearchlist = new rcube_treelist_widget(this.gui_objects.savedsearchlist, {
- 536 :
id_prefix: 'rcmli',
- 537 :
id_encode: this.html_identifier_encode,
- 538 :
id_decode: this.html_identifier_decode
- 539 :
});
- 540 :
- 541 :
this.savedsearchlist.addEventListener('select', function(node) {
- 542 :
ref.triggerEvent('selectfolder', { folder:node.id, prefix:'rcmli' }); });
- 543 :
}
- 544 :
- 545 :
this.set_page_buttons();
- 546 :
- 547 :
if (this.env.cid) {
- 548 :
this.enable_command('show', 'edit', 'qrcode', true);
- 549 :
// register handlers for group assignment via checkboxes
- 550 :
if (this.gui_objects.editform) {
- 551 :
$('input.groupmember').change(function() {
- 552 :
ref.group_member_change(this.checked ? 'add' : 'del', ref.env.cid, ref.env.source, this.value);
- 553 :
});
- 554 :
}
- 555 :
}
- 556 :
- 557 :
if (this.gui_objects.editform) {
- 558 :
this.enable_command('save', true);
- 559 :
if (this.env.action == 'add' || this.env.action == 'edit' || this.env.action == 'search')
- 560 :
this.init_contact_form();
- 561 :
}
- 562 :
else if (this.env.action == 'print') {
- 563 :
this.print_dialog();
- 564 :
}
- 565 :
- 566 :
break;
- 567 :
- 568 :
case 'settings':
- 569 :
this.enable_command('show', 'save', true);
- 570 :
- 571 :
if (this.env.action == 'identities') {
- 572 :
this.enable_command('add', this.env.identities_level < 2);
- 573 :
}
- 574 :
else if (this.env.action == 'edit-identity' || this.env.action == 'add-identity') {
- 575 :
this.enable_command('save', 'edit', 'toggle-editor', true);
- 576 :
this.enable_command('delete', this.env.identities_level < 2);
- 577 :
- 578 :
// initialize HTML editor
- 579 :
this.editor_init(this.env.editor_config, 'rcmfd_signature');
- 580 :
- 581 :
if (this.env.action == 'edit-identity') {
- 582 :
this.check_mailvelope(this.env.action);
- 583 :
}
- 584 :
}
- 585 :
else if (this.env.action == 'folders') {
- 586 :
this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', true);
- 587 :
}
- 588 :
else if (this.env.action == 'edit-folder' && this.gui_objects.editform) {
- 589 :
this.enable_command('save', 'folder-size', true);
- 590 :
parent.rcmail.env.exists = this.env.messagecount;
- 591 :
parent.rcmail.enable_command('purge', this.env.messagecount);
- 592 :
}
- 593 :
else if (this.env.action == 'responses') {
- 594 :
this.enable_command('add', true);
- 595 :
}
- 596 :
- 597 :
if (this.gui_objects.identitieslist) {
- 598 :
this.identity_list = new rcube_list_widget(this.gui_objects.identitieslist,
- 599 :
{multiselect:false, draggable:false, keyboard:true});
- 600 :
this.identity_list
- 601 :
.addEventListener('select', function(o) { ref.identity_select(o); })
- 602 :
.addEventListener('keypress', function(o) { ref.list_keypress(o); })
- 603 :
.init()
- 604 :
.focus();
- 605 :
}
- 606 :
else if (this.gui_objects.sectionslist) {
- 607 :
this.sections_list = new rcube_list_widget(this.gui_objects.sectionslist, {multiselect:false, draggable:false, keyboard:true});
- 608 :
this.sections_list
- 609 :
.addEventListener('select', function(o) { ref.section_select(o); })
- 610 :
.init()
- 611 :
.focus();
- 612 :
}
- 613 :
else if (this.gui_objects.subscriptionlist) {
- 614 :
this.init_subscription_list();
- 615 :
}
- 616 :
else if (this.gui_objects.responseslist) {
- 617 :
this.responses_list = new rcube_list_widget(this.gui_objects.responseslist, {multiselect:false, draggable:false, keyboard:true});
- 618 :
this.responses_list
- 619 :
.addEventListener('select', function(o) { ref.response_select(o); })
- 620 :
.addEventListener('keypress', function(o) { ref.list_keypress(o); })
- 621 :
.init()
- 622 :
.focus();
- 623 :
}
- 624 :
- 625 :
break;
- 626 :
- 627 :
case 'login':
- 628 :
var tz, tz_name,
- 629 :
input_user = $('#rcmloginuser'),
- 630 :
input_tz = $('#rcmlogintz');
- 631 :
- 632 :
if (input_user.val() == '')
- 633 :
input_user.focus();
- 634 :
else
- 635 :
$('#rcmloginpwd').focus();
- 636 :
- 637 :
// detect client timezone
- 638 :
if (window.jstz && (tz = jstz.determine()))
- 639 :
tz_name = tz.name();
- 640 :
- 641 :
input_tz.val(tz_name ? tz_name : (new Date().getStdTimezoneOffset() / -60));
- 642 :
- 643 :
// display 'loading' message on form submit, lock submit button
- 644 :
$('form').submit(function () {
- 645 :
$('[type=submit]', this).prop('disabled', true);
- 646 :
ref.clear_messages();
- 647 :
ref.display_message('', 'loading');
- 648 :
});
- 649 :
- 650 :
break;
- 651 :
}
- 652 :
- 653 :
// select first input field in an edit form
- 654 :
if (this.gui_objects.editform)
- 655 :
$("input,select,textarea", this.gui_objects.editform)
- 656 :
.not(':hidden').not(':disabled').first().select().focus();
- 657 :
- 658 :
// prevent from form submit with Enter key in file input fields
- 659 :
if (bw.ie)
- 660 :
$('input[type=file]').keydown(function(e) { if (e.keyCode == '13') e.preventDefault(); });
- 661 :
- 662 :
// flag object as complete
- 663 :
this.loaded = true;
- 664 :
this.env.lastrefresh = new Date();
- 665 :
- 666 :
// show message
- 667 :
if (this.pending_message)
- 668 :
this.display_message.apply(this, this.pending_message);
- 669 :
- 670 :
// init treelist widget
- 671 :
if (this.gui_objects.folderlist && window.rcube_treelist_widget
- 672 :
// some plugins may load rcube_treelist_widget and there's one case
- 673 :
// when this will cause problems - addressbook widget in compose,
- 674 :
// which already has been initialized using rcube_list_widget
- 675 :
&& this.gui_objects.folderlist != this.gui_objects.addressbookslist
- 676 :
) {
- 677 :
this.treelist = new rcube_treelist_widget(this.gui_objects.folderlist, {
- 678 :
selectable: true,
- 679 :
id_prefix: 'rcmli',
- 680 :
parent_focus: true,
- 681 :
id_encode: this.html_identifier_encode,
- 682 :
id_decode: this.html_identifier_decode,
- 683 :
check_droptarget: function(node) { return !node.virtual && ref.check_droptarget(node.id) }
- 684 :
});
- 685 :
- 686 :
this.treelist
- 687 :
.addEventListener('collapse', function(node) { ref.folder_collapsed(node) })
- 688 :
.addEventListener('expand', function(node) { ref.folder_collapsed(node) })
- 689 :
.addEventListener('beforeselect', function(node) { return !ref.busy; })
- 690 :
.addEventListener('select', function(node) {
- 691 :
ref.triggerEvent('selectfolder', { folder:node.id, prefix:'rcmli' });
- 692 :
ref.mark_all_read_state();
- 693 :
});
- 694 :
}
- 695 :
- 696 :
// activate html5 file drop feature (if browser supports it and if configured)
- 697 :
if (this.gui_objects.filedrop && this.env.filedrop && window.FormData) {
- 698 :
$(document.body).on('dragover dragleave drop', function(e) { return ref.document_drag_hover(e, e.type == 'dragover'); });
- 699 :
$(this.gui_objects.filedrop).addClass('droptarget')
- 700 :
.on('dragover dragleave', function(e) { return ref.file_drag_hover(e, e.type == 'dragover'); })
- 701 :
.get(0).addEventListener('drop', function(e) { return ref.file_dropped(e); }, false);
- 702 :
}
- 703 :
- 704 :
// catch document (and iframe) mouse clicks
- 705 :
var body_mouseup = function(e) { return ref.doc_mouse_up(e); };
- 706 :
$(document.body)
- 707 :
.mouseup(body_mouseup)
- 708 :
.keydown(function(e) { return ref.doc_keypress(e); });
- 709 :
- 710 :
rcube_webmail.set_iframe_events({mouseup: body_mouseup});
- 711 :
- 712 :
// trigger init event hook
- 713 :
this.triggerEvent('init', { task:this.task, action:this.env.action });
- 714 :
- 715 :
// execute all foreign onload scripts
- 716 :
// @deprecated
- 717 :
for (n in this.onloads) {
- 718 :
if (typeof this.onloads[n] === 'string')
- 719 :
eval(this.onloads[n]);
- 720 :
else if (typeof this.onloads[n] === 'function')
- 721 :
this.onloads[n]();
- 722 :
}
- 723 :
- 724 :
// register menu buttons
- 725 :
$('[data-popup]').each(function() { ref.register_menu_button(this, $(this).data('popup')); });
- 726 :
- 727 :
// start keep-alive and refresh intervals
- 728 :
this.start_refresh();
- 729 :
this.start_keepalive();
- 730 :
};
- 731 :
- 732 :
this.log = function(msg)
- 733 :
{
- 734 :
if (this.env.devel_mode && window.console && console.log)
- 735 :
console.log(msg);
- 736 :
};
- 737 :
- 738 :
/*********************************************************/
- 739 :
/********* client command interface *********/
- 740 :
/*********************************************************/
- 741 :
- 742 :
// execute a specific command on the web client
- 743 :
this.command = function(command, props, obj, event, allow_disabled)
- 744 :
{
- 745 :
var ret;
- 746 :
- 747 :
if (obj && obj.blur && !(event && rcube_event.is_keyboard(event)))
- 748 :
obj.blur();
- 749 :
- 750 :
// do nothing if interface is locked by another command
- 751 :
// with exception for searching reset and menu
- 752 :
if (this.busy && !(command == 'reset-search' && this.last_command == 'search') && !command.match(/^menu-/))
- 753 :
return false;
- 754 :
- 755 :
// let the browser handle this click (shift/ctrl usually opens the link in a new window/tab)
- 756 :
if ((obj && obj.href && String(obj.href).indexOf('#') < 0) && rcube_event.get_modifier(event)) {
- 757 :
return true;
- 758 :
}
- 759 :
- 760 :
// command not supported or allowed
- 761 :
if (!allow_disabled && !this.commands[command]) {
- 762 :
// pass command to parent window
- 763 :
if (this.is_framed())
- 764 :
parent.rcmail.command(command, props);
- 765 :
- 766 :
return false;
- 767 :
}
- 768 :
- 769 :
// check input before leaving compose step
- 770 :
if (this.task == 'mail' && this.env.action == 'compose' && !this.env.server_error && command != 'save-pref'
- 771 :
&& ($.inArray(command, this.env.compose_commands) < 0 || command.startsWith('compose-encrypted') && ref.mailvelope_editor)
- 772 :
&& !this.compose_skip_unsavedcheck
- 773 :
) {
- 774 :
if (!this.env.is_sent && this.cmp_hash != this.compose_field_hash()) {
- 775 :
this.confirm_dialog(this.get_label('notsentwarning'), 'discard', function() {
- 776 :
// remove copy from local storage if compose screen is left intentionally
- 777 :
ref.remove_compose_data(ref.env.compose_id);
- 778 :
ref.compose_skip_unsavedcheck = true;
- 779 :
ref.command(command, props, obj, event);
- 780 :
});
- 781 :
- 782 :
return false;
- 783 :
}
- 784 :
}
- 785 :
- 786 :
this.last_command = command;
- 787 :
this.command_aborted = false;
- 788 :
- 789 :
// trigger plugin hooks
- 790 :
this.triggerEvent('actionbefore', {props: props, action: command, originalEvent: event});
- 791 :
- 792 :
if ((ret = this.triggerEvent('before' + command, props || event)) !== undefined) {
- 793 :
// abort if one of the handlers returned false
- 794 :
if (ret === false)
- 795 :
return false;
- 796 :
- 797 :
props = ret;
- 798 :
}
- 799 :
- 800 :
// process external commands
- 801 :
if (typeof this.command_handlers[command] === 'function') {
- 802 :
ret = this.command_handlers[command](props, obj, event);
- 803 :
}
- 804 :
else if (typeof this.command_handlers[command] === 'string') {
- 805 :
ret = window[this.command_handlers[command]](props, obj, event);
- 806 :
}
- 807 :
// process internal commands
- 808 :
else {
- 809 :
ret = this.command_handler(command, props, obj, event);
- 810 :
}
- 811 :
- 812 :
if (!this.command_aborted && this.triggerEvent('after' + command, props) === false)
- 813 :
ret = false;
- 814 :
- 815 :
this.triggerEvent('actionafter', {props: props, action: command, aborted: this.command_aborted, ret: ret, originalEvent: event});
- 816 :
- 817 :
if (ret === false)
- 818 :
return false;
- 819 :
- 820 :
if ((obj && ret !== true) || this.command_aborted === true)
- 821 :
return false;
- 822 :
- 823 :
return true;
- 824 :
};
- 825 :
- 826 :
// execute a specific known command
- 827 :
this.command_handler = function(command, props, obj, event)
- 828 :
{
- 829 :
var uid, cid, url, flag;
- 830 :
- 831 :
// process internal command
- 832 :
switch (command) {
- 833 :
- 834 :
// commands to switch task
- 835 :
case 'logout':
- 836 :
case 'mail':
- 837 :
case 'addressbook':
- 838 :
case 'settings':
- 839 :
this.switch_task(command);
- 840 :
break;
- 841 :
- 842 :
case 'about':
- 843 :
this.redirect('?_task=settings&_action=about', false);
- 844 :
break;
- 845 :
- 846 :
case 'permaurl':
- 847 :
if (obj && obj.href && obj.target)
- 848 :
return true;
- 849 :
if (this.env.permaurl)
- 850 :
parent.location.href = this.env.permaurl;
- 851 :
break;
- 852 :
- 853 :
case 'extwin':
- 854 :
if (this.env.action == 'compose') {
- 855 :
var form = this.gui_objects.messageform,
- 856 :
win = this.open_window('');
- 857 :
- 858 :
if (win) {
- 859 :
this.save_compose_form_local();
- 860 :
this.compose_skip_unsavedcheck = true;
- 861 :
$("[name='_action']", form).val('compose');
- 862 :
form.action = this.url('mail/compose', { _id: this.env.compose_id, _extwin: 1 });
- 863 :
form.target = win.name;
- 864 :
form.submit();
- 865 :
}
- 866 :
}
- 867 :
else {
- 868 :
this.open_window(this.env.permaurl, true);
- 869 :
}
- 870 :
break;
- 871 :
- 872 :
case 'change-format':
- 873 :
url = this.env.permaurl + '&_format=' + props;
- 874 :
- 875 :
if (this.env.action == 'preview')
- 876 :
url = url.replace(/_action=show/, '_action=preview') + '&_framed=1';
- 877 :
if (this.env.extwin)
- 878 :
url += '&_extwin=1';
- 879 :
- 880 :
location.href = url;
- 881 :
break;
- 882 :
- 883 :
case 'menu-open':
- 884 :
if (props && props.menu == 'attachmentmenu') {
- 885 :
var mimetype = this.env.attachments[props.id];
- 886 :
if (mimetype && mimetype.mimetype) // in compose format is different
- 887 :
mimetype = mimetype.mimetype;
- 888 :
this.enable_command('open-attachment', mimetype && this.env.mimetypes && $.inArray(mimetype, this.env.mimetypes) >= 0);
- 889 :
}
- 890 :
this.show_menu(props, props.show || undefined, event);
- 891 :
break;
- 892 :
- 893 :
case 'menu-close':
- 894 :
this.hide_menu(props, event);
- 895 :
break;
- 896 :
- 897 :
case 'menu-save':
- 898 :
this.triggerEvent(command, {props:props, originalEvent:event});
- 899 :
return false;
- 900 :
- 901 :
case 'open':
- 902 :
if (uid = this.get_single_uid()) {
- 903 :
obj.href = this.url('show', this.params_from_uid(uid, {_extwin: 1}));
- 904 :
return true;
- 905 :
}
- 906 :
break;
- 907 :
- 908 :
case 'close':
- 909 :
if (this.env.extwin)
- 910 :
window.close();
- 911 :
break;
- 912 :
- 913 :
case 'list':
- 914 :
if (props && props != '') {
- 915 :
this.reset_qsearch(true);
- 916 :
}
- 917 :
if (this.env.action == 'compose' && this.env.extwin) {
- 918 :
window.close();
- 919 :
}
- 920 :
else if (this.task == 'mail') {
- 921 :
this.list_mailbox(props, props ? 1 : '');
- 922 :
this.set_button_titles();
- 923 :
}
- 924 :
else if (this.task == 'addressbook')
- 925 :
this.list_contacts(props);
- 926 :
break;
- 927 :
- 928 :
case 'set-listmode':
- 929 :
this.set_list_options(null, undefined, undefined, props == 'threads' ? 1 : 0);
- 930 :
break;
- 931 :
- 932 :
case 'sort':
- 933 :
var sort_order = this.env.sort_order,
- 934 :
sort_col = !this.env.disabled_sort_col ? props : this.env.sort_col;
- 935 :
- 936 :
if (!this.env.disabled_sort_order)
- 937 :
sort_order = this.env.sort_col == sort_col && sort_order == 'ASC' ? 'DESC' : 'ASC';
- 938 :
- 939 :
// set table header and update env
- 940 :
this.set_list_sorting(sort_col, sort_order);
- 941 :
- 942 :
// reload message list
- 943 :
this.list_mailbox('', '', sort_col+'_'+sort_order);
- 944 :
break;
- 945 :
- 946 :
case 'nextpage':
- 947 :
this.list_page('next');
- 948 :
break;
- 949 :
- 950 :
case 'lastpage':
- 951 :
this.list_page('last');
- 952 :
break;
- 953 :
- 954 :
case 'previouspage':
- 955 :
this.list_page('prev');
- 956 :
break;
- 957 :
- 958 :
case 'firstpage':
- 959 :
this.list_page('first');
- 960 :
break;
- 961 :
- 962 :
case 'expunge':
- 963 :
if (this.env.exists)
- 964 :
this.expunge_mailbox(this.env.mailbox);
- 965 :
break;
- 966 :
- 967 :
case 'purge':
- 968 :
case 'empty-mailbox':
- 969 :
if (this.env.exists)
- 970 :
this.purge_mailbox(this.env.mailbox);
- 971 :
break;
- 972 :
- 973 :
// common commands used in multiple tasks
- 974 :
case 'show':
- 975 :
if (this.task == 'mail') {
- 976 :
uid = this.get_single_uid();
- 977 :
if (uid && (!this.env.uid || uid != this.env.uid)) {
- 978 :
var mbox = this.get_message_mailbox(uid);
- 979 :
if (mbox == this.env.drafts_mailbox)
- 980 :
this.open_compose_step({_draft_uid: uid, _mbox: mbox});
- 981 :
else
- 982 :
this.show_message(uid);
- 983 :
}
- 984 :
}
- 985 :
else if (this.task == 'addressbook') {
- 986 :
cid = props ? props : this.get_single_cid();
- 987 :
if (cid && !(this.env.action == 'show' && cid == this.env.cid))
- 988 :
this.load_contact(cid, 'show');
- 989 :
}
- 990 :
else if (this.task == 'settings') {
- 991 :
this.goto_url('settings/' + props, {_framed: 0});
- 992 :
}
- 993 :
break;
- 994 :
- 995 :
case 'add':
- 996 :
if (this.task == 'addressbook')
- 997 :
this.load_contact(0, 'add');
- 998 :
else if (this.task == 'settings' && this.env.action == 'responses')
- 999 :
this.load_response(0, 'add-response');
- 1000 :
else if (this.task == 'settings')
- 1001 :
this.load_identity(0, 'add-identity');
- 1002 :
break;
- 1003 :
- 1004 :
case 'edit':
- 1005 :
if (this.task == 'addressbook' && (cid = this.get_single_cid()))
- 1006 :
this.load_contact(cid, 'edit');
- 1007 :
else if (this.task == 'mail' && (uid = this.get_single_uid())) {
- 1008 :
url = { _mbox: this.get_message_mailbox(uid) };
- 1009 :
url[url._mbox == this.env.drafts_mailbox && props != 'new' ? '_draft_uid' : '_uid'] = uid;
- 1010 :
this.open_compose_step(url);
- 1011 :
}
- 1012 :
break;
- 1013 :
- 1014 :
case 'save':
- 1015 :
var input, form = this.gui_objects.editform;
- 1016 :
if (form) {
- 1017 :
// user prefs
- 1018 :
if ((input = $("[name='_pagesize']", form)) && input.length && isNaN(parseInt(input.val()))) {
- 1019 :
this.alert_dialog(this.get_label('nopagesizewarning'), function() {
- 1020 :
input.focus();
- 1021 :
});
- 1022 :
break;
- 1023 :
}
- 1024 :
// contacts/identities
- 1025 :
else {
- 1026 :
// reload form
- 1027 :
if (props == 'reload') {
- 1028 :
form.action += '&_reload=1';
- 1029 :
}
- 1030 :
else if (this.task == 'settings' && (this.env.identities_level % 2) == 0 &&
- 1031 :
(input = $("[name='_email']", form)) && input.length && !rcube_check_email(input.val())
- 1032 :
) {
- 1033 :
this.alert_dialog(this.get_label('noemailwarning'), function() {
- 1034 :
input.focus();
- 1035 :
});
- 1036 :
break;
- 1037 :
}
- 1038 :
}
- 1039 :
- 1040 :
// add selected source (on the list)
- 1041 :
if (parent.rcmail && parent.rcmail.env.source)
- 1042 :
form.action = this.add_url(form.action, '_orig_source', parent.rcmail.env.source);
- 1043 :
- 1044 :
form.submit();
- 1045 :
}
- 1046 :
break;
- 1047 :
- 1048 :
case 'delete':
- 1049 :
// mail task
- 1050 :
if (this.task == 'mail')
- 1051 :
this.delete_messages(event);
- 1052 :
// addressbook task
- 1053 :
else if (this.task == 'addressbook')
- 1054 :
this.delete_contacts();
- 1055 :
// settings: canned response
- 1056 :
else if (this.task == 'settings' && this.env.action == 'responses')
- 1057 :
this.delete_response();
- 1058 :
// settings: user identities
- 1059 :
else if (this.task == 'settings')
- 1060 :
this.delete_identity();
- 1061 :
break;
- 1062 :
- 1063 :
// mail task commands
- 1064 :
case 'move':
- 1065 :
case 'moveto': // deprecated
- 1066 :
if (this.task == 'mail')
- 1067 :
this.move_messages(props, event);
- 1068 :
else if (this.task == 'addressbook')
- 1069 :
this.move_contacts(props, event);
- 1070 :
break;
- 1071 :
- 1072 :
case 'copy':
- 1073 :
if (this.task == 'mail')
- 1074 :
this.copy_messages(props, event);
- 1075 :
else if (this.task == 'addressbook')
- 1076 :
this.copy_contacts(props, event);
- 1077 :
break;
- 1078 :
- 1079 :
case 'mark':
- 1080 :
if (props)
- 1081 :
this.mark_message(props);
- 1082 :
break;
- 1083 :
- 1084 :
case 'toggle_status':
- 1085 :
case 'toggle_flag':
- 1086 :
flag = command == 'toggle_flag' ? 'flagged' : 'read';
- 1087 :
- 1088 :
if (uid = props) {
- 1089 :
// toggle flagged/unflagged
- 1090 :
if (flag == 'flagged') {
- 1091 :
if (this.message_list.rows[uid].flagged)
- 1092 :
flag = 'unflagged';
- 1093 :
}
- 1094 :
// toggle read/unread
- 1095 :
else if (this.message_list.rows[uid].deleted)
- 1096 :
flag = 'undelete';
- 1097 :
else if (!this.message_list.rows[uid].unread)
- 1098 :
flag = 'unread';
- 1099 :
- 1100 :
this.mark_message(flag, uid);
- 1101 :
}
- 1102 :
- 1103 :
break;
- 1104 :
- 1105 :
case 'add-contact':
- 1106 :
this.add_contact(props);
- 1107 :
break;
- 1108 :
- 1109 :
case 'load-remote':
- 1110 :
if (this.env.uid) {
- 1111 :
if (props && this.env.sender) {
- 1112 :
this.add_contact(this.env.sender, true, props);
- 1113 :
break;
- 1114 :
}
- 1115 :
- 1116 :
this.show_message(this.env.uid, true, this.env.action == 'preview');
- 1117 :
}
- 1118 :
break;
- 1119 :
- 1120 :
case 'load-attachment':
- 1121 :
case 'open-attachment':
- 1122 :
case 'download-attachment':
- 1123 :
var params, mimetype = this.env.attachments[props];
- 1124 :
- 1125 :
if (this.env.action == 'compose') {
- 1126 :
params = {_file: props, _id: this.env.compose_id};
- 1127 :
mimetype = mimetype ? mimetype.mimetype : '';
- 1128 :
}
- 1129 :
else {
- 1130 :
params = {_mbox: this.env.mailbox, _uid: this.env.uid, _part: props};
- 1131 :
}
- 1132 :
- 1133 :
// open attachment in frame if it's of a supported mimetype
- 1134 :
if (command != 'download-attachment' && mimetype && this.env.mimetypes && $.inArray(mimetype, this.env.mimetypes) >= 0) {
- 1135 :
// Note: We disable _framed for proper X-Frame-Options:deny support (#6688)
- 1136 :
if (this.open_window(this.url('get', $.extend({_frame: 1, _framed: 0}, params))))
- 1137 :
break;
- 1138 :
}
- 1139 :
- 1140 :
params._download = 1;
- 1141 :
- 1142 :
// prevent from page unload warning in compose
- 1143 :
this.compose_skip_unsavedcheck = 1;
- 1144 :
this.goto_url('get', params, false, true);
- 1145 :
this.compose_skip_unsavedcheck = 0;
- 1146 :
- 1147 :
break;
- 1148 :
- 1149 :
case 'select-all':
- 1150 :
this.select_all_mode = props ? false : true;
- 1151 :
this.dummy_select = true; // prevent msg opening if there's only one msg on the list
- 1152 :
var list = this[this.task == 'addressbook' ? 'contact_list' : 'message_list'];
- 1153 :
if (props == 'invert')
- 1154 :
list.invert_selection();
- 1155 :
else
- 1156 :
list.select_all(props == 'page' ? '' : props);
- 1157 :
this.dummy_select = null;
- 1158 :
break;
- 1159 :
- 1160 :
case 'select-none':
- 1161 :
this.select_all_mode = false;
- 1162 :
this[this.task == 'addressbook' ? 'contact_list' : 'message_list'].clear_selection();
- 1163 :
break;
- 1164 :
- 1165 :
case 'expand-all':
- 1166 :
this.env.autoexpand_threads = 1;
- 1167 :
this.message_list.expand_all();
- 1168 :
break;
- 1169 :
- 1170 :
case 'expand-unread':
- 1171 :
this.env.autoexpand_threads = 2;
- 1172 :
this.message_list.collapse_all();
- 1173 :
this.expand_unread();
- 1174 :
break;
- 1175 :
- 1176 :
case 'collapse-all':
- 1177 :
this.env.autoexpand_threads = 0;
- 1178 :
this.message_list.collapse_all();
- 1179 :
break;
- 1180 :
- 1181 :
case 'nextmessage':
- 1182 :
if (this.env.next_uid)
- 1183 :
this.show_message(this.env.next_uid, false, this.env.action == 'preview');
- 1184 :
break;
- 1185 :
- 1186 :
case 'lastmessage':
- 1187 :
if (this.env.last_uid)
- 1188 :
this.show_message(this.env.last_uid);
- 1189 :
break;
- 1190 :
- 1191 :
case 'previousmessage':
- 1192 :
if (this.env.prev_uid)
- 1193 :
this.show_message(this.env.prev_uid, false, this.env.action == 'preview');
- 1194 :
break;
- 1195 :
- 1196 :
case 'firstmessage':
- 1197 :
if (this.env.first_uid)
- 1198 :
this.show_message(this.env.first_uid);
- 1199 :
break;
- 1200 :
- 1201 :
case 'compose':
- 1202 :
url = {};
- 1203 :
- 1204 :
if (this.task == 'mail') {
- 1205 :
url = {_mbox: this.env.mailbox, _search: this.env.search_request};
- 1206 :
if (props)
- 1207 :
url._to = props;
- 1208 :
}
- 1209 :
// modify url if we're in addressbook
- 1210 :
else if (this.task == 'addressbook') {
- 1211 :
// switch to mail compose step directly
- 1212 :
if (props && props.indexOf('@') > 0) {
- 1213 :
url._to = props;
- 1214 :
}
- 1215 :
else {
- 1216 :
var a_cids = [];
- 1217 :
// use contact id passed as command parameter
- 1218 :
if (props)
- 1219 :
a_cids.push(props);
- 1220 :
// get selected contacts
- 1221 :
else if (this.contact_list)
- 1222 :
a_cids = this.contact_list.get_selection();
- 1223 :
- 1224 :
if (a_cids.length) {
- 1225 :
this.http_post('mailto', { _cid: a_cids.join(','), _source: this.env.source }, true);
- 1226 :
break;
- 1227 :
}
- 1228 :
else if (this.env.group && this.env.pagecount) {
- 1229 :
this.http_post('mailto', { _gid: this.env.group, _source: this.env.source }, true);
- 1230 :
break;
- 1231 :
}
- 1232 :
}
- 1233 :
}
- 1234 :
else if (props && typeof props == 'string') {
- 1235 :
url._to = props;
- 1236 :
}
- 1237 :
else if (props && typeof props == 'object') {
- 1238 :
$.extend(url, props);
- 1239 :
}
- 1240 :
- 1241 :
this.open_compose_step(url);
- 1242 :
break;
- 1243 :
- 1244 :
case 'spellcheck':
- 1245 :
if (this.spellcheck_state()) {
- 1246 :
this.editor.spellcheck_stop();
- 1247 :
}
- 1248 :
else {
- 1249 :
this.editor.spellcheck_start();
- 1250 :
}
- 1251 :
break;
- 1252 :
- 1253 :
case 'savedraft':
- 1254 :
// Reset the auto-save timer
- 1255 :
clearTimeout(this.save_timer);
- 1256 :
- 1257 :
// compose form did not change (and draft wasn't saved already)
- 1258 :
if (this.env.draft_id && this.cmp_hash == this.compose_field_hash()) {
- 1259 :
this.auto_save_start();
- 1260 :
break;
- 1261 :
}
- 1262 :
- 1263 :
this.submit_messageform(true);
- 1264 :
break;
- 1265 :
- 1266 :
case 'send':
- 1267 :
if (!props.nocheck && !this.env.is_sent && !this.check_compose_input(command))
- 1268 :
break;
- 1269 :
- 1270 :
// Reset the auto-save timer
- 1271 :
clearTimeout(this.save_timer);
- 1272 :
- 1273 :
this.submit_messageform();
- 1274 :
break;
- 1275 :
- 1276 :
case 'send-attachment':
- 1277 :
// Reset the auto-save timer
- 1278 :
clearTimeout(this.save_timer);
- 1279 :
- 1280 :
if (!(flag = this.upload_file(props || this.gui_objects.uploadform, 'upload'))) {
- 1281 :
if (flag !== false)
- 1282 :
this.alert_dialog(this.get_label('selectimportfile'));
- 1283 :
aborted = true;
- 1284 :
}
- 1285 :
break;
- 1286 :
- 1287 :
case 'insert-sig':
- 1288 :
this.change_identity($("[name='_from']")[0], true);
- 1289 :
break;
- 1290 :
- 1291 :
case 'list-addresses':
- 1292 :
this.list_contacts(props);
- 1293 :
this.enable_command('add-recipient', false);
- 1294 :
break;
- 1295 :
- 1296 :
case 'add-recipient':
- 1297 :
this.compose_add_recipient(props);
- 1298 :
break;
- 1299 :
- 1300 :
case 'reply-all':
- 1301 :
case 'reply-list':
- 1302 :
case 'reply':
- 1303 :
if (uid = this.get_single_uid()) {
- 1304 :
url = {_reply_uid: uid, _mbox: this.get_message_mailbox(uid), _search: this.env.search_request};
- 1305 :
if (command == 'reply-all')
- 1306 :
// do reply-list, when list is detected and popup menu wasn't used
- 1307 :
url._all = (!props && this.env.reply_all_mode == 1 && this.commands['reply-list'] ? 'list' : 'all');
- 1308 :
else if (command == 'reply-list')
- 1309 :
url._all = 'list';
- 1310 :
- 1311 :
this.open_compose_step(url);
- 1312 :
}
- 1313 :
break;
- 1314 :
- 1315 :
case 'forward-attachment':
- 1316 :
case 'forward-inline':
- 1317 :
case 'forward':
- 1318 :
var uids = this.env.uid ? [this.env.uid] : (this.message_list ? this.message_list.get_selection() : []);
- 1319 :
if (uids.length) {
- 1320 :
url = { _forward_uid: this.uids_to_list(uids), _mbox: this.env.mailbox, _search: this.env.search_request };
- 1321 :
if (command == 'forward-attachment' || (!props && this.env.forward_attachment) || uids.length > 1)
- 1322 :
url._attachment = 1;
- 1323 :
this.open_compose_step(url);
- 1324 :
}
- 1325 :
break;
- 1326 :
- 1327 :
case 'print':
- 1328 :
if (this.task == 'addressbook') {
- 1329 :
if (uid = this.get_single_cid()) {
- 1330 :
url = '&_action=print&_cid=' + uid;
- 1331 :
if (this.env.source)
- 1332 :
url += '&_source=' + urlencode(this.env.source);
- 1333 :
this.open_window(this.env.comm_path + url, true, true);
- 1334 :
}
- 1335 :
}
- 1336 :
else if (this.env.action == 'get' && !this.env.is_message) {
- 1337 :
this.gui_objects.messagepartframe.contentWindow.print();
- 1338 :
}
- 1339 :
else if (uid = this.get_single_uid()) {
- 1340 :
url = this.url('print', this.params_from_uid(uid, {_safe: this.env.safemode ? 1 : 0}));
- 1341 :
if (this.open_window(url, true, true)) {
- 1342 :
if (this.env.action != 'show' && this.env.action != 'get')
- 1343 :
this.mark_message('read', uid);
- 1344 :
}
- 1345 :
}
- 1346 :
break;
- 1347 :
- 1348 :
case 'viewsource':
- 1349 :
if (uid = this.get_single_uid())
- 1350 :
this.open_window(this.url('viewsource', this.params_from_uid(uid)), true, true);
- 1351 :
break;
- 1352 :
- 1353 :
case 'download':
- 1354 :
if (this.env.action == 'get') {
- 1355 :
location.href = this.secure_url(location.href.replace(/_frame=/, '_download='));
- 1356 :
}
- 1357 :
else if (uid = this.get_single_uid()) {
- 1358 :
this.goto_url('viewsource', this.params_from_uid(uid, {_save: 1}), false, true);
- 1359 :
}
- 1360 :
break;
- 1361 :
- 1362 :
// quicksearch
- 1363 :
case 'search':
- 1364 :
return this.qsearch(props);
- 1365 :
- 1366 :
// reset quicksearch
- 1367 :
case 'reset-search':
- 1368 :
var n, s = this.env.search_request || this.env.qsearch;
- 1369 :
- 1370 :
this.reset_qsearch(true);
- 1371 :
- 1372 :
if (s && this.env.action == 'compose') {
- 1373 :
if (this.contact_list)
- 1374 :
this.list_contacts_clear();
- 1375 :
}
- 1376 :
else if (s && this.env.mailbox) {
- 1377 :
this.list_mailbox(this.env.mailbox, 1);
- 1378 :
}
- 1379 :
else if (s && this.task == 'addressbook') {
- 1380 :
this.env.source = this.env.last_source || '';
- 1381 :
this.env.group = this.env.last_group || '';
- 1382 :
this.list_contacts(this.env.source, this.env.group, 1);
- 1383 :
}
- 1384 :
break;
- 1385 :
- 1386 :
case 'pushgroup':
- 1387 :
// add group ID and current search to stack
- 1388 :
var group = {
- 1389 :
id: props.id,
- 1390 :
search_request: this.env.search_request,
- 1391 :
page: this.env.current_page,
- 1392 :
search: this.env.search_request && this.gui_objects.qsearchbox ? this.gui_objects.qsearchbox.value : null
- 1393 :
};
- 1394 :
- 1395 :
this.env.address_group_stack.push(group);
- 1396 :
if (obj && event)
- 1397 :
rcube_event.cancel(event);
- 1398 :
- 1399 :
// FIXME: no break?
- 1400 :
- 1401 :
case 'listgroup':
- 1402 :
this.reset_qsearch();
- 1403 :
this.list_contacts(props.source, props.id, 1, group);
- 1404 :
break;
- 1405 :
- 1406 :
case 'popgroup':
- 1407 :
if (this.env.address_group_stack.length) {
- 1408 :
var old = this.env.address_group_stack.pop();
- 1409 :
this.reset_qsearch();
- 1410 :
- 1411 :
if (old.search_request) {
- 1412 :
// this code is executed when going back to the search result
- 1413 :
if (old.search && this.gui_objects.qsearchbox)
- 1414 :
$(this.gui_objects.qsearchbox).val(old.search);
- 1415 :
this.env.search_request = old.search_request;
- 1416 :
this.list_contacts_remote(null, null, this.env.current_page = old.page);
- 1417 :
}
- 1418 :
else
- 1419 :
this.list_contacts(props.source, this.env.address_group_stack[this.env.address_group_stack.length-1].id);
- 1420 :
}
- 1421 :
break;
- 1422 :
- 1423 :
case 'import-messages':
- 1424 :
var form = props || this.gui_objects.importform,
- 1425 :
importlock = this.set_busy(true, 'importwait');
- 1426 :
- 1427 :
if (!(flag = this.upload_file(form, 'import', importlock))) {
- 1428 :
this.set_busy(false, null, importlock);
- 1429 :
if (flag !== false)
- 1430 :
this.alert_dialog(this.get_label('selectimportfile'));
- 1431 :
this.command_aborted = true;
- 1432 :
}
- 1433 :
break;
- 1434 :
- 1435 :
case 'import':
- 1436 :
var dialog = $('<iframe>').attr('src', this.url('import', {_framed: 1, _target: this.env.source})),
- 1437 :
import_func = function(e) {
- 1438 :
var win = dialog[0].contentWindow,
- 1439 :
form = null;
- 1440 :
- 1441 :
if (win.rcmail.gui_objects.importformmap)
- 1442 :
form = win.rcmail.gui_objects.importformmap;
- 1443 :
else
- 1444 :
form = win.rcmail.gui_objects.importform;
- 1445 :
- 1446 :
if (form) {
- 1447 :
var lock, file = win.$('#rcmimportfile')[0];
- 1448 :
if (file && !file.value) {
- 1449 :
win.rcmail.alert_dialog(win.rcmail.get_label('selectimportfile'));
- 1450 :
return;
- 1451 :
}
- 1452 :
- 1453 :
lock = win.rcmail.set_busy(true, 'importwait');
- 1454 :
$('[name="_unlock"]', form).val(lock);
- 1455 :
form.submit();
- 1456 :
win.rcmail.lock_form(form, true);
- 1457 :
// disable Import button
- 1458 :
$(e.target).attr('disabled', true).next().focus();
- 1459 :
}
- 1460 :
},
- 1461 :
close_func = function(event, ui) {
- 1462 :
$(this).remove();
- 1463 :
if (ref.import_state == 'reload')
- 1464 :
ref.command('list');
- 1465 :
};
- 1466 :
- 1467 :
this.import_state = null;
- 1468 :
this.import_dialog = this.simple_dialog(dialog, this.gettext('importcontacts'), import_func, {
- 1469 :
close: close_func,
- 1470 :
button: 'import',
- 1471 :
width: 500,
- 1472 :
height: 300
- 1473 :
});
- 1474 :
- 1475 :
break;
- 1476 :
- 1477 :
case 'export':
- 1478 :
if (this.contact_list.rowcount > 0) {
- 1479 :
this.goto_url('export', { _source: this.env.source, _gid: this.env.group, _search: this.env.search_request }, false, true);
- 1480 :
}
- 1481 :
break;
- 1482 :
- 1483 :
case 'export-selected':
- 1484 :
if (this.contact_list.rowcount > 0) {
- 1485 :
this.goto_url('export', { _source: this.env.source, _gid: this.env.group, _cid: this.contact_list.get_selection().join(',') }, false, true);
- 1486 :
}
- 1487 :
break;
- 1488 :
- 1489 :
case 'upload-photo':
- 1490 :
this.upload_contact_photo(props || this.gui_objects.uploadform);
- 1491 :
break;
- 1492 :
- 1493 :
case 'delete-photo':
- 1494 :
this.replace_contact_photo('-del-');
- 1495 :
break;
- 1496 :
- 1497 :
case 'undo':
- 1498 :
this.http_request('undo', '', this.display_message('', 'loading'));
- 1499 :
break;
- 1500 :
- 1501 :
// unified command call (command name == function name)
- 1502 :
default:
- 1503 :
var func = command.replace(/-/g, '_');
- 1504 :
if (this[func] && typeof this[func] === 'function') {
- 1505 :
return this[func](props, obj, event);
- 1506 :
}
- 1507 :
}
- 1508 :
};
- 1509 :
- 1510 :
// set command(s) enabled or disabled
- 1511 :
this.enable_command = function()
- 1512 :
{
- 1513 :
var i, n, args = Array.prototype.slice.call(arguments),
- 1514 :
enable = args.pop(), cmd;
- 1515 :
- 1516 :
for (n=0; n<args.length; n++) {
- 1517 :
cmd = args[n];
- 1518 :
// argument of type array
- 1519 :
if (typeof cmd === 'string') {
- 1520 :
this.commands[cmd] = enable;
- 1521 :
this.set_button(cmd, (enable ? 'act' : 'pas'));
- 1522 :
this.triggerEvent('enable-command', {command: cmd, status: enable});
- 1523 :
}
- 1524 :
// push array elements into commands array
- 1525 :
else {
- 1526 :
for (i in cmd)
- 1527 :
args.push(cmd[i]);
- 1528 :
}
- 1529 :
}
- 1530 :
- 1531 :
this.set_menu_buttons();
- 1532 :
};
- 1533 :
- 1534 :
this.command_enabled = function(cmd)
- 1535 :
{
- 1536 :
return this.commands[cmd];
- 1537 :
};
- 1538 :
- 1539 :
// lock/unlock interface
- 1540 :
this.set_busy = function(a, message, id)
- 1541 :
{
- 1542 :
if (a && message) {
- 1543 :
var msg = this.get_label(message);
- 1544 :
if (msg == message)
- 1545 :
msg = 'Loading...';
- 1546 :
- 1547 :
id = this.display_message(msg, 'loading');
- 1548 :
}
- 1549 :
else if (!a && id) {
- 1550 :
this.hide_message(id);
- 1551 :
}
- 1552 :
- 1553 :
this.busy = a;
- 1554 :
- 1555 :
if (this.gui_objects.editform)
- 1556 :
this.lock_form(this.gui_objects.editform, a);
- 1557 :
- 1558 :
return id;
- 1559 :
};
- 1560 :
- 1561 :
// return a localized string
- 1562 :
this.get_label = function(name, domain)
- 1563 :
{
- 1564 :
if (domain && this.labels[domain+'.'+name])
- 1565 :
return this.labels[domain+'.'+name];
- 1566 :
else if (this.labels[name])
- 1567 :
return this.labels[name];
- 1568 :
else
- 1569 :
return name;
- 1570 :
};
- 1571 :
- 1572 :
// alias for convenience reasons
- 1573 :
this.gettext = this.get_label;
- 1574 :
- 1575 :
// switch to another application task
- 1576 :
this.switch_task = function(task)
- 1577 :
{
- 1578 :
var action, path;
- 1579 :
- 1580 :
if ((path = task.split('/')).length == 2) {
- 1581 :
task = path[0];
- 1582 :
action = path[1];
- 1583 :
}
- 1584 :
- 1585 :
if (this.task === task && task != 'mail')
- 1586 :
return;
- 1587 :
- 1588 :
var url = this.get_task_url(task);
- 1589 :
- 1590 :
if (action)
- 1591 :
url += '&_action=' + action;
- 1592 :
- 1593 :
if (task == 'mail')
- 1594 :
url += '&_mbox=INBOX';
- 1595 :
else if (task == 'logout') {
- 1596 :
url = this.secure_url(url);
- 1597 :
this.clear_compose_data();
- 1598 :
}
- 1599 :
- 1600 :
this.redirect(url);
- 1601 :
};
- 1602 :
- 1603 :
this.get_task_url = function(task, url)
- 1604 :
{
- 1605 :
if (!url)
- 1606 :
url = this.env.comm_path;
- 1607 :
- 1608 :
if (url.match(/[?&]_task=[a-zA-Z0-9_-]+/))
- 1609 :
return url.replace(/_task=[a-zA-Z0-9_-]+/, '_task=' + task);
- 1610 :
else
- 1611 :
return url.replace(/\?.*$/, '') + '?_task=' + task;
- 1612 :
};
- 1613 :
- 1614 :
this.reload = function(delay)
- 1615 :
{
- 1616 :
if (this.is_framed())
- 1617 :
parent.rcmail.reload(delay);
- 1618 :
else if (delay)
- 1619 :
setTimeout(function() { ref.reload(); }, delay);
- 1620 :
else if (window.location)
- 1621 :
location.href = this.url('', {_extwin: this.env.extwin});
- 1622 :
};
- 1623 :
- 1624 :
// Add variable to GET string, replace old value if exists
- 1625 :
this.add_url = function(url, name, value)
- 1626 :
{
- 1627 :
var urldata, datax, hash = '';
- 1628 :
- 1629 :
value = urlencode(value);
- 1630 :
- 1631 :
if (/(#[a-z0-9_-]*)$/.test(url)) {
- 1632 :
hash = RegExp.$1;
- 1633 :
url = url.substr(0, url.length - hash.length);
- 1634 :
}
- 1635 :
- 1636 :
if (/(\?.*)$/.test(url)) {
- 1637 :
urldata = RegExp.$1;
- 1638 :
datax = RegExp('((\\?|&)'+RegExp.escape(name)+'=[^&]*)');
- 1639 :
- 1640 :
if (datax.test(urldata))
- 1641 :
urldata = urldata.replace(datax, RegExp.$2 + name + '=' + value);
- 1642 :
else
- 1643 :
urldata += '&' + name + '=' + value;
- 1644 :
- 1645 :
return url.replace(/(\?.*)$/, urldata) + hash;
- 1646 :
}
- 1647 :
- 1648 :
return url + '?' + name + '=' + value + hash;
- 1649 :
};
- 1650 :
- 1651 :
// append CSRF protection token to the given url
- 1652 :
this.secure_url = function(url)
- 1653 :
{
- 1654 :
return this.add_url(url, '_token', this.env.request_token);
- 1655 :
},
- 1656 :
- 1657 :
this.is_framed = function()
- 1658 :
{
- 1659 :
return this.env.framed && parent.rcmail && parent.rcmail != this && typeof parent.rcmail.command == 'function';
- 1660 :
};
- 1661 :
- 1662 :
this.save_pref = function(prop)
- 1663 :
{
- 1664 :
var request = {_name: prop.name, _value: prop.value};
- 1665 :
- 1666 :
if (prop.session)
- 1667 :
request._session = prop.session;
- 1668 :
if (prop.env)
- 1669 :
this.env[prop.env] = prop.value;
- 1670 :
- 1671 :
this.http_post('save-pref', request);
- 1672 :
};
- 1673 :
- 1674 :
this.html_identifier = function(str, encode)
- 1675 :
{
- 1676 :
return encode ? this.html_identifier_encode(str) : String(str).replace(this.identifier_expr, '_');
- 1677 :
};
- 1678 :
- 1679 :
this.html_identifier_encode = function(str)
- 1680 :
{
- 1681 :
return Base64.encode(String(str)).replace(/=+$/, '').replace(/\+/g, '-').replace(/\//g, '_');
- 1682 :
};
- 1683 :
- 1684 :
this.html_identifier_decode = function(str)
- 1685 :
{
- 1686 :
str = String(str).replace(/-/g, '+').replace(/_/g, '/');
- 1687 :
- 1688 :
while (str.length % 4) str += '=';
- 1689 :
- 1690 :
return Base64.decode(str);
- 1691 :
};
- 1692 :
- 1693 :
- 1694 :
/*********************************************************/
- 1695 :
/********* event handling methods *********/
- 1696 :
/*********************************************************/
- 1697 :
- 1698 :
this.drag_menu = function(e, target)
- 1699 :
{
- 1700 :
var modkey = rcube_event.get_modifier(e),
- 1701 :
menu = this.gui_objects.dragmenu;
- 1702 :
- 1703 :
if (menu && modkey == SHIFT_KEY && this.commands['copy']) {
- 1704 :
var pos = rcube_event.get_mouse_pos(e);
- 1705 :
this.env.drag_target = target;
- 1706 :
this.show_menu(this.gui_objects.dragmenu.id, true, e);
- 1707 :
$(menu).css({top: (pos.y-10)+'px', left: (pos.x-10)+'px'});
- 1708 :
return true;
- 1709 :
}
- 1710 :
- 1711 :
return false;
- 1712 :
};
- 1713 :
- 1714 :
this.drag_menu_action = function(action)
- 1715 :
{
- 1716 :
var menu = this.gui_objects.dragmenu;
- 1717 :
if (menu) {
- 1718 :
$(menu).hide();
- 1719 :
}
- 1720 :
- 1721 :
this.command(action, this.env.drag_target);
- 1722 :
this.env.drag_target = null;
- 1723 :
};
- 1724 :
- 1725 :
this.drag_start = function(list)
- 1726 :
{
- 1727 :
this.drag_active = true;
- 1728 :
- 1729 :
if (this.preview_timer)
- 1730 :
clearTimeout(this.preview_timer);
- 1731 :
- 1732 :
// prepare treelist widget for dragging interactions
- 1733 :
if (this.treelist)
- 1734 :
this.treelist.drag_start();
- 1735 :
};
- 1736 :
- 1737 :
this.drag_end = function(e)
- 1738 :
{
- 1739 :
var list, model;
- 1740 :
- 1741 :
if (this.treelist)
- 1742 :
this.treelist.drag_end();
- 1743 :
- 1744 :
// execute drag & drop action when mouse was released
- 1745 :
if (list = this.message_list)
- 1746 :
model = this.env.mailboxes;
- 1747 :
else if (list = this.contact_list)
- 1748 :
model = this.env.contactfolders;
- 1749 :
- 1750 :
// Note: we accept only mouse events to ignore dragging aborts with ESC key (#6623)
- 1751 :
if (this.drag_active && model && this.env.last_folder_target && !rcube_event.is_keyboard(e)) {
- 1752 :
var target = model[this.env.last_folder_target];
- 1753 :
list.draglayer.hide();
- 1754 :
- 1755 :
if (this.contact_list) {
- 1756 :
if (!this.contacts_drag_menu(e, target))
- 1757 :
this.command('move', target);
- 1758 :
}
- 1759 :
else if (!this.drag_menu(e, target))
- 1760 :
this.command('move', target);
- 1761 :
}
- 1762 :
- 1763 :
this.drag_active = false;
- 1764 :
this.env.last_folder_target = null;
- 1765 :
};
- 1766 :
- 1767 :
this.drag_move = function(e)
- 1768 :
{
- 1769 :
if (this.gui_objects.folderlist) {
- 1770 :
var drag_target, oldclass,
- 1771 :
layerclass = 'draglayernormal',
- 1772 :
mouse = rcube_event.get_mouse_pos(e);
- 1773 :
- 1774 :
if (this.contact_list && this.contact_list.draglayer)
- 1775 :
oldclass = this.contact_list.draglayer.attr('class');
- 1776 :
- 1777 :
// mouse intersects a valid drop target on the treelist
- 1778 :
if (this.treelist && (drag_target = this.treelist.intersects(mouse, true))) {
- 1779 :
this.env.last_folder_target = drag_target;
- 1780 :
layerclass = 'draglayer' + (this.check_droptarget(drag_target) > 1 ? 'copy' : 'normal');
- 1781 :
}
- 1782 :
else {
- 1783 :
// Clear target, otherwise drag end will trigger move into last valid droptarget
- 1784 :
this.env.last_folder_target = null;
- 1785 :
}
- 1786 :
- 1787 :
if (layerclass != oldclass && this.contact_list && this.contact_list.draglayer)
- 1788 :
this.contact_list.draglayer.attr('class', layerclass);
- 1789 :
}
- 1790 :
};
- 1791 :
- 1792 :
this.collapse_folder = function(name)
- 1793 :
{
- 1794 :
if (this.treelist)
- 1795 :
this.treelist.toggle(name);
- 1796 :
};
- 1797 :
- 1798 :
this.folder_collapsed = function(node)
- 1799 :
{
- 1800 :
if (this.folder_collapsed_timer)
- 1801 :
clearTimeout(this.folder_collapsed_timer);
- 1802 :
- 1803 :
var prefname = this.env.task == 'addressbook' ? 'collapsed_abooks' : 'collapsed_folders',
- 1804 :
old = this.env[prefname];
- 1805 :
- 1806 :
if (node.collapsed) {
- 1807 :
this.env[prefname] = this.env[prefname] + '&'+urlencode(node.id)+'&';
- 1808 :
- 1809 :
// select the folder if one of its children is currently selected
- 1810 :
// don't select if it's virtual (#1488346)
- 1811 :
if (!node.virtual && this.env.mailbox && this.env.mailbox.startsWith(node.id + this.env.delimiter))
- 1812 :
this.command('list', node.id);
- 1813 :
}
- 1814 :
else {
- 1815 :
var reg = new RegExp('&'+urlencode(node.id)+'&');
- 1816 :
this.env[prefname] = this.env[prefname].replace(reg, '');
- 1817 :
}
- 1818 :
- 1819 :
if (!this.drag_active) {
- 1820 :
if (old !== this.env[prefname])
- 1821 :
this.folder_collapsed_timer = setTimeout(function() { ref.command('save-pref', { name: prefname, value: ref.env[prefname] }); }, 10);
- 1822 :
- 1823 :
if (this.env.unread_counts)
- 1824 :
this.set_unread_count_display(node.id, false);
- 1825 :
}
- 1826 :
};
- 1827 :
- 1828 :
// global mouse-click handler to cleanup some UI elements
- 1829 :
this.doc_mouse_up = function(e)
- 1830 :
{
- 1831 :
var list, id, target = rcube_event.get_target(e);
- 1832 :
- 1833 :
// ignore event if jquery UI dialog is open
- 1834 :
if ($(target).closest('.ui-dialog, .ui-widget-overlay').length)
- 1835 :
return;
- 1836 :
- 1837 :
// remove focus from list widgets
- 1838 :
if (window.rcube_list_widget && rcube_list_widget._instances.length) {
- 1839 :
$.each(rcube_list_widget._instances, function(i,list) {
- 1840 :
if (list && !rcube_mouse_is_over(e, list.list.parentNode))
- 1841 :
list.blur();
- 1842 :
});
- 1843 :
}
- 1844 :
- 1845 :
// reset 'pressed' buttons
- 1846 :
if (this.buttons_sel) {
- 1847 :
for (id in this.buttons_sel)
- 1848 :
if (typeof id !== 'function')
- 1849 :
this.button_out(this.buttons_sel[id], id);
- 1850 :
this.buttons_sel = {};
- 1851 :
}
- 1852 :
- 1853 :
// reset popup menus; delayed to have updated menu_stack data
- 1854 :
setTimeout(function(e) {
- 1855 :
var obj, skip, config, id, i, parents = $(target).parents();
- 1856 :
for (i = ref.menu_stack.length - 1; i >= 0; i--) {
- 1857 :
id = ref.menu_stack[i];
- 1858 :
obj = $('#' + id);
- 1859 :
- 1860 :
if (obj.is(':visible')
- 1861 :
&& target != obj.data('opener')
- 1862 :
&& target != obj.get(0) // check if scroll bar was clicked (#1489832)
- 1863 :
&& !parents.is(obj.data('opener'))
- 1864 :
&& id != skip
- 1865 :
&& (obj.attr('data-editable') != 'true' || !$(target).parents('#' + id).length)
- 1866 :
&& (obj.attr('data-sticky') != 'true' || !rcube_mouse_is_over(e, obj.get(0)))
- 1867 :
) {
- 1868 :
ref.hide_menu(id, e);
- 1869 :
}
- 1870 :
skip = obj.data('parent');
- 1871 :
}
- 1872 :
}, 10, e);
- 1873 :
};
- 1874 :
- 1875 :
// global keypress event handler
- 1876 :
this.doc_keypress = function(e)
- 1877 :
{
- 1878 :
// Helper method to move focus to the next/prev active menu item
- 1879 :
var focus_menu_item = function(dir) {
- 1880 :
var obj, item, mod = dir < 0 ? 'prevAll' : 'nextAll', limit = dir < 0 ? 'last' : 'first';
- 1881 :
if (ref.focused_menu && (obj = $('#'+ref.focused_menu))) {
- 1882 :
item = obj.find(':focus').closest('li')[mod]().has(':not([aria-disabled=true])').find('a,input')[limit]();
- 1883 :
if (!item.length)
- 1884 :
item = obj.find(':focus').closest('ul')[mod]().has(':not([aria-disabled=true])').find('a,input')[limit]();
- 1885 :
return item.focus().length;
- 1886 :
}
- 1887 :
- 1888 :
return 0;
- 1889 :
};
- 1890 :
- 1891 :
var target = e.target || {},
- 1892 :
keyCode = rcube_event.get_keycode(e);
- 1893 :
- 1894 :
if (e.keyCode != 27 && (!this.menu_keyboard_active || target.nodeName == 'TEXTAREA' || target.nodeName == 'SELECT')) {
- 1895 :
return true;
- 1896 :
}
- 1897 :
- 1898 :
switch (keyCode) {
- 1899 :
case 38:
- 1900 :
case 40:
- 1901 :
case 63232: // "up", in safari keypress
- 1902 :
case 63233: // "down", in safari keypress
- 1903 :
focus_menu_item(keyCode == 38 || keyCode == 63232 ? -1 : 1);
- 1904 :
return rcube_event.cancel(e);
- 1905 :
- 1906 :
case 9: // tab
- 1907 :
if (this.focused_menu) {
- 1908 :
var mod = rcube_event.get_modifier(e);
- 1909 :
if (!focus_menu_item(mod == SHIFT_KEY ? -1 : 1)) {
- 1910 :
this.hide_menu(this.focused_menu, e);
- 1911 :
}
- 1912 :
}
- 1913 :
return rcube_event.cancel(e);
- 1914 :
- 1915 :
case 27: // esc
- 1916 :
if (this.menu_stack.length)
- 1917 :
this.hide_menu(this.menu_stack[this.menu_stack.length-1], e);
- 1918 :
break;
- 1919 :
}
- 1920 :
- 1921 :
return true;
- 1922 :
}
- 1923 :
- 1924 :
// Common handler for a keypress event on a list widget
- 1925 :
this.list_keypress = function(list, conf)
- 1926 :
{
- 1927 :
if (list.modkey == CONTROL_KEY)
- 1928 :
return;
- 1929 :
- 1930 :
if (list.key_pressed == list.DELETE_KEY || list.key_pressed == list.BACKSPACE_KEY)
- 1931 :
this.command(conf && conf.del ? conf.del : 'delete');
- 1932 :
else if (list.key_pressed == 33)
- 1933 :
this.command(conf && conf.prev ? conf.prev : 'previouspage');
- 1934 :
else if (list.key_pressed == 34)
- 1935 :
this.command(conf && conf.next ? conf.next : 'nextpage');
- 1936 :
};
- 1937 :
- 1938 :
// Handler for a keypress event on a messages list widget
- 1939 :
this.msglist_keypress = function(list)
- 1940 :
{
- 1941 :
// On Enter open the message in list layout mode (no preview frame)
- 1942 :
if (list.key_pressed == list.ENTER_KEY && !this.env.contentframe)
- 1943 :
this.command('show');
- 1944 :
else
- 1945 :
this.list_keypress(list);
- 1946 :
};
- 1947 :
- 1948 :
this.msglist_select = function(list)
- 1949 :
{
- 1950 :
if (this.preview_timer)
- 1951 :
clearTimeout(this.preview_timer);
- 1952 :
- 1953 :
var isDraft = false,
- 1954 :
selected = list.get_single_selection(),
- 1955 :
selection = list.get_selection(false),
- 1956 :
selected_count = selection.length;
- 1957 :
- 1958 :
this.enable_command(this.env.message_commands, selected != null);
- 1959 :
- 1960 :
// Find out whether any of the selected messages comes from the Drafts folder
- 1961 :
if (selected_count > 0) {
- 1962 :
if (!this.env.multifolder_listing) {
- 1963 :
isDraft = this.env.mailbox == this.env.drafts_mailbox
- 1964 :
}
- 1965 :
else {
- 1966 :
$.each(selection, function(i, v) {
- 1967 :
if (ref.get_message_mailbox(v) == ref.env.drafts_mailbox) {
- 1968 :
isDraft = true;
- 1969 :
return false;
- 1970 :
}
- 1971 :
});
- 1972 :
}
- 1973 :
}
- 1974 :
- 1975 :
// Disable some actions enabled above
- 1976 :
if (selected) {
- 1977 :
if (isDraft) {
- 1978 :
this.enable_command('reply', 'reply-all', 'reply-list', 'forward', 'forward-inline',
- 1979 :
'forward-attachment', 'bounce', false);
- 1980 :
}
- 1981 :
else {
- 1982 :
var msg = this.env.messages[selected];
- 1983 :
if (!msg.ml)
- 1984 :
this.enable_command('reply-list', false);
- 1985 :
}
- 1986 :
}
- 1987 :
- 1988 :
// Multi-message commands
- 1989 :
this.enable_command('delete', 'move', 'copy', 'mark', selected_count > 0);
- 1990 :
this.enable_command('forward', 'forward-attachment', !isDraft && selected_count > 0);
- 1991 :
- 1992 :
// reset all-pages-selection
- 1993 :
if (selected || (selected_count && selected_count != list.rowcount))
- 1994 :
this.select_all_mode = false;
- 1995 :
- 1996 :
// start timer for message preview (wait for double click)
- 1997 :
if (selected && this.env.contentframe && !list.multi_selecting && !this.dummy_select) {
- 1998 :
// try to be responsive and try not to overload the server when user is pressing up/down key repeatedly
- 1999 :
var now = new Date().getTime(),
- 2000 :
time_diff = now - (this._last_msglist_select_time || 0),
- 2001 :
preview_pane_delay = this.preview_delay_click;
- 2002 :
- 2003 :
// user is selecting messages repeatedly, wait until this ends (use larger delay)
- 2004 :
if (time_diff < this.preview_delay_select) {
- 2005 :
preview_pane_delay = this.preview_delay_select;
- 2006 :
if (this.preview_timer) {
- 2007 :
clearTimeout(this.preview_timer);
- 2008 :
}
- 2009 :
if (this.env.contentframe) {
- 2010 :
this.show_contentframe(false);
- 2011 :
}
- 2012 :
}
- 2013 :
- 2014 :
this._last_msglist_select_time = now;
- 2015 :
this.preview_timer = setTimeout(function() { ref.msglist_get_preview(); }, preview_pane_delay);
- 2016 :
}
- 2017 :
else if (this.env.contentframe) {
- 2018 :
this.show_contentframe(false);
- 2019 :
}
- 2020 :
};
- 2021 :
- 2022 :
this.msglist_dbl_click = function(list)
- 2023 :
{
- 2024 :
if (this.preview_timer)
- 2025 :
clearTimeout(this.preview_timer);
- 2026 :
- 2027 :
var mbox, uid = list.get_single_selection();
- 2028 :
- 2029 :
// TODO: Here we should just use this.command('show') but we can't
- 2030 :
// because at this point this.busy=true (set by msglist_get_preview())
- 2031 :
- 2032 :
if (uid) {
- 2033 :
mbox = this.get_message_mailbox(uid);
- 2034 :
if (mbox == this.env.drafts_mailbox)
- 2035 :
this.open_compose_step({_draft_uid: uid, _mbox: mbox});
- 2036 :
else
- 2037 :
this.show_message(uid);
- 2038 :
}
- 2039 :
};
- 2040 :
- 2041 :
this.msglist_get_preview = function()
- 2042 :
{
- 2043 :
var uid = this.get_single_uid();
- 2044 :
if (uid && this.env.contentframe && !this.drag_active)
- 2045 :
this.show_message(uid, false, true);
- 2046 :
else if (this.env.contentframe)
- 2047 :
this.show_contentframe(false);
- 2048 :
};
- 2049 :
- 2050 :
this.msglist_expand = function(row)
- 2051 :
{
- 2052 :
if (this.env.messages[row.uid])
- 2053 :
this.env.messages[row.uid].expanded = row.expanded;
- 2054 :
$(row.obj)[row.expanded?'addClass':'removeClass']('expanded');
- 2055 :
};
- 2056 :
- 2057 :
this.msglist_set_coltypes = function(list)
- 2058 :
{
- 2059 :
var i, found, name, cols = list.thead.rows[0].cells;
- 2060 :
- 2061 :
this.env.listcols = [];
- 2062 :
- 2063 :
for (i=0; i<cols.length; i++)
- 2064 :
if (cols[i].id && cols[i].id.startsWith('rcm')) {
- 2065 :
name = cols[i].id.slice(3);
- 2066 :
this.env.listcols.push(name);
- 2067 :
}
- 2068 :
- 2069 :
// update message list setup
- 2070 :
this.msglist_setup(this.env.layout);
- 2071 :
- 2072 :
if ((found = $.inArray('flag', this.env.listcols)) >= 0)
- 2073 :
this.env.flagged_col = found;
- 2074 :
- 2075 :
if ((found = $.inArray('subject', this.env.listcols)) >= 0)
- 2076 :
this.env.subject_col = found;
- 2077 :
- 2078 :
this.command('save-pref', { name: 'list_cols', value: this.env.listcols, session: 'list_attrib/columns' });
- 2079 :
};
- 2080 :
- 2081 :
this.msglist_setup = function(layout)
- 2082 :
{
- 2083 :
var ret, listcols;
- 2084 :
- 2085 :
// allow plugins or skins to override default list layout
- 2086 :
if (ret = this.triggerEvent('msglist_layout', layout))
- 2087 :
layout = ret;
- 2088 :
- 2089 :
listcols = this.env[layout == 'widescreen' ? 'listcols_widescreen' : 'listcols'];
- 2090 :
- 2091 :
if (layout == 'widescreen' && !this.env.threading)
- 2092 :
listcols = $.grep(listcols, function(value) { return value != 'threads'; });
- 2093 :
- 2094 :
// set env vars for message list
- 2095 :
this.env.msglist_layout = layout;
- 2096 :
this.env.msglist_cols = listcols;
- 2097 :
- 2098 :
// Set sort-* class on the list element
- 2099 :
var list = this.gui_objects.messagelist,
- 2100 :
classes = list.className.split(' ').filter(function(v) { return !v.startsWith('sort-'); });
- 2101 :
- 2102 :
classes.push('sort-' + (this.env.sort_col || 'none'));
- 2103 :
list.className = classes.join(' ');
- 2104 :
};
- 2105 :
- 2106 :
this.check_droptarget = function(id)
- 2107 :
{
- 2108 :
switch (this.task) {
- 2109 :
case 'mail':
- 2110 :
return (this.env.mailboxes[id]
- 2111 :
&& !this.env.mailboxes[id].virtual
- 2112 :
&& (this.env.mailboxes[id].id != this.env.mailbox || this.is_multifolder_listing())) ? 1 : 0;
- 2113 :
- 2114 :
case 'addressbook':
- 2115 :
var target;
- 2116 :
if (id != this.env.source && (target = this.env.contactfolders[id])) {
- 2117 :
// droptarget is a group
- 2118 :
if (target.type == 'group') {
- 2119 :
if (target.id != this.env.group && !this.env.contactfolders[target.source].readonly) {
- 2120 :
var is_other = this.env.selection_sources.length > 1 || $.inArray(target.source, this.env.selection_sources) == -1;
- 2121 :
return !is_other || this.commands.move ? 1 : 2;
- 2122 :
}
- 2123 :
}
- 2124 :
// droptarget is a (writable) addressbook and it's not the source
- 2125 :
else if (!target.readonly && (this.env.selection_sources.length > 1 || $.inArray(id, this.env.selection_sources) == -1)) {
- 2126 :
return this.commands.move ? 1 : 2;
- 2127 :
}
- 2128 :
}
- 2129 :
}
- 2130 :
- 2131 :
return 0;
- 2132 :
};
- 2133 :
- 2134 :
// open popup window
- 2135 :
this.open_window = function(url, small, toolbar)
- 2136 :
{
- 2137 :
var wname = 'rcmextwin' + new Date().getTime();
- 2138 :
- 2139 :
url += (url.match(/\?/) ? '&' : '?') + '_extwin=1';
- 2140 :
- 2141 :
if (this.env.standard_windows)
- 2142 :
var extwin = window.open(url, wname);
- 2143 :
else {
- 2144 :
var win = this.is_framed() ? parent.window : window,
- 2145 :
page = $(win),
- 2146 :
page_width = page.width(),
- 2147 :
page_height = bw.mz ? $('body', win).height() : page.height(),
- 2148 :
w = Math.min(small ? this.env.popup_width_small : this.env.popup_width, page_width),
- 2149 :
h = page_height, // always use same height
- 2150 :
l = (win.screenLeft || win.screenX) + 20,
- 2151 :
t = (win.screenTop || win.screenY) + 20,
- 2152 :
extwin = window.open(url, wname,
- 2153 :
'width='+w+',height='+h+',top='+t+',left='+l+',resizable=yes,location=no,scrollbars=yes'
- 2154 :
+(toolbar ? ',toolbar=yes,menubar=yes,status=yes' : ',toolbar=no,menubar=no,status=no'));
- 2155 :
}
- 2156 :
- 2157 :
// detect popup blocker (#1489618)
- 2158 :
// don't care this might not work with all browsers
- 2159 :
if (!extwin || extwin.closed) {
- 2160 :
this.display_message('windowopenerror', 'warning');
- 2161 :
return;
- 2162 :
}
- 2163 :
- 2164 :
// write loading... message to empty windows
- 2165 :
if (!url && extwin.document) {
- 2166 :
extwin.document.write('<html><body>' + this.get_label('loading') + '</body></html>');
- 2167 :
}
- 2168 :
- 2169 :
// allow plugins to grab the window reference (#1489413)
- 2170 :
this.triggerEvent('openwindow', { url:url, handle:extwin });
- 2171 :
- 2172 :
// focus window, delayed to bring to front
- 2173 :
setTimeout(function() { extwin && extwin.focus(); }, 10);
- 2174 :
- 2175 :
return extwin;
- 2176 :
};
- 2177 :
- 2178 :
- 2179 :
/*********************************************************/
- 2180 :
/********* (message) list functionality *********/
- 2181 :
/*********************************************************/
- 2182 :
- 2183 :
this.init_message_row = function(row)
- 2184 :
{
- 2185 :
var i, fn = {}, uid = row.uid,
- 2186 :
status_icon = (this.env.status_col != null ? 'status' : 'msg') + 'icn' + row.id;
- 2187 :
- 2188 :
if (uid && this.env.messages[uid])
- 2189 :
$.extend(row, this.env.messages[uid]);
- 2190 :
- 2191 :
// set eventhandler to status icon
- 2192 :
if (row.icon = document.getElementById(status_icon)) {
- 2193 :
fn.icon = function(e) { ref.command('toggle_status', uid); };
- 2194 :
}
- 2195 :
- 2196 :
// save message icon position too
- 2197 :
if (this.env.status_col != null)
- 2198 :
row.msgicon = document.getElementById('msgicn'+row.id);
- 2199 :
else
- 2200 :
row.msgicon = row.icon;
- 2201 :
- 2202 :
// set eventhandler to flag icon
- 2203 :
if (this.env.flagged_col != null && (row.flagicon = document.getElementById('flagicn'+row.id))) {
- 2204 :
fn.flagicon = function(e) { ref.command('toggle_flag', uid); };
- 2205 :
}
- 2206 :
- 2207 :
// set event handler to thread expand/collapse icon
- 2208 :
if (!row.depth && row.has_children && (row.expando = document.getElementById('rcmexpando'+row.id))) {
- 2209 :
fn.expando = function(e) { ref.expand_message_row(e, uid); };
- 2210 :
}
- 2211 :
- 2212 :
// attach events
- 2213 :
$.each(fn, function(i, f) {
- 2214 :
row[i].onclick = function(e) { f(e); return rcube_event.cancel(e); };
- 2215 :
if (bw.touch && row[i].addEventListener) {
- 2216 :
row[i].addEventListener('touchend', function(e) {
- 2217 :
if (e.changedTouches.length == 1) {
- 2218 :
f(e);
- 2219 :
return rcube_event.cancel(e);
- 2220 :
}
- 2221 :
}, false);
- 2222 :
}
- 2223 :
});
- 2224 :
- 2225 :
this.triggerEvent('insertrow', { uid:uid, row:row });
- 2226 :
};
- 2227 :
- 2228 :
// create a table row in the message list
- 2229 :
this.add_message_row = function(uid, cols, flags, attop)
- 2230 :
{
- 2231 :
if (!this.gui_objects.messagelist || !this.message_list)
- 2232 :
return false;
- 2233 :
- 2234 :
// Prevent from adding messages from different folder (#1487752)
- 2235 :
if (flags.mbox != this.env.mailbox && !flags.skip_mbox_check)
- 2236 :
return false;
- 2237 :
- 2238 :
// When deleting messages fast it may happen that the same message
- 2239 :
// from the next page could be added many times, we prevent this here
- 2240 :
if (this.message_list.rows[uid])
- 2241 :
return false;
- 2242 :
- 2243 :
if (!this.env.messages[uid])
- 2244 :
this.env.messages[uid] = {};
- 2245 :
- 2246 :
// merge flags over local message object
- 2247 :
$.extend(this.env.messages[uid], {
- 2248 :
deleted: flags.deleted?1:0,
- 2249 :
replied: flags.answered?1:0,
- 2250 :
unread: !flags.seen?1:0,
- 2251 :
forwarded: flags.forwarded?1:0,
- 2252 :
flagged: flags.flagged?1:0,
- 2253 :
has_children: flags.has_children?1:0,
- 2254 :
depth: flags.depth?flags.depth:0,
- 2255 :
unread_children: flags.unread_children || 0,
- 2256 :
flagged_children: flags.flagged_children || 0,
- 2257 :
parent_uid: flags.parent_uid || 0,
- 2258 :
selected: this.select_all_mode || this.message_list.in_selection(uid),
- 2259 :
ml: flags.ml?1:0,
- 2260 :
ctype: flags.ctype,
- 2261 :
mbox: flags.mbox,
- 2262 :
// flags from plugins
- 2263 :
flags: flags.extra_flags
- 2264 :
});
- 2265 :
- 2266 :
var c, n, col, html, css_class, label, status_class = '', status_label = '',
- 2267 :
tree = '', expando = '',
- 2268 :
list = this.message_list,
- 2269 :
rows = list.rows,
- 2270 :
message = this.env.messages[uid],
- 2271 :
msg_id = this.html_identifier(uid,true),
- 2272 :
row_class = 'message'
- 2273 :
+ (!flags.seen ? ' unread' : '')
- 2274 :
+ (flags.deleted ? ' deleted' : '')
- 2275 :
+ (flags.flagged ? ' flagged' : '')
- 2276 :
+ (message.selected ? ' selected' : ''),
- 2277 :
row = { cols:[], style:{}, id:'rcmrow'+msg_id, uid:uid },
- 2278 :
layout = this.env.msglist_layout,
- 2279 :
listcols = this.env.msglist_cols;
- 2280 :
- 2281 :
// widescreen layout does not have a separate status column
- 2282 :
if (layout == 'widescreen')
- 2283 :
this.env.status_col = null;
- 2284 :
else if ((n = $.inArray('status', listcols)) >= 0)
- 2285 :
this.env.status_col = n;
- 2286 :
- 2287 :
// message status icons
- 2288 :
css_class = 'msgicon';
- 2289 :
if (this.env.status_col === null) {
- 2290 :
css_class += ' status';
- 2291 :
if (flags.deleted) {
- 2292 :
status_class += ' deleted';
- 2293 :
status_label += this.get_label('deleted') + ' ';
- 2294 :
}
- 2295 :
else if (!flags.seen) {
- 2296 :
status_class += ' unread';
- 2297 :
status_label += this.get_label('unread') + ' ';
- 2298 :
}
- 2299 :
else if (flags.unread_children > 0) {
- 2300 :
status_class += ' unreadchildren';
- 2301 :
}
- 2302 :
}
- 2303 :
if (flags.answered) {
- 2304 :
status_class += ' replied';
- 2305 :
status_label += this.get_label('replied') + ' ';
- 2306 :
}
- 2307 :
if (flags.forwarded) {
- 2308 :
status_class += ' forwarded';
- 2309 :
status_label += this.get_label('forwarded') + ' ';
- 2310 :
}
- 2311 :
- 2312 :
// update selection
- 2313 :
if (message.selected && !list.in_selection(uid))
- 2314 :
list.selection.push(uid);
- 2315 :
- 2316 :
// threads
- 2317 :
if (this.env.threading) {
- 2318 :
if (message.depth) {
- 2319 :
// This assumes that div width is hardcoded to 15px,
- 2320 :
tree += '<span id="rcmtab' + msg_id + '" class="branch" style="width:' + (message.depth * 15) + 'px;"> </span>';
- 2321 :
- 2322 :
if ((rows[message.parent_uid] && rows[message.parent_uid].expanded === false)
- 2323 :
|| ((this.env.autoexpand_threads == 0 || this.env.autoexpand_threads == 2) &&
- 2324 :
(!rows[message.parent_uid] || !rows[message.parent_uid].expanded))
- 2325 :
) {
- 2326 :
row.style.display = 'none';
- 2327 :
message.expanded = false;
- 2328 :
}
- 2329 :
else
- 2330 :
message.expanded = true;
- 2331 :
- 2332 :
row_class += ' thread expanded';
- 2333 :
}
- 2334 :
else if (message.has_children) {
- 2335 :
if (message.expanded === undefined && (this.env.autoexpand_threads == 1 || (this.env.autoexpand_threads == 2 && message.unread_children))) {
- 2336 :
message.expanded = true;
- 2337 :
}
- 2338 :
- 2339 :
expando = '<div id="rcmexpando' + row.id + '" class="' + (message.expanded ? 'expanded' : 'collapsed') + '"> </div>';
- 2340 :
row_class += ' thread' + (message.expanded ? ' expanded' : '');
- 2341 :
}
- 2342 :
- 2343 :
if (flags.unread_children && flags.seen && !message.expanded)
- 2344 :
row_class += ' unroot';
- 2345 :
- 2346 :
if (flags.flagged_children && !message.expanded)
- 2347 :
row_class += ' flaggedroot';
- 2348 :
}
- 2349 :
- 2350 :
tree += '<span id="msgicn'+row.id+'" class="'+css_class+status_class+'" title="'+status_label+'"></span>';
- 2351 :
row.className = row_class;
- 2352 :
- 2353 :
// build subject link
- 2354 :
if (cols.subject) {
- 2355 :
var action = flags.mbox == this.env.drafts_mailbox ? 'compose' : 'show',
- 2356 :
uid_param = flags.mbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid',
- 2357 :
query = { _mbox: flags.mbox };
- 2358 :
query[uid_param] = uid;
- 2359 :
cols.subject = '<a href="' + this.url(action, query) + '" onclick="return rcube_event.keyboard_only(event)"' +
- 2360 :
' onmouseover="rcube_webmail.long_subject_title(this,'+(message.depth+1)+')" tabindex="-1"><span>'+cols.subject+'</span></a>';
- 2361 :
}
- 2362 :
- 2363 :
// add each submitted col
- 2364 :
for (n in listcols) {
- 2365 :
c = listcols[n];
- 2366 :
col = {className: String(c).toLowerCase(), events:{}};
- 2367 :
- 2368 :
if (this.env.coltypes[c] && this.env.coltypes[c].hidden) {
- 2369 :
col.className += ' hidden';
- 2370 :
}
- 2371 :
- 2372 :
if (c == 'flag') {
- 2373 :
css_class = (flags.flagged ? 'flagged' : 'unflagged');
- 2374 :
label = this.get_label(css_class);
- 2375 :
html = '<span id="flagicn'+row.id+'" class="'+css_class+'" title="'+label+'"></span>';
- 2376 :
}
- 2377 :
else if (c == 'attachment') {
- 2378 :
label = this.get_label('withattachment');
- 2379 :
if (flags.attachmentClass)
- 2380 :
html = '<span class="'+flags.attachmentClass+'" title="'+label+'"></span>';
- 2381 :
else if (flags.ctype == 'multipart/report')
- 2382 :
html = '<span class="report"></span>';
- 2383 :
else if (flags.ctype == 'multipart/encrypted' || flags.ctype == 'application/pkcs7-mime')
- 2384 :
html = '<span class="encrypted"></span>';
- 2385 :
else if (flags.hasattachment || (!flags.hasnoattachment && /application\/|multipart\/(m|signed)/.test(flags.ctype)))
- 2386 :
html = '<span class="attachment" title="'+label+'"></span>';
- 2387 :
else
- 2388 :
html = ' ';
- 2389 :
}
- 2390 :
else if (c == 'status') {
- 2391 :
label = '';
- 2392 :
if (flags.deleted) {
- 2393 :
css_class = 'deleted';
- 2394 :
label = this.get_label('deleted');
- 2395 :
}
- 2396 :
else if (!flags.seen) {
- 2397 :
css_class = 'unread';
- 2398 :
label = this.get_label('unread');
- 2399 :
}
- 2400 :
else if (flags.unread_children > 0) {
- 2401 :
css_class = 'unreadchildren';
- 2402 :
}
- 2403 :
else
- 2404 :
css_class = 'msgicon';
- 2405 :
html = '<span id="statusicn'+row.id+'" class="'+css_class+status_class+'" title="'+label+'"></span>';
- 2406 :
}
- 2407 :
else if (c == 'threads')
- 2408 :
html = expando;
- 2409 :
else if (c == "fromto")//PAMELA
- 2410 :
{
- 2411 :
html = tree + cols[c];
- 2412 :
}
- 2413 :
else if (c == 'subject') {
- 2414 :
html = cols[c];
- 2415 :
}
- 2416 :
else if (c == 'priority') {
- 2417 :
if (flags.prio > 0 && flags.prio < 6) {
- 2418 :
label = this.get_label('priority') + ' ' + flags.prio;
- 2419 :
html = '<span class="prio'+flags.prio+'" title="'+label+'"></span>';
- 2420 :
}
- 2421 :
else
- 2422 :
html = ' ';
- 2423 :
}
- 2424 :
else if (c == 'folder') {
- 2425 :
html = '<span onmouseover="rcube_webmail.long_subject_title(this)">' + cols[c] + '<span>';
- 2426 :
}
- 2427 :
else
- 2428 :
html = cols[c];
- 2429 :
- 2430 :
// PAMELA
- 2431 :
col.innerHTML = rcmail.triggerEvent('rcmail.addrow.update_html', {html, uid, cols, flags, attop, c}) || html;;
- 2432 :
row.cols.push(col);
- 2433 :
}
- 2434 :
- 2435 :
if (layout == 'widescreen')
- 2436 :
row = this.widescreen_message_row(row, uid, message);
- 2437 :
- 2438 :
list.insert_row(row, attop);
- 2439 :
- 2440 :
// remove 'old' row
- 2441 :
if (attop && this.env.pagesize && list.rowcount > this.env.pagesize) {
- 2442 :
var uid = list.get_last_row();
- 2443 :
list.remove_row(uid);
- 2444 :
list.clear_selection(uid);
- 2445 :
}
- 2446 :
};
- 2447 :
- 2448 :
// Converts standard message list record into "widescreen" (3-column) layout
- 2449 :
this.widescreen_message_row = function(row, uid, message)
- 2450 :
{
- 2451 :
var domrow = document.createElement('tr');
- 2452 :
- 2453 :
domrow.id = row.id;
- 2454 :
domrow.uid = row.uid;
- 2455 :
domrow.className = row.className;
- 2456 :
if (row.style) $.extend(domrow.style, row.style);
- 2457 :
- 2458 :
$.each(this.env.widescreen_list_template, function() {
- 2459 :
if (!ref.env.threading && this.className == 'threads')
- 2460 :
return;
- 2461 :
- 2462 :
var i, n, e, col, domcol,
- 2463 :
domcell = document.createElement('td');
- 2464 :
- 2465 :
if (this.className) domcell.className = this.className;
- 2466 :
- 2467 :
for (i=0; this.cells && i < this.cells.length; i++) {
- 2468 :
for (n=0; row.cols && n < row.cols.length; n++) {
- 2469 :
if (this.cells[i] == row.cols[n].className) {
- 2470 :
col = row.cols[n];
- 2471 :
domcol = document.createElement('span');
- 2472 :
domcol.className = this.cells[i];
- 2473 :
if (this.className == 'subject' && domcol.className != 'subject')
- 2474 :
domcol.className += ' skip-on-drag';
- 2475 :
if (col.innerHTML)
- 2476 :
domcol.innerHTML = col.innerHTML;
- 2477 :
domcell.appendChild(domcol);
- 2478 :
break;
- 2479 :
}
- 2480 :
}
- 2481 :
}
- 2482 :
- 2483 :
domrow.appendChild(domcell);
- 2484 :
});
- 2485 :
- 2486 :
if (this.env.threading && message.depth) {
- 2487 :
n = this.calculate_thread_padding(message.depth);
- 2488 :
$('td.subject', domrow).attr('style', 'padding-left:' + n + ' !important');
- 2489 :
$('span.branch', domrow).remove();
- 2490 :
}
- 2491 :
- 2492 :
return domrow;
- 2493 :
};
- 2494 :
- 2495 :
this.calculate_thread_padding = function(level)
- 2496 :
{
- 2497 :
ref.env.thread_padding.match(/^([0-9.]+)(.+)/);
- 2498 :
return (Math.min(6, level) * parseFloat(RegExp.$1)) + RegExp.$2;
- 2499 :
};
- 2500 :
- 2501 :
this.set_list_sorting = function(sort_col, sort_order)
- 2502 :
{
- 2503 :
var sort_old = this.env.sort_col == 'arrival' ? 'date' : this.env.sort_col,
- 2504 :
sort_new = sort_col == 'arrival' ? 'date' : sort_col;
- 2505 :
- 2506 :
// set table header class
- 2507 :
$('#rcm' + sort_old).removeClass('sorted' + this.env.sort_order.toUpperCase());
- 2508 :
if (sort_new)
- 2509 :
$('#rcm' + sort_new).addClass('sorted' + sort_order);
- 2510 :
- 2511 :
// if sorting by 'arrival' is selected, click on date column should not switch to 'date'
- 2512 :
$('#rcmdate > a').prop('rel', sort_col == 'arrival' ? 'arrival' : 'date');
- 2513 :
- 2514 :
this.env.sort_col = sort_col;
- 2515 :
this.env.sort_order = sort_order;
- 2516 :
};
- 2517 :
- 2518 :
this.set_list_options = function(cols, sort_col, sort_order, threads, layout)
- 2519 :
{
- 2520 :
var update, post_data = {};
- 2521 :
- 2522 :
if (sort_col === undefined)
- 2523 :
sort_col = this.env.sort_col;
- 2524 :
if (!sort_order)
- 2525 :
sort_order = this.env.sort_order;
- 2526 :
- 2527 :
if (this.env.sort_col != sort_col || this.env.sort_order != sort_order) {
- 2528 :
update = 1;
- 2529 :
this.set_list_sorting(sort_col, sort_order);
- 2530 :
}
- 2531 :
- 2532 :
if (this.env.threading != threads) {
- 2533 :
update = 1;
- 2534 :
post_data._threads = threads;
- 2535 :
}
- 2536 :
- 2537 :
if (layout && this.env.layout != layout) {
- 2538 :
this.triggerEvent('layout-change', {old_layout: this.env.layout, new_layout: layout});
- 2539 :
update = 1;
- 2540 :
this.env.layout = post_data._layout = layout;
- 2541 :
- 2542 :
// update message list setup
- 2543 :
this.msglist_setup(this.env.layout);
- 2544 :
}
- 2545 :
- 2546 :
if (cols && cols.length) {
- 2547 :
// make sure new columns are added at the end of the list
- 2548 :
var i, idx, name, newcols = [], oldcols = this.env.listcols;
- 2549 :
- 2550 :
for (i=0; i<oldcols.length; i++) {
- 2551 :
name = oldcols[i];
- 2552 :
idx = $.inArray(name, cols);
- 2553 :
if (idx != -1) {
- 2554 :
newcols.push(name);
- 2555 :
delete cols[idx];
- 2556 :
}
- 2557 :
}
- 2558 :
for (i=0; i<cols.length; i++)
- 2559 :
if (cols[i])
- 2560 :
newcols.push(cols[i]);
- 2561 :
- 2562 :
if (newcols.join() != oldcols.join()) {
- 2563 :
update = 1;
- 2564 :
post_data._cols = newcols.join(',');
- 2565 :
}
- 2566 :
}
- 2567 :
- 2568 :
if (update)
- 2569 :
this.list_mailbox('', '', sort_col+'_'+sort_order, post_data);
- 2570 :
};
- 2571 :
- 2572 :
// when user double-clicks on a row
- 2573 :
this.show_message = function(id, safe, preview)
- 2574 :
{
- 2575 :
if (!id)
- 2576 :
return;
- 2577 :
- 2578 :
var win, target = window,
- 2579 :
url = this.params_from_uid(id, {_caps: this.browser_capabilities()});
- 2580 :
- 2581 :
if (preview && (win = this.get_frame_window(this.env.contentframe))) {
- 2582 :
target = win;
- 2583 :
url._framed = 1;
- 2584 :
}
- 2585 :
- 2586 :
if (safe)
- 2587 :
url._safe = 1;
- 2588 :
- 2589 :
// also send search request to get the right messages
- 2590 :
if (this.env.search_request)
- 2591 :
url._search = this.env.search_request;
- 2592 :
- 2593 :
if (this.env.extwin)
- 2594 :
url._extwin = 1;
- 2595 :
- 2596 :
url = this.url(preview ? 'preview': 'show', url);
- 2597 :
- 2598 :
if (preview)
- 2599 :
this.preview_id = id;
- 2600 :
- 2601 :
if (preview && String(target.location.href).indexOf(url) >= 0) {
- 2602 :
this.show_contentframe(true);
- 2603 :
}
- 2604 :
else {
- 2605 :
if (!preview && this.env.message_extwin && !this.env.extwin)
- 2606 :
this.open_window(url, true);
- 2607 :
else {
- 2608 :
// "Allow remote resources" reloads the page, we remove this request from the history,
- 2609 :
// so Back button works as expected, i.e. ignores the reload request (#6620)
- 2610 :
if (safe && document.referrer && window.history.replaceState)
- 2611 :
window.history.replaceState({}, '', document.referrer);
- 2612 :
- 2613 :
this.location_href(url, target, true);
- 2614 :
}
- 2615 :
}
- 2616 :
};
- 2617 :
- 2618 :
// update message status and unread counter after marking a message as read
- 2619 :
this.set_unread_message = function(id, folder)
- 2620 :
{
- 2621 :
var self = this;
- 2622 :
- 2623 :
// find window with messages list
- 2624 :
if (!self.message_list)
- 2625 :
self = self.opener();
- 2626 :
- 2627 :
if (!self && window.parent)
- 2628 :
self = parent.rcmail;
- 2629 :
- 2630 :
if (!self || !self.message_list)
- 2631 :
return;
- 2632 :
- 2633 :
// this may fail in multifolder mode
- 2634 :
if (self.set_message(id, 'unread', false) === false)
- 2635 :
self.set_message(id + '-' + folder, 'unread', false);
- 2636 :
- 2637 :
if (self.env.unread_counts[folder] > 0) {
- 2638 :
self.env.unread_counts[folder] -= 1;
- 2639 :
self.set_unread_count(folder, self.env.unread_counts[folder], folder == 'INBOX' && !self.is_multifolder_listing());
- 2640 :
}
- 2641 :
};
- 2642 :
- 2643 :
this.show_contentframe = function(show)
- 2644 :
{
- 2645 :
var frame, win, name = this.env.contentframe;
- 2646 :
- 2647 :
if (frame = this.get_frame_element(name)) {
- 2648 :
if (!show && (win = this.get_frame_window(name))) {
- 2649 :
if (win.location.href.indexOf(this.env.blankpage) < 0) {
- 2650 :
if (win.stop)
- 2651 :
win.stop();
- 2652 :
else // IE
- 2653 :
win.document.execCommand('Stop');
- 2654 :
- 2655 :
win.location.href = this.env.blankpage;
- 2656 :
}
- 2657 :
}
- 2658 :
else if (!bw.safari && !bw.konq)
- 2659 :
$(frame)[show ? 'show' : 'hide']();
- 2660 :
}
- 2661 :
- 2662 :
if (!show) {
- 2663 :
this.unlock_frame();
- 2664 :
delete this.preview_id;
- 2665 :
}
- 2666 :
};
- 2667 :
- 2668 :
this.get_frame_element = function(id)
- 2669 :
{
- 2670 :
var frame;
- 2671 :
- 2672 :
if (id && (frame = document.getElementById(id)))
- 2673 :
return frame;
- 2674 :
};
- 2675 :
- 2676 :
this.get_frame_window = function(id)
- 2677 :
{
- 2678 :
var frame = this.get_frame_element(id);
- 2679 :
- 2680 :
if (frame && frame.name && window.frames)
- 2681 :
return window.frames[frame.name];
- 2682 :
};
- 2683 :
- 2684 :
this.lock_frame = function(target)
- 2685 :
{
- 2686 :
var rc = this.is_framed() ? parent.rcmail : this;
- 2687 :
- 2688 :
if (!rc.env.frame_lock)
- 2689 :
rc.env.frame_lock = rc.set_busy(true, 'loading');
- 2690 :
- 2691 :
if (target.frameElement)
- 2692 :
$(target.frameElement).on('load.lock', function(e) {
- 2693 :
rc.unlock_frame();
- 2694 :
$(this).off('load.lock');
- 2695 :
});
- 2696 :
};
- 2697 :
- 2698 :
this.unlock_frame = function()
- 2699 :
{
- 2700 :
if (this.env.frame_lock) {
- 2701 :
this.set_busy(false, null, this.env.frame_lock);
- 2702 :
this.env.frame_lock = null;
- 2703 :
}
- 2704 :
};
- 2705 :
- 2706 :
// list a specific page
- 2707 :
this.list_page = function(page)
- 2708 :
{
- 2709 :
if (page == 'next')
- 2710 :
page = this.env.current_page+1;
- 2711 :
else if (page == 'last')
- 2712 :
page = this.env.pagecount;
- 2713 :
else if (page == 'prev' && this.env.current_page > 1)
- 2714 :
page = this.env.current_page-1;
- 2715 :
else if (page == 'first' && this.env.current_page > 1)
- 2716 :
page = 1;
- 2717 :
- 2718 :
if (page > 0 && page <= this.env.pagecount) {
- 2719 :
this.env.current_page = page;
- 2720 :
- 2721 :
if (this.task == 'addressbook' || this.contact_list)
- 2722 :
this.list_contacts(this.env.source, this.env.group, page);
- 2723 :
else if (this.task == 'mail')
- 2724 :
this.list_mailbox(this.env.mailbox, page);
- 2725 :
}
- 2726 :
};
- 2727 :
- 2728 :
// sends request to check for recent messages
- 2729 :
this.checkmail = function()
- 2730 :
{
- 2731 :
var lock = this.set_busy(true, 'checkingmail'),
- 2732 :
params = this.check_recent_params();
- 2733 :
- 2734 :
this.http_post('check-recent', params, lock);
- 2735 :
};
- 2736 :
- 2737 :
// list messages of a specific mailbox using filter
- 2738 :
this.filter_mailbox = function(filter)
- 2739 :
{
- 2740 :
if (this.filter_disabled)
- 2741 :
return;
- 2742 :
- 2743 :
var params = this.search_params(false, filter),
- 2744 :
lock = this.set_busy(true, 'searching');
- 2745 :
- 2746 :
this.clear_message_list();
- 2747 :
- 2748 :
// reset vars
- 2749 :
this.env.current_page = 1;
- 2750 :
this.env.search_filter = filter;
- 2751 :
this.http_request('search', params, lock);
- 2752 :
this.update_state({_mbox: params._mbox, _filter: filter, _scope: params._scope});
- 2753 :
};
- 2754 :
- 2755 :
// reload the current message listing
- 2756 :
this.refresh_list = function()
- 2757 :
{
- 2758 :
this.list_mailbox(this.env.mailbox, this.env.current_page || 1, null, { _clear:1 }, true);
- 2759 :
if (this.message_list)
- 2760 :
this.message_list.clear_selection();
- 2761 :
};
- 2762 :
- 2763 :
// list messages of a specific mailbox
- 2764 :
this.list_mailbox = function(mbox, page, sort, url, update_only)
- 2765 :
{
- 2766 :
var win, target = window;
- 2767 :
- 2768 :
if (typeof url != 'object')
- 2769 :
url = {};
- 2770 :
- 2771 :
if (!mbox)
- 2772 :
mbox = this.env.mailbox ? this.env.mailbox : 'INBOX';
- 2773 :
- 2774 :
// add sort to url if set
- 2775 :
if (sort)
- 2776 :
url._sort = sort;
- 2777 :
- 2778 :
// folder change, reset page, search scope, etc.
- 2779 :
if (this.env.mailbox != mbox) {
- 2780 :
page = 1;
- 2781 :
this.env.current_page = page;
- 2782 :
this.env.search_scope = 'base';
- 2783 :
this.select_all_mode = false;
- 2784 :
this.reset_search_filter();
- 2785 :
}
- 2786 :
// also send search request to get the right messages
- 2787 :
else if (this.env.search_request)
- 2788 :
url._search = this.env.search_request;
- 2789 :
- 2790 :
if (!update_only) {
- 2791 :
// unselect selected messages and clear the list and message data
- 2792 :
this.clear_message_list();
- 2793 :
- 2794 :
if (mbox != this.env.mailbox || (mbox == this.env.mailbox && !page && !sort))
- 2795 :
url._refresh = 1;
- 2796 :
- 2797 :
this.select_folder(mbox, '', true);
- 2798 :
this.unmark_folder(mbox, 'recent', '', true);
- 2799 :
this.env.mailbox = mbox;
- 2800 :
}
- 2801 :
- 2802 :
// load message list remotely
- 2803 :
if (this.gui_objects.messagelist) {
- 2804 :
this.list_mailbox_remote(mbox, page, url);
- 2805 :
return;
- 2806 :
}
- 2807 :
- 2808 :
if (win = this.get_frame_window(this.env.contentframe)) {
- 2809 :
target = win;
- 2810 :
url._framed = 1;
- 2811 :
}
- 2812 :
- 2813 :
if (this.env.uid)
- 2814 :
url._uid = this.env.uid;
- 2815 :
- 2816 :
if (page)
- 2817 :
url._page = page;
- 2818 :
- 2819 :
// load message list to target frame/window
- 2820 :
if (mbox) {
- 2821 :
url._mbox = mbox;
- 2822 :
this.set_busy(true, 'loading');
- 2823 :
this.location_href(url, target);
- 2824 :
}
- 2825 :
};
- 2826 :
- 2827 :
this.clear_message_list = function()
- 2828 :
{
- 2829 :
this.env.messages = {};
- 2830 :
- 2831 :
this.show_contentframe(false);
- 2832 :
if (this.message_list)
- 2833 :
this.message_list.clear(true);
- 2834 :
};
- 2835 :
- 2836 :
// send remote request to load message list
- 2837 :
this.list_mailbox_remote = function(mbox, page, url)
- 2838 :
{
- 2839 :
var lock = this.set_busy(true, 'loading');
- 2840 :
- 2841 :
if (typeof url != 'object')
- 2842 :
url = {};
- 2843 :
- 2844 :
url._layout = this.env.layout
- 2845 :
url._mbox = mbox;
- 2846 :
url._page = page;
- 2847 :
- 2848 :
this.http_request('list', url, lock);
- 2849 :
this.update_state({ _mbox: mbox, _page: (page && page > 1 ? page : null) });
- 2850 :
};
- 2851 :
- 2852 :
// removes messages that doesn't exists from list selection array
- 2853 :
this.update_selection = function()
- 2854 :
{
- 2855 :
var list = this.message_list,
- 2856 :
selected = list.selection,
- 2857 :
rows = list.rows,
- 2858 :
i, selection = [];
- 2859 :
- 2860 :
for (i in selected)
- 2861 :
if (rows[selected[i]])
- 2862 :
selection.push(selected[i]);
- 2863 :
- 2864 :
list.selection = selection;
- 2865 :
- 2866 :
// reset preview frame, if currently previewed message is not selected (has been removed)
- 2867 :
try {
- 2868 :
var win = this.get_frame_window(this.env.contentframe),
- 2869 :
id = win.rcmail.env.uid;
- 2870 :
- 2871 :
if (id && !list.in_selection(id))
- 2872 :
this.show_contentframe(false);
- 2873 :
}
- 2874 :
catch (e) {};
- 2875 :
};
- 2876 :
- 2877 :
// expand all threads with unread children
- 2878 :
this.expand_unread = function()
- 2879 :
{
- 2880 :
var r, tbody = this.message_list.tbody,
- 2881 :
new_row = tbody.firstChild;
- 2882 :
- 2883 :
while (new_row) {
- 2884 :
if (new_row.nodeType == 1 && (r = this.message_list.rows[new_row.uid]) && r.unread_children) {
- 2885 :
this.message_list.expand_all(r);
- 2886 :
this.set_unread_children(r.uid);
- 2887 :
}
- 2888 :
- 2889 :
new_row = new_row.nextSibling;
- 2890 :
}
- 2891 :
- 2892 :
return false;
- 2893 :
};
- 2894 :
- 2895 :
// thread expanding/collapsing handler
- 2896 :
this.expand_message_row = function(e, uid)
- 2897 :
{
- 2898 :
var row = this.message_list.rows[uid];
- 2899 :
- 2900 :
// handle unread_children/flagged_children mark
- 2901 :
row.expanded = !row.expanded;
- 2902 :
this.set_unread_children(uid);
- 2903 :
this.set_flagged_children(uid);
- 2904 :
row.expanded = !row.expanded;
- 2905 :
- 2906 :
this.message_list.expand_row(e, uid);
- 2907 :
};
- 2908 :
- 2909 :
// message list expanding
- 2910 :
this.expand_threads = function()
- 2911 :
{
- 2912 :
if (!this.env.threading || !this.env.autoexpand_threads || !this.message_list)
- 2913 :
return;
- 2914 :
- 2915 :
switch (this.env.autoexpand_threads) {
- 2916 :
case 2: this.expand_unread(); break;
- 2917 :
case 1: this.message_list.expand_all(); break;
- 2918 :
}
- 2919 :
};
- 2920 :
- 2921 :
// Initializes threads indicators/expanders after list update
- 2922 :
this.init_threads = function(roots, mbox)
- 2923 :
{
- 2924 :
// #1487752
- 2925 :
if (mbox && mbox != this.env.mailbox)
- 2926 :
return false;
- 2927 :
- 2928 :
for (var n=0, len=roots.length; n<len; n++)
- 2929 :
this.add_tree_icons(roots[n]);
- 2930 :
this.expand_threads();
- 2931 :
};
- 2932 :
- 2933 :
// adds threads tree icons to the list (or specified thread)
- 2934 :
this.add_tree_icons = function(root)
- 2935 :
{
- 2936 :
var i, l, r, n, len, pos, tmp = [], uid = [],
- 2937 :
row, rows = this.message_list.rows;
- 2938 :
- 2939 :
if (root)
- 2940 :
row = rows[root] ? rows[root].obj : null;
- 2941 :
else
- 2942 :
row = this.message_list.tbody.firstChild;
- 2943 :
- 2944 :
while (row) {
- 2945 :
if (row.nodeType == 1 && (r = rows[row.uid])) {
- 2946 :
if (r.depth) {
- 2947 :
for (i=tmp.length-1; i>=0; i--) {
- 2948 :
len = tmp[i].length;
- 2949 :
if (len > r.depth) {
- 2950 :
pos = len - r.depth;
- 2951 :
if (!(tmp[i][pos] & 2))
- 2952 :
tmp[i][pos] = tmp[i][pos] ? tmp[i][pos]+2 : 2;
- 2953 :
}
- 2954 :
else if (len == r.depth) {
- 2955 :
if (!(tmp[i][0] & 2))
- 2956 :
tmp[i][0] += 2;
- 2957 :
}
- 2958 :
if (r.depth > len)
- 2959 :
break;
- 2960 :
}
- 2961 :
- 2962 :
tmp.push(new Array(r.depth));
- 2963 :
tmp[tmp.length-1][0] = 1;
- 2964 :
uid.push(r.uid);
- 2965 :
}
- 2966 :
else {
- 2967 :
if (tmp.length) {
- 2968 :
for (i in tmp) {
- 2969 :
this.set_tree_icons(uid[i], tmp[i]);
- 2970 :
}
- 2971 :
tmp = [];
- 2972 :
uid = [];
- 2973 :
}
- 2974 :
if (root && row != rows[root].obj)
- 2975 :
break;
- 2976 :
}
- 2977 :
}
- 2978 :
row = row.nextSibling;
- 2979 :
}
- 2980 :
- 2981 :
if (tmp.length) {
- 2982 :
for (i in tmp) {
- 2983 :
this.set_tree_icons(uid[i], tmp[i]);
- 2984 :
}
- 2985 :
}
- 2986 :
};
- 2987 :
- 2988 :
// adds tree icons to specified message row
- 2989 :
this.set_tree_icons = function(uid, tree)
- 2990 :
{
- 2991 :
var i, divs = [], html = '', len = tree.length;
- 2992 :
- 2993 :
for (i=0; i<len; i++) {
- 2994 :
if (tree[i] > 2)
- 2995 :
divs.push({'class': 'l3', width: 15});
- 2996 :
else if (tree[i] > 1)
- 2997 :
divs.push({'class': 'l2', width: 15});
- 2998 :
else if (tree[i] > 0)
- 2999 :
divs.push({'class': 'l1', width: 15});
- 3000 :
// separator div
- 3001 :
else if (divs.length && !divs[divs.length-1]['class'])
- 3002 :
divs[divs.length-1].width += 15;
- 3003 :
else
- 3004 :
divs.push({'class': null, width: 15});
- 3005 :
}
- 3006 :
- 3007 :
for (i=divs.length-1; i>=0; i--) {
- 3008 :
if (divs[i]['class'])
- 3009 :
html += '<div class="tree '+divs[i]['class']+'" />';
- 3010 :
else
- 3011 :
html += '<div style="width:'+divs[i].width+'px" />';
- 3012 :
}
- 3013 :
- 3014 :
if (html)
- 3015 :
$('#rcmtab'+this.html_identifier(uid, true)).html(html);
- 3016 :
};
- 3017 :
- 3018 :
// update parent in a thread
- 3019 :
this.update_thread_root = function(uid, flag)
- 3020 :
{
- 3021 :
if (!this.env.threading)
- 3022 :
return;
- 3023 :
- 3024 :
var root = this.message_list.find_root(uid);
- 3025 :
- 3026 :
if (uid == root)
- 3027 :
return;
- 3028 :
- 3029 :
var p = this.message_list.rows[root];
- 3030 :
- 3031 :
if (flag == 'read' && p.unread_children) {
- 3032 :
p.unread_children--;
- 3033 :
}
- 3034 :
else if (flag == 'unread' && p.has_children) {
- 3035 :
// unread_children may be undefined
- 3036 :
p.unread_children = (p.unread_children || 0) + 1;
- 3037 :
}
- 3038 :
else if (flag == 'unflagged' && p.flagged_children) {
- 3039 :
p.flagged_children--;
- 3040 :
}
- 3041 :
else if (flag == 'flagged' && p.has_children) {
- 3042 :
p.flagged_children = (p.flagged_children || 0) + 1;
- 3043 :
}
- 3044 :
else {
- 3045 :
return;
- 3046 :
}
- 3047 :
- 3048 :
this.set_message_icon(root);
- 3049 :
this.set_unread_children(root);
- 3050 :
this.set_flagged_children(root);
- 3051 :
};
- 3052 :
- 3053 :
// update thread indicators for all messages in a thread below the specified message
- 3054 :
// return number of removed/added root level messages
- 3055 :
this.update_thread = function(uid)
- 3056 :
{
- 3057 :
if (!this.env.threading || !this.message_list.rows[uid])
- 3058 :
return 0;
- 3059 :
- 3060 :
var r, parent, count = 0,
- 3061 :
list = this.message_list,
- 3062 :
rows = list.rows,
- 3063 :
row = rows[uid],
- 3064 :
depth = rows[uid].depth,
- 3065 :
roots = [];
- 3066 :
- 3067 :
if (!row.depth) // root message: decrease roots count
- 3068 :
count--;
- 3069 :
- 3070 :
// update unread_children for thread root
- 3071 :
if (row.depth && row.unread) {
- 3072 :
parent = list.find_root(uid);
- 3073 :
rows[parent].unread_children--;
- 3074 :
this.set_unread_children(parent);
- 3075 :
}
- 3076 :
- 3077 :
// update unread_children for thread root
- 3078 :
if (row.depth && row.flagged) {
- 3079 :
parent = list.find_root(uid);
- 3080 :
rows[parent].flagged_children--;
- 3081 :
this.set_flagged_children(parent);
- 3082 :
}
- 3083 :
- 3084 :
parent = row.parent_uid;
- 3085 :
- 3086 :
// children
- 3087 :
row = row.obj.nextSibling;
- 3088 :
while (row) {
- 3089 :
if (row.nodeType == 1 && (r = rows[row.uid])) {
- 3090 :
if (!r.depth || r.depth <= depth)
- 3091 :
break;
- 3092 :
- 3093 :
r.depth--; // move left
- 3094 :
// reset width and clear the content of a tab, icons will be added later
- 3095 :
$('#rcmtab'+r.id).width(r.depth * 15).html('');
- 3096 :
if (!r.depth) { // a new root
- 3097 :
count++; // increase roots count
- 3098 :
r.parent_uid = 0;
- 3099 :
if (r.has_children) {
- 3100 :
// replace 'leaf' with 'collapsed'
- 3101 :
$('#' + r.id + ' .leaf').first()
- 3102 :
.attr('id', 'rcmexpando' + r.id)
- 3103 :
.attr('class', (r.obj.style.display != 'none' ? 'expanded' : 'collapsed'))
- 3104 :
.mousedown({uid: r.uid}, function(e) {
- 3105 :
return ref.expand_message_row(e, e.data.uid);
- 3106 :
});
- 3107 :
- 3108 :
r.unread_children = 0;
- 3109 :
roots.push(r);
- 3110 :
}
- 3111 :
// show if it was hidden
- 3112 :
if (r.obj.style.display == 'none')
- 3113 :
$(r.obj).show();
- 3114 :
}
- 3115 :
else {
- 3116 :
if (r.depth == depth)
- 3117 :
r.parent_uid = parent;
- 3118 :
if (r.unread && roots.length)
- 3119 :
roots[roots.length-1].unread_children++;
- 3120 :
}
- 3121 :
}
- 3122 :
row = row.nextSibling;
- 3123 :
}
- 3124 :
- 3125 :
// update unread_children/flagged_children for roots
- 3126 :
for (r=0; r<roots.length; r++) {
- 3127 :
this.set_unread_children(roots[r].uid);
- 3128 :
this.set_flagged_children(roots[r].uid);
- 3129 :
}
- 3130 :
- 3131 :
return count;
- 3132 :
};
- 3133 :
- 3134 :
this.delete_excessive_thread_rows = function()
- 3135 :
{
- 3136 :
var rows = this.message_list.rows,
- 3137 :
tbody = this.message_list.tbody,
- 3138 :
row = tbody.firstChild,
- 3139 :
cnt = this.env.pagesize + 1;
- 3140 :
- 3141 :
while (row) {
- 3142 :
if (row.nodeType == 1 && (r = rows[row.uid])) {
- 3143 :
if (!r.depth && cnt)
- 3144 :
cnt--;
- 3145 :
- 3146 :
if (!cnt)
- 3147 :
this.message_list.remove_row(row.uid);
- 3148 :
}
- 3149 :
row = row.nextSibling;
- 3150 :
}
- 3151 :
};
- 3152 :
- 3153 :
// set message icon
- 3154 :
this.set_message_icon = function(uid)
- 3155 :
{
- 3156 :
var css_class, label = '',
- 3157 :
row = this.message_list.rows[uid];
- 3158 :
- 3159 :
if (!row)
- 3160 :
return false;
- 3161 :
- 3162 :
if (row.icon) {
- 3163 :
css_class = 'msgicon';
- 3164 :
if (row.deleted) {
- 3165 :
css_class += ' deleted';
- 3166 :
label += this.get_label('deleted') + ' ';
- 3167 :
}
- 3168 :
else if (row.unread) {
- 3169 :
css_class += ' unread';
- 3170 :
label += this.get_label('unread') + ' ';
- 3171 :
}
- 3172 :
else if (row.unread_children)
- 3173 :
css_class += ' unreadchildren';
- 3174 :
if (row.msgicon == row.icon) {
- 3175 :
if (row.replied) {
- 3176 :
css_class += ' replied';
- 3177 :
label += this.get_label('replied') + ' ';
- 3178 :
}
- 3179 :
if (row.forwarded) {
- 3180 :
css_class += ' forwarded';
- 3181 :
label += this.get_label('forwarded') + ' ';
- 3182 :
}
- 3183 :
css_class += ' status';
- 3184 :
}
- 3185 :
- 3186 :
$(row.icon).attr({'class': css_class, title: label});
- 3187 :
}
- 3188 :
- 3189 :
if (row.msgicon && row.msgicon != row.icon) {
- 3190 :
label = '';
- 3191 :
css_class = 'msgicon';
- 3192 :
if (!row.unread && row.unread_children) {
- 3193 :
css_class += ' unreadchildren';
- 3194 :
}
- 3195 :
if (row.replied) {
- 3196 :
css_class += ' replied';
- 3197 :
label += this.get_label('replied') + ' ';
- 3198 :
}
- 3199 :
if (row.forwarded) {
- 3200 :
css_class += ' forwarded';
- 3201 :
label += this.get_label('forwarded') + ' ';
- 3202 :
}
- 3203 :
- 3204 :
$(row.msgicon).attr({'class': css_class, title: label});
- 3205 :
}
- 3206 :
- 3207 :
if (row.flagicon) {
- 3208 :
css_class = (row.flagged ? 'flagged' : 'unflagged');
- 3209 :
label = this.get_label(css_class);
- 3210 :
$(row.flagicon).attr('class', css_class)
- 3211 :
.attr({'aria-label': label, title: label});
- 3212 :
}
- 3213 :
};
- 3214 :
- 3215 :
// set message status
- 3216 :
this.set_message_status = function(uid, flag, status)
- 3217 :
{
- 3218 :
var row = this.message_list.rows[uid];
- 3219 :
- 3220 :
if (!row)
- 3221 :
return false;
- 3222 :
- 3223 :
if (flag == 'unread') {
- 3224 :
if (row.unread != status)
- 3225 :
this.update_thread_root(uid, status ? 'unread' : 'read');
- 3226 :
}
- 3227 :
else if (flag == 'flagged') {
- 3228 :
this.update_thread_root(uid, status ? 'flagged' : 'unflagged');
- 3229 :
}
- 3230 :
- 3231 :
if ($.inArray(flag, ['unread', 'deleted', 'replied', 'forwarded', 'flagged']) > -1)
- 3232 :
row[flag] = status;
- 3233 :
};
- 3234 :
- 3235 :
// set message row status, class and icon
- 3236 :
this.set_message = function(uid, flag, status)
- 3237 :
{
- 3238 :
var row = this.message_list && this.message_list.rows[uid];
- 3239 :
- 3240 :
if (!row)
- 3241 :
return false;
- 3242 :
- 3243 :
if (flag)
- 3244 :
this.set_message_status(uid, flag, status);
- 3245 :
- 3246 :
if ($.inArray(flag, ['unread', 'deleted', 'flagged']) > -1)
- 3247 :
$(row.obj)[row[flag] ? 'addClass' : 'removeClass'](flag);
- 3248 :
- 3249 :
this.set_unread_children(uid);
- 3250 :
this.set_message_icon(uid);
- 3251 :
};
- 3252 :
- 3253 :
// sets unroot (unread_children) class of parent row
- 3254 :
this.set_unread_children = function(uid)
- 3255 :
{
- 3256 :
var row = this.message_list.rows[uid];
- 3257 :
- 3258 :
if (row.parent_uid)
- 3259 :
return;
- 3260 :
- 3261 :
var enable = !row.unread && row.unread_children && !row.expanded;
- 3262 :
$(row.obj)[enable ? 'addClass' : 'removeClass']('unroot');
- 3263 :
};
- 3264 :
- 3265 :
// sets flaggedroot (flagged_children) class of parent row
- 3266 :
this.set_flagged_children = function(uid)
- 3267 :
{
- 3268 :
var row = this.message_list.rows[uid];
- 3269 :
- 3270 :
if (row.parent_uid)
- 3271 :
return;
- 3272 :
- 3273 :
var enable = row.flagged_children && !row.expanded;
- 3274 :
$(row.obj)[enable ? 'addClass' : 'removeClass']('flaggedroot');
- 3275 :
};
- 3276 :
- 3277 :
// copy selected messages to the specified mailbox
- 3278 :
this.copy_messages = function(mbox, event, uids)
- 3279 :
{
- 3280 :
if (mbox && typeof mbox === 'object') {
- 3281 :
if (mbox.uids) uids = mbox.uids;
- 3282 :
mbox = mbox.id;
- 3283 :
}
- 3284 :
else if (!mbox) {
- 3285 :
uids = this.env.uid ? [this.env.uid] : this.message_list.get_selection();
- 3286 :
return this.folder_selector(event, function(folder, obj) {
- 3287 :
ref.command('copy', {id: folder, uids: uids}, obj, event, true);
- 3288 :
});
- 3289 :
}
- 3290 :
- 3291 :
// exit if current or no mailbox specified
- 3292 :
if (!mbox || mbox == this.env.mailbox)
- 3293 :
return;
- 3294 :
- 3295 :
var post_data = this.selection_post_data({_target_mbox: mbox, _uid: uids});
- 3296 :
- 3297 :
// exit if selection is empty
- 3298 :
if (!post_data._uid)
- 3299 :
return;
- 3300 :
- 3301 :
// send request to server
- 3302 :
this.http_post('copy', post_data, this.display_message('copyingmessage', 'loading'));
- 3303 :
};
- 3304 :
- 3305 :
// move selected messages to the specified mailbox
- 3306 :
this.move_messages = function(mbox, event, uids)
- 3307 :
{
- 3308 :
if (mbox && typeof mbox === 'object') {
- 3309 :
if (mbox.uids) uids = mbox.uids;
- 3310 :
mbox = mbox.id;
- 3311 :
}
- 3312 :
else if (!mbox) {
- 3313 :
uids = this.env.uid ? [this.env.uid] : this.message_list.get_selection();
- 3314 :
return this.folder_selector(event, function(folder, obj) {
- 3315 :
ref.command('move', {id: folder, uids: uids}, obj, event, true);
- 3316 :
});
- 3317 :
}
- 3318 :
- 3319 :
// exit if current or no mailbox specified
- 3320 :
if (!mbox || (mbox == this.env.mailbox && !this.is_multifolder_listing()))
- 3321 :
return;
- 3322 :
- 3323 :
var lock = false, post_data = this.selection_post_data({_target_mbox: mbox, _uid: uids});
- 3324 :
- 3325 :
// exit if selection is empty
- 3326 :
if (!post_data._uid)
- 3327 :
return;
- 3328 :
- 3329 :
// show wait message
- 3330 :
if (this.env.action == 'show')
- 3331 :
lock = this.set_busy(true, 'movingmessage');
- 3332 :
- 3333 :
// Hide message command buttons until a message is selected
- 3334 :
this.enable_command(this.env.message_commands, false);
- 3335 :
- 3336 :
this.with_selected_messages('move', post_data, lock);
- 3337 :
- 3338 :
if (this.env.action != 'show')
- 3339 :
this.show_contentframe(false);
- 3340 :
};
- 3341 :
- 3342 :
// delete selected messages from the current mailbox
- 3343 :
this.delete_messages = function(event)
- 3344 :
{
- 3345 :
var list = this.message_list, trash = this.env.trash_mailbox;
- 3346 :
- 3347 :
// if config is set to flag for deletion
- 3348 :
if (this.env.flag_for_deletion) {
- 3349 :
this.mark_message('delete');
- 3350 :
return false;
- 3351 :
}
- 3352 :
// if there isn't a defined trash mailbox or we are in it
- 3353 :
else if (!trash || this.env.mailbox == trash)
- 3354 :
this.permanently_remove_messages();
- 3355 :
// we're in Junk folder and delete_junk is enabled
- 3356 :
else if (this.env.delete_junk && this.env.junk_mailbox && this.env.mailbox == this.env.junk_mailbox)
- 3357 :
this.permanently_remove_messages();
- 3358 :
// if there is a trash mailbox defined and we're not currently in it
- 3359 :
else {
- 3360 :
// if shift was pressed delete it immediately
- 3361 :
if ((list && list.modkey == SHIFT_KEY) || (event && rcube_event.get_modifier(event) == SHIFT_KEY)) {
- 3362 :
this.confirm_dialog(this.get_label('deletemessagesconfirm'), 'delete', function() {
- 3363 :
ref.permanently_remove_messages();
- 3364 :
});
- 3365 :
}
- 3366 :
else
- 3367 :
this.move_messages(trash);
- 3368 :
}
- 3369 :
- 3370 :
return true;
- 3371 :
};
- 3372 :
- 3373 :
// delete the selected messages permanently
- 3374 :
this.permanently_remove_messages = function()
- 3375 :
{
- 3376 :
var post_data = this.selection_post_data();
- 3377 :
- 3378 :
// exit if selection is empty
- 3379 :
if (!post_data._uid)
- 3380 :
return;
- 3381 :
- 3382 :
this.with_selected_messages('delete', post_data);
- 3383 :
this.show_contentframe(false);
- 3384 :
};
- 3385 :
- 3386 :
// Send a specific move/delete request with UIDs of all selected messages
- 3387 :
this.with_selected_messages = function(action, post_data, lock, http_action)
- 3388 :
{
- 3389 :
var count = 0, msg,
- 3390 :
remove = (action == 'delete' || !this.is_multifolder_listing());
- 3391 :
- 3392 :
// update the list (remove rows, clear selection)
- 3393 :
if (this.message_list) {
- 3394 :
var n, len, id, root, roots = [],
- 3395 :
selection = post_data._uid,
- 3396 :
display_next = this.check_display_next();
- 3397 :
- 3398 :
if (selection === '*')
- 3399 :
selection = this.message_list.get_selection();
- 3400 :
else if (typeof selection == 'string')
- 3401 :
selection = selection.split(',');
- 3402 :
- 3403 :
for (n=0, len=selection.length; n<len; n++) {
- 3404 :
id = selection[n];
- 3405 :
- 3406 :
if (this.env.threading) {
- 3407 :
count += this.update_thread(id);
- 3408 :
root = this.message_list.find_root(id);
- 3409 :
if (root != id && $.inArray(root, roots) < 0) {
- 3410 :
roots.push(root);
- 3411 :
}
- 3412 :
}
- 3413 :
- 3414 :
if (remove)
- 3415 :
this.message_list.remove_row(id, display_next && n == selection.length-1);
- 3416 :
}
- 3417 :
- 3418 :
// make sure there are no selected rows
- 3419 :
if (!display_next && remove)
- 3420 :
this.message_list.clear_selection();
- 3421 :
- 3422 :
// update thread tree icons
- 3423 :
for (n=0, len=roots.length; n<len; n++) {
- 3424 :
this.add_tree_icons(roots[n]);
- 3425 :
}
- 3426 :
}
- 3427 :
- 3428 :
if (count < 0)
- 3429 :
post_data._count = (count*-1);
- 3430 :
// remove threads from the end of the list
- 3431 :
else if (count > 0 && remove)
- 3432 :
this.delete_excessive_thread_rows();
- 3433 :
- 3434 :
if (!remove)
- 3435 :
post_data._refresh = 1;
- 3436 :
- 3437 :
if (!lock) {
- 3438 :
msg = action == 'move' ? 'movingmessage' : 'deletingmessage';
- 3439 :
lock = this.display_message(msg, 'loading');
- 3440 :
}
- 3441 :
- 3442 :
// send request to server
- 3443 :
this.http_post(http_action || action, post_data, lock);
- 3444 :
};
- 3445 :
- 3446 :
// build post data for message delete/move/copy/flag requests
- 3447 :
this.selection_post_data = function(data)
- 3448 :
{
- 3449 :
if (typeof(data) != 'object')
- 3450 :
data = {};
- 3451 :
- 3452 :
if (!data._uid)
- 3453 :
data._uid = this.env.uid ? [this.env.uid] : this.message_list.get_selection();
- 3454 :
- 3455 :
data._mbox = this.env.mailbox;
- 3456 :
data._uid = this.uids_to_list(data._uid);
- 3457 :
- 3458 :
if (this.env.action)
- 3459 :
data._from = this.env.action;
- 3460 :
- 3461 :
// also send search request to get the right messages
- 3462 :
if (this.env.search_request)
- 3463 :
data._search = this.env.search_request;
- 3464 :
- 3465 :
if (this.env.display_next && this.env.next_uid)
- 3466 :
data._next_uid = this.env.next_uid;
- 3467 :
- 3468 :
return data;
- 3469 :
};
- 3470 :
- 3471 :
this.check_display_next = function()
- 3472 :
{
- 3473 :
return this.env.display_next && (this.preview_id || !this.env.contentframe);
- 3474 :
};
- 3475 :
- 3476 :
// set a specific flag to one or more messages
- 3477 :
this.mark_message = function(flag, uid)
- 3478 :
{
- 3479 :
var a_uids = [], r_uids = [], len, n, id,
- 3480 :
list = this.message_list;
- 3481 :
- 3482 :
if (uid)
- 3483 :
a_uids[0] = uid;
- 3484 :
else if (this.env.uid)
- 3485 :
a_uids[0] = this.env.uid;
- 3486 :
else if (list)
- 3487 :
a_uids = list.get_selection();
- 3488 :
- 3489 :
if (!list)
- 3490 :
r_uids = a_uids;
- 3491 :
else {
- 3492 :
list.focus();
- 3493 :
for (n=0, len=a_uids.length; n<len; n++) {
- 3494 :
id = a_uids[n];
- 3495 :
if ((flag == 'read' && list.rows[id].unread)
- 3496 :
|| (flag == 'unread' && !list.rows[id].unread)
- 3497 :
|| (flag == 'delete' && !list.rows[id].deleted)
- 3498 :
|| (flag == 'undelete' && list.rows[id].deleted)
- 3499 :
|| (flag == 'flagged' && !list.rows[id].flagged)
- 3500 :
|| (flag == 'unflagged' && list.rows[id].flagged))
- 3501 :
{
- 3502 :
r_uids.push(id);
- 3503 :
}
- 3504 :
}
- 3505 :
}
- 3506 :
- 3507 :
// nothing to do
- 3508 :
if (!r_uids.length && !this.select_all_mode)
- 3509 :
return;
- 3510 :
- 3511 :
switch (flag) {
- 3512 :
case 'read':
- 3513 :
case 'unread':
- 3514 :
this.toggle_read_status(flag, r_uids);
- 3515 :
break;
- 3516 :
case 'delete':
- 3517 :
case 'undelete':
- 3518 :
this.toggle_delete_status(r_uids);
- 3519 :
break;
- 3520 :
case 'flagged':
- 3521 :
case 'unflagged':
- 3522 :
this.toggle_flagged_status(flag, a_uids);
- 3523 :
break;
- 3524 :
}
- 3525 :
};
- 3526 :
- 3527 :
// set class to read/unread
- 3528 :
this.toggle_read_status = function(flag, a_uids)
- 3529 :
{
- 3530 :
var i, len = a_uids.length,
- 3531 :
post_data = this.selection_post_data({_uid: a_uids, _flag: flag}),
- 3532 :
lock = this.display_message('markingmessage', 'loading');
- 3533 :
- 3534 :
// mark all message rows as read/unread
- 3535 :
for (i=0; i<len; i++)
- 3536 :
this.set_message(a_uids[i], 'unread', (flag == 'unread' ? true : false));
- 3537 :
- 3538 :
this.http_post('mark', post_data, lock);
- 3539 :
};
- 3540 :
- 3541 :
// set image to flagged or unflagged
- 3542 :
this.toggle_flagged_status = function(flag, a_uids)
- 3543 :
{
- 3544 :
var i, len = a_uids.length,
- 3545 :
win = this.env.contentframe ? this.get_frame_window(this.env.contentframe) : window,
- 3546 :
post_data = this.selection_post_data({_uid: a_uids, _flag: flag}),
- 3547 :
lock = this.display_message('markingmessage', 'loading');
- 3548 :
- 3549 :
// mark all message rows as flagged/unflagged
- 3550 :
for (i=0; i<len; i++)
- 3551 :
this.set_message(a_uids[i], 'flagged', (flag == 'flagged' ? true : false));
- 3552 :
- 3553 :
if (this.env.action == 'show' || $.inArray(this.preview_id, a_uids) >= 0)
- 3554 :
$(win.document.body)[flag == 'flagged' ? 'addClass' : 'removeClass']('status-flagged');
- 3555 :
- 3556 :
this.http_post('mark', post_data, lock);
- 3557 :
};
- 3558 :
- 3559 :
// mark all message rows as deleted/undeleted
- 3560 :
this.toggle_delete_status = function(a_uids)
- 3561 :
{
- 3562 :
var i, uid, all_deleted = true,
- 3563 :
len = a_uids.length,
- 3564 :
rows = this.message_list ? this.message_list.rows : {};
- 3565 :
- 3566 :
if (len == 1) {
- 3567 :
if (!this.message_list || (rows[a_uids[0]] && !rows[a_uids[0]].deleted))
- 3568 :
this.flag_as_deleted(a_uids);
- 3569 :
else
- 3570 :
this.flag_as_undeleted(a_uids);
- 3571 :
- 3572 :
return true;
- 3573 :
}
- 3574 :
- 3575 :
for (i=0; i<len; i++) {
- 3576 :
uid = a_uids[i];
- 3577 :
if (rows[uid] && !rows[uid].deleted) {
- 3578 :
all_deleted = false;
- 3579 :
break;
- 3580 :
}
- 3581 :
}
- 3582 :
- 3583 :
if (all_deleted)
- 3584 :
this.flag_as_undeleted(a_uids);
- 3585 :
else
- 3586 :
this.flag_as_deleted(a_uids);
- 3587 :
- 3588 :
return true;
- 3589 :
};
- 3590 :
- 3591 :
this.flag_as_undeleted = function(a_uids)
- 3592 :
{
- 3593 :
var i, len = a_uids.length,
- 3594 :
post_data = this.selection_post_data({_uid: a_uids, _flag: 'undelete'}),
- 3595 :
lock = this.display_message('markingmessage', 'loading');
- 3596 :
- 3597 :
for (i=0; i<len; i++)
- 3598 :
this.set_message(a_uids[i], 'deleted', false);
- 3599 :
- 3600 :
this.http_post('mark', post_data, lock);
- 3601 :
};
- 3602 :
- 3603 :
this.flag_as_deleted = function(a_uids)
- 3604 :
{
- 3605 :
var r_uids = [],
- 3606 :
post_data = this.selection_post_data({_uid: a_uids, _flag: 'delete'}),
- 3607 :
lock = this.display_message('markingmessage', 'loading'),
- 3608 :
list = this.message_list,
- 3609 :
rows = list ? list.rows : {},
- 3610 :
count = 0,
- 3611 :
display_next = this.check_display_next();
- 3612 :
- 3613 :
for (var i=0, len=a_uids.length; i<len; i++) {
- 3614 :
uid = a_uids[i];
- 3615 :
if (rows[uid]) {
- 3616 :
if (rows[uid].unread)
- 3617 :
r_uids[r_uids.length] = uid;
- 3618 :
- 3619 :
if (this.env.skip_deleted) {
- 3620 :
count += this.update_thread(uid);
- 3621 :
list.remove_row(uid, display_next && i == list.get_selection(false).length-1);
- 3622 :
}
- 3623 :
else
- 3624 :
this.set_message(uid, 'deleted', true);
- 3625 :
}
- 3626 :
}
- 3627 :
- 3628 :
// make sure there are no selected rows
- 3629 :
if (this.env.skip_deleted && list) {
- 3630 :
if (!display_next || !list.rowcount)
- 3631 :
list.clear_selection();
- 3632 :
if (count < 0)
- 3633 :
post_data._count = (count*-1);
- 3634 :
else if (count > 0)
- 3635 :
// remove threads from the end of the list
- 3636 :
this.delete_excessive_thread_rows();
- 3637 :
}
- 3638 :
- 3639 :
// set of messages to mark as seen
- 3640 :
if (r_uids.length)
- 3641 :
post_data._ruid = this.uids_to_list(r_uids);
- 3642 :
- 3643 :
if (this.env.skip_deleted && this.env.display_next && this.env.next_uid)
- 3644 :
post_data._next_uid = this.env.next_uid;
- 3645 :
- 3646 :
this.http_post('mark', post_data, lock);
- 3647 :
};
- 3648 :
- 3649 :
// flag as read without mark request (called from backend)
- 3650 :
// argument should be a coma-separated list of uids
- 3651 :
this.flag_deleted_as_read = function(uids)
- 3652 :
{
- 3653 :
var uid, i, len,
- 3654 :
rows = this.message_list ? this.message_list.rows : {};
- 3655 :
- 3656 :
if (typeof uids == 'string')
- 3657 :
uids = uids.split(',');
- 3658 :
- 3659 :
for (i=0, len=uids.length; i<len; i++) {
- 3660 :
uid = uids[i];
- 3661 :
if (rows[uid])
- 3662 :
this.set_message(uid, 'unread', false);
- 3663 :
}
- 3664 :
};
- 3665 :
- 3666 :
// Converts array of message UIDs to comma-separated list for use in URL
- 3667 :
// with select_all mode checking
- 3668 :
this.uids_to_list = function(uids)
- 3669 :
{
- 3670 :
if (this.select_all_mode)
- 3671 :
return '*';
- 3672 :
- 3673 :
// multi-folder list of uids cannot be passed as a string (#6845)
- 3674 :
if ($.isArray(uids) && (uids.length == 1 || String(uids[0]).indexOf('-') == -1))
- 3675 :
uids = uids.join(',');
- 3676 :
- 3677 :
return uids;
- 3678 :
};
- 3679 :
- 3680 :
// Sets title of the delete button
- 3681 :
this.set_button_titles = function()
- 3682 :
{
- 3683 :
var label = 'deletemessage';
- 3684 :
- 3685 :
if (!this.env.flag_for_deletion
- 3686 :
&& this.env.trash_mailbox && this.env.mailbox != this.env.trash_mailbox
- 3687 :
&& (!this.env.delete_junk || !this.env.junk_mailbox || this.env.mailbox != this.env.junk_mailbox)
- 3688 :
)
- 3689 :
label = 'movemessagetotrash';
- 3690 :
- 3691 :
this.set_alttext('delete', label);
- 3692 :
};
- 3693 :
- 3694 :
// Initialize input element for list page jump
- 3695 :
this.init_pagejumper = function(element)
- 3696 :
{
- 3697 :
$(element).addClass('rcpagejumper')
- 3698 :
.on('focus', function(e) {
- 3699 :
// create and display popup with page selection
- 3700 :
var i, html = '';
- 3701 :
- 3702 :
for (i = 1; i <= ref.env.pagecount; i++)
- 3703 :
html += '<li>' + i + '</li>';
- 3704 :
- 3705 :
html = '<ul class="toolbarmenu menu">' + html + '</ul>';
- 3706 :
- 3707 :
if (!ref.pagejump) {
- 3708 :
ref.pagejump = $('<div id="pagejump-selector" class="popupmenu"></div>')
- 3709 :
.appendTo(document.body)
- 3710 :
.on('click', 'li', function() {
- 3711 :
if (!ref.busy)
- 3712 :
$(element).val($(this).text()).change();
- 3713 :
});
- 3714 :
}
- 3715 :
- 3716 :
if (ref.pagejump.data('count') != i)
- 3717 :
ref.pagejump.html(html);
- 3718 :
- 3719 :
ref.pagejump.attr('rel', '#' + this.id).data('count', i);
- 3720 :
- 3721 :
// display page selector
- 3722 :
ref.show_menu('pagejump-selector', true, e);
- 3723 :
$(this).keydown();
- 3724 :
})
- 3725 :
// keyboard navigation
- 3726 :
.on('keydown keyup click', function(e) {
- 3727 :
var current, selector = $('#pagejump-selector'),
- 3728 :
ul = $('ul', selector),
- 3729 :
list = $('li', ul),
- 3730 :
height = ul.height(),
- 3731 :
p = parseInt(this.value);
- 3732 :
- 3733 :
if (e.which != 27 && e.which != 9 && e.which != 13 && !selector.is(':visible'))
- 3734 :
return ref.show_menu('pagejump-selector', true, e);
- 3735 :
- 3736 :
if (e.type == 'keydown') {
- 3737 :
// arrow-down
- 3738 :
if (e.which == 40) {
- 3739 :
if (list.length > p)
- 3740 :
this.value = (p += 1);
- 3741 :
}
- 3742 :
// arrow-up
- 3743 :
else if (e.which == 38) {
- 3744 :
if (p > 1 && list.length > p - 1)
- 3745 :
this.value = (p -= 1);
- 3746 :
}
- 3747 :
// enter
- 3748 :
else if (e.which == 13) {
- 3749 :
return $(this).change();
- 3750 :
}
- 3751 :
// esc, tab
- 3752 :
else if (e.which == 27 || e.which == 9) {
- 3753 :
ref.hide_menu('pagejump-selector', e);
- 3754 :
return $(element).val(ref.env.current_page);
- 3755 :
}
- 3756 :
}
- 3757 :
- 3758 :
$('li.selected', ul).removeClass('selected');
- 3759 :
- 3760 :
if ((current = $(list[p - 1])).length) {
- 3761 :
current.addClass('selected');
- 3762 :
$('#pagejump-selector').scrollTop(((ul.height() / list.length) * (p - 1)) - selector.height() / 2);
- 3763 :
}
- 3764 :
})
- 3765 :
.on('change', function(e) {
- 3766 :
// go to specified page
- 3767 :
var p = parseInt(this.value);
- 3768 :
if (p && p != ref.env.current_page && !ref.busy) {
- 3769 :
ref.hide_menu('pagejump-selector', e);
- 3770 :
ref.list_page(p);
- 3771 :
}
- 3772 :
});
- 3773 :
};
- 3774 :
- 3775 :
// Update page-jumper state on list updates
- 3776 :
this.update_pagejumper = function()
- 3777 :
{
- 3778 :
$('input.rcpagejumper').val(this.env.current_page).prop('disabled', this.env.pagecount < 2);
- 3779 :
};
- 3780 :
- 3781 :
// check for mailvelope API
- 3782 :
this.check_mailvelope = function(action)
- 3783 :
{
- 3784 :
if (window.mailvelope) {
- 3785 :
this.mailvelope_load(action);
- 3786 :
}
- 3787 :
else {
- 3788 :
$(window).on('mailvelope', function() {
- 3789 :
ref.mailvelope_load(action);
- 3790 :
});
- 3791 :
}
- 3792 :
};
- 3793 :
- 3794 :
// Load Mailvelope functionality (and initialize keyring if needed)
- 3795 :
this.mailvelope_load = function(action)
- 3796 :
{
- 3797 :
var keyring = this.env.mailvelope_main_keyring ? undefined : this.env.user_id,
- 3798 :
fn = function(kr) {
- 3799 :
ref.mailvelope_keyring = kr;
- 3800 :
ref.mailvelope_init(action, kr);
- 3801 :
};
- 3802 :
- 3803 :
mailvelope.getVersion().then(function(v) {
- 3804 :
mailvelope.VERSION = v;
- 3805 :
mailvelope.VERSION_MAJOR = Math.floor(parseFloat(v));
- 3806 :
return mailvelope.getKeyring(keyring);
- 3807 :
}).then(fn, function(err) {
- 3808 :
if (keyring) {
- 3809 :
// attempt to create a new keyring for this app/user
- 3810 :
mailvelope.createKeyring(keyring).then(fn, function(err) {
- 3811 :
console.error(err);
- 3812 :
});
- 3813 :
} else {
- 3814 :
console.error(err);
- 3815 :
}
- 3816 :
});
- 3817 :
};
- 3818 :
- 3819 :
// Initializes Mailvelope editor or display container
- 3820 :
this.mailvelope_init = function(action, keyring)
- 3821 :
{
- 3822 :
if (!window.mailvelope)
- 3823 :
return;
- 3824 :
- 3825 :
if (action == 'show' || action == 'preview' || action == 'print') {
- 3826 :
// decrypt text body
- 3827 :
if (this.env.is_pgp_content) {
- 3828 :
var data = $(this.env.is_pgp_content).text();
- 3829 :
ref.mailvelope_display_container(this.env.is_pgp_content, data, keyring);
- 3830 :
}
- 3831 :
// load pgp/mime message and pass it to the mailvelope display container
- 3832 :
else if (this.env.pgp_mime_part) {
- 3833 :
var msgid = this.display_message('loadingdata', 'loading'),
- 3834 :
selector = this.env.pgp_mime_container;
- 3835 :
- 3836 :
$.ajax({
- 3837 :
type: 'GET',
- 3838 :
url: this.url('get', { '_mbox': this.env.mailbox, '_uid': this.env.uid, '_part': this.env.pgp_mime_part }),
- 3839 :
error: function(o, status, err) {
- 3840 :
ref.http_error(o, status, err, msgid);
- 3841 :
},
- 3842 :
success: function(data) {
- 3843 :
ref.mailvelope_display_container(selector, data, keyring, msgid);
- 3844 :
}
- 3845 :
});
- 3846 :
}
- 3847 :
}
- 3848 :
else if (action == 'compose') {
- 3849 :
this.env.compose_commands.push('compose-encrypted');
- 3850 :
- 3851 :
var sign_supported = mailvelope.VERSION_MAJOR >= 2;
- 3852 :
var is_html = $('[name="_is_html"]').val() > 0;
- 3853 :
- 3854 :
if (sign_supported)
- 3855 :
this.env.compose_commands.push('compose-encrypted-signed');
- 3856 :
- 3857 :
if (this.env.pgp_mime_message) {
- 3858 :
// fetch PGP/Mime part and open load into Mailvelope editor
- 3859 :
var lock = this.set_busy(true, this.get_label('loadingdata'));
- 3860 :
- 3861 :
$.ajax({
- 3862 :
type: 'GET',
- 3863 :
url: this.url('get', this.env.pgp_mime_message),
- 3864 :
error: function(o, status, err) {
- 3865 :
ref.http_error(o, status, err, lock);
- 3866 :
ref.enable_command('compose-encrypted', !is_html);
- 3867 :
if (sign_supported)
- 3868 :
ref.enable_command('compose-encrypted-signed', !is_html);
- 3869 :
},
- 3870 :
success: function(data) {
- 3871 :
ref.set_busy(false, null, lock);
- 3872 :
- 3873 :
if (is_html) {
- 3874 :
ref.command('toggle-editor', {html: false, noconvert: true});
- 3875 :
$('#' + ref.env.composebody).val('');
- 3876 :
}
- 3877 :
- 3878 :
ref.compose_encrypted({ quotedMail: data });
- 3879 :
ref.enable_command('compose-encrypted', true);
- 3880 :
ref.enable_command('compose-encrypted-signed', false);
- 3881 :
}
- 3882 :
});
- 3883 :
}
- 3884 :
else {
- 3885 :
// enable encrypted compose toggle
- 3886 :
this.enable_command('compose-encrypted', !is_html);
- 3887 :
if (sign_supported)
- 3888 :
this.enable_command('compose-encrypted-signed', !is_html);
- 3889 :
}
- 3890 :
- 3891 :
// make sure to disable encryption button after toggling editor into HTML mode
- 3892 :
this.addEventListener('actionafter', function(args) {
- 3893 :
if (args.ret && args.action == 'toggle-editor') {
- 3894 :
ref.enable_command('compose-encrypted', !args.props.html);
- 3895 :
if (sign_supported)
- 3896 :
ref.enable_command('compose-encrypted-signed', !args.props.html);
- 3897 :
}
- 3898 :
});
- 3899 :
} else if (action == 'edit-identity') {
- 3900 :
ref.mailvelope_identity_keygen();
- 3901 :
}
- 3902 :
};
- 3903 :
- 3904 :
// handler for the 'compose-encrypted-signed' command
- 3905 :
this.compose_encrypted_signed = function(props)
- 3906 :
{
- 3907 :
props = props || {};
- 3908 :
props.signMsg = true;
- 3909 :
this.compose_encrypted(props);
- 3910 :
};
- 3911 :
- 3912 :
// handler for the 'compose-encrypted' command
- 3913 :
this.compose_encrypted = function(props)
- 3914 :
{
- 3915 :
var options, container = $('#' + this.env.composebody).parent();
- 3916 :
- 3917 :
// remove Mailvelope editor if active
- 3918 :
if (ref.mailvelope_editor) {
- 3919 :
ref.mailvelope_editor = null;
- 3920 :
ref.set_button('compose-encrypted', 'act');
- 3921 :
- 3922 :
container.removeClass('mailvelope').find('iframe:not([aria-hidden=true])').remove();
- 3923 :
$('#' + ref.env.composebody).show();
- 3924 :
$("[name='_pgpmime']").remove();
- 3925 :
- 3926 :
// re-enable commands that operate on the compose body
- 3927 :
ref.enable_command('toggle-editor', 'insert-response', 'save-response', true);
- 3928 :
ref.enable_command('spellcheck', !!window.googie);
- 3929 :
ref.enable_command('insert-sig', !!(ref.env.signatures && ref.env.identity && ref.env.signatures[ref.env.identity]));
- 3930 :
- 3931 :
ref.triggerEvent('compose-encrypted', { active:false });
- 3932 :
}
- 3933 :
// embed Mailvelope editor container
- 3934 :
else {
- 3935 :
if (this.spellcheck_state())
- 3936 :
this.editor.spellcheck_stop();
- 3937 :
- 3938 :
if (props.quotedMail) {
- 3939 :
options = { quotedMail: props.quotedMail, quotedMailIndent: false };
- 3940 :
}
- 3941 :
else {
- 3942 :
options = { predefinedText: $('#' + this.env.composebody).val() };
- 3943 :
}
- 3944 :
- 3945 :
if (props.signMsg) {
- 3946 :
options.signMsg = props.signMsg;
- 3947 :
}
- 3948 :
- 3949 :
if (this.env.compose_mode == 'reply') {
- 3950 :
options.quotedMailIndent = true;
- 3951 :
options.quotedMailHeader = this.env.compose_reply_header;
- 3952 :
}
- 3953 :
- 3954 :
mailvelope.createEditorContainer('#' + container.attr('id'), ref.mailvelope_keyring, options).then(function(editor) {
- 3955 :
ref.mailvelope_editor = editor;
- 3956 :
ref.set_button('compose-encrypted', 'sel');
- 3957 :
- 3958 :
container.addClass('mailvelope');
- 3959 :
$('#' + ref.env.composebody).hide();
- 3960 :
- 3961 :
// disable commands that operate on the compose body
- 3962 :
ref.enable_command('spellcheck', 'insert-sig', 'toggle-editor', 'insert-response', 'save-response', false);
- 3963 :
ref.triggerEvent('compose-encrypted', { active:true });
- 3964 :
- 3965 :
if (!$.isEmptyObject(ref.env.attachments)) {
- 3966 :
// notify user if losing attachments
- 3967 :
if (ref.env.compose_mode != 'draft'
- 3968 :
|| Object.keys(ref.env.attachments).length != 1
- 3969 :
|| ref.env.attachments[Object.keys(ref.env.attachments)[0]].name != 'encrypted.asc'
- 3970 :
) {
- 3971 :
ref.alert_dialog(ref.get_label('encryptnoattachments'));
- 3972 :
}
- 3973 :
- 3974 :
$.each(ref.env.attachments, function(name, attach) {
- 3975 :
ref.remove_from_attachment_list(name);
- 3976 :
});
- 3977 :
}
- 3978 :
}, function(err) {
- 3979 :
console.error(err);
- 3980 :
console.log(options);
- 3981 :
});
- 3982 :
}
- 3983 :
};
- 3984 :
- 3985 :
// callback to replace the message body with the full armored
- 3986 :
this.mailvelope_submit_messageform = function(draft, saveonly)
- 3987 :
{
- 3988 :
// get recipients
- 3989 :
var recipients = [];
- 3990 :
$.each(['to', 'cc', 'bcc'], function(i,field) {
- 3991 :
var pos, rcpt, val = $('[name="_' + field + '"]').val().trim();
- 3992 :
while (val.length && rcube_check_email(val, true)) {
- 3993 :
rcpt = RegExp.$2.replace(/^<+/, '').replace(/>+$/, '');
- 3994 :
recipients.push(rcpt);
- 3995 :
val = val.substr(val.indexOf(rcpt) + rcpt.length + 1).replace(/^\s*,\s*/, '');
- 3996 :
}
- 3997 :
});
- 3998 :
- 3999 :
// check if we have keys for all recipients
- 4000 :
var isvalid = recipients.length > 0;
- 4001 :
ref.mailvelope_keyring.validKeyForAddress(recipients).then(function(status) {
- 4002 :
var missing_keys = [];
- 4003 :
$.each(status, function(k,v) {
- 4004 :
if (v === false) {
- 4005 :
isvalid = false;
- 4006 :
missing_keys.push(k);
- 4007 :
}
- 4008 :
});
- 4009 :
- 4010 :
// list recipients with missing keys
- 4011 :
if (!isvalid && missing_keys.length) {
- 4012 :
// display dialog with missing keys
- 4013 :
ref.simple_dialog(
- 4014 :
ref.get_label('nopubkeyfor').replace('$email', missing_keys.join(', ')) +
- 4015 :
'<p>' + ref.get_label('searchpubkeyservers') + '</p>',
- 4016 :
'encryptedsendialog',
- 4017 :
function() {
- 4018 :
ref.mailvelope_search_pubkeys(missing_keys, function() {
- 4019 :
return true; // close dialog
- 4020 :
});
- 4021 :
},
- 4022 :
{button: 'search'}
- 4023 :
);
- 4024 :
return false;
- 4025 :
}
- 4026 :
- 4027 :
if (!isvalid) {
- 4028 :
if (!recipients.length) {
- 4029 :
ref.alert_dialog(ref.get_label('norecipientwarning'), function() {
- 4030 :
$("[name='_to']").focus();
- 4031 :
});
- 4032 :
}
- 4033 :
return false;
- 4034 :
}
- 4035 :
- 4036 :
// add sender identity to recipients to be able to decrypt our very own message
- 4037 :
var senders = [], selected_sender = ref.env.identities[$("[name='_from'] option:selected").val()];
- 4038 :
$.each(ref.env.identities, function(k, sender) {
- 4039 :
senders.push(sender.email);
- 4040 :
});
- 4041 :
- 4042 :
ref.mailvelope_keyring.validKeyForAddress(senders).then(function(status) {
- 4043 :
valid_sender = null;
- 4044 :
$.each(status, function(k,v) {
- 4045 :
if (v !== false) {
- 4046 :
valid_sender = k;
- 4047 :
if (valid_sender == selected_sender) {
- 4048 :
return false; // break
- 4049 :
}
- 4050 :
}
- 4051 :
});
- 4052 :
- 4053 :
if (!valid_sender) {
- 4054 :
if (!confirm(ref.get_label('nopubkeyforsender'))) {
- 4055 :
return false;
- 4056 :
}
- 4057 :
}
- 4058 :
- 4059 :
recipients.push(valid_sender);
- 4060 :
- 4061 :
ref.mailvelope_editor.encrypt(recipients).then(function(armored) {
- 4062 :
// all checks passed, send message
- 4063 :
var form = ref.gui_objects.messageform,
- 4064 :
hidden = $("[name='_pgpmime']", form),
- 4065 :
msgid = ref.set_busy(true, draft || saveonly ? 'savingmessage' : 'sendingmessage');
- 4066 :
- 4067 :
form.target = ref.get_save_target(msgid);
- 4068 :
form._draft.value = draft ? '1' : '';
- 4069 :
form.action = ref.add_url(form.action, '_unlock', msgid);
- 4070 :
form.action = ref.add_url(form.action, '_framed', 1);
- 4071 :
- 4072 :
if (saveonly) {
- 4073 :
form.action = ref.add_url(form.action, '_saveonly', 1);
- 4074 :
}
- 4075 :
- 4076 :
// send pgp content via hidden field
- 4077 :
if (!hidden.length) {
- 4078 :
hidden = $('<input type="hidden" name="_pgpmime">').appendTo(form);
- 4079 :
}
- 4080 :
hidden.val(armored);
- 4081 :
- 4082 :
form.submit();
- 4083 :
- 4084 :
}, function(err) {
- 4085 :
console.log(err);
- 4086 :
}); // mailvelope_editor.encrypt()
- 4087 :
- 4088 :
}, function(err) {
- 4089 :
console.error(err);
- 4090 :
}); // mailvelope_keyring.validKeyForAddress(senders)
- 4091 :
- 4092 :
}, function(err) {
- 4093 :
console.error(err);
- 4094 :
}); // mailvelope_keyring.validKeyForAddress(recipients)
- 4095 :
- 4096 :
return false;
- 4097 :
};
- 4098 :
- 4099 :
// wrapper for the mailvelope.createDisplayContainer API call
- 4100 :
this.mailvelope_display_container = function(selector, data, keyring, msgid)
- 4101 :
{
- 4102 :
var error_handler = function(error) {
- 4103 :
// remove mailvelope frame with the error message
- 4104 :
$(selector + ' > iframe').remove();
- 4105 :
ref.hide_message(msgid);
- 4106 :
ref.display_message(error.message, 'error');
- 4107 :
};
- 4108 :
- 4109 :
mailvelope.createDisplayContainer(selector, data, keyring, { senderAddress: this.env.sender }).then(function(status) {
- 4110 :
if (status.error && status.error.message) {
- 4111 :
return error_handler(status.error);
- 4112 :
}
- 4113 :
- 4114 :
ref.hide_message(msgid);
- 4115 :
$(selector).children().not('iframe').hide();
- 4116 :
$(ref.gui_objects.messagebody).addClass('mailvelope');
- 4117 :
- 4118 :
// on success we can remove encrypted part from the attachments list
- 4119 :
if (ref.env.pgp_mime_part)
- 4120 :
$('#attach' + ref.env.pgp_mime_part).remove();
- 4121 :
- 4122 :
setTimeout(function() { $(window).resize(); }, 10);
- 4123 :
}, error_handler);
- 4124 :
};
- 4125 :
- 4126 :
// subroutine to query keyservers for public keys
- 4127 :
this.mailvelope_search_pubkeys = function(emails, resolve, import_handler)
- 4128 :
{
- 4129 :
// query with publickey.js
- 4130 :
var deferred = [],
- 4131 :
pk = new PublicKey(this.env.keyservers),
- 4132 :
lock = ref.display_message('', 'loading');
- 4133 :
- 4134 :
$.each(emails, function(i, email) {
- 4135 :
var d = $.Deferred();
- 4136 :
pk.search(email, function(results, errorCode) {
- 4137 :
if (errorCode !== null) {
- 4138 :
// rejecting would make all fail
- 4139 :
// d.reject(email);
- 4140 :
d.resolve([email]);
- 4141 :
}
- 4142 :
else {
- 4143 :
d.resolve([email].concat(results));
- 4144 :
}
- 4145 :
});
- 4146 :
deferred.push(d);
- 4147 :
});
- 4148 :
- 4149 :
$.when.apply($, deferred).then(function() {
- 4150 :
var missing_keys = [],
- 4151 :
key_selection = [];
- 4152 :
- 4153 :
// analyze results of all queries
- 4154 :
$.each(arguments, function(i, result) {
- 4155 :
var email = result.shift();
- 4156 :
if (!result.length) {
- 4157 :
missing_keys.push(email);
- 4158 :
}
- 4159 :
else {
- 4160 :
key_selection = key_selection.concat(result);
- 4161 :
}
- 4162 :
});
- 4163 :
- 4164 :
ref.hide_message(lock);
- 4165 :
resolve(true);
- 4166 :
- 4167 :
// show key import dialog
- 4168 :
if (key_selection.length) {
- 4169 :
ref.mailvelope_key_import_dialog(key_selection, import_handler);
- 4170 :
}
- 4171 :
// some keys could not be found
- 4172 :
if (missing_keys.length) {
- 4173 :
ref.display_message(ref.get_label('nopubkeyfor').replace('$email', missing_keys.join(', ')), 'warning');
- 4174 :
}
- 4175 :
}).fail(function() {
- 4176 :
console.error('Pubkey lookup failed with', arguments);
- 4177 :
ref.hide_message(lock);
- 4178 :
ref.display_message('pubkeysearcherror', 'error');
- 4179 :
resolve(false);
- 4180 :
});
- 4181 :
};
- 4182 :
- 4183 :
// list the given public keys in a dialog with options to import
- 4184 :
// them into the local Mailvelope keyring
- 4185 :
this.mailvelope_key_import_dialog = function(candidates, import_handler)
- 4186 :
{
- 4187 :
var ul = $('<div>').addClass('listing pgpkeyimport');
- 4188 :
- 4189 :
$.each(candidates, function(i, keyrec) {
- 4190 :
var li = $('<div>').addClass('key');
- 4191 :
- 4192 :
if (keyrec.revoked) li.addClass('revoked');
- 4193 :
if (keyrec.disabled) li.addClass('disabled');
- 4194 :
if (keyrec.expired) li.addClass('expired');
- 4195 :
- 4196 :
li.append($('<label>').addClass('keyid').text(ref.get_label('keyid')));
- 4197 :
li.append($('<a>').text(keyrec.keyid.substr(-8).toUpperCase())
- 4198 :
.attr({href: keyrec.info, target: '_blank', tabindex: '-1'}));
- 4199 :
- 4200 :
li.append($('<label>').addClass('keylen').text(ref.get_label('keylength')));
- 4201 :
li.append($('<span>').text(keyrec.keylen));
- 4202 :
- 4203 :
if (keyrec.expirationdate) {
- 4204 :
li.append($('<label>').addClass('keyexpired').text(ref.get_label('keyexpired')));
- 4205 :
li.append($('<span>').text(new Date(keyrec.expirationdate * 1000).toDateString()));
- 4206 :
}
- 4207 :
- 4208 :
if (keyrec.revoked) {
- 4209 :
li.append($('<span>').addClass('keyrevoked').text(ref.get_label('keyrevoked')));
- 4210 :
}
- 4211 :
- 4212 :
var ul_ = $('<ul>').addClass('uids');
- 4213 :
$.each(keyrec.uids, function(j, uid) {
- 4214 :
var li_ = $('<li>').addClass('uid');
- 4215 :
if (uid.revoked) li_.addClass('revoked');
- 4216 :
if (uid.disabled) li_.addClass('disabled');
- 4217 :
if (uid.expired) li_.addClass('expired');
- 4218 :
- 4219 :
ul_.append(li_.text(uid.uid));
- 4220 :
});
- 4221 :
- 4222 :
li.append(ul_);
- 4223 :
li.append($('<button>')
- 4224 :
.attr('rel', keyrec.keyid)
- 4225 :
.text(ref.get_label('import'))
- 4226 :
.addClass('button import importkey')
- 4227 :
.prop('disabled', keyrec.revoked || keyrec.disabled || keyrec.expired));
- 4228 :
- 4229 :
ul.append(li);
- 4230 :
});
- 4231 :
- 4232 :
// display dialog with missing keys
- 4233 :
ref.simple_dialog(
- 4234 :
$('<div>')
- 4235 :
.append($('<p>').html(ref.get_label('encryptpubkeysfound')))
- 4236 :
.append(ul),
- 4237 :
ref.get_label('importpubkeys'),
- 4238 :
null,
- 4239 :
{cancel_label: 'close', cancel_button: 'close'}
- 4240 :
);
- 4241 :
- 4242 :
// delegate handler for import button clicks
- 4243 :
ul.on('click', 'button.importkey', function() {
- 4244 :
var btn = $(this),
- 4245 :
keyid = btn.attr('rel'),
- 4246 :
pk = new PublicKey(ref.env.keyservers),
- 4247 :
lock = ref.display_message('', 'loading');
- 4248 :
- 4249 :
// fetch from keyserver and import to Mailvelope keyring
- 4250 :
pk.get(keyid, function(armored, errorCode) {
- 4251 :
ref.hide_message(lock);
- 4252 :
- 4253 :
if (errorCode) {
- 4254 :
ref.display_message('keyservererror', 'error');
- 4255 :
return;
- 4256 :
}
- 4257 :
- 4258 :
if (import_handler) {
- 4259 :
import_handler(armored);
- 4260 :
return;
- 4261 :
}
- 4262 :
- 4263 :
// import to keyring
- 4264 :
ref.mailvelope_keyring.importPublicKey(armored).then(function(status) {
- 4265 :
if (status === 'REJECTED') {
- 4266 :
// ref.alert_dialog(ref.get_label('Key import was rejected'));
- 4267 :
}
- 4268 :
else {
- 4269 :
var $key = keyid.substr(-8).toUpperCase();
- 4270 :
btn.closest('.key').fadeOut();
- 4271 :
ref.display_message(ref.get_label('keyimportsuccess').replace('$key', $key), 'confirmation');
- 4272 :
}
- 4273 :
}, function(err) {
- 4274 :
console.log(err);
- 4275 :
});
- 4276 :
});
- 4277 :
});
- 4278 :
};
- 4279 :
- 4280 :
// enable key management for identity
- 4281 :
this.mailvelope_identity_keygen = function()
- 4282 :
{
- 4283 :
var container = $(this.gui_objects.editform).find('.identity-encryption').first();
- 4284 :
var identity_email = $(this.gui_objects.editform).find('.ff_email').val().trim();
- 4285 :
- 4286 :
if (!container.length || !identity_email || !this.mailvelope_keyring.createKeyGenContainer)
- 4287 :
return;
- 4288 :
- 4289 :
var key_fingerprint;
- 4290 :
this.mailvelope_keyring.validKeyForAddress([identity_email])
- 4291 :
.then(function(keys) {
- 4292 :
var private_keys = [];
- 4293 :
- 4294 :
if (keys && keys[identity_email] && Array.isArray(keys[identity_email].keys)) {
- 4295 :
var checks = [];
- 4296 :
for (var j=0; j < keys[identity_email].keys.length; j++) {
- 4297 :
checks.push((function(key) {
- 4298 :
return ref.mailvelope_keyring.hasPrivateKey(key.fingerprint)
- 4299 :
.then(function(found) {
- 4300 :
if (found) {
- 4301 :
private_keys.push(key);
- 4302 :
}
- 4303 :
});
- 4304 :
})(keys[identity_email].keys[j]));
- 4305 :
}
- 4306 :
return Promise.all(checks)
- 4307 :
.then(function() {
- 4308 :
return private_keys;
- 4309 :
});
- 4310 :
}
- 4311 :
- 4312 :
return private_keys;
- 4313 :
}).then(function(private_keys) {
- 4314 :
var content = container.find('.identity-encryption-block').empty();
- 4315 :
if (private_keys && private_keys.length) {
- 4316 :
// show private key information
- 4317 :
$('<p>').text(ref.get_label('encryptionprivkeysinmailvelope').replace('$nr', private_keys.length)).appendTo(content);
- 4318 :
var ul = $('<ul>').addClass('keylist').appendTo(content);
- 4319 :
$.each(private_keys, function(i, key) {
- 4320 :
$('<li>').appendTo(ul)
- 4321 :
.append($('<strong>').addClass('fingerprint').text(String(key.fingerprint).toUpperCase()))
- 4322 :
.append($('<span>').addClass('identity').text('<' + identity_email + '> '));
- 4323 :
});
- 4324 :
} else {
- 4325 :
$('<p>').text(ref.get_label('encryptionnoprivkeysinmailvelope')).appendTo(content);
- 4326 :
}
- 4327 :
- 4328 :
// show button to create a new key
- 4329 :
$('<button>')
- 4330 :
.attr('type', 'button')
- 4331 :
.addClass('button create')
- 4332 :
.text(ref.get_label('encryptioncreatekey'))
- 4333 :
.appendTo(content)
- 4334 :
.on('click', function() { ref.mailvelope_show_keygen_container(content, identity_email); });
- 4335 :
$('<span>').addClass('space').html(' ').appendTo(content);
- 4336 :
$('<button>')
- 4337 :
.attr('type', 'button')
- 4338 :
.addClass('button settings')
- 4339 :
.text(ref.get_label('openmailvelopesettings'))
- 4340 :
.appendTo(content)
- 4341 :
.on('click', function() { ref.mailvelope_keyring.openSettings(); });
- 4342 :
- 4343 :
container.show();
- 4344 :
ref.triggerEvent('identity-encryption-show', { container: container });
- 4345 :
})
- 4346 :
.catch(function(err) {
- 4347 :
console.error('Mailvelope keyring error', err);
- 4348 :
})
- 4349 :
};
- 4350 :
- 4351 :
// start pgp key generation using Mailvelope
- 4352 :
this.mailvelope_show_keygen_container = function(container, identity_email)
- 4353 :
{
- 4354 :
var cid = new Date().getTime();
- 4355 :
var user_id = {email: identity_email, fullName: $(ref.gui_objects.editform).find('.ff_name').val().trim()};
- 4356 :
var options = {userIds: [user_id], keySize: this.env.mailvelope_keysize};
- 4357 :
- 4358 :
$('<div>').attr('id', 'mailvelope-keygen-container-' + cid)
- 4359 :
.css({height: '245px', marginBottom: '10px'})
- 4360 :
.appendTo(container.empty());
- 4361 :
- 4362 :
this.mailvelope_keyring.createKeyGenContainer('#mailvelope-keygen-container-' + cid, options)
- 4363 :
.then(function(generator) {
- 4364 :
if (generator instanceof Error) {
- 4365 :
throw generator;
- 4366 :
}
- 4367 :
- 4368 :
// append button to start key generation
- 4369 :
$('<button>')
- 4370 :
.attr('type', 'button')
- 4371 :
.addClass('button mainaction generate')
- 4372 :
.text(ref.get_label('generate'))
- 4373 :
.appendTo(container)
- 4374 :
.on('click', function() {
- 4375 :
var btn = $(this).prop('disabled', true);
- 4376 :
generator.generate()
- 4377 :
.then(function(result) {
- 4378 :
if (typeof result === 'string' && result.indexOf('BEGIN PGP') > 0) {
- 4379 :
ref.display_message(ref.get_label('keypaircreatesuccess').replace('$identity', identity_email), 'confirmation');
- 4380 :
// reset keygen view
- 4381 :
ref.mailvelope_identity_keygen();
- 4382 :
}
- 4383 :
})
- 4384 :
.catch(function(err) {
- 4385 :
ref.display_message(err.message || 'errortitle', 'error');
- 4386 :
btn.prop('disabled', false);
- 4387 :
});
- 4388 :
});
- 4389 :
- 4390 :
$('<span>').addClass('space').html(' ').appendTo(container);
- 4391 :
- 4392 :
$('<button>')
- 4393 :
.attr('type', 'button')
- 4394 :
.addClass('button cancel')
- 4395 :
.text(ref.get_label('cancel'))
- 4396 :
.appendTo(container)
- 4397 :
.on('click', function() { ref.mailvelope_identity_keygen(); });
- 4398 :
- 4399 :
ref.triggerEvent('identity-encryption-update', { container: container });
- 4400 :
})
- 4401 :
.catch(function(err) {
- 4402 :
ref.display_message('errortitle', 'error');
- 4403 :
// start over
- 4404 :
ref.mailvelope_identity_keygen();
- 4405 :
});
- 4406 :
};
- 4407 :
- 4408 :
this.mdn_request_dialog = function(uid, mailbox)
- 4409 :
{
- 4410 :
var props = {
- 4411 :
action: 'mark',
- 4412 :
data: { _uid: uid, _mbox: mailbox, _flag: 'mdnsent' }
- 4413 :
},
- 4414 :
buttons = [
- 4415 :
{
- 4416 :
text: this.get_label('send'),
- 4417 :
'class': 'mainaction send',
- 4418 :
click: function(e, ui, dialog) {
- 4419 :
props.action = 'sendmdn';
- 4420 :
(ref.is_framed() ? parent.$ : $)(dialog || this).dialog('close');
- 4421 :
}
- 4422 :
},
- 4423 :
{
- 4424 :
text: this.get_label('ignore'),
- 4425 :
'class': 'cancel',
- 4426 :
click: function(e, ui, dialog) {
- 4427 :
(ref.is_framed() ? parent.$ : $)(dialog || this).dialog('close');
- 4428 :
}
- 4429 :
}
- 4430 :
],
- 4431 :
mdn_func = function(event, ui) {
- 4432 :
ref.http_post(props.action, props.data);
- 4433 :
// from default close function
- 4434 :
$(this).remove();
- 4435 :
};
- 4436 :
- 4437 :
if (this.env.mdn_request_save) {
- 4438 :
buttons.unshift({
- 4439 :
text: this.get_label('sendalwaysto').replace('$email', this.env.mdn_request_sender.mailto),
- 4440 :
'class': 'mainaction send',
- 4441 :
click: function(e, ui, dialog) {
- 4442 :
props.data._save = ref.env.mdn_request_save;
- 4443 :
props.data._address = ref.env.mdn_request_sender.string;
- 4444 :
$(e.target).next().click();
- 4445 :
}
- 4446 :
});
- 4447 :
}
- 4448 :
- 4449 :
this.show_popup_dialog(this.get_label('mdnrequest'), this.get_label('sendreceipt'), buttons, { close: mdn_func });
- 4450 :
};
- 4451 :
- 4452 :
- 4453 :
/*********************************************************/
- 4454 :
/********* mailbox folders methods *********/
- 4455 :
/*********************************************************/
- 4456 :
- 4457 :
this.expunge_mailbox = function(mbox)
- 4458 :
{
- 4459 :
var lock, post_data = {_mbox: mbox};
- 4460 :
- 4461 :
// lock interface if it's the active mailbox
- 4462 :
if (mbox == this.env.mailbox) {
- 4463 :
lock = this.set_busy(true, 'loading');
- 4464 :
post_data._reload = 1;
- 4465 :
if (this.env.search_request)
- 4466 :
post_data._search = this.env.search_request;
- 4467 :
}
- 4468 :
- 4469 :
// send request to server
- 4470 :
this.http_post('expunge', post_data, lock);
- 4471 :
};
- 4472 :
- 4473 :
this.purge_mailbox = function(mbox)
- 4474 :
{
- 4475 :
this.confirm_dialog(this.get_label('purgefolderconfirm'), 'delete', function() {
- 4476 :
var lock, post_data = {_mbox: mbox};
- 4477 :
- 4478 :
// lock interface if it's the active mailbox
- 4479 :
if (mbox == ref.env.mailbox) {
- 4480 :
lock = ref.set_busy(true, 'loading');
- 4481 :
post_data._reload = 1;
- 4482 :
}
- 4483 :
- 4484 :
// send request to server
- 4485 :
ref.http_post('purge', post_data, lock);
- 4486 :
});
- 4487 :
- 4488 :
return false;
- 4489 :
};
- 4490 :
- 4491 :
// Mark all messages as read in:
- 4492 :
// - selected folder (mode=cur)
- 4493 :
// - selected folder and its subfolders (mode=sub)
- 4494 :
// - all folders (mode=all)
- 4495 :
this.mark_all_read = function(mbox, mode)
- 4496 :
{
- 4497 :
var state, content, nodes = [],
- 4498 :
list = this.message_list,
- 4499 :
folder = mbox || this.env.mailbox,
- 4500 :
post_data = {_uid: '*', _flag: 'read', _mbox: folder, _folders: mode};
- 4501 :
- 4502 :
if (typeof mode != 'string') {
- 4503 :
state = this.mark_all_read_state(folder);
- 4504 :
if (!state)
- 4505 :
return;
- 4506 :
- 4507 :
if (state > 1) {
- 4508 :
// build content of the dialog
- 4509 :
$.each({cur: 1, sub: 2, all: 4}, function(i, v) {
- 4510 :
var id = 'readallmode' + i,
- 4511 :
label = $('<label>').attr('for', id).text(ref.get_label('folders-' + i)),
- 4512 :
input = $('<input>').attr({type: 'radio', value: i, name: 'mode', id: id, disabled: !(state & v)});
- 4513 :
- 4514 :
nodes.push($('<li>').append([input, label]));
- 4515 :
});
- 4516 :
- 4517 :
content = $('<ul class="proplist">').append(nodes);
- 4518 :
$('input:not([disabled])', content).first().attr('checked', true);
- 4519 :
- 4520 :
this.simple_dialog(content, this.get_label('markallread'),
- 4521 :
function() {
- 4522 :
ref.mark_all_read(folder, $('input:checked', content).val());
- 4523 :
return true;
- 4524 :
},
- 4525 :
{button: 'mark', button_class: 'save'}
- 4526 :
);
- 4527 :
- 4528 :
return;
- 4529 :
}
- 4530 :
- 4531 :
post_data._folders = 'cur'; // only current folder has unread messages
- 4532 :
}
- 4533 :
- 4534 :
// mark messages on the list
- 4535 :
$.each(list ? list.rows : [], function(uid, row) {
- 4536 :
if (!row.unread)
- 4537 :
return;
- 4538 :
- 4539 :
var mbox = ref.env.messages[uid].mbox;
- 4540 :
if (mode == 'all' || mbox == ref.env.mailbox
- 4541 :
|| (mode == 'sub' && mbox.startsWith(ref.env.mailbox + ref.env.delimiter))
- 4542 :
) {
- 4543 :
ref.set_message(uid, 'unread', false);
- 4544 :
}
- 4545 :
});
- 4546 :
- 4547 :
// send the request
- 4548 :
this.http_post('mark', post_data, this.display_message('markingmessage', 'loading'));
- 4549 :
};
- 4550 :
- 4551 :
// Enable/disable mark-all-read action depending on folders state
- 4552 :
this.mark_all_read_state = function(mbox)
- 4553 :
{
- 4554 :
var state = 0,
- 4555 :
li = this.treelist.get_item(mbox || this.env.mailbox),
- 4556 :
folder_item = $(li).is('.unread') ? 1 : 0,
- 4557 :
subfolder_items = $('li.unread', li).length,
- 4558 :
all_items = $('li.unread', ref.gui_objects.folderlist).length;
- 4559 :
- 4560 :
state += folder_item;
- 4561 :
state += subfolder_items ? 2 : 0;
- 4562 :
state += all_items > folder_item + subfolder_items ? 4 : 0;
- 4563 :
- 4564 :
this.enable_command('mark-all-read', state > 0);
- 4565 :
- 4566 :
return state;
- 4567 :
};
- 4568 :
- 4569 :
// Display "bounce message" dialog
- 4570 :
this.bounce = function(props, obj, event)
- 4571 :
{
- 4572 :
// get message uid and folder
- 4573 :
var uid = this.get_single_uid(),
- 4574 :
url = this.url('bounce', {_framed: 1, _uid: uid, _mbox: this.get_message_mailbox(uid)}),
- 4575 :
dialog = $('<iframe>').attr('src', url),
- 4576 :
get_form = function() {
- 4577 :
var rc = $('iframe', dialog)[0].contentWindow.rcmail;
- 4578 :
return {rc: rc, form: rc.gui_objects.messageform};
- 4579 :
},
- 4580 :
post_func = function() {
- 4581 :
var post = {}, form = get_form();
- 4582 :
- 4583 :
$.each($(form.form).serializeArray(), function() { post[this.name] = this.value; });
- 4584 :
- 4585 :
post._uid = form.rc.env.uid;
- 4586 :
post._mbox = form.rc.env.mailbox;
- 4587 :
delete post._action;
- 4588 :
delete post._task;
- 4589 :
- 4590 :
if (post._to || post._cc || post._bcc) {
- 4591 :
ref.http_post('bounce', post, ref.set_busy(true, 'sendingmessage'));
- 4592 :
dialog.dialog('close');
- 4593 :
}
- 4594 :
},
- 4595 :
submit_func = function() {
- 4596 :
var form = get_form();
- 4597 :
- 4598 :
if (typeof form.form != 'object')
- 4599 :
return false;
- 4600 :
- 4601 :
if (!form.rc.check_compose_address_fields(post_func, form.form))
- 4602 :
return false;
- 4603 :
- 4604 :
return post_func();
- 4605 :
};
- 4606 :
- 4607 :
this.hide_menu('forwardmenu', event);
- 4608 :
- 4609 :
dialog = this.simple_dialog(dialog, this.gettext('bouncemsg'), submit_func, {
- 4610 :
button: 'bounce',
- 4611 :
width: 400,
- 4612 :
height: 300
- 4613 :
});
- 4614 :
- 4615 :
return true;
- 4616 :
};
- 4617 :
- 4618 :
- 4619 :
/*********************************************************/
- 4620 :
/********* message compose methods *********/
- 4621 :
/*********************************************************/
- 4622 :
- 4623 :
this.open_compose_step = function(p)
- 4624 :
{
- 4625 :
var url = this.url('mail/compose', p);
- 4626 :
- 4627 :
// open new compose window
- 4628 :
if (this.env.compose_extwin && !this.env.extwin) {
- 4629 :
this.open_window(url);
- 4630 :
}
- 4631 :
else {
- 4632 :
this.redirect(url);
- 4633 :
if (this.env.extwin)
- 4634 :
window.resizeTo(Math.max(this.env.popup_width, $(window).width()), $(window).height() + 24);
- 4635 :
}
- 4636 :
};
- 4637 :
- 4638 :
// init message compose form: set focus and eventhandlers
- 4639 :
this.init_messageform = function()
- 4640 :
{
- 4641 :
if (!this.gui_objects.messageform)
- 4642 :
return false;
- 4643 :
- 4644 :
var elem, pos,
- 4645 :
input_from = $("[name='_from']"),
- 4646 :
input_to = $("[name='_to']"),
- 4647 :
input_subject = $("[name='_subject']"),
- 4648 :
input_message = $("[name='_message']").get(0),
- 4649 :
html_mode = $("[name='_is_html']").val() == '1',
- 4650 :
opener_rc = this.opener();
- 4651 :
- 4652 :
// close compose step in opener
- 4653 :
if (opener_rc && opener_rc.env.action == 'compose') {
- 4654 :
setTimeout(function() {
- 4655 :
if (opener.history.length > 1)
- 4656 :
opener.history.back();
- 4657 :
else
- 4658 :
opener_rc.redirect(opener_rc.get_task_url('mail'));
- 4659 :
}, 100);
- 4660 :
this.env.opened_extwin = true;
- 4661 :
}
- 4662 :
- 4663 :
if (!html_mode) {
- 4664 :
// On Back button Chrome will overwrite textarea with old content
- 4665 :
// causing e.g. the same signature is added twice (#5809)
- 4666 :
if (input_message.value && input_message.defaultValue !== undefined)
- 4667 :
input_message.value = input_message.defaultValue;
- 4668 :
- 4669 :
pos = this.env.top_posting && this.env.compose_mode ? 0 : input_message.value.length;
- 4670 :
- 4671 :
// add signature according to selected identity
- 4672 :
// if we have HTML editor, signature is added in a callback
- 4673 :
if (input_from.prop('type') == 'select-one') {
- 4674 :
// for some reason the caret initially is not at pos=0 in Firefox 51 (#5628)
- 4675 :
this.set_caret_pos(input_message, 0);
- 4676 :
this.change_identity(input_from[0]);
- 4677 :
}
- 4678 :
- 4679 :
// set initial cursor position
- 4680 :
this.set_caret_pos(input_message, pos);
- 4681 :
- 4682 :
// scroll to the bottom of the textarea (#1490114)
- 4683 :
if (pos) {
- 4684 :
$(input_message).scrollTop(input_message.scrollHeight);
- 4685 :
}
- 4686 :
}
- 4687 :
- 4688 :
// check for locally stored compose data
- 4689 :
if (this.env.save_localstorage)
- 4690 :
this.compose_restore_dialog(0, html_mode)
- 4691 :
- 4692 :
if (input_to.val() == '')
- 4693 :
elem = input_to;
- 4694 :
else if (input_subject.val() == '')
- 4695 :
elem = input_subject;
- 4696 :
else if (input_message)
- 4697 :
elem = input_message;
- 4698 :
- 4699 :
this.env.compose_focus_elem = this.init_messageform_inputs(elem);
- 4700 :
- 4701 :
// get summary of all field values
- 4702 :
this.compose_field_hash(true);
- 4703 :
- 4704 :
// start the auto-save timer
- 4705 :
this.auto_save_start();
- 4706 :
};
- 4707 :
- 4708 :
// init autocomplete events on compose form inputs
- 4709 :
this.init_messageform_inputs = function(focused)
- 4710 :
{
- 4711 :
var i,
- 4712 :
input_to = $("[name='_to']"),
- 4713 :
ac_fields = ['cc', 'bcc', 'replyto', 'followupto'];
- 4714 :
- 4715 :
// init live search events
- 4716 :
this.init_address_input_events(input_to);
- 4717 :
for (i in ac_fields) {
- 4718 :
this.init_address_input_events($("[name='_"+ac_fields[i]+"']"));
- 4719 :
}
- 4720 :
- 4721 :
if (!focused)
- 4722 :
focused = input_to;
- 4723 :
- 4724 :
// focus first empty element (and return it)
- 4725 :
return $(focused).focus().get(0);
- 4726 :
};
- 4727 :
- 4728 :
this.compose_restore_dialog = function(j, html_mode)
- 4729 :
{
- 4730 :
var i, key, formdata, index = this.local_storage_get_item('compose.index', []);
- 4731 :
- 4732 :
var show_next = function(i) {
- 4733 :
if (++i < index.length)
- 4734 :
ref.compose_restore_dialog(i, html_mode)
- 4735 :
}
- 4736 :
- 4737 :
for (i = j || 0; i < index.length; i++) {
- 4738 :
key = index[i];
- 4739 :
formdata = this.local_storage_get_item('compose.' + key, null, true);
- 4740 :
if (!formdata) {
- 4741 :
continue;
- 4742 :
}
- 4743 :
// restore saved copy of current compose_id
- 4744 :
if (formdata.changed && key == this.env.compose_id) {
- 4745 :
this.restore_compose_form(key, html_mode);
- 4746 :
break;
- 4747 :
}
- 4748 :
// skip records from 'other' drafts
- 4749 :
if (this.env.draft_id && formdata.draft_id && formdata.draft_id != this.env.draft_id) {
- 4750 :
continue;
- 4751 :
}
- 4752 :
// skip records on reply
- 4753 :
if (this.env.reply_msgid && formdata.reply_msgid != this.env.reply_msgid) {
- 4754 :
continue;
- 4755 :
}
- 4756 :
// show dialog asking to restore the message
- 4757 :
if (formdata.changed && formdata.session != this.env.session_id) {
- 4758 :
this.show_popup_dialog(
- 4759 :
this.get_label('restoresavedcomposedata')
- 4760 :
.replace('$date', new Date(formdata.changed).toLocaleString())
- 4761 :
.replace('$subject', formdata._subject)
- 4762 :
.replace(/\n/g, '<br/>'),
- 4763 :
this.get_label('restoremessage'),
- 4764 :
[{
- 4765 :
text: this.get_label('restore'),
- 4766 :
'class': 'mainaction restore',
- 4767 :
click: function(){
- 4768 :
ref.restore_compose_form(key, html_mode);
- 4769 :
ref.remove_compose_data(key); // remove old copy
- 4770 :
ref.save_compose_form_local(); // save under current compose_id
- 4771 :
$(this).dialog('close');
- 4772 :
}
- 4773 :
},
- 4774 :
{
- 4775 :
text: this.get_label('delete'),
- 4776 :
'class': 'delete',
- 4777 :
click: function(){
- 4778 :
ref.remove_compose_data(key);
- 4779 :
$(this).dialog('close');
- 4780 :
show_next(i);
- 4781 :
}
- 4782 :
},
- 4783 :
{
- 4784 :
text: this.get_label('ignore'),
- 4785 :
'class': 'cancel',
- 4786 :
click: function(){
- 4787 :
$(this).dialog('close');
- 4788 :
show_next(i);
- 4789 :
}
- 4790 :
}]
- 4791 :
);
- 4792 :
break;
- 4793 :
}
- 4794 :
}
- 4795 :
}
- 4796 :
- 4797 :
this.init_address_input_events = function(obj, props)
- 4798 :
{
- 4799 :
// configure parallel autocompletion
- 4800 :
if (!props && this.env.autocomplete_threads > 0) {
- 4801 :
props = {
- 4802 :
threads: this.env.autocomplete_threads,
- 4803 :
sources: this.env.autocomplete_sources
- 4804 :
};
- 4805 :
}
- 4806 :
- 4807 :
obj.keydown(function(e) { return ref.ksearch_keydown(e, this, props); })
- 4808 :
.attr({autocomplete: 'off', 'aria-autocomplete': 'list', 'aria-expanded': 'false', role: 'combobox'});
- 4809 :
- 4810 :
var callback = function(e) {
- 4811 :
if (ref.ksearch_pane && e.target === ref.ksearch_pane.get(0)) {
- 4812 :
return;
- 4813 :
}
- 4814 :
ref.ksearch_hide();
- 4815 :
};
- 4816 :
- 4817 :
// hide the popup on any click
- 4818 :
$(document).on('click', callback);
- 4819 :
// and on scroll (that cannot be jQuery.on())
- 4820 :
document.addEventListener('scroll', callback, true);
- 4821 :
};
- 4822 :
- 4823 :
this.submit_messageform = function(draft, saveonly)
- 4824 :
{
- 4825 :
var form = this.gui_objects.messageform;
- 4826 :
- 4827 :
if (!form)
- 4828 :
return;
- 4829 :
- 4830 :
// the message has been sent but not saved, ask the user what to do
- 4831 :
if (!saveonly && this.env.is_sent) {
- 4832 :
return this.simple_dialog(this.get_label('messageissent'), '', // TODO: dialog title
- 4833 :
function() {
- 4834 :
ref.submit_messageform(false, true);
- 4835 :
return true;
- 4836 :
}
- 4837 :
);
- 4838 :
}
- 4839 :
- 4840 :
// delegate sending to Mailvelope routine
- 4841 :
if (this.mailvelope_editor) {
- 4842 :
return this.mailvelope_submit_messageform(draft, saveonly);
- 4843 :
}
- 4844 :
- 4845 :
// all checks passed, send message
- 4846 :
var msgid = this.set_busy(true, draft || saveonly ? 'savingmessage' : 'sendingmessage'),
- 4847 :
lang = this.spellcheck_lang(),
- 4848 :
files = [];
- 4849 :
- 4850 :
// send files list
- 4851 :
$('li', this.gui_objects.attachmentlist).each(function() { files.push(this.id.replace(/^rcmfile/, '')); });
- 4852 :
$('[name="_attachments"]', form).val(files.join());
- 4853 :
- 4854 :
form.target = this.get_save_target(msgid);
- 4855 :
form._draft.value = draft ? '1' : '';
- 4856 :
form.action = this.add_url(form.action, '_unlock', msgid);
- 4857 :
form.action = this.add_url(form.action, '_framed', 1);
- 4858 :
- 4859 :
if (lang)
- 4860 :
form.action = this.add_url(form.action, '_lang', lang);
- 4861 :
if (saveonly)
- 4862 :
form.action = this.add_url(form.action, '_saveonly', 1);
- 4863 :
- 4864 :
// register timer to notify about connection timeout
- 4865 :
this.submit_timer = setTimeout(function() {
- 4866 :
ref.set_busy(false, null, msgid);
- 4867 :
ref.display_message('requesttimedout', 'error');
- 4868 :
}, this.env.request_timeout * 1000);
- 4869 :
- 4870 :
form.submit();
- 4871 :
};
- 4872 :
- 4873 :
this.compose_recipient_select = function(list)
- 4874 :
{
- 4875 :
var id, n, recipients = 0, selection = list.get_selection();
- 4876 :
for (n=0; n < selection.length; n++) {
- 4877 :
id = selection[n];
- 4878 :
if (this.env.contactdata[id])
- 4879 :
recipients++;
- 4880 :
}
- 4881 :
this.enable_command('add-recipient', recipients);
- 4882 :
};
- 4883 :
- 4884 :
this.compose_add_recipient = function(field)
- 4885 :
{
- 4886 :
// find last focused field name
- 4887 :
if (!field) {
- 4888 :
field = $(this.env.focused_field).filter(':visible');
- 4889 :
field = field.length ? field.attr('id').replace('_', '') : 'to';
- 4890 :
}
- 4891 :
- 4892 :
var recipients = [], input = $('#_' + field), selection = this.contact_list.get_selection();
- 4893 :
- 4894 :
if (this.contact_list && selection.length) {
- 4895 :
var data, name, n, id;
- 4896 :
for (n = 0; n < selection.length; n++) {
- 4897 :
if ((id = selection[n]) && (data = this.env.contactdata[id])) {
- 4898 :
name = data.name || data
- 4899 :
- 4900 :
// group is added, expand it
- 4901 :
if (id.charAt(0) == 'E' && input.length) {
- 4902 :
// We wrap the group name with invisible markers to prevent from problems with group expanding (#7569)
- 4903 :
name = '\u200b' + name + '\u200b';
- 4904 :
var gid = id.substr(1);
- 4905 :
this.group2expand[gid] = {name: name, input: input.get(0)};
- 4906 :
this.http_request('group-expand', {_source: data.source || this.env.source, _gid: gid}, false);
- 4907 :
}
- 4908 :
- 4909 :
recipients.push(name);
- 4910 :
}
- 4911 :
}
- 4912 :
}
- 4913 :
- 4914 :
if (recipients.length && input.length) {
- 4915 :
var oldval = input.val();
- 4916 :
if (oldval && !/[,;]\s*$/.test(oldval))
- 4917 :
oldval += ', ';
- 4918 :
input.val(oldval + recipients.join(', ') + ', ').change();
- 4919 :
this.triggerEvent('add-recipient', { field:field, recipients:recipients });
- 4920 :
}
- 4921 :
- 4922 :
return recipients.length;
- 4923 :
};
- 4924 :
- 4925 :
// checks the input fields before sending a message
- 4926 :
this.check_compose_input = function(cmd)
- 4927 :
{
- 4928 :
var key,
- 4929 :
input_subject = $("[name='_subject']");
- 4930 :
- 4931 :
// check if all files has been uploaded
- 4932 :
for (key in this.env.attachments) {
- 4933 :
if (typeof this.env.attachments[key] === 'object' && !this.env.attachments[key].complete) {
- 4934 :
this.alert_dialog(this.get_label('notuploadedwarning'));
- 4935 :
return false;
- 4936 :
}
- 4937 :
}
- 4938 :
- 4939 :
// display localized warning for missing subject
- 4940 :
if (!this.env.nosubject_warned && input_subject.val() == '') {
- 4941 :
var dialog,
- 4942 :
prompt_value = $('<input>').attr({type: 'text', size: 40, 'data-submit': 'true'}),
- 4943 :
myprompt = $('<div class="prompt">')
- 4944 :
.append($('<p class="message">').text(this.get_label('nosubjectwarning')))
- 4945 :
.append(prompt_value),
- 4946 :
save_func = function() {
- 4947 :
input_subject.val(prompt_value.val());
- 4948 :
dialog.dialog('close');
- 4949 :
if (ref.check_compose_input(cmd))
- 4950 :
ref.command(cmd, { nocheck:true }); // repeat command which triggered this
- 4951 :
};
- 4952 :
- 4953 :
dialog = this.show_popup_dialog(
- 4954 :
myprompt,
- 4955 :
this.get_label('nosubjecttitle'),
- 4956 :
[{
- 4957 :
text: this.get_label('sendmessage'),
- 4958 :
'class': 'mainaction send',
- 4959 :
click: function() { save_func(); }
- 4960 :
}, {
- 4961 :
text: this.get_label('cancel'),
- 4962 :
'class': 'cancel',
- 4963 :
click: function() {
- 4964 :
input_subject.focus();
- 4965 :
dialog.dialog('close');
- 4966 :
}
- 4967 :
}],
- 4968 :
{dialogClass: 'warning'}
- 4969 :
);
- 4970 :
- 4971 :
this.env.nosubject_warned = true;
- 4972 :
return false;
- 4973 :
}
- 4974 :
- 4975 :
// check for empty body (only possible if not mailvelope encrypted)
- 4976 :
if (!this.mailvelope_editor && !this.editor.get_content() && !confirm(this.get_label('nobodywarning'))) {
- 4977 :
this.editor.focus();
- 4978 :
return false;
- 4979 :
}
- 4980 :
- 4981 :
if (!this.check_compose_address_fields(cmd))
- 4982 :
return false;
- 4983 :
- 4984 :
// move body from html editor to textarea (just to be sure, #1485860)
- 4985 :
this.editor.save();
- 4986 :
- 4987 :
return true;
- 4988 :
};
- 4989 :
- 4990 :
this.check_compose_address_fields = function(cmd, form)
- 4991 :
{
- 4992 :
if (!form)
- 4993 :
form = window.document;
- 4994 :
- 4995 :
// check input fields
- 4996 :
var key, recipients, dialog,
- 4997 :
limit = this.env.max_disclosed_recipients,
- 4998 :
input_to = $("[name='_to']", form),
- 4999 :
input_cc = $("[name='_cc']", form),
- 5000 :
input_bcc = $("[name='_bcc']", form),
- 5001 :
input_from = $("[name='_from']", form),
- 5002 :
get_recipients = function(fields) {
- 5003 :
fields = $.map(fields, function(v) {
- 5004 :
v = v.val().trim();
- 5005 :
return v.length ? v : null;
- 5006 :
});
- 5007 :
return fields.join(',').replace(/^[\s,;]+/, '').replace(/[\s,;]+$/, '');
- 5008 :
};
- 5009 :
- 5010 :
// check sender (if have no identities)
- 5011 :
if (input_from.prop('type') == 'text' && !rcube_check_email(input_from.val(), true)) {
- 5012 :
this.alert_dialog(this.get_label('nosenderwarning'), function() {
- 5013 :
input_from.focus();
- 5014 :
});
- 5015 :
return false;
- 5016 :
}
- 5017 :
- 5018 :
// check for empty recipient
- 5019 :
if (!rcube_check_email(get_recipients([input_to, input_cc, input_bcc]), true)) {
- 5020 :
this.alert_dialog(this.get_label('norecipientwarning'), function() {
- 5021 :
input_to.focus();
- 5022 :
});
- 5023 :
return false;
- 5024 :
}
- 5025 :
- 5026 :
// check disclosed recipients limit
- 5027 :
if (limit && !this.env.disclosed_recipients_warned
- 5028 :
&& rcube_check_email(recipients = get_recipients([input_to, input_cc]), true, true) > limit
- 5029 :
) {
- 5030 :
var save_func = function(move_to_bcc) {
- 5031 :
if (move_to_bcc) {
- 5032 :
var bcc = input_bcc.val();
- 5033 :
input_bcc.val((bcc ? (bcc + ', ') : '') + recipients).change();
- 5034 :
input_to.val('').change();
- 5035 :
input_cc.val('').change();
- 5036 :
}
- 5037 :
- 5038 :
dialog.dialog('close');
- 5039 :
- 5040 :
if (typeof cmd == 'function')
- 5041 :
cmd();
- 5042 :
else if (cmd)
- 5043 :
ref.command(cmd, { nocheck:true }); // repeat command which triggered this
- 5044 :
};
- 5045 :
- 5046 :
dialog = this.show_popup_dialog(
- 5047 :
this.get_label('disclosedrecipwarning'),
- 5048 :
this.get_label('disclosedreciptitle'),
- 5049 :
[{
- 5050 :
text: this.get_label('sendmessage'),
- 5051 :
click: function() { save_func(false); },
- 5052 :
'class': 'mainaction'
- 5053 :
}, {
- 5054 :
text: this.get_label('bccinstead'),
- 5055 :
click: function() { save_func(true); }
- 5056 :
}, {
- 5057 :
text: this.get_label('cancel'),
- 5058 :
click: function() { dialog.dialog('close'); },
- 5059 :
'class': 'cancel'
- 5060 :
}],
- 5061 :
{dialogClass: 'warning'}
- 5062 :
);
- 5063 :
- 5064 :
this.env.disclosed_recipients_warned = true;
- 5065 :
return false;
- 5066 :
}
- 5067 :
- 5068 :
return true;
- 5069 :
};
- 5070 :
- 5071 :
this.toggle_editor = function(props, obj, e)
- 5072 :
{
- 5073 :
// @todo: this should work also with many editors on page
- 5074 :
var mode, result = this.editor.toggle(props.html, props.noconvert || false),
- 5075 :
control = $('#' + this.editor.id).data('control') || $(e ? e.target : []);
- 5076 :
- 5077 :
if (result)
- 5078 :
mode = props.html ? 'html' : 'plain';
- 5079 :
else
- 5080 :
mode = props.html ? 'plain' : 'html';
- 5081 :
- 5082 :
// update internal format flag
- 5083 :
$("[name='_is_html']").val(mode == 'html' ? 1 : 0);
- 5084 :
- 5085 :
if (control.is('[type=checkbox]'))
- 5086 :
control.prop('checked', mode == 'html');
- 5087 :
else
- 5088 :
control.val(mode);
- 5089 :
- 5090 :
return result;
- 5091 :
};
- 5092 :
- 5093 :
// Inserts a predefined response to the compose editor
- 5094 :
this.insert_response = function(key)
- 5095 :
{
- 5096 :
this.editor.replace(this.env.textresponses[key]);
- 5097 :
this.display_message('responseinserted', 'confirmation');
- 5098 :
};
- 5099 :
- 5100 :
/**
- 5101 :
* Open the dialog to save a new canned response
- 5102 :
*/
- 5103 :
this.save_response = function()
- 5104 :
{
- 5105 :
// show dialog to enter a name and to modify the text to be saved
- 5106 :
var buttons = {}, text = this.editor.get_content({selection: true, format: 'text', nosig: true}),
- 5107 :
html = '<form class="propform">' +
- 5108 :
'<div class="prop block"><label for="ffresponsename">' + this.get_label('responsename') + '</label>' +
- 5109 :
'<input type="text" name="name" id="ffresponsename" size="40" /></div>' +
- 5110 :
'<div class="prop block"><label for="ffresponsetext">' + this.get_label('responsetext') + '</label>' +
- 5111 :
'<textarea name="text" id="ffresponsetext" cols="40" rows="8"></textarea></div>' +
- 5112 :
'</form>';
- 5113 :
- 5114 :
buttons[this.get_label('save')] = function(e) {
- 5115 :
var name = $('#ffresponsename').val(),
- 5116 :
text = $('#ffresponsetext').val();
- 5117 :
- 5118 :
if (!text) {
- 5119 :
$('#ffresponsetext').select();
- 5120 :
return false;
- 5121 :
}
- 5122 :
if (!name)
- 5123 :
name = text.replace(/[\r\n]+/g, ' ').substring(0,40);
- 5124 :
- 5125 :
var lock = ref.display_message('savingresponse', 'loading');
- 5126 :
ref.http_post('settings/responses', { _insert:1, _name:name, _text:text }, lock);
- 5127 :
$(this).dialog('close');
- 5128 :
};
- 5129 :
- 5130 :
buttons[this.get_label('cancel')] = function() {
- 5131 :
$(this).dialog('close');
- 5132 :
};
- 5133 :
- 5134 :
this.show_popup_dialog(html, this.get_label('newresponse'), buttons, {button_classes: ['mainaction save', 'cancel']});
- 5135 :
- 5136 :
$('#ffresponsetext').val(text);
- 5137 :
$('#ffresponsename').select();
- 5138 :
};
- 5139 :
- 5140 :
this.add_response_item = function(response)
- 5141 :
{
- 5142 :
var key = response.key;
- 5143 :
this.env.textresponses[key] = response;
- 5144 :
- 5145 :
// append to responses list
- 5146 :
if (this.gui_objects.responseslist) {
- 5147 :
var li = $('<li>').appendTo(this.gui_objects.responseslist);
- 5148 :
$('<a>').addClass('insertresponse active')
- 5149 :
.attr({href: '#', rel: key, tabindex: '0'})
- 5150 :
.html(this.quote_html(response.name))
- 5151 :
.appendTo(li)
- 5152 :
.mousedown(function(e) {
- 5153 :
return rcube_event.cancel(e);
- 5154 :
})
- 5155 :
.on('mouseup keypress', function(e) {
- 5156 :
if (e.type == 'mouseup' || rcube_event.get_keycode(e) == 13) {
- 5157 :
ref.command('insert-response', $(this).attr('rel'));
- 5158 :
$(document.body).trigger('mouseup'); // hides the menu
- 5159 :
return rcube_event.cancel(e);
- 5160 :
}
- 5161 :
});
- 5162 :
- 5163 :
// remove the placeholder item if its there
- 5164 :
$(this.gui_objects.responseslist).find('li > a.insertresponse.placeholder').parent().remove();
- 5165 :
}
- 5166 :
};
- 5167 :
- 5168 :
this.edit_responses = function()
- 5169 :
{
- 5170 :
// TODO: implement inline editing of responses
- 5171 :
};
- 5172 :
- 5173 :
this.delete_response = function(key)
- 5174 :
{
- 5175 :
if (!key && this.responses_list) {
- 5176 :
var selection = this.responses_list.get_selection();
- 5177 :
key = selection[0];
- 5178 :
}
- 5179 :
- 5180 :
// submit delete request
- 5181 :
if (key) {
- 5182 :
this.confirm_dialog(this.get_label('deleteresponseconfirm'), 'delete', function() {
- 5183 :
ref.http_post('settings/delete-response', { _key: key }, false);
- 5184 :
});
- 5185 :
}
- 5186 :
};
- 5187 :
- 5188 :
// updates spellchecker buttons on state change
- 5189 :
this.spellcheck_state = function()
- 5190 :
{
- 5191 :
var active = this.editor.spellcheck_state();
- 5192 :
- 5193 :
$.each(this.buttons.spellcheck || [], function(i, v) {
- 5194 :
$('#' + v.id)[active ? 'addClass' : 'removeClass']('selected');
- 5195 :
});
- 5196 :
- 5197 :
return active;
- 5198 :
};
- 5199 :
- 5200 :
// get selected language
- 5201 :
this.spellcheck_lang = function()
- 5202 :
{
- 5203 :
return this.editor.get_language();
- 5204 :
};
- 5205 :
- 5206 :
this.spellcheck_lang_set = function(lang)
- 5207 :
{
- 5208 :
this.editor.set_language(lang);
- 5209 :
};
- 5210 :
- 5211 :
// resume spellchecking, highlight provided misspellings without new ajax request
- 5212 :
this.spellcheck_resume = function(data)
- 5213 :
{
- 5214 :
this.editor.spellcheck_resume(data);
- 5215 :
};
- 5216 :
- 5217 :
this.set_draft_id = function(id)
- 5218 :
{
- 5219 :
if (id && id != this.env.draft_id) {
- 5220 :
var filter = {task: 'mail', action: ''},
- 5221 :
rc = this.opener(false, filter) || this.opener(true, filter);
- 5222 :
- 5223 :
// refresh the drafts folder in the opener window
- 5224 :
if (rc && rc.env.mailbox == this.env.drafts_mailbox)
- 5225 :
rc.command('checkmail');
- 5226 :
- 5227 :
this.env.draft_id = id;
- 5228 :
$("[name='_draft_saveid']").val(id);
- 5229 :
}
- 5230 :
- 5231 :
// always remove local copy upon saving as draft
- 5232 :
this.remove_compose_data(this.env.compose_id);
- 5233 :
this.compose_skip_unsavedcheck = false;
- 5234 :
};
- 5235 :
- 5236 :
// Create (attach) 'savetarget' iframe before use
- 5237 :
this.get_save_target = function(unlock)
- 5238 :
{
- 5239 :
// Removing the frame on load/error to workaround issues with window history
- 5240 :
this.dummy_iframe('savetarget', 'about:blank')
- 5241 :
.on('load error', function() {
- 5242 :
// catch invalid/error response from server and unlock the UI (#7494, #7488, #7522)
- 5243 :
if (unlock && $(this).contents().find('meta[name="generator"][content="Roundcube"]').length == 0) {
- 5244 :
ref.iframe_loaded(unlock);
- 5245 :
ref.display_message('connerror', 'error');
- 5246 :
}
- 5247 :
- 5248 :
$(this).remove();
- 5249 :
});
- 5250 :
- 5251 :
return 'savetarget';
- 5252 :
};
- 5253 :
- 5254 :
this.auto_save_start = function()
- 5255 :
{
- 5256 :
if (this.env.draft_autosave) {
- 5257 :
this.save_timer = setTimeout(function() {
- 5258 :
ref.command("savedraft");
- 5259 :
}, this.env.draft_autosave * 1000);
- 5260 :
}
- 5261 :
- 5262 :
// save compose form content to local storage every 5 seconds
- 5263 :
if (!this.local_save_timer && window.localStorage && this.env.save_localstorage) {
- 5264 :
// track typing activity and only save on changes
- 5265 :
this.compose_type_activity = this.compose_type_activity_last = 0;
- 5266 :
$(document).keypress(function(e) { ref.compose_type_activity++; });
- 5267 :
- 5268 :
this.local_save_timer = setInterval(function(){
- 5269 :
if (ref.compose_type_activity > ref.compose_type_activity_last) {
- 5270 :
ref.save_compose_form_local();
- 5271 :
ref.compose_type_activity_last = ref.compose_type_activity;
- 5272 :
}
- 5273 :
}, 5000);
- 5274 :
- 5275 :
$(window).on('unload', function() {
- 5276 :
// remove copy from local storage if compose screen is left after warning
- 5277 :
if (!ref.env.server_error)
- 5278 :
ref.remove_compose_data(ref.env.compose_id);
- 5279 :
});
- 5280 :
}
- 5281 :
- 5282 :
// check for unsaved changes before leaving the compose page
- 5283 :
if (!window.onbeforeunload) {
- 5284 :
window.onbeforeunload = function() {
- 5285 :
if (!ref.compose_skip_unsavedcheck && ref.cmp_hash != ref.compose_field_hash()) {
- 5286 :
return ref.get_label('notsentwarning');
- 5287 :
}
- 5288 :
};
- 5289 :
}
- 5290 :
- 5291 :
// Unlock interface now that saving is complete
- 5292 :
this.busy = false;
- 5293 :
};
- 5294 :
- 5295 :
this.compose_field_hash = function(save)
- 5296 :
{
- 5297 :
// check input fields
- 5298 :
var i, id, val, str = '', hash_fields = ['to', 'cc', 'bcc', 'subject'];
- 5299 :
- 5300 :
for (i=0; i<hash_fields.length; i++)
- 5301 :
if (val = $('[name="_' + hash_fields[i] + '"]').val())
- 5302 :
str += val + ':';
- 5303 :
- 5304 :
str += this.editor.get_content({refresh: false});
- 5305 :
- 5306 :
for (id in this.env.attachments)
- 5307 :
str += id;
- 5308 :
- 5309 :
// we can't detect changes in the Mailvelope editor so assume it changed
- 5310 :
if (this.mailvelope_editor) {
- 5311 :
str += ';' + new Date().getTime();
- 5312 :
}
- 5313 :
- 5314 :
if (save)
- 5315 :
this.cmp_hash = str;
- 5316 :
- 5317 :
return str;
- 5318 :
};
- 5319 :
- 5320 :
// store the contents of the compose form to localstorage
- 5321 :
this.save_compose_form_local = function()
- 5322 :
{
- 5323 :
// feature is disabled
- 5324 :
if (!this.env.save_localstorage)
- 5325 :
return;
- 5326 :
- 5327 :
var formdata = { session:this.env.session_id, changed:new Date().getTime() },
- 5328 :
ed, empty = true;
- 5329 :
- 5330 :
// get fresh content from editor
- 5331 :
this.editor.save();
- 5332 :
- 5333 :
if (this.env.draft_id) {
- 5334 :
formdata.draft_id = this.env.draft_id;
- 5335 :
}
- 5336 :
if (this.env.reply_msgid) {
- 5337 :
formdata.reply_msgid = this.env.reply_msgid;
- 5338 :
}
- 5339 :
- 5340 :
$('input, select, textarea', this.gui_objects.messageform).each(function(i, elem) {
- 5341 :
switch (elem.tagName.toLowerCase()) {
- 5342 :
case 'input':
- 5343 :
if (elem.type == 'button' || elem.type == 'submit' || (elem.type == 'hidden' && elem.name != '_is_html')) {
- 5344 :
break;
- 5345 :
}
- 5346 :
formdata[elem.name] = elem.type != 'checkbox' || elem.checked ? $(elem).val() : '';
- 5347 :
- 5348 :
if (formdata[elem.name] != '' && elem.type != 'hidden')
- 5349 :
empty = false;
- 5350 :
break;
- 5351 :
- 5352 :
case 'select':
- 5353 :
formdata[elem.name] = $('option:checked', elem).val();
- 5354 :
break;
- 5355 :
- 5356 :
default:
- 5357 :
formdata[elem.name] = $(elem).val();
- 5358 :
if (formdata[elem.name] != '')
- 5359 :
empty = false;
- 5360 :
}
- 5361 :
});
- 5362 :
- 5363 :
if (!empty) {
- 5364 :
var index = this.local_storage_get_item('compose.index', []),
- 5365 :
key = this.env.compose_id;
- 5366 :
- 5367 :
if ($.inArray(key, index) < 0) {
- 5368 :
index.push(key);
- 5369 :
}
- 5370 :
- 5371 :
this.local_storage_set_item('compose.' + key, formdata, true);
- 5372 :
this.local_storage_set_item('compose.index', index);
- 5373 :
}
- 5374 :
};
- 5375 :
- 5376 :
// write stored compose data back to form
- 5377 :
this.restore_compose_form = function(key, html_mode)
- 5378 :
{
- 5379 :
var ed, formdata = this.local_storage_get_item('compose.' + key, true);
- 5380 :
- 5381 :
if (formdata && typeof formdata == 'object') {
- 5382 :
$.each(formdata, function(k, value) {
- 5383 :
if (k[0] == '_') {
- 5384 :
var elem = $("[name=" + k + "]");
- 5385 :
if (elem[0] && elem[0].type == 'checkbox') {
- 5386 :
elem.prop('checked', value != '');
- 5387 :
}
- 5388 :
else {
- 5389 :
elem.val(value).change();
- 5390 :
}
- 5391 :
}
- 5392 :
});
- 5393 :
- 5394 :
// initialize HTML editor
- 5395 :
if ((formdata._is_html == '1' && !html_mode) || (formdata._is_html != '1' && html_mode)) {
- 5396 :
this.command('toggle-editor', {id: this.env.composebody, html: !html_mode, noconvert: true});
- 5397 :
}
- 5398 :
}
- 5399 :
};
- 5400 :
- 5401 :
// remove stored compose data from localStorage
- 5402 :
this.remove_compose_data = function(key)
- 5403 :
{
- 5404 :
var index = this.local_storage_get_item('compose.index', []);
- 5405 :
- 5406 :
if ($.inArray(key, index) >= 0) {
- 5407 :
this.local_storage_remove_item('compose.' + key);
- 5408 :
this.local_storage_set_item('compose.index', $.grep(index, function(val,i) { return val != key; }));
- 5409 :
}
- 5410 :
};
- 5411 :
- 5412 :
// clear all stored compose data of this user
- 5413 :
this.clear_compose_data = function()
- 5414 :
{
- 5415 :
var i, index = this.local_storage_get_item('compose.index', []);
- 5416 :
- 5417 :
for (i=0; i < index.length; i++) {
- 5418 :
this.local_storage_remove_item('compose.' + index[i]);
- 5419 :
}
- 5420 :
- 5421 :
this.local_storage_remove_item('compose.index');
- 5422 :
};
- 5423 :
- 5424 :
this.change_identity = function(obj, show)
- 5425 :
{
- 5426 :
if (!obj || !obj.options)
- 5427 :
return false;
- 5428 :
- 5429 :
var id = $(obj).val(),
- 5430 :
got_sig = this.env.signatures && this.env.signatures[id],
- 5431 :
sig = this.env.identity,
- 5432 :
show_sig = show ? show : this.env.show_sig;
- 5433 :
- 5434 :
// enable manual signature insert
- 5435 :
if (got_sig) {
- 5436 :
this.enable_command('insert-sig', true);
- 5437 :
this.env.compose_commands.push('insert-sig');
- 5438 :
got_sig = true;
- 5439 :
}
- 5440 :
else
- 5441 :
this.enable_command('insert-sig', false);
- 5442 :
- 5443 :
// first function execution
- 5444 :
if (!this.env.identities_initialized) {
- 5445 :
this.env.identities_initialized = true;
- 5446 :
if (this.env.show_sig_later)
- 5447 :
this.env.show_sig = true;
- 5448 :
if (this.env.opened_extwin)
- 5449 :
return;
- 5450 :
}
- 5451 :
- 5452 :
// update reply-to/bcc fields with addresses defined in identities
- 5453 :
$.each(['replyto', 'bcc'], function() {
- 5454 :
var rx, key = this,
- 5455 :
old_val = sig && ref.env.identities[sig] ? ref.env.identities[sig][key] : '',
- 5456 :
new_val = id && ref.env.identities[id] ? ref.env.identities[id][key] : '',
- 5457 :
input = $('[name="_'+key+'"]'), input_val = input.val();
- 5458 :
- 5459 :
// remove old address(es)
- 5460 :
if (old_val && input_val) {
- 5461 :
rx = new RegExp('\\s*' + RegExp.escape(old_val) + '\\s*');
- 5462 :
input_val = input_val.replace(rx, '');
- 5463 :
}
- 5464 :
- 5465 :
// cleanup
- 5466 :
input_val = String(input_val).replace(/[,;]\s*[,;]/g, ',').replace(/^[\s,;]+/, '');
- 5467 :
- 5468 :
// add new address(es)
- 5469 :
if (new_val && input_val.indexOf(new_val) == -1 && input_val.indexOf(new_val.replace(/"/g, '')) == -1) {
- 5470 :
if (input_val) {
- 5471 :
input_val = input_val.replace(/[,;\s]+$/, '') + ', ';
- 5472 :
}
- 5473 :
- 5474 :
input_val += new_val + ', ';
- 5475 :
}
- 5476 :
- 5477 :
if (old_val || new_val)
- 5478 :
input.val(input_val).change();
- 5479 :
});
- 5480 :
- 5481 :
if (this.editor)
- 5482 :
this.editor.change_signature(id, show_sig);
- 5483 :
- 5484 :
if (show && got_sig)
- 5485 :
this.display_message('siginserted', 'confirmation');
- 5486 :
- 5487 :
this.env.identity = id;
- 5488 :
this.triggerEvent('change_identity');
- 5489 :
- 5490 :
return true;
- 5491 :
};
- 5492 :
- 5493 :
// Open file selection dialog for defined upload form
- 5494 :
// Works only on click and only with smart-upload forms
- 5495 :
this.upload_input = function(name)
- 5496 :
{
- 5497 :
$('#' + name + ' input[type="file"]').click();
- 5498 :
};
- 5499 :
- 5500 :
// upload (attachment) file
- 5501 :
this.upload_file = function(form, action, lock)
- 5502 :
{
- 5503 :
if (form) {
- 5504 :
var fname, files = [];
- 5505 :
$('input', form).each(function() {
- 5506 :
if (this.files) {
- 5507 :
fname = this.name;
- 5508 :
for (var i=0; i < this.files.length; i++)
- 5509 :
files.push(this.files[i]);
- 5510 :
}
- 5511 :
});
- 5512 :
- 5513 :
return this.file_upload(files, {_id: this.env.compose_id || ''}, {
- 5514 :
name: fname,
- 5515 :
action: action,
- 5516 :
lock: lock
- 5517 :
});
- 5518 :
}
- 5519 :
};
- 5520 :
- 5521 :
// add file name to attachment list
- 5522 :
// called from upload page
- 5523 :
this.add2attachment_list = function(name, att, upload_id)
- 5524 :
{
- 5525 :
if (upload_id)
- 5526 :
this.triggerEvent('fileuploaded', {name: name, attachment: att, id: upload_id});
- 5527 :
- 5528 :
if (upload_id && this.env.attachments[upload_id])
- 5529 :
delete this.env.attachments[upload_id];
- 5530 :
- 5531 :
this.env.attachments[name] = att;
- 5532 :
- 5533 :
if (!this.gui_objects.attachmentlist)
- 5534 :
return false;
- 5535 :
- 5536 :
var label, indicator, li = $('<li>');
- 5537 :
- 5538 :
if (!att.complete && att.html.indexOf('<') < 0)
- 5539 :
att.html = '<span class="uploading">' + att.html + '</span>';
- 5540 :
- 5541 :
if (!att.complete && this.env.loadingicon)
- 5542 :
att.html = '<img src="'+this.env.loadingicon+'" alt="" class="uploading" />' + att.html;
- 5543 :
- 5544 :
if (!att.complete) {
- 5545 :
label = this.get_label('cancel');
- 5546 :
att.html = '<a title="'+label+'" onclick="return rcmail.cancel_attachment_upload(\''+name+'\');" href="#cancelupload" class="cancelupload">'
- 5547 :
+ (this.env.cancelicon ? '<img src="'+this.env.cancelicon+'" alt="'+label+'" />' : '<span class="inner">' + label + '</span>') + '</a>' + att.html;
- 5548 :
}
- 5549 :
- 5550 :
li.attr('id', name).addClass(att.classname).html(att.html)
- 5551 :
.find('.attachment-name').on('mouseover', function() { rcube_webmail.long_subject_title_ex(this); });
- 5552 :
- 5553 :
// replace indicator's li
- 5554 :
if (upload_id && (indicator = document.getElementById(upload_id))) {
- 5555 :
li.replaceAll(indicator);
- 5556 :
}
- 5557 :
else { // add new li
- 5558 :
li.appendTo(this.gui_objects.attachmentlist);
- 5559 :
}
- 5560 :
- 5561 :
// set tabindex attribute
- 5562 :
var tabindex = $(this.gui_objects.attachmentlist).attr('data-tabindex') || '0';
- 5563 :
li.find('a').attr('tabindex', tabindex);
- 5564 :
- 5565 :
this.triggerEvent('fileappended', {name: name, attachment: att, id: upload_id, item: li});
- 5566 :
- 5567 :
return true;
- 5568 :
};
- 5569 :
- 5570 :
this.remove_from_attachment_list = function(name)
- 5571 :
{
- 5572 :
delete this.env.attachments[name];
- 5573 :
$('#'+name).remove();
- 5574 :
};
- 5575 :
- 5576 :
this.remove_attachment = function(name)
- 5577 :
{
- 5578 :
if (name && this.env.attachments[name])
- 5579 :
this.http_post('remove-attachment', { _id:this.env.compose_id, _file:name });
- 5580 :
- 5581 :
return false;
- 5582 :
};
- 5583 :
- 5584 :
this.cancel_attachment_upload = function(name)
- 5585 :
{
- 5586 :
if (!name || !this.uploads[name])
- 5587 :
return false;
- 5588 :
- 5589 :
this.remove_from_attachment_list(name);
- 5590 :
this.uploads[name].abort();
- 5591 :
return false;
- 5592 :
};
- 5593 :
- 5594 :
// rename uploaded attachment (in compose)
- 5595 :
this.rename_attachment = function(id)
- 5596 :
{
- 5597 :
var attachment = this.env.attachments[id];
- 5598 :
- 5599 :
if (!attachment)
- 5600 :
return;
- 5601 :
- 5602 :
var input = $('<input>').attr({type: 'text', size: 50}).val(attachment.name),
- 5603 :
content = $('<label>').text(this.get_label('namex')).append(input);
- 5604 :
- 5605 :
this.simple_dialog(content, 'attachmentrename', function() {
- 5606 :
var name;
- 5607 :
if ((name = input.val()) && name != attachment.name) {
- 5608 :
ref.http_post('rename-attachment', {_id: ref.env.compose_id, _file: id, _name: name},
- 5609 :
ref.set_busy(true, 'loading'));
- 5610 :
return true;
- 5611 :
}
- 5612 :
}
- 5613 :
);
- 5614 :
};
- 5615 :
- 5616 :
// update attachments list with the new name
- 5617 :
this.rename_attachment_handler = function(id, name)
- 5618 :
{
- 5619 :
var attachment = this.env.attachments[id];
- 5620 :
- 5621 :
if (!attachment || !name)
- 5622 :
return;
- 5623 :
- 5624 :
attachment.name = name;
- 5625 :
- 5626 :
$('#' + id + ' .attachment-name').text(name).attr('title', '');
- 5627 :
};
- 5628 :
- 5629 :
// send remote request to add a new contact
- 5630 :
this.add_contact = function(value, reload, source)
- 5631 :
{
- 5632 :
if (value)
- 5633 :
this.http_post('addcontact', {_address: value, _reload: reload, _source: source});
- 5634 :
};
- 5635 :
- 5636 :
// send remote request to search mail or contacts
- 5637 :
this.qsearch = function(value)
- 5638 :
{
- 5639 :
// Note: Some plugins would like to do search without value,
- 5640 :
// so we keep value != '' check to allow that use-case. Which means
- 5641 :
// e.g. that qsearch() with no argument will execute the search.
- 5642 :
if (value != '' || $(this.gui_objects.qsearchbox).val() || $(this.gui_objects.search_interval).val()) {
- 5643 :
var r, lock = this.set_busy(true, 'searching'),
- 5644 :
url = this.search_params(value),
- 5645 :
action = this.env.action == 'compose' && this.contact_list ? 'search-contacts' : 'search';
- 5646 :
- 5647 :
if (this.message_list)
- 5648 :
this.clear_message_list();
- 5649 :
else if (this.contact_list)
- 5650 :
this.list_contacts_clear();
- 5651 :
- 5652 :
if (this.env.source)
- 5653 :
url._source = this.env.source;
- 5654 :
if (this.env.group)
- 5655 :
url._gid = this.env.group;
- 5656 :
- 5657 :
// reset vars
- 5658 :
this.env.current_page = 1;
- 5659 :
- 5660 :
r = this.http_request(action, url, lock);
- 5661 :
- 5662 :
this.env.qsearch = {lock: lock, request: r};
- 5663 :
this.enable_command('set-listmode', this.env.threads && (this.env.search_scope || 'base') == 'base');
- 5664 :
- 5665 :
return true;
- 5666 :
}
- 5667 :
- 5668 :
return false;
- 5669 :
};
- 5670 :
- 5671 :
this.continue_search = function(request_id)
- 5672 :
{
- 5673 :
var lock = this.set_busy(true, 'stillsearching');
- 5674 :
- 5675 :
setTimeout(function() {
- 5676 :
var url = ref.search_params();
- 5677 :
url._continue = request_id;
- 5678 :
ref.env.qsearch = { lock: lock, request: ref.http_request('search', url, lock) };
- 5679 :
}, 100);
- 5680 :
};
- 5681 :
- 5682 :
// build URL params for search
- 5683 :
this.search_params = function(search, filter)
- 5684 :
{
- 5685 :
var n, url = {}, mods_arr = [],
- 5686 :
mods = this.env.search_mods,
- 5687 :
scope = this.env.search_scope || 'base',
- 5688 :
mbox = this.env.mailbox;
- 5689 :
- 5690 :
if (!filter && this.gui_objects.search_filter)
- 5691 :
filter = this.gui_objects.search_filter.value;
- 5692 :
- 5693 :
if (!search && this.gui_objects.qsearchbox)
- 5694 :
search = this.gui_objects.qsearchbox.value;
- 5695 :
- 5696 :
if (this.gui_objects.search_interval)
- 5697 :
url._interval = $(this.gui_objects.search_interval).val();
- 5698 :
- 5699 :
if (search) {
- 5700 :
url._q = search;
- 5701 :
- 5702 :
if (mods && this.message_list)
- 5703 :
mods = mods[mbox] || mods['*'];
- 5704 :
- 5705 :
if (mods) {
- 5706 :
for (n in mods)
- 5707 :
mods_arr.push(n);
- 5708 :
url._headers = mods_arr.join(',');
- 5709 :
}
- 5710 :
}
- 5711 :
- 5712 :
url._layout = this.env.layout;
- 5713 :
url._filter = filter;
- 5714 :
url._scope = scope;
- 5715 :
url._mbox = mbox;
- 5716 :
- 5717 :
return url;
- 5718 :
};
- 5719 :
- 5720 :
// reset search filter
- 5721 :
this.reset_search_filter = function()
- 5722 :
{
- 5723 :
this.filter_disabled = true;
- 5724 :
if (this.gui_objects.search_filter)
- 5725 :
$(this.gui_objects.search_filter).val('ALL').change();
- 5726 :
this.filter_disabled = false;
- 5727 :
};
- 5728 :
- 5729 :
// reset quick-search form
- 5730 :
this.reset_qsearch = function(all)
- 5731 :
{
- 5732 :
if (this.gui_objects.qsearchbox)
- 5733 :
this.gui_objects.qsearchbox.value = '';
- 5734 :
- 5735 :
if (this.gui_objects.search_interval)
- 5736 :
$(this.gui_objects.search_interval).val('');
- 5737 :
- 5738 :
if (this.env.qsearch)
- 5739 :
this.abort_request(this.env.qsearch);
- 5740 :
- 5741 :
if (all) {
- 5742 :
this.env.search_scope = 'base';
- 5743 :
this.reset_search_filter();
- 5744 :
}
- 5745 :
- 5746 :
this.env.qsearch = null;
- 5747 :
this.env.search_request = null;
- 5748 :
this.env.search_id = null;
- 5749 :
this.select_all_mode = false;
- 5750 :
- 5751 :
this.enable_command('set-listmode', this.env.threads);
- 5752 :
};
- 5753 :
- 5754 :
this.set_searchscope = function(scope)
- 5755 :
{
- 5756 :
this.env.search_scope = scope;
- 5757 :
};
- 5758 :
- 5759 :
this.set_searchinterval = function(interval)
- 5760 :
{
- 5761 :
this.env.search_interval = interval;
- 5762 :
};
- 5763 :
- 5764 :
this.set_searchmods = function(mods)
- 5765 :
{
- 5766 :
var mbox = this.env.mailbox,
- 5767 :
scope = this.env.search_scope || 'base';
- 5768 :
- 5769 :
if (!this.env.search_mods)
- 5770 :
this.env.search_mods = {};
- 5771 :
- 5772 :
if (mbox)
- 5773 :
this.env.search_mods[mbox] = mods;
- 5774 :
- 5775 :
// MANTIS 0005913: Problème pour une recherche dans tous les dossiers
- 5776 :
if (mbox != '*')
- 5777 :
this.env.search_mods['*'] = mods;
- 5778 :
};
- 5779 :
- 5780 :
this.is_multifolder_listing = function()
- 5781 :
{
- 5782 :
return this.env.multifolder_listing !== undefined ? this.env.multifolder_listing :
- 5783 :
(this.env.search_request && (this.env.search_scope || 'base') != 'base');
- 5784 :
};
- 5785 :
- 5786 :
// action executed after mail is sent
- 5787 :
this.sent_successfully = function(type, msg, folders, save_error)
- 5788 :
{
- 5789 :
this.display_message(msg, type);
- 5790 :
this.compose_skip_unsavedcheck = true;
- 5791 :
- 5792 :
if (this.env.extwin) {
- 5793 :
if (!save_error)
- 5794 :
this.lock_form(this.gui_objects.messageform);
- 5795 :
- 5796 :
var filter = {task: 'mail', action: ''},
- 5797 :
rc = this.opener(false, filter) || this.opener(true, filter);
- 5798 :
- 5799 :
if (rc) {
- 5800 :
rc.display_message(msg, type);
- 5801 :
// refresh the folder where sent message was saved or replied message comes from
- 5802 :
if (folders && $.inArray(rc.env.mailbox, folders) >= 0) {
- 5803 :
rc.command('checkmail');
- 5804 :
}
- 5805 :
}
- 5806 :
- 5807 :
if (!save_error)
- 5808 :
setTimeout(function() { window.close(); }, 1000);
- 5809 :
}
- 5810 :
else if (!save_error) {
- 5811 :
// before redirect we need to wait some time for Chrome (#1486177)
- 5812 :
setTimeout(function() { ref.list_mailbox(); }, 500);
- 5813 :
}
- 5814 :
- 5815 :
if (save_error)
- 5816 :
this.env.is_sent = true;
- 5817 :
};
- 5818 :
- 5819 :
this.image_rotate = function()
- 5820 :
{
- 5821 :
var curr = this.image_style ? (this.image_style.rotate || 0) : 0;
- 5822 :
- 5823 :
this.image_style.rotate = curr > 180 ? 0 : curr + 90;
- 5824 :
this.apply_image_style();
- 5825 :
};
- 5826 :
- 5827 :
this.image_scale = function(prop)
- 5828 :
{
- 5829 :
var curr = this.image_style ? (this.image_style.scale || 1) : 1;
- 5830 :
- 5831 :
this.image_style.scale = Math.max(0.1, curr + 0.1 * (prop == '-' ? -1 : 1));
- 5832 :
this.apply_image_style();
- 5833 :
};
- 5834 :
- 5835 :
this.apply_image_style = function()
- 5836 :
{
- 5837 :
var style = [],
- 5838 :
head = $(this.gui_objects.messagepartframe).contents().find('head');
- 5839 :
- 5840 :
$('#image-style', head).remove();
- 5841 :
- 5842 :
$.each({scale: '', rotate: 'deg'}, function(i, v) {
- 5843 :
var val = ref.image_style[i];
- 5844 :
if (val)
- 5845 :
style.push(i + '(' + val + v + ')');
- 5846 :
});
- 5847 :
- 5848 :
if (style)
- 5849 :
head.append($('<style id="image-style">').text('img { transform: ' + style.join(' ') + '}'));
- 5850 :
};
- 5851 :
- 5852 :
// Update import dialog state
- 5853 :
this.import_state_set = function(state)
- 5854 :
{
- 5855 :
if (this.import_dialog) {
- 5856 :
this.import_state = state;
- 5857 :
- 5858 :
var button = $(this.import_dialog).parent().find('.ui-dialog-buttonset > button').first();
- 5859 :
- 5860 :
if (state != 'error') {
- 5861 :
// replace Import/Cancel buttons with Close button
- 5862 :
button.hide();
- 5863 :
button.next().text(this.gettext('close')).focus();
- 5864 :
}
- 5865 :
else {
- 5866 :
// activate the Import button
- 5867 :
button.prop('disabled', false);
- 5868 :
}
- 5869 :
}
- 5870 :
};
- 5871 :
- 5872 :
- 5873 :
/*********************************************************/
- 5874 :
/********* keyboard live-search methods *********/
- 5875 :
/*********************************************************/
- 5876 :
- 5877 :
// handler for keyboard events on address-fields
- 5878 :
this.ksearch_keydown = function(e, obj, props)
- 5879 :
{
- 5880 :
if (this.ksearch_timer)
- 5881 :
clearTimeout(this.ksearch_timer);
- 5882 :
- 5883 :
var key = rcube_event.get_keycode(e);
- 5884 :
- 5885 :
switch (key) {
- 5886 :
case 38: // arrow up
- 5887 :
case 40: // arrow down
- 5888 :
if (!this.ksearch_visible())
- 5889 :
return;
- 5890 :
- 5891 :
var dir = key == 38 ? 1 : 0,
- 5892 :
highlight = this.ksearch_pane.find('li.selected')[0];
- 5893 :
- 5894 :
if (!highlight)
- 5895 :
highlight = this.ksearch_pane.__ul.firstChild;
- 5896 :
- 5897 :
if (highlight)
- 5898 :
this.ksearch_select(dir ? highlight.previousSibling : highlight.nextSibling);
- 5899 :
- 5900 :
return rcube_event.cancel(e);
- 5901 :
- 5902 :
case 9: // tab
- 5903 :
if (rcube_event.get_modifier(e) == SHIFT_KEY || !this.ksearch_visible()) {
- 5904 :
this.ksearch_hide();
- 5905 :
return;
- 5906 :
}
- 5907 :
- 5908 :
case 13: // enter
- 5909 :
if (!this.ksearch_visible())
- 5910 :
return false;
- 5911 :
- 5912 :
// insert selected address and hide ksearch pane
- 5913 :
this.insert_recipient(this.ksearch_selected);
- 5914 :
this.ksearch_hide();
- 5915 :
- 5916 :
// Don't cancel on Tab, we want to jump to the next field (#5659)
- 5917 :
return key == 9 ? null : rcube_event.cancel(e);
- 5918 :
- 5919 :
case 27: // escape
- 5920 :
this.ksearch_hide();
- 5921 :
return;
- 5922 :
- 5923 :
case 37: // left
- 5924 :
case 39: // right
- 5925 :
return;
- 5926 :
}
- 5927 :
- 5928 :
// start timer
- 5929 :
this.ksearch_timer = setTimeout(function() { ref.ksearch_get_results(props); }, 200);
- 5930 :
this.ksearch_input = obj;
- 5931 :
- 5932 :
return true;
- 5933 :
};
- 5934 :
- 5935 :
this.ksearch_visible = function()
- 5936 :
{
- 5937 :
return this.ksearch_selected !== null && this.ksearch_selected !== undefined && this.ksearch_value;
- 5938 :
};
- 5939 :
- 5940 :
this.ksearch_select = function(node)
- 5941 :
{
- 5942 :
if (this.ksearch_pane && node) {
- 5943 :
this.ksearch_pane.find('li.selected').removeClass('selected').removeAttr('aria-selected');
- 5944 :
}
- 5945 :
- 5946 :
if (node) {
- 5947 :
$(node).addClass('selected').attr('aria-selected', 'true');
- 5948 :
this.ksearch_selected = node._rcm_id;
- 5949 :
$(this.ksearch_input).attr('aria-activedescendant', 'rcmkSearchItem' + this.ksearch_selected);
- 5950 :
}
- 5951 :
};
- 5952 :
- 5953 :
this.insert_recipient = function(id)
- 5954 :
{
- 5955 :
if (id === null || !this.env.contacts[id] || !this.ksearch_input)
- 5956 :
return;
- 5957 :
- 5958 :
var trigger = false, insert = '', delim = ', ',
- 5959 :
contact = this.env.contacts[id];
- 5960 :
- 5961 :
this.ksearch_destroy();
- 5962 :
- 5963 :
// insert all members of a group
- 5964 :
if (typeof contact === 'object' && contact.type == 'group' && !contact.email && contact.id) {
- 5965 :
// We wrap the group name with invisible markers to prevent from problems with group expanding (#7569)
- 5966 :
var name = '\u200b' + contact.name + '\u200b';
- 5967 :
insert = name + delim;
- 5968 :
this.group2expand[contact.id] = {name: name, input: this.ksearch_input};
- 5969 :
this.http_request('mail/group-expand', {_source: contact.source, _gid: contact.id}, false);
- 5970 :
}
- 5971 :
else if (typeof contact === 'object' && contact.name) {
- 5972 :
insert = contact.name + delim;
- 5973 :
trigger = true;
- 5974 :
}
- 5975 :
else if (typeof contact === 'string') {
- 5976 :
insert = contact + delim;
- 5977 :
trigger = true;
- 5978 :
}
- 5979 :
- 5980 :
this.ksearch_input_replace(this.ksearch_value, insert, null, trigger);
- 5981 :
- 5982 :
if (trigger) {
- 5983 :
this.triggerEvent('autocomplete_insert', {
- 5984 :
field: this.ksearch_input,
- 5985 :
insert: insert,
- 5986 :
data: contact,
- 5987 :
search: this.ksearch_value_last,
- 5988 :
result_type: 'person'
- 5989 :
});
- 5990 :
- 5991 :
this.ksearch_value_last = null;
- 5992 :
this.compose_type_activity++;
- 5993 :
}
- 5994 :
};
- 5995 :
- 5996 :
this.replace_group_recipients = function(id, recipients)
- 5997 :
{
- 5998 :
var data = this.group2expand[id];
- 5999 :
- 6000 :
if (data) {
- 6001 :
this.ksearch_input_replace(data.name, recipients, data.input);
- 6002 :
- 6003 :
this.triggerEvent('autocomplete_insert', {
- 6004 :
field: data.input,
- 6005 :
insert: recipients,
- 6006 :
data: data,
- 6007 :
search: this.ksearch_value_last,
- 6008 :
result_type: 'group'
- 6009 :
});
- 6010 :
- 6011 :
this.ksearch_value_last = null;
- 6012 :
this.group2expand[id] = null;
- 6013 :
this.compose_type_activity++;
- 6014 :
}
- 6015 :
};
- 6016 :
- 6017 :
// address search processor
- 6018 :
this.ksearch_get_results = function(props)
- 6019 :
{
- 6020 :
if (this.ksearch_pane && this.ksearch_pane.is(":visible"))
- 6021 :
this.ksearch_pane.hide();
- 6022 :
- 6023 :
// get string from cursor position back to the last comma or semicolon
- 6024 :
var q = this.ksearch_input_get(),
- 6025 :
min = this.env.autocomplete_min_length,
- 6026 :
data = this.ksearch_data;
- 6027 :
- 6028 :
// trim query string
- 6029 :
q = q.trim();
- 6030 :
- 6031 :
// Don't (re-)search if the last results are still active
- 6032 :
if (q == this.ksearch_value)
- 6033 :
return;
- 6034 :
- 6035 :
this.ksearch_destroy();
- 6036 :
- 6037 :
if (q.length && q.length < min) {
- 6038 :
if (!this.ksearch_info) {
- 6039 :
this.ksearch_info = this.display_message(this.get_label('autocompletechars').replace('$min', min));
- 6040 :
}
- 6041 :
return;
- 6042 :
}
- 6043 :
- 6044 :
var old_value = this.ksearch_value;
- 6045 :
this.ksearch_value = q;
- 6046 :
this.ksearch_value_last = q; // Group expansion clears ksearch_value before calling autocomplete_insert trigger, therefore store it in separate variable for later consumption.
- 6047 :
- 6048 :
// ...string is empty
- 6049 :
if (!q.length)
- 6050 :
return;
- 6051 :
- 6052 :
// ...new search value contains old one and previous search was not finished or its result was empty
- 6053 :
if (old_value && old_value.length && q.startsWith(old_value) && (!data || data.num <= 0) && this.env.contacts && !this.env.contacts.length)
- 6054 :
return;
- 6055 :
- 6056 :
var sources = props && props.sources ? props.sources : [''];
- 6057 :
var reqid = this.multi_thread_http_request({
- 6058 :
items: sources,
- 6059 :
threads: props && props.threads ? props.threads : 1,
- 6060 :
action: props && props.action ? props.action : 'mail/autocomplete',
- 6061 :
postdata: { _search:q, _source:'%s' },
- 6062 :
lock: this.display_message('searching', 'loading')
- 6063 :
});
- 6064 :
- 6065 :
this.ksearch_data = { id:reqid, sources:sources.slice(), num:sources.length };
- 6066 :
};
- 6067 :
- 6068 :
this.ksearch_query_results = function(results, search, reqid)
- 6069 :
{
- 6070 :
// trigger multi-thread http response callback
- 6071 :
this.multi_thread_http_response(results, reqid);
- 6072 :
- 6073 :
// search stopped in meantime?
- 6074 :
if (!this.ksearch_value)
- 6075 :
return;
- 6076 :
- 6077 :
// ignore this outdated search response
- 6078 :
if (this.ksearch_input && search != this.ksearch_value)
- 6079 :
return;
- 6080 :
- 6081 :
// display search results
- 6082 :
var i, id, len, ul, text, type, init,
- 6083 :
is_framed = this.is_framed(),
- 6084 :
value = this.ksearch_value,
- 6085 :
maxlen = this.env.autocomplete_max ? this.env.autocomplete_max : 15;
- 6086 :
- 6087 :
// create results pane if not present
- 6088 :
if (!this.ksearch_pane) {
- 6089 :
ul = $('<ul>');
- 6090 :
this.ksearch_pane = $('<div>')
- 6091 :
.attr({id: 'rcmKSearchpane', role: 'listbox', 'class': 'select-menu inline'})
- 6092 :
.css({position: 'absolute', 'z-index': 30000})
- 6093 :
.append(ul)
- 6094 :
.appendTo(is_framed ? parent.document.body : document.body);
- 6095 :
- 6096 :
this.ksearch_pane.__ul = ul[0];
- 6097 :
this.triggerEvent('autocomplete_create', {obj: this.ksearch_pane});
- 6098 :
}
- 6099 :
- 6100 :
ul = this.ksearch_pane.__ul;
- 6101 :
- 6102 :
// remove all search results or add to existing list if parallel search
- 6103 :
if (reqid && this.ksearch_pane.data('reqid') == reqid) {
- 6104 :
maxlen -= ul.childNodes.length;
- 6105 :
}
- 6106 :
else {
- 6107 :
this.ksearch_pane.data('reqid', reqid);
- 6108 :
init = 1;
- 6109 :
// reset content
- 6110 :
ul.innerHTML = '';
- 6111 :
this.env.contacts = [];
- 6112 :
- 6113 :
// Calculate the results pane position and size
- 6114 :
// Elastic: On small screen we use the width/position of the whole .ac-input element (input's parent)
- 6115 :
var is_composite_input = $('html').is('.layout-small,.layout-phone') && $(this.ksearch_input).parents('.ac-input').length == 1,
- 6116 :
input = is_composite_input ? $(this.ksearch_input).parents('.ac-input')[0] : $(this.ksearch_input)[0],
- 6117 :
pos = $(input).offset();
- 6118 :
- 6119 :
// ... consider scroll position
- 6120 :
pos.left -= $(document.documentElement).scrollLeft();
- 6121 :
pos.top -= $(document.documentElement).scrollTop();
- 6122 :
- 6123 :
// ... consider iframe position
- 6124 :
if (is_framed) {
- 6125 :
try {
- 6126 :
parent.$('iframe').each(function() {
- 6127 :
if (this.contentWindow == window) {
- 6128 :
var offset = $(this).offset();
- 6129 :
pos.left += offset.left;
- 6130 :
pos.top += offset.top;
- 6131 :
}
- 6132 :
});
- 6133 :
}
- 6134 :
catch(e) {}
- 6135 :
}
- 6136 :
- 6137 :
var w = $(is_framed ? parent : window).width(),
- 6138 :
input_width = $(input).outerWidth(),
- 6139 :
left = w - pos.left > 200 ? pos.left : w - 200,
- 6140 :
top = (pos.top + input.offsetHeight + 1),
- 6141 :
//PAMELA - Changement de la taille de l'input pour les grands écrans
- 6142 :
width = $(input).outerWidth() >= 400 ? $(input).outerWidth() : Math.min(400, w - left);
- 6143 :
- 6144 :
this.ksearch_pane.css({
- 6145 :
left: (is_composite_input ? pos.left : left) + 'px',
- 6146 :
top: top + 'px',
- 6147 :
maxWidth: (is_composite_input ? input_width : width) + 'px',
- 6148 :
minWidth: '200px',
- 6149 :
width: is_composite_input ? (input_width + 'px') : 'auto',
- 6150 :
display: 'none'
- 6151 :
});
- 6152 :
}
- 6153 :
- 6154 :
// add each result line to list
- 6155 :
if (results && (len = results.length)) {
- 6156 :
for (i=0; i < len && maxlen > 0; i++) {
- 6157 :
text = typeof results[i] === 'object' ? (results[i].display || results[i].name) : results[i];
- 6158 :
type = typeof results[i] === 'object' ? results[i].type : '';
- 6159 :
id = i + this.env.contacts.length;
- 6160 :
if (this.env.contacts.length && this.env.autocomplete_clean_duplicates) {
- 6161 :
if (this.env.contacts.find(contact => contact.name == text || contact.display == text)) {
- 6162 :
continue;
- 6163 :
}
- 6164 :
}
- 6165 :
$('<li>').attr({id: 'rcmkSearchItem' + id, role: 'option'})
- 6166 :
.html('<i class="icon"></i>' + this.quote_html(text.replace(new RegExp('('+RegExp.escape(value)+')', 'ig'), '##$1%%')).replace(/##([^%]+)%%/g, '<b>$1</b>'))
- 6167 :
.addClass(type || '')
- 6168 :
.appendTo(ul)
- 6169 :
.mouseover(function() { ref.ksearch_select(this); })
- 6170 :
.mouseup(function() { ref.ksearch_click(this); })
- 6171 :
.get(0)._rcm_id = id;
- 6172 :
maxlen -= 1;
- 6173 :
}
- 6174 :
}
- 6175 :
- 6176 :
if (ul.childNodes.length) {
- 6177 :
// set the right aria-* attributes to the input field
- 6178 :
$(this.ksearch_input)
- 6179 :
.attr({'aria-haspopup': 'true', 'aria-expanded': 'true', 'aria-owns': 'rcmKSearchpane'});
- 6180 :
- 6181 :
this.ksearch_pane.show();
- 6182 :
- 6183 :
// select the first
- 6184 :
if (!this.env.contacts.length) {
- 6185 :
this.ksearch_select($('li', ul)[0]);
- 6186 :
}
- 6187 :
}
- 6188 :
- 6189 :
if (len)
- 6190 :
this.env.contacts = this.env.contacts.concat(results);
- 6191 :
- 6192 :
if (this.ksearch_data.id == reqid)
- 6193 :
this.ksearch_data.num--;
- 6194 :
};
- 6195 :
- 6196 :
// Getter for input value
- 6197 :
// returns a string from the last comma to current cursor position
- 6198 :
this.ksearch_input_get = function()
- 6199 :
{
- 6200 :
if (!this.ksearch_input)
- 6201 :
return '';
- 6202 :
- 6203 :
var cp = this.get_caret_pos(this.ksearch_input);
- 6204 :
- 6205 :
return this.ksearch_input.value.substr(0, cp).split(/[,;]/).pop();
- 6206 :
};
- 6207 :
- 6208 :
// Setter for input value
- 6209 :
// replaces 'from' string with 'to' and sets cursor position at the end
- 6210 :
this.ksearch_input_replace = function(from, to, input, trigger)
- 6211 :
{
- 6212 :
if (!this.ksearch_input && !input)
- 6213 :
return;
- 6214 :
- 6215 :
if (!input)
- 6216 :
input = this.ksearch_input;
- 6217 :
- 6218 :
var cpos = this.get_caret_pos(input),
- 6219 :
p = input.value.lastIndexOf(from, cpos),
- 6220 :
pre = input.value.substring(0, p),
- 6221 :
end = input.value.substring(p + from.length, input.value.length);
- 6222 :
- 6223 :
input.value = pre + to + end;
- 6224 :
- 6225 :
// set caret to insert pos
- 6226 :
this.set_caret_pos(input, cpos + to.length - from.length);
- 6227 :
- 6228 :
// run onchange action on the element
- 6229 :
$(input).trigger('change', [true, trigger]);
- 6230 :
};
- 6231 :
- 6232 :
this.ksearch_click = function(node)
- 6233 :
{
- 6234 :
if (this.ksearch_input)
- 6235 :
this.ksearch_input.focus();
- 6236 :
- 6237 :
this.insert_recipient(node._rcm_id);
- 6238 :
this.ksearch_hide();
- 6239 :
};
- 6240 :
- 6241 :
this.ksearch_blur = function()
- 6242 :
{
- 6243 :
if (this.ksearch_timer)
- 6244 :
clearTimeout(this.ksearch_timer);
- 6245 :
- 6246 :
this.ksearch_input = null;
- 6247 :
this.ksearch_hide();
- 6248 :
};
- 6249 :
- 6250 :
this.ksearch_hide = function()
- 6251 :
{
- 6252 :
this.ksearch_selected = null;
- 6253 :
this.ksearch_value = '';
- 6254 :
- 6255 :
if (this.ksearch_pane)
- 6256 :
this.ksearch_pane.hide();
- 6257 :
- 6258 :
$(this.ksearch_input)
- 6259 :
.attr({'aria-haspopup': 'false', 'aria-expanded': 'false'})
- 6260 :
.removeAttr('aria-activedescendant')
- 6261 :
.removeAttr('aria-owns');
- 6262 :
- 6263 :
this.ksearch_destroy();
- 6264 :
};
- 6265 :
- 6266 :
// Clears autocomplete data/requests
- 6267 :
this.ksearch_destroy = function()
- 6268 :
{
- 6269 :
if (this.ksearch_data)
- 6270 :
this.multi_thread_request_abort(this.ksearch_data.id);
- 6271 :
- 6272 :
if (this.ksearch_info)
- 6273 :
this.hide_message(this.ksearch_info);
- 6274 :
- 6275 :
if (this.ksearch_msg)
- 6276 :
this.hide_message(this.ksearch_msg);
- 6277 :
- 6278 :
this.ksearch_data = null;
- 6279 :
this.ksearch_info = null;
- 6280 :
this.ksearch_msg = null;
- 6281 :
};
- 6282 :
- 6283 :
- 6284 :
/*********************************************************/
- 6285 :
/********* address book methods *********/
- 6286 :
/*********************************************************/
- 6287 :
- 6288 :
this.contactlist_select = function(list)
- 6289 :
{
- 6290 :
if (this.preview_timer)
- 6291 :
clearTimeout(this.preview_timer);
- 6292 :
- 6293 :
var id, targets,
- 6294 :
groupcount = 0,
- 6295 :
writable = false,
- 6296 :
deletable = false,
- 6297 :
copy_writable = false,
- 6298 :
selected = list.get_selection().length,
- 6299 :
source = this.env.source ? this.env.address_sources[this.env.source] : null;
- 6300 :
- 6301 :
// we don't have dblclick handler here, so use 50 instead of this.dblclick_time
- 6302 :
if (this.env.contentframe && !list.multi_selecting && (id = list.get_single_selection()))
- 6303 :
this.preview_timer = setTimeout(function() { ref.load_contact(id, 'show'); }, this.preview_delay_click);
- 6304 :
else if (this.env.contentframe)
- 6305 :
this.show_contentframe(false);
- 6306 :
- 6307 :
if (selected) {
- 6308 :
list.draggable = false;
- 6309 :
- 6310 :
// no source = search result, we'll need to detect if any of
- 6311 :
// selected contacts are in writable addressbook to enable edit/delete
- 6312 :
// we'll also need to know sources used in selection for copy
- 6313 :
// and group-addmember operations (drag&drop)
- 6314 :
this.env.selection_sources = [];
- 6315 :
- 6316 :
if (source) {
- 6317 :
this.env.selection_sources.push(this.env.source);
- 6318 :
}
- 6319 :
- 6320 :
$.each(list.get_selection(), function(i, v) {
- 6321 :
var book, sid, contact = list.data[v];
- 6322 :
if (!source) {
- 6323 :
sid = String(v).replace(/^[^-]+-/, '');
- 6324 :
book = sid ? ref.env.address_sources[sid] : null;
- 6325 :
- 6326 :
if (book) {
- 6327 :
writable = writable || (!book.readonly && !contact.readonly);
- 6328 :
deletable = deletable || book.deletable === true;
- 6329 :
ref.env.selection_sources.push(sid);
- 6330 :
}
- 6331 :
}
- 6332 :
else {
- 6333 :
writable = writable || (!source.readonly && !contact.readonly);
- 6334 :
deletable = deletable || source.deletable === true;
- 6335 :
}
- 6336 :
- 6337 :
if (contact._type != 'group')
- 6338 :
list.draggable = true;
- 6339 :
});
- 6340 :
- 6341 :
this.env.selection_sources = $.unique(this.env.selection_sources);
- 6342 :
- 6343 :
if (source && source.groups)
- 6344 :
$.each(this.env.contactgroups, function() { if (this.source === ref.env.source) groupcount++; });
- 6345 :
- 6346 :
targets = $.map(this.env.address_sources, function(v, i) { return v.readonly ? null : i; });
- 6347 :
copy_writable = $.grep(targets, function(v) { return jQuery.inArray(v, ref.env.selection_sources) < 0; }).length > 0;
- 6348 :
}
- 6349 :
- 6350 :
// if a group is currently selected, and there is at least one contact selected
- 6351 :
// we can enable the group-remove-selected command
- 6352 :
this.enable_command('group-assign-selected', groupcount > 0 && writable);
- 6353 :
this.enable_command('group-remove-selected', this.env.group && writable);
- 6354 :
this.enable_command('print', 'qrcode', selected == 1);
- 6355 :
this.enable_command('export-selected', selected > 0);
- 6356 :
this.enable_command('edit', id && writable);
- 6357 :
this.enable_command('delete', 'move', writable || deletable);
- 6358 :
this.enable_command('copy', copy_writable);
- 6359 :
- 6360 :
return false;
- 6361 :
};
- 6362 :
- 6363 :
this.list_contacts = function(src, group, page, search)
- 6364 :
{
- 6365 :
var win, folder, index = -1, url = {},
- 6366 :
refresh = src === undefined && group === undefined && page === undefined,
- 6367 :
target = window;
- 6368 :
- 6369 :
if (!src)
- 6370 :
src = this.env.source;
- 6371 :
- 6372 :
if (refresh)
- 6373 :
group = this.env.group;
- 6374 :
- 6375 :
if (src != this.env.source) {
- 6376 :
page = this.env.current_page = 1;
- 6377 :
this.reset_qsearch();
- 6378 :
}
- 6379 :
else if (!refresh && group != this.env.group)
- 6380 :
page = this.env.current_page = 1;
- 6381 :
- 6382 :
if (this.env.search_id)
- 6383 :
folder = 'S'+this.env.search_id;
- 6384 :
else if (!this.env.search_request)
- 6385 :
folder = group ? 'G'+src+group : src;
- 6386 :
- 6387 :
this.env.source = this.env.last_source = src;
- 6388 :
this.env.group = this.env.last_group = group;
- 6389 :
- 6390 :
// truncate groups listing stack
- 6391 :
$.each(this.env.address_group_stack, function(i, v) {
- 6392 :
if (ref.env.group == v.id) {
- 6393 :
index = i;
- 6394 :
return false;
- 6395 :
}
- 6396 :
});
- 6397 :
- 6398 :
this.env.address_group_stack = index < 0 ? [] : this.env.address_group_stack.slice(0, index);
- 6399 :
- 6400 :
// remove cached contact group selector
- 6401 :
this.destroy_entity_selector('contactgroup-selector');
- 6402 :
- 6403 :
// make sure the current group is on top of the stack
- 6404 :
if (this.env.group) {
- 6405 :
if (!search) search = {};
- 6406 :
search.id = this.env.group;
- 6407 :
this.env.address_group_stack.push(search);
- 6408 :
- 6409 :
// mark the first group on the stack as selected in the directory list
- 6410 :
folder = 'G'+src+this.env.address_group_stack[0].id;
- 6411 :
}
- 6412 :
else if (this.gui_objects.addresslist_title) {
- 6413 :
$(this.gui_objects.addresslist_title).text(this.get_label('contacts'));
- 6414 :
}
- 6415 :
- 6416 :
if (!this.env.search_id)
- 6417 :
this.select_folder(folder, '', true);
- 6418 :
- 6419 :
// load contacts remotely
- 6420 :
if (this.gui_objects.contactslist) {
- 6421 :
this.list_contacts_remote(src, group, page);
- 6422 :
return;
- 6423 :
}
- 6424 :
- 6425 :
if (win = this.get_frame_window(this.env.contentframe)) {
- 6426 :
target = win;
- 6427 :
url._framed = 1;
- 6428 :
}
- 6429 :
- 6430 :
if (group)
- 6431 :
url._gid = group;
- 6432 :
if (page)
- 6433 :
url._page = page;
- 6434 :
if (src)
- 6435 :
url._source = src;
- 6436 :
- 6437 :
// also send search request to get the correct listing
- 6438 :
if (this.env.search_request)
- 6439 :
url._search = this.env.search_request;
- 6440 :
- 6441 :
this.set_busy(true, 'loading');
- 6442 :
this.location_href(url, target);
- 6443 :
};
- 6444 :
- 6445 :
// send remote request to load contacts list
- 6446 :
this.list_contacts_remote = function(src, group, page)
- 6447 :
{
- 6448 :
// clear message list first
- 6449 :
this.list_contacts_clear();
- 6450 :
- 6451 :
// send request to server
- 6452 :
var url = {}, lock = this.set_busy(true, 'loading');
- 6453 :
- 6454 :
if (src)
- 6455 :
url._source = src;
- 6456 :
if (page)
- 6457 :
url._page = page;
- 6458 :
if (group)
- 6459 :
url._gid = group;
- 6460 :
- 6461 :
this.env.source = src;
- 6462 :
this.env.group = group;
- 6463 :
- 6464 :
// also send search request to get the right records
- 6465 :
if (this.env.search_request)
- 6466 :
url._search = this.env.search_request;
- 6467 :
- 6468 :
this.http_request(this.env.task == 'mail' ? 'list-contacts' : 'list', url, lock);
- 6469 :
- 6470 :
if (this.env.task != 'mail')
- 6471 :
this.update_state({_source: src, _page: page && page > 1 ? page : null, _gid: group});
- 6472 :
};
- 6473 :
- 6474 :
this.list_contacts_clear = function()
- 6475 :
{
- 6476 :
this.contact_list.data = {};
- 6477 :
this.contact_list.clear(true);
- 6478 :
this.show_contentframe(false);
- 6479 :
this.enable_command('delete', 'move', 'copy', 'print', false);
- 6480 :
};
- 6481 :
- 6482 :
this.set_group_prop = function(prop)
- 6483 :
{
- 6484 :
if (this.gui_objects.addresslist_title) {
- 6485 :
var boxtitle = $(this.gui_objects.addresslist_title).html(''); // clear contents
- 6486 :
- 6487 :
// add link to pop back to parent group
- 6488 :
if (this.env.address_group_stack.length > 1
- 6489 :
|| (this.env.address_group_stack.length == 1 && this.env.address_group_stack[0].search_request)
- 6490 :
) {
- 6491 :
var link = $('<a href="#list">...</a>')
- 6492 :
.attr({title: this.get_label('uponelevel'), 'class': 'poplink'})
- 6493 :
.click(function() { return ref.command('popgroup', '', this); });
- 6494 :
- 6495 :
boxtitle.append(link).append(' » ');
- 6496 :
}
- 6497 :
- 6498 :
boxtitle.append($('<span>').text(prop ? prop.name : this.get_label('contacts')));
- 6499 :
}
- 6500 :
};
- 6501 :
- 6502 :
// load contact record
- 6503 :
this.load_contact = function(cid, action, framed)
- 6504 :
{
- 6505 :
var win, url = {}, target = window,
- 6506 :
rec = this.contact_list ? this.contact_list.data[cid] : null;
- 6507 :
- 6508 :
if (win = this.get_frame_window(this.env.contentframe)) {
- 6509 :
url._framed = 1;
- 6510 :
target = win;
- 6511 :
this.show_contentframe(true);
- 6512 :
- 6513 :
// load dummy content, unselect selected row(s)
- 6514 :
if (!cid)
- 6515 :
this.contact_list.clear_selection();
- 6516 :
- 6517 :
this.enable_command('export-selected', 'print', rec && rec._type != 'group');
- 6518 :
}
- 6519 :
else if (framed)
- 6520 :
return false;
- 6521 :
- 6522 :
if (action && (cid || action == 'add') && !this.drag_active) {
- 6523 :
if (this.env.group)
- 6524 :
url._gid = this.env.group;
- 6525 :
- 6526 :
if (this.env.search_request)
- 6527 :
url._search = this.env.search_request;
- 6528 :
- 6529 :
if (cid)
- 6530 :
url._cid = this.preview_id = cid;
- 6531 :
- 6532 :
url._action = action;
- 6533 :
url._source = this.env.source;
- 6534 :
- 6535 :
this.location_href(url, target, true);
- 6536 :
}
- 6537 :
- 6538 :
return true;
- 6539 :
};
- 6540 :
- 6541 :
// add/delete member to/from the group
- 6542 :
this.group_member_change = function(what, cid, source, gid)
- 6543 :
{
- 6544 :
if (what != 'add')
- 6545 :
what = 'del';
- 6546 :
- 6547 :
var lock = this.display_message(what == 'add' ? 'addingmember' : 'removingmember', 'loading'),
- 6548 :
post_data = {_cid: cid, _source: source, _gid: gid};
- 6549 :
- 6550 :
this.http_post('group-'+what+'members', post_data, lock);
- 6551 :
};
- 6552 :
- 6553 :
this.contacts_drag_menu = function(e, to)
- 6554 :
{
- 6555 :
var dest = to.type == 'group' ? to.source : to.id,
- 6556 :
source = this.env.source;
- 6557 :
- 6558 :
if (!this.env.address_sources[dest] || this.env.address_sources[dest].readonly)
- 6559 :
return true;
- 6560 :
- 6561 :
// search result may contain contacts from many sources, but if there is only one...
- 6562 :
if (source == '' && this.env.selection_sources.length == 1)
- 6563 :
source = this.env.selection_sources[0];
- 6564 :
- 6565 :
if (to.type == 'group' && dest == source) {
- 6566 :
var cid = this.contact_list.get_selection().join(',');
- 6567 :
this.group_member_change('add', cid, dest, to.id);
- 6568 :
return true;
- 6569 :
}
- 6570 :
// move action is not possible, "redirect" to copy if menu wasn't requested
- 6571 :
else if (!this.commands.move && rcube_event.get_modifier(e) != SHIFT_KEY) {
- 6572 :
this.copy_contacts(to);
- 6573 :
return true;
- 6574 :
}
- 6575 :
- 6576 :
return this.drag_menu(e, to);
- 6577 :
};
- 6578 :
- 6579 :
// copy contact(s) to the specified target (group or directory)
- 6580 :
this.copy_contacts = function(to, event, cid)
- 6581 :
{
- 6582 :
if (!to) {
- 6583 :
cid = this.contact_list.get_selection();
- 6584 :
return this.addressbook_selector(event, function(to, obj) {
- 6585 :
var to = $(obj).data('source') ? ref.env.contactgroups['G' + $(obj).data('source') + $(obj).data('gid')] : ref.env.address_sources[to];
- 6586 :
ref.copy_contacts(to, null, cid);
- 6587 :
});
- 6588 :
}
- 6589 :
- 6590 :
var dest = to.type == 'group' ? to.source : to.id,
- 6591 :
source = this.env.source,
- 6592 :
group = this.env.group ? this.env.group : '';
- 6593 :
- 6594 :
cid = cid ? cid.join(',') : this.contact_list.get_selection().join(',');
- 6595 :
- 6596 :
if (!cid || !this.env.address_sources[dest] || this.env.address_sources[dest].readonly)
- 6597 :
return;
- 6598 :
- 6599 :
// search result may contain contacts from many sources, but if there is only one...
- 6600 :
if (source == '' && this.env.selection_sources.length == 1)
- 6601 :
source = this.env.selection_sources[0];
- 6602 :
- 6603 :
// target is a group
- 6604 :
if (to.type == 'group') {
- 6605 :
if (dest == source)
- 6606 :
return;
- 6607 :
- 6608 :
var lock = this.display_message('copyingcontact', 'loading'),
- 6609 :
post_data = {_cid: cid, _source: this.env.source, _to: dest, _togid: to.id, _gid: group};
- 6610 :
- 6611 :
this.http_post('copy', post_data, lock);
- 6612 :
}
- 6613 :
// target is an addressbook
- 6614 :
else if (to.id != source) {
- 6615 :
var lock = this.display_message('copyingcontact', 'loading'),
- 6616 :
post_data = {_cid: cid, _source: this.env.source, _to: to.id, _gid: group};
- 6617 :
- 6618 :
this.http_post('copy', post_data, lock);
- 6619 :
}
- 6620 :
};
- 6621 :
- 6622 :
// move contact(s) to the specified target (group or directory)
- 6623 :
this.move_contacts = function(to, event, cid)
- 6624 :
{
- 6625 :
if (!to) {
- 6626 :
cid = this.contact_list.get_selection();
- 6627 :
return this.addressbook_selector(event, function(to, obj) {
- 6628 :
var to = $(obj).data('source') ? ref.env.contactgroups['G' + $(obj).data('source') + $(obj).data('gid')] : ref.env.address_sources[to];
- 6629 :
ref.move_contacts(to, null, cid);
- 6630 :
});
- 6631 :
}
- 6632 :
- 6633 :
var dest = to.type == 'group' ? to.source : to.id,
- 6634 :
source = this.env.source,
- 6635 :
group = this.env.group ? this.env.group : '';
- 6636 :
- 6637 :
if (!this.env.address_sources[dest] || this.env.address_sources[dest].readonly)
- 6638 :
return;
- 6639 :
- 6640 :
if (!cid)
- 6641 :
cid = this.contact_list.get_selection();
- 6642 :
- 6643 :
// search result may contain contacts from many sources, but if there is only one...
- 6644 :
if (source == '' && this.env.selection_sources.length == 1)
- 6645 :
source = this.env.selection_sources[0];
- 6646 :
- 6647 :
if (to.type == 'group') {
- 6648 :
if (dest == source)
- 6649 :
return;
- 6650 :
- 6651 :
this._with_selected_contacts('move', {_to: dest, _togid: to.id, _cid: cid});
- 6652 :
}
- 6653 :
// target is an addressbook
- 6654 :
else if (to.id != source)
- 6655 :
this._with_selected_contacts('move', {_to: to.id, _cid: cid});
- 6656 :
};
- 6657 :
- 6658 :
// delete contact(s)
- 6659 :
this.delete_contacts = function()
- 6660 :
{
- 6661 :
var undelete = this.env.source && this.env.address_sources[this.env.source].undelete;
- 6662 :
- 6663 :
if (undelete) {
- 6664 :
this._with_selected_contacts('delete', {_cid: this.contact_list.get_selection()});
- 6665 :
}
- 6666 :
else {
- 6667 :
var cid = this.contact_list.get_selection();
- 6668 :
this.confirm_dialog(this.get_label('deletecontactconfirm'), 'delete', function() {
- 6669 :
ref._with_selected_contacts('delete', {_cid: cid});
- 6670 :
});
- 6671 :
}
- 6672 :
};
- 6673 :
- 6674 :
this._with_selected_contacts = function(action, post_data)
- 6675 :
{
- 6676 :
var selection = post_data._cid;
- 6677 :
- 6678 :
// exit if no contact specified or if selection is empty
- 6679 :
if (!selection.length && !this.env.cid)
- 6680 :
return;
- 6681 :
- 6682 :
var n, a_cids = [],
- 6683 :
label = action == 'delete' ? 'contactdeleting' : 'movingcontact',
- 6684 :
lock = this.display_message(label, 'loading'),
- 6685 :
display_next = this.check_display_next();
- 6686 :
- 6687 :
if (this.env.cid)
- 6688 :
a_cids.push(this.env.cid);
- 6689 :
else {
- 6690 :
for (n=0; n<selection.length; n++) {
- 6691 :
id = selection[n];
- 6692 :
a_cids.push(id);
- 6693 :
this.contact_list.remove_row(id, display_next && n == selection.length-1);
- 6694 :
}
- 6695 :
- 6696 :
if (!display_next)
- 6697 :
this.contact_list.clear_selection();
- 6698 :
}
- 6699 :
- 6700 :
if (!post_data)
- 6701 :
post_data = {};
- 6702 :
- 6703 :
post_data._source = this.env.source;
- 6704 :
post_data._from = this.env.action;
- 6705 :
post_data._cid = a_cids.join(',');
- 6706 :
- 6707 :
if (this.env.group)
- 6708 :
post_data._gid = this.env.group;
- 6709 :
- 6710 :
// also send search request to get the right records from the next page
- 6711 :
if (this.env.search_request)
- 6712 :
post_data._search = this.env.search_request;
- 6713 :
- 6714 :
// send request to server
- 6715 :
this.http_post(action, post_data, lock)
- 6716 :
- 6717 :
return true;
- 6718 :
};
- 6719 :
- 6720 :
// update a contact record in the list
- 6721 :
this.update_contact_row = function(cid, cols_arr, newcid, source, data)
- 6722 :
{
- 6723 :
var list = this.contact_list;
- 6724 :
- 6725 :
cid = this.html_identifier(cid);
- 6726 :
- 6727 :
// when in searching mode, concat cid with the source name
- 6728 :
if (!list.rows[cid]) {
- 6729 :
cid = cid + '-' + source;
- 6730 :
if (newcid)
- 6731 :
newcid = newcid + '-' + source;
- 6732 :
}
- 6733 :
- 6734 :
list.update_row(cid, cols_arr, newcid, true);
- 6735 :
list.data[cid] = data;
- 6736 :
};
- 6737 :
- 6738 :
// add row to contacts list
- 6739 :
this.add_contact_row = function(cid, cols, classes, data)
- 6740 :
{
- 6741 :
if (!this.gui_objects.contactslist)
- 6742 :
return false;
- 6743 :
- 6744 :
var c, col, list = this.contact_list,
- 6745 :
row = { cols:[] };
- 6746 :
- 6747 :
row.id = 'rcmrow' + this.html_identifier(cid);
- 6748 :
row.className = 'contact ' + (classes || '');
- 6749 :
- 6750 :
if (list.in_selection(cid))
- 6751 :
row.className += ' selected';
- 6752 :
- 6753 :
// add each submitted col
- 6754 :
for (c in cols) {
- 6755 :
col = {};
- 6756 :
col.className = String(c).toLowerCase();
- 6757 :
col.innerHTML = cols[c];
- 6758 :
row.cols.push(col);
- 6759 :
}
- 6760 :
- 6761 :
// store data in list member
- 6762 :
list.data[cid] = data;
- 6763 :
list.insert_row(row);
- 6764 :
- 6765 :
this.enable_command('export', list.rowcount > 0);
- 6766 :
};
- 6767 :
- 6768 :
this.init_contact_form = function()
- 6769 :
{
- 6770 :
var col;
- 6771 :
- 6772 :
if (this.env.coltypes) {
- 6773 :
this.set_photo_actions($('#ff_photo').val());
- 6774 :
for (col in this.env.coltypes)
- 6775 :
this.init_edit_field(col, null);
- 6776 :
}
- 6777 :
- 6778 :
$('.contactfieldgroup .row a.deletebutton').click(function() {
- 6779 :
ref.delete_edit_field(this);
- 6780 :
return false;
- 6781 :
});
- 6782 :
- 6783 :
$('select.addfieldmenu').change(function() {
- 6784 :
ref.insert_edit_field($(this).val(), $(this).attr('rel'), this);
- 6785 :
this.selectedIndex = 0;
- 6786 :
});
- 6787 :
- 6788 :
// enable date pickers on date fields
- 6789 :
if ($.datepicker && this.env.date_format) {
- 6790 :
$.datepicker.setDefaults({
- 6791 :
dateFormat: this.env.date_format,
- 6792 :
changeMonth: true,
- 6793 :
changeYear: true,
- 6794 :
yearRange: '-120:+10',
- 6795 :
showOtherMonths: true,
- 6796 :
selectOtherMonths: true
- 6797 :
});
- 6798 :
$('input.datepicker').datepicker();
- 6799 :
}
- 6800 :
- 6801 :
// Submit search form on Enter
- 6802 :
if (this.env.action == 'search')
- 6803 :
$(this.gui_objects.editform).append($('<input type="submit">').hide())
- 6804 :
.submit(function() { $('input.mainaction').click(); return false; });
- 6805 :
};
- 6806 :
- 6807 :
// group creation dialog
- 6808 :
this.group_create = function()
- 6809 :
{
- 6810 :
var input = $('<input>').attr({type: 'text', 'data-submit': 'true'}),
- 6811 :
content = $('<label>').text(this.get_label('namex')).append(input),
- 6812 :
source = this.env.source;
- 6813 :
- 6814 :
this.simple_dialog(content, 'newgroup', function() {
- 6815 :
var name;
- 6816 :
if (name = input.val()) {
- 6817 :
ref.http_post('group-create', {_source: source, _name: name},
- 6818 :
ref.set_busy(true, 'loading'));
- 6819 :
return true;
- 6820 :
}
- 6821 :
});
- 6822 :
};
- 6823 :
- 6824 :
// group rename dialog
- 6825 :
this.group_rename = function()
- 6826 :
{
- 6827 :
if (!this.env.group)
- 6828 :
return;
- 6829 :
- 6830 :
var group_name = this.env.contactgroups['G' + this.env.source + this.env.group].name,
- 6831 :
input = $('<input>').attr({type: 'text', 'data-submit': 'true'}).val(group_name),
- 6832 :
content = $('<label>').text(this.get_label('namex')).append(input),
- 6833 :
source = this.env.source,
- 6834 :
group = this.env.group;
- 6835 :
- 6836 :
this.simple_dialog(content, 'grouprename', function() {
- 6837 :
var name;
- 6838 :
if ((name = input.val()) && name != group_name) {
- 6839 :
ref.http_post('group-rename', {_source: source, _gid: group, _name: name},
- 6840 :
ref.set_busy(true, 'loading'));
- 6841 :
return true;
- 6842 :
}
- 6843 :
});
- 6844 :
};
- 6845 :
- 6846 :
this.group_delete = function()
- 6847 :
{
- 6848 :
if (this.env.group) {
- 6849 :
var group = this.env.group;
- 6850 :
this.confirm_dialog(this.get_label('deletegroupconfirm'), 'delete', function() {
- 6851 :
var lock = ref.set_busy(true, 'groupdeleting');
- 6852 :
ref.http_post('group-delete', {_source: ref.env.source, _gid: group}, lock);
- 6853 :
});
- 6854 :
}
- 6855 :
};
- 6856 :
- 6857 :
// callback from server upon group-delete command
- 6858 :
this.remove_group_item = function(prop)
- 6859 :
{
- 6860 :
var key = 'G' + prop.source + prop.id;
- 6861 :
- 6862 :
if (this.treelist.remove(key)) {
- 6863 :
// make sure there is no cached address book or contact group selectors
- 6864 :
this.destroy_entity_selector('addressbook-selector');
- 6865 :
this.destroy_entity_selector('contactgroup-selector');
- 6866 :
- 6867 :
this.triggerEvent('group_delete', { source:prop.source, id:prop.id });
- 6868 :
delete this.env.contactfolders[key];
- 6869 :
delete this.env.contactgroups[key];
- 6870 :
}
- 6871 :
- 6872 :
if (prop.source == this.env.source && prop.id == this.env.group)
- 6873 :
this.list_contacts(prop.source, 0);
- 6874 :
};
- 6875 :
- 6876 :
//assign selected contacts to a group
- 6877 :
this.group_assign_selected = function(props, obj, event)
- 6878 :
{
- 6879 :
var cid = ref.contact_list.get_selection();
- 6880 :
var source = ref.env.source;
- 6881 :
this.contactgroup_selector(event, function(to) { ref.group_member_change('add', cid, source, to); });
- 6882 :
};
- 6883 :
- 6884 :
//remove selected contacts from current active group
- 6885 :
this.group_remove_selected = function()
- 6886 :
{
- 6887 :
this.http_post('group-delmembers', {_cid: this.contact_list.get_selection(),
- 6888 :
_source: this.env.source, _gid: this.env.group});
- 6889 :
};
- 6890 :
- 6891 :
//callback after deleting contact(s) from current group
- 6892 :
this.remove_group_contacts = function(props)
- 6893 :
{
- 6894 :
if (this.env.group !== undefined && (this.env.group === props.gid)) {
- 6895 :
var n, selection = this.contact_list.get_selection(),
- 6896 :
display_next= this.check_display_next();
- 6897 :
- 6898 :
for (n=0; n<selection.length; n++) {
- 6899 :
id = selection[n];
- 6900 :
this.contact_list.remove_row(id, display_next && n == selection.length-1);
- 6901 :
}
- 6902 :
- 6903 :
if (!display_next)
- 6904 :
this.contact_list.clear_selection();
- 6905 :
}
- 6906 :
};
- 6907 :
- 6908 :
// callback for creating a new contact group
- 6909 :
this.insert_contact_group = function(prop)
- 6910 :
{
- 6911 :
prop.type = 'group';
- 6912 :
- 6913 :
var key = 'G'+prop.source+prop.id,
- 6914 :
link = $('<a>').attr({href: '#', rel: prop.source + ':' + prop.id})
- 6915 :
.click(function() { return ref.command('listgroup', prop, this); })
- 6916 :
.text(prop.name);
- 6917 :
- 6918 :
this.env.contactfolders[key] = this.env.contactgroups[key] = prop;
- 6919 :
this.treelist.insert({ id:key, html:link, classes:['contactgroup'] }, prop.source, 'contactgroup');
- 6920 :
- 6921 :
// make sure there is no cached address book or contact group selectors
- 6922 :
this.destroy_entity_selector('addressbook-selector');
- 6923 :
this.destroy_entity_selector('contactgroup-selector');
- 6924 :
- 6925 :
this.triggerEvent('group_insert', { id:prop.id, source:prop.source, name:prop.name, li:this.treelist.get_item(key) });
- 6926 :
};
- 6927 :
- 6928 :
// callback for renaming a contact group
- 6929 :
this.update_contact_group = function(prop)
- 6930 :
{
- 6931 :
var key = 'G'+prop.source+prop.id,
- 6932 :
newnode = {};
- 6933 :
- 6934 :
// group ID has changed, replace link node and identifiers
- 6935 :
if (prop.newid) {
- 6936 :
var newkey = 'G'+prop.source+prop.newid,
- 6937 :
newprop = $.extend({}, prop);
- 6938 :
- 6939 :
this.env.contactfolders[newkey] = this.env.contactfolders[key];
- 6940 :
this.env.contactfolders[newkey].id = prop.newid;
- 6941 :
this.env.group = prop.newid;
- 6942 :
- 6943 :
delete this.env.contactfolders[key];
- 6944 :
delete this.env.contactgroups[key];
- 6945 :
- 6946 :
newprop.id = prop.newid;
- 6947 :
newprop.type = 'group';
- 6948 :
- 6949 :
newnode.id = newkey;
- 6950 :
newnode.html = $('<a>').attr({href: '#', rel: prop.source + ':' + prop.newid})
- 6951 :
.click(function() { return ref.command('listgroup', newprop, this); })
- 6952 :
.text(prop.name);
- 6953 :
}
- 6954 :
// update displayed group name
- 6955 :
else {
- 6956 :
$(this.treelist.get_item(key)).children().first().text(prop.name);
- 6957 :
this.env.contactfolders[key].name = this.env.contactgroups[key].name = prop.name;
- 6958 :
- 6959 :
if (prop.source == this.env.source && prop.id == this.env.group)
- 6960 :
this.set_group_prop(prop);
- 6961 :
}
- 6962 :
- 6963 :
// update list node and re-sort it
- 6964 :
this.treelist.update(key, newnode, true);
- 6965 :
- 6966 :
// make sure there is no cached address book or contact group selectors
- 6967 :
this.destroy_entity_selector('addressbook-selector');
- 6968 :
this.destroy_entity_selector('contactgroup-selector');
- 6969 :
- 6970 :
this.triggerEvent('group_update', { id:prop.id, source:prop.source, name:prop.name, li:this.treelist.get_item(key), newid:prop.newid });
- 6971 :
};
- 6972 :
- 6973 :
this.update_group_commands = function()
- 6974 :
{
- 6975 :
var source = this.env.source != '' ? this.env.address_sources[this.env.source] : null,
- 6976 :
supported = source && source.groups && !source.readonly;
- 6977 :
- 6978 :
this.enable_command('group-create', supported);
- 6979 :
this.enable_command('group-rename', 'group-delete', supported && this.env.group);
- 6980 :
};
- 6981 :
- 6982 :
this.init_edit_field = function(col, elem)
- 6983 :
{
- 6984 :
var label = this.env.coltypes[col].label;
- 6985 :
- 6986 :
if (!elem)
- 6987 :
elem = $('.ff_' + col);
- 6988 :
- 6989 :
if (label && !$('label[for="ff_' + col + '"]').length)
- 6990 :
elem.placeholder(label);
- 6991 :
};
- 6992 :
- 6993 :
this.insert_edit_field = function(col, section, menu)
- 6994 :
{
- 6995 :
// just make pre-defined input field visible
- 6996 :
var elem = $('#ff_' + col);
- 6997 :
if (elem.length) {
- 6998 :
$('label[for="ff_' + col + '"]').parent().show();
- 6999 :
elem.show().focus();
- 7000 :
$(menu).children('option[value="' + col + '"]').prop('disabled', true);
- 7001 :
}
- 7002 :
else {
- 7003 :
var lastelem = $('.ff_' + col),
- 7004 :
appendcontainer = $('#contactsection'+section+' .contactcontroller'+col);
- 7005 :
- 7006 :
if (!appendcontainer.length) {
- 7007 :
var sect = $('#contactsection'+section),
- 7008 :
lastgroup = $('.contactfieldgroup', sect).last();
- 7009 :
appendcontainer = $('<fieldset>').addClass('contactfieldgroup contactcontroller'+col);
- 7010 :
if (lastgroup.length)
- 7011 :
appendcontainer.insertAfter(lastgroup);
- 7012 :
else
- 7013 :
sect.prepend(appendcontainer);
- 7014 :
}
- 7015 :
- 7016 :
if (appendcontainer.get(0).nodeName == 'FIELDSET') {
- 7017 :
var label, input,
- 7018 :
colprop = this.env.coltypes[col],
- 7019 :
name_suffix = colprop.limit != 1 ? '[]' : '',
- 7020 :
compact = $(menu).data('compact') ? true : false,
- 7021 :
input_id = 'ff_' + col + (colprop.count || 0),
- 7022 :
row = $('<div>').addClass('row input-group'),
- 7023 :
cell = $('<div>').addClass('contactfieldcontent ' + colprop.type);
- 7024 :
- 7025 :
// Field label
- 7026 :
if (colprop.subtypes_select) {
- 7027 :
label = $(colprop.subtypes_select);
- 7028 :
if (!compact)
- 7029 :
label = $('<div>').addClass('contactfieldlabel label').append(label);
- 7030 :
else
- 7031 :
label.addClass('input-group-prepend');
- 7032 :
}
- 7033 :
else {
- 7034 :
label = $('<label>').addClass('contactfieldlabel label input-group-text')
- 7035 :
.attr('for', input_id).text(colprop.label);
- 7036 :
- 7037 :
if (compact)
- 7038 :
label = $('<span class="input-group-prepend">').append(label);
- 7039 :
}
- 7040 :
- 7041 :
// Field input
- 7042 :
if (colprop.type == 'text' || colprop.type == 'date') {
- 7043 :
input = $('<input>')
- 7044 :
.addClass('form-control ff_' + col)
- 7045 :
.attr({type: 'text', name: '_'+col+name_suffix, size: colprop.size, id: input_id});
- 7046 :
- 7047 :
this.init_edit_field(col, input);
- 7048 :
- 7049 :
if (colprop.type == 'date' && $.datepicker)
- 7050 :
input.addClass('datepicker').datepicker();
- 7051 :
}
- 7052 :
else if (colprop.type == 'textarea') {
- 7053 :
input = $('<textarea>')
- 7054 :
.addClass('form-control ff_' + col)
- 7055 :
.attr({ name: '_' + col + name_suffix, cols: colprop.size, rows: colprop.rows, id: input_id });
- 7056 :
- 7057 :
this.init_edit_field(col, input);
- 7058 :
}
- 7059 :
else if (colprop.type == 'composite') {
- 7060 :
var i, childcol, cp, first, templ, cols = [], suffices = [], content = cell;
- 7061 :
- 7062 :
row.addClass('composite');
- 7063 :
- 7064 :
if (compact)
- 7065 :
content = $('<div class="content input-group-text">');
- 7066 :
- 7067 :
// read template for composite field order
- 7068 :
if (templ = this.env[col + '_template']) {
- 7069 :
for (i=0; i < templ.length; i++) {
- 7070 :
cols.push(templ[i][1]);
- 7071 :
suffices.push(templ[i][2]);
- 7072 :
}
- 7073 :
}
- 7074 :
else { // list fields according to appearance in colprop
- 7075 :
for (childcol in colprop.childs)
- 7076 :
cols.push(childcol);
- 7077 :
}
- 7078 :
- 7079 :
for (i=0; i < cols.length; i++) {
- 7080 :
childcol = cols[i];
- 7081 :
cp = colprop.childs[childcol];
- 7082 :
input = $('<input>')
- 7083 :
.addClass('form-control ff_' + childcol)
- 7084 :
.attr({ type: 'text', name: '_' + childcol + name_suffix, size: cp.size })
- 7085 :
.appendTo(content);
- 7086 :
- 7087 :
if (!compact)
- 7088 :
content.append(suffices[i] || " ");
- 7089 :
- 7090 :
this.init_edit_field(childcol, input);
- 7091 :
if (!first) first = input;
- 7092 :
}
- 7093 :
- 7094 :
if (compact)
- 7095 :
input = content;
- 7096 :
else
- 7097 :
input = first; // set focus to the first of this composite fields
- 7098 :
}
- 7099 :
else if (colprop.type == 'select') {
- 7100 :
input = $('<select>')
- 7101 :
.addClass('custom-select ff_' + col)
- 7102 :
.attr({ name: '_' + col + name_suffix, id: input_id });
- 7103 :
- 7104 :
var options = input.attr('options');
- 7105 :
options[options.length] = new Option('---', '');
- 7106 :
if (colprop.options)
- 7107 :
$.each(colprop.options, function(i, val) { options[options.length] = new Option(val, i); });
- 7108 :
}
- 7109 :
- 7110 :
if (input) {
- 7111 :
var delbutton = $('<a href="#del"></a>')
- 7112 :
.addClass('contactfieldbutton deletebutton input-group-text icon delete')
- 7113 :
.attr({title: this.get_label('delete'), rel: col})
- 7114 :
.html(this.env.delbutton)
- 7115 :
.click(function() { ref.delete_edit_field(this); return false; });
- 7116 :
- 7117 :
row.append(label);
- 7118 :
- 7119 :
if (!compact) {
- 7120 :
if (colprop.type != 'composite')
- 7121 :
cell.append(input);
- 7122 :
row.append(cell.append(delbutton));
- 7123 :
}
- 7124 :
else {
- 7125 :
row.append(input).append(delbutton);
- 7126 :
delbutton.wrap('<span class="input-group-append">');
- 7127 :
}
- 7128 :
- 7129 :
row.appendTo(appendcontainer.show());
- 7130 :
- 7131 :
if (input.is('div'))
- 7132 :
input.find('input').first().focus();
- 7133 :
else
- 7134 :
input.first().focus();
- 7135 :
- 7136 :
// disable option if limit reached
- 7137 :
if (!colprop.count) colprop.count = 0;
- 7138 :
if (++colprop.count == colprop.limit && colprop.limit)
- 7139 :
$(menu).children('option[value="' + col + '"]').prop('disabled', true);
- 7140 :
- 7141 :
this.triggerEvent('insert-edit-field', input);
- 7142 :
}
- 7143 :
}
- 7144 :
}
- 7145 :
};
- 7146 :
- 7147 :
this.delete_edit_field = function(elem)
- 7148 :
{
- 7149 :
var col = $(elem).attr('rel'),
- 7150 :
colprop = this.env.coltypes[col],
- 7151 :
input_group = $(elem).parents('div.row'),
- 7152 :
fieldset = $(elem).parents('fieldset.contactfieldgroup'),
- 7153 :
addmenu = fieldset.parent().find('select.addfieldmenu');
- 7154 :
- 7155 :
// just clear input but don't hide the last field
- 7156 :
if (--colprop.count <= 0 && colprop.visible)
- 7157 :
input_group.find('input').val('').blur();
- 7158 :
else {
- 7159 :
input_group.remove();
- 7160 :
// hide entire fieldset if no more rows
- 7161 :
if (!fieldset.children('div.row').length)
- 7162 :
fieldset.hide();
- 7163 :
}
- 7164 :
- 7165 :
// enable option in add-field selector or insert it if necessary
- 7166 :
if (addmenu.length) {
- 7167 :
var option = addmenu.children('option[value="'+col+'"]');
- 7168 :
if (option.length)
- 7169 :
option.prop('disabled', false);
- 7170 :
else
- 7171 :
option = $('<option>').attr('value', col).html(colprop.label).appendTo(addmenu);
- 7172 :
addmenu.show();
- 7173 :
}
- 7174 :
};
- 7175 :
- 7176 :
this.upload_contact_photo = function(form)
- 7177 :
{
- 7178 :
if (form && form.elements._photo.value) {
- 7179 :
this.async_upload_form(form, 'upload-photo', function(e) {
- 7180 :
ref.set_busy(false, null, ref.file_upload_id);
- 7181 :
});
- 7182 :
- 7183 :
// display upload indicator
- 7184 :
this.file_upload_id = this.set_busy(true, 'uploading');
- 7185 :
}
- 7186 :
};
- 7187 :
- 7188 :
this.replace_contact_photo = function(id)
- 7189 :
{
- 7190 :
var img_src = id == '-del-' ? this.env.photo_placeholder :
- 7191 :
this.env.comm_path + '&_action=photo&_source=' + this.env.source + '&_cid=' + (this.env.cid || 0) + '&_photo=' + id;
- 7192 :
- 7193 :
this.set_photo_actions(id);
- 7194 :
$(this.gui_objects.contactphoto).children('img').attr('src', img_src);
- 7195 :
};
- 7196 :
- 7197 :
this.photo_upload_end = function()
- 7198 :
{
- 7199 :
this.set_busy(false, null, this.file_upload_id);
- 7200 :
delete this.file_upload_id;
- 7201 :
};
- 7202 :
- 7203 :
this.set_photo_actions = function(id)
- 7204 :
{
- 7205 :
var n, buttons = this.buttons['upload-photo'];
- 7206 :
for (n=0; buttons && n < buttons.length; n++)
- 7207 :
$('a#'+buttons[n].id).html(this.get_label(id == '-del-' ? 'addphoto' : 'replacephoto'));
- 7208 :
- 7209 :
$('#ff_photo').val(id);
- 7210 :
this.enable_command('upload-photo', this.env.coltypes.photo ? true : false);
- 7211 :
this.enable_command('delete-photo', this.env.coltypes.photo && id != '-del-');
- 7212 :
};
- 7213 :
- 7214 :
// load advanced search page
- 7215 :
this.advanced_search = function()
- 7216 :
{
- 7217 :
var dialog = $('<iframe>').attr('src', this.url('search', {_form: 1, _framed: 1})),
- 7218 :
search_func = function() {
- 7219 :
var valid = false, form = {_adv: 1};
- 7220 :
- 7221 :
$.each($(dialog[0].contentWindow.rcmail.gui_objects.editform).serializeArray(), function() {
- 7222 :
if (this.name.match(/^_search/) && this.value != '') {
- 7223 :
form[this.name] = this.value;
- 7224 :
valid = true;
- 7225 :
}
- 7226 :
});
- 7227 :
- 7228 :
if (valid) {
- 7229 :
ref.http_post('search', form, ref.set_busy(true, 'searching'));
- 7230 :
return true;
- 7231 :
}
- 7232 :
};
- 7233 :
- 7234 :
this.simple_dialog(dialog, this.gettext('advsearch'), search_func, {
- 7235 :
button: 'search',
- 7236 :
width: 600,
- 7237 :
height: 500
- 7238 :
});
- 7239 :
- 7240 :
return true;
- 7241 :
};
- 7242 :
- 7243 :
// unselect directory/group
- 7244 :
this.unselect_directory = function()
- 7245 :
{
- 7246 :
this.select_folder('');
- 7247 :
this.enable_command('search-delete', false);
- 7248 :
};
- 7249 :
- 7250 :
// callback for creating a new saved search record
- 7251 :
this.insert_saved_search = function(name, id)
- 7252 :
{
- 7253 :
var key = 'S'+id,
- 7254 :
link = $('<a>').attr({href: '#', rel: id})
- 7255 :
.click(function() { return ref.command('listsearch', id, this); })
- 7256 :
.html(name),
- 7257 :
prop = { name:name, id:id };
- 7258 :
- 7259 :
this.savedsearchlist.insert({ id:key, html:link, classes:['contactsearch'] }, null, 'contactsearch');
- 7260 :
this.select_folder(key,'',true);
- 7261 :
this.enable_command('search-delete', true);
- 7262 :
this.env.search_id = id;
- 7263 :
- 7264 :
this.triggerEvent('abook_search_insert', prop);
- 7265 :
};
- 7266 :
- 7267 :
// creates a dialog for saved search
- 7268 :
this.search_create = function()
- 7269 :
{
- 7270 :
var input = $('<input>').attr('type', 'text'),
- 7271 :
content = $('<label>').text(this.get_label('namex')).append(input);
- 7272 :
- 7273 :
this.simple_dialog(content, 'searchsave',
- 7274 :
function() {
- 7275 :
var name;
- 7276 :
if (name = input.val()) {
- 7277 :
ref.http_post('search-create', {_search: ref.env.search_request, /* PAMELA - Search contacts by source */_source: rcmail.env.source, _name: name},
- 7278 :
ref.set_busy(true, 'loading'));
- 7279 :
return true;
- 7280 :
}
- 7281 :
}
- 7282 :
);
- 7283 :
};
- 7284 :
- 7285 :
this.search_delete = function()
- 7286 :
{
- 7287 :
if (this.env.search_request) {
- 7288 :
var lock = this.set_busy(true, 'savedsearchdeleting');
- 7289 :
this.http_post('search-delete', {_sid: this.env.search_id}, lock);
- 7290 :
}
- 7291 :
};
- 7292 :
- 7293 :
// callback from server upon search-delete command
- 7294 :
this.remove_search_item = function(id)
- 7295 :
{
- 7296 :
if (this.savedsearchlist.remove('S' + id)) {
- 7297 :
this.triggerEvent('search_delete', {id: id});
- 7298 :
}
- 7299 :
- 7300 :
this.env.search_id = null;
- 7301 :
this.env.search_request = null;
- 7302 :
this.list_contacts_clear();
- 7303 :
this.reset_qsearch();
- 7304 :
this.enable_command('search-delete', 'search-create', false);
- 7305 :
};
- 7306 :
- 7307 :
this.listsearch = function(id)
- 7308 :
{
- 7309 :
var lock = this.set_busy(true, 'searching');
- 7310 :
- 7311 :
if (this.contact_list) {
- 7312 :
this.list_contacts_clear();
- 7313 :
}
- 7314 :
- 7315 :
this.reset_qsearch();
- 7316 :
- 7317 :
if (this.savedsearchlist) {
- 7318 :
this.treelist.select('');
- 7319 :
this.savedsearchlist.select('S'+id);
- 7320 :
}
- 7321 :
else
- 7322 :
this.select_folder('S'+id, '', true);
- 7323 :
- 7324 :
// reset vars
- 7325 :
this.env.current_page = 1;
- 7326 :
this.http_request('search', {_sid: id}, lock);
- 7327 :
};
- 7328 :
- 7329 :
// display a dialog with QR code image
- 7330 :
this.qrcode = function()
- 7331 :
{
- 7332 :
var title = this.get_label('qrcode'),
- 7333 :
options = {button: false, cancel_button: 'close', width: 300, height: 300},
- 7334 :
img = new Image(300, 300);
- 7335 :
- 7336 :
img.src = this.url('addressbook/qrcode', {_source: this.env.source, _cid: this.get_single_cid()});
- 7337 :
- 7338 :
return this.simple_dialog(img, title, null, options);
- 7339 :
};
- 7340 :
- 7341 :
- 7342 :
/*********************************************************/
- 7343 :
/********* user settings methods *********/
- 7344 :
/*********************************************************/
- 7345 :
- 7346 :
// preferences section select and load options frame
- 7347 :
this.section_select = function(list)
- 7348 :
{
- 7349 :
var win, id = list.get_single_selection();
- 7350 :
- 7351 :
if (id && (win = this.get_frame_window(this.env.contentframe))) {
- 7352 :
this.location_href({_action: 'edit-prefs', _section: id, _framed: 1}, win, true);
- 7353 :
}
- 7354 :
};
- 7355 :
- 7356 :
this.response_select = function(list)
- 7357 :
{
- 7358 :
var id = list.get_single_selection();
- 7359 :
- 7360 :
this.enable_command('delete', !!id && $.inArray(id, this.env.readonly_responses) < 0);
- 7361 :
- 7362 :
if (id) {
- 7363 :
this.load_response(id, 'edit-response');
- 7364 :
}
- 7365 :
};
- 7366 :
- 7367 :
// load response record
- 7368 :
this.load_response = function(id, action)
- 7369 :
{
- 7370 :
var win;
- 7371 :
- 7372 :
if (win = this.get_frame_window(this.env.contentframe)) {
- 7373 :
if (id || action == 'add-response') {
- 7374 :
if (!id)
- 7375 :
this.responses_list.clear_selection();
- 7376 :
- 7377 :
this.location_href({_action: action, _key: id, _framed: 1}, win, true);
- 7378 :
}
- 7379 :
}
- 7380 :
};
- 7381 :
- 7382 :
this.identity_select = function(list)
- 7383 :
{
- 7384 :
var id = list.get_single_selection();
- 7385 :
- 7386 :
this.enable_command('delete', !!id && list.rowcount > 1 && this.env.identities_level < 2);
- 7387 :
- 7388 :
if (id) {
- 7389 :
this.load_identity(id, 'edit-identity');
- 7390 :
}
- 7391 :
};
- 7392 :
- 7393 :
// load identity record
- 7394 :
this.load_identity = function(id, action)
- 7395 :
{
- 7396 :
var win;
- 7397 :
- 7398 :
if (win = this.get_frame_window(this.env.contentframe)) {
- 7399 :
if (id || action == 'add-identity') {
- 7400 :
if (!id)
- 7401 :
this.identity_list.clear_selection();
- 7402 :
- 7403 :
this.location_href({_action: action, _iid: id, _framed: 1}, win, true);
- 7404 :
}
- 7405 :
}
- 7406 :
};
- 7407 :
- 7408 :
this.delete_identity = function(id)
- 7409 :
{
- 7410 :
// exit if no identity is specified or if selection is empty
- 7411 :
var selection = this.identity_list.get_selection();
- 7412 :
if (!(selection.length || this.env.iid))
- 7413 :
return;
- 7414 :
- 7415 :
if (!id)
- 7416 :
id = this.env.iid ? this.env.iid : selection[0];
- 7417 :
- 7418 :
// submit request with appended token
- 7419 :
if (id) {
- 7420 :
this.confirm_dialog(this.get_label('deleteidentityconfirm'), 'delete', function() {
- 7421 :
ref.http_post('settings/delete-identity', { _iid: id }, true);
- 7422 :
});
- 7423 :
}
- 7424 :
};
- 7425 :
- 7426 :
this.update_identity_row = function(id, name, add)
- 7427 :
{
- 7428 :
var list = this.identity_list,
- 7429 :
rid = this.html_identifier(id);
- 7430 :
- 7431 :
if (add) {
- 7432 :
list.insert_row({ id:'rcmrow'+rid, cols:[ { className:'mail', innerHTML:name } ] });
- 7433 :
list.select(rid);
- 7434 :
}
- 7435 :
else {
- 7436 :
list.update_row(rid, [ name ]);
- 7437 :
}
- 7438 :
};
- 7439 :
- 7440 :
this.update_response_row = function(response, oldkey)
- 7441 :
{
- 7442 :
var list = this.responses_list;
- 7443 :
- 7444 :
if (list && oldkey) {
- 7445 :
list.update_row(oldkey, [ response.name ], response.key, true);
- 7446 :
}
- 7447 :
else if (list) {
- 7448 :
list.insert_row({ id:'rcmrow'+response.key, cols:[ { className:'name', innerHTML:response.name } ] });
- 7449 :
list.select(response.key);
- 7450 :
}
- 7451 :
};
- 7452 :
- 7453 :
this.remove_response = function(key)
- 7454 :
{
- 7455 :
var frame;
- 7456 :
- 7457 :
if (this.env.textresponses) {
- 7458 :
delete this.env.textresponses[key];
- 7459 :
}
- 7460 :
- 7461 :
if (this.responses_list) {
- 7462 :
this.responses_list.remove_row(key);
- 7463 :
this.show_contentframe(false);
- 7464 :
}
- 7465 :
- 7466 :
this.enable_command('delete', false);
- 7467 :
};
- 7468 :
- 7469 :
this.remove_identity = function(id)
- 7470 :
{
- 7471 :
var frame, list = this.identity_list,
- 7472 :
rid = this.html_identifier(id);
- 7473 :
- 7474 :
if (list && id) {
- 7475 :
list.remove_row(rid);
- 7476 :
this.show_contentframe(false);
- 7477 :
}
- 7478 :
- 7479 :
this.enable_command('delete', false);
- 7480 :
};
- 7481 :
- 7482 :
- 7483 :
/*********************************************************/
- 7484 :
/********* folder manager methods *********/
- 7485 :
/*********************************************************/
- 7486 :
- 7487 :
this.init_subscription_list = function()
- 7488 :
{
- 7489 :
var delim = RegExp.escape(this.env.delimiter);
- 7490 :
- 7491 :
this.last_sub_rx = RegExp('['+delim+']?[^'+delim+']+$');
- 7492 :
- 7493 :
this.subscription_list = new rcube_treelist_widget(this.gui_objects.subscriptionlist, {
- 7494 :
selectable: true,
- 7495 :
tabexit: false,
- 7496 :
parent_focus: true,
- 7497 :
id_prefix: 'rcmli',
- 7498 :
id_encode: this.html_identifier_encode,
- 7499 :
id_decode: this.html_identifier_decode,
- 7500 :
searchbox: '#foldersearch'
- 7501 :
});
- 7502 :
- 7503 :
this.subscription_list
- 7504 :
.addEventListener('select', function(node) { ref.subscription_select(node.id); })
- 7505 :
.addEventListener('collapse', function(node) { ref.folder_collapsed(node) })
- 7506 :
.addEventListener('expand', function(node) { ref.folder_collapsed(node) })
- 7507 :
.addEventListener('search', function(p) { if (p.query) ref.subscription_select(); })
- 7508 :
.draggable({cancel: 'li.mailbox.root,input,div.treetoggle,.custom-control'})
- 7509 :
.droppable({
- 7510 :
// @todo: find better way, accept callback is executed for every folder
- 7511 :
// on the list when dragging starts (and stops), this is slow, but
- 7512 :
// I didn't find a method to check droptarget on over event
- 7513 :
accept: function(node) {
- 7514 :
if (!node.is('.mailbox'))
- 7515 :
return false;
- 7516 :
- 7517 :
var source_folder = ref.folder_id2name(node.attr('id')),
- 7518 :
dest_folder = ref.folder_id2name(this.id),
- 7519 :
source = ref.env.subscriptionrows[source_folder],
- 7520 :
dest = ref.env.subscriptionrows[dest_folder];
- 7521 :
- 7522 :
return source && !source[2]
- 7523 :
&& dest_folder != source_folder.replace(ref.last_sub_rx, '')
- 7524 :
&& !dest_folder.startsWith(source_folder + ref.env.delimiter);
- 7525 :
},
- 7526 :
drop: function(e, ui) {
- 7527 :
var source = ref.folder_id2name(ui.draggable.attr('id')),
- 7528 :
dest = ref.folder_id2name(this.id);
- 7529 :
- 7530 :
ref.subscription_move_folder(source, dest);
- 7531 :
}
- 7532 :
});
- 7533 :
};
- 7534 :
- 7535 :
this.folder_id2name = function(id)
- 7536 :
{
- 7537 :
return id ? ref.html_identifier_decode(id.replace(/^rcmli/, '')) : null;
- 7538 :
};
- 7539 :
- 7540 :
this.subscription_select = function(id)
- 7541 :
{
- 7542 :
var folder;
- 7543 :
- 7544 :
if (id && id != '*' && (folder = this.env.subscriptionrows[id])) {
- 7545 :
this.env.mailbox = id;
- 7546 :
this.show_folder(id);
- 7547 :
this.enable_command('delete-folder', !folder[2]);
- 7548 :
}
- 7549 :
else {
- 7550 :
this.env.mailbox = null;
- 7551 :
this.show_contentframe(false);
- 7552 :
this.enable_command('delete-folder', 'purge', false);
- 7553 :
}
- 7554 :
};
- 7555 :
- 7556 :
this.subscription_move_folder = function(from, to)
- 7557 :
{
- 7558 :
if (from && to !== null && from != to && to != from.replace(this.last_sub_rx, '')) {
- 7559 :
var path = from.split(this.env.delimiter),
- 7560 :
basename = path.pop(),
- 7561 :
newname = to === '' || to === '*' ? basename : to + this.env.delimiter + basename;
- 7562 :
- 7563 :
if (newname != from) {
- 7564 :
this.confirm_dialog(this.get_label('movefolderconfirm'), 'move', function() {
- 7565 :
ref.http_post('rename-folder', {_folder_oldname: from, _folder_newname: newname},
- 7566 :
ref.set_busy(true, 'foldermoving'));
- 7567 :
}, {button_class: 'save move'});
- 7568 :
}
- 7569 :
}
- 7570 :
};
- 7571 :
- 7572 :
// tell server to create and subscribe a new mailbox
- 7573 :
this.create_folder = function()
- 7574 :
{
- 7575 :
this.show_folder('', this.env.mailbox);
- 7576 :
};
- 7577 :
- 7578 :
// delete a specific mailbox with all its messages
- 7579 :
this.delete_folder = function(name)
- 7580 :
{
- 7581 :
if (!name)
- 7582 :
name = this.env.mailbox;
- 7583 :
- 7584 :
if (name) {
- 7585 :
this.confirm_dialog(this.get_label('deletefolderconfirm'), 'delete', function() {
- 7586 :
ref.http_post('delete-folder', {_mbox: name}, ref.set_busy(true, 'folderdeleting'));
- 7587 :
});
- 7588 :
}
- 7589 :
};
- 7590 :
- 7591 :
// Add folder row to the table and initialize it
- 7592 :
this.add_folder_row = function (id, name, display_name, is_protected, subscribed, class_name, refrow, subfolders)
- 7593 :
{
- 7594 :
if (!this.gui_objects.subscriptionlist)
- 7595 :
return false;
- 7596 :
- 7597 :
// reset searching
- 7598 :
if (this.subscription_list.is_search()) {
- 7599 :
this.subscription_select();
- 7600 :
this.subscription_list.reset_search();
- 7601 :
}
- 7602 :
- 7603 :
// disable drag-n-drop temporarily
- 7604 :
// some skins disable dragging in mobile mode, so we have to check if it is still draggable
- 7605 :
if (this.subscription_list.is_draggable())
- 7606 :
this.subscription_list.draggable('destroy').droppable('destroy');
- 7607 :
- 7608 :
var row, n, tmp, tmp_name, rowid, collator, pos, p, parent = '',
- 7609 :
folders = [], list = [], slist = [],
- 7610 :
list_element = $(this.gui_objects.subscriptionlist);
- 7611 :
row = refrow ? refrow : $($('li', list_element).get(1)).clone(true);
- 7612 :
- 7613 :
if (!row.length) {
- 7614 :
// Refresh page if we don't have a table row to clone
- 7615 :
this.goto_url('folders');
- 7616 :
return false;
- 7617 :
}
- 7618 :
- 7619 :
// set ID, reset css class
- 7620 :
row.attr({id: 'rcmli' + this.html_identifier_encode(id), 'class': class_name});
- 7621 :
- 7622 :
if (!refrow || !refrow.length) {
- 7623 :
// remove old data, subfolders and toggle
- 7624 :
$('ul,div.treetoggle', row).remove();
- 7625 :
row.removeData('filtered');
- 7626 :
}
- 7627 :
- 7628 :
// set folder name
- 7629 :
$('a', row).first().text(display_name).removeAttr('title');
- 7630 :
- 7631 :
// update subscription checkbox
- 7632 :
$('input[name="_subscribed[]"]', row).first().val(id)
- 7633 :
.prop({checked: subscribed ? true : false, disabled: is_protected ? true : false});
- 7634 :
- 7635 :
// add to folder/row-ID map
- 7636 :
this.env.subscriptionrows[id] = [name, display_name, false];
- 7637 :
- 7638 :
// copy folders data to an array for sorting
- 7639 :
$.each(this.env.subscriptionrows, function(k, v) { v[3] = k; folders.push(v); });
- 7640 :
- 7641 :
try {
- 7642 :
// use collator if supported (FF29, IE11, Opera15, Chrome24)
- 7643 :
collator = new Intl.Collator(this.env.locale.replace('_', '-'));
- 7644 :
}
- 7645 :
catch (e) {};
- 7646 :
- 7647 :
// sort folders
- 7648 :
folders.sort(function(a, b) {
- 7649 :
var i, f1, f2,
- 7650 :
path1 = a[0].split(ref.env.delimiter),
- 7651 :
path2 = b[0].split(ref.env.delimiter),
- 7652 :
len = path1.length;
- 7653 :
- 7654 :
for (i=0; i<len; i++) {
- 7655 :
f1 = path1[i];
- 7656 :
f2 = path2[i];
- 7657 :
- 7658 :
if (f1 !== f2) {
- 7659 :
if (f2 === undefined)
- 7660 :
return 1;
- 7661 :
if (collator)
- 7662 :
return collator.compare(f1, f2);
- 7663 :
else
- 7664 :
return f1 < f2 ? -1 : 1;
- 7665 :
}
- 7666 :
else if (i == len-1) {
- 7667 :
return -1
- 7668 :
}
- 7669 :
}
- 7670 :
});
- 7671 :
- 7672 :
for (n in folders) {
- 7673 :
p = folders[n][3];
- 7674 :
// protected folder
- 7675 :
if (folders[n][2]) {
- 7676 :
tmp_name = p + this.env.delimiter;
- 7677 :
// prefix namespace cannot have subfolders (#1488349)
- 7678 :
if (tmp_name == this.env.prefix_ns)
- 7679 :
continue;
- 7680 :
slist.push(p);
- 7681 :
tmp = tmp_name;
- 7682 :
}
- 7683 :
// protected folder's child
- 7684 :
else if (tmp && p.startsWith(tmp))
- 7685 :
slist.push(p);
- 7686 :
// other
- 7687 :
else {
- 7688 :
list.push(p);
- 7689 :
tmp = null;
- 7690 :
}
- 7691 :
}
- 7692 :
- 7693 :
// check if subfolder of a protected folder
- 7694 :
for (n=0; n<slist.length; n++) {
- 7695 :
if (id.startsWith(slist[n] + this.env.delimiter))
- 7696 :
rowid = slist[n];
- 7697 :
}
- 7698 :
- 7699 :
// find folder position after sorting
- 7700 :
for (n=0; !rowid && n<list.length; n++) {
- 7701 :
if (n && list[n] == id)
- 7702 :
rowid = list[n-1];
- 7703 :
}
- 7704 :
- 7705 :
// add row to the table
- 7706 :
if (rowid && (n = this.subscription_list.get_item(rowid, true))) {
- 7707 :
// find parent folder
- 7708 :
if (pos = id.lastIndexOf(this.env.delimiter)) {
- 7709 :
parent = id.substring(0, pos);
- 7710 :
parent = this.subscription_list.get_item(parent, true);
- 7711 :
- 7712 :
// add required tree elements to the parent if not already there
- 7713 :
if (!$('div.treetoggle', parent).length) {
- 7714 :
$('<div> </div>').addClass('treetoggle collapsed').appendTo(parent);
- 7715 :
}
- 7716 :
if (!$('ul', parent).length) {
- 7717 :
$('<ul>').css('display', 'none').appendTo(parent);
- 7718 :
}
- 7719 :
}
- 7720 :
- 7721 :
if (parent && n == parent) {
- 7722 :
$('ul', parent).first().append(row);
- 7723 :
}
- 7724 :
else {
- 7725 :
while (p = $(n).parent().parent().get(0)) {
- 7726 :
if (parent && p == parent)
- 7727 :
break;
- 7728 :
if (!$(p).is('li.mailbox'))
- 7729 :
break;
- 7730 :
n = p;
- 7731 :
}
- 7732 :
- 7733 :
$(n).after(row);
- 7734 :
}
- 7735 :
}
- 7736 :
else {
- 7737 :
list_element.append(row);
- 7738 :
}
- 7739 :
- 7740 :
// add subfolders
- 7741 :
$.extend(this.env.subscriptionrows, subfolders || {});
- 7742 :
- 7743 :
// update list widget
- 7744 :
this.subscription_list.reset(true);
- 7745 :
this.subscription_select();
- 7746 :
- 7747 :
// expand parent
- 7748 :
if (parent) {
- 7749 :
this.subscription_list.expand(this.folder_id2name(parent.id));
- 7750 :
}
- 7751 :
- 7752 :
row = row.show().get(0);
- 7753 :
if (row.scrollIntoView)
- 7754 :
row.scrollIntoView(false);
- 7755 :
- 7756 :
// Let skins to do their magic, e.g. Elastic will fix pretty checkbox
- 7757 :
if (!refrow)
- 7758 :
this.triggerEvent('clonerow', {row: row, id: id});
- 7759 :
- 7760 :
return row;
- 7761 :
};
- 7762 :
- 7763 :
// replace an existing table row with a new folder line (with subfolders)
- 7764 :
this.replace_folder_row = function(oldid, id, name, display_name, is_protected, class_name)
- 7765 :
{
- 7766 :
if (!this.gui_objects.subscriptionlist) {
- 7767 :
if (this.is_framed()) {
- 7768 :
// @FIXME: for some reason this 'parent' variable need to be prefixed with 'window.'
- 7769 :
return window.parent.rcmail.replace_folder_row(oldid, id, name, display_name, is_protected, class_name);
- 7770 :
}
- 7771 :
- 7772 :
return false;
- 7773 :
}
- 7774 :
- 7775 :
// reset searching
- 7776 :
if (this.subscription_list.is_search()) {
- 7777 :
this.subscription_select();
- 7778 :
this.subscription_list.reset_search();
- 7779 :
}
- 7780 :
- 7781 :
var subfolders = {},
- 7782 :
row = this.subscription_list.get_item(oldid, true),
- 7783 :
parent = $(row).parent(),
- 7784 :
old_folder = this.env.subscriptionrows[oldid],
- 7785 :
prefix_len_id = oldid.length,
- 7786 :
prefix_len_name = old_folder[0].length,
- 7787 :
subscribed = $('input[name="_subscribed[]"]', row).first().prop('checked');
- 7788 :
- 7789 :
// no renaming, only update class_name
- 7790 :
if (oldid == id) {
- 7791 :
$(row).attr('class', class_name || '');
- 7792 :
return;
- 7793 :
}
- 7794 :
- 7795 :
// update subfolders
- 7796 :
$('li', row).each(function() {
- 7797 :
var fname = ref.folder_id2name(this.id),
- 7798 :
folder = ref.env.subscriptionrows[fname],
- 7799 :
newid = id + fname.slice(prefix_len_id);
- 7800 :
- 7801 :
this.id = 'rcmli' + ref.html_identifier_encode(newid);
- 7802 :
$('input[name="_subscribed[]"]', this).first().val(newid);
- 7803 :
folder[0] = name + folder[0].slice(prefix_len_name);
- 7804 :
- 7805 :
subfolders[newid] = folder;
- 7806 :
delete ref.env.subscriptionrows[fname];
- 7807 :
});
- 7808 :
- 7809 :
// get row off the list
- 7810 :
row = $(row).detach();
- 7811 :
- 7812 :
delete this.env.subscriptionrows[oldid];
- 7813 :
- 7814 :
// remove parent list/toggle elements if not needed
- 7815 :
if (parent.get(0) != this.gui_objects.subscriptionlist && !$('li', parent).length) {
- 7816 :
$('ul,div.treetoggle', parent.parent()).remove();
- 7817 :
}
- 7818 :
- 7819 :
// move the existing table row
- 7820 :
this.add_folder_row(id, name, display_name, is_protected, subscribed, class_name, row, subfolders);
- 7821 :
};
- 7822 :
- 7823 :
// remove the table row of a specific mailbox from the table
- 7824 :
this.remove_folder_row = function(folder)
- 7825 :
{
- 7826 :
// reset searching
- 7827 :
if (this.subscription_list.is_search()) {
- 7828 :
this.subscription_select();
- 7829 :
this.subscription_list.reset_search();
- 7830 :
}
- 7831 :
- 7832 :
var list = [], row = this.subscription_list.get_item(folder, true);
- 7833 :
- 7834 :
// get subfolders if any
- 7835 :
$('li', row).each(function() { list.push(ref.folder_id2name(this.id)); });
- 7836 :
- 7837 :
// remove folder row (and subfolders)
- 7838 :
this.subscription_list.remove(folder);
- 7839 :
- 7840 :
// update local list variable
- 7841 :
list.push(folder);
- 7842 :
$.each(list, function(i, v) { delete ref.env.subscriptionrows[v]; });
- 7843 :
};
- 7844 :
- 7845 :
this.subscribe = function(folder)
- 7846 :
{
- 7847 :
this.change_subscription_state(folder, true);
- 7848 :
};
- 7849 :
- 7850 :
this.unsubscribe = function(folder)
- 7851 :
{
- 7852 :
this.change_subscription_state(folder, false);
- 7853 :
};
- 7854 :
- 7855 :
this.change_subscription_state = function(folder, state)
- 7856 :
{
- 7857 :
if (folder) {
- 7858 :
var prefix = state ? '' : 'un',
- 7859 :
lock = this.display_message('folder' + prefix + 'subscribing', 'loading');
- 7860 :
- 7861 :
this.http_post(prefix + 'subscribe', {_mbox: folder}, lock);
- 7862 :
- 7863 :
// in case this was a list of search results, update also the main list
- 7864 :
$(this.gui_objects.subscriptionlist).find('input[value="' + folder + '"]').prop('checked', state);
- 7865 :
}
- 7866 :
};
- 7867 :
- 7868 :
- 7869 :
// when user select a folder in manager
- 7870 :
this.show_folder = function(folder, path, force)
- 7871 :
{
- 7872 :
var win, target = window,
- 7873 :
action = folder === '' ? 'add' : 'edit',
- 7874 :
url = '&_action=' + action + '-folder&_mbox=' + urlencode(folder);
- 7875 :
- 7876 :
if (path)
- 7877 :
url += '&_path='+urlencode(path);
- 7878 :
- 7879 :
if (win = this.get_frame_window(this.env.contentframe)) {
- 7880 :
target = win;
- 7881 :
url += '&_framed=1';
- 7882 :
}
- 7883 :
- 7884 :
if (String(target.location.href).indexOf(url) >= 0 && !force)
- 7885 :
this.show_contentframe(true);
- 7886 :
else
- 7887 :
this.location_href(this.env.comm_path+url, target, true);
- 7888 :
};
- 7889 :
- 7890 :
// disables subscription checkbox (for protected folder)
- 7891 :
this.disable_subscription = function(folder)
- 7892 :
{
- 7893 :
var row = this.subscription_list.get_item(folder, true);
- 7894 :
if (row)
- 7895 :
$('input[name="_subscribed[]"]', row).first().prop('disabled', true);
- 7896 :
};
- 7897 :
- 7898 :
// resets state of subscription checkbox (e.g. on error)
- 7899 :
this.reset_subscription = function(folder, state)
- 7900 :
{
- 7901 :
var row = this.subscription_list.get_item(folder, true);
- 7902 :
if (row)
- 7903 :
$('input[name="_subscribed[]"]', row).first().prop('checked', state);
- 7904 :
};
- 7905 :
- 7906 :
this.folder_size = function(folder)
- 7907 :
{
- 7908 :
var lock = this.set_busy(true, 'loading');
- 7909 :
this.http_post('folder-size', {_mbox: folder}, lock);
- 7910 :
};
- 7911 :
- 7912 :
this.folder_size_update = function(size)
- 7913 :
{
- 7914 :
$('#folder-size').replaceWith(size);
- 7915 :
};
- 7916 :
- 7917 :
// filter folders by namespace
- 7918 :
this.folder_filter = function(prefix)
- 7919 :
{
- 7920 :
this.subscription_list.reset_search();
- 7921 :
- 7922 :
this.subscription_list.container.children('li').each(function() {
- 7923 :
var i, folder = ref.folder_id2name(this.id);
- 7924 :
// show all folders
- 7925 :
if (prefix == '---') {
- 7926 :
}
- 7927 :
// got namespace prefix
- 7928 :
else if (prefix) {
- 7929 :
if (folder !== prefix) {
- 7930 :
$(this).data('filtered', true).hide();
- 7931 :
return
- 7932 :
}
- 7933 :
}
- 7934 :
// no namespace prefix, filter out all other namespaces
- 7935 :
else {
- 7936 :
// first get all namespace roots
- 7937 :
for (i in ref.env.ns_roots) {
- 7938 :
if (folder === ref.env.ns_roots[i]) {
- 7939 :
$(this).data('filtered', true).hide();
- 7940 :
return;
- 7941 :
}
- 7942 :
}
- 7943 :
}
- 7944 :
- 7945 :
$(this).removeData('filtered').show();
- 7946 :
});
- 7947 :
};
- 7948 :
- 7949 :
/*********************************************************/
- 7950 :
/********* GUI functionality *********/
- 7951 :
/*********************************************************/
- 7952 :
- 7953 :
this.init_button = function(cmd, prop)
- 7954 :
{
- 7955 :
var elm = document.getElementById(prop.id);
- 7956 :
if (!elm)
- 7957 :
return;
- 7958 :
- 7959 :
var preload = false;
- 7960 :
if (prop.type == 'image') {
- 7961 :
elm = elm.parentNode;
- 7962 :
preload = true;
- 7963 :
}
- 7964 :
- 7965 :
elm._command = cmd;
- 7966 :
elm._id = prop.id;
- 7967 :
if (prop.sel) {
- 7968 :
elm.onmousedown = function(e) { return ref.button_sel(this._command, this._id); };
- 7969 :
elm.onmouseup = function(e) { return ref.button_out(this._command, this._id); };
- 7970 :
if (preload)
- 7971 :
new Image().src = prop.sel;
- 7972 :
}
- 7973 :
if (prop.over) {
- 7974 :
elm.onmouseover = function(e) { return ref.button_over(this._command, this._id); };
- 7975 :
elm.onmouseout = function(e) { return ref.button_out(this._command, this._id); };
- 7976 :
if (preload)
- 7977 :
new Image().src = prop.over;
- 7978 :
}
- 7979 :
};
- 7980 :
- 7981 :
// set event handlers on registered buttons
- 7982 :
this.init_buttons = function()
- 7983 :
{
- 7984 :
for (var cmd in this.buttons) {
- 7985 :
if (typeof cmd !== 'string')
- 7986 :
continue;
- 7987 :
- 7988 :
for (var i=0; i<this.buttons[cmd].length; i++) {
- 7989 :
this.init_button(cmd, this.buttons[cmd][i]);
- 7990 :
}
- 7991 :
}
- 7992 :
};
- 7993 :
- 7994 :
// set button to a specific state
- 7995 :
this.set_button = function(command, state)
- 7996 :
{
- 7997 :
var n, button, obj, a_buttons = this.buttons[command],
- 7998 :
len = a_buttons ? a_buttons.length : 0;
- 7999 :
- 8000 :
for (n=0; n<len; n++) {
- 8001 :
button = a_buttons[n];
- 8002 :
obj = document.getElementById(button.id);
- 8003 :
- 8004 :
if (!obj || button.status === state)
- 8005 :
continue;
- 8006 :
- 8007 :
// get default/passive setting of the button
- 8008 :
if (button.type == 'image' && !button.status) {
- 8009 :
button.pas = obj._original_src ? obj._original_src : obj.src;
- 8010 :
// respect PNG fix on IE browsers
- 8011 :
if (obj.runtimeStyle && obj.runtimeStyle.filter && obj.runtimeStyle.filter.match(/src=['"]([^'"]+)['"]/))
- 8012 :
button.pas = RegExp.$1;
- 8013 :
}
- 8014 :
else if (!button.status)
- 8015 :
button.pas = String(obj.className);
- 8016 :
- 8017 :
button.status = state;
- 8018 :
- 8019 :
// set image according to button state
- 8020 :
if (button.type == 'image' && button[state]) {
- 8021 :
obj.src = button[state];
- 8022 :
}
- 8023 :
// set class name according to button state
- 8024 :
else if (button[state] !== undefined) {
- 8025 :
obj.className = button[state];
- 8026 :
}
- 8027 :
- 8028 :
// disable/enable input buttons
- 8029 :
if (button.type == 'input' || button.type == 'button') {
- 8030 :
obj.disabled = state == 'pas';
- 8031 :
}
- 8032 :
else {
- 8033 :
$(obj).attr({
- 8034 :
tabindex: state == 'pas' || state == 'sel' ? '-1' : ($(obj).attr('data-tabindex') || '0'),
- 8035 :
'aria-disabled': state == 'pas' || state == 'sel' ? 'true' : 'false'
- 8036 :
});
- 8037 :
}
- 8038 :
}
- 8039 :
};
- 8040 :
- 8041 :
// display a specific alttext
- 8042 :
this.set_alttext = function(command, label)
- 8043 :
{
- 8044 :
var n, button, obj, link, label,
- 8045 :
a_buttons = this.buttons[command],
- 8046 :
len = a_buttons ? a_buttons.length : 0;
- 8047 :
- 8048 :
for (n=0; n<len; n++) {
- 8049 :
button = a_buttons[n];
- 8050 :
obj = document.getElementById(button.id);
- 8051 :
label = this.get_label(label);
- 8052 :
- 8053 :
if (obj && button.type == 'image') {
- 8054 :
obj.setAttribute('alt', label);
- 8055 :
if ((link = obj.parentNode) && link.tagName.toLowerCase() == 'a')
- 8056 :
link.setAttribute('title', label);
- 8057 :
}
- 8058 :
else if (obj)
- 8059 :
obj.setAttribute('title', label);
- 8060 :
}
- 8061 :
};
- 8062 :
- 8063 :
// mouse over button
- 8064 :
this.button_over = function(command, id)
- 8065 :
{
- 8066 :
this.button_event(command, id, 'over');
- 8067 :
};
- 8068 :
- 8069 :
// mouse down on button
- 8070 :
this.button_sel = function(command, id)
- 8071 :
{
- 8072 :
this.button_event(command, id, 'sel');
- 8073 :
};
- 8074 :
- 8075 :
// mouse out of button
- 8076 :
this.button_out = function(command, id)
- 8077 :
{
- 8078 :
this.button_event(command, id, 'act');
- 8079 :
};
- 8080 :
- 8081 :
// event of button
- 8082 :
this.button_event = function(command, id, event)
- 8083 :
{
- 8084 :
var n, button, obj, a_buttons = this.buttons[command],
- 8085 :
len = a_buttons ? a_buttons.length : 0;
- 8086 :
- 8087 :
for (n=0; n<len; n++) {
- 8088 :
button = a_buttons[n];
- 8089 :
if (button.id == id && button.status == 'act') {
- 8090 :
if (button[event] && (obj = document.getElementById(button.id))) {
- 8091 :
obj[button.type == 'image' ? 'src' : 'className'] = button[event];
- 8092 :
}
- 8093 :
- 8094 :
if (event == 'sel') {
- 8095 :
this.buttons_sel[id] = command;
- 8096 :
}
- 8097 :
}
- 8098 :
}
- 8099 :
};
- 8100 :
- 8101 :
// write to the document/window title
- 8102 :
this.set_pagetitle = function(title)
- 8103 :
{
- 8104 :
if (title && document.title)
- 8105 :
document.title = title;
- 8106 :
};
- 8107 :
- 8108 :
// display a system message, list of types in common.css (below #message definition)
- 8109 :
this.display_message = function(msg, type, timeout, key)
- 8110 :
{
- 8111 :
if (msg && msg.length && /^[a-z._]+$/.test(msg))
- 8112 :
msg = this.get_label(msg);
- 8113 :
- 8114 :
// pass command to parent window
- 8115 :
if (this.is_framed())
- 8116 :
return parent.rcmail.display_message(msg, type, timeout);
- 8117 :
- 8118 :
if (!this.gui_objects.message) {
- 8119 :
// save message in order to display after page loaded
- 8120 :
if (type != 'loading')
- 8121 :
this.pending_message = [msg, type, timeout, key];
- 8122 :
return 1;
- 8123 :
}
- 8124 :
- 8125 :
if (!type)
- 8126 :
type = 'notice';
- 8127 :
else if (type == 'loading') {
- 8128 :
if (!key)
- 8129 :
key = 'loading';
- 8130 :
if (!timeout)
- 8131 :
timeout = this.env.request_timeout * 1000;
- 8132 :
if (!msg)
- 8133 :
msg = this.get_label('loading');
- 8134 :
}
- 8135 :
- 8136 :
if (!key)
- 8137 :
key = this.html_identifier(msg);
- 8138 :
- 8139 :
var date = new Date(),
- 8140 :
id = type + date.getTime();
- 8141 :
- 8142 :
if (!timeout) {
- 8143 :
switch (type) {
- 8144 :
case 'error':
- 8145 :
case 'warning':
- 8146 :
timeout = this.message_time * 2;
- 8147 :
break;
- 8148 :
- 8149 :
case 'uploading':
- 8150 :
timeout = 0;
- 8151 :
break;
- 8152 :
- 8153 :
default:
- 8154 :
timeout = this.message_time;
- 8155 :
}
- 8156 :
}
- 8157 :
- 8158 :
// The same message is already displayed
- 8159 :
if (this.messages[key]) {
- 8160 :
// replace label
- 8161 :
if (this.messages[key].obj)
- 8162 :
$('div.content', this.messages[key].obj).html(msg);
- 8163 :
// store label in stack
- 8164 :
if (type == 'loading') {
- 8165 :
this.messages[key].labels.push({'id': id, 'msg': msg});
- 8166 :
}
- 8167 :
// add element and set timeout
- 8168 :
this.messages[key].elements.push(id);
- 8169 :
setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout);
- 8170 :
return id;
- 8171 :
}
- 8172 :
- 8173 :
// create DOM object and display it
- 8174 :
var obj = $('<div>').addClass(type + ' content').html(msg).data('key', key),
- 8175 :
cont = $(this.gui_objects.message).append(obj).show();
- 8176 :
- 8177 :
this.messages[key] = {'obj': obj, 'elements': [id]};
- 8178 :
- 8179 :
if (type == 'loading') {
- 8180 :
this.messages[key].labels = [{'id': id, 'msg': msg}];
- 8181 :
}
- 8182 :
else if (type != 'uploading') {
- 8183 :
obj.click(function() { return ref.hide_message(obj); })
- 8184 :
.attr('role', 'alert');
- 8185 :
}
- 8186 :
- 8187 :
this.triggerEvent('message', { message:msg, type:type, timeout:timeout, object:obj });
- 8188 :
- 8189 :
if (timeout > 0)
- 8190 :
setTimeout(function() { ref.hide_message(id, type != 'loading'); }, timeout);
- 8191 :
- 8192 :
return id;
- 8193 :
};
- 8194 :
- 8195 :
// make a message to disappear
- 8196 :
this.hide_message = function(obj, fade)
- 8197 :
{
- 8198 :
// pass command to parent window
- 8199 :
if (this.is_framed())
- 8200 :
return parent.rcmail.hide_message(obj, fade);
- 8201 :
- 8202 :
if (!this.gui_objects.message)
- 8203 :
return;
- 8204 :
- 8205 :
var k, n, i, o, m = this.messages;
- 8206 :
- 8207 :
// Hide message by object, don't use for 'loading'!
- 8208 :
if (typeof obj === 'object') {
- 8209 :
o = $(obj);
- 8210 :
k = o.data('key');
- 8211 :
this.hide_message_object(o, fade);
- 8212 :
if (m[k])
- 8213 :
delete m[k];
- 8214 :
}
- 8215 :
// Hide message by id
- 8216 :
else {
- 8217 :
for (k in m) {
- 8218 :
for (n in m[k].elements) {
- 8219 :
if (m[k] && m[k].elements[n] == obj) {
- 8220 :
m[k].elements.splice(n, 1);
- 8221 :
// hide DOM element if last instance is removed
- 8222 :
if (!m[k].elements.length) {
- 8223 :
this.hide_message_object(m[k].obj, fade);
- 8224 :
delete m[k];
- 8225 :
}
- 8226 :
// set pending action label for 'loading' message
- 8227 :
else if (k == 'loading') {
- 8228 :
for (i in m[k].labels) {
- 8229 :
if (m[k].labels[i].id == obj) {
- 8230 :
delete m[k].labels[i];
- 8231 :
}
- 8232 :
else {
- 8233 :
o = m[k].labels[i].msg;
- 8234 :
$('div.content', m[k].obj).html(o);
- 8235 :
}
- 8236 :
}
- 8237 :
}
- 8238 :
}
- 8239 :
}
- 8240 :
}
- 8241 :
}
- 8242 :
};
- 8243 :
- 8244 :
// hide message object and remove from the DOM
- 8245 :
this.hide_message_object = function(o, fade)
- 8246 :
{
- 8247 :
if (fade)
- 8248 :
o.fadeOut(600, function() { $(this).remove(); });
- 8249 :
else
- 8250 :
o.hide().remove();
- 8251 :
};
- 8252 :
- 8253 :
// remove all messages immediately
- 8254 :
this.clear_messages = function()
- 8255 :
{
- 8256 :
// pass command to parent window
- 8257 :
if (this.is_framed())
- 8258 :
return parent.rcmail.clear_messages();
- 8259 :
- 8260 :
var k, n, m = this.messages;
- 8261 :
- 8262 :
for (k in m)
- 8263 :
for (n in m[k].elements)
- 8264 :
if (m[k].obj)
- 8265 :
this.hide_message_object(m[k].obj);
- 8266 :
- 8267 :
this.messages = {};
- 8268 :
};
- 8269 :
- 8270 :
// display uploading message with progress indicator
- 8271 :
// data should contain: name, total, current, percent, text
- 8272 :
this.display_progress = function(data)
- 8273 :
{
- 8274 :
if (!data || !data.name)
- 8275 :
return;
- 8276 :
- 8277 :
var msg = this.messages['progress' + data.name];
- 8278 :
- 8279 :
if (!data.label)
- 8280 :
data.label = this.get_label('uploadingmany');
- 8281 :
- 8282 :
if (!msg) {
- 8283 :
if (!data.percent || data.percent < 100)
- 8284 :
this.display_message(data.label, 'uploading', 0, 'progress' + data.name);
- 8285 :
return;
- 8286 :
}
- 8287 :
- 8288 :
if (!data.total || data.percent >= 100) {
- 8289 :
this.hide_message(msg.obj);
- 8290 :
return;
- 8291 :
}
- 8292 :
- 8293 :
if (data.text)
- 8294 :
data.label += ' ' + data.text;
- 8295 :
- 8296 :
msg.obj.text(data.label);
- 8297 :
};
- 8298 :
- 8299 :
// open a jquery UI dialog with the given content
- 8300 :
this.show_popup_dialog = function(content, title, buttons, options)
- 8301 :
{
- 8302 :
// forward call to parent window
- 8303 :
if (this.is_framed()) {
- 8304 :
return parent.rcmail.show_popup_dialog(content, title, buttons, options);
- 8305 :
}
- 8306 :
- 8307 :
var popup = $('<div class="popup">');
- 8308 :
- 8309 :
if (typeof content == 'object') {
- 8310 :
popup.append(content);
- 8311 :
if ($(content).is('iframe'))
- 8312 :
popup.addClass('iframe');
- 8313 :
}
- 8314 :
else
- 8315 :
popup.html(content);
- 8316 :
- 8317 :
// assign special classes to dialog buttons
- 8318 :
var i = 0, fn = function(button, classes, idx) {
- 8319 :
if (typeof button == 'function') {
- 8320 :
button = {
- 8321 :
click: button,
- 8322 :
text: idx,
- 8323 :
'class': classes
- 8324 :
};
- 8325 :
}
- 8326 :
else {
- 8327 :
buttons['class'] = classes;
- 8328 :
}
- 8329 :
- 8330 :
return button;
- 8331 :
};
- 8332 :
- 8333 :
if (options && options.button_classes)
- 8334 :
$.each(buttons, function(idx, button) {
- 8335 :
var cl = options.button_classes[i];
- 8336 :
if (cl)
- 8337 :
buttons[idx] = fn(button, cl, idx);
- 8338 :
i++;
- 8339 :
});
- 8340 :
- 8341 :
options = $.extend({
- 8342 :
title: title,
- 8343 :
buttons: buttons,
- 8344 :
modal: true,
- 8345 :
resizable: true,
- 8346 :
width: 500,
- 8347 :
close: function(event, ui) { $(this).remove(); }
- 8348 :
}, options || {});
- 8349 :
- 8350 :
popup.dialog(options);
- 8351 :
- 8352 :
if (options.width)
- 8353 :
popup.width(options.width);
- 8354 :
if (options.height)
- 8355 :
popup.height(options.height);
- 8356 :
- 8357 :
var dialog = popup.parent();
- 8358 :
- 8359 :
if (!options.noresize) {
- 8360 :
// resize and center popup
- 8361 :
var win = $(window), w = win.width(), h = win.height(),
- 8362 :
width = popup.width(),
- 8363 :
height = options.height || (popup[0].scrollHeight + 20),
- 8364 :
titlebar_height = $('.ui-dialog-titlebar', dialog).outerHeight() || 0,
- 8365 :
buttonpane_height = $('.ui-dialog-buttonpane', dialog).outerHeight() || 0,
- 8366 :
padding = (parseInt(dialog.css('padding-top')) + parseInt(popup.css('padding-top'))) * 2;
- 8367 :
- 8368 :
popup.dialog('option', {
- 8369 :
height: Math.min(h - 40, height + titlebar_height + buttonpane_height + padding + 2),
- 8370 :
width: Math.min(w - 20, width + 28)
- 8371 :
});
- 8372 :
}
- 8373 :
else {
- 8374 :
popup.css('width', 'auto');
- 8375 :
}
- 8376 :
- 8377 :
// Don't propagate keyboard events to the UI below the dialog (#6055)
- 8378 :
dialog.on('keydown keyup', function(e) { e.stopPropagation(); });
- 8379 :
- 8380 :
// Add Enter key handler to the input, click the 'mainaction' button
- 8381 :
dialog.find('input[data-submit]').on('keydown', function(e) {
- 8382 :
if (e.which == 13) {
- 8383 :
dialog.find('.ui-dialog-buttonpane button.mainaction').click();
- 8384 :
}
- 8385 :
});
- 8386 :
- 8387 :
this.triggerEvent('dialog-open', {obj: popup});
- 8388 :
- 8389 :
return popup;
- 8390 :
};
- 8391 :
- 8392 :
// show_popup_dialog() wrapper for simple dialogs with action and Cancel buttons
- 8393 :
this.simple_dialog = function(content, title, action_func, options)
- 8394 :
{
- 8395 :
if (!options)
- 8396 :
options = {};
- 8397 :
- 8398 :
var title = this.get_label(title),
- 8399 :
save_label = options.button || 'save',
- 8400 :
save_class = options.button_class || save_label.replace(/^[^\.]+\./i, ''),
- 8401 :
cancel_label = options.cancel_button || 'cancel',
- 8402 :
cancel_class = options.cancel_class || cancel_label.replace(/^[^\.]+\./i, ''),
- 8403 :
close_func = function(e, ui, dialog) {
- 8404 :
(ref.is_framed() ? parent.$ : $)(dialog || this).dialog('close');
- 8405 :
if (options.cancel_func) options.cancel_func(e, ref);
- 8406 :
},
- 8407 :
buttons = [{
- 8408 :
text: this.get_label(cancel_label),
- 8409 :
'class': cancel_class.replace(/close/i, 'cancel'),
- 8410 :
click: close_func
- 8411 :
}];
- 8412 :
- 8413 :
if (!action_func)
- 8414 :
buttons[0]['class'] += ' mainaction';
- 8415 :
else
- 8416 :
buttons.unshift({
- 8417 :
text: this.get_label(save_label),
- 8418 :
'class': 'mainaction ' + save_class,
- 8419 :
click: function(e, ui) { if (action_func(e, ref)) close_func(e, ui, this); }
- 8420 :
});
- 8421 :
- 8422 :
return this.show_popup_dialog(content, title, buttons, options);
- 8423 :
};
- 8424 :
- 8425 :
// show_popup_dialog() wrapper for alert() type dialogs
- 8426 :
this.alert_dialog = function(content, action, options)
- 8427 :
{
- 8428 :
options = $.extend(options || {}, {
- 8429 :
cancel_button: 'ok',
- 8430 :
cancel_class: 'save',
- 8431 :
cancel_func: action,
- 8432 :
noresize: true
- 8433 :
});
- 8434 :
- 8435 :
return this.simple_dialog(content, options.title || 'alerttitle', null, options);
- 8436 :
};
- 8437 :
- 8438 :
// simple_dialog() wrapper for confirm() type dialogs
- 8439 :
this.confirm_dialog = function(content, button_label, action, options)
- 8440 :
{
- 8441 :
var action_func = function(e, ref) { action(e, ref); return true; };
- 8442 :
- 8443 :
options = $.extend(options || {}, {
- 8444 :
button: button_label || 'continue',
- 8445 :
noresize: true
- 8446 :
});
- 8447 :
- 8448 :
return this.simple_dialog(content, options.title || 'confirmationtitle', action_func, options);
- 8449 :
};
- 8450 :
- 8451 :
// enable/disable buttons for page shifting
- 8452 :
this.set_page_buttons = function()
- 8453 :
{
- 8454 :
this.enable_command('nextpage', 'lastpage', this.env.pagecount > this.env.current_page);
- 8455 :
this.enable_command('previouspage', 'firstpage', this.env.current_page > 1);
- 8456 :
- 8457 :
this.update_pagejumper();
- 8458 :
};
- 8459 :
- 8460 :
// mark a mailbox as selected and set environment variable
- 8461 :
this.select_folder = function(name, prefix, encode)
- 8462 :
{
- 8463 :
if (this.savedsearchlist) {
- 8464 :
this.savedsearchlist.select('');
- 8465 :
}
- 8466 :
- 8467 :
if (this.treelist) {
- 8468 :
this.treelist.select(name);
- 8469 :
}
- 8470 :
else if (this.gui_objects.folderlist) {
- 8471 :
$('li.selected', this.gui_objects.folderlist).removeClass('selected');
- 8472 :
$(this.get_folder_li(name, prefix, encode)).addClass('selected');
- 8473 :
- 8474 :
// trigger event hook
- 8475 :
this.triggerEvent('selectfolder', { folder:name, prefix:prefix });
- 8476 :
}
- 8477 :
};
- 8478 :
- 8479 :
// adds a class to selected folder
- 8480 :
this.mark_folder = function(name, class_name, prefix, encode)
- 8481 :
{
- 8482 :
$(this.get_folder_li(name, prefix, encode)).addClass(class_name);
- 8483 :
this.triggerEvent('markfolder', {folder: name, mark: class_name, status: true});
- 8484 :
};
- 8485 :
- 8486 :
// adds a class to selected folder
- 8487 :
this.unmark_folder = function(name, class_name, prefix, encode)
- 8488 :
{
- 8489 :
$(this.get_folder_li(name, prefix, encode)).removeClass(class_name);
- 8490 :
this.triggerEvent('markfolder', {folder: name, mark: class_name, status: false});
- 8491 :
};
- 8492 :
- 8493 :
// helper method to find a folder list item
- 8494 :
this.get_folder_li = function(name, prefix, encode)
- 8495 :
{
- 8496 :
if (!prefix)
- 8497 :
prefix = 'rcmli';
- 8498 :
- 8499 :
if (this.gui_objects.folderlist) {
- 8500 :
name = this.html_identifier(name, encode);
- 8501 :
return document.getElementById(prefix+name);
- 8502 :
}
- 8503 :
};
- 8504 :
- 8505 :
// for reordering column array (Konqueror workaround)
- 8506 :
// and for setting some message list global variables
- 8507 :
this.set_message_coltypes = function(cols, repl, smart_col)
- 8508 :
{
- 8509 :
// update list mode columns list
- 8510 :
this.env.listcols = cols;
- 8511 :
- 8512 :
// reset message list cols
- 8513 :
this.msglist_setup(this.env.layout);
- 8514 :
- 8515 :
var list = this.message_list,
- 8516 :
thead = list ? list.thead : null,
- 8517 :
repl, cell, col, c, n, len, tr,
- 8518 :
listcols = this.env.msglist_cols;
- 8519 :
- 8520 :
if (!this.env.coltypes)
- 8521 :
this.env.coltypes = {};
- 8522 :
- 8523 :
// replace old column headers
- 8524 :
if (thead) {
- 8525 :
if (repl) {
- 8526 :
thead.innerHTML = '';
- 8527 :
tr = document.createElement('tr');
- 8528 :
- 8529 :
for (n in listcols) {
- 8530 :
c = listcols[n];
- 8531 :
cell = document.createElement('th');
- 8532 :
cell.innerHTML = repl[c].html || '';
- 8533 :
if (repl[c].id) cell.id = repl[c].id;
- 8534 :
if (repl[c].className) cell.className = repl[c].className;
- 8535 :
tr.appendChild(cell);
- 8536 :
}
- 8537 :
- 8538 :
if (list.checkbox_selection)
- 8539 :
list.insert_checkbox(tr, 'thead');
- 8540 :
- 8541 :
thead.appendChild(tr);
- 8542 :
}
- 8543 :
- 8544 :
for (n = 0, len = listcols.length; n < len; n++) {
- 8545 :
col = listcols[list.checkbox_selection ? n-1 : n];
- 8546 :
if ((cell = thead.rows[0].cells[n]) && (col == 'from' || col == 'to' || col == 'fromto')) {
- 8547 :
$(cell).attr('rel', col).find('span,a').text(this.get_label(col == 'fromto' ? smart_col : col));
- 8548 :
}
- 8549 :
}
- 8550 :
}
- 8551 :
- 8552 :
this.env.subject_col = null;
- 8553 :
this.env.flagged_col = null;
- 8554 :
this.env.status_col = null;
- 8555 :
- 8556 :
if (this.env.coltypes.folder)
- 8557 :
this.env.coltypes.folder.hidden = !(this.env.search_request || this.env.search_id) || this.env.search_scope == 'base';
- 8558 :
- 8559 :
if ((n = $.inArray('subject', listcols)) >= 0) {
- 8560 :
this.env.subject_col = n;
- 8561 :
if (list)
- 8562 :
list.subject_col = n;
- 8563 :
}
- 8564 :
if ((n = $.inArray('flag', listcols)) >= 0)
- 8565 :
this.env.flagged_col = n;
- 8566 :
if ((n = $.inArray('status', listcols)) >= 0)
- 8567 :
this.env.status_col = n;
- 8568 :
- 8569 :
if (list) {
- 8570 :
list.hide_column('folder', (this.env.coltypes.folder && this.env.coltypes.folder.hidden) || $.inArray('folder', listcols) < 0);
- 8571 :
list.init_header();
- 8572 :
}
- 8573 :
};
- 8574 :
- 8575 :
// replace content of row count display
- 8576 :
this.set_rowcount = function(text, mbox)
- 8577 :
{
- 8578 :
// #1487752
- 8579 :
if (mbox && mbox != this.env.mailbox)
- 8580 :
return false;
- 8581 :
- 8582 :
$(this.gui_objects.countdisplay).html(text);
- 8583 :
- 8584 :
// update page navigation buttons
- 8585 :
this.set_page_buttons();
- 8586 :
};
- 8587 :
- 8588 :
// replace content of mailboxname display
- 8589 :
this.set_mailboxname = function(content)
- 8590 :
{
- 8591 :
if (this.gui_objects.mailboxname && content)
- 8592 :
this.gui_objects.mailboxname.innerHTML = content;
- 8593 :
};
- 8594 :
- 8595 :
// replace content of quota display
- 8596 :
this.set_quota = function(content)
- 8597 :
{
- 8598 :
if (this.gui_objects.quotadisplay && content && content.type == 'text')
- 8599 :
$(this.gui_objects.quotadisplay).text((content.percent||0) + '%').attr('title', content.title || '');
- 8600 :
- 8601 :
this.triggerEvent('setquota', content);
- 8602 :
this.env.quota_content = content;
- 8603 :
};
- 8604 :
- 8605 :
// update trash folder state
- 8606 :
this.set_trash_count = function(count)
- 8607 :
{
- 8608 :
this[(count ? 'un' : '') + 'mark_folder'](this.env.trash_mailbox, 'empty', '', true);
- 8609 :
};
- 8610 :
- 8611 :
// update the mailboxlist
- 8612 :
this.set_unread_count = function(mbox, count, set_title, mark)
- 8613 :
{
- 8614 :
if (!this.gui_objects.mailboxlist)
- 8615 :
return false;
- 8616 :
- 8617 :
this.env.unread_counts[mbox] = count;
- 8618 :
this.set_unread_count_display(mbox, set_title);
- 8619 :
- 8620 :
if (mark)
- 8621 :
this.mark_folder(mbox, mark, '', true);
- 8622 :
else if (!count)
- 8623 :
this.unmark_folder(mbox, 'recent', '', true);
- 8624 :
- 8625 :
this.mark_all_read_state();
- 8626 :
};
- 8627 :
- 8628 :
// update the mailbox count display
- 8629 :
this.set_unread_count_display = function(mbox, set_title)
- 8630 :
{
- 8631 :
var reg, link, text_obj, item, mycount, childcount, div;
- 8632 :
- 8633 :
if (item = this.get_folder_li(mbox, '', true)) {
- 8634 :
mycount = this.env.unread_counts[mbox] ? this.env.unread_counts[mbox] : 0;
- 8635 :
link = $(item).children('a').eq(0);
- 8636 :
text_obj = link.children('span.unreadcount');
- 8637 :
if (!text_obj.length && mycount)
- 8638 :
text_obj = $('<span>').addClass('unreadcount skip-content').appendTo(link);
- 8639 :
reg = /\s+\([0-9]+\)$/i;
- 8640 :
- 8641 :
childcount = 0;
- 8642 :
if ((div = item.getElementsByTagName('div')[0]) &&
- 8643 :
div.className.match(/collapsed/)) {
- 8644 :
// add children's counters
- 8645 :
for (var k in this.env.unread_counts)
- 8646 :
if (k.startsWith(mbox + this.env.delimiter))
- 8647 :
childcount += this.env.unread_counts[k];
- 8648 :
}
- 8649 :
- 8650 :
if (mycount && text_obj.length)
- 8651 :
text_obj.html(this.env.unreadwrap.replace(/%[sd]/, mycount));
- 8652 :
else if (text_obj.length)
- 8653 :
text_obj.remove();
- 8654 :
- 8655 :
// set parent's display
- 8656 :
reg = new RegExp(RegExp.escape(this.env.delimiter) + '[^' + RegExp.escape(this.env.delimiter) + ']+$');
- 8657 :
if (mbox.match(reg))
- 8658 :
this.set_unread_count_display(mbox.replace(reg, ''), false);
- 8659 :
- 8660 :
// set the right classes
- 8661 :
if ((mycount+childcount)>0)
- 8662 :
$(item).addClass('unread');
- 8663 :
else
- 8664 :
$(item).removeClass('unread');
- 8665 :
}
- 8666 :
- 8667 :
// set unread count to window title
- 8668 :
reg = /^\([0-9]+\)\s+/i;
- 8669 :
if (set_title && document.title) {
- 8670 :
var new_title = '',
- 8671 :
doc_title = String(document.title);
- 8672 :
- 8673 :
if (mycount && doc_title.match(reg))
- 8674 :
new_title = doc_title.replace(reg, '('+mycount+') ');
- 8675 :
else if (mycount)
- 8676 :
new_title = '('+mycount+') '+doc_title;
- 8677 :
else
- 8678 :
new_title = doc_title.replace(reg, '');
- 8679 :
- 8680 :
this.set_pagetitle(new_title);
- 8681 :
}
- 8682 :
};
- 8683 :
- 8684 :
// display fetched raw headers
- 8685 :
this.set_headers = function(content)
- 8686 :
{
- 8687 :
if (this.gui_objects.all_headers_box && content)
- 8688 :
$(this.gui_objects.all_headers_box).html(content).show();
- 8689 :
};
- 8690 :
- 8691 :
// display all-headers row and fetch raw message headers
- 8692 :
this.show_headers = function(props, elem)
- 8693 :
{
- 8694 :
if (!this.gui_objects.all_headers_row || !this.gui_objects.all_headers_box || !this.env.uid)
- 8695 :
return;
- 8696 :
- 8697 :
$(elem).removeClass('show-headers').addClass('hide-headers');
- 8698 :
$(this.gui_objects.all_headers_row).show();
- 8699 :
elem.onclick = function() { ref.command('hide-headers', '', elem); };
- 8700 :
- 8701 :
// fetch headers only once
- 8702 :
if (!this.gui_objects.all_headers_box.innerHTML) {
- 8703 :
this.http_request('headers', {_uid: this.env.uid, _mbox: this.env.mailbox},
- 8704 :
this.display_message('', 'loading')
- 8705 :
);
- 8706 :
}
- 8707 :
};
- 8708 :
- 8709 :
// hide all-headers row
- 8710 :
this.hide_headers = function(props, elem)
- 8711 :
{
- 8712 :
if (!this.gui_objects.all_headers_row || !this.gui_objects.all_headers_box)
- 8713 :
return;
- 8714 :
- 8715 :
$(elem).removeClass('hide-headers').addClass('show-headers');
- 8716 :
$(this.gui_objects.all_headers_row).hide();
- 8717 :
elem.onclick = function() { ref.command('show-headers', '', elem); };
- 8718 :
};
- 8719 :
- 8720 :
// create folder selector popup
- 8721 :
this.folder_selector = function(event, callback)
- 8722 :
{
- 8723 :
this.entity_selector('folder-selector', callback, this.env.mailboxes_list, function(obj, a) {
- 8724 :
var n = 0, s = 0,
- 8725 :
delim = ref.env.delimiter,
- 8726 :
folder = ref.env.mailboxes[obj],
- 8727 :
id = folder.id,
- 8728 :
row = $('<li>');
- 8729 :
- 8730 :
if (folder.virtual)
- 8731 :
a.addClass('virtual').attr({'aria-disabled': 'true', tabindex: '-1'});
- 8732 :
else
- 8733 :
a.addClass('active').data('id', folder.id);
- 8734 :
- 8735 :
if (folder['class'])
- 8736 :
row.addClass(folder['class']);
- 8737 :
- 8738 :
// calculate/set indentation level
- 8739 :
while ((s = id.indexOf(delim, s)) >= 0) {
- 8740 :
n++; s++;
- 8741 :
}
- 8742 :
a.css('padding-left', n ? (n * 16) + 'px' : 0);
- 8743 :
- 8744 :
// add folder name element
- 8745 :
a.append($('<span>').text(folder.name));
- 8746 :
- 8747 :
return row.append(a);
- 8748 :
}, event);
- 8749 :
};
- 8750 :
- 8751 :
// create addressbook selector popup
- 8752 :
this.addressbook_selector = function(event, callback)
- 8753 :
{
- 8754 :
// build addressbook + groups list
- 8755 :
var combined_sources = [];
- 8756 :
- 8757 :
// check we really need it before processing
- 8758 :
if (!this.entity_selectors['addressbook-selector']) {
- 8759 :
$.each(this.env.address_sources, function() {
- 8760 :
if (!this.readonly) {
- 8761 :
var source = this;
- 8762 :
combined_sources.push(source);
- 8763 :
- 8764 :
$.each(ref.env.contactgroups, function() {
- 8765 :
if (source.id === this.source) {
- 8766 :
combined_sources.push(this);
- 8767 :
}
- 8768 :
});
- 8769 :
}
- 8770 :
});
- 8771 :
}
- 8772 :
- 8773 :
this.entity_selector('addressbook-selector', callback, combined_sources, function(obj, a) {
- 8774 :
if (obj.type == 'group') {
- 8775 :
a.attr('rel', obj.source + ':' + obj.id)
- 8776 :
.addClass('contactgroup active')
- 8777 :
.data({source: obj.source, gid: obj.id, id: obj.source + ':' + obj.id})
- 8778 :
.css('padding-left', '16px');
- 8779 :
}
- 8780 :
else {
- 8781 :
a.addClass('addressbook active').data('id', obj.id);
- 8782 :
}
- 8783 :
a.append($('<span>').text(obj.name));
- 8784 :
- 8785 :
return $('<li>').append(a);
- 8786 :
}, event);
- 8787 :
};
- 8788 :
- 8789 :
// create contactgroup selector popup
- 8790 :
this.contactgroup_selector = function(event, callback)
- 8791 :
{
- 8792 :
this.entity_selector('contactgroup-selector', callback, this.env.contactgroups, function(obj, a) {
- 8793 :
if (ref.env.source === obj.source) {
- 8794 :
a.addClass('contactgroup active')
- 8795 :
.data({id: obj.id})
- 8796 :
.append($('<span>').text(obj.name));
- 8797 :
- 8798 :
return $('<li>').append(a);
- 8799 :
}
- 8800 :
}, event);
- 8801 :
};
- 8802 :
- 8803 :
// create selector popup (eg for folders or address books), position and display it
- 8804 :
this.entity_selector = function(name, click_callback, entity_list, list_callback, event)
- 8805 :
{
- 8806 :
var container = this.entity_selectors[name];
- 8807 :
- 8808 :
if (!container) {
- 8809 :
var rows = [],
- 8810 :
container = $('<div>').attr('id', name).addClass('popupmenu'),
- 8811 :
ul = $('<ul>').addClass('toolbarmenu menu'),
- 8812 :
link = document.createElement('a');
- 8813 :
- 8814 :
link.href = '#';
- 8815 :
link.className = 'icon';
- 8816 :
- 8817 :
// loop over entity list
- 8818 :
$.each(entity_list, function(i) {
- 8819 :
var a = $(link.cloneNode(false)).attr('rel', this.id);
- 8820 :
rows.push(list_callback(this, a, i));
- 8821 :
});
- 8822 :
- 8823 :
ul.append(rows).appendTo(container);
- 8824 :
- 8825 :
// temporarily show element to calculate its size
- 8826 :
container.css({left: '-1000px', top: '-1000px'})
- 8827 :
.appendTo(document.body).show();
- 8828 :
- 8829 :
// set max-height if the list is long
- 8830 :
if (rows.length > 10)
- 8831 :
container.css('max-height', $('li', container)[0].offsetHeight * 10 + 9);
- 8832 :
- 8833 :
// register delegate event handler for folder item clicks
- 8834 :
container.on('click', 'a.active', function(e) {
- 8835 :
container.data('callback')($(this).data('id'), this);
- 8836 :
});
- 8837 :
- 8838 :
this.entity_selectors[name] = container;
- 8839 :
}
- 8840 :
- 8841 :
container.data('callback', click_callback);
- 8842 :
- 8843 :
// position menu on the screen
- 8844 :
this.show_menu(name, true, event);
- 8845 :
};
- 8846 :
- 8847 :
this.destroy_entity_selector = function(name)
- 8848 :
{
- 8849 :
$("#" + name).remove();
- 8850 :
delete this.entity_selectors[name];
- 8851 :
this.triggerEvent('destroy-entity-selector', { name: name });
- 8852 :
};
- 8853 :
- 8854 :
/***********************************************/
- 8855 :
/********* popup menu functions *********/
- 8856 :
/***********************************************/
- 8857 :
- 8858 :
// Show/hide a specific popup menu
- 8859 :
this.show_menu = function(prop, show, event)
- 8860 :
{
- 8861 :
var name = typeof prop == 'object' ? prop.menu : prop,
- 8862 :
obj = $('#'+name),
- 8863 :
ref = event && event.target ? $(event.target) : $(obj.attr('rel') || '#'+name+'link'),
- 8864 :
keyboard = rcube_event.is_keyboard(event),
- 8865 :
align = obj.attr('data-align') || '',
- 8866 :
stack = false;
- 8867 :
- 8868 :
// find "real" button element
- 8869 :
if (ref.get(0).tagName != 'A' && ref.closest('a').length)
- 8870 :
ref = ref.closest('a');
- 8871 :
- 8872 :
if (typeof prop == 'string')
- 8873 :
prop = { menu:name };
- 8874 :
- 8875 :
// let plugins or skins provide the menu element
- 8876 :
if (!obj.length) {
- 8877 :
obj = this.triggerEvent('menu-get', { name:name, props:prop, originalEvent:event });
- 8878 :
}
- 8879 :
- 8880 :
if (!obj || !obj.length) {
- 8881 :
// just delegate the action to subscribers
- 8882 :
return this.triggerEvent(show === false ? 'menu-close' : 'menu-open', { name:name, props:prop, originalEvent:event });
- 8883 :
}
- 8884 :
- 8885 :
// move element to top for proper absolute positioning
- 8886 :
obj.appendTo(document.body);
- 8887 :
- 8888 :
if (typeof show == 'undefined')
- 8889 :
show = obj.is(':visible') ? false : true;
- 8890 :
- 8891 :
if (show && ref.length) {
- 8892 :
var win = $(window),
- 8893 :
pos = ref.offset(),
- 8894 :
above = align.indexOf('bottom') >= 0;
- 8895 :
- 8896 :
stack = ref.attr('role') == 'menuitem' || ref.closest('[role=menuitem]').length > 0;
- 8897 :
- 8898 :
ref.offsetWidth = ref.outerWidth();
- 8899 :
ref.offsetHeight = ref.outerHeight();
- 8900 :
if (!above && pos.top + ref.offsetHeight + obj.height() > win.height()) {
- 8901 :
above = true;
- 8902 :
}
- 8903 :
if (align.indexOf('right') >= 0) {
- 8904 :
pos.left = pos.left + ref.outerWidth() - obj.width();
- 8905 :
}
- 8906 :
else if (stack) {
- 8907 :
pos.left = pos.left + ref.offsetWidth - 5;
- 8908 :
pos.top -= ref.offsetHeight;
- 8909 :
}
- 8910 :
if (pos.left + obj.width() > win.width()) {
- 8911 :
pos.left = win.width() - obj.width() - 12;
- 8912 :
}
- 8913 :
pos.top = Math.max(0, pos.top + (above ? -obj.height() : ref.offsetHeight));
- 8914 :
obj.css({ left:pos.left+'px', top:pos.top+'px' });
- 8915 :
}
- 8916 :
- 8917 :
// add menu to stack
- 8918 :
if (show) {
- 8919 :
// truncate stack down to the one containing the ref link
- 8920 :
for (var i = this.menu_stack.length - 1; stack && i >= 0; i--) {
- 8921 :
if (!$(ref).parents('#'+this.menu_stack[i]).length && $(event.target).parent().attr('role') != 'menuitem')
- 8922 :
this.hide_menu(this.menu_stack[i], event);
- 8923 :
}
- 8924 :
if (stack && this.menu_stack.length) {
- 8925 :
obj.data('parent', $.last(this.menu_stack));
- 8926 :
obj.css('z-index', ($('#'+$.last(this.menu_stack)).css('z-index') || 0) + 1);
- 8927 :
}
- 8928 :
else if (!stack && this.menu_stack.length) {
- 8929 :
this.hide_menu(this.menu_stack[0], event);
- 8930 :
}
- 8931 :
- 8932 :
obj.show().attr('aria-hidden', 'false').data('opener', ref.attr('aria-expanded', 'true').get(0));
- 8933 :
this.triggerEvent('menu-open', { name:name, obj:obj, props:prop, originalEvent:event });
- 8934 :
this.menu_stack.push(name);
- 8935 :
- 8936 :
this.menu_keyboard_active = show && keyboard;
- 8937 :
if (this.menu_keyboard_active) {
- 8938 :
this.focused_menu = name;
- 8939 :
obj.find('a,input:not(:disabled)').not('[aria-disabled=true]').first().focus();
- 8940 :
}
- 8941 :
}
- 8942 :
else { // close menu
- 8943 :
this.hide_menu(name, event);
- 8944 :
}
- 8945 :
- 8946 :
return show;
- 8947 :
};
- 8948 :
- 8949 :
// hide the given popup menu (and its children)
- 8950 :
this.hide_menu = function(name, event)
- 8951 :
{
- 8952 :
if (!this.menu_stack.length) {
- 8953 :
// delegate to subscribers
- 8954 :
this.triggerEvent('menu-close', { name:name, props:{ menu:name }, originalEvent:event });
- 8955 :
return;
- 8956 :
}
- 8957 :
- 8958 :
var obj, keyboard = rcube_event.is_keyboard(event);
- 8959 :
for (var j=this.menu_stack.length-1; j >= 0; j--) {
- 8960 :
obj = $('#' + this.menu_stack[j]).hide().attr('aria-hidden', 'true').data('parent', false);
- 8961 :
this.triggerEvent('menu-close', { name:this.menu_stack[j], obj:obj, props:{ menu:this.menu_stack[j] }, originalEvent:event });
- 8962 :
if (this.menu_stack[j] == name) {
- 8963 :
j = -1; // stop loop
- 8964 :
if (obj.data('opener')) {
- 8965 :
$(obj.data('opener')).attr('aria-expanded', 'false');
- 8966 :
if (keyboard)
- 8967 :
obj.data('opener').focus();
- 8968 :
}
- 8969 :
}
- 8970 :
this.menu_stack.pop();
- 8971 :
}
- 8972 :
- 8973 :
// focus previous menu in stack
- 8974 :
if (this.menu_stack.length && keyboard) {
- 8975 :
this.menu_keyboard_active = true;
- 8976 :
this.focused_menu = $.last(this.menu_stack);
- 8977 :
if (!obj || !obj.data('opener'))
- 8978 :
$('#'+this.focused_menu).find('a,input:not(:disabled)').not('[aria-disabled=true]').first().focus();
- 8979 :
}
- 8980 :
else {
- 8981 :
this.focused_menu = null;
- 8982 :
this.menu_keyboard_active = false;
- 8983 :
}
- 8984 :
};
- 8985 :
- 8986 :
// position a menu element on the screen in relation to other object
- 8987 :
this.element_position = function(element, obj)
- 8988 :
{
- 8989 :
var obj = $(obj), win = $(window),
- 8990 :
width = obj.outerWidth(),
- 8991 :
height = obj.outerHeight(),
- 8992 :
menu_pos = obj.data('menu-pos'),
- 8993 :
win_height = win.height(),
- 8994 :
elem_height = $(element).height(),
- 8995 :
elem_width = $(element).width(),
- 8996 :
pos = obj.offset(),
- 8997 :
top = pos.top,
- 8998 :
left = pos.left + width;
- 8999 :
- 9000 :
if (menu_pos == 'bottom') {
- 9001 :
top += height;
- 9002 :
left -= width;
- 9003 :
}
- 9004 :
else
- 9005 :
left -= 5;
- 9006 :
- 9007 :
if (top + elem_height > win_height) {
- 9008 :
top -= elem_height - height;
- 9009 :
if (top < 0)
- 9010 :
top = Math.max(0, (win_height - elem_height) / 2);
- 9011 :
}
- 9012 :
- 9013 :
if (left + elem_width > win.width())
- 9014 :
left -= elem_width + width;
- 9015 :
- 9016 :
element.css({left: left + 'px', top: top + 'px'});
- 9017 :
};
- 9018 :
- 9019 :
// initialize HTML editor
- 9020 :
this.editor_init = function(config, id)
- 9021 :
{
- 9022 :
this.editor = new rcube_text_editor(config, id);
- 9023 :
};
- 9024 :
- 9025 :
- 9026 :
/********************************************************/
- 9027 :
/********* html to text conversion functions *********/
- 9028 :
/********************************************************/
- 9029 :
- 9030 :
this.html2plain = function(html, func)
- 9031 :
{
- 9032 :
return this.format_converter(html, 'html', func);
- 9033 :
};
- 9034 :
- 9035 :
this.plain2html = function(plain, func)
- 9036 :
{
- 9037 :
return this.format_converter(plain, 'plain', func);
- 9038 :
};
- 9039 :
- 9040 :
this.format_converter = function(text, format, func)
- 9041 :
{
- 9042 :
// warn the user (if converted content is not empty)
- 9043 :
if (!text
- 9044 :
|| (format == 'html' && !(text.replace(/<[^>]+>| |\xC2\xA0|\s/g, '')).length)
- 9045 :
|| (format != 'html' && !(text.replace(/\xC2\xA0|\s/g, '')).length)
- 9046 :
) {
- 9047 :
// without setTimeout() here, textarea is filled with initial (onload) content
- 9048 :
if (func)
- 9049 :
setTimeout(function() { func(''); }, 50);
- 9050 :
return true;
- 9051 :
}
- 9052 :
- 9053 :
var confirmed = this.env.editor_warned || confirm(this.get_label('editorwarning'));
- 9054 :
- 9055 :
this.env.editor_warned = true;
- 9056 :
- 9057 :
if (!confirmed)
- 9058 :
return false;
- 9059 :
- 9060 :
var url = '?_task=utils&_action=' + (format == 'html' ? 'html2text' : 'text2html'),
- 9061 :
lock = this.set_busy(true, 'converting');
- 9062 :
- 9063 :
$.ajax({ type: 'POST', url: url, data: text, contentType: 'application/octet-stream',
- 9064 :
error: function(o, status, err) { ref.http_error(o, status, err, lock); },
- 9065 :
success: function(data) {
- 9066 :
ref.set_busy(false, null, lock);
- 9067 :
if (func) func(data);
- 9068 :
}
- 9069 :
});
- 9070 :
- 9071 :
return true;
- 9072 :
};
- 9073 :
- 9074 :
- 9075 :
/********************************************************/
- 9076 :
/********* remote request methods *********/
- 9077 :
/********************************************************/
- 9078 :
- 9079 :
// compose a valid url with the given parameters
- 9080 :
this.url = function(action, query)
- 9081 :
{
- 9082 :
var querystring = typeof query === 'string' ? query : '';
- 9083 :
- 9084 :
if (typeof action !== 'string')
- 9085 :
query = action;
- 9086 :
else if (!query || typeof query !== 'object')
- 9087 :
query = {};
- 9088 :
- 9089 :
if (action)
- 9090 :
query._action = action;
- 9091 :
else if (this.env.action)
- 9092 :
query._action = this.env.action;
- 9093 :
- 9094 :
var url = this.env.comm_path, k, param = {};
- 9095 :
- 9096 :
// overwrite task name
- 9097 :
if (action && action.match(/([a-z0-9_-]+)\/([a-z0-9-_.]+)/)) {
- 9098 :
query._action = RegExp.$2;
- 9099 :
url = url.replace(/\_task=[a-z0-9_-]+/, '_task=' + RegExp.$1);
- 9100 :
}
- 9101 :
- 9102 :
// force _framed=0
- 9103 :
if (query._framed === 0) {
- 9104 :
url = url.replace('&_framed=1', '');
- 9105 :
query._framed = null;
- 9106 :
}
- 9107 :
- 9108 :
// remove undefined values
- 9109 :
for (k in query) {
- 9110 :
if (query[k] !== undefined && query[k] !== null)
- 9111 :
param[k] = query[k];
- 9112 :
}
- 9113 :
- 9114 :
if (param = $.param(param))
- 9115 :
url += (url.indexOf('?') > -1 ? '&' : '?') + param;
- 9116 :
- 9117 :
if (querystring)
- 9118 :
url += (url.indexOf('?') > -1 ? '&' : '?') + querystring;
- 9119 :
- 9120 :
return url;
- 9121 :
};
- 9122 :
- 9123 :
this.redirect = function(url, lock)
- 9124 :
{
- 9125 :
if (lock !== false)
- 9126 :
this.set_busy(true, 'loading');
- 9127 :
- 9128 :
if (this.is_framed()) {
- 9129 :
url = url.replace(/&_framed=1/, '');
- 9130 :
parent.rcmail.redirect(url, lock);
- 9131 :
}
- 9132 :
else {
- 9133 :
if (this.env.extwin) {
- 9134 :
if (typeof url == 'string')
- 9135 :
url += (url.indexOf('?') < 0 ? '?' : '&') + '_extwin=1';
- 9136 :
else
- 9137 :
url._extwin = 1;
- 9138 :
}
- 9139 :
this.location_href(url, window);
- 9140 :
}
- 9141 :
};
- 9142 :
- 9143 :
this.goto_url = function(action, query, lock, secure)
- 9144 :
{
- 9145 :
var url = this.url(action, query)
- 9146 :
if (secure) url = this.secure_url(url);
- 9147 :
this.redirect(url, lock);
- 9148 :
};
- 9149 :
- 9150 :
this.location_href = function(url, target, frame)
- 9151 :
{
- 9152 :
if (frame)
- 9153 :
this.lock_frame(target);
- 9154 :
- 9155 :
if (typeof url == 'object')
- 9156 :
url = this.env.comm_path + '&' + $.param(url);
- 9157 :
- 9158 :
// simulate real link click to force IE to send referer header
- 9159 :
if (bw.ie && target == window)
- 9160 :
$('<a>').attr('href', url).appendTo(document.body).get(0).click();
- 9161 :
else
- 9162 :
target.location.href = url;
- 9163 :
- 9164 :
// reset keep-alive interval
- 9165 :
this.start_keepalive();
- 9166 :
};
- 9167 :
- 9168 :
// update browser location to remember current view
- 9169 :
this.update_state = function(query)
- 9170 :
{
- 9171 :
if (window.history.replaceState)
- 9172 :
try {
- 9173 :
// This may throw security exception in Firefox (#5400)
- 9174 :
window.history.replaceState({}, document.title, rcmail.url('', query));
- 9175 :
}
- 9176 :
catch(e) { /* ignore */ };
- 9177 :
};
- 9178 :
- 9179 :
// send a http request to the server
- 9180 :
this.http_request = function(action, data, lock, type)
- 9181 :
{
- 9182 :
if (type != 'POST')
- 9183 :
type = 'GET';
- 9184 :
- 9185 :
if (typeof data !== 'object')
- 9186 :
data = rcube_parse_query(data);
- 9187 :
- 9188 :
data._remote = 1;
- 9189 :
data._unlock = lock ? lock : 0;
- 9190 :
- 9191 :
// trigger plugin hook
- 9192 :
var result = this.triggerEvent('request' + action, data);
- 9193 :
- 9194 :
// abort if one of the handlers returned false
- 9195 :
if (result === false) {
- 9196 :
if (data._unlock)
- 9197 :
this.set_busy(false, null, data._unlock);
- 9198 :
return false;
- 9199 :
}
- 9200 :
else if (result && result.getResponseHeader) {
- 9201 :
return result;
- 9202 :
}
- 9203 :
else if (result !== undefined) {
- 9204 :
data = result;
- 9205 :
if (data._action) {
- 9206 :
action = data._action;
- 9207 :
delete data._action;
- 9208 :
}
- 9209 :
}
- 9210 :
- 9211 :
var url = this.url(action);
- 9212 :
- 9213 :
// reset keep-alive interval
- 9214 :
this.start_keepalive();
- 9215 :
- 9216 :
// send request
- 9217 :
return $.ajax({
- 9218 :
type: type, url: url, data: data, dataType: 'json',
- 9219 :
success: function(data) { ref.http_response(data); },
- 9220 :
error: function(o, status, err) { ref.http_error(o, status, err, lock, action); }
- 9221 :
});
- 9222 :
};
- 9223 :
- 9224 :
// send a http GET request to the server
- 9225 :
this.http_get = this.http_request;
- 9226 :
- 9227 :
// send a http POST request to the server
- 9228 :
this.http_post = function(action, data, lock)
- 9229 :
{
- 9230 :
return this.http_request(action, data, lock, 'POST');
- 9231 :
};
- 9232 :
- 9233 :
// aborts ajax request
- 9234 :
this.abort_request = function(r)
- 9235 :
{
- 9236 :
if (r.request)
- 9237 :
r.request.abort();
- 9238 :
if (r.lock)
- 9239 :
this.set_busy(false, null, r.lock);
- 9240 :
};
- 9241 :
- 9242 :
// handle HTTP response
- 9243 :
this.http_response = function(response)
- 9244 :
{
- 9245 :
if (!response)
- 9246 :
return;
- 9247 :
- 9248 :
if (response.unlock)
- 9249 :
this.set_busy(false, null, response.unlock);
- 9250 :
- 9251 :
this.triggerEvent('responsebefore', {response: response});
- 9252 :
this.triggerEvent('responsebefore'+response.action, {response: response});
- 9253 :
- 9254 :
// set env vars
- 9255 :
if (response.env)
- 9256 :
this.set_env(response.env);
- 9257 :
- 9258 :
var i;
- 9259 :
- 9260 :
// we have labels to add
- 9261 :
if (typeof response.texts === 'object') {
- 9262 :
for (i in response.texts)
- 9263 :
if (typeof response.texts[i] === 'string')
- 9264 :
this.add_label(i, response.texts[i]);
- 9265 :
}
- 9266 :
- 9267 :
// if we get javascript code from server -> execute it
- 9268 :
if (response.exec) {
- 9269 :
eval(response.exec);
- 9270 :
}
- 9271 :
- 9272 :
// execute callback functions of plugins
- 9273 :
if (response.callbacks && response.callbacks.length) {
- 9274 :
for (i=0; i < response.callbacks.length; i++)
- 9275 :
this.triggerEvent(response.callbacks[i][0], response.callbacks[i][1]);
- 9276 :
}
- 9277 :
- 9278 :
// process the response data according to the sent action
- 9279 :
switch (response.action) {
- 9280 :
case 'mark':
- 9281 :
// Mark the message as Seen also in the opener/parent
- 9282 :
if ((this.env.action == 'show' || this.env.action == 'preview') && this.env.last_flag == 'SEEN')
- 9283 :
this.set_unread_message(this.env.uid, this.env.mailbox);
- 9284 :
break;
- 9285 :
- 9286 :
case 'delete':
- 9287 :
if (this.task == 'addressbook') {
- 9288 :
var sid, uid = this.contact_list.get_selection(), writable = false;
- 9289 :
- 9290 :
if (uid && this.contact_list.rows[uid]) {
- 9291 :
// search results, get source ID from record ID
- 9292 :
if (this.env.source == '') {
- 9293 :
sid = String(uid).replace(/^[^-]+-/, '');
- 9294 :
writable = sid && this.env.address_sources[sid] && !this.env.address_sources[sid].readonly;
- 9295 :
}
- 9296 :
else {
- 9297 :
writable = !this.env.address_sources[this.env.source].readonly;
- 9298 :
}
- 9299 :
}
- 9300 :
this.enable_command('delete', 'edit', writable);
- 9301 :
this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0));
- 9302 :
this.enable_command('export-selected', 'print', false);
- 9303 :
}
- 9304 :
- 9305 :
case 'move':
- 9306 :
if (this.env.action == 'show') {
- 9307 :
// re-enable commands on move/delete error
- 9308 :
this.enable_command(this.env.message_commands, true);
- 9309 :
if (!this.env.list_post)
- 9310 :
this.enable_command('reply-list', false);
- 9311 :
}
- 9312 :
else if (this.task == 'addressbook') {
- 9313 :
this.triggerEvent('listupdate', { list:this.contact_list, folder:this.env.source, rowcount:this.contact_list.rowcount });
- 9314 :
}
- 9315 :
- 9316 :
case 'purge':
- 9317 :
case 'expunge':
- 9318 :
if (this.task == 'mail') {
- 9319 :
if (!this.env.exists) {
- 9320 :
// clear preview pane content
- 9321 :
if (this.env.contentframe)
- 9322 :
this.show_contentframe(false);
- 9323 :
// disable commands useless when mailbox is empty
- 9324 :
this.enable_command(this.env.message_commands, 'purge', 'expunge',
- 9325 :
'select-all', 'select-none', 'expand-all', 'expand-unread', 'collapse-all', false);
- 9326 :
}
- 9327 :
if (this.message_list)
- 9328 :
this.triggerEvent('listupdate', { list:this.message_list, folder:this.env.mailbox, rowcount:this.message_list.rowcount });
- 9329 :
}
- 9330 :
break;
- 9331 :
- 9332 :
case 'refresh':
- 9333 :
case 'check-recent':
- 9334 :
// update message flags
- 9335 :
$.each(this.env.recent_flags || {}, function(uid, flags) {
- 9336 :
ref.set_message(uid, 'deleted', flags.deleted);
- 9337 :
ref.set_message(uid, 'replied', flags.answered);
- 9338 :
ref.set_message(uid, 'unread', !flags.seen);
- 9339 :
ref.set_message(uid, 'forwarded', flags.forwarded);
- 9340 :
ref.set_message(uid, 'flagged', flags.flagged);
- 9341 :
});
- 9342 :
delete this.env.recent_flags;
- 9343 :
- 9344 :
case 'getunread':
- 9345 :
case 'search':
- 9346 :
this.env.qsearch = null;
- 9347 :
case 'list':
- 9348 :
if (this.task == 'mail') {
- 9349 :
var is_multifolder = this.is_multifolder_listing(),
- 9350 :
list = this.message_list,
- 9351 :
uid = this.env.list_uid;
- 9352 :
- 9353 :
this.enable_command('show', 'select-all', 'select-none', this.env.messagecount > 0);
- 9354 :
this.enable_command('expunge', 'purge', this.env.exists && !is_multifolder);
- 9355 :
this.enable_command('import-messages', !is_multifolder);
- 9356 :
this.enable_command('expand-all', 'expand-unread', 'collapse-all', this.env.threading && this.env.messagecount && !is_multifolder);
- 9357 :
- 9358 :
if (list) {
- 9359 :
if (response.action == 'list' || response.action == 'search') {
- 9360 :
// highlight message row when we're back from message page
- 9361 :
if (uid) {
- 9362 :
if (uid === 'FIRST') {
- 9363 :
uid = list.get_first_row();
- 9364 :
}
- 9365 :
else if (uid === 'LAST') {
- 9366 :
uid = list.get_last_row();
- 9367 :
}
- 9368 :
else if (!list.rows[uid]) {
- 9369 :
uid += '-' + this.env.mailbox;
- 9370 :
}
- 9371 :
- 9372 :
if (uid && list.rows[uid]) {
- 9373 :
list.select(uid);
- 9374 :
}
- 9375 :
- 9376 :
delete this.env.list_uid;
- 9377 :
}
- 9378 :
- 9379 :
this.enable_command('set-listmode', this.env.threads && !is_multifolder);
- 9380 :
if (list.rowcount > 0 && !$(document.activeElement).is('input,textarea'))
- 9381 :
list.focus();
- 9382 :
- 9383 :
// trigger 'select' so all dependent actions update its state
- 9384 :
// e.g. plugins use this event to activate buttons (#1490647)
- 9385 :
list.triggerEvent('select');
- 9386 :
}
- 9387 :
- 9388 :
if (response.action != 'getunread')
- 9389 :
this.triggerEvent('listupdate', { list:list, folder:this.env.mailbox, rowcount:list.rowcount });
- 9390 :
}
- 9391 :
}
- 9392 :
else if (this.task == 'addressbook') {
- 9393 :
var list = this.contact_list,
- 9394 :
uid = this.env.list_uid;
- 9395 :
- 9396 :
this.enable_command('export', 'select-all', 'select-none', (list && list.rowcount > 0));
- 9397 :
- 9398 :
if (response.action == 'list' || response.action == 'search') {
- 9399 :
// PAMELA - Search contacts by source
- 9400 :
this.enable_command('search-create', response.action == 'search');
- 9401 :
this.enable_command('search-delete', this.env.search_id);
- 9402 :
this.update_group_commands();
- 9403 :
- 9404 :
if (list && uid) {
- 9405 :
if (uid === 'FIRST') {
- 9406 :
uid = list.get_first_row();
- 9407 :
}
- 9408 :
else if (uid === 'LAST') {
- 9409 :
uid = list.get_last_row();
- 9410 :
}
- 9411 :
- 9412 :
if (uid && list.rows[uid]) {
- 9413 :
list.select(uid);
- 9414 :
}
- 9415 :
- 9416 :
delete this.env.list_uid;
- 9417 :
- 9418 :
// trigger 'select' so all dependent actions update its state
- 9419 :
list.triggerEvent('select');
- 9420 :
}
- 9421 :
- 9422 :
if (list.rowcount > 0 && !$(document.activeElement).is('input,textarea'))
- 9423 :
list.focus();
- 9424 :
- 9425 :
this.triggerEvent('listupdate', { list:list, folder:this.env.source, rowcount:list.rowcount });
- 9426 :
}
- 9427 :
}
- 9428 :
break;
- 9429 :
- 9430 :
case 'list-contacts':
- 9431 :
case 'search-contacts':
- 9432 :
if (this.contact_list) {
- 9433 :
if (this.contact_list.rowcount > 0)
- 9434 :
this.contact_list.focus();
- 9435 :
this.triggerEvent('listupdate', { list:this.contact_list, rowcount:this.contact_list.rowcount });
- 9436 :
}
- 9437 :
break;
- 9438 :
}
- 9439 :
- 9440 :
if (response.unlock)
- 9441 :
this.hide_message(response.unlock);
- 9442 :
- 9443 :
this.triggerEvent('responseafter', {response: response});
- 9444 :
this.triggerEvent('responseafter'+response.action, {response: response});
- 9445 :
- 9446 :
// reset keep-alive interval
- 9447 :
this.start_keepalive();
- 9448 :
};
- 9449 :
- 9450 :
// handle HTTP request errors
- 9451 :
this.http_error = function(request, status, err, lock, action)
- 9452 :
{
- 9453 :
var errmsg = request.statusText;
- 9454 :
- 9455 :
this.set_busy(false, null, lock);
- 9456 :
request.abort();
- 9457 :
- 9458 :
// don't display error message on page unload (#1488547)
- 9459 :
if (this.unload)
- 9460 :
return;
- 9461 :
- 9462 :
if (request.status && errmsg)
- 9463 :
this.display_message(this.get_label('servererror') + ' (' + errmsg + ')', 'error');
- 9464 :
else if (status == 'timeout')
- 9465 :
this.display_message('requesttimedout', 'error');
- 9466 :
else if (request.status == 0 && status != 'abort')
- 9467 :
this.display_message('connerror', 'error');
- 9468 :
- 9469 :
// redirect to url specified in location header if not empty
- 9470 :
var location_url = request.getResponseHeader("Location");
- 9471 :
if (location_url && this.env.action != 'compose') // don't redirect on compose screen, contents might get lost (#1488926)
- 9472 :
this.redirect(location_url);
- 9473 :
- 9474 :
// 403 Forbidden response (CSRF prevention) - reload the page.
- 9475 :
// In case there's a new valid session it will be used, otherwise
- 9476 :
// login form will be presented (#1488960).
- 9477 :
if (request.status == 403) {
- 9478 :
(this.is_framed() ? parent : window).location.reload();
- 9479 :
return;
- 9480 :
}
- 9481 :
- 9482 :
// re-send keep-alive requests after 30 seconds
- 9483 :
if (action == 'keep-alive')
- 9484 :
setTimeout(function() { ref.keep_alive(); ref.start_keepalive(); }, 30000);
- 9485 :
};
- 9486 :
- 9487 :
// handler for session errors detected on the server
- 9488 :
this.session_error = function(redirect_url)
- 9489 :
{
- 9490 :
this.env.server_error = 401;
- 9491 :
- 9492 :
// save message in local storage and do not redirect
- 9493 :
if (this.env.action == 'compose') {
- 9494 :
this.save_compose_form_local();
- 9495 :
this.compose_skip_unsavedcheck = true;
- 9496 :
// stop keep-alive and refresh processes
- 9497 :
this.env.session_lifetime = 0;
- 9498 :
if (this._keepalive)
- 9499 :
clearInterval(this._keepalive);
- 9500 :
if (this._refresh)
- 9501 :
clearInterval(this._refresh);
- 9502 :
}
- 9503 :
else if (redirect_url) {
- 9504 :
setTimeout(function() { ref.redirect(redirect_url, true); }, 2000);
- 9505 :
}
- 9506 :
};
- 9507 :
- 9508 :
// callback when an iframe finished loading
- 9509 :
this.iframe_loaded = function(unlock)
- 9510 :
{
- 9511 :
if (!unlock)
- 9512 :
unlock = this.env.frame_lock;
- 9513 :
- 9514 :
this.set_busy(false, null, unlock);
- 9515 :
- 9516 :
if (this.submit_timer)
- 9517 :
clearTimeout(this.submit_timer);
- 9518 :
};
- 9519 :
- 9520 :
/**
- 9521 :
Send multi-threaded parallel HTTP requests to the server for a list if items.
- 9522 :
The string '%' in either a GET query or POST parameters will be replaced with the respective item value.
- 9523 :
This is the argument object expected: {
- 9524 :
items: ['foo','bar','gna'], // list of items to send requests for
- 9525 :
action: 'task/some-action', // Roundcube action to call
- 9526 :
query: { q:'%s' }, // GET query parameters
- 9527 :
postdata: { source:'%s' }, // POST data (sends a POST request if present)
- 9528 :
threads: 3, // max. number of concurrent requests
- 9529 :
onresponse: function(data){ }, // Callback function called for every response received from server
- 9530 :
whendone: function(alldata){ } // Callback function called when all requests have been sent
- 9531 :
}
- 9532 :
*/
- 9533 :
this.multi_thread_http_request = function(prop)
- 9534 :
{
- 9535 :
var i, item, reqid = new Date().getTime(),
- 9536 :
threads = prop.threads || 1;
- 9537 :
- 9538 :
prop.reqid = reqid;
- 9539 :
prop.running = 0;
- 9540 :
prop.requests = [];
- 9541 :
prop.result = [];
- 9542 :
prop._items = $.extend([], prop.items); // copy items
- 9543 :
- 9544 :
if (!prop.lock)
- 9545 :
prop.lock = this.display_message('', 'loading');
- 9546 :
- 9547 :
// add the request arguments to the jobs pool
- 9548 :
this.http_request_jobs[reqid] = prop;
- 9549 :
- 9550 :
// start n threads
- 9551 :
for (i=0; i < threads; i++) {
- 9552 :
item = prop._items.shift();
- 9553 :
if (item === undefined)
- 9554 :
break;
- 9555 :
- 9556 :
prop.running++;
- 9557 :
prop.requests.push(this.multi_thread_send_request(prop, item));
- 9558 :
}
- 9559 :
- 9560 :
return reqid;
- 9561 :
};
- 9562 :
- 9563 :
// helper method to send an HTTP request with the given iterator value
- 9564 :
this.multi_thread_send_request = function(prop, item)
- 9565 :
{
- 9566 :
var k, postdata, query;
- 9567 :
- 9568 :
// replace %s in post data
- 9569 :
if (prop.postdata) {
- 9570 :
postdata = {};
- 9571 :
for (k in prop.postdata) {
- 9572 :
postdata[k] = String(prop.postdata[k]).replace('%s', item);
- 9573 :
}
- 9574 :
postdata._reqid = prop.reqid;
- 9575 :
}
- 9576 :
// replace %s in query
- 9577 :
else if (typeof prop.query == 'string') {
- 9578 :
query = prop.query.replace('%s', item);
- 9579 :
query += '&_reqid=' + prop.reqid;
- 9580 :
}
- 9581 :
else if (typeof prop.query == 'object' && prop.query) {
- 9582 :
query = {};
- 9583 :
for (k in prop.query) {
- 9584 :
query[k] = String(prop.query[k]).replace('%s', item);
- 9585 :
}
- 9586 :
query._reqid = prop.reqid;
- 9587 :
}
- 9588 :
- 9589 :
// send HTTP GET or POST request
- 9590 :
return postdata ? this.http_post(prop.action, postdata) : this.http_request(prop.action, query);
- 9591 :
};
- 9592 :
- 9593 :
// callback function for multi-threaded http responses
- 9594 :
this.multi_thread_http_response = function(data, reqid)
- 9595 :
{
- 9596 :
var prop = this.http_request_jobs[reqid];
- 9597 :
if (!prop || prop.running <= 0 || prop.cancelled)
- 9598 :
return;
- 9599 :
- 9600 :
prop.running--;
- 9601 :
- 9602 :
// trigger response callback
- 9603 :
if (prop.onresponse && typeof prop.onresponse == 'function') {
- 9604 :
prop.onresponse(data);
- 9605 :
}
- 9606 :
- 9607 :
prop.result = $.extend(prop.result, data);
- 9608 :
- 9609 :
// send next request if prop.items is not yet empty
- 9610 :
var item = prop._items.shift();
- 9611 :
if (item !== undefined) {
- 9612 :
prop.running++;
- 9613 :
prop.requests.push(this.multi_thread_send_request(prop, item));
- 9614 :
}
- 9615 :
// trigger whendone callback and mark this request as done
- 9616 :
else if (prop.running == 0) {
- 9617 :
if (prop.whendone && typeof prop.whendone == 'function') {
- 9618 :
prop.whendone(prop.result);
- 9619 :
}
- 9620 :
- 9621 :
this.set_busy(false, '', prop.lock);
- 9622 :
- 9623 :
// remove from this.http_request_jobs pool
- 9624 :
delete this.http_request_jobs[reqid];
- 9625 :
}
- 9626 :
};
- 9627 :
- 9628 :
// abort a running multi-thread request with the given identifier
- 9629 :
this.multi_thread_request_abort = function(reqid)
- 9630 :
{
- 9631 :
var prop = this.http_request_jobs[reqid];
- 9632 :
if (prop) {
- 9633 :
for (var i=0; prop.running > 0 && i < prop.requests.length; i++) {
- 9634 :
if (prop.requests[i].abort)
- 9635 :
prop.requests[i].abort();
- 9636 :
}
- 9637 :
- 9638 :
prop.running = 0;
- 9639 :
prop.cancelled = true;
- 9640 :
this.set_busy(false, '', prop.lock);
- 9641 :
}
- 9642 :
};
- 9643 :
- 9644 :
// post the given form to a hidden iframe
- 9645 :
this.async_upload_form = function(form, action, onload)
- 9646 :
{
- 9647 :
// create hidden iframe
- 9648 :
var ts = new Date().getTime(),
- 9649 :
frame_name = 'rcmupload' + ts,
- 9650 :
frame = this.dummy_iframe(frame_name);
- 9651 :
- 9652 :
// handle upload errors by parsing iframe content in onload
- 9653 :
frame.on('load', {ts:ts}, onload);
- 9654 :
- 9655 :
$(form).attr({
- 9656 :
target: frame_name,
- 9657 :
action: this.url(action, {_id: this.env.compose_id || '', _uploadid: ts, _from: this.env.action}),
- 9658 :
method: 'POST',
- 9659 :
enctype: 'multipart/form-data'
- 9660 :
})
- 9661 :
.submit();
- 9662 :
- 9663 :
return frame_name;
- 9664 :
};
- 9665 :
- 9666 :
// create hidden iframe element
- 9667 :
this.dummy_iframe = function(name, src)
- 9668 :
{
- 9669 :
return $('<iframe>').attr({
- 9670 :
name: name,
- 9671 :
src: src,
- 9672 :
style: 'width:0;height:0;visibility:hidden',
- 9673 :
'aria-hidden': 'true'
- 9674 :
})
- 9675 :
.appendTo(document.body);
- 9676 :
};
- 9677 :
- 9678 :
// html5 file-drop API
- 9679 :
this.document_drag_hover = function(e, over)
- 9680 :
{
- 9681 :
// don't e.preventDefault() here to not block text dragging on the page (#1490619)
- 9682 :
$(this.gui_objects.filedrop)[(over?'addClass':'removeClass')]('active');
- 9683 :
};
- 9684 :
- 9685 :
this.file_drag_hover = function(e, over)
- 9686 :
{
- 9687 :
e.preventDefault();
- 9688 :
e.stopPropagation();
- 9689 :
$(this.gui_objects.filedrop)[(over?'addClass':'removeClass')]('hover');
- 9690 :
};
- 9691 :
- 9692 :
// handler when files are dropped to a designated area.
- 9693 :
// compose a multipart form data and submit it to the server
- 9694 :
this.file_dropped = function(e)
- 9695 :
{
- 9696 :
// abort event and reset UI
- 9697 :
this.file_drag_hover(e, false);
- 9698 :
- 9699 :
// prepare multipart form data composition
- 9700 :
var uri,
- 9701 :
files = e.target.files || e.dataTransfer.files,
- 9702 :
args = {_id: this.env.compose_id || this.env.cid || '', _remote: 1, _from: this.env.action};
- 9703 :
- 9704 :
if (!files || !files.length) {
- 9705 :
// Roundcube attachment, pass its uri to the backend and attach
- 9706 :
if (uri = e.dataTransfer.getData('roundcube-uri')) {
- 9707 :
var ts = 'upload' + new Date().getTime(),
- 9708 :
// jQuery way to escape filename (#1490530)
- 9709 :
content = $('<span>').text(e.dataTransfer.getData('roundcube-name') || this.get_label('attaching')).html();
- 9710 :
- 9711 :
args._uri = uri;
- 9712 :
args._uploadid = ts;
- 9713 :
- 9714 :
// add to attachments list
- 9715 :
if (!this.add2attachment_list(ts, {name: '', html: content, classname: 'uploading', complete: false}))
- 9716 :
this.file_upload_id = this.set_busy(true, 'attaching');
- 9717 :
- 9718 :
this.http_post(this.env.filedrop.action || 'upload', args);
- 9719 :
}
- 9720 :
- 9721 :
return;
- 9722 :
}
- 9723 :
- 9724 :
this.file_upload(files, args, {
- 9725 :
name: (this.env.filedrop.fieldname || '_file') + (this.env.filedrop.single ? '' : '[]'),
- 9726 :
single: this.env.filedrop.single,
- 9727 :
filter: this.env.filedrop.filter,
- 9728 :
action: ref.env.filedrop.action
- 9729 :
});
- 9730 :
};
- 9731 :
- 9732 :
// Files upload using ajax
- 9733 :
this.file_upload = function(files, post_args, props)
- 9734 :
{
- 9735 :
if (!window.FormData || !files || !files.length)
- 9736 :
return false;
- 9737 :
- 9738 :
var f, i, fname, size = 0, numfiles = 0,
- 9739 :
formdata = new FormData(),
- 9740 :
fieldname = props.name || '_file[]',
- 9741 :
limit = props.single ? 1 : files.length;
- 9742 :
args = $.extend({_remote: 1, _from: this.env.action}, post_args || {});
- 9743 :
- 9744 :
// add files to form data
- 9745 :
for (i=0; numfiles < limit && (f = files[i]); i++) {
- 9746 :
// filter by file type if requested
- 9747 :
if (props.filter && !f.type.match(new RegExp(props.filter))) {
- 9748 :
// TODO: show message to user
- 9749 :
continue;
- 9750 :
}
- 9751 :
- 9752 :
formdata.append(fieldname, f);
- 9753 :
size += f.size;
- 9754 :
fname = f.name;
- 9755 :
numfiles++;
- 9756 :
}
- 9757 :
- 9758 :
if (numfiles) {
- 9759 :
if (this.env.max_filesize && this.env.filesizeerror && size > this.env.max_filesize) {
- 9760 :
this.display_message(this.env.filesizeerror, 'error');
- 9761 :
return false;
- 9762 :
}
- 9763 :
- 9764 :
if (this.env.max_filecount && this.env.filecounterror && numfiles > this.env.max_filecount) {
- 9765 :
this.display_message(this.env.filecounterror, 'error');
- 9766 :
return false;
- 9767 :
}
- 9768 :
- 9769 :
var ts = 'upload' + new Date().getTime(),
- 9770 :
label = numfiles > 1 ? this.get_label('uploadingmany') : fname,
- 9771 :
// jQuery way to escape filename (#1490530)
- 9772 :
content = $('<span>').text(label).html();
- 9773 :
- 9774 :
// add to attachments list
- 9775 :
if (!this.add2attachment_list(ts, {name: '', html: content, classname: 'uploading', complete: false}) && !props.lock)
- 9776 :
props.lock = this.file_upload_id = this.set_busy(true, 'uploading');
- 9777 :
- 9778 :
args._uploadid = ts;
- 9779 :
args._unlock = props.lock;
- 9780 :
- 9781 :
this.uploads[ts] = $.ajax({
- 9782 :
type: 'POST',
- 9783 :
dataType: 'json',
- 9784 :
url: this.url(props.action || 'upload', args),
- 9785 :
contentType: false,
- 9786 :
processData: false,
- 9787 :
timeout: this.uploadTimeout, // ajax call timeout for loading attachment
- 9788 :
data: formdata,
- 9789 :
headers: {'X-Roundcube-Request': this.env.request_token},
- 9790 :
xhr: function() {
- 9791 :
var xhr = $.ajaxSettings.xhr();
- 9792 :
if (xhr.upload && ref.labels.uploadprogress) {
- 9793 :
xhr.upload.onprogress = function(e) {
- 9794 :
var msg = ref.file_upload_msg(e.loaded, e.total);
- 9795 :
if (msg) {
- 9796 :
$('#' + ts).find('.uploading').text(msg);
- 9797 :
}
- 9798 :
};
- 9799 :
}
- 9800 :
return xhr;
- 9801 :
},
- 9802 :
success: function(data) {
- 9803 :
delete ref.uploads[ts];
- 9804 :
ref.http_response(data);
- 9805 :
},
- 9806 :
error: function(o, status, err) {
- 9807 :
delete ref.uploads[ts];
- 9808 :
ref.remove_from_attachment_list(ts);
- 9809 :
ref.http_error(o, status, err, props.lock, 'attachment');
- 9810 :
}
- 9811 :
});
- 9812 :
}
- 9813 :
- 9814 :
return true;
- 9815 :
};
- 9816 :
- 9817 :
this.file_upload_msg = function(current, total)
- 9818 :
{
- 9819 :
if (total && current < total) {
- 9820 :
var percent = Math.round(current/total * 100),
- 9821 :
label = ref.get_label('uploadprogress');
- 9822 :
- 9823 :
if (total >= 1073741824) {
- 9824 :
total = parseFloat(total/1073741824).toFixed(1) + ' ' . this.get_label('GB');
- 9825 :
current = parseFloat(current/1073741824).toFixed(1);
- 9826 :
}
- 9827 :
else if (total >= 1048576) {
- 9828 :
total = parseFloat(total/1048576).toFixed(1) + ' ' + this.get_label('MB');
- 9829 :
current = parseFloat(current/1048576).toFixed(1);
- 9830 :
}
- 9831 :
else if (total >= 1024) {
- 9832 :
total = parseInt(total/1024) + ' ' + this.get_label('KB');
- 9833 :
current = parseInt(current/1024);
- 9834 :
}
- 9835 :
else {
- 9836 :
total = total + ' ' + this.get_label('B');
- 9837 :
}
- 9838 :
- 9839 :
return label.replace('$percent', percent + '%').replace('$current', current).replace('$total', total);
- 9840 :
}
- 9841 :
};
- 9842 :
- 9843 :
// starts interval for keep-alive signal
- 9844 :
this.start_keepalive = function()
- 9845 :
{
- 9846 :
if (!this.env.session_lifetime || this.env.framed || this.env.extwin || this.task == 'login' || this.env.action == 'print')
- 9847 :
return;
- 9848 :
- 9849 :
if (this._keepalive)
- 9850 :
clearInterval(this._keepalive);
- 9851 :
- 9852 :
// use Math to prevent from an integer overflow (#5273)
- 9853 :
// maximum interval is 15 minutes, minimum is 30 seconds
- 9854 :
var interval = Math.min(1800, this.env.session_lifetime) * 0.5 * 1000;
- 9855 :
this._keepalive = setInterval(function() { ref.keep_alive(); }, interval < 30000 ? 30000 : interval);
- 9856 :
};
- 9857 :
- 9858 :
// starts interval for refresh signal
- 9859 :
this.start_refresh = function()
- 9860 :
{
- 9861 :
if (!this.env.refresh_interval || this.env.framed || this.env.extwin || this.task == 'login' || this.env.action == 'print')
- 9862 :
return;
- 9863 :
- 9864 :
if (this._refresh)
- 9865 :
clearInterval(this._refresh);
- 9866 :
- 9867 :
this._refresh = setInterval(function(){ ref.refresh(); }, this.env.refresh_interval * 1000);
- 9868 :
};
- 9869 :
- 9870 :
// sends keep-alive signal
- 9871 :
this.keep_alive = function()
- 9872 :
{
- 9873 :
if (!this.busy)
- 9874 :
this.http_request('keep-alive');
- 9875 :
};
- 9876 :
- 9877 :
// sends refresh signal
- 9878 :
this.refresh = function()
- 9879 :
{
- 9880 :
if (this.busy) {
- 9881 :
// try again after 10 seconds
- 9882 :
setTimeout(function() { ref.refresh(); ref.start_refresh(); }, 10000);
- 9883 :
return;
- 9884 :
}
- 9885 :
- 9886 :
var params = {}, lock = this.set_busy(true, 'refreshing');
- 9887 :
- 9888 :
if (this.task == 'mail' && this.gui_objects.mailboxlist)
- 9889 :
params = this.check_recent_params();
- 9890 :
- 9891 :
params._last = Math.floor(this.env.lastrefresh.getTime() / 1000);
- 9892 :
this.env.lastrefresh = new Date();
- 9893 :
- 9894 :
// plugins should bind to 'requestrefresh' event to add own params
- 9895 :
this.http_post('refresh', params, lock);
- 9896 :
};
- 9897 :
- 9898 :
// returns check-recent request parameters
- 9899 :
this.check_recent_params = function()
- 9900 :
{
- 9901 :
var params = {_mbox: this.env.mailbox};
- 9902 :
- 9903 :
if (this.gui_objects.mailboxlist)
- 9904 :
params._folderlist = 1;
- 9905 :
if (this.gui_objects.quotadisplay)
- 9906 :
params._quota = 1;
- 9907 :
if (this.env.search_request)
- 9908 :
params._search = this.env.search_request;
- 9909 :
- 9910 :
if (this.gui_objects.messagelist) {
- 9911 :
params._list = 1;
- 9912 :
- 9913 :
// message uids for flag updates check
- 9914 :
params._uids = $.map(this.message_list.rows, function(row, uid) { return uid; }).join(',');
- 9915 :
}
- 9916 :
- 9917 :
return params;
- 9918 :
};
- 9919 :
- 9920 :
- 9921 :
/********************************************************/
- 9922 :
/********* helper methods *********/
- 9923 :
/********************************************************/
- 9924 :
- 9925 :
/**
- 9926 :
* Quote html entities
- 9927 :
*/
- 9928 :
this.quote_html = function(str)
- 9929 :
{
- 9930 :
return String(str).replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
- 9931 :
};
- 9932 :
- 9933 :
// get window.opener.rcmail if available
- 9934 :
this.opener = function(deep, filter)
- 9935 :
{
- 9936 :
var i, win = window.opener;
- 9937 :
- 9938 :
// catch Error: Permission denied to access property rcmail
- 9939 :
try {
- 9940 :
if (win && !win.closed && win !== window) {
- 9941 :
// try parent of the opener window, e.g. preview frame
- 9942 :
if (deep && (!win.rcmail || win.rcmail.env.framed) && win.parent && win.parent.rcmail)
- 9943 :
win = win.parent;
- 9944 :
- 9945 :
if (win.rcmail && filter)
- 9946 :
for (i in filter)
- 9947 :
if (win.rcmail.env[i] != filter[i])
- 9948 :
return;
- 9949 :
- 9950 :
return win.rcmail;
- 9951 :
}
- 9952 :
}
- 9953 :
catch (e) {}
- 9954 :
};
- 9955 :
- 9956 :
// check if we're in show mode or if we have a unique selection
- 9957 :
// and return the message uid
- 9958 :
this.get_single_uid = function()
- 9959 :
{
- 9960 :
var uid = this.env.uid || (this.message_list ? this.message_list.get_single_selection() : null);
- 9961 :
var result = ref.triggerEvent('get_single_uid', { uid: uid });
- 9962 :
return result || uid;
- 9963 :
};
- 9964 :
- 9965 :
// same as above but for contacts
- 9966 :
this.get_single_cid = function()
- 9967 :
{
- 9968 :
var cid = this.env.cid || (this.contact_list ? this.contact_list.get_single_selection() : null);
- 9969 :
var result = ref.triggerEvent('get_single_cid', { cid: cid });
- 9970 :
return result || cid;
- 9971 :
};
- 9972 :
- 9973 :
// get the IMP mailbox of the message with the given UID
- 9974 :
this.get_message_mailbox = function(uid)
- 9975 :
{
- 9976 :
var msg;
- 9977 :
- 9978 :
if (this.env.messages && uid && (msg = this.env.messages[uid]) && msg.mbox)
- 9979 :
return msg.mbox;
- 9980 :
- 9981 :
if (/^[0-9]+-(.*)$/.test(uid))
- 9982 :
return RegExp.$1;
- 9983 :
- 9984 :
return this.env.mailbox;
- 9985 :
};
- 9986 :
- 9987 :
// build request parameters from single message id (maybe with mailbox name)
- 9988 :
this.params_from_uid = function(uid, params)
- 9989 :
{
- 9990 :
if (!params)
- 9991 :
params = {};
- 9992 :
- 9993 :
params._uid = String(uid).split('-')[0];
- 9994 :
params._mbox = this.get_message_mailbox(uid);
- 9995 :
- 9996 :
return params;
- 9997 :
};
- 9998 :
- 9999 :
// gets cursor position
- 10000 :
this.get_caret_pos = function(obj)
- 10001 :
{
- 10002 :
if (obj.selectionEnd !== undefined)
- 10003 :
return obj.selectionEnd;
- 10004 :
- 10005 :
return obj.value.length;
- 10006 :
};
- 10007 :
- 10008 :
// moves cursor to specified position
- 10009 :
this.set_caret_pos = function(obj, pos)
- 10010 :
{
- 10011 :
try {
- 10012 :
if (obj.setSelectionRange)
- 10013 :
obj.setSelectionRange(pos, pos);
- 10014 :
}
- 10015 :
catch(e) {} // catch Firefox exception if obj is hidden
- 10016 :
};
- 10017 :
- 10018 :
// get selected text from an input field
- 10019 :
this.get_input_selection = function(obj)
- 10020 :
{
- 10021 :
var start = 0, end = 0, normalizedValue = '';
- 10022 :
- 10023 :
if (typeof obj.selectionStart == "number" && typeof obj.selectionEnd == "number") {
- 10024 :
normalizedValue = obj.value;
- 10025 :
start = obj.selectionStart;
- 10026 :
end = obj.selectionEnd;
- 10027 :
}
- 10028 :
- 10029 :
return {start: start, end: end, text: normalizedValue.substr(start, end-start)};
- 10030 :
};
- 10031 :
- 10032 :
// disable/enable all fields of a form
- 10033 :
this.lock_form = function(form, lock)
- 10034 :
{
- 10035 :
if (!form || !form.elements)
- 10036 :
return;
- 10037 :
- 10038 :
if (lock)
- 10039 :
this.disabled_form_elements = [];
- 10040 :
- 10041 :
$.each(form.elements, function() {
- 10042 :
if (this.type == 'hidden')
- 10043 :
return;
- 10044 :
// remember which elem was disabled before lock
- 10045 :
if (lock && this.disabled)
- 10046 :
ref.disabled_form_elements.push(this);
- 10047 :
else if (lock || $.inArray(this, ref.disabled_form_elements) < 0)
- 10048 :
this.disabled = lock;
- 10049 :
});
- 10050 :
};
- 10051 :
- 10052 :
this.mailto_handler_uri = function()
- 10053 :
{
- 10054 :
return location.href.split('?')[0] + '?_task=mail&_action=compose&_to=%s';
- 10055 :
};
- 10056 :
- 10057 :
this.register_protocol_handler = function(name)
- 10058 :
{
- 10059 :
try {
- 10060 :
window.navigator.registerProtocolHandler('mailto', this.mailto_handler_uri(), name);
- 10061 :
}
- 10062 :
catch(e) {
- 10063 :
this.display_message(String(e), 'error');
- 10064 :
}
- 10065 :
};
- 10066 :
- 10067 :
this.check_protocol_handler = function(name, elem)
- 10068 :
{
- 10069 :
var nav = window.navigator;
- 10070 :
- 10071 :
if (!nav || (typeof nav.registerProtocolHandler != 'function')) {
- 10072 :
$(elem).addClass('disabled').click(function() {
- 10073 :
ref.display_message('nosupporterror', 'error');
- 10074 :
return false;
- 10075 :
});
- 10076 :
}
- 10077 :
else if (typeof nav.isProtocolHandlerRegistered == 'function') {
- 10078 :
var status = nav.isProtocolHandlerRegistered('mailto', this.mailto_handler_uri());
- 10079 :
if (status)
- 10080 :
$(elem).parent().find('.mailtoprotohandler-status').html(status);
- 10081 :
}
- 10082 :
else {
- 10083 :
$(elem).click(function() { ref.register_protocol_handler(name); return false; });
- 10084 :
}
- 10085 :
};
- 10086 :
- 10087 :
// Checks browser capabilities e.g. PDF support, TIF support
- 10088 :
this.browser_capabilities_check = function()
- 10089 :
{
- 10090 :
if (!this.env.browser_capabilities)
- 10091 :
this.env.browser_capabilities = {};
- 10092 :
- 10093 :
$.each(['pdf', 'flash', 'tiff', 'webp', 'pgpmime'], function() {
- 10094 :
if (ref.env.browser_capabilities[this] === undefined)
- 10095 :
ref.env.browser_capabilities[this] = ref[this + '_support_check']();
- 10096 :
});
- 10097 :
};
- 10098 :
- 10099 :
// Returns browser capabilities string
- 10100 :
this.browser_capabilities = function()
- 10101 :
{
- 10102 :
if (!this.env.browser_capabilities)
- 10103 :
return '';
- 10104 :
- 10105 :
var n, ret = [];
- 10106 :
- 10107 :
for (n in this.env.browser_capabilities)
- 10108 :
ret.push(n + '=' + this.env.browser_capabilities[n]);
- 10109 :
- 10110 :
return ret.join();
- 10111 :
};
- 10112 :
- 10113 :
this.tiff_support_check = function()
- 10114 :
{
- 10115 :
this.image_support_check('tiff');
- 10116 :
return 0;
- 10117 :
};
- 10118 :
- 10119 :
this.webp_support_check = function()
- 10120 :
{
- 10121 :
this.image_support_check('webp');
- 10122 :
return 0;
- 10123 :
};
- 10124 :
- 10125 :
this.image_support_check = function(type)
- 10126 :
{
- 10127 :
setTimeout(function() {
- 10128 :
var img = new Image();
- 10129 :
img.onload = function() { ref.env.browser_capabilities[type] = 1; };
- 10130 :
img.onerror = function() { ref.env.browser_capabilities[type] = 0; };
- 10131 :
img.src = ref.assets_path('program/resources/blank.' + type);
- 10132 :
}, 10);
- 10133 :
};
- 10134 :
- 10135 :
this.pdf_support_check = function()
- 10136 :
{
- 10137 :
var i, plugin = navigator.mimeTypes ? navigator.mimeTypes["application/pdf"] : {},
- 10138 :
plugins = navigator.plugins,
- 10139 :
len = plugins.length,
- 10140 :
regex = /Adobe Reader|PDF|Acrobat/i;
- 10141 :
- 10142 :
if (plugin && plugin.enabledPlugin)
- 10143 :
return 1;
- 10144 :
- 10145 :
if ('ActiveXObject' in window) {
- 10146 :
try {
- 10147 :
if (plugin = new ActiveXObject("AcroPDF.PDF"))
- 10148 :
return 1;
- 10149 :
}
- 10150 :
catch (e) {}
- 10151 :
try {
- 10152 :
if (plugin = new ActiveXObject("PDF.PdfCtrl"))
- 10153 :
return 1;
- 10154 :
}
- 10155 :
catch (e) {}
- 10156 :
}
- 10157 :
- 10158 :
for (i=0; i<len; i++) {
- 10159 :
plugin = plugins[i];
- 10160 :
if (typeof plugin === 'string') {
- 10161 :
if (regex.test(plugin))
- 10162 :
return 1;
- 10163 :
}
- 10164 :
else if (plugin.name && regex.test(plugin.name))
- 10165 :
return 1;
- 10166 :
}
- 10167 :
- 10168 :
setTimeout(function() {
- 10169 :
$('<object>').attr({
- 10170 :
data: ref.assets_path('program/resources/dummy.pdf'),
- 10171 :
type: 'application/pdf',
- 10172 :
style: 'position: "absolute"; top: -1000px; height: 1px; width: 1px'
- 10173 :
})
- 10174 :
.on('load error', function(e) {
- 10175 :
ref.env.browser_capabilities.pdf = e.type == 'load' ? 1 : 0;
- 10176 :
- 10177 :
// add a short delay before attempting to remove element (#8128)
- 10178 :
var obj = this;
- 10179 :
setTimeout(function() { $(obj).remove(); }, 10);
- 10180 :
})
- 10181 :
.appendTo(document.body);
- 10182 :
}, 10);
- 10183 :
- 10184 :
return 0;
- 10185 :
};
- 10186 :
- 10187 :
this.flash_support_check = function()
- 10188 :
{
- 10189 :
var plugin = navigator.mimeTypes ? navigator.mimeTypes["application/x-shockwave-flash"] : {};
- 10190 :
- 10191 :
if (plugin && plugin.enabledPlugin)
- 10192 :
return 1;
- 10193 :
- 10194 :
if ('ActiveXObject' in window) {
- 10195 :
try {
- 10196 :
if (plugin = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"))
- 10197 :
return 1;
- 10198 :
}
- 10199 :
catch (e) {}
- 10200 :
}
- 10201 :
- 10202 :
return 0;
- 10203 :
};
- 10204 :
- 10205 :
// check for mailvelope API
- 10206 :
this.pgpmime_support_check = function(action)
- 10207 :
{
- 10208 :
if (window.mailvelope)
- 10209 :
return 1;
- 10210 :
- 10211 :
$(window).on('mailvelope', function() {
- 10212 :
ref.env.browser_capabilities['pgpmime'] = 1;
- 10213 :
});
- 10214 :
- 10215 :
return 0;
- 10216 :
};
- 10217 :
- 10218 :
this.assets_path = function(path)
- 10219 :
{
- 10220 :
if (this.env.assets_path && !path.startsWith(this.env.assets_path)) {
- 10221 :
path = this.env.assets_path + path;
- 10222 :
}
- 10223 :
- 10224 :
return path;
- 10225 :
};
- 10226 :
- 10227 :
// Cookie setter
- 10228 :
this.set_cookie = function(name, value, expires)
- 10229 :
{
- 10230 :
if (expires === false) {
- 10231 :
var expires = new Date();
- 10232 :
expires.setYear(expires.getFullYear() + 1);
- 10233 :
}
- 10234 :
- 10235 :
setCookie(name, value, expires, this.env.cookie_path, this.env.cookie_domain, this.env.cookie_secure);
- 10236 :
};
- 10237 :
- 10238 :
this.get_local_storage_prefix = function()
- 10239 :
{
- 10240 :
if (!this.local_storage_prefix)
- 10241 :
this.local_storage_prefix = 'roundcube.' + (this.env.user_id || 'anonymous') + '.';
- 10242 :
- 10243 :
return this.local_storage_prefix;
- 10244 :
};
- 10245 :
- 10246 :
// wrapper for localStorage.getItem(key)
- 10247 :
this.local_storage_get_item = function(key, deflt, encrypted)
- 10248 :
{
- 10249 :
var item, result;
- 10250 :
- 10251 :
// TODO: add encryption
- 10252 :
try {
- 10253 :
item = localStorage.getItem(this.get_local_storage_prefix() + key);
- 10254 :
result = JSON.parse(item);
- 10255 :
}
- 10256 :
catch (e) { }
- 10257 :
- 10258 :
return result || deflt || null;
- 10259 :
};
- 10260 :
- 10261 :
// wrapper for localStorage.setItem(key, data)
- 10262 :
this.local_storage_set_item = function(key, data, encrypted)
- 10263 :
{
- 10264 :
// try/catch to handle no localStorage support, but also error
- 10265 :
// in Safari-in-private-browsing-mode where localStorage exists
- 10266 :
// but can't be used (#1489996)
- 10267 :
try {
- 10268 :
// TODO: add encryption
- 10269 :
localStorage.setItem(this.get_local_storage_prefix() + key, JSON.stringify(data));
- 10270 :
return true;
- 10271 :
}
- 10272 :
catch (e) {
- 10273 :
return false;
- 10274 :
}
- 10275 :
};
- 10276 :
- 10277 :
// wrapper for localStorage.removeItem(key)
- 10278 :
this.local_storage_remove_item = function(key)
- 10279 :
{
- 10280 :
try {
- 10281 :
localStorage.removeItem(this.get_local_storage_prefix() + key);
- 10282 :
return true;
- 10283 :
}
- 10284 :
catch (e) {
- 10285 :
return false;
- 10286 :
}
- 10287 :
};
- 10288 :
- 10289 :
this.print_dialog = function()
- 10290 :
{
- 10291 :
// setTimeout for Safari
- 10292 :
setTimeout('window.print()', 10);
- 10293 :
};
- 10294 :
} // end object rcube_webmail
- 10295 :
- 10296 :
- 10297 :
// some static methods
- 10298 :
rcube_webmail.long_subject_title = function(elem, indent, text_elem)
- 10299 :
{
- 10300 :
if (!elem.title) {
- 10301 :
var $elem = $(text_elem || elem);
- 10302 :
if ($elem.width() + (indent || 0) * 15 > $elem.parent().width())
- 10303 :
elem.title = rcube_webmail.subject_text($elem[0]);
- 10304 :
}
- 10305 :
};
- 10306 :
- 10307 :
rcube_webmail.long_subject_title_ex = function(elem)
- 10308 :
{
- 10309 :
if (!elem.title) {
- 10310 :
var $elem = $(elem),
- 10311 :
txt = $elem.text().trim(),
- 10312 :
indent = $('span.branch', $elem).width() || 0,
- 10313 :
tmp = $('<span>').text(txt)
- 10314 :
.css({position: 'absolute', 'float': 'left', visibility: 'hidden',
- 10315 :
'font-size': $elem.css('font-size'), 'font-weight': $elem.css('font-weight')})
- 10316 :
.appendTo(document.body),
- 10317 :
w = tmp.width();
- 10318 :
- 10319 :
tmp.remove();
- 10320 :
if (w + indent * 15 > $elem.width())
- 10321 :
elem.title = rcube_webmail.subject_text(elem);
- 10322 :
}
- 10323 :
};
- 10324 :
- 10325 :
rcube_webmail.subject_text = function(elem)
- 10326 :
{
- 10327 :
var t = $(elem).clone();
- 10328 :
t.find('.skip-on-drag,.skip-content,.voice').remove();
- 10329 :
return t.text().trim();
- 10330 :
};
- 10331 :
- 10332 :
// set event handlers on all iframe elements (and their contents)
- 10333 :
rcube_webmail.set_iframe_events = function(events)
- 10334 :
{
- 10335 :
$('iframe').each(function() {
- 10336 :
var frame = $(this);
- 10337 :
$.each(events, function(event_name, event_handler) {
- 10338 :
frame.on('load', function(e) {
- 10339 :
try { $(this).contents().on(event_name, event_handler); }
- 10340 :
catch (e) {/* catch possible permission error in IE */ }
- 10341 :
});
- 10342 :
- 10343 :
try { frame.contents().on(event_name, event_handler); }
- 10344 :
catch (e) {/* catch possible permission error in IE */ }
- 10345 :
});
- 10346 :
});
- 10347 :
};
- 10348 :
- 10349 :
rcube_webmail.prototype.get_cookie = getCookie;
- 10350 :
- 10351 :
// copy event engine prototype
- 10352 :
rcube_webmail.prototype.addEventListener = rcube_event_engine.prototype.addEventListener;
- 10353 :
rcube_webmail.prototype.removeEventListener = rcube_event_engine.prototype.removeEventListener;
- 10354 :
rcube_webmail.prototype.triggerEvent = rcube_event_engine.prototype.triggerEvent;