plugin.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /**
  2. * TinyMCE version 6.4.2 (2023-04-26)
  3. */
  4. (function () {
  5. 'use strict';
  6. var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');
  7. const eq = t => a => t === a;
  8. const isNull = eq(null);
  9. const identity = x => {
  10. return x;
  11. };
  12. const zeroWidth = '\uFEFF';
  13. const removeZwsp$1 = s => s.replace(/\uFEFF/g, '');
  14. const map = (xs, f) => {
  15. const len = xs.length;
  16. const r = new Array(len);
  17. for (let i = 0; i < len; i++) {
  18. const x = xs[i];
  19. r[i] = f(x, i);
  20. }
  21. return r;
  22. };
  23. const punctuationStr = '[!-#%-*,-\\/:;?@\\[-\\]_{}\xA1\xAB\xB7\xBB\xBF;\xB7\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1361-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u3008\u3009\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30\u2E31\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uff3f\uFF5B\uFF5D\uFF5F-\uFF65]';
  24. const regExps = {
  25. aletter: '[A-Za-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05F3\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u10a0-\u10c5\u10d0-\u10fa\u10fc\u1100-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1a00-\u1a16\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bc0-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u24B6-\u24E9\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2d00-\u2d25\u2d30-\u2d65\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005\u303b\u303c\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790\ua791\ua7a0-\ua7a9\ua7fa-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uffa0-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]',
  26. midnumlet: `[-'\\.\u2018\u2019\u2024\uFE52\uFF07\uFF0E]`,
  27. midletter: '[:\xB7\xB7\u05F4\u2027\uFE13\uFE55\uFF1A]',
  28. midnum: '[\xB1+*/,;;\u0589\u060C\u060D\u066C\u07F8\u2044\uFE10\uFE14\uFE50\uFE54\uFF0C\uFF1B]',
  29. numeric: '[0-9\u0660-\u0669\u066B\u06f0-\u06f9\u07c0-\u07c9\u0966-\u096f\u09e6-\u09ef\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be6-\u0bef\u0c66-\u0c6f\u0ce6-\u0cef\u0d66-\u0d6f\u0e50-\u0e59\u0ed0-\u0ed9\u0f20-\u0f29\u1040-\u1049\u1090-\u1099\u17e0-\u17e9\u1810-\u1819\u1946-\u194f\u19d0-\u19d9\u1a80-\u1a89\u1a90-\u1a99\u1b50-\u1b59\u1bb0-\u1bb9\u1c40-\u1c49\u1c50-\u1c59\ua620-\ua629\ua8d0-\ua8d9\ua900-\ua909\ua9d0-\ua9d9\uaa50-\uaa59\uabf0-\uabf9]',
  30. cr: '\\r',
  31. lf: '\\n',
  32. newline: '[\x0B\f\x85\u2028\u2029]',
  33. extend: '[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0c01-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d02\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f\u109a-\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b6-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u192b\u1930-\u193b\u19b0-\u19c0\u19c8\u19c9\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f\u1b00-\u1b04\u1b34-\u1b44\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1baa\u1be6-\u1bf3\u1c24-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2\u1dc0-\u1de6\u1dfc-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\uA672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa7b\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe3-\uabea\uabec\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]',
  34. format: '[\xAD\u0600-\u0603\u06DD\u070F\u17b4\u17b5\u200E\u200F\u202A-\u202E\u2060-\u2064\u206A-\u206F\uFEFF\uFFF9-\uFFFB]',
  35. katakana: '[\u3031-\u3035\u309B\u309C\u30A0-\u30fa\u30fc-\u30ff\u31f0-\u31ff\u32D0-\u32FE\u3300-\u3357\uff66-\uff9d]',
  36. extendnumlet: '[=_\u203f\u2040\u2054\ufe33\ufe34\ufe4d-\ufe4f\uff3f\u2200-\u22FF<>]',
  37. punctuation: punctuationStr
  38. };
  39. const characterIndices = {
  40. ALETTER: 0,
  41. MIDNUMLET: 1,
  42. MIDLETTER: 2,
  43. MIDNUM: 3,
  44. NUMERIC: 4,
  45. CR: 5,
  46. LF: 6,
  47. NEWLINE: 7,
  48. EXTEND: 8,
  49. FORMAT: 9,
  50. KATAKANA: 10,
  51. EXTENDNUMLET: 11,
  52. AT: 12,
  53. OTHER: 13
  54. };
  55. const SETS$1 = [
  56. new RegExp(regExps.aletter),
  57. new RegExp(regExps.midnumlet),
  58. new RegExp(regExps.midletter),
  59. new RegExp(regExps.midnum),
  60. new RegExp(regExps.numeric),
  61. new RegExp(regExps.cr),
  62. new RegExp(regExps.lf),
  63. new RegExp(regExps.newline),
  64. new RegExp(regExps.extend),
  65. new RegExp(regExps.format),
  66. new RegExp(regExps.katakana),
  67. new RegExp(regExps.extendnumlet),
  68. new RegExp('@')
  69. ];
  70. const EMPTY_STRING$1 = '';
  71. const PUNCTUATION$1 = new RegExp('^' + regExps.punctuation + '$');
  72. const WHITESPACE$1 = /^\s+$/;
  73. const SETS = SETS$1;
  74. const OTHER = characterIndices.OTHER;
  75. const getType = char => {
  76. let type = OTHER;
  77. const setsLength = SETS.length;
  78. for (let j = 0; j < setsLength; ++j) {
  79. const set = SETS[j];
  80. if (set && set.test(char)) {
  81. type = j;
  82. break;
  83. }
  84. }
  85. return type;
  86. };
  87. const memoize = func => {
  88. const cache = {};
  89. return char => {
  90. if (cache[char]) {
  91. return cache[char];
  92. } else {
  93. const result = func(char);
  94. cache[char] = result;
  95. return result;
  96. }
  97. };
  98. };
  99. const classify = characters => {
  100. const memoized = memoize(getType);
  101. return map(characters, memoized);
  102. };
  103. const isWordBoundary = (map, index) => {
  104. const type = map[index];
  105. const nextType = map[index + 1];
  106. if (index < 0 || index > map.length - 1 && index !== 0) {
  107. return false;
  108. }
  109. if (type === characterIndices.ALETTER && nextType === characterIndices.ALETTER) {
  110. return false;
  111. }
  112. const nextNextType = map[index + 2];
  113. if (type === characterIndices.ALETTER && (nextType === characterIndices.MIDLETTER || nextType === characterIndices.MIDNUMLET || nextType === characterIndices.AT) && nextNextType === characterIndices.ALETTER) {
  114. return false;
  115. }
  116. const prevType = map[index - 1];
  117. if ((type === characterIndices.MIDLETTER || type === characterIndices.MIDNUMLET || nextType === characterIndices.AT) && nextType === characterIndices.ALETTER && prevType === characterIndices.ALETTER) {
  118. return false;
  119. }
  120. if ((type === characterIndices.NUMERIC || type === characterIndices.ALETTER) && (nextType === characterIndices.NUMERIC || nextType === characterIndices.ALETTER)) {
  121. return false;
  122. }
  123. if ((type === characterIndices.MIDNUM || type === characterIndices.MIDNUMLET) && nextType === characterIndices.NUMERIC && prevType === characterIndices.NUMERIC) {
  124. return false;
  125. }
  126. if (type === characterIndices.NUMERIC && (nextType === characterIndices.MIDNUM || nextType === characterIndices.MIDNUMLET) && nextNextType === characterIndices.NUMERIC) {
  127. return false;
  128. }
  129. if (type === characterIndices.EXTEND || type === characterIndices.FORMAT || prevType === characterIndices.EXTEND || prevType === characterIndices.FORMAT || nextType === characterIndices.EXTEND || nextType === characterIndices.FORMAT) {
  130. return false;
  131. }
  132. if (type === characterIndices.CR && nextType === characterIndices.LF) {
  133. return false;
  134. }
  135. if (type === characterIndices.NEWLINE || type === characterIndices.CR || type === characterIndices.LF) {
  136. return true;
  137. }
  138. if (nextType === characterIndices.NEWLINE || nextType === characterIndices.CR || nextType === characterIndices.LF) {
  139. return true;
  140. }
  141. if (type === characterIndices.KATAKANA && nextType === characterIndices.KATAKANA) {
  142. return false;
  143. }
  144. if (nextType === characterIndices.EXTENDNUMLET && (type === characterIndices.ALETTER || type === characterIndices.NUMERIC || type === characterIndices.KATAKANA || type === characterIndices.EXTENDNUMLET)) {
  145. return false;
  146. }
  147. if (type === characterIndices.EXTENDNUMLET && (nextType === characterIndices.ALETTER || nextType === characterIndices.NUMERIC || nextType === characterIndices.KATAKANA)) {
  148. return false;
  149. }
  150. if (type === characterIndices.AT) {
  151. return false;
  152. }
  153. return true;
  154. };
  155. const EMPTY_STRING = EMPTY_STRING$1;
  156. const WHITESPACE = WHITESPACE$1;
  157. const PUNCTUATION = PUNCTUATION$1;
  158. const isProtocol = str => str === 'http' || str === 'https';
  159. const findWordEnd = (characters, startIndex) => {
  160. let i;
  161. for (i = startIndex; i < characters.length; i++) {
  162. if (WHITESPACE.test(characters[i])) {
  163. break;
  164. }
  165. }
  166. return i;
  167. };
  168. const findUrlEnd = (characters, startIndex) => {
  169. const endIndex = findWordEnd(characters, startIndex + 1);
  170. const peakedWord = characters.slice(startIndex + 1, endIndex).join(EMPTY_STRING);
  171. return peakedWord.substr(0, 3) === '://' ? endIndex : startIndex;
  172. };
  173. const findWords = (chars, sChars, characterMap, options) => {
  174. const words = [];
  175. let word = [];
  176. for (let i = 0; i < characterMap.length; ++i) {
  177. word.push(chars[i]);
  178. if (isWordBoundary(characterMap, i)) {
  179. const ch = sChars[i];
  180. if ((options.includeWhitespace || !WHITESPACE.test(ch)) && (options.includePunctuation || !PUNCTUATION.test(ch))) {
  181. const startOfWord = i - word.length + 1;
  182. const endOfWord = i + 1;
  183. const str = sChars.slice(startOfWord, endOfWord).join(EMPTY_STRING);
  184. if (isProtocol(str)) {
  185. const endOfUrl = findUrlEnd(sChars, i);
  186. const url = chars.slice(endOfWord, endOfUrl);
  187. Array.prototype.push.apply(word, url);
  188. i = endOfUrl;
  189. }
  190. words.push(word);
  191. }
  192. word = [];
  193. }
  194. }
  195. return words;
  196. };
  197. const getDefaultOptions = () => ({
  198. includeWhitespace: false,
  199. includePunctuation: false
  200. });
  201. const getWords$1 = (chars, extract, options) => {
  202. options = {
  203. ...getDefaultOptions(),
  204. ...options
  205. };
  206. const filteredChars = [];
  207. const extractedChars = [];
  208. for (let i = 0; i < chars.length; i++) {
  209. const ch = extract(chars[i]);
  210. if (ch !== zeroWidth) {
  211. filteredChars.push(chars[i]);
  212. extractedChars.push(ch);
  213. }
  214. }
  215. const characterMap = classify(extractedChars);
  216. return findWords(filteredChars, extractedChars, characterMap, options);
  217. };
  218. const getWords = getWords$1;
  219. var global$1 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');
  220. const getText = (node, schema) => {
  221. const blockElements = schema.getBlockElements();
  222. const voidElements = schema.getVoidElements();
  223. const isNewline = node => blockElements[node.nodeName] || voidElements[node.nodeName];
  224. const textBlocks = [];
  225. let txt = '';
  226. const treeWalker = new global$1(node, node);
  227. let tempNode;
  228. while (tempNode = treeWalker.next()) {
  229. if (tempNode.nodeType === 3) {
  230. txt += removeZwsp$1(tempNode.data);
  231. } else if (isNewline(tempNode) && txt.length) {
  232. textBlocks.push(txt);
  233. txt = '';
  234. }
  235. }
  236. if (txt.length) {
  237. textBlocks.push(txt);
  238. }
  239. return textBlocks;
  240. };
  241. const removeZwsp = text => text.replace(/\u200B/g, '');
  242. const strLen = str => str.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '_').length;
  243. const countWords = (node, schema) => {
  244. const text = removeZwsp(getText(node, schema).join('\n'));
  245. return getWords(text.split(''), identity).length;
  246. };
  247. const countCharacters = (node, schema) => {
  248. const text = getText(node, schema).join('');
  249. return strLen(text);
  250. };
  251. const countCharactersWithoutSpaces = (node, schema) => {
  252. const text = getText(node, schema).join('').replace(/\s/g, '');
  253. return strLen(text);
  254. };
  255. const createBodyCounter = (editor, count) => () => count(editor.getBody(), editor.schema);
  256. const createSelectionCounter = (editor, count) => () => count(editor.selection.getRng().cloneContents(), editor.schema);
  257. const createBodyWordCounter = editor => createBodyCounter(editor, countWords);
  258. const get = editor => ({
  259. body: {
  260. getWordCount: createBodyWordCounter(editor),
  261. getCharacterCount: createBodyCounter(editor, countCharacters),
  262. getCharacterCountWithoutSpaces: createBodyCounter(editor, countCharactersWithoutSpaces)
  263. },
  264. selection: {
  265. getWordCount: createSelectionCounter(editor, countWords),
  266. getCharacterCount: createSelectionCounter(editor, countCharacters),
  267. getCharacterCountWithoutSpaces: createSelectionCounter(editor, countCharactersWithoutSpaces)
  268. },
  269. getCount: createBodyWordCounter(editor)
  270. });
  271. const open = (editor, api) => {
  272. editor.windowManager.open({
  273. title: 'Word Count',
  274. body: {
  275. type: 'panel',
  276. items: [{
  277. type: 'table',
  278. header: [
  279. 'Count',
  280. 'Document',
  281. 'Selection'
  282. ],
  283. cells: [
  284. [
  285. 'Words',
  286. String(api.body.getWordCount()),
  287. String(api.selection.getWordCount())
  288. ],
  289. [
  290. 'Characters (no spaces)',
  291. String(api.body.getCharacterCountWithoutSpaces()),
  292. String(api.selection.getCharacterCountWithoutSpaces())
  293. ],
  294. [
  295. 'Characters',
  296. String(api.body.getCharacterCount()),
  297. String(api.selection.getCharacterCount())
  298. ]
  299. ]
  300. }]
  301. },
  302. buttons: [{
  303. type: 'cancel',
  304. name: 'close',
  305. text: 'Close',
  306. primary: true
  307. }]
  308. });
  309. };
  310. const register$1 = (editor, api) => {
  311. editor.addCommand('mceWordCount', () => open(editor, api));
  312. };
  313. const first = (fn, rate) => {
  314. let timer = null;
  315. const cancel = () => {
  316. if (!isNull(timer)) {
  317. clearTimeout(timer);
  318. timer = null;
  319. }
  320. };
  321. const throttle = (...args) => {
  322. if (isNull(timer)) {
  323. timer = setTimeout(() => {
  324. timer = null;
  325. fn.apply(null, args);
  326. }, rate);
  327. }
  328. };
  329. return {
  330. cancel,
  331. throttle
  332. };
  333. };
  334. var global = tinymce.util.Tools.resolve('tinymce.util.Delay');
  335. const fireWordCountUpdate = (editor, api) => {
  336. editor.dispatch('wordCountUpdate', {
  337. wordCount: {
  338. words: api.body.getWordCount(),
  339. characters: api.body.getCharacterCount(),
  340. charactersWithoutSpaces: api.body.getCharacterCountWithoutSpaces()
  341. }
  342. });
  343. };
  344. const updateCount = (editor, api) => {
  345. fireWordCountUpdate(editor, api);
  346. };
  347. const setup = (editor, api, delay) => {
  348. const debouncedUpdate = first(() => updateCount(editor, api), delay);
  349. editor.on('init', () => {
  350. updateCount(editor, api);
  351. global.setEditorTimeout(editor, () => {
  352. editor.on('SetContent BeforeAddUndo Undo Redo ViewUpdate keyup', debouncedUpdate.throttle);
  353. }, 0);
  354. editor.on('remove', debouncedUpdate.cancel);
  355. });
  356. };
  357. const register = editor => {
  358. const onAction = () => editor.execCommand('mceWordCount');
  359. editor.ui.registry.addButton('wordcount', {
  360. tooltip: 'Word count',
  361. icon: 'character-count',
  362. onAction
  363. });
  364. editor.ui.registry.addMenuItem('wordcount', {
  365. text: 'Word count',
  366. icon: 'character-count',
  367. onAction
  368. });
  369. };
  370. var Plugin = (delay = 300) => {
  371. global$2.add('wordcount', editor => {
  372. const api = get(editor);
  373. register$1(editor, api);
  374. register(editor);
  375. setup(editor, api, delay);
  376. return api;
  377. });
  378. };
  379. Plugin();
  380. })();