/**\r
- * $Id: editor_plugin_src.js 425 2007-11-21 15:17:39Z spocke $\r
+ * editor_plugin_src.js\r
*\r
- * @author Moxiecode\r
- * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.\r
+ * Copyright 2009, Moxiecode Systems AB\r
+ * Released under LGPL License.\r
+ *\r
+ * License: http://tinymce.moxiecode.com/license\r
+ * Contributing: http://tinymce.moxiecode.com/contributing\r
*/\r
\r
(function() {\r
\r
t.url = url;\r
t.editor = ed;\r
+ t.rpcUrl = ed.getParam("spellchecker_rpc_url", "{backend}");\r
+\r
+ if (t.rpcUrl == '{backend}') {\r
+ // Sniff if the browser supports native spellchecking (Don't know of a better way)\r
+ if (tinymce.isIE)\r
+ return;\r
+\r
+ t.hasSupport = true;\r
+\r
+ // Disable the context menu when spellchecking is active\r
+ ed.onContextMenu.addToTop(function(ed, e) {\r
+ if (t.active)\r
+ return false;\r
+ });\r
+ }\r
\r
// Register commands\r
ed.addCommand('mceSpellCheck', function() {\r
+ if (t.rpcUrl == '{backend}') {\r
+ // Enable/disable native spellchecker\r
+ t.editor.getBody().spellcheck = t.active = !t.active;\r
+ return;\r
+ }\r
+\r
if (!t.active) {\r
ed.setProgressState(1);\r
t._sendRPC('checkWords', [t.selectedLang, t._getWords()], function(r) {\r
ed.nodeChanged();\r
} else {\r
ed.setProgressState(0);\r
- ed.windowManager.alert('spellchecker.no_mpell');\r
+\r
+ if (ed.getParam('spellchecker_report_no_misspellings', true))\r
+ ed.windowManager.alert('spellchecker.no_mpell');\r
}\r
});\r
} else\r
t._done();\r
});\r
\r
- ed.onInit.add(function() {\r
- if (ed.settings.content_css !== false)\r
- ed.dom.loadCSS(url + '/css/content.css');\r
- });\r
+ if (ed.settings.content_css !== false)\r
+ ed.contentCSS.push(url + '/css/content.css');\r
\r
ed.onClick.add(t._showMenu, t);\r
ed.onContextMenu.add(t._showMenu, t);\r
var t = this, c, ed = t.editor;\r
\r
if (n == 'spellchecker') {\r
+ // Use basic button if we use the native spellchecker\r
+ if (t.rpcUrl == '{backend}') {\r
+ // Create simple toggle button if we have native support\r
+ if (t.hasSupport)\r
+ c = cm.createButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t});\r
+\r
+ return c;\r
+ }\r
+\r
c = cm.createSplitButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t});\r
\r
c.onRenderMenu.add(function(c, m) {\r
var o = {icon : 1}, mi;\r
\r
o.onclick = function() {\r
+ if (v == t.selectedLang) {\r
+ return;\r
+ }\r
mi.setSelected(1);\r
t.selectedItem.setSelected(0);\r
t.selectedItem = mi;\r
},\r
\r
_getWords : function() {\r
- var ed = this.editor, wl = [], tx = '', lo = {};\r
+ var ed = this.editor, wl = [], tx = '', lo = {}, rawWords = [];\r
\r
// Get area text\r
this._walk(ed.getBody(), function(n) {\r
tx += n.nodeValue + ' ';\r
});\r
\r
- // Split words by separator\r
- tx = tx.replace(new RegExp('([0-9]|[' + this._getSeparators() + '])', 'g'), ' ');\r
- tx = tinymce.trim(tx.replace(/(\s+)/g, ' '));\r
+ // split the text up into individual words\r
+ if (ed.getParam('spellchecker_word_pattern')) {\r
+ // look for words that match the pattern\r
+ rawWords = tx.match('(' + ed.getParam('spellchecker_word_pattern') + ')', 'gi');\r
+ } else {\r
+ // Split words by separator\r
+ tx = tx.replace(new RegExp('([0-9]|[' + this._getSeparators() + '])', 'g'), ' ');\r
+ tx = tinymce.trim(tx.replace(/(\s+)/g, ' '));\r
+ rawWords = tx.split(' ');\r
+ }\r
\r
// Build word array and remove duplicates\r
- each(tx.split(' '), function(v) {\r
+ each(rawWords, function(v) {\r
if (!lo[v]) {\r
wl.push(v);\r
lo[v] = 1;\r
},\r
\r
_markWords : function(wl) {\r
- var r1, r2, r3, r4, r5, w = '', ed = this.editor, re = this._getSeparators(), dom = ed.dom, nl = [];\r
- var se = ed.selection, b = se.getBookmark();\r
-\r
- each(wl, function(v) {\r
- w += (w ? '|' : '') + v;\r
- });\r
-\r
- r1 = new RegExp('([' + re + '])(' + w + ')([' + re + '])', 'g');\r
- r2 = new RegExp('^(' + w + ')', 'g');\r
- r3 = new RegExp('(' + w + ')([' + re + ']?)$', 'g');\r
- r4 = new RegExp('^(' + w + ')([' + re + ']?)$', 'g');\r
- r5 = new RegExp('(' + w + ')([' + re + '])', 'g');\r
+ var ed = this.editor, dom = ed.dom, doc = ed.getDoc(), se = ed.selection, b = se.getBookmark(), nl = [],\r
+ w = wl.join('|'), re = this._getSeparators(), rx = new RegExp('(^|[' + re + '])(' + w + ')(?=[' + re + ']|$)', 'g');\r
\r
// Collect all text nodes\r
- this._walk(this.editor.getBody(), function(n) {\r
+ this._walk(ed.getBody(), function(n) {\r
if (n.nodeType == 3) {\r
nl.push(n);\r
}\r
\r
// Wrap incorrect words in spans\r
each(nl, function(n) {\r
- var v;\r
-\r
- if (n.nodeType == 3) {\r
- v = n.nodeValue;\r
-\r
- if (r1.test(v) || r2.test(v) || r3.test(v) || r4.test(v)) {\r
- v = dom.encode(v);\r
- v = v.replace(r5, '<span class="mceItemHiddenSpellWord">$1</span>$2');\r
- v = v.replace(r3, '<span class="mceItemHiddenSpellWord">$1</span>$2');\r
-\r
- dom.replace(dom.create('span', {'class' : 'mceItemHidden'}, v), n);\r
+ var node, elem, txt, pos, v = n.nodeValue;\r
+\r
+ if (rx.test(v)) {\r
+ // Encode the content\r
+ v = dom.encode(v);\r
+ // Create container element\r
+ elem = dom.create('span', {'class' : 'mceItemHidden'});\r
+\r
+ // Following code fixes IE issues by creating text nodes\r
+ // using DOM methods instead of innerHTML.\r
+ // Bug #3124: <PRE> elements content is broken after spellchecking.\r
+ // Bug #1408: Preceding whitespace characters are removed\r
+ // @TODO: I'm not sure that both are still issues on IE9.\r
+ if (tinymce.isIE) {\r
+ // Enclose mispelled words with temporal tag\r
+ v = v.replace(rx, '$1<mcespell>$2</mcespell>');\r
+ // Loop over the content finding mispelled words\r
+ while ((pos = v.indexOf('<mcespell>')) != -1) {\r
+ // Add text node for the content before the word\r
+ txt = v.substring(0, pos);\r
+ if (txt.length) {\r
+ node = doc.createTextNode(dom.decode(txt));\r
+ elem.appendChild(node);\r
+ }\r
+ v = v.substring(pos+10);\r
+ pos = v.indexOf('</mcespell>');\r
+ txt = v.substring(0, pos);\r
+ v = v.substring(pos+11);\r
+ // Add span element for the word\r
+ elem.appendChild(dom.create('span', {'class' : 'mceItemHiddenSpellWord'}, txt));\r
+ }\r
+ // Add text node for the rest of the content\r
+ if (v.length) {\r
+ node = doc.createTextNode(dom.decode(v));\r
+ elem.appendChild(node);\r
+ }\r
+ } else {\r
+ // Other browsers preserve whitespace characters on innerHTML usage\r
+ elem.innerHTML = v.replace(rx, '$1<span class="mceItemHiddenSpellWord">$2</span>');\r
}\r
+\r
+ // Finally, replace the node with the container\r
+ dom.replace(elem, n);\r
}\r
});\r
\r
},\r
\r
_showMenu : function(ed, e) {\r
- var t = this, ed = t.editor, m = t._menu, p1, dom = ed.dom, vp = dom.getViewPort(ed.getWin());\r
-\r
- if (!m) {\r
- p1 = DOM.getPos(ed.getContentAreaContainer());\r
- //p2 = DOM.getPos(ed.getContainer());\r
+ var t = this, ed = t.editor, m = t._menu, p1, dom = ed.dom, vp = dom.getViewPort(ed.getWin()), wordSpan = e.target;\r
\r
- m = ed.controlManager.createDropMenu('spellcheckermenu', {\r
- offset_x : p1.x,\r
- offset_y : p1.y,\r
- 'class' : 'mceNoIcons'\r
- });\r
+ e = 0; // Fixes IE memory leak\r
\r
+ if (!m) {\r
+ m = ed.controlManager.createDropMenu('spellcheckermenu', {'class' : 'mceNoIcons'});\r
t._menu = m;\r
}\r
\r
- if (dom.hasClass(e.target, 'mceItemHiddenSpellWord')) {\r
+ if (dom.hasClass(wordSpan, 'mceItemHiddenSpellWord')) {\r
m.removeAll();\r
m.add({title : 'spellchecker.wait', 'class' : 'mceMenuItemTitle'}).setDisabled(1);\r
\r
- t._sendRPC('getSuggestions', [t.selectedLang, dom.decode(e.target.innerHTML)], function(r) {\r
+ t._sendRPC('getSuggestions', [t.selectedLang, dom.decode(wordSpan.innerHTML)], function(r) {\r
+ var ignoreRpc;\r
+\r
m.removeAll();\r
\r
if (r.length > 0) {\r
m.add({title : 'spellchecker.sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);\r
each(r, function(v) {\r
m.add({title : v, onclick : function() {\r
- dom.replace(ed.getDoc().createTextNode(v), e.target);\r
+ dom.replace(ed.getDoc().createTextNode(v), wordSpan);\r
t._checkDone();\r
}});\r
});\r
} else\r
m.add({title : 'spellchecker.no_sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);\r
\r
+ ignoreRpc = t.editor.getParam("spellchecker_enable_ignore_rpc", '');\r
m.add({\r
title : 'spellchecker.ignore_word',\r
onclick : function() {\r
- dom.remove(e.target, 1);\r
+ var word = wordSpan.innerHTML;\r
+\r
+ dom.remove(wordSpan, 1);\r
t._checkDone();\r
+\r
+ // tell the server if we need to\r
+ if (ignoreRpc) {\r
+ ed.setProgressState(1);\r
+ t._sendRPC('ignoreWord', [t.selectedLang, word], function(r) {\r
+ ed.setProgressState(0);\r
+ });\r
+ }\r
}\r
});\r
\r
m.add({\r
title : 'spellchecker.ignore_words',\r
onclick : function() {\r
- t._removeWords(dom.decode(e.target.innerHTML));\r
+ var word = wordSpan.innerHTML;\r
+\r
+ t._removeWords(dom.decode(word));\r
t._checkDone();\r
+\r
+ // tell the server if we need to\r
+ if (ignoreRpc) {\r
+ ed.setProgressState(1);\r
+ t._sendRPC('ignoreWords', [t.selectedLang, word], function(r) {\r
+ ed.setProgressState(0);\r
+ });\r
+ }\r
}\r
});\r
\r
+ if (t.editor.getParam("spellchecker_enable_learn_rpc")) {\r
+ m.add({\r
+ title : 'spellchecker.learn_word',\r
+ onclick : function() {\r
+ var word = wordSpan.innerHTML;\r
+\r
+ dom.remove(wordSpan, 1);\r
+ t._checkDone();\r
+\r
+ ed.setProgressState(1);\r
+ t._sendRPC('learnWord', [t.selectedLang, word], function(r) {\r
+ ed.setProgressState(0);\r
+ });\r
+ }\r
+ });\r
+ }\r
+\r
m.update();\r
});\r
\r
- ed.selection.select(e.target);\r
- p1 = dom.getPos(e.target);\r
- m.showMenu(p1.x, p1.y + e.target.offsetHeight - vp.y);\r
+ p1 = DOM.getPos(ed.getContentAreaContainer());\r
+ m.settings.offset_x = p1.x;\r
+ m.settings.offset_y = p1.y;\r
+\r
+ ed.selection.select(wordSpan);\r
+ p1 = dom.getPos(wordSpan);\r
+ m.showMenu(p1.x, p1.y + wordSpan.offsetHeight - vp.y);\r
\r
return tinymce.dom.Event.cancel(e);\r
} else\r
},\r
\r
_sendRPC : function(m, p, cb) {\r
- var t = this, url = t.editor.getParam("spellchecker_rpc_url", "{backend}");\r
-\r
- if (url == '{backend}') {\r
- t.editor.setProgressState(0);\r
- alert('Please specify: spellchecker_rpc_url');\r
- return;\r
- }\r
+ var t = this;\r
\r
JSONRequest.sendRPC({\r
- url : url,\r
+ url : t.rpcUrl,\r
method : m,\r
params : p,\r
success : cb,\r
\r
// Register plugin\r
tinymce.PluginManager.add('spellchecker', tinymce.plugins.SpellcheckerPlugin);\r
-})();
\ No newline at end of file
+})();\r