markupeditor 0.9.53 → 0.9.55
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/markup-editor.js +101 -60
- package/package.json +1 -1
package/dist/markup-editor.js
CHANGED
|
@@ -35519,17 +35519,11 @@ function _getMarkTypes() {
|
|
|
35519
35519
|
*/
|
|
35520
35520
|
function getLinkAttributes() {
|
|
35521
35521
|
const view = activeView();
|
|
35522
|
-
const
|
|
35523
|
-
|
|
35524
|
-
|
|
35525
|
-
if (
|
|
35526
|
-
}
|
|
35527
|
-
const selectedNode = (selectedNodes.length === 1) && selectedNodes[0];
|
|
35528
|
-
if (selectedNode) {
|
|
35529
|
-
const linkMarks = selectedNode.marks.filter(mark => mark.type === schema.marks.link);
|
|
35530
|
-
if (linkMarks.length === 1) {
|
|
35531
|
-
return {href: linkMarks[0].attrs.href, link: selectedNode.text};
|
|
35532
|
-
} } // A linked image carries the link mark on the image node, which has no text to report as the link.
|
|
35522
|
+
const link = linkRangeAt(view.state);
|
|
35523
|
+
if (link) {
|
|
35524
|
+
const text = view.state.doc.textBetween(link.from, link.to);
|
|
35525
|
+
if (text) return {href: link.mark.attrs.href, link: text};
|
|
35526
|
+
} // A linked image carries the link mark on the image node, which has no text to report as the link.
|
|
35533
35527
|
const image = _selectedImageNode(view.state);
|
|
35534
35528
|
if (image) {
|
|
35535
35529
|
const linkMarks = image.marks.filter(mark => mark.type === schema.marks.link);
|
|
@@ -36182,6 +36176,9 @@ function testCodeLanguageTabInfo() {
|
|
|
36182
36176
|
* Insert a link to url. When the selection is collapsed, the url is inserted
|
|
36183
36177
|
* at the selection point as a link.
|
|
36184
36178
|
*
|
|
36179
|
+
* When the selection is in an existing link, or contains one, the href of the whole link is
|
|
36180
|
+
* changed to url and its text is left alone.
|
|
36181
|
+
*
|
|
36185
36182
|
* When done, leave the link selected.
|
|
36186
36183
|
*
|
|
36187
36184
|
* @param {string} url The url/href to use for the link
|
|
@@ -36196,25 +36193,94 @@ function insertLinkCommand(url) {
|
|
|
36196
36193
|
const commandAdapter = (state, dispatch, view) => {
|
|
36197
36194
|
const selection = state.selection;
|
|
36198
36195
|
const linkMark = state.schema.marks.link.create({ href: url });
|
|
36199
|
-
|
|
36196
|
+
const transaction = state.tr;
|
|
36197
|
+
if (!addLinkMark(transaction, state, linkMark)) {
|
|
36198
|
+
// A collapsed selection outside any link
|
|
36200
36199
|
const textNode = state.schema.text(url).mark([linkMark]);
|
|
36201
|
-
|
|
36200
|
+
transaction.replaceSelectionWith(textNode, false);
|
|
36202
36201
|
const linkSelection = TextSelection.create(transaction.doc, selection.from, selection.from + textNode.nodeSize);
|
|
36203
36202
|
transaction.setSelection(linkSelection);
|
|
36204
|
-
dispatch(transaction);
|
|
36205
|
-
stateChanged(view);
|
|
36206
|
-
} else {
|
|
36207
|
-
const toggle = toggleMark(linkMark.type, linkMark.attrs);
|
|
36208
|
-
if (toggle) {
|
|
36209
|
-
toggle(state, dispatch);
|
|
36210
|
-
stateChanged(view);
|
|
36211
|
-
}
|
|
36212
36203
|
}
|
|
36204
|
+
dispatch(transaction);
|
|
36205
|
+
stateChanged(view);
|
|
36213
36206
|
return true;
|
|
36214
36207
|
};
|
|
36215
36208
|
return commandAdapter;
|
|
36216
36209
|
}
|
|
36217
36210
|
|
|
36211
|
+
/**
|
|
36212
|
+
* Add `linkMark` to `transaction`, replacing the href of the link the selection is in if there is one.
|
|
36213
|
+
*
|
|
36214
|
+
* The selection can be anywhere within a link or contain the full link. A link that spans several
|
|
36215
|
+
* text nodes, because part of it has other marks, is changed as a whole.
|
|
36216
|
+
*
|
|
36217
|
+
* @ignore
|
|
36218
|
+
* @param {Transaction} transaction
|
|
36219
|
+
* @param {EditorState} state The state `transaction` was created from
|
|
36220
|
+
* @param {Mark} linkMark
|
|
36221
|
+
* @returns {boolean} False, with `transaction` unchanged, when the selection is collapsed and not in a link
|
|
36222
|
+
*/
|
|
36223
|
+
function addLinkMark(transaction, state, linkMark) {
|
|
36224
|
+
const range = linkRangeAt(state);
|
|
36225
|
+
if (range) {
|
|
36226
|
+
transaction.addMark(range.from, range.to, linkMark);
|
|
36227
|
+
const {from, to} = state.selection;
|
|
36228
|
+
if (range.from !== from || range.to !== to) {
|
|
36229
|
+
transaction.setSelection(TextSelection.create(transaction.doc, range.from, range.to));
|
|
36230
|
+
} return true;
|
|
36231
|
+
} if (state.selection.empty) return false;
|
|
36232
|
+
state.selection.ranges.forEach(range => transaction.addMark(range.$from.pos, range.$to.pos, linkMark));
|
|
36233
|
+
return true;
|
|
36234
|
+
}
|
|
36235
|
+
|
|
36236
|
+
/**
|
|
36237
|
+
* The extent of the link the selection is in, or null if it is not in one.
|
|
36238
|
+
*
|
|
36239
|
+
* Every inline node in the selection has to carry the same link, and a collapsed selection has to be inside the
|
|
36240
|
+
* link, not at its edge.
|
|
36241
|
+
*
|
|
36242
|
+
* @ignore
|
|
36243
|
+
* @param {EditorState} state
|
|
36244
|
+
* @returns {{from: number, to: number, mark: Mark}|null}
|
|
36245
|
+
*/
|
|
36246
|
+
function linkRangeAt(state) {
|
|
36247
|
+
const linkType = state.schema.marks.link;
|
|
36248
|
+
const {from, to, $from, $to} = state.selection;
|
|
36249
|
+
if (!$from.sameParent($to)) return null;
|
|
36250
|
+
let target = null;
|
|
36251
|
+
let uniform = true;
|
|
36252
|
+
const check = node => {
|
|
36253
|
+
const mark = linkType.isInSet(node.marks);
|
|
36254
|
+
if (!mark || (target && !target.eq(mark))) uniform = false;
|
|
36255
|
+
else target = mark;
|
|
36256
|
+
};
|
|
36257
|
+
if (state.selection.empty) {
|
|
36258
|
+
// A collapsed selection is in a link when the same link is on both sides of it, including between its text nodes
|
|
36259
|
+
if (!$from.nodeBefore || !$from.nodeAfter) return null;
|
|
36260
|
+
check($from.nodeBefore);
|
|
36261
|
+
check($from.nodeAfter);
|
|
36262
|
+
} else {
|
|
36263
|
+
state.doc.nodesBetween(from, to, node => {
|
|
36264
|
+
if (!node.isInline) return true;
|
|
36265
|
+
check(node);
|
|
36266
|
+
return false;
|
|
36267
|
+
});
|
|
36268
|
+
} if (!uniform || !target) return null;
|
|
36269
|
+
// Widen to the contiguous children carrying the same link
|
|
36270
|
+
const base = $from.start();
|
|
36271
|
+
const spans = [];
|
|
36272
|
+
$from.parent.forEach((child, offset) => {
|
|
36273
|
+
spans.push({start: base + offset, end: base + offset + child.nodeSize, linked: !!target.isInSet(child.marks)});
|
|
36274
|
+
});
|
|
36275
|
+
const collapsed = state.selection.empty;
|
|
36276
|
+
let first = spans.findIndex(span => collapsed ? span.end >= from : span.end > from);
|
|
36277
|
+
let last = spans.findLastIndex(span => collapsed ? span.start <= to : span.start < to);
|
|
36278
|
+
if (first < 0 || last < first) return null;
|
|
36279
|
+
while (first > 0 && spans[first - 1].linked) first--;
|
|
36280
|
+
while (last < spans.length - 1 && spans[last + 1].linked) last++;
|
|
36281
|
+
return {from: spans[first].start, to: spans[last].end, mark: target};
|
|
36282
|
+
}
|
|
36283
|
+
|
|
36218
36284
|
/**
|
|
36219
36285
|
* Insert a link to the header identified by hTag ('H1'-'H6') and index (its position among
|
|
36220
36286
|
* headers with that tag), assigning the header a unique id if it doesn't already have one.
|
|
@@ -36237,27 +36303,16 @@ function insertInternalLinkCommand(hTag, index) {
|
|
|
36237
36303
|
let id = idForHeader(node, state);
|
|
36238
36304
|
// Same transaction as the mark insertion below, so both land as one undo step.
|
|
36239
36305
|
let tr = state.tr.setNodeMarkup(pos, undefined, { ...node.attrs, id });
|
|
36240
|
-
const selection = state.selection;
|
|
36241
36306
|
const linkMark = state.schema.marks.link.create({ href: '#' + id });
|
|
36242
|
-
if (
|
|
36307
|
+
if (!addLinkMark(tr, state, linkMark)) {
|
|
36243
36308
|
// In case of an empty selection, insert the textContent of the header and then use
|
|
36244
36309
|
// that to link-to the header
|
|
36245
36310
|
const textNode = state.schema.text(node.textContent, [linkMark]);
|
|
36246
36311
|
tr.replaceSelectionWith(textNode, false);
|
|
36247
|
-
dispatch(tr);
|
|
36248
|
-
stateChanged(view);
|
|
36249
|
-
return true;
|
|
36250
|
-
} else {
|
|
36251
|
-
// Capture toggleMark's steps rather than let it dispatch, then replay them onto
|
|
36252
|
-
// `tr` (setNodeMarkup doesn't shift positions, so they apply cleanly).
|
|
36253
|
-
let captured = null;
|
|
36254
|
-
toggleMark(linkMark.type, linkMark.attrs)(state, t => { captured = t; });
|
|
36255
|
-
if (!captured) return false;
|
|
36256
|
-
for (const step of captured.steps) tr.step(step);
|
|
36257
|
-
dispatch(tr);
|
|
36258
|
-
stateChanged(view);
|
|
36259
|
-
return true;
|
|
36260
36312
|
}
|
|
36313
|
+
dispatch(tr);
|
|
36314
|
+
stateChanged(view);
|
|
36315
|
+
return true;
|
|
36261
36316
|
};
|
|
36262
36317
|
return commandAdapter;
|
|
36263
36318
|
}
|
|
@@ -36445,29 +36500,14 @@ function deleteLinkCommand() {
|
|
|
36445
36500
|
}
|
|
36446
36501
|
|
|
36447
36502
|
function selectFullLink(view) {
|
|
36448
|
-
|
|
36449
|
-
|
|
36450
|
-
|
|
36451
|
-
|
|
36452
|
-
const
|
|
36453
|
-
|
|
36454
|
-
|
|
36455
|
-
|
|
36456
|
-
return false;
|
|
36457
|
-
} return true;
|
|
36458
|
-
});
|
|
36459
|
-
if (nodePos.length !== 1) return;
|
|
36460
|
-
const selectedNode = nodePos[0].node;
|
|
36461
|
-
const selectedPos = nodePos[0].pos;
|
|
36462
|
-
const linkMarks = selectedNode && selectedNode.marks.filter(mark => mark.type === linkType);
|
|
36463
|
-
if (linkMarks.length !== 1) return;
|
|
36464
|
-
|
|
36465
|
-
// Select the entire text of selectedNode
|
|
36466
|
-
const anchor = selectedPos;
|
|
36467
|
-
const head = anchor + selectedNode.nodeSize;
|
|
36468
|
-
const linkSelection = TextSelection.create(view.state.doc, anchor, head);
|
|
36469
|
-
const transaction = view.state.tr.setSelection(linkSelection);
|
|
36470
|
-
view.dispatch(transaction);
|
|
36503
|
+
// A node selection (a linked image, for example) is left alone
|
|
36504
|
+
if (view.state.selection instanceof NodeSelection) return;
|
|
36505
|
+
const range = linkRangeAt(view.state);
|
|
36506
|
+
if (!range) return;
|
|
36507
|
+
const {from, to} = view.state.selection;
|
|
36508
|
+
if (range.from === from && range.to === to) return;
|
|
36509
|
+
const linkSelection = TextSelection.create(view.state.doc, range.from, range.to);
|
|
36510
|
+
view.dispatch(view.state.tr.setSelection(linkSelection));
|
|
36471
36511
|
}
|
|
36472
36512
|
|
|
36473
36513
|
/********************************************************************************
|
|
@@ -40806,7 +40846,8 @@ function baseKeyString(itemName, keymap) {
|
|
|
40806
40846
|
keyString = keyString.replaceAll('Shift', '\u21E7'); // ⇧
|
|
40807
40847
|
keyString = keyString.replaceAll('Alt', '\u2325'); // ⌥
|
|
40808
40848
|
keyString = keyString.replaceAll('-', '');
|
|
40809
|
-
|
|
40849
|
+
// Show a letter key in capitals, as the menu bar does
|
|
40850
|
+
return keyString.replace(/(^|[\u2318\u2303\u21E7\u2325])([a-z])$/, (_, lead, letter) => lead + letter.toUpperCase())
|
|
40810
40851
|
}
|
|
40811
40852
|
|
|
40812
40853
|
/**
|