Aleksander Machniak
2016-02-05 bd0551b22076b82a6d49e9f7a2b2e0c90a1b2326
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
@@ -335,7 +352,7 @@
        if (this.gui_objects.mailboxlist) {
          this.env.unread_counts = {};
          this.gui_objects.folderlist = this.gui_objects.mailboxlist;
          this.http_request('getunread');
          this.http_request('getunread', {_page: this.env.current_page});
        }
        // init address book widget
@@ -518,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();
@@ -583,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);  }
@@ -1002,7 +1019,7 @@
            break;
        }
        this.goto_url('get', qstring+'&_download=1', false);
        this.goto_url('get', qstring+'&_download=1', false, true);
        break;
      case 'select-all':
@@ -1208,10 +1225,10 @@
      case 'download':
        if (this.env.action == 'get') {
          location.href = location.href.replace(/_frame=/, '_download=');
          location.href = this.secure_url(location.href.replace(/_frame=/, '_download='));
        }
        else if (uid = this.get_single_uid()) {
          this.goto_url('viewsource', this.params_from_uid(uid, {_save: 1}));
          this.goto_url('viewsource', this.params_from_uid(uid, {_save: 1}), false, true);
        }
        break;
@@ -1299,13 +1316,13 @@
      case 'export':
        if (this.contact_list.rowcount > 0) {
          this.goto_url('export', { _source: this.env.source, _gid: this.env.group, _search: this.env.search_request });
          this.goto_url('export', { _source: this.env.source, _gid: this.env.group, _search: this.env.search_request }, false, true);
        }
        break;
      case 'export-selected':
        if (this.contact_list.rowcount > 0) {
          this.goto_url('export', { _source: this.env.source, _gid: this.env.group, _cid: this.contact_list.get_selection().join(',') });
          this.goto_url('export', { _source: this.env.source, _gid: this.env.group, _cid: this.contact_list.get_selection().join(',') }, false, true);
        }
        break;
@@ -1420,7 +1437,7 @@
    if (task == 'mail')
      url += '&_mbox=INBOX';
    else if (task == 'logout' && !this.env.server_error) {
      url += '&_token=' + this.env.request_token;
      url = this.secure_url(url);
      this.clear_compose_data();
    }
@@ -1468,6 +1485,12 @@
    return url + '?' + name + '=' + value;
  };
  // append CSRF protection token to the given url
  this.secure_url = function(url)
  {
    return this.add_url(url, '_token', this.env.request_token);
  },
  this.is_framed = function()
  {
@@ -2497,22 +2520,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) {};
@@ -2728,8 +2752,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);
@@ -3374,12 +3399,12 @@
    mailvelope.getKeyring(keyring).then(function(kr) {
      ref.mailvelope_keyring = kr;
      ref.mailvelope_init(action, kr);
    }).catch(function(err) {
    }, function(err) {
      // attempt to create a new keyring for this app/user
      mailvelope.createKeyring(keyring).then(function(kr) {
        ref.mailvelope_keyring = kr;
        ref.mailvelope_init(action, kr);
      }).catch(function(err) {
      }, function(err) {
        console.error(err);
      });
    });
@@ -3416,8 +3441,6 @@
    }
    else if (action == 'compose') {
      this.env.compose_commands.push('compose-encrypted');
      // display the toolbar button
      $('#' + this.buttons['compose-encrypted'][0].id).show();
      var is_html = $('input[name="_is_html"]').val() > 0;
@@ -3509,7 +3532,7 @@
            ref.remove_from_attachment_list(name);
          });
        }
      }).catch(function(err) {
      }, function(err) {
        console.error(err);
        console.log(options);
      });
@@ -3632,15 +3655,15 @@
          form.submit();
        }).catch(function(err) {
        }, function(err) {
          console.log(err);
        });  // mailvelope_editor.encrypt()
      }).catch(function(err) {
      }, function(err) {
        console.error(err);
      });  // mailvelope_keyring.validKeyForAddress(senders)
    }).catch(function(err) {
    }, function(err) {
      console.error(err);
    });  // mailvelope_keyring.validKeyForAddress(recipients)
@@ -3654,7 +3677,7 @@
      $(selector).addClass('mailvelope').children().not('iframe').hide();
      ref.hide_message(msgid);
      setTimeout(function() { $(window).resize(); }, 10);
    }).catch(function(err) {
    }, function(err) {
      console.error(err);
      ref.hide_message(msgid);
      ref.display_message('Message decryption failed: ' + err.message, 'error')
@@ -3710,7 +3733,7 @@
      if (missing_keys.length) {
        ref.display_message(ref.get_label('nopubkeyfor').replace('$email', missing_keys.join(', ')), 'warning');
      }
    }, function() {
    }).fail(function() {
      console.error('Pubkey lookup failed with', arguments);
      ref.hide_message(lock);
      ref.display_message('pubkeysearcherror', 'error');
@@ -3808,7 +3831,7 @@
              btn.closest('.key').fadeOut();
              ref.display_message(ref.get_label('keyimportsuccess').replace('$key', $key), 'confirmation');
            }
          }).catch(function(err) {
          }, function(err) {
            console.log(err);
          });
        });
