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
    var global$5 = tinymce.util.Tools.resolve('tinymce.PluginManager');
9
 
10
    const hasProto = (v, constructor, predicate) => {
11
      var _a;
12
      if (predicate(v, constructor.prototype)) {
13
        return true;
14
      } else {
15
        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
16
      }
17
    };
18
    const typeOf = x => {
19
      const t = typeof x;
20
      if (x === null) {
21
        return 'null';
22
      } else if (t === 'object' && Array.isArray(x)) {
23
        return 'array';
24
      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
25
        return 'string';
26
      } else {
27
        return t;
28
      }
29
    };
30
    const isType = type => value => typeOf(value) === type;
31
    const isSimpleType = type => value => typeof value === type;
32
    const eq = t => a => t === a;
33
    const isString = isType('string');
34
    const isObject = isType('object');
35
    const isArray = isType('array');
36
    const isNull = eq(null);
37
    const isBoolean = isSimpleType('boolean');
38
    const isNullable = a => a === null || a === undefined;
39
    const isNonNullable = a => !isNullable(a);
40
    const isFunction = isSimpleType('function');
41
    const isArrayOf = (value, pred) => {
42
      if (isArray(value)) {
43
        for (let i = 0, len = value.length; i < len; ++i) {
44
          if (!pred(value[i])) {
45
            return false;
46
          }
47
        }
48
        return true;
49
      }
50
      return false;
51
    };
52
 
53
    const noop = () => {
54
    };
55
    const constant = value => {
56
      return () => {
57
        return value;
58
      };
59
    };
60
    const tripleEquals = (a, b) => {
61
      return a === b;
62
    };
63
 
64
    class Optional {
65
      constructor(tag, value) {
66
        this.tag = tag;
67
        this.value = value;
68
      }
69
      static some(value) {
70
        return new Optional(true, value);
71
      }
72
      static none() {
73
        return Optional.singletonNone;
74
      }
75
      fold(onNone, onSome) {
76
        if (this.tag) {
77
          return onSome(this.value);
78
        } else {
79
          return onNone();
80
        }
81
      }
82
      isSome() {
83
        return this.tag;
84
      }
85
      isNone() {
86
        return !this.tag;
87
      }
88
      map(mapper) {
89
        if (this.tag) {
90
          return Optional.some(mapper(this.value));
91
        } else {
92
          return Optional.none();
93
        }
94
      }
95
      bind(binder) {
96
        if (this.tag) {
97
          return binder(this.value);
98
        } else {
99
          return Optional.none();
100
        }
101
      }
102
      exists(predicate) {
103
        return this.tag && predicate(this.value);
104
      }
105
      forall(predicate) {
106
        return !this.tag || predicate(this.value);
107
      }
108
      filter(predicate) {
109
        if (!this.tag || predicate(this.value)) {
110
          return this;
111
        } else {
112
          return Optional.none();
113
        }
114
      }
115
      getOr(replacement) {
116
        return this.tag ? this.value : replacement;
117
      }
118
      or(replacement) {
119
        return this.tag ? this : replacement;
120
      }
121
      getOrThunk(thunk) {
122
        return this.tag ? this.value : thunk();
123
      }
124
      orThunk(thunk) {
125
        return this.tag ? this : thunk();
126
      }
127
      getOrDie(message) {
128
        if (!this.tag) {
129
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
130
        } else {
131
          return this.value;
132
        }
133
      }
134
      static from(value) {
135
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
136
      }
137
      getOrNull() {
138
        return this.tag ? this.value : null;
139
      }
140
      getOrUndefined() {
141
        return this.value;
142
      }
143
      each(worker) {
144
        if (this.tag) {
145
          worker(this.value);
146
        }
147
      }
148
      toArray() {
149
        return this.tag ? [this.value] : [];
150
      }
151
      toString() {
152
        return this.tag ? `some(${ this.value })` : 'none()';
153
      }
154
    }
155
    Optional.singletonNone = new Optional(false);
156
 
157
    const nativeIndexOf = Array.prototype.indexOf;
158
    const nativePush = Array.prototype.push;
159
    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
160
    const contains = (xs, x) => rawIndexOf(xs, x) > -1;
161
    const map = (xs, f) => {
162
      const len = xs.length;
163
      const r = new Array(len);
164
      for (let i = 0; i < len; i++) {
165
        const x = xs[i];
166
        r[i] = f(x, i);
167
      }
168
      return r;
169
    };
170
    const each$1 = (xs, f) => {
171
      for (let i = 0, len = xs.length; i < len; i++) {
172
        const x = xs[i];
173
        f(x, i);
174
      }
175
    };
176
    const foldl = (xs, f, acc) => {
177
      each$1(xs, (x, i) => {
178
        acc = f(acc, x, i);
179
      });
180
      return acc;
181
    };
182
    const flatten = xs => {
183
      const r = [];
184
      for (let i = 0, len = xs.length; i < len; ++i) {
185
        if (!isArray(xs[i])) {
186
          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
187
        }
188
        nativePush.apply(r, xs[i]);
189
      }
190
      return r;
191
    };
192
    const bind = (xs, f) => flatten(map(xs, f));
193
    const findMap = (arr, f) => {
194
      for (let i = 0; i < arr.length; i++) {
195
        const r = f(arr[i], i);
196
        if (r.isSome()) {
197
          return r;
198
        }
199
      }
200
      return Optional.none();
201
    };
202
 
203
    const is = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));
204
    const cat = arr => {
205
      const r = [];
206
      const push = x => {
207
        r.push(x);
208
      };
209
      for (let i = 0; i < arr.length; i++) {
210
        arr[i].each(push);
211
      }
212
      return r;
213
    };
