Aleksander Machniak
2015-10-15 fe8ff85d7eabb370d85fc31ca55b9a6c6f86a356
program/js/app.js
@@ -6,8 +6,8 @@
 * @licstart  The following is the entire license notice for the
 * JavaScript code in this file.
 *
 * Copyright (C) 2005-2014, The Roundcube Dev Team
 * Copyright (C) 2011-2014, Kolab Systems AG
 * Copyright (C) 2005-2015, The Roundcube Dev Team
 * Copyright (C) 2011-2015, Kolab Systems AG
 *
 * The JavaScript code in this page is free software: you can
 * redistribute it and/or modify it under the terms of the GNU
@@ -77,7 +77,7 @@
  });
  // unload fix
  $(window).bind('beforeunload', function() { ref.unload = true; });
  $(window).on('beforeunload', function() { ref.unload = true; });
  // set environment variable(s)
  this.set_env = function(p, value)
@@ -274,6 +274,23 @@
            this.enable_command('compose', 'add-contact', false);
            parent.rcmail.show_contentframe(true);
          }
          // initialize drag-n-drop on attachments, so they can e.g.
          // be dropped into mail compose attachments in another window
          if (this.gui_objects.attachments)
            $('li > a', this.gui_objects.attachments).not('.drop').on('dragstart', function(e) {
              var n, href = this.href, dt = e.originalEvent.dataTransfer;
              if (dt) {
                // inject username to the uri
                href = href.replace(/^https?:\/\//, function(m) { return m + urlencode(ref.env.username) + '@'});
                // cleanup the node to get filename without the size test
                n = $(this).clone();
                n.children().remove();
                dt.setData('roundcube-uri', href);
                dt.setData('roundcube-name', $.trim(n.text()));
              }
            });
        }
        else if (this.env.action == 'compose') {
          this.env.address_group_stack = [];
@@ -304,8 +321,8 @@
          if (this.gui_objects.responseslist) {
            $('a.insertresponse', this.gui_objects.responseslist)
              .attr('unselectable', 'on')
              .mousedown(function(e){ return rcube_event.cancel(e); })
              .bind('mouseup keypress', function(e){
              .mousedown(function(e) { return rcube_event.cancel(e); })
              .on('mouseup keypress', function(e) {
                if (e.type == 'mouseup' || rcube_event.get_keycode(e) == 13) {
                  ref.command('insert-response', $(this).attr('rel'));
                  $(document.body).trigger('mouseup');  // hides the menu
@@ -325,7 +342,9 @@
        else if (this.env.action == 'get')
          this.enable_command('download', 'print', true);
        // show printing dialog
        else if (this.env.action == 'print' && this.env.uid) {
        else if (this.env.action == 'print' && this.env.uid
          && !this.env.is_pgp_content && !this.env.pgp_mime_part
        ) {
          this.print_dialog();
        }
@@ -516,7 +535,7 @@
            input_user = $('#rcmloginuser'),
            input_tz = $('#rcmlogintz');
        input_user.bind('keyup', function(e) { return ref.login_user_keyup(e); });
        input_user.keyup(function(e) { return ref.login_user_keyup(e); });
        if (input_user.val() == '')
          input_user.focus();
@@ -581,17 +600,17 @@
    // activate html5 file drop feature (if browser supports it and if configured)
    if (this.gui_objects.filedrop && this.env.filedrop && ((window.XMLHttpRequest && XMLHttpRequest.prototype && XMLHttpRequest.prototype.sendAsBinary) || window.FormData)) {
      $(document.body).bind('dragover dragleave drop', function(e){ return ref.document_drag_hover(e, e.type == 'dragover'); });
      $(document.body).on('dragover dragleave drop', function(e) { return ref.document_drag_hover(e, e.type == 'dragover'); });
      $(this.gui_objects.filedrop).addClass('droptarget')
        .bind('dragover dragleave', function(e){ return ref.file_drag_hover(e, e.type == 'dragover'); })
        .get(0).addEventListener('drop', function(e){ return ref.file_dropped(e); }, false);
        .on('dragover dragleave', function(e) { return ref.file_drag_hover(e, e.type == 'dragover'); })
        .get(0).addEventListener('drop', function(e) { return ref.file_dropped(e); }, false);
    }
    // catch document (and iframe) mouse clicks
    var body_mouseup = function(e){ return ref.doc_mouse_up(e); };
    $(document.body)
      .bind('mouseup', body_mouseup)
      .bind('keydown', function(e){ return ref.doc_keypress(e); });
      .mouseup(body_mouseup)
      .keydown(function(e){ return ref.doc_keypress(e); });
    $('iframe').on('load', function(e) {
        try { $(this.contentDocument || this.contentWindow).on('mouseup', body_mouseup);  }
@@ -2495,22 +2514,23 @@
  // removes messages that doesn't exists from list selection array
  this.update_selection = function()
  {
    var selected = this.message_list.selection,
      rows = this.message_list.rows,
    var list = this.message_list,
      selected = list.selection,
      rows = list.rows,
      i, selection = [];
    for (i in selected)
      if (rows[selected[i]])
        selection.push(selected[i]);
    this.message_list.selection = selection;
    list.selection = selection;
    // reset preview frame, if currently previewed message is not selected (has been removed)
    try {
      var win = this.get_frame_window(this.env.contentframe),
        id = win.rcmail.env.uid;
      if (id && $.inArray(id, selection) < 0)
      if (id && !list.in_selection(id))
        this.show_contentframe(false);
    }
    catch (e) {};
@@ -2726,8 +2746,9 @@
            $('#'+r.id+' .leaf:first')
              .attr('id', 'rcmexpando' + r.id)
              .attr('class', (r.obj.style.display != 'none' ? 'expanded' : 'collapsed'))
              .bind('mousedown', {uid: r.uid},
                function(e) { return ref.expand_message_row(e, e.data.uid); });
              .mousedown({uid: r.uid}, function(e) {
                return ref.expand_message_row(e, e.data.uid);
              });
            r.unread_children = 0;
            roots.push(r);
@@ -3361,13 +3382,13 @@
    }
  };
  //
  // Load Mailvelope functionality (and initialize keyring if needed)
  this.mailvelope_load = function(action)
  {
    if (this.env.browser_capabilities)
      this.env.browser_capabilities['pgpmime'] = 1;
    var keyring = this.get_local_storage_prefix();
    var keyring = this.env.user_id;
    mailvelope.getKeyring(keyring).then(function(kr) {
      ref.mailvelope_keyring = kr;
@@ -3383,17 +3404,20 @@
    });
  };
  //
  // Initializes Mailvelope editor or display container
  this.mailvelope_init = function(action, keyring)
  {
    if (action == 'show' || action == 'preview') {
    if (!window.mailvelope)
      return;
    if (action == 'show' || action == 'preview' || action == 'print') {
      // decrypt text body
      if (this.env.is_pgp_content && window.mailvelope) {
      if (this.env.is_pgp_content) {
        var data = $(this.env.is_pgp_content).text();
        ref.mailvelope_display_container(this.env.is_pgp_content, data, keyring);
      }
      // load pgp/mime message and pass it to the mailvelope display container
      else if (this.env.pgp_mime_part && window.mailvelope) {
      else if (this.env.pgp_mime_part) {
        var msgid = this.display_message(this.get_label('loadingdata'), 'loading'),
          selector = this.env.pgp_mime_container;
@@ -3409,21 +3433,30 @@
        });
      }
    }
    else if (action == 'compose' && window.mailvelope) {
    else if (action == 'compose') {
      this.env.compose_commands.push('compose-encrypted');
      var is_html = $('input[name="_is_html"]').val() > 0;
      if (this.env.pgp_mime_message) {
        // fetch PGP/Mime part and open load into Mailvelope editor
        var lock = this.set_busy(true, this.get_label('loadingdata'));
        $.ajax({
          type: 'GET',
          url: this.url('get', this.env.pgp_mime_message),
          error: function(o, status, err) {
            ref.http_error(o, status, err, lock);
            ref.enable_command('compose-encrypted', true);
            ref.enable_command('compose-encrypted', !is_html);
          },
          success: function(data) {
            ref.set_busy(false, null, lock);
            if (is_html) {
              ref.command('toggle-editor', {html: false, noconvert: true});
              $('#' + ref.env.composebody).val('');
            }
            ref.compose_encrypted({ quotedMail: data });
            ref.enable_command('compose-encrypted', true);
          }
@@ -3431,7 +3464,7 @@
      }
      else {
        // enable encrypted compose toggle
        this.enable_command('compose-encrypted', true);
        this.enable_command('compose-encrypted', !is_html);
      }
    }
  };
@@ -3439,7 +3472,7 @@
  // handler for the 'compose-encrypted' command
  this.compose_encrypted = function(props)
  {
    var container = $('#' + this.env.composebody).parent();
    var options, container = $('#' + this.env.composebody).parent();
    // remove Mailvelope editor if active
    if (ref.mailvelope_editor) {
@@ -3451,13 +3484,23 @@
        .find('iframe:not([aria-hidden=true])').remove();
      $('#' + ref.env.composebody).show();
      $("[name='_pgpmime']").remove();
      // disable commands that operate on the compose body
      ref.enable_command('spellcheck', 'insert-sig', 'toggle-editor', 'insert-response', 'save-response', true);
      ref.triggerEvent('compose-encrypted', { active:false });
    }
    // embed Mailvelope editor container
    else {
      var options = { predefinedText: $('#' + this.env.composebody).val() };
      if (this.spellcheck_state())
        this.editor.spellcheck_stop();
      if (props.quotedMail) {
        options = { quotedMail: props.quotedMail, quotedMailIndent: false };
      }
      else {
        options = { predefinedText: $('#' + this.env.composebody).val() };
      }
      if (this.env.compose_mode == 'reply') {
        options.quotedMailIndent = true;
        options.quotedMailHeader = this.env.compose_reply_header;
@@ -3470,6 +3513,10 @@
        container.addClass('mailvelope');
        $('#' + ref.env.composebody).hide();
        // disable commands that operate on the compose body
        ref.enable_command('spellcheck', 'insert-sig', 'toggle-editor', 'insert-response', 'save-response', false);
        ref.triggerEvent('compose-encrypted', { active:true });
        // notify user about loosing attachments
        if (ref.env.attachments && !$.isEmptyObject(ref.env.attachments)) {
@@ -3621,7 +3668,7 @@
  this.mailvelope_display_container = function(selector, data, keyring, msgid)
  {
    mailvelope.createDisplayContainer(selector, data, keyring, { showExternalContent: this.env.safemode }).then(function() {
      $(selector).addClass('mailvelope').find('.message-part, .part-notice').hide();
      $(selector).addClass('mailvelope').children().not('iframe').hide();
      ref.hide_message(msgid);
      setTimeout(function() { $(window).resize(); }, 10);
    }).catch(function(err) {
@@ -3764,7 +3811,7 @@
          ref.hide_message(lock);
          if (errorCode) {
            ref.display_message('Failed to get key from keyserver', 'error');
            ref.display_message(ref.get_label('keyservererror'), 'error');
            return;
          }
@@ -3774,9 +3821,9 @@
              // alert(ref.get_label('Key import was rejected'));
            }
            else {
              var $key = keyid.substr(-8).toUpperCase();
              btn.closest('.key').fadeOut();
              ref.display_message(ref.get_label('Public key $key successfully imported into your key ring')
                .replace('$key', keyid.substr(-8).toUpperCase()), 'confirmation');
              ref.display_message(ref.get_label('keyimportsuccess').replace('$key', $key), 'confirmation');
            }
          }).catch(function(err) {
            console.log(err);
@@ -4236,6 +4283,8 @@
    if (result) {
      // update internal format flag
      $("input[name='_is_html']").val(props.html ? 1 : 0);
      // enable encrypted compose toggle
      this.enable_command('compose-encrypted', !props.html);
    }
    return result;
@@ -4305,10 +4354,10 @@
        .attr('tabindex', '0')
        .html(this.quote_html(response.name))
        .appendTo(li)
        .mousedown(function(e){
        .mousedown(function(e) {
          return rcube_event.cancel(e);
        })
        .bind('mouseup keypress', function(e){
        .on('mouseup keypress', function(e) {
          if (e.type == 'mouseup' || rcube_event.get_keycode(e) == 13) {
            ref.command('insert-response', $(this).attr('rel'));
            $(document.body).trigger('mouseup');  // hides the menu
@@ -4406,7 +4455,7 @@
    if (!this.local_save_timer && window.localStorage && this.env.save_localstorage) {
      // track typing activity and only save on changes
      this.compose_type_activity = this.compose_type_activity_last = 0;
      $(document).bind('keypress', function(e){ ref.compose_type_activity++; });
      $(document).keypress(function(e) { ref.compose_type_activity++; });
      this.local_save_timer = setInterval(function(){
        if (ref.compose_type_activity > ref.compose_type_activity_last) {
@@ -4864,6 +4913,9 @@
    if (filter)
      url._filter = filter;
    if (this.gui_objects.search_interval)
      url._interval = $(this.gui_objects.search_interval).val();
    if (search) {
      url._q = search;
@@ -4900,6 +4952,9 @@
    if (this.gui_objects.qsearchbox)
      this.gui_objects.qsearchbox.value = '';
    if (this.gui_objects.search_interval)
      $(this.gui_objects.search_interval).val('');
    if (this.env.qsearch)
      this.abort_request(this.env.qsearch);
@@ -4925,6 +4980,20 @@
      if (!this.qsearch(this.gui_objects.qsearchbox.value) && this.env.search_filter && this.env.search_filter != 'ALL')
        this.filter_mailbox(this.env.search_filter);
      if (scope != 'all')
        this.select_folder(this.env.mailbox, '', true);
    }
  };
  this.set_searchinterval = function(interval)
  {
    var old = this.env.search_interval;
    this.env.search_interval = interval;
    // re-send search query with new interval
    if (interval != old && this.env.search_request) {
      if (!this.qsearch(this.gui_objects.qsearchbox.value) && this.env.search_filter && this.env.search_filter != 'ALL')
        this.filter_mailbox(this.env.search_filter);
      if (interval)
        this.select_folder(this.env.mailbox, '', true);
    }
  };
@@ -8317,7 +8386,7 @@
    }
    // handle upload errors by parsing iframe content in onload
    frame.bind('load', {ts:ts}, onload);
    frame.on('load', {ts:ts}, onload);
    $(form).attr({
        target: frame_name,
@@ -8358,15 +8427,32 @@
    this.file_drag_hover(e, false);
    // prepare multipart form data composition
    var files = e.target.files || e.dataTransfer.files,
    var uri, files = e.target.files || e.dataTransfer.files,
      formdata = window.FormData ? new FormData() : null,
      fieldname = (this.env.filedrop.fieldname || '_file') + (this.env.filedrop.single ? '' : '[]'),
      boundary = '------multipartformboundary' + (new Date).getTime(),
      dashdash = '--', crlf = '\r\n',
      multipart = dashdash + boundary + crlf;
      multipart = dashdash + boundary + crlf,
      args = {_id: this.env.compose_id || this.env.cid || '', _remote: 1, _from: this.env.action};
    if (!files || !files.length)
    if (!files || !files.length) {
      // Roundcube attachment, pass its uri to the backend and attach
      if (uri = e.dataTransfer.getData('roundcube-uri')) {
        var ts = new Date().getTime(),
          // jQuery way to escape filename (#1490530)
          content = $('<span>').text(e.dataTransfer.getData('roundcube-name') || this.gettext('attaching')).html();
        args._uri = uri;
        args._uploadid = ts;
        // add to attachments list
        if (!this.add2attachment_list(ts, {name: '', html: content, classname: 'uploading', complete: false}))
          this.file_upload_id = this.set_busy(true, 'attaching');
        this.http_post(this.env.filedrop.action || 'upload', args);
      }
      return;
    }
    // inline function to submit the files to the server
    var submit_data = function() {
@@ -8382,10 +8468,12 @@
      // complete multipart content and post request
      multipart += dashdash + boundary + dashdash + crlf;
      args._uploadid = ts;
      $.ajax({
        type: 'POST',
        dataType: 'json',
        url: ref.url(ref.env.filedrop.action || 'upload', {_id: ref.env.compose_id||ref.env.cid||'', _uploadid: ts, _remote: 1, _from: ref.env.action}),
        url: ref.url(ref.env.filedrop.action || 'upload', args),
        contentType: formdata ? false : 'multipart/form-data; boundary=' + boundary,
        processData: false,
        timeout: 0, // disable default timeout set in ajaxSetup()