PageRenderTime 53ms CodeModel.GetById 26ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/static/scripts/tiny_mce/plugins/spellchecker/editor_plugin_src.js

http://n23.googlecode.com/
JavaScript | 338 lines | 304 code | 25 blank | 9 comment | 16 complexity | 64590a32e53e5236ab8f68a2473a9999 MD5 | raw file
  1/**
  2 * $Id: editor_plugin_src.js 425 2007-11-21 15:17:39Z spocke $
  3 *
  4 * @author Moxiecode
  5 * @copyright Copyright Š 2004-2008, Moxiecode Systems AB, All rights reserved.
  6 */
  7
  8(function() {
  9	var JSONRequest = tinymce.util.JSONRequest, each = tinymce.each, DOM = tinymce.DOM;
 10
 11	tinymce.create('tinymce.plugins.SpellcheckerPlugin', {
 12		getInfo : function() {
 13			return {
 14				longname : 'Spellchecker',
 15				author : 'Moxiecode Systems AB',
 16				authorurl : 'http://tinymce.moxiecode.com',
 17				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker',
 18				version : tinymce.majorVersion + "." + tinymce.minorVersion
 19			};
 20		},
 21
 22		init : function(ed, url) {
 23			var t = this, cm;
 24
 25			t.url = url;
 26			t.editor = ed;
 27
 28			// Register commands
 29			ed.addCommand('mceSpellCheck', function() {
 30				if (!t.active) {
 31					ed.setProgressState(1);
 32					t._sendRPC('checkWords', [t.selectedLang, t._getWords()], function(r) {
 33						if (r.length > 0) {
 34							t.active = 1;
 35							t._markWords(r);
 36							ed.setProgressState(0);
 37							ed.nodeChanged();
 38						} else {
 39							ed.setProgressState(0);
 40							ed.windowManager.alert('spellchecker.no_mpell');
 41						}
 42					});
 43				} else
 44					t._done();
 45			});
 46
 47			ed.onInit.add(function() {
 48				if (ed.settings.content_css !== false)
 49					ed.dom.loadCSS(url + '/css/content.css');
 50			});
 51
 52			ed.onClick.add(t._showMenu, t);
 53			ed.onContextMenu.add(t._showMenu, t);
 54			ed.onBeforeGetContent.add(function() {
 55				if (t.active)
 56					t._removeWords();
 57			});
 58
 59			ed.onNodeChange.add(function(ed, cm) {
 60				cm.setActive('spellchecker', t.active);
 61			});
 62
 63			ed.onSetContent.add(function() {
 64				t._done();
 65			});
 66
 67			ed.onBeforeGetContent.add(function() {
 68				t._done();
 69			});
 70
 71			ed.onBeforeExecCommand.add(function(ed, cmd) {
 72				if (cmd == 'mceFullScreen')
 73					t._done();
 74			});
 75
 76			// Find selected language
 77			t.languages = {};
 78			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) {
 79				if (k.indexOf('+') === 0) {
 80					k = k.substring(1);
 81					t.selectedLang = v;
 82				}
 83
 84				t.languages[k] = v;
 85			});
 86		},
 87
 88		createControl : function(n, cm) {
 89			var t = this, c, ed = t.editor;
 90
 91			if (n == 'spellchecker') {
 92				c = cm.createSplitButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t});
 93
 94				c.onRenderMenu.add(function(c, m) {
 95					m.add({title : 'spellchecker.langs', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
 96					each(t.languages, function(v, k) {
 97						var o = {icon : 1}, mi;
 98
 99						o.onclick = function() {
100							mi.setSelected(1);
101							t.selectedItem.setSelected(0);
102							t.selectedItem = mi;
103							t.selectedLang = v;
104						};
105
106						o.title = k;
107						mi = m.add(o);
108						mi.setSelected(v == t.selectedLang);
109
110						if (v == t.selectedLang)
111							t.selectedItem = mi;
112					})
113				});
114
115				return c;
116			}
117		},
118
119		// Internal functions
120
121		_walk : function(n, f) {
122			var d = this.editor.getDoc(), w;
123
124			if (d.createTreeWalker) {
125				w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
126
127				while ((n = w.nextNode()) != null)
128					f.call(this, n);
129			} else
130				tinymce.walk(n, f, 'childNodes');
131		},
132
133		_getSeparators : function() {
134			var re = '', i, str = this.editor.getParam('spellchecker_word_separator_chars', '\\s!"#$%&()*+,-./:;<=>?@[\]^_{|}§Š?Ž???¸???ž?×÷¤\u201d\u201c');
135
136			// Build word separator regexp
137			for (i=0; i<str.length; i++)
138				re += '\\' + str.charAt(i);
139
140			return re;
141		},
142
143		_getWords : function() {
144			var ed = this.editor, wl = [], tx = '', lo = {};
145
146			// Get area text
147			this._walk(ed.getBody(), function(n) {
148				if (n.nodeType == 3)
149					tx += n.nodeValue + ' ';
150			});
151
152			// Split words by separator
153			tx = tx.replace(new RegExp('([0-9]|[' + this._getSeparators() + '])', 'g'), ' ');
154			tx = tinymce.trim(tx.replace(/(\s+)/g, ' '));
155
156			// Build word array and remove duplicates
157			each(tx.split(' '), function(v) {
158				if (!lo[v]) {
159					wl.push(v);
160					lo[v] = 1;
161				}
162			});
163
164			return wl;
165		},
166
167		_removeWords : function(w) {
168			var ed = this.editor, dom = ed.dom, se = ed.selection, b = se.getBookmark();
169
170			each(dom.select('span').reverse(), function(n) {
171				if (n && (dom.hasClass(n, 'mceItemHiddenSpellWord') || dom.hasClass(n, 'mceItemHidden'))) {
172					if (!w || dom.decode(n.innerHTML) == w)
173						dom.remove(n, 1);
174				}
175			});
176
177			se.moveToBookmark(b);
178		},
179
180		_markWords : function(wl) {
181			var r1, r2, r3, r4, r5, w = '', ed = this.editor, re = this._getSeparators(), dom = ed.dom, nl = [];
182			var se = ed.selection, b = se.getBookmark();
183
184			each(wl, function(v) {
185				w += (w ? '|' : '') + v;
186			});
187
188			r1 = new RegExp('([' + re + '])(' + w + ')([' + re + '])', 'g');
189			r2 = new RegExp('^(' + w + ')', 'g');
190			r3 = new RegExp('(' + w + ')([' + re + ']?)$', 'g');
191			r4 = new RegExp('^(' + w + ')([' + re + ']?)$', 'g');
192			r5 = new RegExp('(' + w + ')([' + re + '])', 'g');
193
194			// Collect all text nodes
195			this._walk(this.editor.getBody(), function(n) {
196				if (n.nodeType == 3) {
197					nl.push(n);
198				}
199			});
200
201			// Wrap incorrect words in spans
202			each(nl, function(n) {
203				var v;
204
205				if (n.nodeType == 3) {
206					v = n.nodeValue;
207
208					if (r1.test(v) || r2.test(v) || r3.test(v) || r4.test(v)) {
209						v = dom.encode(v);
210						v = v.replace(r5, '<span class="mceItemHiddenSpellWord">$1</span>$2');
211						v = v.replace(r3, '<span class="mceItemHiddenSpellWord">$1</span>$2');
212
213						dom.replace(dom.create('span', {'class' : 'mceItemHidden'}, v), n);
214					}
215				}
216			});
217
218			se.moveToBookmark(b);
219		},
220
221		_showMenu : function(ed, e) {
222			var t = this, ed = t.editor, m = t._menu, p1, dom = ed.dom, vp = dom.getViewPort(ed.getWin());
223
224			if (!m) {
225				p1 = DOM.getPos(ed.getContentAreaContainer());
226				//p2 = DOM.getPos(ed.getContainer());
227
228				m = ed.controlManager.createDropMenu('spellcheckermenu', {
229					offset_x : p1.x,
230					offset_y : p1.y,
231					'class' : 'mceNoIcons'
232				});
233
234				t._menu = m;
235			}
236
237			if (dom.hasClass(e.target, 'mceItemHiddenSpellWord')) {
238				m.removeAll();
239				m.add({title : 'spellchecker.wait', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
240
241				t._sendRPC('getSuggestions', [t.selectedLang, dom.decode(e.target.innerHTML)], function(r) {
242					m.removeAll();
243
244					if (r.length > 0) {
245						m.add({title : 'spellchecker.sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
246						each(r, function(v) {
247							m.add({title : v, onclick : function() {
248								dom.replace(ed.getDoc().createTextNode(v), e.target);
249								t._checkDone();
250							}});
251						});
252
253						m.addSeparator();
254					} else
255						m.add({title : 'spellchecker.no_sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
256
257					m.add({
258						title : 'spellchecker.ignore_word',
259						onclick : function() {
260							dom.remove(e.target, 1);
261							t._checkDone();
262						}
263					});
264
265					m.add({
266						title : 'spellchecker.ignore_words',
267						onclick : function() {
268							t._removeWords(dom.decode(e.target.innerHTML));
269							t._checkDone();
270						}
271					});
272
273					m.update();
274				});
275
276				ed.selection.select(e.target);
277				p1 = dom.getPos(e.target);
278				m.showMenu(p1.x, p1.y + e.target.offsetHeight - vp.y);
279
280				return tinymce.dom.Event.cancel(e);
281			} else
282				m.hideMenu();
283		},
284
285		_checkDone : function() {
286			var t = this, ed = t.editor, dom = ed.dom, o;
287
288			each(dom.select('span'), function(n) {
289				if (n && dom.hasClass(n, 'mceItemHiddenSpellWord')) {
290					o = true;
291					return false;
292				}
293			});
294
295			if (!o)
296				t._done();
297		},
298
299		_done : function() {
300			var t = this, la = t.active;
301
302			if (t.active) {
303				t.active = 0;
304				t._removeWords();
305
306				if (t._menu)
307					t._menu.hideMenu();
308
309				if (la)
310					t.editor.nodeChanged();
311			}
312		},
313
314		_sendRPC : function(m, p, cb) {
315			var t = this, url = t.editor.getParam("spellchecker_rpc_url", "{backend}");
316
317			if (url == '{backend}') {
318				t.editor.setProgressState(0);
319				alert('Please specify: spellchecker_rpc_url');
320				return;
321			}
322
323			JSONRequest.sendRPC({
324				url : url,
325				method : m,
326				params : p,
327				success : cb,
328				error : function(e, x) {
329					t.editor.setProgressState(0);
330					t.editor.windowManager.alert(e.errstr || ('Error response: ' + x.responseText));
331				}
332			});
333		}
334	});
335
336	// Register plugin
337	tinymce.PluginManager.add('spellchecker', tinymce.plugins.SpellcheckerPlugin);
338})();