thomascube
2011-01-22 de06fc3d29ac67e8879c54a0d5a16a15723db7b6
program/js/app.js
@@ -3,7 +3,7 @@
 | Roundcube Webmail Client Script                                       |
 |                                                                       |
 | This file is part of the Roundcube Webmail client                     |
 | Copyright (C) 2005-2010, Roundcube Dev, - Switzerland                 |
 | Copyright (C) 2005-2010, The Roundcube Dev Team                       |
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 +-----------------------------------------------------------------------+
@@ -37,7 +37,7 @@
  // webmail client settings
  this.dblclick_time = 500;
  this.message_time = 1500;
  this.message_time = 2000;
  this.identifier_expr = new RegExp('[^0-9a-z\-_]', 'gi');
@@ -319,7 +319,20 @@
        if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform) {
          this.enable_command('save', true);
          $("input[type='text']").first().select();
          this.enable_command('upload-photo', this.env.coltypes.photo ? true : false);
          this.enable_command('delete-photo', this.env.coltypes.photo && this.env.action == 'edit');
          for (var col in this.env.coltypes)
            this.init_edit_field(col, null);
          $('.contactfieldgroup .row a.deletebutton').click(function(){ ref.delete_edit_field(this); return false });
          $('select.addfieldmenu').change(function(e){
            ref.insert_edit_field($(this).val(), $(this).attr('rel'), this);
            this.selectedIndex = 0;
          });
          $("input[type='text']").first().focus();
        }
        else if (this.gui_objects.qsearchbox) {
          this.enable_command('search', 'reset-search', 'moveto', true);
@@ -343,8 +356,15 @@
          this.enable_command('add', this.env.identities_level < 2);
          this.enable_command('save', 'delete', 'edit', 'toggle-editor', true);
        }
        else if (this.env.action=='folders')
          this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', 'enable-threading', 'disable-threading', true);
        else if (this.env.action=='folders') {
          this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', true);
        }
        else if (this.env.action == 'edit-folder' && this.gui_objects.editform) {
          this.enable_command('save', 'folder-size', true);
          parent.rcmail.env.messagecount = this.env.messagecount;
          parent.rcmail.enable_command('purge', this.env.messagecount);
          $("input[type='text']").first().select();
        }
        if (this.gui_objects.identitieslist) {
          this.identity_list = new rcube_list_widget(this.gui_objects.identitieslist, {multiselect:false, draggable:false, keyboard:false});
@@ -632,6 +652,9 @@
              input_email.focus();
              break;
            }
            // clear empty input fields
            $('input.placeholder').each(function(){ if (this.value == this._placeholder) this.value = ''; });
          }
          this.gui_objects.editform.submit();
@@ -989,25 +1012,23 @@
      case 'export':
        if (this.contact_list.rowcount > 0) {
          var add_url = (this.env.source ? '_source='+urlencode(this.env.source)+'&' : '');
          if (this.env.search_request)
            add_url += '_search='+this.env.search_request;
          this.goto_url('export', add_url);
          this.goto_url('export', { _source:this.env.source, _gid:this.env.group, _search:this.env.search_request });
        }
        break;
      case 'upload-photo':
        this.upload_contact_photo(props);
        break;
      case 'delete-photo':
        this.replace_contact_photo('-del-');
        break;
      // user settings commands
      case 'preferences':
        this.goto_url('');
        break;
      case 'identities':
        this.goto_url('settings/identities');
        break;
      case 'folders':
        this.goto_url('settings/folders');
        this.goto_url('settings/' + command);
        break;
      // unified command call (command name == function name)
@@ -1125,7 +1146,7 @@
    else if (delay)
      window.setTimeout(function(){ rcmail.reload(); }, delay);
    else if (window.location)
      location.href = this.env.comm_path;
      location.href = this.env.comm_path + (this.env.action ? '&_action='+this.env.action : '');
  };
  // Add variable to GET string, replace old value if exists
@@ -1151,7 +1172,7 @@
  this.is_framed = function()
  {
    return (this.env.framed && parent.rcmail);
    return (this.env.framed && parent.rcmail && parent.rcmail != this && parent.rcmail.command);
  };