214
    const someIf = (b, a) => b ? Optional.some(a) : Optional.none();
215
 
216
    const option = name => editor => editor.options.get(name);
217
    const register$1 = editor => {
218
      const registerOption = editor.options.register;
219
      registerOption('link_assume_external_targets', {
220
        processor: value => {
221
          const valid = isString(value) || isBoolean(value);
222
          if (valid) {
223
            if (value === true) {
224
              return {
225
                value: 1,
226
                valid
227
              };
228
            } else if (value === 'http' || value === 'https') {
229
              return {
230
                value,
231
                valid
232
              };
233
            } else {
234
              return {
235
                value: 0,
236
                valid
237
              };
238
            }
239
          } else {
240
            return {
241
              valid: false,
242
              message: 'Must be a string or a boolean.'
243
            };
244
          }
245
        },
246
        default: false
247
      });
248
      registerOption('link_context_toolbar', {
249
        processor: 'boolean',
250
        default: false
251
      });
252
      registerOption('link_list', { processor: value => isString(value) || isFunction(value) || isArrayOf(value, isObject) });
253
      registerOption('link_default_target', { processor: 'string' });
254
      registerOption('link_default_protocol', {
255
        processor: 'string',
256
        default: 'https'
257
      });
258
      registerOption('link_target_list', {
259
        processor: value => isBoolean(value) || isArrayOf(value, isObject),
260
        default: true
261
      });
262
      registerOption('link_rel_list', {
263
        processor: 'object[]',
264
        default: []
265
      });
266
      registerOption('link_class_list', {
267
        processor: 'object[]',
268
        default: []
269
      });
270
      registerOption('link_title', {
271
        processor: 'boolean',
272
        default: true
273
      });
274
      registerOption('allow_unsafe_link_target', {
275
        processor: 'boolean',
276
        default: false
277
      });
278
      registerOption('link_quicklink', {
279
        processor: 'boolean',
280
        default: false
281
      });
282
    };
283
    const assumeExternalTargets = option('link_assume_external_targets');
284
    const hasContextToolbar = option('link_context_toolbar');
285
    const getLinkList = option('link_list');
286
    const getDefaultLinkTarget = option('link_default_target');
287
    const getDefaultLinkProtocol = option('link_default_protocol');
288
    const getTargetList = option('link_target_list');
289
    const getRelList = option('link_rel_list');
290
    const getLinkClassList = option('link_class_list');
291
    const shouldShowLinkTitle = option('link_title');
292
    const allowUnsafeLinkTarget = option('allow_unsafe_link_target');
293
    const useQuickLink = option('link_quicklink');
294
 
295
    var global$4 = tinymce.util.Tools.resolve('tinymce.util.Tools');
296
 
297
    const getValue = item => isString(item.value) ? item.value : '';
298
    const getText = item => {
299
      if (isString(item.text)) {
300
        return item.text;
301
      } else if (isString(item.title)) {
302
        return item.title;
303
      } else {
304
        return '';
305
      }
306
    };
307
    const sanitizeList = (list, extractValue) => {
308
      const out = [];
309
      global$4.each(list, item => {
310
        const text = getText(item);
311
        if (item.menu !== undefined) {
312
          const items = sanitizeList(item.menu, extractValue);
313
          out.push({
314
            text,
315
            items
316
          });
317
        } else {
318
          const value = extractValue(item);
319
          out.push({
320
            text,
321
            value
322
          });
323
        }
324
      });
325
      return out;
326
    };
327
    const sanitizeWith = (extracter = getValue) => list => Optional.from(list).map(list => sanitizeList(list, extracter));
328
    const sanitize = list => sanitizeWith(getValue)(list);
329
    const createUi = (name, label) => items => ({
330
      name,
331
      type: 'listbox',
332
      label,
333
      items
334
    });
335
    const ListOptions = {
336
      sanitize,
337
      sanitizeWith,
338
      createUi,
339
      getValue
340
    };
341
 
342
    const keys = Object.keys;
343
    const hasOwnProperty = Object.hasOwnProperty;
344
    const each = (obj, f) => {
345
      const props = keys(obj);
346
      for (let k = 0, len = props.length; k < len; k++) {
347
        const i = props[k];
348
        const x = obj[i];
349
        f(x, i);
350
      }
351
    };
352
    const objAcc = r => (x, i) => {
353
      r[i] = x;
354
    };
355
    const internalFilter = (obj, pred, onTrue, onFalse) => {
356
      each(obj, (x, i) => {
357
        (pred(x, i) ? onTrue : onFalse)(x, i);
358
      });
359
    };
360
    const filter = (obj, pred) => {
361
      const t = {};
362
      internalFilter(obj, pred, objAcc(t), noop);
363
      return t;
364
    };
365
    const has = (obj, key) => hasOwnProperty.call(obj, key);
366
    const hasNonNullableKey = (obj, key) => has(obj, key) && obj[key] !== undefined && obj[key] !== null;
367
 
368
    var global$3 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');
369
 
370
    var global$2 = tinymce.util.Tools.resolve('tinymce.util.URI');
371
 
372
    const isAnchor = elm => isNonNullable(elm) && elm.nodeName.toLowerCase() === 'a';
373
    const isLink = elm => isAnchor(elm) && !!getHref(elm);
374
    const collectNodesInRange = (rng, predicate) => {
375
      if (rng.collapsed) {
376
        return [];
377
      } else {
378
        const contents = rng.cloneContents();
379
        const firstChild = contents.firstChild;
380
        const walker = new global$3(firstChild, contents);
381
        const elements = [];
382
        let current = firstChild;
383
        do {
384
          if (predicate(current)) {
385
            elements.push(current);
386
          }
387
        } while (current = walker.next());
388
        return elements;
389
      }
390
    };
