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