@tiptap/extension-link 3.30.2 → 3.30.4
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/dist/index.cjs +494 -633
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +134 -133
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +134 -133
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +487 -609
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,639 +1,517 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import { find as find2, registerCustomProtocol, reset } from "linkifyjs";
|
|
4
|
-
|
|
5
|
-
// src/helpers/autolink.ts
|
|
6
|
-
import {
|
|
7
|
-
combineTransactionSteps,
|
|
8
|
-
findChildrenInRange,
|
|
9
|
-
getChangedRanges,
|
|
10
|
-
getMarksBetween
|
|
11
|
-
} from "@tiptap/core";
|
|
1
|
+
import { InputRule, Mark, PasteRule, combineTransactionSteps, findChildrenInRange, getAttributes, getChangedRanges, getMarksBetween, markInputRule, markPasteRule, mergeAttributes } from "@tiptap/core";
|
|
2
|
+
import { find, registerCustomProtocol, reset, tokenize } from "linkifyjs";
|
|
12
3
|
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
4
|
+
//#region src/helpers/whitespace.ts
|
|
5
|
+
const UNICODE_WHITESPACE_PATTERN = "[\0- \xA0 -\u2029 ]";
|
|
6
|
+
const UNICODE_WHITESPACE_REGEX = new RegExp(UNICODE_WHITESPACE_PATTERN);
|
|
7
|
+
const UNICODE_WHITESPACE_REGEX_END = new RegExp(`${UNICODE_WHITESPACE_PATTERN}$`);
|
|
8
|
+
const UNICODE_WHITESPACE_REGEX_GLOBAL = new RegExp(UNICODE_WHITESPACE_PATTERN, "g");
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/helpers/autolink.ts
|
|
11
|
+
/**
|
|
12
|
+
* Check if the provided tokens form a valid link structure, which can either be a single link token
|
|
13
|
+
* or a link token surrounded by parentheses or square brackets.
|
|
14
|
+
*
|
|
15
|
+
* This ensures that only complete and valid text is hyperlinked, preventing cases where a valid
|
|
16
|
+
* top-level domain (TLD) is immediately followed by an invalid character, like a number. For
|
|
17
|
+
* example, with the `find` method from Linkify, entering `example.com1` would result in
|
|
18
|
+
* `example.com` being linked and the trailing `1` left as plain text. By using the `tokenize`
|
|
19
|
+
* method, we can perform more comprehensive validation on the input text.
|
|
20
|
+
*/
|
|
22
21
|
function isValidLinkStructure(tokens) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (tokens.length === 3 && tokens[1].isLink) {
|
|
27
|
-
return ["()", "[]"].includes(tokens[0].value + tokens[2].value);
|
|
28
|
-
}
|
|
29
|
-
return false;
|
|
22
|
+
if (tokens.length === 1) return tokens[0].isLink;
|
|
23
|
+
if (tokens.length === 3 && tokens[1].isLink) return ["()", "[]"].includes(tokens[0].value + tokens[2].value);
|
|
24
|
+
return false;
|
|
30
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* This plugin allows you to automatically add links to your editor.
|
|
28
|
+
* @param options The plugin options
|
|
29
|
+
* @returns The plugin instance
|
|
30
|
+
*/
|
|
31
31
|
function autolink(options) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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;
|
|
97
|
-
}
|
|
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 (getMarksBetween(link.from, link.to, newState.doc).some(
|
|
101
|
-
(item) => item.mark.type === options.type
|
|
102
|
-
)) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
tr.addMark(
|
|
106
|
-
link.from,
|
|
107
|
-
link.to,
|
|
108
|
-
options.type.create({
|
|
109
|
-
href: link.href
|
|
110
|
-
})
|
|
111
|
-
);
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
if (!tr.steps.length) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
return tr;
|
|
119
|
-
}
|
|
120
|
-
});
|
|
32
|
+
return new Plugin({
|
|
33
|
+
key: new PluginKey("autolink"),
|
|
34
|
+
appendTransaction: (transactions, oldState, newState) => {
|
|
35
|
+
/**
|
|
36
|
+
* Does the transaction change the document?
|
|
37
|
+
*/
|
|
38
|
+
const docChanges = transactions.some((transaction) => transaction.docChanged) && !oldState.doc.eq(newState.doc);
|
|
39
|
+
/**
|
|
40
|
+
* Prevent autolink if the transaction is not a document change or if the transaction has the meta `preventAutolink`.
|
|
41
|
+
*/
|
|
42
|
+
const preventAutolink = transactions.some((transaction) => transaction.getMeta("preventAutolink"));
|
|
43
|
+
/**
|
|
44
|
+
* Prevent autolink if the transaction is not a document change
|
|
45
|
+
* or if the transaction has the meta `preventAutolink`.
|
|
46
|
+
*/
|
|
47
|
+
if (!docChanges || preventAutolink) return;
|
|
48
|
+
const { tr } = newState;
|
|
49
|
+
const transform = combineTransactionSteps(oldState.doc, [...transactions]);
|
|
50
|
+
getChangedRanges(transform).forEach(({ newRange }) => {
|
|
51
|
+
const nodesInChangedRanges = findChildrenInRange(newState.doc, newRange, (node) => node.isTextblock);
|
|
52
|
+
let textBlock;
|
|
53
|
+
let textBeforeWhitespace;
|
|
54
|
+
if (nodesInChangedRanges.length > 1) {
|
|
55
|
+
textBlock = nodesInChangedRanges[0];
|
|
56
|
+
textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, textBlock.pos + textBlock.node.nodeSize, void 0, " ");
|
|
57
|
+
} else if (nodesInChangedRanges.length) {
|
|
58
|
+
const endText = newState.doc.textBetween(newRange.from, newRange.to, " ", " ");
|
|
59
|
+
if (!UNICODE_WHITESPACE_REGEX_END.test(endText)) return;
|
|
60
|
+
textBlock = nodesInChangedRanges[0];
|
|
61
|
+
textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, newRange.to, void 0, " ");
|
|
62
|
+
}
|
|
63
|
+
if (textBlock && textBeforeWhitespace) {
|
|
64
|
+
const wordsBeforeWhitespace = textBeforeWhitespace.split(UNICODE_WHITESPACE_REGEX).filter(Boolean);
|
|
65
|
+
if (wordsBeforeWhitespace.length <= 0) return false;
|
|
66
|
+
const lastWordBeforeSpace = wordsBeforeWhitespace[wordsBeforeWhitespace.length - 1];
|
|
67
|
+
const lastWordAndBlockOffset = textBlock.pos + textBeforeWhitespace.lastIndexOf(lastWordBeforeSpace);
|
|
68
|
+
if (!lastWordBeforeSpace) return false;
|
|
69
|
+
const linksBeforeSpace = tokenize(lastWordBeforeSpace).map((t) => t.toObject(options.defaultProtocol));
|
|
70
|
+
if (!isValidLinkStructure(linksBeforeSpace)) return false;
|
|
71
|
+
linksBeforeSpace.filter((link) => link.isLink).map((link) => ({
|
|
72
|
+
...link,
|
|
73
|
+
from: lastWordAndBlockOffset + link.start + 1,
|
|
74
|
+
to: lastWordAndBlockOffset + link.end + 1
|
|
75
|
+
})).filter((link) => {
|
|
76
|
+
if (!newState.schema.marks.code) return true;
|
|
77
|
+
return !newState.doc.rangeHasMark(link.from, link.to, newState.schema.marks.code);
|
|
78
|
+
}).filter((link) => options.validate(link.value)).filter((link) => options.shouldAutoLink(link.value)).forEach((link) => {
|
|
79
|
+
if (getMarksBetween(link.from, link.to, newState.doc).some((item) => item.mark.type === options.type)) return;
|
|
80
|
+
tr.addMark(link.from, link.to, options.type.create({ href: link.href }));
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
if (!tr.steps.length) return;
|
|
85
|
+
return tr;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
121
88
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
import { getAttributes } from "@tiptap/core";
|
|
125
|
-
import { Plugin as Plugin2, PluginKey as PluginKey2 } from "@tiptap/pm/state";
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/helpers/clickHandler.ts
|
|
126
91
|
function clickHandler(options) {
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
const commandResult = options.editor.commands.extendMarkRange(options.type.name);
|
|
158
|
-
handled = commandResult;
|
|
159
|
-
}
|
|
160
|
-
if (options.openOnClick) {
|
|
161
|
-
const attrs = getAttributes(view.state, options.type.name);
|
|
162
|
-
const href = (_a = link.href) != null ? _a : attrs.href;
|
|
163
|
-
const target = (_b = link.target) != null ? _b : attrs.target;
|
|
164
|
-
if (href) {
|
|
165
|
-
window.open(href, target);
|
|
166
|
-
handled = true;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return handled;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
});
|
|
92
|
+
return new Plugin({
|
|
93
|
+
key: new PluginKey("handleClickLink"),
|
|
94
|
+
props: { handleClick: (view, pos, event) => {
|
|
95
|
+
if (event.button !== 0) return false;
|
|
96
|
+
if (!view.editable) return false;
|
|
97
|
+
let link = null;
|
|
98
|
+
if (event.target instanceof HTMLAnchorElement) link = event.target;
|
|
99
|
+
else {
|
|
100
|
+
const target = event.target;
|
|
101
|
+
if (!target) return false;
|
|
102
|
+
const root = options.editor.view.dom;
|
|
103
|
+
link = target.closest("a");
|
|
104
|
+
if (link && !root.contains(link)) link = null;
|
|
105
|
+
}
|
|
106
|
+
if (!link) return false;
|
|
107
|
+
let handled = false;
|
|
108
|
+
if (options.enableClickSelection) handled = options.editor.commands.extendMarkRange(options.type.name);
|
|
109
|
+
if (options.openOnClick) {
|
|
110
|
+
var _link$href, _link$target;
|
|
111
|
+
const attrs = getAttributes(view.state, options.type.name);
|
|
112
|
+
const href = (_link$href = link.href) !== null && _link$href !== void 0 ? _link$href : attrs.href;
|
|
113
|
+
const target = (_link$target = link.target) !== null && _link$target !== void 0 ? _link$target : attrs.target;
|
|
114
|
+
if (href) {
|
|
115
|
+
window.open(href, target);
|
|
116
|
+
handled = true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return handled;
|
|
120
|
+
} }
|
|
121
|
+
});
|
|
173
122
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/helpers/markdownLink.ts
|
|
125
|
+
/**
|
|
126
|
+
* Matches a Markdown link with an optional quoted title.
|
|
127
|
+
* for ex: [Tiptap](https://tiptap.dev) or [Tiptap](https://tiptap.dev "some title")
|
|
128
|
+
* the URL may also contain one level of balanced parentheses, as in CommonMark
|
|
129
|
+
* (titles accept curly quotes too, the Typography extension swaps them in while typing)
|
|
130
|
+
* the title delimiters must come in matching pairs
|
|
131
|
+
*/
|
|
132
|
+
const MARKDOWN_LINK_INPUT_REGEX = /\[([^[\]]+)\]\(((?:[^\s()]|\([^\s()]*\))+)(?:\s+(?:(["'])(.*?)\3|“(.*?)”|‘(.*?)’))?\)$/;
|
|
133
|
+
/**
|
|
134
|
+
* Same as the input regex but global, to find every Markdown link in pasted text.
|
|
135
|
+
*/
|
|
136
|
+
const MARKDOWN_LINK_PASTE_REGEX = /\[([^[\]]+)\]\(((?:[^\s()]|\([^\s()]*\))+)(?:\s+(?:(["'])(.*?)\3|“(.*?)”|‘(.*?)’))?\)/g;
|
|
179
137
|
function isEscaped(text, index) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
return backslashes % 2 === 1;
|
|
138
|
+
let backslashes = 0;
|
|
139
|
+
for (let position = index - 1; position >= 0 && text[position] === "\\"; position -= 1) backslashes += 1;
|
|
140
|
+
return backslashes % 2 === 1;
|
|
185
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Pairs the backtick runs before the match by length, as CommonMark does.
|
|
144
|
+
* A run left open means the match sits in an unfinished code span.
|
|
145
|
+
*/
|
|
186
146
|
function isInsideCodeSpan(text, matchIndex) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return openRunLength > 0;
|
|
147
|
+
let openRunLength = 0;
|
|
148
|
+
let index = 0;
|
|
149
|
+
while (index < matchIndex) {
|
|
150
|
+
if (text[index] !== "`") {
|
|
151
|
+
index += 1;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (openRunLength === 0 && isEscaped(text, index)) {
|
|
155
|
+
index += 1;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
let runLength = 0;
|
|
159
|
+
while (index < matchIndex && text[index] === "`") {
|
|
160
|
+
runLength += 1;
|
|
161
|
+
index += 1;
|
|
162
|
+
}
|
|
163
|
+
if (openRunLength === 0) openRunLength = runLength;
|
|
164
|
+
else if (runLength === openRunLength) openRunLength = 0;
|
|
165
|
+
}
|
|
166
|
+
return openRunLength > 0;
|
|
210
167
|
}
|
|
211
168
|
function isConvertibleLink(text, match, isAllowedHref) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
if (isInsideCodeSpan(text, (_b = match.index) != null ? _b : 0)) {
|
|
219
|
-
return false;
|
|
220
|
-
}
|
|
221
|
-
return !!linkText.trim() && isAllowedHref(href);
|
|
169
|
+
var _match$index, _match$index2;
|
|
170
|
+
const [, linkText, href] = match;
|
|
171
|
+
if ((match.index ? text[match.index - 1] : void 0) === "!" || isEscaped(text, (_match$index = match.index) !== null && _match$index !== void 0 ? _match$index : 0)) return false;
|
|
172
|
+
if (isInsideCodeSpan(text, (_match$index2 = match.index) !== null && _match$index2 !== void 0 ? _match$index2 : 0)) return false;
|
|
173
|
+
return !!linkText.trim() && isAllowedHref(href);
|
|
222
174
|
}
|
|
223
175
|
function toRuleMatch(match) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
};
|
|
176
|
+
var _ref, _match$index3;
|
|
177
|
+
const [linkSyntax, linkText, href, , straightQuotedTitle, curlyDoubleTitle, curlySingleTitle] = match;
|
|
178
|
+
const title = (_ref = straightQuotedTitle !== null && straightQuotedTitle !== void 0 ? straightQuotedTitle : curlyDoubleTitle) !== null && _ref !== void 0 ? _ref : curlySingleTitle;
|
|
179
|
+
return {
|
|
180
|
+
index: (_match$index3 = match.index) !== null && _match$index3 !== void 0 ? _match$index3 : 0,
|
|
181
|
+
text: linkSyntax,
|
|
182
|
+
replaceWith: linkText,
|
|
183
|
+
data: {
|
|
184
|
+
href,
|
|
185
|
+
title: title || null,
|
|
186
|
+
markdown: true
|
|
187
|
+
}
|
|
188
|
+
};
|
|
238
189
|
}
|
|
239
190
|
function matchesOverlap(a, b) {
|
|
240
|
-
|
|
191
|
+
return a.index < b.index + b.text.length && b.index < a.index + a.text.length;
|
|
241
192
|
}
|
|
242
193
|
function getMarkdownLinkAttributes(match) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
194
|
+
var _match$data, _match$data$title, _match$data2;
|
|
195
|
+
return {
|
|
196
|
+
href: (_match$data = match.data) === null || _match$data === void 0 ? void 0 : _match$data.href,
|
|
197
|
+
title: (_match$data$title = (_match$data2 = match.data) === null || _match$data2 === void 0 ? void 0 : _match$data2.title) !== null && _match$data$title !== void 0 ? _match$data$title : null
|
|
198
|
+
};
|
|
248
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* Turns typed Markdown link syntax into a link mark as soon as the closing `)` comes in.
|
|
202
|
+
* The transaction gets flagged so autolink doesn't touch the converted text again.
|
|
203
|
+
*/
|
|
249
204
|
function markdownLinkInputRule(config) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
268
|
-
return result;
|
|
269
|
-
}
|
|
270
|
-
});
|
|
205
|
+
const rule = markInputRule({
|
|
206
|
+
find: (text) => {
|
|
207
|
+
const match = MARKDOWN_LINK_INPUT_REGEX.exec(text);
|
|
208
|
+
if (!match || !isConvertibleLink(text, match, config.isAllowedHref)) return null;
|
|
209
|
+
return toRuleMatch(match);
|
|
210
|
+
},
|
|
211
|
+
type: config.type,
|
|
212
|
+
getAttributes: getMarkdownLinkAttributes
|
|
213
|
+
});
|
|
214
|
+
return new InputRule({
|
|
215
|
+
find: rule.find,
|
|
216
|
+
handler: (props) => {
|
|
217
|
+
const result = rule.handler(props);
|
|
218
|
+
if (result !== null && props.state.tr.steps.length) props.state.tr.setMeta("preventAutolink", true);
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
});
|
|
271
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Same for pasting, converts every Markdown link found in the pasted text
|
|
225
|
+
* and links the plain URLs from `findPlainUrls`.
|
|
226
|
+
*/
|
|
272
227
|
function markdownLinkPasteRule(config) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
var _a;
|
|
294
|
-
const result = rule.handler(props);
|
|
295
|
-
if (result !== null && props.state.tr.steps.length && ((_a = props.match.data) == null ? void 0 : _a.markdown)) {
|
|
296
|
-
props.state.tr.setMeta("preventAutolink", true);
|
|
297
|
-
}
|
|
298
|
-
return result;
|
|
299
|
-
}
|
|
300
|
-
});
|
|
228
|
+
const rule = markPasteRule({
|
|
229
|
+
find: (text) => {
|
|
230
|
+
var _config$findPlainUrls, _config$findPlainUrls2;
|
|
231
|
+
const markdownMatches = [];
|
|
232
|
+
for (const match of text.matchAll(MARKDOWN_LINK_PASTE_REGEX)) if (isConvertibleLink(text, match, config.isAllowedHref)) markdownMatches.push(toRuleMatch(match));
|
|
233
|
+
const plainUrlMatches = ((_config$findPlainUrls = (_config$findPlainUrls2 = config.findPlainUrls) === null || _config$findPlainUrls2 === void 0 ? void 0 : _config$findPlainUrls2.call(config, text)) !== null && _config$findPlainUrls !== void 0 ? _config$findPlainUrls : []).filter((urlMatch) => !markdownMatches.some((markdownMatch) => matchesOverlap(markdownMatch, urlMatch)));
|
|
234
|
+
return [...markdownMatches, ...plainUrlMatches];
|
|
235
|
+
},
|
|
236
|
+
type: config.type,
|
|
237
|
+
getAttributes: getMarkdownLinkAttributes
|
|
238
|
+
});
|
|
239
|
+
return new PasteRule({
|
|
240
|
+
find: rule.find,
|
|
241
|
+
handler: (props) => {
|
|
242
|
+
var _props$match$data;
|
|
243
|
+
const result = rule.handler(props);
|
|
244
|
+
if (result !== null && props.state.tr.steps.length && ((_props$match$data = props.match.data) === null || _props$match$data === void 0 ? void 0 : _props$match$data.markdown)) props.state.tr.setMeta("preventAutolink", true);
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
});
|
|
301
248
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
import { Plugin as Plugin3, PluginKey as PluginKey3 } from "@tiptap/pm/state";
|
|
305
|
-
import { find } from "linkifyjs";
|
|
249
|
+
//#endregion
|
|
250
|
+
//#region src/helpers/pasteHandler.ts
|
|
306
251
|
function pasteHandler(options) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
);
|
|
325
|
-
if (!textContent || !link || shouldAutoLink !== void 0 && !shouldAutoLink(link.value)) {
|
|
326
|
-
return false;
|
|
327
|
-
}
|
|
328
|
-
return options.editor.commands.setMark(options.type, {
|
|
329
|
-
href: link.href
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
});
|
|
252
|
+
return new Plugin({
|
|
253
|
+
key: new PluginKey("handlePasteLink"),
|
|
254
|
+
props: { handlePaste: (view, _event, slice) => {
|
|
255
|
+
const { shouldAutoLink } = options;
|
|
256
|
+
const { state } = view;
|
|
257
|
+
const { selection } = state;
|
|
258
|
+
const { empty } = selection;
|
|
259
|
+
if (empty) return false;
|
|
260
|
+
let textContent = "";
|
|
261
|
+
slice.content.forEach((node) => {
|
|
262
|
+
textContent += node.textContent;
|
|
263
|
+
});
|
|
264
|
+
const link = find(textContent, { defaultProtocol: options.defaultProtocol }).find((item) => item.isLink && item.value === textContent);
|
|
265
|
+
if (!textContent || !link || shouldAutoLink !== void 0 && !shouldAutoLink(link.value)) return false;
|
|
266
|
+
return options.editor.commands.setMark(options.type, { href: link.href });
|
|
267
|
+
} }
|
|
268
|
+
});
|
|
334
269
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
270
|
+
//#endregion
|
|
271
|
+
//#region src/link.ts
|
|
272
|
+
const pasteRegex = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi;
|
|
338
273
|
function isAllowedUri(uri, protocols) {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
return !uri || uri.replace(UNICODE_WHITESPACE_REGEX_GLOBAL, "").match(
|
|
360
|
-
new RegExp(
|
|
361
|
-
`^(?:(?:${allowedProtocols.map((protocol) => protocol.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")).join("|")}):|[^a-z]|[a-z0-9+.\\-]+(?:[^a-z+.\\-:]|$))`,
|
|
362
|
-
"i"
|
|
363
|
-
)
|
|
364
|
-
);
|
|
274
|
+
const allowedProtocols = [
|
|
275
|
+
"http",
|
|
276
|
+
"https",
|
|
277
|
+
"ftp",
|
|
278
|
+
"ftps",
|
|
279
|
+
"mailto",
|
|
280
|
+
"tel",
|
|
281
|
+
"callto",
|
|
282
|
+
"sms",
|
|
283
|
+
"cid",
|
|
284
|
+
"xmpp"
|
|
285
|
+
];
|
|
286
|
+
if (protocols) protocols.forEach((protocol) => {
|
|
287
|
+
const nextProtocol = typeof protocol === "string" ? protocol : protocol.scheme;
|
|
288
|
+
if (nextProtocol) allowedProtocols.push(nextProtocol);
|
|
289
|
+
});
|
|
290
|
+
return !uri || uri.replace(UNICODE_WHITESPACE_REGEX_GLOBAL, "").match(new RegExp(`^(?:(?:${allowedProtocols.map((protocol) => protocol.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")).join("|")}):|[^a-z]|[a-z0-9+.\\-]+(?:[^a-z+.\\-:]|$))`, "i"));
|
|
365
291
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
var _a;
|
|
585
|
-
return {
|
|
586
|
-
href: (_a = match.data) == null ? void 0 : _a.href
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
})
|
|
590
|
-
];
|
|
591
|
-
},
|
|
592
|
-
addProseMirrorPlugins() {
|
|
593
|
-
const plugins = [];
|
|
594
|
-
const { protocols, defaultProtocol } = this.options;
|
|
595
|
-
if (this.options.autolink) {
|
|
596
|
-
plugins.push(
|
|
597
|
-
autolink({
|
|
598
|
-
type: this.type,
|
|
599
|
-
defaultProtocol: this.options.defaultProtocol,
|
|
600
|
-
validate: (url) => this.options.isAllowedUri(url, {
|
|
601
|
-
defaultValidate: (href) => !!isAllowedUri(href, protocols),
|
|
602
|
-
protocols,
|
|
603
|
-
defaultProtocol
|
|
604
|
-
}),
|
|
605
|
-
shouldAutoLink: this.options.shouldAutoLink
|
|
606
|
-
})
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
plugins.push(
|
|
610
|
-
clickHandler({
|
|
611
|
-
type: this.type,
|
|
612
|
-
editor: this.editor,
|
|
613
|
-
openOnClick: this.options.openOnClick === "whenNotEditable" ? true : this.options.openOnClick,
|
|
614
|
-
enableClickSelection: this.options.enableClickSelection
|
|
615
|
-
})
|
|
616
|
-
);
|
|
617
|
-
if (this.options.linkOnPaste) {
|
|
618
|
-
plugins.push(
|
|
619
|
-
pasteHandler({
|
|
620
|
-
editor: this.editor,
|
|
621
|
-
defaultProtocol: this.options.defaultProtocol,
|
|
622
|
-
type: this.type,
|
|
623
|
-
shouldAutoLink: this.options.shouldAutoLink
|
|
624
|
-
})
|
|
625
|
-
);
|
|
626
|
-
}
|
|
627
|
-
return plugins;
|
|
628
|
-
}
|
|
292
|
+
/**
|
|
293
|
+
* This extension allows you to create links.
|
|
294
|
+
* @see https://www.tiptap.dev/api/marks/link
|
|
295
|
+
*/
|
|
296
|
+
const Link = Mark.create({
|
|
297
|
+
name: "link",
|
|
298
|
+
priority: 1e3,
|
|
299
|
+
keepOnSplit: false,
|
|
300
|
+
exitable: true,
|
|
301
|
+
onCreate() {
|
|
302
|
+
if (this.options.validate && !this.options.shouldAutoLink) {
|
|
303
|
+
this.options.shouldAutoLink = this.options.validate;
|
|
304
|
+
console.warn("The `validate` option is deprecated. Rename to the `shouldAutoLink` option instead.");
|
|
305
|
+
}
|
|
306
|
+
this.options.protocols.forEach((protocol) => {
|
|
307
|
+
if (typeof protocol === "string") {
|
|
308
|
+
registerCustomProtocol(protocol);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
registerCustomProtocol(protocol.scheme, protocol.optionalSlashes);
|
|
312
|
+
});
|
|
313
|
+
},
|
|
314
|
+
onDestroy() {
|
|
315
|
+
reset();
|
|
316
|
+
},
|
|
317
|
+
inclusive() {
|
|
318
|
+
return this.options.autolink;
|
|
319
|
+
},
|
|
320
|
+
addOptions() {
|
|
321
|
+
return {
|
|
322
|
+
openOnClick: true,
|
|
323
|
+
enableClickSelection: false,
|
|
324
|
+
linkOnPaste: true,
|
|
325
|
+
markdownLinks: false,
|
|
326
|
+
autolink: true,
|
|
327
|
+
protocols: [],
|
|
328
|
+
defaultProtocol: "http",
|
|
329
|
+
HTMLAttributes: {
|
|
330
|
+
target: "_blank",
|
|
331
|
+
rel: "noopener noreferrer nofollow",
|
|
332
|
+
class: null
|
|
333
|
+
},
|
|
334
|
+
isAllowedUri: (url, ctx) => !!isAllowedUri(url, ctx.protocols),
|
|
335
|
+
validate: (url) => !!url,
|
|
336
|
+
shouldAutoLink: (url) => {
|
|
337
|
+
const hasProtocol = /^[a-z][a-z0-9+.-]*:\/\//i.test(url);
|
|
338
|
+
const hasMaybeProtocol = /^[a-z][a-z0-9+.-]*:/i.test(url);
|
|
339
|
+
if (hasProtocol || hasMaybeProtocol && !url.includes("@")) return true;
|
|
340
|
+
const hostname = (url.includes("@") ? url.split("@").pop() : url).split(/[/?#:]/)[0];
|
|
341
|
+
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) return false;
|
|
342
|
+
if (!/\./.test(hostname)) return false;
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
},
|
|
347
|
+
addAttributes() {
|
|
348
|
+
var _this$options$HTMLAtt, _this$options$HTMLAtt2, _this$options$HTMLAtt3;
|
|
349
|
+
return {
|
|
350
|
+
href: {
|
|
351
|
+
default: null,
|
|
352
|
+
parseHTML(element) {
|
|
353
|
+
return element.getAttribute("href");
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
target: { default: (_this$options$HTMLAtt = this.options.HTMLAttributes.target) !== null && _this$options$HTMLAtt !== void 0 ? _this$options$HTMLAtt : null },
|
|
357
|
+
rel: { default: (_this$options$HTMLAtt2 = this.options.HTMLAttributes.rel) !== null && _this$options$HTMLAtt2 !== void 0 ? _this$options$HTMLAtt2 : null },
|
|
358
|
+
class: { default: (_this$options$HTMLAtt3 = this.options.HTMLAttributes.class) !== null && _this$options$HTMLAtt3 !== void 0 ? _this$options$HTMLAtt3 : null },
|
|
359
|
+
title: { default: null }
|
|
360
|
+
};
|
|
361
|
+
},
|
|
362
|
+
parseHTML() {
|
|
363
|
+
return [{
|
|
364
|
+
tag: "a[href]",
|
|
365
|
+
getAttrs: (dom) => {
|
|
366
|
+
const href = dom.getAttribute("href");
|
|
367
|
+
if (!href || !this.options.isAllowedUri(href, {
|
|
368
|
+
defaultValidate: (url) => !!isAllowedUri(url, this.options.protocols),
|
|
369
|
+
protocols: this.options.protocols,
|
|
370
|
+
defaultProtocol: this.options.defaultProtocol
|
|
371
|
+
})) return false;
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
}];
|
|
375
|
+
},
|
|
376
|
+
renderHTML({ HTMLAttributes }) {
|
|
377
|
+
if (!this.options.isAllowedUri(HTMLAttributes.href, {
|
|
378
|
+
defaultValidate: (href) => !!isAllowedUri(href, this.options.protocols),
|
|
379
|
+
protocols: this.options.protocols,
|
|
380
|
+
defaultProtocol: this.options.defaultProtocol
|
|
381
|
+
})) return [
|
|
382
|
+
"a",
|
|
383
|
+
mergeAttributes(this.options.HTMLAttributes, {
|
|
384
|
+
...HTMLAttributes,
|
|
385
|
+
href: ""
|
|
386
|
+
}),
|
|
387
|
+
0
|
|
388
|
+
];
|
|
389
|
+
return [
|
|
390
|
+
"a",
|
|
391
|
+
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
|
392
|
+
0
|
|
393
|
+
];
|
|
394
|
+
},
|
|
395
|
+
markdownTokenName: "link",
|
|
396
|
+
parseMarkdown: (token, helpers) => {
|
|
397
|
+
return helpers.applyMark("link", helpers.parseInline(token.tokens || []), {
|
|
398
|
+
href: token.href,
|
|
399
|
+
title: token.title || null
|
|
400
|
+
});
|
|
401
|
+
},
|
|
402
|
+
renderMarkdown: (node, h) => {
|
|
403
|
+
var _node$attrs$href, _node$attrs, _node$attrs$title, _node$attrs2;
|
|
404
|
+
const href = (_node$attrs$href = (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.href) !== null && _node$attrs$href !== void 0 ? _node$attrs$href : "";
|
|
405
|
+
const title = (_node$attrs$title = (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.title) !== null && _node$attrs$title !== void 0 ? _node$attrs$title : "";
|
|
406
|
+
const text = h.renderChildren(node);
|
|
407
|
+
return title ? `[${text}](${href} "${title}")` : `[${text}](${href})`;
|
|
408
|
+
},
|
|
409
|
+
addCommands() {
|
|
410
|
+
return {
|
|
411
|
+
setLink: (attributes) => ({ chain }) => {
|
|
412
|
+
const { href } = attributes;
|
|
413
|
+
if (!this.options.isAllowedUri(href, {
|
|
414
|
+
defaultValidate: (url) => !!isAllowedUri(url, this.options.protocols),
|
|
415
|
+
protocols: this.options.protocols,
|
|
416
|
+
defaultProtocol: this.options.defaultProtocol
|
|
417
|
+
})) return false;
|
|
418
|
+
return chain().setMark(this.name, attributes).setMeta("preventAutolink", true).run();
|
|
419
|
+
},
|
|
420
|
+
toggleLink: (attributes) => ({ chain }) => {
|
|
421
|
+
const { href } = attributes || {};
|
|
422
|
+
if (href && !this.options.isAllowedUri(href, {
|
|
423
|
+
defaultValidate: (url) => !!isAllowedUri(url, this.options.protocols),
|
|
424
|
+
protocols: this.options.protocols,
|
|
425
|
+
defaultProtocol: this.options.defaultProtocol
|
|
426
|
+
})) return false;
|
|
427
|
+
return chain().toggleMark(this.name, attributes, { extendEmptyMarkRange: true }).setMeta("preventAutolink", true).run();
|
|
428
|
+
},
|
|
429
|
+
unsetLink: () => ({ chain }) => {
|
|
430
|
+
return chain().unsetMark(this.name, { extendEmptyMarkRange: true }).setMeta("preventAutolink", true).run();
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
},
|
|
434
|
+
addInputRules() {
|
|
435
|
+
if (!this.options.markdownLinks) return [];
|
|
436
|
+
return [markdownLinkInputRule({
|
|
437
|
+
type: this.type,
|
|
438
|
+
isAllowedHref: (href) => this.options.isAllowedUri(href, {
|
|
439
|
+
defaultValidate: (url) => !!isAllowedUri(url, this.options.protocols),
|
|
440
|
+
protocols: this.options.protocols,
|
|
441
|
+
defaultProtocol: this.options.defaultProtocol
|
|
442
|
+
})
|
|
443
|
+
})];
|
|
444
|
+
},
|
|
445
|
+
addPasteRules() {
|
|
446
|
+
const findPlainUrls = (text) => {
|
|
447
|
+
const foundLinks = [];
|
|
448
|
+
if (text) {
|
|
449
|
+
const { protocols, defaultProtocol } = this.options;
|
|
450
|
+
find(text).filter((item) => item.isLink && this.options.isAllowedUri(item.value, {
|
|
451
|
+
defaultValidate: (href) => !!isAllowedUri(href, protocols),
|
|
452
|
+
protocols,
|
|
453
|
+
defaultProtocol
|
|
454
|
+
})).forEach((link) => {
|
|
455
|
+
if (!this.options.shouldAutoLink(link.value)) return;
|
|
456
|
+
foundLinks.push({
|
|
457
|
+
text: link.value,
|
|
458
|
+
data: { href: link.href },
|
|
459
|
+
index: link.start
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
return foundLinks;
|
|
464
|
+
};
|
|
465
|
+
if (this.options.markdownLinks) return [markdownLinkPasteRule({
|
|
466
|
+
type: this.type,
|
|
467
|
+
isAllowedHref: (href) => this.options.isAllowedUri(href, {
|
|
468
|
+
defaultValidate: (url) => !!isAllowedUri(url, this.options.protocols),
|
|
469
|
+
protocols: this.options.protocols,
|
|
470
|
+
defaultProtocol: this.options.defaultProtocol
|
|
471
|
+
}),
|
|
472
|
+
findPlainUrls
|
|
473
|
+
})];
|
|
474
|
+
return [markPasteRule({
|
|
475
|
+
find: findPlainUrls,
|
|
476
|
+
type: this.type,
|
|
477
|
+
getAttributes: (match) => {
|
|
478
|
+
var _match$data;
|
|
479
|
+
return { href: (_match$data = match.data) === null || _match$data === void 0 ? void 0 : _match$data.href };
|
|
480
|
+
}
|
|
481
|
+
})];
|
|
482
|
+
},
|
|
483
|
+
addProseMirrorPlugins() {
|
|
484
|
+
const plugins = [];
|
|
485
|
+
const { protocols, defaultProtocol } = this.options;
|
|
486
|
+
if (this.options.autolink) plugins.push(autolink({
|
|
487
|
+
type: this.type,
|
|
488
|
+
defaultProtocol: this.options.defaultProtocol,
|
|
489
|
+
validate: (url) => this.options.isAllowedUri(url, {
|
|
490
|
+
defaultValidate: (href) => !!isAllowedUri(href, protocols),
|
|
491
|
+
protocols,
|
|
492
|
+
defaultProtocol
|
|
493
|
+
}),
|
|
494
|
+
shouldAutoLink: this.options.shouldAutoLink
|
|
495
|
+
}));
|
|
496
|
+
plugins.push(clickHandler({
|
|
497
|
+
type: this.type,
|
|
498
|
+
editor: this.editor,
|
|
499
|
+
openOnClick: this.options.openOnClick === "whenNotEditable" ? true : this.options.openOnClick,
|
|
500
|
+
enableClickSelection: this.options.enableClickSelection
|
|
501
|
+
}));
|
|
502
|
+
if (this.options.linkOnPaste) plugins.push(pasteHandler({
|
|
503
|
+
editor: this.editor,
|
|
504
|
+
defaultProtocol: this.options.defaultProtocol,
|
|
505
|
+
type: this.type,
|
|
506
|
+
shouldAutoLink: this.options.shouldAutoLink
|
|
507
|
+
}));
|
|
508
|
+
return plugins;
|
|
509
|
+
}
|
|
629
510
|
});
|
|
511
|
+
//#endregion
|
|
512
|
+
//#region src/index.ts
|
|
513
|
+
var src_default = Link;
|
|
514
|
+
//#endregion
|
|
515
|
+
export { Link, src_default as default, isAllowedUri, pasteRegex };
|
|
630
516
|
|
|
631
|
-
// src/index.ts
|
|
632
|
-
var index_default = Link;
|
|
633
|
-
export {
|
|
634
|
-
Link,
|
|
635
|
-
index_default as default,
|
|
636
|
-
isAllowedUri,
|
|
637
|
-
pasteRegex
|
|
638
|
-
};
|
|
639
517
|
//# sourceMappingURL=index.js.map
|