@@ -1534,7 +1555,7 @@
    if (this.task == 'mail')
      allow = (this.env.mailboxes[id] && this.env.mailboxes[id].id != this.env.mailbox && !this.env.mailboxes[id].virtual);
    else if (this.task == 'settings')
      allow = (id != this.env.folder);
      allow = (id != this.env.mailbox);
    else if (this.task == 'addressbook') {
      if (id != this.env.source && this.env.contactfolders[id]) {
        if (this.env.contactfolders[id].type == 'group') {
@@ -1694,7 +1715,7 @@
      var action = flags.mbox == this.env.drafts_mailbox ? 'compose' : 'show';
      var uid_param = flags.mbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid';
      cols.subject = '<a href="./?_task=mail&_action='+action+'&_mbox='+urlencode(flags.mbox)+'&'+uid_param+'='+uid+'"'+
        ' onclick="return rcube_event.cancel(event)">'+cols.subject+'</a>';
        ' onclick="return rcube_event.cancel(event)" onmouseover="rcube_webmail.long_subject_title(this,'+(message.depth+1)+')">'+cols.subject+'</a>';
    }
    // add each submitted col
@@ -1910,7 +1931,7 @@
    var url = '', target = window;
    if (!mbox)
      mbox = this.env.mailbox;
      mbox = this.env.mailbox ? this.env.mailbox : 'INBOX';
    if (add_url)
      url += add_url;
@@ -2379,8 +2400,10 @@
      }
    }
    add_url += '&_uid='+this.uids_to_list(a_uids);
    // send request to server
    this.http_post('copy', '_uid='+a_uids.join(',')+'&_mbox='+urlencode(this.env.mailbox)+add_url, lock);
    this.http_post('copy', '_mbox='+urlencode(this.env.mailbox)+add_url, lock);
  };
  // move selected messages to the specified mailbox