@@ -4297,7 +4320,7 @@
      '<textarea name="text" id="ffresponsetext" cols="40" rows="8"></textarea></div>' +
      '</form>';
    buttons[this.gettext('save')] = function(e) {
    buttons[this.get_label('save')] = function(e) {
      var name = $('#ffresponsename').val(),
        text = $('#ffresponsetext').val();
@@ -4313,11 +4336,11 @@
      $(this).dialog('close');
    };
    buttons[this.gettext('cancel')] = function() {
    buttons[this.get_label('cancel')] = function() {
      $(this).dialog('close');
    };
    this.show_popup_dialog(html, this.gettext('newresponse'), buttons, {button_classes: ['mainaction']});
    this.show_popup_dialog(html, this.get_label('newresponse'), buttons, {button_classes: ['mainaction']});
    $('#ffresponsetext').val(text);
    $('#ffresponsename').select();
@@ -4337,10 +4360,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
@@ -4438,7 +4461,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) {
@@ -5553,7 +5576,7 @@
      // add link to pop back to parent group
      if (this.env.address_group_stack.length > 1) {
        $('<a href="#list">...</a>')
          .attr('title', this.gettext('uponelevel'))
          .attr('title', this.get_label('uponelevel'))
          .addClass('poplink')
          .appendTo(boxtitle)
          .click(function(e){ return ref.command('popgroup','',this); });
@@ -7877,9 +7900,11 @@
    }
  };
  this.goto_url = function(action, query, lock)
  this.goto_url = function(action, query, lock, secure)
  {
    this.redirect(this.url(action, query), lock);
    var url = this.url(action, query)
    if (secure) url = this.secure_url(url);
    this.redirect(url, lock);
  };
  this.location_href = function(url, target, frame)
@@ -7908,8 +7933,11 @@
  };
  // send a http request to the server
  this.http_request = function(action, data, lock)
  this.http_request = function(action, data, lock, type)
  {
    if (type != 'POST')
      type = 'GET';
    if (typeof data !== 'object')
      data = rcube_parse_query(data);
@@ -7933,60 +7961,26 @@
      }
    }
    var url = this.url(action, data);
    // send request
    this.log('HTTP GET: ' + url);
    var url = this.url(action);
    // reset keep-alive interval
    this.start_keepalive();
    // send request
    return $.ajax({
      type: 'GET', url: url, dataType: 'json',
      type: type, url: url, data: data, dataType: 'json',
      success: function(data) { ref.http_response(data); },
      error: function(o, status, err) { ref.http_error(o, status, err, lock, action); }
    });
  };
  // send a http GET request to the server
  this.http_get = this.http_request;
  // send a http POST request to the server
  this.http_post = function(action, data, lock)
  {
    if (typeof data !== 'object')
      data = rcube_parse_query(data);
    data._remote = 1;
    data._unlock = lock ? lock : 0;
    // trigger plugin hook
    var result = this.triggerEvent('request'+action, data);
    // abort if one of the handlers returned false
    if (result === false) {
      if (data._unlock)
        this.set_busy(false, null, data._unlock);
      return false;
    }
    else if (result !== undefined) {
      data = result;
      if (data._action) {
        action = data._action;
        delete data._action;
      }
    }
    var url = this.url(action);
    // send request
    this.log('HTTP POST: ' + url);
    // reset keep-alive interval
    this.start_keepalive();
    return $.ajax({
      type: 'POST', url: url, data: data, dataType: 'json',
      success: function(data){ ref.http_response(data); },
      error: function(o, status, err) { ref.http_error(o, status, err, lock, action); }
    });
    return this.http_request(action, data, lock, 'POST');
  };
  // aborts ajax request
@@ -8369,7 +8363,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,
@@ -8391,7 +8385,7 @@
  // html5 file-drop API
  this.document_drag_hover = function(e, over)
  {
    e.preventDefault();
    // don't e.preventDefault() here to not block text dragging on the page (#1490619)
    $(this.gui_objects.filedrop)[(over?'addClass':'removeClass')]('active');
  };
@@ -8410,15 +8404,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.get_label('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() {
@@ -8434,10 +8445,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()
@@ -8750,14 +8763,10 @@
    if (!this.env.browser_capabilities)
      this.env.browser_capabilities = {};
    if (this.env.browser_capabilities.pdf === undefined)
      this.env.browser_capabilities.pdf = this.pdf_support_check();
    if (this.env.browser_capabilities.flash === undefined)
      this.env.browser_capabilities.flash = this.flash_support_check();
    if (this.env.browser_capabilities.tif === undefined)
      this.tif_support_check();
    $.each(['pdf', 'flash', 'tif'], function() {
      if (ref.env.browser_capabilities[this] === undefined)
        ref.env.browser_capabilities[this] = ref[this + '_support_check']();
    });
  };
  // Returns browser capabilities string
@@ -8776,11 +8785,14 @@
  this.tif_support_check = function()
  {
    var img = new Image();
    window.setTimeout(function() {
      var img = new Image();
      img.onload = function() { ref.env.browser_capabilities.tif = 1; };
      img.onerror = function() { ref.env.browser_capabilities.tif = 0; };
      img.src = ref.assets_path('program/resources/blank.tif');
    }, 10);
    img.onload = function() { ref.env.browser_capabilities.tif = 1; };
    img.onerror = function() { ref.env.browser_capabilities.tif = 0; };
    img.src = this.assets_path('program/resources/blank.tif');
    return 0;
  };
  this.pdf_support_check = function()
@@ -8816,6 +8828,14 @@
        return 1;
    }
    window.setTimeout(function() {
      $('<object>').css({position: 'absolute', left: '-10000px'})
        .attr({data: ref.assets_path('program/resources/dummy.pdf'), width: 1, height: 1, type: 'application/pdf'})
        .load(function() { ref.env.browser_capabilities.pdf = 1; })
        .error(function() { ref.env.browser_capabilities.pdf = 0; })
        .appendTo($('body'));
      }, 10);
    return 0;
  };