2 * TinyMCE version 6.6.2 (2023-08-09)
8 var global$5 = tinymce.util.Tools.resolve('tinymce.PluginManager');
10 const hasProto = (v, constructor, predicate) => {
12 if (predicate(v, constructor.prototype)) {
15 return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
22 } else if (t === 'object' && Array.isArray(x)) {
24 } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
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) => {
43 for (let i = 0, len = value.length; i < len; ++i) {
44 if (!pred(value[i])) {
55 const constant = value => {
60 const tripleEquals = (a, b) => {
65 constructor(tag, value) {
70 return new Optional(true, value);
73 return Optional.singletonNone;
75 fold(onNone, onSome) {
77 return onSome(this.value);
90 return Optional.some(mapper(this.value));
92 return Optional.none();
97 return binder(this.value);
99 return Optional.none();
103 return this.tag && predicate(this.value);
106 return !this.tag || predicate(this.value);
109 if (!this.tag || predicate(this.value)) {
112 return Optional.none();
116 return this.tag ? this.value : replacement;
119 return this.tag ? this : replacement;
122 return this.tag ? this.value : thunk();
125 return this.tag ? this : thunk();
129 throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
135 return isNonNullable(value) ? Optional.some(value) : Optional.none();
138 return this.tag ? this.value : null;
149 return this.tag ? [this.value] : [];
152 return this.tag ? `some(${ this.value })` : 'none()';
155 Optional.singletonNone = new Optional(false);
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++) {
170 const each$1 = (xs, f) => {
171 for (let i = 0, len = xs.length; i < len; i++) {
176 const foldl = (xs, f, acc) => {
177 each$1(xs, (x, i) => {
182 const flatten = xs => {
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);
188 nativePush.apply(r, xs[i]);
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);
200 return Optional.none();
203 const is = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));
209 for (let i = 0; i < arr.length; i++) {
214 const someIf = (b, a) => b ? Optional.some(a) : Optional.none();
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);
223 if (value === true) {
228 } else if (value === 'http' || value === 'https') {
242 message: 'Must be a string or a boolean.'
248 registerOption('link_context_toolbar', {
249 processor: 'boolean',
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', {
258 registerOption('link_target_list', {
259 processor: value => isBoolean(value) || isArrayOf(value, isObject),
262 registerOption('link_rel_list', {
263 processor: 'object[]',
266 registerOption('link_class_list', {
267 processor: 'object[]',
270 registerOption('link_title', {
271 processor: 'boolean',
274 registerOption('allow_unsafe_link_target', {
275 processor: 'boolean',
278 registerOption('link_quicklink', {
279 processor: 'boolean',
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');
295 var global$4 = tinymce.util.Tools.resolve('tinymce.util.Tools');
297 const getValue = item => isString(item.value) ? item.value : '';
298 const getText = item => {
299 if (isString(item.text)) {
301 } else if (isString(item.title)) {
307 const sanitizeList = (list, extractValue) => {
309 global$4.each(list, item => {
310 const text = getText(item);
311 if (item.menu !== undefined) {
312 const items = sanitizeList(item.menu, extractValue);
318 const value = extractValue(item);
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 => ({
335 const ListOptions = {
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++) {
352 const objAcc = r => (x, i) => {
355 const internalFilter = (obj, pred, onTrue, onFalse) => {
356 each(obj, (x, i) => {
357 (pred(x, i) ? onTrue : onFalse)(x, i);
360 const filter = (obj, pred) => {
362 internalFilter(obj, pred, objAcc(t), noop);
365 const has = (obj, key) => hasOwnProperty.call(obj, key);
366 const hasNonNullableKey = (obj, key) => has(obj, key) && obj[key] !== undefined && obj[key] !== null;
368 var global$3 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');
370 var global$2 = tinymce.util.Tools.resolve('tinymce.util.URI');
372 const isAnchor = elm => isNonNullable(elm) && elm.nodeName.toLowerCase() === 'a';
373 const isLink = elm => isAnchor(elm) && !!getHref(elm);
374 const collectNodesInRange = (rng, predicate) => {
378 const contents = rng.cloneContents();
379 const firstChild = contents.firstChild;
380 const walker = new global$3(firstChild, contents);
382 let current = firstChild;
384 if (predicate(current)) {
385 elements.push(current);
387 } while (current = walker.next());
391 const hasProtocol = url => /^\w+:/i.test(url);
392 const getHref = elm => {
394 return (_b = (_a = elm.getAttribute('data-mce-href')) !== null && _a !== void 0 ? _a : elm.getAttribute('href')) !== null && _b !== void 0 ? _b : '';
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;
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) : '';
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]);
414 return Optional.from(editor.dom.getParent(selectedElm, 'a[href]'));
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);
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) {
433 const rng = editor.selection.getRng();
434 if (!rng.collapsed) {
435 const elements = collectNodesInRange(rng, isElement);
436 return elements.length === 0;
441 const isImageFigure = elm => isNonNullable(elm) && elm.nodeName === 'FIGURE' && /\bimage\b/i.test(elm.className);
442 const getLinkAttrs = data => {
449 return foldl(attrs, (acc, key) => {
450 data[key].each(value => {
451 acc[key] = value.length > 0 ? value : null;
454 }, { href: data.href });
456 const handleExternalTargets = (href, assumeExternalTargets) => {
457 if ((assumeExternalTargets === 'http' || assumeExternalTargets === 'https') && !hasProtocol(href)) {
458 return assumeExternalTargets + '://' + href;
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;
468 if (Optional.from(newLinkAttrs.target).isNone() && getTargetList(editor) === false) {
469 newLinkAttrs.target = getDefaultLinkTarget(editor);
471 newLinkAttrs.href = handleExternalTargets(newLinkAttrs.href, assumeExternalTargets(editor));
474 const updateLink = (editor, anchorElm, text, linkAttrs) => {
476 if (has(anchorElm, 'innerText')) {
477 anchorElm.innerText = text;
479 anchorElm.textContent = text;
482 editor.dom.setAttribs(anchorElm, linkAttrs);
483 editor.selection.select(anchorElm);
485 const createLink = (editor, selectedElm, text, linkAttrs) => {
486 const dom = editor.dom;
487 if (isImageFigure(selectedElm)) {
488 linkImageFigure(dom, selectedElm, linkAttrs);
491 editor.execCommand('mceInsertLink', false, linkAttrs);
493 editor.insertContent(dom.createHTML('a', linkAttrs, dom.encode(text)));
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();
505 anchorElm.fold(() => {
506 createLink(editor, selectedElm, data.text, linkAttrs);
509 updateLink(editor, elm, data.text, linkAttrs);
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);
523 rng.setEndAfter(endAnchorElm);
525 selection.setRng(rng);
526 editor.execCommand('unlink');
527 selection.moveToBookmark(bookmark);
529 const unlinkDomMutation = editor => {
530 editor.undoManager.transact(() => {
531 const node = editor.selection.getNode();
532 if (isImageFigure(node)) {
533 unlinkImageFigure(editor, node);
535 unlinkSelection(editor);
540 const unwrapOptions = data => {
550 class: cls.getOrNull(),
552 rel: rel.getOrNull(),
553 target: target.getOrNull(),
554 text: text.getOrNull(),
555 title: title.getOrNull()
556 }, (v, _k) => isNull(v) === false);
558 const sanitizeData = (editor, data) => {
559 const getOption = editor.options.get;
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')
565 const href = data.href;
568 href: global$2.isDomSafe(href, 'a', uriOptions) ? href : ''
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);
575 const unlink = editor => {
576 editor.hasPlugin('rtc', true) ? editor.execCommand('unlink') : unlinkDomMutation(editor);
578 const unlinkImageFigure = (editor, fig) => {
580 const img = editor.dom.select('img', fig)[0];
582 const a = editor.dom.getParents(img, 'a[href]', fig)[0];
584 (_a = a.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(img, a);
585 editor.dom.remove(a);
589 const linkImageFigure = (dom, fig, attrs) => {
591 const img = dom.select('img', fig)[0];
593 const a = dom.create('a', attrs);
594 (_a = img.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(a, img);
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);
604 return someIf(item.value === value, item);
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 => ({
614 text: hasPersistentText ? persistentText : i.text,
618 text: hasPersistentText ? persistentText : i.text
619 })) : Optional.none();
621 const findCatalog = (catalogs, fieldName) => {
622 if (fieldName === 'link') {
623 return catalogs.link;
624 } else if (fieldName === 'anchor') {
625 return catalogs.anchor;
627 return Optional.none();
630 const init = (initialData, linkCatalog) => {
631 const persistentData = {
632 text: initialData.text,
633 title: initialData.title
635 const getTitleFromUrlChange = url => {
637 return someIf(persistentData.title.length <= 0, Optional.from((_a = url.meta) === null || _a === void 0 ? void 0 : _a.title).getOr(''));
639 const getTextFromUrlChange = url => {
641 return someIf(persistentData.text.length <= 0, Optional.from((_a = url.meta) === null || _a === void 0 ? void 0 : _a.text).getOr(url.value));
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({})
652 return Optional.none();
655 const onCatalogChange = (data, change) => {
656 const catalog = findCatalog(linkCatalog, change).getOr([]);
657 return getDelta(persistentData.text, change, catalog, data);
659 const onChange = (getData, change) => {
660 const name = change.name;
661 if (name === 'url') {
662 return onUrlChange(getData());
663 } else if (contains([
667 return onCatalogChange(getData(), name);
668 } else if (name === 'text' || name === 'title') {
669 persistentData[name] = getData()[name];
670 return Optional.none();
672 return Optional.none();
677 const DialogChanges = {
682 var global$1 = tinymce.util.Tools.resolve('tinymce.util.Delay');
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);
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 => ({
700 href: 'mailto:' + url
702 }) : Optional.none();
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 => ({
711 href: defaultLinkProtocol + '://' + url
713 }) : Optional.none();
715 const preprocess = (editor, data) => findMap([
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);
723 const DialogConfirms = { preprocess };
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;
734 return anchors.length > 0 ? Optional.some([{
737 }].concat(anchors)) : Optional.none();
739 const AnchorListOptions = { getAnchors };
741 const getClasses = editor => {
742 const list = getLinkClassList(editor);
743 if (list.length > 0) {
744 return ListOptions.sanitize(list);
746 return Optional.none();
748 const ClassListOptions = { getClasses };
750 const parseJson = text => {
752 return Optional.some(JSON.parse(text));
754 return Optional.none();
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)));
766 resolve(Optional.from(linkList));
768 }).then(optItems => optItems.bind(ListOptions.sanitizeWith(extractor)).map(items => {
769 if (items.length > 0) {
774 return noneItem.concat(items);
780 const LinkListOptions = { getLinks };
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);
791 return Optional.none();
793 const RelOptions = { getRels };
797 text: 'Current window',
805 const getTargets = editor => {
806 const list = getTargetList(editor);
808 return ListOptions.sanitize(list).orThunk(() => Optional.some(fallbacks));
809 } else if (list === false) {
810 return Optional.none();
812 return Optional.some(fallbacks);
814 const TargetOptions = { getTargets };
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();
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'));
838 const collect = (editor, linkNode) => LinkListOptions.getLinks(editor).then(links => {
839 const anchor = extractFromAnchor(editor, linkNode);
843 targets: TargetOptions.getTargets(editor),
844 rels: RelOptions.getRels(editor, anchor.target),
845 classes: ClassListOptions.getClasses(editor),
846 anchor: AnchorListOptions.getAnchors(editor),
850 flags: { titleEnabled: shouldShowLinkTitle(editor) }
853 const DialogInfo = { collect };
855 const handleSubmit = (editor, info) => api => {
856 const data = api.getData();
857 if (!data.url.value) {
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')
871 const attachState = {
872 href: data.url.value,
873 attach: data.url.meta !== undefined && data.url.meta.attach ? data.url.meta.attach : noop
875 DialogConfirms.preprocess(editor, changedData).then(pData => {
876 link(editor, attachState, pData);
880 const collectData = editor => {
881 const anchorNode = getAnchorElement(editor);
882 return DialogInfo.collect(editor, anchorNode);
884 const getInitialData = (info, defaultTarget) => {
885 const anchor = info.anchor;
886 const url = anchor.url.getOr('');
890 meta: { original: { value: url } }
892 text: anchor.text.getOr(''),
893 title: anchor.title.getOr(''),
896 rel: anchor.rel.getOr(''),
897 target: anchor.target.or(defaultTarget).getOr(''),
898 linkClass: anchor.linkClass.getOr('')
901 const makeDialog = (settings, onSubmit, editor) => {
908 const displayText = settings.anchor.text.map(() => ({
911 label: 'Text to display'
913 const titleText = settings.flags.titleEnabled ? [{
918 const defaultTarget = Optional.from(getDefaultLinkTarget(editor));
919 const initialData = getInitialData(settings, defaultTarget);
920 const catalogs = settings.catalogs;
921 const dialogDelta = DialogChanges.init(initialData, catalogs);
929 catalogs.anchor.map(ListOptions.createUi('anchor', 'Anchors')),
930 catalogs.rels.map(ListOptions.createUi('rel', 'Rel')),
931 catalogs.targets.map(ListOptions.createUi('target', 'Open link in...')),
932 catalogs.link.map(ListOptions.createUi('link', 'Link list')),
933 catalogs.classes.map(ListOptions.createUi('linkClass', 'Class'))
938 title: 'Insert/Edit Link',
955 onChange: (api, {name}) => {
956 dialogDelta.onChange(api.getData, { name }).each(newData => {
957 api.setData(newData);
963 const open$1 = editor => {
964 const data = collectData(editor);
966 const onSubmit = handleSubmit(editor, info);
967 return makeDialog(info, onSubmit, editor);
969 editor.windowManager.open(spec);
973 const register = editor => {
974 editor.addCommand('mceLink', (_ui, value) => {
975 if ((value === null || value === void 0 ? void 0 : value.dialog) === true || !useQuickLink(editor)) {
978 editor.dispatch('contexttoolbar-show', { toolbarKey: 'quicklink' });
983 var global = tinymce.util.Tools.resolve('tinymce.util.VK');
985 const appendClickRemove = (link, evt) => {
986 document.body.appendChild(link);
987 link.dispatchEvent(evt);
988 document.body.removeChild(link);
990 const open = url => {
991 const link = document.createElement('a');
992 link.target = '_blank';
994 link.rel = 'noreferrer noopener';
995 const evt = document.createEvent('MouseEvents');
996 evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
997 appendClickRemove(link, evt);
1000 const getLink = (editor, elm) => editor.dom.getParent(elm, 'a[href]');
1001 const getSelectedLink = editor => getLink(editor, editor.selection.getStart());
1002 const hasOnlyAltModifier = e => {
1003 return e.altKey === true && e.shiftKey === false && e.ctrlKey === false && e.metaKey === false;
1005 const gotoLink = (editor, a) => {
1007 const href = getHref(a);
1008 if (/^#/.test(href)) {
1009 const targetEl = editor.dom.select(href);
1010 if (targetEl.length) {
1011 editor.selection.scrollIntoView(targetEl[0], true);
1018 const openDialog = editor => () => {
1019 editor.execCommand('mceLink', false, { dialog: true });
1021 const gotoSelectedLink = editor => () => {
1022 gotoLink(editor, getSelectedLink(editor));
1024 const setupGotoLinks = editor => {
1025 editor.on('click', e => {
1026 const link = getLink(editor, e.target);
1027 if (link && global.metaKeyPressed(e)) {
1029 gotoLink(editor, link);
1032 editor.on('keydown', e => {
1033 if (!e.isDefaultPrevented() && e.keyCode === 13 && hasOnlyAltModifier(e)) {
1034 const link = getSelectedLink(editor);
1037 gotoLink(editor, link);
1042 const toggleState = (editor, toggler) => {
1043 editor.on('NodeChange', toggler);
1044 return () => editor.off('NodeChange', toggler);
1046 const toggleLinkState = editor => api => {
1047 const updateState = () => {
1048 api.setActive(!editor.mode.isReadOnly() && isInAnchor(editor, editor.selection.getNode()));
1049 api.setEnabled(editor.selection.isEditable());
1052 return toggleState(editor, updateState);
1054 const toggleLinkMenuState = editor => api => {
1055 const updateState = () => {
1056 api.setEnabled(editor.selection.isEditable());
1059 return toggleState(editor, updateState);
1061 const hasExactlyOneLinkInSelection = editor => {
1062 const links = editor.selection.isCollapsed() ? getLinks$1(editor.dom.getParents(editor.selection.getStart())) : getLinksInSelection(editor.selection.getRng());
1063 return links.length === 1;
1065 const toggleGotoLinkState = editor => api => {
1066 const updateState = () => api.setEnabled(hasExactlyOneLinkInSelection(editor));
1068 return toggleState(editor, updateState);
1070 const toggleUnlinkState = editor => api => {
1071 const hasLinks$1 = parents => hasLinks(parents) || hasLinksInSelection(editor.selection.getRng());
1072 const parents = editor.dom.getParents(editor.selection.getStart());
1073 const updateEnabled = parents => {
1074 api.setEnabled(hasLinks$1(parents) && editor.selection.isEditable());
1076 updateEnabled(parents);
1077 return toggleState(editor, e => updateEnabled(e.parents));
1080 const setup = editor => {
1081 editor.addShortcut('Meta+K', '', () => {
1082 editor.execCommand('mceLink');
1086 const setupButtons = editor => {
1087 editor.ui.registry.addToggleButton('link', {
1089 tooltip: 'Insert/edit link',
1090 onAction: openDialog(editor),
1091 onSetup: toggleLinkState(editor)
1093 editor.ui.registry.addButton('openlink', {
1095 tooltip: 'Open link',
1096 onAction: gotoSelectedLink(editor),
1097 onSetup: toggleGotoLinkState(editor)
1099 editor.ui.registry.addButton('unlink', {
1101 tooltip: 'Remove link',
1102 onAction: () => unlink(editor),
1103 onSetup: toggleUnlinkState(editor)
1106 const setupMenuItems = editor => {
1107 editor.ui.registry.addMenuItem('openlink', {
1110 onAction: gotoSelectedLink(editor),
1111 onSetup: toggleGotoLinkState(editor)
1113 editor.ui.registry.addMenuItem('link', {
1117 onSetup: toggleLinkMenuState(editor),
1118 onAction: openDialog(editor)
1120 editor.ui.registry.addMenuItem('unlink', {
1122 text: 'Remove link',
1123 onAction: () => unlink(editor),
1124 onSetup: toggleUnlinkState(editor)
1127 const setupContextMenu = editor => {
1128 const inLink = 'link unlink openlink';
1129 const noLink = 'link';
1130 editor.ui.registry.addContextMenu('link', {
1131 update: element => {
1132 const isEditable = editor.dom.isEditable(element);
1136 return hasLinks(editor.dom.getParents(element, 'a')) ? inLink : noLink;
1140 const setupContextToolbars = editor => {
1141 const collapseSelectionToEnd = editor => {
1142 editor.selection.collapse(false);
1144 const onSetupLink = buttonApi => {
1145 const node = editor.selection.getNode();
1146 buttonApi.setEnabled(isInAnchor(editor, node));
1149 const getLinkText = value => {
1150 const anchor = getAnchorElement(editor);
1151 const onlyText = isOnlyTextSelected(editor);
1152 if (anchor.isNone() && onlyText) {
1153 const text = getAnchorText(editor.selection, anchor);
1154 return someIf(text.length === 0, value);
1156 return Optional.none();
1159 editor.ui.registry.addContextForm('quicklink', {
1161 type: 'contextformtogglebutton',
1164 onSetup: toggleLinkState(editor)
1167 predicate: node => hasContextToolbar(editor) && isInAnchor(editor, node),
1169 const elm = getAnchorElement(editor);
1170 return elm.fold(constant(''), getHref);
1174 type: 'contextformtogglebutton',
1178 onSetup: buttonApi => {
1179 const node = editor.selection.getNode();
1180 buttonApi.setActive(isInAnchor(editor, node));
1181 return toggleLinkState(editor)(buttonApi);
1183 onAction: formApi => {
1184 const value = formApi.getValue();
1185 const text = getLinkText(value);
1186 const attachState = {
1190 link(editor, attachState, {
1193 title: Optional.none(),
1194 rel: Optional.none(),
1195 target: Optional.none(),
1196 class: Optional.none()
1198 collapseSelectionToEnd(editor);
1203 type: 'contextformbutton',
1205 tooltip: 'Remove link',
1206 onSetup: onSetupLink,
1207 onAction: formApi => {
1213 type: 'contextformbutton',
1215 tooltip: 'Open link',
1216 onSetup: onSetupLink,
1217 onAction: formApi => {
1218 gotoSelectedLink(editor)();
1226 var Plugin = () => {
1227 global$5.add('link', editor => {
1229 setupButtons(editor);
1230 setupMenuItems(editor);
1231 setupContextMenu(editor);
1232 setupContextToolbars(editor);
1233 setupGotoLinks(editor);