Total Complexity | 165 |
Complexity/F | 1.74 |
Lines of Code | 755 |
Function Count | 95 |
Duplicated Lines | 138 |
Ratio | 18.28 % |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like public/js/tinymce/plugins/spellchecker/plugin.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | (function () { |
||
2 | var spellchecker = (function () { |
||
3 | 'use strict'; |
||
4 | |||
5 | var Cell = function (initial) { |
||
6 | var value = initial; |
||
7 | var get = function () { |
||
8 | return value; |
||
9 | }; |
||
10 | var set = function (v) { |
||
11 | value = v; |
||
12 | }; |
||
13 | var clone = function () { |
||
14 | return Cell(get()); |
||
15 | }; |
||
16 | return { |
||
17 | get: get, |
||
18 | set: set, |
||
19 | clone: clone |
||
20 | }; |
||
21 | }; |
||
22 | |||
23 | var global = tinymce.util.Tools.resolve('tinymce.PluginManager'); |
||
|
|||
24 | |||
25 | var hasProPlugin = function (editor) { |
||
26 | if (/(^|[ ,])tinymcespellchecker([, ]|$)/.test(editor.settings.plugins) && global.get('tinymcespellchecker')) { |
||
27 | if (typeof window.console !== 'undefined' && window.console.log) { |
||
28 | window.console.log('Spell Checker Pro is incompatible with Spell Checker plugin! ' + 'Remove \'spellchecker\' from the \'plugins\' option.'); |
||
29 | } |
||
30 | return true; |
||
31 | } else { |
||
32 | return false; |
||
33 | } |
||
34 | }; |
||
35 | var DetectProPlugin = { hasProPlugin: hasProPlugin }; |
||
36 | |||
37 | var getLanguages = function (editor) { |
||
38 | var defaultLanguages = 'English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr_FR,German=de,Italian=it,Polish=pl,Portuguese=pt_BR,Spanish=es,Swedish=sv'; |
||
39 | return editor.getParam('spellchecker_languages', defaultLanguages); |
||
40 | }; |
||
41 | var getLanguage = function (editor) { |
||
42 | var defaultLanguage = editor.getParam('language', 'en'); |
||
43 | return editor.getParam('spellchecker_language', defaultLanguage); |
||
44 | }; |
||
45 | var getRpcUrl = function (editor) { |
||
46 | return editor.getParam('spellchecker_rpc_url'); |
||
47 | }; |
||
48 | var getSpellcheckerCallback = function (editor) { |
||
49 | return editor.getParam('spellchecker_callback'); |
||
50 | }; |
||
51 | var getSpellcheckerWordcharPattern = function (editor) { |
||
52 | var defaultPattern = new RegExp('[^' + '\\s!"#$%&()*+,-./:;<=>?@[\\]^_{|}`' + '\xA7\xA9\xAB\xAE\xB1\xB6\xB7\xB8\xBB' + '\xBC\xBD\xBE\xBF\xD7\xF7\xA4\u201D\u201C\u201E\xA0\u2002\u2003\u2009' + ']+', 'g'); |
||
53 | return editor.getParam('spellchecker_wordchar_pattern', defaultPattern); |
||
54 | }; |
||
55 | var Settings = { |
||
56 | getLanguages: getLanguages, |
||
57 | getLanguage: getLanguage, |
||
58 | getRpcUrl: getRpcUrl, |
||
59 | getSpellcheckerCallback: getSpellcheckerCallback, |
||
60 | getSpellcheckerWordcharPattern: getSpellcheckerWordcharPattern |
||
61 | }; |
||
62 | |||
63 | var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools'); |
||
64 | |||
65 | var global$2 = tinymce.util.Tools.resolve('tinymce.util.URI'); |
||
66 | |||
67 | var global$3 = tinymce.util.Tools.resolve('tinymce.util.XHR'); |
||
68 | |||
69 | var fireSpellcheckStart = function (editor) { |
||
70 | return editor.fire('SpellcheckStart'); |
||
71 | }; |
||
72 | var fireSpellcheckEnd = function (editor) { |
||
73 | return editor.fire('SpellcheckEnd'); |
||
74 | }; |
||
75 | var Events = { |
||
76 | fireSpellcheckStart: fireSpellcheckStart, |
||
77 | fireSpellcheckEnd: fireSpellcheckEnd |
||
78 | }; |
||
79 | |||
80 | function isContentEditableFalse(node) { |
||
81 | return node && node.nodeType === 1 && node.contentEditable === 'false'; |
||
82 | } |
||
83 | var DomTextMatcher = function (node, editor) { |
||
84 | var m, matches = [], text; |
||
85 | var dom = editor.dom; |
||
86 | var blockElementsMap, hiddenTextElementsMap, shortEndedElementsMap; |
||
87 | blockElementsMap = editor.schema.getBlockElements(); |
||
88 | hiddenTextElementsMap = editor.schema.getWhiteSpaceElements(); |
||
89 | shortEndedElementsMap = editor.schema.getShortEndedElements(); |
||
90 | function createMatch(m, data) { |
||
91 | if (!m[0]) { |
||
92 | throw new Error('findAndReplaceDOMText cannot handle zero-length matches'); |
||
93 | } |
||
94 | return { |
||
95 | start: m.index, |
||
96 | end: m.index + m[0].length, |
||
97 | text: m[0], |
||
98 | data: data |
||
99 | }; |
||
100 | } |
||
101 | View Code Duplication | function getText(node) { |
|
102 | var txt; |
||
103 | if (node.nodeType === 3) { |
||
104 | return node.data; |
||
105 | } |
||
106 | if (hiddenTextElementsMap[node.nodeName] && !blockElementsMap[node.nodeName]) { |
||
107 | return ''; |
||
108 | } |
||
109 | if (isContentEditableFalse(node)) { |
||
110 | return '\n'; |
||
111 | } |
||
112 | txt = ''; |
||
113 | if (blockElementsMap[node.nodeName] || shortEndedElementsMap[node.nodeName]) { |
||
114 | txt += '\n'; |
||
115 | } |
||
116 | if (node = node.firstChild) { |
||
117 | do { |
||
118 | txt += getText(node); |
||
119 | } while (node = node.nextSibling); |
||
120 | } |
||
121 | return txt; |
||
122 | } |
||
123 | function stepThroughMatches(node, matches, replaceFn) { |
||
124 | var startNode, endNode, startNodeIndex, endNodeIndex, innerNodes = [], atIndex = 0, curNode = node, matchLocation, matchIndex = 0; |
||
125 | matches = matches.slice(0); |
||
126 | matches.sort(function (a, b) { |
||
127 | return a.start - b.start; |
||
128 | }); |
||
129 | matchLocation = matches.shift(); |
||
130 | out: |
||
131 | View Code Duplication | while (true) { |
|
132 | if (blockElementsMap[curNode.nodeName] || shortEndedElementsMap[curNode.nodeName] || isContentEditableFalse(curNode)) { |
||
133 | atIndex++; |
||
134 | } |
||
135 | if (curNode.nodeType === 3) { |
||
136 | if (!endNode && curNode.length + atIndex >= matchLocation.end) { |
||
137 | endNode = curNode; |
||
138 | endNodeIndex = matchLocation.end - atIndex; |
||
139 | } else if (startNode) { |
||
140 | innerNodes.push(curNode); |
||
141 | } |
||
142 | if (!startNode && curNode.length + atIndex > matchLocation.start) { |
||
143 | startNode = curNode; |
||
144 | startNodeIndex = matchLocation.start - atIndex; |
||
145 | } |
||
146 | atIndex += curNode.length; |
||
147 | } |
||
148 | if (startNode && endNode) { |
||
149 | curNode = replaceFn({ |
||
150 | startNode: startNode, |
||
151 | startNodeIndex: startNodeIndex, |
||
152 | endNode: endNode, |
||
153 | endNodeIndex: endNodeIndex, |
||
154 | innerNodes: innerNodes, |
||
155 | match: matchLocation.text, |
||
156 | matchIndex: matchIndex |
||
157 | }); |
||
158 | atIndex -= endNode.length - endNodeIndex; |
||
159 | startNode = null; |
||
160 | endNode = null; |
||
161 | innerNodes = []; |
||
162 | matchLocation = matches.shift(); |
||
163 | matchIndex++; |
||
164 | if (!matchLocation) { |
||
165 | break; |
||
166 | } |
||
167 | } else if ((!hiddenTextElementsMap[curNode.nodeName] || blockElementsMap[curNode.nodeName]) && curNode.firstChild) { |
||
168 | if (!isContentEditableFalse(curNode)) { |
||
169 | curNode = curNode.firstChild; |
||
170 | continue; |
||
171 | } |
||
172 | } else if (curNode.nextSibling) { |
||
173 | curNode = curNode.nextSibling; |
||
174 | continue; |
||
175 | } |
||
176 | while (true) { |
||
177 | if (curNode.nextSibling) { |
||
178 | curNode = curNode.nextSibling; |
||
179 | break; |
||
180 | } else if (curNode.parentNode !== node) { |
||
181 | curNode = curNode.parentNode; |
||
182 | } else { |
||
183 | break out; |
||
184 | } |
||
185 | } |
||
186 | } |
||
187 | } |
||
188 | function genReplacer(callback) { |
||
189 | function makeReplacementNode(fill, matchIndex) { |
||
190 | var match = matches[matchIndex]; |
||
191 | if (!match.stencil) { |
||
192 | match.stencil = callback(match); |
||
193 | } |
||
194 | var clone = match.stencil.cloneNode(false); |
||
195 | clone.setAttribute('data-mce-index', matchIndex); |
||
196 | if (fill) { |
||
197 | clone.appendChild(dom.doc.createTextNode(fill)); |
||
198 | } |
||
199 | return clone; |
||
200 | } |
||
201 | View Code Duplication | return function (range) { |
|
202 | var before; |
||
203 | var after; |
||
204 | var parentNode; |
||
205 | var startNode = range.startNode; |
||
206 | var endNode = range.endNode; |
||
207 | var matchIndex = range.matchIndex; |
||
208 | var doc = dom.doc; |
||
209 | if (startNode === endNode) { |
||
210 | var node_1 = startNode; |
||
211 | parentNode = node_1.parentNode; |
||
212 | if (range.startNodeIndex > 0) { |
||
213 | before = doc.createTextNode(node_1.data.substring(0, range.startNodeIndex)); |
||
214 | parentNode.insertBefore(before, node_1); |
||
215 | } |
||
216 | var el = makeReplacementNode(range.match, matchIndex); |
||
217 | parentNode.insertBefore(el, node_1); |
||
218 | if (range.endNodeIndex < node_1.length) { |
||
219 | after = doc.createTextNode(node_1.data.substring(range.endNodeIndex)); |
||
220 | parentNode.insertBefore(after, node_1); |
||
221 | } |
||
222 | node_1.parentNode.removeChild(node_1); |
||
223 | return el; |
||
224 | } |
||
225 | before = doc.createTextNode(startNode.data.substring(0, range.startNodeIndex)); |
||
226 | after = doc.createTextNode(endNode.data.substring(range.endNodeIndex)); |
||
227 | var elA = makeReplacementNode(startNode.data.substring(range.startNodeIndex), matchIndex); |
||
228 | for (var i = 0, l = range.innerNodes.length; i < l; ++i) { |
||
229 | var innerNode = range.innerNodes[i]; |
||
230 | var innerEl = makeReplacementNode(innerNode.data, matchIndex); |
||
231 | innerNode.parentNode.replaceChild(innerEl, innerNode); |
||
232 | } |
||
233 | var elB = makeReplacementNode(endNode.data.substring(0, range.endNodeIndex), matchIndex); |
||
234 | parentNode = startNode.parentNode; |
||
235 | parentNode.insertBefore(before, startNode); |
||
236 | parentNode.insertBefore(elA, startNode); |
||
237 | parentNode.removeChild(startNode); |
||
238 | parentNode = endNode.parentNode; |
||
239 | parentNode.insertBefore(elB, endNode); |
||
240 | parentNode.insertBefore(after, endNode); |
||
241 | parentNode.removeChild(endNode); |
||
242 | return elB; |
||
243 | }; |
||
244 | } |
||
245 | function unwrapElement(element) { |
||
246 | var parentNode = element.parentNode; |
||
247 | parentNode.insertBefore(element.firstChild, element); |
||
248 | element.parentNode.removeChild(element); |
||
249 | } |
||
250 | function hasClass(elm) { |
||
251 | return elm.className.indexOf('mce-spellchecker-word') !== -1; |
||
252 | } |
||
253 | function getWrappersByIndex(index) { |
||
254 | var elements = node.getElementsByTagName('*'), wrappers = []; |
||
255 | index = typeof index === 'number' ? '' + index : null; |
||
256 | for (var i = 0; i < elements.length; i++) { |
||
257 | var element = elements[i], dataIndex = element.getAttribute('data-mce-index'); |
||
258 | if (dataIndex !== null && dataIndex.length && hasClass(element)) { |
||
259 | if (dataIndex === index || index === null) { |
||
260 | wrappers.push(element); |
||
261 | } |
||
262 | } |
||
263 | } |
||
264 | return wrappers; |
||
265 | } |
||
266 | function indexOf(match) { |
||
267 | var i = matches.length; |
||
268 | while (i--) { |
||
269 | if (matches[i] === match) { |
||
270 | return i; |
||
271 | } |
||
272 | } |
||
273 | return -1; |
||
274 | } |
||
275 | function filter(callback) { |
||
276 | var filteredMatches = []; |
||
277 | each(function (match, i) { |
||
278 | if (callback(match, i)) { |
||
279 | filteredMatches.push(match); |
||
280 | } |
||
281 | }); |
||
282 | matches = filteredMatches; |
||
283 | return this; |
||
284 | } |
||
285 | function each(callback) { |
||
286 | for (var i = 0, l = matches.length; i < l; i++) { |
||
287 | if (callback(matches[i], i) === false) { |
||
288 | break; |
||
289 | } |
||
290 | } |
||
291 | return this; |
||
292 | } |
||
293 | function wrap(callback) { |
||
294 | if (matches.length) { |
||
295 | stepThroughMatches(node, matches, genReplacer(callback)); |
||
296 | } |
||
297 | return this; |
||
298 | } |
||
299 | function find(regex, data) { |
||
300 | if (text && regex.global) { |
||
301 | while (m = regex.exec(text)) { |
||
302 | matches.push(createMatch(m, data)); |
||
303 | } |
||
304 | } |
||
305 | return this; |
||
306 | } |
||
307 | function unwrap(match) { |
||
308 | var i; |
||
309 | var elements = getWrappersByIndex(match ? indexOf(match) : null); |
||
310 | i = elements.length; |
||
311 | while (i--) { |
||
312 | unwrapElement(elements[i]); |
||
313 | } |
||
314 | return this; |
||
315 | } |
||
316 | function matchFromElement(element) { |
||
317 | return matches[element.getAttribute('data-mce-index')]; |
||
318 | } |
||
319 | function elementFromMatch(match) { |
||
320 | return getWrappersByIndex(indexOf(match))[0]; |
||
321 | } |
||
322 | function add(start, length, data) { |
||
323 | matches.push({ |
||
324 | start: start, |
||
325 | end: start + length, |
||
326 | text: text.substr(start, length), |
||
327 | data: data |
||
328 | }); |
||
329 | return this; |
||
330 | } |
||
331 | function rangeFromMatch(match) { |
||
332 | var wrappers = getWrappersByIndex(indexOf(match)); |
||
333 | var rng = editor.dom.createRng(); |
||
334 | rng.setStartBefore(wrappers[0]); |
||
335 | rng.setEndAfter(wrappers[wrappers.length - 1]); |
||
336 | return rng; |
||
337 | } |
||
338 | function replace(match, text) { |
||
339 | var rng = rangeFromMatch(match); |
||
340 | rng.deleteContents(); |
||
341 | if (text.length > 0) { |
||
342 | rng.insertNode(editor.dom.doc.createTextNode(text)); |
||
343 | } |
||
344 | return rng; |
||
345 | } |
||
346 | function reset() { |
||
347 | matches.splice(0, matches.length); |
||
348 | unwrap(); |
||
349 | return this; |
||
350 | } |
||
351 | text = getText(node); |
||
352 | return { |
||
353 | text: text, |
||
354 | matches: matches, |
||
355 | each: each, |
||
356 | filter: filter, |
||
357 | reset: reset, |
||
358 | matchFromElement: matchFromElement, |
||
359 | elementFromMatch: elementFromMatch, |
||
360 | find: find, |
||
361 | add: add, |
||
362 | wrap: wrap, |
||
363 | unwrap: unwrap, |
||
364 | replace: replace, |
||
365 | rangeFromMatch: rangeFromMatch, |
||
366 | indexOf: indexOf |
||
367 | }; |
||
368 | }; |
||
369 | |||
370 | var getTextMatcher = function (editor, textMatcherState) { |
||
371 | if (!textMatcherState.get()) { |
||
372 | var textMatcher = DomTextMatcher(editor.getBody(), editor); |
||
373 | textMatcherState.set(textMatcher); |
||
374 | } |
||
375 | return textMatcherState.get(); |
||
376 | }; |
||
377 | var isEmpty = function (obj) { |
||
378 | for (var _ in obj) { |
||
379 | return false; |
||
380 | } |
||
381 | return true; |
||
382 | }; |
||
383 | var defaultSpellcheckCallback = function (editor, pluginUrl, currentLanguageState) { |
||
384 | return function (method, text, doneCallback, errorCallback) { |
||
385 | var data = { |
||
386 | method: method, |
||
387 | lang: currentLanguageState.get() |
||
388 | }; |
||
389 | var postData = ''; |
||
390 | data[method === 'addToDictionary' ? 'word' : 'text'] = text; |
||
391 | global$1.each(data, function (value, key) { |
||
392 | if (postData) { |
||
393 | postData += '&'; |
||
394 | } |
||
395 | postData += key + '=' + encodeURIComponent(value); |
||
396 | }); |
||
397 | global$3.send({ |
||
398 | url: new global$2(pluginUrl).toAbsolute(Settings.getRpcUrl(editor)), |
||
399 | type: 'post', |
||
400 | content_type: 'application/x-www-form-urlencoded', |
||
401 | data: postData, |
||
402 | success: function (result) { |
||
403 | result = JSON.parse(result); |
||
404 | if (!result) { |
||
405 | var message = editor.translate('Server response wasn\'t proper JSON.'); |
||
406 | errorCallback(message); |
||
407 | } else if (result.error) { |
||
408 | errorCallback(result.error); |
||
409 | } else { |
||
410 | doneCallback(result); |
||
411 | } |
||
412 | }, |
||
413 | error: function () { |
||
414 | var message = editor.translate('The spelling service was not found: (') + Settings.getRpcUrl(editor) + editor.translate(')'); |
||
415 | errorCallback(message); |
||
416 | } |
||
417 | }); |
||
418 | }; |
||
419 | }; |
||
420 | var sendRpcCall = function (editor, pluginUrl, currentLanguageState, name, data, successCallback, errorCallback) { |
||
421 | var userSpellcheckCallback = Settings.getSpellcheckerCallback(editor); |
||
422 | var spellCheckCallback = userSpellcheckCallback ? userSpellcheckCallback : defaultSpellcheckCallback(editor, pluginUrl, currentLanguageState); |
||
423 | spellCheckCallback.call(editor.plugins.spellchecker, name, data, successCallback, errorCallback); |
||
424 | }; |
||
425 | var spellcheck = function (editor, pluginUrl, startedState, textMatcherState, lastSuggestionsState, currentLanguageState) { |
||
426 | if (finish(editor, startedState, textMatcherState)) { |
||
427 | return; |
||
428 | } |
||
429 | var errorCallback = function (message) { |
||
430 | editor.notificationManager.open({ |
||
431 | text: message, |
||
432 | type: 'error' |
||
433 | }); |
||
434 | editor.setProgressState(false); |
||
435 | finish(editor, startedState, textMatcherState); |
||
436 | }; |
||
437 | var successCallback = function (data) { |
||
438 | markErrors(editor, startedState, textMatcherState, lastSuggestionsState, data); |
||
439 | }; |
||
440 | editor.setProgressState(true); |
||
441 | sendRpcCall(editor, pluginUrl, currentLanguageState, 'spellcheck', getTextMatcher(editor, textMatcherState).text, successCallback, errorCallback); |
||
442 | editor.focus(); |
||
443 | }; |
||
444 | var checkIfFinished = function (editor, startedState, textMatcherState) { |
||
445 | if (!editor.dom.select('span.mce-spellchecker-word').length) { |
||
446 | finish(editor, startedState, textMatcherState); |
||
447 | } |
||
448 | }; |
||
449 | var addToDictionary = function (editor, pluginUrl, startedState, textMatcherState, currentLanguageState, word, spans) { |
||
450 | editor.setProgressState(true); |
||
451 | sendRpcCall(editor, pluginUrl, currentLanguageState, 'addToDictionary', word, function () { |
||
452 | editor.setProgressState(false); |
||
453 | editor.dom.remove(spans, true); |
||
454 | checkIfFinished(editor, startedState, textMatcherState); |
||
455 | }, function (message) { |
||
456 | editor.notificationManager.open({ |
||
457 | text: message, |
||
458 | type: 'error' |
||
459 | }); |
||
460 | editor.setProgressState(false); |
||
461 | }); |
||
462 | }; |
||
463 | var ignoreWord = function (editor, startedState, textMatcherState, word, spans, all) { |
||
464 | editor.selection.collapse(); |
||
465 | if (all) { |
||
466 | global$1.each(editor.dom.select('span.mce-spellchecker-word'), function (span) { |
||
467 | if (span.getAttribute('data-mce-word') === word) { |
||
468 | editor.dom.remove(span, true); |
||
469 | } |
||
470 | }); |
||
471 | } else { |
||
472 | editor.dom.remove(spans, true); |
||
473 | } |
||
474 | checkIfFinished(editor, startedState, textMatcherState); |
||
475 | }; |
||
476 | var finish = function (editor, startedState, textMatcherState) { |
||
477 | getTextMatcher(editor, textMatcherState).reset(); |
||
478 | textMatcherState.set(null); |
||
479 | if (startedState.get()) { |
||
480 | startedState.set(false); |
||
481 | Events.fireSpellcheckEnd(editor); |
||
482 | return true; |
||
483 | } |
||
484 | }; |
||
485 | var getElmIndex = function (elm) { |
||
486 | var value = elm.getAttribute('data-mce-index'); |
||
487 | if (typeof value === 'number') { |
||
488 | return '' + value; |
||
489 | } |
||
490 | return value; |
||
491 | }; |
||
492 | View Code Duplication | var findSpansByIndex = function (editor, index) { |
|
493 | var nodes; |
||
494 | var spans = []; |
||
495 | nodes = global$1.toArray(editor.getBody().getElementsByTagName('span')); |
||
496 | if (nodes.length) { |
||
497 | for (var i = 0; i < nodes.length; i++) { |
||
498 | var nodeIndex = getElmIndex(nodes[i]); |
||
499 | if (nodeIndex === null || !nodeIndex.length) { |
||
500 | continue; |
||
501 | } |
||
502 | if (nodeIndex === index.toString()) { |
||
503 | spans.push(nodes[i]); |
||
504 | } |
||
505 | } |
||
506 | } |
||
507 | return spans; |
||
508 | }; |
||
509 | var markErrors = function (editor, startedState, textMatcherState, lastSuggestionsState, data) { |
||
510 | var suggestions, hasDictionarySupport; |
||
511 | if (typeof data !== 'string' && data.words) { |
||
512 | hasDictionarySupport = !!data.dictionary; |
||
513 | suggestions = data.words; |
||
514 | } else { |
||
515 | suggestions = data; |
||
516 | } |
||
517 | editor.setProgressState(false); |
||
518 | if (isEmpty(suggestions)) { |
||
519 | var message = editor.translate('No misspellings found.'); |
||
520 | editor.notificationManager.open({ |
||
521 | text: message, |
||
522 | type: 'info' |
||
523 | }); |
||
524 | startedState.set(false); |
||
525 | return; |
||
526 | } |
||
527 | lastSuggestionsState.set({ |
||
528 | suggestions: suggestions, |
||
529 | hasDictionarySupport: hasDictionarySupport |
||
530 | }); |
||
531 | getTextMatcher(editor, textMatcherState).find(Settings.getSpellcheckerWordcharPattern(editor)).filter(function (match) { |
||
532 | return !!suggestions[match.text]; |
||
533 | }).wrap(function (match) { |
||
534 | return editor.dom.create('span', { |
||
535 | 'class': 'mce-spellchecker-word', |
||
536 | 'data-mce-bogus': 1, |
||
537 | 'data-mce-word': match.text |
||
538 | }); |
||
539 | }); |
||
540 | startedState.set(true); |
||
541 | Events.fireSpellcheckStart(editor); |
||
542 | }; |
||
543 | var Actions = { |
||
544 | spellcheck: spellcheck, |
||
545 | checkIfFinished: checkIfFinished, |
||
546 | addToDictionary: addToDictionary, |
||
547 | ignoreWord: ignoreWord, |
||
548 | findSpansByIndex: findSpansByIndex, |
||
549 | getElmIndex: getElmIndex, |
||
550 | markErrors: markErrors |
||
551 | }; |
||
552 | |||
553 | var get = function (editor, startedState, lastSuggestionsState, textMatcherState, currentLanguageState, url) { |
||
554 | var getLanguage = function () { |
||
555 | return currentLanguageState.get(); |
||
556 | }; |
||
557 | var getWordCharPattern = function () { |
||
558 | return Settings.getSpellcheckerWordcharPattern(editor); |
||
559 | }; |
||
560 | var markErrors = function (data) { |
||
561 | Actions.markErrors(editor, startedState, textMatcherState, lastSuggestionsState, data); |
||
562 | }; |
||
563 | var getTextMatcher = function () { |
||
564 | return textMatcherState.get(); |
||
565 | }; |
||
566 | return { |
||
567 | getTextMatcher: getTextMatcher, |
||
568 | getWordCharPattern: getWordCharPattern, |
||
569 | markErrors: markErrors, |
||
570 | getLanguage: getLanguage |
||
571 | }; |
||
572 | }; |
||
573 | var Api = { get: get }; |
||
574 | |||
575 | var register = function (editor, pluginUrl, startedState, textMatcherState, lastSuggestionsState, currentLanguageState) { |
||
576 | editor.addCommand('mceSpellCheck', function () { |
||
577 | Actions.spellcheck(editor, pluginUrl, startedState, textMatcherState, lastSuggestionsState, currentLanguageState); |
||
578 | }); |
||
579 | }; |
||
580 | var Commands = { register: register }; |
||
581 | |||
582 | var buildMenuItems = function (listName, languageValues) { |
||
583 | var items = []; |
||
584 | global$1.each(languageValues, function (languageValue) { |
||
585 | items.push({ |
||
586 | selectable: true, |
||
587 | text: languageValue.name, |
||
588 | data: languageValue.value |
||
589 | }); |
||
590 | }); |
||
591 | return items; |
||
592 | }; |
||
593 | var updateSelection = function (editor, currentLanguageState) { |
||
594 | return function (e) { |
||
595 | var selectedLanguage = currentLanguageState.get(); |
||
596 | e.control.items().each(function (ctrl) { |
||
597 | ctrl.active(ctrl.settings.data === selectedLanguage); |
||
598 | }); |
||
599 | }; |
||
600 | }; |
||
601 | var getItems = function (editor) { |
||
602 | return global$1.map(Settings.getLanguages(editor).split(','), function (langPair) { |
||
603 | langPair = langPair.split('='); |
||
604 | return { |
||
605 | name: langPair[0], |
||
606 | value: langPair[1] |
||
607 | }; |
||
608 | }); |
||
609 | }; |
||
610 | var register$1 = function (editor, pluginUrl, startedState, textMatcherState, currentLanguageState, lastSuggestionsState) { |
||
611 | var languageMenuItems = buildMenuItems('Language', getItems(editor)); |
||
612 | var startSpellchecking = function () { |
||
613 | Actions.spellcheck(editor, pluginUrl, startedState, textMatcherState, lastSuggestionsState, currentLanguageState); |
||
614 | }; |
||
615 | var buttonArgs = { |
||
616 | tooltip: 'Spellcheck', |
||
617 | onclick: startSpellchecking, |
||
618 | onPostRender: function (e) { |
||
619 | var ctrl = e.control; |
||
620 | editor.on('SpellcheckStart SpellcheckEnd', function () { |
||
621 | ctrl.active(startedState.get()); |
||
622 | }); |
||
623 | } |
||
624 | }; |
||
625 | if (languageMenuItems.length > 1) { |
||
626 | buttonArgs.type = 'splitbutton'; |
||
627 | buttonArgs.menu = languageMenuItems; |
||
628 | buttonArgs.onshow = updateSelection(editor, currentLanguageState); |
||
629 | buttonArgs.onselect = function (e) { |
||
630 | currentLanguageState.set(e.control.settings.data); |
||
631 | }; |
||
632 | } |
||
633 | editor.addButton('spellchecker', buttonArgs); |
||
634 | editor.addMenuItem('spellchecker', { |
||
635 | text: 'Spellcheck', |
||
636 | context: 'tools', |
||
637 | onclick: startSpellchecking, |
||
638 | selectable: true, |
||
639 | onPostRender: function () { |
||
640 | var self = this; |
||
641 | self.active(startedState.get()); |
||
642 | editor.on('SpellcheckStart SpellcheckEnd', function () { |
||
643 | self.active(startedState.get()); |
||
644 | }); |
||
645 | } |
||
646 | }); |
||
647 | }; |
||
648 | var Buttons = { register: register$1 }; |
||
649 | |||
650 | var global$4 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils'); |
||
651 | |||
652 | var global$5 = tinymce.util.Tools.resolve('tinymce.ui.Factory'); |
||
653 | |||
654 | var suggestionsMenu; |
||
655 | var showSuggestions = function (editor, pluginUrl, lastSuggestionsState, startedState, textMatcherState, currentLanguageState, word, spans) { |
||
656 | var items = [], suggestions = lastSuggestionsState.get().suggestions[word]; |
||
657 | global$1.each(suggestions, function (suggestion) { |
||
658 | items.push({ |
||
659 | text: suggestion, |
||
660 | onclick: function () { |
||
661 | editor.insertContent(editor.dom.encode(suggestion)); |
||
662 | editor.dom.remove(spans); |
||
663 | Actions.checkIfFinished(editor, startedState, textMatcherState); |
||
664 | } |
||
665 | }); |
||
666 | }); |
||
667 | items.push({ text: '-' }); |
||
668 | var hasDictionarySupport = lastSuggestionsState.get().hasDictionarySupport; |
||
669 | if (hasDictionarySupport) { |
||
670 | items.push({ |
||
671 | text: 'Add to Dictionary', |
||
672 | onclick: function () { |
||
673 | Actions.addToDictionary(editor, pluginUrl, startedState, textMatcherState, currentLanguageState, word, spans); |
||
674 | } |
||
675 | }); |
||
676 | } |
||
677 | items.push.apply(items, [ |
||
678 | { |
||
679 | text: 'Ignore', |
||
680 | onclick: function () { |
||
681 | Actions.ignoreWord(editor, startedState, textMatcherState, word, spans); |
||
682 | } |
||
683 | }, |
||
684 | { |
||
685 | text: 'Ignore all', |
||
686 | onclick: function () { |
||
687 | Actions.ignoreWord(editor, startedState, textMatcherState, word, spans, true); |
||
688 | } |
||
689 | } |
||
690 | ]); |
||
691 | suggestionsMenu = global$5.create('menu', { |
||
692 | items: items, |
||
693 | context: 'contextmenu', |
||
694 | onautohide: function (e) { |
||
695 | if (e.target.className.indexOf('spellchecker') !== -1) { |
||
696 | e.preventDefault(); |
||
697 | } |
||
698 | }, |
||
699 | onhide: function () { |
||
700 | suggestionsMenu.remove(); |
||
701 | suggestionsMenu = null; |
||
702 | } |
||
703 | }); |
||
704 | suggestionsMenu.renderTo(document.body); |
||
705 | var pos = global$4.DOM.getPos(editor.getContentAreaContainer()); |
||
706 | var targetPos = editor.dom.getPos(spans[0]); |
||
707 | var root = editor.dom.getRoot(); |
||
708 | if (root.nodeName === 'BODY') { |
||
709 | targetPos.x -= root.ownerDocument.documentElement.scrollLeft || root.scrollLeft; |
||
710 | targetPos.y -= root.ownerDocument.documentElement.scrollTop || root.scrollTop; |
||
711 | } else { |
||
712 | targetPos.x -= root.scrollLeft; |
||
713 | targetPos.y -= root.scrollTop; |
||
714 | } |
||
715 | pos.x += targetPos.x; |
||
716 | pos.y += targetPos.y; |
||
717 | suggestionsMenu.moveTo(pos.x, pos.y + spans[0].offsetHeight); |
||
718 | }; |
||
719 | var setup = function (editor, pluginUrl, lastSuggestionsState, startedState, textMatcherState, currentLanguageState) { |
||
720 | editor.on('click', function (e) { |
||
721 | var target = e.target; |
||
722 | if (target.className === 'mce-spellchecker-word') { |
||
723 | e.preventDefault(); |
||
724 | var spans = Actions.findSpansByIndex(editor, Actions.getElmIndex(target)); |
||
725 | if (spans.length > 0) { |
||
726 | var rng = editor.dom.createRng(); |
||
727 | rng.setStartBefore(spans[0]); |
||
728 | rng.setEndAfter(spans[spans.length - 1]); |
||
729 | editor.selection.setRng(rng); |
||
730 | showSuggestions(editor, pluginUrl, lastSuggestionsState, startedState, textMatcherState, currentLanguageState, target.getAttribute('data-mce-word'), spans); |
||
731 | } |
||
732 | } |
||
733 | }); |
||
734 | }; |
||
735 | var SuggestionsMenu = { setup: setup }; |
||
736 | |||
737 | global.add('spellchecker', function (editor, pluginUrl) { |
||
738 | if (DetectProPlugin.hasProPlugin(editor) === false) { |
||
739 | var startedState = Cell(false); |
||
740 | var currentLanguageState = Cell(Settings.getLanguage(editor)); |
||
741 | var textMatcherState = Cell(null); |
||
742 | var lastSuggestionsState = Cell(null); |
||
743 | Buttons.register(editor, pluginUrl, startedState, textMatcherState, currentLanguageState, lastSuggestionsState); |
||
744 | SuggestionsMenu.setup(editor, pluginUrl, lastSuggestionsState, startedState, textMatcherState, currentLanguageState); |
||
745 | Commands.register(editor, pluginUrl, startedState, textMatcherState, lastSuggestionsState, currentLanguageState); |
||
746 | return Api.get(editor, startedState, lastSuggestionsState, textMatcherState, currentLanguageState, pluginUrl); |
||
747 | } |
||
748 | }); |
||
749 | function Plugin () { |
||
750 | } |
||
751 | |||
752 | return Plugin; |
||
753 | |||
754 | }()); |
||
755 | })(); |
||
756 |
This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.
To learn more about declaring variables in Javascript, see the MDN.