From 32077b1685410b4b5251ca6eade8e6c6a59ecf63 Mon Sep 17 00:00:00 2001 From: alecpl <alec@alec.pl> Date: Tue, 13 Mar 2012 05:09:47 -0400 Subject: [PATCH] - Fix js errors (spellcheck-related) when switching editor mode in identity screen --- program/js/app.js | 751 +++++++++++++++++++++++++++++++++------------------------ 1 files changed, 438 insertions(+), 313 deletions(-) diff --git a/program/js/app.js b/program/js/app.js index ce0f52f..3b2a148 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -3,9 +3,12 @@ | Roundcube Webmail Client Script | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2011, The Roundcube Dev Team | + | Copyright (C) 2005-2012, The Roundcube Dev Team | | Copyright (C) 2011, Kolab Systems AG | - | Licensed under the GNU GPL | + | | + | Licensed under the GNU General Public License version 3 or | + | any later version with exceptions for skins & plugins. | + | See the README file for a full license statement. | | | +-----------------------------------------------------------------------+ | Authors: Thomas Bruederli <roundcube@gmail.com> | @@ -20,7 +23,7 @@ function rcube_webmail() { - this.env = {}; + this.env = { recipients_separator:',', recipients_delimiter:', ' }; this.labels = {}; this.buttons = {}; this.buttons_sel = {}; @@ -30,6 +33,7 @@ this.command_handlers = {}; this.onloads = []; this.messages = {}; + this.group2expand = {}; // create protected reference to myself this.ref = 'rcmail'; @@ -37,7 +41,7 @@ // webmail client settings this.dblclick_time = 500; - this.message_time = 2000; + this.message_time = 4000; this.identifier_expr = new RegExp('[^0-9a-z\-_]', 'gi'); @@ -128,22 +132,38 @@ // initialize webmail client this.init = function() { - var p = this; + var n, p = this; this.task = this.env.task; // check browser - if (!bw.dom || !bw.xmlhttp_test()) { + if (!bw.dom || !bw.xmlhttp_test() || (bw.mz && bw.vendver < 1.9)) { this.goto_url('error', '_code=0x199'); return; } // find all registered gui containers - for (var n in this.gui_containers) + for (n in this.gui_containers) this.gui_containers[n] = $('#'+this.gui_containers[n]); // find all registered gui objects - for (var n in this.gui_objects) + for (n in this.gui_objects) this.gui_objects[n] = rcube_find_object(this.gui_objects[n]); + + // clickjacking protection + if (this.env.x_frame_options) { + try { + // bust frame if not allowed + if (this.env.x_frame_options == 'deny' && top.location.href != self.location.href) + top.location.href = self.location.href; + else if (top.location.hostname != self.location.hostname) + throw 1; + } catch (e) { + // possible clickjacking attack: disable all form elements + $('form').each(function(){ ref.lock_form(this, true); }); + this.display_message("Blocked: possible clickjacking attack!", 'error'); + return; + } + } // init registered buttons this.init_buttons(); @@ -155,7 +175,7 @@ } // enable general commands - this.enable_command('logout', 'mail', 'addressbook', 'settings', 'save-pref', 'compose', 'undo', true); + this.enable_command('logout', 'mail', 'addressbook', 'settings', 'save-pref', 'compose', 'undo', 'about', true); if (this.env.permaurl) this.enable_command('permaurl', true); @@ -207,12 +227,13 @@ 'moveto', 'copy', 'delete', 'open', 'mark', 'edit', 'viewsource', 'download', 'print', 'load-attachment', 'load-headers', 'forward-attachment']; - if (this.env.action=='show' || this.env.action=='preview') { + if (this.env.action == 'show' || this.env.action == 'preview') { this.enable_command(this.env.message_commands, this.env.uid); this.enable_command('reply-list', this.env.list_post); if (this.env.action == 'show') { - this.http_request('pagenav', '_uid='+this.env.uid+'&_mbox='+urlencode(this.env.mailbox), + this.http_request('pagenav', '_uid='+this.env.uid+'&_mbox='+urlencode(this.env.mailbox) + + (this.env.search_request ? '&_search='+this.env.search_request : ''), this.display_message('', 'loading')); } @@ -229,19 +250,20 @@ } } else if (this.env.action == 'compose') { - this.env.compose_commands = ['send-attachment', 'remove-attachment', 'send', 'cancel', 'toggle-editor']; + this.env.compose_commands = ['send-attachment', 'remove-attachment', 'send', 'cancel', 'toggle-editor', 'list-adresses']; if (this.env.drafts_mailbox) this.env.compose_commands.push('savedraft') this.enable_command(this.env.compose_commands, 'identities', true); + // add more commands (not enabled) + $.merge(this.env.compose_commands, ['add-recipient', 'firstpage', 'previouspage', 'nextpage', 'lastpage']); + if (this.env.spellcheck) { - this.env.spellcheck.spelling_state_observer = function(s){ ref.set_spellcheck_state(s); }; + this.env.spellcheck.spelling_state_observer = function(s) { ref.spellcheck_state(); }; this.env.compose_commands.push('spellcheck') - this.set_spellcheck_state('ready'); - if ($("input[name='_is_html']").val() == '1') - this.display_spellcheck_controls(false); + this.enable_command('spellcheck', true); } document.onmouseup = function(e){ return p.doc_mouse_up(e); }; @@ -261,6 +283,20 @@ this.env.unread_counts = {}; this.gui_objects.folderlist = this.gui_objects.mailboxlist; this.http_request('getunread', ''); + } + + // init address book widget + if (this.gui_objects.contactslist) { + this.contact_list = new rcube_list_widget(this.gui_objects.contactslist, + { multiselect:true, draggable:false, keyboard:false }); + this.contact_list.addEventListener('select', function(o){ ref.compose_recipient_select(o); }); + this.contact_list.addEventListener('dblclick', function(o){ ref.compose_add_recipient('to'); }); + this.contact_list.init(); + } + + if (this.gui_objects.addressbookslist) { + this.gui_objects.folderlist = this.gui_objects.addressbookslist; + this.enable_command('list-adresses', true); } // ask user to send MDN @@ -337,8 +373,11 @@ this.enable_command('add', this.env.identities_level < 2); } else if (this.env.action == 'edit-identity' || this.env.action == 'add-identity') { - this.enable_command('add', this.env.identities_level < 2); - this.enable_command('save', 'delete', 'edit', 'toggle-editor', true); + 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); @@ -380,14 +419,17 @@ $('#rcmloginpwd').focus(); // detect client timezone - var tz = new Date().getTimezoneOffset() / -60; - var stdtz = new Date().getStdTimezoneOffset() / -60; + var dt = new Date(), + tz = dt.getTimezoneOffset() / -60, + stdtz = dt.getStdTimezoneOffset() / -60; + $('#rcmlogintz').val(stdtz); $('#rcmlogindst').val(tz > stdtz ? 1 : 0); // display 'loading' message on form submit, lock submit button $('form').submit(function () { $('input[type=submit]', this).prop('disabled', true); + rcmail.clear_messages(); rcmail.display_message('', 'loading'); }); @@ -442,7 +484,7 @@ // execute a specific command on the web client this.command = function(command, props, obj) { - var ret; + var ret, uid, cid, url, flag; if (obj && obj.blur) obj.blur(); @@ -460,7 +502,7 @@ } // 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) { if (this.cmp_hash != this.compose_field_hash() && !confirm(this.get_label('notsentwarning'))) return false; } @@ -504,6 +546,10 @@ this.switch_task(command); break; + case 'about': + location.href = '?_task=settings&_action=about'; + break; + case 'permaurl': if (obj && obj.href && obj.target) return true; @@ -517,7 +563,6 @@ return false; case 'open': - var uid; if (uid = this.get_single_uid()) { obj.href = '?_task='+this.env.task+'&_action=show&_mbox='+urlencode(this.env.mailbox)+'&_uid='+uid; return true; @@ -586,7 +631,7 @@ // common commands used in multiple tasks case 'show': if (this.task == 'mail') { - var uid = this.get_single_uid(); + uid = this.get_single_uid(); if (uid && (!this.env.uid || uid != this.env.uid)) { if (this.env.mailbox == this.env.drafts_mailbox) this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true); @@ -595,7 +640,7 @@ } } else if (this.task == 'addressbook') { - var cid = props ? props : this.get_single_cid(); + cid = props ? props : this.get_single_cid(); if (cid && !(this.env.action == 'show' && cid == this.env.cid)) this.load_contact(cid, 'show'); } @@ -611,13 +656,12 @@ break; case 'edit': - var cid; if (this.task=='addressbook' && (cid = this.get_single_cid())) this.load_contact(cid, 'edit'); else if (this.task=='settings' && props) this.load_identity(props, 'edit-identity'); else if (this.task=='mail' && (cid = this.get_single_uid())) { - var url = (this.env.mailbox == this.env.drafts_mailbox) ? '_draft_uid=' : '_uid='; + url = (this.env.mailbox == this.env.drafts_mailbox) ? '_draft_uid=' : '_uid='; this.goto_url('compose', url+cid+'&_mbox='+urlencode(this.env.mailbox), true); } break; @@ -695,7 +739,7 @@ if (props && !props._row) break; - var uid, flag = 'read'; + flag = 'read'; if (props._row.uid) { uid = props._row.uid; @@ -715,7 +759,7 @@ if (props && !props._row) break; - var uid, flag = 'flagged'; + flag = 'flagged'; if (props._row.uid) { uid = props._row.uid; @@ -811,17 +855,11 @@ break; case 'compose': - var url = this.url('mail/compose'); + url = this.url('mail/compose'); if (this.task == 'mail') { url += '&_mbox='+urlencode(this.env.mailbox); - - if (this.env.mailbox == this.env.drafts_mailbox) { - var uid; - if (uid = this.get_single_uid()) - url += '&_draft_uid='+uid; - } - else if (props) + if (props) url += '&_to='+urlencode(props); } // modify url if we're in addressbook @@ -858,13 +896,18 @@ break; case 'spellcheck': - if (window.tinyMCE && tinyMCE.get(this.env.composebody)) { - tinyMCE.execCommand('mceSpellCheck', true); + if (this.spellcheck_state()) { + this.stop_spellchecking(); } - else if (this.env.spellcheck && this.env.spellcheck.spellCheck && this.spellcheck_ready) { - this.env.spellcheck.spellCheck(); - this.set_spellcheck_state('checking'); + else { + if (window.tinyMCE && tinyMCE.get(this.env.composebody)) { + tinyMCE.execCommand('mceSpellCheck', true); + } + else if (this.env.spellcheck && this.env.spellcheck.spellCheck) { + this.env.spellcheck.spellCheck(); + } } + this.spellcheck_state(); break; case 'savedraft': @@ -892,7 +935,7 @@ if (!this.gui_objects.messageform) break; - if (!this.check_compose_input()) + if (!props.nocheck && !this.check_compose_input(command)) break; // Reset the auto-save timer @@ -916,20 +959,28 @@ case 'send-attachment': // Reset the auto-save timer self.clearTimeout(this.save_timer); - - this.upload_file(props) + + this.upload_file(props || this.gui_objects.uploadform); break; case 'insert-sig': this.change_identity($("[name='_from']")[0], true); break; + case 'list-adresses': + this.list_contacts(props); + this.enable_command('add-recipient', false); + break; + + case 'add-recipient': + this.compose_add_recipient(props); + break; + case 'reply-all': case 'reply-list': case 'reply': - var uid; if (uid = this.get_single_uid()) { - var url = '_reply_uid='+uid+'&_mbox='+urlencode(this.env.mailbox); + url = '_reply_uid='+uid+'&_mbox='+urlencode(this.env.mailbox); if (command == 'reply-all') // do reply-list, when list is detected and popup menu wasn't used url += '&_all=' + (!props && this.commands['reply-list'] ? 'list' : 'all'); @@ -942,7 +993,6 @@ case 'forward-attachment': case 'forward': - var uid, url; if (uid = this.get_single_uid()) { url = '_forward_uid='+uid+'&_mbox='+urlencode(this.env.mailbox); if (command == 'forward-attachment' || (!props && this.env.forward_attachment)) @@ -952,7 +1002,6 @@ break; case 'print': - var uid; if (uid = this.get_single_uid()) { ref.printwin = window.open(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(this.env.safemode ? '&_safe=1' : '')); if (this.printwin) { @@ -964,7 +1013,6 @@ break; case 'viewsource': - var uid; if (uid = this.get_single_uid()) { ref.sourcewin = window.open(this.env.comm_path+'&_action=viewsource&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)); if (this.sourcewin) @@ -973,7 +1021,6 @@ break; case 'download': - var uid; if (uid = this.get_single_uid()) this.goto_url('viewsource', '&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+'&_save=1'); break; @@ -1033,7 +1080,7 @@ break; case 'upload-photo': - this.upload_contact_photo(props); + this.upload_contact_photo(props || this.gui_objects.uploadform); break; case 'delete-photo': @@ -1070,10 +1117,10 @@ // set command(s) enabled or disabled this.enable_command = function() { - var args = Array.prototype.slice.call(arguments), + var i, n, args = Array.prototype.slice.call(arguments), enable = args.pop(), cmd; - for (var n=0; n<args.length; n++) { + for (n=0; n<args.length; n++) { cmd = args[n]; // argument of type array if (typeof cmd === 'string') { @@ -1082,7 +1129,7 @@ } // push array elements into commands array else { - for (var i in cmd) + for (i in cmd) args.push(cmd[i]); } } @@ -1209,6 +1256,24 @@ this.http_post('save-pref', request); }; + this.html_identifier = function(str, encode) + { + str = String(str); + if (encode) + return Base64.encode(str).replace(/=+$/, '').replace(/\+/g, '-').replace(/\//g, '_'); + else + return str.replace(this.identifier_expr, '_'); + }; + + this.html_identifier_decode = function(str) + { + str = String(str).replace(/-/g, '+').replace(/_/g, '/'); + + while (str.length % 4) str += '='; + + return Base64.decode(str); + }; + /*********************************************************/ /********* event handling methods *********/ @@ -1255,13 +1320,14 @@ this.initialBodyScrollTop = bw.ie ? 0 : window.pageYOffset; this.initialListScrollTop = this.gui_objects.folderlist.parentNode.scrollTop; - var li, pos, list, height; - list = $(this.gui_objects.folderlist); - pos = list.offset(); + var k, li, height, + list = $(this.gui_objects.folderlist); + pos = list.offset(); + this.env.folderlist_coords = { x1:pos.left, y1:pos.top, x2:pos.left + list.width(), y2:pos.top + list.height() }; this.env.folder_coords = []; - for (var k in model) { + for (k in model) { if (li = this.get_folder_li(k)) { // only visible folders if (height = li.firstChild.offsetHeight) { @@ -1297,19 +1363,18 @@ this.drag_move = function(e) { if (this.gui_objects.folderlist && this.env.folder_coords) { - // offsets to compensate for scrolling while dragging a message - var boffset = bw.ie ? -document.documentElement.scrollTop : this.initialBodyScrollTop; - var moffset = this.initialListScrollTop-this.gui_objects.folderlist.parentNode.scrollTop; - var toffset = -moffset-boffset; - var li, div, pos, mouse, check, oldclass, - layerclass = 'draglayernormal'; + var k, li, div, check, oldclass, + layerclass = 'draglayernormal', + mouse = rcube_event.get_mouse_pos(e), + pos = this.env.folderlist_coords, + // offsets to compensate for scrolling while dragging a message + boffset = bw.ie ? -document.documentElement.scrollTop : this.initialBodyScrollTop, + moffset = this.initialListScrollTop-this.gui_objects.folderlist.parentNode.scrollTop; if (this.contact_list && this.contact_list.draglayer) oldclass = this.contact_list.draglayer.attr('class'); - mouse = rcube_event.get_mouse_pos(e); - pos = this.env.folderlist_coords; - mouse.y += toffset; + mouse.y += -moffset-boffset; // if mouse pointer is outside of folderlist if (mouse.x < pos.x1 || mouse.x >= pos.x2 || mouse.y < pos.y1 || mouse.y >= pos.y2) { @@ -1324,10 +1389,10 @@ } // over the folders - for (var k in this.env.folder_coords) { + for (k in this.env.folder_coords) { pos = this.env.folder_coords[k]; if (mouse.x >= pos.x1 && mouse.x < pos.x2 && mouse.y >= pos.y1 && mouse.y < pos.y2){ - if ((check = this.check_droptarget(k))) { + if ((check = this.check_droptarget(k))) { li = this.get_folder_li(k); div = $(li.getElementsByTagName('div')[0]); @@ -1336,11 +1401,11 @@ if (this.folder_auto_timer) window.clearTimeout(this.folder_auto_timer); - this.folder_auto_expand = k; + this.folder_auto_expand = this.env.mailboxes[k].id; this.folder_auto_timer = window.setTimeout(function() { - rcmail.command('collapse-folder', rcmail.folder_auto_expand); - rcmail.drag_start(null); - }, 1000); + rcmail.command('collapse-folder', rcmail.folder_auto_expand); + rcmail.drag_start(null); + }, 1000); } else if (this.folder_auto_timer) { window.clearTimeout(this.folder_auto_timer); this.folder_auto_timer = null; @@ -1366,31 +1431,30 @@ } }; - this.collapse_folder = function(id) + this.collapse_folder = function(name) { - var li = this.get_folder_li(id), - div = $(li.getElementsByTagName('div')[0]); - - if (!div || (!div.hasClass('collapsed') && !div.hasClass('expanded'))) - return; - - var ul = $(li.getElementsByTagName('ul')[0]); + var li = this.get_folder_li(name, '', true), + div = $('div:first', li), + ul = $('ul:first', li); if (div.hasClass('collapsed')) { ul.show(); div.removeClass('collapsed').addClass('expanded'); - var reg = new RegExp('&'+urlencode(id)+'&'); + var reg = new RegExp('&'+urlencode(name)+'&'); this.env.collapsed_folders = this.env.collapsed_folders.replace(reg, ''); } - else { + else if (div.hasClass('expanded')) { ul.hide(); div.removeClass('expanded').addClass('collapsed'); - this.env.collapsed_folders = this.env.collapsed_folders+'&'+urlencode(id)+'&'; + this.env.collapsed_folders = this.env.collapsed_folders+'&'+urlencode(name)+'&'; - // select parent folder if one of its childs is currently selected - if (this.env.mailbox.indexOf(id + this.env.delimiter) == 0) - this.command('list', id); + // select the folder if one of its childs is currently selected + // don't select if it's virtual (#1488346) + if (this.env.mailbox.indexOf(name + this.env.delimiter) == 0 && !$(li).hasClass('virtual')) + this.command('list', name); } + else + return; // Work around a bug in IE6 and IE7, see #1485309 if (bw.ie6 || bw.ie7) { @@ -1402,7 +1466,7 @@ } this.command('save-pref', { name: 'collapsed_folders', value: this.env.collapsed_folders }); - this.set_unread_count_display(id, false); + this.set_unread_count_display(name, false); }; this.doc_mouse_up = function(e) @@ -1560,6 +1624,7 @@ { if (this.env.messages[row.uid]) this.env.messages[row.uid].expanded = row.expanded; + $(row.obj)[row.expanded?'addClass':'removeClass']('expanded'); }; this.msglist_set_coltypes = function(list) @@ -1678,11 +1743,12 @@ flags: flags.extra_flags }); - var c, n, col, html, tree = '', expando = '', + var c, n, col, html, css_class, + tree = '', expando = '', list = this.message_list, rows = list.rows, message = this.env.messages[uid], - css_class = 'message' + row_class = 'message' + (!flags.seen ? ' unread' : '') + (flags.deleted ? ' deleted' : '') + (flags.flagged ? ' flagged' : '') @@ -1692,7 +1758,6 @@ row = document.createElement('tr'); row.id = 'rcmrow'+uid; - row.className = css_class; // message status icons css_class = 'msgicon'; @@ -1729,6 +1794,8 @@ } else message.expanded = true; + + row_class += ' thread expanded'; } else if (message.has_children) { if (message.expanded === undefined && (this.env.autoexpand_threads == 1 || (this.env.autoexpand_threads == 2 && message.unread_children))) { @@ -1736,10 +1803,12 @@ } expando = '<div id="rcmexpando' + uid + '" class="' + (message.expanded ? 'expanded' : 'collapsed') + '"> </div>'; + row_class += ' thread' + (message.expanded? ' expanded' : ''); } } tree += '<span id="msgicn'+uid+'" class="'+css_class+'"> </span>'; + row.className = row_class; // build subject link if (!bw.ie && cols.subject) { @@ -1944,10 +2013,10 @@ if (page > 0 && page <= this.env.pagecount) { this.env.current_page = page; - if (this.task == 'mail') - this.list_mailbox(this.env.mailbox, page); - else if (this.task == 'addressbook') + if (this.task == 'addressbook' || this.contact_list) this.list_contacts(this.env.source, this.env.group, page); + else if (this.task == 'mail') + this.list_mailbox(this.env.mailbox, page); } }; @@ -1995,7 +2064,8 @@ if (mbox != this.env.mailbox || (mbox == this.env.mailbox && !page && !sort)) url += '&_refresh=1'; - this.select_folder(mbox); + this.select_folder(mbox, '', true); + this.unmark_folder(mbox, 'recent', '', true); this.env.mailbox = mbox; // load message list remotely @@ -2926,12 +2996,46 @@ this.init_address_input_events = function(obj, props) { + this.env.recipients_delimiter = this.env.recipients_separator + ' '; + obj[bw.ie || bw.safari || bw.chrome ? 'keydown' : 'keypress'](function(e) { return ref.ksearch_keydown(e, this, props); }) .attr('autocomplete', 'off'); }; + + this.compose_recipient_select = function(list) + { + this.enable_command('add-recipient', list.selection.length > 0); + }; + + this.compose_add_recipient = function(field) + { + var recipients = [], input = $('#_'+field); + + if (this.contact_list && this.contact_list.selection.length) { + for (var id, n=0; n < this.contact_list.selection.length; n++) { + id = this.contact_list.selection[n]; + if (id && this.env.contactdata[id]) { + recipients.push(this.env.contactdata[id]); + + // group is added, expand it + if (id.charAt(0) == 'E' && this.env.contactdata[id].indexOf('@') < 0 && input.length) { + var gid = id.substr(1); + this.group2expand[gid] = { name:this.env.contactdata[id], input:input.get(0) }; + this.http_request('group-expand', '_source='+urlencode(this.env.source)+'&_gid='+urlencode(gid), false); + } + } + } + } + + if (recipients.length && input.length) { + var oldval = input.val(); + input.val((oldval ? oldval + this.env.recipients_delimiter : '') + recipients.join(this.env.recipients_delimiter)); + this.triggerEvent('add-recipient', { field:field, recipients:recipients }); + } + }; // checks the input fields before sending a message - this.check_compose_input = function() + this.check_compose_input = function(cmd) { // check input fields var ed, input_to = $("[name='_to']"), @@ -2966,15 +3070,28 @@ // display localized warning for missing subject if (input_subject.val() == '') { - var subject = prompt(this.get_label('nosubjectwarning'), this.get_label('nosubject')); + var myprompt = $('<div class="prompt">').html('<div class="message">' + this.get_label('nosubjectwarning') + '</div>').appendTo(document.body); + var prompt_value = $('<input>').attr('type', 'text').attr('size', 30).appendTo(myprompt).val(this.get_label('nosubject')); - // user hit cancel, so don't send - if (!subject && subject !== '') { + var buttons = {}; + buttons[this.get_label('cancel')] = function(){ input_subject.focus(); - return false; - } - else - input_subject.val((subject ? subject : this.get_label('nosubject'))); + $(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() } + }); + prompt_value.select(); + return false; } // Apply spellcheck changes if spell checker is active @@ -3002,15 +3119,19 @@ this.toggle_editor = function(props) { + this.stop_spellchecking(); + if (props.mode == 'html') { - this.display_spellcheck_controls(false); this.plain2html($('#'+props.id).val(), props.id); tinyMCE.execCommand('mceAddControl', false, props.id); + + if (this.env.default_font) + window.setTimeout(function() { + $(tinyMCE.get(props.id).getBody()).css('font-family', rcmail.env.default_font); + }, 500); } else { var thisMCE = tinyMCE.get(props.id), existingHtml; - if (thisMCE.plugins.spellchecker && thisMCE.plugins.spellchecker.active) - thisMCE.execCommand('mceSpellCheck', false); if (existingHtml = thisMCE.getContent()) { if (!confirm(this.get_label('editorwarning'))) { @@ -3019,7 +3140,6 @@ this.html2plain(existingHtml, props.id); } tinyMCE.execCommand('mceRemoveControl', false, props.id); - this.display_spellcheck_controls(true); } return true; @@ -3028,43 +3148,53 @@ this.stop_spellchecking = function() { var ed; + if (window.tinyMCE && (ed = tinyMCE.get(this.env.composebody))) { - if (ed.plugins.spellchecker && ed.plugins.spellchecker.active) + if (ed.plugins && ed.plugins.spellchecker && ed.plugins.spellchecker.active) ed.execCommand('mceSpellCheck'); } - else if ((ed = this.env.spellcheck) && !this.spellcheck_ready) { - $(ed.spell_span).trigger('click'); - this.set_spellcheck_state('ready'); + else if (ed = this.env.spellcheck) { + if (ed.state && ed.state != 'ready' && ed.state != 'no_error_found') + $(ed.spell_span).trigger('click'); } + + this.spellcheck_state(); }; - this.display_spellcheck_controls = function(vis) + this.spellcheck_state = function() { - if (this.env.spellcheck) { - // stop spellchecking process - if (!vis) - this.stop_spellchecking(); + var ed, active; - $(this.env.spellcheck.spell_container).css('visibility', vis ? 'visible' : 'hidden'); - } - }; + if (window.tinyMCE && (ed = tinyMCE.get(this.env.composebody)) && ed.plugins && ed.plugins.spellchecker) + active = ed.plugins.spellchecker.active; + else if ((ed = this.env.spellcheck) && ed.state) + active = ed.state != 'ready' && ed.state != 'no_error_found'; - this.set_spellcheck_state = function(s) - { - this.spellcheck_ready = (s == 'ready' || s == 'no_error_found'); - this.enable_command('spellcheck', this.spellcheck_ready); + if (rcmail.buttons.spellcheck) + $('#'+rcmail.buttons.spellcheck[0].id)[active ? 'addClass' : 'removeClass']('selected'); + + return active; }; // get selected language this.spellcheck_lang = function() { var ed; - if (window.tinyMCE && (ed = tinyMCE.get(this.env.composebody)) && ed.plugins.spellchecker) { + + if (window.tinyMCE && (ed = tinyMCE.get(this.env.composebody)) && ed.plugins && ed.plugins.spellchecker) return ed.plugins.spellchecker.selectedLang; - } - else if (this.env.spellcheck) { + else if (this.env.spellcheck) return GOOGIE_CUR_LANG; - } + }; + + this.spellcheck_lang_set = function(lang) + { + var ed; + + if (window.tinyMCE && (ed = tinyMCE.get(this.env.composebody)) && ed.plugins) + ed.plugins.spellchecker.selectedLang = lang; + else if (this.env.spellcheck) + this.env.spellcheck.setCurrentLanguage(lang); }; // resume spellchecking, highlight provided mispellings without new ajax request @@ -3083,6 +3213,8 @@ sp.prepare(false, true); sp.processData(data); } + + this.spellcheck_state(); } this.set_draft_id = function(id) @@ -3310,10 +3442,10 @@ ts = frame_name.replace(/^rcmupload/, ''); if (this.env.loadingicon) - content = '<img src="'+this.env.loadingicon+'" alt="" />'+content; + content = '<img src="'+this.env.loadingicon+'" alt="" class="uploading" />'+content; if (this.env.cancelicon) - content = '<a title="'+this.get_label('cancel')+'" onclick="return rcmail.cancel_attachment_upload(\''+ts+'\', \''+frame_name+'\');" href="#cancelupload"><img src="'+this.env.cancelicon+'" alt="" /></a>'+content; - this.add2attachment_list(ts, { name:'', html:content, complete:false }); + content = '<a title="'+this.get_label('cancel')+'" onclick="return rcmail.cancel_attachment_upload(\''+ts+'\', \''+frame_name+'\');" href="#cancelupload" class="cancelupload"><img src="'+this.env.cancelicon+'" alt="" /></a>'+content; + this.add2attachment_list(ts, { name:'', html:content, classname:'uploading', complete:false }); // upload progress support if (this.env.upload_progress_time) { @@ -3333,7 +3465,7 @@ if (!this.gui_objects.attachmentlist) return false; - var indicator, li = $('<li>').attr('id', name).html(att.html); + var indicator, li = $('<li>').attr('id', name).addClass(att.classname).html(att.html); // replace indicator's li if (upload_id && (indicator = document.getElementById(upload_id))) { @@ -3353,16 +3485,8 @@ this.remove_from_attachment_list = function(name) { - if (this.env.attachments[name]) - delete this.env.attachments[name]; - - if (!this.gui_objects.attachmentlist) - return false; - - var list = this.gui_objects.attachmentlist.getElementsByTagName("li"); - for (i=0; i<list.length; i++) - if (list[i].id == name) - this.gui_objects.attachmentlist.removeChild(list[i]); + delete this.env.attachments[name]; + $('#'+name).remove(); }; this.remove_attachment = function(name) @@ -3590,13 +3714,12 @@ // insert all members of a group if (typeof this.env.contacts[id] === 'object' && this.env.contacts[id].id) { - insert += this.env.contacts[id].name + ', '; - this.group2expand = $.extend({}, this.env.contacts[id]); - this.group2expand.input = this.ksearch_input; + 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='+urlencode(this.env.contacts[id].source)+'&_gid='+urlencode(this.env.contacts[id].id), false); } else if (typeof this.env.contacts[id] === 'string') { - insert = this.env.contacts[id] + ', '; + insert = this.env.contacts[id] + this.env.recipients_delimiter; trigger = true; } @@ -3613,10 +3736,10 @@ this.replace_group_recipients = function(id, recipients) { - if (this.group2expand && this.group2expand.id == id) { - this.group2expand.input.value = this.group2expand.input.value.replace(this.group2expand.name, recipients); - this.triggerEvent('autocomplete_insert', { field:this.group2expand.input, insert:recipients }); - this.group2expand = null; + if (this.group2expand[id]) { + 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; } }; @@ -3633,7 +3756,7 @@ // get string from current cursor pos to last comma var cpos = this.get_caret_pos(this.ksearch_input), - p = inp_value.lastIndexOf(',', cpos-1), + p = inp_value.lastIndexOf(this.env.recipients_separator, cpos-1), q = inp_value.substring(p+1, cpos), min = this.env.autocomplete_min_length, ac = this.ksearch_data; @@ -3699,7 +3822,7 @@ return; // display search results - var ul, li, text, init, + var i, len, ul, li, text, init, value = this.ksearch_value, data = this.ksearch_data, maxlen = this.env.autocomplete_max ? this.env.autocomplete_max : 15; @@ -3730,8 +3853,8 @@ } // add each result line to list - if (results && results.length) { - for (i=0; i < results.length && maxlen > 0; i++) { + if (results && (len = results.length)) { + for (i=0; i < len && maxlen > 0; i++) { text = typeof results[i] === 'object' ? results[i].name : results[i]; li = document.createElement('LI'); li.innerHTML = text.replace(new RegExp('('+RegExp.escape(value)+')', 'ig'), '##$1%%').replace(/</g, '<').replace(/>/g, '>').replace(/##([^%]+)%%/g, '<b>$1</b>'); @@ -3752,7 +3875,7 @@ } } - if (results && results.length) + if (len) this.env.contacts = this.env.contacts.concat(results); // run next parallel search @@ -3955,7 +4078,7 @@ if (this.env.search_request) url += '&_search='+this.env.search_request; - this.http_request('list', url, lock); + this.http_request(this.env.task == 'mail' ? 'list-contacts' : 'list', url, lock); }; this.list_contacts_clear = function() @@ -4035,10 +4158,10 @@ this.delete_contacts = function() { - // exit if no mailbox specified or if selection is empty var selection = this.contact_list.get_selection(), - undelete = this.env.address_sources[this.env.source].undelete; + undelete = this.env.source && this.env.address_sources[this.env.source].undelete; + // exit if no mailbox specified or if selection is empty if (!(selection.length || this.env.cid) || (!undelete && !confirm(this.get_label('deletecontactconfirm')))) return; @@ -4079,7 +4202,7 @@ { var c, row, list = this.contact_list; - cid = String(cid).replace(this.identifier_expr, '_'); + cid = this.html_identifier(cid); // when in searching mode, concat cid with the source name if (!list.rows[cid]) { @@ -4095,7 +4218,7 @@ // cid change if (newcid) { - newcid = String(newcid).replace(this.identifier_expr, '_'); + newcid = this.html_identifier(newcid); row.id = 'rcmrow' + newcid; list.remove_row(cid); list.init_row(row); @@ -4114,7 +4237,7 @@ var c, list = this.contact_list, row = document.createElement('tr'); - row.id = 'rcmrow'+String(cid).replace(this.identifier_expr, '_'); + row.id = 'rcmrow'+this.html_identifier(cid); row.className = 'contact'; if (list.in_selection(cid)) @@ -4161,7 +4284,6 @@ yearRange: '-100:+10', showOtherMonths: true, selectOtherMonths: true, - monthNamesShort: this.env.month_names, onSelect: function(dateText) { $(this).focus().val(dateText) } }); $('input.datepicker').datepicker(); @@ -4296,7 +4418,7 @@ .attr('rel', prop.source+':'+prop.id) .click(function() { return rcmail.command('listgroup', prop, this); }) .html(prop.name), - li = $('<li>').attr({id: 'rcmli'+key.replace(this.identifier_expr, '_'), 'class': 'contactgroup'}) + li = $('<li>').attr({id: 'rcmli'+this.html_identifier(key), 'class': 'contactgroup'}) .append(link); this.env.contactfolders[key] = this.env.contactgroups[key] = prop; @@ -4319,7 +4441,7 @@ var newkey = 'G'+prop.source+prop.newid, newprop = $.extend({}, prop);; - li.id = String('rcmli'+newkey).replace(this.identifier_expr, '_'); + li.id = 'rcmli' + this.html_identifier(newkey); this.env.contactfolders[newkey] = this.env.contactfolders[key]; this.env.contactfolders[newkey].id = prop.newid; this.env.group = prop.newid; @@ -4351,7 +4473,7 @@ { var row, name = prop.name.toUpperCase(), sibling = this.get_folder_li(prop.source), - prefix = 'rcmliG'+(prop.source).replace(this.identifier_expr, '_'); + prefix = 'rcmliG' + this.html_identifier(prop.source); // When renaming groups, we need to remove it from DOM and insert it in the proper place if (reloc) { @@ -4385,7 +4507,7 @@ elem.focus(function(){ ref.focus_textfield(this); }) .blur(function(){ ref.blur_textfield(this); }) - .each(function(){ this._placeholder = this.title = ref.env.coltypes[col].label; ref.blur_textfield(this); }); + .each(function(){ this._placeholder = this.title = (ref.env.coltypes[col].label || ''); ref.blur_textfield(this); }); }; this.insert_edit_field = function(col, section, menu) @@ -4422,7 +4544,7 @@ .appendTo(cell); this.init_edit_field(col, input); - + if (colprop.type == 'date' && $.datepicker) input.datepicker(); } @@ -4544,7 +4666,7 @@ { var n, buttons = this.buttons['upload-photo']; for (n=0; buttons && n < buttons.length; n++) - $('#'+buttons[n].id).html(this.get_label(id == '-del-' ? 'addphoto' : 'replacephoto')); + $('a#'+buttons[n].id).html(this.get_label(id == '-del-' ? 'addphoto' : 'replacephoto')); $('#ff_photo').val(id); this.enable_command('upload-photo', this.env.coltypes.photo ? true : false); @@ -4584,7 +4706,7 @@ .attr('rel', id) .click(function() { return rcmail.command('listsearch', id, this); }) .html(name), - li = $('<li>').attr({id: 'rcmli'+key.replace(this.identifier_expr, '_'), 'class': 'contactsearch'}) + li = $('<li>').attr({id: 'rcmli' + this.html_identifier(key), 'class': 'contactsearch'}) .append(link), prop = {name:name, id:id, li:li[0]}; @@ -4696,14 +4818,16 @@ this.identity_select = function(list) { var id; - if (id = list.get_single_selection()) + if (id = list.get_single_selection()) { + this.enable_command('delete', list.rowcount > 1 && this.env.identities_level < 2); this.load_identity(id, 'edit-identity'); + } }; // load identity record this.load_identity = function(id, action) { - if (action=='edit-identity' && (!id || id==this.env.iid)) + if (action == 'edit-identity' && (!id || id == this.env.iid)) return false; var add_url = '', target = window; @@ -4714,7 +4838,7 @@ document.getElementById(this.env.contentframe).style.visibility = 'inherit'; } - if (action && (id || action=='add-identity')) { + if (action && (id || action == 'add-identity')) { this.set_busy(true); this.location_href(this.env.comm_path+'&_action='+action+'&_iid='+id+add_url, target); } @@ -4724,7 +4848,7 @@ this.delete_identity = function(id) { - // exit if no mailbox specified or if selection is empty + // exit if no identity is specified or if selection is empty var selection = this.identity_list.get_selection(); if (!(selection.length || this.env.iid)) return; @@ -4732,10 +4856,27 @@ if (!id) id = this.env.iid ? this.env.iid : selection[0]; - // append token to request - this.goto_url('delete-identity', '_iid='+id+'&_token='+this.env.request_token, true); + // 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; + }; + + this.update_identity_row = function(id, name, add) + { + var row, col, list = this.identity_list, + rid = this.html_identifier(id); + + if (list.rows[rid] && (row = list.rows[rid].obj)) { + $(row.cells[0]).html(name); + } + else if (add) { + row = $('<tr>').attr('id', 'rcmrow'+rid).get(0); + col = $('<td>').addClass('mail').html(name).appendTo(row); + list.insert_row(row); + list.select(rid); + } }; @@ -4856,7 +4997,7 @@ if (!this.gui_objects.subscriptionlist) return false; - var row, n, i, tmp, folders, rowid, list = [], slist = [], + var row, n, i, tmp, tmp_name, folders, rowid, list = [], slist = [], tbody = this.gui_objects.subscriptionlist.tBodies[0], refrow = $('tr', tbody).get(1), id = 'rcmrow'+((new Date).getTime()); @@ -4892,8 +5033,12 @@ for (n in folders) { // protected folder if (folders[n][2]) { + tmp_name = folders[n][0] + this.env.delimiter; + // prefix namespace cannot have subfolders (#1488349) + if (tmp_name == this.env.prefix_ns) + continue; slist.push(folders[n][0]); - tmp = folders[n][0]+this.env.delimiter; + tmp = tmp_name; } // protected folder's child else if (tmp && folders[n][0].indexOf(tmp) == 0) @@ -5155,17 +5300,18 @@ init_button(cmd, this.buttons[cmd][i]); } } + + // set active task button + this.set_button(this.task, 'sel'); }; // set button to a specific state this.set_button = function(command, state) { - var button, obj, a_buttons = this.buttons[command]; + var n, button, obj, a_buttons = this.buttons[command], + len = a_buttons ? a_buttons.length : 0; - if (!a_buttons || !a_buttons.length) - return false; - - for (var n=0; n<a_buttons.length; n++) { + for (n=0; n<len; n++) { button = a_buttons[n]; obj = document.getElementById(button.id); @@ -5200,15 +5346,14 @@ // display a specific alttext this.set_alttext = function(command, label) { - if (!this.buttons[command] || !this.buttons[command].length) - return; + var n, button, obj, link, a_buttons = this.buttons[command], + len = a_buttons ? a_buttons.length : 0; - var button, obj, link; - for (var n=0; n<this.buttons[command].length; n++) { - button = this.buttons[command][n]; + for (n=0; n<len; n++) { + button = a_buttons[n]; obj = document.getElementById(button.id); - if (button.type=='image' && obj) { + if (button.type == 'image' && obj) { obj.setAttribute('alt', this.get_label(label)); if ((link = obj.parentNode) && link.tagName.toLowerCase() == 'a') link.setAttribute('title', this.get_label(label)); @@ -5221,20 +5366,18 @@ // mouse over button this.button_over = function(command, id) { - var button, elm, a_buttons = this.buttons[command]; + var n, button, obj, a_buttons = this.buttons[command], + len = a_buttons ? a_buttons.length : 0; - if (!a_buttons || !a_buttons.length) - return false; - - for (var n=0; n<a_buttons.length; n++) { + for (n=0; n<len; n++) { button = a_buttons[n]; if (button.id == id && button.status == 'act') { - elm = document.getElementById(button.id); - if (elm && button.over) { + obj = document.getElementById(button.id); + if (obj && button.over) { if (button.type == 'image') - elm.src = button.over; + obj.src = button.over; else - elm.className = button.over; + obj.className = button.over; } } } @@ -5243,20 +5386,18 @@ // mouse down on button this.button_sel = function(command, id) { - var button, elm, a_buttons = this.buttons[command]; + var n, button, obj, a_buttons = this.buttons[command], + len = a_buttons ? a_buttons.length : 0; - if (!a_buttons || !a_buttons.length) - return; - - for (var n=0; n<a_buttons.length; n++) { + for (n=0; n<len; n++) { button = a_buttons[n]; if (button.id == id && button.status == 'act') { - elm = document.getElementById(button.id); - if (elm && button.sel) { + obj = document.getElementById(button.id); + if (obj && button.sel) { if (button.type == 'image') - elm.src = button.sel; + obj.src = button.sel; else - elm.className = button.sel; + obj.className = button.sel; } this.buttons_sel[id] = command; } @@ -5266,25 +5407,22 @@ // mouse out of button this.button_out = function(command, id) { - var button, elm, a_buttons = this.buttons[command]; + var n, button, obj, a_buttons = this.buttons[command], + len = a_buttons ? a_buttons.length : 0; - if (!a_buttons || !a_buttons.length) - return; - - for (var n=0; n<a_buttons.length; n++) { + for (n=0; n<len; n++) { button = a_buttons[n]; if (button.id == id && button.status == 'act') { - elm = document.getElementById(button.id); - if (elm && button.act) { + obj = document.getElementById(button.id); + if (obj && button.act) { if (button.type == 'image') - elm.src = button.act; + obj.src = button.act; else - elm.className = button.act; + obj.className = button.act; } } } }; - this.focus_textfield = function(elem) { @@ -5319,14 +5457,14 @@ if (!this.gui_objects.message) { // save message in order to display after page loaded if (type != 'loading') - this.pending_message = new Array(msg, type, timeout); + this.pending_message = [msg, type, timeout]; return false; } type = type ? type : 'notice'; var ref = this, - key = String(msg).replace(this.identifier_expr, '_'), + key = this.html_identifier(msg), date = new Date(), id = type + date.getTime(); @@ -5367,6 +5505,8 @@ else { obj.click(function() { return ref.hide_message(obj); }); } + + this.triggerEvent('message', { message:msg, type:type, timeout:timeout, object:obj }); if (timeout > 0) window.setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout); @@ -5418,8 +5558,25 @@ } }; + // remove all messages immediately + this.clear_messages = function() + { + // pass command to parent window + if (this.is_framed()) + return parent.rcmail.clear_messages(); + + var k, n, m = this.messages; + + for (k in m) + for (n in m[k].elements) + if (m[k].obj) + m[k].obj.hide(); + + this.messages = {}; + }; + // mark a mailbox as selected and set environment variable - this.select_folder = function(name, prefix) + this.select_folder = function(name, prefix, encode) { if (this.gui_objects.folderlist) { var current_li, target_li; @@ -5427,7 +5584,7 @@ if ((current_li = $('li.selected', this.gui_objects.folderlist))) { current_li.removeClass('selected').addClass('unfocused'); } - if ((target_li = this.get_folder_li(name, prefix))) { + if ((target_li = this.get_folder_li(name, prefix, encode))) { $(target_li).removeClass('unfocused').addClass('selected'); } @@ -5436,14 +5593,26 @@ } }; + // adds a class to selected folder + this.mark_folder = function(name, class_name, prefix, encode) + { + $(this.get_folder_li(name, prefix, encode)).addClass(class_name); + }; + + // adds a class to selected folder + this.unmark_folder = function(name, class_name, prefix, encode) + { + $(this.get_folder_li(name, prefix, encode)).removeClass(class_name); + }; + // helper method to find a folder list item - this.get_folder_li = function(name, prefix) + this.get_folder_li = function(name, prefix, encode) { if (!prefix) prefix = 'rcmli'; if (this.gui_objects.folderlist) { - name = String(name).replace(this.identifier_expr, '_'); + name = this.html_identifier(name, encode); return document.getElementById(prefix+name); } @@ -5534,22 +5703,26 @@ // replace content of quota display this.set_quota = function(content) { - if (content && this.gui_objects.quotadisplay) { - if (typeof content === 'object' && content.type == 'image') - this.percent_indicator(this.gui_objects.quotadisplay, content); - else - $(this.gui_objects.quotadisplay).html(content); - } + if (this.gui_objects.quotadisplay && content && content.type == 'text') + $(this.gui_objects.quotadisplay).html(content.percent+'%').attr('title', content.title); + + this.triggerEvent('setquota', content); + this.env.quota_content = content; }; // update the mailboxlist - this.set_unread_count = function(mbox, count, set_title) + this.set_unread_count = function(mbox, count, set_title, mark) { if (!this.gui_objects.mailboxlist) return false; this.env.unread_counts[mbox] = count; this.set_unread_count_display(mbox, set_title); + + if (mark) + this.mark_folder(mbox, mark, '', true); + else if (!count) + this.unmark_folder(mbox, 'recent', '', true); }; // update the mailbox count display @@ -5557,7 +5730,7 @@ { var reg, link, text_obj, item, mycount, childcount, div; - if (item = this.get_folder_li(mbox)) { + if (item = this.get_folder_li(mbox, '', true)) { mycount = this.env.unread_counts[mbox] ? this.env.unread_counts[mbox] : 0; link = $(item).children('a').eq(0); text_obj = link.children('span.unreadcount'); @@ -5569,13 +5742,13 @@ if ((div = item.getElementsByTagName('div')[0]) && div.className.match(/collapsed/)) { // add children's counters - for (var k in this.env.unread_counts) + for (var k in this.env.unread_counts) if (k.indexOf(mbox + this.env.delimiter) == 0) childcount += this.env.unread_counts[k]; } if (mycount && text_obj.length) - text_obj.html(' ('+mycount+')'); + text_obj.html(this.env.unreadwrap.replace(/%[sd]/, mycount)); else if (text_obj.length) text_obj.remove(); @@ -5606,20 +5779,6 @@ this.set_pagetitle(new_title); } - }; - - this.toggle_prefer_html = function(checkbox) - { - var elem; - if (elem = document.getElementById('rcmfd_addrbook_show_images')) - elem.disabled = !checkbox.checked; - }; - - this.toggle_preview_pane = function(checkbox) - { - var elem; - if (elem = document.getElementById('rcmfd_preview_pane_mark_read')) - elem.disabled = !checkbox.checked; }; // display fetched raw headers @@ -5657,69 +5816,6 @@ elem.onclick = function() { rcmail.load_headers(elem); }; }; - // percent (quota) indicator - this.percent_indicator = function(obj, data) - { - if (!data || !obj) - return false; - - var limit_high = 80, - limit_mid = 55, - width = data.width ? data.width : this.env.indicator_width ? this.env.indicator_width : 100, - height = data.height ? data.height : this.env.indicator_height ? this.env.indicator_height : 14, - quota = data.percent ? Math.abs(parseInt(data.percent)) : 0, - quota_width = parseInt(quota / 100 * width), - pos = $(obj).position(); - - // workarounds for Opera and Webkit bugs - pos.top = Math.max(0, pos.top); - pos.left = Math.max(0, pos.left); - - this.env.indicator_width = width; - this.env.indicator_height = height; - - // overlimit - if (quota_width > width) { - quota_width = width; - quota = 100; - } - - if (data.title) - data.title = this.get_label('quota') + ': ' + data.title; - - // main div - var main = $('<div>'); - main.css({position: 'absolute', top: pos.top, left: pos.left, - width: width + 'px', height: height + 'px', zIndex: 100, lineHeight: height + 'px'}) - .attr('title', data.title).addClass('quota_text').html(quota + '%'); - // used bar - var bar1 = $('<div>'); - bar1.css({position: 'absolute', top: pos.top + 1, left: pos.left + 1, - width: quota_width + 'px', height: height + 'px', zIndex: 99}); - // background - var bar2 = $('<div>'); - bar2.css({position: 'absolute', top: pos.top + 1, left: pos.left + 1, - width: width + 'px', height: height + 'px', zIndex: 98}) - .addClass('quota_bg'); - - if (quota >= limit_high) { - main.addClass(' quota_text_high'); - bar1.addClass('quota_high'); - } - else if(quota >= limit_mid) { - main.addClass(' quota_text_mid'); - bar1.addClass('quota_mid'); - } - else { - main.addClass(' quota_text_low'); - bar1.addClass('quota_low'); - } - - // replace quota image - $(obj).html('').append(bar1).append(bar2).append(main); - // update #quotaimg title - $('#quotaimg').attr('title', data.title); - }; /********************************************************/ /********* html to text conversion functions *********/ @@ -5735,14 +5831,17 @@ $.ajax({ type: 'POST', url: url, data: htmlText, contentType: 'application/octet-stream', error: function(o, status, err) { rcmail.http_error(o, status, err, lock); }, - success: function(data) { rcmail.set_busy(false, null, lock); $(document.getElementById(id)).val(data); rcmail.log(data); } + success: function(data) { rcmail.set_busy(false, null, lock); $('#'+id).val(data); rcmail.log(data); } }); }; - this.plain2html = function(plainText, id) + this.plain2html = function(plain, id) { var lock = this.set_busy(true, 'converting'); - $(document.getElementById(id)).val('<pre>'+plainText+'</pre>'); + + plain = plain.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); + $('#'+id).val(plain ? '<pre>'+plain+'</pre>' : ''); + this.set_busy(false, null, lock); }; @@ -5974,7 +6073,7 @@ this.enable_command('purge', this.purge_mailbox_test()); this.enable_command('expand-all', 'expand-unread', 'collapse-all', this.env.threading && this.env.messagecount); - if (response.action == 'list' || response.action == 'search') { + if ((response.action == 'list' || response.action == 'search') && this.message_list) { this.msglist_select(this.message_list); this.triggerEvent('listupdate', { folder:this.env.mailbox, rowcount:this.message_list.rowcount }); } @@ -6186,6 +6285,32 @@ } }; + this.mailto_handler_uri = function() + { + return location.href.split('?')[0] + '?_task=mail&_action=compose&_to=%s'; + }; + + this.register_protocol_handler = function(name) + { + try { + window.navigator.registerProtocolHandler('mailto', this.mailto_handler_uri(), name); + } + catch(e) {}; + }; + + this.check_protocol_handler = function(name, elem) + { + var nav = window.navigator; + if (!nav + || (typeof nav.registerProtocolHandler != 'function') + || ((typeof nav.isProtocolHandlerRegistered == 'function') + && nav.isProtocolHandlerRegistered('mailto', this.mailto_handler_uri()) == 'registered') + ) + $(elem).addClass('disabled'); + else + $(elem).click(function() { rcmail.register_protocol_handler(name); return false; }); + }; + } // end object rcube_webmail -- Gitblit v1.9.1