Aleksander Machniak
2014-07-09 1f164e4526a57ec25c2509d984e671ec411dc1d2
program/js/app.js
@@ -46,12 +46,12 @@
  this.messages = {};
  this.group2expand = {};
  this.http_request_jobs = {};
  this.menu_stack = new Array();
  this.menu_stack = [];
  // webmail client settings
  this.dblclick_time = 500;
  this.message_time = 5000;
  this.identifier_expr = new RegExp('[^0-9a-z\-_]', 'gi');
  this.identifier_expr = /[^0-9a-z_-]/gi;
  // environment defaults
  this.env = {
@@ -314,9 +314,9 @@
              });
            // avoid textarea loosing focus when hitting the save-response button/link
            for (var i=0; this.buttons['save-response'] && i < this.buttons['save-response'].length; i++) {
              $('#'+this.buttons['save-response'][i].id).mousedown(function(e){ return rcube_event.cancel(e); })
            }
            $.each(this.buttons['save-response'] || [], function (i, v) {
              $('#' + v.id).mousedown(function(e){ return rcube_event.cancel(e); })
            });
          }
          // init message compose form
@@ -346,10 +346,10 @@
          this.contact_list
            .addEventListener('initrow', function(o) { ref.triggerEvent('insertrow', { cid:o.uid, row:o }); })
            .addEventListener('select', function(o) { ref.compose_recipient_select(o); })
            .addEventListener('dblclick', function(o) { ref.compose_add_recipient('to'); })
            .addEventListener('dblclick', function(o) { ref.compose_add_recipient(); })
            .addEventListener('keypress', function(o) {
              if (o.key_pressed == o.ENTER_KEY) {
                if (!ref.compose_add_recipient('to')) {
                if (!ref.compose_add_recipient()) {
                  // execute link action on <enter> if not a recipient entry
                  if (o.last_selected && String(o.last_selected).charAt(0) == 'G') {
                    $(o.rows[o.last_selected].obj).find('a').first().click();
@@ -358,6 +358,9 @@
              }
            })
            .init();
          // remember last focused address field
          $('#_to,#_cc,#_bcc').focus(function() { ref.env.focused_field = this; });
        }
        if (this.gui_objects.addressbookslist) {
@@ -403,15 +406,23 @@
            .addEventListener('dragend', function(e) { ref.drag_end(e); })
            .init();
          if (this.env.cid)
            this.contact_list.highlight_row(this.env.cid);
          this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return ref.click_on_list(e); };
          $(this.gui_objects.qsearchbox).focusin(function() { ref.contact_list.blur(); });
          this.update_group_commands();
          this.command('list');
        }
        if (this.gui_objects.savedsearchlist) {
          this.savedsearchlist = new rcube_treelist_widget(this.gui_objects.savedsearchlist, {
            id_prefix: 'rcmli',
            id_encode: this.html_identifier_encode,
            id_decode: this.html_identifier_decode
          });
          this.savedsearchlist.addEventListener('select', function(node) {
            ref.triggerEvent('selectfolder', { folder:node.id, prefix:'rcmli' }); });
        }
        this.set_page_buttons();
@@ -471,9 +482,6 @@
            })
            .init()
            .focus();
          if (this.env.iid)
            this.identity_list.highlight_row(this.env.iid);
        }
        else if (this.gui_objects.sectionslist) {
          this.sections_list = new rcube_list_widget(this.gui_objects.sectionslist, {multiselect:false, draggable:false, keyboard:true});
@@ -557,6 +565,7 @@
    // init treelist widget
    if (this.gui_objects.folderlist && window.rcube_treelist_widget) {
      this.treelist = new rcube_treelist_widget(this.gui_objects.folderlist, {
          selectable: true,
          id_prefix: 'rcmli',
          id_encode: this.html_identifier_encode,
          id_decode: this.html_identifier_decode,
@@ -621,7 +630,7 @@
  {
    var ret, uid, cid, url, flag, aborted = false;
    if (obj && obj.blur && !(event || rcube_event.is_keyboard(event)))
    if (obj && obj.blur && !(event && rcube_event.is_keyboard(event)))
      obj.blur();
    // do nothing if interface is locked by other command (with exception for searching reset)
@@ -655,11 +664,11 @@
    // process external commands
    if (typeof this.command_handlers[command] === 'function') {
      ret = this.command_handlers[command](props, obj);
      ret = this.command_handlers[command](props, obj, event);
      return ret !== undefined ? ret : (obj ? false : true);
    }
    else if (typeof this.command_handlers[command] === 'string') {
      ret = window[this.command_handlers[command]](props, obj);
      ret = window[this.command_handlers[command]](props, obj, event);
      return ret !== undefined ? ret : (obj ? false : true);
    }
@@ -1344,7 +1353,7 @@
  this.command_enabled = function(cmd)
  {
    return this.commands[cmd];
  }
  };
  // lock/unlock interface
  this.set_busy = function(a, message, id)
@@ -1386,10 +1395,11 @@
  // switch to another application task
  this.switch_task = function(task)
  {
    if (this.task===task && task!='mail')
    if (this.task === task && task != 'mail')
      return;
    var url = this.get_task_url(task);
    if (task == 'mail')
      url += '&_mbox=INBOX';
    else if (task == 'logout')
@@ -1444,10 +1454,10 @@
  this.save_pref = function(prop)
  {
    var request = {'_name': prop.name, '_value': prop.value};
    var request = {_name: prop.name, _value: prop.value};
    if (prop.session)
      request['_session'] = prop.session;
      request._session = prop.session;
    if (prop.env)
      this.env[prop.env] = prop.value;
@@ -1587,7 +1597,7 @@
      // select the folder if one of its childs is currently selected
      // don't select if it's virtual (#1488346)
      if (this.env.mailbox && this.env.mailbox.startsWith(name + this.env.delimiter) && !node.virtual)
      if (!node.virtual && this.env.mailbox && this.env.mailbox.startsWith(name + this.env.delimiter))
        this.command('list', name);
    }
    else {
@@ -1629,7 +1639,7 @@
    }
    // reset popup menus; delayed to have updated menu_stack data
    window.setTimeout(function(e){
    setTimeout(function(e){
      var obj, skip, config, id, i, parents = $(target).parents();
      for (i = ref.menu_stack.length - 1; i >= 0; i--) {
        id = ref.menu_stack[i];
@@ -1669,6 +1679,9 @@
    var target = e.target || {},
      keyCode = rcube_event.get_keycode(e);
    // save global reference for keyboard detection on click events in IE
    rcube_event._last_keyboard_event = e;
    if (e.keyCode != 27 && (!this.menu_keyboard_active || target.nodeName == 'TEXTAREA' || target.nodeName == 'SELECT')) {
      return true;
    }
@@ -1678,8 +1691,8 @@
      case 40:
      case 63232: // "up", in safari keypress
      case 63233: // "down", in safari keypress
        focus_menu_item(mod = keyCode == 38 || keyCode == 63232 ? -1 : 1);
        break;
        focus_menu_item(keyCode == 38 || keyCode == 63232 ? -1 : 1);
        return rcube_event.cancel(e);
      case 9:   // tab
        if (this.focused_menu) {
@@ -1842,9 +1855,6 @@
        return (this.env.mailboxes[id]
            && !this.env.mailboxes[id].virtual
            && (this.env.mailboxes[id].id != this.env.mailbox || this.is_multifolder_listing())) ? 1 : 0;
      case 'settings':
        return id != this.env.mailbox ? 1 : 0;
      case 'addressbook':
        var target;
@@ -2428,6 +2438,9 @@
      url._framed = 1;
    }
    if (this.env.uid)
      url._uid = this.env.uid;
    // load message list to target frame/window
    if (mbox) {
      this.set_busy(true, 'loading');
@@ -2474,7 +2487,7 @@
        selection.push(selected[i]);
    this.message_list.selection = selection;
  }
  };
  // expand all threads with unread children
  this.expand_unread = function()
@@ -2487,8 +2500,10 @@
        this.message_list.expand_all(r);
        this.set_unread_children(r.uid);
      }
      new_row = new_row.nextSibling;
    }
    return false;
  };
@@ -2705,8 +2720,8 @@
    }
    // update unread_children for roots
    for (var i=0; i<roots.length; i++)
      this.set_unread_children(roots[i].uid);
    for (r=0; r<roots.length; r++)
      this.set_unread_children(roots[r].uid);
    return count;
  };
@@ -3482,6 +3497,12 @@
  this.compose_add_recipient = function(field)
  {
    // find last focused field name
    if (!field) {
      field = $(this.env.focused_field).filter(':visible');
      field = field.length ? field.attr('id').replace('_', '') : 'to';
    }
    var recipients = [], input = $('#_'+field), delim = this.env.recipients_delimiter;
    if (this.contact_list && this.contact_list.selection.length) {
@@ -3550,26 +3571,32 @@
        myprompt = $('<div class="prompt">').html('<div class="message">' + this.get_label('nosubjectwarning') + '</div>')
          .appendTo(document.body),
        prompt_value = $('<input>').attr({type: 'text', size: 30}).val(this.get_label('nosubject'))
          .appendTo(myprompt);
          .appendTo(myprompt),
        save_func = function() {
          input_subject.val(prompt_value.val());
          myprompt.dialog('close');
          ref.command(cmd, { nocheck:true });  // repeat command which triggered this
        };
      buttons[this.get_label('cancel')] = function(){
      buttons[this.get_label('sendmessage')] = function() {
        save_func($(this));
      };
      buttons[this.get_label('cancel')] = function() {
        input_subject.focus();
        $(this).dialog('close');
      };
      buttons[this.get_label('sendmessage')] = function(){
        input_subject.val(prompt_value.val());
        $(this).dialog('close');
        ref.command(cmd, { nocheck:true });  // repeat command which triggered this
      };
      myprompt.dialog({
        modal: true,
        resizable: false,
        buttons: buttons,
        close: function(event, ui) { $(this).remove() }
        close: function(event, ui) { $(this).remove(); }
      });
      prompt_value.select();
      prompt_value.select().keydown(function(e) {
        if (e.which == 13) save_func();
      });
      return false;
    }
@@ -3593,6 +3620,11 @@
    if (!result && e) {
      // fix selector value if operation failed
      $(e.target).filter('select').val(props.html ? 'plain' : 'html');
    }
    if (result) {
      // update internal format flag
      $("input[name='_is_html']").val(props.html ? 1 : 0);
    }
    return result;
@@ -3642,7 +3674,7 @@
      $(this).dialog('close');
    };
    this.show_popup_dialog(html, this.gettext('savenewresponse'), buttons);
    this.show_popup_dialog(html, this.gettext('newresponse'), buttons);
    $('#ffresponsetext').val(text);
    $('#ffresponsename').select();
@@ -3690,10 +3722,7 @@
    // submit delete request
    if (key && confirm(this.get_label('deleteresponseconfirm'))) {
      this.http_post('settings/delete-response', { _key: key }, false);
      return true;
    }
    return false;
  };
  // updates spellchecker buttons on state change
@@ -3701,8 +3730,9 @@
  {
    var active = this.editor.spellcheck_state();
    if (this.buttons.spellcheck)
      $('#'+this.buttons.spellcheck[0].id)[active ? 'addClass' : 'removeClass']('selected');
    $.each(this.buttons.spellcheck || [], function(i, v) {
      $('#' + v.id)[active ? 'addClass' : 'removeClass']('selected');
    });
    return active;
  };
@@ -3900,7 +3930,7 @@
      }
      this.local_storage_remove_item('compose.index');
    }
  }
  };
  this.change_identity = function(obj, show_sig)
