find.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. /*
  2. Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  3. For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. (function() {
  6. function C(c) {
  7. return (
  8. c.type == CKEDITOR.NODE_TEXT &&
  9. 0 < c.getLength() &&
  10. (!m || !c.isReadOnly())
  11. );
  12. }
  13. function v(c) {
  14. return !(
  15. c.type == CKEDITOR.NODE_ELEMENT &&
  16. c.isBlockBoundary(
  17. CKEDITOR.tools.extend(
  18. {},
  19. CKEDITOR.dtd.$empty,
  20. CKEDITOR.dtd.$nonEditable
  21. )
  22. )
  23. );
  24. }
  25. var m,
  26. w = function() {
  27. return {
  28. textNode: this.textNode,
  29. offset: this.offset,
  30. character: this.textNode
  31. ? this.textNode.getText().charAt(this.offset)
  32. : null,
  33. hitMatchBoundary: this._.matchBoundary
  34. };
  35. },
  36. x = ["find", "replace"],
  37. q = [
  38. ["txtFindFind", "txtFindReplace"],
  39. ["txtFindCaseChk", "txtReplaceCaseChk"],
  40. ["txtFindWordChk", "txtReplaceWordChk"],
  41. ["txtFindCyclic", "txtReplaceCyclic"]
  42. ];
  43. CKEDITOR.dialog.add("find", function(c) {
  44. function n(a, b) {
  45. var d = this,
  46. c = new CKEDITOR.dom.walker(a);
  47. c.guard = b
  48. ? v
  49. : function(a) {
  50. !v(a) && (d._.matchBoundary = !0);
  51. };
  52. c.evaluator = C;
  53. c.breakOnFalse = 1;
  54. a.startContainer.type == CKEDITOR.NODE_TEXT &&
  55. ((this.textNode = a.startContainer), (this.offset = a.startOffset - 1));
  56. this._ = { matchWord: b, walker: c, matchBoundary: !1 };
  57. }
  58. function y(a, b) {
  59. var d = c.createRange();
  60. d.setStart(a.textNode, b ? a.offset : a.offset + 1);
  61. d.setEndAt(c.editable(), CKEDITOR.POSITION_BEFORE_END);
  62. return d;
  63. }
  64. function r(a) {
  65. var b = c.getSelection().getRanges()[0],
  66. d = c.editable();
  67. b && !a
  68. ? ((a = b.clone()), a.collapse(!0))
  69. : ((a = c.createRange()),
  70. a.setStartAt(d, CKEDITOR.POSITION_AFTER_START));
  71. a.setEndAt(d, CKEDITOR.POSITION_BEFORE_END);
  72. return a;
  73. }
  74. var z = new CKEDITOR.style(
  75. CKEDITOR.tools.extend(
  76. {
  77. attributes: { "data-cke-highlight": 1 },
  78. fullMatch: 1,
  79. ignoreReadonly: 1,
  80. childRule: function() {
  81. return 0;
  82. }
  83. },
  84. c.config.find_highlight,
  85. !0
  86. )
  87. );
  88. n.prototype = {
  89. next: function() {
  90. return this.move();
  91. },
  92. back: function() {
  93. return this.move(!0);
  94. },
  95. move: function(a) {
  96. var b = this.textNode;
  97. if (null === b) return w.call(this);
  98. this._.matchBoundary = !1;
  99. if (b && a && 0 < this.offset) this.offset--;
  100. else if (b && this.offset < b.getLength() - 1) this.offset++;
  101. else {
  102. for (
  103. b = null;
  104. !b &&
  105. !((b = this._.walker[a ? "previous" : "next"].call(this._.walker)),
  106. (this._.matchWord && !b) || this._.walker._.end);
  107. );
  108. this.offset = (this.textNode = b) ? (a ? b.getLength() - 1 : 0) : 0;
  109. }
  110. return w.call(this);
  111. }
  112. };
  113. var t = function(a, b) {
  114. this._ = {
  115. walker: a,
  116. cursors: [],
  117. rangeLength: b,
  118. highlightRange: null,
  119. isMatched: 0
  120. };
  121. };
  122. t.prototype = {
  123. toDomRange: function() {
  124. var a = c.createRange(),
  125. b = this._.cursors;
  126. if (1 > b.length) {
  127. var d = this._.walker.textNode;
  128. if (d) a.setStartAfter(d);
  129. else return null;
  130. } else
  131. (d = b[0]),
  132. (b = b[b.length - 1]),
  133. a.setStart(d.textNode, d.offset),
  134. a.setEnd(b.textNode, b.offset + 1);
  135. return a;
  136. },
  137. updateFromDomRange: function(a) {
  138. var b = new n(a);
  139. this._.cursors = [];
  140. do (a = b.next()), a.character && this._.cursors.push(a);
  141. while (a.character);
  142. this._.rangeLength = this._.cursors.length;
  143. },
  144. setMatched: function() {
  145. this._.isMatched = !0;
  146. },
  147. clearMatched: function() {
  148. this._.isMatched = !1;
  149. },
  150. isMatched: function() {
  151. return this._.isMatched;
  152. },
  153. highlight: function() {
  154. if (!(1 > this._.cursors.length)) {
  155. this._.highlightRange && this.removeHighlight();
  156. var a = this.toDomRange(),
  157. b = a.createBookmark();
  158. z.applyToRange(a, c);
  159. a.moveToBookmark(b);
  160. this._.highlightRange = a;
  161. b = a.startContainer;
  162. b.type != CKEDITOR.NODE_ELEMENT && (b = b.getParent());
  163. b.scrollIntoView();
  164. this.updateFromDomRange(a);
  165. }
  166. },
  167. removeHighlight: function() {
  168. if (this._.highlightRange) {
  169. var a = this._.highlightRange.createBookmark();
  170. z.removeFromRange(this._.highlightRange, c);
  171. this._.highlightRange.moveToBookmark(a);
  172. this.updateFromDomRange(this._.highlightRange);
  173. this._.highlightRange = null;
  174. }
  175. },
  176. isReadOnly: function() {
  177. return this._.highlightRange
  178. ? this._.highlightRange.startContainer.isReadOnly()
  179. : 0;
  180. },
  181. moveBack: function() {
  182. var a = this._.walker.back(),
  183. b = this._.cursors;
  184. a.hitMatchBoundary && (this._.cursors = b = []);
  185. b.unshift(a);
  186. b.length > this._.rangeLength && b.pop();
  187. return a;
  188. },
  189. moveNext: function() {
  190. var a = this._.walker.next(),
  191. b = this._.cursors;
  192. a.hitMatchBoundary && (this._.cursors = b = []);
  193. b.push(a);
  194. b.length > this._.rangeLength && b.shift();
  195. return a;
  196. },
  197. getEndCharacter: function() {
  198. var a = this._.cursors;
  199. return 1 > a.length ? null : a[a.length - 1].character;
  200. },
  201. getNextCharacterRange: function(a) {
  202. var b, d;
  203. d = this._.cursors;
  204. d = (b = d[d.length - 1]) && b.textNode ? new n(y(b)) : this._.walker;
  205. return new t(d, a);
  206. },
  207. getCursors: function() {
  208. return this._.cursors;
  209. }
  210. };
  211. var A = function(a, b) {
  212. var d = [-1];
  213. b && (a = a.toLowerCase());
  214. for (var c = 0; c < a.length; c++)
  215. for (
  216. d.push(d[c] + 1);
  217. 0 < d[c + 1] && a.charAt(c) != a.charAt(d[c + 1] - 1);
  218. )
  219. d[c + 1] = d[d[c + 1] - 1] + 1;
  220. this._ = { overlap: d, state: 0, ignoreCase: !!b, pattern: a };
  221. };
  222. A.prototype = {
  223. feedCharacter: function(a) {
  224. for (this._.ignoreCase && (a = a.toLowerCase()); ; ) {
  225. if (a == this._.pattern.charAt(this._.state))
  226. return (
  227. this._.state++,
  228. this._.state == this._.pattern.length
  229. ? ((this._.state = 0), 2)
  230. : 1
  231. );
  232. if (this._.state) this._.state = this._.overlap[this._.state];
  233. else return 0;
  234. }
  235. },
  236. reset: function() {
  237. this._.state = 0;
  238. }
  239. };
  240. var E = /[.,"'?!;: \u0085\u00a0\u1680\u280e\u2028\u2029\u202f\u205f\u3000]/,
  241. B = function(a) {
  242. if (!a) return !0;
  243. var b = a.charCodeAt(0);
  244. return (9 <= b && 13 >= b) || (8192 <= b && 8202 >= b) || E.test(a);
  245. },
  246. f = {
  247. searchRange: null,
  248. matchRange: null,
  249. find: function(a, b, d, e, D, u) {
  250. this.matchRange
  251. ? (this.matchRange.removeHighlight(),
  252. (this.matchRange = this.matchRange.getNextCharacterRange(
  253. a.length
  254. )))
  255. : (this.matchRange = new t(new n(this.searchRange), a.length));
  256. for (var h = new A(a, !b), k = 0, l = "%"; null !== l; ) {
  257. for (
  258. this.matchRange.moveNext();
  259. (l = this.matchRange.getEndCharacter());
  260. ) {
  261. k = h.feedCharacter(l);
  262. if (2 == k) break;
  263. this.matchRange.moveNext().hitMatchBoundary && h.reset();
  264. }
  265. if (2 == k) {
  266. if (d) {
  267. var g = this.matchRange.getCursors(),
  268. p = g[g.length - 1],
  269. g = g[0],
  270. m = c.createRange();
  271. m.setStartAt(c.editable(), CKEDITOR.POSITION_AFTER_START);
  272. m.setEnd(g.textNode, g.offset);
  273. g = m;
  274. p = y(p);
  275. g.trim();
  276. p.trim();
  277. g = new n(g, !0);
  278. p = new n(p, !0);
  279. if (!B(g.back().character) || !B(p.next().character)) continue;
  280. }
  281. this.matchRange.setMatched();
  282. !1 !== D && this.matchRange.highlight();
  283. return !0;
  284. }
  285. }
  286. this.matchRange.clearMatched();
  287. this.matchRange.removeHighlight();
  288. return e && !u
  289. ? ((this.searchRange = r(1)),
  290. (this.matchRange = null),
  291. f.find.apply(
  292. this,
  293. Array.prototype.slice.call(arguments).concat([!0])
  294. ))
  295. : !1;
  296. },
  297. replaceCounter: 0,
  298. replace: function(a, b, d, e, f, u, h) {
  299. m = 1;
  300. a = 0;
  301. a = this.hasMatchOptionsChanged(b, e, f);
  302. if (
  303. !this.matchRange ||
  304. !this.matchRange.isMatched() ||
  305. this.matchRange._.isReplaced ||
  306. this.matchRange.isReadOnly() ||
  307. a
  308. )
  309. a &&
  310. this.matchRange &&
  311. (this.matchRange.clearMatched(),
  312. this.matchRange.removeHighlight(),
  313. (this.matchRange = null)),
  314. (a = this.find(b, e, f, u, !h));
  315. else {
  316. this.matchRange.removeHighlight();
  317. b = this.matchRange.toDomRange();
  318. d = c.document.createText(d);
  319. if (!h) {
  320. var k = c.getSelection();
  321. k.selectRanges([b]);
  322. c.fire("saveSnapshot");
  323. }
  324. b.deleteContents();
  325. b.insertNode(d);
  326. h || (k.selectRanges([b]), c.fire("saveSnapshot"));
  327. this.matchRange.updateFromDomRange(b);
  328. h || this.matchRange.highlight();
  329. this.matchRange._.isReplaced = !0;
  330. this.replaceCounter++;
  331. a = 1;
  332. }
  333. m = 0;
  334. return a;
  335. },
  336. matchOptions: null,
  337. hasMatchOptionsChanged: function(a, b, d) {
  338. a = [a, b, d].join(".");
  339. b = this.matchOptions && this.matchOptions != a;
  340. this.matchOptions = a;
  341. return b;
  342. }
  343. },
  344. e = c.lang.find;
  345. return {
  346. title: e.title,
  347. resizable: CKEDITOR.DIALOG_RESIZE_NONE,
  348. minWidth: 350,
  349. minHeight: 170,
  350. buttons: [
  351. CKEDITOR.dialog.cancelButton(c, { label: c.lang.common.close })
  352. ],
  353. contents: [
  354. {
  355. id: "find",
  356. label: e.find,
  357. title: e.find,
  358. accessKey: "",
  359. elements: [
  360. {
  361. type: "hbox",
  362. widths: ["230px", "90px"],
  363. children: [
  364. {
  365. type: "text",
  366. id: "txtFindFind",
  367. label: e.findWhat,
  368. isChanged: !1,
  369. labelLayout: "horizontal",
  370. accessKey: "F"
  371. },
  372. {
  373. type: "button",
  374. id: "btnFind",
  375. align: "left",
  376. style: "width:100%",
  377. label: e.find,
  378. onClick: function() {
  379. var a = this.getDialog();
  380. f.find(
  381. a.getValueOf("find", "txtFindFind"),
  382. a.getValueOf("find", "txtFindCaseChk"),
  383. a.getValueOf("find", "txtFindWordChk"),
  384. a.getValueOf("find", "txtFindCyclic")
  385. ) || alert(e.notFoundMsg);
  386. }
  387. }
  388. ]
  389. },
  390. {
  391. type: "fieldset",
  392. className: "cke_dialog_find_fieldset",
  393. label: CKEDITOR.tools.htmlEncode(e.findOptions),
  394. style: "margin-top:29px",
  395. children: [
  396. {
  397. type: "vbox",
  398. padding: 0,
  399. children: [
  400. {
  401. type: "checkbox",
  402. id: "txtFindCaseChk",
  403. isChanged: !1,
  404. label: e.matchCase
  405. },
  406. {
  407. type: "checkbox",
  408. id: "txtFindWordChk",
  409. isChanged: !1,
  410. label: e.matchWord
  411. },
  412. {
  413. type: "checkbox",
  414. id: "txtFindCyclic",
  415. isChanged: !1,
  416. default: !0,
  417. label: e.matchCyclic
  418. }
  419. ]
  420. }
  421. ]
  422. }
  423. ]
  424. },
  425. {
  426. id: "replace",
  427. label: e.replace,
  428. accessKey: "M",
  429. elements: [
  430. {
  431. type: "hbox",
  432. widths: ["230px", "90px"],
  433. children: [
  434. {
  435. type: "text",
  436. id: "txtFindReplace",
  437. label: e.findWhat,
  438. isChanged: !1,
  439. labelLayout: "horizontal",
  440. accessKey: "F"
  441. },
  442. {
  443. type: "button",
  444. id: "btnFindReplace",
  445. align: "left",
  446. style: "width:100%",
  447. label: e.replace,
  448. onClick: function() {
  449. var a = this.getDialog();
  450. f.replace(
  451. a,
  452. a.getValueOf("replace", "txtFindReplace"),
  453. a.getValueOf("replace", "txtReplace"),
  454. a.getValueOf("replace", "txtReplaceCaseChk"),
  455. a.getValueOf("replace", "txtReplaceWordChk"),
  456. a.getValueOf("replace", "txtReplaceCyclic")
  457. ) || alert(e.notFoundMsg);
  458. }
  459. }
  460. ]
  461. },
  462. {
  463. type: "hbox",
  464. widths: ["230px", "90px"],
  465. children: [
  466. {
  467. type: "text",
  468. id: "txtReplace",
  469. label: e.replaceWith,
  470. isChanged: !1,
  471. labelLayout: "horizontal",
  472. accessKey: "R"
  473. },
  474. {
  475. type: "button",
  476. id: "btnReplaceAll",
  477. align: "left",
  478. style: "width:100%",
  479. label: e.replaceAll,
  480. isChanged: !1,
  481. onClick: function() {
  482. var a = this.getDialog();
  483. f.replaceCounter = 0;
  484. f.searchRange = r(1);
  485. f.matchRange &&
  486. (f.matchRange.removeHighlight(), (f.matchRange = null));
  487. for (
  488. c.fire("saveSnapshot");
  489. f.replace(
  490. a,
  491. a.getValueOf("replace", "txtFindReplace"),
  492. a.getValueOf("replace", "txtReplace"),
  493. a.getValueOf("replace", "txtReplaceCaseChk"),
  494. a.getValueOf("replace", "txtReplaceWordChk"),
  495. !1,
  496. !0
  497. );
  498. );
  499. f.replaceCounter
  500. ? (alert(
  501. e.replaceSuccessMsg.replace(/%1/, f.replaceCounter)
  502. ),
  503. c.fire("saveSnapshot"))
  504. : alert(e.notFoundMsg);
  505. }
  506. }
  507. ]
  508. },
  509. {
  510. type: "fieldset",
  511. label: CKEDITOR.tools.htmlEncode(e.findOptions),
  512. children: [
  513. {
  514. type: "vbox",
  515. padding: 0,
  516. children: [
  517. {
  518. type: "checkbox",
  519. id: "txtReplaceCaseChk",
  520. isChanged: !1,
  521. label: e.matchCase
  522. },
  523. {
  524. type: "checkbox",
  525. id: "txtReplaceWordChk",
  526. isChanged: !1,
  527. label: e.matchWord
  528. },
  529. {
  530. type: "checkbox",
  531. id: "txtReplaceCyclic",
  532. isChanged: !1,
  533. default: !0,
  534. label: e.matchCyclic
  535. }
  536. ]
  537. }
  538. ]
  539. }
  540. ]
  541. }
  542. ],
  543. onLoad: function() {
  544. var a = this,
  545. b,
  546. d = 0;
  547. this.on("hide", function() {
  548. d = 0;
  549. });
  550. this.on("show", function() {
  551. d = 1;
  552. });
  553. this.selectPage = CKEDITOR.tools.override(this.selectPage, function(c) {
  554. return function(e) {
  555. c.call(a, e);
  556. var f = a._.tabs[e],
  557. h;
  558. h = "find" === e ? "txtFindWordChk" : "txtReplaceWordChk";
  559. b = a.getContentElement(
  560. e,
  561. "find" === e ? "txtFindFind" : "txtFindReplace"
  562. );
  563. a.getContentElement(e, h);
  564. f.initialized ||
  565. (CKEDITOR.document.getById(b._.inputId), (f.initialized = !0));
  566. if (d) {
  567. var k;
  568. e = "find" === e ? 1 : 0;
  569. var f = 1 - e,
  570. l,
  571. g = q.length;
  572. for (l = 0; l < g; l++)
  573. (h = this.getContentElement(x[e], q[l][e])),
  574. (k = this.getContentElement(x[f], q[l][f])),
  575. k.setValue(h.getValue());
  576. }
  577. };
  578. });
  579. },
  580. onShow: function() {
  581. f.searchRange = r();
  582. var a = this._.currentTabId,
  583. b = this.getParentEditor()
  584. .getSelection()
  585. .getSelectedText(),
  586. c = this.getContentElement(
  587. a,
  588. "find" == a ? "txtFindFind" : "txtFindReplace"
  589. );
  590. c.setValue(b);
  591. c.select();
  592. this[
  593. ("find" == a && this._.editor.readOnly ? "hide" : "show") + "Page"
  594. ]("replace");
  595. },
  596. onHide: function() {
  597. var a;
  598. f.matchRange &&
  599. f.matchRange.isMatched() &&
  600. (f.matchRange.removeHighlight(),
  601. (a = f.matchRange.toDomRange()) && c.getSelection().selectRanges([a]),
  602. c.focus());
  603. delete f.matchRange;
  604. },
  605. onFocus: function() {
  606. return "replace" == this._.currentTabId
  607. ? this.getContentElement("replace", "txtFindReplace")
  608. : this.getContentElement("find", "txtFindFind");
  609. }
  610. };
  611. });
  612. })();