diff options
Diffstat (limited to 'program/js/app.js')
-rw-r--r-- | program/js/app.js | 413 |
1 files changed, 306 insertions, 107 deletions
diff --git a/program/js/app.js b/program/js/app.js index 1f75e219c..636db82d4 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -34,7 +34,7 @@ function rcube_webmail() // webmail client settings this.dblclick_time = 500; - this.message_time = 4000; + this.message_time = 5000; this.identifier_expr = new RegExp('[^0-9a-z\-_]', 'gi'); // environment defaults @@ -187,6 +187,8 @@ function rcube_webmail() if (this.env.permaurl) this.enable_command('permaurl', 'extwin', true); + this.local_storage_prefix = 'roundcube.' + (this.env.user_id || 'anonymous') + '.'; + switch (this.task) { case 'mail': @@ -218,12 +220,8 @@ function rcube_webmail() // load messages this.command('list'); - } - if (this.gui_objects.qsearchbox) { - if (this.env.search_text != null) - this.gui_objects.qsearchbox.value = this.env.search_text; - $(this.gui_objects.qsearchbox).focusin(function() { rcmail.message_list && rcmail.message_list.blur(); }); + $(this.gui_objects.qsearchbox).val(this.env.search_text).focusin(function() { rcmail.message_list.blur(); }); } this.set_button_titles(); @@ -285,10 +283,10 @@ function rcube_webmail() return rcube_event.cancel(e); }); - // 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); }) - } + // 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); }) + } } document.onmouseup = function(e){ return p.doc_mouse_up(e); }; @@ -351,7 +349,7 @@ function rcube_webmail() this.env.contactfolders = $.extend($.extend({}, this.env.address_sources), this.env.contactgroups); this.enable_command('add', 'import', this.env.writable_source); - this.enable_command('list', 'listgroup', 'pushgroup', 'popgroup', 'listsearch', 'advanced-search', true); + this.enable_command('list', 'listgroup', 'pushgroup', 'popgroup', 'listsearch', 'search', 'reset-search', 'advanced-search', true); if (this.gui_objects.contactslist) { this.contact_list = new rcube_list_widget(this.gui_objects.contactslist, @@ -369,8 +367,8 @@ function rcube_webmail() this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return p.click_on_list(e); }; document.onmouseup = function(e){ return p.doc_mouse_up(e); }; - if (this.gui_objects.qsearchbox) - $(this.gui_objects.qsearchbox).focusin(function() { rcmail.contact_list.blur(); }); + + $(this.gui_objects.qsearchbox).focusin(function() { rcmail.contact_list.blur(); }); this.update_group_commands(); this.command('list'); @@ -394,9 +392,6 @@ function rcube_webmail() this.init_contact_form(); } - if (this.gui_objects.qsearchbox) - this.enable_command('search', 'reset-search', true); - break; case 'settings': @@ -408,9 +403,6 @@ function rcube_webmail() else if (this.env.action == 'edit-identity' || this.env.action == 'add-identity') { this.enable_command('save', 'edit', 'toggle-editor', true); this.enable_command('delete', this.env.identities_level < 2); - - if (this.env.action == 'add-identity') - $("input[type='text']").first().select(); } else if (this.env.action == 'folders') { this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', true); @@ -419,7 +411,6 @@ function rcube_webmail() this.enable_command('save', 'folder-size', true); parent.rcmail.env.exists = this.env.messagecount; parent.rcmail.enable_command('purge', this.env.messagecount); - $("input[type='text']").first().select(); } else if (this.env.action == 'responses') { this.enable_command('add', true); @@ -445,7 +436,7 @@ function rcube_webmail() } else if (this.gui_objects.responseslist) { this.responses_list = new rcube_list_widget(this.gui_objects.responseslist, {multiselect:false, draggable:false, keyboard:false}); - this.responses_list.addEventListener('select', function(list){ + this.responses_list.addEventListener('select', function(list) { var win, id = list.get_single_selection(); p.enable_command('delete', !!id && $.inArray(id, p.env.readonly_responses) < 0); if (id && (win = p.get_frame_window(p.env.contentframe))) { @@ -489,6 +480,11 @@ function rcube_webmail() break; } + // select first input field in an edit form + if (this.gui_objects.editform) + $("input,select,textarea", this.gui_objects.editform) + .not(':hidden').not(':disabled').first().select(); + // unset contentframe variable if preview_pane is enabled if (this.env.contentframe && !$('#' + this.env.contentframe).is(':visible')) this.env.contentframe = null; @@ -584,9 +580,12 @@ function rcube_webmail() } // check input before leaving compose step - if (this.task == 'mail' && this.env.action == 'compose' && $.inArray(command, this.env.compose_commands)<0) { + if (this.task == 'mail' && this.env.action == 'compose' && $.inArray(command, this.env.compose_commands) < 0 && !this.env.server_error) { if (this.cmp_hash != this.compose_field_hash() && !confirm(this.get_label('notsentwarning'))) return false; + + // remove copy from local storage if compose screen is left intentionally + this.remove_compose_data(this.env.compose_id); } // process external commands @@ -621,10 +620,10 @@ function rcube_webmail() break; // commands to switch task + case 'logout': case 'mail': case 'addressbook': case 'settings': - case 'logout': this.switch_task(command); break; @@ -644,6 +643,7 @@ function rcube_webmail() var form = this.gui_objects.messageform, win = this.open_window(''); + this.save_compose_form_local(); $("input[name='_action']", form).val('compose'); form.action = this.url('mail/compose', { _id: this.env.compose_id, _extwin: 1 }); form.target = win.name; @@ -1298,8 +1298,10 @@ function rcube_webmail() return; var url = this.get_task_url(task); - if (task=='mail') + if (task == 'mail') url += '&_mbox=INBOX'; + else if (task == 'logout') + this.clear_compose_data(); this.redirect(url); }; @@ -1706,7 +1708,7 @@ function rcube_webmail() url += (url.match(/\?/) ? '&' : '?') + '_extwin=1'; if (this.env.standard_windows) - extwin = window.open(url, wname); + var extwin = window.open(url, wname); else { var win = this.is_framed() ? parent.window : window, page = $(win), @@ -1727,7 +1729,7 @@ function rcube_webmail() } // focus window, delayed to bring to front - window.setTimeout(function() { extwin.focus(); }, 10); + window.setTimeout(function() { extwin && extwin.focus(); }, 10); return extwin; }; @@ -3123,6 +3125,60 @@ function rcube_webmail() } } + // check for locally stored compose data + if (window.localStorage) { + var index = this.local_storage_get_item('compose.index', []); + + for (var key, i = 0; i < index.length; i++) { + key = index[i], formdata = this.local_storage_get_item('compose.' + key, null, true); + if (!formdata) { + continue; + } + // restore saved copy of current compose_id + if (formdata.changed && key == this.env.compose_id) { + this.restore_compose_form(key, html_mode); + break; + } + // skip records from 'other' drafts + if (this.env.draft_id && formdata.draft_id && formdata.draft_id != this.env.draft_id) { + continue; + } + // show dialog asking to restore the message + if (formdata.changed && formdata.session != this.env.session_id) { + this.show_popup_dialog( + this.get_label('restoresavedcomposedata') + .replace('$date', new Date(formdata.changed).toLocaleString()) + .replace('$subject', formdata._subject) + .replace(/\n/g, '<br/>'), + this.get_label('restoremessage'), + [{ + text: this.get_label('restore'), + click: function(){ + ref.restore_compose_form(key, html_mode); + ref.remove_compose_data(key); // remove old copy + ref.save_compose_form_local(); // save under current compose_id + $(this).dialog('close'); + } + }, + { + text: this.get_label('delete'), + click: function(){ + ref.remove_compose_data(key); + $(this).dialog('close'); + } + }, + { + text: this.get_label('ignore'), + click: function(){ + $(this).dialog('close'); + } + }] + ); + break; + } + } + } + if (input_to.val() == '') input_to.focus(); else if (input_subject.val() == '') @@ -3560,6 +3616,8 @@ function rcube_webmail() this.env.draft_id = id; $("input[name='_draft_saveid']").val(id); + + this.remove_compose_data(this.env.compose_id); }; this.auto_save_start = function() @@ -3567,6 +3625,20 @@ function rcube_webmail() if (this.env.draft_autosave) this.save_timer = setTimeout(function(){ ref.command("savedraft"); }, this.env.draft_autosave * 1000); + // save compose form content to local storage every 5 seconds + if (!this.local_save_timer && window.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++; }); + + this.local_save_timer = setInterval(function(){ + if (ref.compose_type_activity > ref.compose_type_activity_last) { + ref.save_compose_form_local(); + ref.compose_type_activity_last = ref.compose_type_activity; + } + }, 5000); + } + // Unlock interface now that saving is complete this.busy = false; }; @@ -3595,6 +3667,115 @@ function rcube_webmail() return str; }; + // store the contents of the compose form to localstorage + this.save_compose_form_local = function() + { + var formdata = { session:this.env.session_id, changed:new Date().getTime() }, + ed, empty = true; + + // get fresh content from editor + if (window.tinyMCE && (ed = tinyMCE.get(this.env.composebody))) { + tinyMCE.triggerSave(); + } + + if (this.env.draft_id) { + formdata.draft_id = this.env.draft_id; + } + + $('input, select, textarea', this.gui_objects.messageform).each(function(i, elem) { + switch (elem.tagName.toLowerCase()) { + case 'input': + if (elem.type == 'button' || elem.type == 'submit' || (elem.type == 'hidden' && elem.name != '_is_html')) { + break; + } + formdata[elem.name] = elem.type != 'checkbox' || elem.checked ? $(elem).val() : ''; + + if (formdata[elem.name] != '' && elem.type != 'hidden') + empty = false; + break; + + case 'select': + formdata[elem.name] = $('option:checked', elem).val(); + break; + + default: + formdata[elem.name] = $(elem).val(); + if (formdata[elem.name] != '') + empty = false; + } + }); + + if (window.localStorage && !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); + } + }; + + // write stored compose data back to form + this.restore_compose_form = function(key, html_mode) + { + var ed, formdata = this.local_storage_get_item('compose.' + key, true); + + if (formdata && typeof formdata == 'object') { + $.each(formdata, function(k, value) { + if (k[0] == '_') { + var elem = $("*[name='"+k+"']"); + if (elem[0] && elem[0].type == 'checkbox') { + elem.prop('checked', value != ''); + } + else { + elem.val(value); + } + } + }); + + // initialize HTML editor + if (formdata._is_html == '1') { + if (!html_mode) { + tinyMCE.execCommand('mceAddControl', false, this.env.composebody); + this.triggerEvent('aftertoggle-editor', { mode:'html' }); + } + } + else if (html_mode) { + tinyMCE.execCommand('mceRemoveControl', false, this.env.composebody); + this.triggerEvent('aftertoggle-editor', { mode:'plain' }); + } + } + }; + + // remove stored compose data from localStorage + this.remove_compose_data = function(key) + { + if (window.localStorage) { + 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; })); + } + } + }; + + // clear all stored compose data of this user + this.clear_compose_data = function() + { + if (window.localStorage) { + var index = this.local_storage_get_item('compose.index', []); + + for (var i=0; i < index.length; i++) { + this.local_storage_remove_item('compose.' + index[i]); + } + this.local_storage_remove_item('compose.index'); + } + } + + this.change_identity = function(obj, show_sig) { if (!obj || !obj.options) @@ -4027,7 +4208,7 @@ function rcube_webmail() case 38: // arrow up case 40: // arrow down if (!this.ksearch_visible()) - break; + return; var dir = key==38 ? 1 : 0; @@ -4062,8 +4243,7 @@ function rcube_webmail() case 37: // left case 39: // right - if (mod != SHIFT_KEY) - return; + return; } // start timer @@ -4126,8 +4306,10 @@ function rcube_webmail() if (this.ksearch_input.setSelectionRange) this.ksearch_input.setSelectionRange(cpos, cpos); - if (trigger) + if (trigger) { this.triggerEvent('autocomplete_insert', { field:this.ksearch_input, insert:insert }); + this.compose_type_activity++; + } }; this.replace_group_recipients = function(id, recipients) @@ -4136,6 +4318,7 @@ function rcube_webmail() this.group2expand[id].input.value = this.group2expand[id].input.value.replace(this.group2expand[id].name, recipients); this.triggerEvent('autocomplete_insert', { field:this.group2expand[id].input, insert:recipients }); this.group2expand[id] = null; + this.compose_type_activity++; } }; @@ -4820,8 +5003,6 @@ function rcube_webmail() $('input.datepicker').datepicker(); } - $("input[type='text']:visible").first().focus(); - // Submit search form on Enter if (this.env.action == 'search') $(this.gui_objects.editform).append($('<input type="submit">').hide()) @@ -5438,7 +5619,10 @@ function rcube_webmail() this.init_subscription_list = function() { - var p = this; + var p = this, delim = RegExp.escape(this.env.delimiter); + + this.last_sub_rx = RegExp('['+delim+']?[^'+delim+']+$'); + 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); }); @@ -5449,6 +5633,7 @@ function rcube_webmail() row.obj.onmouseout = function() { p.unfocus_subscription(row.id); }; }; this.subscription_list.init(); + $('#mailboxroot') .mouseover(function(){ p.focus_subscription(this.id); }) .mouseout(function(){ p.unfocus_subscription(this.id); }) @@ -5456,9 +5641,7 @@ function rcube_webmail() this.focus_subscription = function(id) { - var row, folder, - delim = RegExp.escape(this.env.delimiter), - reg = RegExp('['+delim+']?[^'+delim+']+$'); + var row, folder; if (this.drag_active && this.env.mailbox && (row = document.getElementById(id))) if (this.env.subscriptionrows[id] && @@ -5466,8 +5649,8 @@ function rcube_webmail() ) { if (this.check_droptarget(folder) && !this.env.subscriptionrows[this.get_folder_row_id(this.env.mailbox)][2] && - (folder != this.env.mailbox.replace(reg, '')) && - (!folder.startsWith(this.env.mailbox + this.env.delimiter)) + folder != this.env.mailbox.replace(this.last_sub_rx, '') && + !folder.startsWith(this.env.mailbox + this.env.delimiter) ) { this.env.dstfolder = folder; $(row).addClass('droptarget'); @@ -5480,7 +5663,8 @@ function rcube_webmail() var row = $('#'+id); this.env.dstfolder = null; - if (this.env.subscriptionrows[id] && row[0]) + + if (this.env.subscriptionrows[id] && row.length) row.removeClass('droptarget'); else $(this.subscription_list.frame).removeClass('droptarget'); @@ -5506,21 +5690,20 @@ function rcube_webmail() this.subscription_move_folder = function(list) { - var delim = RegExp.escape(this.env.delimiter), - reg = RegExp('['+delim+']?[^'+delim+']+$'); - - if (this.env.mailbox && this.env.dstfolder !== null && (this.env.dstfolder != this.env.mailbox) && - (this.env.dstfolder != this.env.mailbox.replace(reg, '')) + 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, '') ) { - reg = new RegExp('[^'+delim+']*['+delim+']', 'g'); - var basename = this.env.mailbox.replace(reg, ''), - newname = this.env.dstfolder === '' ? basename : this.env.dstfolder+this.env.delimiter+basename; + var path = this.env.mailbox.split(this.env.delimiter), + basename = path.pop(), + newname = this.env.dstfolder === '' ? basename : this.env.dstfolder + this.env.delimiter + basename; 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(); } } + this.drag_active = false; this.unfocus_subscription(this.get_folder_row_id(this.env.dstfolder)); }; @@ -5549,7 +5732,7 @@ function rcube_webmail() if (!this.gui_objects.subscriptionlist) return false; - var row, n, i, tmp, tmp_name, folders, rowid, list = [], slist = [], + var row, n, i, tmp, tmp_name, rowid, folders = [], list = [], slist = [], tbody = this.gui_objects.subscriptionlist.tBodies[0], refrow = $('tr', tbody).get(1), id = 'rcmrow'+((new Date).getTime()); @@ -5564,8 +5747,7 @@ function rcube_webmail() row = $(refrow).clone(true); // set ID, reset css class - row.attr('id', id); - row.attr('class', class_name); + row.attr({id: id, 'class': class_name}); // set folder name row.find('td:first').html(display_name); @@ -5577,10 +5759,21 @@ function rcube_webmail() // add to folder/row-ID map this.env.subscriptionrows[id] = [name, display_name, 0]; - // sort folders, to find a place where to insert the row - folders = []; - $.each(this.env.subscriptionrows, function(k,v){ folders.push(v) }); - folders.sort(function(a,b){ return a[0] < b[0] ? -1 : (a[0] > b[0] ? 1 : 0) }); + // sort folders (to find a place where to insert the row) + // replace delimiter with \0 character to fix sorting + // issue where 'Abc Abc' would be placed before 'Abc/def' + var replace_from = RegExp(RegExp.escape(this.env.delimiter), 'g'), + replace_to = String.fromCharCode(0); + $.each(this.env.subscriptionrows, function(k,v) { + var n = v[0]; + n = n.replace(replace_from, replace_to); + v.push(n); + folders.push(v); + }); + folders.sort(function(a, b) { + var len = a.length - 1; n1 = a[len], n2 = b[len]; + return n1 < n2 ? -1 : 1; + }); for (n in folders) { // protected folder @@ -5642,7 +5835,7 @@ function rcube_webmail() tbody = this.gui_objects.subscriptionlist.tBodies[0], folders = this.env.subscriptionrows, id = this.get_folder_row_id(oldfolder), - regex = new RegExp('^'+RegExp.escape(oldfolder)), + prefix_len = oldfolder.length, subscribed = $('input[name="_subscribed[]"]', $('#'+id)).prop('checked'), // find subfolders of renamed folder list = this.get_subfolders(oldfolder); @@ -5667,7 +5860,7 @@ function rcube_webmail() row.after(tmprow); row = tmprow; // update folder index - name = name.replace(regex, newfolder); + name = newfolder + name.slice(prefix_len); $('input[name="_subscribed[]"]', row).val(name); this.env.subscriptionrows[id][0] = name; // update the name if level is changed @@ -5912,59 +6105,36 @@ function rcube_webmail() // mouse over button this.button_over = function(command, id) { - var n, button, obj, a_buttons = this.buttons[command], - len = a_buttons ? a_buttons.length : 0; - - for (n=0; n<len; n++) { - button = a_buttons[n]; - if (button.id == id && button.status == 'act') { - obj = document.getElementById(button.id); - if (obj && button.over) { - if (button.type == 'image') - obj.src = button.over; - else - obj.className = button.over; - } - } - } + this.button_event(command, id, 'over'); }; // mouse down on button this.button_sel = function(command, id) { - var n, button, obj, a_buttons = this.buttons[command], - len = a_buttons ? a_buttons.length : 0; - - for (n=0; n<len; n++) { - button = a_buttons[n]; - if (button.id == id && button.status == 'act') { - obj = document.getElementById(button.id); - if (obj && button.sel) { - if (button.type == 'image') - obj.src = button.sel; - else - obj.className = button.sel; - } - this.buttons_sel[id] = command; - } - } + this.button_event(command, id, 'sel'); }; // mouse out of button this.button_out = function(command, id) { + this.button_event(command, id, 'act'); + }; + + // event of button + this.button_event = function(command, id, event) + { var n, button, obj, a_buttons = this.buttons[command], len = a_buttons ? a_buttons.length : 0; for (n=0; n<len; n++) { button = a_buttons[n]; if (button.id == id && button.status == 'act') { - obj = document.getElementById(button.id); - if (obj && button.act) { - if (button.type == 'image') - obj.src = button.act; - else - obj.className = button.act; + if (button[event] && (obj = document.getElementById(button.id))) { + obj[button.type == 'image' ? 'src' : 'className'] = button[event]; + } + + if (event == 'sel') { + this.buttons_sel[id] = command; } } } @@ -6039,7 +6209,7 @@ function rcube_webmail() this.triggerEvent('message', { message:msg, type:type, timeout:timeout, object:obj }); if (timeout > 0) - setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout); + setTimeout(function() { ref.hide_message(id, type != 'loading'); }, timeout); return id; }; @@ -6152,8 +6322,8 @@ function rcube_webmail() // enable/disable buttons for page shifting this.set_page_buttons = function() { - this.enable_command('nextpage', 'lastpage', (this.env.pagecount > this.env.current_page)); - this.enable_command('previouspage', 'firstpage', (this.env.current_page > 1)); + this.enable_command('nextpage', 'lastpage', this.env.pagecount > this.env.current_page); + this.enable_command('previouspage', 'firstpage', this.env.current_page > 1); }; // mark a mailbox as selected and set environment variable @@ -6163,14 +6333,10 @@ function rcube_webmail() this.treelist.select(name); } else if (this.gui_objects.folderlist) { - var current_li, target_li; - - if ((current_li = $('li.selected', this.gui_objects.folderlist))) { - current_li.removeClass('selected').addClass('unfocused'); - } - if ((target_li = this.get_folder_li(name, prefix, encode))) { - $(target_li).removeClass('unfocused').addClass('selected'); - } + $('li.selected', this.gui_objects.folderlist) + .removeClass('selected').addClass('unfocused'); + $(this.get_folder_li(name, prefix, encode)) + .removeClass('unfocused').addClass('selected'); // trigger event hook this.triggerEvent('selectfolder', { folder:name, prefix:prefix }); @@ -6199,8 +6365,6 @@ function rcube_webmail() name = this.html_identifier(name, encode); return document.getElementById(prefix+name); } - - return null; }; // for reordering column array (Konqueror workaround) @@ -6744,6 +6908,20 @@ function rcube_webmail() setTimeout(function(){ ref.keep_alive(); ref.start_keepalive(); }, 30000); }; + // handler for session errors detected on the server + this.session_error = function(redirect_url) + { + this.env.server_error = 401; + + // save message in local storage and do not redirect + if (this.env.action == 'compose') { + this.save_compose_form_local(); + } + else if (redirect_url) { + window.setTimeout(function(){ ref.redirect(redirect_url, true); }, 2000); + } + }; + // callback when an iframe finished loading this.iframe_loaded = function(unlock) { @@ -7265,7 +7443,28 @@ function rcube_webmail() this.set_cookie = function(name, value, expires) { setCookie(name, value, expires, this.env.cookie_path, this.env.cookie_domain, this.env.cookie_secure); - } + }; + + // wrapper for localStorage.getItem(key) + this.local_storage_get_item = function(key, deflt, encrypted) + { + // TODO: add encryption + var item = localStorage.getItem(this.local_storage_prefix + key); + 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.local_storage_prefix + key, JSON.stringify(data)); + }; + + // wrapper for localStorage.removeItem(key) + this.local_storage_remove_item = function(key) + { + return localStorage.removeItem(this.local_storage_prefix + key); + }; } // end object rcube_webmail |