Aleksander Machniak
2014-10-30 a075df6f086656f9a01f0023111be6eb83c9d0db
program/js/app.js
@@ -236,8 +236,6 @@
            return ref.command('sort', $(this).attr('rel'), this);
          });
          this.gui_objects.messagelist.parentNode.onclick = function(e){ return ref.click_on_list(e || window.event); };
          this.enable_command('toggle_status', 'toggle_flag', 'sort', true);
          this.enable_command('set-listmode', this.env.threads && !this.is_multifolder_listing());
@@ -406,8 +404,6 @@
            .addEventListener('dragend', function(e) { ref.drag_end(e); })
            .init();
          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();
@@ -567,6 +563,7 @@
      this.treelist = new rcube_treelist_widget(this.gui_objects.folderlist, {
          selectable: true,
          id_prefix: 'rcmli',
          parent_focus: true,
          id_encode: this.html_identifier_encode,
          id_decode: this.html_identifier_decode,
          check_droptarget: function(node) { return !node.virtual && ref.check_droptarget(node.id) }
@@ -1056,7 +1053,7 @@
        if (this.task == 'mail') {
          url._mbox = this.env.mailbox;
          if (props)
             url._to = props;
            url._to = props;
          // also send search request so we can go back to search result after message is sent
          if (this.env.search_request)
            url._search = this.env.search_request;
@@ -1084,8 +1081,12 @@
            break;
          }
        }
        else if (props)
        else if (props && typeof props == 'string') {
          url._to = props;
        }
        else if (props && typeof props == 'object') {
          $.extend(url, props);
        }
        this.open_compose_step(url);
        break;
@@ -1451,7 +1452,7 @@
  this.is_framed = function()
  {
    return (this.env.framed && parent.rcmail && parent.rcmail != this && parent.rcmail.command);
    return this.env.framed && parent.rcmail && parent.rcmail != this && typeof parent.rcmail.command == 'function';
  };
  this.save_pref = function(prop)
@@ -1659,7 +1660,7 @@
        }
        skip = obj.data('parent');
      }
    }, 10);
    }, 10, e);
  };
  // global keypress event handler