391
    const hasProtocol = url => /^\w+:/i.test(url);
392
    const getHref = elm => {
393
      var _a, _b;
394
      return (_b = (_a = elm.getAttribute('data-mce-href')) !== null && _a !== void 0 ? _a : elm.getAttribute('href')) !== null && _b !== void 0 ? _b : '';
395
    };
396
    const applyRelTargetRules = (rel, isUnsafe) => {
397
      const rules = ['noopener'];
398
      const rels = rel ? rel.split(/\s+/) : [];
399
      const toString = rels => global$4.trim(rels.sort().join(' '));
400
      const addTargetRules = rels => {
401
        rels = removeTargetRules(rels);
402
        return rels.length > 0 ? rels.concat(rules) : rules;
403
      };
404
      const removeTargetRules = rels => rels.filter(val => global$4.inArray(rules, val) === -1);
405
      const newRels = isUnsafe ? addTargetRules(rels) : removeTargetRules(rels);
406
      return newRels.length > 0 ? toString(newRels) : '';
407
    };
408
    const trimCaretContainers = text => text.replace(/\uFEFF/g, '');
409
    const getAnchorElement = (editor, selectedElm) => {
410
      selectedElm = selectedElm || getLinksInSelection(editor.selection.getRng())[0] || editor.selection.getNode();
411
      if (isImageFigure(selectedElm)) {
412
        return Optional.from(editor.dom.select('a[href]', selectedElm)[0]);
413
      } else {
414
        return Optional.from(editor.dom.getParent(selectedElm, 'a[href]'));
415
      }
416
    };
417
    const isInAnchor = (editor, selectedElm) => getAnchorElement(editor, selectedElm).isSome();
418
    const getAnchorText = (selection, anchorElm) => {
419
      const text = anchorElm.fold(() => selection.getContent({ format: 'text' }), anchorElm => anchorElm.innerText || anchorElm.textContent || '');
420
      return trimCaretContainers(text);
421
    };
422
    const getLinksInSelection = rng => collectNodesInRange(rng, isLink);
423
    const getLinks$1 = elements => global$4.grep(elements, isLink);
424
    const hasLinks = elements => getLinks$1(elements).length > 0;
425
    const hasLinksInSelection = rng => getLinksInSelection(rng).length > 0;
426
    const isOnlyTextSelected = editor => {
427
      const inlineTextElements = editor.schema.getTextInlineElements();
428
      const isElement = elm => elm.nodeType === 1 && !isAnchor(elm) && !has(inlineTextElements, elm.nodeName.toLowerCase());
429
      const isInBlockAnchor = getAnchorElement(editor).exists(anchor => anchor.hasAttribute('data-mce-block'));
430
      if (isInBlockAnchor) {
431
        return false;
432
      }
433
      const rng = editor.selection.getRng();
434
      if (!rng.collapsed) {
435
        const elements = collectNodesInRange(rng, isElement);
436
        return elements.length === 0;
437
      } else {
438
        return true;
439
      }
440
    };
441
    const isImageFigure = elm => isNonNullable(elm) && elm.nodeName === 'FIGURE' && /\bimage\b/i.test(elm.className);
442
    const getLinkAttrs = data => {
443
      const attrs = [
444
        'title',
445
        'rel',
446
        'class',
447
        'target'
448
      ];
449
      return foldl(attrs, (acc, key) => {
450
        data[key].each(value => {
451
          acc[key] = value.length > 0 ? value : null;
452
        });
453
        return acc;
454
      }, { href: data.href });
455
    };
456
    const handleExternalTargets = (href, assumeExternalTargets) => {
457
      if ((assumeExternalTargets === 'http' || assumeExternalTargets === 'https') && !hasProtocol(href)) {
458
        return assumeExternalTargets + '://' + href;
459
      }
460
      return href;
461
    };
462
    const applyLinkOverrides = (editor, linkAttrs) => {
463
      const newLinkAttrs = { ...linkAttrs };
464
      if (getRelList(editor).length === 0 && !allowUnsafeLinkTarget(editor)) {
465
        const newRel = applyRelTargetRules(newLinkAttrs.rel, newLinkAttrs.target === '_blank');
466
        newLinkAttrs.rel = newRel ? newRel : null;
467
      }
468
      if (Optional.from(newLinkAttrs.target).isNone() && getTargetList(editor) === false) {
469
        newLinkAttrs.target = getDefaultLinkTarget(editor);
470
      }
471
      newLinkAttrs.href = handleExternalTargets(newLinkAttrs.href, assumeExternalTargets(editor));
472
      return newLinkAttrs;
473
    };
474
    const updateLink = (editor, anchorElm, text, linkAttrs) => {
475
      text.each(text => {
476
        if (has(anchorElm, 'innerText')) {
477
          anchorElm.innerText = text;
478
        } else {
479
          anchorElm.textContent = text;
480
        }
481
      });
482
      editor.dom.setAttribs(anchorElm, linkAttrs);
483
      editor.selection.select(anchorElm);
484
    };
485
    const createLink = (editor, selectedElm, text, linkAttrs) => {
486
      const dom = editor.dom;
487
      if (isImageFigure(selectedElm)) {
488
        linkImageFigure(dom, selectedElm, linkAttrs);
489
      } else {
490
        text.fold(() => {
491
          editor.execCommand('mceInsertLink', false, linkAttrs);
492
        }, text => {
493
          editor.insertContent(dom.createHTML('a', linkAttrs, dom.encode(text)));
494
        });
495
      }
496
    };