@@ -4047,6 +4077,14 @@
    if (upload_id)
      this.triggerEvent('fileuploaded', {name: name, attachment: att, id: upload_id});
    if (!this.env.attachments)
      this.env.attachments = {};
    if (upload_id && this.env.attachments[upload_id])
      delete this.env.attachments[upload_id];
    this.env.attachments[name] = att;
    if (!this.gui_objects.attachmentlist)
      return false;
@@ -4055,7 +4093,7 @@
    if (!att.complete && att.frame)
      att.html = '<a title="'+this.get_label('cancel')+'" onclick="return rcmail.cancel_attachment_upload(\''+name+'\', \''+att.frame+'\');" href="#cancelupload" class="cancelupload">'
        + (this.env.cancelicon ? '<img src="'+this.env.cancelicon+'" alt="" />' : this.get_label('cancel')) + '</a>' + att.html;
        + (this.env.cancelicon ? '<img src="'+this.env.cancelicon+'" alt="'+this.get_label('cancel')+'" />' : this.get_label('cancel')) + '</a>' + att.html;
    var indicator, li = $('<li>');
@@ -4072,10 +4110,9 @@
      li.appendTo(this.gui_objects.attachmentlist);
    }
    if (upload_id && this.env.attachments[upload_id])
      delete this.env.attachments[upload_id];
    this.env.attachments[name] = att;
    // set tabindex attribute
    var tabindex = $(this.gui_objects.attachmentlist).attr('data-tabindex') || '0';
    li.find('a').attr('tabindex', tabindex);
    return true;
  };
