Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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