@@ -1713,19 +1714,6 @@
    return true;
  }
  this.click_on_list = function(e)
  {
    if (this.gui_objects.qsearchbox)
      this.gui_objects.qsearchbox.blur();
    if (this.message_list)
      this.message_list.focus(e);
    else if (this.contact_list)
      this.contact_list.focus(e);
    return true;
  };
  this.msglist_select = function(list)
  {
@@ -2494,7 +2482,7 @@
  // expand all threads with unread children
  this.expand_unread = function()
  {
    var r, tbody = this.gui_objects.messagelist.tBodies[0],
    var r, tbody = this.message_list.tbody,
      new_row = tbody.firstChild;
    while (new_row) {
@@ -3331,7 +3319,7 @@
    if (!this.gui_objects.messageform)
      return false;
    var i, input_from = $("[name='_from']"),
    var i, pos, input_from = $("[name='_from']"),
      input_to = $("[name='_to']"),
      input_subject = $("input[name='_subject']"),
      input_message = $("[name='_message']").get(0),
@@ -3365,18 +3353,24 @@
    }
    if (!html_mode) {
      this.set_caret_pos(input_message, this.env.top_posting ? 0 : $(input_message).val().length);
      pos = this.env.top_posting ? 0 : input_message.value.length;
      this.set_caret_pos(input_message, pos);
      // add signature according to selected identity
      // if we have HTML editor, signature is added in callback
      if (input_from.prop('type') == 'select-one') {
        this.change_identity(input_from[0]);
      }
      // scroll to the bottom of the textarea (#1490114)
      if (pos) {
        $(input_message).scrollTop(input_message.scrollHeight);
      }
    }
    // check for locally stored compose data
    if (window.localStorage) {
    if (this.env.save_localstorage)
      this.compose_restore_dialog(0, html_mode)
    }
    if (input_to.val() == '')
      input_to.focus();
@@ -3630,11 +3624,15 @@
  this.toggle_editor = function(props, obj, e)
  {
    // @todo: this should work also with many editors on page
    var result = this.editor.toggle(props.html);
    var result = this.editor.toggle(props.html, props.noconvert || false);
    // satisfy the expectations of aftertoggle-editor event subscribers
    props.mode = props.html ? 'html' : 'plain';
    if (!result && e) {
      // fix selector value if operation failed
      $(e.target).filter('select').val(props.html ? 'plain' : 'html');
      props.mode = props.html ? 'plain' : 'html';
      $(e.target).filter('select').val(props.mode);
    }
    if (result) {
@@ -3661,7 +3659,7 @@
  this.save_response = function()
  {
    // show dialog to enter a name and to modify the text to be saved
    var buttons = {}, text = this.editor.get_content(true, true),
    var buttons = {}, text = this.editor.get_content({selection: true, format: 'text', nosig: true}),
      html = '<form class="propform">' +
      '<div class="prop block"><label>' + this.get_label('responsename') + '</label>' +
      '<input type="text" name="name" id="ffresponsename" size="40" /></div>' +
@@ -3808,7 +3806,7 @@
    }
    // save compose form content to local storage every 5 seconds
    if (!this.local_save_timer && window.localStorage) {
    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++; });
@@ -3849,7 +3847,7 @@
      if (val = $('[name="_' + hash_fields[i] + '"]').val())
        str += val + ':';
    str += this.editor.get_content();
    str += this.editor.get_content({refresh: false});
    if (this.env.attachments)
      for (id in this.env.attachments)
@@ -3864,6 +3862,10 @@
  // store the contents of the compose form to localstorage
  this.save_compose_form_local = function()
  {
    // feature is disabled
    if (!this.env.save_localstorage)
      return;
    var formdata = { session:this.env.session_id, changed:new Date().getTime() },
      ed, empty = true;
@@ -3900,15 +3902,16 @@
      }
    });
    if (window.localStorage && !empty) {
    if (!empty) {
      var index = this.local_storage_get_item('compose.index', []),
        key = this.env.compose_id;
        if ($.inArray(key, index) < 0) {
          index.push(key);
        }
        this.local_storage_set_item('compose.' + key, formdata, true);
        this.local_storage_set_item('compose.index', index);
      if ($.inArray(key, index) < 0) {
        index.push(key);
      }
      this.local_storage_set_item('compose.' + key, formdata, true);
      this.local_storage_set_item('compose.index', index);
    }
  };
@@ -3932,7 +3935,7 @@
      // initialize HTML editor
      if ((formdata._is_html == '1' && !html_mode) || (formdata._is_html != '1' && html_mode)) {
        this.command('toggle-editor', {id: this.env.composebody, html: !html_mode});
        this.command('toggle-editor', {id: this.env.composebody, html: !html_mode, noconvert: true});
      }
    }
  };