@@ -4114,7 +4151,7 @@
  this.upload_progress_update = function(param)
  {
    var elem = $('#'+param.name + '> span');
    var elem = $('#'+param.name + ' > span');
    if (!elem.length || !param.text)
      return;
@@ -4271,21 +4308,22 @@
    this.display_message(msg, type);
    if (this.env.extwin) {
      var rc = this.opener();
      this.lock_form(this.gui_objects.messageform);
      var rc = this.opener();
      if (rc) {
        rc.display_message(msg, type);
        // refresh the folder where sent message was saved or replied message comes from
        if (folders && rc.env.task == 'mail' && rc.env.action == '' && $.inArray(rc.env.mailbox, folders) >= 0) {
          // @TODO: try with 'checkmail' here when #1485186 is fixed. See also #1489249.
          rc.command('list');
          rc.command('checkmail');
        }
      }
      setTimeout(function(){ window.close() }, 1000);
      setTimeout(function() { window.close(); }, 1000);
    }
    else {
      // before redirect we need to wait some time for Chrome (#1486177)
      setTimeout(function(){ ref.list_mailbox(); }, 500);
      setTimeout(function() { ref.list_mailbox(); }, 500);
    }
  };
@@ -4505,10 +4543,6 @@
      this.ksearch_pane = $('<div>').attr('id', 'rcmKSearchpane').attr('role', 'listbox')
        .css({ position:'absolute', 'z-index':30000 }).append(ul).appendTo(document.body);
      this.ksearch_pane.__ul = ul[0];
      // register (delegate) event handlers
      ul.on('mouseover', 'li', function(e){ ref.ksearch_select(e.target); })
        .on('onmouseup', 'li', function(e){ ref.ksearch_click(e.target); })
    }
    ul = this.ksearch_pane.__ul;
