plugin.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  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.1.0 (2019-10-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. var clone = function () {
  20. return Cell(get());
  21. };
  22. return {
  23. get: get,
  24. set: set,
  25. clone: clone
  26. };
  27. };
  28. var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
  29. var __assign = function () {
  30. __assign = Object.assign || function __assign(t) {
  31. for (var s, i = 1, n = arguments.length; i < n; i++) {
  32. s = arguments[i];
  33. for (var p in s)
  34. if (Object.prototype.hasOwnProperty.call(s, p))
  35. t[p] = s[p];
  36. }
  37. return t;
  38. };
  39. return __assign.apply(this, arguments);
  40. };
  41. var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');
  42. function isContentEditableFalse(node) {
  43. return node && node.nodeType === 1 && node.contentEditable === 'false';
  44. }
  45. function findAndReplaceDOMText(regex, node, replacementNode, captureGroup, schema) {
  46. var m;
  47. var matches = [];
  48. var text, count = 0, doc;
  49. var blockElementsMap, hiddenTextElementsMap, shortEndedElementsMap;
  50. doc = node.ownerDocument;
  51. blockElementsMap = schema.getBlockElements();
  52. hiddenTextElementsMap = schema.getWhiteSpaceElements();
  53. shortEndedElementsMap = schema.getShortEndedElements();
  54. function getMatchIndexes(m, captureGroup) {
  55. captureGroup = captureGroup || 0;
  56. if (!m[0]) {
  57. throw new Error('findAndReplaceDOMText cannot handle zero-length matches');
  58. }
  59. var index = m.index;
  60. if (captureGroup > 0) {
  61. var cg = m[captureGroup];
  62. if (!cg) {
  63. throw new Error('Invalid capture group');
  64. }
  65. index += m[0].indexOf(cg);
  66. m[0] = cg;
  67. }
  68. return [
  69. index,
  70. index + m[0].length,
  71. [m[0]]
  72. ];
  73. }
  74. function getText(node) {
  75. var txt;
  76. if (node.nodeType === 3) {
  77. return node.data;
  78. }
  79. if (hiddenTextElementsMap[node.nodeName] && !blockElementsMap[node.nodeName]) {
  80. return '';
  81. }
  82. txt = '';
  83. if (isContentEditableFalse(node)) {
  84. return '\n';
  85. }
  86. if (blockElementsMap[node.nodeName] || shortEndedElementsMap[node.nodeName]) {
  87. txt += '\n';
  88. }
  89. if (node = node.firstChild) {
  90. do {
  91. txt += getText(node);
  92. } while (node = node.nextSibling);
  93. }
  94. return txt;
  95. }
  96. function stepThroughMatches(node, matches, replaceFn) {
  97. var startNode, endNode, startNodeIndex, endNodeIndex, innerNodes = [], atIndex = 0, curNode = node, matchLocation = matches.shift(), matchIndex = 0;
  98. out:
  99. while (true) {
  100. if (blockElementsMap[curNode.nodeName] || shortEndedElementsMap[curNode.nodeName] || isContentEditableFalse(curNode)) {
  101. atIndex++;
  102. }
  103. if (curNode.nodeType === 3) {
  104. if (!endNode && curNode.length + atIndex >= matchLocation[1]) {
  105. endNode = curNode;
  106. endNodeIndex = matchLocation[1] - atIndex;
  107. } else if (startNode) {
  108. innerNodes.push(curNode);
  109. }
  110. if (!startNode && curNode.length + atIndex > matchLocation[0]) {
  111. startNode = curNode;
  112. startNodeIndex = matchLocation[0] - atIndex;
  113. }
  114. atIndex += curNode.length;
  115. }
  116. if (startNode && endNode) {
  117. curNode = replaceFn({
  118. startNode: startNode,
  119. startNodeIndex: startNodeIndex,
  120. endNode: endNode,
  121. endNodeIndex: endNodeIndex,
  122. innerNodes: innerNodes,
  123. match: matchLocation[2],
  124. matchIndex: matchIndex
  125. });
  126. atIndex -= endNode.length - endNodeIndex;
  127. startNode = null;
  128. endNode = null;
  129. innerNodes = [];
  130. matchLocation = matches.shift();
  131. matchIndex++;
  132. if (!matchLocation) {
  133. break;
  134. }
  135. } else if ((!hiddenTextElementsMap[curNode.nodeName] || blockElementsMap[curNode.nodeName]) && curNode.firstChild) {
  136. if (!isContentEditableFalse(curNode)) {
  137. curNode = curNode.firstChild;
  138. continue;
  139. }
  140. } else if (curNode.nextSibling) {
  141. curNode = curNode.nextSibling;
  142. continue;
  143. }
  144. while (true) {
  145. if (curNode.nextSibling) {
  146. curNode = curNode.nextSibling;
  147. break;
  148. } else if (curNode.parentNode !== node) {
  149. curNode = curNode.parentNode;
  150. } else {
  151. break out;
  152. }
  153. }
  154. }
  155. }
  156. function genReplacer(nodeName) {
  157. var makeReplacementNode;
  158. if (typeof nodeName !== 'function') {
  159. var stencilNode_1 = nodeName.nodeType ? nodeName : doc.createElement(nodeName);
  160. makeReplacementNode = function (fill, matchIndex) {
  161. var clone = stencilNode_1.cloneNode(false);
  162. clone.setAttribute('data-mce-index', matchIndex);
  163. if (fill) {
  164. clone.appendChild(doc.createTextNode(fill));
  165. }
  166. return clone;
  167. };
  168. } else {
  169. makeReplacementNode = nodeName;
  170. }
  171. return function (range) {
  172. var before;
  173. var after;
  174. var parentNode;
  175. var startNode = range.startNode;
  176. var endNode = range.endNode;
  177. var matchIndex = range.matchIndex;
  178. if (startNode === endNode) {
  179. var node_1 = startNode;
  180. parentNode = node_1.parentNode;
  181. if (range.startNodeIndex > 0) {
  182. before = doc.createTextNode(node_1.data.substring(0, range.startNodeIndex));
  183. parentNode.insertBefore(before, node_1);
  184. }
  185. var el = makeReplacementNode(range.match[0], matchIndex);
  186. parentNode.insertBefore(el, node_1);
  187. if (range.endNodeIndex < node_1.length) {
  188. after = doc.createTextNode(node_1.data.substring(range.endNodeIndex));
  189. parentNode.insertBefore(after, node_1);
  190. }
  191. node_1.parentNode.removeChild(node_1);
  192. return el;
  193. }
  194. before = doc.createTextNode(startNode.data.substring(0, range.startNodeIndex));
  195. after = doc.createTextNode(endNode.data.substring(range.endNodeIndex));
  196. var elA = makeReplacementNode(startNode.data.substring(range.startNodeIndex), matchIndex);
  197. for (var i = 0, l = range.innerNodes.length; i < l; ++i) {
  198. var innerNode = range.innerNodes[i];
  199. var innerEl = makeReplacementNode(innerNode.data, matchIndex);
  200. innerNode.parentNode.replaceChild(innerEl, innerNode);
  201. }
  202. var elB = makeReplacementNode(endNode.data.substring(0, range.endNodeIndex), matchIndex);
  203. parentNode = startNode.parentNode;
  204. parentNode.insertBefore(before, startNode);
  205. parentNode.insertBefore(elA, startNode);
  206. parentNode.removeChild(startNode);
  207. parentNode = endNode.parentNode;
  208. parentNode.insertBefore(elB, endNode);
  209. parentNode.insertBefore(after, endNode);
  210. parentNode.removeChild(endNode);
  211. return elB;
  212. };
  213. }
  214. text = getText(node);
  215. if (!text) {
  216. return;
  217. }
  218. if (regex.global) {
  219. while (m = regex.exec(text)) {
  220. matches.push(getMatchIndexes(m, captureGroup));
  221. }
  222. } else {
  223. m = text.match(regex);
  224. matches.push(getMatchIndexes(m, captureGroup));
  225. }
  226. if (matches.length) {
  227. count = matches.length;
  228. stepThroughMatches(node, matches, genReplacer(replacementNode));
  229. }
  230. return count;
  231. }
  232. var FindReplaceText = { findAndReplaceDOMText: findAndReplaceDOMText };
  233. var getElmIndex = function (elm) {
  234. var value = elm.getAttribute('data-mce-index');
  235. if (typeof value === 'number') {
  236. return '' + value;
  237. }
  238. return value;
  239. };
  240. var markAllMatches = function (editor, currentSearchState, regex) {
  241. var node, marker;
  242. marker = editor.dom.create('span', { 'data-mce-bogus': 1 });
  243. marker.className = 'mce-match-marker';
  244. node = editor.getBody();
  245. done(editor, currentSearchState, false);
  246. return FindReplaceText.findAndReplaceDOMText(regex, node, marker, false, editor.schema);
  247. };
  248. var unwrap = function (node) {
  249. var parentNode = node.parentNode;
  250. if (node.firstChild) {
  251. parentNode.insertBefore(node.firstChild, node);
  252. }
  253. node.parentNode.removeChild(node);
  254. };
  255. var findSpansByIndex = function (editor, index) {
  256. var nodes;
  257. var spans = [];
  258. nodes = global$1.toArray(editor.getBody().getElementsByTagName('span'));
  259. if (nodes.length) {
  260. for (var i = 0; i < nodes.length; i++) {
  261. var nodeIndex = getElmIndex(nodes[i]);
  262. if (nodeIndex === null || !nodeIndex.length) {
  263. continue;
  264. }
  265. if (nodeIndex === index.toString()) {
  266. spans.push(nodes[i]);
  267. }
  268. }
  269. }
  270. return spans;
  271. };
  272. var moveSelection = function (editor, currentSearchState, forward) {
  273. var searchState = currentSearchState.get();
  274. var testIndex = searchState.index;
  275. var dom = editor.dom;
  276. forward = forward !== false;
  277. if (forward) {
  278. if (testIndex + 1 === searchState.count) {
  279. testIndex = 0;
  280. } else {
  281. testIndex++;
  282. }
  283. } else {
  284. if (testIndex - 1 === -1) {
  285. testIndex = searchState.count - 1;
  286. } else {
  287. testIndex--;
  288. }
  289. }
  290. dom.removeClass(findSpansByIndex(editor, searchState.index), 'mce-match-marker-selected');
  291. var spans = findSpansByIndex(editor, testIndex);
  292. if (spans.length) {
  293. dom.addClass(findSpansByIndex(editor, testIndex), 'mce-match-marker-selected');
  294. editor.selection.scrollIntoView(spans[0]);
  295. return testIndex;
  296. }
  297. return -1;
  298. };
  299. var removeNode = function (dom, node) {
  300. var parent = node.parentNode;
  301. dom.remove(node);
  302. if (dom.isEmpty(parent)) {
  303. dom.remove(parent);
  304. }
  305. };
  306. var find = function (editor, currentSearchState, text, matchCase, wholeWord) {
  307. text = text.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
  308. text = text.replace(/\s/g, '[^\\S\\r\\n]');
  309. text = wholeWord ? '\\b' + text + '\\b' : text;
  310. var count = markAllMatches(editor, currentSearchState, new RegExp(text, matchCase ? 'g' : 'gi'));
  311. if (count) {
  312. var newIndex = moveSelection(editor, currentSearchState, true);
  313. currentSearchState.set({
  314. index: newIndex,
  315. count: count,
  316. text: text,
  317. matchCase: matchCase,
  318. wholeWord: wholeWord
  319. });
  320. }
  321. return count;
  322. };
  323. var next = function (editor, currentSearchState) {
  324. var index = moveSelection(editor, currentSearchState, true);
  325. currentSearchState.set(__assign(__assign({}, currentSearchState.get()), { index: index }));
  326. };
  327. var prev = function (editor, currentSearchState) {
  328. var index = moveSelection(editor, currentSearchState, false);
  329. currentSearchState.set(__assign(__assign({}, currentSearchState.get()), { index: index }));
  330. };
  331. var isMatchSpan = function (node) {
  332. var matchIndex = getElmIndex(node);
  333. return matchIndex !== null && matchIndex.length > 0;
  334. };
  335. var replace = function (editor, currentSearchState, text, forward, all) {
  336. var searchState = currentSearchState.get();
  337. var currentIndex = searchState.index;
  338. var i, nodes, node, matchIndex, currentMatchIndex, nextIndex = currentIndex;
  339. forward = forward !== false;
  340. node = editor.getBody();
  341. nodes = global$1.grep(global$1.toArray(node.getElementsByTagName('span')), isMatchSpan);
  342. for (i = 0; i < nodes.length; i++) {
  343. var nodeIndex = getElmIndex(nodes[i]);
  344. matchIndex = currentMatchIndex = parseInt(nodeIndex, 10);
  345. if (all || matchIndex === searchState.index) {
  346. if (text.length) {
  347. nodes[i].firstChild.nodeValue = text;
  348. unwrap(nodes[i]);
  349. } else {
  350. removeNode(editor.dom, nodes[i]);
  351. }
  352. while (nodes[++i]) {
  353. matchIndex = parseInt(getElmIndex(nodes[i]), 10);
  354. if (matchIndex === currentMatchIndex) {
  355. removeNode(editor.dom, nodes[i]);
  356. } else {
  357. i--;
  358. break;
  359. }
  360. }
  361. if (forward) {
  362. nextIndex--;
  363. }
  364. } else if (currentMatchIndex > currentIndex) {
  365. nodes[i].setAttribute('data-mce-index', String(currentMatchIndex - 1));
  366. }
  367. }
  368. currentSearchState.set(__assign(__assign({}, searchState), {
  369. count: all ? 0 : searchState.count - 1,
  370. index: nextIndex
  371. }));
  372. if (forward) {
  373. next(editor, currentSearchState);
  374. } else {
  375. prev(editor, currentSearchState);
  376. }
  377. return !all && currentSearchState.get().count > 0;
  378. };
  379. var done = function (editor, currentSearchState, keepEditorSelection) {
  380. var i, nodes, startContainer, endContainer;
  381. var searchState = currentSearchState.get();
  382. nodes = global$1.toArray(editor.getBody().getElementsByTagName('span'));
  383. for (i = 0; i < nodes.length; i++) {
  384. var nodeIndex = getElmIndex(nodes[i]);
  385. if (nodeIndex !== null && nodeIndex.length) {
  386. if (nodeIndex === searchState.index.toString()) {
  387. if (!startContainer) {
  388. startContainer = nodes[i].firstChild;
  389. }
  390. endContainer = nodes[i].firstChild;
  391. }
  392. unwrap(nodes[i]);
  393. }
  394. }
  395. currentSearchState.set(__assign(__assign({}, searchState), {
  396. index: -1,
  397. count: 0,
  398. text: ''
  399. }));
  400. if (startContainer && endContainer) {
  401. var rng = editor.dom.createRng();
  402. rng.setStart(startContainer, 0);
  403. rng.setEnd(endContainer, endContainer.data.length);
  404. if (keepEditorSelection !== false) {
  405. editor.selection.setRng(rng);
  406. }
  407. return rng;
  408. }
  409. };
  410. var hasNext = function (editor, currentSearchState) {
  411. return currentSearchState.get().count > 1;
  412. };
  413. var hasPrev = function (editor, currentSearchState) {
  414. return currentSearchState.get().count > 1;
  415. };
  416. var get = function (editor, currentState) {
  417. var done$1 = function (keepEditorSelection) {
  418. return done(editor, currentState, keepEditorSelection);
  419. };
  420. var find$1 = function (text, matchCase, wholeWord) {
  421. return find(editor, currentState, text, matchCase, wholeWord);
  422. };
  423. var next$1 = function () {
  424. return next(editor, currentState);
  425. };
  426. var prev$1 = function () {
  427. return prev(editor, currentState);
  428. };
  429. var replace$1 = function (text, forward, all) {
  430. return replace(editor, currentState, text, forward, all);
  431. };
  432. return {
  433. done: done$1,
  434. find: find$1,
  435. next: next$1,
  436. prev: prev$1,
  437. replace: replace$1
  438. };
  439. };
  440. var Api = { get: get };
  441. var noop = function () {
  442. };
  443. var constant = function (value) {
  444. return function () {
  445. return value;
  446. };
  447. };
  448. var never = constant(false);
  449. var always = constant(true);
  450. var none = function () {
  451. return NONE;
  452. };
  453. var NONE = function () {
  454. var eq = function (o) {
  455. return o.isNone();
  456. };
  457. var call = function (thunk) {
  458. return thunk();
  459. };
  460. var id = function (n) {
  461. return n;
  462. };
  463. var me = {
  464. fold: function (n, s) {
  465. return n();
  466. },
  467. is: never,
  468. isSome: never,
  469. isNone: always,
  470. getOr: id,
  471. getOrThunk: call,
  472. getOrDie: function (msg) {
  473. throw new Error(msg || 'error: getOrDie called on none.');
  474. },
  475. getOrNull: constant(null),
  476. getOrUndefined: constant(undefined),
  477. or: id,
  478. orThunk: call,
  479. map: none,
  480. each: noop,
  481. bind: none,
  482. exists: never,
  483. forall: always,
  484. filter: none,
  485. equals: eq,
  486. equals_: eq,
  487. toArray: function () {
  488. return [];
  489. },
  490. toString: constant('none()')
  491. };
  492. if (Object.freeze) {
  493. Object.freeze(me);
  494. }
  495. return me;
  496. }();
  497. var some = function (a) {
  498. var constant_a = constant(a);
  499. var self = function () {
  500. return me;
  501. };
  502. var bind = function (f) {
  503. return f(a);
  504. };
  505. var me = {
  506. fold: function (n, s) {
  507. return s(a);
  508. },
  509. is: function (v) {
  510. return a === v;
  511. },
  512. isSome: always,
  513. isNone: never,
  514. getOr: constant_a,
  515. getOrThunk: constant_a,
  516. getOrDie: constant_a,
  517. getOrNull: constant_a,
  518. getOrUndefined: constant_a,
  519. or: self,
  520. orThunk: self,
  521. map: function (f) {
  522. return some(f(a));
  523. },
  524. each: function (f) {
  525. f(a);
  526. },
  527. bind: bind,
  528. exists: bind,
  529. forall: bind,
  530. filter: function (f) {
  531. return f(a) ? me : NONE;
  532. },
  533. toArray: function () {
  534. return [a];
  535. },
  536. toString: function () {
  537. return 'some(' + a + ')';
  538. },
  539. equals: function (o) {
  540. return o.is(a);
  541. },
  542. equals_: function (o, elementEq) {
  543. return o.fold(never, function (b) {
  544. return elementEq(a, b);
  545. });
  546. }
  547. };
  548. return me;
  549. };
  550. var from = function (value) {
  551. return value === null || value === undefined ? NONE : some(value);
  552. };
  553. var Option = {
  554. some: some,
  555. none: none,
  556. from: from
  557. };
  558. var typeOf = function (x) {
  559. if (x === null) {
  560. return 'null';
  561. }
  562. var t = typeof x;
  563. if (t === 'object' && (Array.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'Array')) {
  564. return 'array';
  565. }
  566. if (t === 'object' && (String.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'String')) {
  567. return 'string';
  568. }
  569. return t;
  570. };
  571. var isType = function (type) {
  572. return function (value) {
  573. return typeOf(value) === type;
  574. };
  575. };
  576. var isFunction = isType('function');
  577. var nativeSlice = Array.prototype.slice;
  578. var each = function (xs, f) {
  579. for (var i = 0, len = xs.length; i < len; i++) {
  580. var x = xs[i];
  581. f(x, i);
  582. }
  583. };
  584. var from$1 = isFunction(Array.from) ? Array.from : function (x) {
  585. return nativeSlice.call(x);
  586. };
  587. var value = function () {
  588. var subject = Cell(Option.none());
  589. var clear = function () {
  590. subject.set(Option.none());
  591. };
  592. var set = function (s) {
  593. subject.set(Option.some(s));
  594. };
  595. var on = function (f) {
  596. subject.get().each(f);
  597. };
  598. var isSet = function () {
  599. return subject.get().isSome();
  600. };
  601. return {
  602. clear: clear,
  603. set: set,
  604. isSet: isSet,
  605. on: on
  606. };
  607. };
  608. var open = function (editor, currentSearchState) {
  609. var dialogApi = value();
  610. editor.undoManager.add();
  611. var selectedText = global$1.trim(editor.selection.getContent({ format: 'text' }));
  612. function updateButtonStates(api) {
  613. var updateNext = hasNext(editor, currentSearchState) ? api.enable : api.disable;
  614. updateNext('next');
  615. var updatePrev = hasPrev(editor, currentSearchState) ? api.enable : api.disable;
  616. updatePrev('prev');
  617. }
  618. var disableAll = function (api, disable) {
  619. var buttons = [
  620. 'replace',
  621. 'replaceall',
  622. 'prev',
  623. 'next'
  624. ];
  625. var toggle = disable ? api.disable : api.enable;
  626. each(buttons, toggle);
  627. };
  628. function notFoundAlert(api) {
  629. editor.windowManager.alert('Could not find the specified string.', function () {
  630. api.focus('findtext');
  631. });
  632. }
  633. var reset = function (api) {
  634. done(editor, currentSearchState, false);
  635. disableAll(api, true);
  636. updateButtonStates(api);
  637. };
  638. var doFind = function (api) {
  639. var data = api.getData();
  640. var last = currentSearchState.get();
  641. if (!data.findtext.length) {
  642. reset(api);
  643. return;
  644. }
  645. if (last.text === data.findtext && last.matchCase === data.matchcase && last.wholeWord === data.wholewords) {
  646. next(editor, currentSearchState);
  647. } else {
  648. var count = find(editor, currentSearchState, data.findtext, data.matchcase, data.wholewords);
  649. if (count <= 0) {
  650. notFoundAlert(api);
  651. }
  652. disableAll(api, count === 0);
  653. }
  654. updateButtonStates(api);
  655. };
  656. var initialState = currentSearchState.get();
  657. var initialData = {
  658. findtext: selectedText,
  659. replacetext: '',
  660. wholewords: initialState.wholeWord,
  661. matchcase: initialState.matchCase
  662. };
  663. var spec = {
  664. title: 'Find and Replace',
  665. size: 'normal',
  666. body: {
  667. type: 'panel',
  668. items: [
  669. {
  670. type: 'bar',
  671. items: [
  672. {
  673. type: 'input',
  674. name: 'findtext',
  675. placeholder: 'Find',
  676. maximized: true,
  677. inputMode: 'search'
  678. },
  679. {
  680. type: 'button',
  681. name: 'prev',
  682. text: 'Previous',
  683. icon: 'action-prev',
  684. disabled: true,
  685. borderless: true
  686. },
  687. {
  688. type: 'button',
  689. name: 'next',
  690. text: 'Next',
  691. icon: 'action-next',
  692. disabled: true,
  693. borderless: true
  694. }
  695. ]
  696. },
  697. {
  698. type: 'input',
  699. name: 'replacetext',
  700. placeholder: 'Replace with',
  701. inputMode: 'search'
  702. }
  703. ]
  704. },
  705. buttons: [
  706. {
  707. type: 'menu',
  708. name: 'options',
  709. icon: 'preferences',
  710. tooltip: 'Preferences',
  711. align: 'start',
  712. items: [
  713. {
  714. type: 'togglemenuitem',
  715. name: 'matchcase',
  716. text: 'Match case'
  717. },
  718. {
  719. type: 'togglemenuitem',
  720. name: 'wholewords',
  721. text: 'Find whole words only'
  722. }
  723. ]
  724. },
  725. {
  726. type: 'custom',
  727. name: 'find',
  728. text: 'Find',
  729. primary: true
  730. },
  731. {
  732. type: 'custom',
  733. name: 'replace',
  734. text: 'Replace',
  735. disabled: true
  736. },
  737. {
  738. type: 'custom',
  739. name: 'replaceall',
  740. text: 'Replace All',
  741. disabled: true
  742. }
  743. ],
  744. initialData: initialData,
  745. onChange: function (api, details) {
  746. if (details.name === 'findtext' && currentSearchState.get().count > 0) {
  747. reset(api);
  748. }
  749. },
  750. onAction: function (api, details) {
  751. var data = api.getData();
  752. switch (details.name) {
  753. case 'find':
  754. doFind(api);
  755. break;
  756. case 'replace':
  757. if (!replace(editor, currentSearchState, data.replacetext)) {
  758. reset(api);
  759. } else {
  760. updateButtonStates(api);
  761. }
  762. break;
  763. case 'replaceall':
  764. replace(editor, currentSearchState, data.replacetext, true, true);
  765. reset(api);
  766. break;
  767. case 'prev':
  768. prev(editor, currentSearchState);
  769. updateButtonStates(api);
  770. break;
  771. case 'next':
  772. next(editor, currentSearchState);
  773. updateButtonStates(api);
  774. break;
  775. default:
  776. break;
  777. }
  778. },
  779. onSubmit: doFind,
  780. onClose: function () {
  781. editor.focus();
  782. done(editor, currentSearchState);
  783. editor.undoManager.add();
  784. }
  785. };
  786. dialogApi.set(editor.windowManager.open(spec, { inline: 'toolbar' }));
  787. };
  788. var Dialog = { open: open };
  789. var register = function (editor, currentSearchState) {
  790. editor.addCommand('SearchReplace', function () {
  791. Dialog.open(editor, currentSearchState);
  792. });
  793. };
  794. var Commands = { register: register };
  795. var showDialog = function (editor, currentSearchState) {
  796. return function () {
  797. Dialog.open(editor, currentSearchState);
  798. };
  799. };
  800. var register$1 = function (editor, currentSearchState) {
  801. editor.ui.registry.addMenuItem('searchreplace', {
  802. text: 'Find and replace...',
  803. shortcut: 'Meta+F',
  804. onAction: showDialog(editor, currentSearchState),
  805. icon: 'search'
  806. });
  807. editor.ui.registry.addButton('searchreplace', {
  808. tooltip: 'Find and replace',
  809. onAction: showDialog(editor, currentSearchState),
  810. icon: 'search'
  811. });
  812. editor.shortcuts.add('Meta+F', '', showDialog(editor, currentSearchState));
  813. };
  814. var Buttons = { register: register$1 };
  815. function Plugin () {
  816. global.add('searchreplace', function (editor) {
  817. var currentSearchState = Cell({
  818. index: -1,
  819. count: 0,
  820. text: '',
  821. matchCase: false,
  822. wholeWord: false
  823. });
  824. Commands.register(editor, currentSearchState);
  825. Buttons.register(editor, currentSearchState);
  826. return Api.get(editor, currentSearchState);
  827. });
  828. }
  829. Plugin();
  830. }());