|  |  | 'use strict';
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Object.defineProperty(exports, '__esModule', { value: true });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | var core = require('@tiptap/core');
 | 
						
						
						
							|  |  | var linkifyjs = require('linkifyjs');
 | 
						
						
						
							|  |  | var prosemirrorState = require('prosemirror-state');
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | function autolink(options) {
 | 
						
						
						
							|  |  |     return new prosemirrorState.Plugin({
 | 
						
						
						
							|  |  |         key: new prosemirrorState.PluginKey('autolink'),
 | 
						
						
						
							|  |  |         appendTransaction: (transactions, oldState, newState) => {
 | 
						
						
						
							|  |  |             const docChanges = transactions.some(transaction => transaction.docChanged)
 | 
						
						
						
							|  |  |                 && !oldState.doc.eq(newState.doc);
 | 
						
						
						
							|  |  |             const preventAutolink = transactions.some(transaction => transaction.getMeta('preventAutolink'));
 | 
						
						
						
							|  |  |             if (!docChanges || preventAutolink) {
 | 
						
						
						
							|  |  |                 return;
 | 
						
						
						
							|  |  |             }
 | 
						
						
						
							|  |  |             const { tr } = newState;
 | 
						
						
						
							|  |  |             const transform = core.combineTransactionSteps(oldState.doc, [...transactions]);
 | 
						
						
						
							|  |  |             const { mapping } = transform;
 | 
						
						
						
							|  |  |             const changes = core.getChangedRanges(transform);
 | 
						
						
						
							|  |  |             changes.forEach(({ oldRange, newRange }) => {
 | 
						
						
						
							|  |  |                 // at first we check if we have to remove links
 | 
						
						
						
							|  |  |                 core.getMarksBetween(oldRange.from, oldRange.to, oldState.doc)
 | 
						
						
						
							|  |  |                     .filter(item => item.mark.type === options.type)
 | 
						
						
						
							|  |  |                     .forEach(oldMark => {
 | 
						
						
						
							|  |  |                     const newFrom = mapping.map(oldMark.from);
 | 
						
						
						
							|  |  |                     const newTo = mapping.map(oldMark.to);
 | 
						
						
						
							|  |  |                     const newMarks = core.getMarksBetween(newFrom, newTo, newState.doc)
 | 
						
						
						
							|  |  |                         .filter(item => item.mark.type === options.type);
 | 
						
						
						
							|  |  |                     if (!newMarks.length) {
 | 
						
						
						
							|  |  |                         return;
 | 
						
						
						
							|  |  |                     }
 | 
						
						
						
							|  |  |                     const newMark = newMarks[0];
 | 
						
						
						
							|  |  |                     const oldLinkText = oldState.doc.textBetween(oldMark.from, oldMark.to, undefined, ' ');
 | 
						
						
						
							|  |  |                     const newLinkText = newState.doc.textBetween(newMark.from, newMark.to, undefined, ' ');
 | 
						
						
						
							|  |  |                     const wasLink = linkifyjs.test(oldLinkText);
 | 
						
						
						
							|  |  |                     const isLink = linkifyjs.test(newLinkText);
 | 
						
						
						
							|  |  |                     // remove only the link, if it was a link before too
 | 
						
						
						
							|  |  |                     // because we don’t want to remove links that were set manually
 | 
						
						
						
							|  |  |                     if (wasLink && !isLink) {
 | 
						
						
						
							|  |  |                         tr.removeMark(newMark.from, newMark.to, options.type);
 | 
						
						
						
							|  |  |                     }
 | 
						
						
						
							|  |  |                 });
 | 
						
						
						
							|  |  |                 // now let’s see if we can add new links
 | 
						
						
						
							|  |  |                 const nodesInChangedRanges = core.findChildrenInRange(newState.doc, newRange, node => node.isTextblock);
 | 
						
						
						
							|  |  |                 let textBlock;
 | 
						
						
						
							|  |  |                 let textBeforeWhitespace;
 | 
						
						
						
							|  |  |                 if (nodesInChangedRanges.length > 1) {
 | 
						
						
						
							|  |  |                     // Grab the first node within the changed ranges (ex. the first of two paragraphs when hitting enter)
 | 
						
						
						
							|  |  |                     textBlock = nodesInChangedRanges[0];
 | 
						
						
						
							|  |  |                     textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, textBlock.pos + textBlock.node.nodeSize, undefined, ' ');
 | 
						
						
						
							|  |  |                 }
 | 
						
						
						
							|  |  |                 else if (nodesInChangedRanges.length
 | 
						
						
						
							|  |  |                     // We want to make sure to include the block seperator argument to treat hard breaks like spaces
 | 
						
						
						
							|  |  |                     && newState.doc.textBetween(newRange.from, newRange.to, ' ', ' ').endsWith(' ')) {
 | 
						
						
						
							|  |  |                     textBlock = nodesInChangedRanges[0];
 | 
						
						
						
							|  |  |                     textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, newRange.to, undefined, ' ');
 | 
						
						
						
							|  |  |                 }
 | 
						
						
						
							|  |  |                 if (textBlock && textBeforeWhitespace) {
 | 
						
						
						
							|  |  |                     const wordsBeforeWhitespace = textBeforeWhitespace.split(' ').filter(s => s !== '');
 | 
						
						
						
							|  |  |                     if (wordsBeforeWhitespace.length <= 0) {
 | 
						
						
						
							|  |  |                         return false;
 | 
						
						
						
							|  |  |                     }
 | 
						
						
						
							|  |  |                     const lastWordBeforeSpace = wordsBeforeWhitespace[wordsBeforeWhitespace.length - 1];
 | 
						
						
						
							|  |  |                     const lastWordAndBlockOffset = textBlock.pos + textBeforeWhitespace.lastIndexOf(lastWordBeforeSpace);
 | 
						
						
						
							|  |  |                     if (!lastWordBeforeSpace) {
 | 
						
						
						
							|  |  |                         return false;
 | 
						
						
						
							|  |  |                     }
 | 
						
						
						
							|  |  |                     linkifyjs.find(lastWordBeforeSpace)
 | 
						
						
						
							|  |  |                         .filter(link => link.isLink)
 | 
						
						
						
							|  |  |                         .filter(link => {
 | 
						
						
						
							|  |  |                         if (options.validate) {
 | 
						
						
						
							|  |  |                             return options.validate(link.value);
 | 
						
						
						
							|  |  |                         }
 | 
						
						
						
							|  |  |                         return true;
 | 
						
						
						
							|  |  |                     })
 | 
						
						
						
							|  |  |                         // calculate link position
 | 
						
						
						
							|  |  |                         .map(link => ({
 | 
						
						
						
							|  |  |                         ...link,
 | 
						
						
						
							|  |  |                         from: lastWordAndBlockOffset + link.start + 1,
 | 
						
						
						
							|  |  |                         to: lastWordAndBlockOffset + link.end + 1,
 | 
						
						
						
							|  |  |                     }))
 | 
						
						
						
							|  |  |                         // add link mark
 | 
						
						
						
							|  |  |                         .forEach(link => {
 | 
						
						
						
							|  |  |                         tr.addMark(link.from, link.to, options.type.create({
 | 
						
						
						
							|  |  |                             href: link.href,
 | 
						
						
						
							|  |  |                         }));
 | 
						
						
						
							|  |  |                     });
 | 
						
						
						
							|  |  |                 }
 | 
						
						
						
							|  |  |             });
 | 
						
						
						
							|  |  |             if (!tr.steps.length) {
 | 
						
						
						
							|  |  |                 return;
 | 
						
						
						
							|  |  |             }
 | 
						
						
						
							|  |  |             return tr;
 | 
						
						
						
							|  |  |         },
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | function clickHandler(options) {
 | 
						
						
						
							|  |  |     return new prosemirrorState.Plugin({
 | 
						
						
						
							|  |  |         key: new prosemirrorState.PluginKey('handleClickLink'),
 | 
						
						
						
							|  |  |         props: {
 | 
						
						
						
							|  |  |             handleClick: (view, pos, event) => {
 | 
						
						
						
							|  |  |                 var _a;
 | 
						
						
						
							|  |  |                 const attrs = core.getAttributes(view.state, options.type.name);
 | 
						
						
						
							|  |  |                 const link = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest('a');
 | 
						
						
						
							|  |  |                 if (link && attrs.href) {
 | 
						
						
						
							|  |  |                     window.open(attrs.href, attrs.target);
 | 
						
						
						
							|  |  |                     return true;
 | 
						
						
						
							|  |  |                 }
 | 
						
						
						
							|  |  |                 return false;
 | 
						
						
						
							|  |  |             },
 | 
						
						
						
							|  |  |         },
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | function pasteHandler(options) {
 | 
						
						
						
							|  |  |     return new prosemirrorState.Plugin({
 | 
						
						
						
							|  |  |         key: new prosemirrorState.PluginKey('handlePasteLink'),
 | 
						
						
						
							|  |  |         props: {
 | 
						
						
						
							|  |  |             handlePaste: (view, event, slice) => {
 | 
						
						
						
							|  |  |                 const { state } = view;
 | 
						
						
						
							|  |  |                 const { selection } = state;
 | 
						
						
						
							|  |  |                 const { empty } = selection;
 | 
						
						
						
							|  |  |                 if (empty) {
 | 
						
						
						
							|  |  |                     return false;
 | 
						
						
						
							|  |  |                 }
 | 
						
						
						
							|  |  |                 let textContent = '';
 | 
						
						
						
							|  |  |                 slice.content.forEach(node => {
 | 
						
						
						
							|  |  |                     textContent += node.textContent;
 | 
						
						
						
							|  |  |                 });
 | 
						
						
						
							|  |  |                 const link = linkifyjs.find(textContent).find(item => item.isLink && item.value === textContent);
 | 
						
						
						
							|  |  |                 if (!textContent || !link) {
 | 
						
						
						
							|  |  |                     return false;
 | 
						
						
						
							|  |  |                 }
 | 
						
						
						
							|  |  |                 options.editor.commands.setMark(options.type, {
 | 
						
						
						
							|  |  |                     href: link.href,
 | 
						
						
						
							|  |  |                 });
 | 
						
						
						
							|  |  |                 return true;
 | 
						
						
						
							|  |  |             },
 | 
						
						
						
							|  |  |         },
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | const Link = core.Mark.create({
 | 
						
						
						
							|  |  |     name: 'link',
 | 
						
						
						
							|  |  |     priority: 1000,
 | 
						
						
						
							|  |  |     keepOnSplit: false,
 | 
						
						
						
							|  |  |     onCreate() {
 | 
						
						
						
							|  |  |         this.options.protocols.forEach(linkifyjs.registerCustomProtocol);
 | 
						
						
						
							|  |  |     },
 | 
						
						
						
							|  |  |     onDestroy() {
 | 
						
						
						
							|  |  |         linkifyjs.reset();
 | 
						
						
						
							|  |  |     },
 | 
						
						
						
							|  |  |     inclusive() {
 | 
						
						
						
							|  |  |         return this.options.autolink;
 | 
						
						
						
							|  |  |     },
 | 
						
						
						
							|  |  |     addOptions() {
 | 
						
						
						
							|  |  |         return {
 | 
						
						
						
							|  |  |             openOnClick: true,
 | 
						
						
						
							|  |  |             linkOnPaste: true,
 | 
						
						
						
							|  |  |             autolink: true,
 | 
						
						
						
							|  |  |             protocols: [],
 | 
						
						
						
							|  |  |             HTMLAttributes: {
 | 
						
						
						
							|  |  |                 target: '_blank',
 | 
						
						
						
							|  |  |                 rel: 'noopener noreferrer nofollow',
 | 
						
						
						
							|  |  |                 class: null,
 | 
						
						
						
							|  |  |             },
 | 
						
						
						
							|  |  |             validate: undefined,
 | 
						
						
						
							|  |  |         };
 | 
						
						
						
							|  |  |     },
 | 
						
						
						
							|  |  |     addAttributes() {
 | 
						
						
						
							|  |  |         return {
 | 
						
						
						
							|  |  |             href: {
 | 
						
						
						
							|  |  |                 default: null,
 | 
						
						
						
							|  |  |             },
 | 
						
						
						
							|  |  |             target: {
 | 
						
						
						
							|  |  |                 default: this.options.HTMLAttributes.target,
 | 
						
						
						
							|  |  |             },
 | 
						
						
						
							|  |  |             class: {
 | 
						
						
						
							|  |  |                 default: this.options.HTMLAttributes.class,
 | 
						
						
						
							|  |  |             },
 | 
						
						
						
							|  |  |         };
 | 
						
						
						
							|  |  |     },
 | 
						
						
						
							|  |  |     parseHTML() {
 | 
						
						
						
							|  |  |         return [
 | 
						
						
						
							|  |  |             { tag: 'a[href]:not([href *= "javascript:" i])' },
 | 
						
						
						
							|  |  |         ];
 | 
						
						
						
							|  |  |     },
 | 
						
						
						
							|  |  |     renderHTML({ HTMLAttributes }) {
 | 
						
						
						
							|  |  |         return [
 | 
						
						
						
							|  |  |             'a',
 | 
						
						
						
							|  |  |             core.mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
 | 
						
						
						
							|  |  |             0,
 | 
						
						
						
							|  |  |         ];
 | 
						
						
						
							|  |  |     },
 | 
						
						
						
							|  |  |     addCommands() {
 | 
						
						
						
							|  |  |         return {
 | 
						
						
						
							|  |  |             setLink: attributes => ({ chain }) => {
 | 
						
						
						
							|  |  |                 return chain()
 | 
						
						
						
							|  |  |                     .setMark(this.name, attributes)
 | 
						
						
						
							|  |  |                     .setMeta('preventAutolink', true)
 | 
						
						
						
							|  |  |                     .run();
 | 
						
						
						
							|  |  |             },
 | 
						
						
						
							|  |  |             toggleLink: attributes => ({ chain }) => {
 | 
						
						
						
							|  |  |                 return chain()
 | 
						
						
						
							|  |  |                     .toggleMark(this.name, attributes, { extendEmptyMarkRange: true })
 | 
						
						
						
							|  |  |                     .setMeta('preventAutolink', true)
 | 
						
						
						
							|  |  |                     .run();
 | 
						
						
						
							|  |  |             },
 | 
						
						
						
							|  |  |             unsetLink: () => ({ chain }) => {
 | 
						
						
						
							|  |  |                 return chain()
 | 
						
						
						
							|  |  |                     .unsetMark(this.name, { extendEmptyMarkRange: true })
 | 
						
						
						
							|  |  |                     .setMeta('preventAutolink', true)
 | 
						
						
						
							|  |  |                     .run();
 | 
						
						
						
							|  |  |             },
 | 
						
						
						
							|  |  |         };
 | 
						
						
						
							|  |  |     },
 | 
						
						
						
							|  |  |     addPasteRules() {
 | 
						
						
						
							|  |  |         return [
 | 
						
						
						
							|  |  |             core.markPasteRule({
 | 
						
						
						
							|  |  |                 find: text => linkifyjs.find(text)
 | 
						
						
						
							|  |  |                     .filter(link => {
 | 
						
						
						
							|  |  |                     if (this.options.validate) {
 | 
						
						
						
							|  |  |                         return this.options.validate(link.value);
 | 
						
						
						
							|  |  |                     }
 | 
						
						
						
							|  |  |                     return true;
 | 
						
						
						
							|  |  |                 })
 | 
						
						
						
							|  |  |                     .filter(link => link.isLink)
 | 
						
						
						
							|  |  |                     .map(link => ({
 | 
						
						
						
							|  |  |                     text: link.value,
 | 
						
						
						
							|  |  |                     index: link.start,
 | 
						
						
						
							|  |  |                     data: link,
 | 
						
						
						
							|  |  |                 })),
 | 
						
						
						
							|  |  |                 type: this.type,
 | 
						
						
						
							|  |  |                 getAttributes: match => {
 | 
						
						
						
							|  |  |                     var _a;
 | 
						
						
						
							|  |  |                     return ({
 | 
						
						
						
							|  |  |                         href: (_a = match.data) === null || _a === void 0 ? void 0 : _a.href,
 | 
						
						
						
							|  |  |                     });
 | 
						
						
						
							|  |  |                 },
 | 
						
						
						
							|  |  |             }),
 | 
						
						
						
							|  |  |         ];
 | 
						
						
						
							|  |  |     },
 | 
						
						
						
							|  |  |     addProseMirrorPlugins() {
 | 
						
						
						
							|  |  |         const plugins = [];
 | 
						
						
						
							|  |  |         if (this.options.autolink) {
 | 
						
						
						
							|  |  |             plugins.push(autolink({
 | 
						
						
						
							|  |  |                 type: this.type,
 | 
						
						
						
							|  |  |                 validate: this.options.validate,
 | 
						
						
						
							|  |  |             }));
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |         if (this.options.openOnClick) {
 | 
						
						
						
							|  |  |             plugins.push(clickHandler({
 | 
						
						
						
							|  |  |                 type: this.type,
 | 
						
						
						
							|  |  |             }));
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |         if (this.options.linkOnPaste) {
 | 
						
						
						
							|  |  |             plugins.push(pasteHandler({
 | 
						
						
						
							|  |  |                 editor: this.editor,
 | 
						
						
						
							|  |  |                 type: this.type,
 | 
						
						
						
							|  |  |             }));
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |         return plugins;
 | 
						
						
						
							|  |  |     },
 | 
						
						
						
							|  |  | });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | exports.Link = Link;
 | 
						
						
						
							|  |  | exports["default"] = Link;
 | 
						
						
						
							|  |  | //# sourceMappingURL=tiptap-extension-link.cjs.map
 |