annotate static/js/tiny_mce/plugins/spellchecker/editor_plugin_src.js @ 645:99f7917702ca

Fix 081a88b3bfc8, javascript resize of forum images. Commit 081a88b3bfc8 broke those pages that loaded forums.js but did not load the imagesLoaded jQuery extension. Now we have arranged it so that only the forums topic view loads imagesLoaded and put the resizing javascript inline.
author Brian Neal <bgneal@gmail.com>
date Mon, 11 Mar 2013 15:30:25 -0500
parents 6c182ceb7147
children
rev   line source
bgneal@312 1 /**
bgneal@312 2 * editor_plugin_src.js
bgneal@312 3 *
bgneal@312 4 * Copyright 2009, Moxiecode Systems AB
bgneal@312 5 * Released under LGPL License.
bgneal@312 6 *
bgneal@312 7 * License: http://tinymce.moxiecode.com/license
bgneal@312 8 * Contributing: http://tinymce.moxiecode.com/contributing
bgneal@312 9 */
bgneal@312 10
bgneal@312 11 (function() {
bgneal@312 12 var JSONRequest = tinymce.util.JSONRequest, each = tinymce.each, DOM = tinymce.DOM;
bgneal@312 13
bgneal@312 14 tinymce.create('tinymce.plugins.SpellcheckerPlugin', {
bgneal@312 15 getInfo : function() {
bgneal@312 16 return {
bgneal@312 17 longname : 'Spellchecker',
bgneal@312 18 author : 'Moxiecode Systems AB',
bgneal@312 19 authorurl : 'http://tinymce.moxiecode.com',
bgneal@312 20 infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker',
bgneal@312 21 version : tinymce.majorVersion + "." + tinymce.minorVersion
bgneal@312 22 };
bgneal@312 23 },
bgneal@312 24
bgneal@312 25 init : function(ed, url) {
bgneal@312 26 var t = this, cm;
bgneal@312 27
bgneal@312 28 t.url = url;
bgneal@312 29 t.editor = ed;
bgneal@312 30 t.rpcUrl = ed.getParam("spellchecker_rpc_url", "{backend}");
bgneal@312 31
bgneal@312 32 if (t.rpcUrl == '{backend}') {
bgneal@312 33 // Sniff if the browser supports native spellchecking (Don't know of a better way)
bgneal@312 34 if (tinymce.isIE)
bgneal@312 35 return;
bgneal@312 36
bgneal@312 37 t.hasSupport = true;
bgneal@312 38
bgneal@312 39 // Disable the context menu when spellchecking is active
bgneal@312 40 ed.onContextMenu.addToTop(function(ed, e) {
bgneal@312 41 if (t.active)
bgneal@312 42 return false;
bgneal@312 43 });
bgneal@312 44 }
bgneal@312 45
bgneal@312 46 // Register commands
bgneal@312 47 ed.addCommand('mceSpellCheck', function() {
bgneal@312 48 if (t.rpcUrl == '{backend}') {
bgneal@312 49 // Enable/disable native spellchecker
bgneal@312 50 t.editor.getBody().spellcheck = t.active = !t.active;
bgneal@312 51 return;
bgneal@312 52 }
bgneal@312 53
bgneal@312 54 if (!t.active) {
bgneal@312 55 ed.setProgressState(1);
bgneal@312 56 t._sendRPC('checkWords', [t.selectedLang, t._getWords()], function(r) {
bgneal@312 57 if (r.length > 0) {
bgneal@312 58 t.active = 1;
bgneal@312 59 t._markWords(r);
bgneal@312 60 ed.setProgressState(0);
bgneal@312 61 ed.nodeChanged();
bgneal@312 62 } else {
bgneal@312 63 ed.setProgressState(0);
bgneal@312 64
bgneal@312 65 if (ed.getParam('spellchecker_report_no_misspellings', true))
bgneal@312 66 ed.windowManager.alert('spellchecker.no_mpell');
bgneal@312 67 }
bgneal@312 68 });
bgneal@312 69 } else
bgneal@312 70 t._done();
bgneal@312 71 });
bgneal@312 72
bgneal@442 73 if (ed.settings.content_css !== false)
bgneal@442 74 ed.contentCSS.push(url + '/css/content.css');
bgneal@312 75
bgneal@312 76 ed.onClick.add(t._showMenu, t);
bgneal@312 77 ed.onContextMenu.add(t._showMenu, t);
bgneal@312 78 ed.onBeforeGetContent.add(function() {
bgneal@312 79 if (t.active)
bgneal@312 80 t._removeWords();
bgneal@312 81 });
bgneal@312 82
bgneal@312 83 ed.onNodeChange.add(function(ed, cm) {
bgneal@312 84 cm.setActive('spellchecker', t.active);
bgneal@312 85 });
bgneal@312 86
bgneal@312 87 ed.onSetContent.add(function() {
bgneal@312 88 t._done();
bgneal@312 89 });
bgneal@312 90
bgneal@312 91 ed.onBeforeGetContent.add(function() {
bgneal@312 92 t._done();
bgneal@312 93 });
bgneal@312 94
bgneal@312 95 ed.onBeforeExecCommand.add(function(ed, cmd) {
bgneal@312 96 if (cmd == 'mceFullScreen')
bgneal@312 97 t._done();
bgneal@312 98 });
bgneal@312 99
bgneal@312 100 // Find selected language
bgneal@312 101 t.languages = {};
bgneal@312 102 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) {
bgneal@312 103 if (k.indexOf('+') === 0) {
bgneal@312 104 k = k.substring(1);
bgneal@312 105 t.selectedLang = v;
bgneal@312 106 }
bgneal@312 107
bgneal@312 108 t.languages[k] = v;
bgneal@312 109 });
bgneal@312 110 },
bgneal@312 111
bgneal@312 112 createControl : function(n, cm) {
bgneal@312 113 var t = this, c, ed = t.editor;
bgneal@312 114
bgneal@312 115 if (n == 'spellchecker') {
bgneal@312 116 // Use basic button if we use the native spellchecker
bgneal@312 117 if (t.rpcUrl == '{backend}') {
bgneal@312 118 // Create simple toggle button if we have native support
bgneal@312 119 if (t.hasSupport)
bgneal@312 120 c = cm.createButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t});
bgneal@312 121
bgneal@312 122 return c;
bgneal@312 123 }
bgneal@312 124
bgneal@312 125 c = cm.createSplitButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t});
bgneal@312 126
bgneal@312 127 c.onRenderMenu.add(function(c, m) {
bgneal@312 128 m.add({title : 'spellchecker.langs', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
bgneal@312 129 each(t.languages, function(v, k) {
bgneal@312 130 var o = {icon : 1}, mi;
bgneal@312 131
bgneal@312 132 o.onclick = function() {
bgneal@442 133 if (v == t.selectedLang) {
bgneal@442 134 return;
bgneal@442 135 }
bgneal@312 136 mi.setSelected(1);
bgneal@312 137 t.selectedItem.setSelected(0);
bgneal@312 138 t.selectedItem = mi;
bgneal@312 139 t.selectedLang = v;
bgneal@312 140 };
bgneal@312 141
bgneal@312 142 o.title = k;
bgneal@312 143 mi = m.add(o);
bgneal@312 144 mi.setSelected(v == t.selectedLang);
bgneal@312 145
bgneal@312 146 if (v == t.selectedLang)
bgneal@312 147 t.selectedItem = mi;
bgneal@312 148 })
bgneal@312 149 });
bgneal@312 150
bgneal@312 151 return c;
bgneal@312 152 }
bgneal@312 153 },
bgneal@312 154
bgneal@312 155 // Internal functions
bgneal@312 156
bgneal@312 157 _walk : function(n, f) {
bgneal@312 158 var d = this.editor.getDoc(), w;
bgneal@312 159
bgneal@312 160 if (d.createTreeWalker) {
bgneal@312 161 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
bgneal@312 162
bgneal@312 163 while ((n = w.nextNode()) != null)
bgneal@312 164 f.call(this, n);
bgneal@312 165 } else
bgneal@312 166 tinymce.walk(n, f, 'childNodes');
bgneal@312 167 },
bgneal@312 168
bgneal@312 169 _getSeparators : function() {
bgneal@312 170 var re = '', i, str = this.editor.getParam('spellchecker_word_separator_chars', '\\s!"#$%&()*+,-./:;<=>?@[\]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');
bgneal@312 171
bgneal@312 172 // Build word separator regexp
bgneal@312 173 for (i=0; i<str.length; i++)
bgneal@312 174 re += '\\' + str.charAt(i);
bgneal@312 175
bgneal@312 176 return re;
bgneal@312 177 },
bgneal@312 178
bgneal@312 179 _getWords : function() {
bgneal@312 180 var ed = this.editor, wl = [], tx = '', lo = {}, rawWords = [];
bgneal@312 181
bgneal@312 182 // Get area text
bgneal@312 183 this._walk(ed.getBody(), function(n) {
bgneal@312 184 if (n.nodeType == 3)
bgneal@312 185 tx += n.nodeValue + ' ';
bgneal@312 186 });
bgneal@312 187
bgneal@312 188 // split the text up into individual words
bgneal@312 189 if (ed.getParam('spellchecker_word_pattern')) {
bgneal@312 190 // look for words that match the pattern
bgneal@312 191 rawWords = tx.match('(' + ed.getParam('spellchecker_word_pattern') + ')', 'gi');
bgneal@312 192 } else {
bgneal@312 193 // Split words by separator
bgneal@312 194 tx = tx.replace(new RegExp('([0-9]|[' + this._getSeparators() + '])', 'g'), ' ');
bgneal@312 195 tx = tinymce.trim(tx.replace(/(\s+)/g, ' '));
bgneal@312 196 rawWords = tx.split(' ');
bgneal@312 197 }
bgneal@312 198
bgneal@312 199 // Build word array and remove duplicates
bgneal@312 200 each(rawWords, function(v) {
bgneal@312 201 if (!lo[v]) {
bgneal@312 202 wl.push(v);
bgneal@312 203 lo[v] = 1;
bgneal@312 204 }
bgneal@312 205 });
bgneal@312 206
bgneal@312 207 return wl;
bgneal@312 208 },
bgneal@312 209
bgneal@312 210 _removeWords : function(w) {
bgneal@312 211 var ed = this.editor, dom = ed.dom, se = ed.selection, b = se.getBookmark();
bgneal@312 212
bgneal@312 213 each(dom.select('span').reverse(), function(n) {
bgneal@312 214 if (n && (dom.hasClass(n, 'mceItemHiddenSpellWord') || dom.hasClass(n, 'mceItemHidden'))) {
bgneal@312 215 if (!w || dom.decode(n.innerHTML) == w)
bgneal@312 216 dom.remove(n, 1);
bgneal@312 217 }
bgneal@312 218 });
bgneal@312 219
bgneal@312 220 se.moveToBookmark(b);
bgneal@312 221 },
bgneal@312 222
bgneal@312 223 _markWords : function(wl) {
bgneal@442 224 var ed = this.editor, dom = ed.dom, doc = ed.getDoc(), se = ed.selection, b = se.getBookmark(), nl = [],
bgneal@442 225 w = wl.join('|'), re = this._getSeparators(), rx = new RegExp('(^|[' + re + '])(' + w + ')(?=[' + re + ']|$)', 'g');
bgneal@312 226
bgneal@312 227 // Collect all text nodes
bgneal@442 228 this._walk(ed.getBody(), function(n) {
bgneal@312 229 if (n.nodeType == 3) {
bgneal@312 230 nl.push(n);
bgneal@312 231 }
bgneal@312 232 });
bgneal@312 233
bgneal@312 234 // Wrap incorrect words in spans
bgneal@312 235 each(nl, function(n) {
bgneal@442 236 var node, elem, txt, pos, v = n.nodeValue;
bgneal@312 237
bgneal@442 238 if (rx.test(v)) {
bgneal@442 239 // Encode the content
bgneal@442 240 v = dom.encode(v);
bgneal@442 241 // Create container element
bgneal@442 242 elem = dom.create('span', {'class' : 'mceItemHidden'});
bgneal@312 243
bgneal@442 244 // Following code fixes IE issues by creating text nodes
bgneal@442 245 // using DOM methods instead of innerHTML.
bgneal@442 246 // Bug #3124: <PRE> elements content is broken after spellchecking.
bgneal@442 247 // Bug #1408: Preceding whitespace characters are removed
bgneal@442 248 // @TODO: I'm not sure that both are still issues on IE9.
bgneal@442 249 if (tinymce.isIE) {
bgneal@442 250 // Enclose mispelled words with temporal tag
bgneal@442 251 v = v.replace(rx, '$1<mcespell>$2</mcespell>');
bgneal@442 252 // Loop over the content finding mispelled words
bgneal@442 253 while ((pos = v.indexOf('<mcespell>')) != -1) {
bgneal@442 254 // Add text node for the content before the word
bgneal@442 255 txt = v.substring(0, pos);
bgneal@442 256 if (txt.length) {
bgneal@442 257 node = doc.createTextNode(dom.decode(txt));
bgneal@442 258 elem.appendChild(node);
bgneal@442 259 }
bgneal@442 260 v = v.substring(pos+10);
bgneal@442 261 pos = v.indexOf('</mcespell>');
bgneal@442 262 txt = v.substring(0, pos);
bgneal@442 263 v = v.substring(pos+11);
bgneal@442 264 // Add span element for the word
bgneal@442 265 elem.appendChild(dom.create('span', {'class' : 'mceItemHiddenSpellWord'}, txt));
bgneal@442 266 }
bgneal@442 267 // Add text node for the rest of the content
bgneal@442 268 if (v.length) {
bgneal@442 269 node = doc.createTextNode(dom.decode(v));
bgneal@442 270 elem.appendChild(node);
bgneal@442 271 }
bgneal@442 272 } else {
bgneal@442 273 // Other browsers preserve whitespace characters on innerHTML usage
bgneal@442 274 elem.innerHTML = v.replace(rx, '$1<span class="mceItemHiddenSpellWord">$2</span>');
bgneal@442 275 }
bgneal@312 276
bgneal@442 277 // Finally, replace the node with the container
bgneal@442 278 dom.replace(elem, n);
bgneal@312 279 }
bgneal@312 280 });
bgneal@312 281
bgneal@312 282 se.moveToBookmark(b);
bgneal@312 283 },
bgneal@312 284
bgneal@312 285 _showMenu : function(ed, e) {
bgneal@312 286 var t = this, ed = t.editor, m = t._menu, p1, dom = ed.dom, vp = dom.getViewPort(ed.getWin()), wordSpan = e.target;
bgneal@312 287
bgneal@312 288 e = 0; // Fixes IE memory leak
bgneal@312 289
bgneal@312 290 if (!m) {
bgneal@442 291 m = ed.controlManager.createDropMenu('spellcheckermenu', {'class' : 'mceNoIcons'});
bgneal@312 292 t._menu = m;
bgneal@312 293 }
bgneal@312 294
bgneal@312 295 if (dom.hasClass(wordSpan, 'mceItemHiddenSpellWord')) {
bgneal@312 296 m.removeAll();
bgneal@312 297 m.add({title : 'spellchecker.wait', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
bgneal@312 298
bgneal@312 299 t._sendRPC('getSuggestions', [t.selectedLang, dom.decode(wordSpan.innerHTML)], function(r) {
bgneal@312 300 var ignoreRpc;
bgneal@312 301
bgneal@312 302 m.removeAll();
bgneal@312 303
bgneal@312 304 if (r.length > 0) {
bgneal@312 305 m.add({title : 'spellchecker.sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
bgneal@312 306 each(r, function(v) {
bgneal@312 307 m.add({title : v, onclick : function() {
bgneal@312 308 dom.replace(ed.getDoc().createTextNode(v), wordSpan);
bgneal@312 309 t._checkDone();
bgneal@312 310 }});
bgneal@312 311 });
bgneal@312 312
bgneal@312 313 m.addSeparator();
bgneal@312 314 } else
bgneal@312 315 m.add({title : 'spellchecker.no_sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
bgneal@312 316
bgneal@312 317 ignoreRpc = t.editor.getParam("spellchecker_enable_ignore_rpc", '');
bgneal@312 318 m.add({
bgneal@312 319 title : 'spellchecker.ignore_word',
bgneal@312 320 onclick : function() {
bgneal@312 321 var word = wordSpan.innerHTML;
bgneal@312 322
bgneal@312 323 dom.remove(wordSpan, 1);
bgneal@312 324 t._checkDone();
bgneal@312 325
bgneal@312 326 // tell the server if we need to
bgneal@312 327 if (ignoreRpc) {
bgneal@312 328 ed.setProgressState(1);
bgneal@312 329 t._sendRPC('ignoreWord', [t.selectedLang, word], function(r) {
bgneal@312 330 ed.setProgressState(0);
bgneal@312 331 });
bgneal@312 332 }
bgneal@312 333 }
bgneal@312 334 });
bgneal@312 335
bgneal@312 336 m.add({
bgneal@312 337 title : 'spellchecker.ignore_words',
bgneal@312 338 onclick : function() {
bgneal@312 339 var word = wordSpan.innerHTML;
bgneal@312 340
bgneal@312 341 t._removeWords(dom.decode(word));
bgneal@312 342 t._checkDone();
bgneal@312 343
bgneal@312 344 // tell the server if we need to
bgneal@312 345 if (ignoreRpc) {
bgneal@312 346 ed.setProgressState(1);
bgneal@312 347 t._sendRPC('ignoreWords', [t.selectedLang, word], function(r) {
bgneal@312 348 ed.setProgressState(0);
bgneal@312 349 });
bgneal@312 350 }
bgneal@312 351 }
bgneal@312 352 });
bgneal@312 353
bgneal@312 354
bgneal@312 355 if (t.editor.getParam("spellchecker_enable_learn_rpc")) {
bgneal@312 356 m.add({
bgneal@312 357 title : 'spellchecker.learn_word',
bgneal@312 358 onclick : function() {
bgneal@312 359 var word = wordSpan.innerHTML;
bgneal@312 360
bgneal@312 361 dom.remove(wordSpan, 1);
bgneal@312 362 t._checkDone();
bgneal@312 363
bgneal@312 364 ed.setProgressState(1);
bgneal@312 365 t._sendRPC('learnWord', [t.selectedLang, word], function(r) {
bgneal@312 366 ed.setProgressState(0);
bgneal@312 367 });
bgneal@312 368 }
bgneal@312 369 });
bgneal@312 370 }
bgneal@312 371
bgneal@312 372 m.update();
bgneal@312 373 });
bgneal@312 374
bgneal@442 375 p1 = dom.getPos(ed.getContentAreaContainer());
bgneal@442 376 m.settings.offset_x = p1.x;
bgneal@442 377 m.settings.offset_y = p1.y;
bgneal@442 378
bgneal@312 379 ed.selection.select(wordSpan);
bgneal@312 380 p1 = dom.getPos(wordSpan);
bgneal@312 381 m.showMenu(p1.x, p1.y + wordSpan.offsetHeight - vp.y);
bgneal@312 382
bgneal@312 383 return tinymce.dom.Event.cancel(e);
bgneal@312 384 } else
bgneal@312 385 m.hideMenu();
bgneal@312 386 },
bgneal@312 387
bgneal@312 388 _checkDone : function() {
bgneal@312 389 var t = this, ed = t.editor, dom = ed.dom, o;
bgneal@312 390
bgneal@312 391 each(dom.select('span'), function(n) {
bgneal@312 392 if (n && dom.hasClass(n, 'mceItemHiddenSpellWord')) {
bgneal@312 393 o = true;
bgneal@312 394 return false;
bgneal@312 395 }
bgneal@312 396 });
bgneal@312 397
bgneal@312 398 if (!o)
bgneal@312 399 t._done();
bgneal@312 400 },
bgneal@312 401
bgneal@312 402 _done : function() {
bgneal@312 403 var t = this, la = t.active;
bgneal@312 404
bgneal@312 405 if (t.active) {
bgneal@312 406 t.active = 0;
bgneal@312 407 t._removeWords();
bgneal@312 408
bgneal@312 409 if (t._menu)
bgneal@312 410 t._menu.hideMenu();
bgneal@312 411
bgneal@312 412 if (la)
bgneal@312 413 t.editor.nodeChanged();
bgneal@312 414 }
bgneal@312 415 },
bgneal@312 416
bgneal@312 417 _sendRPC : function(m, p, cb) {
bgneal@312 418 var t = this;
bgneal@312 419
bgneal@312 420 JSONRequest.sendRPC({
bgneal@312 421 url : t.rpcUrl,
bgneal@312 422 method : m,
bgneal@312 423 params : p,
bgneal@312 424 success : cb,
bgneal@312 425 error : function(e, x) {
bgneal@312 426 t.editor.setProgressState(0);
bgneal@312 427 t.editor.windowManager.alert(e.errstr || ('Error response: ' + x.responseText));
bgneal@312 428 }
bgneal@312 429 });
bgneal@312 430 }
bgneal@312 431 });
bgneal@312 432
bgneal@312 433 // Register plugin
bgneal@312 434 tinymce.PluginManager.add('spellchecker', tinymce.plugins.SpellcheckerPlugin);
bgneal@312 435 })();