@@ -4531,7 +4565,7 @@
    // add each result line to list
    if (results && (len = results.length)) {
      for (i=0; i < len && maxlen > 0; i++) {
        text = typeof results[i] === 'object' ? results[i].name : results[i];
        text = typeof results[i] === 'object' ? (results[i].display || results[i].name) : results[i];
        type = typeof results[i] === 'object' ? results[i].type : '';
        id = i + this.env.contacts.length;
        $('<li>').attr('id', 'rcmkSearchItem' + id)
@@ -4539,6 +4573,8 @@
          .html(this.quote_html(text.replace(new RegExp('('+RegExp.escape(value)+')', 'ig'), '##$1%%')).replace(/##([^%]+)%%/g, '<b>$1</b>'))
          .addClass(type || '')
          .appendTo(ul)
          .mouseover(function() { ref.ksearch_select(this); })
          .mouseup(function() { ref.ksearch_click(this); })
          .get(0)._rcm_id = id;
        maxlen -= 1;
      }
@@ -4731,7 +4767,8 @@
        $(this.gui_objects.addresslist_title).html(this.get_label('contacts'));
    }
    this.select_folder(folder, '', true);
    if (!this.env.search_id)
      this.select_folder(folder, '', true);
    // load contacts remotely
    if (this.gui_objects.contactslist) {
@@ -4963,7 +5000,7 @@
  {
    var selection = this.contact_list ? this.contact_list.get_selection() : [];
    // exit if no mailbox specified or if selection is empty
    // exit if no contact specified or if selection is empty
    if (!selection.length && !this.env.cid)
      return;
@@ -5155,7 +5192,7 @@
      // find list (UL) element
      if (type == 'contactsearch')
        ul = this.gui_objects.folderlist;
        ul = this.gui_objects.savedsearchlist;
      else
        ul = $('ul.groups', this.get_folder_li(this.env.source,'',true));
@@ -5256,7 +5293,7 @@
        .html(prop.name);
    this.env.contactfolders[key] = this.env.contactgroups[key] = prop;
    this.treelist.insert({ id:key, html:link, classes:['contactgroup'] }, prop.source, true);
    this.treelist.insert({ id:key, html:link, classes:['contactgroup'] }, prop.source, 'contactgroup');
    this.triggerEvent('group_insert', { id:prop.id, source:prop.source, name:prop.name, li:this.treelist.get_item(key) });
  };
@@ -5538,7 +5575,7 @@
        .html(name),
      prop = { name:name, id:id };
    this.treelist.insert({ id:key, html:link, classes:['contactsearch'] }, null, 'contactsearch');
    this.savedsearchlist.insert({ id:key, html:link, classes:['contactsearch'] }, null, 'contactsearch');
    this.select_folder(key,'',true);
    this.enable_command('search-delete', true);
    this.env.search_id = id;
@@ -5564,7 +5601,7 @@
  this.remove_search_item = function(id)
  {
    var li, key = 'S'+id;
    if (this.treelist.remove(key)) {
    if (this.savedsearchlist.remove(key)) {
      this.triggerEvent('search_delete', { id:id, li:li });
    }
@@ -5584,7 +5621,13 @@
    }
    this.reset_qsearch();
    this.select_folder('S'+id, '', true);
    if (this.savedsearchlist) {
      this.treelist.select('');
      this.savedsearchlist.select('S'+id);
    }
    else
      this.select_folder('S'+id, '', true);
    // reset vars
    this.env.current_page = 1;
@@ -5654,10 +5697,8 @@
      id = this.env.iid ? this.env.iid : selection[0];
    // submit request with appended token
    if (confirm(this.get_label('deleteidentityconfirm')))
      this.goto_url('delete-identity', { _iid: id, _token: this.env.request_token }, true);
    return true;
    if (id && confirm(this.get_label('deleteidentityconfirm')))
      this.http_post('settings/delete-identity', { _iid: id }, true);
  };
  this.update_identity_row = function(id, name, add)
@@ -5701,6 +5742,23 @@
        frame.location.href = this.env.blankpage;
      }
    }
    this.enable_command('delete', false);
  };
  this.remove_identity = function(id)
  {
    var frame, list = this.identity_list,
      rid = this.html_identifier(id);
    if (list && id) {
      list.remove_row(rid);
      if (this.env.contentframe && (frame = this.get_frame_window(this.env.contentframe))) {
        frame.location.href = this.env.blankpage;
      }
    }
    this.enable_command('delete', false);
  };