497
    const linkDomMutation = (editor, attachState, data) => {
498
      const selectedElm = editor.selection.getNode();
499
      const anchorElm = getAnchorElement(editor, selectedElm);
500
      const linkAttrs = applyLinkOverrides(editor, getLinkAttrs(data));
501
      editor.undoManager.transact(() => {
502
        if (data.href === attachState.href) {
503
          attachState.attach();
504
        }
505
        anchorElm.fold(() => {
506
          createLink(editor, selectedElm, data.text, linkAttrs);
507
        }, elm => {
508
          editor.focus();
509
          updateLink(editor, elm, data.text, linkAttrs);
510
        });
511
      });
512
    };
513
    const unlinkSelection = editor => {
514
      const dom = editor.dom, selection = editor.selection;
515
      const bookmark = selection.getBookmark();
516
      const rng = selection.getRng().cloneRange();
517
      const startAnchorElm = dom.getParent(rng.startContainer, 'a[href]', editor.getBody());
518
      const endAnchorElm = dom.getParent(rng.endContainer, 'a[href]', editor.getBody());
519
      if (startAnchorElm) {
520
        rng.setStartBefore(startAnchorElm);
521
      }
522
      if (endAnchorElm) {
523
        rng.setEndAfter(endAnchorElm);
524
      }
525
      selection.setRng(rng);
526
      editor.execCommand('unlink');
527
      selection.moveToBookmark(bookmark);
528
    };
529
    const unlinkDomMutation = editor => {
530
      editor.undoManager.transact(() => {
531
        const node = editor.selection.getNode();
532
        if (isImageFigure(node)) {
533
          unlinkImageFigure(editor, node);
534
        } else {
535
          unlinkSelection(editor);
536
        }
537
        editor.focus();
538
      });
539
    };
540
    const unwrapOptions = data => {
541
      const {
542
        class: cls,
543
        href,
544
        rel,
545
        target,
546
        text,
547
        title
548
      } = data;
549
      return filter({
550
        class: cls.getOrNull(),
551
        href,
552
        rel: rel.getOrNull(),
553
        target: target.getOrNull(),
554
        text: text.getOrNull(),
555
        title: title.getOrNull()
556
      }, (v, _k) => isNull(v) === false);
557
    };
558
    const sanitizeData = (editor, data) => {
559
      const getOption = editor.options.get;
560
      const uriOptions = {
561
        allow_html_data_urls: getOption('allow_html_data_urls'),
562
        allow_script_urls: getOption('allow_script_urls'),
563
        allow_svg_data_urls: getOption('allow_svg_data_urls')
564
      };
565
      const href = data.href;
566
      return {
567
        ...data,
568
        href: global$2.isDomSafe(href, 'a', uriOptions) ? href : ''
569
      };
570
    };
571
    const link = (editor, attachState, data) => {
572
      const sanitizedData = sanitizeData(editor, data);
573
      editor.hasPlugin('rtc', true) ? editor.execCommand('createlink', false, unwrapOptions(sanitizedData)) : linkDomMutation(editor, attachState, sanitizedData);
574
    };
575
    const unlink = editor => {
576
      editor.hasPlugin('rtc', true) ? editor.execCommand('unlink') : unlinkDomMutation(editor);
577
    };
