src/pyams_skin/resources/js/ext/tinymce/dev/plugins/spellchecker/plugin.js
changeset 566 a1707c607eec
parent 565 318533413200
child 567 bca1726b1d85
--- a/src/pyams_skin/resources/js/ext/tinymce/dev/plugins/spellchecker/plugin.js	Sun Jul 19 02:02:20 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,996 +0,0 @@
-/**
- * Compiled inline version. (Library mode)
- */
-
-/*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
-/*globals $code */
-
-(function(exports, undefined) {
-	"use strict";
-
-	var modules = {};
-
-	function require(ids, callback) {
-		var module, defs = [];
-
-		for (var i = 0; i < ids.length; ++i) {
-			module = modules[ids[i]] || resolve(ids[i]);
-			if (!module) {
-				throw 'module definition dependecy not found: ' + ids[i];
-			}
-
-			defs.push(module);
-		}
-
-		callback.apply(null, defs);
-	}
-
-	function define(id, dependencies, definition) {
-		if (typeof id !== 'string') {
-			throw 'invalid module definition, module id must be defined and be a string';
-		}
-
-		if (dependencies === undefined) {
-			throw 'invalid module definition, dependencies must be specified';
-		}
-
-		if (definition === undefined) {
-			throw 'invalid module definition, definition function must be specified';
-		}
-
-		require(dependencies, function() {
-			modules[id] = definition.apply(null, arguments);
-		});
-	}
-
-	function defined(id) {
-		return !!modules[id];
-	}
-
-	function resolve(id) {
-		var target = exports;
-		var fragments = id.split(/[.\/]/);
-
-		for (var fi = 0; fi < fragments.length; ++fi) {
-			if (!target[fragments[fi]]) {
-				return;
-			}
-
-			target = target[fragments[fi]];
-		}
-
-		return target;
-	}
-
-	function expose(ids) {
-		for (var i = 0; i < ids.length; i++) {
-			var target = exports;
-			var id = ids[i];
-			var fragments = id.split(/[.\/]/);
-
-			for (var fi = 0; fi < fragments.length - 1; ++fi) {
-				if (target[fragments[fi]] === undefined) {
-					target[fragments[fi]] = {};
-				}
-
-				target = target[fragments[fi]];
-			}
-
-			target[fragments[fragments.length - 1]] = modules[id];
-		}
-	}
-
-// Included from: js/tinymce/plugins/spellchecker/classes/DomTextMatcher.js
-
-/**
- * DomTextMatcher.js
- *
- * Copyright, Moxiecode Systems AB
- * Released under LGPL License.
- *
- * License: http://www.tinymce.com/license
- * Contributing: http://www.tinymce.com/contributing
- */
-
-/*eslint no-labels:0, no-constant-condition: 0 */
-
-/**
- * This class logic for filtering text and matching words.
- *
- * @class tinymce.spellcheckerplugin.TextFilter
- * @private
- */
-define("tinymce/spellcheckerplugin/DomTextMatcher", [], function() {
-	// Based on work developed by: James Padolsey http://james.padolsey.com
-	// released under UNLICENSE that is compatible with LGPL
-	// TODO: Handle contentEditable edgecase:
-	// <p>text<span contentEditable="false">text<span contentEditable="true">text</span>text</span>text</p>
-	return function(node, editor) {
-		var m, matches = [], text, dom = editor.dom;
-		var blockElementsMap, hiddenTextElementsMap, shortEndedElementsMap;
-
-		blockElementsMap = editor.schema.getBlockElements(); // H1-H6, P, TD etc
-		hiddenTextElementsMap = editor.schema.getWhiteSpaceElements(); // TEXTAREA, PRE, STYLE, SCRIPT
-		shortEndedElementsMap = editor.schema.getShortEndedElements(); // BR, IMG, INPUT
-
-		function createMatch(m, data) {
-			if (!m[0]) {
-				throw 'findAndReplaceDOMText cannot handle zero-length matches';
-			}
-
-			return {
-				start: m.index,
-				end: m.index + m[0].length,
-				text: m[0],
-				data: data
-			};
-		}
-
-		function getText(node) {
-			var txt;
-
-			if (node.nodeType === 3) {
-				return node.data;
-			}
-
-			if (hiddenTextElementsMap[node.nodeName] && !blockElementsMap[node.nodeName]) {
-				return '';
-			}
-
-			txt = '';
-
-			if (blockElementsMap[node.nodeName] || shortEndedElementsMap[node.nodeName]) {
-				txt += '\n';
-			}
-
-			if ((node = node.firstChild)) {
-				do {
-					txt += getText(node);
-				} while ((node = node.nextSibling));
-			}
-
-			return txt;
-		}
-
-		function stepThroughMatches(node, matches, replaceFn) {
-			var startNode, endNode, startNodeIndex,
-				endNodeIndex, innerNodes = [], atIndex = 0, curNode = node,
-				matchLocation, matchIndex = 0;
-
-			matches = matches.slice(0);
-			matches.sort(function(a, b) {
-				return a.start - b.start;
-			});
-
-			matchLocation = matches.shift();
-
-			out: while (true) {
-				if (blockElementsMap[curNode.nodeName] || shortEndedElementsMap[curNode.nodeName]) {
-					atIndex++;
-				}
-
-				if (curNode.nodeType === 3) {
-					if (!endNode && curNode.length + atIndex >= matchLocation.end) {
-						// We've found the ending
-						endNode = curNode;
-						endNodeIndex = matchLocation.end - atIndex;
-					} else if (startNode) {
-						// Intersecting node
-						innerNodes.push(curNode);
-					}
-
-					if (!startNode && curNode.length + atIndex > matchLocation.start) {
-						// We've found the match start
-						startNode = curNode;
-						startNodeIndex = matchLocation.start - atIndex;
-					}
-
-					atIndex += curNode.length;
-				}
-
-				if (startNode && endNode) {
-					curNode = replaceFn({
-						startNode: startNode,
-						startNodeIndex: startNodeIndex,
-						endNode: endNode,
-						endNodeIndex: endNodeIndex,
-						innerNodes: innerNodes,
-						match: matchLocation.text,
-						matchIndex: matchIndex
-					});
-
-					// replaceFn has to return the node that replaced the endNode
-					// and then we step back so we can continue from the end of the
-					// match:
-					atIndex -= (endNode.length - endNodeIndex);
-					startNode = null;
-					endNode = null;
-					innerNodes = [];
-					matchLocation = matches.shift();
-					matchIndex++;
-
-					if (!matchLocation) {
-						break; // no more matches
-					}
-				} else if ((!hiddenTextElementsMap[curNode.nodeName] || blockElementsMap[curNode.nodeName]) && curNode.firstChild) {
-					// Move down
-					curNode = curNode.firstChild;
-					continue;
-				} else if (curNode.nextSibling) {
-					// Move forward:
-					curNode = curNode.nextSibling;
-					continue;
-				}
-
-				// Move forward or up:
-				while (true) {
-					if (curNode.nextSibling) {
-						curNode = curNode.nextSibling;
-						break;
-					} else if (curNode.parentNode !== node) {
-						curNode = curNode.parentNode;
-					} else {
-						break out;
-					}
-				}
-			}
-		}
-
-		/**
-		* Generates the actual replaceFn which splits up text nodes
-		* and inserts the replacement element.
-		*/
-		function genReplacer(callback) {
-			function makeReplacementNode(fill, matchIndex) {
-				var match = matches[matchIndex];
-
-				if (!match.stencil) {
-					match.stencil = callback(match);
-				}
-
-				var clone = match.stencil.cloneNode(false);
-				clone.setAttribute('data-mce-index', matchIndex);
-
-				if (fill) {
-					clone.appendChild(dom.doc.createTextNode(fill));
-				}
-
-				return clone;
-			}
-
-			return function(range) {
-				var before, after, parentNode, startNode = range.startNode,
-					endNode = range.endNode, matchIndex = range.matchIndex,
-					doc = dom.doc;
-
-				if (startNode === endNode) {
-					var node = startNode;
-
-					parentNode = node.parentNode;
-					if (range.startNodeIndex > 0) {
-						// Add "before" text node (before the match)
-						before = doc.createTextNode(node.data.substring(0, range.startNodeIndex));
-						parentNode.insertBefore(before, node);
-					}
-
-					// Create the replacement node:
-					var el = makeReplacementNode(range.match, matchIndex);
-					parentNode.insertBefore(el, node);
-					if (range.endNodeIndex < node.length) {
-						// Add "after" text node (after the match)
-						after = doc.createTextNode(node.data.substring(range.endNodeIndex));
-						parentNode.insertBefore(after, node);
-					}
-
-					node.parentNode.removeChild(node);
-
-					return el;
-				} else {
-					// Replace startNode -> [innerNodes...] -> endNode (in that order)
-					before = doc.createTextNode(startNode.data.substring(0, range.startNodeIndex));
-					after = doc.createTextNode(endNode.data.substring(range.endNodeIndex));
-					var elA = makeReplacementNode(startNode.data.substring(range.startNodeIndex), matchIndex);
-					var innerEls = [];
-
-					for (var i = 0, l = range.innerNodes.length; i < l; ++i) {
-						var innerNode = range.innerNodes[i];
-						var innerEl = makeReplacementNode(innerNode.data, matchIndex);
-						innerNode.parentNode.replaceChild(innerEl, innerNode);
-						innerEls.push(innerEl);
-					}
-
-					var elB = makeReplacementNode(endNode.data.substring(0, range.endNodeIndex), matchIndex);
-
-					parentNode = startNode.parentNode;
-					parentNode.insertBefore(before, startNode);
-					parentNode.insertBefore(elA, startNode);
-					parentNode.removeChild(startNode);
-
-					parentNode = endNode.parentNode;
-					parentNode.insertBefore(elB, endNode);
-					parentNode.insertBefore(after, endNode);
-					parentNode.removeChild(endNode);
-
-					return elB;
-				}
-			};
-		}
-
-		function unwrapElement(element) {
-			var parentNode = element.parentNode;
-			parentNode.insertBefore(element.firstChild, element);
-			element.parentNode.removeChild(element);
-		}
-
-		function getWrappersByIndex(index) {
-			var elements = node.getElementsByTagName('*'), wrappers = [];
-
-			index = typeof index == "number" ? "" + index : null;
-
-			for (var i = 0; i < elements.length; i++) {
-				var element = elements[i], dataIndex = element.getAttribute('data-mce-index');
-
-				if (dataIndex !== null && dataIndex.length) {
-					if (dataIndex === index || index === null) {
-						wrappers.push(element);
-					}
-				}
-			}
-
-			return wrappers;
-		}
-
-		/**
-		 * Returns the index of a specific match object or -1 if it isn't found.
-		 *
-		 * @param  {Match} match Text match object.
-		 * @return {Number} Index of match or -1 if it isn't found.
-		 */
-		function indexOf(match) {
-			var i = matches.length;
-			while (i--) {
-				if (matches[i] === match) {
-					return i;
-				}
-			}
-
-			return -1;
-		}
-
-		/**
-		 * Filters the matches. If the callback returns true it stays if not it gets removed.
-		 *
-		 * @param {Function} callback Callback to execute for each match.
-		 * @return {DomTextMatcher} Current DomTextMatcher instance.
-		 */
-		function filter(callback) {
-			var filteredMatches = [];
-
-			each(function(match, i) {
-				if (callback(match, i)) {
-					filteredMatches.push(match);
-				}
-			});
-
-			matches = filteredMatches;
-
-			/*jshint validthis:true*/
-			return this;
-		}
-
-		/**
-		 * Executes the specified callback for each match.
-		 *
-		 * @param {Function} callback  Callback to execute for each match.
-		 * @return {DomTextMatcher} Current DomTextMatcher instance.
-		 */
-		function each(callback) {
-			for (var i = 0, l = matches.length; i < l; i++) {
-				if (callback(matches[i], i) === false) {
-					break;
-				}
-			}
-
-			/*jshint validthis:true*/
-			return this;
-		}
-
-		/**
-		 * Wraps the current matches with nodes created by the specified callback.
-		 * Multiple clones of these matches might occur on matches that are on multiple nodex.
-		 *
-		 * @param {Function} callback Callback to execute in order to create elements for matches.
-		 * @return {DomTextMatcher} Current DomTextMatcher instance.
-		 */
-		function wrap(callback) {
-			if (matches.length) {
-				stepThroughMatches(node, matches, genReplacer(callback));
-			}
-
-			/*jshint validthis:true*/
-			return this;
-		}
-
-		/**
-		 * Finds the specified regexp and adds them to the matches collection.
-		 *
-		 * @param {RegExp} regex Global regexp to search the current node by.
-		 * @param {Object} [data] Optional custom data element for the match.
-		 * @return {DomTextMatcher} Current DomTextMatcher instance.
-		 */
-		function find(regex, data) {
-			if (text && regex.global) {
-				while ((m = regex.exec(text))) {
-					matches.push(createMatch(m, data));
-				}
-			}
-
-			return this;
-		}
-
-		/**
-		 * Unwraps the specified match object or all matches if unspecified.
-		 *
-		 * @param {Object} [match] Optional match object.
-		 * @return {DomTextMatcher} Current DomTextMatcher instance.
-		 */
-		function unwrap(match) {
-			var i, elements = getWrappersByIndex(match ? indexOf(match) : null);
-
-			i = elements.length;
-			while (i--) {
-				unwrapElement(elements[i]);
-			}
-
-			return this;
-		}
-
-		/**
-		 * Returns a match object by the specified DOM element.
-		 *
-		 * @param {DOMElement} element Element to return match object for.
-		 * @return {Object} Match object for the specified element.
-		 */
-		function matchFromElement(element) {
-			return matches[element.getAttribute('data-mce-index')];
-		}
-
-		/**
-		 * Returns a DOM element from the specified match element. This will be the first element if it's split
-		 * on multiple nodes.
-		 *
-		 * @param {Object} match Match element to get first element of.
-		 * @return {DOMElement} DOM element for the specified match object.
-		 */
-		function elementFromMatch(match) {
-			return getWrappersByIndex(indexOf(match))[0];
-		}
-
-		/**
-		 * Adds match the specified range for example a grammar line.
-		 *
-		 * @param {Number} start Start offset.
-		 * @param {Number} length Length of the text.
-		 * @param {Object} data Custom data object for match.
-		 * @return {DomTextMatcher} Current DomTextMatcher instance.
-		 */
-		function add(start, length, data) {
-			matches.push({
-				start: start,
-				end: start + length,
-				text: text.substr(start, length),
-				data: data
-			});
-
-			return this;
-		}
-
-		/**
-		 * Returns a DOM range for the specified match.
-		 *
-		 * @param  {Object} match Match object to get range for.
-		 * @return {DOMRange} DOM Range for the specified match.
-		 */
-		function rangeFromMatch(match) {
-			var wrappers = getWrappersByIndex(indexOf(match));
-
-			var rng = editor.dom.createRng();
-			rng.setStartBefore(wrappers[0]);
-			rng.setEndAfter(wrappers[wrappers.length - 1]);
-
-			return rng;
-		}
-
-		/**
-		 * Replaces the specified match with the specified text.
-		 *
-		 * @param {Object} match Match object to replace.
-		 * @param {String} text Text to replace the match with.
-		 * @return {DOMRange} DOM range produced after the replace.
-		 */
-		function replace(match, text) {
-			var rng = rangeFromMatch(match);
-
-			rng.deleteContents();
-
-			if (text.length > 0) {
-				rng.insertNode(editor.dom.doc.createTextNode(text));
-			}
-
-			return rng;
-		}
-
-		/**
-		 * Resets the DomTextMatcher instance. This will remove any wrapped nodes and remove any matches.
-		 *
-		 * @return {[type]} [description]
-		 */
-		function reset() {
-			matches.splice(0, matches.length);
-			unwrap();
-
-			return this;
-		}
-
-		text = getText(node);
-
-		return {
-			text: text,
-			matches: matches,
-			each: each,
-			filter: filter,
-			reset: reset,
-			matchFromElement: matchFromElement,
-			elementFromMatch: elementFromMatch,
-			find: find,
-			add: add,
-			wrap: wrap,
-			unwrap: unwrap,
-			replace: replace,
-			rangeFromMatch: rangeFromMatch,
-			indexOf: indexOf
-		};
-	};
-});
-
-// Included from: js/tinymce/plugins/spellchecker/classes/Plugin.js
-
-/**
- * Plugin.js
- *
- * Copyright, Moxiecode Systems AB
- * Released under LGPL License.
- *
- * License: http://www.tinymce.com/license
- * Contributing: http://www.tinymce.com/contributing
- */
-
-/*jshint camelcase:false */
-
-/**
- * This class contains all core logic for the spellchecker plugin.
- *
- * @class tinymce.spellcheckerplugin.Plugin
- * @private
- */
-define("tinymce/spellcheckerplugin/Plugin", [
-	"tinymce/spellcheckerplugin/DomTextMatcher",
-	"tinymce/PluginManager",
-	"tinymce/util/Tools",
-	"tinymce/ui/Menu",
-	"tinymce/dom/DOMUtils",
-	"tinymce/util/XHR",
-	"tinymce/util/URI",
-	"tinymce/util/JSON"
-], function(DomTextMatcher, PluginManager, Tools, Menu, DOMUtils, XHR, URI, JSON) {
-	PluginManager.add('spellchecker', function(editor, url) {
-		var languageMenuItems, self = this, lastSuggestions, started, suggestionsMenu, settings = editor.settings;
-		var hasDictionarySupport;
-
-		function getTextMatcher() {
-			if (!self.textMatcher) {
-				self.textMatcher = new DomTextMatcher(editor.getBody(), editor);
-			}
-
-			return self.textMatcher;
-		}
-
-		function buildMenuItems(listName, languageValues) {
-			var items = [];
-
-			Tools.each(languageValues, function(languageValue) {
-				items.push({
-					selectable: true,
-					text: languageValue.name,
-					data: languageValue.value
-				});
-			});
-
-			return items;
-		}
-
-		var languagesString = settings.spellchecker_languages ||
-			'English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr_FR,' +
-			'German=de,Italian=it,Polish=pl,Portuguese=pt_BR,' +
-			'Spanish=es,Swedish=sv';
-
-		languageMenuItems = buildMenuItems('Language',
-			Tools.map(languagesString.split(','), function(langPair) {
-				langPair = langPair.split('=');
-
-				return {
-					name: langPair[0],
-					value: langPair[1]
-				};
-			})
-		);
-
-		function isEmpty(obj) {
-			/*jshint unused:false*/
-			/*eslint no-unused-vars:0 */
-			for (var name in obj) {
-				return false;
-			}
-
-			return true;
-		}
-
-		function showSuggestions(word, spans) {
-			var items = [], suggestions = lastSuggestions[word];
-
-			Tools.each(suggestions, function(suggestion) {
-				items.push({
-					text: suggestion,
-					onclick: function() {
-						editor.insertContent(editor.dom.encode(suggestion));
-						editor.dom.remove(spans);
-						checkIfFinished();
-					}
-				});
-			});
-
-			items.push({text: '-'});
-
-			if (hasDictionarySupport) {
-				items.push({text: 'Add to Dictionary', onclick: function() {
-					addToDictionary(word, spans);
-				}});
-			}
-
-			items.push.apply(items, [
-				{text: 'Ignore', onclick: function() {
-					ignoreWord(word, spans);
-				}},
-
-				{text: 'Ignore all', onclick: function() {
-					ignoreWord(word, spans, true);
-				}}
-			]);
-
-			// Render menu
-			suggestionsMenu = new Menu({
-				items: items,
-				context: 'contextmenu',
-				onautohide: function(e) {
-					if (e.target.className.indexOf('spellchecker') != -1) {
-						e.preventDefault();
-					}
-				},
-				onhide: function() {
-					suggestionsMenu.remove();
-					suggestionsMenu = null;
-				}
-			});
-
-			suggestionsMenu.renderTo(document.body);
-
-			// Position menu
-			var pos = DOMUtils.DOM.getPos(editor.getContentAreaContainer());
-			var targetPos = editor.dom.getPos(spans[0]);
-			var root = editor.dom.getRoot();
-
-			// Adjust targetPos for scrolling in the editor
-			if (root.nodeName == 'BODY') {
-				targetPos.x -= root.ownerDocument.documentElement.scrollLeft || root.scrollLeft;
-				targetPos.y -= root.ownerDocument.documentElement.scrollTop || root.scrollTop;
-			} else {
-				targetPos.x -= root.scrollLeft;
-				targetPos.y -= root.scrollTop;
-			}
-
-			pos.x += targetPos.x;
-			pos.y += targetPos.y;
-
-			suggestionsMenu.moveTo(pos.x, pos.y + spans[0].offsetHeight);
-		}
-
-		function getWordCharPattern() {
-			// Regexp for finding word specific characters this will split words by
-			// spaces, quotes, copy right characters etc. It's escaped with unicode characters
-			// to make it easier to output scripts on servers using different encodings
-			// so if you add any characters outside the 128 byte range make sure to escape it
-			return editor.getParam('spellchecker_wordchar_pattern') || new RegExp("[^" +
-				"\\s!\"#$%&()*+,-./:;<=>?@[\\]^_{|}`" +
-				"\u00a7\u00a9\u00ab\u00ae\u00b1\u00b6\u00b7\u00b8\u00bb" +
-				"\u00bc\u00bd\u00be\u00bf\u00d7\u00f7\u00a4\u201d\u201c\u201e\u00a0\u2002\u2003\u2009" +
-			"]+", "g");
-		}
-
-		function defaultSpellcheckCallback(method, text, doneCallback, errorCallback) {
-			var data = {method: method}, postData = '';
-
-			if (method == "spellcheck") {
-				data.text = text;
-				data.lang = settings.spellchecker_language;
-			}
-
-			if (method == "addToDictionary") {
-				data.word = text;
-			}
-
-			Tools.each(data, function(value, key) {
-				if (postData) {
-					postData += '&';
-				}
-
-				postData += key + '=' + encodeURIComponent(value);
-			});
-
-			XHR.send({
-				url: new URI(url).toAbsolute(settings.spellchecker_rpc_url),
-				type: "post",
-				content_type: 'application/x-www-form-urlencoded',
-				data: postData,
-				success: function(result) {
-					result = JSON.parse(result);
-
-					if (!result) {
-						errorCallback("Sever response wasn't proper JSON.");
-					} else if (result.error) {
-						errorCallback(result.error);
-					} else {
-						doneCallback(result);
-					}
-				},
-				error: function(type, xhr) {
-					errorCallback("Spellchecker request error: " + xhr.status);
-				}
-			});
-		}
-
-		function sendRpcCall(name, data, successCallback, errorCallback) {
-			var spellCheckCallback = settings.spellchecker_callback || defaultSpellcheckCallback;
-			spellCheckCallback.call(self, name, data, successCallback, errorCallback);
-		}
-
-		function spellcheck() {
-			if (started) {
-				finish();
-				return;
-			} else {
-				finish();
-			}
-
-			function errorCallback(message) {
-				editor.windowManager.alert(message);
-				editor.setProgressState(false);
-				finish();
-			}
-
-			editor.setProgressState(true);
-			sendRpcCall("spellcheck", getTextMatcher().text, markErrors, errorCallback);
-			editor.focus();
-		}
-
-		function checkIfFinished() {
-			if (!editor.dom.select('span.mce-spellchecker-word').length) {
-				finish();
-			}
-		}
-
-		function addToDictionary(word, spans) {
-			editor.setProgressState(true);
-
-			sendRpcCall("addToDictionary", word, function() {
-				editor.setProgressState(false);
-				editor.dom.remove(spans, true);
-				checkIfFinished();
-			}, function(message) {
-				editor.windowManager.alert(message);
-				editor.setProgressState(false);
-			});
-		}
-
-		function ignoreWord(word, spans, all) {
-			editor.selection.collapse();
-
-			if (all) {
-				Tools.each(editor.dom.select('span.mce-spellchecker-word'), function(span) {
-					if (span.getAttribute('data-mce-word') == word) {
-						editor.dom.remove(span, true);
-					}
-				});
-			} else {
-				editor.dom.remove(spans, true);
-			}
-
-			checkIfFinished();
-		}
-
-		function finish() {
-			getTextMatcher().reset();
-			self.textMatcher = null;
-
-			if (started) {
-				started = false;
-				editor.fire('SpellcheckEnd');
-			}
-		}
-
-		function getElmIndex(elm) {
-			var value = elm.getAttribute('data-mce-index');
-
-			if (typeof value == "number") {
-				return "" + value;
-			}
-
-			return value;
-		}
-
-		function findSpansByIndex(index) {
-			var nodes, spans = [];
-
-			nodes = Tools.toArray(editor.getBody().getElementsByTagName('span'));
-			if (nodes.length) {
-				for (var i = 0; i < nodes.length; i++) {
-					var nodeIndex = getElmIndex(nodes[i]);
-
-					if (nodeIndex === null || !nodeIndex.length) {
-						continue;
-					}
-
-					if (nodeIndex === index.toString()) {
-						spans.push(nodes[i]);
-					}
-				}
-			}
-
-			return spans;
-		}
-
-		editor.on('click', function(e) {
-			var target = e.target;
-
-			if (target.className == "mce-spellchecker-word") {
-				e.preventDefault();
-
-				var spans = findSpansByIndex(getElmIndex(target));
-
-				if (spans.length > 0) {
-					var rng = editor.dom.createRng();
-					rng.setStartBefore(spans[0]);
-					rng.setEndAfter(spans[spans.length - 1]);
-					editor.selection.setRng(rng);
-					showSuggestions(target.getAttribute('data-mce-word'), spans);
-				}
-			}
-		});
-
-		editor.addMenuItem('spellchecker', {
-			text: 'Spellcheck',
-			context: 'tools',
-			onclick: spellcheck,
-			selectable: true,
-			onPostRender: function() {
-				var self = this;
-
-				self.active(started);
-
-				editor.on('SpellcheckStart SpellcheckEnd', function() {
-					self.active(started);
-				});
-			}
-		});
-
-		function updateSelection(e) {
-			var selectedLanguage = settings.spellchecker_language;
-
-			e.control.items().each(function(ctrl) {
-				ctrl.active(ctrl.settings.data === selectedLanguage);
-			});
-		}
-
-		/**
-		 * Find the specified words and marks them. It will also show suggestions for those words.
-		 *
-		 * @example
-		 * editor.plugins.spellchecker.markErrors({
-		 *     dictionary: true,
-		 *     words: {
-		 *         "word1": ["suggestion 1", "Suggestion 2"]
-		 *     }
-		 * });
-		 * @param {Object} data Data object containing the words with suggestions.
-		 */
-		function markErrors(data) {
-			var suggestions;
-
-			if (data.words) {
-				hasDictionarySupport = !!data.dictionary;
-				suggestions = data.words;
-			} else {
-				// Fallback to old format
-				suggestions = data;
-			}
-
-			editor.setProgressState(false);
-
-			if (isEmpty(suggestions)) {
-				editor.windowManager.alert('No misspellings found');
-				started = false;
-				return;
-			}
-
-			lastSuggestions = suggestions;
-
-			getTextMatcher().find(getWordCharPattern()).filter(function(match) {
-				return !!suggestions[match.text];
-			}).wrap(function(match) {
-				return editor.dom.create('span', {
-					"class": 'mce-spellchecker-word',
-					"data-mce-bogus": 1,
-					"data-mce-word": match.text
-				});
-			});
-
-			started = true;
-			editor.fire('SpellcheckStart');
-		}
-
-		var buttonArgs = {
-			tooltip: 'Spellcheck',
-			onclick: spellcheck,
-			onPostRender: function() {
-				var self = this;
-
-				editor.on('SpellcheckStart SpellcheckEnd', function() {
-					self.active(started);
-				});
-			}
-		};
-
-		if (languageMenuItems.length > 1) {
-			buttonArgs.type = 'splitbutton';
-			buttonArgs.menu = languageMenuItems;
-			buttonArgs.onshow = updateSelection;
-			buttonArgs.onselect = function(e) {
-				settings.spellchecker_language = e.control.settings.data;
-			};
-		}
-
-		editor.addButton('spellchecker', buttonArgs);
-		editor.addCommand('mceSpellCheck', spellcheck);
-
-		editor.on('remove', function() {
-			if (suggestionsMenu) {
-				suggestionsMenu.remove();
-				suggestionsMenu = null;
-			}
-		});
-
-		editor.on('change', checkIfFinished);
-
-		this.getTextMatcher = getTextMatcher;
-		this.getWordCharPattern = getWordCharPattern;
-		this.markErrors = markErrors;
-		this.getLanguage = function() {
-			return settings.spellchecker_language;
-		};
-
-		// Set default spellchecker language if it's not specified
-		settings.spellchecker_language = settings.spellchecker_language || settings.language || 'en';
-	});
-});
-
-expose(["tinymce/spellcheckerplugin/DomTextMatcher"]);
-})(this);
\ No newline at end of file