@@ -5714,62 +5772,38 @@
    this.last_sub_rx = RegExp('['+delim+']?[^'+delim+']+$');
    this.subscription_list = new rcube_list_widget(this.gui_objects.subscriptionlist,
      {multiselect:false, draggable:true, keyboard:true, toggleselect:true});
    this.subscription_list = new rcube_treelist_widget(this.gui_objects.subscriptionlist, {
        selectable: true
    });
    this.subscription_list
      .addEventListener('select', function(o){ ref.subscription_select(o); })
      .addEventListener('dragstart', function(o){ ref.drag_active = true; })
      .addEventListener('dragend', function(o){ ref.subscription_move_folder(o); })
      .addEventListener('initrow', function (row) {
        row.obj.onmouseover = function() { ref.focus_subscription(row.id); };
        row.obj.onmouseout = function() { ref.unfocus_subscription(row.id); };
      })
      .init()
      .focus();
      .addEventListener('select', function(node) { ref.subscription_select(node.id); })
      .draggable({cancel: '#mailboxroot'})
      .droppable({
        // @todo: find better way, accept callback is executed for every folder
        // on the list when dragging starts (and stops), this is slow, but
        // I didn't find a method to check droptarget on over event
        accept: function(node) {
          var source = ref.env.subscriptionrows[$(node).attr('id')],
            dest = ref.env.subscriptionrows[this.id],
            source_name = source[0],
            dest_name = dest[0];
    $('#mailboxroot')
      .mouseover(function(){ ref.focus_subscription(this.id); })
      .mouseout(function(){ ref.unfocus_subscription(this.id); })
  };
  this.focus_subscription = function(id)
  {
    var row, folder;
    if (this.drag_active && this.env.mailbox && (row = document.getElementById(id)))
      if (this.env.subscriptionrows[id] &&
          (folder = this.env.subscriptionrows[id][0]) !== null
      ) {
        if (this.check_droptarget(folder) &&
            !this.env.subscriptionrows[this.get_folder_row_id(this.env.mailbox)][2] &&
            folder != this.env.mailbox.replace(this.last_sub_rx, '') &&
            !folder.startsWith(this.env.mailbox + this.env.delimiter)
        ) {
          this.env.dstfolder = folder;
          $(row).addClass('droptarget');
          return !source[2]
            && dest_name != source_name.replace(ref.last_sub_rx, '')
            && !dest_name.startsWith(source_name + ref.env.delimiter);
        },
        drop: function(e, ui) {
          ref.subscription_move_folder(ui.draggable.attr('id'), this.id);
        }
      }
      });
  };
  this.unfocus_subscription = function(id)
  this.subscription_select = function(id)
  {
    var row = $('#'+id);
    var folder;
    this.env.dstfolder = null;
    if (row.length && this.env.subscriptionrows[id])
      row.removeClass('droptarget');
    else
      $(this.subscription_list.frame).removeClass('droptarget');
  };
  this.subscription_select = function(list)
  {
    var id, folder;
    if (list && (id = list.get_single_selection()) &&
        (folder = this.env.subscriptionrows['rcmrow'+id])
    ) {
    if (id && id != 'mailboxroot' && (folder = this.env.subscriptionrows[id])) {
      this.env.mailbox = folder[0];
      this.show_folder(folder[0]);
      this.enable_command('delete-folder', !folder[2]);
@@ -5781,24 +5815,21 @@
    }
  };
  this.subscription_move_folder = function(list)
  this.subscription_move_folder = function(from, to)
  {
    if (this.env.mailbox && this.env.dstfolder !== null &&
        this.env.dstfolder != this.env.mailbox &&
        this.env.dstfolder != this.env.mailbox.replace(this.last_sub_rx, '')
    ) {
      var path = this.env.mailbox.split(this.env.delimiter),
        basename = path.pop(),
        newname = this.env.dstfolder === '' ? basename : this.env.dstfolder + this.env.delimiter + basename;
    var source = this.env.subscriptionrows[from][0];
      dest = this.env.subscriptionrows[to][0];
      if (newname != this.env.mailbox) {
        this.http_post('rename-folder', {_folder_oldname: this.env.mailbox, _folder_newname: newname}, this.set_busy(true, 'foldermoving'));
        this.subscription_list.draglayer.hide();
    if (source && dest !== null && source != dest && dest != source.replace(this.last_sub_rx, '')) {
      var path = source.split(this.env.delimiter),
        basename = path.pop(),
        newname = dest === '' ? basename : dest + this.env.delimiter + basename;
      if (newname != source) {
        this.http_post('rename-folder', {_folder_oldname: source, _folder_newname: newname},
          this.set_busy(true, 'foldermoving'));
      }
    }
    this.drag_active = false;
    this.unfocus_subscription(this.get_folder_row_id(this.env.dstfolder));
  };
  // tell server to create and subscribe a new mailbox
@@ -5814,8 +5845,7 @@
      folder = this.env.subscriptionrows[id][0];
    if (folder && confirm(this.get_label('deletefolderconfirm'))) {
      var lock = this.set_busy(true, 'folderdeleting');
      this.http_post('delete-folder', {_mbox: folder}, lock);
      this.http_post('delete-folder', {_mbox: folder}, this.set_busy(true, 'folderdeleting'));
    }
  };
@@ -5827,9 +5857,9 @@
    var row, n, tmp, tmp_name, rowid, collator,
      folders = [], list = [], slist = [],
      tbody = this.gui_objects.subscriptionlist.tBodies[0],
      refrow = $('tr', tbody).get(1),
      id = 'rcmrow'+((new Date).getTime());
      list_element = $(this.gui_objects.subscriptionlist),
      refrow = $('li', list_element).get(1),
      id = 'rcmli'+((new Date).getTime());
    if (!refrow) {
      // Refresh page if we don't have a table row to clone
@@ -5844,7 +5874,7 @@
    row.attr({id: id, 'class': class_name});
    // set folder name
    row.find('td:first').html(display_name);
    $('.name', row).html(display_name);
    // update subscription checkbox
    $('input[name="_subscribed[]"]', row).val(name)
@@ -5915,12 +5945,13 @@
    // add row to the table
    if (rowid)
      $('#'+rowid).after(row);
      $('#' + rowid).after(row);
    else
      row.appendTo(tbody);
      list_element.append(row);
    // update list widget
    this.subscription_list.clear_selection();
    this.subscription_list.select();
    if (!skip_init)
      this.init_subscription_list();
@@ -5937,11 +5968,11 @@
    if (!this.gui_objects.subscriptionlist) {
      if (this.is_framed)
        return parent.rcmail.replace_folder_row(oldfolder, newfolder, display_name, is_protected, class_name);
      return false;
    }
    var i, n, len, name, dispname, oldrow, tmprow, row, level,
      tbody = this.gui_objects.subscriptionlist.tBodies[0],
      folders = this.env.subscriptionrows,
      id = this.get_folder_row_id(oldfolder),
      prefix_len = oldfolder.length,
@@ -5952,7 +5983,6 @@
    // no renaming, only update class_name
    if (oldfolder == newfolder) {
      $('#'+id).attr('class', class_name || '');
      this.subscription_list.focus();
      return;
    }
@@ -5989,7 +6019,7 @@
          for (i=level; i<0; i++)
            dispname = '&nbsp;&nbsp;&nbsp;&nbsp;' + dispname;
        }
        row.find('td:first').html(dispname);
        $('.name', row).html(dispname);
        this.env.subscriptionrows[id][1] = dispname;
      }
    }
