plugin.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. /**
  2. * Copyright (c) Tiny Technologies, Inc. All rights reserved.
  3. * Licensed under the LGPL or a commercial license.
  4. * For LGPL see License.txt in the project root for license information.
  5. * For commercial licenses see https://www.tiny.cloud/
  6. *
  7. * Version: 5.10.2 (2021-11-17)
  8. */
  9. (function () {
  10. 'use strict';
  11. var Cell = function (initial) {
  12. var value = initial;
  13. var get = function () {
  14. return value;
  15. };
  16. var set = function (v) {
  17. value = v;
  18. };
  19. return {
  20. get: get,
  21. set: set
  22. };
  23. };
  24. var global$3 = tinymce.util.Tools.resolve('tinymce.PluginManager');
  25. var hasProPlugin = function (editor) {
  26. if (editor.hasPlugin('tinymcespellchecker', true)) {
  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 hasOwnProperty = Object.hasOwnProperty;
  36. var isEmpty = function (r) {
  37. for (var x in r) {
  38. if (hasOwnProperty.call(r, x)) {
  39. return false;
  40. }
  41. }
  42. return true;
  43. };
  44. var global$2 = tinymce.util.Tools.resolve('tinymce.util.Tools');
  45. var global$1 = tinymce.util.Tools.resolve('tinymce.util.URI');
  46. var global = tinymce.util.Tools.resolve('tinymce.util.XHR');
  47. var fireSpellcheckStart = function (editor) {
  48. return editor.fire('SpellcheckStart');
  49. };
  50. var fireSpellcheckEnd = function (editor) {
  51. return editor.fire('SpellcheckEnd');
  52. };
  53. var getLanguages = function (editor) {
  54. 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';
  55. return editor.getParam('spellchecker_languages', defaultLanguages);
  56. };
  57. var getLanguage = function (editor) {
  58. var defaultLanguage = editor.getParam('language', 'en');
  59. return editor.getParam('spellchecker_language', defaultLanguage);
  60. };
  61. var getRpcUrl = function (editor) {
  62. return editor.getParam('spellchecker_rpc_url');
  63. };
  64. var getSpellcheckerCallback = function (editor) {
  65. return editor.getParam('spellchecker_callback');
  66. };
  67. var getSpellcheckerWordcharPattern = function (editor) {
  68. 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');
  69. return editor.getParam('spellchecker_wordchar_pattern', defaultPattern);
  70. };
  71. var isContentEditableFalse = function (node) {
  72. return node && node.nodeType === 1 && node.contentEditable === 'false';
  73. };
  74. var DomTextMatcher = function (node, editor) {
  75. var m, matches = [];
  76. var dom = editor.dom;
  77. var blockElementsMap = editor.schema.getBlockElements();
  78. var hiddenTextElementsMap = editor.schema.getWhiteSpaceElements();
  79. var shortEndedElementsMap = editor.schema.getShortEndedElements();
  80. var createMatch = function (m, data) {
  81. if (!m[0]) {
  82. throw new Error('findAndReplaceDOMText cannot handle zero-length matches');
  83. }
  84. return {
  85. start: m.index,
  86. end: m.index + m[0].length,
  87. text: m[0],
  88. data: data
  89. };
  90. };
  91. var getText = function (node) {
  92. if (node.nodeType === 3) {
  93. return node.data;
  94. }
  95. if (hiddenTextElementsMap[node.nodeName] && !blockElementsMap[node.nodeName]) {
  96. return '';
  97. }
  98. if (isContentEditableFalse(node)) {
  99. return '\n';
  100. }
  101. var txt = '';
  102. if (blockElementsMap[node.nodeName] || shortEndedElementsMap[node.nodeName]) {
  103. txt += '\n';
  104. }
  105. if (node = node.firstChild) {
  106. do {
  107. txt += getText(node);
  108. } while (node = node.nextSibling);
  109. }
  110. return txt;
  111. };
  112. var stepThroughMatches = function (node, matches, replaceFn) {
  113. var startNode, endNode, startNodeIndex, endNodeIndex, innerNodes = [], atIndex = 0, curNode = node, matchLocation, matchIndex = 0;
  114. matches = matches.slice(0);
  115. matches.sort(function (a, b) {
  116. return a.start - b.start;
  117. });
  118. matchLocation = matches.shift();
  119. out:
  120. while (true) {
  121. if (blockElementsMap[curNode.nodeName] || shortEndedElementsMap[curNode.nodeName] || isContentEditableFalse(curNode)) {
  122. atIndex++;
  123. }
  124. if (curNode.nodeType === 3) {
  125. if (!endNode && curNode.length + atIndex >= matchLocation.end) {
  126. endNode = curNode;
  127. endNodeIndex = matchLocation.end - atIndex;
  128. } else if (startNode) {
  129. innerNodes.push(curNode);
  130. }
  131. if (!startNode && curNode.length + atIndex > matchLocation.start) {
  132. startNode = curNode;
  133. startNodeIndex = matchLocation.start - atIndex;
  134. }
  135. atIndex += curNode.length;
  136. }
  137. if (startNode && endNode) {
  138. curNode = replaceFn({
  139. startNode: startNode,
  140. startNodeIndex: startNodeIndex,
  141. endNode: endNode,
  142. endNodeIndex: endNodeIndex,
  143. innerNodes: innerNodes,
  144. match: matchLocation.text,
  145. matchIndex: matchIndex
  146. });
  147. atIndex -= endNode.length - endNodeIndex;
  148. startNode = null;
  149. endNode = null;
  150. innerNodes = [];
  151. matchLocation = matches.shift();
  152. matchIndex++;
  153. if (!matchLocation) {
  154. break;
  155. }
  156. } else if ((!hiddenTextElementsMap[curNode.nodeName] || blockElementsMap[curNode.nodeName]) && curNode.firstChild) {
  157. if (!isContentEditableFalse(curNode)) {
  158. curNode = curNode.firstChild;
  159. continue;
  160. }
  161. } else if (curNode.nextSibling) {
  162. curNode = curNode.nextSibling;
  163. continue;
  164. }
  165. while (true) {
  166. if (curNode.nextSibling) {
  167. curNode = curNode.nextSibling;
  168. break;
  169. } else if (curNode.parentNode !== node) {
  170. curNode = curNode.parentNode;
  171. } else {
  172. break out;
  173. }
  174. }
  175. }
  176. };
  177. var genReplacer = function (callback) {
  178. var makeReplacementNode = function (fill, matchIndex) {
  179. var match = matches[matchIndex];
  180. if (!match.stencil) {
  181. match.stencil = callback(match);
  182. }
  183. var clone = match.stencil.cloneNode(false);
  184. clone.setAttribute('data-mce-index', '' + matchIndex);
  185. if (fill) {
  186. clone.appendChild(dom.doc.createTextNode(fill));
  187. }
  188. return clone;
  189. };
  190. return function (range) {
  191. var before;
  192. var after;
  193. var parentNode;
  194. var startNode = range.startNode;
  195. var endNode = range.endNode;
  196. var matchIndex = range.matchIndex;
  197. var doc = dom.doc;
  198. if (startNode === endNode) {
  199. var node_1 = startNode;
  200. parentNode = node_1.parentNode;
  201. if (range.startNodeIndex > 0) {
  202. before = doc.createTextNode(node_1.data.substring(0, range.startNodeIndex));
  203. parentNode.insertBefore(before, node_1);
  204. }
  205. var el = makeReplacementNode(range.match, matchIndex);
  206. parentNode.insertBefore(el, node_1);
  207. if (range.endNodeIndex < node_1.length) {
  208. after = doc.createTextNode(node_1.data.substring(range.endNodeIndex));
  209. parentNode.insertBefore(after, node_1);
  210. }
  211. node_1.parentNode.removeChild(node_1);
  212. return el;
  213. }
  214. before = doc.createTextNode(startNode.data.substring(0, range.startNodeIndex));
  215. after = doc.createTextNode(endNode.data.substring(range.endNodeIndex));
  216. var elA = makeReplacementNode(startNode.data.substring(range.startNodeIndex), matchIndex);
  217. for (var i = 0, l = range.innerNodes.length; i < l; ++i) {
  218. var innerNode = range.innerNodes[i];
  219. var innerEl = makeReplacementNode(innerNode.data, matchIndex);
  220. innerNode.parentNode.replaceChild(innerEl, innerNode);
  221. }
  222. var elB = makeReplacementNode(endNode.data.substring(0, range.endNodeIndex), matchIndex);
  223. parentNode = startNode.parentNode;
  224. parentNode.insertBefore(before, startNode);
  225. parentNode.insertBefore(elA, startNode);
  226. parentNode.removeChild(startNode);
  227. parentNode = endNode.parentNode;
  228. parentNode.insertBefore(elB, endNode);
  229. parentNode.insertBefore(after, endNode);
  230. parentNode.removeChild(endNode);
  231. return elB;
  232. };
  233. };
  234. var unwrapElement = function (element) {
  235. var parentNode = element.parentNode;
  236. while (element.childNodes.length > 0) {
  237. parentNode.insertBefore(element.childNodes[0], element);
  238. }
  239. parentNode.removeChild(element);
  240. };
  241. var hasClass = function (elm) {
  242. return elm.className.indexOf('mce-spellchecker-word') !== -1;
  243. };
  244. var getWrappersByIndex = function (index) {
  245. var elements = node.getElementsByTagName('*'), wrappers = [];
  246. index = typeof index === 'number' ? '' + index : null;
  247. for (var i = 0; i < elements.length; i++) {
  248. var element = elements[i], dataIndex = element.getAttribute('data-mce-index');
  249. if (dataIndex !== null && dataIndex.length && hasClass(element)) {
  250. if (dataIndex === index || index === null) {
  251. wrappers.push(element);
  252. }
  253. }
  254. }
  255. return wrappers;
  256. };
  257. var indexOf = function (match) {
  258. var i = matches.length;
  259. while (i--) {
  260. if (matches[i] === match) {
  261. return i;
  262. }
  263. }
  264. return -1;
  265. };
  266. function filter(callback) {
  267. var filteredMatches = [];
  268. each(function (match, i) {
  269. if (callback(match, i)) {
  270. filteredMatches.push(match);
  271. }
  272. });
  273. matches = filteredMatches;
  274. return this;
  275. }
  276. function each(callback) {
  277. for (var i = 0, l = matches.length; i < l; i++) {
  278. if (callback(matches[i], i) === false) {
  279. break;
  280. }
  281. }
  282. return this;
  283. }
  284. function wrap(callback) {
  285. if (matches.length) {
  286. stepThroughMatches(node, matches, genReplacer(callback));
  287. }
  288. return this;
  289. }
  290. function find(regex, data) {
  291. if (text && regex.global) {
  292. while (m = regex.exec(text)) {
  293. matches.push(createMatch(m, data));
  294. }
  295. }
  296. return this;
  297. }
  298. function unwrap(match) {
  299. var i;
  300. var elements = getWrappersByIndex(match ? indexOf(match) : null);
  301. i = elements.length;
  302. while (i--) {
  303. unwrapElement(elements[i]);
  304. }
  305. return this;
  306. }
  307. var matchFromElement = function (element) {
  308. return matches[element.getAttribute('data-mce-index')];
  309. };
  310. var elementFromMatch = function (match) {
  311. return getWrappersByIndex(indexOf(match))[0];
  312. };
  313. function add(start, length, data) {
  314. matches.push({
  315. start: start,
  316. end: start + length,
  317. text: text.substr(start, length),
  318. data: data
  319. });
  320. return this;
  321. }
  322. var rangeFromMatch = function (match) {
  323. var wrappers = getWrappersByIndex(indexOf(match));
  324. var rng = editor.dom.createRng();
  325. rng.setStartBefore(wrappers[0]);
  326. rng.setEndAfter(wrappers[wrappers.length - 1]);
  327. return rng;
  328. };
  329. var replace = function (match, text) {
  330. var rng = rangeFromMatch(match);
  331. rng.deleteContents();
  332. if (text.length > 0) {
  333. rng.insertNode(editor.dom.doc.createTextNode(text));
  334. }
  335. return rng;
  336. };
  337. function reset() {
  338. matches.splice(0, matches.length);
  339. unwrap();
  340. return this;
  341. }
  342. var text = getText(node);
  343. return {
  344. text: text,
  345. matches: matches,
  346. each: each,
  347. filter: filter,
  348. reset: reset,
  349. matchFromElement: matchFromElement,
  350. elementFromMatch: elementFromMatch,
  351. find: find,
  352. add: add,
  353. wrap: wrap,
  354. unwrap: unwrap,
  355. replace: replace,
  356. rangeFromMatch: rangeFromMatch,
  357. indexOf: indexOf
  358. };
  359. };
  360. var getTextMatcher = function (editor, textMatcherState) {
  361. if (!textMatcherState.get()) {
  362. var textMatcher = DomTextMatcher(editor.getBody(), editor);
  363. textMatcherState.set(textMatcher);
  364. }
  365. return textMatcherState.get();
  366. };
  367. var defaultSpellcheckCallback = function (editor, pluginUrl, currentLanguageState) {
  368. return function (method, text, doneCallback, errorCallback) {
  369. var data = {
  370. method: method,
  371. lang: currentLanguageState.get()
  372. };
  373. var postData = '';
  374. data[method === 'addToDictionary' ? 'word' : 'text'] = text;
  375. global$2.each(data, function (value, key) {
  376. if (postData) {
  377. postData += '&';
  378. }
  379. postData += key + '=' + encodeURIComponent(value);
  380. });
  381. global.send({
  382. url: new global$1(pluginUrl).toAbsolute(getRpcUrl(editor)),
  383. type: 'post',
  384. content_type: 'application/x-www-form-urlencoded',
  385. data: postData,
  386. success: function (result) {
  387. var parseResult = JSON.parse(result);
  388. if (!parseResult) {
  389. var message = editor.translate('Server response wasn\'t proper JSON.');
  390. errorCallback(message);
  391. } else if (parseResult.error) {
  392. errorCallback(parseResult.error);
  393. } else {
  394. doneCallback(parseResult);
  395. }
  396. },
  397. error: function () {
  398. var message = editor.translate('The spelling service was not found: (') + getRpcUrl(editor) + editor.translate(')');
  399. errorCallback(message);
  400. }
  401. });
  402. };
  403. };
  404. var sendRpcCall = function (editor, pluginUrl, currentLanguageState, name, data, successCallback, errorCallback) {
  405. var userSpellcheckCallback = getSpellcheckerCallback(editor);
  406. var spellCheckCallback = userSpellcheckCallback ? userSpellcheckCallback : defaultSpellcheckCallback(editor, pluginUrl, currentLanguageState);
  407. spellCheckCallback.call(editor.plugins.spellchecker, name, data, successCallback, errorCallback);
  408. };
  409. var spellcheck = function (editor, pluginUrl, startedState, textMatcherState, lastSuggestionsState, currentLanguageState) {
  410. if (finish(editor, startedState, textMatcherState)) {
  411. return;
  412. }
  413. var errorCallback = function (message) {
  414. editor.notificationManager.open({
  415. text: message,
  416. type: 'error'
  417. });
  418. editor.setProgressState(false);
  419. finish(editor, startedState, textMatcherState);
  420. };
  421. var successCallback = function (data) {
  422. markErrors(editor, startedState, textMatcherState, lastSuggestionsState, data);
  423. };
  424. editor.setProgressState(true);
  425. sendRpcCall(editor, pluginUrl, currentLanguageState, 'spellcheck', getTextMatcher(editor, textMatcherState).text, successCallback, errorCallback);
  426. editor.focus();
  427. };
  428. var checkIfFinished = function (editor, startedState, textMatcherState) {
  429. if (!editor.dom.select('span.mce-spellchecker-word').length) {
  430. finish(editor, startedState, textMatcherState);
  431. }
  432. };
  433. var addToDictionary = function (editor, pluginUrl, startedState, textMatcherState, currentLanguageState, word, spans) {
  434. editor.setProgressState(true);
  435. sendRpcCall(editor, pluginUrl, currentLanguageState, 'addToDictionary', word, function () {
  436. editor.setProgressState(false);
  437. editor.dom.remove(spans, true);
  438. checkIfFinished(editor, startedState, textMatcherState);
  439. }, function (message) {
  440. editor.notificationManager.open({
  441. text: message,
  442. type: 'error'
  443. });
  444. editor.setProgressState(false);
  445. });
  446. };
  447. var ignoreWord = function (editor, startedState, textMatcherState, word, spans, all) {
  448. editor.selection.collapse();
  449. if (all) {
  450. global$2.each(editor.dom.select('span.mce-spellchecker-word'), function (span) {
  451. if (span.getAttribute('data-mce-word') === word) {
  452. editor.dom.remove(span, true);
  453. }
  454. });
  455. } else {
  456. editor.dom.remove(spans, true);
  457. }
  458. checkIfFinished(editor, startedState, textMatcherState);
  459. };
  460. var finish = function (editor, startedState, textMatcherState) {
  461. var bookmark = editor.selection.getBookmark();
  462. getTextMatcher(editor, textMatcherState).reset();
  463. editor.selection.moveToBookmark(bookmark);
  464. textMatcherState.set(null);
  465. if (startedState.get()) {
  466. startedState.set(false);
  467. fireSpellcheckEnd(editor);
  468. return true;
  469. }
  470. };
  471. var getElmIndex = function (elm) {
  472. var value = elm.getAttribute('data-mce-index');
  473. if (typeof value === 'number') {
  474. return '' + value;
  475. }
  476. return value;
  477. };
  478. var findSpansByIndex = function (editor, index) {
  479. var spans = [];
  480. var nodes = global$2.toArray(editor.getBody().getElementsByTagName('span'));
  481. if (nodes.length) {
  482. for (var i = 0; i < nodes.length; i++) {
  483. var nodeIndex = getElmIndex(nodes[i]);
  484. if (nodeIndex === null || !nodeIndex.length) {
  485. continue;
  486. }
  487. if (nodeIndex === index.toString()) {
  488. spans.push(nodes[i]);
  489. }
  490. }
  491. }
  492. return spans;
  493. };
  494. var markErrors = function (editor, startedState, textMatcherState, lastSuggestionsState, data) {
  495. var hasDictionarySupport = !!data.dictionary;
  496. var suggestions = data.words;
  497. editor.setProgressState(false);
  498. if (isEmpty(suggestions)) {
  499. var message = editor.translate('No misspellings found.');
  500. editor.notificationManager.open({
  501. text: message,
  502. type: 'info'
  503. });
  504. startedState.set(false);
  505. return;
  506. }
  507. lastSuggestionsState.set({
  508. suggestions: suggestions,
  509. hasDictionarySupport: hasDictionarySupport
  510. });
  511. var bookmark = editor.selection.getBookmark();
  512. getTextMatcher(editor, textMatcherState).find(getSpellcheckerWordcharPattern(editor)).filter(function (match) {
  513. return !!suggestions[match.text];
  514. }).wrap(function (match) {
  515. return editor.dom.create('span', {
  516. 'class': 'mce-spellchecker-word',
  517. 'aria-invalid': 'spelling',
  518. 'data-mce-bogus': 1,
  519. 'data-mce-word': match.text
  520. });
  521. });
  522. editor.selection.moveToBookmark(bookmark);
  523. startedState.set(true);
  524. fireSpellcheckStart(editor);
  525. };
  526. var get = function (editor, startedState, lastSuggestionsState, textMatcherState, currentLanguageState) {
  527. var getWordCharPattern = function () {
  528. return getSpellcheckerWordcharPattern(editor);
  529. };
  530. var markErrors$1 = function (data) {
  531. markErrors(editor, startedState, textMatcherState, lastSuggestionsState, data);
  532. };
  533. return {
  534. getTextMatcher: textMatcherState.get,
  535. getWordCharPattern: getWordCharPattern,
  536. markErrors: markErrors$1,
  537. getLanguage: currentLanguageState.get
  538. };
  539. };
  540. var register$1 = function (editor, pluginUrl, startedState, textMatcherState, lastSuggestionsState, currentLanguageState) {
  541. editor.addCommand('mceSpellCheck', function () {
  542. spellcheck(editor, pluginUrl, startedState, textMatcherState, lastSuggestionsState, currentLanguageState);
  543. });
  544. };
  545. var __assign = function () {
  546. __assign = Object.assign || function __assign(t) {
  547. for (var s, i = 1, n = arguments.length; i < n; i++) {
  548. s = arguments[i];
  549. for (var p in s)
  550. if (Object.prototype.hasOwnProperty.call(s, p))
  551. t[p] = s[p];
  552. }
  553. return t;
  554. };
  555. return __assign.apply(this, arguments);
  556. };
  557. var spellcheckerEvents = 'SpellcheckStart SpellcheckEnd';
  558. var buildMenuItems = function (listName, languageValues) {
  559. var items = [];
  560. global$2.each(languageValues, function (languageValue) {
  561. items.push({
  562. selectable: true,
  563. text: languageValue.name,
  564. data: languageValue.value
  565. });
  566. });
  567. return items;
  568. };
  569. var getItems = function (editor) {
  570. return global$2.map(getLanguages(editor).split(','), function (langPair) {
  571. var langPairs = langPair.split('=');
  572. return {
  573. name: langPairs[0],
  574. value: langPairs[1]
  575. };
  576. });
  577. };
  578. var register = function (editor, pluginUrl, startedState, textMatcherState, currentLanguageState, lastSuggestionsState) {
  579. var languageMenuItems = buildMenuItems('Language', getItems(editor));
  580. var startSpellchecking = function () {
  581. spellcheck(editor, pluginUrl, startedState, textMatcherState, lastSuggestionsState, currentLanguageState);
  582. };
  583. var buttonArgs = {
  584. tooltip: 'Spellcheck',
  585. onAction: startSpellchecking,
  586. icon: 'spell-check',
  587. onSetup: function (buttonApi) {
  588. var setButtonState = function () {
  589. buttonApi.setActive(startedState.get());
  590. };
  591. editor.on(spellcheckerEvents, setButtonState);
  592. return function () {
  593. editor.off(spellcheckerEvents, setButtonState);
  594. };
  595. }
  596. };
  597. var splitButtonArgs = __assign(__assign({}, buttonArgs), {
  598. type: 'splitbutton',
  599. select: function (value) {
  600. return value === currentLanguageState.get();
  601. },
  602. fetch: function (callback) {
  603. var items = global$2.map(languageMenuItems, function (languageItem) {
  604. return {
  605. type: 'choiceitem',
  606. value: languageItem.data,
  607. text: languageItem.text
  608. };
  609. });
  610. callback(items);
  611. },
  612. onItemAction: function (splitButtonApi, value) {
  613. currentLanguageState.set(value);
  614. }
  615. });
  616. if (languageMenuItems.length > 1) {
  617. editor.ui.registry.addSplitButton('spellchecker', splitButtonArgs);
  618. } else {
  619. editor.ui.registry.addToggleButton('spellchecker', buttonArgs);
  620. }
  621. editor.ui.registry.addToggleMenuItem('spellchecker', {
  622. text: 'Spellcheck',
  623. icon: 'spell-check',
  624. onSetup: function (menuApi) {
  625. menuApi.setActive(startedState.get());
  626. var setMenuItemCheck = function () {
  627. menuApi.setActive(startedState.get());
  628. };
  629. editor.on(spellcheckerEvents, setMenuItemCheck);
  630. return function () {
  631. editor.off(spellcheckerEvents, setMenuItemCheck);
  632. };
  633. },
  634. onAction: startSpellchecking
  635. });
  636. };
  637. var ignoreAll = true;
  638. var getSuggestions = function (editor, pluginUrl, lastSuggestionsState, startedState, textMatcherState, currentLanguageState, word, spans) {
  639. var items = [];
  640. var suggestions = lastSuggestionsState.get().suggestions[word];
  641. global$2.each(suggestions, function (suggestion) {
  642. items.push({
  643. text: suggestion,
  644. onAction: function () {
  645. editor.insertContent(editor.dom.encode(suggestion));
  646. editor.dom.remove(spans);
  647. checkIfFinished(editor, startedState, textMatcherState);
  648. }
  649. });
  650. });
  651. var hasDictionarySupport = lastSuggestionsState.get().hasDictionarySupport;
  652. if (hasDictionarySupport) {
  653. items.push({ type: 'separator' });
  654. items.push({
  655. text: 'Add to dictionary',
  656. onAction: function () {
  657. addToDictionary(editor, pluginUrl, startedState, textMatcherState, currentLanguageState, word, spans);
  658. }
  659. });
  660. }
  661. items.push.apply(items, [
  662. { type: 'separator' },
  663. {
  664. text: 'Ignore',
  665. onAction: function () {
  666. ignoreWord(editor, startedState, textMatcherState, word, spans);
  667. }
  668. },
  669. {
  670. text: 'Ignore all',
  671. onAction: function () {
  672. ignoreWord(editor, startedState, textMatcherState, word, spans, ignoreAll);
  673. }
  674. }
  675. ]);
  676. return items;
  677. };
  678. var setup = function (editor, pluginUrl, lastSuggestionsState, startedState, textMatcherState, currentLanguageState) {
  679. var update = function (element) {
  680. var target = element;
  681. if (target.className === 'mce-spellchecker-word') {
  682. var spans = findSpansByIndex(editor, getElmIndex(target));
  683. if (spans.length > 0) {
  684. var rng = editor.dom.createRng();
  685. rng.setStartBefore(spans[0]);
  686. rng.setEndAfter(spans[spans.length - 1]);
  687. editor.selection.setRng(rng);
  688. return getSuggestions(editor, pluginUrl, lastSuggestionsState, startedState, textMatcherState, currentLanguageState, target.getAttribute('data-mce-word'), spans);
  689. }
  690. } else {
  691. return [];
  692. }
  693. };
  694. editor.ui.registry.addContextMenu('spellchecker', { update: update });
  695. };
  696. function Plugin () {
  697. global$3.add('spellchecker', function (editor, pluginUrl) {
  698. if (hasProPlugin(editor) === false) {
  699. var startedState = Cell(false);
  700. var currentLanguageState = Cell(getLanguage(editor));
  701. var textMatcherState = Cell(null);
  702. var lastSuggestionsState = Cell(null);
  703. register(editor, pluginUrl, startedState, textMatcherState, currentLanguageState, lastSuggestionsState);
  704. setup(editor, pluginUrl, lastSuggestionsState, startedState, textMatcherState, currentLanguageState);
  705. register$1(editor, pluginUrl, startedState, textMatcherState, lastSuggestionsState, currentLanguageState);
  706. return get(editor, startedState, lastSuggestionsState, textMatcherState, currentLanguageState);
  707. }
  708. });
  709. }
  710. Plugin();
  711. }());