annotate static/js/tiny_mce/plugins/spellchecker/editor_plugin_src.js @ 348:d1b11096595b

Fix #168; when nailing a spammer, clear their profile text fields. Guard against topics and forums that don't exist when deleting posts in the signal handler. Make the forum stats template tag only display the latest active users.
author Brian Neal <bgneal@gmail.com>
date Wed, 02 Mar 2011 02:18:28 +0000
parents 88b2b9cb8c1f
children 6c182ceb7147
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@312 73 ed.onInit.add(function() {
bgneal@312 74 if (ed.settings.content_css !== false)
bgneal@312 75 ed.dom.loadCSS(url + '/css/content.css');
bgneal@312 76 });
bgneal@312 77
bgneal@312 78 ed.onClick.add(t._showMenu, t);
bgneal@312 79 ed.onContextMenu.add(t._showMenu, t);
bgneal@312 80 ed.onBeforeGetContent.add(function() {
bgneal@312 81 if (t.active)
bgneal@312 82 t._removeWords();
bgneal@312 83 });
bgneal@312 84
bgneal@312 85 ed.onNodeChange.add(function(ed, cm) {
bgneal@312 86 cm.setActive('spellchecker', t.active);
bgneal@312 87 });
bgneal@312 88
bgneal@312 89 ed.onSetContent.add(function() {
bgneal@312 90 t._done();
bgneal@312 91 });
bgneal@312 92
bgneal@312 93 ed.onBeforeGetContent.add(function() {
bgneal@312 94 t._done();
bgneal@312 95 });
bgneal@312 96
bgneal@312 97 ed.onBeforeExecCommand.add(function(ed, cmd) {
bgneal@312 98 if (cmd == 'mceFullScreen')
bgneal@312 99 t._done();
bgneal@312 100 });
bgneal@312 101
bgneal@312 102 // Find selected language
bgneal@312 103 t.languages = {};
bgneal@312 104 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 105 if (k.indexOf('+') === 0) {
bgneal@312 106 k = k.substring(1);
bgneal@312 107 t.selectedLang = v;
bgneal@312 108 }
bgneal@312 109
bgneal@312 110 t.languages[k] = v;
bgneal@312 111 });
bgneal@312 112 },
bgneal@312 113
bgneal@312 114 createControl : function(n, cm) {
bgneal@312 115 var t = this, c, ed = t.editor;
bgneal@312 116
bgneal@312 117 if (n == 'spellchecker') {
bgneal@312 118 // Use basic button if we use the native spellchecker
bgneal@312 119 if (t.rpcUrl == '{backend}') {
bgneal@312 120 // Create simple toggle button if we have native support
bgneal@312 121 if (t.hasSupport)
bgneal@312 122 c = cm.createButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t});
bgneal@312 123
bgneal@312 124 return c;
bgneal@312 125 }
bgneal@312 126
bgneal@312 127 c = cm.createSplitButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t});
bgneal@312 128
bgneal@312 129 c.onRenderMenu.add(function(c, m) {
bgneal@312 130 m.add({title : 'spellchecker.langs', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
bgneal@312 131 each(t.languages, function(v, k) {
bgneal@312 132 var o = {icon : 1}, mi;
bgneal@312 133
bgneal@312 134 o.onclick = function() {
bgneal@312 135 mi.setSelected(1);
bgneal@312 136 t.selectedItem.setSelected(0);
bgneal@312 137 t.selectedItem = mi;
bgneal@312 138 t.selectedLang = v;
bgneal@312 139 };
bgneal@312 140
bgneal@312 141 o.title = k;
bgneal@312 142 mi = m.add(o);
bgneal@312 143 mi.setSelected(v == t.selectedLang);
bgneal@312 144
bgneal@312 145 if (v == t.selectedLang)
bgneal@312 146 t.selectedItem = mi;
bgneal@312 147 })
bgneal@312 148 });
bgneal@312 149
bgneal@312 150 return c;
bgneal@312 151 }
bgneal@312 152 },
bgneal@312 153
bgneal@312 154 // Internal functions
bgneal@312 155
bgneal@312 156 _walk : function(n, f) {
bgneal@312 157 var d = this.editor.getDoc(), w;
bgneal@312 158
bgneal@312 159 if (d.createTreeWalker) {
bgneal@312 160 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
bgneal@312 161
bgneal@312 162 while ((n = w.nextNode()) != null)
bgneal@312 163 f.call(this, n);
bgneal@312 164 } else
bgneal@312 165 tinymce.walk(n, f, 'childNodes');
bgneal@312 166 },
bgneal@312 167
bgneal@312 168 _getSeparators : function() {
bgneal@312 169 var re = '', i, str = this.editor.getParam('spellchecker_word_separator_chars', '\\s!"#$%&()*+,-./:;<=>?@[\]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');
bgneal@312 170
bgneal@312 171 // Build word separator regexp
bgneal@312 172 for (i=0; i<str.length; i++)
bgneal@312 173 re += '\\' + str.charAt(i);
bgneal@312 174
bgneal@312 175 return re;
bgneal@312 176 },
bgneal@312 177
bgneal@312 178 _getWords : function() {
bgneal@312 179 var ed = this.editor, wl = [], tx = '', lo = {}, rawWords = [];
bgneal@312 180
bgneal@312 181 // Get area text
bgneal@312 182 this._walk(ed.getBody(), function(n) {
bgneal@312 183 if (n.nodeType == 3)
bgneal@312 184 tx += n.nodeValue + ' ';
bgneal@312 185 });
bgneal@312 186
bgneal@312 187 // split the text up into individual words
bgneal@312 188 if (ed.getParam('spellchecker_word_pattern')) {
bgneal@312 189 // look for words that match the pattern
bgneal@312 190 rawWords = tx.match('(' + ed.getParam('spellchecker_word_pattern') + ')', 'gi');
bgneal@312 191 } else {
bgneal@312 192 // Split words by separator
bgneal@312 193 tx = tx.replace(new RegExp('([0-9]|[' + this._getSeparators() + '])', 'g'), ' ');
bgneal@312 194 tx = tinymce.trim(tx.replace(/(\s+)/g, ' '));
bgneal@312 195 rawWords = tx.split(' ');
bgneal@312 196 }
bgneal@312 197
bgneal@312 198 // Build word array and remove duplicates
bgneal@312 199 each(rawWords, function(v) {
bgneal@312 200 if (!lo[v]) {
bgneal@312 201 wl.push(v);
bgneal@312 202 lo[v] = 1;
bgneal@312 203 }
bgneal@312 204 });
bgneal@312 205
bgneal@312 206 return wl;
bgneal@312 207 },
bgneal@312 208
bgneal@312 209 _removeWords : function(w) {
bgneal@312 210 var ed = this.editor, dom = ed.dom, se = ed.selection, b = se.getBookmark();
bgneal@312 211
bgneal@312 212 each(dom.select('span').reverse(), function(n) {
bgneal@312 213 if (n && (dom.hasClass(n, 'mceItemHiddenSpellWord') || dom.hasClass(n, 'mceItemHidden'))) {
bgneal@312 214 if (!w || dom.decode(n.innerHTML) == w)
bgneal@312 215 dom.remove(n, 1);
bgneal@312 216 }
bgneal@312 217 });
bgneal@312 218
bgneal@312 219 se.moveToBookmark(b);
bgneal@312 220 },
bgneal@312 221
bgneal@312 222 _markWords : function(wl) {
bgneal@312 223 var r1, r2, r3, r4, r5, w = '', ed = this.editor, re = this._getSeparators(), dom = ed.dom, nl = [];
bgneal@312 224 var se = ed.selection, b = se.getBookmark();
bgneal@312 225
bgneal@312 226 each(wl, function(v) {
bgneal@312 227 w += (w ? '|' : '') + v;
bgneal@312 228 });
bgneal@312 229
bgneal@312 230 r1 = new RegExp('([' + re + '])(' + w + ')([' + re + '])', 'g');
bgneal@312 231 r2 = new RegExp('^(' + w + ')', 'g');
bgneal@312 232 r3 = new RegExp('(' + w + ')([' + re + ']?)$', 'g');
bgneal@312 233 r4 = new RegExp('^(' + w + ')([' + re + ']?)$', 'g');
bgneal@312 234 r5 = new RegExp('(' + w + ')([' + re + '])', 'g');
bgneal@312 235
bgneal@312 236 // Collect all text nodes
bgneal@312 237 this._walk(this.editor.getBody(), function(n) {
bgneal@312 238 if (n.nodeType == 3) {
bgneal@312 239 nl.push(n);
bgneal@312 240 }
bgneal@312 241 });
bgneal@312 242
bgneal@312 243 // Wrap incorrect words in spans
bgneal@312 244 each(nl, function(n) {
bgneal@312 245 var v;
bgneal@312 246
bgneal@312 247 if (n.nodeType == 3) {
bgneal@312 248 v = n.nodeValue;
bgneal@312 249
bgneal@312 250 if (r1.test(v) || r2.test(v) || r3.test(v) || r4.test(v)) {
bgneal@312 251 v = dom.encode(v);
bgneal@312 252 v = v.replace(r5, '<span class="mceItemHiddenSpellWord">$1</span>$2');
bgneal@312 253 v = v.replace(r3, '<span class="mceItemHiddenSpellWord">$1</span>$2');
bgneal@312 254
bgneal@312 255 dom.replace(dom.create('span', {'class' : 'mceItemHidden'}, v), n);
bgneal@312 256 }
bgneal@312 257 }
bgneal@312 258 });
bgneal@312 259
bgneal@312 260 se.moveToBookmark(b);
bgneal@312 261 },
bgneal@312 262
bgneal@312 263 _showMenu : function(ed, e) {
bgneal@312 264 var t = this, ed = t.editor, m = t._menu, p1, dom = ed.dom, vp = dom.getViewPort(ed.getWin()), wordSpan = e.target;
bgneal@312 265
bgneal@312 266 e = 0; // Fixes IE memory leak
bgneal@312 267
bgneal@312 268 if (!m) {
bgneal@312 269 p1 = DOM.getPos(ed.getContentAreaContainer());
bgneal@312 270 //p2 = DOM.getPos(ed.getContainer());
bgneal@312 271
bgneal@312 272 m = ed.controlManager.createDropMenu('spellcheckermenu', {
bgneal@312 273 offset_x : p1.x,
bgneal@312 274 offset_y : p1.y,
bgneal@312 275 'class' : 'mceNoIcons'
bgneal@312 276 });
bgneal@312 277
bgneal@312 278 t._menu = m;
bgneal@312 279 }
bgneal@312 280
bgneal@312 281 if (dom.hasClass(wordSpan, 'mceItemHiddenSpellWord')) {
bgneal@312 282 m.removeAll();
bgneal@312 283 m.add({title : 'spellchecker.wait', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
bgneal@312 284
bgneal@312 285 t._sendRPC('getSuggestions', [t.selectedLang, dom.decode(wordSpan.innerHTML)], function(r) {
bgneal@312 286 var ignoreRpc;
bgneal@312 287
bgneal@312 288 m.removeAll();
bgneal@312 289
bgneal@312 290 if (r.length > 0) {
bgneal@312 291 m.add({title : 'spellchecker.sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
bgneal@312 292 each(r, function(v) {
bgneal@312 293 m.add({title : v, onclick : function() {
bgneal@312 294 dom.replace(ed.getDoc().createTextNode(v), wordSpan);
bgneal@312 295 t._checkDone();
bgneal@312 296 }});
bgneal@312 297 });
bgneal@312 298
bgneal@312 299 m.addSeparator();
bgneal@312 300 } else
bgneal@312 301 m.add({title : 'spellchecker.no_sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
bgneal@312 302
bgneal@312 303 ignoreRpc = t.editor.getParam("spellchecker_enable_ignore_rpc", '');
bgneal@312 304 m.add({
bgneal@312 305 title : 'spellchecker.ignore_word',
bgneal@312 306 onclick : function() {
bgneal@312 307 var word = wordSpan.innerHTML;
bgneal@312 308
bgneal@312 309 dom.remove(wordSpan, 1);
bgneal@312 310 t._checkDone();
bgneal@312 311
bgneal@312 312 // tell the server if we need to
bgneal@312 313 if (ignoreRpc) {
bgneal@312 314 ed.setProgressState(1);
bgneal@312 315 t._sendRPC('ignoreWord', [t.selectedLang, word], function(r) {
bgneal@312 316 ed.setProgressState(0);
bgneal@312 317 });
bgneal@312 318 }
bgneal@312 319 }
bgneal@312 320 });
bgneal@312 321
bgneal@312 322 m.add({
bgneal@312 323 title : 'spellchecker.ignore_words',
bgneal@312 324 onclick : function() {
bgneal@312 325 var word = wordSpan.innerHTML;
bgneal@312 326
bgneal@312 327 t._removeWords(dom.decode(word));
bgneal@312 328 t._checkDone();
bgneal@312 329
bgneal@312 330 // tell the server if we need to
bgneal@312 331 if (ignoreRpc) {
bgneal@312 332 ed.setProgressState(1);
bgneal@312 333 t._sendRPC('ignoreWords', [t.selectedLang, word], function(r) {
bgneal@312 334 ed.setProgressState(0);
bgneal@312 335 });
bgneal@312 336 }
bgneal@312 337 }
bgneal@312 338 });
bgneal@312 339
bgneal@312 340
bgneal@312 341 if (t.editor.getParam("spellchecker_enable_learn_rpc")) {
bgneal@312 342 m.add({
bgneal@312 343 title : 'spellchecker.learn_word',
bgneal@312 344 onclick : function() {
bgneal@312 345 var word = wordSpan.innerHTML;
bgneal@312 346
bgneal@312 347 dom.remove(wordSpan, 1);
bgneal@312 348 t._checkDone();
bgneal@312 349
bgneal@312 350 ed.setProgressState(1);
bgneal@312 351 t._sendRPC('learnWord', [t.selectedLang, word], function(r) {
bgneal@312 352 ed.setProgressState(0);
bgneal@312 353 });
bgneal@312 354 }
bgneal@312 355 });
bgneal@312 356 }
bgneal@312 357
bgneal@312 358 m.update();
bgneal@312 359 });
bgneal@312 360
bgneal@312 361 ed.selection.select(wordSpan);
bgneal@312 362 p1 = dom.getPos(wordSpan);
bgneal@312 363 m.showMenu(p1.x, p1.y + wordSpan.offsetHeight - vp.y);
bgneal@312 364
bgneal@312 365 return tinymce.dom.Event.cancel(e);
bgneal@312 366 } else
bgneal@312 367 m.hideMenu();
bgneal@312 368 },
bgneal@312 369
bgneal@312 370 _checkDone : function() {
bgneal@312 371 var t = this, ed = t.editor, dom = ed.dom, o;
bgneal@312 372
bgneal@312 373 each(dom.select('span'), function(n) {
bgneal@312 374 if (n && dom.hasClass(n, 'mceItemHiddenSpellWord')) {
bgneal@312 375 o = true;
bgneal@312 376 return false;
bgneal@312 377 }
bgneal@312 378 });
bgneal@312 379
bgneal@312 380 if (!o)
bgneal@312 381 t._done();
bgneal@312 382 },
bgneal@312 383
bgneal@312 384 _done : function() {
bgneal@312 385 var t = this, la = t.active;
bgneal@312 386
bgneal@312 387 if (t.active) {
bgneal@312 388 t.active = 0;
bgneal@312 389 t._removeWords();
bgneal@312 390
bgneal@312 391 if (t._menu)
bgneal@312 392 t._menu.hideMenu();
bgneal@312 393
bgneal@312 394 if (la)
bgneal@312 395 t.editor.nodeChanged();
bgneal@312 396 }
bgneal@312 397 },
bgneal@312 398
bgneal@312 399 _sendRPC : function(m, p, cb) {
bgneal@312 400 var t = this;
bgneal@312 401
bgneal@312 402 JSONRequest.sendRPC({
bgneal@312 403 url : t.rpcUrl,
bgneal@312 404 method : m,
bgneal@312 405 params : p,
bgneal@312 406 success : cb,
bgneal@312 407 error : function(e, x) {
bgneal@312 408 t.editor.setProgressState(0);
bgneal@312 409 t.editor.windowManager.alert(e.errstr || ('Error response: ' + x.responseText));
bgneal@312 410 }
bgneal@312 411 });
bgneal@312 412 }
bgneal@312 413 });
bgneal@312 414
bgneal@312 415 // Register plugin
bgneal@312 416 tinymce.PluginManager.add('spellchecker', tinymce.plugins.SpellcheckerPlugin);
bgneal@312 417 })();