@tiptap/extension-link 2.11.7 → 3.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +5 -1
- package/dist/index.cjs +391 -398
- package/dist/index.cjs.map +1 -1
- package/dist/{link.d.ts → index.d.cts} +8 -7
- package/dist/index.d.ts +143 -4
- package/dist/index.js +364 -393
- package/dist/index.js.map +1 -1
- package/package.json +12 -10
- package/src/helpers/autolink.ts +10 -27
- package/src/helpers/clickHandler.ts +15 -10
- package/src/helpers/pasteHandler.ts +5 -3
- package/src/link.ts +85 -96
- package/dist/helpers/autolink.d.ts +0 -16
- package/dist/helpers/autolink.d.ts.map +0 -1
- package/dist/helpers/clickHandler.d.ts +0 -8
- package/dist/helpers/clickHandler.d.ts.map +0 -1
- package/dist/helpers/pasteHandler.d.ts +0 -11
- package/dist/helpers/pasteHandler.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.umd.js +0 -426
- package/dist/index.umd.js.map +0 -1
- package/dist/link.d.ts.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,424 +1,417 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
2
19
|
|
|
3
|
-
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Link: () => Link,
|
|
24
|
+
default: () => index_default,
|
|
25
|
+
isAllowedUri: () => isAllowedUri,
|
|
26
|
+
pasteRegex: () => pasteRegex
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
4
29
|
|
|
5
|
-
|
|
6
|
-
var
|
|
7
|
-
var
|
|
30
|
+
// src/link.ts
|
|
31
|
+
var import_core3 = require("@tiptap/core");
|
|
32
|
+
var import_linkifyjs3 = require("linkifyjs");
|
|
8
33
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* This ensures that only complete and valid text is hyperlinked, preventing cases where a valid
|
|
14
|
-
* top-level domain (TLD) is immediately followed by an invalid character, like a number. For
|
|
15
|
-
* example, with the `find` method from Linkify, entering `example.com1` would result in
|
|
16
|
-
* `example.com` being linked and the trailing `1` left as plain text. By using the `tokenize`
|
|
17
|
-
* method, we can perform more comprehensive validation on the input text.
|
|
18
|
-
*/
|
|
34
|
+
// src/helpers/autolink.ts
|
|
35
|
+
var import_core = require("@tiptap/core");
|
|
36
|
+
var import_state = require("@tiptap/pm/state");
|
|
37
|
+
var import_linkifyjs = require("linkifyjs");
|
|
19
38
|
function isValidLinkStructure(tokens) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
39
|
+
if (tokens.length === 1) {
|
|
40
|
+
return tokens[0].isLink;
|
|
41
|
+
}
|
|
42
|
+
if (tokens.length === 3 && tokens[1].isLink) {
|
|
43
|
+
return ["()", "[]"].includes(tokens[0].value + tokens[2].value);
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
27
46
|
}
|
|
28
|
-
/**
|
|
29
|
-
* This plugin allows you to automatically add links to your editor.
|
|
30
|
-
* @param options The plugin options
|
|
31
|
-
* @returns The plugin instance
|
|
32
|
-
*/
|
|
33
47
|
function autolink(options) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
return new import_state.Plugin({
|
|
49
|
+
key: new import_state.PluginKey("autolink"),
|
|
50
|
+
appendTransaction: (transactions, oldState, newState) => {
|
|
51
|
+
const docChanges = transactions.some((transaction) => transaction.docChanged) && !oldState.doc.eq(newState.doc);
|
|
52
|
+
const preventAutolink = transactions.some((transaction) => transaction.getMeta("preventAutolink"));
|
|
53
|
+
if (!docChanges || preventAutolink) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const { tr } = newState;
|
|
57
|
+
const transform = (0, import_core.combineTransactionSteps)(oldState.doc, [...transactions]);
|
|
58
|
+
const changes = (0, import_core.getChangedRanges)(transform);
|
|
59
|
+
changes.forEach(({ newRange }) => {
|
|
60
|
+
const nodesInChangedRanges = (0, import_core.findChildrenInRange)(newState.doc, newRange, (node) => node.isTextblock);
|
|
61
|
+
let textBlock;
|
|
62
|
+
let textBeforeWhitespace;
|
|
63
|
+
if (nodesInChangedRanges.length > 1) {
|
|
64
|
+
textBlock = nodesInChangedRanges[0];
|
|
65
|
+
textBeforeWhitespace = newState.doc.textBetween(
|
|
66
|
+
textBlock.pos,
|
|
67
|
+
textBlock.pos + textBlock.node.nodeSize,
|
|
68
|
+
void 0,
|
|
69
|
+
" "
|
|
70
|
+
);
|
|
71
|
+
} else if (nodesInChangedRanges.length && // We want to make sure to include the block seperator argument to treat hard breaks like spaces.
|
|
72
|
+
newState.doc.textBetween(newRange.from, newRange.to, " ", " ").endsWith(" ")) {
|
|
73
|
+
textBlock = nodesInChangedRanges[0];
|
|
74
|
+
textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, newRange.to, void 0, " ");
|
|
75
|
+
}
|
|
76
|
+
if (textBlock && textBeforeWhitespace) {
|
|
77
|
+
const wordsBeforeWhitespace = textBeforeWhitespace.split(" ").filter((s) => s !== "");
|
|
78
|
+
if (wordsBeforeWhitespace.length <= 0) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const lastWordBeforeSpace = wordsBeforeWhitespace[wordsBeforeWhitespace.length - 1];
|
|
82
|
+
const lastWordAndBlockOffset = textBlock.pos + textBeforeWhitespace.lastIndexOf(lastWordBeforeSpace);
|
|
83
|
+
if (!lastWordBeforeSpace) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
const linksBeforeSpace = (0, import_linkifyjs.tokenize)(lastWordBeforeSpace).map((t) => t.toObject(options.defaultProtocol));
|
|
87
|
+
if (!isValidLinkStructure(linksBeforeSpace)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
linksBeforeSpace.filter((link) => link.isLink).map((link) => ({
|
|
91
|
+
...link,
|
|
92
|
+
from: lastWordAndBlockOffset + link.start + 1,
|
|
93
|
+
to: lastWordAndBlockOffset + link.end + 1
|
|
94
|
+
})).filter((link) => {
|
|
95
|
+
if (!newState.schema.marks.code) {
|
|
96
|
+
return true;
|
|
51
97
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// Now let’s see if we can add new links.
|
|
57
|
-
const nodesInChangedRanges = core.findChildrenInRange(newState.doc, newRange, node => node.isTextblock);
|
|
58
|
-
let textBlock;
|
|
59
|
-
let textBeforeWhitespace;
|
|
60
|
-
if (nodesInChangedRanges.length > 1) {
|
|
61
|
-
// Grab the first node within the changed ranges (ex. the first of two paragraphs when hitting enter).
|
|
62
|
-
textBlock = nodesInChangedRanges[0];
|
|
63
|
-
textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, textBlock.pos + textBlock.node.nodeSize, undefined, ' ');
|
|
64
|
-
}
|
|
65
|
-
else if (nodesInChangedRanges.length
|
|
66
|
-
// We want to make sure to include the block seperator argument to treat hard breaks like spaces.
|
|
67
|
-
&& newState.doc.textBetween(newRange.from, newRange.to, ' ', ' ').endsWith(' ')) {
|
|
68
|
-
textBlock = nodesInChangedRanges[0];
|
|
69
|
-
textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, newRange.to, undefined, ' ');
|
|
70
|
-
}
|
|
71
|
-
if (textBlock && textBeforeWhitespace) {
|
|
72
|
-
const wordsBeforeWhitespace = textBeforeWhitespace.split(' ').filter(s => s !== '');
|
|
73
|
-
if (wordsBeforeWhitespace.length <= 0) {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
const lastWordBeforeSpace = wordsBeforeWhitespace[wordsBeforeWhitespace.length - 1];
|
|
77
|
-
const lastWordAndBlockOffset = textBlock.pos + textBeforeWhitespace.lastIndexOf(lastWordBeforeSpace);
|
|
78
|
-
if (!lastWordBeforeSpace) {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
const linksBeforeSpace = linkifyjs.tokenize(lastWordBeforeSpace).map(t => t.toObject(options.defaultProtocol));
|
|
82
|
-
if (!isValidLinkStructure(linksBeforeSpace)) {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
linksBeforeSpace
|
|
86
|
-
.filter(link => link.isLink)
|
|
87
|
-
// Calculate link position.
|
|
88
|
-
.map(link => ({
|
|
89
|
-
...link,
|
|
90
|
-
from: lastWordAndBlockOffset + link.start + 1,
|
|
91
|
-
to: lastWordAndBlockOffset + link.end + 1,
|
|
92
|
-
}))
|
|
93
|
-
// ignore link inside code mark
|
|
94
|
-
.filter(link => {
|
|
95
|
-
if (!newState.schema.marks.code) {
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
return !newState.doc.rangeHasMark(link.from, link.to, newState.schema.marks.code);
|
|
99
|
-
})
|
|
100
|
-
// validate link
|
|
101
|
-
.filter(link => options.validate(link.value))
|
|
102
|
-
// check whether should autolink
|
|
103
|
-
.filter(link => options.shouldAutoLink(link.value))
|
|
104
|
-
// Add link mark.
|
|
105
|
-
.forEach(link => {
|
|
106
|
-
if (core.getMarksBetween(link.from, link.to, newState.doc).some(item => item.mark.type === options.type)) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
tr.addMark(link.from, link.to, options.type.create({
|
|
110
|
-
href: link.href,
|
|
111
|
-
}));
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
if (!tr.steps.length) {
|
|
116
|
-
return;
|
|
98
|
+
return !newState.doc.rangeHasMark(link.from, link.to, newState.schema.marks.code);
|
|
99
|
+
}).filter((link) => options.validate(link.value)).filter((link) => options.shouldAutoLink(link.value)).forEach((link) => {
|
|
100
|
+
if ((0, import_core.getMarksBetween)(link.from, link.to, newState.doc).some((item) => item.mark.type === options.type)) {
|
|
101
|
+
return;
|
|
117
102
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
103
|
+
tr.addMark(
|
|
104
|
+
link.from,
|
|
105
|
+
link.to,
|
|
106
|
+
options.type.create({
|
|
107
|
+
href: link.href
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
if (!tr.steps.length) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
return tr;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
121
119
|
}
|
|
122
120
|
|
|
121
|
+
// src/helpers/clickHandler.ts
|
|
122
|
+
var import_core2 = require("@tiptap/core");
|
|
123
|
+
var import_state2 = require("@tiptap/pm/state");
|
|
123
124
|
function clickHandler(options) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
125
|
+
return new import_state2.Plugin({
|
|
126
|
+
key: new import_state2.PluginKey("handleClickLink"),
|
|
127
|
+
props: {
|
|
128
|
+
handleClick: (view, pos, event) => {
|
|
129
|
+
var _a, _b;
|
|
130
|
+
if (event.button !== 0) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
if (!view.editable) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
let link = null;
|
|
137
|
+
if (event.target instanceof HTMLAnchorElement) {
|
|
138
|
+
link = event.target;
|
|
139
|
+
} else {
|
|
140
|
+
let a = event.target;
|
|
141
|
+
const els = [];
|
|
142
|
+
while (a.nodeName !== "DIV") {
|
|
143
|
+
els.push(a);
|
|
144
|
+
a = a.parentNode;
|
|
145
|
+
}
|
|
146
|
+
link = els.find((value) => value.nodeName === "A");
|
|
147
|
+
}
|
|
148
|
+
if (!link) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
const attrs = (0, import_core2.getAttributes)(view.state, options.type.name);
|
|
152
|
+
const href = (_a = link == null ? void 0 : link.href) != null ? _a : attrs.href;
|
|
153
|
+
const target = (_b = link == null ? void 0 : link.target) != null ? _b : attrs.target;
|
|
154
|
+
if (link && href) {
|
|
155
|
+
window.open(href, target);
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
});
|
|
156
162
|
}
|
|
157
163
|
|
|
164
|
+
// src/helpers/pasteHandler.ts
|
|
165
|
+
var import_state3 = require("@tiptap/pm/state");
|
|
166
|
+
var import_linkifyjs2 = require("linkifyjs");
|
|
158
167
|
function pasteHandler(options) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
168
|
+
return new import_state3.Plugin({
|
|
169
|
+
key: new import_state3.PluginKey("handlePasteLink"),
|
|
170
|
+
props: {
|
|
171
|
+
handlePaste: (view, event, slice) => {
|
|
172
|
+
const { state } = view;
|
|
173
|
+
const { selection } = state;
|
|
174
|
+
const { empty } = selection;
|
|
175
|
+
if (empty) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
let textContent = "";
|
|
179
|
+
slice.content.forEach((node) => {
|
|
180
|
+
textContent += node.textContent;
|
|
181
|
+
});
|
|
182
|
+
const link = (0, import_linkifyjs2.find)(textContent, { defaultProtocol: options.defaultProtocol }).find(
|
|
183
|
+
(item) => item.isLink && item.value === textContent
|
|
184
|
+
);
|
|
185
|
+
if (!textContent || !link) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
return options.editor.commands.setMark(options.type, {
|
|
189
|
+
href: link.href
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
183
194
|
}
|
|
184
195
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
// eslint-disable-next-line no-control-regex
|
|
189
|
-
const ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
|
|
196
|
+
// src/link.ts
|
|
197
|
+
var pasteRegex = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi;
|
|
198
|
+
var ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
|
|
190
199
|
function isAllowedUri(uri, protocols) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
allowedProtocols.push(nextProtocol);
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
return (!uri
|
|
212
|
-
|| uri
|
|
213
|
-
.replace(ATTR_WHITESPACE, '')
|
|
214
|
-
.match(new RegExp(
|
|
215
|
-
// eslint-disable-next-line no-useless-escape
|
|
216
|
-
`^(?:(?:${allowedProtocols.join('|')}):|[^a-z]|[a-z0-9+.\-]+(?:[^a-z+.\-:]|$))`, 'i')));
|
|
200
|
+
const allowedProtocols = ["http", "https", "ftp", "ftps", "mailto", "tel", "callto", "sms", "cid", "xmpp"];
|
|
201
|
+
if (protocols) {
|
|
202
|
+
protocols.forEach((protocol) => {
|
|
203
|
+
const nextProtocol = typeof protocol === "string" ? protocol : protocol.scheme;
|
|
204
|
+
if (nextProtocol) {
|
|
205
|
+
allowedProtocols.push(nextProtocol);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
return !uri || uri.replace(ATTR_WHITESPACE, "").match(
|
|
210
|
+
new RegExp(
|
|
211
|
+
// eslint-disable-next-line no-useless-escape
|
|
212
|
+
`^(?:(?:${allowedProtocols.join("|")}):|[^a-z]|[a-z0-9+.-]+(?:[^a-z+.-:]|$))`,
|
|
213
|
+
"i"
|
|
214
|
+
)
|
|
215
|
+
);
|
|
217
216
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
217
|
+
var Link = import_core3.Mark.create({
|
|
218
|
+
name: "link",
|
|
219
|
+
priority: 1e3,
|
|
220
|
+
keepOnSplit: false,
|
|
221
|
+
exitable: true,
|
|
222
|
+
onCreate() {
|
|
223
|
+
if (this.options.validate && !this.options.shouldAutoLink) {
|
|
224
|
+
this.options.shouldAutoLink = this.options.validate;
|
|
225
|
+
console.warn("The `validate` option is deprecated. Rename to the `shouldAutoLink` option instead.");
|
|
226
|
+
}
|
|
227
|
+
this.options.protocols.forEach((protocol) => {
|
|
228
|
+
if (typeof protocol === "string") {
|
|
229
|
+
(0, import_linkifyjs3.registerCustomProtocol)(protocol);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
(0, import_linkifyjs3.registerCustomProtocol)(protocol.scheme, protocol.optionalSlashes);
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
onDestroy() {
|
|
236
|
+
(0, import_linkifyjs3.reset)();
|
|
237
|
+
},
|
|
238
|
+
inclusive() {
|
|
239
|
+
return this.options.autolink;
|
|
240
|
+
},
|
|
241
|
+
addOptions() {
|
|
242
|
+
return {
|
|
243
|
+
openOnClick: true,
|
|
244
|
+
linkOnPaste: true,
|
|
245
|
+
autolink: true,
|
|
246
|
+
protocols: [],
|
|
247
|
+
defaultProtocol: "http",
|
|
248
|
+
HTMLAttributes: {
|
|
249
|
+
target: "_blank",
|
|
250
|
+
rel: "noopener noreferrer nofollow",
|
|
251
|
+
class: null
|
|
252
|
+
},
|
|
253
|
+
isAllowedUri: (url, ctx) => !!isAllowedUri(url, ctx.protocols),
|
|
254
|
+
validate: (url) => !!url,
|
|
255
|
+
shouldAutoLink: (url) => !!url
|
|
256
|
+
};
|
|
257
|
+
},
|
|
258
|
+
addAttributes() {
|
|
259
|
+
return {
|
|
260
|
+
href: {
|
|
261
|
+
default: null,
|
|
262
|
+
parseHTML(element) {
|
|
263
|
+
return element.getAttribute("href");
|
|
232
264
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
defaultProtocol: 'http',
|
|
254
|
-
HTMLAttributes: {
|
|
255
|
-
target: '_blank',
|
|
256
|
-
rel: 'noopener noreferrer nofollow',
|
|
257
|
-
class: null,
|
|
258
|
-
},
|
|
259
|
-
isAllowedUri: (url, ctx) => !!isAllowedUri(url, ctx.protocols),
|
|
260
|
-
validate: url => !!url,
|
|
261
|
-
shouldAutoLink: url => !!url,
|
|
262
|
-
};
|
|
263
|
-
},
|
|
264
|
-
addAttributes() {
|
|
265
|
-
return {
|
|
266
|
-
href: {
|
|
267
|
-
default: null,
|
|
268
|
-
parseHTML(element) {
|
|
269
|
-
return element.getAttribute('href');
|
|
270
|
-
},
|
|
271
|
-
},
|
|
272
|
-
target: {
|
|
273
|
-
default: this.options.HTMLAttributes.target,
|
|
274
|
-
},
|
|
275
|
-
rel: {
|
|
276
|
-
default: this.options.HTMLAttributes.rel,
|
|
277
|
-
},
|
|
278
|
-
class: {
|
|
279
|
-
default: this.options.HTMLAttributes.class,
|
|
280
|
-
},
|
|
281
|
-
};
|
|
282
|
-
},
|
|
283
|
-
parseHTML() {
|
|
284
|
-
return [
|
|
285
|
-
{
|
|
286
|
-
tag: 'a[href]',
|
|
287
|
-
getAttrs: dom => {
|
|
288
|
-
const href = dom.getAttribute('href');
|
|
289
|
-
// prevent XSS attacks
|
|
290
|
-
if (!href
|
|
291
|
-
|| !this.options.isAllowedUri(href, {
|
|
292
|
-
defaultValidate: url => !!isAllowedUri(url, this.options.protocols),
|
|
293
|
-
protocols: this.options.protocols,
|
|
294
|
-
defaultProtocol: this.options.defaultProtocol,
|
|
295
|
-
})) {
|
|
296
|
-
return false;
|
|
297
|
-
}
|
|
298
|
-
return null;
|
|
299
|
-
},
|
|
300
|
-
},
|
|
301
|
-
];
|
|
302
|
-
},
|
|
303
|
-
renderHTML({ HTMLAttributes }) {
|
|
304
|
-
// prevent XSS attacks
|
|
305
|
-
if (!this.options.isAllowedUri(HTMLAttributes.href, {
|
|
306
|
-
defaultValidate: href => !!isAllowedUri(href, this.options.protocols),
|
|
265
|
+
},
|
|
266
|
+
target: {
|
|
267
|
+
default: this.options.HTMLAttributes.target
|
|
268
|
+
},
|
|
269
|
+
rel: {
|
|
270
|
+
default: this.options.HTMLAttributes.rel
|
|
271
|
+
},
|
|
272
|
+
class: {
|
|
273
|
+
default: this.options.HTMLAttributes.class
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
},
|
|
277
|
+
parseHTML() {
|
|
278
|
+
return [
|
|
279
|
+
{
|
|
280
|
+
tag: "a[href]",
|
|
281
|
+
getAttrs: (dom) => {
|
|
282
|
+
const href = dom.getAttribute("href");
|
|
283
|
+
if (!href || !this.options.isAllowedUri(href, {
|
|
284
|
+
defaultValidate: (url) => !!isAllowedUri(url, this.options.protocols),
|
|
307
285
|
protocols: this.options.protocols,
|
|
308
|
-
defaultProtocol: this.options.defaultProtocol
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
core.mergeAttributes(this.options.HTMLAttributes, { ...HTMLAttributes, href: '' }),
|
|
314
|
-
0,
|
|
315
|
-
];
|
|
286
|
+
defaultProtocol: this.options.defaultProtocol
|
|
287
|
+
})) {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
return null;
|
|
316
291
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
return chain()
|
|
342
|
-
.toggleMark(this.name, attributes, { extendEmptyMarkRange: true })
|
|
343
|
-
.setMeta('preventAutolink', true)
|
|
344
|
-
.run();
|
|
345
|
-
},
|
|
346
|
-
unsetLink: () => ({ chain }) => {
|
|
347
|
-
return chain()
|
|
348
|
-
.unsetMark(this.name, { extendEmptyMarkRange: true })
|
|
349
|
-
.setMeta('preventAutolink', true)
|
|
350
|
-
.run();
|
|
351
|
-
},
|
|
352
|
-
};
|
|
353
|
-
},
|
|
354
|
-
addPasteRules() {
|
|
355
|
-
return [
|
|
356
|
-
core.markPasteRule({
|
|
357
|
-
find: text => {
|
|
358
|
-
const foundLinks = [];
|
|
359
|
-
if (text) {
|
|
360
|
-
const { protocols, defaultProtocol } = this.options;
|
|
361
|
-
const links = linkifyjs.find(text).filter(item => item.isLink
|
|
362
|
-
&& this.options.isAllowedUri(item.value, {
|
|
363
|
-
defaultValidate: href => !!isAllowedUri(href, protocols),
|
|
364
|
-
protocols,
|
|
365
|
-
defaultProtocol,
|
|
366
|
-
}));
|
|
367
|
-
if (links.length) {
|
|
368
|
-
links.forEach(link => foundLinks.push({
|
|
369
|
-
text: link.value,
|
|
370
|
-
data: {
|
|
371
|
-
href: link.href,
|
|
372
|
-
},
|
|
373
|
-
index: link.start,
|
|
374
|
-
}));
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return foundLinks;
|
|
378
|
-
},
|
|
379
|
-
type: this.type,
|
|
380
|
-
getAttributes: match => {
|
|
381
|
-
var _a;
|
|
382
|
-
return {
|
|
383
|
-
href: (_a = match.data) === null || _a === void 0 ? void 0 : _a.href,
|
|
384
|
-
};
|
|
385
|
-
},
|
|
386
|
-
}),
|
|
387
|
-
];
|
|
388
|
-
},
|
|
389
|
-
addProseMirrorPlugins() {
|
|
390
|
-
const plugins = [];
|
|
391
|
-
const { protocols, defaultProtocol } = this.options;
|
|
392
|
-
if (this.options.autolink) {
|
|
393
|
-
plugins.push(autolink({
|
|
394
|
-
type: this.type,
|
|
395
|
-
defaultProtocol: this.options.defaultProtocol,
|
|
396
|
-
validate: url => this.options.isAllowedUri(url, {
|
|
397
|
-
defaultValidate: href => !!isAllowedUri(href, protocols),
|
|
398
|
-
protocols,
|
|
399
|
-
defaultProtocol,
|
|
400
|
-
}),
|
|
401
|
-
shouldAutoLink: this.options.shouldAutoLink,
|
|
402
|
-
}));
|
|
292
|
+
}
|
|
293
|
+
];
|
|
294
|
+
},
|
|
295
|
+
renderHTML({ HTMLAttributes }) {
|
|
296
|
+
if (!this.options.isAllowedUri(HTMLAttributes.href, {
|
|
297
|
+
defaultValidate: (href) => !!isAllowedUri(href, this.options.protocols),
|
|
298
|
+
protocols: this.options.protocols,
|
|
299
|
+
defaultProtocol: this.options.defaultProtocol
|
|
300
|
+
})) {
|
|
301
|
+
return ["a", (0, import_core3.mergeAttributes)(this.options.HTMLAttributes, { ...HTMLAttributes, href: "" }), 0];
|
|
302
|
+
}
|
|
303
|
+
return ["a", (0, import_core3.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes), 0];
|
|
304
|
+
},
|
|
305
|
+
addCommands() {
|
|
306
|
+
return {
|
|
307
|
+
setLink: (attributes) => ({ chain }) => {
|
|
308
|
+
const { href } = attributes;
|
|
309
|
+
if (!this.options.isAllowedUri(href, {
|
|
310
|
+
defaultValidate: (url) => !!isAllowedUri(url, this.options.protocols),
|
|
311
|
+
protocols: this.options.protocols,
|
|
312
|
+
defaultProtocol: this.options.defaultProtocol
|
|
313
|
+
})) {
|
|
314
|
+
return false;
|
|
403
315
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
316
|
+
return chain().setMark(this.name, attributes).setMeta("preventAutolink", true).run();
|
|
317
|
+
},
|
|
318
|
+
toggleLink: (attributes) => ({ chain }) => {
|
|
319
|
+
const { href } = attributes;
|
|
320
|
+
if (!this.options.isAllowedUri(href, {
|
|
321
|
+
defaultValidate: (url) => !!isAllowedUri(url, this.options.protocols),
|
|
322
|
+
protocols: this.options.protocols,
|
|
323
|
+
defaultProtocol: this.options.defaultProtocol
|
|
324
|
+
})) {
|
|
325
|
+
return false;
|
|
408
326
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
327
|
+
return chain().toggleMark(this.name, attributes, { extendEmptyMarkRange: true }).setMeta("preventAutolink", true).run();
|
|
328
|
+
},
|
|
329
|
+
unsetLink: () => ({ chain }) => {
|
|
330
|
+
return chain().unsetMark(this.name, { extendEmptyMarkRange: true }).setMeta("preventAutolink", true).run();
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
},
|
|
334
|
+
addPasteRules() {
|
|
335
|
+
return [
|
|
336
|
+
(0, import_core3.markPasteRule)({
|
|
337
|
+
find: (text) => {
|
|
338
|
+
const foundLinks = [];
|
|
339
|
+
if (text) {
|
|
340
|
+
const { protocols, defaultProtocol } = this.options;
|
|
341
|
+
const links = (0, import_linkifyjs3.find)(text).filter(
|
|
342
|
+
(item) => item.isLink && this.options.isAllowedUri(item.value, {
|
|
343
|
+
defaultValidate: (href) => !!isAllowedUri(href, protocols),
|
|
344
|
+
protocols,
|
|
345
|
+
defaultProtocol
|
|
346
|
+
})
|
|
347
|
+
);
|
|
348
|
+
if (links.length) {
|
|
349
|
+
links.forEach(
|
|
350
|
+
(link) => foundLinks.push({
|
|
351
|
+
text: link.value,
|
|
352
|
+
data: {
|
|
353
|
+
href: link.href
|
|
354
|
+
},
|
|
355
|
+
index: link.start
|
|
356
|
+
})
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return foundLinks;
|
|
361
|
+
},
|
|
362
|
+
type: this.type,
|
|
363
|
+
getAttributes: (match) => {
|
|
364
|
+
var _a;
|
|
365
|
+
return {
|
|
366
|
+
href: (_a = match.data) == null ? void 0 : _a.href
|
|
367
|
+
};
|
|
415
368
|
}
|
|
416
|
-
|
|
417
|
-
|
|
369
|
+
})
|
|
370
|
+
];
|
|
371
|
+
},
|
|
372
|
+
addProseMirrorPlugins() {
|
|
373
|
+
const plugins = [];
|
|
374
|
+
const { protocols, defaultProtocol } = this.options;
|
|
375
|
+
if (this.options.autolink) {
|
|
376
|
+
plugins.push(
|
|
377
|
+
autolink({
|
|
378
|
+
type: this.type,
|
|
379
|
+
defaultProtocol: this.options.defaultProtocol,
|
|
380
|
+
validate: (url) => this.options.isAllowedUri(url, {
|
|
381
|
+
defaultValidate: (href) => !!isAllowedUri(href, protocols),
|
|
382
|
+
protocols,
|
|
383
|
+
defaultProtocol
|
|
384
|
+
}),
|
|
385
|
+
shouldAutoLink: this.options.shouldAutoLink
|
|
386
|
+
})
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
if (this.options.openOnClick === true) {
|
|
390
|
+
plugins.push(
|
|
391
|
+
clickHandler({
|
|
392
|
+
type: this.type
|
|
393
|
+
})
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
if (this.options.linkOnPaste) {
|
|
397
|
+
plugins.push(
|
|
398
|
+
pasteHandler({
|
|
399
|
+
editor: this.editor,
|
|
400
|
+
defaultProtocol: this.options.defaultProtocol,
|
|
401
|
+
type: this.type
|
|
402
|
+
})
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
return plugins;
|
|
406
|
+
}
|
|
418
407
|
});
|
|
419
408
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
exports
|
|
424
|
-
|
|
409
|
+
// src/index.ts
|
|
410
|
+
var index_default = Link;
|
|
411
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
412
|
+
0 && (module.exports = {
|
|
413
|
+
Link,
|
|
414
|
+
isAllowedUri,
|
|
415
|
+
pasteRegex
|
|
416
|
+
});
|
|
417
|
+
//# sourceMappingURL=index.cjs.map
|