plugin.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161
  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.9.1 (2021-08-27)
  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 __assign = function () {
  26. __assign = Object.assign || function __assign(t) {
  27. for (var s, i = 1, n = arguments.length; i < n; i++) {
  28. s = arguments[i];
  29. for (var p in s)
  30. if (Object.prototype.hasOwnProperty.call(s, p))
  31. t[p] = s[p];
  32. }
  33. return t;
  34. };
  35. return __assign.apply(this, arguments);
  36. };
  37. var typeOf = function (x) {
  38. var t = typeof x;
  39. if (x === null) {
  40. return 'null';
  41. } else if (t === 'object' && (Array.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'Array')) {
  42. return 'array';
  43. } else if (t === 'object' && (String.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'String')) {
  44. return 'string';
  45. } else {
  46. return t;
  47. }
  48. };
  49. var isType$1 = function (type) {
  50. return function (value) {
  51. return typeOf(value) === type;
  52. };
  53. };
  54. var isSimpleType = function (type) {
  55. return function (value) {
  56. return typeof value === type;
  57. };
  58. };
  59. var isString = isType$1('string');
  60. var isArray = isType$1('array');
  61. var isBoolean = isSimpleType('boolean');
  62. var isNumber = isSimpleType('number');
  63. var noop = function () {
  64. };
  65. var constant = function (value) {
  66. return function () {
  67. return value;
  68. };
  69. };
  70. var identity = function (x) {
  71. return x;
  72. };
  73. var never = constant(false);
  74. var always = constant(true);
  75. var 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]';
  76. var punctuation$1 = constant(punctuationStr);
  77. var none = function () {
  78. return NONE;
  79. };
  80. var NONE = function () {
  81. var call = function (thunk) {
  82. return thunk();
  83. };
  84. var id = identity;
  85. var me = {
  86. fold: function (n, _s) {
  87. return n();
  88. },
  89. isSome: never,
  90. isNone: always,
  91. getOr: id,
  92. getOrThunk: call,
  93. getOrDie: function (msg) {
  94. throw new Error(msg || 'error: getOrDie called on none.');
  95. },
  96. getOrNull: constant(null),
  97. getOrUndefined: constant(undefined),
  98. or: id,
  99. orThunk: call,
  100. map: none,
  101. each: noop,
  102. bind: none,
  103. exists: never,
  104. forall: always,
  105. filter: function () {
  106. return none();
  107. },
  108. toArray: function () {
  109. return [];
  110. },
  111. toString: constant('none()')
  112. };
  113. return me;
  114. }();
  115. var some = function (a) {
  116. var constant_a = constant(a);
  117. var self = function () {
  118. return me;
  119. };
  120. var bind = function (f) {
  121. return f(a);
  122. };
  123. var me = {
  124. fold: function (n, s) {
  125. return s(a);
  126. },
  127. isSome: always,
  128. isNone: never,
  129. getOr: constant_a,
  130. getOrThunk: constant_a,
  131. getOrDie: constant_a,
  132. getOrNull: constant_a,
  133. getOrUndefined: constant_a,
  134. or: self,
  135. orThunk: self,
  136. map: function (f) {
  137. return some(f(a));
  138. },
  139. each: function (f) {
  140. f(a);
  141. },
  142. bind: bind,
  143. exists: bind,
  144. forall: bind,
  145. filter: function (f) {
  146. return f(a) ? me : NONE;
  147. },
  148. toArray: function () {
  149. return [a];
  150. },
  151. toString: function () {
  152. return 'some(' + a + ')';
  153. }
  154. };
  155. return me;
  156. };
  157. var from = function (value) {
  158. return value === null || value === undefined ? NONE : some(value);
  159. };
  160. var Optional = {
  161. some: some,
  162. none: none,
  163. from: from
  164. };
  165. var punctuation = punctuation$1;
  166. var global$2 = tinymce.util.Tools.resolve('tinymce.util.Tools');
  167. var nativeSlice = Array.prototype.slice;
  168. var nativePush = Array.prototype.push;
  169. var map = function (xs, f) {
  170. var len = xs.length;
  171. var r = new Array(len);
  172. for (var i = 0; i < len; i++) {
  173. var x = xs[i];
  174. r[i] = f(x, i);
  175. }
  176. return r;
  177. };
  178. var each = function (xs, f) {
  179. for (var i = 0, len = xs.length; i < len; i++) {
  180. var x = xs[i];
  181. f(x, i);
  182. }
  183. };
  184. var eachr = function (xs, f) {
  185. for (var i = xs.length - 1; i >= 0; i--) {
  186. var x = xs[i];
  187. f(x, i);
  188. }
  189. };
  190. var groupBy = function (xs, f) {
  191. if (xs.length === 0) {
  192. return [];
  193. } else {
  194. var wasType = f(xs[0]);
  195. var r = [];
  196. var group = [];
  197. for (var i = 0, len = xs.length; i < len; i++) {
  198. var x = xs[i];
  199. var type = f(x);
  200. if (type !== wasType) {
  201. r.push(group);
  202. group = [];
  203. }
  204. wasType = type;
  205. group.push(x);
  206. }
  207. if (group.length !== 0) {
  208. r.push(group);
  209. }
  210. return r;
  211. }
  212. };
  213. var foldl = function (xs, f, acc) {
  214. each(xs, function (x, i) {
  215. acc = f(acc, x, i);
  216. });
  217. return acc;
  218. };
  219. var flatten = function (xs) {
  220. var r = [];
  221. for (var i = 0, len = xs.length; i < len; ++i) {
  222. if (!isArray(xs[i])) {
  223. throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
  224. }
  225. nativePush.apply(r, xs[i]);
  226. }
  227. return r;
  228. };
  229. var bind = function (xs, f) {
  230. return flatten(map(xs, f));
  231. };
  232. var sort = function (xs, comparator) {
  233. var copy = nativeSlice.call(xs, 0);
  234. copy.sort(comparator);
  235. return copy;
  236. };
  237. var hasOwnProperty = Object.hasOwnProperty;
  238. var has = function (obj, key) {
  239. return hasOwnProperty.call(obj, key);
  240. };
  241. typeof window !== 'undefined' ? window : Function('return this;')();
  242. var DOCUMENT = 9;
  243. var DOCUMENT_FRAGMENT = 11;
  244. var ELEMENT = 1;
  245. var TEXT = 3;
  246. var type = function (element) {
  247. return element.dom.nodeType;
  248. };
  249. var isType = function (t) {
  250. return function (element) {
  251. return type(element) === t;
  252. };
  253. };
  254. var isText$1 = isType(TEXT);
  255. var rawSet = function (dom, key, value) {
  256. if (isString(value) || isBoolean(value) || isNumber(value)) {
  257. dom.setAttribute(key, value + '');
  258. } else {
  259. console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
  260. throw new Error('Attribute value was not simple');
  261. }
  262. };
  263. var set = function (element, key, value) {
  264. rawSet(element.dom, key, value);
  265. };
  266. var compareDocumentPosition = function (a, b, match) {
  267. return (a.compareDocumentPosition(b) & match) !== 0;
  268. };
  269. var documentPositionPreceding = function (a, b) {
  270. return compareDocumentPosition(a, b, Node.DOCUMENT_POSITION_PRECEDING);
  271. };
  272. var fromHtml = function (html, scope) {
  273. var doc = scope || document;
  274. var div = doc.createElement('div');
  275. div.innerHTML = html;
  276. if (!div.hasChildNodes() || div.childNodes.length > 1) {
  277. console.error('HTML does not have a single root node', html);
  278. throw new Error('HTML must have a single root node');
  279. }
  280. return fromDom(div.childNodes[0]);
  281. };
  282. var fromTag = function (tag, scope) {
  283. var doc = scope || document;
  284. var node = doc.createElement(tag);
  285. return fromDom(node);
  286. };
  287. var fromText = function (text, scope) {
  288. var doc = scope || document;
  289. var node = doc.createTextNode(text);
  290. return fromDom(node);
  291. };
  292. var fromDom = function (node) {
  293. if (node === null || node === undefined) {
  294. throw new Error('Node cannot be null or undefined');
  295. }
  296. return { dom: node };
  297. };
  298. var fromPoint = function (docElm, x, y) {
  299. return Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
  300. };
  301. var SugarElement = {
  302. fromHtml: fromHtml,
  303. fromTag: fromTag,
  304. fromText: fromText,
  305. fromDom: fromDom,
  306. fromPoint: fromPoint
  307. };
  308. var bypassSelector = function (dom) {
  309. return dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;
  310. };
  311. var all = function (selector, scope) {
  312. var base = scope === undefined ? document : scope.dom;
  313. return bypassSelector(base) ? [] : map(base.querySelectorAll(selector), SugarElement.fromDom);
  314. };
  315. var parent = function (element) {
  316. return Optional.from(element.dom.parentNode).map(SugarElement.fromDom);
  317. };
  318. var children = function (element) {
  319. return map(element.dom.childNodes, SugarElement.fromDom);
  320. };
  321. var spot = function (element, offset) {
  322. return {
  323. element: element,
  324. offset: offset
  325. };
  326. };
  327. var leaf = function (element, offset) {
  328. var cs = children(element);
  329. return cs.length > 0 && offset < cs.length ? spot(cs[offset], 0) : spot(element, offset);
  330. };
  331. var before = function (marker, element) {
  332. var parent$1 = parent(marker);
  333. parent$1.each(function (v) {
  334. v.dom.insertBefore(element.dom, marker.dom);
  335. });
  336. };
  337. var append = function (parent, element) {
  338. parent.dom.appendChild(element.dom);
  339. };
  340. var wrap = function (element, wrapper) {
  341. before(element, wrapper);
  342. append(wrapper, element);
  343. };
  344. var NodeValue = function (is, name) {
  345. var get = function (element) {
  346. if (!is(element)) {
  347. throw new Error('Can only get ' + name + ' value of a ' + name + ' node');
  348. }
  349. return getOption(element).getOr('');
  350. };
  351. var getOption = function (element) {
  352. return is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();
  353. };
  354. var set = function (element, value) {
  355. if (!is(element)) {
  356. throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');
  357. }
  358. element.dom.nodeValue = value;
  359. };
  360. return {
  361. get: get,
  362. getOption: getOption,
  363. set: set
  364. };
  365. };
  366. var api = NodeValue(isText$1, 'text');
  367. var get$1 = function (element) {
  368. return api.get(element);
  369. };
  370. var descendants = function (scope, selector) {
  371. return all(selector, scope);
  372. };
  373. var global$1 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');
  374. var isSimpleBoundary = function (dom, node) {
  375. return dom.isBlock(node) || has(dom.schema.getShortEndedElements(), node.nodeName);
  376. };
  377. var isContentEditableFalse = function (dom, node) {
  378. return dom.getContentEditable(node) === 'false';
  379. };
  380. var isContentEditableTrueInCef = function (dom, node) {
  381. return dom.getContentEditable(node) === 'true' && dom.getContentEditableParent(node.parentNode) === 'false';
  382. };
  383. var isHidden = function (dom, node) {
  384. return !dom.isBlock(node) && has(dom.schema.getWhiteSpaceElements(), node.nodeName);
  385. };
  386. var isBoundary = function (dom, node) {
  387. return isSimpleBoundary(dom, node) || isContentEditableFalse(dom, node) || isHidden(dom, node) || isContentEditableTrueInCef(dom, node);
  388. };
  389. var isText = function (node) {
  390. return node.nodeType === 3;
  391. };
  392. var nuSection = function () {
  393. return {
  394. sOffset: 0,
  395. fOffset: 0,
  396. elements: []
  397. };
  398. };
  399. var toLeaf = function (node, offset) {
  400. return leaf(SugarElement.fromDom(node), offset);
  401. };
  402. var walk = function (dom, walkerFn, startNode, callbacks, endNode, skipStart) {
  403. if (skipStart === void 0) {
  404. skipStart = true;
  405. }
  406. var next = skipStart ? walkerFn(false) : startNode;
  407. while (next) {
  408. var isCefNode = isContentEditableFalse(dom, next);
  409. if (isCefNode || isHidden(dom, next)) {
  410. var stopWalking = isCefNode ? callbacks.cef(next) : callbacks.boundary(next);
  411. if (stopWalking) {
  412. break;
  413. } else {
  414. next = walkerFn(true);
  415. continue;
  416. }
  417. } else if (isSimpleBoundary(dom, next)) {
  418. if (callbacks.boundary(next)) {
  419. break;
  420. }
  421. } else if (isText(next)) {
  422. callbacks.text(next);
  423. }
  424. if (next === endNode) {
  425. break;
  426. } else {
  427. next = walkerFn(false);
  428. }
  429. }
  430. };
  431. var collectTextToBoundary = function (dom, section, node, rootNode, forwards) {
  432. if (isBoundary(dom, node)) {
  433. return;
  434. }
  435. var rootBlock = dom.getParent(rootNode, dom.isBlock);
  436. var walker = new global$1(node, rootBlock);
  437. var walkerFn = forwards ? walker.next.bind(walker) : walker.prev.bind(walker);
  438. walk(dom, walkerFn, node, {
  439. boundary: always,
  440. cef: always,
  441. text: function (next) {
  442. if (forwards) {
  443. section.fOffset += next.length;
  444. } else {
  445. section.sOffset += next.length;
  446. }
  447. section.elements.push(SugarElement.fromDom(next));
  448. }
  449. });
  450. };
  451. var collect = function (dom, rootNode, startNode, endNode, callbacks, skipStart) {
  452. if (skipStart === void 0) {
  453. skipStart = true;
  454. }
  455. var walker = new global$1(startNode, rootNode);
  456. var sections = [];
  457. var current = nuSection();
  458. collectTextToBoundary(dom, current, startNode, rootNode, false);
  459. var finishSection = function () {
  460. if (current.elements.length > 0) {
  461. sections.push(current);
  462. current = nuSection();
  463. }
  464. return false;
  465. };
  466. walk(dom, walker.next.bind(walker), startNode, {
  467. boundary: finishSection,
  468. cef: function (node) {
  469. finishSection();
  470. if (callbacks) {
  471. sections.push.apply(sections, callbacks.cef(node));
  472. }
  473. return false;
  474. },
  475. text: function (next) {
  476. current.elements.push(SugarElement.fromDom(next));
  477. if (callbacks) {
  478. callbacks.text(next, current);
  479. }
  480. }
  481. }, endNode, skipStart);
  482. if (endNode) {
  483. collectTextToBoundary(dom, current, endNode, rootNode, true);
  484. }
  485. finishSection();
  486. return sections;
  487. };
  488. var collectRangeSections = function (dom, rng) {
  489. var start = toLeaf(rng.startContainer, rng.startOffset);
  490. var startNode = start.element.dom;
  491. var end = toLeaf(rng.endContainer, rng.endOffset);
  492. var endNode = end.element.dom;
  493. return collect(dom, rng.commonAncestorContainer, startNode, endNode, {
  494. text: function (node, section) {
  495. if (node === endNode) {
  496. section.fOffset += node.length - end.offset;
  497. } else if (node === startNode) {
  498. section.sOffset += start.offset;
  499. }
  500. },
  501. cef: function (node) {
  502. var sections = bind(descendants(SugarElement.fromDom(node), '*[contenteditable=true]'), function (e) {
  503. var ceTrueNode = e.dom;
  504. return collect(dom, ceTrueNode, ceTrueNode);
  505. });
  506. return sort(sections, function (a, b) {
  507. return documentPositionPreceding(a.elements[0].dom, b.elements[0].dom) ? 1 : -1;
  508. });
  509. }
  510. }, false);
  511. };
  512. var fromRng = function (dom, rng) {
  513. return rng.collapsed ? [] : collectRangeSections(dom, rng);
  514. };
  515. var fromNode = function (dom, node) {
  516. var rng = dom.createRng();
  517. rng.selectNode(node);
  518. return fromRng(dom, rng);
  519. };
  520. var fromNodes = function (dom, nodes) {
  521. return bind(nodes, function (node) {
  522. return fromNode(dom, node);
  523. });
  524. };
  525. var find$2 = function (text, pattern, start, finish) {
  526. if (start === void 0) {
  527. start = 0;
  528. }
  529. if (finish === void 0) {
  530. finish = text.length;
  531. }
  532. var regex = pattern.regex;
  533. regex.lastIndex = start;
  534. var results = [];
  535. var match;
  536. while (match = regex.exec(text)) {
  537. var matchedText = match[pattern.matchIndex];
  538. var matchStart = match.index + match[0].indexOf(matchedText);
  539. var matchFinish = matchStart + matchedText.length;
  540. if (matchFinish > finish) {
  541. break;
  542. }
  543. results.push({
  544. start: matchStart,
  545. finish: matchFinish
  546. });
  547. regex.lastIndex = matchFinish;
  548. }
  549. return results;
  550. };
  551. var extract = function (elements, matches) {
  552. var nodePositions = foldl(elements, function (acc, element) {
  553. var content = get$1(element);
  554. var start = acc.last;
  555. var finish = start + content.length;
  556. var positions = bind(matches, function (match, matchIdx) {
  557. if (match.start < finish && match.finish > start) {
  558. return [{
  559. element: element,
  560. start: Math.max(start, match.start) - start,
  561. finish: Math.min(finish, match.finish) - start,
  562. matchId: matchIdx
  563. }];
  564. } else {
  565. return [];
  566. }
  567. });
  568. return {
  569. results: acc.results.concat(positions),
  570. last: finish
  571. };
  572. }, {
  573. results: [],
  574. last: 0
  575. }).results;
  576. return groupBy(nodePositions, function (position) {
  577. return position.matchId;
  578. });
  579. };
  580. var find$1 = function (pattern, sections) {
  581. return bind(sections, function (section) {
  582. var elements = section.elements;
  583. var content = map(elements, get$1).join('');
  584. var positions = find$2(content, pattern, section.sOffset, content.length - section.fOffset);
  585. return extract(elements, positions);
  586. });
  587. };
  588. var mark = function (matches, replacementNode) {
  589. eachr(matches, function (match, idx) {
  590. eachr(match, function (pos) {
  591. var wrapper = SugarElement.fromDom(replacementNode.cloneNode(false));
  592. set(wrapper, 'data-mce-index', idx);
  593. var textNode = pos.element.dom;
  594. if (textNode.length === pos.finish && pos.start === 0) {
  595. wrap(pos.element, wrapper);
  596. } else {
  597. if (textNode.length !== pos.finish) {
  598. textNode.splitText(pos.finish);
  599. }
  600. var matchNode = textNode.splitText(pos.start);
  601. wrap(SugarElement.fromDom(matchNode), wrapper);
  602. }
  603. });
  604. });
  605. };
  606. var findAndMark = function (dom, pattern, node, replacementNode) {
  607. var textSections = fromNode(dom, node);
  608. var matches = find$1(pattern, textSections);
  609. mark(matches, replacementNode);
  610. return matches.length;
  611. };
  612. var findAndMarkInSelection = function (dom, pattern, selection, replacementNode) {
  613. var bookmark = selection.getBookmark();
  614. var nodes = dom.select('td[data-mce-selected],th[data-mce-selected]');
  615. var textSections = nodes.length > 0 ? fromNodes(dom, nodes) : fromRng(dom, selection.getRng());
  616. var matches = find$1(pattern, textSections);
  617. mark(matches, replacementNode);
  618. selection.moveToBookmark(bookmark);
  619. return matches.length;
  620. };
  621. var getElmIndex = function (elm) {
  622. var value = elm.getAttribute('data-mce-index');
  623. if (typeof value === 'number') {
  624. return '' + value;
  625. }
  626. return value;
  627. };
  628. var markAllMatches = function (editor, currentSearchState, pattern, inSelection) {
  629. var marker = editor.dom.create('span', { 'data-mce-bogus': 1 });
  630. marker.className = 'mce-match-marker';
  631. var node = editor.getBody();
  632. done(editor, currentSearchState, false);
  633. if (inSelection) {
  634. return findAndMarkInSelection(editor.dom, pattern, editor.selection, marker);
  635. } else {
  636. return findAndMark(editor.dom, pattern, node, marker);
  637. }
  638. };
  639. var unwrap = function (node) {
  640. var parentNode = node.parentNode;
  641. if (node.firstChild) {
  642. parentNode.insertBefore(node.firstChild, node);
  643. }
  644. node.parentNode.removeChild(node);
  645. };
  646. var findSpansByIndex = function (editor, index) {
  647. var spans = [];
  648. var nodes = global$2.toArray(editor.getBody().getElementsByTagName('span'));
  649. if (nodes.length) {
  650. for (var i = 0; i < nodes.length; i++) {
  651. var nodeIndex = getElmIndex(nodes[i]);
  652. if (nodeIndex === null || !nodeIndex.length) {
  653. continue;
  654. }
  655. if (nodeIndex === index.toString()) {
  656. spans.push(nodes[i]);
  657. }
  658. }
  659. }
  660. return spans;
  661. };
  662. var moveSelection = function (editor, currentSearchState, forward) {
  663. var searchState = currentSearchState.get();
  664. var testIndex = searchState.index;
  665. var dom = editor.dom;
  666. forward = forward !== false;
  667. if (forward) {
  668. if (testIndex + 1 === searchState.count) {
  669. testIndex = 0;
  670. } else {
  671. testIndex++;
  672. }
  673. } else {
  674. if (testIndex - 1 === -1) {
  675. testIndex = searchState.count - 1;
  676. } else {
  677. testIndex--;
  678. }
  679. }
  680. dom.removeClass(findSpansByIndex(editor, searchState.index), 'mce-match-marker-selected');
  681. var spans = findSpansByIndex(editor, testIndex);
  682. if (spans.length) {
  683. dom.addClass(findSpansByIndex(editor, testIndex), 'mce-match-marker-selected');
  684. editor.selection.scrollIntoView(spans[0]);
  685. return testIndex;
  686. }
  687. return -1;
  688. };
  689. var removeNode = function (dom, node) {
  690. var parent = node.parentNode;
  691. dom.remove(node);
  692. if (dom.isEmpty(parent)) {
  693. dom.remove(parent);
  694. }
  695. };
  696. var escapeSearchText = function (text, wholeWord) {
  697. var escapedText = text.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&').replace(/\s/g, '[^\\S\\r\\n\\uFEFF]');
  698. var wordRegex = '(' + escapedText + ')';
  699. return wholeWord ? '(?:^|\\s|' + punctuation() + ')' + wordRegex + ('(?=$|\\s|' + punctuation() + ')') : wordRegex;
  700. };
  701. var find = function (editor, currentSearchState, text, matchCase, wholeWord, inSelection) {
  702. var escapedText = escapeSearchText(text, wholeWord);
  703. var pattern = {
  704. regex: new RegExp(escapedText, matchCase ? 'g' : 'gi'),
  705. matchIndex: 1
  706. };
  707. var count = markAllMatches(editor, currentSearchState, pattern, inSelection);
  708. if (count) {
  709. var newIndex = moveSelection(editor, currentSearchState, true);
  710. currentSearchState.set({
  711. index: newIndex,
  712. count: count,
  713. text: text,
  714. matchCase: matchCase,
  715. wholeWord: wholeWord,
  716. inSelection: inSelection
  717. });
  718. }
  719. return count;
  720. };
  721. var next = function (editor, currentSearchState) {
  722. var index = moveSelection(editor, currentSearchState, true);
  723. currentSearchState.set(__assign(__assign({}, currentSearchState.get()), { index: index }));
  724. };
  725. var prev = function (editor, currentSearchState) {
  726. var index = moveSelection(editor, currentSearchState, false);
  727. currentSearchState.set(__assign(__assign({}, currentSearchState.get()), { index: index }));
  728. };
  729. var isMatchSpan = function (node) {
  730. var matchIndex = getElmIndex(node);
  731. return matchIndex !== null && matchIndex.length > 0;
  732. };
  733. var replace = function (editor, currentSearchState, text, forward, all) {
  734. var searchState = currentSearchState.get();
  735. var currentIndex = searchState.index;
  736. var currentMatchIndex, nextIndex = currentIndex;
  737. forward = forward !== false;
  738. var node = editor.getBody();
  739. var nodes = global$2.grep(global$2.toArray(node.getElementsByTagName('span')), isMatchSpan);
  740. for (var i = 0; i < nodes.length; i++) {
  741. var nodeIndex = getElmIndex(nodes[i]);
  742. var matchIndex = currentMatchIndex = parseInt(nodeIndex, 10);
  743. if (all || matchIndex === searchState.index) {
  744. if (text.length) {
  745. nodes[i].firstChild.nodeValue = text;
  746. unwrap(nodes[i]);
  747. } else {
  748. removeNode(editor.dom, nodes[i]);
  749. }
  750. while (nodes[++i]) {
  751. matchIndex = parseInt(getElmIndex(nodes[i]), 10);
  752. if (matchIndex === currentMatchIndex) {
  753. removeNode(editor.dom, nodes[i]);
  754. } else {
  755. i--;
  756. break;
  757. }
  758. }
  759. if (forward) {
  760. nextIndex--;
  761. }
  762. } else if (currentMatchIndex > currentIndex) {
  763. nodes[i].setAttribute('data-mce-index', String(currentMatchIndex - 1));
  764. }
  765. }
  766. currentSearchState.set(__assign(__assign({}, searchState), {
  767. count: all ? 0 : searchState.count - 1,
  768. index: nextIndex
  769. }));
  770. if (forward) {
  771. next(editor, currentSearchState);
  772. } else {
  773. prev(editor, currentSearchState);
  774. }
  775. return !all && currentSearchState.get().count > 0;
  776. };
  777. var done = function (editor, currentSearchState, keepEditorSelection) {
  778. var startContainer, endContainer;
  779. var searchState = currentSearchState.get();
  780. var nodes = global$2.toArray(editor.getBody().getElementsByTagName('span'));
  781. for (var i = 0; i < nodes.length; i++) {
  782. var nodeIndex = getElmIndex(nodes[i]);
  783. if (nodeIndex !== null && nodeIndex.length) {
  784. if (nodeIndex === searchState.index.toString()) {
  785. if (!startContainer) {
  786. startContainer = nodes[i].firstChild;
  787. }
  788. endContainer = nodes[i].firstChild;
  789. }
  790. unwrap(nodes[i]);
  791. }
  792. }
  793. currentSearchState.set(__assign(__assign({}, searchState), {
  794. index: -1,
  795. count: 0,
  796. text: ''
  797. }));
  798. if (startContainer && endContainer) {
  799. var rng = editor.dom.createRng();
  800. rng.setStart(startContainer, 0);
  801. rng.setEnd(endContainer, endContainer.data.length);
  802. if (keepEditorSelection !== false) {
  803. editor.selection.setRng(rng);
  804. }
  805. return rng;
  806. }
  807. };
  808. var hasNext = function (editor, currentSearchState) {
  809. return currentSearchState.get().count > 1;
  810. };
  811. var hasPrev = function (editor, currentSearchState) {
  812. return currentSearchState.get().count > 1;
  813. };
  814. var get = function (editor, currentState) {
  815. var done$1 = function (keepEditorSelection) {
  816. return done(editor, currentState, keepEditorSelection);
  817. };
  818. var find$1 = function (text, matchCase, wholeWord, inSelection) {
  819. if (inSelection === void 0) {
  820. inSelection = false;
  821. }
  822. return find(editor, currentState, text, matchCase, wholeWord, inSelection);
  823. };
  824. var next$1 = function () {
  825. return next(editor, currentState);
  826. };
  827. var prev$1 = function () {
  828. return prev(editor, currentState);
  829. };
  830. var replace$1 = function (text, forward, all) {
  831. return replace(editor, currentState, text, forward, all);
  832. };
  833. return {
  834. done: done$1,
  835. find: find$1,
  836. next: next$1,
  837. prev: prev$1,
  838. replace: replace$1
  839. };
  840. };
  841. var singleton = function (doRevoke) {
  842. var subject = Cell(Optional.none());
  843. var revoke = function () {
  844. return subject.get().each(doRevoke);
  845. };
  846. var clear = function () {
  847. revoke();
  848. subject.set(Optional.none());
  849. };
  850. var isSet = function () {
  851. return subject.get().isSome();
  852. };
  853. var get = function () {
  854. return subject.get();
  855. };
  856. var set = function (s) {
  857. revoke();
  858. subject.set(Optional.some(s));
  859. };
  860. return {
  861. clear: clear,
  862. isSet: isSet,
  863. get: get,
  864. set: set
  865. };
  866. };
  867. var value = function () {
  868. var subject = singleton(noop);
  869. var on = function (f) {
  870. return subject.get().each(f);
  871. };
  872. return __assign(__assign({}, subject), { on: on });
  873. };
  874. var global = tinymce.util.Tools.resolve('tinymce.Env');
  875. var open = function (editor, currentSearchState) {
  876. var dialogApi = value();
  877. editor.undoManager.add();
  878. var selectedText = global$2.trim(editor.selection.getContent({ format: 'text' }));
  879. var updateButtonStates = function (api) {
  880. var updateNext = hasNext(editor, currentSearchState) ? api.enable : api.disable;
  881. updateNext('next');
  882. var updatePrev = hasPrev(editor, currentSearchState) ? api.enable : api.disable;
  883. updatePrev('prev');
  884. };
  885. var updateSearchState = function (api) {
  886. var data = api.getData();
  887. var current = currentSearchState.get();
  888. currentSearchState.set(__assign(__assign({}, current), {
  889. matchCase: data.matchcase,
  890. wholeWord: data.wholewords,
  891. inSelection: data.inselection
  892. }));
  893. };
  894. var disableAll = function (api, disable) {
  895. var buttons = [
  896. 'replace',
  897. 'replaceall',
  898. 'prev',
  899. 'next'
  900. ];
  901. var toggle = disable ? api.disable : api.enable;
  902. each(buttons, toggle);
  903. };
  904. var notFoundAlert = function (api) {
  905. editor.windowManager.alert('Could not find the specified string.', function () {
  906. api.focus('findtext');
  907. });
  908. };
  909. var focusButtonIfRequired = function (api, name) {
  910. if (global.browser.isSafari() && global.deviceType.isTouch() && (name === 'find' || name === 'replace' || name === 'replaceall')) {
  911. api.focus(name);
  912. }
  913. };
  914. var reset = function (api) {
  915. done(editor, currentSearchState, false);
  916. disableAll(api, true);
  917. updateButtonStates(api);
  918. };
  919. var doFind = function (api) {
  920. var data = api.getData();
  921. var last = currentSearchState.get();
  922. if (!data.findtext.length) {
  923. reset(api);
  924. return;
  925. }
  926. if (last.text === data.findtext && last.matchCase === data.matchcase && last.wholeWord === data.wholewords) {
  927. next(editor, currentSearchState);
  928. } else {
  929. var count = find(editor, currentSearchState, data.findtext, data.matchcase, data.wholewords, data.inselection);
  930. if (count <= 0) {
  931. notFoundAlert(api);
  932. }
  933. disableAll(api, count === 0);
  934. }
  935. updateButtonStates(api);
  936. };
  937. var initialState = currentSearchState.get();
  938. var initialData = {
  939. findtext: selectedText,
  940. replacetext: '',
  941. wholewords: initialState.wholeWord,
  942. matchcase: initialState.matchCase,
  943. inselection: initialState.inSelection
  944. };
  945. var spec = {
  946. title: 'Find and Replace',
  947. size: 'normal',
  948. body: {
  949. type: 'panel',
  950. items: [
  951. {
  952. type: 'bar',
  953. items: [
  954. {
  955. type: 'input',
  956. name: 'findtext',
  957. placeholder: 'Find',
  958. maximized: true,
  959. inputMode: 'search'
  960. },
  961. {
  962. type: 'button',
  963. name: 'prev',
  964. text: 'Previous',
  965. icon: 'action-prev',
  966. disabled: true,
  967. borderless: true
  968. },
  969. {
  970. type: 'button',
  971. name: 'next',
  972. text: 'Next',
  973. icon: 'action-next',
  974. disabled: true,
  975. borderless: true
  976. }
  977. ]
  978. },
  979. {
  980. type: 'input',
  981. name: 'replacetext',
  982. placeholder: 'Replace with',
  983. inputMode: 'search'
  984. }
  985. ]
  986. },
  987. buttons: [
  988. {
  989. type: 'menu',
  990. name: 'options',
  991. icon: 'preferences',
  992. tooltip: 'Preferences',
  993. align: 'start',
  994. items: [
  995. {
  996. type: 'togglemenuitem',
  997. name: 'matchcase',
  998. text: 'Match case'
  999. },
  1000. {
  1001. type: 'togglemenuitem',
  1002. name: 'wholewords',
  1003. text: 'Find whole words only'
  1004. },
  1005. {
  1006. type: 'togglemenuitem',
  1007. name: 'inselection',
  1008. text: 'Find in selection'
  1009. }
  1010. ]
  1011. },
  1012. {
  1013. type: 'custom',
  1014. name: 'find',
  1015. text: 'Find',
  1016. primary: true
  1017. },
  1018. {
  1019. type: 'custom',
  1020. name: 'replace',
  1021. text: 'Replace',
  1022. disabled: true
  1023. },
  1024. {
  1025. type: 'custom',
  1026. name: 'replaceall',
  1027. text: 'Replace all',
  1028. disabled: true
  1029. }
  1030. ],
  1031. initialData: initialData,
  1032. onChange: function (api, details) {
  1033. if (details.name === 'findtext' && currentSearchState.get().count > 0) {
  1034. reset(api);
  1035. }
  1036. },
  1037. onAction: function (api, details) {
  1038. var data = api.getData();
  1039. switch (details.name) {
  1040. case 'find':
  1041. doFind(api);
  1042. break;
  1043. case 'replace':
  1044. if (!replace(editor, currentSearchState, data.replacetext)) {
  1045. reset(api);
  1046. } else {
  1047. updateButtonStates(api);
  1048. }
  1049. break;
  1050. case 'replaceall':
  1051. replace(editor, currentSearchState, data.replacetext, true, true);
  1052. reset(api);
  1053. break;
  1054. case 'prev':
  1055. prev(editor, currentSearchState);
  1056. updateButtonStates(api);
  1057. break;
  1058. case 'next':
  1059. next(editor, currentSearchState);
  1060. updateButtonStates(api);
  1061. break;
  1062. case 'matchcase':
  1063. case 'wholewords':
  1064. case 'inselection':
  1065. updateSearchState(api);
  1066. reset(api);
  1067. break;
  1068. }
  1069. focusButtonIfRequired(api, details.name);
  1070. },
  1071. onSubmit: function (api) {
  1072. doFind(api);
  1073. focusButtonIfRequired(api, 'find');
  1074. },
  1075. onClose: function () {
  1076. editor.focus();
  1077. done(editor, currentSearchState);
  1078. editor.undoManager.add();
  1079. }
  1080. };
  1081. dialogApi.set(editor.windowManager.open(spec, { inline: 'toolbar' }));
  1082. };
  1083. var register$1 = function (editor, currentSearchState) {
  1084. editor.addCommand('SearchReplace', function () {
  1085. open(editor, currentSearchState);
  1086. });
  1087. };
  1088. var showDialog = function (editor, currentSearchState) {
  1089. return function () {
  1090. open(editor, currentSearchState);
  1091. };
  1092. };
  1093. var register = function (editor, currentSearchState) {
  1094. editor.ui.registry.addMenuItem('searchreplace', {
  1095. text: 'Find and replace...',
  1096. shortcut: 'Meta+F',
  1097. onAction: showDialog(editor, currentSearchState),
  1098. icon: 'search'
  1099. });
  1100. editor.ui.registry.addButton('searchreplace', {
  1101. tooltip: 'Find and replace',
  1102. onAction: showDialog(editor, currentSearchState),
  1103. icon: 'search'
  1104. });
  1105. editor.shortcuts.add('Meta+F', '', showDialog(editor, currentSearchState));
  1106. };
  1107. function Plugin () {
  1108. global$3.add('searchreplace', function (editor) {
  1109. var currentSearchState = Cell({
  1110. index: -1,
  1111. count: 0,
  1112. text: '',
  1113. matchCase: false,
  1114. wholeWord: false,
  1115. inSelection: false
  1116. });
  1117. register$1(editor, currentSearchState);
  1118. register(editor, currentSearchState);
  1119. return get(editor, currentSearchState);
  1120. });
  1121. }
  1122. Plugin();
  1123. }());