program/include/rcmail.php | ●●●●● patch | view | raw | blame | history | |
program/js/app.js | ●●●●● patch | view | raw | blame | history | |
program/js/editor.js | ●●●●● patch | view | raw | blame | history | |
program/steps/mail/compose.inc | ●●●●● patch | view | raw | blame | history | |
program/steps/mail/sendmail.inc | ●●●●● patch | view | raw | blame | history | |
program/steps/settings/edit_identity.inc | ●●●●● patch | view | raw | blame | history |
program/include/rcmail.php
@@ -1786,18 +1786,18 @@ $lang = 'en'; } $script = json_encode(array( $config = array( 'mode' => $mode, 'lang' => $lang, 'skin_path' => $this->output->get_skin_path(), 'spellcheck' => intval($this->config->get('enable_spellcheck')), 'spelldict' => intval($this->config->get('spellcheck_dictionary')) )); ); $this->output->add_label('selectimage', 'addimage'); $this->output->set_env('editor_config', $config); $this->output->include_script('tinymce/tinymce.min.js'); $this->output->include_script('editor.js'); $this->output->add_script("rcmail_editor_init($script)", 'docready'); } /** program/js/app.js
@@ -286,11 +286,16 @@ // 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.spellcheck_state(); }; if (window.googie) { this.env.editor_config.spellchecker = googie; this.env.editor_config.spellcheck_observer = function(s) { ref.spellcheck_state(); }; this.env.compose_commands.push('spellcheck') this.enable_command('spellcheck', true); } // initialize HTML editor this.editor_init(this.env.editor_config, this.env.composebody); // init canned response functions if (this.gui_objects.responseslist) { @@ -426,6 +431,9 @@ 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); // initialize HTML editor this.editor_init(this.env.editor_config, 'rcmfd_signature'); } else if (this.env.action == 'folders') { this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', true); @@ -1040,17 +1048,11 @@ case 'spellcheck': if (this.spellcheck_state()) { this.stop_spellchecking(); this.editor.spellcheck_stop(); } else { if (window.tinymce && tinymce.get(this.env.composebody)) { tinymce.execCommand('mceSpellCheck', true); this.editor.spellcheck_start(); } else if (this.env.spellcheck && this.env.spellcheck.spellCheck) { this.env.spellcheck.spellCheck(); } } this.spellcheck_state(); break; case 'savedraft': @@ -1272,7 +1274,7 @@ default: var func = command.replace(/-/g, '_'); if (this[func] && typeof this[func] === 'function') { ret = this[func](props, obj); ret = this[func](props, obj, event); } break; } @@ -3158,7 +3160,7 @@ if (!this.gui_objects.messageform) return false; var input_from = $("[name='_from']"), var i, input_from = $("[name='_from']"), input_to = $("[name='_to']"), input_subject = $("input[name='_subject']"), input_message = $("[name='_message']").get(0), @@ -3187,7 +3189,7 @@ // init live search events this.init_address_input_events(input_to, ac_props); for (var i in ac_fields) { for (i in ac_fields) { this.init_address_input_events($("[name='_"+ac_fields[i]+"']"), ac_props); } @@ -3202,10 +3204,11 @@ // check for locally stored compose data if (window.localStorage) { var index = this.local_storage_get_item('compose.index', []); var key, formdata, 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); for (i = 0; i < index.length; i++) { key = index[i]; formdata = this.local_storage_get_item('compose.' + key, null, true); if (!formdata) { continue; } @@ -3356,12 +3359,11 @@ this.check_compose_input = function(cmd) { // check input fields var ed, input_to = $("[name='_to']"), var input_to = $("[name='_to']"), input_cc = $("[name='_cc']"), input_bcc = $("[name='_bcc']"), input_from = $("[name='_from']"), input_subject = $("[name='_subject']"), input_message = $("[name='_message']"); input_subject = $("[name='_subject']"); // check sender (if have no identities) if (input_from.prop('type') == 'text' && !rcube_check_email(input_from.val(), true)) { @@ -3388,10 +3390,12 @@ // display localized warning for missing subject if (input_subject.val() == '') { 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')); var buttons = {}, myprompt = $('<div class="prompt">').html('<div class="message">' + this.get_label('nosubjectwarning') + '</div>') .appendTo(document.body), prompt_value = $('<input>').attr({type: 'text', size: 30}).val(this.get_label('nosubject')) .appendTo(myprompt); var buttons = {}; buttons[this.get_label('cancel')] = function(){ input_subject.focus(); $(this).dialog('close'); @@ -3408,100 +3412,31 @@ buttons: buttons, close: function(event, ui) { $(this).remove() } }); prompt_value.select(); return false; } // Apply spellcheck changes if spell checker is active this.stop_spellchecking(); if (window.tinymce) ed = tinymce.get(this.env.composebody); // check for empty body if (!ed && input_message.val() == '' && !confirm(this.get_label('nobodywarning'))) { input_message.focus(); if (!this.editor.get_content() && !confirm(this.get_label('nobodywarning'))) { this.editor.focus(); return false; } else if (ed) { if (!ed.getContent() && !confirm(this.get_label('nobodywarning'))) { ed.focus(); return false; } // move body from html editor to textarea (just to be sure, #1485860) tinymce.triggerSave(); } this.editor.save(); return true; }; this.toggle_editor = function(props) this.toggle_editor = function(props, obj, e) { this.stop_spellchecking(); // @todo: this should work also with many editors on page var result = this.editor.toggle(props.html); var ed, curr, content, result, // these non-printable chars are not removed on text2html and html2text // we can use them as temp signature replacement sig_mark = "\u0002\u0003", input = $('#' + props.id), signature = this.env.identity ? this.env.signatures[this.env.identity] : null, is_sig = signature && signature.text && signature.text.length > 1; if (props.mode == 'html') { content = input.val(); // replace current text signature with temp mark if (is_sig) content = content.replace(signature.text, sig_mark); // convert to html result = this.plain2html(content, function(data) { // replace signature mark with html version of the signature if (is_sig) data = data.replace(sig_mark, '<div id="_rc_sig">' + signature.html + '</div>'); input.val(data); tinyMCE.execCommand('mceAddEditor', false, props.id); if (ref.env.default_font) setTimeout(function() { $(tinyMCE.get(props.id).getBody()).css('font-family', ref.env.default_font); }, 500); }); } else { ed = tinyMCE.get(props.id); if (is_sig) { // get current version of signature, we'll need it in // case of html2text conversion abort if (curr = ed.dom.get('_rc_sig')) curr = curr.innerHTML; // replace current signature with some non-printable characters // we use non-printable characters, because this replacement // is visible to the user // doing this after getContent() would be hard ed.dom.setHTML('_rc_sig', sig_mark); } // get html content content = ed.getContent(); // convert html to text result = this.html2plain(content, function(data) { tinyMCE.execCommand('mceRemoveEditor', false, props.id); // replace signture mark with text version of the signature if (is_sig) data = data.replace(sig_mark, "\n" + signature.text); input.val(data); }); // bring back current signature if (!result && curr) ed.dom.setHTML('_rc_sig', curr); if (!result && e) { // fix selector value if operation failed $(e.target).filter('select').val(props.html ? 'plain' : 'html'); } return result; @@ -3510,30 +3445,11 @@ this.insert_response = function(key) { var insert = this.env.textresponses[key] ? this.env.textresponses[key].text : null; if (!insert) return false; // insert into tinymce editor if ($("input[name='_is_html']").val() == '1') { var editor = tinymce.get(this.env.composebody); editor.getWin().focus(); // correct focus in IE & Chrome editor.selection.setContent(this.quote_html(insert).replace(/\r?\n/g, '<br/>'), { format:'text' }); } // replace selection in compose textarea else { var textarea = rcube_find_object(this.env.composebody), selection = $(textarea).is(':focus') ? this.get_input_selection(textarea) : { start:0, end:0 }, inp_value = textarea.value; pre = inp_value.substring(0, selection.start), end = inp_value.substring(selection.end, inp_value.length); // insert response text textarea.value = pre + insert + end; // set caret after inserted text this.set_caret_pos(textarea, selection.start + insert.length); textarea.focus(); } this.editor.replace(insert); }; /** @@ -3541,42 +3457,8 @@ */ this.save_response = function() { var sigstart, text = '', strip = false; // get selected text from tinymce editor if ($("input[name='_is_html']").val() == '1') { var editor = tinymce.get(this.env.composebody); editor.getWin().focus(); // correct focus in IE & Chrome text = editor.selection.getContent({ format:'text' }); if (!text) { text = editor.getContent({ format:'text' }); strip = true; } } // get selected text from compose textarea else { var textarea = rcube_find_object(this.env.composebody), sigstart; if (textarea && $(textarea).is(':focus')) { text = this.get_input_selection(textarea).text; } if (!text && textarea) { text = textarea.value; strip = true; } } // strip off signature if (strip) { sigstart = text.indexOf('-- \n'); if (sigstart > 0) { text = text.substring(0, sigstart); } } // show dialog to enter a name and to modify the text to be saved var buttons = {}, var buttons = {}, text = this.editor.get_content(true, 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>' + @@ -3655,30 +3537,10 @@ return false; }; this.stop_spellchecking = function() { var ed; if (window.tinymce && (ed = tinymce.get(this.env.composebody))) { if (ed.plugins && ed.plugins.spellchecker && this.env.spellcheck_active) ed.execCommand('mceSpellCheck'); } 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(); }; // updates spellchecker buttons on state change this.spellcheck_state = function() { var ed, active; if (window.tinymce && (ed = tinymce.get(this.env.composebody))) active = this.env.spellcheck_active; else if ((ed = this.env.spellcheck) && ed.state) active = ed.state != 'ready' && ed.state != 'no_error_found'; var active = this.editor.spellcheck_state(); if (this.buttons.spellcheck) $('#'+this.buttons.spellcheck[0].id)[active ? 'addClass' : 'removeClass']('selected'); @@ -3689,41 +3551,19 @@ // get selected language this.spellcheck_lang = function() { var ed; if (window.tinymce && (ed = tinymce.get(this.env.composebody))) return ed.settings.spellchecker_language || this.env.spell_lang; else if (this.env.spellcheck) return GOOGIE_CUR_LANG; return this.editor.get_language(); }; this.spellcheck_lang_set = function(lang) { var ed; if (window.tinymce && (ed = tinymce.get(this.env.composebody))) ed.settings.spellchecker_language = lang; else if (this.env.spellcheck) this.env.spellcheck.setCurrentLanguage(lang); this.editor.set_language(lang); }; // resume spellchecking, highlight provided mispellings without new ajax request this.spellcheck_resume = function(ishtml, data) this.spellcheck_resume = function(data) { if (ishtml) { var ed = tinymce.get(this.env.composebody); ed.settings.spellchecker_callback = function(name, text, done, error) { done(data); }; ed.execCommand('mceSpellCheck'); ed.settings.spellchecker_callback = null; } else { var sp = this.env.spellcheck; sp.prepare(false, true); sp.processData(data); } this.spellcheck_state(); } this.editor.spellcheck_resume(data); }; this.set_draft_id = function(id) { @@ -3783,16 +3623,13 @@ this.compose_field_hash = function(save) { // check input fields var ed, i, id, val, str = '', hash_fields = ['to', 'cc', 'bcc', 'subject']; var i, id, val, str = '', hash_fields = ['to', 'cc', 'bcc', 'subject']; for (i=0; i<hash_fields.length; i++) if (val = $('[name="_' + hash_fields[i] + '"]').val()) str += val + ':'; if (window.tinymce && (ed = tinymce.get(this.env.composebody))) str += ed.getContent(); else str += $("[name='_message']").val(); str += this.editor.get_content(); if (this.env.attachments) for (id in this.env.attachments) @@ -3811,9 +3648,7 @@ ed, empty = true; // get fresh content from editor if (window.tinymce && (ed = tinymce.get(this.env.composebody))) { tinymce.triggerSave(); } this.editor.save(); if (this.env.draft_id) { formdata.draft_id = this.env.draft_id; @@ -3876,15 +3711,8 @@ }); // initialize HTML editor if (formdata._is_html == '1') { if (!html_mode) { tinymce.execCommand('mceAddEditor', false, this.env.composebody); this.triggerEvent('aftertoggle-editor', { mode:'html' }); } } else if (html_mode) { tinymce.execCommand('mceRemoveEditor', false, this.env.composebody); this.triggerEvent('aftertoggle-editor', { mode:'plain' }); if ((formdata._is_html == '1' && !html_mode) || (formdata._is_html != '1' && html_mode)) { this.command('toggle-editor', {id: this.env.composebody, html: !html_mode}); } } }; @@ -3933,11 +3761,8 @@ return; } var i, rx, cursor_pos, p = -1, var i, rx, id = obj.options[obj.selectedIndex].value, input_message = $("[name='_message']"), message = input_message.val(), is_html = ($("input[name='_is_html']").val() == '1'), sig = this.env.identity, delim = this.env.recipients_separator, rx_delim = RegExp.escape(delim), @@ -3984,89 +3809,7 @@ else this.enable_command('insert-sig', false); if (!is_html) { // remove the 'old' signature if (show_sig && sig && this.env.signatures && this.env.signatures[sig]) { sig = this.env.signatures[sig].text; sig = sig.replace(/\r\n/g, '\n'); p = this.env.top_posting ? message.indexOf(sig) : message.lastIndexOf(sig); if (p >= 0) message = message.substring(0, p) + message.substring(p+sig.length, message.length); } // add the new signature string if (show_sig && this.env.signatures && this.env.signatures[id]) { sig = this.env.signatures[id].text; sig = sig.replace(/\r\n/g, '\n'); if (this.env.top_posting) { if (p >= 0) { // in place of removed signature message = message.substring(0, p) + sig + message.substring(p, message.length); cursor_pos = p - 1; } else if (!message) { // empty message cursor_pos = 0; message = '\n\n' + sig; } else if (pos = this.get_caret_pos(input_message.get(0))) { // at cursor position message = message.substring(0, pos) + '\n' + sig + '\n\n' + message.substring(pos, message.length); cursor_pos = pos; } else { // on top cursor_pos = 0; message = '\n\n' + sig + '\n\n' + message.replace(/^[\r\n]+/, ''); } } else { message = message.replace(/[\r\n]+$/, ''); cursor_pos = !this.env.top_posting && message.length ? message.length+1 : 0; message += '\n\n' + sig; } } else cursor_pos = this.env.top_posting ? 0 : message.length; input_message.val(message); // move cursor before the signature this.set_caret_pos(input_message.get(0), cursor_pos); } else if (show_sig && this.env.signatures) { // html var editor = tinymce.get(this.env.composebody), sigElem = editor.dom.get('_rc_sig'); // Append the signature as a div within the body if (!sigElem) { var body = editor.getBody(), doc = editor.getDoc(); sigElem = doc.createElement('div'); sigElem.setAttribute('id', '_rc_sig'); if (this.env.top_posting) { // if no existing sig and top posting then insert at caret pos editor.getWin().focus(); // correct focus in IE & Chrome var node = editor.selection.getNode(); if (node.nodeName == 'BODY') { // no real focus, insert at start body.insertBefore(sigElem, body.firstChild); body.insertBefore(doc.createElement('br'), body.firstChild); } else { body.insertBefore(sigElem, node.nextSibling); body.insertBefore(doc.createElement('br'), node.nextSibling); } } else { body.appendChild(sigElem); } } if (this.env.signatures[id]) sigElem.innerHTML = this.env.signatures[id].html; } this.editor.change_signature(id, show_sig); this.env.identity = id; this.triggerEvent('change_identity'); return true; @@ -6864,6 +6607,12 @@ element.css({left: left + 'px', top: top + 'px'}); }; // initialize HTML editor this.editor_init = function(config, id) { this.editor = new rcube_text_editor(config, id); }; /********************************************************/ /********* html to text conversion functions *********/ @@ -7890,7 +7639,6 @@ { return localStorage.removeItem(this.get_local_storage_prefix() + key); }; } // end object rcube_webmail program/js/editor.js
@@ -26,13 +26,18 @@ * for the JavaScript code in this file. * * @author Eric Stadtherr <estadtherr@gmail.com> * @author Aleksander Machniak <alec@alec.pl> */ // Initialize HTML editor function rcmail_editor_init(config) /** * Roundcube Text Editor Widget class * @constructor */ function rcube_text_editor(config, id) { var ret, conf = { selector: '.mce_editor', var ref = this, conf = { selector: '#' + ($('#' + id).is('.mce_editor') ? id : 'fake-editor-id'), theme: 'modern', language: config.lang, content_css: config.skin_path + '/editor_content.css?v2', @@ -48,14 +53,26 @@ paste_data_images: true }; if (config.mode == 'identity') // register spellchecker for plain text editor this.spellcheck_observer = function() {}; if (config.spellchecker) { this.spellchecker = config.spellchecker; if (config.spellcheck_observer) { this.spellchecker.spelling_state_observer = this.spellcheck_observer = config.spellcheck_observer; } } // minimal editor if (config.mode == 'identity') { $.extend(conf, { plugins: ['autolink charmap code hr link paste tabfocus textcolor'], toolbar: 'bold italic underline alignleft aligncenter alignright alignjustify' + ' | outdent indent charmap hr link unlink code forecolor' + ' | fontselect fontsizeselect' }); else { // mail compose } // full-featured editor else { $.extend(conf, { plugins: ['autolink charmap code directionality emoticons link image media nonbreaking' + ' paste table tabfocus textcolor searchreplace' + (config.spellcheck ? ' spellchecker' : '')], @@ -65,34 +82,44 @@ spellchecker_rpc_url: '../../../../../?_task=utils&_action=spell_html&_remote=1', spellchecker_language: rcmail.env.spell_lang, accessibility_focus: false, file_browser_callback: rcmail_file_browser_callback, file_browser_callback: function(name, url, type, win) { ref.file_browser_callback(name, url, type); }, // @todo: support more than image (types: file, image, media) file_browser_callback_types: 'image' }); conf.setup = function(ed) { ed.on('init', rcmail_editor_callback); // add handler for spellcheck button state update ed.on('SpellcheckStart SpellcheckEnd', function(args) { rcmail.env.spellcheck_active = args.type == 'spellcheckstart'; rcmail.spellcheck_state(); }); ed.on('keypress', function() { rcmail.compose_type_activity++; }); } } // support external configuration settings e.g. from skin if (window.rcmail_editor_settings) $.extend(conf, window.rcmail_editor_settings); conf.setup = function(ed) { ed.on('init', function(ed) { ref.init_callback(ed); }); // add handler for spellcheck button state update ed.on('SpellcheckStart SpellcheckEnd', function(args) { ref.spellcheck_active = args.type == 'spellcheckstart'; ref.spellcheck_observer(); }); ed.on('keypress', function() { rcmail.compose_type_activity++; }); }; // textarea identifier this.id = id; // reference to active editor (if in HTML mode) this.editor = null; tinymce.init(conf); } // react to real individual tinyMCE editor init function rcmail_editor_callback() this.init_callback = function(event) { this.editor = event.target; if (rcmail.env.action != 'compose') { return; } var css = {}, elem = rcube_find_object('_from'), fe = rcmail.env.compose_focus_elem; @@ -104,7 +131,7 @@ css['font-size'] = rcmail.env.default_font_size; if (css['font-family'] || css['font-size']) $(tinymce.get(rcmail.env.composebody).getBody()).css(css); $(this.editor.getBody()).css(css); if (elem && elem.type == 'select-one') { // insert signature (only for the first time) @@ -112,7 +139,7 @@ rcmail.change_identity(elem); // Focus previously focused element if (fe && fe.id != rcmail.env.composebody) { if (fe && fe.id != this.id) { // use setTimeout() for IE9 (#1488541) window.setTimeout(function() { window.focus(); // for WebKit (#1486674) @@ -122,62 +149,372 @@ } // set tabIndex and set focus to element that was focused before rcmail_editor_tabindex(fe && fe.id == rcmail.env.composebody); this.tabindex(fe && fe.id == this.id); // Trigger resize (needed for proper editor resizing in some browsers) window.setTimeout(function() { $(window).resize(); }, 100); } }; // set tabIndex on tinymce editor function rcmail_editor_tabindex(focus) this.tabindex = function(focus) { if (rcmail.env.task == 'mail') { var editor = tinymce.get(rcmail.env.composebody); if (editor) { var textarea = editor.getElement(), node = editor.getContentAreaContainer().childNodes[0]; if (rcmail.env.task == 'mail' && this.editor) { var textarea = this.editor.getElement(), node = this.editor.getContentAreaContainer().childNodes[0]; if (textarea && node) node.tabIndex = textarea.tabIndex; if (focus) editor.getBody().focus(); this.editor.getBody().focus(); } } } }; // switch html/plain mode function rcmail_toggle_editor(select, textAreaId) this.toggle = function(ishtml) { var ishtml = select.tagName != 'SELECT' ? select.checked : select.value == 'html', res = rcmail.command('toggle-editor', {id: textAreaId, mode: ishtml ? 'html' : 'plain'}); var curr, content, result, // these non-printable chars are not removed on text2html and html2text // we can use them as temp signature replacement sig_mark = "\u0002\u0003", input = $('#' + this.id), signature = rcmail.env.identity ? rcmail.env.signatures[rcmail.env.identity] : null, is_sig = signature && signature.text && signature.text.length > 1; if (!res) { if (select.tagName == 'SELECT') select.value = 'html'; else if (select.tagName == 'INPUT') select.checked = true; } else if (ishtml) { // apply spellcheck changes if spell checker is active this.spellcheck_stop(); if (ishtml) { content = input.val(); // replace current text signature with temp mark if (is_sig) content = content.replace(signature.text, sig_mark); // convert to html result = rcmail.plain2html(content, function(data) { // replace signature mark with html version of the signature if (is_sig) data = data.replace(sig_mark, '<div id="_rc_sig">' + signature.html + '</div>'); input.val(data); tinymce.execCommand('mceAddEditor', false, ref.id); setTimeout(function() { if (ref.editor) { if (rcmail.env.default_font) $(ref.editor.getBody()).css('font-family', rcmail.env.default_font); // #1486593 setTimeout("rcmail_editor_tabindex(true);", 500); ref.tabindex(true); } else if (rcmail.env.composebody) { rcube_find_object(rcmail.env.composebody).focus(); }, 500); }); } else if (this.editor) { if (is_sig) { // get current version of signature, we'll need it in // case of html2text conversion abort if (curr = this.editor.dom.get('_rc_sig')) curr = curr.innerHTML; // replace current signature with some non-printable characters // we use non-printable characters, because this replacement // is visible to the user // doing this after getContent() would be hard this.editor.dom.setHTML('_rc_sig', sig_mark); } // get html content content = this.editor.getContent(); // convert html to text result = rcmail.html2plain(content, function(data) { tinymce.execCommand('mceRemoveEditor', false, ref.id); ref.editor = null; // replace signture mark with text version of the signature if (is_sig) data = data.replace(sig_mark, "\n" + signature.text); input.val(data).focus(); }); // bring back current signature if (!result && curr) this.editor.dom.setHTML('_rc_sig', curr); } return result; }; // start spellchecker this.spellcheck_start = function() { if (this.editor) { tinymce.execCommand('mceSpellCheck', true); this.spellcheck_observer(); } else if (this.spellchecker && this.spellchecker.spellCheck) { this.spellchecker.spellCheck(); } }; // stop spellchecker this.spellcheck_stop = function() { var ed = this.editor; if (ed) { if (ed.plugins && ed.plugins.spellchecker && this.spellcheck_active) ed.execCommand('mceSpellCheck'); this.spellcheck_observer(); } else if (ed = this.spellchecker) { if (ed.state && ed.state != 'ready' && ed.state != 'no_error_found') $(ed.spell_span).trigger('click'); } }; // spellchecker state this.spellcheck_state = function() { var ed; if (this.editor) return this.spellcheck_active; else if ((ed = this.spellchecker) && ed.state) return ed.state != 'ready' && ed.state != 'no_error_found'; }; // resume spellchecking, highlight provided mispellings without new ajax request this.spellcheck_resume = function(data) { var ed = this.editor; if (ed) { ed.settings.spellchecker_callback = function(name, text, done, error) { done(data); }; ed.execCommand('mceSpellCheck'); ed.settings.spellchecker_callback = null; this.spellcheck_observer(); } else if (ed = this.spellchecker) { ed.prepare(false, true); ed.processData(data); } }; // get selected (spellcheker) language this.get_language = function() { if (this.editor) { return this.editor.settings.spellchecker_language || rcmail.env.spell_lang; } else if (this.spellchecker) { return GOOGIE_CUR_LANG; } }; // set language for spellchecking this.set_language = function(lang) { var ed = this.editor; if (ed) { ed.settings.spellchecker_language = lang; } if (ed = this.spellchecker) { ed.setCurrentLanguage(lang); } }; // replace selection with text snippet this.replace = function(text) { var ed = this.editor; // insert into tinymce editor if (ed) { ed.getWin().focus(); // correct focus in IE & Chrome ed.selection.setContent(rcmail.quote_html(text).replace(/\r?\n/g, '<br/>'), { format:'text' }); } // replace selection in compose textarea else if (ed = rcube_find_object(this.id)) { var selection = $(ed).is(':focus') ? rcmail.get_input_selection(ed) : { start:0, end:0 }, inp_value = ed.value; pre = inp_value.substring(0, selection.start), end = inp_value.substring(selection.end, inp_value.length); // insert response text ed.value = pre + text + end; // set caret after inserted text rcmail.set_caret_pos(ed, selection.start + text.length); ed.focus(); } }; // get selected text (if no selection returns all text) from the editor this.get_content = function(selected, plain) { // apply spellcheck changes if spell checker is active this.spellcheck_stop(); var sigstart, ed = this.editor, format = plain ? 'text' : 'html', text = '', strip = false; // get selected text from tinymce editor if (ed) { ed.getWin().focus(); // correct focus in IE & Chrome if (selected) text = ed.selection.getContent({format: format}); if (!text) { text = ed.getContent({format: format}); strip = true; } } // get selected text from compose textarea else if (ed = rcube_find_object(this.id)) { if (selected && $(ed).is(':focus')) { text = rcmail.get_input_selection(ed).text; } if (!text) { text = ed.value; strip = true; } } // strip off signature if (strip) { sigstart = text.indexOf('-- \n'); if (sigstart > 0) { text = text.substring(0, sigstart); } } return text; }; // change user signature text this.change_signature = function(id, show_sig) { var cursor_pos, p = -1, input_message = $('#' + this.id), message = input_message.val(), sig = rcmail.env.identity; if (!this.editor) { // plain text mode // remove the 'old' signature if (show_sig && sig && rcmail.env.signatures && rcmail.env.signatures[sig]) { sig = rcmail.env.signatures[sig].text; sig = sig.replace(/\r\n/g, '\n'); p = rcmail.env.top_posting ? message.indexOf(sig) : message.lastIndexOf(sig); if (p >= 0) message = message.substring(0, p) + message.substring(p+sig.length, message.length); } // add the new signature string if (show_sig && rcmail.env.signatures && rcmail.env.signatures[id]) { sig = rcmail.env.signatures[id].text; sig = sig.replace(/\r\n/g, '\n'); if (rcmail.env.top_posting) { if (p >= 0) { // in place of removed signature message = message.substring(0, p) + sig + message.substring(p, message.length); cursor_pos = p - 1; } else if (!message) { // empty message cursor_pos = 0; message = '\n\n' + sig; } else if (pos = rcmail.get_caret_pos(input_message.get(0))) { // at cursor position message = message.substring(0, pos) + '\n' + sig + '\n\n' + message.substring(pos, message.length); cursor_pos = pos; } else { // on top cursor_pos = 0; message = '\n\n' + sig + '\n\n' + message.replace(/^[\r\n]+/, ''); } } else { message = message.replace(/[\r\n]+$/, ''); cursor_pos = !rcmail.env.top_posting && message.length ? message.length+1 : 0; message += '\n\n' + sig; } } else cursor_pos = rcmail.env.top_posting ? 0 : message.length; input_message.val(message); // move cursor before the signature rcmail.set_caret_pos(input_message.get(0), cursor_pos); } else if (show_sig && rcmail.env.signatures) { // html var sigElem = this.editor.dom.get('_rc_sig'); // Append the signature as a div within the body if (!sigElem) { var body = this.editor.getBody(), doc = this.editor.getDoc(); sigElem = doc.createElement('div'); sigElem.setAttribute('id', '_rc_sig'); if (rcmail.env.top_posting) { // if no existing sig and top posting then insert at caret pos this.editor.getWin().focus(); // correct focus in IE & Chrome var node = this.editor.selection.getNode(); if (node.nodeName == 'BODY') { // no real focus, insert at start body.insertBefore(sigElem, body.firstChild); body.insertBefore(doc.createElement('br'), body.firstChild); } else { body.insertBefore(sigElem, node.nextSibling); body.insertBefore(doc.createElement('br'), node.nextSibling); } } else { body.appendChild(sigElem); } } if (rcmail.env.signatures[id]) { sigElem.innerHTML = rcmail.env.signatures[id].html; } } }; // trigger content save this.save = function() { if (this.editor) { this.editor.save(); } }; // focus the editing area this.focus = function() { (this.editor || rcube_find_object(this.id)).focus(); }; // image selector function rcmail_file_browser_callback(field_name, url, type, win) this.file_browser_callback = function(field_name, url, type) { var i, elem, dialog, list = [], editor = tinyMCE.activeEditor; var i, elem, dialog, list = []; // open image selector dialog dialog = editor.windowManager.open({ dialog = this.editor.windowManager.open({ title: rcmail.gettext('select' + type), width: 500, height: 300, html: '<div id="image-selector-list"><ul></ul></div>' + '<div id="image-selector-form"><div id="image-upload-button" class="mce-widget mce-btn" role="button"></div></div>', buttons: [{text: 'Cancel', onclick: function() { rcmail_file_browser_close(); }}] buttons: [{text: 'Cancel', onclick: function() { ref.file_browser_close(); }}] }); rcmail.env.file_browser_field = field_name; @@ -185,7 +522,7 @@ // fill images list with available images for (i in rcmail.env.attachments) { if (elem = rcmail_file_browser_entry(i, rcmail.env.attachments[i])) { if (elem = ref.file_browser_entry(i, rcmail.env.attachments[i])) { list.push(elem); } } @@ -199,7 +536,7 @@ // enable (smart) upload button elem = $('#image-upload-button').append($('<span>').text(rcmail.gettext('add' + type))); hack_file_input(elem, rcmail.gui_objects.uploadform); this.hack_file_input(elem, rcmail.gui_objects.uploadform); // enable drag-n-drop area if (rcmail.gui_objects.filedrop && rcmail.env.filedrop && ((window.XMLHttpRequest && XMLHttpRequest.prototype && XMLHttpRequest.prototype.sendAsBinary) || window.FormData)) { @@ -219,27 +556,27 @@ rcmail.env.file_dialog_event = true; rcmail.addEventListener('fileuploaded', function(attr) { var elem; if (elem = rcmail_file_browser_entry(attr.name, attr.attachment)) { if (elem = ref.file_browser_entry(attr.name, attr.attachment)) { $('#image-selector-list > ul').prepend(elem); } }); } } }; // close file browser window function rcmail_file_browser_close(url) this.file_browser_close = function(url) { if (url) $('#' + rcmail.env.file_browser_field).val(url); tinyMCE.activeEditor.windowManager.close(); this.editor.windowManager.close(); if (rcmail.env.old_file_drop) rcmail.gui_objects.filedrop = rcmail.env.old_file_drop; } }; // creates file browser entry function rcmail_file_browser_entry(file_id, file) this.file_browser_entry = function(file_id, file) { if (!file.complete || !file.mimetype) { return; @@ -252,12 +589,12 @@ return $('<li>').data('url', href) .append($('<span class="img">').append(img)) .append($('<span class="name">').text(file.name)) .click(function() { rcmail_file_browser_close($(this).data('url')); }); .click(function() { ref.file_browser_close($(this).data('url')); }); } } }; // create smart files upload button function hack_file_input(elem, clone_form) this.hack_file_input = function(elem, clone_form) { var link = $(elem), file = $('<input>'), @@ -312,4 +649,5 @@ }) .mouseleave() .append(form); }; } program/steps/mail/compose.inc
@@ -951,8 +951,7 @@ "googie.setLanguages(%s);\n". "googie.setCurrentLanguage('%s');\n". "googie.setDecoration(false);\n". "googie.decorateTextarea('%s');\n". "%s.set_env('spellcheck', googie);", "googie.decorateTextarea('%s');\n", $RCMAIL->output->get_skin_path(), $RCMAIL->url(array('_task' => 'utils', '_action' => 'spell', '_remote' => 1)), !empty($dictionary) ? 'true' : 'false', @@ -964,8 +963,7 @@ rcube::JQ(rcube::Q($RCMAIL->gettext('addtodict'))), rcube_output::json_serialize($spellcheck_langs), $lang, $attrib['id'], rcmail_output::JS_OBJECT_NAME), 'foot'); $attrib['id']), 'foot'); $OUTPUT->add_label('checking'); $OUTPUT->set_env('spellcheck_langs', join(',', $editor_lang_set)); @@ -1704,7 +1702,7 @@ if (empty($attrib['name'])) $attrib['name'] = 'editorSelect'; $attrib['onchange'] = "return rcmail_toggle_editor(this, '".$attrib['editorid']."')"; $attrib['onchange'] = "return rcmail.command('toggle-editor', {id: '".$attrib['editorid']."', html: this.value == 'html'}, '', event)"; $select = new html_select($attrib); program/steps/mail/sendmail.inc
@@ -320,7 +320,7 @@ } $OUTPUT->show_message('mispellingsfound', 'error'); $OUTPUT->command('spellcheck_resume', $isHtml, $result); $OUTPUT->command('spellcheck_resume', $result); $OUTPUT->send('iframe'); } } program/steps/settings/edit_identity.inc
@@ -96,7 +96,7 @@ 'spellcheck' => true), 'html_signature' => array('type' => 'checkbox', 'label' => $RCMAIL->gettext('htmlsignature'), 'onclick' => 'return rcmail_toggle_editor(this, \'rcmfd_signature\');'), 'onclick' => 'return rcmail.command(\'toggle-editor\', {id: \'rcmfd_signature\', html: this.checked}, \'\', event)'), )) );