@@ -3940,27 +3943,24 @@
  // remove stored compose data from localStorage
  this.remove_compose_data = function(key)
  {
    if (window.localStorage) {
      var index = this.local_storage_get_item('compose.index', []);
    var index = this.local_storage_get_item('compose.index', []);
      if ($.inArray(key, index) >= 0) {
        this.local_storage_remove_item('compose.' + key);
        this.local_storage_set_item('compose.index', $.grep(index, function(val,i) { return val != key; }));
      }
    if ($.inArray(key, index) >= 0) {
      this.local_storage_remove_item('compose.' + key);
      this.local_storage_set_item('compose.index', $.grep(index, function(val,i) { return val != key; }));
    }
  };
  // clear all stored compose data of this user
  this.clear_compose_data = function()
  {
    if (window.localStorage) {
      var i, index = this.local_storage_get_item('compose.index', []);
    var i, index = this.local_storage_get_item('compose.index', []);
      for (i=0; i < index.length; i++) {
        this.local_storage_remove_item('compose.' + index[i]);
      }
      this.local_storage_remove_item('compose.index');
    for (i=0; i < index.length; i++) {
      this.local_storage_remove_item('compose.' + index[i]);
    }
    this.local_storage_remove_item('compose.index');
  };
@@ -3972,6 +3972,19 @@
    if (!show_sig)
      show_sig = this.env.show_sig;
    var id = obj.options[obj.selectedIndex].value,
      sig = this.env.identity,
      delim = this.env.recipients_separator,
      rx_delim = RegExp.escape(delim);
    // enable manual signature insert
    if (this.env.signatures && this.env.signatures[id]) {
      this.enable_command('insert-sig', true);
      this.env.compose_commands.push('insert-sig');
    }
    else
      this.enable_command('insert-sig', false);
    // first function execution
    if (!this.env.identities_initialized) {
      this.env.identities_initialized = true;
@@ -3980,11 +3993,6 @@
      if (this.env.opened_extwin)
        return;
    }
    var id = obj.options[obj.selectedIndex].value,
      sig = this.env.identity,
      delim = this.env.recipients_separator,
      rx_delim = RegExp.escape(delim);
    // update reply-to/bcc fields with addresses defined in identities
    $.each(['replyto', 'bcc'], function() {
@@ -4018,14 +4026,6 @@
      if (old_val || new_val)
        input.val(input_val).change();
    });
    // enable manual signature insert
    if (this.env.signatures && this.env.signatures[id]) {
      this.enable_command('insert-sig', true);
      this.env.compose_commands.push('insert-sig');
    }
    else
      this.enable_command('insert-sig', false);
    this.editor.change_signature(id, show_sig);
    this.env.identity = id;
@@ -4456,7 +4456,7 @@
    this.ksearch_destroy();
    // insert all members of a group
    if (typeof this.env.contacts[id] === 'object' && this.env.contacts[id].type == 'group') {
    if (typeof this.env.contacts[id] === 'object' && this.env.contacts[id].type == 'group' && !this.env.contacts[id].email) {
      insert += this.env.contacts[id].name + this.env.recipients_delimiter;
      this.group2expand[this.env.contacts[id].id] = $.extend({ input: this.ksearch_input }, this.env.contacts[id]);
      this.http_request('mail/group-expand', {_source: this.env.contacts[id].source, _gid: this.env.contacts[id].id}, false);
@@ -4600,7 +4600,7 @@
        id = i + this.env.contacts.length;
        $('<li>').attr('id', 'rcmkSearchItem' + id)
          .attr('role', 'option')
          .html(this.quote_html(text.replace(new RegExp('('+RegExp.escape(value)+')', 'ig'), '##$1%%')).replace(/##([^%]+)%%/g, '<b>$1</b>'))
          .html('<i class="icon"></i>' + 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); })
@@ -4756,6 +4756,7 @@
  this.list_contacts = function(src, group, page)
  {
    var win, folder, url = {},
      refresh = src === undefined && group === undefined && page === undefined,
      target = window;
    if (!src)
@@ -4768,7 +4769,7 @@
      page = this.env.current_page = 1;
      this.reset_qsearch();
    }
    else if (group != this.env.group)
    else if (!refresh && group != this.env.group)
      page = this.env.current_page = 1;
    if (this.env.search_id)
@@ -4906,6 +4907,9 @@
    if (action && (cid || action == 'add') && !this.drag_active) {
      if (this.env.group)
        url._gid = this.env.group;
      if (this.env.search_request)
        url._search = this.env.search_request;
      url._action = action;
      url._source = this.env.source;
@@ -5160,29 +5164,55 @@
        .submit(function() { $('input.mainaction').click(); return false; });
  };
  // group creation dialog
  this.group_create = function()
  {
    this.add_input_row('contactgroup');
    var input = $('<input>').attr('type', 'text'),
      content = $('<label>').text(this.get_label('namex')).append(input);
    this.show_popup_dialog(content, this.get_label('newgroup'),
      [{
        text: this.get_label('save'),
        click: function() {
          var name;
          if (name = input.val()) {
            ref.http_post('group-create', {_source: ref.env.source, _name: name},
              ref.set_busy(true, 'loading'));
          }
          $(this).dialog('close');
        }
      }]
    );
  };
  // group rename dialog
  this.group_rename = function()
  {
    if (!this.env.group || !this.gui_objects.folderlist)
    if (!this.env.group)
      return;
    if (!this.name_input) {
      this.enable_command('list', 'listgroup', false);
      this.name_input = $('<input>').attr('type', 'text').val(this.env.contactgroups['G'+this.env.source+this.env.group].name);
      this.name_input.bind('keydown', function(e) { return ref.add_input_keydown(e); });
      this.env.group_renaming = true;
    var group_name = this.env.contactgroups['G' + this.env.source + this.env.group].name,
      input = $('<input>').attr('type', 'text').val(group_name),
      content = $('<label>').text(this.get_label('namex')).append(input);
      var link, li = this.get_folder_li('G'+this.env.source+this.env.group,'',true);
      if (li && (link = li.firstChild)) {
        $(link).hide().before(this.name_input);
      }
    }
    this.show_popup_dialog(content, this.get_label('grouprename'),
      [{
        text: this.get_label('save'),
        click: function() {
          var name;
    this.name_input.select().focus();
          if ((name = input.val()) && name != group_name) {
            ref.http_post('group-rename', {_source: ref.env.source, _gid: ref.env.group, _name: name},
              ref.set_busy(true, 'loading'));
          }
          $(this).dialog('close');
        }
      }],
      {open: function() { input.select(); }}
    );
  };
  this.group_delete = function()
@@ -5207,38 +5237,6 @@
    this.list_contacts(prop.source, 0);
  };
  // @TODO: maybe it would be better to use popup instead of inserting input to the list?
  this.add_input_row = function(type)
  {
    if (!this.gui_objects.folderlist)
      return;
    if (!this.name_input) {
      this.name_input = $('<input>').attr('type', 'text').data('tt', type);
      this.name_input.bind('keydown', function(e) { return ref.add_input_keydown(e); });
      this.name_input_li = $('<li>').addClass(type).append(this.name_input);
      var ul, li;
      // find list (UL) element
      if (type == 'contactsearch')
        ul = this.gui_objects.savedsearchlist;
      else
        ul = $('ul.groups', this.get_folder_li(this.env.source,'',true));
      // append to the list
      li = $('li:last', ul);
      if (li.length)
        this.name_input_li.insertAfter(li);
      else {
        this.name_input_li.appendTo(ul);
        ul.show(); // make sure the list is visible
      }
    }
    this.name_input.select().focus();
  };
  //remove selected contacts from current active group
  this.group_remove_selected = function()
  {
@@ -5258,62 +5256,9 @@
    }
  };
  // handler for keyboard events on the input field
  this.add_input_keydown = function(e)
  {
    var key = rcube_event.get_keycode(e),
      input = $(e.target), itype = input.data('tt');
    // enter
    if (key == 13) {
      var newname = input.val();
      if (newname) {
        var lock = this.set_busy(true, 'loading');
        if (itype == 'contactsearch')
          this.http_post('search-create', {_search: this.env.search_request, _name: newname}, lock);
        else if (this.env.group_renaming)
          this.http_post('group-rename', {_source: this.env.source, _gid: this.env.group, _name: newname}, lock);
        else
          this.http_post('group-create', {_source: this.env.source, _name: newname}, lock);
      }
      return false;
    }
    // escape
    else if (key == 27)
      this.reset_add_input();
    return true;
  };
  this.reset_add_input = function()
  {
    if (this.name_input) {
      var li = this.name_input.parent();
      if (this.env.group_renaming) {
        li.children().last().show();
        this.env.group_renaming = false;
      }
      else if ($('li', li.parent()).length == 1)
        li.parent().hide();
      this.name_input.remove();
      if (this.name_input_li)
        this.name_input_li.remove();
      this.name_input = this.name_input_li = null;
    }
    this.enable_command('list', 'listgroup', true);
  };
  // callback for creating a new contact group
  this.insert_contact_group = function(prop)
  {
    this.reset_add_input();
    prop.type = 'group';
    var key = 'G'+prop.source+prop.id,
@@ -5331,8 +5276,6 @@
  // callback for renaming a contact group
  this.update_contact_group = function(prop)
  {
    this.reset_add_input();
    var key = 'G'+prop.source+prop.id,
      newnode = {};
@@ -5596,8 +5539,6 @@
  // callback for creating a new saved search record
  this.insert_saved_search = function(name, id)
  {
    this.reset_add_input();
    var key = 'S'+id,
      link = $('<a>').attr('href', '#')
        .attr('rel', id)
@@ -5613,10 +5554,27 @@
    this.triggerEvent('abook_search_insert', prop);
  };
  // creates an input for saved search name
  // creates a dialog for saved search
  this.search_create = function()
  {
    this.add_input_row('contactsearch');
    var input = $('<input>').attr('type', 'text'),
      content = $('<label>').text(this.get_label('namex')).append(input);
    this.show_popup_dialog(content, this.get_label('searchsave'),
      [{
        text: this.get_label('save'),
        click: function() {
          var name;
          if (name = input.val()) {
            ref.http_post('search-create', {_search: ref.env.search_request, _name: name},
              ref.set_busy(true, 'loading'));
          }
          $(this).dialog('close');
        }
      }]
    );
  };
  this.search_delete = function()
@@ -5804,15 +5762,19 @@
    this.subscription_list = new rcube_treelist_widget(this.gui_objects.subscriptionlist, {
        selectable: true,
        tabexit: false,
        parent_focus: true,
        id_prefix: 'rcmli',
        id_encode: this.html_identifier_encode,
        id_decode: this.html_identifier_decode
        id_decode: this.html_identifier_decode,
        searchbox: '#foldersearch'
    });
    this.subscription_list
      .addEventListener('select', function(node) { ref.subscription_select(node.id); })
      .addEventListener('collapse', function(node) { ref.folder_collapsed(node) })
      .addEventListener('expand', function(node) { ref.folder_collapsed(node) })
      .addEventListener('search', function(p) { if (p.query) ref.subscription_select(); })
      .draggable({cancel: 'li.mailbox.root'})
      .droppable({
        // @todo: find better way, accept callback is executed for every folder
@@ -5895,6 +5857,15 @@
    if (!this.gui_objects.subscriptionlist)
      return false;
    // reset searching
    if (this.subscription_list.is_search()) {
      this.subscription_select();
      this.subscription_list.reset_search();
    }
    // disable drag-n-drop temporarily
    this.subscription_list.draggable('destroy').droppable('destroy');
    var row, n, tmp, tmp_name, rowid, collator, pos, p, parent = '',
      folders = [], list = [], slist = [],
      list_element = $(this.gui_objects.subscriptionlist);
@@ -5910,8 +5881,9 @@
    row.attr({id: 'rcmli' + this.html_identifier_encode(id), 'class': class_name});
    if (!refrow || !refrow.length) {
      // remove old subfolders and toggle
      // remove old data, subfolders and toggle
      $('ul,div.treetoggle', row).remove();
      row.removeData('filtered');
    }
    // set folder name
@@ -6038,7 +6010,7 @@
      this.subscription_list.expand(this.folder_id2name(parent.id));
    }
    row = row.get(0);
    row = row.show().get(0);
    if (row.scrollIntoView)
      row.scrollIntoView();
@@ -6049,10 +6021,18 @@
  this.replace_folder_row = function(oldid, id, name, display_name, is_protected, class_name)
  {
    if (!this.gui_objects.subscriptionlist) {
      if (this.is_framed)
        return parent.rcmail.replace_folder_row(oldid, id, name, display_name, is_protected, class_name);
      if (this.is_framed()) {
        // @FIXME: for some reason this 'parent' variable need to be prefixed with 'window.'
        return window.parent.rcmail.replace_folder_row(oldid, id, name, display_name, is_protected, class_name);
      }
      return false;
    }
    // reset searching
    if (this.subscription_list.is_search()) {
      this.subscription_select();
      this.subscription_list.reset_search();
    }
    var subfolders = {},
@@ -6100,6 +6080,12 @@
  // remove the table row of a specific mailbox from the table
  this.remove_folder_row = function(folder)
  {
    // reset searching
    if (this.subscription_list.is_search()) {
      this.subscription_select();
      this.subscription_list.reset_search();
    }
    var list = [], row = this.subscription_list.get_item(folder, true);
    // get subfolders if any
@@ -6168,6 +6154,37 @@
    $('#folder-size').replaceWith(size);
  };
  // filter folders by namespace
  this.folder_filter = function(prefix)
  {
    this.subscription_list.reset_search();
    this.subscription_list.container.children('li').each(function() {
      var i, folder = ref.folder_id2name(this.id);
      // show all folders
      if (prefix == '---') {
      }
      // got namespace prefix
      else if (prefix) {
        if (folder !== prefix) {
          $(this).data('filtered', true).hide();
          return
        }
      }
      // no namespace prefix, filter out all other namespaces
      else {
        // first get all namespace roots
        for (i in ref.env.ns_roots) {
          if (folder === ref.env.ns_roots[i]) {
            $(this).data('filtered', true).hide();
            return;
          }
        }
      }
      $(this).removeData('filtered').show();
    });
  };
  /*********************************************************/
  /*********           GUI functionality           *********/
@@ -6471,22 +6488,27 @@
  };
  // open a jquery UI dialog with the given content
  this.show_popup_dialog = function(html, title, buttons, options)
  this.show_popup_dialog = function(content, title, buttons, options)
  {
    // forward call to parent window
    if (this.is_framed()) {
      return parent.rcmail.show_popup_dialog(html, title, buttons, options);
      return parent.rcmail.show_popup_dialog(content, title, buttons, options);
    }
    var popup = $('<div class="popup">')
      .html(html)
      .dialog($.extend({
    var popup = $('<div class="popup">');
    if (typeof content == 'object')
      popup.append(content);
    else
      popup.html(content);
    popup.dialog($.extend({
        title: title,
        buttons: buttons,
        modal: true,
        resizable: true,
        width: 500,
        close: function(event, ui) { $(this).remove() }
        close: function(event, ui) { $(this).remove(); }
      }, options || {}));
    // resize and center popup
@@ -6891,7 +6913,7 @@
      // truncate stack down to the one containing the ref link
      for (var i = this.menu_stack.length - 1; stack && i >= 0; i--) {
        if (!$(ref).parents('#'+this.menu_stack[i]).length)
          this.hide_menu(this.menu_stack[i]);
          this.hide_menu(this.menu_stack[i], event);
      }
      if (stack && this.menu_stack.length) {
        obj.data('parent', $.last(this.menu_stack));
@@ -7815,13 +7837,17 @@
  // and return the message uid
  this.get_single_uid = function()
  {
    return this.env.uid ? this.env.uid : (this.message_list ? this.message_list.get_single_selection() : null);
    var uid = this.env.uid || (this.message_list ? this.message_list.get_single_selection() : null);
    var result = ref.triggerEvent('get_single_uid', { uid: uid });
    return result || uid;
  };
  // same as above but for contacts
  this.get_single_cid = function()
  {
    return this.env.cid ? this.env.cid : (this.contact_list ? this.contact_list.get_single_selection() : null);
    var cid = this.env.cid || (this.contact_list ? this.contact_list.get_single_selection() : null);
    var result = ref.triggerEvent('get_single_cid', { cid: cid });
    return result || cid;
  };
  // get the IMP mailbox of the message with the given UID
@@ -8030,22 +8056,43 @@
  // wrapper for localStorage.getItem(key)
  this.local_storage_get_item = function(key, deflt, encrypted)
  {
    var item;
    // TODO: add encryption
    var item = localStorage.getItem(this.get_local_storage_prefix() + key);
    try {
      item = localStorage.getItem(this.get_local_storage_prefix() + key);
    }
    catch (e) { }
    return item !== null ? JSON.parse(item) : (deflt || null);
  };
  // wrapper for localStorage.setItem(key, data)
  this.local_storage_set_item = function(key, data, encrypted)
  {
    // TODO: add encryption
    return localStorage.setItem(this.get_local_storage_prefix() + key, JSON.stringify(data));
    // try/catch to handle no localStorage support, but also error
    // in Safari-in-private-browsing-mode where localStorage exists
    // but can't be used (#1489996)
    try {
      // TODO: add encryption
      localStorage.setItem(this.get_local_storage_prefix() + key, JSON.stringify(data));
      return true;
    }
    catch (e) {
      return false;
    }
  };
  // wrapper for localStorage.removeItem(key)
  this.local_storage_remove_item = function(key)
  {
    return localStorage.removeItem(this.get_local_storage_prefix() + key);
    try {
      localStorage.removeItem(this.get_local_storage_prefix() + key);
      return true;
    }
    catch (e) {
      return false;
    }
  };
}  // end object rcube_webmail