X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=program%2Fjs%2Ftiny_mce%2Fplugins%2Fspellchecker%2Feditor_plugin_src.js;h=9757aec9c3eb7680de10da4607498196c13d6a51;hb=511160353722ebec8c258ddd6fa963ec8851eede;hp=196d246404006f63205bd4c10fc70d5a979001f0;hpb=ade1655456ffdb799be8081f57ec90a408b99dd9;p=roundcube.git diff --git a/program/js/tiny_mce/plugins/spellchecker/editor_plugin_src.js b/program/js/tiny_mce/plugins/spellchecker/editor_plugin_src.js index 196d246..9757aec 100644 --- a/program/js/tiny_mce/plugins/spellchecker/editor_plugin_src.js +++ b/program/js/tiny_mce/plugins/spellchecker/editor_plugin_src.js @@ -1,588 +1,435 @@ /** - * $Id: editor_plugin_src.js 28 2006-08-01 16:02:56Z spocke $ + * editor_plugin_src.js * - * @author Moxiecode - * @copyright Copyright © 2004-2006, Moxiecode Systems AB, All rights reserved. + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing */ -tinyMCE.importPluginLanguagePack('spellchecker', 'en,fr,sv,nn,nb'); - -// Plucin static class -var TinyMCE_SpellCheckerPlugin = { - _contextMenu : new TinyMCE_Menu(), - _menu : new TinyMCE_Menu(), - _counter : 0, - _ajaxPage : '/tinyspell.php', - - getInfo : function() { - return { - longname : 'Spellchecker', - author : 'Moxiecode Systems AB', - authorurl : 'http://tinymce.moxiecode.com', - infourl : 'http://tinymce.moxiecode.com/tinymce/docs/plugin_spellchecker.html', - version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion - }; - }, - - handleEvent : function(e) { - var elm = tinyMCE.isMSIE ? e.srcElement : e.target; - var inst = tinyMCE.selectedInstance, args = ''; - var self = TinyMCE_SpellCheckerPlugin; - var cm = self._contextMenu; - var p, p2, x, y, sx, sy, h, elm; - - // Handle click on word - if ((e.type == "click" || e.type == "contextmenu") && elm) { - do { - if (tinyMCE.getAttrib(elm, 'class') == "mceItemHiddenSpellWord") { - inst.spellCheckerElm = elm; - - // Setup arguments - args += 'id=' + inst.editorId + "|" + (++self._counter); - args += '&cmd=suggest&check=' + encodeURIComponent(elm.innerHTML); - args += '&lang=' + escape(inst.spellCheckerLang); - - elm = inst.spellCheckerElm; - p = tinyMCE.getAbsPosition(inst.iframeElement); - p2 = tinyMCE.getAbsPosition(elm); - h = parseInt(elm.offsetHeight); - sx = inst.getBody().scrollLeft; - sy = inst.getBody().scrollTop; - x = p.absLeft + p2.absLeft - sx; - y = p.absTop + p2.absTop - sy + h; - - cm.clear(); - cm.addTitle(tinyMCE.getLang('lang_spellchecker_wait', '', true)); - cm.show(); - cm.moveTo(x, y); - - inst.selection.selectNode(elm, false, false); - - self._sendAjax(self.baseURL + self._ajaxPage, self._ajaxResponse, 'post', args); - - tinyMCE.cancelEvent(e); - return false; - } - } while ((elm = elm.parentNode)); - } - - return true; - }, - - initInstance : function(inst) { - var self = TinyMCE_SpellCheckerPlugin, m = self._menu, cm = self._contextMenu, e; - - tinyMCE.importCSS(inst.getDoc(), tinyMCE.baseURL + "/plugins/spellchecker/css/content.css"); - - if (!tinyMCE.hasMenu('spellcheckercontextmenu')) { - tinyMCE.importCSS(document, tinyMCE.baseURL + "/plugins/spellchecker/css/spellchecker.css"); - - cm.init({drop_menu : false}); - tinyMCE.addMenu('spellcheckercontextmenu', cm); - } - - if (!tinyMCE.hasMenu('spellcheckermenu')) { - m.init({}); - tinyMCE.addMenu('spellcheckermenu', m); - } - - inst.spellCheckerLang = 'en'; - self._buildSettingsMenu(inst, null); - - e = self._getBlockBoxLayer(inst).create('div', 'mceBlockBox', document.getElementById(inst.editorId + '_parent')); - self._getMsgBoxLayer(inst).create('div', 'mceMsgBox', document.getElementById(inst.editorId + '_parent')); - }, - - _getMsgBoxLayer : function(inst) { - if (!inst.spellCheckerMsgBoxL) - inst.spellCheckerMsgBoxL = new TinyMCE_Layer(inst.editorId + '_spellcheckerMsgBox', false); - - return inst.spellCheckerMsgBoxL; - }, - - _getBlockBoxLayer : function(inst) { - if (!inst.spellCheckerBoxL) - inst.spellCheckerBoxL = new TinyMCE_Layer(inst.editorId + '_spellcheckerBlockBox', false); +(function() { + var JSONRequest = tinymce.util.JSONRequest, each = tinymce.each, DOM = tinymce.DOM; + + tinymce.create('tinymce.plugins.SpellcheckerPlugin', { + getInfo : function() { + return { + longname : 'Spellchecker', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker', + version : tinymce.majorVersion + "." + tinymce.minorVersion + }; + }, + + init : function(ed, url) { + var t = this, cm; + + t.url = url; + t.editor = ed; + t.rpcUrl = ed.getParam("spellchecker_rpc_url", "{backend}"); + + if (t.rpcUrl == '{backend}') { + // Sniff if the browser supports native spellchecking (Don't know of a better way) + if (tinymce.isIE) + return; + + t.hasSupport = true; + + // Disable the context menu when spellchecking is active + ed.onContextMenu.addToTop(function(ed, e) { + if (t.active) + return false; + }); + } - return inst.spellCheckerBoxL; - }, + // Register commands + ed.addCommand('mceSpellCheck', function() { + if (t.rpcUrl == '{backend}') { + // Enable/disable native spellchecker + t.editor.getBody().spellcheck = t.active = !t.active; + return; + } - _buildSettingsMenu : function(inst, lang) { - var i, ar = tinyMCE.getParam('spellchecker_languages', '+English=en').split(','), p; - var self = TinyMCE_SpellCheckerPlugin, m = self._menu, c; + if (!t.active) { + ed.setProgressState(1); + t._sendRPC('checkWords', [t.selectedLang, t._getWords()], function(r) { + if (r.length > 0) { + t.active = 1; + t._markWords(r); + ed.setProgressState(0); + ed.nodeChanged(); + } else { + ed.setProgressState(0); + + if (ed.getParam('spellchecker_report_no_misspellings', true)) + ed.windowManager.alert('spellchecker.no_mpell'); + } + }); + } else + t._done(); + }); + + if (ed.settings.content_css !== false) + ed.contentCSS.push(url + '/css/content.css'); + + ed.onClick.add(t._showMenu, t); + ed.onContextMenu.add(t._showMenu, t); + ed.onBeforeGetContent.add(function() { + if (t.active) + t._removeWords(); + }); + + ed.onNodeChange.add(function(ed, cm) { + cm.setActive('spellchecker', t.active); + }); + + ed.onSetContent.add(function() { + t._done(); + }); + + ed.onBeforeGetContent.add(function() { + t._done(); + }); + + ed.onBeforeExecCommand.add(function(ed, cmd) { + if (cmd == 'mceFullScreen') + t._done(); + }); + + // Find selected language + t.languages = {}; + each(ed.getParam('spellchecker_languages', '+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv', 'hash'), function(v, k) { + if (k.indexOf('+') === 0) { + k = k.substring(1); + t.selectedLang = v; + } - m.clear(); - m.addTitle(tinyMCE.getLang('lang_spellchecker_langs', '', true)); + t.languages[k] = v; + }); + }, - for (i=0; i?@[\]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c'); + + // Build word separator regexp + for (i=0; i'; - h += ''; - h += ''; - h += ''; - } else { - if (tinyMCE.isMSIE && !tinyMCE.isOpera) - h += ''; - else - h += ''; - - h += ''; - h += ''; - h += ''; - h += ''; - } - - return h; - }, - - _menuButtonEvent : function(e, o) { - if (o.className == 'mceMenuButtonFocus') - return; - - if (e == 'over') - o.className = o.className + ' mceMenuHover'; - else - o.className = o.className.replace(/\s.*$/, ''); - }, - - _toggleMenu : function(editor_id, id) { - var self = TinyMCE_SpellCheckerPlugin; - var e = document.getElementById(editor_id + '_spellchecker'); - var inst = tinyMCE.getInstanceById(editor_id); - - if (self._menu.isVisible()) { - tinyMCE.hideMenus(); - return; - } - - tinyMCE.lastMenuBtnClass = e.className.replace(/\s.*$/, ''); - tinyMCE.switchClass(editor_id + '_spellchecker', 'mceMenuButtonFocus'); - - self._menu.moveRelativeTo(e, 'bl'); - self._menu.moveBy(tinyMCE.isMSIE && !tinyMCE.isOpera ? 0 : 1, -1); - - if (tinyMCE.isOpera) - self._menu.moveBy(0, -2); - - self._onMenuEvent(inst, self._menu, 'show'); - - self._menu.show(); - - tinyMCE.lastSelectedMenuBtn = editor_id + '_spellchecker'; - }, - _onMenuEvent : function(inst, m, n) { - TinyMCE_SpellCheckerPlugin._buildSettingsMenu(inst, inst.spellCheckerLang); - }, + // Build word array and remove duplicates + each(rawWords, function(v) { + if (!lo[v]) { + wl.push(v); + lo[v] = 1; + } + }); - execCommand : function(editor_id, element, command, user_interface, value) { - var inst = tinyMCE.getInstanceById(editor_id), self = TinyMCE_SpellCheckerPlugin, args = '', co, bb, mb, nl, i, e; + return wl; + }, - // Handle commands - switch (command) { - case "mceSpellCheck": - if (!inst.spellcheckerOn) { - inst.spellCheckerBookmark = inst.selection.getBookmark(); + _removeWords : function(w) { + var ed = this.editor, dom = ed.dom, se = ed.selection, b = se.getBookmark(); - // Setup arguments - args += 'id=' + inst.editorId + "|" + (++self._counter); - args += '&cmd=spell&check=' + encodeURIComponent(self._getWordList(inst.getBody())).replace( /\'/g, '%27' ); - args += '&lang=' + escape(inst.spellCheckerLang); + each(dom.select('span').reverse(), function(n) { + if (n && (dom.hasClass(n, 'mceItemHiddenSpellWord') || dom.hasClass(n, 'mceItemHidden'))) { + if (!w || dom.decode(n.innerHTML) == w) + dom.remove(n, 1); + } + }); - co = document.getElementById(inst.editorId + '_parent').firstChild; - bb = self._getBlockBoxLayer(inst); - bb.moveRelativeTo(co, 'tl'); - bb.resizeTo(co.offsetWidth, co.offsetHeight); - bb.show(); + se.moveToBookmark(b); + }, - // Setup message box - mb = self._getMsgBoxLayer(inst); - e = mb.getElement(); - e.innerHTML = '' + tinyMCE.getLang('lang_spellchecker_swait', '', true) + ''; - mb.show(); - mb.moveRelativeTo(co, 'cc'); + _markWords : function(wl) { + var ed = this.editor, dom = ed.dom, doc = ed.getDoc(), se = ed.selection, b = se.getBookmark(), nl = [], + w = wl.join('|'), re = this._getSeparators(), rx = new RegExp('(^|[' + re + '])(' + w + ')(?=[' + re + ']|$)', 'g'); - if (tinyMCE.isMSIE && !tinyMCE.isOpera) { - nl = co.getElementsByTagName('select'); - for (i=0; i elements content is broken after spellchecking. + // Bug #1408: Preceding whitespace characters are removed + // @TODO: I'm not sure that both are still issues on IE9. + if (tinymce.isIE) { + // Enclose mispelled words with temporal tag + v = v.replace(rx, '$1$2'); + // Loop over the content finding mispelled words + while ((pos = v.indexOf('')) != -1) { + // Add text node for the content before the word + txt = v.substring(0, pos); + if (txt.length) { + node = doc.createTextNode(dom.decode(txt)); + elem.appendChild(node); + } + v = v.substring(pos+10); + pos = v.indexOf(''); + txt = v.substring(0, pos); + v = v.substring(pos+11); + // Add span element for the word + elem.appendChild(dom.create('span', {'class' : 'mceItemHiddenSpellWord'}, txt)); + } + // Add text node for the rest of the content + if (v.length) { + node = doc.createTextNode(dom.decode(v)); + elem.appendChild(node); + } + } else { + // Other browsers preserve whitespace characters on innerHTML usage + elem.innerHTML = v.replace(rx, '$1$2'); } - inst.spellcheckerOn = true; - tinyMCE.switchClass(editor_id + '_spellchecker', 'mceMenuButtonSelected'); - - self._sendAjax(self.baseURL + self._ajaxPage, self._ajaxResponse, 'post', args); - } else { - self._removeWords(inst.getDoc()); - inst.spellcheckerOn = false; - tinyMCE.switchClass(editor_id + '_spellchecker', 'mceMenuButton'); + // Finally, replace the node with the container + dom.replace(elem, n); } + }); - return true; - - case "mceSpellCheckReplace": - if (inst.spellCheckerElm) - tinyMCE.setOuterHTML(inst.spellCheckerElm, value); - - self._checkDone(inst); - self._contextMenu.hide(); - self._menu.hide(); - - return true; + se.moveToBookmark(b); + }, - case "mceSpellCheckIgnore": - if (inst.spellCheckerElm) - self._removeWord(inst.spellCheckerElm); + _showMenu : function(ed, e) { + var t = this, ed = t.editor, m = t._menu, p1, dom = ed.dom, vp = dom.getViewPort(ed.getWin()), wordSpan = e.target; - self._checkDone(inst); - self._contextMenu.hide(); - self._menu.hide(); - return true; + e = 0; // Fixes IE memory leak - case "mceSpellCheckIgnoreAll": - if (inst.spellCheckerElm) - self._removeWords(inst.getDoc(), inst.spellCheckerElm.innerHTML); - - self._checkDone(inst); - self._contextMenu.hide(); - self._menu.hide(); - return true; - - case "mceSpellCheckerSetLang": - tinyMCE.hideMenus(); - inst.spellCheckerLang = value; - self._removeWords(inst.getDoc()); - inst.spellcheckerOn = false; - tinyMCE.switchClass(editor_id + '_spellchecker', 'mceMenuButton'); - return true; - } - - // Pass to next handler in chain - return false; - }, - - cleanup : function(type, content, inst) { - switch (type) { - case "get_from_editor_dom": - TinyMCE_SpellCheckerPlugin._removeWords(content); - inst.spellcheckerOn = false; - break; - } - - return content; - }, - - // Private plugin specific methods - - _displayUI : function(inst) { - var self = TinyMCE_SpellCheckerPlugin; - var bb = self._getBlockBoxLayer(inst); - var mb = self._getMsgBoxLayer(inst); - var nl, i; - var co = document.getElementById(inst.editorId + '_parent').firstChild; - - if (tinyMCE.isMSIE && !tinyMCE.isOpera) { - nl = co.getElementsByTagName('select'); - for (i=0; i?@[\]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c'); - - for (i=0; i 0) - wl[wl.length] = nl[i]; - } - - return wl.join(' '); - }, - - _removeWords : function(doc, word) { - var i, c, nl = doc.getElementsByTagName("span"); - var self = TinyMCE_SpellCheckerPlugin; - var inst = tinyMCE.selectedInstance, b = inst ? inst.selection.getBookmark() : null; - - word = typeof(word) == 'undefined' ? null : word; - - for (i=nl.length-1; i>=0; i--) { - c = tinyMCE.getAttrib(nl[i], 'class'); - - if ((c == 'mceItemHiddenSpellWord' || c == 'mceItemHidden') && (word == null || nl[i].innerHTML == word)) - self._removeWord(nl[i]); - } + if (dom.hasClass(wordSpan, 'mceItemHiddenSpellWord')) { + m.removeAll(); + m.add({title : 'spellchecker.wait', 'class' : 'mceMenuItemTitle'}).setDisabled(1); + + t._sendRPC('getSuggestions', [t.selectedLang, dom.decode(wordSpan.innerHTML)], function(r) { + var ignoreRpc; + + m.removeAll(); + + if (r.length > 0) { + m.add({title : 'spellchecker.sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1); + each(r, function(v) { + m.add({title : v, onclick : function() { + dom.replace(ed.getDoc().createTextNode(v), wordSpan); + t._checkDone(); + }}); + }); + + m.addSeparator(); + } else + m.add({title : 'spellchecker.no_sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1); + + ignoreRpc = t.editor.getParam("spellchecker_enable_ignore_rpc", ''); + m.add({ + title : 'spellchecker.ignore_word', + onclick : function() { + var word = wordSpan.innerHTML; + + dom.remove(wordSpan, 1); + t._checkDone(); + + // tell the server if we need to + if (ignoreRpc) { + ed.setProgressState(1); + t._sendRPC('ignoreWord', [t.selectedLang, word], function(r) { + ed.setProgressState(0); + }); + } + } + }); + + m.add({ + title : 'spellchecker.ignore_words', + onclick : function() { + var word = wordSpan.innerHTML; + + t._removeWords(dom.decode(word)); + t._checkDone(); + + // tell the server if we need to + if (ignoreRpc) { + ed.setProgressState(1); + t._sendRPC('ignoreWords', [t.selectedLang, word], function(r) { + ed.setProgressState(0); + }); + } + } + }); + + + if (t.editor.getParam("spellchecker_enable_learn_rpc")) { + m.add({ + title : 'spellchecker.learn_word', + onclick : function() { + var word = wordSpan.innerHTML; + + dom.remove(wordSpan, 1); + t._checkDone(); + + ed.setProgressState(1); + t._sendRPC('learnWord', [t.selectedLang, word], function(r) { + ed.setProgressState(0); + }); + } + }); + } - if (b) - inst.selection.moveToBookmark(b); - }, + m.update(); + }); - _checkDone : function(inst) { - var self = TinyMCE_SpellCheckerPlugin; - var w = self._countWords(inst); + p1 = dom.getPos(ed.getContentAreaContainer()); + m.settings.offset_x = p1.x; + m.settings.offset_y = p1.y; - if (w == 0) { - self._removeWords(inst.getDoc()); - inst.spellcheckerOn = false; - tinyMCE.switchClass(inst.editorId + '_spellchecker', 'mceMenuButton'); - } - }, + ed.selection.select(wordSpan); + p1 = dom.getPos(wordSpan); + m.showMenu(p1.x, p1.y + wordSpan.offsetHeight - vp.y); - _countWords : function(inst) { - var i, w = 0, nl = inst.getDoc().getElementsByTagName("span"), c; - var self = TinyMCE_SpellCheckerPlugin; + return tinymce.dom.Event.cancel(e); + } else + m.hideMenu(); + }, - for (i=nl.length-1; i>=0; i--) { - c = tinyMCE.getAttrib(nl[i], 'class'); + _checkDone : function() { + var t = this, ed = t.editor, dom = ed.dom, o; - if (c == 'mceItemHiddenSpellWord') - w++; - } + each(dom.select('span'), function(n) { + if (n && dom.hasClass(n, 'mceItemHiddenSpellWord')) { + o = true; + return false; + } + }); - return w; - }, + if (!o) + t._done(); + }, - _removeWord : function(e) { - if (e != null) - tinyMCE.setOuterHTML(e, e.innerHTML); - }, + _done : function() { + var t = this, la = t.active; - _markWords : function(doc, n, wl) { - var i, nv, nn, nl = tinyMCE.getNodeTree(n, new Array(), 3); - var r1, r2, r3, r4, r5, w = ''; - var re = TinyMCE_SpellCheckerPlugin._getWordSeparators(); + if (t.active) { + t.active = 0; + t._removeWords(); - for (i=0; i 0) - w += wl[i] + ((i == wl.length-1) ? '' : '|'); - } + if (t._menu) + t._menu.hideMenu(); - for (i=0; i$1$2'); - nv = nv.replace(r3, '$1$2'); - - nn = doc.createElement('span'); - nn.className = "mceItemHidden"; - nn.innerHTML = nv; - - // Remove old text node - nl[i].parentNode.replaceChild(nn, nl[i]); + if (la) + t.editor.nodeChanged(); } + }, + + _sendRPC : function(m, p, cb) { + var t = this; + + JSONRequest.sendRPC({ + url : t.rpcUrl, + method : m, + params : p, + success : cb, + error : function(e, x) { + t.editor.setProgressState(0); + t.editor.windowManager.alert(e.errstr || ('Error response: ' + x.responseText)); + } + }); } - }, - - _buildMenu : function(sg, max) { - var i, self = TinyMCE_SpellCheckerPlugin, cm = self._contextMenu; - - cm.clear(); - - if (sg != null) { - cm.addTitle(tinyMCE.getLang('lang_spellchecker_sug', '', true)); + }); - for (i=0; i