@@ -2393,8 +2416,8 @@
    if (!mbox || mbox == this.env.mailbox || (!this.env.uid && (!this.message_list || !this.message_list.get_selection().length)))
      return;
    var lock = false;
    var add_url = '&_target_mbox='+urlencode(mbox)+'&_from='+(this.env.action ? this.env.action : '');
    var lock = false,
      add_url = '&_target_mbox='+urlencode(mbox)+'&_from='+(this.env.action ? this.env.action : '');
    // show wait message
    if (this.env.action == 'show') {
@@ -2735,24 +2758,23 @@
  this.expunge_mailbox = function(mbox)
  {
    var lock = false;
    var add_url = '';
    var lock = false,
      url = '_mbox='+urlencode(mbox);
    // lock interface if it's the active mailbox
    if (mbox == this.env.mailbox) {
       lock = this.set_busy(true, 'loading');
       add_url = '&_reload=1';
       url += '&_reload=1';
     }
    // send request to server
    var url = '_mbox='+urlencode(mbox);
    this.http_post('expunge', url+add_url, lock);
    this.http_post('expunge', url, lock);
  };
  this.purge_mailbox = function(mbox)
  {
    var lock = false;
    var add_url = '';
    var lock = false,
      url = '_mbox='+urlencode(mbox);
    if (!confirm(this.get_label('purgefolderconfirm')))
      return false;
@@ -2760,13 +2782,11 @@
    // lock interface if it's the active mailbox
    if (mbox == this.env.mailbox) {
       lock = this.set_busy(true, 'loading');
       add_url = '&_reload=1';
       url += '&_reload=1';
     }
    // send request to server
    var url = '_mbox='+urlencode(mbox);
    this.http_post('purge', url+add_url, lock);
    return true;
    this.http_post('purge', url, lock);
  };
  // test if purge command is allowed
@@ -2813,7 +2833,7 @@
      input_subject = $("input[name='_subject']"),
      input_message = $("[name='_message']").get(0),
      html_mode = $("input[name='_is_html']").val() == '1',
      ac_fields = ['cc', 'bcc', 'replyto', 'mailreplyto', 'mailfollowupto'];
      ac_fields = ['cc', 'bcc', 'replyto', 'followupto'];
    // init live search events
    this.init_address_input_events(input_to);
@@ -3171,27 +3191,7 @@
    // create hidden iframe and post upload form
    if (send) {
      var ts = new Date().getTime();
      var frame_name = 'rcmupload'+ts;
      // have to do it this way for IE
      // otherwise the form will be posted to a new window
      if (document.all) {
        var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
        document.body.insertAdjacentHTML('BeforeEnd',html);
      }
      else { // for standards-compilant browsers
        var frame = document.createElement('iframe');
        frame.name = frame_name;
        frame.style.border = 'none';
        frame.style.width = 0;
        frame.style.height = 0;
        frame.style.visibility = 'hidden';
        document.body.appendChild(frame);
      }
      // handle upload errors, parsing iframe content in onload
      $(frame_name).bind('load', {ts:ts}, function(e) {
      this.async_upload_form(form, 'upload', function(e) {
        var d, content = '';
        try {
          if (this.contentDocument) {
@@ -3211,11 +3211,6 @@
        if (bw.opera)
          rcmail.env.uploadframe = e.data.ts;
      });
      form.target = frame_name;
      form.action = this.env.comm_path+'&_action=upload&_uploadid='+ts;
      form.setAttribute('enctype', 'multipart/form-data');
      form.submit();
      // display upload indicator and cancel button
      var content = this.get_label('uploading');
@@ -3973,6 +3968,165 @@
  };
  this.init_edit_field = function(col, elem)
  {
    if (!elem)
      elem = $('.ff_' + col);
    elem.focus(function(){ ref.focus_textfield(this); })
      .blur(function(){ ref.blur_textfield(this); })
      .each(function(){ this._placeholder = ref.env.coltypes[col].label; ref.blur_textfield(this); });
  };
  this.insert_edit_field = function(col, section, menu)
  {
    // just make pre-defined input field visible
    var elem = $('#ff_'+col);
    if (elem.length) {
      elem.show().focus();
      $(menu).children('option[value="'+col+'"]').attr('disabled', true);
    }
    else {
      var lastelem = $('.ff_'+col),
        appendcontainer = $('#contactsection'+section+' .contactcontroller'+col);
      if (!appendcontainer.length)
        appendcontainer = $('<fieldset>').addClass('contactfieldgroup contactcontroller'+col).insertAfter($('#contactsection'+section+' .contactfieldgroup').last());
      if (appendcontainer.length && appendcontainer.get(0).nodeName == 'FIELDSET') {
        var input, colprop = this.env.coltypes[col],
          row = $('<div>').addClass('row'),
          cell = $('<div>').addClass('contactfieldcontent data'),
          label = $('<div>').addClass('contactfieldlabel label');
        if (colprop.subtypes_select)
          label.html(colprop.subtypes_select);
        else
          label.html(colprop.label);
        var name_suffix = colprop.limit != 1 ? '[]' : '';
        if (colprop.type == 'text' || colprop.type == 'date') {
          input = $('<input>')
            .addClass('ff_'+col)
            .attr('type', 'text')
            .attr('name', '_'+col+name_suffix)
            .attr('size', colprop.size)
            .appendTo(cell);
          this.init_edit_field(col, input);
        }
        else if (colprop.type == 'composite') {
          var childcol, cp, first;
          for (var childcol in colprop.childs) {
            cp = colprop.childs[childcol];
            input = $('<input>')
              .addClass('ff_'+childcol)
              .attr('type', 'text')
              .attr('name', '_'+childcol+name_suffix)
              .attr('size', cp.size)
              .appendTo(cell);
            cell.append(" ");
            this.init_edit_field(childcol, input);
            if (!first) first = input;
          }
          input = first;  // set focus to the first of this composite fields
        }
        else if (colprop.type == 'select') {
          input = $('<select>')
            .addClass('ff_'+col)
            .attr('name', '_'+col+name_suffix)
            .appendTo(cell);
          var options = input.attr('options');
          options[options.length] = new Option('---', '');
          if (colprop.options)
            $.each(colprop.options, function(i, val){ options[options.length] = new Option(val, i); });
        }
        if (input) {
          var delbutton = $('<a href="#del"></a>')
            .addClass('contactfieldbutton deletebutton')
            .attr('title', this.get_label('delete'))
            .attr('rel', col)
            .html(this.env.delbutton)
            .click(function(){ ref.delete_edit_field(this); return false })
            .appendTo(cell);
          row.append(label).append(cell).appendTo(appendcontainer.show());
          input.first().focus();
          // disable option if limit reached
          if (!colprop.count) colprop.count = 0;
          if (++colprop.count == colprop.limit && colprop.limit)
            $(menu).children('option[value="'+col+'"]').attr('disabled', true);
        }
      }
    }
  };
  this.delete_edit_field = function(elem)
  {
    var col = $(elem).attr('rel'),
      colprop = this.env.coltypes[col],
      fieldset = $(elem).parents('fieldset.contactfieldgroup'),
      addmenu = fieldset.parent().find('select.addfieldmenu');
    // just clear input but don't hide the last field
    if (--colprop.count <= 0 && colprop.visible)
      $(elem).parent().children('input').val('').blur();
    else {
      $(elem).parents('div.row').remove();
      // hide entire fieldset if no more rows
      if (!fieldset.children('div.row').length)
        fieldset.hide();
    }
    // enable option in add-field selector or insert it if necessary
    if (addmenu.length) {
      var option = addmenu.children('option[value="'+col+'"]');
      if (option.length)
        option.attr('disabled', false);
      else
        option = $('<option>').attr('value', col).html(colprop.label).appendTo(addmenu);
      addmenu.show();
    }
  };
  this.upload_contact_photo = function(form)
  {
    if (form && form.elements._photo.value) {
      this.async_upload_form(form, 'upload-photo', function(e) {
        rcmail.set_busy(false, null, rcmail.photo_upload_id);
      });
      // display upload indicator
      this.photo_upload_id = this.set_busy(true, 'uploading');
    }
  };
  this.replace_contact_photo = function(id)
  {
    $('#ff_photo').val(id);
    var buttons = this.buttons['upload-photo'];
    for (var n=0; n < buttons.length; n++)
      $('#'+buttons[n].id).html(this.get_label(id == '-del-' ? 'addphoto' : 'replacephoto'));
    var img_src = id == '-del-' ? this.env.photo_placeholder :
      this.env.comm_path + '&_action=photo&_source=' + this.env.source + '&_cid=' + this.env.cid + '&_photo=' + id;
    $(this.gui_objects.contactphoto).children('img').attr('src', img_src);
    this.enable_command('delete-photo', id != '-del-');
  };
  this.photo_upload_end = function()
  {
    this.set_busy(false, null, this.photo_upload_id);
    delete this.photo_upload_id;
  };
  /*********************************************************/
  /*********        user settings methods          *********/
  /*********************************************************/
@@ -3980,16 +4134,12 @@
  this.init_subscription_list = function()
  {
    var p = this;
    this.subscription_list = new rcube_list_widget(this.gui_objects.subscriptionlist, {multiselect:false, draggable:true, keyboard:false, toggleselect:true});
    this.subscription_list = new rcube_list_widget(this.gui_objects.subscriptionlist,
      {multiselect:false, draggable:true, keyboard:false, toggleselect:true});
    this.subscription_list.addEventListener('select', function(o){ p.subscription_select(o); });
    this.subscription_list.addEventListener('dragstart', function(o){ p.drag_active = true; });
    this.subscription_list.addEventListener('dragend', function(o){ p.subscription_move_folder(o); });
    this.subscription_list.row_init = function (row) {
      var anchors = row.obj.getElementsByTagName('a');
      if (anchors[0])
        anchors[0].onclick = function() { p.command('rename-folder', row.id); return false; };
      if (anchors[1])
        anchors[1].onclick = function() { p.command('delete-folder', row.id); return false; };
      row.obj.onmouseover = function() { p.focus_subscription(row.id); };
      row.obj.onmouseout = function() { p.unfocus_subscription(row.id); };
    };
@@ -4066,18 +4216,18 @@
      delim = RegExp.escape(this.env.delimiter),
      reg = RegExp('['+delim+']?[^'+delim+']+$');
    if (this.drag_active && this.env.folder && (row = document.getElementById(id)))
    if (this.drag_active && this.env.mailbox && (row = document.getElementById(id)))
      if (this.env.subscriptionrows[id] &&
          (folder = this.env.subscriptionrows[id][0])) {
        if (this.check_droptarget(folder) &&
            !this.env.subscriptionrows[this.get_folder_row_id(this.env.folder)][2] &&
            (folder != this.env.folder.replace(reg, '')) &&
            (!folder.match(new RegExp('^'+RegExp.escape(this.env.folder+this.env.delimiter))))) {
            !this.env.subscriptionrows[this.get_folder_row_id(this.env.mailbox)][2] &&
            (folder != this.env.mailbox.replace(reg, '')) &&
            (!folder.match(new RegExp('^'+RegExp.escape(this.env.mailbox+this.env.delimiter))))) {
          this.set_env('dstfolder', folder);
          $(row).addClass('droptarget');
        }
      }
      else if (this.env.folder.match(new RegExp(delim))) {
      else if (this.env.mailbox.match(new RegExp(delim))) {
        this.set_env('dstfolder', this.env.delimiter);
        $(this.subscription_list.frame).addClass('droptarget');
      }
@@ -4097,15 +4247,19 @@
  this.subscription_select = function(list)
  {
    var id, folder;
    if ((id = list.get_single_selection()) &&
        this.env.subscriptionrows['rcmrow'+id] &&
        (folder = this.env.subscriptionrows['rcmrow'+id][0]))
      this.set_env('folder', folder);
    else
      this.set_env('folder', null);
    if (this.gui_objects.createfolderhint)
      $(this.gui_objects.createfolderhint).html(this.env.folder ? this.get_label('addsubfolderhint') : '');
    if (list && (id = list.get_single_selection()) &&
        (folder = this.env.subscriptionrows['rcmrow'+id])
    ) {
      this.set_env('mailbox', folder[0]);
      this.show_folder(folder[0]);
      this.enable_command('delete-folder', !folder[2]);
    }
    else {
      this.env.mailbox = null;
      this.show_contentframe(false);
      this.enable_command('delete-folder', 'purge', false);
    }
  };
  this.subscription_move_folder = function(list)
@@ -4113,129 +4267,35 @@
    var delim = RegExp.escape(this.env.delimiter),
      reg = RegExp('['+delim+']?[^'+delim+']+$');
    if (this.env.folder && this.env.dstfolder && (this.env.dstfolder != this.env.folder) &&
        (this.env.dstfolder != this.env.folder.replace(reg, ''))) {
      var reg = new RegExp('[^'+delim+']*['+delim+']', 'g');
      var basename = this.env.folder.replace(reg, '');
      var newname = this.env.dstfolder==this.env.delimiter ? basename : this.env.dstfolder+this.env.delimiter+basename;
    if (this.env.mailbox && this.env.dstfolder && (this.env.dstfolder != this.env.mailbox) &&
        (this.env.dstfolder != this.env.mailbox.replace(reg, ''))
    ) {
      reg = new RegExp('[^'+delim+']*['+delim+']', 'g');
      var lock = this.set_busy(true, 'foldermoving'),
        basename = this.env.mailbox.replace(reg, ''),
        newname = this.env.dstfolder==this.env.delimiter ? basename : this.env.dstfolder+this.env.delimiter+basename;
      var lock = this.set_busy(true, 'foldermoving');
      this.http_post('rename-folder', '_folder_oldname='+urlencode(this.env.folder)+'&_folder_newname='+urlencode(newname), lock);
      this.http_post('rename-folder', '_folder_oldname='+urlencode(this.env.mailbox)+'&_folder_newname='+urlencode(newname), lock);
    }
    this.drag_active = false;
    this.unfocus_subscription(this.get_folder_row_id(this.env.dstfolder));
  };
  // tell server to create and subscribe a new mailbox
  this.create_folder = function(name)
  this.create_folder = function()
  {
    if (this.edit_folder)
      this.reset_folder_rename();
    var form;
    if ((form = this.gui_objects.editform) && form.elements['_folder_name']) {
      name = form.elements['_folder_name'].value;
      if (name.indexOf(this.env.delimiter)>=0) {
        alert(this.get_label('forbiddencharacter')+' ('+this.env.delimiter+')');
        return false;
      }
      if (this.env.folder && name != '')
        name = this.env.folder+this.env.delimiter+name;
      var lock = this.set_busy(true, 'foldercreating');
      this.http_post('create-folder', '_name='+urlencode(name), lock);
    }
    else if (form.elements['_folder_name'])
      form.elements['_folder_name'].focus();
  };
  // start renaming the mailbox name.
  // this will replace the name string with an input field
  this.rename_folder = function(id)
  {
    var temp, row, form;
    // reset current renaming
    if (temp = this.edit_folder) {
      this.reset_folder_rename();
      if (temp == id)
        return;
    }
    if (id && this.env.subscriptionrows[id] && (row = document.getElementById(id))) {
      var delim = RegExp.escape(this.env.delimiter),
        reg = new RegExp('.*['+delim+']');
      this.name_input = document.createElement('input');
      this.name_input.type = 'text';
      this.name_input.value = this.env.subscriptionrows[id][0].replace(reg, '');
      reg = new RegExp('['+delim+']?[^'+delim+']+$');
      this.name_input.__parent = this.env.subscriptionrows[id][0].replace(reg, '');
      this.name_input.onkeydown = function(e){ rcmail.name_input_keydown(e); };
      row.cells[0].replaceChild(this.name_input, row.cells[0].firstChild);
      this.edit_folder = id;
      this.name_input.select();
      if (form = this.gui_objects.editform)
        form.onsubmit = function(){ return false; };
    }
  };
  // remove the input field and write the current mailbox name to the table cell
  this.reset_folder_rename = function()
  {
    var cell = this.name_input ? this.name_input.parentNode : null;
    if (cell && this.edit_folder && this.env.subscriptionrows[this.edit_folder])
      $(cell).html(this.env.subscriptionrows[this.edit_folder][1]);
    this.edit_folder = null;
  };
  // handler for keyboard events on the input field
  this.name_input_keydown = function(e)
  {
    var key = rcube_event.get_keycode(e);
    // enter
    if (key==13) {
      var newname = this.name_input ? this.name_input.value : null;
      if (this.edit_folder && newname) {
        if (newname.indexOf(this.env.delimiter)>=0) {
          alert(this.get_label('forbiddencharacter')+' ('+this.env.delimiter+')');
          return false;
        }
        if (this.name_input.__parent)
          newname = this.name_input.__parent + this.env.delimiter + newname;
        var lock = this.set_busy(true, 'folderrenaming');
        this.http_post('rename-folder', '_folder_oldname='+urlencode(this.env.subscriptionrows[this.edit_folder][0])+'&_folder_newname='+urlencode(newname), lock);
      }
    }
    // escape
    else if (key==27)
      this.reset_folder_rename();
    this.show_folder('', this.env.mailbox);
  };
  // delete a specific mailbox with all its messages
  this.delete_folder = function(id)
  this.delete_folder = function(name)
  {
    var folder = this.env.subscriptionrows[id][0];
    if (this.edit_folder)
      this.reset_folder_rename();
    var id = this.get_folder_row_id(name ? name : this.env.mailbox),
      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', '_mboxes='+urlencode(folder), lock);
      this.set_env('folder', null);
      $(this.gui_objects.createfolderhint).html('');
      this.http_post('delete-folder', '_mbox='+urlencode(folder), lock);
    }
  };
@@ -4292,18 +4352,8 @@
      // set messages count to zero
      row.cells[1].innerHTML = '*';
      // update subscription/threading checkboxes
      // update subscription checkbox
      $('input[name="_subscribed[]"]', row).val(name).attr('checked', true);
      $('input[name="_threaded[]"]', row).val(name).attr('checked', false);
      var elem;
      // add new folder to rename-folder list and clear input field
      if (form = this.gui_objects.editform) {
        if (elem = form.elements['_folder_oldname'])
          elem.options[elem.options.length] = new Option(name, name);
        if (elem = form.elements['_folder_name'])
          elem.value = '';
      }
    }
    this.init_subscription_list();
@@ -4317,72 +4367,37 @@
  // replace an existing table row with a new folder line
  this.replace_folder_row = function(oldfolder, newfolder, display_name, before)
  {
    var form, elm,
      id = this.get_folder_row_id(oldfolder),
    var id = this.get_folder_row_id(oldfolder),
      row = document.getElementById(id);
    // replace an existing table row (if found)
    this.add_folder_row(newfolder, display_name, row, before);
    // rename folder in rename-folder dropdown
    if ((form = this.gui_objects.editform) && (elm = form.elements['_folder_oldname'])) {
      for (var i=0; i<elm.options.length; i++) {
        if (elm.options[i].value == oldfolder) {
          elm.options[i].text = display_name;
          elm.options[i].value = newfolder;
          break;
        }
      }
      form.elements['_folder_newname'].value = '';
    }
  };
  // remove the table row of a specific mailbox from the table
  // (the row will not be removed, just hidden)
  this.remove_folder_row = function(folder)
  {
    var form, elm, row, id = this.get_folder_row_id(folder);
    var row, id = this.get_folder_row_id(folder);
    if (id && (row = document.getElementById(id)))
      row.style.display = 'none';
    // remove folder from rename-folder list
    if ((form = this.gui_objects.editform) && (elm = form.elements['_folder_oldname'])) {
      for (var i=0; i<elm.options.length; i++) {
        if (elm.options[i].value == folder) {
          elm.options[i] = null;
          break;
        }
      }
    }
    if (form && (elm = form.elements['_folder_newname']))
      elm.value = '';
  };
  this.subscribe = function(folder)
  {
    if (folder)
      this.http_post('subscribe', '_mbox='+urlencode(folder));
    if (folder) {
      var lock = this.display_message(this.get_label('foldersubscribing'), 'loading');
      this.http_post('subscribe', '_mbox='+urlencode(folder), lock);
    }
  };
  this.unsubscribe = function(folder)
  {
    if (folder)
      this.http_post('unsubscribe', '_mbox='+urlencode(folder));
  };
  this.enable_threading = function(folder)
  {
    if (folder)
      this.http_post('enable-threading', '_mbox='+urlencode(folder));
  };
  this.disable_threading = function(folder)
  {
    if (folder)
      this.http_post('disable-threading', '_mbox='+urlencode(folder));
    if (folder) {
      var lock = this.display_message(this.get_label('folderunsubscribing'), 'loading');
      this.http_post('unsubscribe', '_mbox='+urlencode(folder), lock);
    }
  };
  // helper method to find a specific mailbox row ID
@@ -4401,7 +4416,7 @@
    var cell, td,
      new_row = document.createElement('tr');
    for(var n=0; n<row.cells.length; n++) {
    for (var n=0; n<row.cells.length; n++) {
      cell = row.cells[n];
      td = document.createElement('td');
@@ -4415,6 +4430,50 @@
    }
    return new_row;
  };
  // when user select a folder in manager
  this.show_folder = function(folder, path, force)
  {
    var target = window,
      url = '&_action=edit-folder&_mbox='+urlencode(folder);
    if (path)
      url += '&_path='+urlencode(path);
    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
      target = window.frames[this.env.contentframe];
      url += '&_framed=1';
    }
    if (String(target.location.href).indexOf(url) >= 0 && !force) {
      this.show_contentframe(true);
    }
    else {
      if (!this.env.frame_lock) {
        (parent.rcmail ? parent.rcmail : this).env.frame_lock = this.set_busy(true, 'loading');
      }
      target.location.href = this.env.comm_path+url;
    }
  };
  // disables subscription checkbox (for protected folder)
  this.disable_subscription = function(folder)
  {
    var id = this.get_folder_row_id(folder);
    if (id)
      $('input[name="_subscribed[]"]', $('#'+id)).attr('disabled', true);
  };
  this.folder_size = function(folder)
  {
    var lock = this.set_busy(true, 'loading');
    this.http_post('folder-size', '_mbox='+urlencode(folder), lock);
  };
  this.folder_size_update = function(size)
  {
    $('#folder-size').replaceWith(size);
  };
@@ -4594,6 +4653,23 @@
    }
  };
  this.focus_textfield = function(elem)
  {
    elem._hasfocus = true;
    var $elem = $(elem);
    if ($elem.hasClass('placeholder') || $elem.val() == elem._placeholder)
      $elem.val('').removeClass('placeholder').attr('spellcheck', true);
  };
  this.blur_textfield = function(elem)
  {
    elem._hasfocus = false;
    var $elem = $(elem);
    if (elem._placeholder && (!$elem.val() || $elem.val() == elem._placeholder))
      $elem.addClass('placeholder').attr('spellcheck', false).val(elem._placeholder);
  };
  // write to the document/window title
  this.set_pagetitle = function(title)
  {
@@ -4617,36 +4693,49 @@
    type = type ? type : 'notice';
    var date = new Date(),
      id = type + date.getTime();
    var ref = this,
      key = msg,
      date = new Date(),
      id = type + date.getTime(),
      timeout = this.message_time * (type == 'error' || type == 'warning' ? 2 : 1);
    if (type == 'loading') {
      key = 'loading';
      timeout = this.env.request_timeout * 1000;
      if (!msg)
        msg = this.get_label('loading');
      // The same message of type 'loading' is already displayed
      if (this.messages[msg]) {
        this.messages[msg].elements.push(id);
        return id;
      }
    }
    var ref = this,
      obj = $('<div>').addClass(type).html(msg),
      cont = $(this.gui_objects.message).show();
    if (type == 'loading') {
      obj.appendTo(cont);
      this.messages[msg] = {'obj': obj, 'elements': [id]};
      window.setTimeout(function() { rcmail.hide_message(id); }, this.env.request_timeout * 1000);
    // The same message is already displayed
    if (this.messages[key]) {
      // replace label
      if (this.messages[key].obj)
        this.messages[key].obj.html(msg);
      // store label in stack
      if (type == 'loading') {
        this.messages[key].labels.push({'id': id, 'msg': msg});
      }
      // add element and set timeout
      this.messages[key].elements.push(id);
      window.setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout);
      return id;
    }
    else {
      obj.appendTo(cont).bind('mousedown', function() { return ref.hide_message(obj); });
      window.setTimeout(function() { ref.hide_message(obj, true); },
        this.message_time * (type == 'error' ? 2 : 1));
      return obj;
    // create DOM object and display it
    var obj = $('<div>').addClass(type).html(msg).data('key', key),
      cont = $(this.gui_objects.message).append(obj).show();
    this.messages[key] = {'obj': obj, 'elements': [id]};
    if (type == 'loading') {
      this.messages[key].labels = [{'id': id, 'msg': msg}];
    }
    else {
      obj.click(function() { return ref.hide_message(obj); });
    }
    window.setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout);
    return id;
  };
  // make a message to disapear
@@ -4656,20 +4745,37 @@
    if (this.is_framed())
      return parent.rcmail.hide_message(obj, fade);
    var k, n, i, msg, m = this.messages;
    // Hide message by object, don't use for 'loading'!
    if (typeof(obj) == 'object') {
      // custom message
      $(obj)[fade?'fadeOut':'hide']();
      msg = $(obj).data('key');
      if (this.messages[msg])
        delete this.messages[msg];
    }
    // Hide message by id
    else {
      // 'loading' message
      var k, n, m = this.messages;
      for (k in m) {
        for (n in m[k].elements) {
          if (m[k] && m[k].elements[n] == obj) {
            m[k].elements.splice(n, 1);
            // hide DOM element if last instance is removed
            if (!m[k].elements.length) {
              m[k].obj[fade?'fadeOut':'hide']();
              delete m[k];
            }
            // set pending action label for 'loading' message
            else if (k == 'loading') {
              for (i in m[k].labels) {
                if (m[k].labels[i].id == obj) {
                  delete m[k].labels[i];
                }
                else {
                  msg = m[k].labels[i].msg;
                }
                m[k].obj.html(msg);
              }
            }
          }
        }
@@ -4733,6 +4839,7 @@
        }
        th.appendChild(tr);
        thead.parentNode.replaceChild(th, thead);
        thead = th;
      }
      for (n=0, len=this.env.coltypes.length; n<len; n++) {
@@ -4807,11 +4914,14 @@
  // update the mailbox count display
  this.set_unread_count_display = function(mbox, set_title)
  {
    var reg, text_obj, item, mycount, childcount, div;
    var reg, link, text_obj, item, mycount, childcount, div;
    if (item = this.get_folder_li(mbox)) {
      mycount = this.env.unread_counts[mbox] ? this.env.unread_counts[mbox] : 0;
      text_obj = item.getElementsByTagName('a')[0];
      link = $(item).children('a').eq(0);
      text_obj = link.children('span.unreadcount');
      if (!text_obj.length && mycount)
        text_obj = $('<span>').addClass('unreadcount').appendTo(link);
      reg = /\s+\([0-9]+\)$/i;
      childcount = 0;
@@ -4823,12 +4933,10 @@
            childcount += this.env.unread_counts[k];
      }
      if (mycount && text_obj.innerHTML.match(reg))
        text_obj.innerHTML = text_obj.innerHTML.replace(reg, ' ('+mycount+')');
      else if (mycount)
        text_obj.innerHTML += ' ('+mycount+')';
      else
        text_obj.innerHTML = text_obj.innerHTML.replace(reg, '');
      if (mycount && text_obj.length)
        text_obj.html(' ('+mycount+')');
      else if (text_obj.length)
        text_obj.remove();
      // set parent's display
      reg = new RegExp(RegExp.escape(this.env.delimiter) + '[^' + RegExp.escape(this.env.delimiter) + ']+$');
@@ -5010,6 +5118,39 @@
  /********************************************************/
  /*********        remote request methods        *********/
  /********************************************************/
  // compose a valid url with the given parameters
  this.url = function(action, query)
  {
    var querystring = typeof(query) == 'string' ? '&' + query : '';
    if (typeof action != 'string')
      query = action;
    else if (!query || typeof(query) != 'object')
      query = {};
    if (action)
      query._action = action;
    else
      query._action = this.env.action;
    var base = this.env.comm_path;
    // overwrite task name
    if (query._action.match(/([a-z]+)\/([a-z-_]+)/)) {
      query._action = RegExp.$2;
      base = base.replace(/\_task=[a-z]+/, '_task='+RegExp.$1);
    }
    // remove undefined values
    var param = {};
    for (var k in query) {
      if (typeof(query[k]) != 'undefined' && query[k] !== null)
        param[k] = query[k];
    }
    return base + '&' + $.param(param) + querystring;
  };
  this.redirect = function(url, lock)
  {
@@ -5024,28 +5165,13 @@
  this.goto_url = function(action, query, lock)
  {
    var url = this.env.comm_path,
     querystring = query ? '&'+query : '';
    // overwrite task name
    if (action.match(/([a-z]+)\/([a-z-_]+)/)) {
      action = RegExp.$2;
      url = url.replace(/\_task=[a-z]+/, '_task='+RegExp.$1);
    }
    this.redirect(url+'&_action='+action+querystring, lock);
    this.redirect(this.url(action, query));
  };
  // send a http request to the server
  this.http_request = function(action, query, lock)
  {
    var url = this.env.comm_path;
    // overwrite task name
    if (action.match(/([a-z]+)\/([a-z-_]+)/)) {
      action = RegExp.$2;
      url = url.replace(/\_task=[a-z]+/, '_task='+RegExp.$1);
    }
    var url = this.url(action, query);
    // trigger plugin hook
    var result = this.triggerEvent('request'+action, query);
@@ -5058,7 +5184,7 @@
        query = result;
    }
    url += '&_remote=1&_action=' + action + (query ? '&' : '') + query;
    url += '&_remote=1';
    // send request
    console.log('HTTP GET: ' + url);
@@ -5072,15 +5198,7 @@
  // send a http POST request to the server
  this.http_post = function(action, postdata, lock)
  {
    var url = this.env.comm_path;
    // overwrite task name
    if (action.match(/([a-z]+)\/([a-z-_]+)/)) {
      action = RegExp.$2;
      url = url.replace(/\_task=[a-z]+/, '_task='+RegExp.$1);
    }
    url += '&_action=' + action;
    var url = this.url(action);
    if (postdata && typeof(postdata) == 'object') {
      postdata._remote = 1;
@@ -5227,6 +5345,37 @@
      this.display_message(this.get_label('servererror') + ' (' + errmsg + ')', 'error');
  };
  // post the given form to a hidden iframe
  this.async_upload_form = function(form, action, onload)
  {
    var ts = new Date().getTime();
    var frame_name = 'rcmupload'+ts;
    // have to do it this way for IE
    // otherwise the form will be posted to a new window
    if (document.all) {
      var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
      document.body.insertAdjacentHTML('BeforeEnd', html);
    }
    else { // for standards-compilant browsers
      var frame = document.createElement('iframe');
      frame.name = frame_name;
      frame.style.border = 'none';
      frame.style.width = 0;
      frame.style.height = 0;
      frame.style.visibility = 'hidden';
      document.body.appendChild(frame);
    }
    // handle upload errors, parsing iframe content in onload
    $(frame_name).bind('load', {ts:ts}, onload);
    form.target = frame_name;
    form.action = this.url(action, { _uploadid:ts });
    form.setAttribute('enctype', 'multipart/form-data');
    form.submit();
  };
  // starts interval for keep-alive/check-recent signal
  this.start_keepalive = function()
  {
@@ -5355,6 +5504,17 @@
}  // end object rcube_webmail
// some static methods
rcube_webmail.long_subject_title = function(elem, indent)
{
  if (!elem.title) {
    var $elem = $(elem);
    if ($elem.width() + indent * 15 > $elem.parent().width())
      elem.title = $elem.html();
  }
};
// copy event engine prototype
rcube_webmail.prototype.addEventListener = rcube_event_engine.prototype.addEventListener;
rcube_webmail.prototype.removeEventListener = rcube_event_engine.prototype.removeEventListener;