@@ -6017,8 +6047,8 @@
  this._remove_folder_row = function(id)
  {
    this.subscription_list.remove_row(id.replace(/^rcmrow/, ''));
    $('#'+id).remove();
    this.subscription_list.remove(id.replace(/^rcmli/, ''));
    $('#' + id).remove();
    delete this.env.subscriptionrows[id];
  };
@@ -6449,6 +6479,10 @@
  // mark a mailbox as selected and set environment variable
  this.select_folder = function(name, prefix, encode)
  {
    if (this.savedsearchlist) {
      this.savedsearchlist.select('');
    }
    if (this.treelist) {
      this.treelist.select(name);
    }
@@ -7250,11 +7284,24 @@
          this.enable_command('expand-all', 'expand-unread', 'collapse-all', this.env.threading && this.env.messagecount && !is_multifolder);
          if ((response.action == 'list' || response.action == 'search') && this.message_list) {
            var list = this.message_list, uid = this.env.list_uid;
            // highlight message row when we're back from message page
            if (uid) {
              if (!list.rows[uid])
                uid += '-' + this.env.mailbox;
              if (list.rows[uid]) {
                list.select(uid);
              }
              delete this.env.list_uid;
            }
            this.enable_command('set-listmode', this.env.threads && !is_multifolder);
            if (this.message_list.rowcount > 0)
              this.message_list.focus();
            this.msglist_select(this.message_list);
            this.triggerEvent('listupdate', { folder:this.env.mailbox, rowcount:this.message_list.rowcount });
            if (list.rowcount > 0)
              list.focus();
            this.msglist_select(list);
            this.triggerEvent('listupdate', { folder:this.env.mailbox, rowcount:list.rowcount });
          }
        }
        else if (this.task == 'addressbook') {
@@ -7475,8 +7522,10 @@
  // post the given form to a hidden iframe
  this.async_upload_form = function(form, action, onload)
  {
    var frame, ts = new Date().getTime(),
      frame_name = 'rcmupload'+ts;
    // create hidden iframe
    var ts = new Date().getTime(),
      frame_name = 'rcmupload' + ts,
      frame = this.async_upload_form_frame(frame_name);
    // upload progress support
    if (this.env.upload_progress_name) {
@@ -7491,31 +7540,24 @@
      field.val(ts);
    }
    // have to do it this way for IE
    // otherwise the form will be posted to a new window
    if (document.all) {
      document.body.insertAdjacentHTML('BeforeEnd', '<iframe name="'+frame_name+'"'
        + ' src="program/resources/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>');
      frame = $('iframe[name="'+frame_name+'"]');
    }
    // for standards-compliant browsers
    else {
      frame = $('<iframe>').attr('name', frame_name)
        .css({border: 'none', width: 0, height: 0, visibility: 'hidden'})
        .appendTo(document.body);
    }
    // handle upload errors, parsing iframe content in onload
    // handle upload errors by parsing iframe content in onload
    frame.bind('load', {ts:ts}, onload);
    $(form).attr({
        target: frame_name,
        action: this.url(action, { _id:this.env.compose_id||'', _uploadid:ts }),
        action: this.url(action, {_id: this.env.compose_id || '', _uploadid: ts, _from: this.env.action}),
        method: 'POST'})
      .attr(form.encoding ? 'encoding' : 'enctype', 'multipart/form-data')
      .submit();
    return frame_name;
  };
  // create iframe element for files upload
  this.async_upload_form_frame = function(name)
  {
    return $('<iframe>').attr({name: name, style: 'border: none; width: 0; height: 0; visibility: hidden'})
      .appendTo(document.body);
  };
  // html5 file-drop API
@@ -7566,7 +7608,7 @@
      $.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 }),
        url: ref.url(ref.env.filedrop.action || 'upload', {_id: ref.env.compose_id||ref.env.cid||'', _uploadid: ts, _remote: 1, _from: ref.env.action}),
        contentType: formdata ? false : 'multipart/form-data; boundary=' + boundary,
        processData: false,
        timeout: 0, // disable default timeout set in ajaxSetup()