578
    const unlinkImageFigure = (editor, fig) => {
579
      var _a;
580
      const img = editor.dom.select('img', fig)[0];
581
      if (img) {
582
        const a = editor.dom.getParents(img, 'a[href]', fig)[0];
583
        if (a) {
584
          (_a = a.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(img, a);
585
          editor.dom.remove(a);
586
        }
587
      }
588
    };
589
    const linkImageFigure = (dom, fig, attrs) => {
590
      var _a;
591
      const img = dom.select('img', fig)[0];
592
      if (img) {
593
        const a = dom.create('a', attrs);
594
        (_a = img.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(a, img);
595
        a.appendChild(img);
596
      }
597
    };
598
 
599
    const isListGroup = item => hasNonNullableKey(item, 'items');
600
    const findTextByValue = (value, catalog) => findMap(catalog, item => {
601
      if (isListGroup(item)) {
602
        return findTextByValue(value, item.items);
603
      } else {
604
        return someIf(item.value === value, item);
605
      }
606
    });
607
    const getDelta = (persistentText, fieldName, catalog, data) => {
608
      const value = data[fieldName];
609
      const hasPersistentText = persistentText.length > 0;
610
      return value !== undefined ? findTextByValue(value, catalog).map(i => ({
611
        url: {
612
          value: i.value,
613
          meta: {
614
            text: hasPersistentText ? persistentText : i.text,
615
            attach: noop
616
          }
617
        },
618
        text: hasPersistentText ? persistentText : i.text
619
      })) : Optional.none();
620
    };
621
    const findCatalog = (catalogs, fieldName) => {
622
      if (fieldName === 'link') {
623
        return catalogs.link;
624
      } else if (fieldName === 'anchor') {
625
        return catalogs.anchor;
626
      } else {
627
        return Optional.none();
628
      }
629
    };
630
    const init = (initialData, linkCatalog) => {
631
      const persistentData = {
632
        text: initialData.text,
633
        title: initialData.title
634
      };
635
      const getTitleFromUrlChange = url => {
636
        var _a;
637
        return someIf(persistentData.title.length <= 0, Optional.from((_a = url.meta) === null || _a === void 0 ? void 0 : _a.title).getOr(''));
638
      };
639
      const getTextFromUrlChange = url => {
640
        var _a;
641
        return someIf(persistentData.text.length <= 0, Optional.from((_a = url.meta) === null || _a === void 0 ? void 0 : _a.text).getOr(url.value));
642
      };
643
      const onUrlChange = data => {
644
        const text = getTextFromUrlChange(data.url);
645
        const title = getTitleFromUrlChange(data.url);
646
        if (text.isSome() || title.isSome()) {
647
          return Optional.some({
648
            ...text.map(text => ({ text })).getOr({}),
649
            ...title.map(title => ({ title })).getOr({})
650
          });
651
        } else {
652
          return Optional.none();
653
        }
654
      };
655
      const onCatalogChange = (data, change) => {
656
        const catalog = findCatalog(linkCatalog, change).getOr([]);
657
        return getDelta(persistentData.text, change, catalog, data);
658
      };
659
      const onChange = (getData, change) => {
660
        const name = change.name;
661
        if (name === 'url') {
662
          return onUrlChange(getData());
663
        } else if (contains([
664
            'anchor',
665
            'link'
666
          ], name)) {
667
          return onCatalogChange(getData(), name);
668
        } else if (name === 'text' || name === 'title') {
669
          persistentData[name] = getData()[name];
670
          return Optional.none();
671
        } else {
672
          return Optional.none();
673
        }
674
      };
675
      return { onChange };
676
    };
677
    const DialogChanges = {
678
      init,
679
      getDelta
680
    };
681
 
682
    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Delay');
683
 
684
    const delayedConfirm = (editor, message, callback) => {
685
      const rng = editor.selection.getRng();
686
      global$1.setEditorTimeout(editor, () => {
687
        editor.windowManager.confirm(message, state => {
688
          editor.selection.setRng(rng);
689
          callback(state);
690
        });
691
      });
692
    };
693
    const tryEmailTransform = data => {
694
      const url = data.href;
695
      const suggestMailTo = url.indexOf('@') > 0 && url.indexOf('/') === -1 && url.indexOf('mailto:') === -1;
696
      return suggestMailTo ? Optional.some({
697
        message: 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?',
698
        preprocess: oldData => ({
699
          ...oldData,
700
          href: 'mailto:' + url
701
        })
702
      }) : Optional.none();
703
    };
704
    const tryProtocolTransform = (assumeExternalTargets, defaultLinkProtocol) => data => {
705
      const url = data.href;
706
      const suggestProtocol = assumeExternalTargets === 1 && !hasProtocol(url) || assumeExternalTargets === 0 && /^\s*www(\.|\d\.)/i.test(url);
707
      return suggestProtocol ? Optional.some({
708
        message: `The URL you entered seems to be an external link. Do you want to add the required ${ defaultLinkProtocol }:// prefix?`,
709
        preprocess: oldData => ({
710
          ...oldData,
711
          href: defaultLinkProtocol + '://' + url
712
        })
713
      }) : Optional.none();
714
    };
715
    const preprocess = (editor, data) => findMap([
716
      tryEmailTransform,
717
      tryProtocolTransform(assumeExternalTargets(editor), getDefaultLinkProtocol(editor))
718
    ], f => f(data)).fold(() => Promise.resolve(data), transform => new Promise(callback => {
719
      delayedConfirm(editor, transform.message, state => {
720
        callback(state ? transform.preprocess(data) : data);
721
      });
722
    }));
723
    const DialogConfirms = { preprocess };
724
 
725
    const getAnchors = editor => {
726
      const anchorNodes = editor.dom.select('a:not([href])');
727
      const anchors = bind(anchorNodes, anchor => {
728
        const id = anchor.name || anchor.id;
729
        return id ? [{
730
            text: id,
731
            value: '#' + id
732
          }] : [];
733
      });
734
      return anchors.length > 0 ? Optional.some([{
735
          text: 'None',
736
          value: ''
737
        }].concat(anchors)) : Optional.none();
738
    };
739
    const AnchorListOptions = { getAnchors };
740
 
741
    const getClasses = editor => {
742
      const list = getLinkClassList(editor);
743
      if (list.length > 0) {
744
        return ListOptions.sanitize(list);
745
      }
746
      return Optional.none();
747
    };
748
    const ClassListOptions = { getClasses };
749
 
750
    const parseJson = text => {
751
      try {
752
        return Optional.some(JSON.parse(text));
753
      } catch (err) {
754
        return Optional.none();
755
      }
756
    };
757
    const getLinks = editor => {
758
      const extractor = item => editor.convertURL(item.value || item.url || '', 'href');
759
      const linkList = getLinkList(editor);
760
      return new Promise(resolve => {
761
        if (isString(linkList)) {
762
          fetch(linkList).then(res => res.ok ? res.text().then(parseJson) : Promise.reject()).then(resolve, () => resolve(Optional.none()));
763
        } else if (isFunction(linkList)) {
764
          linkList(output => resolve(Optional.some(output)));
765
        } else {
766
          resolve(Optional.from(linkList));
767
        }
768
      }).then(optItems => optItems.bind(ListOptions.sanitizeWith(extractor)).map(items => {
769
        if (items.length > 0) {
770
          const noneItem = [{
771
              text: 'None',
772
              value: ''
773
            }];
774
          return noneItem.concat(items);
775
        } else {
776
          return items;
777
        }
778
      }));
779
    };
780
    const LinkListOptions = { getLinks };
781
 
782
    const getRels = (editor, initialTarget) => {
783
      const list = getRelList(editor);
784
      if (list.length > 0) {
785
        const isTargetBlank = is(initialTarget, '_blank');
786
        const enforceSafe = allowUnsafeLinkTarget(editor) === false;
787
        const safeRelExtractor = item => applyRelTargetRules(ListOptions.getValue(item), isTargetBlank);
788
        const sanitizer = enforceSafe ? ListOptions.sanitizeWith(safeRelExtractor) : ListOptions.sanitize;
789
        return sanitizer(list);
790
      }
791
      return Optional.none();
792
    };
793
    const RelOptions = { getRels };
794
 
795
    const fallbacks = [
796
      {
797
        text: 'Current window',
798
        value: ''
799
      },
800
      {
801
        text: 'New window',
802
        value: '_blank'
803
      }
804
    ];
805
    const getTargets = editor => {
806
      const list = getTargetList(editor);
807
      if (isArray(list)) {
808
        return ListOptions.sanitize(list).orThunk(() => Optional.some(fallbacks));
809
      } else if (list === false) {
810
        return Optional.none();
811
      }
812
      return Optional.some(fallbacks);
813
    };
814
    const TargetOptions = { getTargets };
815
 
816
    const nonEmptyAttr = (dom, elem, name) => {
817
      const val = dom.getAttrib(elem, name);
818
      return val !== null && val.length > 0 ? Optional.some(val) : Optional.none();
819
    };
820
    const extractFromAnchor = (editor, anchor) => {
821
      const dom = editor.dom;
822
      const onlyText = isOnlyTextSelected(editor);
823
      const text = onlyText ? Optional.some(getAnchorText(editor.selection, anchor)) : Optional.none();
824
      const url = anchor.bind(anchorElm => Optional.from(dom.getAttrib(anchorElm, 'href')));
825
      const target = anchor.bind(anchorElm => Optional.from(dom.getAttrib(anchorElm, 'target')));
826
      const rel = anchor.bind(anchorElm => nonEmptyAttr(dom, anchorElm, 'rel'));
827
      const linkClass = anchor.bind(anchorElm => nonEmptyAttr(dom, anchorElm, 'class'));
828
      const title = anchor.bind(anchorElm => nonEmptyAttr(dom, anchorElm, 'title'));
829
      return {
830
        url,
831
        text,
832
        title,
833
        target,
834
        rel,
835
        linkClass
836
      };
837
    };
838
    const collect = (editor, linkNode) => LinkListOptions.getLinks(editor).then(links => {
839
      const anchor = extractFromAnchor(editor, linkNode);
840
      return {
841
        anchor,
842
        catalogs: {
843
          targets: TargetOptions.getTargets(editor),
844
          rels: RelOptions.getRels(editor, anchor.target),
845
          classes: ClassListOptions.getClasses(editor),
846
          anchor: AnchorListOptions.getAnchors(editor),
847
          link: links
848
        },
849
        optNode: linkNode,
850
        flags: { titleEnabled: shouldShowLinkTitle(editor) }
851
      };
852
    });
853
    const DialogInfo = { collect };
854
 
855
    const handleSubmit = (editor, info) => api => {
856
      const data = api.getData();
857
      if (!data.url.value) {
858
        unlink(editor);
859
        api.close();
860
        return;
861
      }
862
      const getChangedValue = key => Optional.from(data[key]).filter(value => !is(info.anchor[key], value));
863
      const changedData = {
864
        href: data.url.value,
865
        text: getChangedValue('text'),
866
        target: getChangedValue('target'),
867
        rel: getChangedValue('rel'),
868
        class: getChangedValue('linkClass'),
869
        title: getChangedValue('title')
870
      };
871
      const attachState = {
872
        href: data.url.value,
873
        attach: data.url.meta !== undefined && data.url.meta.attach ? data.url.meta.attach : noop
874
      };
875
      DialogConfirms.preprocess(editor, changedData).then(pData => {
876
        link(editor, attachState, pData);
877
      });
878
      api.close();
879
    };
880
    const collectData = editor => {
881
      const anchorNode = getAnchorElement(editor);
882
      return DialogInfo.collect(editor, anchorNode);
883
    };
884
    const getInitialData = (info, defaultTarget) => {
885
      const anchor = info.anchor;
886
      const url = anchor.url.getOr('');
887
      return {
888
        url: {
889
          value: url,
890
          meta: { original: { value: url } }
891
        },
892
        text: anchor.text.getOr(''),
893
        title: anchor.title.getOr(''),
894
        anchor: url,
895
        link: url,
896
        rel: anchor.rel.getOr(''),
897
        target: anchor.target.or(defaultTarget).getOr(''),
898
        linkClass: anchor.linkClass.getOr('')
899
      };
900
    };
901
    const makeDialog = (settings, onSubmit, editor) => {
902
      const urlInput = [{
903
          name: 'url',
904
          type: 'urlinput',
905
          filetype: 'file',
906
          label: 'URL',
907
          picker_text: 'Browse links'
908
        }];
909
      const displayText = settings.anchor.text.map(() => ({
910
        name: 'text',
911
        type: 'input',
912
        label: 'Text to display'
913
      })).toArray();
914
      const titleText = settings.flags.titleEnabled ? [{
915
          name: 'title',
916
          type: 'input',
917
          label: 'Title'
918
        }] : [];
919
      const defaultTarget = Optional.from(getDefaultLinkTarget(editor));
920
      const initialData = getInitialData(settings, defaultTarget);
921
      const catalogs = settings.catalogs;
922
      const dialogDelta = DialogChanges.init(initialData, catalogs);
923
      const body = {
924
        type: 'panel',
925
        items: flatten([
926
          urlInput,
927
          displayText,
928
          titleText,
929
          cat([
930
            catalogs.anchor.map(ListOptions.createUi('anchor', 'Anchors')),
931
            catalogs.rels.map(ListOptions.createUi('rel', 'Rel')),
932
            catalogs.targets.map(ListOptions.createUi('target', 'Open link in...')),
933
            catalogs.link.map(ListOptions.createUi('link', 'Link list')),
934
            catalogs.classes.map(ListOptions.createUi('linkClass', 'Class'))
935
          ])
936
        ])
937
      };
938
      return {
939
        title: 'Insert/Edit Link',
940
        size: 'normal',
941
        body,
942
        buttons: [
943
          {
944
            type: 'cancel',
945
            name: 'cancel',
946
            text: 'Cancel'
947
          },
948
          {
949
            type: 'submit',
950
            name: 'save',
951
            text: 'Save',
952
            primary: true
953
          }
954
        ],
955
        initialData,
956
        onChange: (api, {name}) => {
957
          dialogDelta.onChange(api.getData, { name }).each(newData => {
958
            api.setData(newData);
959
          });
960
        },
961
        onSubmit
962
      };
963
    };
964
    const open$1 = editor => {
965
      const data = collectData(editor);
966
      data.then(info => {
967
        const onSubmit = handleSubmit(editor, info);
968
        return makeDialog(info, onSubmit, editor);
969
      }).then(spec => {
970
        editor.windowManager.open(spec);
971
      });
972
    };
973
 
974
    const register = editor => {
975
      editor.addCommand('mceLink', (_ui, value) => {
976
        if ((value === null || value === void 0 ? void 0 : value.dialog) === true || !useQuickLink(editor)) {
977
          open$1(editor);
978
        } else {
979
          editor.dispatch('contexttoolbar-show', { toolbarKey: 'quicklink' });
980
        }
981
      });
982
    };
983
 
984
    var global = tinymce.util.Tools.resolve('tinymce.util.VK');
985
 
986
    const appendClickRemove = (link, evt) => {
987
      document.body.appendChild(link);
988
      link.dispatchEvent(evt);
989
      document.body.removeChild(link);
990
    };
991
    const open = url => {
992
      const link = document.createElement('a');
993
      link.target = '_blank';
994
      link.href = url;
995
      link.rel = 'noreferrer noopener';
996
      const evt = document.createEvent('MouseEvents');
997
      evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
998
      appendClickRemove(link, evt);
999
    };
1000
 
1001
    const getLink = (editor, elm) => editor.dom.getParent(elm, 'a[href]');
1002
    const getSelectedLink = editor => getLink(editor, editor.selection.getStart());
1003
    const hasOnlyAltModifier = e => {
1004
      return e.altKey === true && e.shiftKey === false && e.ctrlKey === false && e.metaKey === false;
1005
    };
1006
    const gotoLink = (editor, a) => {
1007
      if (a) {
1008
        const href = getHref(a);
1009
        if (/^#/.test(href)) {
1010
          const targetEl = editor.dom.select(href);
1011
          if (targetEl.length) {
1012
            editor.selection.scrollIntoView(targetEl[0], true);
1013
          }
1014
        } else {
1015
          open(a.href);
1016
        }
1017
      }
1018
    };
1019
    const openDialog = editor => () => {
1020
      editor.execCommand('mceLink', false, { dialog: true });
1021
    };
1022
    const gotoSelectedLink = editor => () => {
1023
      gotoLink(editor, getSelectedLink(editor));
1024
    };
1025
    const setupGotoLinks = editor => {
1026
      editor.on('click', e => {
1027
        const link = getLink(editor, e.target);
1028
        if (link && global.metaKeyPressed(e)) {
1029
          e.preventDefault();
1030
          gotoLink(editor, link);
1031
        }
1032
      });
1033
      editor.on('keydown', e => {
1034
        if (!e.isDefaultPrevented() && e.keyCode === 13 && hasOnlyAltModifier(e)) {
1035
          const link = getSelectedLink(editor);
1036
          if (link) {
1037
            e.preventDefault();
1038
            gotoLink(editor, link);
1039
          }
1040
        }
1041
      });
1042
    };
1043
    const toggleState = (editor, toggler) => {
1044
      editor.on('NodeChange', toggler);
1045
      return () => editor.off('NodeChange', toggler);
1046
    };
1047
    const toggleLinkState = editor => api => {
1048
      const updateState = () => {
1049
        api.setActive(!editor.mode.isReadOnly() && isInAnchor(editor, editor.selection.getNode()));
1050
        api.setEnabled(editor.selection.isEditable());
1051
      };
1052
      updateState();
1053
      return toggleState(editor, updateState);
1054
    };
1055
    const toggleLinkMenuState = editor => api => {
1056
      const updateState = () => {
1057
        api.setEnabled(editor.selection.isEditable());
1058
      };
1059
      updateState();
1060
      return toggleState(editor, updateState);
1061
    };
1062
    const hasExactlyOneLinkInSelection = editor => {
1063
      const links = editor.selection.isCollapsed() ? getLinks$1(editor.dom.getParents(editor.selection.getStart())) : getLinksInSelection(editor.selection.getRng());
1064
      return links.length === 1;
1065
    };
1066
    const toggleGotoLinkState = editor => api => {
1067
      const updateState = () => api.setEnabled(hasExactlyOneLinkInSelection(editor));
1068
      updateState();
1069
      return toggleState(editor, updateState);
1070
    };
1071
    const toggleUnlinkState = editor => api => {
1072
      const hasLinks$1 = parents => hasLinks(parents) || hasLinksInSelection(editor.selection.getRng());
1073
      const parents = editor.dom.getParents(editor.selection.getStart());
1074
      const updateEnabled = parents => {
1075
        api.setEnabled(hasLinks$1(parents) && editor.selection.isEditable());
1076
      };
1077
      updateEnabled(parents);
1078
      return toggleState(editor, e => updateEnabled(e.parents));
1079
    };
1080
 
1081
    const setup = editor => {
1082
      editor.addShortcut('Meta+K', '', () => {
1083
        editor.execCommand('mceLink');
1084
      });
1085
    };
1086
 
1087
    const setupButtons = editor => {
1088
      editor.ui.registry.addToggleButton('link', {
1089
        icon: 'link',
1090
        tooltip: 'Insert/edit link',
1091
        onAction: openDialog(editor),
1092
        onSetup: toggleLinkState(editor)
1093
      });
1094
      editor.ui.registry.addButton('openlink', {
1095
        icon: 'new-tab',
1096
        tooltip: 'Open link',
1097
        onAction: gotoSelectedLink(editor),
1098
        onSetup: toggleGotoLinkState(editor)
1099
      });
1100
      editor.ui.registry.addButton('unlink', {
1101
        icon: 'unlink',
1102
        tooltip: 'Remove link',
1103
        onAction: () => unlink(editor),
1104
        onSetup: toggleUnlinkState(editor)
1105
      });
1106
    };
1107
    const setupMenuItems = editor => {
1108
      editor.ui.registry.addMenuItem('openlink', {
1109
        text: 'Open link',
1110
        icon: 'new-tab',
1111
        onAction: gotoSelectedLink(editor),
1112
        onSetup: toggleGotoLinkState(editor)
1113
      });
1114
      editor.ui.registry.addMenuItem('link', {
1115
        icon: 'link',
1116
        text: 'Link...',
1117
        shortcut: 'Meta+K',
1118
        onSetup: toggleLinkMenuState(editor),
1119
        onAction: openDialog(editor)
1120
      });
1121
      editor.ui.registry.addMenuItem('unlink', {
1122
        icon: 'unlink',
1123
        text: 'Remove link',
1124
        onAction: () => unlink(editor),
1125
        onSetup: toggleUnlinkState(editor)
1126
      });
1127
    };
1128
    const setupContextMenu = editor => {
1129
      const inLink = 'link unlink openlink';
1130
      const noLink = 'link';
1131
      editor.ui.registry.addContextMenu('link', {
1132
        update: element => {
1133
          const isEditable = editor.dom.isEditable(element);
1134
          if (!isEditable) {
1135
            return '';
1136
          }
1137
          return hasLinks(editor.dom.getParents(element, 'a')) ? inLink : noLink;
1138
        }
1139
      });
1140
    };
1141
    const setupContextToolbars = editor => {
1142
      const collapseSelectionToEnd = editor => {
1143
        editor.selection.collapse(false);
1144
      };
1145
      const onSetupLink = buttonApi => {
1146
        const node = editor.selection.getNode();
1147
        buttonApi.setEnabled(isInAnchor(editor, node));
1148
        return noop;
1149
      };
1150
      const getLinkText = value => {
1151
        const anchor = getAnchorElement(editor);
1152
        const onlyText = isOnlyTextSelected(editor);
1153
        if (anchor.isNone() && onlyText) {
1154
          const text = getAnchorText(editor.selection, anchor);
1155
          return someIf(text.length === 0, value);
1156
        } else {
1157
          return Optional.none();
1158
        }
1159
      };
1160
      editor.ui.registry.addContextForm('quicklink', {
1161
        launch: {
1162
          type: 'contextformtogglebutton',
1163
          icon: 'link',
1164
          tooltip: 'Link',
1165
          onSetup: toggleLinkState(editor)
1166
        },
1167
        label: 'Link',
1168
        predicate: node => hasContextToolbar(editor) && isInAnchor(editor, node),
1169
        initValue: () => {
1170
          const elm = getAnchorElement(editor);
1171
          return elm.fold(constant(''), getHref);
1172
        },
1173
        commands: [
1174
          {
1175
            type: 'contextformtogglebutton',
1176
            icon: 'link',
1177
            tooltip: 'Link',
1178
            primary: true,
1179
            onSetup: buttonApi => {
1180
              const node = editor.selection.getNode();
1181
              buttonApi.setActive(isInAnchor(editor, node));
1182
              return toggleLinkState(editor)(buttonApi);
1183
            },
1184
            onAction: formApi => {
1185
              const value = formApi.getValue();
1186
              const text = getLinkText(value);
1187
              const attachState = {
1188
                href: value,
1189
                attach: noop
1190
              };
1191
              link(editor, attachState, {
1192
                href: value,
1193
                text,
1194
                title: Optional.none(),
1195
                rel: Optional.none(),
1196
                target: Optional.none(),
1197
                class: Optional.none()
1198
              });
1199
              collapseSelectionToEnd(editor);
1200
              formApi.hide();
1201
            }
1202
          },
1203
          {
1204
            type: 'contextformbutton',
1205
            icon: 'unlink',
1206
            tooltip: 'Remove link',
1207
            onSetup: onSetupLink,
1208
            onAction: formApi => {
1209
              unlink(editor);
1210
              formApi.hide();
1211
            }
1212
          },
1213
          {
1214
            type: 'contextformbutton',
1215
            icon: 'new-tab',
1216
            tooltip: 'Open link',
1217
            onSetup: onSetupLink,
1218
            onAction: formApi => {
1219
              gotoSelectedLink(editor)();
1220
              formApi.hide();
1221
            }
1222
          }
1223
        ]
1224
      });
1225
    };
1226
 
1227
    var Plugin = () => {
1228
      global$5.add('link', editor => {
1229
        register$1(editor);
1230
        setupButtons(editor);
1231
        setupMenuItems(editor);
1232
        setupContextMenu(editor);
1233
        setupContextToolbars(editor);
1234
        setupGotoLinks(editor);
1235
        register(editor);
1236
        setup(editor);
1237
      });
1238
    };
1239
 
1240
    Plugin();
1241
 
1242
})();