plugin.js 26 KB

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