10598 lines
357 KiB
Plaintext
10598 lines
357 KiB
Plaintext
import {
|
||
AllSelection,
|
||
DOMParser,
|
||
DOMSerializer,
|
||
EditorState,
|
||
Fragment,
|
||
Mark,
|
||
Node,
|
||
NodeRange,
|
||
NodeSelection,
|
||
Plugin,
|
||
PluginKey,
|
||
ReplaceAroundStep,
|
||
ReplaceStep,
|
||
Schema,
|
||
Selection,
|
||
Slice,
|
||
TextSelection,
|
||
Transform,
|
||
canJoin,
|
||
canSplit,
|
||
dropPoint,
|
||
findWrapping,
|
||
joinPoint,
|
||
liftTarget,
|
||
replaceStep
|
||
} from "/_nuxt/node_modules/.cache/vite/client/deps/chunk-B5J3BDGG.js?v=e4f18c29";
|
||
|
||
// node_modules/prosemirror-view/dist/index.js
|
||
var domIndex = function(node) {
|
||
for (var index2 = 0; ; index2++) {
|
||
node = node.previousSibling;
|
||
if (!node)
|
||
return index2;
|
||
}
|
||
};
|
||
var parentNode = function(node) {
|
||
let parent = node.assignedSlot || node.parentNode;
|
||
return parent && parent.nodeType == 11 ? parent.host : parent;
|
||
};
|
||
var reusedRange = null;
|
||
var textRange = function(node, from, to) {
|
||
let range = reusedRange || (reusedRange = document.createRange());
|
||
range.setEnd(node, to == null ? node.nodeValue.length : to);
|
||
range.setStart(node, from || 0);
|
||
return range;
|
||
};
|
||
var clearReusedRange = function() {
|
||
reusedRange = null;
|
||
};
|
||
var isEquivalentPosition = function(node, off, targetNode, targetOff) {
|
||
return targetNode && (scanFor(node, off, targetNode, targetOff, -1) || scanFor(node, off, targetNode, targetOff, 1));
|
||
};
|
||
var atomElements = /^(img|br|input|textarea|hr)$/i;
|
||
function scanFor(node, off, targetNode, targetOff, dir) {
|
||
for (; ; ) {
|
||
if (node == targetNode && off == targetOff)
|
||
return true;
|
||
if (off == (dir < 0 ? 0 : nodeSize(node))) {
|
||
let parent = node.parentNode;
|
||
if (!parent || parent.nodeType != 1 || hasBlockDesc(node) || atomElements.test(node.nodeName) || node.contentEditable == "false")
|
||
return false;
|
||
off = domIndex(node) + (dir < 0 ? 0 : 1);
|
||
node = parent;
|
||
} else if (node.nodeType == 1) {
|
||
node = node.childNodes[off + (dir < 0 ? -1 : 0)];
|
||
if (node.contentEditable == "false")
|
||
return false;
|
||
off = dir < 0 ? nodeSize(node) : 0;
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
function nodeSize(node) {
|
||
return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
|
||
}
|
||
function textNodeBefore$1(node, offset) {
|
||
for (; ; ) {
|
||
if (node.nodeType == 3 && offset)
|
||
return node;
|
||
if (node.nodeType == 1 && offset > 0) {
|
||
if (node.contentEditable == "false")
|
||
return null;
|
||
node = node.childNodes[offset - 1];
|
||
offset = nodeSize(node);
|
||
} else if (node.parentNode && !hasBlockDesc(node)) {
|
||
offset = domIndex(node);
|
||
node = node.parentNode;
|
||
} else {
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
function textNodeAfter$1(node, offset) {
|
||
for (; ; ) {
|
||
if (node.nodeType == 3 && offset < node.nodeValue.length)
|
||
return node;
|
||
if (node.nodeType == 1 && offset < node.childNodes.length) {
|
||
if (node.contentEditable == "false")
|
||
return null;
|
||
node = node.childNodes[offset];
|
||
offset = 0;
|
||
} else if (node.parentNode && !hasBlockDesc(node)) {
|
||
offset = domIndex(node) + 1;
|
||
node = node.parentNode;
|
||
} else {
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
function isOnEdge(node, offset, parent) {
|
||
for (let atStart = offset == 0, atEnd = offset == nodeSize(node); atStart || atEnd; ) {
|
||
if (node == parent)
|
||
return true;
|
||
let index2 = domIndex(node);
|
||
node = node.parentNode;
|
||
if (!node)
|
||
return false;
|
||
atStart = atStart && index2 == 0;
|
||
atEnd = atEnd && index2 == nodeSize(node);
|
||
}
|
||
}
|
||
function hasBlockDesc(dom) {
|
||
let desc;
|
||
for (let cur = dom; cur; cur = cur.parentNode)
|
||
if (desc = cur.pmViewDesc)
|
||
break;
|
||
return desc && desc.node && desc.node.isBlock && (desc.dom == dom || desc.contentDOM == dom);
|
||
}
|
||
var selectionCollapsed = function(domSel) {
|
||
return domSel.focusNode && isEquivalentPosition(domSel.focusNode, domSel.focusOffset, domSel.anchorNode, domSel.anchorOffset);
|
||
};
|
||
function keyEvent(keyCode, key) {
|
||
let event = document.createEvent("Event");
|
||
event.initEvent("keydown", true, true);
|
||
event.keyCode = keyCode;
|
||
event.key = event.code = key;
|
||
return event;
|
||
}
|
||
function deepActiveElement(doc2) {
|
||
let elt = doc2.activeElement;
|
||
while (elt && elt.shadowRoot)
|
||
elt = elt.shadowRoot.activeElement;
|
||
return elt;
|
||
}
|
||
function caretFromPoint(doc2, x, y) {
|
||
if (doc2.caretPositionFromPoint) {
|
||
try {
|
||
let pos = doc2.caretPositionFromPoint(x, y);
|
||
if (pos)
|
||
return { node: pos.offsetNode, offset: Math.min(nodeSize(pos.offsetNode), pos.offset) };
|
||
} catch (_) {
|
||
}
|
||
}
|
||
if (doc2.caretRangeFromPoint) {
|
||
let range = doc2.caretRangeFromPoint(x, y);
|
||
if (range)
|
||
return { node: range.startContainer, offset: Math.min(nodeSize(range.startContainer), range.startOffset) };
|
||
}
|
||
}
|
||
var nav = typeof navigator != "undefined" ? navigator : null;
|
||
var doc = typeof document != "undefined" ? document : null;
|
||
var agent = nav && nav.userAgent || "";
|
||
var ie_edge = /Edge\/(\d+)/.exec(agent);
|
||
var ie_upto10 = /MSIE \d/.exec(agent);
|
||
var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(agent);
|
||
var ie = !!(ie_upto10 || ie_11up || ie_edge);
|
||
var ie_version = ie_upto10 ? document.documentMode : ie_11up ? +ie_11up[1] : ie_edge ? +ie_edge[1] : 0;
|
||
var gecko = !ie && /gecko\/(\d+)/i.test(agent);
|
||
gecko && +(/Firefox\/(\d+)/.exec(agent) || [0, 0])[1];
|
||
var _chrome = !ie && /Chrome\/(\d+)/.exec(agent);
|
||
var chrome = !!_chrome;
|
||
var chrome_version = _chrome ? +_chrome[1] : 0;
|
||
var safari = !ie && !!nav && /Apple Computer/.test(nav.vendor);
|
||
var ios = safari && (/Mobile\/\w+/.test(agent) || !!nav && nav.maxTouchPoints > 2);
|
||
var mac = ios || (nav ? /Mac/.test(nav.platform) : false);
|
||
var windows = nav ? /Win/.test(nav.platform) : false;
|
||
var android = /Android \d/.test(agent);
|
||
var webkit = !!doc && "webkitFontSmoothing" in doc.documentElement.style;
|
||
var webkit_version = webkit ? +(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent) || [0, 0])[1] : 0;
|
||
function windowRect(doc2) {
|
||
let vp = doc2.defaultView && doc2.defaultView.visualViewport;
|
||
if (vp)
|
||
return {
|
||
left: 0,
|
||
right: vp.width,
|
||
top: 0,
|
||
bottom: vp.height
|
||
};
|
||
return {
|
||
left: 0,
|
||
right: doc2.documentElement.clientWidth,
|
||
top: 0,
|
||
bottom: doc2.documentElement.clientHeight
|
||
};
|
||
}
|
||
function getSide(value, side) {
|
||
return typeof value == "number" ? value : value[side];
|
||
}
|
||
function clientRect(node) {
|
||
let rect = node.getBoundingClientRect();
|
||
let scaleX = rect.width / node.offsetWidth || 1;
|
||
let scaleY = rect.height / node.offsetHeight || 1;
|
||
return {
|
||
left: rect.left,
|
||
right: rect.left + node.clientWidth * scaleX,
|
||
top: rect.top,
|
||
bottom: rect.top + node.clientHeight * scaleY
|
||
};
|
||
}
|
||
function scrollRectIntoView(view, rect, startDOM) {
|
||
let scrollThreshold = view.someProp("scrollThreshold") || 0, scrollMargin = view.someProp("scrollMargin") || 5;
|
||
let doc2 = view.dom.ownerDocument;
|
||
for (let parent = startDOM || view.dom; ; parent = parentNode(parent)) {
|
||
if (!parent)
|
||
break;
|
||
if (parent.nodeType != 1)
|
||
continue;
|
||
let elt = parent;
|
||
let atTop = elt == doc2.body;
|
||
let bounding = atTop ? windowRect(doc2) : clientRect(elt);
|
||
let moveX = 0, moveY = 0;
|
||
if (rect.top < bounding.top + getSide(scrollThreshold, "top"))
|
||
moveY = -(bounding.top - rect.top + getSide(scrollMargin, "top"));
|
||
else if (rect.bottom > bounding.bottom - getSide(scrollThreshold, "bottom"))
|
||
moveY = rect.bottom - rect.top > bounding.bottom - bounding.top ? rect.top + getSide(scrollMargin, "top") - bounding.top : rect.bottom - bounding.bottom + getSide(scrollMargin, "bottom");
|
||
if (rect.left < bounding.left + getSide(scrollThreshold, "left"))
|
||
moveX = -(bounding.left - rect.left + getSide(scrollMargin, "left"));
|
||
else if (rect.right > bounding.right - getSide(scrollThreshold, "right"))
|
||
moveX = rect.right - bounding.right + getSide(scrollMargin, "right");
|
||
if (moveX || moveY) {
|
||
if (atTop) {
|
||
doc2.defaultView.scrollBy(moveX, moveY);
|
||
} else {
|
||
let startX = elt.scrollLeft, startY = elt.scrollTop;
|
||
if (moveY)
|
||
elt.scrollTop += moveY;
|
||
if (moveX)
|
||
elt.scrollLeft += moveX;
|
||
let dX = elt.scrollLeft - startX, dY = elt.scrollTop - startY;
|
||
rect = { left: rect.left - dX, top: rect.top - dY, right: rect.right - dX, bottom: rect.bottom - dY };
|
||
}
|
||
}
|
||
if (atTop || /^(fixed|sticky)$/.test(getComputedStyle(parent).position))
|
||
break;
|
||
}
|
||
}
|
||
function storeScrollPos(view) {
|
||
let rect = view.dom.getBoundingClientRect(), startY = Math.max(0, rect.top);
|
||
let refDOM, refTop;
|
||
for (let x = (rect.left + rect.right) / 2, y = startY + 1; y < Math.min(innerHeight, rect.bottom); y += 5) {
|
||
let dom = view.root.elementFromPoint(x, y);
|
||
if (!dom || dom == view.dom || !view.dom.contains(dom))
|
||
continue;
|
||
let localRect = dom.getBoundingClientRect();
|
||
if (localRect.top >= startY - 20) {
|
||
refDOM = dom;
|
||
refTop = localRect.top;
|
||
break;
|
||
}
|
||
}
|
||
return { refDOM, refTop, stack: scrollStack(view.dom) };
|
||
}
|
||
function scrollStack(dom) {
|
||
let stack = [], doc2 = dom.ownerDocument;
|
||
for (let cur = dom; cur; cur = parentNode(cur)) {
|
||
stack.push({ dom: cur, top: cur.scrollTop, left: cur.scrollLeft });
|
||
if (dom == doc2)
|
||
break;
|
||
}
|
||
return stack;
|
||
}
|
||
function resetScrollPos({ refDOM, refTop, stack }) {
|
||
let newRefTop = refDOM ? refDOM.getBoundingClientRect().top : 0;
|
||
restoreScrollStack(stack, newRefTop == 0 ? 0 : newRefTop - refTop);
|
||
}
|
||
function restoreScrollStack(stack, dTop) {
|
||
for (let i = 0; i < stack.length; i++) {
|
||
let { dom, top, left } = stack[i];
|
||
if (dom.scrollTop != top + dTop)
|
||
dom.scrollTop = top + dTop;
|
||
if (dom.scrollLeft != left)
|
||
dom.scrollLeft = left;
|
||
}
|
||
}
|
||
var preventScrollSupported = null;
|
||
function focusPreventScroll(dom) {
|
||
if (dom.setActive)
|
||
return dom.setActive();
|
||
if (preventScrollSupported)
|
||
return dom.focus(preventScrollSupported);
|
||
let stored = scrollStack(dom);
|
||
dom.focus(preventScrollSupported == null ? {
|
||
get preventScroll() {
|
||
preventScrollSupported = { preventScroll: true };
|
||
return true;
|
||
}
|
||
} : void 0);
|
||
if (!preventScrollSupported) {
|
||
preventScrollSupported = false;
|
||
restoreScrollStack(stored, 0);
|
||
}
|
||
}
|
||
function findOffsetInNode(node, coords) {
|
||
let closest, dxClosest = 2e8, coordsClosest, offset = 0;
|
||
let rowBot = coords.top, rowTop = coords.top;
|
||
let firstBelow, coordsBelow;
|
||
for (let child = node.firstChild, childIndex = 0; child; child = child.nextSibling, childIndex++) {
|
||
let rects;
|
||
if (child.nodeType == 1)
|
||
rects = child.getClientRects();
|
||
else if (child.nodeType == 3)
|
||
rects = textRange(child).getClientRects();
|
||
else
|
||
continue;
|
||
for (let i = 0; i < rects.length; i++) {
|
||
let rect = rects[i];
|
||
if (rect.top <= rowBot && rect.bottom >= rowTop) {
|
||
rowBot = Math.max(rect.bottom, rowBot);
|
||
rowTop = Math.min(rect.top, rowTop);
|
||
let dx = rect.left > coords.left ? rect.left - coords.left : rect.right < coords.left ? coords.left - rect.right : 0;
|
||
if (dx < dxClosest) {
|
||
closest = child;
|
||
dxClosest = dx;
|
||
coordsClosest = dx && closest.nodeType == 3 ? {
|
||
left: rect.right < coords.left ? rect.right : rect.left,
|
||
top: coords.top
|
||
} : coords;
|
||
if (child.nodeType == 1 && dx)
|
||
offset = childIndex + (coords.left >= (rect.left + rect.right) / 2 ? 1 : 0);
|
||
continue;
|
||
}
|
||
} else if (rect.top > coords.top && !firstBelow && rect.left <= coords.left && rect.right >= coords.left) {
|
||
firstBelow = child;
|
||
coordsBelow = { left: Math.max(rect.left, Math.min(rect.right, coords.left)), top: rect.top };
|
||
}
|
||
if (!closest && (coords.left >= rect.right && coords.top >= rect.top || coords.left >= rect.left && coords.top >= rect.bottom))
|
||
offset = childIndex + 1;
|
||
}
|
||
}
|
||
if (!closest && firstBelow) {
|
||
closest = firstBelow;
|
||
coordsClosest = coordsBelow;
|
||
dxClosest = 0;
|
||
}
|
||
if (closest && closest.nodeType == 3)
|
||
return findOffsetInText(closest, coordsClosest);
|
||
if (!closest || dxClosest && closest.nodeType == 1)
|
||
return { node, offset };
|
||
return findOffsetInNode(closest, coordsClosest);
|
||
}
|
||
function findOffsetInText(node, coords) {
|
||
let len = node.nodeValue.length;
|
||
let range = document.createRange();
|
||
for (let i = 0; i < len; i++) {
|
||
range.setEnd(node, i + 1);
|
||
range.setStart(node, i);
|
||
let rect = singleRect(range, 1);
|
||
if (rect.top == rect.bottom)
|
||
continue;
|
||
if (inRect(coords, rect))
|
||
return { node, offset: i + (coords.left >= (rect.left + rect.right) / 2 ? 1 : 0) };
|
||
}
|
||
return { node, offset: 0 };
|
||
}
|
||
function inRect(coords, rect) {
|
||
return coords.left >= rect.left - 1 && coords.left <= rect.right + 1 && coords.top >= rect.top - 1 && coords.top <= rect.bottom + 1;
|
||
}
|
||
function targetKludge(dom, coords) {
|
||
let parent = dom.parentNode;
|
||
if (parent && /^li$/i.test(parent.nodeName) && coords.left < dom.getBoundingClientRect().left)
|
||
return parent;
|
||
return dom;
|
||
}
|
||
function posFromElement(view, elt, coords) {
|
||
let { node, offset } = findOffsetInNode(elt, coords), bias = -1;
|
||
if (node.nodeType == 1 && !node.firstChild) {
|
||
let rect = node.getBoundingClientRect();
|
||
bias = rect.left != rect.right && coords.left > (rect.left + rect.right) / 2 ? 1 : -1;
|
||
}
|
||
return view.docView.posFromDOM(node, offset, bias);
|
||
}
|
||
function posFromCaret(view, node, offset, coords) {
|
||
let outsideBlock = -1;
|
||
for (let cur = node, sawBlock = false; ; ) {
|
||
if (cur == view.dom)
|
||
break;
|
||
let desc = view.docView.nearestDesc(cur, true);
|
||
if (!desc)
|
||
return null;
|
||
if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent || !desc.contentDOM)) {
|
||
let rect = desc.dom.getBoundingClientRect();
|
||
if (desc.node.isBlock && desc.parent) {
|
||
if (!sawBlock && rect.left > coords.left || rect.top > coords.top)
|
||
outsideBlock = desc.posBefore;
|
||
else if (!sawBlock && rect.right < coords.left || rect.bottom < coords.top)
|
||
outsideBlock = desc.posAfter;
|
||
sawBlock = true;
|
||
}
|
||
if (!desc.contentDOM && outsideBlock < 0 && !desc.node.isText) {
|
||
let before = desc.node.isBlock ? coords.top < (rect.top + rect.bottom) / 2 : coords.left < (rect.left + rect.right) / 2;
|
||
return before ? desc.posBefore : desc.posAfter;
|
||
}
|
||
}
|
||
cur = desc.dom.parentNode;
|
||
}
|
||
return outsideBlock > -1 ? outsideBlock : view.docView.posFromDOM(node, offset, -1);
|
||
}
|
||
function elementFromPoint(element, coords, box) {
|
||
let len = element.childNodes.length;
|
||
if (len && box.top < box.bottom) {
|
||
for (let startI = Math.max(0, Math.min(len - 1, Math.floor(len * (coords.top - box.top) / (box.bottom - box.top)) - 2)), i = startI; ; ) {
|
||
let child = element.childNodes[i];
|
||
if (child.nodeType == 1) {
|
||
let rects = child.getClientRects();
|
||
for (let j = 0; j < rects.length; j++) {
|
||
let rect = rects[j];
|
||
if (inRect(coords, rect))
|
||
return elementFromPoint(child, coords, rect);
|
||
}
|
||
}
|
||
if ((i = (i + 1) % len) == startI)
|
||
break;
|
||
}
|
||
}
|
||
return element;
|
||
}
|
||
function posAtCoords(view, coords) {
|
||
let doc2 = view.dom.ownerDocument, node, offset = 0;
|
||
let caret = caretFromPoint(doc2, coords.left, coords.top);
|
||
if (caret)
|
||
({ node, offset } = caret);
|
||
let elt = (view.root.elementFromPoint ? view.root : doc2).elementFromPoint(coords.left, coords.top);
|
||
let pos;
|
||
if (!elt || !view.dom.contains(elt.nodeType != 1 ? elt.parentNode : elt)) {
|
||
let box = view.dom.getBoundingClientRect();
|
||
if (!inRect(coords, box))
|
||
return null;
|
||
elt = elementFromPoint(view.dom, coords, box);
|
||
if (!elt)
|
||
return null;
|
||
}
|
||
if (safari) {
|
||
for (let p = elt; node && p; p = parentNode(p))
|
||
if (p.draggable)
|
||
node = void 0;
|
||
}
|
||
elt = targetKludge(elt, coords);
|
||
if (node) {
|
||
if (gecko && node.nodeType == 1) {
|
||
offset = Math.min(offset, node.childNodes.length);
|
||
if (offset < node.childNodes.length) {
|
||
let next = node.childNodes[offset], box;
|
||
if (next.nodeName == "IMG" && (box = next.getBoundingClientRect()).right <= coords.left && box.bottom > coords.top)
|
||
offset++;
|
||
}
|
||
}
|
||
let prev;
|
||
if (webkit && offset && node.nodeType == 1 && (prev = node.childNodes[offset - 1]).nodeType == 1 && prev.contentEditable == "false" && prev.getBoundingClientRect().top >= coords.top)
|
||
offset--;
|
||
if (node == view.dom && offset == node.childNodes.length - 1 && node.lastChild.nodeType == 1 && coords.top > node.lastChild.getBoundingClientRect().bottom)
|
||
pos = view.state.doc.content.size;
|
||
else if (offset == 0 || node.nodeType != 1 || node.childNodes[offset - 1].nodeName != "BR")
|
||
pos = posFromCaret(view, node, offset, coords);
|
||
}
|
||
if (pos == null)
|
||
pos = posFromElement(view, elt, coords);
|
||
let desc = view.docView.nearestDesc(elt, true);
|
||
return { pos, inside: desc ? desc.posAtStart - desc.border : -1 };
|
||
}
|
||
function nonZero(rect) {
|
||
return rect.top < rect.bottom || rect.left < rect.right;
|
||
}
|
||
function singleRect(target, bias) {
|
||
let rects = target.getClientRects();
|
||
if (rects.length) {
|
||
let first2 = rects[bias < 0 ? 0 : rects.length - 1];
|
||
if (nonZero(first2))
|
||
return first2;
|
||
}
|
||
return Array.prototype.find.call(rects, nonZero) || target.getBoundingClientRect();
|
||
}
|
||
var BIDI = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
|
||
function coordsAtPos(view, pos, side) {
|
||
let { node, offset, atom } = view.docView.domFromPos(pos, side < 0 ? -1 : 1);
|
||
let supportEmptyRange = webkit || gecko;
|
||
if (node.nodeType == 3) {
|
||
if (supportEmptyRange && (BIDI.test(node.nodeValue) || (side < 0 ? !offset : offset == node.nodeValue.length))) {
|
||
let rect = singleRect(textRange(node, offset, offset), side);
|
||
if (gecko && offset && /\s/.test(node.nodeValue[offset - 1]) && offset < node.nodeValue.length) {
|
||
let rectBefore = singleRect(textRange(node, offset - 1, offset - 1), -1);
|
||
if (rectBefore.top == rect.top) {
|
||
let rectAfter = singleRect(textRange(node, offset, offset + 1), -1);
|
||
if (rectAfter.top != rect.top)
|
||
return flattenV(rectAfter, rectAfter.left < rectBefore.left);
|
||
}
|
||
}
|
||
return rect;
|
||
} else {
|
||
let from = offset, to = offset, takeSide = side < 0 ? 1 : -1;
|
||
if (side < 0 && !offset) {
|
||
to++;
|
||
takeSide = -1;
|
||
} else if (side >= 0 && offset == node.nodeValue.length) {
|
||
from--;
|
||
takeSide = 1;
|
||
} else if (side < 0) {
|
||
from--;
|
||
} else {
|
||
to++;
|
||
}
|
||
return flattenV(singleRect(textRange(node, from, to), takeSide), takeSide < 0);
|
||
}
|
||
}
|
||
let $dom = view.state.doc.resolve(pos - (atom || 0));
|
||
if (!$dom.parent.inlineContent) {
|
||
if (atom == null && offset && (side < 0 || offset == nodeSize(node))) {
|
||
let before = node.childNodes[offset - 1];
|
||
if (before.nodeType == 1)
|
||
return flattenH(before.getBoundingClientRect(), false);
|
||
}
|
||
if (atom == null && offset < nodeSize(node)) {
|
||
let after = node.childNodes[offset];
|
||
if (after.nodeType == 1)
|
||
return flattenH(after.getBoundingClientRect(), true);
|
||
}
|
||
return flattenH(node.getBoundingClientRect(), side >= 0);
|
||
}
|
||
if (atom == null && offset && (side < 0 || offset == nodeSize(node))) {
|
||
let before = node.childNodes[offset - 1];
|
||
let target = before.nodeType == 3 ? textRange(before, nodeSize(before) - (supportEmptyRange ? 0 : 1)) : before.nodeType == 1 && (before.nodeName != "BR" || !before.nextSibling) ? before : null;
|
||
if (target)
|
||
return flattenV(singleRect(target, 1), false);
|
||
}
|
||
if (atom == null && offset < nodeSize(node)) {
|
||
let after = node.childNodes[offset];
|
||
while (after.pmViewDesc && after.pmViewDesc.ignoreForCoords)
|
||
after = after.nextSibling;
|
||
let target = !after ? null : after.nodeType == 3 ? textRange(after, 0, supportEmptyRange ? 0 : 1) : after.nodeType == 1 ? after : null;
|
||
if (target)
|
||
return flattenV(singleRect(target, -1), true);
|
||
}
|
||
return flattenV(singleRect(node.nodeType == 3 ? textRange(node) : node, -side), side >= 0);
|
||
}
|
||
function flattenV(rect, left) {
|
||
if (rect.width == 0)
|
||
return rect;
|
||
let x = left ? rect.left : rect.right;
|
||
return { top: rect.top, bottom: rect.bottom, left: x, right: x };
|
||
}
|
||
function flattenH(rect, top) {
|
||
if (rect.height == 0)
|
||
return rect;
|
||
let y = top ? rect.top : rect.bottom;
|
||
return { top: y, bottom: y, left: rect.left, right: rect.right };
|
||
}
|
||
function withFlushedState(view, state, f) {
|
||
let viewState = view.state, active = view.root.activeElement;
|
||
if (viewState != state)
|
||
view.updateState(state);
|
||
if (active != view.dom)
|
||
view.focus();
|
||
try {
|
||
return f();
|
||
} finally {
|
||
if (viewState != state)
|
||
view.updateState(viewState);
|
||
if (active != view.dom && active)
|
||
active.focus();
|
||
}
|
||
}
|
||
function endOfTextblockVertical(view, state, dir) {
|
||
let sel = state.selection;
|
||
let $pos = dir == "up" ? sel.$from : sel.$to;
|
||
return withFlushedState(view, state, () => {
|
||
let { node: dom } = view.docView.domFromPos($pos.pos, dir == "up" ? -1 : 1);
|
||
for (; ; ) {
|
||
let nearest = view.docView.nearestDesc(dom, true);
|
||
if (!nearest)
|
||
break;
|
||
if (nearest.node.isBlock) {
|
||
dom = nearest.contentDOM || nearest.dom;
|
||
break;
|
||
}
|
||
dom = nearest.dom.parentNode;
|
||
}
|
||
let coords = coordsAtPos(view, $pos.pos, 1);
|
||
for (let child = dom.firstChild; child; child = child.nextSibling) {
|
||
let boxes;
|
||
if (child.nodeType == 1)
|
||
boxes = child.getClientRects();
|
||
else if (child.nodeType == 3)
|
||
boxes = textRange(child, 0, child.nodeValue.length).getClientRects();
|
||
else
|
||
continue;
|
||
for (let i = 0; i < boxes.length; i++) {
|
||
let box = boxes[i];
|
||
if (box.bottom > box.top + 1 && (dir == "up" ? coords.top - box.top > (box.bottom - coords.top) * 2 : box.bottom - coords.bottom > (coords.bottom - box.top) * 2))
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
});
|
||
}
|
||
var maybeRTL = /[\u0590-\u08ac]/;
|
||
function endOfTextblockHorizontal(view, state, dir) {
|
||
let { $head } = state.selection;
|
||
if (!$head.parent.isTextblock)
|
||
return false;
|
||
let offset = $head.parentOffset, atStart = !offset, atEnd = offset == $head.parent.content.size;
|
||
let sel = view.domSelection();
|
||
if (!sel)
|
||
return $head.pos == $head.start() || $head.pos == $head.end();
|
||
if (!maybeRTL.test($head.parent.textContent) || !sel.modify)
|
||
return dir == "left" || dir == "backward" ? atStart : atEnd;
|
||
return withFlushedState(view, state, () => {
|
||
let { focusNode: oldNode, focusOffset: oldOff, anchorNode, anchorOffset } = view.domSelectionRange();
|
||
let oldBidiLevel = sel.caretBidiLevel;
|
||
sel.modify("move", dir, "character");
|
||
let parentDOM = $head.depth ? view.docView.domAfterPos($head.before()) : view.dom;
|
||
let { focusNode: newNode, focusOffset: newOff } = view.domSelectionRange();
|
||
let result = newNode && !parentDOM.contains(newNode.nodeType == 1 ? newNode : newNode.parentNode) || oldNode == newNode && oldOff == newOff;
|
||
try {
|
||
sel.collapse(anchorNode, anchorOffset);
|
||
if (oldNode && (oldNode != anchorNode || oldOff != anchorOffset) && sel.extend)
|
||
sel.extend(oldNode, oldOff);
|
||
} catch (_) {
|
||
}
|
||
if (oldBidiLevel != null)
|
||
sel.caretBidiLevel = oldBidiLevel;
|
||
return result;
|
||
});
|
||
}
|
||
var cachedState = null;
|
||
var cachedDir = null;
|
||
var cachedResult = false;
|
||
function endOfTextblock(view, state, dir) {
|
||
if (cachedState == state && cachedDir == dir)
|
||
return cachedResult;
|
||
cachedState = state;
|
||
cachedDir = dir;
|
||
return cachedResult = dir == "up" || dir == "down" ? endOfTextblockVertical(view, state, dir) : endOfTextblockHorizontal(view, state, dir);
|
||
}
|
||
var NOT_DIRTY = 0;
|
||
var CHILD_DIRTY = 1;
|
||
var CONTENT_DIRTY = 2;
|
||
var NODE_DIRTY = 3;
|
||
var ViewDesc = class {
|
||
constructor(parent, children, dom, contentDOM) {
|
||
this.parent = parent;
|
||
this.children = children;
|
||
this.dom = dom;
|
||
this.contentDOM = contentDOM;
|
||
this.dirty = NOT_DIRTY;
|
||
dom.pmViewDesc = this;
|
||
}
|
||
// Used to check whether a given description corresponds to a
|
||
// widget/mark/node.
|
||
matchesWidget(widget) {
|
||
return false;
|
||
}
|
||
matchesMark(mark) {
|
||
return false;
|
||
}
|
||
matchesNode(node, outerDeco, innerDeco) {
|
||
return false;
|
||
}
|
||
matchesHack(nodeName) {
|
||
return false;
|
||
}
|
||
// When parsing in-editor content (in domchange.js), we allow
|
||
// descriptions to determine the parse rules that should be used to
|
||
// parse them.
|
||
parseRule() {
|
||
return null;
|
||
}
|
||
// Used by the editor's event handler to ignore events that come
|
||
// from certain descs.
|
||
stopEvent(event) {
|
||
return false;
|
||
}
|
||
// The size of the content represented by this desc.
|
||
get size() {
|
||
let size = 0;
|
||
for (let i = 0; i < this.children.length; i++)
|
||
size += this.children[i].size;
|
||
return size;
|
||
}
|
||
// For block nodes, this represents the space taken up by their
|
||
// start/end tokens.
|
||
get border() {
|
||
return 0;
|
||
}
|
||
destroy() {
|
||
this.parent = void 0;
|
||
if (this.dom.pmViewDesc == this)
|
||
this.dom.pmViewDesc = void 0;
|
||
for (let i = 0; i < this.children.length; i++)
|
||
this.children[i].destroy();
|
||
}
|
||
posBeforeChild(child) {
|
||
for (let i = 0, pos = this.posAtStart; ; i++) {
|
||
let cur = this.children[i];
|
||
if (cur == child)
|
||
return pos;
|
||
pos += cur.size;
|
||
}
|
||
}
|
||
get posBefore() {
|
||
return this.parent.posBeforeChild(this);
|
||
}
|
||
get posAtStart() {
|
||
return this.parent ? this.parent.posBeforeChild(this) + this.border : 0;
|
||
}
|
||
get posAfter() {
|
||
return this.posBefore + this.size;
|
||
}
|
||
get posAtEnd() {
|
||
return this.posAtStart + this.size - 2 * this.border;
|
||
}
|
||
localPosFromDOM(dom, offset, bias) {
|
||
if (this.contentDOM && this.contentDOM.contains(dom.nodeType == 1 ? dom : dom.parentNode)) {
|
||
if (bias < 0) {
|
||
let domBefore, desc;
|
||
if (dom == this.contentDOM) {
|
||
domBefore = dom.childNodes[offset - 1];
|
||
} else {
|
||
while (dom.parentNode != this.contentDOM)
|
||
dom = dom.parentNode;
|
||
domBefore = dom.previousSibling;
|
||
}
|
||
while (domBefore && !((desc = domBefore.pmViewDesc) && desc.parent == this))
|
||
domBefore = domBefore.previousSibling;
|
||
return domBefore ? this.posBeforeChild(desc) + desc.size : this.posAtStart;
|
||
} else {
|
||
let domAfter, desc;
|
||
if (dom == this.contentDOM) {
|
||
domAfter = dom.childNodes[offset];
|
||
} else {
|
||
while (dom.parentNode != this.contentDOM)
|
||
dom = dom.parentNode;
|
||
domAfter = dom.nextSibling;
|
||
}
|
||
while (domAfter && !((desc = domAfter.pmViewDesc) && desc.parent == this))
|
||
domAfter = domAfter.nextSibling;
|
||
return domAfter ? this.posBeforeChild(desc) : this.posAtEnd;
|
||
}
|
||
}
|
||
let atEnd;
|
||
if (dom == this.dom && this.contentDOM) {
|
||
atEnd = offset > domIndex(this.contentDOM);
|
||
} else if (this.contentDOM && this.contentDOM != this.dom && this.dom.contains(this.contentDOM)) {
|
||
atEnd = dom.compareDocumentPosition(this.contentDOM) & 2;
|
||
} else if (this.dom.firstChild) {
|
||
if (offset == 0)
|
||
for (let search = dom; ; search = search.parentNode) {
|
||
if (search == this.dom) {
|
||
atEnd = false;
|
||
break;
|
||
}
|
||
if (search.previousSibling)
|
||
break;
|
||
}
|
||
if (atEnd == null && offset == dom.childNodes.length)
|
||
for (let search = dom; ; search = search.parentNode) {
|
||
if (search == this.dom) {
|
||
atEnd = true;
|
||
break;
|
||
}
|
||
if (search.nextSibling)
|
||
break;
|
||
}
|
||
}
|
||
return (atEnd == null ? bias > 0 : atEnd) ? this.posAtEnd : this.posAtStart;
|
||
}
|
||
nearestDesc(dom, onlyNodes = false) {
|
||
for (let first2 = true, cur = dom; cur; cur = cur.parentNode) {
|
||
let desc = this.getDesc(cur), nodeDOM;
|
||
if (desc && (!onlyNodes || desc.node)) {
|
||
if (first2 && (nodeDOM = desc.nodeDOM) && !(nodeDOM.nodeType == 1 ? nodeDOM.contains(dom.nodeType == 1 ? dom : dom.parentNode) : nodeDOM == dom))
|
||
first2 = false;
|
||
else
|
||
return desc;
|
||
}
|
||
}
|
||
}
|
||
getDesc(dom) {
|
||
let desc = dom.pmViewDesc;
|
||
for (let cur = desc; cur; cur = cur.parent)
|
||
if (cur == this)
|
||
return desc;
|
||
}
|
||
posFromDOM(dom, offset, bias) {
|
||
for (let scan = dom; scan; scan = scan.parentNode) {
|
||
let desc = this.getDesc(scan);
|
||
if (desc)
|
||
return desc.localPosFromDOM(dom, offset, bias);
|
||
}
|
||
return -1;
|
||
}
|
||
// Find the desc for the node after the given pos, if any. (When a
|
||
// parent node overrode rendering, there might not be one.)
|
||
descAt(pos) {
|
||
for (let i = 0, offset = 0; i < this.children.length; i++) {
|
||
let child = this.children[i], end = offset + child.size;
|
||
if (offset == pos && end != offset) {
|
||
while (!child.border && child.children.length)
|
||
child = child.children[0];
|
||
return child;
|
||
}
|
||
if (pos < end)
|
||
return child.descAt(pos - offset - child.border);
|
||
offset = end;
|
||
}
|
||
}
|
||
domFromPos(pos, side) {
|
||
if (!this.contentDOM)
|
||
return { node: this.dom, offset: 0, atom: pos + 1 };
|
||
let i = 0, offset = 0;
|
||
for (let curPos = 0; i < this.children.length; i++) {
|
||
let child = this.children[i], end = curPos + child.size;
|
||
if (end > pos || child instanceof TrailingHackViewDesc) {
|
||
offset = pos - curPos;
|
||
break;
|
||
}
|
||
curPos = end;
|
||
}
|
||
if (offset)
|
||
return this.children[i].domFromPos(offset - this.children[i].border, side);
|
||
for (let prev; i && !(prev = this.children[i - 1]).size && prev instanceof WidgetViewDesc && prev.side >= 0; i--) {
|
||
}
|
||
if (side <= 0) {
|
||
let prev, enter2 = true;
|
||
for (; ; i--, enter2 = false) {
|
||
prev = i ? this.children[i - 1] : null;
|
||
if (!prev || prev.dom.parentNode == this.contentDOM)
|
||
break;
|
||
}
|
||
if (prev && side && enter2 && !prev.border && !prev.domAtom)
|
||
return prev.domFromPos(prev.size, side);
|
||
return { node: this.contentDOM, offset: prev ? domIndex(prev.dom) + 1 : 0 };
|
||
} else {
|
||
let next, enter2 = true;
|
||
for (; ; i++, enter2 = false) {
|
||
next = i < this.children.length ? this.children[i] : null;
|
||
if (!next || next.dom.parentNode == this.contentDOM)
|
||
break;
|
||
}
|
||
if (next && enter2 && !next.border && !next.domAtom)
|
||
return next.domFromPos(0, side);
|
||
return { node: this.contentDOM, offset: next ? domIndex(next.dom) : this.contentDOM.childNodes.length };
|
||
}
|
||
}
|
||
// Used to find a DOM range in a single parent for a given changed
|
||
// range.
|
||
parseRange(from, to, base2 = 0) {
|
||
if (this.children.length == 0)
|
||
return { node: this.contentDOM, from, to, fromOffset: 0, toOffset: this.contentDOM.childNodes.length };
|
||
let fromOffset = -1, toOffset = -1;
|
||
for (let offset = base2, i = 0; ; i++) {
|
||
let child = this.children[i], end = offset + child.size;
|
||
if (fromOffset == -1 && from <= end) {
|
||
let childBase = offset + child.border;
|
||
if (from >= childBase && to <= end - child.border && child.node && child.contentDOM && this.contentDOM.contains(child.contentDOM))
|
||
return child.parseRange(from, to, childBase);
|
||
from = offset;
|
||
for (let j = i; j > 0; j--) {
|
||
let prev = this.children[j - 1];
|
||
if (prev.size && prev.dom.parentNode == this.contentDOM && !prev.emptyChildAt(1)) {
|
||
fromOffset = domIndex(prev.dom) + 1;
|
||
break;
|
||
}
|
||
from -= prev.size;
|
||
}
|
||
if (fromOffset == -1)
|
||
fromOffset = 0;
|
||
}
|
||
if (fromOffset > -1 && (end > to || i == this.children.length - 1)) {
|
||
to = end;
|
||
for (let j = i + 1; j < this.children.length; j++) {
|
||
let next = this.children[j];
|
||
if (next.size && next.dom.parentNode == this.contentDOM && !next.emptyChildAt(-1)) {
|
||
toOffset = domIndex(next.dom);
|
||
break;
|
||
}
|
||
to += next.size;
|
||
}
|
||
if (toOffset == -1)
|
||
toOffset = this.contentDOM.childNodes.length;
|
||
break;
|
||
}
|
||
offset = end;
|
||
}
|
||
return { node: this.contentDOM, from, to, fromOffset, toOffset };
|
||
}
|
||
emptyChildAt(side) {
|
||
if (this.border || !this.contentDOM || !this.children.length)
|
||
return false;
|
||
let child = this.children[side < 0 ? 0 : this.children.length - 1];
|
||
return child.size == 0 || child.emptyChildAt(side);
|
||
}
|
||
domAfterPos(pos) {
|
||
let { node, offset } = this.domFromPos(pos, 0);
|
||
if (node.nodeType != 1 || offset == node.childNodes.length)
|
||
throw new RangeError("No node after pos " + pos);
|
||
return node.childNodes[offset];
|
||
}
|
||
// View descs are responsible for setting any selection that falls
|
||
// entirely inside of them, so that custom implementations can do
|
||
// custom things with the selection. Note that this falls apart when
|
||
// a selection starts in such a node and ends in another, in which
|
||
// case we just use whatever domFromPos produces as a best effort.
|
||
setSelection(anchor, head, view, force = false) {
|
||
let from = Math.min(anchor, head), to = Math.max(anchor, head);
|
||
for (let i = 0, offset = 0; i < this.children.length; i++) {
|
||
let child = this.children[i], end = offset + child.size;
|
||
if (from > offset && to < end)
|
||
return child.setSelection(anchor - offset - child.border, head - offset - child.border, view, force);
|
||
offset = end;
|
||
}
|
||
let anchorDOM = this.domFromPos(anchor, anchor ? -1 : 1);
|
||
let headDOM = head == anchor ? anchorDOM : this.domFromPos(head, head ? -1 : 1);
|
||
let domSel = view.root.getSelection();
|
||
let selRange = view.domSelectionRange();
|
||
let brKludge = false;
|
||
if ((gecko || safari) && anchor == head) {
|
||
let { node, offset } = anchorDOM;
|
||
if (node.nodeType == 3) {
|
||
brKludge = !!(offset && node.nodeValue[offset - 1] == "\n");
|
||
if (brKludge && offset == node.nodeValue.length) {
|
||
for (let scan = node, after; scan; scan = scan.parentNode) {
|
||
if (after = scan.nextSibling) {
|
||
if (after.nodeName == "BR")
|
||
anchorDOM = headDOM = { node: after.parentNode, offset: domIndex(after) + 1 };
|
||
break;
|
||
}
|
||
let desc = scan.pmViewDesc;
|
||
if (desc && desc.node && desc.node.isBlock)
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
let prev = node.childNodes[offset - 1];
|
||
brKludge = prev && (prev.nodeName == "BR" || prev.contentEditable == "false");
|
||
}
|
||
}
|
||
if (gecko && selRange.focusNode && selRange.focusNode != headDOM.node && selRange.focusNode.nodeType == 1) {
|
||
let after = selRange.focusNode.childNodes[selRange.focusOffset];
|
||
if (after && after.contentEditable == "false")
|
||
force = true;
|
||
}
|
||
if (!(force || brKludge && safari) && isEquivalentPosition(anchorDOM.node, anchorDOM.offset, selRange.anchorNode, selRange.anchorOffset) && isEquivalentPosition(headDOM.node, headDOM.offset, selRange.focusNode, selRange.focusOffset))
|
||
return;
|
||
let domSelExtended = false;
|
||
if ((domSel.extend || anchor == head) && !brKludge) {
|
||
domSel.collapse(anchorDOM.node, anchorDOM.offset);
|
||
try {
|
||
if (anchor != head)
|
||
domSel.extend(headDOM.node, headDOM.offset);
|
||
domSelExtended = true;
|
||
} catch (_) {
|
||
}
|
||
}
|
||
if (!domSelExtended) {
|
||
if (anchor > head) {
|
||
let tmp = anchorDOM;
|
||
anchorDOM = headDOM;
|
||
headDOM = tmp;
|
||
}
|
||
let range = document.createRange();
|
||
range.setEnd(headDOM.node, headDOM.offset);
|
||
range.setStart(anchorDOM.node, anchorDOM.offset);
|
||
domSel.removeAllRanges();
|
||
domSel.addRange(range);
|
||
}
|
||
}
|
||
ignoreMutation(mutation) {
|
||
return !this.contentDOM && mutation.type != "selection";
|
||
}
|
||
get contentLost() {
|
||
return this.contentDOM && this.contentDOM != this.dom && !this.dom.contains(this.contentDOM);
|
||
}
|
||
// Remove a subtree of the element tree that has been touched
|
||
// by a DOM change, so that the next update will redraw it.
|
||
markDirty(from, to) {
|
||
for (let offset = 0, i = 0; i < this.children.length; i++) {
|
||
let child = this.children[i], end = offset + child.size;
|
||
if (offset == end ? from <= end && to >= offset : from < end && to > offset) {
|
||
let startInside = offset + child.border, endInside = end - child.border;
|
||
if (from >= startInside && to <= endInside) {
|
||
this.dirty = from == offset || to == end ? CONTENT_DIRTY : CHILD_DIRTY;
|
||
if (from == startInside && to == endInside && (child.contentLost || child.dom.parentNode != this.contentDOM))
|
||
child.dirty = NODE_DIRTY;
|
||
else
|
||
child.markDirty(from - startInside, to - startInside);
|
||
return;
|
||
} else {
|
||
child.dirty = child.dom == child.contentDOM && child.dom.parentNode == this.contentDOM && !child.children.length ? CONTENT_DIRTY : NODE_DIRTY;
|
||
}
|
||
}
|
||
offset = end;
|
||
}
|
||
this.dirty = CONTENT_DIRTY;
|
||
}
|
||
markParentsDirty() {
|
||
let level = 1;
|
||
for (let node = this.parent; node; node = node.parent, level++) {
|
||
let dirty = level == 1 ? CONTENT_DIRTY : CHILD_DIRTY;
|
||
if (node.dirty < dirty)
|
||
node.dirty = dirty;
|
||
}
|
||
}
|
||
get domAtom() {
|
||
return false;
|
||
}
|
||
get ignoreForCoords() {
|
||
return false;
|
||
}
|
||
isText(text) {
|
||
return false;
|
||
}
|
||
};
|
||
var WidgetViewDesc = class extends ViewDesc {
|
||
constructor(parent, widget, view, pos) {
|
||
let self, dom = widget.type.toDOM;
|
||
if (typeof dom == "function")
|
||
dom = dom(view, () => {
|
||
if (!self)
|
||
return pos;
|
||
if (self.parent)
|
||
return self.parent.posBeforeChild(self);
|
||
});
|
||
if (!widget.type.spec.raw) {
|
||
if (dom.nodeType != 1) {
|
||
let wrap = document.createElement("span");
|
||
wrap.appendChild(dom);
|
||
dom = wrap;
|
||
}
|
||
dom.contentEditable = "false";
|
||
dom.classList.add("ProseMirror-widget");
|
||
}
|
||
super(parent, [], dom, null);
|
||
this.widget = widget;
|
||
this.widget = widget;
|
||
self = this;
|
||
}
|
||
matchesWidget(widget) {
|
||
return this.dirty == NOT_DIRTY && widget.type.eq(this.widget.type);
|
||
}
|
||
parseRule() {
|
||
return { ignore: true };
|
||
}
|
||
stopEvent(event) {
|
||
let stop = this.widget.spec.stopEvent;
|
||
return stop ? stop(event) : false;
|
||
}
|
||
ignoreMutation(mutation) {
|
||
return mutation.type != "selection" || this.widget.spec.ignoreSelection;
|
||
}
|
||
destroy() {
|
||
this.widget.type.destroy(this.dom);
|
||
super.destroy();
|
||
}
|
||
get domAtom() {
|
||
return true;
|
||
}
|
||
get side() {
|
||
return this.widget.type.side;
|
||
}
|
||
};
|
||
var CompositionViewDesc = class extends ViewDesc {
|
||
constructor(parent, dom, textDOM, text) {
|
||
super(parent, [], dom, null);
|
||
this.textDOM = textDOM;
|
||
this.text = text;
|
||
}
|
||
get size() {
|
||
return this.text.length;
|
||
}
|
||
localPosFromDOM(dom, offset) {
|
||
if (dom != this.textDOM)
|
||
return this.posAtStart + (offset ? this.size : 0);
|
||
return this.posAtStart + offset;
|
||
}
|
||
domFromPos(pos) {
|
||
return { node: this.textDOM, offset: pos };
|
||
}
|
||
ignoreMutation(mut) {
|
||
return mut.type === "characterData" && mut.target.nodeValue == mut.oldValue;
|
||
}
|
||
};
|
||
var MarkViewDesc = class _MarkViewDesc extends ViewDesc {
|
||
constructor(parent, mark, dom, contentDOM, spec) {
|
||
super(parent, [], dom, contentDOM);
|
||
this.mark = mark;
|
||
this.spec = spec;
|
||
}
|
||
static create(parent, mark, inline, view) {
|
||
let custom = view.nodeViews[mark.type.name];
|
||
let spec = custom && custom(mark, view, inline);
|
||
if (!spec || !spec.dom)
|
||
spec = DOMSerializer.renderSpec(document, mark.type.spec.toDOM(mark, inline), null, mark.attrs);
|
||
return new _MarkViewDesc(parent, mark, spec.dom, spec.contentDOM || spec.dom, spec);
|
||
}
|
||
parseRule() {
|
||
if (this.dirty & NODE_DIRTY || this.mark.type.spec.reparseInView)
|
||
return null;
|
||
return { mark: this.mark.type.name, attrs: this.mark.attrs, contentElement: this.contentDOM };
|
||
}
|
||
matchesMark(mark) {
|
||
return this.dirty != NODE_DIRTY && this.mark.eq(mark);
|
||
}
|
||
markDirty(from, to) {
|
||
super.markDirty(from, to);
|
||
if (this.dirty != NOT_DIRTY) {
|
||
let parent = this.parent;
|
||
while (!parent.node)
|
||
parent = parent.parent;
|
||
if (parent.dirty < this.dirty)
|
||
parent.dirty = this.dirty;
|
||
this.dirty = NOT_DIRTY;
|
||
}
|
||
}
|
||
slice(from, to, view) {
|
||
let copy = _MarkViewDesc.create(this.parent, this.mark, true, view);
|
||
let nodes = this.children, size = this.size;
|
||
if (to < size)
|
||
nodes = replaceNodes(nodes, to, size, view);
|
||
if (from > 0)
|
||
nodes = replaceNodes(nodes, 0, from, view);
|
||
for (let i = 0; i < nodes.length; i++)
|
||
nodes[i].parent = copy;
|
||
copy.children = nodes;
|
||
return copy;
|
||
}
|
||
ignoreMutation(mutation) {
|
||
return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation);
|
||
}
|
||
destroy() {
|
||
if (this.spec.destroy)
|
||
this.spec.destroy();
|
||
super.destroy();
|
||
}
|
||
};
|
||
var NodeViewDesc = class _NodeViewDesc extends ViewDesc {
|
||
constructor(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, view, pos) {
|
||
super(parent, [], dom, contentDOM);
|
||
this.node = node;
|
||
this.outerDeco = outerDeco;
|
||
this.innerDeco = innerDeco;
|
||
this.nodeDOM = nodeDOM;
|
||
}
|
||
// By default, a node is rendered using the `toDOM` method from the
|
||
// node type spec. But client code can use the `nodeViews` spec to
|
||
// supply a custom node view, which can influence various aspects of
|
||
// the way the node works.
|
||
//
|
||
// (Using subclassing for this was intentionally decided against,
|
||
// since it'd require exposing a whole slew of finicky
|
||
// implementation details to the user code that they probably will
|
||
// never need.)
|
||
static create(parent, node, outerDeco, innerDeco, view, pos) {
|
||
let custom = view.nodeViews[node.type.name], descObj;
|
||
let spec = custom && custom(node, view, () => {
|
||
if (!descObj)
|
||
return pos;
|
||
if (descObj.parent)
|
||
return descObj.parent.posBeforeChild(descObj);
|
||
}, outerDeco, innerDeco);
|
||
let dom = spec && spec.dom, contentDOM = spec && spec.contentDOM;
|
||
if (node.isText) {
|
||
if (!dom)
|
||
dom = document.createTextNode(node.text);
|
||
else if (dom.nodeType != 3)
|
||
throw new RangeError("Text must be rendered as a DOM text node");
|
||
} else if (!dom) {
|
||
let spec2 = DOMSerializer.renderSpec(document, node.type.spec.toDOM(node), null, node.attrs);
|
||
({ dom, contentDOM } = spec2);
|
||
}
|
||
if (!contentDOM && !node.isText && dom.nodeName != "BR") {
|
||
if (!dom.hasAttribute("contenteditable"))
|
||
dom.contentEditable = "false";
|
||
if (node.type.spec.draggable)
|
||
dom.draggable = true;
|
||
}
|
||
let nodeDOM = dom;
|
||
dom = applyOuterDeco(dom, outerDeco, node);
|
||
if (spec)
|
||
return descObj = new CustomNodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM, spec, view, pos + 1);
|
||
else if (node.isText)
|
||
return new TextViewDesc(parent, node, outerDeco, innerDeco, dom, nodeDOM, view);
|
||
else
|
||
return new _NodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM, view, pos + 1);
|
||
}
|
||
parseRule() {
|
||
if (this.node.type.spec.reparseInView)
|
||
return null;
|
||
let rule = { node: this.node.type.name, attrs: this.node.attrs };
|
||
if (this.node.type.whitespace == "pre")
|
||
rule.preserveWhitespace = "full";
|
||
if (!this.contentDOM) {
|
||
rule.getContent = () => this.node.content;
|
||
} else if (!this.contentLost) {
|
||
rule.contentElement = this.contentDOM;
|
||
} else {
|
||
for (let i = this.children.length - 1; i >= 0; i--) {
|
||
let child = this.children[i];
|
||
if (this.dom.contains(child.dom.parentNode)) {
|
||
rule.contentElement = child.dom.parentNode;
|
||
break;
|
||
}
|
||
}
|
||
if (!rule.contentElement)
|
||
rule.getContent = () => Fragment.empty;
|
||
}
|
||
return rule;
|
||
}
|
||
matchesNode(node, outerDeco, innerDeco) {
|
||
return this.dirty == NOT_DIRTY && node.eq(this.node) && sameOuterDeco(outerDeco, this.outerDeco) && innerDeco.eq(this.innerDeco);
|
||
}
|
||
get size() {
|
||
return this.node.nodeSize;
|
||
}
|
||
get border() {
|
||
return this.node.isLeaf ? 0 : 1;
|
||
}
|
||
// Syncs `this.children` to match `this.node.content` and the local
|
||
// decorations, possibly introducing nesting for marks. Then, in a
|
||
// separate step, syncs the DOM inside `this.contentDOM` to
|
||
// `this.children`.
|
||
updateChildren(view, pos) {
|
||
let inline = this.node.inlineContent, off = pos;
|
||
let composition = view.composing ? this.localCompositionInfo(view, pos) : null;
|
||
let localComposition = composition && composition.pos > -1 ? composition : null;
|
||
let compositionInChild = composition && composition.pos < 0;
|
||
let updater = new ViewTreeUpdater(this, localComposition && localComposition.node, view);
|
||
iterDeco(this.node, this.innerDeco, (widget, i, insideNode) => {
|
||
if (widget.spec.marks)
|
||
updater.syncToMarks(widget.spec.marks, inline, view);
|
||
else if (widget.type.side >= 0 && !insideNode)
|
||
updater.syncToMarks(i == this.node.childCount ? Mark.none : this.node.child(i).marks, inline, view);
|
||
updater.placeWidget(widget, view, off);
|
||
}, (child, outerDeco, innerDeco, i) => {
|
||
updater.syncToMarks(child.marks, inline, view);
|
||
let compIndex;
|
||
if (updater.findNodeMatch(child, outerDeco, innerDeco, i)) ;
|
||
else if (compositionInChild && view.state.selection.from > off && view.state.selection.to < off + child.nodeSize && (compIndex = updater.findIndexWithChild(composition.node)) > -1 && updater.updateNodeAt(child, outerDeco, innerDeco, compIndex, view)) ;
|
||
else if (updater.updateNextNode(child, outerDeco, innerDeco, view, i, off)) ;
|
||
else {
|
||
updater.addNode(child, outerDeco, innerDeco, view, off);
|
||
}
|
||
off += child.nodeSize;
|
||
});
|
||
updater.syncToMarks([], inline, view);
|
||
if (this.node.isTextblock)
|
||
updater.addTextblockHacks();
|
||
updater.destroyRest();
|
||
if (updater.changed || this.dirty == CONTENT_DIRTY) {
|
||
if (localComposition)
|
||
this.protectLocalComposition(view, localComposition);
|
||
renderDescs(this.contentDOM, this.children, view);
|
||
if (ios)
|
||
iosHacks(this.dom);
|
||
}
|
||
}
|
||
localCompositionInfo(view, pos) {
|
||
let { from, to } = view.state.selection;
|
||
if (!(view.state.selection instanceof TextSelection) || from < pos || to > pos + this.node.content.size)
|
||
return null;
|
||
let textNode = view.input.compositionNode;
|
||
if (!textNode || !this.dom.contains(textNode.parentNode))
|
||
return null;
|
||
if (this.node.inlineContent) {
|
||
let text = textNode.nodeValue;
|
||
let textPos = findTextInFragment(this.node.content, text, from - pos, to - pos);
|
||
return textPos < 0 ? null : { node: textNode, pos: textPos, text };
|
||
} else {
|
||
return { node: textNode, pos: -1, text: "" };
|
||
}
|
||
}
|
||
protectLocalComposition(view, { node, pos, text }) {
|
||
if (this.getDesc(node))
|
||
return;
|
||
let topNode = node;
|
||
for (; ; topNode = topNode.parentNode) {
|
||
if (topNode.parentNode == this.contentDOM)
|
||
break;
|
||
while (topNode.previousSibling)
|
||
topNode.parentNode.removeChild(topNode.previousSibling);
|
||
while (topNode.nextSibling)
|
||
topNode.parentNode.removeChild(topNode.nextSibling);
|
||
if (topNode.pmViewDesc)
|
||
topNode.pmViewDesc = void 0;
|
||
}
|
||
let desc = new CompositionViewDesc(this, topNode, node, text);
|
||
view.input.compositionNodes.push(desc);
|
||
this.children = replaceNodes(this.children, pos, pos + text.length, view, desc);
|
||
}
|
||
// If this desc must be updated to match the given node decoration,
|
||
// do so and return true.
|
||
update(node, outerDeco, innerDeco, view) {
|
||
if (this.dirty == NODE_DIRTY || !node.sameMarkup(this.node))
|
||
return false;
|
||
this.updateInner(node, outerDeco, innerDeco, view);
|
||
return true;
|
||
}
|
||
updateInner(node, outerDeco, innerDeco, view) {
|
||
this.updateOuterDeco(outerDeco);
|
||
this.node = node;
|
||
this.innerDeco = innerDeco;
|
||
if (this.contentDOM)
|
||
this.updateChildren(view, this.posAtStart);
|
||
this.dirty = NOT_DIRTY;
|
||
}
|
||
updateOuterDeco(outerDeco) {
|
||
if (sameOuterDeco(outerDeco, this.outerDeco))
|
||
return;
|
||
let needsWrap = this.nodeDOM.nodeType != 1;
|
||
let oldDOM = this.dom;
|
||
this.dom = patchOuterDeco(this.dom, this.nodeDOM, computeOuterDeco(this.outerDeco, this.node, needsWrap), computeOuterDeco(outerDeco, this.node, needsWrap));
|
||
if (this.dom != oldDOM) {
|
||
oldDOM.pmViewDesc = void 0;
|
||
this.dom.pmViewDesc = this;
|
||
}
|
||
this.outerDeco = outerDeco;
|
||
}
|
||
// Mark this node as being the selected node.
|
||
selectNode() {
|
||
if (this.nodeDOM.nodeType == 1)
|
||
this.nodeDOM.classList.add("ProseMirror-selectednode");
|
||
if (this.contentDOM || !this.node.type.spec.draggable)
|
||
this.dom.draggable = true;
|
||
}
|
||
// Remove selected node marking from this node.
|
||
deselectNode() {
|
||
if (this.nodeDOM.nodeType == 1) {
|
||
this.nodeDOM.classList.remove("ProseMirror-selectednode");
|
||
if (this.contentDOM || !this.node.type.spec.draggable)
|
||
this.dom.removeAttribute("draggable");
|
||
}
|
||
}
|
||
get domAtom() {
|
||
return this.node.isAtom;
|
||
}
|
||
};
|
||
function docViewDesc(doc2, outerDeco, innerDeco, dom, view) {
|
||
applyOuterDeco(dom, outerDeco, doc2);
|
||
let docView = new NodeViewDesc(void 0, doc2, outerDeco, innerDeco, dom, dom, dom, view, 0);
|
||
if (docView.contentDOM)
|
||
docView.updateChildren(view, 0);
|
||
return docView;
|
||
}
|
||
var TextViewDesc = class _TextViewDesc extends NodeViewDesc {
|
||
constructor(parent, node, outerDeco, innerDeco, dom, nodeDOM, view) {
|
||
super(parent, node, outerDeco, innerDeco, dom, null, nodeDOM, view, 0);
|
||
}
|
||
parseRule() {
|
||
let skip = this.nodeDOM.parentNode;
|
||
while (skip && skip != this.dom && !skip.pmIsDeco)
|
||
skip = skip.parentNode;
|
||
return { skip: skip || true };
|
||
}
|
||
update(node, outerDeco, innerDeco, view) {
|
||
if (this.dirty == NODE_DIRTY || this.dirty != NOT_DIRTY && !this.inParent() || !node.sameMarkup(this.node))
|
||
return false;
|
||
this.updateOuterDeco(outerDeco);
|
||
if ((this.dirty != NOT_DIRTY || node.text != this.node.text) && node.text != this.nodeDOM.nodeValue) {
|
||
this.nodeDOM.nodeValue = node.text;
|
||
if (view.trackWrites == this.nodeDOM)
|
||
view.trackWrites = null;
|
||
}
|
||
this.node = node;
|
||
this.dirty = NOT_DIRTY;
|
||
return true;
|
||
}
|
||
inParent() {
|
||
let parentDOM = this.parent.contentDOM;
|
||
for (let n = this.nodeDOM; n; n = n.parentNode)
|
||
if (n == parentDOM)
|
||
return true;
|
||
return false;
|
||
}
|
||
domFromPos(pos) {
|
||
return { node: this.nodeDOM, offset: pos };
|
||
}
|
||
localPosFromDOM(dom, offset, bias) {
|
||
if (dom == this.nodeDOM)
|
||
return this.posAtStart + Math.min(offset, this.node.text.length);
|
||
return super.localPosFromDOM(dom, offset, bias);
|
||
}
|
||
ignoreMutation(mutation) {
|
||
return mutation.type != "characterData" && mutation.type != "selection";
|
||
}
|
||
slice(from, to, view) {
|
||
let node = this.node.cut(from, to), dom = document.createTextNode(node.text);
|
||
return new _TextViewDesc(this.parent, node, this.outerDeco, this.innerDeco, dom, dom, view);
|
||
}
|
||
markDirty(from, to) {
|
||
super.markDirty(from, to);
|
||
if (this.dom != this.nodeDOM && (from == 0 || to == this.nodeDOM.nodeValue.length))
|
||
this.dirty = NODE_DIRTY;
|
||
}
|
||
get domAtom() {
|
||
return false;
|
||
}
|
||
isText(text) {
|
||
return this.node.text == text;
|
||
}
|
||
};
|
||
var TrailingHackViewDesc = class extends ViewDesc {
|
||
parseRule() {
|
||
return { ignore: true };
|
||
}
|
||
matchesHack(nodeName) {
|
||
return this.dirty == NOT_DIRTY && this.dom.nodeName == nodeName;
|
||
}
|
||
get domAtom() {
|
||
return true;
|
||
}
|
||
get ignoreForCoords() {
|
||
return this.dom.nodeName == "IMG";
|
||
}
|
||
};
|
||
var CustomNodeViewDesc = class extends NodeViewDesc {
|
||
constructor(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, spec, view, pos) {
|
||
super(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, view, pos);
|
||
this.spec = spec;
|
||
}
|
||
// A custom `update` method gets to decide whether the update goes
|
||
// through. If it does, and there's a `contentDOM` node, our logic
|
||
// updates the children.
|
||
update(node, outerDeco, innerDeco, view) {
|
||
if (this.dirty == NODE_DIRTY)
|
||
return false;
|
||
if (this.spec.update && (this.node.type == node.type || this.spec.multiType)) {
|
||
let result = this.spec.update(node, outerDeco, innerDeco);
|
||
if (result)
|
||
this.updateInner(node, outerDeco, innerDeco, view);
|
||
return result;
|
||
} else if (!this.contentDOM && !node.isLeaf) {
|
||
return false;
|
||
} else {
|
||
return super.update(node, outerDeco, innerDeco, view);
|
||
}
|
||
}
|
||
selectNode() {
|
||
this.spec.selectNode ? this.spec.selectNode() : super.selectNode();
|
||
}
|
||
deselectNode() {
|
||
this.spec.deselectNode ? this.spec.deselectNode() : super.deselectNode();
|
||
}
|
||
setSelection(anchor, head, view, force) {
|
||
this.spec.setSelection ? this.spec.setSelection(anchor, head, view.root) : super.setSelection(anchor, head, view, force);
|
||
}
|
||
destroy() {
|
||
if (this.spec.destroy)
|
||
this.spec.destroy();
|
||
super.destroy();
|
||
}
|
||
stopEvent(event) {
|
||
return this.spec.stopEvent ? this.spec.stopEvent(event) : false;
|
||
}
|
||
ignoreMutation(mutation) {
|
||
return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation);
|
||
}
|
||
};
|
||
function renderDescs(parentDOM, descs, view) {
|
||
let dom = parentDOM.firstChild, written = false;
|
||
for (let i = 0; i < descs.length; i++) {
|
||
let desc = descs[i], childDOM = desc.dom;
|
||
if (childDOM.parentNode == parentDOM) {
|
||
while (childDOM != dom) {
|
||
dom = rm(dom);
|
||
written = true;
|
||
}
|
||
dom = dom.nextSibling;
|
||
} else {
|
||
written = true;
|
||
parentDOM.insertBefore(childDOM, dom);
|
||
}
|
||
if (desc instanceof MarkViewDesc) {
|
||
let pos = dom ? dom.previousSibling : parentDOM.lastChild;
|
||
renderDescs(desc.contentDOM, desc.children, view);
|
||
dom = pos ? pos.nextSibling : parentDOM.firstChild;
|
||
}
|
||
}
|
||
while (dom) {
|
||
dom = rm(dom);
|
||
written = true;
|
||
}
|
||
if (written && view.trackWrites == parentDOM)
|
||
view.trackWrites = null;
|
||
}
|
||
var OuterDecoLevel = function(nodeName) {
|
||
if (nodeName)
|
||
this.nodeName = nodeName;
|
||
};
|
||
OuterDecoLevel.prototype = /* @__PURE__ */ Object.create(null);
|
||
var noDeco = [new OuterDecoLevel()];
|
||
function computeOuterDeco(outerDeco, node, needsWrap) {
|
||
if (outerDeco.length == 0)
|
||
return noDeco;
|
||
let top = needsWrap ? noDeco[0] : new OuterDecoLevel(), result = [top];
|
||
for (let i = 0; i < outerDeco.length; i++) {
|
||
let attrs = outerDeco[i].type.attrs;
|
||
if (!attrs)
|
||
continue;
|
||
if (attrs.nodeName)
|
||
result.push(top = new OuterDecoLevel(attrs.nodeName));
|
||
for (let name in attrs) {
|
||
let val = attrs[name];
|
||
if (val == null)
|
||
continue;
|
||
if (needsWrap && result.length == 1)
|
||
result.push(top = new OuterDecoLevel(node.isInline ? "span" : "div"));
|
||
if (name == "class")
|
||
top.class = (top.class ? top.class + " " : "") + val;
|
||
else if (name == "style")
|
||
top.style = (top.style ? top.style + ";" : "") + val;
|
||
else if (name != "nodeName")
|
||
top[name] = val;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
function patchOuterDeco(outerDOM, nodeDOM, prevComputed, curComputed) {
|
||
if (prevComputed == noDeco && curComputed == noDeco)
|
||
return nodeDOM;
|
||
let curDOM = nodeDOM;
|
||
for (let i = 0; i < curComputed.length; i++) {
|
||
let deco = curComputed[i], prev = prevComputed[i];
|
||
if (i) {
|
||
let parent;
|
||
if (prev && prev.nodeName == deco.nodeName && curDOM != outerDOM && (parent = curDOM.parentNode) && parent.nodeName.toLowerCase() == deco.nodeName) {
|
||
curDOM = parent;
|
||
} else {
|
||
parent = document.createElement(deco.nodeName);
|
||
parent.pmIsDeco = true;
|
||
parent.appendChild(curDOM);
|
||
prev = noDeco[0];
|
||
curDOM = parent;
|
||
}
|
||
}
|
||
patchAttributes(curDOM, prev || noDeco[0], deco);
|
||
}
|
||
return curDOM;
|
||
}
|
||
function patchAttributes(dom, prev, cur) {
|
||
for (let name in prev)
|
||
if (name != "class" && name != "style" && name != "nodeName" && !(name in cur))
|
||
dom.removeAttribute(name);
|
||
for (let name in cur)
|
||
if (name != "class" && name != "style" && name != "nodeName" && cur[name] != prev[name])
|
||
dom.setAttribute(name, cur[name]);
|
||
if (prev.class != cur.class) {
|
||
let prevList = prev.class ? prev.class.split(" ").filter(Boolean) : [];
|
||
let curList = cur.class ? cur.class.split(" ").filter(Boolean) : [];
|
||
for (let i = 0; i < prevList.length; i++)
|
||
if (curList.indexOf(prevList[i]) == -1)
|
||
dom.classList.remove(prevList[i]);
|
||
for (let i = 0; i < curList.length; i++)
|
||
if (prevList.indexOf(curList[i]) == -1)
|
||
dom.classList.add(curList[i]);
|
||
if (dom.classList.length == 0)
|
||
dom.removeAttribute("class");
|
||
}
|
||
if (prev.style != cur.style) {
|
||
if (prev.style) {
|
||
let prop = /\s*([\w\-\xa1-\uffff]+)\s*:(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|\(.*?\)|[^;])*/g, m;
|
||
while (m = prop.exec(prev.style))
|
||
dom.style.removeProperty(m[1]);
|
||
}
|
||
if (cur.style)
|
||
dom.style.cssText += cur.style;
|
||
}
|
||
}
|
||
function applyOuterDeco(dom, deco, node) {
|
||
return patchOuterDeco(dom, dom, noDeco, computeOuterDeco(deco, node, dom.nodeType != 1));
|
||
}
|
||
function sameOuterDeco(a, b) {
|
||
if (a.length != b.length)
|
||
return false;
|
||
for (let i = 0; i < a.length; i++)
|
||
if (!a[i].type.eq(b[i].type))
|
||
return false;
|
||
return true;
|
||
}
|
||
function rm(dom) {
|
||
let next = dom.nextSibling;
|
||
dom.parentNode.removeChild(dom);
|
||
return next;
|
||
}
|
||
var ViewTreeUpdater = class {
|
||
constructor(top, lock, view) {
|
||
this.lock = lock;
|
||
this.view = view;
|
||
this.index = 0;
|
||
this.stack = [];
|
||
this.changed = false;
|
||
this.top = top;
|
||
this.preMatch = preMatch(top.node.content, top);
|
||
}
|
||
// Destroy and remove the children between the given indices in
|
||
// `this.top`.
|
||
destroyBetween(start, end) {
|
||
if (start == end)
|
||
return;
|
||
for (let i = start; i < end; i++)
|
||
this.top.children[i].destroy();
|
||
this.top.children.splice(start, end - start);
|
||
this.changed = true;
|
||
}
|
||
// Destroy all remaining children in `this.top`.
|
||
destroyRest() {
|
||
this.destroyBetween(this.index, this.top.children.length);
|
||
}
|
||
// Sync the current stack of mark descs with the given array of
|
||
// marks, reusing existing mark descs when possible.
|
||
syncToMarks(marks, inline, view) {
|
||
let keep = 0, depth = this.stack.length >> 1;
|
||
let maxKeep = Math.min(depth, marks.length);
|
||
while (keep < maxKeep && (keep == depth - 1 ? this.top : this.stack[keep + 1 << 1]).matchesMark(marks[keep]) && marks[keep].type.spec.spanning !== false)
|
||
keep++;
|
||
while (keep < depth) {
|
||
this.destroyRest();
|
||
this.top.dirty = NOT_DIRTY;
|
||
this.index = this.stack.pop();
|
||
this.top = this.stack.pop();
|
||
depth--;
|
||
}
|
||
while (depth < marks.length) {
|
||
this.stack.push(this.top, this.index + 1);
|
||
let found = -1;
|
||
for (let i = this.index; i < Math.min(this.index + 3, this.top.children.length); i++) {
|
||
let next = this.top.children[i];
|
||
if (next.matchesMark(marks[depth]) && !this.isLocked(next.dom)) {
|
||
found = i;
|
||
break;
|
||
}
|
||
}
|
||
if (found > -1) {
|
||
if (found > this.index) {
|
||
this.changed = true;
|
||
this.destroyBetween(this.index, found);
|
||
}
|
||
this.top = this.top.children[this.index];
|
||
} else {
|
||
let markDesc = MarkViewDesc.create(this.top, marks[depth], inline, view);
|
||
this.top.children.splice(this.index, 0, markDesc);
|
||
this.top = markDesc;
|
||
this.changed = true;
|
||
}
|
||
this.index = 0;
|
||
depth++;
|
||
}
|
||
}
|
||
// Try to find a node desc matching the given data. Skip over it and
|
||
// return true when successful.
|
||
findNodeMatch(node, outerDeco, innerDeco, index2) {
|
||
let found = -1, targetDesc;
|
||
if (index2 >= this.preMatch.index && (targetDesc = this.preMatch.matches[index2 - this.preMatch.index]).parent == this.top && targetDesc.matchesNode(node, outerDeco, innerDeco)) {
|
||
found = this.top.children.indexOf(targetDesc, this.index);
|
||
} else {
|
||
for (let i = this.index, e = Math.min(this.top.children.length, i + 5); i < e; i++) {
|
||
let child = this.top.children[i];
|
||
if (child.matchesNode(node, outerDeco, innerDeco) && !this.preMatch.matched.has(child)) {
|
||
found = i;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (found < 0)
|
||
return false;
|
||
this.destroyBetween(this.index, found);
|
||
this.index++;
|
||
return true;
|
||
}
|
||
updateNodeAt(node, outerDeco, innerDeco, index2, view) {
|
||
let child = this.top.children[index2];
|
||
if (child.dirty == NODE_DIRTY && child.dom == child.contentDOM)
|
||
child.dirty = CONTENT_DIRTY;
|
||
if (!child.update(node, outerDeco, innerDeco, view))
|
||
return false;
|
||
this.destroyBetween(this.index, index2);
|
||
this.index++;
|
||
return true;
|
||
}
|
||
findIndexWithChild(domNode) {
|
||
for (; ; ) {
|
||
let parent = domNode.parentNode;
|
||
if (!parent)
|
||
return -1;
|
||
if (parent == this.top.contentDOM) {
|
||
let desc = domNode.pmViewDesc;
|
||
if (desc)
|
||
for (let i = this.index; i < this.top.children.length; i++) {
|
||
if (this.top.children[i] == desc)
|
||
return i;
|
||
}
|
||
return -1;
|
||
}
|
||
domNode = parent;
|
||
}
|
||
}
|
||
// Try to update the next node, if any, to the given data. Checks
|
||
// pre-matches to avoid overwriting nodes that could still be used.
|
||
updateNextNode(node, outerDeco, innerDeco, view, index2, pos) {
|
||
for (let i = this.index; i < this.top.children.length; i++) {
|
||
let next = this.top.children[i];
|
||
if (next instanceof NodeViewDesc) {
|
||
let preMatch2 = this.preMatch.matched.get(next);
|
||
if (preMatch2 != null && preMatch2 != index2)
|
||
return false;
|
||
let nextDOM = next.dom, updated;
|
||
let locked = this.isLocked(nextDOM) && !(node.isText && next.node && next.node.isText && next.nodeDOM.nodeValue == node.text && next.dirty != NODE_DIRTY && sameOuterDeco(outerDeco, next.outerDeco));
|
||
if (!locked && next.update(node, outerDeco, innerDeco, view)) {
|
||
this.destroyBetween(this.index, i);
|
||
if (next.dom != nextDOM)
|
||
this.changed = true;
|
||
this.index++;
|
||
return true;
|
||
} else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
|
||
this.destroyBetween(this.index, i);
|
||
this.top.children[this.index] = updated;
|
||
if (updated.contentDOM) {
|
||
updated.dirty = CONTENT_DIRTY;
|
||
updated.updateChildren(view, pos + 1);
|
||
updated.dirty = NOT_DIRTY;
|
||
}
|
||
this.changed = true;
|
||
this.index++;
|
||
return true;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
// When a node with content is replaced by a different node with
|
||
// identical content, move over its children.
|
||
recreateWrapper(next, node, outerDeco, innerDeco, view, pos) {
|
||
if (next.dirty || node.isAtom || !next.children.length || !next.node.content.eq(node.content) || !sameOuterDeco(outerDeco, next.outerDeco) || !innerDeco.eq(next.innerDeco))
|
||
return null;
|
||
let wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos);
|
||
if (wrapper.contentDOM) {
|
||
wrapper.children = next.children;
|
||
next.children = [];
|
||
for (let ch of wrapper.children)
|
||
ch.parent = wrapper;
|
||
}
|
||
next.destroy();
|
||
return wrapper;
|
||
}
|
||
// Insert the node as a newly created node desc.
|
||
addNode(node, outerDeco, innerDeco, view, pos) {
|
||
let desc = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos);
|
||
if (desc.contentDOM)
|
||
desc.updateChildren(view, pos + 1);
|
||
this.top.children.splice(this.index++, 0, desc);
|
||
this.changed = true;
|
||
}
|
||
placeWidget(widget, view, pos) {
|
||
let next = this.index < this.top.children.length ? this.top.children[this.index] : null;
|
||
if (next && next.matchesWidget(widget) && (widget == next.widget || !next.widget.type.toDOM.parentNode)) {
|
||
this.index++;
|
||
} else {
|
||
let desc = new WidgetViewDesc(this.top, widget, view, pos);
|
||
this.top.children.splice(this.index++, 0, desc);
|
||
this.changed = true;
|
||
}
|
||
}
|
||
// Make sure a textblock looks and behaves correctly in
|
||
// contentEditable.
|
||
addTextblockHacks() {
|
||
let lastChild = this.top.children[this.index - 1], parent = this.top;
|
||
while (lastChild instanceof MarkViewDesc) {
|
||
parent = lastChild;
|
||
lastChild = parent.children[parent.children.length - 1];
|
||
}
|
||
if (!lastChild || // Empty textblock
|
||
!(lastChild instanceof TextViewDesc) || /\n$/.test(lastChild.node.text) || this.view.requiresGeckoHackNode && /\s$/.test(lastChild.node.text)) {
|
||
if ((safari || chrome) && lastChild && lastChild.dom.contentEditable == "false")
|
||
this.addHackNode("IMG", parent);
|
||
this.addHackNode("BR", this.top);
|
||
}
|
||
}
|
||
addHackNode(nodeName, parent) {
|
||
if (parent == this.top && this.index < parent.children.length && parent.children[this.index].matchesHack(nodeName)) {
|
||
this.index++;
|
||
} else {
|
||
let dom = document.createElement(nodeName);
|
||
if (nodeName == "IMG") {
|
||
dom.className = "ProseMirror-separator";
|
||
dom.alt = "";
|
||
}
|
||
if (nodeName == "BR")
|
||
dom.className = "ProseMirror-trailingBreak";
|
||
let hack = new TrailingHackViewDesc(this.top, [], dom, null);
|
||
if (parent != this.top)
|
||
parent.children.push(hack);
|
||
else
|
||
parent.children.splice(this.index++, 0, hack);
|
||
this.changed = true;
|
||
}
|
||
}
|
||
isLocked(node) {
|
||
return this.lock && (node == this.lock || node.nodeType == 1 && node.contains(this.lock.parentNode));
|
||
}
|
||
};
|
||
function preMatch(frag, parentDesc) {
|
||
let curDesc = parentDesc, descI = curDesc.children.length;
|
||
let fI = frag.childCount, matched = /* @__PURE__ */ new Map(), matches = [];
|
||
outer: while (fI > 0) {
|
||
let desc;
|
||
for (; ; ) {
|
||
if (descI) {
|
||
let next = curDesc.children[descI - 1];
|
||
if (next instanceof MarkViewDesc) {
|
||
curDesc = next;
|
||
descI = next.children.length;
|
||
} else {
|
||
desc = next;
|
||
descI--;
|
||
break;
|
||
}
|
||
} else if (curDesc == parentDesc) {
|
||
break outer;
|
||
} else {
|
||
descI = curDesc.parent.children.indexOf(curDesc);
|
||
curDesc = curDesc.parent;
|
||
}
|
||
}
|
||
let node = desc.node;
|
||
if (!node)
|
||
continue;
|
||
if (node != frag.child(fI - 1))
|
||
break;
|
||
--fI;
|
||
matched.set(desc, fI);
|
||
matches.push(desc);
|
||
}
|
||
return { index: fI, matched, matches: matches.reverse() };
|
||
}
|
||
function compareSide(a, b) {
|
||
return a.type.side - b.type.side;
|
||
}
|
||
function iterDeco(parent, deco, onWidget, onNode) {
|
||
let locals = deco.locals(parent), offset = 0;
|
||
if (locals.length == 0) {
|
||
for (let i = 0; i < parent.childCount; i++) {
|
||
let child = parent.child(i);
|
||
onNode(child, locals, deco.forChild(offset, child), i);
|
||
offset += child.nodeSize;
|
||
}
|
||
return;
|
||
}
|
||
let decoIndex = 0, active = [], restNode = null;
|
||
for (let parentIndex = 0; ; ) {
|
||
let widget, widgets;
|
||
while (decoIndex < locals.length && locals[decoIndex].to == offset) {
|
||
let next = locals[decoIndex++];
|
||
if (next.widget) {
|
||
if (!widget)
|
||
widget = next;
|
||
else
|
||
(widgets || (widgets = [widget])).push(next);
|
||
}
|
||
}
|
||
if (widget) {
|
||
if (widgets) {
|
||
widgets.sort(compareSide);
|
||
for (let i = 0; i < widgets.length; i++)
|
||
onWidget(widgets[i], parentIndex, !!restNode);
|
||
} else {
|
||
onWidget(widget, parentIndex, !!restNode);
|
||
}
|
||
}
|
||
let child, index2;
|
||
if (restNode) {
|
||
index2 = -1;
|
||
child = restNode;
|
||
restNode = null;
|
||
} else if (parentIndex < parent.childCount) {
|
||
index2 = parentIndex;
|
||
child = parent.child(parentIndex++);
|
||
} else {
|
||
break;
|
||
}
|
||
for (let i = 0; i < active.length; i++)
|
||
if (active[i].to <= offset)
|
||
active.splice(i--, 1);
|
||
while (decoIndex < locals.length && locals[decoIndex].from <= offset && locals[decoIndex].to > offset)
|
||
active.push(locals[decoIndex++]);
|
||
let end = offset + child.nodeSize;
|
||
if (child.isText) {
|
||
let cutAt = end;
|
||
if (decoIndex < locals.length && locals[decoIndex].from < cutAt)
|
||
cutAt = locals[decoIndex].from;
|
||
for (let i = 0; i < active.length; i++)
|
||
if (active[i].to < cutAt)
|
||
cutAt = active[i].to;
|
||
if (cutAt < end) {
|
||
restNode = child.cut(cutAt - offset);
|
||
child = child.cut(0, cutAt - offset);
|
||
end = cutAt;
|
||
index2 = -1;
|
||
}
|
||
} else {
|
||
while (decoIndex < locals.length && locals[decoIndex].to < end)
|
||
decoIndex++;
|
||
}
|
||
let outerDeco = child.isInline && !child.isLeaf ? active.filter((d) => !d.inline) : active.slice();
|
||
onNode(child, outerDeco, deco.forChild(offset, child), index2);
|
||
offset = end;
|
||
}
|
||
}
|
||
function iosHacks(dom) {
|
||
if (dom.nodeName == "UL" || dom.nodeName == "OL") {
|
||
let oldCSS = dom.style.cssText;
|
||
dom.style.cssText = oldCSS + "; list-style: square !important";
|
||
window.getComputedStyle(dom).listStyle;
|
||
dom.style.cssText = oldCSS;
|
||
}
|
||
}
|
||
function findTextInFragment(frag, text, from, to) {
|
||
for (let i = 0, pos = 0; i < frag.childCount && pos <= to; ) {
|
||
let child = frag.child(i++), childStart = pos;
|
||
pos += child.nodeSize;
|
||
if (!child.isText)
|
||
continue;
|
||
let str = child.text;
|
||
while (i < frag.childCount) {
|
||
let next = frag.child(i++);
|
||
pos += next.nodeSize;
|
||
if (!next.isText)
|
||
break;
|
||
str += next.text;
|
||
}
|
||
if (pos >= from) {
|
||
if (pos >= to && str.slice(to - text.length - childStart, to - childStart) == text)
|
||
return to - text.length;
|
||
let found = childStart < to ? str.lastIndexOf(text, to - childStart - 1) : -1;
|
||
if (found >= 0 && found + text.length + childStart >= from)
|
||
return childStart + found;
|
||
if (from == to && str.length >= to + text.length - childStart && str.slice(to - childStart, to - childStart + text.length) == text)
|
||
return to;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
function replaceNodes(nodes, from, to, view, replacement) {
|
||
let result = [];
|
||
for (let i = 0, off = 0; i < nodes.length; i++) {
|
||
let child = nodes[i], start = off, end = off += child.size;
|
||
if (start >= to || end <= from) {
|
||
result.push(child);
|
||
} else {
|
||
if (start < from)
|
||
result.push(child.slice(0, from - start, view));
|
||
if (replacement) {
|
||
result.push(replacement);
|
||
replacement = void 0;
|
||
}
|
||
if (end > to)
|
||
result.push(child.slice(to - start, child.size, view));
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
function selectionFromDOM(view, origin = null) {
|
||
let domSel = view.domSelectionRange(), doc2 = view.state.doc;
|
||
if (!domSel.focusNode)
|
||
return null;
|
||
let nearestDesc = view.docView.nearestDesc(domSel.focusNode), inWidget = nearestDesc && nearestDesc.size == 0;
|
||
let head = view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset, 1);
|
||
if (head < 0)
|
||
return null;
|
||
let $head = doc2.resolve(head), anchor, selection;
|
||
if (selectionCollapsed(domSel)) {
|
||
anchor = head;
|
||
while (nearestDesc && !nearestDesc.node)
|
||
nearestDesc = nearestDesc.parent;
|
||
let nearestDescNode = nearestDesc.node;
|
||
if (nearestDesc && nearestDescNode.isAtom && NodeSelection.isSelectable(nearestDescNode) && nearestDesc.parent && !(nearestDescNode.isInline && isOnEdge(domSel.focusNode, domSel.focusOffset, nearestDesc.dom))) {
|
||
let pos = nearestDesc.posBefore;
|
||
selection = new NodeSelection(head == pos ? $head : doc2.resolve(pos));
|
||
}
|
||
} else {
|
||
if (domSel instanceof view.dom.ownerDocument.defaultView.Selection && domSel.rangeCount > 1) {
|
||
let min = head, max = head;
|
||
for (let i = 0; i < domSel.rangeCount; i++) {
|
||
let range = domSel.getRangeAt(i);
|
||
min = Math.min(min, view.docView.posFromDOM(range.startContainer, range.startOffset, 1));
|
||
max = Math.max(max, view.docView.posFromDOM(range.endContainer, range.endOffset, -1));
|
||
}
|
||
if (min < 0)
|
||
return null;
|
||
[anchor, head] = max == view.state.selection.anchor ? [max, min] : [min, max];
|
||
$head = doc2.resolve(head);
|
||
} else {
|
||
anchor = view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset, 1);
|
||
}
|
||
if (anchor < 0)
|
||
return null;
|
||
}
|
||
let $anchor = doc2.resolve(anchor);
|
||
if (!selection) {
|
||
let bias = origin == "pointer" || view.state.selection.head < $head.pos && !inWidget ? 1 : -1;
|
||
selection = selectionBetween(view, $anchor, $head, bias);
|
||
}
|
||
return selection;
|
||
}
|
||
function editorOwnsSelection(view) {
|
||
return view.editable ? view.hasFocus() : hasSelection(view) && document.activeElement && document.activeElement.contains(view.dom);
|
||
}
|
||
function selectionToDOM(view, force = false) {
|
||
let sel = view.state.selection;
|
||
syncNodeSelection(view, sel);
|
||
if (!editorOwnsSelection(view))
|
||
return;
|
||
if (!force && view.input.mouseDown && view.input.mouseDown.allowDefault && chrome) {
|
||
let domSel = view.domSelectionRange(), curSel = view.domObserver.currentSelection;
|
||
if (domSel.anchorNode && curSel.anchorNode && isEquivalentPosition(domSel.anchorNode, domSel.anchorOffset, curSel.anchorNode, curSel.anchorOffset)) {
|
||
view.input.mouseDown.delayedSelectionSync = true;
|
||
view.domObserver.setCurSelection();
|
||
return;
|
||
}
|
||
}
|
||
view.domObserver.disconnectSelection();
|
||
if (view.cursorWrapper) {
|
||
selectCursorWrapper(view);
|
||
} else {
|
||
let { anchor, head } = sel, resetEditableFrom, resetEditableTo;
|
||
if (brokenSelectBetweenUneditable && !(sel instanceof TextSelection)) {
|
||
if (!sel.$from.parent.inlineContent)
|
||
resetEditableFrom = temporarilyEditableNear(view, sel.from);
|
||
if (!sel.empty && !sel.$from.parent.inlineContent)
|
||
resetEditableTo = temporarilyEditableNear(view, sel.to);
|
||
}
|
||
view.docView.setSelection(anchor, head, view, force);
|
||
if (brokenSelectBetweenUneditable) {
|
||
if (resetEditableFrom)
|
||
resetEditable(resetEditableFrom);
|
||
if (resetEditableTo)
|
||
resetEditable(resetEditableTo);
|
||
}
|
||
if (sel.visible) {
|
||
view.dom.classList.remove("ProseMirror-hideselection");
|
||
} else {
|
||
view.dom.classList.add("ProseMirror-hideselection");
|
||
if ("onselectionchange" in document)
|
||
removeClassOnSelectionChange(view);
|
||
}
|
||
}
|
||
view.domObserver.setCurSelection();
|
||
view.domObserver.connectSelection();
|
||
}
|
||
var brokenSelectBetweenUneditable = safari || chrome && chrome_version < 63;
|
||
function temporarilyEditableNear(view, pos) {
|
||
let { node, offset } = view.docView.domFromPos(pos, 0);
|
||
let after = offset < node.childNodes.length ? node.childNodes[offset] : null;
|
||
let before = offset ? node.childNodes[offset - 1] : null;
|
||
if (safari && after && after.contentEditable == "false")
|
||
return setEditable(after);
|
||
if ((!after || after.contentEditable == "false") && (!before || before.contentEditable == "false")) {
|
||
if (after)
|
||
return setEditable(after);
|
||
else if (before)
|
||
return setEditable(before);
|
||
}
|
||
}
|
||
function setEditable(element) {
|
||
element.contentEditable = "true";
|
||
if (safari && element.draggable) {
|
||
element.draggable = false;
|
||
element.wasDraggable = true;
|
||
}
|
||
return element;
|
||
}
|
||
function resetEditable(element) {
|
||
element.contentEditable = "false";
|
||
if (element.wasDraggable) {
|
||
element.draggable = true;
|
||
element.wasDraggable = null;
|
||
}
|
||
}
|
||
function removeClassOnSelectionChange(view) {
|
||
let doc2 = view.dom.ownerDocument;
|
||
doc2.removeEventListener("selectionchange", view.input.hideSelectionGuard);
|
||
let domSel = view.domSelectionRange();
|
||
let node = domSel.anchorNode, offset = domSel.anchorOffset;
|
||
doc2.addEventListener("selectionchange", view.input.hideSelectionGuard = () => {
|
||
if (domSel.anchorNode != node || domSel.anchorOffset != offset) {
|
||
doc2.removeEventListener("selectionchange", view.input.hideSelectionGuard);
|
||
setTimeout(() => {
|
||
if (!editorOwnsSelection(view) || view.state.selection.visible)
|
||
view.dom.classList.remove("ProseMirror-hideselection");
|
||
}, 20);
|
||
}
|
||
});
|
||
}
|
||
function selectCursorWrapper(view) {
|
||
let domSel = view.domSelection(), range = document.createRange();
|
||
if (!domSel)
|
||
return;
|
||
let node = view.cursorWrapper.dom, img = node.nodeName == "IMG";
|
||
if (img)
|
||
range.setStart(node.parentNode, domIndex(node) + 1);
|
||
else
|
||
range.setStart(node, 0);
|
||
range.collapse(true);
|
||
domSel.removeAllRanges();
|
||
domSel.addRange(range);
|
||
if (!img && !view.state.selection.visible && ie && ie_version <= 11) {
|
||
node.disabled = true;
|
||
node.disabled = false;
|
||
}
|
||
}
|
||
function syncNodeSelection(view, sel) {
|
||
if (sel instanceof NodeSelection) {
|
||
let desc = view.docView.descAt(sel.from);
|
||
if (desc != view.lastSelectedViewDesc) {
|
||
clearNodeSelection(view);
|
||
if (desc)
|
||
desc.selectNode();
|
||
view.lastSelectedViewDesc = desc;
|
||
}
|
||
} else {
|
||
clearNodeSelection(view);
|
||
}
|
||
}
|
||
function clearNodeSelection(view) {
|
||
if (view.lastSelectedViewDesc) {
|
||
if (view.lastSelectedViewDesc.parent)
|
||
view.lastSelectedViewDesc.deselectNode();
|
||
view.lastSelectedViewDesc = void 0;
|
||
}
|
||
}
|
||
function selectionBetween(view, $anchor, $head, bias) {
|
||
return view.someProp("createSelectionBetween", (f) => f(view, $anchor, $head)) || TextSelection.between($anchor, $head, bias);
|
||
}
|
||
function hasFocusAndSelection(view) {
|
||
if (view.editable && !view.hasFocus())
|
||
return false;
|
||
return hasSelection(view);
|
||
}
|
||
function hasSelection(view) {
|
||
let sel = view.domSelectionRange();
|
||
if (!sel.anchorNode)
|
||
return false;
|
||
try {
|
||
return view.dom.contains(sel.anchorNode.nodeType == 3 ? sel.anchorNode.parentNode : sel.anchorNode) && (view.editable || view.dom.contains(sel.focusNode.nodeType == 3 ? sel.focusNode.parentNode : sel.focusNode));
|
||
} catch (_) {
|
||
return false;
|
||
}
|
||
}
|
||
function anchorInRightPlace(view) {
|
||
let anchorDOM = view.docView.domFromPos(view.state.selection.anchor, 0);
|
||
let domSel = view.domSelectionRange();
|
||
return isEquivalentPosition(anchorDOM.node, anchorDOM.offset, domSel.anchorNode, domSel.anchorOffset);
|
||
}
|
||
function moveSelectionBlock(state, dir) {
|
||
let { $anchor, $head } = state.selection;
|
||
let $side = dir > 0 ? $anchor.max($head) : $anchor.min($head);
|
||
let $start = !$side.parent.inlineContent ? $side : $side.depth ? state.doc.resolve(dir > 0 ? $side.after() : $side.before()) : null;
|
||
return $start && Selection.findFrom($start, dir);
|
||
}
|
||
function apply(view, sel) {
|
||
view.dispatch(view.state.tr.setSelection(sel).scrollIntoView());
|
||
return true;
|
||
}
|
||
function selectHorizontally(view, dir, mods) {
|
||
let sel = view.state.selection;
|
||
if (sel instanceof TextSelection) {
|
||
if (mods.indexOf("s") > -1) {
|
||
let { $head } = sel, node = $head.textOffset ? null : dir < 0 ? $head.nodeBefore : $head.nodeAfter;
|
||
if (!node || node.isText || !node.isLeaf)
|
||
return false;
|
||
let $newHead = view.state.doc.resolve($head.pos + node.nodeSize * (dir < 0 ? -1 : 1));
|
||
return apply(view, new TextSelection(sel.$anchor, $newHead));
|
||
} else if (!sel.empty) {
|
||
return false;
|
||
} else if (view.endOfTextblock(dir > 0 ? "forward" : "backward")) {
|
||
let next = moveSelectionBlock(view.state, dir);
|
||
if (next && next instanceof NodeSelection)
|
||
return apply(view, next);
|
||
return false;
|
||
} else if (!(mac && mods.indexOf("m") > -1)) {
|
||
let $head = sel.$head, node = $head.textOffset ? null : dir < 0 ? $head.nodeBefore : $head.nodeAfter, desc;
|
||
if (!node || node.isText)
|
||
return false;
|
||
let nodePos = dir < 0 ? $head.pos - node.nodeSize : $head.pos;
|
||
if (!(node.isAtom || (desc = view.docView.descAt(nodePos)) && !desc.contentDOM))
|
||
return false;
|
||
if (NodeSelection.isSelectable(node)) {
|
||
return apply(view, new NodeSelection(dir < 0 ? view.state.doc.resolve($head.pos - node.nodeSize) : $head));
|
||
} else if (webkit) {
|
||
return apply(view, new TextSelection(view.state.doc.resolve(dir < 0 ? nodePos : nodePos + node.nodeSize)));
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
} else if (sel instanceof NodeSelection && sel.node.isInline) {
|
||
return apply(view, new TextSelection(dir > 0 ? sel.$to : sel.$from));
|
||
} else {
|
||
let next = moveSelectionBlock(view.state, dir);
|
||
if (next)
|
||
return apply(view, next);
|
||
return false;
|
||
}
|
||
}
|
||
function nodeLen(node) {
|
||
return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
|
||
}
|
||
function isIgnorable(dom, dir) {
|
||
let desc = dom.pmViewDesc;
|
||
return desc && desc.size == 0 && (dir < 0 || dom.nextSibling || dom.nodeName != "BR");
|
||
}
|
||
function skipIgnoredNodes(view, dir) {
|
||
return dir < 0 ? skipIgnoredNodesBefore(view) : skipIgnoredNodesAfter(view);
|
||
}
|
||
function skipIgnoredNodesBefore(view) {
|
||
let sel = view.domSelectionRange();
|
||
let node = sel.focusNode, offset = sel.focusOffset;
|
||
if (!node)
|
||
return;
|
||
let moveNode, moveOffset, force = false;
|
||
if (gecko && node.nodeType == 1 && offset < nodeLen(node) && isIgnorable(node.childNodes[offset], -1))
|
||
force = true;
|
||
for (; ; ) {
|
||
if (offset > 0) {
|
||
if (node.nodeType != 1) {
|
||
break;
|
||
} else {
|
||
let before = node.childNodes[offset - 1];
|
||
if (isIgnorable(before, -1)) {
|
||
moveNode = node;
|
||
moveOffset = --offset;
|
||
} else if (before.nodeType == 3) {
|
||
node = before;
|
||
offset = node.nodeValue.length;
|
||
} else
|
||
break;
|
||
}
|
||
} else if (isBlockNode(node)) {
|
||
break;
|
||
} else {
|
||
let prev = node.previousSibling;
|
||
while (prev && isIgnorable(prev, -1)) {
|
||
moveNode = node.parentNode;
|
||
moveOffset = domIndex(prev);
|
||
prev = prev.previousSibling;
|
||
}
|
||
if (!prev) {
|
||
node = node.parentNode;
|
||
if (node == view.dom)
|
||
break;
|
||
offset = 0;
|
||
} else {
|
||
node = prev;
|
||
offset = nodeLen(node);
|
||
}
|
||
}
|
||
}
|
||
if (force)
|
||
setSelFocus(view, node, offset);
|
||
else if (moveNode)
|
||
setSelFocus(view, moveNode, moveOffset);
|
||
}
|
||
function skipIgnoredNodesAfter(view) {
|
||
let sel = view.domSelectionRange();
|
||
let node = sel.focusNode, offset = sel.focusOffset;
|
||
if (!node)
|
||
return;
|
||
let len = nodeLen(node);
|
||
let moveNode, moveOffset;
|
||
for (; ; ) {
|
||
if (offset < len) {
|
||
if (node.nodeType != 1)
|
||
break;
|
||
let after = node.childNodes[offset];
|
||
if (isIgnorable(after, 1)) {
|
||
moveNode = node;
|
||
moveOffset = ++offset;
|
||
} else
|
||
break;
|
||
} else if (isBlockNode(node)) {
|
||
break;
|
||
} else {
|
||
let next = node.nextSibling;
|
||
while (next && isIgnorable(next, 1)) {
|
||
moveNode = next.parentNode;
|
||
moveOffset = domIndex(next) + 1;
|
||
next = next.nextSibling;
|
||
}
|
||
if (!next) {
|
||
node = node.parentNode;
|
||
if (node == view.dom)
|
||
break;
|
||
offset = len = 0;
|
||
} else {
|
||
node = next;
|
||
offset = 0;
|
||
len = nodeLen(node);
|
||
}
|
||
}
|
||
}
|
||
if (moveNode)
|
||
setSelFocus(view, moveNode, moveOffset);
|
||
}
|
||
function isBlockNode(dom) {
|
||
let desc = dom.pmViewDesc;
|
||
return desc && desc.node && desc.node.isBlock;
|
||
}
|
||
function textNodeAfter(node, offset) {
|
||
while (node && offset == node.childNodes.length && !hasBlockDesc(node)) {
|
||
offset = domIndex(node) + 1;
|
||
node = node.parentNode;
|
||
}
|
||
while (node && offset < node.childNodes.length) {
|
||
let next = node.childNodes[offset];
|
||
if (next.nodeType == 3)
|
||
return next;
|
||
if (next.nodeType == 1 && next.contentEditable == "false")
|
||
break;
|
||
node = next;
|
||
offset = 0;
|
||
}
|
||
}
|
||
function textNodeBefore(node, offset) {
|
||
while (node && !offset && !hasBlockDesc(node)) {
|
||
offset = domIndex(node);
|
||
node = node.parentNode;
|
||
}
|
||
while (node && offset) {
|
||
let next = node.childNodes[offset - 1];
|
||
if (next.nodeType == 3)
|
||
return next;
|
||
if (next.nodeType == 1 && next.contentEditable == "false")
|
||
break;
|
||
node = next;
|
||
offset = node.childNodes.length;
|
||
}
|
||
}
|
||
function setSelFocus(view, node, offset) {
|
||
if (node.nodeType != 3) {
|
||
let before, after;
|
||
if (after = textNodeAfter(node, offset)) {
|
||
node = after;
|
||
offset = 0;
|
||
} else if (before = textNodeBefore(node, offset)) {
|
||
node = before;
|
||
offset = before.nodeValue.length;
|
||
}
|
||
}
|
||
let sel = view.domSelection();
|
||
if (!sel)
|
||
return;
|
||
if (selectionCollapsed(sel)) {
|
||
let range = document.createRange();
|
||
range.setEnd(node, offset);
|
||
range.setStart(node, offset);
|
||
sel.removeAllRanges();
|
||
sel.addRange(range);
|
||
} else if (sel.extend) {
|
||
sel.extend(node, offset);
|
||
}
|
||
view.domObserver.setCurSelection();
|
||
let { state } = view;
|
||
setTimeout(() => {
|
||
if (view.state == state)
|
||
selectionToDOM(view);
|
||
}, 50);
|
||
}
|
||
function findDirection(view, pos) {
|
||
let $pos = view.state.doc.resolve(pos);
|
||
if (!(chrome || windows) && $pos.parent.inlineContent) {
|
||
let coords = view.coordsAtPos(pos);
|
||
if (pos > $pos.start()) {
|
||
let before = view.coordsAtPos(pos - 1);
|
||
let mid = (before.top + before.bottom) / 2;
|
||
if (mid > coords.top && mid < coords.bottom && Math.abs(before.left - coords.left) > 1)
|
||
return before.left < coords.left ? "ltr" : "rtl";
|
||
}
|
||
if (pos < $pos.end()) {
|
||
let after = view.coordsAtPos(pos + 1);
|
||
let mid = (after.top + after.bottom) / 2;
|
||
if (mid > coords.top && mid < coords.bottom && Math.abs(after.left - coords.left) > 1)
|
||
return after.left > coords.left ? "ltr" : "rtl";
|
||
}
|
||
}
|
||
let computed = getComputedStyle(view.dom).direction;
|
||
return computed == "rtl" ? "rtl" : "ltr";
|
||
}
|
||
function selectVertically(view, dir, mods) {
|
||
let sel = view.state.selection;
|
||
if (sel instanceof TextSelection && !sel.empty || mods.indexOf("s") > -1)
|
||
return false;
|
||
if (mac && mods.indexOf("m") > -1)
|
||
return false;
|
||
let { $from, $to } = sel;
|
||
if (!$from.parent.inlineContent || view.endOfTextblock(dir < 0 ? "up" : "down")) {
|
||
let next = moveSelectionBlock(view.state, dir);
|
||
if (next && next instanceof NodeSelection)
|
||
return apply(view, next);
|
||
}
|
||
if (!$from.parent.inlineContent) {
|
||
let side = dir < 0 ? $from : $to;
|
||
let beyond = sel instanceof AllSelection ? Selection.near(side, dir) : Selection.findFrom(side, dir);
|
||
return beyond ? apply(view, beyond) : false;
|
||
}
|
||
return false;
|
||
}
|
||
function stopNativeHorizontalDelete(view, dir) {
|
||
if (!(view.state.selection instanceof TextSelection))
|
||
return true;
|
||
let { $head, $anchor, empty: empty2 } = view.state.selection;
|
||
if (!$head.sameParent($anchor))
|
||
return true;
|
||
if (!empty2)
|
||
return false;
|
||
if (view.endOfTextblock(dir > 0 ? "forward" : "backward"))
|
||
return true;
|
||
let nextNode = !$head.textOffset && (dir < 0 ? $head.nodeBefore : $head.nodeAfter);
|
||
if (nextNode && !nextNode.isText) {
|
||
let tr = view.state.tr;
|
||
if (dir < 0)
|
||
tr.delete($head.pos - nextNode.nodeSize, $head.pos);
|
||
else
|
||
tr.delete($head.pos, $head.pos + nextNode.nodeSize);
|
||
view.dispatch(tr);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
function switchEditable(view, node, state) {
|
||
view.domObserver.stop();
|
||
node.contentEditable = state;
|
||
view.domObserver.start();
|
||
}
|
||
function safariDownArrowBug(view) {
|
||
if (!safari || view.state.selection.$head.parentOffset > 0)
|
||
return false;
|
||
let { focusNode, focusOffset } = view.domSelectionRange();
|
||
if (focusNode && focusNode.nodeType == 1 && focusOffset == 0 && focusNode.firstChild && focusNode.firstChild.contentEditable == "false") {
|
||
let child = focusNode.firstChild;
|
||
switchEditable(view, child, "true");
|
||
setTimeout(() => switchEditable(view, child, "false"), 20);
|
||
}
|
||
return false;
|
||
}
|
||
function getMods(event) {
|
||
let result = "";
|
||
if (event.ctrlKey)
|
||
result += "c";
|
||
if (event.metaKey)
|
||
result += "m";
|
||
if (event.altKey)
|
||
result += "a";
|
||
if (event.shiftKey)
|
||
result += "s";
|
||
return result;
|
||
}
|
||
function captureKeyDown(view, event) {
|
||
let code = event.keyCode, mods = getMods(event);
|
||
if (code == 8 || mac && code == 72 && mods == "c") {
|
||
return stopNativeHorizontalDelete(view, -1) || skipIgnoredNodes(view, -1);
|
||
} else if (code == 46 && !event.shiftKey || mac && code == 68 && mods == "c") {
|
||
return stopNativeHorizontalDelete(view, 1) || skipIgnoredNodes(view, 1);
|
||
} else if (code == 13 || code == 27) {
|
||
return true;
|
||
} else if (code == 37 || mac && code == 66 && mods == "c") {
|
||
let dir = code == 37 ? findDirection(view, view.state.selection.from) == "ltr" ? -1 : 1 : -1;
|
||
return selectHorizontally(view, dir, mods) || skipIgnoredNodes(view, dir);
|
||
} else if (code == 39 || mac && code == 70 && mods == "c") {
|
||
let dir = code == 39 ? findDirection(view, view.state.selection.from) == "ltr" ? 1 : -1 : 1;
|
||
return selectHorizontally(view, dir, mods) || skipIgnoredNodes(view, dir);
|
||
} else if (code == 38 || mac && code == 80 && mods == "c") {
|
||
return selectVertically(view, -1, mods) || skipIgnoredNodes(view, -1);
|
||
} else if (code == 40 || mac && code == 78 && mods == "c") {
|
||
return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodes(view, 1);
|
||
} else if (mods == (mac ? "m" : "c") && (code == 66 || code == 73 || code == 89 || code == 90)) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
function serializeForClipboard(view, slice) {
|
||
view.someProp("transformCopied", (f) => {
|
||
slice = f(slice, view);
|
||
});
|
||
let context = [], { content, openStart, openEnd } = slice;
|
||
while (openStart > 1 && openEnd > 1 && content.childCount == 1 && content.firstChild.childCount == 1) {
|
||
openStart--;
|
||
openEnd--;
|
||
let node = content.firstChild;
|
||
context.push(node.type.name, node.attrs != node.type.defaultAttrs ? node.attrs : null);
|
||
content = node.content;
|
||
}
|
||
let serializer = view.someProp("clipboardSerializer") || DOMSerializer.fromSchema(view.state.schema);
|
||
let doc2 = detachedDoc(), wrap = doc2.createElement("div");
|
||
wrap.appendChild(serializer.serializeFragment(content, { document: doc2 }));
|
||
let firstChild = wrap.firstChild, needsWrap, wrappers = 0;
|
||
while (firstChild && firstChild.nodeType == 1 && (needsWrap = wrapMap[firstChild.nodeName.toLowerCase()])) {
|
||
for (let i = needsWrap.length - 1; i >= 0; i--) {
|
||
let wrapper = doc2.createElement(needsWrap[i]);
|
||
while (wrap.firstChild)
|
||
wrapper.appendChild(wrap.firstChild);
|
||
wrap.appendChild(wrapper);
|
||
wrappers++;
|
||
}
|
||
firstChild = wrap.firstChild;
|
||
}
|
||
if (firstChild && firstChild.nodeType == 1)
|
||
firstChild.setAttribute("data-pm-slice", `${openStart} ${openEnd}${wrappers ? ` -${wrappers}` : ""} ${JSON.stringify(context)}`);
|
||
let text = view.someProp("clipboardTextSerializer", (f) => f(slice, view)) || slice.content.textBetween(0, slice.content.size, "\n\n");
|
||
return { dom: wrap, text, slice };
|
||
}
|
||
function parseFromClipboard(view, text, html, plainText, $context) {
|
||
let inCode = $context.parent.type.spec.code;
|
||
let dom, slice;
|
||
if (!html && !text)
|
||
return null;
|
||
let asText = text && (plainText || inCode || !html);
|
||
if (asText) {
|
||
view.someProp("transformPastedText", (f) => {
|
||
text = f(text, inCode || plainText, view);
|
||
});
|
||
if (inCode)
|
||
return text ? new Slice(Fragment.from(view.state.schema.text(text.replace(/\r\n?/g, "\n"))), 0, 0) : Slice.empty;
|
||
let parsed = view.someProp("clipboardTextParser", (f) => f(text, $context, plainText, view));
|
||
if (parsed) {
|
||
slice = parsed;
|
||
} else {
|
||
let marks = $context.marks();
|
||
let { schema } = view.state, serializer = DOMSerializer.fromSchema(schema);
|
||
dom = document.createElement("div");
|
||
text.split(/(?:\r\n?|\n)+/).forEach((block) => {
|
||
let p = dom.appendChild(document.createElement("p"));
|
||
if (block)
|
||
p.appendChild(serializer.serializeNode(schema.text(block, marks)));
|
||
});
|
||
}
|
||
} else {
|
||
view.someProp("transformPastedHTML", (f) => {
|
||
html = f(html, view);
|
||
});
|
||
dom = readHTML(html);
|
||
if (webkit)
|
||
restoreReplacedSpaces(dom);
|
||
}
|
||
let contextNode = dom && dom.querySelector("[data-pm-slice]");
|
||
let sliceData = contextNode && /^(\d+) (\d+)(?: -(\d+))? (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "");
|
||
if (sliceData && sliceData[3])
|
||
for (let i = +sliceData[3]; i > 0; i--) {
|
||
let child = dom.firstChild;
|
||
while (child && child.nodeType != 1)
|
||
child = child.nextSibling;
|
||
if (!child)
|
||
break;
|
||
dom = child;
|
||
}
|
||
if (!slice) {
|
||
let parser = view.someProp("clipboardParser") || view.someProp("domParser") || DOMParser.fromSchema(view.state.schema);
|
||
slice = parser.parseSlice(dom, {
|
||
preserveWhitespace: !!(asText || sliceData),
|
||
context: $context,
|
||
ruleFromNode(dom2) {
|
||
if (dom2.nodeName == "BR" && !dom2.nextSibling && dom2.parentNode && !inlineParents.test(dom2.parentNode.nodeName))
|
||
return { ignore: true };
|
||
return null;
|
||
}
|
||
});
|
||
}
|
||
if (sliceData) {
|
||
slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[4]);
|
||
} else {
|
||
slice = Slice.maxOpen(normalizeSiblings(slice.content, $context), true);
|
||
if (slice.openStart || slice.openEnd) {
|
||
let openStart = 0, openEnd = 0;
|
||
for (let node = slice.content.firstChild; openStart < slice.openStart && !node.type.spec.isolating; openStart++, node = node.firstChild) {
|
||
}
|
||
for (let node = slice.content.lastChild; openEnd < slice.openEnd && !node.type.spec.isolating; openEnd++, node = node.lastChild) {
|
||
}
|
||
slice = closeSlice(slice, openStart, openEnd);
|
||
}
|
||
}
|
||
view.someProp("transformPasted", (f) => {
|
||
slice = f(slice, view);
|
||
});
|
||
return slice;
|
||
}
|
||
var inlineParents = /^(a|abbr|acronym|b|cite|code|del|em|i|ins|kbd|label|output|q|ruby|s|samp|span|strong|sub|sup|time|u|tt|var)$/i;
|
||
function normalizeSiblings(fragment, $context) {
|
||
if (fragment.childCount < 2)
|
||
return fragment;
|
||
for (let d = $context.depth; d >= 0; d--) {
|
||
let parent = $context.node(d);
|
||
let match = parent.contentMatchAt($context.index(d));
|
||
let lastWrap, result = [];
|
||
fragment.forEach((node) => {
|
||
if (!result)
|
||
return;
|
||
let wrap = match.findWrapping(node.type), inLast;
|
||
if (!wrap)
|
||
return result = null;
|
||
if (inLast = result.length && lastWrap.length && addToSibling(wrap, lastWrap, node, result[result.length - 1], 0)) {
|
||
result[result.length - 1] = inLast;
|
||
} else {
|
||
if (result.length)
|
||
result[result.length - 1] = closeRight(result[result.length - 1], lastWrap.length);
|
||
let wrapped = withWrappers(node, wrap);
|
||
result.push(wrapped);
|
||
match = match.matchType(wrapped.type);
|
||
lastWrap = wrap;
|
||
}
|
||
});
|
||
if (result)
|
||
return Fragment.from(result);
|
||
}
|
||
return fragment;
|
||
}
|
||
function withWrappers(node, wrap, from = 0) {
|
||
for (let i = wrap.length - 1; i >= from; i--)
|
||
node = wrap[i].create(null, Fragment.from(node));
|
||
return node;
|
||
}
|
||
function addToSibling(wrap, lastWrap, node, sibling, depth) {
|
||
if (depth < wrap.length && depth < lastWrap.length && wrap[depth] == lastWrap[depth]) {
|
||
let inner = addToSibling(wrap, lastWrap, node, sibling.lastChild, depth + 1);
|
||
if (inner)
|
||
return sibling.copy(sibling.content.replaceChild(sibling.childCount - 1, inner));
|
||
let match = sibling.contentMatchAt(sibling.childCount);
|
||
if (match.matchType(depth == wrap.length - 1 ? node.type : wrap[depth + 1]))
|
||
return sibling.copy(sibling.content.append(Fragment.from(withWrappers(node, wrap, depth + 1))));
|
||
}
|
||
}
|
||
function closeRight(node, depth) {
|
||
if (depth == 0)
|
||
return node;
|
||
let fragment = node.content.replaceChild(node.childCount - 1, closeRight(node.lastChild, depth - 1));
|
||
let fill = node.contentMatchAt(node.childCount).fillBefore(Fragment.empty, true);
|
||
return node.copy(fragment.append(fill));
|
||
}
|
||
function closeRange(fragment, side, from, to, depth, openEnd) {
|
||
let node = side < 0 ? fragment.firstChild : fragment.lastChild, inner = node.content;
|
||
if (fragment.childCount > 1)
|
||
openEnd = 0;
|
||
if (depth < to - 1)
|
||
inner = closeRange(inner, side, from, to, depth + 1, openEnd);
|
||
if (depth >= from)
|
||
inner = side < 0 ? node.contentMatchAt(0).fillBefore(inner, openEnd <= depth).append(inner) : inner.append(node.contentMatchAt(node.childCount).fillBefore(Fragment.empty, true));
|
||
return fragment.replaceChild(side < 0 ? 0 : fragment.childCount - 1, node.copy(inner));
|
||
}
|
||
function closeSlice(slice, openStart, openEnd) {
|
||
if (openStart < slice.openStart)
|
||
slice = new Slice(closeRange(slice.content, -1, openStart, slice.openStart, 0, slice.openEnd), openStart, slice.openEnd);
|
||
if (openEnd < slice.openEnd)
|
||
slice = new Slice(closeRange(slice.content, 1, openEnd, slice.openEnd, 0, 0), slice.openStart, openEnd);
|
||
return slice;
|
||
}
|
||
var wrapMap = {
|
||
thead: ["table"],
|
||
tbody: ["table"],
|
||
tfoot: ["table"],
|
||
caption: ["table"],
|
||
colgroup: ["table"],
|
||
col: ["table", "colgroup"],
|
||
tr: ["table", "tbody"],
|
||
td: ["table", "tbody", "tr"],
|
||
th: ["table", "tbody", "tr"]
|
||
};
|
||
var _detachedDoc = null;
|
||
function detachedDoc() {
|
||
return _detachedDoc || (_detachedDoc = document.implementation.createHTMLDocument("title"));
|
||
}
|
||
var _policy = null;
|
||
function maybeWrapTrusted(html) {
|
||
let trustedTypes = window.trustedTypes;
|
||
if (!trustedTypes)
|
||
return html;
|
||
if (!_policy)
|
||
_policy = trustedTypes.createPolicy("ProseMirrorClipboard", { createHTML: (s) => s });
|
||
return _policy.createHTML(html);
|
||
}
|
||
function readHTML(html) {
|
||
let metas = /^(\s*<meta [^>]*>)*/.exec(html);
|
||
if (metas)
|
||
html = html.slice(metas[0].length);
|
||
let elt = detachedDoc().createElement("div");
|
||
let firstTag = /<([a-z][^>\s]+)/i.exec(html), wrap;
|
||
if (wrap = firstTag && wrapMap[firstTag[1].toLowerCase()])
|
||
html = wrap.map((n) => "<" + n + ">").join("") + html + wrap.map((n) => "</" + n + ">").reverse().join("");
|
||
elt.innerHTML = maybeWrapTrusted(html);
|
||
if (wrap)
|
||
for (let i = 0; i < wrap.length; i++)
|
||
elt = elt.querySelector(wrap[i]) || elt;
|
||
return elt;
|
||
}
|
||
function restoreReplacedSpaces(dom) {
|
||
let nodes = dom.querySelectorAll(chrome ? "span:not([class]):not([style])" : "span.Apple-converted-space");
|
||
for (let i = 0; i < nodes.length; i++) {
|
||
let node = nodes[i];
|
||
if (node.childNodes.length == 1 && node.textContent == " " && node.parentNode)
|
||
node.parentNode.replaceChild(dom.ownerDocument.createTextNode(" "), node);
|
||
}
|
||
}
|
||
function addContext(slice, context) {
|
||
if (!slice.size)
|
||
return slice;
|
||
let schema = slice.content.firstChild.type.schema, array;
|
||
try {
|
||
array = JSON.parse(context);
|
||
} catch (e) {
|
||
return slice;
|
||
}
|
||
let { content, openStart, openEnd } = slice;
|
||
for (let i = array.length - 2; i >= 0; i -= 2) {
|
||
let type = schema.nodes[array[i]];
|
||
if (!type || type.hasRequiredAttrs())
|
||
break;
|
||
content = Fragment.from(type.create(array[i + 1], content));
|
||
openStart++;
|
||
openEnd++;
|
||
}
|
||
return new Slice(content, openStart, openEnd);
|
||
}
|
||
var handlers = {};
|
||
var editHandlers = {};
|
||
var passiveHandlers = { touchstart: true, touchmove: true };
|
||
var InputState = class {
|
||
constructor() {
|
||
this.shiftKey = false;
|
||
this.mouseDown = null;
|
||
this.lastKeyCode = null;
|
||
this.lastKeyCodeTime = 0;
|
||
this.lastClick = { time: 0, x: 0, y: 0, type: "" };
|
||
this.lastSelectionOrigin = null;
|
||
this.lastSelectionTime = 0;
|
||
this.lastIOSEnter = 0;
|
||
this.lastIOSEnterFallbackTimeout = -1;
|
||
this.lastFocus = 0;
|
||
this.lastTouch = 0;
|
||
this.lastAndroidDelete = 0;
|
||
this.composing = false;
|
||
this.compositionNode = null;
|
||
this.composingTimeout = -1;
|
||
this.compositionNodes = [];
|
||
this.compositionEndedAt = -2e8;
|
||
this.compositionID = 1;
|
||
this.compositionPendingChanges = 0;
|
||
this.domChangeCount = 0;
|
||
this.eventHandlers = /* @__PURE__ */ Object.create(null);
|
||
this.hideSelectionGuard = null;
|
||
}
|
||
};
|
||
function initInput(view) {
|
||
for (let event in handlers) {
|
||
let handler = handlers[event];
|
||
view.dom.addEventListener(event, view.input.eventHandlers[event] = (event2) => {
|
||
if (eventBelongsToView(view, event2) && !runCustomHandler(view, event2) && (view.editable || !(event2.type in editHandlers)))
|
||
handler(view, event2);
|
||
}, passiveHandlers[event] ? { passive: true } : void 0);
|
||
}
|
||
if (safari)
|
||
view.dom.addEventListener("input", () => null);
|
||
ensureListeners(view);
|
||
}
|
||
function setSelectionOrigin(view, origin) {
|
||
view.input.lastSelectionOrigin = origin;
|
||
view.input.lastSelectionTime = Date.now();
|
||
}
|
||
function destroyInput(view) {
|
||
view.domObserver.stop();
|
||
for (let type in view.input.eventHandlers)
|
||
view.dom.removeEventListener(type, view.input.eventHandlers[type]);
|
||
clearTimeout(view.input.composingTimeout);
|
||
clearTimeout(view.input.lastIOSEnterFallbackTimeout);
|
||
}
|
||
function ensureListeners(view) {
|
||
view.someProp("handleDOMEvents", (currentHandlers) => {
|
||
for (let type in currentHandlers)
|
||
if (!view.input.eventHandlers[type])
|
||
view.dom.addEventListener(type, view.input.eventHandlers[type] = (event) => runCustomHandler(view, event));
|
||
});
|
||
}
|
||
function runCustomHandler(view, event) {
|
||
return view.someProp("handleDOMEvents", (handlers2) => {
|
||
let handler = handlers2[event.type];
|
||
return handler ? handler(view, event) || event.defaultPrevented : false;
|
||
});
|
||
}
|
||
function eventBelongsToView(view, event) {
|
||
if (!event.bubbles)
|
||
return true;
|
||
if (event.defaultPrevented)
|
||
return false;
|
||
for (let node = event.target; node != view.dom; node = node.parentNode)
|
||
if (!node || node.nodeType == 11 || node.pmViewDesc && node.pmViewDesc.stopEvent(event))
|
||
return false;
|
||
return true;
|
||
}
|
||
function dispatchEvent(view, event) {
|
||
if (!runCustomHandler(view, event) && handlers[event.type] && (view.editable || !(event.type in editHandlers)))
|
||
handlers[event.type](view, event);
|
||
}
|
||
editHandlers.keydown = (view, _event) => {
|
||
let event = _event;
|
||
view.input.shiftKey = event.keyCode == 16 || event.shiftKey;
|
||
if (inOrNearComposition(view, event))
|
||
return;
|
||
view.input.lastKeyCode = event.keyCode;
|
||
view.input.lastKeyCodeTime = Date.now();
|
||
if (android && chrome && event.keyCode == 13)
|
||
return;
|
||
if (event.keyCode != 229)
|
||
view.domObserver.forceFlush();
|
||
if (ios && event.keyCode == 13 && !event.ctrlKey && !event.altKey && !event.metaKey) {
|
||
let now = Date.now();
|
||
view.input.lastIOSEnter = now;
|
||
view.input.lastIOSEnterFallbackTimeout = setTimeout(() => {
|
||
if (view.input.lastIOSEnter == now) {
|
||
view.someProp("handleKeyDown", (f) => f(view, keyEvent(13, "Enter")));
|
||
view.input.lastIOSEnter = 0;
|
||
}
|
||
}, 200);
|
||
} else if (view.someProp("handleKeyDown", (f) => f(view, event)) || captureKeyDown(view, event)) {
|
||
event.preventDefault();
|
||
} else {
|
||
setSelectionOrigin(view, "key");
|
||
}
|
||
};
|
||
editHandlers.keyup = (view, event) => {
|
||
if (event.keyCode == 16)
|
||
view.input.shiftKey = false;
|
||
};
|
||
editHandlers.keypress = (view, _event) => {
|
||
let event = _event;
|
||
if (inOrNearComposition(view, event) || !event.charCode || event.ctrlKey && !event.altKey || mac && event.metaKey)
|
||
return;
|
||
if (view.someProp("handleKeyPress", (f) => f(view, event))) {
|
||
event.preventDefault();
|
||
return;
|
||
}
|
||
let sel = view.state.selection;
|
||
if (!(sel instanceof TextSelection) || !sel.$from.sameParent(sel.$to)) {
|
||
let text = String.fromCharCode(event.charCode);
|
||
if (!/[\r\n]/.test(text) && !view.someProp("handleTextInput", (f) => f(view, sel.$from.pos, sel.$to.pos, text)))
|
||
view.dispatch(view.state.tr.insertText(text).scrollIntoView());
|
||
event.preventDefault();
|
||
}
|
||
};
|
||
function eventCoords(event) {
|
||
return { left: event.clientX, top: event.clientY };
|
||
}
|
||
function isNear(event, click) {
|
||
let dx = click.x - event.clientX, dy = click.y - event.clientY;
|
||
return dx * dx + dy * dy < 100;
|
||
}
|
||
function runHandlerOnContext(view, propName, pos, inside, event) {
|
||
if (inside == -1)
|
||
return false;
|
||
let $pos = view.state.doc.resolve(inside);
|
||
for (let i = $pos.depth + 1; i > 0; i--) {
|
||
if (view.someProp(propName, (f) => i > $pos.depth ? f(view, pos, $pos.nodeAfter, $pos.before(i), event, true) : f(view, pos, $pos.node(i), $pos.before(i), event, false)))
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
function updateSelection(view, selection, origin) {
|
||
if (!view.focused)
|
||
view.focus();
|
||
if (view.state.selection.eq(selection))
|
||
return;
|
||
let tr = view.state.tr.setSelection(selection);
|
||
if (origin == "pointer")
|
||
tr.setMeta("pointer", true);
|
||
view.dispatch(tr);
|
||
}
|
||
function selectClickedLeaf(view, inside) {
|
||
if (inside == -1)
|
||
return false;
|
||
let $pos = view.state.doc.resolve(inside), node = $pos.nodeAfter;
|
||
if (node && node.isAtom && NodeSelection.isSelectable(node)) {
|
||
updateSelection(view, new NodeSelection($pos), "pointer");
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
function selectClickedNode(view, inside) {
|
||
if (inside == -1)
|
||
return false;
|
||
let sel = view.state.selection, selectedNode, selectAt;
|
||
if (sel instanceof NodeSelection)
|
||
selectedNode = sel.node;
|
||
let $pos = view.state.doc.resolve(inside);
|
||
for (let i = $pos.depth + 1; i > 0; i--) {
|
||
let node = i > $pos.depth ? $pos.nodeAfter : $pos.node(i);
|
||
if (NodeSelection.isSelectable(node)) {
|
||
if (selectedNode && sel.$from.depth > 0 && i >= sel.$from.depth && $pos.before(sel.$from.depth + 1) == sel.$from.pos)
|
||
selectAt = $pos.before(sel.$from.depth);
|
||
else
|
||
selectAt = $pos.before(i);
|
||
break;
|
||
}
|
||
}
|
||
if (selectAt != null) {
|
||
updateSelection(view, NodeSelection.create(view.state.doc, selectAt), "pointer");
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
function handleSingleClick(view, pos, inside, event, selectNode) {
|
||
return runHandlerOnContext(view, "handleClickOn", pos, inside, event) || view.someProp("handleClick", (f) => f(view, pos, event)) || (selectNode ? selectClickedNode(view, inside) : selectClickedLeaf(view, inside));
|
||
}
|
||
function handleDoubleClick(view, pos, inside, event) {
|
||
return runHandlerOnContext(view, "handleDoubleClickOn", pos, inside, event) || view.someProp("handleDoubleClick", (f) => f(view, pos, event));
|
||
}
|
||
function handleTripleClick(view, pos, inside, event) {
|
||
return runHandlerOnContext(view, "handleTripleClickOn", pos, inside, event) || view.someProp("handleTripleClick", (f) => f(view, pos, event)) || defaultTripleClick(view, inside, event);
|
||
}
|
||
function defaultTripleClick(view, inside, event) {
|
||
if (event.button != 0)
|
||
return false;
|
||
let doc2 = view.state.doc;
|
||
if (inside == -1) {
|
||
if (doc2.inlineContent) {
|
||
updateSelection(view, TextSelection.create(doc2, 0, doc2.content.size), "pointer");
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
let $pos = doc2.resolve(inside);
|
||
for (let i = $pos.depth + 1; i > 0; i--) {
|
||
let node = i > $pos.depth ? $pos.nodeAfter : $pos.node(i);
|
||
let nodePos = $pos.before(i);
|
||
if (node.inlineContent)
|
||
updateSelection(view, TextSelection.create(doc2, nodePos + 1, nodePos + 1 + node.content.size), "pointer");
|
||
else if (NodeSelection.isSelectable(node))
|
||
updateSelection(view, NodeSelection.create(doc2, nodePos), "pointer");
|
||
else
|
||
continue;
|
||
return true;
|
||
}
|
||
}
|
||
function forceDOMFlush(view) {
|
||
return endComposition(view);
|
||
}
|
||
var selectNodeModifier = mac ? "metaKey" : "ctrlKey";
|
||
handlers.mousedown = (view, _event) => {
|
||
let event = _event;
|
||
view.input.shiftKey = event.shiftKey;
|
||
let flushed = forceDOMFlush(view);
|
||
let now = Date.now(), type = "singleClick";
|
||
if (now - view.input.lastClick.time < 500 && isNear(event, view.input.lastClick) && !event[selectNodeModifier]) {
|
||
if (view.input.lastClick.type == "singleClick")
|
||
type = "doubleClick";
|
||
else if (view.input.lastClick.type == "doubleClick")
|
||
type = "tripleClick";
|
||
}
|
||
view.input.lastClick = { time: now, x: event.clientX, y: event.clientY, type };
|
||
let pos = view.posAtCoords(eventCoords(event));
|
||
if (!pos)
|
||
return;
|
||
if (type == "singleClick") {
|
||
if (view.input.mouseDown)
|
||
view.input.mouseDown.done();
|
||
view.input.mouseDown = new MouseDown(view, pos, event, !!flushed);
|
||
} else if ((type == "doubleClick" ? handleDoubleClick : handleTripleClick)(view, pos.pos, pos.inside, event)) {
|
||
event.preventDefault();
|
||
} else {
|
||
setSelectionOrigin(view, "pointer");
|
||
}
|
||
};
|
||
var MouseDown = class {
|
||
constructor(view, pos, event, flushed) {
|
||
this.view = view;
|
||
this.pos = pos;
|
||
this.event = event;
|
||
this.flushed = flushed;
|
||
this.delayedSelectionSync = false;
|
||
this.mightDrag = null;
|
||
this.startDoc = view.state.doc;
|
||
this.selectNode = !!event[selectNodeModifier];
|
||
this.allowDefault = event.shiftKey;
|
||
let targetNode, targetPos;
|
||
if (pos.inside > -1) {
|
||
targetNode = view.state.doc.nodeAt(pos.inside);
|
||
targetPos = pos.inside;
|
||
} else {
|
||
let $pos = view.state.doc.resolve(pos.pos);
|
||
targetNode = $pos.parent;
|
||
targetPos = $pos.depth ? $pos.before() : 0;
|
||
}
|
||
const target = flushed ? null : event.target;
|
||
const targetDesc = target ? view.docView.nearestDesc(target, true) : null;
|
||
this.target = targetDesc && targetDesc.dom.nodeType == 1 ? targetDesc.dom : null;
|
||
let { selection } = view.state;
|
||
if (event.button == 0 && targetNode.type.spec.draggable && targetNode.type.spec.selectable !== false || selection instanceof NodeSelection && selection.from <= targetPos && selection.to > targetPos)
|
||
this.mightDrag = {
|
||
node: targetNode,
|
||
pos: targetPos,
|
||
addAttr: !!(this.target && !this.target.draggable),
|
||
setUneditable: !!(this.target && gecko && !this.target.hasAttribute("contentEditable"))
|
||
};
|
||
if (this.target && this.mightDrag && (this.mightDrag.addAttr || this.mightDrag.setUneditable)) {
|
||
this.view.domObserver.stop();
|
||
if (this.mightDrag.addAttr)
|
||
this.target.draggable = true;
|
||
if (this.mightDrag.setUneditable)
|
||
setTimeout(() => {
|
||
if (this.view.input.mouseDown == this)
|
||
this.target.setAttribute("contentEditable", "false");
|
||
}, 20);
|
||
this.view.domObserver.start();
|
||
}
|
||
view.root.addEventListener("mouseup", this.up = this.up.bind(this));
|
||
view.root.addEventListener("mousemove", this.move = this.move.bind(this));
|
||
setSelectionOrigin(view, "pointer");
|
||
}
|
||
done() {
|
||
this.view.root.removeEventListener("mouseup", this.up);
|
||
this.view.root.removeEventListener("mousemove", this.move);
|
||
if (this.mightDrag && this.target) {
|
||
this.view.domObserver.stop();
|
||
if (this.mightDrag.addAttr)
|
||
this.target.removeAttribute("draggable");
|
||
if (this.mightDrag.setUneditable)
|
||
this.target.removeAttribute("contentEditable");
|
||
this.view.domObserver.start();
|
||
}
|
||
if (this.delayedSelectionSync)
|
||
setTimeout(() => selectionToDOM(this.view));
|
||
this.view.input.mouseDown = null;
|
||
}
|
||
up(event) {
|
||
this.done();
|
||
if (!this.view.dom.contains(event.target))
|
||
return;
|
||
let pos = this.pos;
|
||
if (this.view.state.doc != this.startDoc)
|
||
pos = this.view.posAtCoords(eventCoords(event));
|
||
this.updateAllowDefault(event);
|
||
if (this.allowDefault || !pos) {
|
||
setSelectionOrigin(this.view, "pointer");
|
||
} else if (handleSingleClick(this.view, pos.pos, pos.inside, event, this.selectNode)) {
|
||
event.preventDefault();
|
||
} else if (event.button == 0 && (this.flushed || // Safari ignores clicks on draggable elements
|
||
safari && this.mightDrag && !this.mightDrag.node.isAtom || // Chrome will sometimes treat a node selection as a
|
||
// cursor, but still report that the node is selected
|
||
// when asked through getSelection. You'll then get a
|
||
// situation where clicking at the point where that
|
||
// (hidden) cursor is doesn't change the selection, and
|
||
// thus doesn't get a reaction from ProseMirror. This
|
||
// works around that.
|
||
chrome && !this.view.state.selection.visible && Math.min(Math.abs(pos.pos - this.view.state.selection.from), Math.abs(pos.pos - this.view.state.selection.to)) <= 2)) {
|
||
updateSelection(this.view, Selection.near(this.view.state.doc.resolve(pos.pos)), "pointer");
|
||
event.preventDefault();
|
||
} else {
|
||
setSelectionOrigin(this.view, "pointer");
|
||
}
|
||
}
|
||
move(event) {
|
||
this.updateAllowDefault(event);
|
||
setSelectionOrigin(this.view, "pointer");
|
||
if (event.buttons == 0)
|
||
this.done();
|
||
}
|
||
updateAllowDefault(event) {
|
||
if (!this.allowDefault && (Math.abs(this.event.x - event.clientX) > 4 || Math.abs(this.event.y - event.clientY) > 4))
|
||
this.allowDefault = true;
|
||
}
|
||
};
|
||
handlers.touchstart = (view) => {
|
||
view.input.lastTouch = Date.now();
|
||
forceDOMFlush(view);
|
||
setSelectionOrigin(view, "pointer");
|
||
};
|
||
handlers.touchmove = (view) => {
|
||
view.input.lastTouch = Date.now();
|
||
setSelectionOrigin(view, "pointer");
|
||
};
|
||
handlers.contextmenu = (view) => forceDOMFlush(view);
|
||
function inOrNearComposition(view, event) {
|
||
if (view.composing)
|
||
return true;
|
||
if (safari && Math.abs(event.timeStamp - view.input.compositionEndedAt) < 500) {
|
||
view.input.compositionEndedAt = -2e8;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
var timeoutComposition = android ? 5e3 : -1;
|
||
editHandlers.compositionstart = editHandlers.compositionupdate = (view) => {
|
||
if (!view.composing) {
|
||
view.domObserver.flush();
|
||
let { state } = view, $pos = state.selection.$to;
|
||
if (state.selection instanceof TextSelection && (state.storedMarks || !$pos.textOffset && $pos.parentOffset && $pos.nodeBefore.marks.some((m) => m.type.spec.inclusive === false))) {
|
||
view.markCursor = view.state.storedMarks || $pos.marks();
|
||
endComposition(view, true);
|
||
view.markCursor = null;
|
||
} else {
|
||
endComposition(view, !state.selection.empty);
|
||
if (gecko && state.selection.empty && $pos.parentOffset && !$pos.textOffset && $pos.nodeBefore.marks.length) {
|
||
let sel = view.domSelectionRange();
|
||
for (let node = sel.focusNode, offset = sel.focusOffset; node && node.nodeType == 1 && offset != 0; ) {
|
||
let before = offset < 0 ? node.lastChild : node.childNodes[offset - 1];
|
||
if (!before)
|
||
break;
|
||
if (before.nodeType == 3) {
|
||
let sel2 = view.domSelection();
|
||
if (sel2)
|
||
sel2.collapse(before, before.nodeValue.length);
|
||
break;
|
||
} else {
|
||
node = before;
|
||
offset = -1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
view.input.composing = true;
|
||
}
|
||
scheduleComposeEnd(view, timeoutComposition);
|
||
};
|
||
editHandlers.compositionend = (view, event) => {
|
||
if (view.composing) {
|
||
view.input.composing = false;
|
||
view.input.compositionEndedAt = event.timeStamp;
|
||
view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0;
|
||
view.input.compositionNode = null;
|
||
if (view.input.compositionPendingChanges)
|
||
Promise.resolve().then(() => view.domObserver.flush());
|
||
view.input.compositionID++;
|
||
scheduleComposeEnd(view, 20);
|
||
}
|
||
};
|
||
function scheduleComposeEnd(view, delay) {
|
||
clearTimeout(view.input.composingTimeout);
|
||
if (delay > -1)
|
||
view.input.composingTimeout = setTimeout(() => endComposition(view), delay);
|
||
}
|
||
function clearComposition(view) {
|
||
if (view.composing) {
|
||
view.input.composing = false;
|
||
view.input.compositionEndedAt = timestampFromCustomEvent();
|
||
}
|
||
while (view.input.compositionNodes.length > 0)
|
||
view.input.compositionNodes.pop().markParentsDirty();
|
||
}
|
||
function findCompositionNode(view) {
|
||
let sel = view.domSelectionRange();
|
||
if (!sel.focusNode)
|
||
return null;
|
||
let textBefore = textNodeBefore$1(sel.focusNode, sel.focusOffset);
|
||
let textAfter = textNodeAfter$1(sel.focusNode, sel.focusOffset);
|
||
if (textBefore && textAfter && textBefore != textAfter) {
|
||
let descAfter = textAfter.pmViewDesc, lastChanged = view.domObserver.lastChangedTextNode;
|
||
if (textBefore == lastChanged || textAfter == lastChanged)
|
||
return lastChanged;
|
||
if (!descAfter || !descAfter.isText(textAfter.nodeValue)) {
|
||
return textAfter;
|
||
} else if (view.input.compositionNode == textAfter) {
|
||
let descBefore = textBefore.pmViewDesc;
|
||
if (!(!descBefore || !descBefore.isText(textBefore.nodeValue)))
|
||
return textAfter;
|
||
}
|
||
}
|
||
return textBefore || textAfter;
|
||
}
|
||
function timestampFromCustomEvent() {
|
||
let event = document.createEvent("Event");
|
||
event.initEvent("event", true, true);
|
||
return event.timeStamp;
|
||
}
|
||
function endComposition(view, restarting = false) {
|
||
if (android && view.domObserver.flushingSoon >= 0)
|
||
return;
|
||
view.domObserver.forceFlush();
|
||
clearComposition(view);
|
||
if (restarting || view.docView && view.docView.dirty) {
|
||
let sel = selectionFromDOM(view);
|
||
if (sel && !sel.eq(view.state.selection))
|
||
view.dispatch(view.state.tr.setSelection(sel));
|
||
else if ((view.markCursor || restarting) && !view.state.selection.empty)
|
||
view.dispatch(view.state.tr.deleteSelection());
|
||
else
|
||
view.updateState(view.state);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
function captureCopy(view, dom) {
|
||
if (!view.dom.parentNode)
|
||
return;
|
||
let wrap = view.dom.parentNode.appendChild(document.createElement("div"));
|
||
wrap.appendChild(dom);
|
||
wrap.style.cssText = "position: fixed; left: -10000px; top: 10px";
|
||
let sel = getSelection(), range = document.createRange();
|
||
range.selectNodeContents(dom);
|
||
view.dom.blur();
|
||
sel.removeAllRanges();
|
||
sel.addRange(range);
|
||
setTimeout(() => {
|
||
if (wrap.parentNode)
|
||
wrap.parentNode.removeChild(wrap);
|
||
view.focus();
|
||
}, 50);
|
||
}
|
||
var brokenClipboardAPI = ie && ie_version < 15 || ios && webkit_version < 604;
|
||
handlers.copy = editHandlers.cut = (view, _event) => {
|
||
let event = _event;
|
||
let sel = view.state.selection, cut2 = event.type == "cut";
|
||
if (sel.empty)
|
||
return;
|
||
let data = brokenClipboardAPI ? null : event.clipboardData;
|
||
let slice = sel.content(), { dom, text } = serializeForClipboard(view, slice);
|
||
if (data) {
|
||
event.preventDefault();
|
||
data.clearData();
|
||
data.setData("text/html", dom.innerHTML);
|
||
data.setData("text/plain", text);
|
||
} else {
|
||
captureCopy(view, dom);
|
||
}
|
||
if (cut2)
|
||
view.dispatch(view.state.tr.deleteSelection().scrollIntoView().setMeta("uiEvent", "cut"));
|
||
};
|
||
function sliceSingleNode(slice) {
|
||
return slice.openStart == 0 && slice.openEnd == 0 && slice.content.childCount == 1 ? slice.content.firstChild : null;
|
||
}
|
||
function capturePaste(view, event) {
|
||
if (!view.dom.parentNode)
|
||
return;
|
||
let plainText = view.input.shiftKey || view.state.selection.$from.parent.type.spec.code;
|
||
let target = view.dom.parentNode.appendChild(document.createElement(plainText ? "textarea" : "div"));
|
||
if (!plainText)
|
||
target.contentEditable = "true";
|
||
target.style.cssText = "position: fixed; left: -10000px; top: 10px";
|
||
target.focus();
|
||
let plain = view.input.shiftKey && view.input.lastKeyCode != 45;
|
||
setTimeout(() => {
|
||
view.focus();
|
||
if (target.parentNode)
|
||
target.parentNode.removeChild(target);
|
||
if (plainText)
|
||
doPaste(view, target.value, null, plain, event);
|
||
else
|
||
doPaste(view, target.textContent, target.innerHTML, plain, event);
|
||
}, 50);
|
||
}
|
||
function doPaste(view, text, html, preferPlain, event) {
|
||
let slice = parseFromClipboard(view, text, html, preferPlain, view.state.selection.$from);
|
||
if (view.someProp("handlePaste", (f) => f(view, event, slice || Slice.empty)))
|
||
return true;
|
||
if (!slice)
|
||
return false;
|
||
let singleNode = sliceSingleNode(slice);
|
||
let tr = singleNode ? view.state.tr.replaceSelectionWith(singleNode, preferPlain) : view.state.tr.replaceSelection(slice);
|
||
view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste"));
|
||
return true;
|
||
}
|
||
function getText(clipboardData) {
|
||
let text = clipboardData.getData("text/plain") || clipboardData.getData("Text");
|
||
if (text)
|
||
return text;
|
||
let uris = clipboardData.getData("text/uri-list");
|
||
return uris ? uris.replace(/\r?\n/g, " ") : "";
|
||
}
|
||
editHandlers.paste = (view, _event) => {
|
||
let event = _event;
|
||
if (view.composing && !android)
|
||
return;
|
||
let data = brokenClipboardAPI ? null : event.clipboardData;
|
||
let plain = view.input.shiftKey && view.input.lastKeyCode != 45;
|
||
if (data && doPaste(view, getText(data), data.getData("text/html"), plain, event))
|
||
event.preventDefault();
|
||
else
|
||
capturePaste(view, event);
|
||
};
|
||
var Dragging = class {
|
||
constructor(slice, move, node) {
|
||
this.slice = slice;
|
||
this.move = move;
|
||
this.node = node;
|
||
}
|
||
};
|
||
var dragCopyModifier = mac ? "altKey" : "ctrlKey";
|
||
handlers.dragstart = (view, _event) => {
|
||
let event = _event;
|
||
let mouseDown = view.input.mouseDown;
|
||
if (mouseDown)
|
||
mouseDown.done();
|
||
if (!event.dataTransfer)
|
||
return;
|
||
let sel = view.state.selection;
|
||
let pos = sel.empty ? null : view.posAtCoords(eventCoords(event));
|
||
let node;
|
||
if (pos && pos.pos >= sel.from && pos.pos <= (sel instanceof NodeSelection ? sel.to - 1 : sel.to)) ;
|
||
else if (mouseDown && mouseDown.mightDrag) {
|
||
node = NodeSelection.create(view.state.doc, mouseDown.mightDrag.pos);
|
||
} else if (event.target && event.target.nodeType == 1) {
|
||
let desc = view.docView.nearestDesc(event.target, true);
|
||
if (desc && desc.node.type.spec.draggable && desc != view.docView)
|
||
node = NodeSelection.create(view.state.doc, desc.posBefore);
|
||
}
|
||
let draggedSlice = (node || view.state.selection).content();
|
||
let { dom, text, slice } = serializeForClipboard(view, draggedSlice);
|
||
if (!event.dataTransfer.files.length || !chrome || chrome_version > 120)
|
||
event.dataTransfer.clearData();
|
||
event.dataTransfer.setData(brokenClipboardAPI ? "Text" : "text/html", dom.innerHTML);
|
||
event.dataTransfer.effectAllowed = "copyMove";
|
||
if (!brokenClipboardAPI)
|
||
event.dataTransfer.setData("text/plain", text);
|
||
view.dragging = new Dragging(slice, !event[dragCopyModifier], node);
|
||
};
|
||
handlers.dragend = (view) => {
|
||
let dragging = view.dragging;
|
||
window.setTimeout(() => {
|
||
if (view.dragging == dragging)
|
||
view.dragging = null;
|
||
}, 50);
|
||
};
|
||
editHandlers.dragover = editHandlers.dragenter = (_, e) => e.preventDefault();
|
||
editHandlers.drop = (view, _event) => {
|
||
let event = _event;
|
||
let dragging = view.dragging;
|
||
view.dragging = null;
|
||
if (!event.dataTransfer)
|
||
return;
|
||
let eventPos = view.posAtCoords(eventCoords(event));
|
||
if (!eventPos)
|
||
return;
|
||
let $mouse = view.state.doc.resolve(eventPos.pos);
|
||
let slice = dragging && dragging.slice;
|
||
if (slice) {
|
||
view.someProp("transformPasted", (f) => {
|
||
slice = f(slice, view);
|
||
});
|
||
} else {
|
||
slice = parseFromClipboard(view, getText(event.dataTransfer), brokenClipboardAPI ? null : event.dataTransfer.getData("text/html"), false, $mouse);
|
||
}
|
||
let move = !!(dragging && !event[dragCopyModifier]);
|
||
if (view.someProp("handleDrop", (f) => f(view, event, slice || Slice.empty, move))) {
|
||
event.preventDefault();
|
||
return;
|
||
}
|
||
if (!slice)
|
||
return;
|
||
event.preventDefault();
|
||
let insertPos = slice ? dropPoint(view.state.doc, $mouse.pos, slice) : $mouse.pos;
|
||
if (insertPos == null)
|
||
insertPos = $mouse.pos;
|
||
let tr = view.state.tr;
|
||
if (move) {
|
||
let { node } = dragging;
|
||
if (node)
|
||
node.replace(tr);
|
||
else
|
||
tr.deleteSelection();
|
||
}
|
||
let pos = tr.mapping.map(insertPos);
|
||
let isNode = slice.openStart == 0 && slice.openEnd == 0 && slice.content.childCount == 1;
|
||
let beforeInsert = tr.doc;
|
||
if (isNode)
|
||
tr.replaceRangeWith(pos, pos, slice.content.firstChild);
|
||
else
|
||
tr.replaceRange(pos, pos, slice);
|
||
if (tr.doc.eq(beforeInsert))
|
||
return;
|
||
let $pos = tr.doc.resolve(pos);
|
||
if (isNode && NodeSelection.isSelectable(slice.content.firstChild) && $pos.nodeAfter && $pos.nodeAfter.sameMarkup(slice.content.firstChild)) {
|
||
tr.setSelection(new NodeSelection($pos));
|
||
} else {
|
||
let end = tr.mapping.map(insertPos);
|
||
tr.mapping.maps[tr.mapping.maps.length - 1].forEach((_from, _to, _newFrom, newTo) => end = newTo);
|
||
tr.setSelection(selectionBetween(view, $pos, tr.doc.resolve(end)));
|
||
}
|
||
view.focus();
|
||
view.dispatch(tr.setMeta("uiEvent", "drop"));
|
||
};
|
||
handlers.focus = (view) => {
|
||
view.input.lastFocus = Date.now();
|
||
if (!view.focused) {
|
||
view.domObserver.stop();
|
||
view.dom.classList.add("ProseMirror-focused");
|
||
view.domObserver.start();
|
||
view.focused = true;
|
||
setTimeout(() => {
|
||
if (view.docView && view.hasFocus() && !view.domObserver.currentSelection.eq(view.domSelectionRange()))
|
||
selectionToDOM(view);
|
||
}, 20);
|
||
}
|
||
};
|
||
handlers.blur = (view, _event) => {
|
||
let event = _event;
|
||
if (view.focused) {
|
||
view.domObserver.stop();
|
||
view.dom.classList.remove("ProseMirror-focused");
|
||
view.domObserver.start();
|
||
if (event.relatedTarget && view.dom.contains(event.relatedTarget))
|
||
view.domObserver.currentSelection.clear();
|
||
view.focused = false;
|
||
}
|
||
};
|
||
handlers.beforeinput = (view, _event) => {
|
||
let event = _event;
|
||
if (chrome && android && event.inputType == "deleteContentBackward") {
|
||
view.domObserver.flushSoon();
|
||
let { domChangeCount } = view.input;
|
||
setTimeout(() => {
|
||
if (view.input.domChangeCount != domChangeCount)
|
||
return;
|
||
view.dom.blur();
|
||
view.focus();
|
||
if (view.someProp("handleKeyDown", (f) => f(view, keyEvent(8, "Backspace"))))
|
||
return;
|
||
let { $cursor } = view.state.selection;
|
||
if ($cursor && $cursor.pos > 0)
|
||
view.dispatch(view.state.tr.delete($cursor.pos - 1, $cursor.pos).scrollIntoView());
|
||
}, 50);
|
||
}
|
||
};
|
||
for (let prop in editHandlers)
|
||
handlers[prop] = editHandlers[prop];
|
||
function compareObjs(a, b) {
|
||
if (a == b)
|
||
return true;
|
||
for (let p in a)
|
||
if (a[p] !== b[p])
|
||
return false;
|
||
for (let p in b)
|
||
if (!(p in a))
|
||
return false;
|
||
return true;
|
||
}
|
||
var WidgetType = class _WidgetType {
|
||
constructor(toDOM, spec) {
|
||
this.toDOM = toDOM;
|
||
this.spec = spec || noSpec;
|
||
this.side = this.spec.side || 0;
|
||
}
|
||
map(mapping, span, offset, oldOffset) {
|
||
let { pos, deleted } = mapping.mapResult(span.from + oldOffset, this.side < 0 ? -1 : 1);
|
||
return deleted ? null : new Decoration(pos - offset, pos - offset, this);
|
||
}
|
||
valid() {
|
||
return true;
|
||
}
|
||
eq(other) {
|
||
return this == other || other instanceof _WidgetType && (this.spec.key && this.spec.key == other.spec.key || this.toDOM == other.toDOM && compareObjs(this.spec, other.spec));
|
||
}
|
||
destroy(node) {
|
||
if (this.spec.destroy)
|
||
this.spec.destroy(node);
|
||
}
|
||
};
|
||
var InlineType = class _InlineType {
|
||
constructor(attrs, spec) {
|
||
this.attrs = attrs;
|
||
this.spec = spec || noSpec;
|
||
}
|
||
map(mapping, span, offset, oldOffset) {
|
||
let from = mapping.map(span.from + oldOffset, this.spec.inclusiveStart ? -1 : 1) - offset;
|
||
let to = mapping.map(span.to + oldOffset, this.spec.inclusiveEnd ? 1 : -1) - offset;
|
||
return from >= to ? null : new Decoration(from, to, this);
|
||
}
|
||
valid(_, span) {
|
||
return span.from < span.to;
|
||
}
|
||
eq(other) {
|
||
return this == other || other instanceof _InlineType && compareObjs(this.attrs, other.attrs) && compareObjs(this.spec, other.spec);
|
||
}
|
||
static is(span) {
|
||
return span.type instanceof _InlineType;
|
||
}
|
||
destroy() {
|
||
}
|
||
};
|
||
var NodeType = class _NodeType {
|
||
constructor(attrs, spec) {
|
||
this.attrs = attrs;
|
||
this.spec = spec || noSpec;
|
||
}
|
||
map(mapping, span, offset, oldOffset) {
|
||
let from = mapping.mapResult(span.from + oldOffset, 1);
|
||
if (from.deleted)
|
||
return null;
|
||
let to = mapping.mapResult(span.to + oldOffset, -1);
|
||
if (to.deleted || to.pos <= from.pos)
|
||
return null;
|
||
return new Decoration(from.pos - offset, to.pos - offset, this);
|
||
}
|
||
valid(node, span) {
|
||
let { index: index2, offset } = node.content.findIndex(span.from), child;
|
||
return offset == span.from && !(child = node.child(index2)).isText && offset + child.nodeSize == span.to;
|
||
}
|
||
eq(other) {
|
||
return this == other || other instanceof _NodeType && compareObjs(this.attrs, other.attrs) && compareObjs(this.spec, other.spec);
|
||
}
|
||
destroy() {
|
||
}
|
||
};
|
||
var Decoration = class _Decoration {
|
||
/**
|
||
@internal
|
||
*/
|
||
constructor(from, to, type) {
|
||
this.from = from;
|
||
this.to = to;
|
||
this.type = type;
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
copy(from, to) {
|
||
return new _Decoration(from, to, this.type);
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
eq(other, offset = 0) {
|
||
return this.type.eq(other.type) && this.from + offset == other.from && this.to + offset == other.to;
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
map(mapping, offset, oldOffset) {
|
||
return this.type.map(mapping, this, offset, oldOffset);
|
||
}
|
||
/**
|
||
Creates a widget decoration, which is a DOM node that's shown in
|
||
the document at the given position. It is recommended that you
|
||
delay rendering the widget by passing a function that will be
|
||
called when the widget is actually drawn in a view, but you can
|
||
also directly pass a DOM node. `getPos` can be used to find the
|
||
widget's current document position.
|
||
*/
|
||
static widget(pos, toDOM, spec) {
|
||
return new _Decoration(pos, pos, new WidgetType(toDOM, spec));
|
||
}
|
||
/**
|
||
Creates an inline decoration, which adds the given attributes to
|
||
each inline node between `from` and `to`.
|
||
*/
|
||
static inline(from, to, attrs, spec) {
|
||
return new _Decoration(from, to, new InlineType(attrs, spec));
|
||
}
|
||
/**
|
||
Creates a node decoration. `from` and `to` should point precisely
|
||
before and after a node in the document. That node, and only that
|
||
node, will receive the given attributes.
|
||
*/
|
||
static node(from, to, attrs, spec) {
|
||
return new _Decoration(from, to, new NodeType(attrs, spec));
|
||
}
|
||
/**
|
||
The spec provided when creating this decoration. Can be useful
|
||
if you've stored extra information in that object.
|
||
*/
|
||
get spec() {
|
||
return this.type.spec;
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
get inline() {
|
||
return this.type instanceof InlineType;
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
get widget() {
|
||
return this.type instanceof WidgetType;
|
||
}
|
||
};
|
||
var none = [];
|
||
var noSpec = {};
|
||
var DecorationSet = class _DecorationSet {
|
||
/**
|
||
@internal
|
||
*/
|
||
constructor(local, children) {
|
||
this.local = local.length ? local : none;
|
||
this.children = children.length ? children : none;
|
||
}
|
||
/**
|
||
Create a set of decorations, using the structure of the given
|
||
document. This will consume (modify) the `decorations` array, so
|
||
you must make a copy if you want need to preserve that.
|
||
*/
|
||
static create(doc2, decorations) {
|
||
return decorations.length ? buildTree(decorations, doc2, 0, noSpec) : empty;
|
||
}
|
||
/**
|
||
Find all decorations in this set which touch the given range
|
||
(including decorations that start or end directly at the
|
||
boundaries) and match the given predicate on their spec. When
|
||
`start` and `end` are omitted, all decorations in the set are
|
||
considered. When `predicate` isn't given, all decorations are
|
||
assumed to match.
|
||
*/
|
||
find(start, end, predicate) {
|
||
let result = [];
|
||
this.findInner(start == null ? 0 : start, end == null ? 1e9 : end, result, 0, predicate);
|
||
return result;
|
||
}
|
||
findInner(start, end, result, offset, predicate) {
|
||
for (let i = 0; i < this.local.length; i++) {
|
||
let span = this.local[i];
|
||
if (span.from <= end && span.to >= start && (!predicate || predicate(span.spec)))
|
||
result.push(span.copy(span.from + offset, span.to + offset));
|
||
}
|
||
for (let i = 0; i < this.children.length; i += 3) {
|
||
if (this.children[i] < end && this.children[i + 1] > start) {
|
||
let childOff = this.children[i] + 1;
|
||
this.children[i + 2].findInner(start - childOff, end - childOff, result, offset + childOff, predicate);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
Map the set of decorations in response to a change in the
|
||
document.
|
||
*/
|
||
map(mapping, doc2, options) {
|
||
if (this == empty || mapping.maps.length == 0)
|
||
return this;
|
||
return this.mapInner(mapping, doc2, 0, 0, options || noSpec);
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
mapInner(mapping, node, offset, oldOffset, options) {
|
||
let newLocal;
|
||
for (let i = 0; i < this.local.length; i++) {
|
||
let mapped = this.local[i].map(mapping, offset, oldOffset);
|
||
if (mapped && mapped.type.valid(node, mapped))
|
||
(newLocal || (newLocal = [])).push(mapped);
|
||
else if (options.onRemove)
|
||
options.onRemove(this.local[i].spec);
|
||
}
|
||
if (this.children.length)
|
||
return mapChildren(this.children, newLocal || [], mapping, node, offset, oldOffset, options);
|
||
else
|
||
return newLocal ? new _DecorationSet(newLocal.sort(byPos), none) : empty;
|
||
}
|
||
/**
|
||
Add the given array of decorations to the ones in the set,
|
||
producing a new set. Consumes the `decorations` array. Needs
|
||
access to the current document to create the appropriate tree
|
||
structure.
|
||
*/
|
||
add(doc2, decorations) {
|
||
if (!decorations.length)
|
||
return this;
|
||
if (this == empty)
|
||
return _DecorationSet.create(doc2, decorations);
|
||
return this.addInner(doc2, decorations, 0);
|
||
}
|
||
addInner(doc2, decorations, offset) {
|
||
let children, childIndex = 0;
|
||
doc2.forEach((childNode, childOffset) => {
|
||
let baseOffset = childOffset + offset, found;
|
||
if (!(found = takeSpansForNode(decorations, childNode, baseOffset)))
|
||
return;
|
||
if (!children)
|
||
children = this.children.slice();
|
||
while (childIndex < children.length && children[childIndex] < childOffset)
|
||
childIndex += 3;
|
||
if (children[childIndex] == childOffset)
|
||
children[childIndex + 2] = children[childIndex + 2].addInner(childNode, found, baseOffset + 1);
|
||
else
|
||
children.splice(childIndex, 0, childOffset, childOffset + childNode.nodeSize, buildTree(found, childNode, baseOffset + 1, noSpec));
|
||
childIndex += 3;
|
||
});
|
||
let local = moveSpans(childIndex ? withoutNulls(decorations) : decorations, -offset);
|
||
for (let i = 0; i < local.length; i++)
|
||
if (!local[i].type.valid(doc2, local[i]))
|
||
local.splice(i--, 1);
|
||
return new _DecorationSet(local.length ? this.local.concat(local).sort(byPos) : this.local, children || this.children);
|
||
}
|
||
/**
|
||
Create a new set that contains the decorations in this set, minus
|
||
the ones in the given array.
|
||
*/
|
||
remove(decorations) {
|
||
if (decorations.length == 0 || this == empty)
|
||
return this;
|
||
return this.removeInner(decorations, 0);
|
||
}
|
||
removeInner(decorations, offset) {
|
||
let children = this.children, local = this.local;
|
||
for (let i = 0; i < children.length; i += 3) {
|
||
let found;
|
||
let from = children[i] + offset, to = children[i + 1] + offset;
|
||
for (let j = 0, span; j < decorations.length; j++)
|
||
if (span = decorations[j]) {
|
||
if (span.from > from && span.to < to) {
|
||
decorations[j] = null;
|
||
(found || (found = [])).push(span);
|
||
}
|
||
}
|
||
if (!found)
|
||
continue;
|
||
if (children == this.children)
|
||
children = this.children.slice();
|
||
let removed = children[i + 2].removeInner(found, from + 1);
|
||
if (removed != empty) {
|
||
children[i + 2] = removed;
|
||
} else {
|
||
children.splice(i, 3);
|
||
i -= 3;
|
||
}
|
||
}
|
||
if (local.length) {
|
||
for (let i = 0, span; i < decorations.length; i++)
|
||
if (span = decorations[i]) {
|
||
for (let j = 0; j < local.length; j++)
|
||
if (local[j].eq(span, offset)) {
|
||
if (local == this.local)
|
||
local = this.local.slice();
|
||
local.splice(j--, 1);
|
||
}
|
||
}
|
||
}
|
||
if (children == this.children && local == this.local)
|
||
return this;
|
||
return local.length || children.length ? new _DecorationSet(local, children) : empty;
|
||
}
|
||
forChild(offset, node) {
|
||
if (this == empty)
|
||
return this;
|
||
if (node.isLeaf)
|
||
return _DecorationSet.empty;
|
||
let child, local;
|
||
for (let i = 0; i < this.children.length; i += 3)
|
||
if (this.children[i] >= offset) {
|
||
if (this.children[i] == offset)
|
||
child = this.children[i + 2];
|
||
break;
|
||
}
|
||
let start = offset + 1, end = start + node.content.size;
|
||
for (let i = 0; i < this.local.length; i++) {
|
||
let dec = this.local[i];
|
||
if (dec.from < end && dec.to > start && dec.type instanceof InlineType) {
|
||
let from = Math.max(start, dec.from) - start, to = Math.min(end, dec.to) - start;
|
||
if (from < to)
|
||
(local || (local = [])).push(dec.copy(from, to));
|
||
}
|
||
}
|
||
if (local) {
|
||
let localSet = new _DecorationSet(local.sort(byPos), none);
|
||
return child ? new DecorationGroup([localSet, child]) : localSet;
|
||
}
|
||
return child || empty;
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
eq(other) {
|
||
if (this == other)
|
||
return true;
|
||
if (!(other instanceof _DecorationSet) || this.local.length != other.local.length || this.children.length != other.children.length)
|
||
return false;
|
||
for (let i = 0; i < this.local.length; i++)
|
||
if (!this.local[i].eq(other.local[i]))
|
||
return false;
|
||
for (let i = 0; i < this.children.length; i += 3)
|
||
if (this.children[i] != other.children[i] || this.children[i + 1] != other.children[i + 1] || !this.children[i + 2].eq(other.children[i + 2]))
|
||
return false;
|
||
return true;
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
locals(node) {
|
||
return removeOverlap(this.localsInner(node));
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
localsInner(node) {
|
||
if (this == empty)
|
||
return none;
|
||
if (node.inlineContent || !this.local.some(InlineType.is))
|
||
return this.local;
|
||
let result = [];
|
||
for (let i = 0; i < this.local.length; i++) {
|
||
if (!(this.local[i].type instanceof InlineType))
|
||
result.push(this.local[i]);
|
||
}
|
||
return result;
|
||
}
|
||
forEachSet(f) {
|
||
f(this);
|
||
}
|
||
};
|
||
DecorationSet.empty = new DecorationSet([], []);
|
||
DecorationSet.removeOverlap = removeOverlap;
|
||
var empty = DecorationSet.empty;
|
||
var DecorationGroup = class _DecorationGroup {
|
||
constructor(members) {
|
||
this.members = members;
|
||
}
|
||
map(mapping, doc2) {
|
||
const mappedDecos = this.members.map((member) => member.map(mapping, doc2, noSpec));
|
||
return _DecorationGroup.from(mappedDecos);
|
||
}
|
||
forChild(offset, child) {
|
||
if (child.isLeaf)
|
||
return DecorationSet.empty;
|
||
let found = [];
|
||
for (let i = 0; i < this.members.length; i++) {
|
||
let result = this.members[i].forChild(offset, child);
|
||
if (result == empty)
|
||
continue;
|
||
if (result instanceof _DecorationGroup)
|
||
found = found.concat(result.members);
|
||
else
|
||
found.push(result);
|
||
}
|
||
return _DecorationGroup.from(found);
|
||
}
|
||
eq(other) {
|
||
if (!(other instanceof _DecorationGroup) || other.members.length != this.members.length)
|
||
return false;
|
||
for (let i = 0; i < this.members.length; i++)
|
||
if (!this.members[i].eq(other.members[i]))
|
||
return false;
|
||
return true;
|
||
}
|
||
locals(node) {
|
||
let result, sorted = true;
|
||
for (let i = 0; i < this.members.length; i++) {
|
||
let locals = this.members[i].localsInner(node);
|
||
if (!locals.length)
|
||
continue;
|
||
if (!result) {
|
||
result = locals;
|
||
} else {
|
||
if (sorted) {
|
||
result = result.slice();
|
||
sorted = false;
|
||
}
|
||
for (let j = 0; j < locals.length; j++)
|
||
result.push(locals[j]);
|
||
}
|
||
}
|
||
return result ? removeOverlap(sorted ? result : result.sort(byPos)) : none;
|
||
}
|
||
// Create a group for the given array of decoration sets, or return
|
||
// a single set when possible.
|
||
static from(members) {
|
||
switch (members.length) {
|
||
case 0:
|
||
return empty;
|
||
case 1:
|
||
return members[0];
|
||
default:
|
||
return new _DecorationGroup(members.every((m) => m instanceof DecorationSet) ? members : members.reduce((r, m) => r.concat(m instanceof DecorationSet ? m : m.members), []));
|
||
}
|
||
}
|
||
forEachSet(f) {
|
||
for (let i = 0; i < this.members.length; i++)
|
||
this.members[i].forEachSet(f);
|
||
}
|
||
};
|
||
function mapChildren(oldChildren, newLocal, mapping, node, offset, oldOffset, options) {
|
||
let children = oldChildren.slice();
|
||
for (let i = 0, baseOffset = oldOffset; i < mapping.maps.length; i++) {
|
||
let moved = 0;
|
||
mapping.maps[i].forEach((oldStart, oldEnd, newStart, newEnd) => {
|
||
let dSize = newEnd - newStart - (oldEnd - oldStart);
|
||
for (let i2 = 0; i2 < children.length; i2 += 3) {
|
||
let end = children[i2 + 1];
|
||
if (end < 0 || oldStart > end + baseOffset - moved)
|
||
continue;
|
||
let start = children[i2] + baseOffset - moved;
|
||
if (oldEnd >= start) {
|
||
children[i2 + 1] = oldStart <= start ? -2 : -1;
|
||
} else if (oldStart >= baseOffset && dSize) {
|
||
children[i2] += dSize;
|
||
children[i2 + 1] += dSize;
|
||
}
|
||
}
|
||
moved += dSize;
|
||
});
|
||
baseOffset = mapping.maps[i].map(baseOffset, -1);
|
||
}
|
||
let mustRebuild = false;
|
||
for (let i = 0; i < children.length; i += 3)
|
||
if (children[i + 1] < 0) {
|
||
if (children[i + 1] == -2) {
|
||
mustRebuild = true;
|
||
children[i + 1] = -1;
|
||
continue;
|
||
}
|
||
let from = mapping.map(oldChildren[i] + oldOffset), fromLocal = from - offset;
|
||
if (fromLocal < 0 || fromLocal >= node.content.size) {
|
||
mustRebuild = true;
|
||
continue;
|
||
}
|
||
let to = mapping.map(oldChildren[i + 1] + oldOffset, -1), toLocal = to - offset;
|
||
let { index: index2, offset: childOffset } = node.content.findIndex(fromLocal);
|
||
let childNode = node.maybeChild(index2);
|
||
if (childNode && childOffset == fromLocal && childOffset + childNode.nodeSize == toLocal) {
|
||
let mapped = children[i + 2].mapInner(mapping, childNode, from + 1, oldChildren[i] + oldOffset + 1, options);
|
||
if (mapped != empty) {
|
||
children[i] = fromLocal;
|
||
children[i + 1] = toLocal;
|
||
children[i + 2] = mapped;
|
||
} else {
|
||
children[i + 1] = -2;
|
||
mustRebuild = true;
|
||
}
|
||
} else {
|
||
mustRebuild = true;
|
||
}
|
||
}
|
||
if (mustRebuild) {
|
||
let decorations = mapAndGatherRemainingDecorations(children, oldChildren, newLocal, mapping, offset, oldOffset, options);
|
||
let built = buildTree(decorations, node, 0, options);
|
||
newLocal = built.local;
|
||
for (let i = 0; i < children.length; i += 3)
|
||
if (children[i + 1] < 0) {
|
||
children.splice(i, 3);
|
||
i -= 3;
|
||
}
|
||
for (let i = 0, j = 0; i < built.children.length; i += 3) {
|
||
let from = built.children[i];
|
||
while (j < children.length && children[j] < from)
|
||
j += 3;
|
||
children.splice(j, 0, built.children[i], built.children[i + 1], built.children[i + 2]);
|
||
}
|
||
}
|
||
return new DecorationSet(newLocal.sort(byPos), children);
|
||
}
|
||
function moveSpans(spans, offset) {
|
||
if (!offset || !spans.length)
|
||
return spans;
|
||
let result = [];
|
||
for (let i = 0; i < spans.length; i++) {
|
||
let span = spans[i];
|
||
result.push(new Decoration(span.from + offset, span.to + offset, span.type));
|
||
}
|
||
return result;
|
||
}
|
||
function mapAndGatherRemainingDecorations(children, oldChildren, decorations, mapping, offset, oldOffset, options) {
|
||
function gather(set, oldOffset2) {
|
||
for (let i = 0; i < set.local.length; i++) {
|
||
let mapped = set.local[i].map(mapping, offset, oldOffset2);
|
||
if (mapped)
|
||
decorations.push(mapped);
|
||
else if (options.onRemove)
|
||
options.onRemove(set.local[i].spec);
|
||
}
|
||
for (let i = 0; i < set.children.length; i += 3)
|
||
gather(set.children[i + 2], set.children[i] + oldOffset2 + 1);
|
||
}
|
||
for (let i = 0; i < children.length; i += 3)
|
||
if (children[i + 1] == -1)
|
||
gather(children[i + 2], oldChildren[i] + oldOffset + 1);
|
||
return decorations;
|
||
}
|
||
function takeSpansForNode(spans, node, offset) {
|
||
if (node.isLeaf)
|
||
return null;
|
||
let end = offset + node.nodeSize, found = null;
|
||
for (let i = 0, span; i < spans.length; i++) {
|
||
if ((span = spans[i]) && span.from > offset && span.to < end) {
|
||
(found || (found = [])).push(span);
|
||
spans[i] = null;
|
||
}
|
||
}
|
||
return found;
|
||
}
|
||
function withoutNulls(array) {
|
||
let result = [];
|
||
for (let i = 0; i < array.length; i++)
|
||
if (array[i] != null)
|
||
result.push(array[i]);
|
||
return result;
|
||
}
|
||
function buildTree(spans, node, offset, options) {
|
||
let children = [], hasNulls = false;
|
||
node.forEach((childNode, localStart) => {
|
||
let found = takeSpansForNode(spans, childNode, localStart + offset);
|
||
if (found) {
|
||
hasNulls = true;
|
||
let subtree = buildTree(found, childNode, offset + localStart + 1, options);
|
||
if (subtree != empty)
|
||
children.push(localStart, localStart + childNode.nodeSize, subtree);
|
||
}
|
||
});
|
||
let locals = moveSpans(hasNulls ? withoutNulls(spans) : spans, -offset).sort(byPos);
|
||
for (let i = 0; i < locals.length; i++)
|
||
if (!locals[i].type.valid(node, locals[i])) {
|
||
if (options.onRemove)
|
||
options.onRemove(locals[i].spec);
|
||
locals.splice(i--, 1);
|
||
}
|
||
return locals.length || children.length ? new DecorationSet(locals, children) : empty;
|
||
}
|
||
function byPos(a, b) {
|
||
return a.from - b.from || a.to - b.to;
|
||
}
|
||
function removeOverlap(spans) {
|
||
let working = spans;
|
||
for (let i = 0; i < working.length - 1; i++) {
|
||
let span = working[i];
|
||
if (span.from != span.to)
|
||
for (let j = i + 1; j < working.length; j++) {
|
||
let next = working[j];
|
||
if (next.from == span.from) {
|
||
if (next.to != span.to) {
|
||
if (working == spans)
|
||
working = spans.slice();
|
||
working[j] = next.copy(next.from, span.to);
|
||
insertAhead(working, j + 1, next.copy(span.to, next.to));
|
||
}
|
||
continue;
|
||
} else {
|
||
if (next.from < span.to) {
|
||
if (working == spans)
|
||
working = spans.slice();
|
||
working[i] = span.copy(span.from, next.from);
|
||
insertAhead(working, j, span.copy(next.from, span.to));
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return working;
|
||
}
|
||
function insertAhead(array, i, deco) {
|
||
while (i < array.length && byPos(deco, array[i]) > 0)
|
||
i++;
|
||
array.splice(i, 0, deco);
|
||
}
|
||
function viewDecorations(view) {
|
||
let found = [];
|
||
view.someProp("decorations", (f) => {
|
||
let result = f(view.state);
|
||
if (result && result != empty)
|
||
found.push(result);
|
||
});
|
||
if (view.cursorWrapper)
|
||
found.push(DecorationSet.create(view.state.doc, [view.cursorWrapper.deco]));
|
||
return DecorationGroup.from(found);
|
||
}
|
||
var observeOptions = {
|
||
childList: true,
|
||
characterData: true,
|
||
characterDataOldValue: true,
|
||
attributes: true,
|
||
attributeOldValue: true,
|
||
subtree: true
|
||
};
|
||
var useCharData = ie && ie_version <= 11;
|
||
var SelectionState = class {
|
||
constructor() {
|
||
this.anchorNode = null;
|
||
this.anchorOffset = 0;
|
||
this.focusNode = null;
|
||
this.focusOffset = 0;
|
||
}
|
||
set(sel) {
|
||
this.anchorNode = sel.anchorNode;
|
||
this.anchorOffset = sel.anchorOffset;
|
||
this.focusNode = sel.focusNode;
|
||
this.focusOffset = sel.focusOffset;
|
||
}
|
||
clear() {
|
||
this.anchorNode = this.focusNode = null;
|
||
}
|
||
eq(sel) {
|
||
return sel.anchorNode == this.anchorNode && sel.anchorOffset == this.anchorOffset && sel.focusNode == this.focusNode && sel.focusOffset == this.focusOffset;
|
||
}
|
||
};
|
||
var DOMObserver = class {
|
||
constructor(view, handleDOMChange) {
|
||
this.view = view;
|
||
this.handleDOMChange = handleDOMChange;
|
||
this.queue = [];
|
||
this.flushingSoon = -1;
|
||
this.observer = null;
|
||
this.currentSelection = new SelectionState();
|
||
this.onCharData = null;
|
||
this.suppressingSelectionUpdates = false;
|
||
this.lastChangedTextNode = null;
|
||
this.observer = window.MutationObserver && new window.MutationObserver((mutations) => {
|
||
for (let i = 0; i < mutations.length; i++)
|
||
this.queue.push(mutations[i]);
|
||
if (ie && ie_version <= 11 && mutations.some((m) => m.type == "childList" && m.removedNodes.length || m.type == "characterData" && m.oldValue.length > m.target.nodeValue.length))
|
||
this.flushSoon();
|
||
else
|
||
this.flush();
|
||
});
|
||
if (useCharData) {
|
||
this.onCharData = (e) => {
|
||
this.queue.push({ target: e.target, type: "characterData", oldValue: e.prevValue });
|
||
this.flushSoon();
|
||
};
|
||
}
|
||
this.onSelectionChange = this.onSelectionChange.bind(this);
|
||
}
|
||
flushSoon() {
|
||
if (this.flushingSoon < 0)
|
||
this.flushingSoon = window.setTimeout(() => {
|
||
this.flushingSoon = -1;
|
||
this.flush();
|
||
}, 20);
|
||
}
|
||
forceFlush() {
|
||
if (this.flushingSoon > -1) {
|
||
window.clearTimeout(this.flushingSoon);
|
||
this.flushingSoon = -1;
|
||
this.flush();
|
||
}
|
||
}
|
||
start() {
|
||
if (this.observer) {
|
||
this.observer.takeRecords();
|
||
this.observer.observe(this.view.dom, observeOptions);
|
||
}
|
||
if (this.onCharData)
|
||
this.view.dom.addEventListener("DOMCharacterDataModified", this.onCharData);
|
||
this.connectSelection();
|
||
}
|
||
stop() {
|
||
if (this.observer) {
|
||
let take = this.observer.takeRecords();
|
||
if (take.length) {
|
||
for (let i = 0; i < take.length; i++)
|
||
this.queue.push(take[i]);
|
||
window.setTimeout(() => this.flush(), 20);
|
||
}
|
||
this.observer.disconnect();
|
||
}
|
||
if (this.onCharData)
|
||
this.view.dom.removeEventListener("DOMCharacterDataModified", this.onCharData);
|
||
this.disconnectSelection();
|
||
}
|
||
connectSelection() {
|
||
this.view.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange);
|
||
}
|
||
disconnectSelection() {
|
||
this.view.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
|
||
}
|
||
suppressSelectionUpdates() {
|
||
this.suppressingSelectionUpdates = true;
|
||
setTimeout(() => this.suppressingSelectionUpdates = false, 50);
|
||
}
|
||
onSelectionChange() {
|
||
if (!hasFocusAndSelection(this.view))
|
||
return;
|
||
if (this.suppressingSelectionUpdates)
|
||
return selectionToDOM(this.view);
|
||
if (ie && ie_version <= 11 && !this.view.state.selection.empty) {
|
||
let sel = this.view.domSelectionRange();
|
||
if (sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset))
|
||
return this.flushSoon();
|
||
}
|
||
this.flush();
|
||
}
|
||
setCurSelection() {
|
||
this.currentSelection.set(this.view.domSelectionRange());
|
||
}
|
||
ignoreSelectionChange(sel) {
|
||
if (!sel.focusNode)
|
||
return true;
|
||
let ancestors = /* @__PURE__ */ new Set(), container;
|
||
for (let scan = sel.focusNode; scan; scan = parentNode(scan))
|
||
ancestors.add(scan);
|
||
for (let scan = sel.anchorNode; scan; scan = parentNode(scan))
|
||
if (ancestors.has(scan)) {
|
||
container = scan;
|
||
break;
|
||
}
|
||
let desc = container && this.view.docView.nearestDesc(container);
|
||
if (desc && desc.ignoreMutation({
|
||
type: "selection",
|
||
target: container.nodeType == 3 ? container.parentNode : container
|
||
})) {
|
||
this.setCurSelection();
|
||
return true;
|
||
}
|
||
}
|
||
pendingRecords() {
|
||
if (this.observer)
|
||
for (let mut of this.observer.takeRecords())
|
||
this.queue.push(mut);
|
||
return this.queue;
|
||
}
|
||
flush() {
|
||
let { view } = this;
|
||
if (!view.docView || this.flushingSoon > -1)
|
||
return;
|
||
let mutations = this.pendingRecords();
|
||
if (mutations.length)
|
||
this.queue = [];
|
||
let sel = view.domSelectionRange();
|
||
let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel);
|
||
let from = -1, to = -1, typeOver = false, added = [];
|
||
if (view.editable) {
|
||
for (let i = 0; i < mutations.length; i++) {
|
||
let result = this.registerMutation(mutations[i], added);
|
||
if (result) {
|
||
from = from < 0 ? result.from : Math.min(result.from, from);
|
||
to = to < 0 ? result.to : Math.max(result.to, to);
|
||
if (result.typeOver)
|
||
typeOver = true;
|
||
}
|
||
}
|
||
}
|
||
if (gecko && added.length) {
|
||
let brs = added.filter((n) => n.nodeName == "BR");
|
||
if (brs.length == 2) {
|
||
let [a, b] = brs;
|
||
if (a.parentNode && a.parentNode.parentNode == b.parentNode)
|
||
b.remove();
|
||
else
|
||
a.remove();
|
||
} else {
|
||
let { focusNode } = this.currentSelection;
|
||
for (let br of brs) {
|
||
let parent = br.parentNode;
|
||
if (parent && parent.nodeName == "LI" && (!focusNode || blockParent(view, focusNode) != parent))
|
||
br.remove();
|
||
}
|
||
}
|
||
}
|
||
let readSel = null;
|
||
if (from < 0 && newSel && view.input.lastFocus > Date.now() - 200 && Math.max(view.input.lastTouch, view.input.lastClick.time) < Date.now() - 300 && selectionCollapsed(sel) && (readSel = selectionFromDOM(view)) && readSel.eq(Selection.near(view.state.doc.resolve(0), 1))) {
|
||
view.input.lastFocus = 0;
|
||
selectionToDOM(view);
|
||
this.currentSelection.set(sel);
|
||
view.scrollToSelection();
|
||
} else if (from > -1 || newSel) {
|
||
if (from > -1) {
|
||
view.docView.markDirty(from, to);
|
||
checkCSS(view);
|
||
}
|
||
this.handleDOMChange(from, to, typeOver, added);
|
||
if (view.docView && view.docView.dirty)
|
||
view.updateState(view.state);
|
||
else if (!this.currentSelection.eq(sel))
|
||
selectionToDOM(view);
|
||
this.currentSelection.set(sel);
|
||
}
|
||
}
|
||
registerMutation(mut, added) {
|
||
if (added.indexOf(mut.target) > -1)
|
||
return null;
|
||
let desc = this.view.docView.nearestDesc(mut.target);
|
||
if (mut.type == "attributes" && (desc == this.view.docView || mut.attributeName == "contenteditable" || // Firefox sometimes fires spurious events for null/empty styles
|
||
mut.attributeName == "style" && !mut.oldValue && !mut.target.getAttribute("style")))
|
||
return null;
|
||
if (!desc || desc.ignoreMutation(mut))
|
||
return null;
|
||
if (mut.type == "childList") {
|
||
for (let i = 0; i < mut.addedNodes.length; i++) {
|
||
let node = mut.addedNodes[i];
|
||
added.push(node);
|
||
if (node.nodeType == 3)
|
||
this.lastChangedTextNode = node;
|
||
}
|
||
if (desc.contentDOM && desc.contentDOM != desc.dom && !desc.contentDOM.contains(mut.target))
|
||
return { from: desc.posBefore, to: desc.posAfter };
|
||
let prev = mut.previousSibling, next = mut.nextSibling;
|
||
if (ie && ie_version <= 11 && mut.addedNodes.length) {
|
||
for (let i = 0; i < mut.addedNodes.length; i++) {
|
||
let { previousSibling, nextSibling } = mut.addedNodes[i];
|
||
if (!previousSibling || Array.prototype.indexOf.call(mut.addedNodes, previousSibling) < 0)
|
||
prev = previousSibling;
|
||
if (!nextSibling || Array.prototype.indexOf.call(mut.addedNodes, nextSibling) < 0)
|
||
next = nextSibling;
|
||
}
|
||
}
|
||
let fromOffset = prev && prev.parentNode == mut.target ? domIndex(prev) + 1 : 0;
|
||
let from = desc.localPosFromDOM(mut.target, fromOffset, -1);
|
||
let toOffset = next && next.parentNode == mut.target ? domIndex(next) : mut.target.childNodes.length;
|
||
let to = desc.localPosFromDOM(mut.target, toOffset, 1);
|
||
return { from, to };
|
||
} else if (mut.type == "attributes") {
|
||
return { from: desc.posAtStart - desc.border, to: desc.posAtEnd + desc.border };
|
||
} else {
|
||
this.lastChangedTextNode = mut.target;
|
||
return {
|
||
from: desc.posAtStart,
|
||
to: desc.posAtEnd,
|
||
// An event was generated for a text change that didn't change
|
||
// any text. Mark the dom change to fall back to assuming the
|
||
// selection was typed over with an identical value if it can't
|
||
// find another change.
|
||
typeOver: mut.target.nodeValue == mut.oldValue
|
||
};
|
||
}
|
||
}
|
||
};
|
||
var cssChecked = /* @__PURE__ */ new WeakMap();
|
||
var cssCheckWarned = false;
|
||
function checkCSS(view) {
|
||
if (cssChecked.has(view))
|
||
return;
|
||
cssChecked.set(view, null);
|
||
if (["normal", "nowrap", "pre-line"].indexOf(getComputedStyle(view.dom).whiteSpace) !== -1) {
|
||
view.requiresGeckoHackNode = gecko;
|
||
if (cssCheckWarned)
|
||
return;
|
||
console["warn"]("ProseMirror expects the CSS white-space property to be set, preferably to 'pre-wrap'. It is recommended to load style/prosemirror.css from the prosemirror-view package.");
|
||
cssCheckWarned = true;
|
||
}
|
||
}
|
||
function rangeToSelectionRange(view, range) {
|
||
let anchorNode = range.startContainer, anchorOffset = range.startOffset;
|
||
let focusNode = range.endContainer, focusOffset = range.endOffset;
|
||
let currentAnchor = view.domAtPos(view.state.selection.anchor);
|
||
if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
|
||
[anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
|
||
return { anchorNode, anchorOffset, focusNode, focusOffset };
|
||
}
|
||
function safariShadowSelectionRange(view, selection) {
|
||
if (selection.getComposedRanges) {
|
||
let range = selection.getComposedRanges(view.root)[0];
|
||
if (range)
|
||
return rangeToSelectionRange(view, range);
|
||
}
|
||
let found;
|
||
function read(event) {
|
||
event.preventDefault();
|
||
event.stopImmediatePropagation();
|
||
found = event.getTargetRanges()[0];
|
||
}
|
||
view.dom.addEventListener("beforeinput", read, true);
|
||
document.execCommand("indent");
|
||
view.dom.removeEventListener("beforeinput", read, true);
|
||
return found ? rangeToSelectionRange(view, found) : null;
|
||
}
|
||
function blockParent(view, node) {
|
||
for (let p = node.parentNode; p && p != view.dom; p = p.parentNode) {
|
||
let desc = view.docView.nearestDesc(p, true);
|
||
if (desc && desc.node.isBlock)
|
||
return p;
|
||
}
|
||
return null;
|
||
}
|
||
function parseBetween(view, from_, to_) {
|
||
let { node: parent, fromOffset, toOffset, from, to } = view.docView.parseRange(from_, to_);
|
||
let domSel = view.domSelectionRange();
|
||
let find;
|
||
let anchor = domSel.anchorNode;
|
||
if (anchor && view.dom.contains(anchor.nodeType == 1 ? anchor : anchor.parentNode)) {
|
||
find = [{ node: anchor, offset: domSel.anchorOffset }];
|
||
if (!selectionCollapsed(domSel))
|
||
find.push({ node: domSel.focusNode, offset: domSel.focusOffset });
|
||
}
|
||
if (chrome && view.input.lastKeyCode === 8) {
|
||
for (let off = toOffset; off > fromOffset; off--) {
|
||
let node = parent.childNodes[off - 1], desc = node.pmViewDesc;
|
||
if (node.nodeName == "BR" && !desc) {
|
||
toOffset = off;
|
||
break;
|
||
}
|
||
if (!desc || desc.size)
|
||
break;
|
||
}
|
||
}
|
||
let startDoc = view.state.doc;
|
||
let parser = view.someProp("domParser") || DOMParser.fromSchema(view.state.schema);
|
||
let $from = startDoc.resolve(from);
|
||
let sel = null, doc2 = parser.parse(parent, {
|
||
topNode: $from.parent,
|
||
topMatch: $from.parent.contentMatchAt($from.index()),
|
||
topOpen: true,
|
||
from: fromOffset,
|
||
to: toOffset,
|
||
preserveWhitespace: $from.parent.type.whitespace == "pre" ? "full" : true,
|
||
findPositions: find,
|
||
ruleFromNode,
|
||
context: $from
|
||
});
|
||
if (find && find[0].pos != null) {
|
||
let anchor2 = find[0].pos, head = find[1] && find[1].pos;
|
||
if (head == null)
|
||
head = anchor2;
|
||
sel = { anchor: anchor2 + from, head: head + from };
|
||
}
|
||
return { doc: doc2, sel, from, to };
|
||
}
|
||
function ruleFromNode(dom) {
|
||
let desc = dom.pmViewDesc;
|
||
if (desc) {
|
||
return desc.parseRule();
|
||
} else if (dom.nodeName == "BR" && dom.parentNode) {
|
||
if (safari && /^(ul|ol)$/i.test(dom.parentNode.nodeName)) {
|
||
let skip = document.createElement("div");
|
||
skip.appendChild(document.createElement("li"));
|
||
return { skip };
|
||
} else if (dom.parentNode.lastChild == dom || safari && /^(tr|table)$/i.test(dom.parentNode.nodeName)) {
|
||
return { ignore: true };
|
||
}
|
||
} else if (dom.nodeName == "IMG" && dom.getAttribute("mark-placeholder")) {
|
||
return { ignore: true };
|
||
}
|
||
return null;
|
||
}
|
||
var isInline = /^(a|abbr|acronym|b|bd[io]|big|br|button|cite|code|data(list)?|del|dfn|em|i|ins|kbd|label|map|mark|meter|output|q|ruby|s|samp|small|span|strong|su[bp]|time|u|tt|var)$/i;
|
||
function readDOMChange(view, from, to, typeOver, addedNodes) {
|
||
let compositionID = view.input.compositionPendingChanges || (view.composing ? view.input.compositionID : 0);
|
||
view.input.compositionPendingChanges = 0;
|
||
if (from < 0) {
|
||
let origin = view.input.lastSelectionTime > Date.now() - 50 ? view.input.lastSelectionOrigin : null;
|
||
let newSel = selectionFromDOM(view, origin);
|
||
if (newSel && !view.state.selection.eq(newSel)) {
|
||
if (chrome && android && view.input.lastKeyCode === 13 && Date.now() - 100 < view.input.lastKeyCodeTime && view.someProp("handleKeyDown", (f) => f(view, keyEvent(13, "Enter"))))
|
||
return;
|
||
let tr2 = view.state.tr.setSelection(newSel);
|
||
if (origin == "pointer")
|
||
tr2.setMeta("pointer", true);
|
||
else if (origin == "key")
|
||
tr2.scrollIntoView();
|
||
if (compositionID)
|
||
tr2.setMeta("composition", compositionID);
|
||
view.dispatch(tr2);
|
||
}
|
||
return;
|
||
}
|
||
let $before = view.state.doc.resolve(from);
|
||
let shared = $before.sharedDepth(to);
|
||
from = $before.before(shared + 1);
|
||
to = view.state.doc.resolve(to).after(shared + 1);
|
||
let sel = view.state.selection;
|
||
let parse = parseBetween(view, from, to);
|
||
let doc2 = view.state.doc, compare = doc2.slice(parse.from, parse.to);
|
||
let preferredPos, preferredSide;
|
||
if (view.input.lastKeyCode === 8 && Date.now() - 100 < view.input.lastKeyCodeTime) {
|
||
preferredPos = view.state.selection.to;
|
||
preferredSide = "end";
|
||
} else {
|
||
preferredPos = view.state.selection.from;
|
||
preferredSide = "start";
|
||
}
|
||
view.input.lastKeyCode = null;
|
||
let change = findDiff(compare.content, parse.doc.content, parse.from, preferredPos, preferredSide);
|
||
if (change)
|
||
view.input.domChangeCount++;
|
||
if ((ios && view.input.lastIOSEnter > Date.now() - 225 || android) && addedNodes.some((n) => n.nodeType == 1 && !isInline.test(n.nodeName)) && (!change || change.endA >= change.endB) && view.someProp("handleKeyDown", (f) => f(view, keyEvent(13, "Enter")))) {
|
||
view.input.lastIOSEnter = 0;
|
||
return;
|
||
}
|
||
if (!change) {
|
||
if (typeOver && sel instanceof TextSelection && !sel.empty && sel.$head.sameParent(sel.$anchor) && !view.composing && !(parse.sel && parse.sel.anchor != parse.sel.head)) {
|
||
change = { start: sel.from, endA: sel.to, endB: sel.to };
|
||
} else {
|
||
if (parse.sel) {
|
||
let sel2 = resolveSelection(view, view.state.doc, parse.sel);
|
||
if (sel2 && !sel2.eq(view.state.selection)) {
|
||
let tr2 = view.state.tr.setSelection(sel2);
|
||
if (compositionID)
|
||
tr2.setMeta("composition", compositionID);
|
||
view.dispatch(tr2);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
if (view.state.selection.from < view.state.selection.to && change.start == change.endB && view.state.selection instanceof TextSelection) {
|
||
if (change.start > view.state.selection.from && change.start <= view.state.selection.from + 2 && view.state.selection.from >= parse.from) {
|
||
change.start = view.state.selection.from;
|
||
} else if (change.endA < view.state.selection.to && change.endA >= view.state.selection.to - 2 && view.state.selection.to <= parse.to) {
|
||
change.endB += view.state.selection.to - change.endA;
|
||
change.endA = view.state.selection.to;
|
||
}
|
||
}
|
||
if (ie && ie_version <= 11 && change.endB == change.start + 1 && change.endA == change.start && change.start > parse.from && parse.doc.textBetween(change.start - parse.from - 1, change.start - parse.from + 1) == " ") {
|
||
change.start--;
|
||
change.endA--;
|
||
change.endB--;
|
||
}
|
||
let $from = parse.doc.resolveNoCache(change.start - parse.from);
|
||
let $to = parse.doc.resolveNoCache(change.endB - parse.from);
|
||
let $fromA = doc2.resolve(change.start);
|
||
let inlineChange = $from.sameParent($to) && $from.parent.inlineContent && $fromA.end() >= change.endA;
|
||
let nextSel;
|
||
if ((ios && view.input.lastIOSEnter > Date.now() - 225 && (!inlineChange || addedNodes.some((n) => n.nodeName == "DIV" || n.nodeName == "P")) || !inlineChange && $from.pos < parse.doc.content.size && !$from.sameParent($to) && (nextSel = Selection.findFrom(parse.doc.resolve($from.pos + 1), 1, true)) && nextSel.head == $to.pos) && view.someProp("handleKeyDown", (f) => f(view, keyEvent(13, "Enter")))) {
|
||
view.input.lastIOSEnter = 0;
|
||
return;
|
||
}
|
||
if (view.state.selection.anchor > change.start && looksLikeBackspace(doc2, change.start, change.endA, $from, $to) && view.someProp("handleKeyDown", (f) => f(view, keyEvent(8, "Backspace")))) {
|
||
if (android && chrome)
|
||
view.domObserver.suppressSelectionUpdates();
|
||
return;
|
||
}
|
||
if (chrome && android && change.endB == change.start)
|
||
view.input.lastAndroidDelete = Date.now();
|
||
if (android && !inlineChange && $from.start() != $to.start() && $to.parentOffset == 0 && $from.depth == $to.depth && parse.sel && parse.sel.anchor == parse.sel.head && parse.sel.head == change.endA) {
|
||
change.endB -= 2;
|
||
$to = parse.doc.resolveNoCache(change.endB - parse.from);
|
||
setTimeout(() => {
|
||
view.someProp("handleKeyDown", function(f) {
|
||
return f(view, keyEvent(13, "Enter"));
|
||
});
|
||
}, 20);
|
||
}
|
||
let chFrom = change.start, chTo = change.endA;
|
||
let tr, storedMarks, markChange;
|
||
if (inlineChange) {
|
||
if ($from.pos == $to.pos) {
|
||
if (ie && ie_version <= 11 && $from.parentOffset == 0) {
|
||
view.domObserver.suppressSelectionUpdates();
|
||
setTimeout(() => selectionToDOM(view), 20);
|
||
}
|
||
tr = view.state.tr.delete(chFrom, chTo);
|
||
storedMarks = doc2.resolve(change.start).marksAcross(doc2.resolve(change.endA));
|
||
} else if (
|
||
// Adding or removing a mark
|
||
change.endA == change.endB && (markChange = isMarkChange($from.parent.content.cut($from.parentOffset, $to.parentOffset), $fromA.parent.content.cut($fromA.parentOffset, change.endA - $fromA.start())))
|
||
) {
|
||
tr = view.state.tr;
|
||
if (markChange.type == "add")
|
||
tr.addMark(chFrom, chTo, markChange.mark);
|
||
else
|
||
tr.removeMark(chFrom, chTo, markChange.mark);
|
||
} else if ($from.parent.child($from.index()).isText && $from.index() == $to.index() - ($to.textOffset ? 0 : 1)) {
|
||
let text = $from.parent.textBetween($from.parentOffset, $to.parentOffset);
|
||
if (view.someProp("handleTextInput", (f) => f(view, chFrom, chTo, text)))
|
||
return;
|
||
tr = view.state.tr.insertText(text, chFrom, chTo);
|
||
}
|
||
}
|
||
if (!tr)
|
||
tr = view.state.tr.replace(chFrom, chTo, parse.doc.slice(change.start - parse.from, change.endB - parse.from));
|
||
if (parse.sel) {
|
||
let sel2 = resolveSelection(view, tr.doc, parse.sel);
|
||
if (sel2 && !(chrome && android && view.composing && sel2.empty && (change.start != change.endB || view.input.lastAndroidDelete < Date.now() - 100) && (sel2.head == chFrom || sel2.head == tr.mapping.map(chTo) - 1) || ie && sel2.empty && sel2.head == chFrom))
|
||
tr.setSelection(sel2);
|
||
}
|
||
if (storedMarks)
|
||
tr.ensureMarks(storedMarks);
|
||
if (compositionID)
|
||
tr.setMeta("composition", compositionID);
|
||
view.dispatch(tr.scrollIntoView());
|
||
}
|
||
function resolveSelection(view, doc2, parsedSel) {
|
||
if (Math.max(parsedSel.anchor, parsedSel.head) > doc2.content.size)
|
||
return null;
|
||
return selectionBetween(view, doc2.resolve(parsedSel.anchor), doc2.resolve(parsedSel.head));
|
||
}
|
||
function isMarkChange(cur, prev) {
|
||
let curMarks = cur.firstChild.marks, prevMarks = prev.firstChild.marks;
|
||
let added = curMarks, removed = prevMarks, type, mark, update;
|
||
for (let i = 0; i < prevMarks.length; i++)
|
||
added = prevMarks[i].removeFromSet(added);
|
||
for (let i = 0; i < curMarks.length; i++)
|
||
removed = curMarks[i].removeFromSet(removed);
|
||
if (added.length == 1 && removed.length == 0) {
|
||
mark = added[0];
|
||
type = "add";
|
||
update = (node) => node.mark(mark.addToSet(node.marks));
|
||
} else if (added.length == 0 && removed.length == 1) {
|
||
mark = removed[0];
|
||
type = "remove";
|
||
update = (node) => node.mark(mark.removeFromSet(node.marks));
|
||
} else {
|
||
return null;
|
||
}
|
||
let updated = [];
|
||
for (let i = 0; i < prev.childCount; i++)
|
||
updated.push(update(prev.child(i)));
|
||
if (Fragment.from(updated).eq(cur))
|
||
return { mark, type };
|
||
}
|
||
function looksLikeBackspace(old, start, end, $newStart, $newEnd) {
|
||
if (
|
||
// The content must have shrunk
|
||
end - start <= $newEnd.pos - $newStart.pos || // newEnd must point directly at or after the end of the block that newStart points into
|
||
skipClosingAndOpening($newStart, true, false) < $newEnd.pos
|
||
)
|
||
return false;
|
||
let $start = old.resolve(start);
|
||
if (!$newStart.parent.isTextblock) {
|
||
let after = $start.nodeAfter;
|
||
return after != null && end == start + after.nodeSize;
|
||
}
|
||
if ($start.parentOffset < $start.parent.content.size || !$start.parent.isTextblock)
|
||
return false;
|
||
let $next = old.resolve(skipClosingAndOpening($start, true, true));
|
||
if (!$next.parent.isTextblock || $next.pos > end || skipClosingAndOpening($next, true, false) < end)
|
||
return false;
|
||
return $newStart.parent.content.cut($newStart.parentOffset).eq($next.parent.content);
|
||
}
|
||
function skipClosingAndOpening($pos, fromEnd, mayOpen) {
|
||
let depth = $pos.depth, end = fromEnd ? $pos.end() : $pos.pos;
|
||
while (depth > 0 && (fromEnd || $pos.indexAfter(depth) == $pos.node(depth).childCount)) {
|
||
depth--;
|
||
end++;
|
||
fromEnd = false;
|
||
}
|
||
if (mayOpen) {
|
||
let next = $pos.node(depth).maybeChild($pos.indexAfter(depth));
|
||
while (next && !next.isLeaf) {
|
||
next = next.firstChild;
|
||
end++;
|
||
}
|
||
}
|
||
return end;
|
||
}
|
||
function findDiff(a, b, pos, preferredPos, preferredSide) {
|
||
let start = a.findDiffStart(b, pos);
|
||
if (start == null)
|
||
return null;
|
||
let { a: endA, b: endB } = a.findDiffEnd(b, pos + a.size, pos + b.size);
|
||
if (preferredSide == "end") {
|
||
let adjust = Math.max(0, start - Math.min(endA, endB));
|
||
preferredPos -= endA + adjust - start;
|
||
}
|
||
if (endA < start && a.size < b.size) {
|
||
let move = preferredPos <= start && preferredPos >= endA ? start - preferredPos : 0;
|
||
start -= move;
|
||
if (start && start < b.size && isSurrogatePair(b.textBetween(start - 1, start + 1)))
|
||
start += move ? 1 : -1;
|
||
endB = start + (endB - endA);
|
||
endA = start;
|
||
} else if (endB < start) {
|
||
let move = preferredPos <= start && preferredPos >= endB ? start - preferredPos : 0;
|
||
start -= move;
|
||
if (start && start < a.size && isSurrogatePair(a.textBetween(start - 1, start + 1)))
|
||
start += move ? 1 : -1;
|
||
endA = start + (endA - endB);
|
||
endB = start;
|
||
}
|
||
return { start, endA, endB };
|
||
}
|
||
function isSurrogatePair(str) {
|
||
if (str.length != 2)
|
||
return false;
|
||
let a = str.charCodeAt(0), b = str.charCodeAt(1);
|
||
return a >= 56320 && a <= 57343 && b >= 55296 && b <= 56319;
|
||
}
|
||
var EditorView = class {
|
||
/**
|
||
Create a view. `place` may be a DOM node that the editor should
|
||
be appended to, a function that will place it into the document,
|
||
or an object whose `mount` property holds the node to use as the
|
||
document container. If it is `null`, the editor will not be
|
||
added to the document.
|
||
*/
|
||
constructor(place, props) {
|
||
this._root = null;
|
||
this.focused = false;
|
||
this.trackWrites = null;
|
||
this.mounted = false;
|
||
this.markCursor = null;
|
||
this.cursorWrapper = null;
|
||
this.lastSelectedViewDesc = void 0;
|
||
this.input = new InputState();
|
||
this.prevDirectPlugins = [];
|
||
this.pluginViews = [];
|
||
this.requiresGeckoHackNode = false;
|
||
this.dragging = null;
|
||
this._props = props;
|
||
this.state = props.state;
|
||
this.directPlugins = props.plugins || [];
|
||
this.directPlugins.forEach(checkStateComponent);
|
||
this.dispatch = this.dispatch.bind(this);
|
||
this.dom = place && place.mount || document.createElement("div");
|
||
if (place) {
|
||
if (place.appendChild)
|
||
place.appendChild(this.dom);
|
||
else if (typeof place == "function")
|
||
place(this.dom);
|
||
else if (place.mount)
|
||
this.mounted = true;
|
||
}
|
||
this.editable = getEditable(this);
|
||
updateCursorWrapper(this);
|
||
this.nodeViews = buildNodeViews(this);
|
||
this.docView = docViewDesc(this.state.doc, computeDocDeco(this), viewDecorations(this), this.dom, this);
|
||
this.domObserver = new DOMObserver(this, (from, to, typeOver, added) => readDOMChange(this, from, to, typeOver, added));
|
||
this.domObserver.start();
|
||
initInput(this);
|
||
this.updatePluginViews();
|
||
}
|
||
/**
|
||
Holds `true` when a
|
||
[composition](https://w3c.github.io/uievents/#events-compositionevents)
|
||
is active.
|
||
*/
|
||
get composing() {
|
||
return this.input.composing;
|
||
}
|
||
/**
|
||
The view's current [props](https://prosemirror.net/docs/ref/#view.EditorProps).
|
||
*/
|
||
get props() {
|
||
if (this._props.state != this.state) {
|
||
let prev = this._props;
|
||
this._props = {};
|
||
for (let name in prev)
|
||
this._props[name] = prev[name];
|
||
this._props.state = this.state;
|
||
}
|
||
return this._props;
|
||
}
|
||
/**
|
||
Update the view's props. Will immediately cause an update to
|
||
the DOM.
|
||
*/
|
||
update(props) {
|
||
if (props.handleDOMEvents != this._props.handleDOMEvents)
|
||
ensureListeners(this);
|
||
let prevProps = this._props;
|
||
this._props = props;
|
||
if (props.plugins) {
|
||
props.plugins.forEach(checkStateComponent);
|
||
this.directPlugins = props.plugins;
|
||
}
|
||
this.updateStateInner(props.state, prevProps);
|
||
}
|
||
/**
|
||
Update the view by updating existing props object with the object
|
||
given as argument. Equivalent to `view.update(Object.assign({},
|
||
view.props, props))`.
|
||
*/
|
||
setProps(props) {
|
||
let updated = {};
|
||
for (let name in this._props)
|
||
updated[name] = this._props[name];
|
||
updated.state = this.state;
|
||
for (let name in props)
|
||
updated[name] = props[name];
|
||
this.update(updated);
|
||
}
|
||
/**
|
||
Update the editor's `state` prop, without touching any of the
|
||
other props.
|
||
*/
|
||
updateState(state) {
|
||
this.updateStateInner(state, this._props);
|
||
}
|
||
updateStateInner(state, prevProps) {
|
||
var _a;
|
||
let prev = this.state, redraw = false, updateSel = false;
|
||
if (state.storedMarks && this.composing) {
|
||
clearComposition(this);
|
||
updateSel = true;
|
||
}
|
||
this.state = state;
|
||
let pluginsChanged = prev.plugins != state.plugins || this._props.plugins != prevProps.plugins;
|
||
if (pluginsChanged || this._props.plugins != prevProps.plugins || this._props.nodeViews != prevProps.nodeViews) {
|
||
let nodeViews = buildNodeViews(this);
|
||
if (changedNodeViews(nodeViews, this.nodeViews)) {
|
||
this.nodeViews = nodeViews;
|
||
redraw = true;
|
||
}
|
||
}
|
||
if (pluginsChanged || prevProps.handleDOMEvents != this._props.handleDOMEvents) {
|
||
ensureListeners(this);
|
||
}
|
||
this.editable = getEditable(this);
|
||
updateCursorWrapper(this);
|
||
let innerDeco = viewDecorations(this), outerDeco = computeDocDeco(this);
|
||
let scroll = prev.plugins != state.plugins && !prev.doc.eq(state.doc) ? "reset" : state.scrollToSelection > prev.scrollToSelection ? "to selection" : "preserve";
|
||
let updateDoc = redraw || !this.docView.matchesNode(state.doc, outerDeco, innerDeco);
|
||
if (updateDoc || !state.selection.eq(prev.selection))
|
||
updateSel = true;
|
||
let oldScrollPos = scroll == "preserve" && updateSel && this.dom.style.overflowAnchor == null && storeScrollPos(this);
|
||
if (updateSel) {
|
||
this.domObserver.stop();
|
||
let forceSelUpdate = updateDoc && (ie || chrome) && !this.composing && !prev.selection.empty && !state.selection.empty && selectionContextChanged(prev.selection, state.selection);
|
||
if (updateDoc) {
|
||
let chromeKludge = chrome ? this.trackWrites = this.domSelectionRange().focusNode : null;
|
||
if (this.composing)
|
||
this.input.compositionNode = findCompositionNode(this);
|
||
if (redraw || !this.docView.update(state.doc, outerDeco, innerDeco, this)) {
|
||
this.docView.updateOuterDeco(outerDeco);
|
||
this.docView.destroy();
|
||
this.docView = docViewDesc(state.doc, outerDeco, innerDeco, this.dom, this);
|
||
}
|
||
if (chromeKludge && !this.trackWrites)
|
||
forceSelUpdate = true;
|
||
}
|
||
if (forceSelUpdate || !(this.input.mouseDown && this.domObserver.currentSelection.eq(this.domSelectionRange()) && anchorInRightPlace(this))) {
|
||
selectionToDOM(this, forceSelUpdate);
|
||
} else {
|
||
syncNodeSelection(this, state.selection);
|
||
this.domObserver.setCurSelection();
|
||
}
|
||
this.domObserver.start();
|
||
}
|
||
this.updatePluginViews(prev);
|
||
if (((_a = this.dragging) === null || _a === void 0 ? void 0 : _a.node) && !prev.doc.eq(state.doc))
|
||
this.updateDraggedNode(this.dragging, prev);
|
||
if (scroll == "reset") {
|
||
this.dom.scrollTop = 0;
|
||
} else if (scroll == "to selection") {
|
||
this.scrollToSelection();
|
||
} else if (oldScrollPos) {
|
||
resetScrollPos(oldScrollPos);
|
||
}
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
scrollToSelection() {
|
||
let startDOM = this.domSelectionRange().focusNode;
|
||
if (this.someProp("handleScrollToSelection", (f) => f(this))) ;
|
||
else if (this.state.selection instanceof NodeSelection) {
|
||
let target = this.docView.domAfterPos(this.state.selection.from);
|
||
if (target.nodeType == 1)
|
||
scrollRectIntoView(this, target.getBoundingClientRect(), startDOM);
|
||
} else {
|
||
scrollRectIntoView(this, this.coordsAtPos(this.state.selection.head, 1), startDOM);
|
||
}
|
||
}
|
||
destroyPluginViews() {
|
||
let view;
|
||
while (view = this.pluginViews.pop())
|
||
if (view.destroy)
|
||
view.destroy();
|
||
}
|
||
updatePluginViews(prevState) {
|
||
if (!prevState || prevState.plugins != this.state.plugins || this.directPlugins != this.prevDirectPlugins) {
|
||
this.prevDirectPlugins = this.directPlugins;
|
||
this.destroyPluginViews();
|
||
for (let i = 0; i < this.directPlugins.length; i++) {
|
||
let plugin = this.directPlugins[i];
|
||
if (plugin.spec.view)
|
||
this.pluginViews.push(plugin.spec.view(this));
|
||
}
|
||
for (let i = 0; i < this.state.plugins.length; i++) {
|
||
let plugin = this.state.plugins[i];
|
||
if (plugin.spec.view)
|
||
this.pluginViews.push(plugin.spec.view(this));
|
||
}
|
||
} else {
|
||
for (let i = 0; i < this.pluginViews.length; i++) {
|
||
let pluginView = this.pluginViews[i];
|
||
if (pluginView.update)
|
||
pluginView.update(this, prevState);
|
||
}
|
||
}
|
||
}
|
||
updateDraggedNode(dragging, prev) {
|
||
let sel = dragging.node, found = -1;
|
||
if (this.state.doc.nodeAt(sel.from) == sel.node) {
|
||
found = sel.from;
|
||
} else {
|
||
let movedPos = sel.from + (this.state.doc.content.size - prev.doc.content.size);
|
||
let moved = movedPos > 0 && this.state.doc.nodeAt(movedPos);
|
||
if (moved == sel.node)
|
||
found = movedPos;
|
||
}
|
||
this.dragging = new Dragging(dragging.slice, dragging.move, found < 0 ? void 0 : NodeSelection.create(this.state.doc, found));
|
||
}
|
||
someProp(propName, f) {
|
||
let prop = this._props && this._props[propName], value;
|
||
if (prop != null && (value = f ? f(prop) : prop))
|
||
return value;
|
||
for (let i = 0; i < this.directPlugins.length; i++) {
|
||
let prop2 = this.directPlugins[i].props[propName];
|
||
if (prop2 != null && (value = f ? f(prop2) : prop2))
|
||
return value;
|
||
}
|
||
let plugins = this.state.plugins;
|
||
if (plugins)
|
||
for (let i = 0; i < plugins.length; i++) {
|
||
let prop2 = plugins[i].props[propName];
|
||
if (prop2 != null && (value = f ? f(prop2) : prop2))
|
||
return value;
|
||
}
|
||
}
|
||
/**
|
||
Query whether the view has focus.
|
||
*/
|
||
hasFocus() {
|
||
if (ie) {
|
||
let node = this.root.activeElement;
|
||
if (node == this.dom)
|
||
return true;
|
||
if (!node || !this.dom.contains(node))
|
||
return false;
|
||
while (node && this.dom != node && this.dom.contains(node)) {
|
||
if (node.contentEditable == "false")
|
||
return false;
|
||
node = node.parentElement;
|
||
}
|
||
return true;
|
||
}
|
||
return this.root.activeElement == this.dom;
|
||
}
|
||
/**
|
||
Focus the editor.
|
||
*/
|
||
focus() {
|
||
this.domObserver.stop();
|
||
if (this.editable)
|
||
focusPreventScroll(this.dom);
|
||
selectionToDOM(this);
|
||
this.domObserver.start();
|
||
}
|
||
/**
|
||
Get the document root in which the editor exists. This will
|
||
usually be the top-level `document`, but might be a [shadow
|
||
DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM)
|
||
root if the editor is inside one.
|
||
*/
|
||
get root() {
|
||
let cached = this._root;
|
||
if (cached == null)
|
||
for (let search = this.dom.parentNode; search; search = search.parentNode) {
|
||
if (search.nodeType == 9 || search.nodeType == 11 && search.host) {
|
||
if (!search.getSelection)
|
||
Object.getPrototypeOf(search).getSelection = () => search.ownerDocument.getSelection();
|
||
return this._root = search;
|
||
}
|
||
}
|
||
return cached || document;
|
||
}
|
||
/**
|
||
When an existing editor view is moved to a new document or
|
||
shadow tree, call this to make it recompute its root.
|
||
*/
|
||
updateRoot() {
|
||
this._root = null;
|
||
}
|
||
/**
|
||
Given a pair of viewport coordinates, return the document
|
||
position that corresponds to them. May return null if the given
|
||
coordinates aren't inside of the editor. When an object is
|
||
returned, its `pos` property is the position nearest to the
|
||
coordinates, and its `inside` property holds the position of the
|
||
inner node that the position falls inside of, or -1 if it is at
|
||
the top level, not in any node.
|
||
*/
|
||
posAtCoords(coords) {
|
||
return posAtCoords(this, coords);
|
||
}
|
||
/**
|
||
Returns the viewport rectangle at a given document position.
|
||
`left` and `right` will be the same number, as this returns a
|
||
flat cursor-ish rectangle. If the position is between two things
|
||
that aren't directly adjacent, `side` determines which element
|
||
is used. When < 0, the element before the position is used,
|
||
otherwise the element after.
|
||
*/
|
||
coordsAtPos(pos, side = 1) {
|
||
return coordsAtPos(this, pos, side);
|
||
}
|
||
/**
|
||
Find the DOM position that corresponds to the given document
|
||
position. When `side` is negative, find the position as close as
|
||
possible to the content before the position. When positive,
|
||
prefer positions close to the content after the position. When
|
||
zero, prefer as shallow a position as possible.
|
||
|
||
Note that you should **not** mutate the editor's internal DOM,
|
||
only inspect it (and even that is usually not necessary).
|
||
*/
|
||
domAtPos(pos, side = 0) {
|
||
return this.docView.domFromPos(pos, side);
|
||
}
|
||
/**
|
||
Find the DOM node that represents the document node after the
|
||
given position. May return `null` when the position doesn't point
|
||
in front of a node or if the node is inside an opaque node view.
|
||
|
||
This is intended to be able to call things like
|
||
`getBoundingClientRect` on that DOM node. Do **not** mutate the
|
||
editor DOM directly, or add styling this way, since that will be
|
||
immediately overriden by the editor as it redraws the node.
|
||
*/
|
||
nodeDOM(pos) {
|
||
let desc = this.docView.descAt(pos);
|
||
return desc ? desc.nodeDOM : null;
|
||
}
|
||
/**
|
||
Find the document position that corresponds to a given DOM
|
||
position. (Whenever possible, it is preferable to inspect the
|
||
document structure directly, rather than poking around in the
|
||
DOM, but sometimes—for example when interpreting an event
|
||
target—you don't have a choice.)
|
||
|
||
The `bias` parameter can be used to influence which side of a DOM
|
||
node to use when the position is inside a leaf node.
|
||
*/
|
||
posAtDOM(node, offset, bias = -1) {
|
||
let pos = this.docView.posFromDOM(node, offset, bias);
|
||
if (pos == null)
|
||
throw new RangeError("DOM position not inside the editor");
|
||
return pos;
|
||
}
|
||
/**
|
||
Find out whether the selection is at the end of a textblock when
|
||
moving in a given direction. When, for example, given `"left"`,
|
||
it will return true if moving left from the current cursor
|
||
position would leave that position's parent textblock. Will apply
|
||
to the view's current state by default, but it is possible to
|
||
pass a different state.
|
||
*/
|
||
endOfTextblock(dir, state) {
|
||
return endOfTextblock(this, state || this.state, dir);
|
||
}
|
||
/**
|
||
Run the editor's paste logic with the given HTML string. The
|
||
`event`, if given, will be passed to the
|
||
[`handlePaste`](https://prosemirror.net/docs/ref/#view.EditorProps.handlePaste) hook.
|
||
*/
|
||
pasteHTML(html, event) {
|
||
return doPaste(this, "", html, false, event || new ClipboardEvent("paste"));
|
||
}
|
||
/**
|
||
Run the editor's paste logic with the given plain-text input.
|
||
*/
|
||
pasteText(text, event) {
|
||
return doPaste(this, text, null, true, event || new ClipboardEvent("paste"));
|
||
}
|
||
/**
|
||
Removes the editor from the DOM and destroys all [node
|
||
views](https://prosemirror.net/docs/ref/#view.NodeView).
|
||
*/
|
||
destroy() {
|
||
if (!this.docView)
|
||
return;
|
||
destroyInput(this);
|
||
this.destroyPluginViews();
|
||
if (this.mounted) {
|
||
this.docView.update(this.state.doc, [], viewDecorations(this), this);
|
||
this.dom.textContent = "";
|
||
} else if (this.dom.parentNode) {
|
||
this.dom.parentNode.removeChild(this.dom);
|
||
}
|
||
this.docView.destroy();
|
||
this.docView = null;
|
||
clearReusedRange();
|
||
}
|
||
/**
|
||
This is true when the view has been
|
||
[destroyed](https://prosemirror.net/docs/ref/#view.EditorView.destroy) (and thus should not be
|
||
used anymore).
|
||
*/
|
||
get isDestroyed() {
|
||
return this.docView == null;
|
||
}
|
||
/**
|
||
Used for testing.
|
||
*/
|
||
dispatchEvent(event) {
|
||
return dispatchEvent(this, event);
|
||
}
|
||
/**
|
||
Dispatch a transaction. Will call
|
||
[`dispatchTransaction`](https://prosemirror.net/docs/ref/#view.DirectEditorProps.dispatchTransaction)
|
||
when given, and otherwise defaults to applying the transaction to
|
||
the current state and calling
|
||
[`updateState`](https://prosemirror.net/docs/ref/#view.EditorView.updateState) with the result.
|
||
This method is bound to the view instance, so that it can be
|
||
easily passed around.
|
||
*/
|
||
dispatch(tr) {
|
||
let dispatchTransaction = this._props.dispatchTransaction;
|
||
if (dispatchTransaction)
|
||
dispatchTransaction.call(this, tr);
|
||
else
|
||
this.updateState(this.state.apply(tr));
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
domSelectionRange() {
|
||
let sel = this.domSelection();
|
||
if (!sel)
|
||
return { focusNode: null, focusOffset: 0, anchorNode: null, anchorOffset: 0 };
|
||
return safari && this.root.nodeType === 11 && deepActiveElement(this.dom.ownerDocument) == this.dom && safariShadowSelectionRange(this, sel) || sel;
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
domSelection() {
|
||
return this.root.getSelection();
|
||
}
|
||
};
|
||
function computeDocDeco(view) {
|
||
let attrs = /* @__PURE__ */ Object.create(null);
|
||
attrs.class = "ProseMirror";
|
||
attrs.contenteditable = String(view.editable);
|
||
view.someProp("attributes", (value) => {
|
||
if (typeof value == "function")
|
||
value = value(view.state);
|
||
if (value)
|
||
for (let attr in value) {
|
||
if (attr == "class")
|
||
attrs.class += " " + value[attr];
|
||
else if (attr == "style")
|
||
attrs.style = (attrs.style ? attrs.style + ";" : "") + value[attr];
|
||
else if (!attrs[attr] && attr != "contenteditable" && attr != "nodeName")
|
||
attrs[attr] = String(value[attr]);
|
||
}
|
||
});
|
||
if (!attrs.translate)
|
||
attrs.translate = "no";
|
||
return [Decoration.node(0, view.state.doc.content.size, attrs)];
|
||
}
|
||
function updateCursorWrapper(view) {
|
||
if (view.markCursor) {
|
||
let dom = document.createElement("img");
|
||
dom.className = "ProseMirror-separator";
|
||
dom.setAttribute("mark-placeholder", "true");
|
||
dom.setAttribute("alt", "");
|
||
view.cursorWrapper = { dom, deco: Decoration.widget(view.state.selection.from, dom, { raw: true, marks: view.markCursor }) };
|
||
} else {
|
||
view.cursorWrapper = null;
|
||
}
|
||
}
|
||
function getEditable(view) {
|
||
return !view.someProp("editable", (value) => value(view.state) === false);
|
||
}
|
||
function selectionContextChanged(sel1, sel2) {
|
||
let depth = Math.min(sel1.$anchor.sharedDepth(sel1.head), sel2.$anchor.sharedDepth(sel2.head));
|
||
return sel1.$anchor.start(depth) != sel2.$anchor.start(depth);
|
||
}
|
||
function buildNodeViews(view) {
|
||
let result = /* @__PURE__ */ Object.create(null);
|
||
function add(obj) {
|
||
for (let prop in obj)
|
||
if (!Object.prototype.hasOwnProperty.call(result, prop))
|
||
result[prop] = obj[prop];
|
||
}
|
||
view.someProp("nodeViews", add);
|
||
view.someProp("markViews", add);
|
||
return result;
|
||
}
|
||
function changedNodeViews(a, b) {
|
||
let nA = 0, nB = 0;
|
||
for (let prop in a) {
|
||
if (a[prop] != b[prop])
|
||
return true;
|
||
nA++;
|
||
}
|
||
for (let _ in b)
|
||
nB++;
|
||
return nA != nB;
|
||
}
|
||
function checkStateComponent(plugin) {
|
||
if (plugin.spec.state || plugin.spec.filterTransaction || plugin.spec.appendTransaction)
|
||
throw new RangeError("Plugins passed directly to the view must not have a state component");
|
||
}
|
||
|
||
// node_modules/w3c-keyname/index.es.js
|
||
var base = {
|
||
8: "Backspace",
|
||
9: "Tab",
|
||
10: "Enter",
|
||
12: "NumLock",
|
||
13: "Enter",
|
||
16: "Shift",
|
||
17: "Control",
|
||
18: "Alt",
|
||
20: "CapsLock",
|
||
27: "Escape",
|
||
32: " ",
|
||
33: "PageUp",
|
||
34: "PageDown",
|
||
35: "End",
|
||
36: "Home",
|
||
37: "ArrowLeft",
|
||
38: "ArrowUp",
|
||
39: "ArrowRight",
|
||
40: "ArrowDown",
|
||
44: "PrintScreen",
|
||
45: "Insert",
|
||
46: "Delete",
|
||
59: ";",
|
||
61: "=",
|
||
91: "Meta",
|
||
92: "Meta",
|
||
106: "*",
|
||
107: "+",
|
||
108: ",",
|
||
109: "-",
|
||
110: ".",
|
||
111: "/",
|
||
144: "NumLock",
|
||
145: "ScrollLock",
|
||
160: "Shift",
|
||
161: "Shift",
|
||
162: "Control",
|
||
163: "Control",
|
||
164: "Alt",
|
||
165: "Alt",
|
||
173: "-",
|
||
186: ";",
|
||
187: "=",
|
||
188: ",",
|
||
189: "-",
|
||
190: ".",
|
||
191: "/",
|
||
192: "`",
|
||
219: "[",
|
||
220: "\\",
|
||
221: "]",
|
||
222: "'",
|
||
229: "q"
|
||
};
|
||
var shift = {
|
||
48: ")",
|
||
49: "!",
|
||
50: "@",
|
||
51: "#",
|
||
52: "$",
|
||
53: "%",
|
||
54: "^",
|
||
55: "&",
|
||
56: "*",
|
||
57: "(",
|
||
59: ":",
|
||
61: "+",
|
||
173: "_",
|
||
186: ":",
|
||
187: "+",
|
||
188: "<",
|
||
189: "_",
|
||
190: ">",
|
||
191: "?",
|
||
192: "~",
|
||
219: "{",
|
||
220: "|",
|
||
221: "}",
|
||
222: '"',
|
||
229: "Q"
|
||
};
|
||
var chrome2 = typeof navigator != "undefined" && /Chrome\/(\d+)/.exec(navigator.userAgent);
|
||
var safari2 = typeof navigator != "undefined" && /Apple Computer/.test(navigator.vendor);
|
||
var gecko2 = typeof navigator != "undefined" && /Gecko\/\d+/.test(navigator.userAgent);
|
||
var mac2 = typeof navigator != "undefined" && /Mac/.test(navigator.platform);
|
||
var ie2 = typeof navigator != "undefined" && /MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);
|
||
var brokenModifierNames = chrome2 && (mac2 || +chrome2[1] < 57) || gecko2 && mac2;
|
||
for (i = 0; i < 10; i++) base[48 + i] = base[96 + i] = String(i);
|
||
var i;
|
||
for (i = 1; i <= 24; i++) base[i + 111] = "F" + i;
|
||
var i;
|
||
for (i = 65; i <= 90; i++) {
|
||
base[i] = String.fromCharCode(i + 32);
|
||
shift[i] = String.fromCharCode(i);
|
||
}
|
||
var i;
|
||
for (code in base) if (!shift.hasOwnProperty(code)) shift[code] = base[code];
|
||
var code;
|
||
function keyName(event) {
|
||
var ignoreKey = brokenModifierNames && (event.ctrlKey || event.altKey || event.metaKey) || (safari2 || ie2) && event.shiftKey && event.key && event.key.length == 1;
|
||
var name = !ignoreKey && event.key || (event.shiftKey ? shift : base)[event.keyCode] || event.key || "Unidentified";
|
||
if (name == "Esc") name = "Escape";
|
||
if (name == "Del") name = "Delete";
|
||
if (name == "Left") name = "ArrowLeft";
|
||
if (name == "Up") name = "ArrowUp";
|
||
if (name == "Right") name = "ArrowRight";
|
||
if (name == "Down") name = "ArrowDown";
|
||
return name;
|
||
}
|
||
|
||
// node_modules/prosemirror-keymap/dist/index.js
|
||
var mac3 = typeof navigator != "undefined" ? /Mac|iP(hone|[oa]d)/.test(navigator.platform) : false;
|
||
function normalizeKeyName(name) {
|
||
let parts = name.split(/-(?!$)/), result = parts[parts.length - 1];
|
||
if (result == "Space")
|
||
result = " ";
|
||
let alt, ctrl, shift2, meta;
|
||
for (let i = 0; i < parts.length - 1; i++) {
|
||
let mod = parts[i];
|
||
if (/^(cmd|meta|m)$/i.test(mod))
|
||
meta = true;
|
||
else if (/^a(lt)?$/i.test(mod))
|
||
alt = true;
|
||
else if (/^(c|ctrl|control)$/i.test(mod))
|
||
ctrl = true;
|
||
else if (/^s(hift)?$/i.test(mod))
|
||
shift2 = true;
|
||
else if (/^mod$/i.test(mod)) {
|
||
if (mac3)
|
||
meta = true;
|
||
else
|
||
ctrl = true;
|
||
} else
|
||
throw new Error("Unrecognized modifier name: " + mod);
|
||
}
|
||
if (alt)
|
||
result = "Alt-" + result;
|
||
if (ctrl)
|
||
result = "Ctrl-" + result;
|
||
if (meta)
|
||
result = "Meta-" + result;
|
||
if (shift2)
|
||
result = "Shift-" + result;
|
||
return result;
|
||
}
|
||
function normalize(map) {
|
||
let copy = /* @__PURE__ */ Object.create(null);
|
||
for (let prop in map)
|
||
copy[normalizeKeyName(prop)] = map[prop];
|
||
return copy;
|
||
}
|
||
function modifiers(name, event, shift2 = true) {
|
||
if (event.altKey)
|
||
name = "Alt-" + name;
|
||
if (event.ctrlKey)
|
||
name = "Ctrl-" + name;
|
||
if (event.metaKey)
|
||
name = "Meta-" + name;
|
||
if (shift2 && event.shiftKey)
|
||
name = "Shift-" + name;
|
||
return name;
|
||
}
|
||
function keymap(bindings) {
|
||
return new Plugin({ props: { handleKeyDown: keydownHandler(bindings) } });
|
||
}
|
||
function keydownHandler(bindings) {
|
||
let map = normalize(bindings);
|
||
return function(view, event) {
|
||
let name = keyName(event), baseName, direct = map[modifiers(name, event)];
|
||
if (direct && direct(view.state, view.dispatch, view))
|
||
return true;
|
||
if (name.length == 1 && name != " ") {
|
||
if (event.shiftKey) {
|
||
let noShift = map[modifiers(name, event, false)];
|
||
if (noShift && noShift(view.state, view.dispatch, view))
|
||
return true;
|
||
}
|
||
if ((event.shiftKey || event.altKey || event.metaKey || name.charCodeAt(0) > 127) && (baseName = base[event.keyCode]) && baseName != name) {
|
||
let fromCode = map[modifiers(baseName, event)];
|
||
if (fromCode && fromCode(view.state, view.dispatch, view))
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
};
|
||
}
|
||
|
||
// node_modules/prosemirror-commands/dist/index.js
|
||
var deleteSelection = (state, dispatch) => {
|
||
if (state.selection.empty)
|
||
return false;
|
||
if (dispatch)
|
||
dispatch(state.tr.deleteSelection().scrollIntoView());
|
||
return true;
|
||
};
|
||
function atBlockStart(state, view) {
|
||
let { $cursor } = state.selection;
|
||
if (!$cursor || (view ? !view.endOfTextblock("backward", state) : $cursor.parentOffset > 0))
|
||
return null;
|
||
return $cursor;
|
||
}
|
||
var joinBackward = (state, dispatch, view) => {
|
||
let $cursor = atBlockStart(state, view);
|
||
if (!$cursor)
|
||
return false;
|
||
let $cut = findCutBefore($cursor);
|
||
if (!$cut) {
|
||
let range = $cursor.blockRange(), target = range && liftTarget(range);
|
||
if (target == null)
|
||
return false;
|
||
if (dispatch)
|
||
dispatch(state.tr.lift(range, target).scrollIntoView());
|
||
return true;
|
||
}
|
||
let before = $cut.nodeBefore;
|
||
if (deleteBarrier(state, $cut, dispatch, -1))
|
||
return true;
|
||
if ($cursor.parent.content.size == 0 && (textblockAt(before, "end") || NodeSelection.isSelectable(before))) {
|
||
for (let depth = $cursor.depth; ; depth--) {
|
||
let delStep = replaceStep(state.doc, $cursor.before(depth), $cursor.after(depth), Slice.empty);
|
||
if (delStep && delStep.slice.size < delStep.to - delStep.from) {
|
||
if (dispatch) {
|
||
let tr = state.tr.step(delStep);
|
||
tr.setSelection(textblockAt(before, "end") ? Selection.findFrom(tr.doc.resolve(tr.mapping.map($cut.pos, -1)), -1) : NodeSelection.create(tr.doc, $cut.pos - before.nodeSize));
|
||
dispatch(tr.scrollIntoView());
|
||
}
|
||
return true;
|
||
}
|
||
if (depth == 1 || $cursor.node(depth - 1).childCount > 1)
|
||
break;
|
||
}
|
||
}
|
||
if (before.isAtom && $cut.depth == $cursor.depth - 1) {
|
||
if (dispatch)
|
||
dispatch(state.tr.delete($cut.pos - before.nodeSize, $cut.pos).scrollIntoView());
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
var joinTextblockBackward = (state, dispatch, view) => {
|
||
let $cursor = atBlockStart(state, view);
|
||
if (!$cursor)
|
||
return false;
|
||
let $cut = findCutBefore($cursor);
|
||
return $cut ? joinTextblocksAround(state, $cut, dispatch) : false;
|
||
};
|
||
var joinTextblockForward = (state, dispatch, view) => {
|
||
let $cursor = atBlockEnd(state, view);
|
||
if (!$cursor)
|
||
return false;
|
||
let $cut = findCutAfter($cursor);
|
||
return $cut ? joinTextblocksAround(state, $cut, dispatch) : false;
|
||
};
|
||
function joinTextblocksAround(state, $cut, dispatch) {
|
||
let before = $cut.nodeBefore, beforeText = before, beforePos = $cut.pos - 1;
|
||
for (; !beforeText.isTextblock; beforePos--) {
|
||
if (beforeText.type.spec.isolating)
|
||
return false;
|
||
let child = beforeText.lastChild;
|
||
if (!child)
|
||
return false;
|
||
beforeText = child;
|
||
}
|
||
let after = $cut.nodeAfter, afterText = after, afterPos = $cut.pos + 1;
|
||
for (; !afterText.isTextblock; afterPos++) {
|
||
if (afterText.type.spec.isolating)
|
||
return false;
|
||
let child = afterText.firstChild;
|
||
if (!child)
|
||
return false;
|
||
afterText = child;
|
||
}
|
||
let step = replaceStep(state.doc, beforePos, afterPos, Slice.empty);
|
||
if (!step || step.from != beforePos || step instanceof ReplaceStep && step.slice.size >= afterPos - beforePos)
|
||
return false;
|
||
if (dispatch) {
|
||
let tr = state.tr.step(step);
|
||
tr.setSelection(TextSelection.create(tr.doc, beforePos));
|
||
dispatch(tr.scrollIntoView());
|
||
}
|
||
return true;
|
||
}
|
||
function textblockAt(node, side, only = false) {
|
||
for (let scan = node; scan; scan = side == "start" ? scan.firstChild : scan.lastChild) {
|
||
if (scan.isTextblock)
|
||
return true;
|
||
if (only && scan.childCount != 1)
|
||
return false;
|
||
}
|
||
return false;
|
||
}
|
||
var selectNodeBackward = (state, dispatch, view) => {
|
||
let { $head, empty: empty2 } = state.selection, $cut = $head;
|
||
if (!empty2)
|
||
return false;
|
||
if ($head.parent.isTextblock) {
|
||
if (view ? !view.endOfTextblock("backward", state) : $head.parentOffset > 0)
|
||
return false;
|
||
$cut = findCutBefore($head);
|
||
}
|
||
let node = $cut && $cut.nodeBefore;
|
||
if (!node || !NodeSelection.isSelectable(node))
|
||
return false;
|
||
if (dispatch)
|
||
dispatch(state.tr.setSelection(NodeSelection.create(state.doc, $cut.pos - node.nodeSize)).scrollIntoView());
|
||
return true;
|
||
};
|
||
function findCutBefore($pos) {
|
||
if (!$pos.parent.type.spec.isolating)
|
||
for (let i = $pos.depth - 1; i >= 0; i--) {
|
||
if ($pos.index(i) > 0)
|
||
return $pos.doc.resolve($pos.before(i + 1));
|
||
if ($pos.node(i).type.spec.isolating)
|
||
break;
|
||
}
|
||
return null;
|
||
}
|
||
function atBlockEnd(state, view) {
|
||
let { $cursor } = state.selection;
|
||
if (!$cursor || (view ? !view.endOfTextblock("forward", state) : $cursor.parentOffset < $cursor.parent.content.size))
|
||
return null;
|
||
return $cursor;
|
||
}
|
||
var joinForward = (state, dispatch, view) => {
|
||
let $cursor = atBlockEnd(state, view);
|
||
if (!$cursor)
|
||
return false;
|
||
let $cut = findCutAfter($cursor);
|
||
if (!$cut)
|
||
return false;
|
||
let after = $cut.nodeAfter;
|
||
if (deleteBarrier(state, $cut, dispatch, 1))
|
||
return true;
|
||
if ($cursor.parent.content.size == 0 && (textblockAt(after, "start") || NodeSelection.isSelectable(after))) {
|
||
let delStep = replaceStep(state.doc, $cursor.before(), $cursor.after(), Slice.empty);
|
||
if (delStep && delStep.slice.size < delStep.to - delStep.from) {
|
||
if (dispatch) {
|
||
let tr = state.tr.step(delStep);
|
||
tr.setSelection(textblockAt(after, "start") ? Selection.findFrom(tr.doc.resolve(tr.mapping.map($cut.pos)), 1) : NodeSelection.create(tr.doc, tr.mapping.map($cut.pos)));
|
||
dispatch(tr.scrollIntoView());
|
||
}
|
||
return true;
|
||
}
|
||
}
|
||
if (after.isAtom && $cut.depth == $cursor.depth - 1) {
|
||
if (dispatch)
|
||
dispatch(state.tr.delete($cut.pos, $cut.pos + after.nodeSize).scrollIntoView());
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
var selectNodeForward = (state, dispatch, view) => {
|
||
let { $head, empty: empty2 } = state.selection, $cut = $head;
|
||
if (!empty2)
|
||
return false;
|
||
if ($head.parent.isTextblock) {
|
||
if (view ? !view.endOfTextblock("forward", state) : $head.parentOffset < $head.parent.content.size)
|
||
return false;
|
||
$cut = findCutAfter($head);
|
||
}
|
||
let node = $cut && $cut.nodeAfter;
|
||
if (!node || !NodeSelection.isSelectable(node))
|
||
return false;
|
||
if (dispatch)
|
||
dispatch(state.tr.setSelection(NodeSelection.create(state.doc, $cut.pos)).scrollIntoView());
|
||
return true;
|
||
};
|
||
function findCutAfter($pos) {
|
||
if (!$pos.parent.type.spec.isolating)
|
||
for (let i = $pos.depth - 1; i >= 0; i--) {
|
||
let parent = $pos.node(i);
|
||
if ($pos.index(i) + 1 < parent.childCount)
|
||
return $pos.doc.resolve($pos.after(i + 1));
|
||
if (parent.type.spec.isolating)
|
||
break;
|
||
}
|
||
return null;
|
||
}
|
||
var joinUp = (state, dispatch) => {
|
||
let sel = state.selection, nodeSel = sel instanceof NodeSelection, point;
|
||
if (nodeSel) {
|
||
if (sel.node.isTextblock || !canJoin(state.doc, sel.from))
|
||
return false;
|
||
point = sel.from;
|
||
} else {
|
||
point = joinPoint(state.doc, sel.from, -1);
|
||
if (point == null)
|
||
return false;
|
||
}
|
||
if (dispatch) {
|
||
let tr = state.tr.join(point);
|
||
if (nodeSel)
|
||
tr.setSelection(NodeSelection.create(tr.doc, point - state.doc.resolve(point).nodeBefore.nodeSize));
|
||
dispatch(tr.scrollIntoView());
|
||
}
|
||
return true;
|
||
};
|
||
var joinDown = (state, dispatch) => {
|
||
let sel = state.selection, point;
|
||
if (sel instanceof NodeSelection) {
|
||
if (sel.node.isTextblock || !canJoin(state.doc, sel.to))
|
||
return false;
|
||
point = sel.to;
|
||
} else {
|
||
point = joinPoint(state.doc, sel.to, 1);
|
||
if (point == null)
|
||
return false;
|
||
}
|
||
if (dispatch)
|
||
dispatch(state.tr.join(point).scrollIntoView());
|
||
return true;
|
||
};
|
||
var lift = (state, dispatch) => {
|
||
let { $from, $to } = state.selection;
|
||
let range = $from.blockRange($to), target = range && liftTarget(range);
|
||
if (target == null)
|
||
return false;
|
||
if (dispatch)
|
||
dispatch(state.tr.lift(range, target).scrollIntoView());
|
||
return true;
|
||
};
|
||
var newlineInCode = (state, dispatch) => {
|
||
let { $head, $anchor } = state.selection;
|
||
if (!$head.parent.type.spec.code || !$head.sameParent($anchor))
|
||
return false;
|
||
if (dispatch)
|
||
dispatch(state.tr.insertText("\n").scrollIntoView());
|
||
return true;
|
||
};
|
||
function defaultBlockAt(match) {
|
||
for (let i = 0; i < match.edgeCount; i++) {
|
||
let { type } = match.edge(i);
|
||
if (type.isTextblock && !type.hasRequiredAttrs())
|
||
return type;
|
||
}
|
||
return null;
|
||
}
|
||
var exitCode = (state, dispatch) => {
|
||
let { $head, $anchor } = state.selection;
|
||
if (!$head.parent.type.spec.code || !$head.sameParent($anchor))
|
||
return false;
|
||
let above = $head.node(-1), after = $head.indexAfter(-1), type = defaultBlockAt(above.contentMatchAt(after));
|
||
if (!type || !above.canReplaceWith(after, after, type))
|
||
return false;
|
||
if (dispatch) {
|
||
let pos = $head.after(), tr = state.tr.replaceWith(pos, pos, type.createAndFill());
|
||
tr.setSelection(Selection.near(tr.doc.resolve(pos), 1));
|
||
dispatch(tr.scrollIntoView());
|
||
}
|
||
return true;
|
||
};
|
||
var createParagraphNear = (state, dispatch) => {
|
||
let sel = state.selection, { $from, $to } = sel;
|
||
if (sel instanceof AllSelection || $from.parent.inlineContent || $to.parent.inlineContent)
|
||
return false;
|
||
let type = defaultBlockAt($to.parent.contentMatchAt($to.indexAfter()));
|
||
if (!type || !type.isTextblock)
|
||
return false;
|
||
if (dispatch) {
|
||
let side = (!$from.parentOffset && $to.index() < $to.parent.childCount ? $from : $to).pos;
|
||
let tr = state.tr.insert(side, type.createAndFill());
|
||
tr.setSelection(TextSelection.create(tr.doc, side + 1));
|
||
dispatch(tr.scrollIntoView());
|
||
}
|
||
return true;
|
||
};
|
||
var liftEmptyBlock = (state, dispatch) => {
|
||
let { $cursor } = state.selection;
|
||
if (!$cursor || $cursor.parent.content.size)
|
||
return false;
|
||
if ($cursor.depth > 1 && $cursor.after() != $cursor.end(-1)) {
|
||
let before = $cursor.before();
|
||
if (canSplit(state.doc, before)) {
|
||
if (dispatch)
|
||
dispatch(state.tr.split(before).scrollIntoView());
|
||
return true;
|
||
}
|
||
}
|
||
let range = $cursor.blockRange(), target = range && liftTarget(range);
|
||
if (target == null)
|
||
return false;
|
||
if (dispatch)
|
||
dispatch(state.tr.lift(range, target).scrollIntoView());
|
||
return true;
|
||
};
|
||
function splitBlockAs(splitNode) {
|
||
return (state, dispatch) => {
|
||
let { $from, $to } = state.selection;
|
||
if (state.selection instanceof NodeSelection && state.selection.node.isBlock) {
|
||
if (!$from.parentOffset || !canSplit(state.doc, $from.pos))
|
||
return false;
|
||
if (dispatch)
|
||
dispatch(state.tr.split($from.pos).scrollIntoView());
|
||
return true;
|
||
}
|
||
if (!$from.depth)
|
||
return false;
|
||
let types = [];
|
||
let splitDepth, deflt, atEnd = false, atStart = false;
|
||
for (let d = $from.depth; ; d--) {
|
||
let node = $from.node(d);
|
||
if (node.isBlock) {
|
||
atEnd = $from.end(d) == $from.pos + ($from.depth - d);
|
||
atStart = $from.start(d) == $from.pos - ($from.depth - d);
|
||
deflt = defaultBlockAt($from.node(d - 1).contentMatchAt($from.indexAfter(d - 1)));
|
||
let splitType = splitNode && splitNode($to.parent, atEnd, $from);
|
||
types.unshift(splitType || (atEnd && deflt ? { type: deflt } : null));
|
||
splitDepth = d;
|
||
break;
|
||
} else {
|
||
if (d == 1)
|
||
return false;
|
||
types.unshift(null);
|
||
}
|
||
}
|
||
let tr = state.tr;
|
||
if (state.selection instanceof TextSelection || state.selection instanceof AllSelection)
|
||
tr.deleteSelection();
|
||
let splitPos = tr.mapping.map($from.pos);
|
||
let can = canSplit(tr.doc, splitPos, types.length, types);
|
||
if (!can) {
|
||
types[0] = deflt ? { type: deflt } : null;
|
||
can = canSplit(tr.doc, splitPos, types.length, types);
|
||
}
|
||
tr.split(splitPos, types.length, types);
|
||
if (!atEnd && atStart && $from.node(splitDepth).type != deflt) {
|
||
let first2 = tr.mapping.map($from.before(splitDepth)), $first = tr.doc.resolve(first2);
|
||
if (deflt && $from.node(splitDepth - 1).canReplaceWith($first.index(), $first.index() + 1, deflt))
|
||
tr.setNodeMarkup(tr.mapping.map($from.before(splitDepth)), deflt);
|
||
}
|
||
if (dispatch)
|
||
dispatch(tr.scrollIntoView());
|
||
return true;
|
||
};
|
||
}
|
||
var splitBlock = splitBlockAs();
|
||
var selectParentNode = (state, dispatch) => {
|
||
let { $from, to } = state.selection, pos;
|
||
let same = $from.sharedDepth(to);
|
||
if (same == 0)
|
||
return false;
|
||
pos = $from.before(same);
|
||
if (dispatch)
|
||
dispatch(state.tr.setSelection(NodeSelection.create(state.doc, pos)));
|
||
return true;
|
||
};
|
||
var selectAll = (state, dispatch) => {
|
||
if (dispatch)
|
||
dispatch(state.tr.setSelection(new AllSelection(state.doc)));
|
||
return true;
|
||
};
|
||
function joinMaybeClear(state, $pos, dispatch) {
|
||
let before = $pos.nodeBefore, after = $pos.nodeAfter, index2 = $pos.index();
|
||
if (!before || !after || !before.type.compatibleContent(after.type))
|
||
return false;
|
||
if (!before.content.size && $pos.parent.canReplace(index2 - 1, index2)) {
|
||
if (dispatch)
|
||
dispatch(state.tr.delete($pos.pos - before.nodeSize, $pos.pos).scrollIntoView());
|
||
return true;
|
||
}
|
||
if (!$pos.parent.canReplace(index2, index2 + 1) || !(after.isTextblock || canJoin(state.doc, $pos.pos)))
|
||
return false;
|
||
if (dispatch)
|
||
dispatch(state.tr.join($pos.pos).scrollIntoView());
|
||
return true;
|
||
}
|
||
function deleteBarrier(state, $cut, dispatch, dir) {
|
||
let before = $cut.nodeBefore, after = $cut.nodeAfter, conn, match;
|
||
let isolated = before.type.spec.isolating || after.type.spec.isolating;
|
||
if (!isolated && joinMaybeClear(state, $cut, dispatch))
|
||
return true;
|
||
let canDelAfter = !isolated && $cut.parent.canReplace($cut.index(), $cut.index() + 1);
|
||
if (canDelAfter && (conn = (match = before.contentMatchAt(before.childCount)).findWrapping(after.type)) && match.matchType(conn[0] || after.type).validEnd) {
|
||
if (dispatch) {
|
||
let end = $cut.pos + after.nodeSize, wrap = Fragment.empty;
|
||
for (let i = conn.length - 1; i >= 0; i--)
|
||
wrap = Fragment.from(conn[i].create(null, wrap));
|
||
wrap = Fragment.from(before.copy(wrap));
|
||
let tr = state.tr.step(new ReplaceAroundStep($cut.pos - 1, end, $cut.pos, end, new Slice(wrap, 1, 0), conn.length, true));
|
||
let $joinAt = tr.doc.resolve(end + 2 * conn.length);
|
||
if ($joinAt.nodeAfter && $joinAt.nodeAfter.type == before.type && canJoin(tr.doc, $joinAt.pos))
|
||
tr.join($joinAt.pos);
|
||
dispatch(tr.scrollIntoView());
|
||
}
|
||
return true;
|
||
}
|
||
let selAfter = after.type.spec.isolating || dir > 0 && isolated ? null : Selection.findFrom($cut, 1);
|
||
let range = selAfter && selAfter.$from.blockRange(selAfter.$to), target = range && liftTarget(range);
|
||
if (target != null && target >= $cut.depth) {
|
||
if (dispatch)
|
||
dispatch(state.tr.lift(range, target).scrollIntoView());
|
||
return true;
|
||
}
|
||
if (canDelAfter && textblockAt(after, "start", true) && textblockAt(before, "end")) {
|
||
let at = before, wrap = [];
|
||
for (; ; ) {
|
||
wrap.push(at);
|
||
if (at.isTextblock)
|
||
break;
|
||
at = at.lastChild;
|
||
}
|
||
let afterText = after, afterDepth = 1;
|
||
for (; !afterText.isTextblock; afterText = afterText.firstChild)
|
||
afterDepth++;
|
||
if (at.canReplace(at.childCount, at.childCount, afterText.content)) {
|
||
if (dispatch) {
|
||
let end = Fragment.empty;
|
||
for (let i = wrap.length - 1; i >= 0; i--)
|
||
end = Fragment.from(wrap[i].copy(end));
|
||
let tr = state.tr.step(new ReplaceAroundStep($cut.pos - wrap.length, $cut.pos + after.nodeSize, $cut.pos + afterDepth, $cut.pos + after.nodeSize - afterDepth, new Slice(end, wrap.length, 0), 0, true));
|
||
dispatch(tr.scrollIntoView());
|
||
}
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
function selectTextblockSide(side) {
|
||
return function(state, dispatch) {
|
||
let sel = state.selection, $pos = side < 0 ? sel.$from : sel.$to;
|
||
let depth = $pos.depth;
|
||
while ($pos.node(depth).isInline) {
|
||
if (!depth)
|
||
return false;
|
||
depth--;
|
||
}
|
||
if (!$pos.node(depth).isTextblock)
|
||
return false;
|
||
if (dispatch)
|
||
dispatch(state.tr.setSelection(TextSelection.create(state.doc, side < 0 ? $pos.start(depth) : $pos.end(depth))));
|
||
return true;
|
||
};
|
||
}
|
||
var selectTextblockStart = selectTextblockSide(-1);
|
||
var selectTextblockEnd = selectTextblockSide(1);
|
||
function wrapIn(nodeType, attrs = null) {
|
||
return function(state, dispatch) {
|
||
let { $from, $to } = state.selection;
|
||
let range = $from.blockRange($to), wrapping = range && findWrapping(range, nodeType, attrs);
|
||
if (!wrapping)
|
||
return false;
|
||
if (dispatch)
|
||
dispatch(state.tr.wrap(range, wrapping).scrollIntoView());
|
||
return true;
|
||
};
|
||
}
|
||
function setBlockType(nodeType, attrs = null) {
|
||
return function(state, dispatch) {
|
||
let applicable = false;
|
||
for (let i = 0; i < state.selection.ranges.length && !applicable; i++) {
|
||
let { $from: { pos: from }, $to: { pos: to } } = state.selection.ranges[i];
|
||
state.doc.nodesBetween(from, to, (node, pos) => {
|
||
if (applicable)
|
||
return false;
|
||
if (!node.isTextblock || node.hasMarkup(nodeType, attrs))
|
||
return;
|
||
if (node.type == nodeType) {
|
||
applicable = true;
|
||
} else {
|
||
let $pos = state.doc.resolve(pos), index2 = $pos.index();
|
||
applicable = $pos.parent.canReplaceWith(index2, index2 + 1, nodeType);
|
||
}
|
||
});
|
||
}
|
||
if (!applicable)
|
||
return false;
|
||
if (dispatch) {
|
||
let tr = state.tr;
|
||
for (let i = 0; i < state.selection.ranges.length; i++) {
|
||
let { $from: { pos: from }, $to: { pos: to } } = state.selection.ranges[i];
|
||
tr.setBlockType(from, to, nodeType, attrs);
|
||
}
|
||
dispatch(tr.scrollIntoView());
|
||
}
|
||
return true;
|
||
};
|
||
}
|
||
function chainCommands(...commands2) {
|
||
return function(state, dispatch, view) {
|
||
for (let i = 0; i < commands2.length; i++)
|
||
if (commands2[i](state, dispatch, view))
|
||
return true;
|
||
return false;
|
||
};
|
||
}
|
||
var backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward);
|
||
var del = chainCommands(deleteSelection, joinForward, selectNodeForward);
|
||
var pcBaseKeymap = {
|
||
"Enter": chainCommands(newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock),
|
||
"Mod-Enter": exitCode,
|
||
"Backspace": backspace,
|
||
"Mod-Backspace": backspace,
|
||
"Shift-Backspace": backspace,
|
||
"Delete": del,
|
||
"Mod-Delete": del,
|
||
"Mod-a": selectAll
|
||
};
|
||
var macBaseKeymap = {
|
||
"Ctrl-h": pcBaseKeymap["Backspace"],
|
||
"Alt-Backspace": pcBaseKeymap["Mod-Backspace"],
|
||
"Ctrl-d": pcBaseKeymap["Delete"],
|
||
"Ctrl-Alt-Backspace": pcBaseKeymap["Mod-Delete"],
|
||
"Alt-Delete": pcBaseKeymap["Mod-Delete"],
|
||
"Alt-d": pcBaseKeymap["Mod-Delete"],
|
||
"Ctrl-a": selectTextblockStart,
|
||
"Ctrl-e": selectTextblockEnd
|
||
};
|
||
for (let key in pcBaseKeymap)
|
||
macBaseKeymap[key] = pcBaseKeymap[key];
|
||
var mac4 = typeof navigator != "undefined" ? /Mac|iP(hone|[oa]d)/.test(navigator.platform) : typeof os != "undefined" && os.platform ? os.platform() == "darwin" : false;
|
||
|
||
// node_modules/prosemirror-schema-list/dist/index.js
|
||
function wrapInList(listType, attrs = null) {
|
||
return function(state, dispatch) {
|
||
let { $from, $to } = state.selection;
|
||
let range = $from.blockRange($to);
|
||
if (!range)
|
||
return false;
|
||
let tr = dispatch ? state.tr : null;
|
||
if (!wrapRangeInList(tr, range, listType, attrs))
|
||
return false;
|
||
if (dispatch)
|
||
dispatch(tr.scrollIntoView());
|
||
return true;
|
||
};
|
||
}
|
||
function wrapRangeInList(tr, range, listType, attrs = null) {
|
||
let doJoin = false, outerRange = range, doc2 = range.$from.doc;
|
||
if (range.depth >= 2 && range.$from.node(range.depth - 1).type.compatibleContent(listType) && range.startIndex == 0) {
|
||
if (range.$from.index(range.depth - 1) == 0)
|
||
return false;
|
||
let $insert = doc2.resolve(range.start - 2);
|
||
outerRange = new NodeRange($insert, $insert, range.depth);
|
||
if (range.endIndex < range.parent.childCount)
|
||
range = new NodeRange(range.$from, doc2.resolve(range.$to.end(range.depth)), range.depth);
|
||
doJoin = true;
|
||
}
|
||
let wrap = findWrapping(outerRange, listType, attrs, range);
|
||
if (!wrap)
|
||
return false;
|
||
if (tr)
|
||
doWrapInList(tr, range, wrap, doJoin, listType);
|
||
return true;
|
||
}
|
||
function doWrapInList(tr, range, wrappers, joinBefore, listType) {
|
||
let content = Fragment.empty;
|
||
for (let i = wrappers.length - 1; i >= 0; i--)
|
||
content = Fragment.from(wrappers[i].type.create(wrappers[i].attrs, content));
|
||
tr.step(new ReplaceAroundStep(range.start - (joinBefore ? 2 : 0), range.end, range.start, range.end, new Slice(content, 0, 0), wrappers.length, true));
|
||
let found = 0;
|
||
for (let i = 0; i < wrappers.length; i++)
|
||
if (wrappers[i].type == listType)
|
||
found = i + 1;
|
||
let splitDepth = wrappers.length - found;
|
||
let splitPos = range.start + wrappers.length - (joinBefore ? 2 : 0), parent = range.parent;
|
||
for (let i = range.startIndex, e = range.endIndex, first2 = true; i < e; i++, first2 = false) {
|
||
if (!first2 && canSplit(tr.doc, splitPos, splitDepth)) {
|
||
tr.split(splitPos, splitDepth);
|
||
splitPos += 2 * splitDepth;
|
||
}
|
||
splitPos += parent.child(i).nodeSize;
|
||
}
|
||
return tr;
|
||
}
|
||
function liftListItem(itemType) {
|
||
return function(state, dispatch) {
|
||
let { $from, $to } = state.selection;
|
||
let range = $from.blockRange($to, (node) => node.childCount > 0 && node.firstChild.type == itemType);
|
||
if (!range)
|
||
return false;
|
||
if (!dispatch)
|
||
return true;
|
||
if ($from.node(range.depth - 1).type == itemType)
|
||
return liftToOuterList(state, dispatch, itemType, range);
|
||
else
|
||
return liftOutOfList(state, dispatch, range);
|
||
};
|
||
}
|
||
function liftToOuterList(state, dispatch, itemType, range) {
|
||
let tr = state.tr, end = range.end, endOfList = range.$to.end(range.depth);
|
||
if (end < endOfList) {
|
||
tr.step(new ReplaceAroundStep(end - 1, endOfList, end, endOfList, new Slice(Fragment.from(itemType.create(null, range.parent.copy())), 1, 0), 1, true));
|
||
range = new NodeRange(tr.doc.resolve(range.$from.pos), tr.doc.resolve(endOfList), range.depth);
|
||
}
|
||
const target = liftTarget(range);
|
||
if (target == null)
|
||
return false;
|
||
tr.lift(range, target);
|
||
let after = tr.mapping.map(end, -1) - 1;
|
||
if (canJoin(tr.doc, after))
|
||
tr.join(after);
|
||
dispatch(tr.scrollIntoView());
|
||
return true;
|
||
}
|
||
function liftOutOfList(state, dispatch, range) {
|
||
let tr = state.tr, list = range.parent;
|
||
for (let pos = range.end, i = range.endIndex - 1, e = range.startIndex; i > e; i--) {
|
||
pos -= list.child(i).nodeSize;
|
||
tr.delete(pos - 1, pos + 1);
|
||
}
|
||
let $start = tr.doc.resolve(range.start), item = $start.nodeAfter;
|
||
if (tr.mapping.map(range.end) != range.start + $start.nodeAfter.nodeSize)
|
||
return false;
|
||
let atStart = range.startIndex == 0, atEnd = range.endIndex == list.childCount;
|
||
let parent = $start.node(-1), indexBefore = $start.index(-1);
|
||
if (!parent.canReplace(indexBefore + (atStart ? 0 : 1), indexBefore + 1, item.content.append(atEnd ? Fragment.empty : Fragment.from(list))))
|
||
return false;
|
||
let start = $start.pos, end = start + item.nodeSize;
|
||
tr.step(new ReplaceAroundStep(start - (atStart ? 1 : 0), end + (atEnd ? 1 : 0), start + 1, end - 1, new Slice((atStart ? Fragment.empty : Fragment.from(list.copy(Fragment.empty))).append(atEnd ? Fragment.empty : Fragment.from(list.copy(Fragment.empty))), atStart ? 0 : 1, atEnd ? 0 : 1), atStart ? 0 : 1));
|
||
dispatch(tr.scrollIntoView());
|
||
return true;
|
||
}
|
||
function sinkListItem(itemType) {
|
||
return function(state, dispatch) {
|
||
let { $from, $to } = state.selection;
|
||
let range = $from.blockRange($to, (node) => node.childCount > 0 && node.firstChild.type == itemType);
|
||
if (!range)
|
||
return false;
|
||
let startIndex = range.startIndex;
|
||
if (startIndex == 0)
|
||
return false;
|
||
let parent = range.parent, nodeBefore = parent.child(startIndex - 1);
|
||
if (nodeBefore.type != itemType)
|
||
return false;
|
||
if (dispatch) {
|
||
let nestedBefore = nodeBefore.lastChild && nodeBefore.lastChild.type == parent.type;
|
||
let inner = Fragment.from(nestedBefore ? itemType.create() : null);
|
||
let slice = new Slice(Fragment.from(itemType.create(null, Fragment.from(parent.type.create(null, inner)))), nestedBefore ? 3 : 1, 0);
|
||
let before = range.start, after = range.end;
|
||
dispatch(state.tr.step(new ReplaceAroundStep(before - (nestedBefore ? 3 : 1), after, before, after, slice, 1, true)).scrollIntoView());
|
||
}
|
||
return true;
|
||
};
|
||
}
|
||
|
||
// node_modules/@tiptap/core/dist/index.js
|
||
function createChainableState(config) {
|
||
const { state, transaction } = config;
|
||
let { selection } = transaction;
|
||
let { doc: doc2 } = transaction;
|
||
let { storedMarks } = transaction;
|
||
return {
|
||
...state,
|
||
apply: state.apply.bind(state),
|
||
applyTransaction: state.applyTransaction.bind(state),
|
||
plugins: state.plugins,
|
||
schema: state.schema,
|
||
reconfigure: state.reconfigure.bind(state),
|
||
toJSON: state.toJSON.bind(state),
|
||
get storedMarks() {
|
||
return storedMarks;
|
||
},
|
||
get selection() {
|
||
return selection;
|
||
},
|
||
get doc() {
|
||
return doc2;
|
||
},
|
||
get tr() {
|
||
selection = transaction.selection;
|
||
doc2 = transaction.doc;
|
||
storedMarks = transaction.storedMarks;
|
||
return transaction;
|
||
}
|
||
};
|
||
}
|
||
var CommandManager = class {
|
||
constructor(props) {
|
||
this.editor = props.editor;
|
||
this.rawCommands = this.editor.extensionManager.commands;
|
||
this.customState = props.state;
|
||
}
|
||
get hasCustomState() {
|
||
return !!this.customState;
|
||
}
|
||
get state() {
|
||
return this.customState || this.editor.state;
|
||
}
|
||
get commands() {
|
||
const { rawCommands, editor, state } = this;
|
||
const { view } = editor;
|
||
const { tr } = state;
|
||
const props = this.buildProps(tr);
|
||
return Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
|
||
const method = (...args) => {
|
||
const callback = command2(...args)(props);
|
||
if (!tr.getMeta("preventDispatch") && !this.hasCustomState) {
|
||
view.dispatch(tr);
|
||
}
|
||
return callback;
|
||
};
|
||
return [name, method];
|
||
}));
|
||
}
|
||
get chain() {
|
||
return () => this.createChain();
|
||
}
|
||
get can() {
|
||
return () => this.createCan();
|
||
}
|
||
createChain(startTr, shouldDispatch = true) {
|
||
const { rawCommands, editor, state } = this;
|
||
const { view } = editor;
|
||
const callbacks = [];
|
||
const hasStartTransaction = !!startTr;
|
||
const tr = startTr || state.tr;
|
||
const run2 = () => {
|
||
if (!hasStartTransaction && shouldDispatch && !tr.getMeta("preventDispatch") && !this.hasCustomState) {
|
||
view.dispatch(tr);
|
||
}
|
||
return callbacks.every((callback) => callback === true);
|
||
};
|
||
const chain = {
|
||
...Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
|
||
const chainedCommand = (...args) => {
|
||
const props = this.buildProps(tr, shouldDispatch);
|
||
const callback = command2(...args)(props);
|
||
callbacks.push(callback);
|
||
return chain;
|
||
};
|
||
return [name, chainedCommand];
|
||
})),
|
||
run: run2
|
||
};
|
||
return chain;
|
||
}
|
||
createCan(startTr) {
|
||
const { rawCommands, state } = this;
|
||
const dispatch = false;
|
||
const tr = startTr || state.tr;
|
||
const props = this.buildProps(tr, dispatch);
|
||
const formattedCommands = Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
|
||
return [name, (...args) => command2(...args)({ ...props, dispatch: void 0 })];
|
||
}));
|
||
return {
|
||
...formattedCommands,
|
||
chain: () => this.createChain(tr, dispatch)
|
||
};
|
||
}
|
||
buildProps(tr, shouldDispatch = true) {
|
||
const { rawCommands, editor, state } = this;
|
||
const { view } = editor;
|
||
const props = {
|
||
tr,
|
||
editor,
|
||
view,
|
||
state: createChainableState({
|
||
state,
|
||
transaction: tr
|
||
}),
|
||
dispatch: shouldDispatch ? () => void 0 : void 0,
|
||
chain: () => this.createChain(tr, shouldDispatch),
|
||
can: () => this.createCan(tr),
|
||
get commands() {
|
||
return Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
|
||
return [name, (...args) => command2(...args)(props)];
|
||
}));
|
||
}
|
||
};
|
||
return props;
|
||
}
|
||
};
|
||
var EventEmitter = class {
|
||
constructor() {
|
||
this.callbacks = {};
|
||
}
|
||
on(event, fn) {
|
||
if (!this.callbacks[event]) {
|
||
this.callbacks[event] = [];
|
||
}
|
||
this.callbacks[event].push(fn);
|
||
return this;
|
||
}
|
||
emit(event, ...args) {
|
||
const callbacks = this.callbacks[event];
|
||
if (callbacks) {
|
||
callbacks.forEach((callback) => callback.apply(this, args));
|
||
}
|
||
return this;
|
||
}
|
||
off(event, fn) {
|
||
const callbacks = this.callbacks[event];
|
||
if (callbacks) {
|
||
if (fn) {
|
||
this.callbacks[event] = callbacks.filter((callback) => callback !== fn);
|
||
} else {
|
||
delete this.callbacks[event];
|
||
}
|
||
}
|
||
return this;
|
||
}
|
||
once(event, fn) {
|
||
const onceFn = (...args) => {
|
||
this.off(event, onceFn);
|
||
fn.apply(this, args);
|
||
};
|
||
return this.on(event, onceFn);
|
||
}
|
||
removeAllListeners() {
|
||
this.callbacks = {};
|
||
}
|
||
};
|
||
function getExtensionField(extension, field, context) {
|
||
if (extension.config[field] === void 0 && extension.parent) {
|
||
return getExtensionField(extension.parent, field, context);
|
||
}
|
||
if (typeof extension.config[field] === "function") {
|
||
const value = extension.config[field].bind({
|
||
...context,
|
||
parent: extension.parent ? getExtensionField(extension.parent, field, context) : null
|
||
});
|
||
return value;
|
||
}
|
||
return extension.config[field];
|
||
}
|
||
function splitExtensions(extensions) {
|
||
const baseExtensions = extensions.filter((extension) => extension.type === "extension");
|
||
const nodeExtensions = extensions.filter((extension) => extension.type === "node");
|
||
const markExtensions = extensions.filter((extension) => extension.type === "mark");
|
||
return {
|
||
baseExtensions,
|
||
nodeExtensions,
|
||
markExtensions
|
||
};
|
||
}
|
||
function getAttributesFromExtensions(extensions) {
|
||
const extensionAttributes = [];
|
||
const { nodeExtensions, markExtensions } = splitExtensions(extensions);
|
||
const nodeAndMarkExtensions = [...nodeExtensions, ...markExtensions];
|
||
const defaultAttribute = {
|
||
default: null,
|
||
rendered: true,
|
||
renderHTML: null,
|
||
parseHTML: null,
|
||
keepOnSplit: true,
|
||
isRequired: false
|
||
};
|
||
extensions.forEach((extension) => {
|
||
const context = {
|
||
name: extension.name,
|
||
options: extension.options,
|
||
storage: extension.storage,
|
||
extensions: nodeAndMarkExtensions
|
||
};
|
||
const addGlobalAttributes = getExtensionField(extension, "addGlobalAttributes", context);
|
||
if (!addGlobalAttributes) {
|
||
return;
|
||
}
|
||
const globalAttributes = addGlobalAttributes();
|
||
globalAttributes.forEach((globalAttribute) => {
|
||
globalAttribute.types.forEach((type) => {
|
||
Object.entries(globalAttribute.attributes).forEach(([name, attribute]) => {
|
||
extensionAttributes.push({
|
||
type,
|
||
name,
|
||
attribute: {
|
||
...defaultAttribute,
|
||
...attribute
|
||
}
|
||
});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
nodeAndMarkExtensions.forEach((extension) => {
|
||
const context = {
|
||
name: extension.name,
|
||
options: extension.options,
|
||
storage: extension.storage
|
||
};
|
||
const addAttributes = getExtensionField(extension, "addAttributes", context);
|
||
if (!addAttributes) {
|
||
return;
|
||
}
|
||
const attributes = addAttributes();
|
||
Object.entries(attributes).forEach(([name, attribute]) => {
|
||
const mergedAttr = {
|
||
...defaultAttribute,
|
||
...attribute
|
||
};
|
||
if (typeof (mergedAttr === null || mergedAttr === void 0 ? void 0 : mergedAttr.default) === "function") {
|
||
mergedAttr.default = mergedAttr.default();
|
||
}
|
||
if ((mergedAttr === null || mergedAttr === void 0 ? void 0 : mergedAttr.isRequired) && (mergedAttr === null || mergedAttr === void 0 ? void 0 : mergedAttr.default) === void 0) {
|
||
delete mergedAttr.default;
|
||
}
|
||
extensionAttributes.push({
|
||
type: extension.name,
|
||
name,
|
||
attribute: mergedAttr
|
||
});
|
||
});
|
||
});
|
||
return extensionAttributes;
|
||
}
|
||
function getNodeType(nameOrType, schema) {
|
||
if (typeof nameOrType === "string") {
|
||
if (!schema.nodes[nameOrType]) {
|
||
throw Error(`There is no node type named '${nameOrType}'. Maybe you forgot to add the extension?`);
|
||
}
|
||
return schema.nodes[nameOrType];
|
||
}
|
||
return nameOrType;
|
||
}
|
||
function mergeAttributes(...objects) {
|
||
return objects.filter((item) => !!item).reduce((items, item) => {
|
||
const mergedAttributes = { ...items };
|
||
Object.entries(item).forEach(([key, value]) => {
|
||
const exists = mergedAttributes[key];
|
||
if (!exists) {
|
||
mergedAttributes[key] = value;
|
||
return;
|
||
}
|
||
if (key === "class") {
|
||
const valueClasses = value ? value.split(" ") : [];
|
||
const existingClasses = mergedAttributes[key] ? mergedAttributes[key].split(" ") : [];
|
||
const insertClasses = valueClasses.filter((valueClass) => !existingClasses.includes(valueClass));
|
||
mergedAttributes[key] = [...existingClasses, ...insertClasses].join(" ");
|
||
} else if (key === "style") {
|
||
const newStyles = value ? value.split(";").map((style2) => style2.trim()).filter(Boolean) : [];
|
||
const existingStyles = mergedAttributes[key] ? mergedAttributes[key].split(";").map((style2) => style2.trim()).filter(Boolean) : [];
|
||
const styleMap = /* @__PURE__ */ new Map();
|
||
existingStyles.forEach((style2) => {
|
||
const [property, val] = style2.split(":").map((part) => part.trim());
|
||
styleMap.set(property, val);
|
||
});
|
||
newStyles.forEach((style2) => {
|
||
const [property, val] = style2.split(":").map((part) => part.trim());
|
||
styleMap.set(property, val);
|
||
});
|
||
mergedAttributes[key] = Array.from(styleMap.entries()).map(([property, val]) => `${property}: ${val}`).join("; ");
|
||
} else {
|
||
mergedAttributes[key] = value;
|
||
}
|
||
});
|
||
return mergedAttributes;
|
||
}, {});
|
||
}
|
||
function getRenderedAttributes(nodeOrMark, extensionAttributes) {
|
||
return extensionAttributes.filter((attribute) => attribute.type === nodeOrMark.type.name).filter((item) => item.attribute.rendered).map((item) => {
|
||
if (!item.attribute.renderHTML) {
|
||
return {
|
||
[item.name]: nodeOrMark.attrs[item.name]
|
||
};
|
||
}
|
||
return item.attribute.renderHTML(nodeOrMark.attrs) || {};
|
||
}).reduce((attributes, attribute) => mergeAttributes(attributes, attribute), {});
|
||
}
|
||
function isFunction(value) {
|
||
return typeof value === "function";
|
||
}
|
||
function callOrReturn(value, context = void 0, ...props) {
|
||
if (isFunction(value)) {
|
||
if (context) {
|
||
return value.bind(context)(...props);
|
||
}
|
||
return value(...props);
|
||
}
|
||
return value;
|
||
}
|
||
function isEmptyObject(value = {}) {
|
||
return Object.keys(value).length === 0 && value.constructor === Object;
|
||
}
|
||
function fromString(value) {
|
||
if (typeof value !== "string") {
|
||
return value;
|
||
}
|
||
if (value.match(/^[+-]?(?:\d*\.)?\d+$/)) {
|
||
return Number(value);
|
||
}
|
||
if (value === "true") {
|
||
return true;
|
||
}
|
||
if (value === "false") {
|
||
return false;
|
||
}
|
||
return value;
|
||
}
|
||
function injectExtensionAttributesToParseRule(parseRule, extensionAttributes) {
|
||
if ("style" in parseRule) {
|
||
return parseRule;
|
||
}
|
||
return {
|
||
...parseRule,
|
||
getAttrs: (node) => {
|
||
const oldAttributes = parseRule.getAttrs ? parseRule.getAttrs(node) : parseRule.attrs;
|
||
if (oldAttributes === false) {
|
||
return false;
|
||
}
|
||
const newAttributes = extensionAttributes.reduce((items, item) => {
|
||
const value = item.attribute.parseHTML ? item.attribute.parseHTML(node) : fromString(node.getAttribute(item.name));
|
||
if (value === null || value === void 0) {
|
||
return items;
|
||
}
|
||
return {
|
||
...items,
|
||
[item.name]: value
|
||
};
|
||
}, {});
|
||
return { ...oldAttributes, ...newAttributes };
|
||
}
|
||
};
|
||
}
|
||
function cleanUpSchemaItem(data) {
|
||
return Object.fromEntries(
|
||
// @ts-ignore
|
||
Object.entries(data).filter(([key, value]) => {
|
||
if (key === "attrs" && isEmptyObject(value)) {
|
||
return false;
|
||
}
|
||
return value !== null && value !== void 0;
|
||
})
|
||
);
|
||
}
|
||
function getSchemaByResolvedExtensions(extensions, editor) {
|
||
var _a;
|
||
const allAttributes = getAttributesFromExtensions(extensions);
|
||
const { nodeExtensions, markExtensions } = splitExtensions(extensions);
|
||
const topNode = (_a = nodeExtensions.find((extension) => getExtensionField(extension, "topNode"))) === null || _a === void 0 ? void 0 : _a.name;
|
||
const nodes = Object.fromEntries(nodeExtensions.map((extension) => {
|
||
const extensionAttributes = allAttributes.filter((attribute) => attribute.type === extension.name);
|
||
const context = {
|
||
name: extension.name,
|
||
options: extension.options,
|
||
storage: extension.storage,
|
||
editor
|
||
};
|
||
const extraNodeFields = extensions.reduce((fields, e) => {
|
||
const extendNodeSchema = getExtensionField(e, "extendNodeSchema", context);
|
||
return {
|
||
...fields,
|
||
...extendNodeSchema ? extendNodeSchema(extension) : {}
|
||
};
|
||
}, {});
|
||
const schema = cleanUpSchemaItem({
|
||
...extraNodeFields,
|
||
content: callOrReturn(getExtensionField(extension, "content", context)),
|
||
marks: callOrReturn(getExtensionField(extension, "marks", context)),
|
||
group: callOrReturn(getExtensionField(extension, "group", context)),
|
||
inline: callOrReturn(getExtensionField(extension, "inline", context)),
|
||
atom: callOrReturn(getExtensionField(extension, "atom", context)),
|
||
selectable: callOrReturn(getExtensionField(extension, "selectable", context)),
|
||
draggable: callOrReturn(getExtensionField(extension, "draggable", context)),
|
||
code: callOrReturn(getExtensionField(extension, "code", context)),
|
||
whitespace: callOrReturn(getExtensionField(extension, "whitespace", context)),
|
||
linebreakReplacement: callOrReturn(getExtensionField(extension, "linebreakReplacement", context)),
|
||
defining: callOrReturn(getExtensionField(extension, "defining", context)),
|
||
isolating: callOrReturn(getExtensionField(extension, "isolating", context)),
|
||
attrs: Object.fromEntries(extensionAttributes.map((extensionAttribute) => {
|
||
var _a2;
|
||
return [extensionAttribute.name, { default: (_a2 = extensionAttribute === null || extensionAttribute === void 0 ? void 0 : extensionAttribute.attribute) === null || _a2 === void 0 ? void 0 : _a2.default }];
|
||
}))
|
||
});
|
||
const parseHTML = callOrReturn(getExtensionField(extension, "parseHTML", context));
|
||
if (parseHTML) {
|
||
schema.parseDOM = parseHTML.map((parseRule) => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
|
||
}
|
||
const renderHTML = getExtensionField(extension, "renderHTML", context);
|
||
if (renderHTML) {
|
||
schema.toDOM = (node) => renderHTML({
|
||
node,
|
||
HTMLAttributes: getRenderedAttributes(node, extensionAttributes)
|
||
});
|
||
}
|
||
const renderText = getExtensionField(extension, "renderText", context);
|
||
if (renderText) {
|
||
schema.toText = renderText;
|
||
}
|
||
return [extension.name, schema];
|
||
}));
|
||
const marks = Object.fromEntries(markExtensions.map((extension) => {
|
||
const extensionAttributes = allAttributes.filter((attribute) => attribute.type === extension.name);
|
||
const context = {
|
||
name: extension.name,
|
||
options: extension.options,
|
||
storage: extension.storage,
|
||
editor
|
||
};
|
||
const extraMarkFields = extensions.reduce((fields, e) => {
|
||
const extendMarkSchema = getExtensionField(e, "extendMarkSchema", context);
|
||
return {
|
||
...fields,
|
||
...extendMarkSchema ? extendMarkSchema(extension) : {}
|
||
};
|
||
}, {});
|
||
const schema = cleanUpSchemaItem({
|
||
...extraMarkFields,
|
||
inclusive: callOrReturn(getExtensionField(extension, "inclusive", context)),
|
||
excludes: callOrReturn(getExtensionField(extension, "excludes", context)),
|
||
group: callOrReturn(getExtensionField(extension, "group", context)),
|
||
spanning: callOrReturn(getExtensionField(extension, "spanning", context)),
|
||
code: callOrReturn(getExtensionField(extension, "code", context)),
|
||
attrs: Object.fromEntries(extensionAttributes.map((extensionAttribute) => {
|
||
var _a2;
|
||
return [extensionAttribute.name, { default: (_a2 = extensionAttribute === null || extensionAttribute === void 0 ? void 0 : extensionAttribute.attribute) === null || _a2 === void 0 ? void 0 : _a2.default }];
|
||
}))
|
||
});
|
||
const parseHTML = callOrReturn(getExtensionField(extension, "parseHTML", context));
|
||
if (parseHTML) {
|
||
schema.parseDOM = parseHTML.map((parseRule) => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
|
||
}
|
||
const renderHTML = getExtensionField(extension, "renderHTML", context);
|
||
if (renderHTML) {
|
||
schema.toDOM = (mark) => renderHTML({
|
||
mark,
|
||
HTMLAttributes: getRenderedAttributes(mark, extensionAttributes)
|
||
});
|
||
}
|
||
return [extension.name, schema];
|
||
}));
|
||
return new Schema({
|
||
topNode,
|
||
nodes,
|
||
marks
|
||
});
|
||
}
|
||
function getSchemaTypeByName(name, schema) {
|
||
return schema.nodes[name] || schema.marks[name] || null;
|
||
}
|
||
function isExtensionRulesEnabled(extension, enabled) {
|
||
if (Array.isArray(enabled)) {
|
||
return enabled.some((enabledExtension) => {
|
||
const name = typeof enabledExtension === "string" ? enabledExtension : enabledExtension.name;
|
||
return name === extension.name;
|
||
});
|
||
}
|
||
return enabled;
|
||
}
|
||
function getHTMLFromFragment(fragment, schema) {
|
||
const documentFragment = DOMSerializer.fromSchema(schema).serializeFragment(fragment);
|
||
const temporaryDocument = document.implementation.createHTMLDocument();
|
||
const container = temporaryDocument.createElement("div");
|
||
container.appendChild(documentFragment);
|
||
return container.innerHTML;
|
||
}
|
||
var getTextContentFromNodes = ($from, maxMatch = 500) => {
|
||
let textBefore = "";
|
||
const sliceEndPos = $from.parentOffset;
|
||
$from.parent.nodesBetween(Math.max(0, sliceEndPos - maxMatch), sliceEndPos, (node, pos, parent, index2) => {
|
||
var _a, _b;
|
||
const chunk = ((_b = (_a = node.type.spec).toText) === null || _b === void 0 ? void 0 : _b.call(_a, {
|
||
node,
|
||
pos,
|
||
parent,
|
||
index: index2
|
||
})) || node.textContent || "%leaf%";
|
||
textBefore += node.isAtom && !node.isText ? chunk : chunk.slice(0, Math.max(0, sliceEndPos - pos));
|
||
});
|
||
return textBefore;
|
||
};
|
||
function isRegExp(value) {
|
||
return Object.prototype.toString.call(value) === "[object RegExp]";
|
||
}
|
||
var InputRule = class {
|
||
constructor(config) {
|
||
this.find = config.find;
|
||
this.handler = config.handler;
|
||
}
|
||
};
|
||
var inputRuleMatcherHandler = (text, find) => {
|
||
if (isRegExp(find)) {
|
||
return find.exec(text);
|
||
}
|
||
const inputRuleMatch = find(text);
|
||
if (!inputRuleMatch) {
|
||
return null;
|
||
}
|
||
const result = [inputRuleMatch.text];
|
||
result.index = inputRuleMatch.index;
|
||
result.input = text;
|
||
result.data = inputRuleMatch.data;
|
||
if (inputRuleMatch.replaceWith) {
|
||
if (!inputRuleMatch.text.includes(inputRuleMatch.replaceWith)) {
|
||
console.warn('[tiptap warn]: "inputRuleMatch.replaceWith" must be part of "inputRuleMatch.text".');
|
||
}
|
||
result.push(inputRuleMatch.replaceWith);
|
||
}
|
||
return result;
|
||
};
|
||
function run$1(config) {
|
||
var _a;
|
||
const { editor, from, to, text, rules, plugin } = config;
|
||
const { view } = editor;
|
||
if (view.composing) {
|
||
return false;
|
||
}
|
||
const $from = view.state.doc.resolve(from);
|
||
if (
|
||
// check for code node
|
||
$from.parent.type.spec.code || !!((_a = $from.nodeBefore || $from.nodeAfter) === null || _a === void 0 ? void 0 : _a.marks.find((mark) => mark.type.spec.code))
|
||
) {
|
||
return false;
|
||
}
|
||
let matched = false;
|
||
const textBefore = getTextContentFromNodes($from) + text;
|
||
rules.forEach((rule) => {
|
||
if (matched) {
|
||
return;
|
||
}
|
||
const match = inputRuleMatcherHandler(textBefore, rule.find);
|
||
if (!match) {
|
||
return;
|
||
}
|
||
const tr = view.state.tr;
|
||
const state = createChainableState({
|
||
state: view.state,
|
||
transaction: tr
|
||
});
|
||
const range = {
|
||
from: from - (match[0].length - text.length),
|
||
to
|
||
};
|
||
const { commands: commands2, chain, can } = new CommandManager({
|
||
editor,
|
||
state
|
||
});
|
||
const handler = rule.handler({
|
||
state,
|
||
range,
|
||
match,
|
||
commands: commands2,
|
||
chain,
|
||
can
|
||
});
|
||
if (handler === null || !tr.steps.length) {
|
||
return;
|
||
}
|
||
tr.setMeta(plugin, {
|
||
transform: tr,
|
||
from,
|
||
to,
|
||
text
|
||
});
|
||
view.dispatch(tr);
|
||
matched = true;
|
||
});
|
||
return matched;
|
||
}
|
||
function inputRulesPlugin(props) {
|
||
const { editor, rules } = props;
|
||
const plugin = new Plugin({
|
||
state: {
|
||
init() {
|
||
return null;
|
||
},
|
||
apply(tr, prev, state) {
|
||
const stored = tr.getMeta(plugin);
|
||
if (stored) {
|
||
return stored;
|
||
}
|
||
const simulatedInputMeta = tr.getMeta("applyInputRules");
|
||
const isSimulatedInput = !!simulatedInputMeta;
|
||
if (isSimulatedInput) {
|
||
setTimeout(() => {
|
||
let { text } = simulatedInputMeta;
|
||
if (typeof text === "string") {
|
||
text = text;
|
||
} else {
|
||
text = getHTMLFromFragment(Fragment.from(text), state.schema);
|
||
}
|
||
const { from } = simulatedInputMeta;
|
||
const to = from + text.length;
|
||
run$1({
|
||
editor,
|
||
from,
|
||
to,
|
||
text,
|
||
rules,
|
||
plugin
|
||
});
|
||
});
|
||
}
|
||
return tr.selectionSet || tr.docChanged ? null : prev;
|
||
}
|
||
},
|
||
props: {
|
||
handleTextInput(view, from, to, text) {
|
||
return run$1({
|
||
editor,
|
||
from,
|
||
to,
|
||
text,
|
||
rules,
|
||
plugin
|
||
});
|
||
},
|
||
handleDOMEvents: {
|
||
compositionend: (view) => {
|
||
setTimeout(() => {
|
||
const { $cursor } = view.state.selection;
|
||
if ($cursor) {
|
||
run$1({
|
||
editor,
|
||
from: $cursor.pos,
|
||
to: $cursor.pos,
|
||
text: "",
|
||
rules,
|
||
plugin
|
||
});
|
||
}
|
||
});
|
||
return false;
|
||
}
|
||
},
|
||
// add support for input rules to trigger on enter
|
||
// this is useful for example for code blocks
|
||
handleKeyDown(view, event) {
|
||
if (event.key !== "Enter") {
|
||
return false;
|
||
}
|
||
const { $cursor } = view.state.selection;
|
||
if ($cursor) {
|
||
return run$1({
|
||
editor,
|
||
from: $cursor.pos,
|
||
to: $cursor.pos,
|
||
text: "\n",
|
||
rules,
|
||
plugin
|
||
});
|
||
}
|
||
return false;
|
||
}
|
||
},
|
||
// @ts-ignore
|
||
isInputRules: true
|
||
});
|
||
return plugin;
|
||
}
|
||
function getType(value) {
|
||
return Object.prototype.toString.call(value).slice(8, -1);
|
||
}
|
||
function isPlainObject(value) {
|
||
if (getType(value) !== "Object") {
|
||
return false;
|
||
}
|
||
return value.constructor === Object && Object.getPrototypeOf(value) === Object.prototype;
|
||
}
|
||
function mergeDeep(target, source) {
|
||
const output = { ...target };
|
||
if (isPlainObject(target) && isPlainObject(source)) {
|
||
Object.keys(source).forEach((key) => {
|
||
if (isPlainObject(source[key]) && isPlainObject(target[key])) {
|
||
output[key] = mergeDeep(target[key], source[key]);
|
||
} else {
|
||
output[key] = source[key];
|
||
}
|
||
});
|
||
}
|
||
return output;
|
||
}
|
||
var Mark2 = class _Mark {
|
||
constructor(config = {}) {
|
||
this.type = "mark";
|
||
this.name = "mark";
|
||
this.parent = null;
|
||
this.child = null;
|
||
this.config = {
|
||
name: this.name,
|
||
defaultOptions: {}
|
||
};
|
||
this.config = {
|
||
...this.config,
|
||
...config
|
||
};
|
||
this.name = this.config.name;
|
||
if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
|
||
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
|
||
}
|
||
this.options = this.config.defaultOptions;
|
||
if (this.config.addOptions) {
|
||
this.options = callOrReturn(getExtensionField(this, "addOptions", {
|
||
name: this.name
|
||
}));
|
||
}
|
||
this.storage = callOrReturn(getExtensionField(this, "addStorage", {
|
||
name: this.name,
|
||
options: this.options
|
||
})) || {};
|
||
}
|
||
static create(config = {}) {
|
||
return new _Mark(config);
|
||
}
|
||
configure(options = {}) {
|
||
const extension = this.extend({
|
||
...this.config,
|
||
addOptions: () => {
|
||
return mergeDeep(this.options, options);
|
||
}
|
||
});
|
||
extension.name = this.name;
|
||
extension.parent = this.parent;
|
||
return extension;
|
||
}
|
||
extend(extendedConfig = {}) {
|
||
const extension = new _Mark(extendedConfig);
|
||
extension.parent = this;
|
||
this.child = extension;
|
||
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
|
||
if (extendedConfig.defaultOptions && Object.keys(extendedConfig.defaultOptions).length > 0) {
|
||
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
|
||
}
|
||
extension.options = callOrReturn(getExtensionField(extension, "addOptions", {
|
||
name: extension.name
|
||
}));
|
||
extension.storage = callOrReturn(getExtensionField(extension, "addStorage", {
|
||
name: extension.name,
|
||
options: extension.options
|
||
}));
|
||
return extension;
|
||
}
|
||
static handleExit({ editor, mark }) {
|
||
const { tr } = editor.state;
|
||
const currentPos = editor.state.selection.$from;
|
||
const isAtEnd = currentPos.pos === currentPos.end();
|
||
if (isAtEnd) {
|
||
const currentMarks = currentPos.marks();
|
||
const isInMark = !!currentMarks.find((m) => (m === null || m === void 0 ? void 0 : m.type.name) === mark.name);
|
||
if (!isInMark) {
|
||
return false;
|
||
}
|
||
const removeMark = currentMarks.find((m) => (m === null || m === void 0 ? void 0 : m.type.name) === mark.name);
|
||
if (removeMark) {
|
||
tr.removeStoredMark(removeMark);
|
||
}
|
||
tr.insertText(" ", currentPos.pos);
|
||
editor.view.dispatch(tr);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
};
|
||
function isNumber(value) {
|
||
return typeof value === "number";
|
||
}
|
||
var PasteRule = class {
|
||
constructor(config) {
|
||
this.find = config.find;
|
||
this.handler = config.handler;
|
||
}
|
||
};
|
||
var pasteRuleMatcherHandler = (text, find, event) => {
|
||
if (isRegExp(find)) {
|
||
return [...text.matchAll(find)];
|
||
}
|
||
const matches = find(text, event);
|
||
if (!matches) {
|
||
return [];
|
||
}
|
||
return matches.map((pasteRuleMatch) => {
|
||
const result = [pasteRuleMatch.text];
|
||
result.index = pasteRuleMatch.index;
|
||
result.input = text;
|
||
result.data = pasteRuleMatch.data;
|
||
if (pasteRuleMatch.replaceWith) {
|
||
if (!pasteRuleMatch.text.includes(pasteRuleMatch.replaceWith)) {
|
||
console.warn('[tiptap warn]: "pasteRuleMatch.replaceWith" must be part of "pasteRuleMatch.text".');
|
||
}
|
||
result.push(pasteRuleMatch.replaceWith);
|
||
}
|
||
return result;
|
||
});
|
||
};
|
||
function run(config) {
|
||
const { editor, state, from, to, rule, pasteEvent, dropEvent } = config;
|
||
const { commands: commands2, chain, can } = new CommandManager({
|
||
editor,
|
||
state
|
||
});
|
||
const handlers2 = [];
|
||
state.doc.nodesBetween(from, to, (node, pos) => {
|
||
if (!node.isTextblock || node.type.spec.code) {
|
||
return;
|
||
}
|
||
const resolvedFrom = Math.max(from, pos);
|
||
const resolvedTo = Math.min(to, pos + node.content.size);
|
||
const textToMatch = node.textBetween(resolvedFrom - pos, resolvedTo - pos, void 0, "");
|
||
const matches = pasteRuleMatcherHandler(textToMatch, rule.find, pasteEvent);
|
||
matches.forEach((match) => {
|
||
if (match.index === void 0) {
|
||
return;
|
||
}
|
||
const start = resolvedFrom + match.index + 1;
|
||
const end = start + match[0].length;
|
||
const range = {
|
||
from: state.tr.mapping.map(start),
|
||
to: state.tr.mapping.map(end)
|
||
};
|
||
const handler = rule.handler({
|
||
state,
|
||
range,
|
||
match,
|
||
commands: commands2,
|
||
chain,
|
||
can,
|
||
pasteEvent,
|
||
dropEvent
|
||
});
|
||
handlers2.push(handler);
|
||
});
|
||
});
|
||
const success = handlers2.every((handler) => handler !== null);
|
||
return success;
|
||
}
|
||
var createClipboardPasteEvent = (text) => {
|
||
var _a;
|
||
const event = new ClipboardEvent("paste", {
|
||
clipboardData: new DataTransfer()
|
||
});
|
||
(_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.setData("text/html", text);
|
||
return event;
|
||
};
|
||
function pasteRulesPlugin(props) {
|
||
const { editor, rules } = props;
|
||
let dragSourceElement = null;
|
||
let isPastedFromProseMirror = false;
|
||
let isDroppedFromProseMirror = false;
|
||
let pasteEvent = typeof ClipboardEvent !== "undefined" ? new ClipboardEvent("paste") : null;
|
||
let dropEvent;
|
||
try {
|
||
dropEvent = typeof DragEvent !== "undefined" ? new DragEvent("drop") : null;
|
||
} catch (e) {
|
||
dropEvent = null;
|
||
}
|
||
const processEvent = ({ state, from, to, rule, pasteEvt }) => {
|
||
const tr = state.tr;
|
||
const chainableState = createChainableState({
|
||
state,
|
||
transaction: tr
|
||
});
|
||
const handler = run({
|
||
editor,
|
||
state: chainableState,
|
||
from: Math.max(from - 1, 0),
|
||
to: to.b - 1,
|
||
rule,
|
||
pasteEvent: pasteEvt,
|
||
dropEvent
|
||
});
|
||
if (!handler || !tr.steps.length) {
|
||
return;
|
||
}
|
||
try {
|
||
dropEvent = typeof DragEvent !== "undefined" ? new DragEvent("drop") : null;
|
||
} catch (e) {
|
||
dropEvent = null;
|
||
}
|
||
pasteEvent = typeof ClipboardEvent !== "undefined" ? new ClipboardEvent("paste") : null;
|
||
return tr;
|
||
};
|
||
const plugins = rules.map((rule) => {
|
||
return new Plugin({
|
||
// we register a global drag handler to track the current drag source element
|
||
view(view) {
|
||
const handleDragstart = (event) => {
|
||
var _a;
|
||
dragSourceElement = ((_a = view.dom.parentElement) === null || _a === void 0 ? void 0 : _a.contains(event.target)) ? view.dom.parentElement : null;
|
||
};
|
||
window.addEventListener("dragstart", handleDragstart);
|
||
return {
|
||
destroy() {
|
||
window.removeEventListener("dragstart", handleDragstart);
|
||
}
|
||
};
|
||
},
|
||
props: {
|
||
handleDOMEvents: {
|
||
drop: (view, event) => {
|
||
isDroppedFromProseMirror = dragSourceElement === view.dom.parentElement;
|
||
dropEvent = event;
|
||
return false;
|
||
},
|
||
paste: (_view, event) => {
|
||
var _a;
|
||
const html = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData("text/html");
|
||
pasteEvent = event;
|
||
isPastedFromProseMirror = !!(html === null || html === void 0 ? void 0 : html.includes("data-pm-slice"));
|
||
return false;
|
||
}
|
||
}
|
||
},
|
||
appendTransaction: (transactions, oldState, state) => {
|
||
const transaction = transactions[0];
|
||
const isPaste = transaction.getMeta("uiEvent") === "paste" && !isPastedFromProseMirror;
|
||
const isDrop = transaction.getMeta("uiEvent") === "drop" && !isDroppedFromProseMirror;
|
||
const simulatedPasteMeta = transaction.getMeta("applyPasteRules");
|
||
const isSimulatedPaste = !!simulatedPasteMeta;
|
||
if (!isPaste && !isDrop && !isSimulatedPaste) {
|
||
return;
|
||
}
|
||
if (isSimulatedPaste) {
|
||
let { text } = simulatedPasteMeta;
|
||
if (typeof text === "string") {
|
||
text = text;
|
||
} else {
|
||
text = getHTMLFromFragment(Fragment.from(text), state.schema);
|
||
}
|
||
const { from: from2 } = simulatedPasteMeta;
|
||
const to2 = from2 + text.length;
|
||
const pasteEvt = createClipboardPasteEvent(text);
|
||
return processEvent({
|
||
rule,
|
||
state,
|
||
from: from2,
|
||
to: { b: to2 },
|
||
pasteEvt
|
||
});
|
||
}
|
||
const from = oldState.doc.content.findDiffStart(state.doc.content);
|
||
const to = oldState.doc.content.findDiffEnd(state.doc.content);
|
||
if (!isNumber(from) || !to || from === to.b) {
|
||
return;
|
||
}
|
||
return processEvent({
|
||
rule,
|
||
state,
|
||
from,
|
||
to,
|
||
pasteEvt: pasteEvent
|
||
});
|
||
}
|
||
});
|
||
});
|
||
return plugins;
|
||
}
|
||
function findDuplicates(items) {
|
||
const filtered = items.filter((el, index2) => items.indexOf(el) !== index2);
|
||
return Array.from(new Set(filtered));
|
||
}
|
||
var ExtensionManager = class _ExtensionManager {
|
||
constructor(extensions, editor) {
|
||
this.splittableMarks = [];
|
||
this.editor = editor;
|
||
this.extensions = _ExtensionManager.resolve(extensions);
|
||
this.schema = getSchemaByResolvedExtensions(this.extensions, editor);
|
||
this.setupExtensions();
|
||
}
|
||
/**
|
||
* Returns a flattened and sorted extension list while
|
||
* also checking for duplicated extensions and warns the user.
|
||
* @param extensions An array of Tiptap extensions
|
||
* @returns An flattened and sorted array of Tiptap extensions
|
||
*/
|
||
static resolve(extensions) {
|
||
const resolvedExtensions = _ExtensionManager.sort(_ExtensionManager.flatten(extensions));
|
||
const duplicatedNames = findDuplicates(resolvedExtensions.map((extension) => extension.name));
|
||
if (duplicatedNames.length) {
|
||
console.warn(`[tiptap warn]: Duplicate extension names found: [${duplicatedNames.map((item) => `'${item}'`).join(", ")}]. This can lead to issues.`);
|
||
}
|
||
return resolvedExtensions;
|
||
}
|
||
/**
|
||
* Create a flattened array of extensions by traversing the `addExtensions` field.
|
||
* @param extensions An array of Tiptap extensions
|
||
* @returns A flattened array of Tiptap extensions
|
||
*/
|
||
static flatten(extensions) {
|
||
return extensions.map((extension) => {
|
||
const context = {
|
||
name: extension.name,
|
||
options: extension.options,
|
||
storage: extension.storage
|
||
};
|
||
const addExtensions = getExtensionField(extension, "addExtensions", context);
|
||
if (addExtensions) {
|
||
return [extension, ...this.flatten(addExtensions())];
|
||
}
|
||
return extension;
|
||
}).flat(10);
|
||
}
|
||
/**
|
||
* Sort extensions by priority.
|
||
* @param extensions An array of Tiptap extensions
|
||
* @returns A sorted array of Tiptap extensions by priority
|
||
*/
|
||
static sort(extensions) {
|
||
const defaultPriority = 100;
|
||
return extensions.sort((a, b) => {
|
||
const priorityA = getExtensionField(a, "priority") || defaultPriority;
|
||
const priorityB = getExtensionField(b, "priority") || defaultPriority;
|
||
if (priorityA > priorityB) {
|
||
return -1;
|
||
}
|
||
if (priorityA < priorityB) {
|
||
return 1;
|
||
}
|
||
return 0;
|
||
});
|
||
}
|
||
/**
|
||
* Get all commands from the extensions.
|
||
* @returns An object with all commands where the key is the command name and the value is the command function
|
||
*/
|
||
get commands() {
|
||
return this.extensions.reduce((commands2, extension) => {
|
||
const context = {
|
||
name: extension.name,
|
||
options: extension.options,
|
||
storage: extension.storage,
|
||
editor: this.editor,
|
||
type: getSchemaTypeByName(extension.name, this.schema)
|
||
};
|
||
const addCommands = getExtensionField(extension, "addCommands", context);
|
||
if (!addCommands) {
|
||
return commands2;
|
||
}
|
||
return {
|
||
...commands2,
|
||
...addCommands()
|
||
};
|
||
}, {});
|
||
}
|
||
/**
|
||
* Get all registered Prosemirror plugins from the extensions.
|
||
* @returns An array of Prosemirror plugins
|
||
*/
|
||
get plugins() {
|
||
const { editor } = this;
|
||
const extensions = _ExtensionManager.sort([...this.extensions].reverse());
|
||
const inputRules = [];
|
||
const pasteRules = [];
|
||
const allPlugins = extensions.map((extension) => {
|
||
const context = {
|
||
name: extension.name,
|
||
options: extension.options,
|
||
storage: extension.storage,
|
||
editor,
|
||
type: getSchemaTypeByName(extension.name, this.schema)
|
||
};
|
||
const plugins = [];
|
||
const addKeyboardShortcuts = getExtensionField(extension, "addKeyboardShortcuts", context);
|
||
let defaultBindings = {};
|
||
if (extension.type === "mark" && getExtensionField(extension, "exitable", context)) {
|
||
defaultBindings.ArrowRight = () => Mark2.handleExit({ editor, mark: extension });
|
||
}
|
||
if (addKeyboardShortcuts) {
|
||
const bindings = Object.fromEntries(Object.entries(addKeyboardShortcuts()).map(([shortcut, method]) => {
|
||
return [shortcut, () => method({ editor })];
|
||
}));
|
||
defaultBindings = { ...defaultBindings, ...bindings };
|
||
}
|
||
const keyMapPlugin = keymap(defaultBindings);
|
||
plugins.push(keyMapPlugin);
|
||
const addInputRules = getExtensionField(extension, "addInputRules", context);
|
||
if (isExtensionRulesEnabled(extension, editor.options.enableInputRules) && addInputRules) {
|
||
inputRules.push(...addInputRules());
|
||
}
|
||
const addPasteRules = getExtensionField(extension, "addPasteRules", context);
|
||
if (isExtensionRulesEnabled(extension, editor.options.enablePasteRules) && addPasteRules) {
|
||
pasteRules.push(...addPasteRules());
|
||
}
|
||
const addProseMirrorPlugins = getExtensionField(extension, "addProseMirrorPlugins", context);
|
||
if (addProseMirrorPlugins) {
|
||
const proseMirrorPlugins = addProseMirrorPlugins();
|
||
plugins.push(...proseMirrorPlugins);
|
||
}
|
||
return plugins;
|
||
}).flat();
|
||
return [
|
||
inputRulesPlugin({
|
||
editor,
|
||
rules: inputRules
|
||
}),
|
||
...pasteRulesPlugin({
|
||
editor,
|
||
rules: pasteRules
|
||
}),
|
||
...allPlugins
|
||
];
|
||
}
|
||
/**
|
||
* Get all attributes from the extensions.
|
||
* @returns An array of attributes
|
||
*/
|
||
get attributes() {
|
||
return getAttributesFromExtensions(this.extensions);
|
||
}
|
||
/**
|
||
* Get all node views from the extensions.
|
||
* @returns An object with all node views where the key is the node name and the value is the node view function
|
||
*/
|
||
get nodeViews() {
|
||
const { editor } = this;
|
||
const { nodeExtensions } = splitExtensions(this.extensions);
|
||
return Object.fromEntries(nodeExtensions.filter((extension) => !!getExtensionField(extension, "addNodeView")).map((extension) => {
|
||
const extensionAttributes = this.attributes.filter((attribute) => attribute.type === extension.name);
|
||
const context = {
|
||
name: extension.name,
|
||
options: extension.options,
|
||
storage: extension.storage,
|
||
editor,
|
||
type: getNodeType(extension.name, this.schema)
|
||
};
|
||
const addNodeView = getExtensionField(extension, "addNodeView", context);
|
||
if (!addNodeView) {
|
||
return [];
|
||
}
|
||
const nodeview = (node, view, getPos, decorations, innerDecorations) => {
|
||
const HTMLAttributes = getRenderedAttributes(node, extensionAttributes);
|
||
return addNodeView()({
|
||
// pass-through
|
||
node,
|
||
view,
|
||
getPos,
|
||
decorations,
|
||
innerDecorations,
|
||
// tiptap-specific
|
||
editor,
|
||
extension,
|
||
HTMLAttributes
|
||
});
|
||
};
|
||
return [extension.name, nodeview];
|
||
}));
|
||
}
|
||
/**
|
||
* Go through all extensions, create extension storages & setup marks
|
||
* & bind editor event listener.
|
||
*/
|
||
setupExtensions() {
|
||
this.extensions.forEach((extension) => {
|
||
var _a;
|
||
this.editor.extensionStorage[extension.name] = extension.storage;
|
||
const context = {
|
||
name: extension.name,
|
||
options: extension.options,
|
||
storage: extension.storage,
|
||
editor: this.editor,
|
||
type: getSchemaTypeByName(extension.name, this.schema)
|
||
};
|
||
if (extension.type === "mark") {
|
||
const keepOnSplit = (_a = callOrReturn(getExtensionField(extension, "keepOnSplit", context))) !== null && _a !== void 0 ? _a : true;
|
||
if (keepOnSplit) {
|
||
this.splittableMarks.push(extension.name);
|
||
}
|
||
}
|
||
const onBeforeCreate = getExtensionField(extension, "onBeforeCreate", context);
|
||
const onCreate = getExtensionField(extension, "onCreate", context);
|
||
const onUpdate = getExtensionField(extension, "onUpdate", context);
|
||
const onSelectionUpdate = getExtensionField(extension, "onSelectionUpdate", context);
|
||
const onTransaction = getExtensionField(extension, "onTransaction", context);
|
||
const onFocus = getExtensionField(extension, "onFocus", context);
|
||
const onBlur = getExtensionField(extension, "onBlur", context);
|
||
const onDestroy = getExtensionField(extension, "onDestroy", context);
|
||
if (onBeforeCreate) {
|
||
this.editor.on("beforeCreate", onBeforeCreate);
|
||
}
|
||
if (onCreate) {
|
||
this.editor.on("create", onCreate);
|
||
}
|
||
if (onUpdate) {
|
||
this.editor.on("update", onUpdate);
|
||
}
|
||
if (onSelectionUpdate) {
|
||
this.editor.on("selectionUpdate", onSelectionUpdate);
|
||
}
|
||
if (onTransaction) {
|
||
this.editor.on("transaction", onTransaction);
|
||
}
|
||
if (onFocus) {
|
||
this.editor.on("focus", onFocus);
|
||
}
|
||
if (onBlur) {
|
||
this.editor.on("blur", onBlur);
|
||
}
|
||
if (onDestroy) {
|
||
this.editor.on("destroy", onDestroy);
|
||
}
|
||
});
|
||
}
|
||
};
|
||
var Extension = class _Extension {
|
||
constructor(config = {}) {
|
||
this.type = "extension";
|
||
this.name = "extension";
|
||
this.parent = null;
|
||
this.child = null;
|
||
this.config = {
|
||
name: this.name,
|
||
defaultOptions: {}
|
||
};
|
||
this.config = {
|
||
...this.config,
|
||
...config
|
||
};
|
||
this.name = this.config.name;
|
||
if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
|
||
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
|
||
}
|
||
this.options = this.config.defaultOptions;
|
||
if (this.config.addOptions) {
|
||
this.options = callOrReturn(getExtensionField(this, "addOptions", {
|
||
name: this.name
|
||
}));
|
||
}
|
||
this.storage = callOrReturn(getExtensionField(this, "addStorage", {
|
||
name: this.name,
|
||
options: this.options
|
||
})) || {};
|
||
}
|
||
static create(config = {}) {
|
||
return new _Extension(config);
|
||
}
|
||
configure(options = {}) {
|
||
const extension = this.extend({
|
||
...this.config,
|
||
addOptions: () => {
|
||
return mergeDeep(this.options, options);
|
||
}
|
||
});
|
||
extension.name = this.name;
|
||
extension.parent = this.parent;
|
||
return extension;
|
||
}
|
||
extend(extendedConfig = {}) {
|
||
const extension = new _Extension({ ...this.config, ...extendedConfig });
|
||
extension.parent = this;
|
||
this.child = extension;
|
||
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
|
||
if (extendedConfig.defaultOptions && Object.keys(extendedConfig.defaultOptions).length > 0) {
|
||
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
|
||
}
|
||
extension.options = callOrReturn(getExtensionField(extension, "addOptions", {
|
||
name: extension.name
|
||
}));
|
||
extension.storage = callOrReturn(getExtensionField(extension, "addStorage", {
|
||
name: extension.name,
|
||
options: extension.options
|
||
}));
|
||
return extension;
|
||
}
|
||
};
|
||
function getTextBetween(startNode, range, options) {
|
||
const { from, to } = range;
|
||
const { blockSeparator = "\n\n", textSerializers = {} } = options || {};
|
||
let text = "";
|
||
startNode.nodesBetween(from, to, (node, pos, parent, index2) => {
|
||
var _a;
|
||
if (node.isBlock && pos > from) {
|
||
text += blockSeparator;
|
||
}
|
||
const textSerializer = textSerializers === null || textSerializers === void 0 ? void 0 : textSerializers[node.type.name];
|
||
if (textSerializer) {
|
||
if (parent) {
|
||
text += textSerializer({
|
||
node,
|
||
pos,
|
||
parent,
|
||
index: index2,
|
||
range
|
||
});
|
||
}
|
||
return false;
|
||
}
|
||
if (node.isText) {
|
||
text += (_a = node === null || node === void 0 ? void 0 : node.text) === null || _a === void 0 ? void 0 : _a.slice(Math.max(from, pos) - pos, to - pos);
|
||
}
|
||
});
|
||
return text;
|
||
}
|
||
function getTextSerializersFromSchema(schema) {
|
||
return Object.fromEntries(Object.entries(schema.nodes).filter(([, node]) => node.spec.toText).map(([name, node]) => [name, node.spec.toText]));
|
||
}
|
||
var ClipboardTextSerializer = Extension.create({
|
||
name: "clipboardTextSerializer",
|
||
addOptions() {
|
||
return {
|
||
blockSeparator: void 0
|
||
};
|
||
},
|
||
addProseMirrorPlugins() {
|
||
return [
|
||
new Plugin({
|
||
key: new PluginKey("clipboardTextSerializer"),
|
||
props: {
|
||
clipboardTextSerializer: () => {
|
||
const { editor } = this;
|
||
const { state, schema } = editor;
|
||
const { doc: doc2, selection } = state;
|
||
const { ranges } = selection;
|
||
const from = Math.min(...ranges.map((range2) => range2.$from.pos));
|
||
const to = Math.max(...ranges.map((range2) => range2.$to.pos));
|
||
const textSerializers = getTextSerializersFromSchema(schema);
|
||
const range = { from, to };
|
||
return getTextBetween(doc2, range, {
|
||
...this.options.blockSeparator !== void 0 ? { blockSeparator: this.options.blockSeparator } : {},
|
||
textSerializers
|
||
});
|
||
}
|
||
}
|
||
})
|
||
];
|
||
}
|
||
});
|
||
var blur = () => ({ editor, view }) => {
|
||
requestAnimationFrame(() => {
|
||
var _a;
|
||
if (!editor.isDestroyed) {
|
||
view.dom.blur();
|
||
(_a = window === null || window === void 0 ? void 0 : window.getSelection()) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
|
||
}
|
||
});
|
||
return true;
|
||
};
|
||
var clearContent = (emitUpdate = false) => ({ commands: commands2 }) => {
|
||
return commands2.setContent("", emitUpdate);
|
||
};
|
||
var clearNodes = () => ({ state, tr, dispatch }) => {
|
||
const { selection } = tr;
|
||
const { ranges } = selection;
|
||
if (!dispatch) {
|
||
return true;
|
||
}
|
||
ranges.forEach(({ $from, $to }) => {
|
||
state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
|
||
if (node.type.isText) {
|
||
return;
|
||
}
|
||
const { doc: doc2, mapping } = tr;
|
||
const $mappedFrom = doc2.resolve(mapping.map(pos));
|
||
const $mappedTo = doc2.resolve(mapping.map(pos + node.nodeSize));
|
||
const nodeRange = $mappedFrom.blockRange($mappedTo);
|
||
if (!nodeRange) {
|
||
return;
|
||
}
|
||
const targetLiftDepth = liftTarget(nodeRange);
|
||
if (node.type.isTextblock) {
|
||
const { defaultType } = $mappedFrom.parent.contentMatchAt($mappedFrom.index());
|
||
tr.setNodeMarkup(nodeRange.start, defaultType);
|
||
}
|
||
if (targetLiftDepth || targetLiftDepth === 0) {
|
||
tr.lift(nodeRange, targetLiftDepth);
|
||
}
|
||
});
|
||
});
|
||
return true;
|
||
};
|
||
var command = (fn) => (props) => {
|
||
return fn(props);
|
||
};
|
||
var createParagraphNear2 = () => ({ state, dispatch }) => {
|
||
return createParagraphNear(state, dispatch);
|
||
};
|
||
var cut = (originRange, targetPos) => ({ editor, tr }) => {
|
||
const { state } = editor;
|
||
const contentSlice = state.doc.slice(originRange.from, originRange.to);
|
||
tr.deleteRange(originRange.from, originRange.to);
|
||
const newPos = tr.mapping.map(targetPos);
|
||
tr.insert(newPos, contentSlice.content);
|
||
tr.setSelection(new TextSelection(tr.doc.resolve(newPos - 1)));
|
||
return true;
|
||
};
|
||
var deleteCurrentNode = () => ({ tr, dispatch }) => {
|
||
const { selection } = tr;
|
||
const currentNode = selection.$anchor.node();
|
||
if (currentNode.content.size > 0) {
|
||
return false;
|
||
}
|
||
const $pos = tr.selection.$anchor;
|
||
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
||
const node = $pos.node(depth);
|
||
if (node.type === currentNode.type) {
|
||
if (dispatch) {
|
||
const from = $pos.before(depth);
|
||
const to = $pos.after(depth);
|
||
tr.delete(from, to).scrollIntoView();
|
||
}
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
};
|
||
var deleteNode = (typeOrName) => ({ tr, state, dispatch }) => {
|
||
const type = getNodeType(typeOrName, state.schema);
|
||
const $pos = tr.selection.$anchor;
|
||
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
||
const node = $pos.node(depth);
|
||
if (node.type === type) {
|
||
if (dispatch) {
|
||
const from = $pos.before(depth);
|
||
const to = $pos.after(depth);
|
||
tr.delete(from, to).scrollIntoView();
|
||
}
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
};
|
||
var deleteRange = (range) => ({ tr, dispatch }) => {
|
||
const { from, to } = range;
|
||
if (dispatch) {
|
||
tr.delete(from, to);
|
||
}
|
||
return true;
|
||
};
|
||
var deleteSelection2 = () => ({ state, dispatch }) => {
|
||
return deleteSelection(state, dispatch);
|
||
};
|
||
var enter = () => ({ commands: commands2 }) => {
|
||
return commands2.keyboardShortcut("Enter");
|
||
};
|
||
var exitCode2 = () => ({ state, dispatch }) => {
|
||
return exitCode(state, dispatch);
|
||
};
|
||
function objectIncludes(object1, object2, options = { strict: true }) {
|
||
const keys = Object.keys(object2);
|
||
if (!keys.length) {
|
||
return true;
|
||
}
|
||
return keys.every((key) => {
|
||
if (options.strict) {
|
||
return object2[key] === object1[key];
|
||
}
|
||
if (isRegExp(object2[key])) {
|
||
return object2[key].test(object1[key]);
|
||
}
|
||
return object2[key] === object1[key];
|
||
});
|
||
}
|
||
function findMarkInSet(marks, type, attributes = {}) {
|
||
return marks.find((item) => {
|
||
return item.type === type && objectIncludes(
|
||
// Only check equality for the attributes that are provided
|
||
Object.fromEntries(Object.keys(attributes).map((k) => [k, item.attrs[k]])),
|
||
attributes
|
||
);
|
||
});
|
||
}
|
||
function isMarkInSet(marks, type, attributes = {}) {
|
||
return !!findMarkInSet(marks, type, attributes);
|
||
}
|
||
function getMarkRange($pos, type, attributes) {
|
||
var _a;
|
||
if (!$pos || !type) {
|
||
return;
|
||
}
|
||
let start = $pos.parent.childAfter($pos.parentOffset);
|
||
if (!start.node || !start.node.marks.some((mark2) => mark2.type === type)) {
|
||
start = $pos.parent.childBefore($pos.parentOffset);
|
||
}
|
||
if (!start.node || !start.node.marks.some((mark2) => mark2.type === type)) {
|
||
return;
|
||
}
|
||
attributes = attributes || ((_a = start.node.marks[0]) === null || _a === void 0 ? void 0 : _a.attrs);
|
||
const mark = findMarkInSet([...start.node.marks], type, attributes);
|
||
if (!mark) {
|
||
return;
|
||
}
|
||
let startIndex = start.index;
|
||
let startPos = $pos.start() + start.offset;
|
||
let endIndex = startIndex + 1;
|
||
let endPos = startPos + start.node.nodeSize;
|
||
while (startIndex > 0 && isMarkInSet([...$pos.parent.child(startIndex - 1).marks], type, attributes)) {
|
||
startIndex -= 1;
|
||
startPos -= $pos.parent.child(startIndex).nodeSize;
|
||
}
|
||
while (endIndex < $pos.parent.childCount && isMarkInSet([...$pos.parent.child(endIndex).marks], type, attributes)) {
|
||
endPos += $pos.parent.child(endIndex).nodeSize;
|
||
endIndex += 1;
|
||
}
|
||
return {
|
||
from: startPos,
|
||
to: endPos
|
||
};
|
||
}
|
||
function getMarkType(nameOrType, schema) {
|
||
if (typeof nameOrType === "string") {
|
||
if (!schema.marks[nameOrType]) {
|
||
throw Error(`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`);
|
||
}
|
||
return schema.marks[nameOrType];
|
||
}
|
||
return nameOrType;
|
||
}
|
||
var extendMarkRange = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
||
const type = getMarkType(typeOrName, state.schema);
|
||
const { doc: doc2, selection } = tr;
|
||
const { $from, from, to } = selection;
|
||
if (dispatch) {
|
||
const range = getMarkRange($from, type, attributes);
|
||
if (range && range.from <= from && range.to >= to) {
|
||
const newSelection = TextSelection.create(doc2, range.from, range.to);
|
||
tr.setSelection(newSelection);
|
||
}
|
||
}
|
||
return true;
|
||
};
|
||
var first = (commands2) => (props) => {
|
||
const items = typeof commands2 === "function" ? commands2(props) : commands2;
|
||
for (let i = 0; i < items.length; i += 1) {
|
||
if (items[i](props)) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
};
|
||
function isTextSelection(value) {
|
||
return value instanceof TextSelection;
|
||
}
|
||
function minMax(value = 0, min = 0, max = 0) {
|
||
return Math.min(Math.max(value, min), max);
|
||
}
|
||
function resolveFocusPosition(doc2, position = null) {
|
||
if (!position) {
|
||
return null;
|
||
}
|
||
const selectionAtStart = Selection.atStart(doc2);
|
||
const selectionAtEnd = Selection.atEnd(doc2);
|
||
if (position === "start" || position === true) {
|
||
return selectionAtStart;
|
||
}
|
||
if (position === "end") {
|
||
return selectionAtEnd;
|
||
}
|
||
const minPos = selectionAtStart.from;
|
||
const maxPos = selectionAtEnd.to;
|
||
if (position === "all") {
|
||
return TextSelection.create(doc2, minMax(0, minPos, maxPos), minMax(doc2.content.size, minPos, maxPos));
|
||
}
|
||
return TextSelection.create(doc2, minMax(position, minPos, maxPos), minMax(position, minPos, maxPos));
|
||
}
|
||
function isiOS() {
|
||
return [
|
||
"iPad Simulator",
|
||
"iPhone Simulator",
|
||
"iPod Simulator",
|
||
"iPad",
|
||
"iPhone",
|
||
"iPod"
|
||
].includes(navigator.platform) || navigator.userAgent.includes("Mac") && "ontouchend" in document;
|
||
}
|
||
var focus = (position = null, options = {}) => ({ editor, view, tr, dispatch }) => {
|
||
options = {
|
||
scrollIntoView: true,
|
||
...options
|
||
};
|
||
const delayedFocus = () => {
|
||
if (isiOS()) {
|
||
view.dom.focus();
|
||
}
|
||
requestAnimationFrame(() => {
|
||
if (!editor.isDestroyed) {
|
||
view.focus();
|
||
if (options === null || options === void 0 ? void 0 : options.scrollIntoView) {
|
||
editor.commands.scrollIntoView();
|
||
}
|
||
}
|
||
});
|
||
};
|
||
if (view.hasFocus() && position === null || position === false) {
|
||
return true;
|
||
}
|
||
if (dispatch && position === null && !isTextSelection(editor.state.selection)) {
|
||
delayedFocus();
|
||
return true;
|
||
}
|
||
const selection = resolveFocusPosition(tr.doc, position) || editor.state.selection;
|
||
const isSameSelection = editor.state.selection.eq(selection);
|
||
if (dispatch) {
|
||
if (!isSameSelection) {
|
||
tr.setSelection(selection);
|
||
}
|
||
if (isSameSelection && tr.storedMarks) {
|
||
tr.setStoredMarks(tr.storedMarks);
|
||
}
|
||
delayedFocus();
|
||
}
|
||
return true;
|
||
};
|
||
var forEach = (items, fn) => (props) => {
|
||
return items.every((item, index2) => fn(item, { ...props, index: index2 }));
|
||
};
|
||
var insertContent = (value, options) => ({ tr, commands: commands2 }) => {
|
||
return commands2.insertContentAt({ from: tr.selection.from, to: tr.selection.to }, value, options);
|
||
};
|
||
var removeWhitespaces = (node) => {
|
||
const children = node.childNodes;
|
||
for (let i = children.length - 1; i >= 0; i -= 1) {
|
||
const child = children[i];
|
||
if (child.nodeType === 3 && child.nodeValue && /^(\n\s\s|\n)$/.test(child.nodeValue)) {
|
||
node.removeChild(child);
|
||
} else if (child.nodeType === 1) {
|
||
removeWhitespaces(child);
|
||
}
|
||
}
|
||
return node;
|
||
};
|
||
function elementFromString(value) {
|
||
const wrappedValue = `<body>${value}</body>`;
|
||
const html = new window.DOMParser().parseFromString(wrappedValue, "text/html").body;
|
||
return removeWhitespaces(html);
|
||
}
|
||
function createNodeFromContent(content, schema, options) {
|
||
if (content instanceof Node || content instanceof Fragment) {
|
||
return content;
|
||
}
|
||
options = {
|
||
slice: true,
|
||
parseOptions: {},
|
||
...options
|
||
};
|
||
const isJSONContent = typeof content === "object" && content !== null;
|
||
const isTextContent = typeof content === "string";
|
||
if (isJSONContent) {
|
||
try {
|
||
const isArrayContent = Array.isArray(content) && content.length > 0;
|
||
if (isArrayContent) {
|
||
return Fragment.fromArray(content.map((item) => schema.nodeFromJSON(item)));
|
||
}
|
||
const node = schema.nodeFromJSON(content);
|
||
if (options.errorOnInvalidContent) {
|
||
node.check();
|
||
}
|
||
return node;
|
||
} catch (error) {
|
||
if (options.errorOnInvalidContent) {
|
||
throw new Error("[tiptap error]: Invalid JSON content", { cause: error });
|
||
}
|
||
console.warn("[tiptap warn]: Invalid content.", "Passed value:", content, "Error:", error);
|
||
return createNodeFromContent("", schema, options);
|
||
}
|
||
}
|
||
if (isTextContent) {
|
||
if (options.errorOnInvalidContent) {
|
||
let hasInvalidContent = false;
|
||
let invalidContent = "";
|
||
const contentCheckSchema = new Schema({
|
||
topNode: schema.spec.topNode,
|
||
marks: schema.spec.marks,
|
||
// Prosemirror's schemas are executed such that: the last to execute, matches last
|
||
// This means that we can add a catch-all node at the end of the schema to catch any content that we don't know how to handle
|
||
nodes: schema.spec.nodes.append({
|
||
__tiptap__private__unknown__catch__all__node: {
|
||
content: "inline*",
|
||
group: "block",
|
||
parseDOM: [
|
||
{
|
||
tag: "*",
|
||
getAttrs: (e) => {
|
||
hasInvalidContent = true;
|
||
invalidContent = typeof e === "string" ? e : e.outerHTML;
|
||
return null;
|
||
}
|
||
}
|
||
]
|
||
}
|
||
})
|
||
});
|
||
if (options.slice) {
|
||
DOMParser.fromSchema(contentCheckSchema).parseSlice(elementFromString(content), options.parseOptions);
|
||
} else {
|
||
DOMParser.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions);
|
||
}
|
||
if (options.errorOnInvalidContent && hasInvalidContent) {
|
||
throw new Error("[tiptap error]: Invalid HTML content", { cause: new Error(`Invalid element found: ${invalidContent}`) });
|
||
}
|
||
}
|
||
const parser = DOMParser.fromSchema(schema);
|
||
if (options.slice) {
|
||
return parser.parseSlice(elementFromString(content), options.parseOptions).content;
|
||
}
|
||
return parser.parse(elementFromString(content), options.parseOptions);
|
||
}
|
||
return createNodeFromContent("", schema, options);
|
||
}
|
||
function selectionToInsertionEnd(tr, startLen, bias) {
|
||
const last = tr.steps.length - 1;
|
||
if (last < startLen) {
|
||
return;
|
||
}
|
||
const step = tr.steps[last];
|
||
if (!(step instanceof ReplaceStep || step instanceof ReplaceAroundStep)) {
|
||
return;
|
||
}
|
||
const map = tr.mapping.maps[last];
|
||
let end = 0;
|
||
map.forEach((_from, _to, _newFrom, newTo) => {
|
||
if (end === 0) {
|
||
end = newTo;
|
||
}
|
||
});
|
||
tr.setSelection(Selection.near(tr.doc.resolve(end), bias));
|
||
}
|
||
var isFragment = (nodeOrFragment) => {
|
||
return !("type" in nodeOrFragment);
|
||
};
|
||
var insertContentAt = (position, value, options) => ({ tr, dispatch, editor }) => {
|
||
var _a;
|
||
if (dispatch) {
|
||
options = {
|
||
parseOptions: editor.options.parseOptions,
|
||
updateSelection: true,
|
||
applyInputRules: false,
|
||
applyPasteRules: false,
|
||
...options
|
||
};
|
||
let content;
|
||
try {
|
||
content = createNodeFromContent(value, editor.schema, {
|
||
parseOptions: {
|
||
preserveWhitespace: "full",
|
||
...options.parseOptions
|
||
},
|
||
errorOnInvalidContent: (_a = options.errorOnInvalidContent) !== null && _a !== void 0 ? _a : editor.options.enableContentCheck
|
||
});
|
||
} catch (e) {
|
||
editor.emit("contentError", {
|
||
editor,
|
||
error: e,
|
||
disableCollaboration: () => {
|
||
if (editor.storage.collaboration) {
|
||
editor.storage.collaboration.isDisabled = true;
|
||
}
|
||
}
|
||
});
|
||
return false;
|
||
}
|
||
let { from, to } = typeof position === "number" ? { from: position, to: position } : { from: position.from, to: position.to };
|
||
let isOnlyTextContent = true;
|
||
let isOnlyBlockContent = true;
|
||
const nodes = isFragment(content) ? content : [content];
|
||
nodes.forEach((node) => {
|
||
node.check();
|
||
isOnlyTextContent = isOnlyTextContent ? node.isText && node.marks.length === 0 : false;
|
||
isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false;
|
||
});
|
||
if (from === to && isOnlyBlockContent) {
|
||
const { parent } = tr.doc.resolve(from);
|
||
const isEmptyTextBlock = parent.isTextblock && !parent.type.spec.code && !parent.childCount;
|
||
if (isEmptyTextBlock) {
|
||
from -= 1;
|
||
to += 1;
|
||
}
|
||
}
|
||
let newContent;
|
||
if (isOnlyTextContent) {
|
||
if (Array.isArray(value)) {
|
||
newContent = value.map((v) => v.text || "").join("");
|
||
} else if (value instanceof Fragment) {
|
||
let text = "";
|
||
value.forEach((node) => {
|
||
if (node.text) {
|
||
text += node.text;
|
||
}
|
||
});
|
||
newContent = text;
|
||
} else if (typeof value === "object" && !!value && !!value.text) {
|
||
newContent = value.text;
|
||
} else {
|
||
newContent = value;
|
||
}
|
||
tr.insertText(newContent, from, to);
|
||
} else {
|
||
newContent = content;
|
||
tr.replaceWith(from, to, newContent);
|
||
}
|
||
if (options.updateSelection) {
|
||
selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
|
||
}
|
||
if (options.applyInputRules) {
|
||
tr.setMeta("applyInputRules", { from, text: newContent });
|
||
}
|
||
if (options.applyPasteRules) {
|
||
tr.setMeta("applyPasteRules", { from, text: newContent });
|
||
}
|
||
}
|
||
return true;
|
||
};
|
||
var joinUp2 = () => ({ state, dispatch }) => {
|
||
return joinUp(state, dispatch);
|
||
};
|
||
var joinDown2 = () => ({ state, dispatch }) => {
|
||
return joinDown(state, dispatch);
|
||
};
|
||
var joinBackward2 = () => ({ state, dispatch }) => {
|
||
return joinBackward(state, dispatch);
|
||
};
|
||
var joinForward2 = () => ({ state, dispatch }) => {
|
||
return joinForward(state, dispatch);
|
||
};
|
||
var joinItemBackward = () => ({ state, dispatch, tr }) => {
|
||
try {
|
||
const point = joinPoint(state.doc, state.selection.$from.pos, -1);
|
||
if (point === null || point === void 0) {
|
||
return false;
|
||
}
|
||
tr.join(point, 2);
|
||
if (dispatch) {
|
||
dispatch(tr);
|
||
}
|
||
return true;
|
||
} catch (e) {
|
||
return false;
|
||
}
|
||
};
|
||
var joinItemForward = () => ({ state, dispatch, tr }) => {
|
||
try {
|
||
const point = joinPoint(state.doc, state.selection.$from.pos, 1);
|
||
if (point === null || point === void 0) {
|
||
return false;
|
||
}
|
||
tr.join(point, 2);
|
||
if (dispatch) {
|
||
dispatch(tr);
|
||
}
|
||
return true;
|
||
} catch (e) {
|
||
return false;
|
||
}
|
||
};
|
||
var joinTextblockBackward2 = () => ({ state, dispatch }) => {
|
||
return joinTextblockBackward(state, dispatch);
|
||
};
|
||
var joinTextblockForward2 = () => ({ state, dispatch }) => {
|
||
return joinTextblockForward(state, dispatch);
|
||
};
|
||
function isMacOS() {
|
||
return typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
|
||
}
|
||
function normalizeKeyName2(name) {
|
||
const parts = name.split(/-(?!$)/);
|
||
let result = parts[parts.length - 1];
|
||
if (result === "Space") {
|
||
result = " ";
|
||
}
|
||
let alt;
|
||
let ctrl;
|
||
let shift2;
|
||
let meta;
|
||
for (let i = 0; i < parts.length - 1; i += 1) {
|
||
const mod = parts[i];
|
||
if (/^(cmd|meta|m)$/i.test(mod)) {
|
||
meta = true;
|
||
} else if (/^a(lt)?$/i.test(mod)) {
|
||
alt = true;
|
||
} else if (/^(c|ctrl|control)$/i.test(mod)) {
|
||
ctrl = true;
|
||
} else if (/^s(hift)?$/i.test(mod)) {
|
||
shift2 = true;
|
||
} else if (/^mod$/i.test(mod)) {
|
||
if (isiOS() || isMacOS()) {
|
||
meta = true;
|
||
} else {
|
||
ctrl = true;
|
||
}
|
||
} else {
|
||
throw new Error(`Unrecognized modifier name: ${mod}`);
|
||
}
|
||
}
|
||
if (alt) {
|
||
result = `Alt-${result}`;
|
||
}
|
||
if (ctrl) {
|
||
result = `Ctrl-${result}`;
|
||
}
|
||
if (meta) {
|
||
result = `Meta-${result}`;
|
||
}
|
||
if (shift2) {
|
||
result = `Shift-${result}`;
|
||
}
|
||
return result;
|
||
}
|
||
var keyboardShortcut = (name) => ({ editor, view, tr, dispatch }) => {
|
||
const keys = normalizeKeyName2(name).split(/-(?!$)/);
|
||
const key = keys.find((item) => !["Alt", "Ctrl", "Meta", "Shift"].includes(item));
|
||
const event = new KeyboardEvent("keydown", {
|
||
key: key === "Space" ? " " : key,
|
||
altKey: keys.includes("Alt"),
|
||
ctrlKey: keys.includes("Ctrl"),
|
||
metaKey: keys.includes("Meta"),
|
||
shiftKey: keys.includes("Shift"),
|
||
bubbles: true,
|
||
cancelable: true
|
||
});
|
||
const capturedTransaction = editor.captureTransaction(() => {
|
||
view.someProp("handleKeyDown", (f) => f(view, event));
|
||
});
|
||
capturedTransaction === null || capturedTransaction === void 0 ? void 0 : capturedTransaction.steps.forEach((step) => {
|
||
const newStep = step.map(tr.mapping);
|
||
if (newStep && dispatch) {
|
||
tr.maybeStep(newStep);
|
||
}
|
||
});
|
||
return true;
|
||
};
|
||
function isNodeActive(state, typeOrName, attributes = {}) {
|
||
const { from, to, empty: empty2 } = state.selection;
|
||
const type = typeOrName ? getNodeType(typeOrName, state.schema) : null;
|
||
const nodeRanges = [];
|
||
state.doc.nodesBetween(from, to, (node, pos) => {
|
||
if (node.isText) {
|
||
return;
|
||
}
|
||
const relativeFrom = Math.max(from, pos);
|
||
const relativeTo = Math.min(to, pos + node.nodeSize);
|
||
nodeRanges.push({
|
||
node,
|
||
from: relativeFrom,
|
||
to: relativeTo
|
||
});
|
||
});
|
||
const selectionRange = to - from;
|
||
const matchedNodeRanges = nodeRanges.filter((nodeRange) => {
|
||
if (!type) {
|
||
return true;
|
||
}
|
||
return type.name === nodeRange.node.type.name;
|
||
}).filter((nodeRange) => objectIncludes(nodeRange.node.attrs, attributes, { strict: false }));
|
||
if (empty2) {
|
||
return !!matchedNodeRanges.length;
|
||
}
|
||
const range = matchedNodeRanges.reduce((sum, nodeRange) => sum + nodeRange.to - nodeRange.from, 0);
|
||
return range >= selectionRange;
|
||
}
|
||
var lift2 = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
||
const type = getNodeType(typeOrName, state.schema);
|
||
const isActive2 = isNodeActive(state, type, attributes);
|
||
if (!isActive2) {
|
||
return false;
|
||
}
|
||
return lift(state, dispatch);
|
||
};
|
||
var liftEmptyBlock2 = () => ({ state, dispatch }) => {
|
||
return liftEmptyBlock(state, dispatch);
|
||
};
|
||
var liftListItem2 = (typeOrName) => ({ state, dispatch }) => {
|
||
const type = getNodeType(typeOrName, state.schema);
|
||
return liftListItem(type)(state, dispatch);
|
||
};
|
||
var newlineInCode2 = () => ({ state, dispatch }) => {
|
||
return newlineInCode(state, dispatch);
|
||
};
|
||
function getSchemaTypeNameByName(name, schema) {
|
||
if (schema.nodes[name]) {
|
||
return "node";
|
||
}
|
||
if (schema.marks[name]) {
|
||
return "mark";
|
||
}
|
||
return null;
|
||
}
|
||
function deleteProps(obj, propOrProps) {
|
||
const props = typeof propOrProps === "string" ? [propOrProps] : propOrProps;
|
||
return Object.keys(obj).reduce((newObj, prop) => {
|
||
if (!props.includes(prop)) {
|
||
newObj[prop] = obj[prop];
|
||
}
|
||
return newObj;
|
||
}, {});
|
||
}
|
||
var resetAttributes = (typeOrName, attributes) => ({ tr, state, dispatch }) => {
|
||
let nodeType = null;
|
||
let markType = null;
|
||
const schemaType = getSchemaTypeNameByName(typeof typeOrName === "string" ? typeOrName : typeOrName.name, state.schema);
|
||
if (!schemaType) {
|
||
return false;
|
||
}
|
||
if (schemaType === "node") {
|
||
nodeType = getNodeType(typeOrName, state.schema);
|
||
}
|
||
if (schemaType === "mark") {
|
||
markType = getMarkType(typeOrName, state.schema);
|
||
}
|
||
if (dispatch) {
|
||
tr.selection.ranges.forEach((range) => {
|
||
state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
|
||
if (nodeType && nodeType === node.type) {
|
||
tr.setNodeMarkup(pos, void 0, deleteProps(node.attrs, attributes));
|
||
}
|
||
if (markType && node.marks.length) {
|
||
node.marks.forEach((mark) => {
|
||
if (markType === mark.type) {
|
||
tr.addMark(pos, pos + node.nodeSize, markType.create(deleteProps(mark.attrs, attributes)));
|
||
}
|
||
});
|
||
}
|
||
});
|
||
});
|
||
}
|
||
return true;
|
||
};
|
||
var scrollIntoView = () => ({ tr, dispatch }) => {
|
||
if (dispatch) {
|
||
tr.scrollIntoView();
|
||
}
|
||
return true;
|
||
};
|
||
var selectAll2 = () => ({ tr, commands: commands2 }) => {
|
||
return commands2.setTextSelection({
|
||
from: 0,
|
||
to: tr.doc.content.size
|
||
});
|
||
};
|
||
var selectNodeBackward2 = () => ({ state, dispatch }) => {
|
||
return selectNodeBackward(state, dispatch);
|
||
};
|
||
var selectNodeForward2 = () => ({ state, dispatch }) => {
|
||
return selectNodeForward(state, dispatch);
|
||
};
|
||
var selectParentNode2 = () => ({ state, dispatch }) => {
|
||
return selectParentNode(state, dispatch);
|
||
};
|
||
var selectTextblockEnd2 = () => ({ state, dispatch }) => {
|
||
return selectTextblockEnd(state, dispatch);
|
||
};
|
||
var selectTextblockStart2 = () => ({ state, dispatch }) => {
|
||
return selectTextblockStart(state, dispatch);
|
||
};
|
||
function createDocument(content, schema, parseOptions = {}, options = {}) {
|
||
return createNodeFromContent(content, schema, {
|
||
slice: false,
|
||
parseOptions,
|
||
errorOnInvalidContent: options.errorOnInvalidContent
|
||
});
|
||
}
|
||
var setContent = (content, emitUpdate = false, parseOptions = {}, options = {}) => ({ editor, tr, dispatch, commands: commands2 }) => {
|
||
var _a, _b;
|
||
const { doc: doc2 } = tr;
|
||
if (parseOptions.preserveWhitespace !== "full") {
|
||
const document2 = createDocument(content, editor.schema, parseOptions, {
|
||
errorOnInvalidContent: (_a = options.errorOnInvalidContent) !== null && _a !== void 0 ? _a : editor.options.enableContentCheck
|
||
});
|
||
if (dispatch) {
|
||
tr.replaceWith(0, doc2.content.size, document2).setMeta("preventUpdate", !emitUpdate);
|
||
}
|
||
return true;
|
||
}
|
||
if (dispatch) {
|
||
tr.setMeta("preventUpdate", !emitUpdate);
|
||
}
|
||
return commands2.insertContentAt({ from: 0, to: doc2.content.size }, content, {
|
||
parseOptions,
|
||
errorOnInvalidContent: (_b = options.errorOnInvalidContent) !== null && _b !== void 0 ? _b : editor.options.enableContentCheck
|
||
});
|
||
};
|
||
function getMarkAttributes(state, typeOrName) {
|
||
const type = getMarkType(typeOrName, state.schema);
|
||
const { from, to, empty: empty2 } = state.selection;
|
||
const marks = [];
|
||
if (empty2) {
|
||
if (state.storedMarks) {
|
||
marks.push(...state.storedMarks);
|
||
}
|
||
marks.push(...state.selection.$head.marks());
|
||
} else {
|
||
state.doc.nodesBetween(from, to, (node) => {
|
||
marks.push(...node.marks);
|
||
});
|
||
}
|
||
const mark = marks.find((markItem) => markItem.type.name === type.name);
|
||
if (!mark) {
|
||
return {};
|
||
}
|
||
return { ...mark.attrs };
|
||
}
|
||
function combineTransactionSteps(oldDoc, transactions) {
|
||
const transform = new Transform(oldDoc);
|
||
transactions.forEach((transaction) => {
|
||
transaction.steps.forEach((step) => {
|
||
transform.step(step);
|
||
});
|
||
});
|
||
return transform;
|
||
}
|
||
function defaultBlockAt2(match) {
|
||
for (let i = 0; i < match.edgeCount; i += 1) {
|
||
const { type } = match.edge(i);
|
||
if (type.isTextblock && !type.hasRequiredAttrs()) {
|
||
return type;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function findChildren(node, predicate) {
|
||
const nodesWithPos = [];
|
||
node.descendants((child, pos) => {
|
||
if (predicate(child)) {
|
||
nodesWithPos.push({
|
||
node: child,
|
||
pos
|
||
});
|
||
}
|
||
});
|
||
return nodesWithPos;
|
||
}
|
||
function findChildrenInRange(node, range, predicate) {
|
||
const nodesWithPos = [];
|
||
node.nodesBetween(range.from, range.to, (child, pos) => {
|
||
if (predicate(child)) {
|
||
nodesWithPos.push({
|
||
node: child,
|
||
pos
|
||
});
|
||
}
|
||
});
|
||
return nodesWithPos;
|
||
}
|
||
function findParentNodeClosestToPos($pos, predicate) {
|
||
for (let i = $pos.depth; i > 0; i -= 1) {
|
||
const node = $pos.node(i);
|
||
if (predicate(node)) {
|
||
return {
|
||
pos: i > 0 ? $pos.before(i) : 0,
|
||
start: $pos.start(i),
|
||
depth: i,
|
||
node
|
||
};
|
||
}
|
||
}
|
||
}
|
||
function findParentNode(predicate) {
|
||
return (selection) => findParentNodeClosestToPos(selection.$from, predicate);
|
||
}
|
||
function getSchema(extensions, editor) {
|
||
const resolvedExtensions = ExtensionManager.resolve(extensions);
|
||
return getSchemaByResolvedExtensions(resolvedExtensions, editor);
|
||
}
|
||
function generateHTML(doc2, extensions) {
|
||
const schema = getSchema(extensions);
|
||
const contentNode = Node.fromJSON(schema, doc2);
|
||
return getHTMLFromFragment(contentNode.content, schema);
|
||
}
|
||
function generateJSON(html, extensions) {
|
||
const schema = getSchema(extensions);
|
||
const dom = elementFromString(html);
|
||
return DOMParser.fromSchema(schema).parse(dom).toJSON();
|
||
}
|
||
function getText2(node, options) {
|
||
const range = {
|
||
from: 0,
|
||
to: node.content.size
|
||
};
|
||
return getTextBetween(node, range, options);
|
||
}
|
||
function generateText(doc2, extensions, options) {
|
||
const { blockSeparator = "\n\n", textSerializers = {} } = options || {};
|
||
const schema = getSchema(extensions);
|
||
const contentNode = Node.fromJSON(schema, doc2);
|
||
return getText2(contentNode, {
|
||
blockSeparator,
|
||
textSerializers: {
|
||
...getTextSerializersFromSchema(schema),
|
||
...textSerializers
|
||
}
|
||
});
|
||
}
|
||
function getNodeAttributes(state, typeOrName) {
|
||
const type = getNodeType(typeOrName, state.schema);
|
||
const { from, to } = state.selection;
|
||
const nodes = [];
|
||
state.doc.nodesBetween(from, to, (node2) => {
|
||
nodes.push(node2);
|
||
});
|
||
const node = nodes.reverse().find((nodeItem) => nodeItem.type.name === type.name);
|
||
if (!node) {
|
||
return {};
|
||
}
|
||
return { ...node.attrs };
|
||
}
|
||
function getAttributes(state, typeOrName) {
|
||
const schemaType = getSchemaTypeNameByName(typeof typeOrName === "string" ? typeOrName : typeOrName.name, state.schema);
|
||
if (schemaType === "node") {
|
||
return getNodeAttributes(state, typeOrName);
|
||
}
|
||
if (schemaType === "mark") {
|
||
return getMarkAttributes(state, typeOrName);
|
||
}
|
||
return {};
|
||
}
|
||
function removeDuplicates(array, by = JSON.stringify) {
|
||
const seen = {};
|
||
return array.filter((item) => {
|
||
const key = by(item);
|
||
return Object.prototype.hasOwnProperty.call(seen, key) ? false : seen[key] = true;
|
||
});
|
||
}
|
||
function simplifyChangedRanges(changes) {
|
||
const uniqueChanges = removeDuplicates(changes);
|
||
return uniqueChanges.length === 1 ? uniqueChanges : uniqueChanges.filter((change, index2) => {
|
||
const rest = uniqueChanges.filter((_, i) => i !== index2);
|
||
return !rest.some((otherChange) => {
|
||
return change.oldRange.from >= otherChange.oldRange.from && change.oldRange.to <= otherChange.oldRange.to && change.newRange.from >= otherChange.newRange.from && change.newRange.to <= otherChange.newRange.to;
|
||
});
|
||
});
|
||
}
|
||
function getChangedRanges(transform) {
|
||
const { mapping, steps } = transform;
|
||
const changes = [];
|
||
mapping.maps.forEach((stepMap, index2) => {
|
||
const ranges = [];
|
||
if (!stepMap.ranges.length) {
|
||
const { from, to } = steps[index2];
|
||
if (from === void 0 || to === void 0) {
|
||
return;
|
||
}
|
||
ranges.push({ from, to });
|
||
} else {
|
||
stepMap.forEach((from, to) => {
|
||
ranges.push({ from, to });
|
||
});
|
||
}
|
||
ranges.forEach(({ from, to }) => {
|
||
const newStart = mapping.slice(index2).map(from, -1);
|
||
const newEnd = mapping.slice(index2).map(to);
|
||
const oldStart = mapping.invert().map(newStart, -1);
|
||
const oldEnd = mapping.invert().map(newEnd);
|
||
changes.push({
|
||
oldRange: {
|
||
from: oldStart,
|
||
to: oldEnd
|
||
},
|
||
newRange: {
|
||
from: newStart,
|
||
to: newEnd
|
||
}
|
||
});
|
||
});
|
||
});
|
||
return simplifyChangedRanges(changes);
|
||
}
|
||
function getDebugJSON(node, startOffset = 0) {
|
||
const isTopNode = node.type === node.type.schema.topNodeType;
|
||
const increment = isTopNode ? 0 : 1;
|
||
const from = startOffset;
|
||
const to = from + node.nodeSize;
|
||
const marks = node.marks.map((mark) => {
|
||
const output2 = {
|
||
type: mark.type.name
|
||
};
|
||
if (Object.keys(mark.attrs).length) {
|
||
output2.attrs = { ...mark.attrs };
|
||
}
|
||
return output2;
|
||
});
|
||
const attrs = { ...node.attrs };
|
||
const output = {
|
||
type: node.type.name,
|
||
from,
|
||
to
|
||
};
|
||
if (Object.keys(attrs).length) {
|
||
output.attrs = attrs;
|
||
}
|
||
if (marks.length) {
|
||
output.marks = marks;
|
||
}
|
||
if (node.content.childCount) {
|
||
output.content = [];
|
||
node.forEach((child, offset) => {
|
||
var _a;
|
||
(_a = output.content) === null || _a === void 0 ? void 0 : _a.push(getDebugJSON(child, startOffset + offset + increment));
|
||
});
|
||
}
|
||
if (node.text) {
|
||
output.text = node.text;
|
||
}
|
||
return output;
|
||
}
|
||
function getMarksBetween(from, to, doc2) {
|
||
const marks = [];
|
||
if (from === to) {
|
||
doc2.resolve(from).marks().forEach((mark) => {
|
||
const $pos = doc2.resolve(from);
|
||
const range = getMarkRange($pos, mark.type);
|
||
if (!range) {
|
||
return;
|
||
}
|
||
marks.push({
|
||
mark,
|
||
...range
|
||
});
|
||
});
|
||
} else {
|
||
doc2.nodesBetween(from, to, (node, pos) => {
|
||
if (!node || (node === null || node === void 0 ? void 0 : node.nodeSize) === void 0) {
|
||
return;
|
||
}
|
||
marks.push(...node.marks.map((mark) => ({
|
||
from: pos,
|
||
to: pos + node.nodeSize,
|
||
mark
|
||
})));
|
||
});
|
||
}
|
||
return marks;
|
||
}
|
||
var getNodeAtPosition = (state, typeOrName, pos, maxDepth = 20) => {
|
||
const $pos = state.doc.resolve(pos);
|
||
let currentDepth = maxDepth;
|
||
let node = null;
|
||
while (currentDepth > 0 && node === null) {
|
||
const currentNode = $pos.node(currentDepth);
|
||
if ((currentNode === null || currentNode === void 0 ? void 0 : currentNode.type.name) === typeOrName) {
|
||
node = currentNode;
|
||
} else {
|
||
currentDepth -= 1;
|
||
}
|
||
}
|
||
return [node, currentDepth];
|
||
};
|
||
function getSplittedAttributes(extensionAttributes, typeName, attributes) {
|
||
return Object.fromEntries(Object.entries(attributes).filter(([name]) => {
|
||
const extensionAttribute = extensionAttributes.find((item) => {
|
||
return item.type === typeName && item.name === name;
|
||
});
|
||
if (!extensionAttribute) {
|
||
return false;
|
||
}
|
||
return extensionAttribute.attribute.keepOnSplit;
|
||
}));
|
||
}
|
||
function isMarkActive(state, typeOrName, attributes = {}) {
|
||
const { empty: empty2, ranges } = state.selection;
|
||
const type = typeOrName ? getMarkType(typeOrName, state.schema) : null;
|
||
if (empty2) {
|
||
return !!(state.storedMarks || state.selection.$from.marks()).filter((mark) => {
|
||
if (!type) {
|
||
return true;
|
||
}
|
||
return type.name === mark.type.name;
|
||
}).find((mark) => objectIncludes(mark.attrs, attributes, { strict: false }));
|
||
}
|
||
let selectionRange = 0;
|
||
const markRanges = [];
|
||
ranges.forEach(({ $from, $to }) => {
|
||
const from = $from.pos;
|
||
const to = $to.pos;
|
||
state.doc.nodesBetween(from, to, (node, pos) => {
|
||
if (!node.isText && !node.marks.length) {
|
||
return;
|
||
}
|
||
const relativeFrom = Math.max(from, pos);
|
||
const relativeTo = Math.min(to, pos + node.nodeSize);
|
||
const range2 = relativeTo - relativeFrom;
|
||
selectionRange += range2;
|
||
markRanges.push(...node.marks.map((mark) => ({
|
||
mark,
|
||
from: relativeFrom,
|
||
to: relativeTo
|
||
})));
|
||
});
|
||
});
|
||
if (selectionRange === 0) {
|
||
return false;
|
||
}
|
||
const matchedRange = markRanges.filter((markRange) => {
|
||
if (!type) {
|
||
return true;
|
||
}
|
||
return type.name === markRange.mark.type.name;
|
||
}).filter((markRange) => objectIncludes(markRange.mark.attrs, attributes, { strict: false })).reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
|
||
const excludedRange = markRanges.filter((markRange) => {
|
||
if (!type) {
|
||
return true;
|
||
}
|
||
return markRange.mark.type !== type && markRange.mark.type.excludes(type);
|
||
}).reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
|
||
const range = matchedRange > 0 ? matchedRange + excludedRange : matchedRange;
|
||
return range >= selectionRange;
|
||
}
|
||
function isActive(state, name, attributes = {}) {
|
||
if (!name) {
|
||
return isNodeActive(state, null, attributes) || isMarkActive(state, null, attributes);
|
||
}
|
||
const schemaType = getSchemaTypeNameByName(name, state.schema);
|
||
if (schemaType === "node") {
|
||
return isNodeActive(state, name, attributes);
|
||
}
|
||
if (schemaType === "mark") {
|
||
return isMarkActive(state, name, attributes);
|
||
}
|
||
return false;
|
||
}
|
||
var isAtEndOfNode = (state, nodeType) => {
|
||
const { $from, $to, $anchor } = state.selection;
|
||
if (nodeType) {
|
||
const parentNode2 = findParentNode((node) => node.type.name === nodeType)(state.selection);
|
||
if (!parentNode2) {
|
||
return false;
|
||
}
|
||
const $parentPos = state.doc.resolve(parentNode2.pos + 1);
|
||
if ($anchor.pos + 1 === $parentPos.end()) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
if ($to.parentOffset < $to.parent.nodeSize - 2 || $from.pos !== $to.pos) {
|
||
return false;
|
||
}
|
||
return true;
|
||
};
|
||
var isAtStartOfNode = (state) => {
|
||
const { $from, $to } = state.selection;
|
||
if ($from.parentOffset > 0 || $from.pos !== $to.pos) {
|
||
return false;
|
||
}
|
||
return true;
|
||
};
|
||
function isList(name, extensions) {
|
||
const { nodeExtensions } = splitExtensions(extensions);
|
||
const extension = nodeExtensions.find((item) => item.name === name);
|
||
if (!extension) {
|
||
return false;
|
||
}
|
||
const context = {
|
||
name: extension.name,
|
||
options: extension.options,
|
||
storage: extension.storage
|
||
};
|
||
const group = callOrReturn(getExtensionField(extension, "group", context));
|
||
if (typeof group !== "string") {
|
||
return false;
|
||
}
|
||
return group.split(" ").includes("list");
|
||
}
|
||
function isNodeEmpty(node, { checkChildren = true, ignoreWhitespace = false } = {}) {
|
||
var _a;
|
||
if (ignoreWhitespace) {
|
||
if (node.type.name === "hardBreak") {
|
||
return true;
|
||
}
|
||
if (node.isText) {
|
||
return /^\s*$/m.test((_a = node.text) !== null && _a !== void 0 ? _a : "");
|
||
}
|
||
}
|
||
if (node.isText) {
|
||
return !node.text;
|
||
}
|
||
if (node.isAtom || node.isLeaf) {
|
||
return false;
|
||
}
|
||
if (node.content.childCount === 0) {
|
||
return true;
|
||
}
|
||
if (checkChildren) {
|
||
let isContentEmpty = true;
|
||
node.content.forEach((childNode) => {
|
||
if (isContentEmpty === false) {
|
||
return;
|
||
}
|
||
if (!isNodeEmpty(childNode, { ignoreWhitespace, checkChildren })) {
|
||
isContentEmpty = false;
|
||
}
|
||
});
|
||
return isContentEmpty;
|
||
}
|
||
return false;
|
||
}
|
||
function isNodeSelection(value) {
|
||
return value instanceof NodeSelection;
|
||
}
|
||
function posToDOMRect(view, from, to) {
|
||
const minPos = 0;
|
||
const maxPos = view.state.doc.content.size;
|
||
const resolvedFrom = minMax(from, minPos, maxPos);
|
||
const resolvedEnd = minMax(to, minPos, maxPos);
|
||
const start = view.coordsAtPos(resolvedFrom);
|
||
const end = view.coordsAtPos(resolvedEnd, -1);
|
||
const top = Math.min(start.top, end.top);
|
||
const bottom = Math.max(start.bottom, end.bottom);
|
||
const left = Math.min(start.left, end.left);
|
||
const right = Math.max(start.right, end.right);
|
||
const width = right - left;
|
||
const height = bottom - top;
|
||
const x = left;
|
||
const y = top;
|
||
const data = {
|
||
top,
|
||
bottom,
|
||
left,
|
||
right,
|
||
width,
|
||
height,
|
||
x,
|
||
y
|
||
};
|
||
return {
|
||
...data,
|
||
toJSON: () => data
|
||
};
|
||
}
|
||
function canSetMark(state, tr, newMarkType) {
|
||
var _a;
|
||
const { selection } = tr;
|
||
let cursor = null;
|
||
if (isTextSelection(selection)) {
|
||
cursor = selection.$cursor;
|
||
}
|
||
if (cursor) {
|
||
const currentMarks = (_a = state.storedMarks) !== null && _a !== void 0 ? _a : cursor.marks();
|
||
return !!newMarkType.isInSet(currentMarks) || !currentMarks.some((mark) => mark.type.excludes(newMarkType));
|
||
}
|
||
const { ranges } = selection;
|
||
return ranges.some(({ $from, $to }) => {
|
||
let someNodeSupportsMark = $from.depth === 0 ? state.doc.inlineContent && state.doc.type.allowsMarkType(newMarkType) : false;
|
||
state.doc.nodesBetween($from.pos, $to.pos, (node, _pos, parent) => {
|
||
if (someNodeSupportsMark) {
|
||
return false;
|
||
}
|
||
if (node.isInline) {
|
||
const parentAllowsMarkType = !parent || parent.type.allowsMarkType(newMarkType);
|
||
const currentMarksAllowMarkType = !!newMarkType.isInSet(node.marks) || !node.marks.some((otherMark) => otherMark.type.excludes(newMarkType));
|
||
someNodeSupportsMark = parentAllowsMarkType && currentMarksAllowMarkType;
|
||
}
|
||
return !someNodeSupportsMark;
|
||
});
|
||
return someNodeSupportsMark;
|
||
});
|
||
}
|
||
var setMark = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
||
const { selection } = tr;
|
||
const { empty: empty2, ranges } = selection;
|
||
const type = getMarkType(typeOrName, state.schema);
|
||
if (dispatch) {
|
||
if (empty2) {
|
||
const oldAttributes = getMarkAttributes(state, type);
|
||
tr.addStoredMark(type.create({
|
||
...oldAttributes,
|
||
...attributes
|
||
}));
|
||
} else {
|
||
ranges.forEach((range) => {
|
||
const from = range.$from.pos;
|
||
const to = range.$to.pos;
|
||
state.doc.nodesBetween(from, to, (node, pos) => {
|
||
const trimmedFrom = Math.max(pos, from);
|
||
const trimmedTo = Math.min(pos + node.nodeSize, to);
|
||
const someHasMark = node.marks.find((mark) => mark.type === type);
|
||
if (someHasMark) {
|
||
node.marks.forEach((mark) => {
|
||
if (type === mark.type) {
|
||
tr.addMark(trimmedFrom, trimmedTo, type.create({
|
||
...mark.attrs,
|
||
...attributes
|
||
}));
|
||
}
|
||
});
|
||
} else {
|
||
tr.addMark(trimmedFrom, trimmedTo, type.create(attributes));
|
||
}
|
||
});
|
||
});
|
||
}
|
||
}
|
||
return canSetMark(state, tr, type);
|
||
};
|
||
var setMeta = (key, value) => ({ tr }) => {
|
||
tr.setMeta(key, value);
|
||
return true;
|
||
};
|
||
var setNode = (typeOrName, attributes = {}) => ({ state, dispatch, chain }) => {
|
||
const type = getNodeType(typeOrName, state.schema);
|
||
let attributesToCopy;
|
||
if (state.selection.$anchor.sameParent(state.selection.$head)) {
|
||
attributesToCopy = state.selection.$anchor.parent.attrs;
|
||
}
|
||
if (!type.isTextblock) {
|
||
console.warn('[tiptap warn]: Currently "setNode()" only supports text block nodes.');
|
||
return false;
|
||
}
|
||
return chain().command(({ commands: commands2 }) => {
|
||
const canSetBlock = setBlockType(type, { ...attributesToCopy, ...attributes })(state);
|
||
if (canSetBlock) {
|
||
return true;
|
||
}
|
||
return commands2.clearNodes();
|
||
}).command(({ state: updatedState }) => {
|
||
return setBlockType(type, { ...attributesToCopy, ...attributes })(updatedState, dispatch);
|
||
}).run();
|
||
};
|
||
var setNodeSelection = (position) => ({ tr, dispatch }) => {
|
||
if (dispatch) {
|
||
const { doc: doc2 } = tr;
|
||
const from = minMax(position, 0, doc2.content.size);
|
||
const selection = NodeSelection.create(doc2, from);
|
||
tr.setSelection(selection);
|
||
}
|
||
return true;
|
||
};
|
||
var setTextSelection = (position) => ({ tr, dispatch }) => {
|
||
if (dispatch) {
|
||
const { doc: doc2 } = tr;
|
||
const { from, to } = typeof position === "number" ? { from: position, to: position } : position;
|
||
const minPos = TextSelection.atStart(doc2).from;
|
||
const maxPos = TextSelection.atEnd(doc2).to;
|
||
const resolvedFrom = minMax(from, minPos, maxPos);
|
||
const resolvedEnd = minMax(to, minPos, maxPos);
|
||
const selection = TextSelection.create(doc2, resolvedFrom, resolvedEnd);
|
||
tr.setSelection(selection);
|
||
}
|
||
return true;
|
||
};
|
||
var sinkListItem2 = (typeOrName) => ({ state, dispatch }) => {
|
||
const type = getNodeType(typeOrName, state.schema);
|
||
return sinkListItem(type)(state, dispatch);
|
||
};
|
||
function ensureMarks(state, splittableMarks) {
|
||
const marks = state.storedMarks || state.selection.$to.parentOffset && state.selection.$from.marks();
|
||
if (marks) {
|
||
const filteredMarks = marks.filter((mark) => splittableMarks === null || splittableMarks === void 0 ? void 0 : splittableMarks.includes(mark.type.name));
|
||
state.tr.ensureMarks(filteredMarks);
|
||
}
|
||
}
|
||
var splitBlock2 = ({ keepMarks = true } = {}) => ({ tr, state, dispatch, editor }) => {
|
||
const { selection, doc: doc2 } = tr;
|
||
const { $from, $to } = selection;
|
||
const extensionAttributes = editor.extensionManager.attributes;
|
||
const newAttributes = getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs);
|
||
if (selection instanceof NodeSelection && selection.node.isBlock) {
|
||
if (!$from.parentOffset || !canSplit(doc2, $from.pos)) {
|
||
return false;
|
||
}
|
||
if (dispatch) {
|
||
if (keepMarks) {
|
||
ensureMarks(state, editor.extensionManager.splittableMarks);
|
||
}
|
||
tr.split($from.pos).scrollIntoView();
|
||
}
|
||
return true;
|
||
}
|
||
if (!$from.parent.isBlock) {
|
||
return false;
|
||
}
|
||
const atEnd = $to.parentOffset === $to.parent.content.size;
|
||
const deflt = $from.depth === 0 ? void 0 : defaultBlockAt2($from.node(-1).contentMatchAt($from.indexAfter(-1)));
|
||
let types = atEnd && deflt ? [
|
||
{
|
||
type: deflt,
|
||
attrs: newAttributes
|
||
}
|
||
] : void 0;
|
||
let can = canSplit(tr.doc, tr.mapping.map($from.pos), 1, types);
|
||
if (!types && !can && canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : void 0)) {
|
||
can = true;
|
||
types = deflt ? [
|
||
{
|
||
type: deflt,
|
||
attrs: newAttributes
|
||
}
|
||
] : void 0;
|
||
}
|
||
if (dispatch) {
|
||
if (can) {
|
||
if (selection instanceof TextSelection) {
|
||
tr.deleteSelection();
|
||
}
|
||
tr.split(tr.mapping.map($from.pos), 1, types);
|
||
if (deflt && !atEnd && !$from.parentOffset && $from.parent.type !== deflt) {
|
||
const first2 = tr.mapping.map($from.before());
|
||
const $first = tr.doc.resolve(first2);
|
||
if ($from.node(-1).canReplaceWith($first.index(), $first.index() + 1, deflt)) {
|
||
tr.setNodeMarkup(tr.mapping.map($from.before()), deflt);
|
||
}
|
||
}
|
||
}
|
||
if (keepMarks) {
|
||
ensureMarks(state, editor.extensionManager.splittableMarks);
|
||
}
|
||
tr.scrollIntoView();
|
||
}
|
||
return can;
|
||
};
|
||
var splitListItem = (typeOrName, overrideAttrs = {}) => ({ tr, state, dispatch, editor }) => {
|
||
var _a;
|
||
const type = getNodeType(typeOrName, state.schema);
|
||
const { $from, $to } = state.selection;
|
||
const node = state.selection.node;
|
||
if (node && node.isBlock || $from.depth < 2 || !$from.sameParent($to)) {
|
||
return false;
|
||
}
|
||
const grandParent = $from.node(-1);
|
||
if (grandParent.type !== type) {
|
||
return false;
|
||
}
|
||
const extensionAttributes = editor.extensionManager.attributes;
|
||
if ($from.parent.content.size === 0 && $from.node(-1).childCount === $from.indexAfter(-1)) {
|
||
if ($from.depth === 2 || $from.node(-3).type !== type || $from.index(-2) !== $from.node(-2).childCount - 1) {
|
||
return false;
|
||
}
|
||
if (dispatch) {
|
||
let wrap = Fragment.empty;
|
||
const depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3;
|
||
for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d -= 1) {
|
||
wrap = Fragment.from($from.node(d).copy(wrap));
|
||
}
|
||
const depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount ? 1 : $from.indexAfter(-2) < $from.node(-3).childCount ? 2 : 3;
|
||
const newNextTypeAttributes2 = {
|
||
...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),
|
||
...overrideAttrs
|
||
};
|
||
const nextType2 = ((_a = type.contentMatch.defaultType) === null || _a === void 0 ? void 0 : _a.createAndFill(newNextTypeAttributes2)) || void 0;
|
||
wrap = wrap.append(Fragment.from(type.createAndFill(null, nextType2) || void 0));
|
||
const start = $from.before($from.depth - (depthBefore - 1));
|
||
tr.replace(start, $from.after(-depthAfter), new Slice(wrap, 4 - depthBefore, 0));
|
||
let sel = -1;
|
||
tr.doc.nodesBetween(start, tr.doc.content.size, (n, pos) => {
|
||
if (sel > -1) {
|
||
return false;
|
||
}
|
||
if (n.isTextblock && n.content.size === 0) {
|
||
sel = pos + 1;
|
||
}
|
||
});
|
||
if (sel > -1) {
|
||
tr.setSelection(TextSelection.near(tr.doc.resolve(sel)));
|
||
}
|
||
tr.scrollIntoView();
|
||
}
|
||
return true;
|
||
}
|
||
const nextType = $to.pos === $from.end() ? grandParent.contentMatchAt(0).defaultType : null;
|
||
const newTypeAttributes = {
|
||
...getSplittedAttributes(extensionAttributes, grandParent.type.name, grandParent.attrs),
|
||
...overrideAttrs
|
||
};
|
||
const newNextTypeAttributes = {
|
||
...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),
|
||
...overrideAttrs
|
||
};
|
||
tr.delete($from.pos, $to.pos);
|
||
const types = nextType ? [
|
||
{ type, attrs: newTypeAttributes },
|
||
{ type: nextType, attrs: newNextTypeAttributes }
|
||
] : [{ type, attrs: newTypeAttributes }];
|
||
if (!canSplit(tr.doc, $from.pos, 2)) {
|
||
return false;
|
||
}
|
||
if (dispatch) {
|
||
const { selection, storedMarks } = state;
|
||
const { splittableMarks } = editor.extensionManager;
|
||
const marks = storedMarks || selection.$to.parentOffset && selection.$from.marks();
|
||
tr.split($from.pos, 2, types).scrollIntoView();
|
||
if (!marks || !dispatch) {
|
||
return true;
|
||
}
|
||
const filteredMarks = marks.filter((mark) => splittableMarks.includes(mark.type.name));
|
||
tr.ensureMarks(filteredMarks);
|
||
}
|
||
return true;
|
||
};
|
||
var joinListBackwards = (tr, listType) => {
|
||
const list = findParentNode((node) => node.type === listType)(tr.selection);
|
||
if (!list) {
|
||
return true;
|
||
}
|
||
const before = tr.doc.resolve(Math.max(0, list.pos - 1)).before(list.depth);
|
||
if (before === void 0) {
|
||
return true;
|
||
}
|
||
const nodeBefore = tr.doc.nodeAt(before);
|
||
const canJoinBackwards = list.node.type === (nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.type) && canJoin(tr.doc, list.pos);
|
||
if (!canJoinBackwards) {
|
||
return true;
|
||
}
|
||
tr.join(list.pos);
|
||
return true;
|
||
};
|
||
var joinListForwards = (tr, listType) => {
|
||
const list = findParentNode((node) => node.type === listType)(tr.selection);
|
||
if (!list) {
|
||
return true;
|
||
}
|
||
const after = tr.doc.resolve(list.start).after(list.depth);
|
||
if (after === void 0) {
|
||
return true;
|
||
}
|
||
const nodeAfter = tr.doc.nodeAt(after);
|
||
const canJoinForwards = list.node.type === (nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.type) && canJoin(tr.doc, after);
|
||
if (!canJoinForwards) {
|
||
return true;
|
||
}
|
||
tr.join(after);
|
||
return true;
|
||
};
|
||
var toggleList = (listTypeOrName, itemTypeOrName, keepMarks, attributes = {}) => ({ editor, tr, state, dispatch, chain, commands: commands2, can }) => {
|
||
const { extensions, splittableMarks } = editor.extensionManager;
|
||
const listType = getNodeType(listTypeOrName, state.schema);
|
||
const itemType = getNodeType(itemTypeOrName, state.schema);
|
||
const { selection, storedMarks } = state;
|
||
const { $from, $to } = selection;
|
||
const range = $from.blockRange($to);
|
||
const marks = storedMarks || selection.$to.parentOffset && selection.$from.marks();
|
||
if (!range) {
|
||
return false;
|
||
}
|
||
const parentList = findParentNode((node) => isList(node.type.name, extensions))(selection);
|
||
if (range.depth >= 1 && parentList && range.depth - parentList.depth <= 1) {
|
||
if (parentList.node.type === listType) {
|
||
return commands2.liftListItem(itemType);
|
||
}
|
||
if (isList(parentList.node.type.name, extensions) && listType.validContent(parentList.node.content) && dispatch) {
|
||
return chain().command(() => {
|
||
tr.setNodeMarkup(parentList.pos, listType);
|
||
return true;
|
||
}).command(() => joinListBackwards(tr, listType)).command(() => joinListForwards(tr, listType)).run();
|
||
}
|
||
}
|
||
if (!keepMarks || !marks || !dispatch) {
|
||
return chain().command(() => {
|
||
const canWrapInList = can().wrapInList(listType, attributes);
|
||
if (canWrapInList) {
|
||
return true;
|
||
}
|
||
return commands2.clearNodes();
|
||
}).wrapInList(listType, attributes).command(() => joinListBackwards(tr, listType)).command(() => joinListForwards(tr, listType)).run();
|
||
}
|
||
return chain().command(() => {
|
||
const canWrapInList = can().wrapInList(listType, attributes);
|
||
const filteredMarks = marks.filter((mark) => splittableMarks.includes(mark.type.name));
|
||
tr.ensureMarks(filteredMarks);
|
||
if (canWrapInList) {
|
||
return true;
|
||
}
|
||
return commands2.clearNodes();
|
||
}).wrapInList(listType, attributes).command(() => joinListBackwards(tr, listType)).command(() => joinListForwards(tr, listType)).run();
|
||
};
|
||
var toggleMark = (typeOrName, attributes = {}, options = {}) => ({ state, commands: commands2 }) => {
|
||
const { extendEmptyMarkRange = false } = options;
|
||
const type = getMarkType(typeOrName, state.schema);
|
||
const isActive2 = isMarkActive(state, type, attributes);
|
||
if (isActive2) {
|
||
return commands2.unsetMark(type, { extendEmptyMarkRange });
|
||
}
|
||
return commands2.setMark(type, attributes);
|
||
};
|
||
var toggleNode = (typeOrName, toggleTypeOrName, attributes = {}) => ({ state, commands: commands2 }) => {
|
||
const type = getNodeType(typeOrName, state.schema);
|
||
const toggleType = getNodeType(toggleTypeOrName, state.schema);
|
||
const isActive2 = isNodeActive(state, type, attributes);
|
||
let attributesToCopy;
|
||
if (state.selection.$anchor.sameParent(state.selection.$head)) {
|
||
attributesToCopy = state.selection.$anchor.parent.attrs;
|
||
}
|
||
if (isActive2) {
|
||
return commands2.setNode(toggleType, attributesToCopy);
|
||
}
|
||
return commands2.setNode(type, { ...attributesToCopy, ...attributes });
|
||
};
|
||
var toggleWrap = (typeOrName, attributes = {}) => ({ state, commands: commands2 }) => {
|
||
const type = getNodeType(typeOrName, state.schema);
|
||
const isActive2 = isNodeActive(state, type, attributes);
|
||
if (isActive2) {
|
||
return commands2.lift(type);
|
||
}
|
||
return commands2.wrapIn(type, attributes);
|
||
};
|
||
var undoInputRule = () => ({ state, dispatch }) => {
|
||
const plugins = state.plugins;
|
||
for (let i = 0; i < plugins.length; i += 1) {
|
||
const plugin = plugins[i];
|
||
let undoable;
|
||
if (plugin.spec.isInputRules && (undoable = plugin.getState(state))) {
|
||
if (dispatch) {
|
||
const tr = state.tr;
|
||
const toUndo = undoable.transform;
|
||
for (let j = toUndo.steps.length - 1; j >= 0; j -= 1) {
|
||
tr.step(toUndo.steps[j].invert(toUndo.docs[j]));
|
||
}
|
||
if (undoable.text) {
|
||
const marks = tr.doc.resolve(undoable.from).marks();
|
||
tr.replaceWith(undoable.from, undoable.to, state.schema.text(undoable.text, marks));
|
||
} else {
|
||
tr.delete(undoable.from, undoable.to);
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
};
|
||
var unsetAllMarks = () => ({ tr, dispatch }) => {
|
||
const { selection } = tr;
|
||
const { empty: empty2, ranges } = selection;
|
||
if (empty2) {
|
||
return true;
|
||
}
|
||
if (dispatch) {
|
||
ranges.forEach((range) => {
|
||
tr.removeMark(range.$from.pos, range.$to.pos);
|
||
});
|
||
}
|
||
return true;
|
||
};
|
||
var unsetMark = (typeOrName, options = {}) => ({ tr, state, dispatch }) => {
|
||
var _a;
|
||
const { extendEmptyMarkRange = false } = options;
|
||
const { selection } = tr;
|
||
const type = getMarkType(typeOrName, state.schema);
|
||
const { $from, empty: empty2, ranges } = selection;
|
||
if (!dispatch) {
|
||
return true;
|
||
}
|
||
if (empty2 && extendEmptyMarkRange) {
|
||
let { from, to } = selection;
|
||
const attrs = (_a = $from.marks().find((mark) => mark.type === type)) === null || _a === void 0 ? void 0 : _a.attrs;
|
||
const range = getMarkRange($from, type, attrs);
|
||
if (range) {
|
||
from = range.from;
|
||
to = range.to;
|
||
}
|
||
tr.removeMark(from, to, type);
|
||
} else {
|
||
ranges.forEach((range) => {
|
||
tr.removeMark(range.$from.pos, range.$to.pos, type);
|
||
});
|
||
}
|
||
tr.removeStoredMark(type);
|
||
return true;
|
||
};
|
||
var updateAttributes = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
||
let nodeType = null;
|
||
let markType = null;
|
||
const schemaType = getSchemaTypeNameByName(typeof typeOrName === "string" ? typeOrName : typeOrName.name, state.schema);
|
||
if (!schemaType) {
|
||
return false;
|
||
}
|
||
if (schemaType === "node") {
|
||
nodeType = getNodeType(typeOrName, state.schema);
|
||
}
|
||
if (schemaType === "mark") {
|
||
markType = getMarkType(typeOrName, state.schema);
|
||
}
|
||
if (dispatch) {
|
||
tr.selection.ranges.forEach((range) => {
|
||
const from = range.$from.pos;
|
||
const to = range.$to.pos;
|
||
let lastPos;
|
||
let lastNode;
|
||
let trimmedFrom;
|
||
let trimmedTo;
|
||
if (tr.selection.empty) {
|
||
state.doc.nodesBetween(from, to, (node, pos) => {
|
||
if (nodeType && nodeType === node.type) {
|
||
trimmedFrom = Math.max(pos, from);
|
||
trimmedTo = Math.min(pos + node.nodeSize, to);
|
||
lastPos = pos;
|
||
lastNode = node;
|
||
}
|
||
});
|
||
} else {
|
||
state.doc.nodesBetween(from, to, (node, pos) => {
|
||
if (pos < from && nodeType && nodeType === node.type) {
|
||
trimmedFrom = Math.max(pos, from);
|
||
trimmedTo = Math.min(pos + node.nodeSize, to);
|
||
lastPos = pos;
|
||
lastNode = node;
|
||
}
|
||
if (pos >= from && pos <= to) {
|
||
if (nodeType && nodeType === node.type) {
|
||
tr.setNodeMarkup(pos, void 0, {
|
||
...node.attrs,
|
||
...attributes
|
||
});
|
||
}
|
||
if (markType && node.marks.length) {
|
||
node.marks.forEach((mark) => {
|
||
if (markType === mark.type) {
|
||
const trimmedFrom2 = Math.max(pos, from);
|
||
const trimmedTo2 = Math.min(pos + node.nodeSize, to);
|
||
tr.addMark(trimmedFrom2, trimmedTo2, markType.create({
|
||
...mark.attrs,
|
||
...attributes
|
||
}));
|
||
}
|
||
});
|
||
}
|
||
}
|
||
});
|
||
}
|
||
if (lastNode) {
|
||
if (lastPos !== void 0) {
|
||
tr.setNodeMarkup(lastPos, void 0, {
|
||
...lastNode.attrs,
|
||
...attributes
|
||
});
|
||
}
|
||
if (markType && lastNode.marks.length) {
|
||
lastNode.marks.forEach((mark) => {
|
||
if (markType === mark.type) {
|
||
tr.addMark(trimmedFrom, trimmedTo, markType.create({
|
||
...mark.attrs,
|
||
...attributes
|
||
}));
|
||
}
|
||
});
|
||
}
|
||
}
|
||
});
|
||
}
|
||
return true;
|
||
};
|
||
var wrapIn2 = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
||
const type = getNodeType(typeOrName, state.schema);
|
||
return wrapIn(type, attributes)(state, dispatch);
|
||
};
|
||
var wrapInList2 = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
||
const type = getNodeType(typeOrName, state.schema);
|
||
return wrapInList(type, attributes)(state, dispatch);
|
||
};
|
||
var commands = Object.freeze({
|
||
__proto__: null,
|
||
blur,
|
||
clearContent,
|
||
clearNodes,
|
||
command,
|
||
createParagraphNear: createParagraphNear2,
|
||
cut,
|
||
deleteCurrentNode,
|
||
deleteNode,
|
||
deleteRange,
|
||
deleteSelection: deleteSelection2,
|
||
enter,
|
||
exitCode: exitCode2,
|
||
extendMarkRange,
|
||
first,
|
||
focus,
|
||
forEach,
|
||
insertContent,
|
||
insertContentAt,
|
||
joinBackward: joinBackward2,
|
||
joinDown: joinDown2,
|
||
joinForward: joinForward2,
|
||
joinItemBackward,
|
||
joinItemForward,
|
||
joinTextblockBackward: joinTextblockBackward2,
|
||
joinTextblockForward: joinTextblockForward2,
|
||
joinUp: joinUp2,
|
||
keyboardShortcut,
|
||
lift: lift2,
|
||
liftEmptyBlock: liftEmptyBlock2,
|
||
liftListItem: liftListItem2,
|
||
newlineInCode: newlineInCode2,
|
||
resetAttributes,
|
||
scrollIntoView,
|
||
selectAll: selectAll2,
|
||
selectNodeBackward: selectNodeBackward2,
|
||
selectNodeForward: selectNodeForward2,
|
||
selectParentNode: selectParentNode2,
|
||
selectTextblockEnd: selectTextblockEnd2,
|
||
selectTextblockStart: selectTextblockStart2,
|
||
setContent,
|
||
setMark,
|
||
setMeta,
|
||
setNode,
|
||
setNodeSelection,
|
||
setTextSelection,
|
||
sinkListItem: sinkListItem2,
|
||
splitBlock: splitBlock2,
|
||
splitListItem,
|
||
toggleList,
|
||
toggleMark,
|
||
toggleNode,
|
||
toggleWrap,
|
||
undoInputRule,
|
||
unsetAllMarks,
|
||
unsetMark,
|
||
updateAttributes,
|
||
wrapIn: wrapIn2,
|
||
wrapInList: wrapInList2
|
||
});
|
||
var Commands = Extension.create({
|
||
name: "commands",
|
||
addCommands() {
|
||
return {
|
||
...commands
|
||
};
|
||
}
|
||
});
|
||
var Drop = Extension.create({
|
||
name: "drop",
|
||
addProseMirrorPlugins() {
|
||
return [
|
||
new Plugin({
|
||
key: new PluginKey("tiptapDrop"),
|
||
props: {
|
||
handleDrop: (_, e, slice, moved) => {
|
||
this.editor.emit("drop", {
|
||
editor: this.editor,
|
||
event: e,
|
||
slice,
|
||
moved
|
||
});
|
||
}
|
||
}
|
||
})
|
||
];
|
||
}
|
||
});
|
||
var Editable = Extension.create({
|
||
name: "editable",
|
||
addProseMirrorPlugins() {
|
||
return [
|
||
new Plugin({
|
||
key: new PluginKey("editable"),
|
||
props: {
|
||
editable: () => this.editor.options.editable
|
||
}
|
||
})
|
||
];
|
||
}
|
||
});
|
||
var FocusEvents = Extension.create({
|
||
name: "focusEvents",
|
||
addProseMirrorPlugins() {
|
||
const { editor } = this;
|
||
return [
|
||
new Plugin({
|
||
key: new PluginKey("focusEvents"),
|
||
props: {
|
||
handleDOMEvents: {
|
||
focus: (view, event) => {
|
||
editor.isFocused = true;
|
||
const transaction = editor.state.tr.setMeta("focus", { event }).setMeta("addToHistory", false);
|
||
view.dispatch(transaction);
|
||
return false;
|
||
},
|
||
blur: (view, event) => {
|
||
editor.isFocused = false;
|
||
const transaction = editor.state.tr.setMeta("blur", { event }).setMeta("addToHistory", false);
|
||
view.dispatch(transaction);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
})
|
||
];
|
||
}
|
||
});
|
||
var Keymap = Extension.create({
|
||
name: "keymap",
|
||
addKeyboardShortcuts() {
|
||
const handleBackspace = () => this.editor.commands.first(({ commands: commands2 }) => [
|
||
() => commands2.undoInputRule(),
|
||
// maybe convert first text block node to default node
|
||
() => commands2.command(({ tr }) => {
|
||
const { selection, doc: doc2 } = tr;
|
||
const { empty: empty2, $anchor } = selection;
|
||
const { pos, parent } = $anchor;
|
||
const $parentPos = $anchor.parent.isTextblock && pos > 0 ? tr.doc.resolve(pos - 1) : $anchor;
|
||
const parentIsIsolating = $parentPos.parent.type.spec.isolating;
|
||
const parentPos = $anchor.pos - $anchor.parentOffset;
|
||
const isAtStart = parentIsIsolating && $parentPos.parent.childCount === 1 ? parentPos === $anchor.pos : Selection.atStart(doc2).from === pos;
|
||
if (!empty2 || !parent.type.isTextblock || parent.textContent.length || !isAtStart || isAtStart && $anchor.parent.type.name === "paragraph") {
|
||
return false;
|
||
}
|
||
return commands2.clearNodes();
|
||
}),
|
||
() => commands2.deleteSelection(),
|
||
() => commands2.joinBackward(),
|
||
() => commands2.selectNodeBackward()
|
||
]);
|
||
const handleDelete = () => this.editor.commands.first(({ commands: commands2 }) => [
|
||
() => commands2.deleteSelection(),
|
||
() => commands2.deleteCurrentNode(),
|
||
() => commands2.joinForward(),
|
||
() => commands2.selectNodeForward()
|
||
]);
|
||
const handleEnter = () => this.editor.commands.first(({ commands: commands2 }) => [
|
||
() => commands2.newlineInCode(),
|
||
() => commands2.createParagraphNear(),
|
||
() => commands2.liftEmptyBlock(),
|
||
() => commands2.splitBlock()
|
||
]);
|
||
const baseKeymap = {
|
||
Enter: handleEnter,
|
||
"Mod-Enter": () => this.editor.commands.exitCode(),
|
||
Backspace: handleBackspace,
|
||
"Mod-Backspace": handleBackspace,
|
||
"Shift-Backspace": handleBackspace,
|
||
Delete: handleDelete,
|
||
"Mod-Delete": handleDelete,
|
||
"Mod-a": () => this.editor.commands.selectAll()
|
||
};
|
||
const pcKeymap = {
|
||
...baseKeymap
|
||
};
|
||
const macKeymap = {
|
||
...baseKeymap,
|
||
"Ctrl-h": handleBackspace,
|
||
"Alt-Backspace": handleBackspace,
|
||
"Ctrl-d": handleDelete,
|
||
"Ctrl-Alt-Backspace": handleDelete,
|
||
"Alt-Delete": handleDelete,
|
||
"Alt-d": handleDelete,
|
||
"Ctrl-a": () => this.editor.commands.selectTextblockStart(),
|
||
"Ctrl-e": () => this.editor.commands.selectTextblockEnd()
|
||
};
|
||
if (isiOS() || isMacOS()) {
|
||
return macKeymap;
|
||
}
|
||
return pcKeymap;
|
||
},
|
||
addProseMirrorPlugins() {
|
||
return [
|
||
// With this plugin we check if the whole document was selected and deleted.
|
||
// In this case we will additionally call `clearNodes()` to convert e.g. a heading
|
||
// to a paragraph if necessary.
|
||
// This is an alternative to ProseMirror's `AllSelection`, which doesn’t work well
|
||
// with many other commands.
|
||
new Plugin({
|
||
key: new PluginKey("clearDocument"),
|
||
appendTransaction: (transactions, oldState, newState) => {
|
||
const docChanges = transactions.some((transaction) => transaction.docChanged) && !oldState.doc.eq(newState.doc);
|
||
const ignoreTr = transactions.some((transaction) => transaction.getMeta("preventClearDocument"));
|
||
if (!docChanges || ignoreTr) {
|
||
return;
|
||
}
|
||
const { empty: empty2, from, to } = oldState.selection;
|
||
const allFrom = Selection.atStart(oldState.doc).from;
|
||
const allEnd = Selection.atEnd(oldState.doc).to;
|
||
const allWasSelected = from === allFrom && to === allEnd;
|
||
if (empty2 || !allWasSelected) {
|
||
return;
|
||
}
|
||
const isEmpty = isNodeEmpty(newState.doc);
|
||
if (!isEmpty) {
|
||
return;
|
||
}
|
||
const tr = newState.tr;
|
||
const state = createChainableState({
|
||
state: newState,
|
||
transaction: tr
|
||
});
|
||
const { commands: commands2 } = new CommandManager({
|
||
editor: this.editor,
|
||
state
|
||
});
|
||
commands2.clearNodes();
|
||
if (!tr.steps.length) {
|
||
return;
|
||
}
|
||
return tr;
|
||
}
|
||
})
|
||
];
|
||
}
|
||
});
|
||
var Paste = Extension.create({
|
||
name: "paste",
|
||
addProseMirrorPlugins() {
|
||
return [
|
||
new Plugin({
|
||
key: new PluginKey("tiptapPaste"),
|
||
props: {
|
||
handlePaste: (_view, e, slice) => {
|
||
this.editor.emit("paste", {
|
||
editor: this.editor,
|
||
event: e,
|
||
slice
|
||
});
|
||
}
|
||
}
|
||
})
|
||
];
|
||
}
|
||
});
|
||
var Tabindex = Extension.create({
|
||
name: "tabindex",
|
||
addProseMirrorPlugins() {
|
||
return [
|
||
new Plugin({
|
||
key: new PluginKey("tabindex"),
|
||
props: {
|
||
attributes: () => this.editor.isEditable ? { tabindex: "0" } : {}
|
||
}
|
||
})
|
||
];
|
||
}
|
||
});
|
||
var index = Object.freeze({
|
||
__proto__: null,
|
||
ClipboardTextSerializer,
|
||
Commands,
|
||
Drop,
|
||
Editable,
|
||
FocusEvents,
|
||
Keymap,
|
||
Paste,
|
||
Tabindex
|
||
});
|
||
var NodePos = class _NodePos {
|
||
get name() {
|
||
return this.node.type.name;
|
||
}
|
||
constructor(pos, editor, isBlock = false, node = null) {
|
||
this.currentNode = null;
|
||
this.actualDepth = null;
|
||
this.isBlock = isBlock;
|
||
this.resolvedPos = pos;
|
||
this.editor = editor;
|
||
this.currentNode = node;
|
||
}
|
||
get node() {
|
||
return this.currentNode || this.resolvedPos.node();
|
||
}
|
||
get element() {
|
||
return this.editor.view.domAtPos(this.pos).node;
|
||
}
|
||
get depth() {
|
||
var _a;
|
||
return (_a = this.actualDepth) !== null && _a !== void 0 ? _a : this.resolvedPos.depth;
|
||
}
|
||
get pos() {
|
||
return this.resolvedPos.pos;
|
||
}
|
||
get content() {
|
||
return this.node.content;
|
||
}
|
||
set content(content) {
|
||
let from = this.from;
|
||
let to = this.to;
|
||
if (this.isBlock) {
|
||
if (this.content.size === 0) {
|
||
console.error(`You can’t set content on a block node. Tried to set content on ${this.name} at ${this.pos}`);
|
||
return;
|
||
}
|
||
from = this.from + 1;
|
||
to = this.to - 1;
|
||
}
|
||
this.editor.commands.insertContentAt({ from, to }, content);
|
||
}
|
||
get attributes() {
|
||
return this.node.attrs;
|
||
}
|
||
get textContent() {
|
||
return this.node.textContent;
|
||
}
|
||
get size() {
|
||
return this.node.nodeSize;
|
||
}
|
||
get from() {
|
||
if (this.isBlock) {
|
||
return this.pos;
|
||
}
|
||
return this.resolvedPos.start(this.resolvedPos.depth);
|
||
}
|
||
get range() {
|
||
return {
|
||
from: this.from,
|
||
to: this.to
|
||
};
|
||
}
|
||
get to() {
|
||
if (this.isBlock) {
|
||
return this.pos + this.size;
|
||
}
|
||
return this.resolvedPos.end(this.resolvedPos.depth) + (this.node.isText ? 0 : 1);
|
||
}
|
||
get parent() {
|
||
if (this.depth === 0) {
|
||
return null;
|
||
}
|
||
const parentPos = this.resolvedPos.start(this.resolvedPos.depth - 1);
|
||
const $pos = this.resolvedPos.doc.resolve(parentPos);
|
||
return new _NodePos($pos, this.editor);
|
||
}
|
||
get before() {
|
||
let $pos = this.resolvedPos.doc.resolve(this.from - (this.isBlock ? 1 : 2));
|
||
if ($pos.depth !== this.depth) {
|
||
$pos = this.resolvedPos.doc.resolve(this.from - 3);
|
||
}
|
||
return new _NodePos($pos, this.editor);
|
||
}
|
||
get after() {
|
||
let $pos = this.resolvedPos.doc.resolve(this.to + (this.isBlock ? 2 : 1));
|
||
if ($pos.depth !== this.depth) {
|
||
$pos = this.resolvedPos.doc.resolve(this.to + 3);
|
||
}
|
||
return new _NodePos($pos, this.editor);
|
||
}
|
||
get children() {
|
||
const children = [];
|
||
this.node.content.forEach((node, offset) => {
|
||
const isBlock = node.isBlock && !node.isTextblock;
|
||
const isNonTextAtom = node.isAtom && !node.isText;
|
||
const targetPos = this.pos + offset + (isNonTextAtom ? 0 : 1);
|
||
const $pos = this.resolvedPos.doc.resolve(targetPos);
|
||
if (!isBlock && $pos.depth <= this.depth) {
|
||
return;
|
||
}
|
||
const childNodePos = new _NodePos($pos, this.editor, isBlock, isBlock ? node : null);
|
||
if (isBlock) {
|
||
childNodePos.actualDepth = this.depth + 1;
|
||
}
|
||
children.push(new _NodePos($pos, this.editor, isBlock, isBlock ? node : null));
|
||
});
|
||
return children;
|
||
}
|
||
get firstChild() {
|
||
return this.children[0] || null;
|
||
}
|
||
get lastChild() {
|
||
const children = this.children;
|
||
return children[children.length - 1] || null;
|
||
}
|
||
closest(selector, attributes = {}) {
|
||
let node = null;
|
||
let currentNode = this.parent;
|
||
while (currentNode && !node) {
|
||
if (currentNode.node.type.name === selector) {
|
||
if (Object.keys(attributes).length > 0) {
|
||
const nodeAttributes = currentNode.node.attrs;
|
||
const attrKeys = Object.keys(attributes);
|
||
for (let index2 = 0; index2 < attrKeys.length; index2 += 1) {
|
||
const key = attrKeys[index2];
|
||
if (nodeAttributes[key] !== attributes[key]) {
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
node = currentNode;
|
||
}
|
||
}
|
||
currentNode = currentNode.parent;
|
||
}
|
||
return node;
|
||
}
|
||
querySelector(selector, attributes = {}) {
|
||
return this.querySelectorAll(selector, attributes, true)[0] || null;
|
||
}
|
||
querySelectorAll(selector, attributes = {}, firstItemOnly = false) {
|
||
let nodes = [];
|
||
if (!this.children || this.children.length === 0) {
|
||
return nodes;
|
||
}
|
||
const attrKeys = Object.keys(attributes);
|
||
this.children.forEach((childPos) => {
|
||
if (firstItemOnly && nodes.length > 0) {
|
||
return;
|
||
}
|
||
if (childPos.node.type.name === selector) {
|
||
const doesAllAttributesMatch = attrKeys.every((key) => attributes[key] === childPos.node.attrs[key]);
|
||
if (doesAllAttributesMatch) {
|
||
nodes.push(childPos);
|
||
}
|
||
}
|
||
if (firstItemOnly && nodes.length > 0) {
|
||
return;
|
||
}
|
||
nodes = nodes.concat(childPos.querySelectorAll(selector, attributes, firstItemOnly));
|
||
});
|
||
return nodes;
|
||
}
|
||
setAttribute(attributes) {
|
||
const { tr } = this.editor.state;
|
||
tr.setNodeMarkup(this.from, void 0, {
|
||
...this.node.attrs,
|
||
...attributes
|
||
});
|
||
this.editor.view.dispatch(tr);
|
||
}
|
||
};
|
||
var style = `.ProseMirror {
|
||
position: relative;
|
||
}
|
||
|
||
.ProseMirror {
|
||
word-wrap: break-word;
|
||
white-space: pre-wrap;
|
||
white-space: break-spaces;
|
||
-webkit-font-variant-ligatures: none;
|
||
font-variant-ligatures: none;
|
||
font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */
|
||
}
|
||
|
||
.ProseMirror [contenteditable="false"] {
|
||
white-space: normal;
|
||
}
|
||
|
||
.ProseMirror [contenteditable="false"] [contenteditable="true"] {
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
.ProseMirror pre {
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
img.ProseMirror-separator {
|
||
display: inline !important;
|
||
border: none !important;
|
||
margin: 0 !important;
|
||
width: 0 !important;
|
||
height: 0 !important;
|
||
}
|
||
|
||
.ProseMirror-gapcursor {
|
||
display: none;
|
||
pointer-events: none;
|
||
position: absolute;
|
||
margin: 0;
|
||
}
|
||
|
||
.ProseMirror-gapcursor:after {
|
||
content: "";
|
||
display: block;
|
||
position: absolute;
|
||
top: -2px;
|
||
width: 20px;
|
||
border-top: 1px solid black;
|
||
animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;
|
||
}
|
||
|
||
@keyframes ProseMirror-cursor-blink {
|
||
to {
|
||
visibility: hidden;
|
||
}
|
||
}
|
||
|
||
.ProseMirror-hideselection *::selection {
|
||
background: transparent;
|
||
}
|
||
|
||
.ProseMirror-hideselection *::-moz-selection {
|
||
background: transparent;
|
||
}
|
||
|
||
.ProseMirror-hideselection * {
|
||
caret-color: transparent;
|
||
}
|
||
|
||
.ProseMirror-focused .ProseMirror-gapcursor {
|
||
display: block;
|
||
}
|
||
|
||
.tippy-box[data-animation=fade][data-state=hidden] {
|
||
opacity: 0
|
||
}`;
|
||
function createStyleTag(style2, nonce, suffix) {
|
||
const tiptapStyleTag = document.querySelector(`style[data-tiptap-style${suffix ? `-${suffix}` : ""}]`);
|
||
if (tiptapStyleTag !== null) {
|
||
return tiptapStyleTag;
|
||
}
|
||
const styleNode = document.createElement("style");
|
||
if (nonce) {
|
||
styleNode.setAttribute("nonce", nonce);
|
||
}
|
||
styleNode.setAttribute(`data-tiptap-style${suffix ? `-${suffix}` : ""}`, "");
|
||
styleNode.innerHTML = style2;
|
||
document.getElementsByTagName("head")[0].appendChild(styleNode);
|
||
return styleNode;
|
||
}
|
||
var Editor = class extends EventEmitter {
|
||
constructor(options = {}) {
|
||
super();
|
||
this.isFocused = false;
|
||
this.isInitialized = false;
|
||
this.extensionStorage = {};
|
||
this.options = {
|
||
element: document.createElement("div"),
|
||
content: "",
|
||
injectCSS: true,
|
||
injectNonce: void 0,
|
||
extensions: [],
|
||
autofocus: false,
|
||
editable: true,
|
||
editorProps: {},
|
||
parseOptions: {},
|
||
coreExtensionOptions: {},
|
||
enableInputRules: true,
|
||
enablePasteRules: true,
|
||
enableCoreExtensions: true,
|
||
enableContentCheck: false,
|
||
onBeforeCreate: () => null,
|
||
onCreate: () => null,
|
||
onUpdate: () => null,
|
||
onSelectionUpdate: () => null,
|
||
onTransaction: () => null,
|
||
onFocus: () => null,
|
||
onBlur: () => null,
|
||
onDestroy: () => null,
|
||
onContentError: ({ error }) => {
|
||
throw error;
|
||
},
|
||
onPaste: () => null,
|
||
onDrop: () => null
|
||
};
|
||
this.isCapturingTransaction = false;
|
||
this.capturedTransaction = null;
|
||
this.setOptions(options);
|
||
this.createExtensionManager();
|
||
this.createCommandManager();
|
||
this.createSchema();
|
||
this.on("beforeCreate", this.options.onBeforeCreate);
|
||
this.emit("beforeCreate", { editor: this });
|
||
this.on("contentError", this.options.onContentError);
|
||
this.createView();
|
||
this.injectCSS();
|
||
this.on("create", this.options.onCreate);
|
||
this.on("update", this.options.onUpdate);
|
||
this.on("selectionUpdate", this.options.onSelectionUpdate);
|
||
this.on("transaction", this.options.onTransaction);
|
||
this.on("focus", this.options.onFocus);
|
||
this.on("blur", this.options.onBlur);
|
||
this.on("destroy", this.options.onDestroy);
|
||
this.on("drop", ({ event, slice, moved }) => this.options.onDrop(event, slice, moved));
|
||
this.on("paste", ({ event, slice }) => this.options.onPaste(event, slice));
|
||
window.setTimeout(() => {
|
||
if (this.isDestroyed) {
|
||
return;
|
||
}
|
||
this.commands.focus(this.options.autofocus);
|
||
this.emit("create", { editor: this });
|
||
this.isInitialized = true;
|
||
}, 0);
|
||
}
|
||
/**
|
||
* Returns the editor storage.
|
||
*/
|
||
get storage() {
|
||
return this.extensionStorage;
|
||
}
|
||
/**
|
||
* An object of all registered commands.
|
||
*/
|
||
get commands() {
|
||
return this.commandManager.commands;
|
||
}
|
||
/**
|
||
* Create a command chain to call multiple commands at once.
|
||
*/
|
||
chain() {
|
||
return this.commandManager.chain();
|
||
}
|
||
/**
|
||
* Check if a command or a command chain can be executed. Without executing it.
|
||
*/
|
||
can() {
|
||
return this.commandManager.can();
|
||
}
|
||
/**
|
||
* Inject CSS styles.
|
||
*/
|
||
injectCSS() {
|
||
if (this.options.injectCSS && document) {
|
||
this.css = createStyleTag(style, this.options.injectNonce);
|
||
}
|
||
}
|
||
/**
|
||
* Update editor options.
|
||
*
|
||
* @param options A list of options
|
||
*/
|
||
setOptions(options = {}) {
|
||
this.options = {
|
||
...this.options,
|
||
...options
|
||
};
|
||
if (!this.view || !this.state || this.isDestroyed) {
|
||
return;
|
||
}
|
||
if (this.options.editorProps) {
|
||
this.view.setProps(this.options.editorProps);
|
||
}
|
||
this.view.updateState(this.state);
|
||
}
|
||
/**
|
||
* Update editable state of the editor.
|
||
*/
|
||
setEditable(editable, emitUpdate = true) {
|
||
this.setOptions({ editable });
|
||
if (emitUpdate) {
|
||
this.emit("update", { editor: this, transaction: this.state.tr });
|
||
}
|
||
}
|
||
/**
|
||
* Returns whether the editor is editable.
|
||
*/
|
||
get isEditable() {
|
||
return this.options.editable && this.view && this.view.editable;
|
||
}
|
||
/**
|
||
* Returns the editor state.
|
||
*/
|
||
get state() {
|
||
return this.view.state;
|
||
}
|
||
/**
|
||
* Register a ProseMirror plugin.
|
||
*
|
||
* @param plugin A ProseMirror plugin
|
||
* @param handlePlugins Control how to merge the plugin into the existing plugins.
|
||
* @returns The new editor state
|
||
*/
|
||
registerPlugin(plugin, handlePlugins) {
|
||
const plugins = isFunction(handlePlugins) ? handlePlugins(plugin, [...this.state.plugins]) : [...this.state.plugins, plugin];
|
||
const state = this.state.reconfigure({ plugins });
|
||
this.view.updateState(state);
|
||
return state;
|
||
}
|
||
/**
|
||
* Unregister a ProseMirror plugin.
|
||
*
|
||
* @param nameOrPluginKeyToRemove The plugins name
|
||
* @returns The new editor state or undefined if the editor is destroyed
|
||
*/
|
||
unregisterPlugin(nameOrPluginKeyToRemove) {
|
||
if (this.isDestroyed) {
|
||
return void 0;
|
||
}
|
||
const prevPlugins = this.state.plugins;
|
||
let plugins = prevPlugins;
|
||
[].concat(nameOrPluginKeyToRemove).forEach((nameOrPluginKey) => {
|
||
const name = typeof nameOrPluginKey === "string" ? `${nameOrPluginKey}$` : nameOrPluginKey.key;
|
||
plugins = prevPlugins.filter((plugin) => !plugin.key.startsWith(name));
|
||
});
|
||
if (prevPlugins.length === plugins.length) {
|
||
return void 0;
|
||
}
|
||
const state = this.state.reconfigure({
|
||
plugins
|
||
});
|
||
this.view.updateState(state);
|
||
return state;
|
||
}
|
||
/**
|
||
* Creates an extension manager.
|
||
*/
|
||
createExtensionManager() {
|
||
var _a, _b;
|
||
const coreExtensions = this.options.enableCoreExtensions ? [
|
||
Editable,
|
||
ClipboardTextSerializer.configure({
|
||
blockSeparator: (_b = (_a = this.options.coreExtensionOptions) === null || _a === void 0 ? void 0 : _a.clipboardTextSerializer) === null || _b === void 0 ? void 0 : _b.blockSeparator
|
||
}),
|
||
Commands,
|
||
FocusEvents,
|
||
Keymap,
|
||
Tabindex,
|
||
Drop,
|
||
Paste
|
||
].filter((ext) => {
|
||
if (typeof this.options.enableCoreExtensions === "object") {
|
||
return this.options.enableCoreExtensions[ext.name] !== false;
|
||
}
|
||
return true;
|
||
}) : [];
|
||
const allExtensions = [...coreExtensions, ...this.options.extensions].filter((extension) => {
|
||
return ["extension", "node", "mark"].includes(extension === null || extension === void 0 ? void 0 : extension.type);
|
||
});
|
||
this.extensionManager = new ExtensionManager(allExtensions, this);
|
||
}
|
||
/**
|
||
* Creates an command manager.
|
||
*/
|
||
createCommandManager() {
|
||
this.commandManager = new CommandManager({
|
||
editor: this
|
||
});
|
||
}
|
||
/**
|
||
* Creates a ProseMirror schema.
|
||
*/
|
||
createSchema() {
|
||
this.schema = this.extensionManager.schema;
|
||
}
|
||
/**
|
||
* Creates a ProseMirror view.
|
||
*/
|
||
createView() {
|
||
var _a;
|
||
let doc2;
|
||
try {
|
||
doc2 = createDocument(this.options.content, this.schema, this.options.parseOptions, { errorOnInvalidContent: this.options.enableContentCheck });
|
||
} catch (e) {
|
||
if (!(e instanceof Error) || !["[tiptap error]: Invalid JSON content", "[tiptap error]: Invalid HTML content"].includes(e.message)) {
|
||
throw e;
|
||
}
|
||
this.emit("contentError", {
|
||
editor: this,
|
||
error: e,
|
||
disableCollaboration: () => {
|
||
if (this.storage.collaboration) {
|
||
this.storage.collaboration.isDisabled = true;
|
||
}
|
||
this.options.extensions = this.options.extensions.filter((extension) => extension.name !== "collaboration");
|
||
this.createExtensionManager();
|
||
}
|
||
});
|
||
doc2 = createDocument(this.options.content, this.schema, this.options.parseOptions, { errorOnInvalidContent: false });
|
||
}
|
||
const selection = resolveFocusPosition(doc2, this.options.autofocus);
|
||
this.view = new EditorView(this.options.element, {
|
||
...this.options.editorProps,
|
||
attributes: {
|
||
// add `role="textbox"` to the editor element
|
||
role: "textbox",
|
||
...(_a = this.options.editorProps) === null || _a === void 0 ? void 0 : _a.attributes
|
||
},
|
||
dispatchTransaction: this.dispatchTransaction.bind(this),
|
||
state: EditorState.create({
|
||
doc: doc2,
|
||
selection: selection || void 0
|
||
})
|
||
});
|
||
const newState = this.state.reconfigure({
|
||
plugins: this.extensionManager.plugins
|
||
});
|
||
this.view.updateState(newState);
|
||
this.createNodeViews();
|
||
this.prependClass();
|
||
const dom = this.view.dom;
|
||
dom.editor = this;
|
||
}
|
||
/**
|
||
* Creates all node views.
|
||
*/
|
||
createNodeViews() {
|
||
if (this.view.isDestroyed) {
|
||
return;
|
||
}
|
||
this.view.setProps({
|
||
nodeViews: this.extensionManager.nodeViews
|
||
});
|
||
}
|
||
/**
|
||
* Prepend class name to element.
|
||
*/
|
||
prependClass() {
|
||
this.view.dom.className = `tiptap ${this.view.dom.className}`;
|
||
}
|
||
captureTransaction(fn) {
|
||
this.isCapturingTransaction = true;
|
||
fn();
|
||
this.isCapturingTransaction = false;
|
||
const tr = this.capturedTransaction;
|
||
this.capturedTransaction = null;
|
||
return tr;
|
||
}
|
||
/**
|
||
* The callback over which to send transactions (state updates) produced by the view.
|
||
*
|
||
* @param transaction An editor state transaction
|
||
*/
|
||
dispatchTransaction(transaction) {
|
||
if (this.view.isDestroyed) {
|
||
return;
|
||
}
|
||
if (this.isCapturingTransaction) {
|
||
if (!this.capturedTransaction) {
|
||
this.capturedTransaction = transaction;
|
||
return;
|
||
}
|
||
transaction.steps.forEach((step) => {
|
||
var _a;
|
||
return (_a = this.capturedTransaction) === null || _a === void 0 ? void 0 : _a.step(step);
|
||
});
|
||
return;
|
||
}
|
||
const state = this.state.apply(transaction);
|
||
const selectionHasChanged = !this.state.selection.eq(state.selection);
|
||
this.emit("beforeTransaction", {
|
||
editor: this,
|
||
transaction,
|
||
nextState: state
|
||
});
|
||
this.view.updateState(state);
|
||
this.emit("transaction", {
|
||
editor: this,
|
||
transaction
|
||
});
|
||
if (selectionHasChanged) {
|
||
this.emit("selectionUpdate", {
|
||
editor: this,
|
||
transaction
|
||
});
|
||
}
|
||
const focus2 = transaction.getMeta("focus");
|
||
const blur2 = transaction.getMeta("blur");
|
||
if (focus2) {
|
||
this.emit("focus", {
|
||
editor: this,
|
||
event: focus2.event,
|
||
transaction
|
||
});
|
||
}
|
||
if (blur2) {
|
||
this.emit("blur", {
|
||
editor: this,
|
||
event: blur2.event,
|
||
transaction
|
||
});
|
||
}
|
||
if (!transaction.docChanged || transaction.getMeta("preventUpdate")) {
|
||
return;
|
||
}
|
||
this.emit("update", {
|
||
editor: this,
|
||
transaction
|
||
});
|
||
}
|
||
/**
|
||
* Get attributes of the currently selected node or mark.
|
||
*/
|
||
getAttributes(nameOrType) {
|
||
return getAttributes(this.state, nameOrType);
|
||
}
|
||
isActive(nameOrAttributes, attributesOrUndefined) {
|
||
const name = typeof nameOrAttributes === "string" ? nameOrAttributes : null;
|
||
const attributes = typeof nameOrAttributes === "string" ? attributesOrUndefined : nameOrAttributes;
|
||
return isActive(this.state, name, attributes);
|
||
}
|
||
/**
|
||
* Get the document as JSON.
|
||
*/
|
||
getJSON() {
|
||
return this.state.doc.toJSON();
|
||
}
|
||
/**
|
||
* Get the document as HTML.
|
||
*/
|
||
getHTML() {
|
||
return getHTMLFromFragment(this.state.doc.content, this.schema);
|
||
}
|
||
/**
|
||
* Get the document as text.
|
||
*/
|
||
getText(options) {
|
||
const { blockSeparator = "\n\n", textSerializers = {} } = options || {};
|
||
return getText2(this.state.doc, {
|
||
blockSeparator,
|
||
textSerializers: {
|
||
...getTextSerializersFromSchema(this.schema),
|
||
...textSerializers
|
||
}
|
||
});
|
||
}
|
||
/**
|
||
* Check if there is no content.
|
||
*/
|
||
get isEmpty() {
|
||
return isNodeEmpty(this.state.doc);
|
||
}
|
||
/**
|
||
* Get the number of characters for the current document.
|
||
*
|
||
* @deprecated
|
||
*/
|
||
getCharacterCount() {
|
||
console.warn('[tiptap warn]: "editor.getCharacterCount()" is deprecated. Please use "editor.storage.characterCount.characters()" instead.');
|
||
return this.state.doc.content.size - 2;
|
||
}
|
||
/**
|
||
* Destroy the editor.
|
||
*/
|
||
destroy() {
|
||
this.emit("destroy");
|
||
if (this.view) {
|
||
const dom = this.view.dom;
|
||
if (dom && dom.editor) {
|
||
delete dom.editor;
|
||
}
|
||
this.view.destroy();
|
||
}
|
||
this.removeAllListeners();
|
||
}
|
||
/**
|
||
* Check if the editor is already destroyed.
|
||
*/
|
||
get isDestroyed() {
|
||
var _a;
|
||
return !((_a = this.view) === null || _a === void 0 ? void 0 : _a.docView);
|
||
}
|
||
$node(selector, attributes) {
|
||
var _a;
|
||
return ((_a = this.$doc) === null || _a === void 0 ? void 0 : _a.querySelector(selector, attributes)) || null;
|
||
}
|
||
$nodes(selector, attributes) {
|
||
var _a;
|
||
return ((_a = this.$doc) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selector, attributes)) || null;
|
||
}
|
||
$pos(pos) {
|
||
const $pos = this.state.doc.resolve(pos);
|
||
return new NodePos($pos, this);
|
||
}
|
||
get $doc() {
|
||
return this.$pos(0);
|
||
}
|
||
};
|
||
function markInputRule(config) {
|
||
return new InputRule({
|
||
find: config.find,
|
||
handler: ({ state, range, match }) => {
|
||
const attributes = callOrReturn(config.getAttributes, void 0, match);
|
||
if (attributes === false || attributes === null) {
|
||
return null;
|
||
}
|
||
const { tr } = state;
|
||
const captureGroup = match[match.length - 1];
|
||
const fullMatch = match[0];
|
||
if (captureGroup) {
|
||
const startSpaces = fullMatch.search(/\S/);
|
||
const textStart = range.from + fullMatch.indexOf(captureGroup);
|
||
const textEnd = textStart + captureGroup.length;
|
||
const excludedMarks = getMarksBetween(range.from, range.to, state.doc).filter((item) => {
|
||
const excluded = item.mark.type.excluded;
|
||
return excluded.find((type) => type === config.type && type !== item.mark.type);
|
||
}).filter((item) => item.to > textStart);
|
||
if (excludedMarks.length) {
|
||
return null;
|
||
}
|
||
if (textEnd < range.to) {
|
||
tr.delete(textEnd, range.to);
|
||
}
|
||
if (textStart > range.from) {
|
||
tr.delete(range.from + startSpaces, textStart);
|
||
}
|
||
const markEnd = range.from + startSpaces + captureGroup.length;
|
||
tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}));
|
||
tr.removeStoredMark(config.type);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
function nodeInputRule(config) {
|
||
return new InputRule({
|
||
find: config.find,
|
||
handler: ({ state, range, match }) => {
|
||
const attributes = callOrReturn(config.getAttributes, void 0, match) || {};
|
||
const { tr } = state;
|
||
const start = range.from;
|
||
let end = range.to;
|
||
const newNode = config.type.create(attributes);
|
||
if (match[1]) {
|
||
const offset = match[0].lastIndexOf(match[1]);
|
||
let matchStart = start + offset;
|
||
if (matchStart > end) {
|
||
matchStart = end;
|
||
} else {
|
||
end = matchStart + match[1].length;
|
||
}
|
||
const lastChar = match[0][match[0].length - 1];
|
||
tr.insertText(lastChar, start + match[0].length - 1);
|
||
tr.replaceWith(matchStart, end, newNode);
|
||
} else if (match[0]) {
|
||
const insertionStart = config.type.isInline ? start : start - 1;
|
||
tr.insert(insertionStart, config.type.create(attributes)).delete(tr.mapping.map(start), tr.mapping.map(end));
|
||
}
|
||
tr.scrollIntoView();
|
||
}
|
||
});
|
||
}
|
||
function textblockTypeInputRule(config) {
|
||
return new InputRule({
|
||
find: config.find,
|
||
handler: ({ state, range, match }) => {
|
||
const $start = state.doc.resolve(range.from);
|
||
const attributes = callOrReturn(config.getAttributes, void 0, match) || {};
|
||
if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), config.type)) {
|
||
return null;
|
||
}
|
||
state.tr.delete(range.from, range.to).setBlockType(range.from, range.from, config.type, attributes);
|
||
}
|
||
});
|
||
}
|
||
function textInputRule(config) {
|
||
return new InputRule({
|
||
find: config.find,
|
||
handler: ({ state, range, match }) => {
|
||
let insert = config.replace;
|
||
let start = range.from;
|
||
const end = range.to;
|
||
if (match[1]) {
|
||
const offset = match[0].lastIndexOf(match[1]);
|
||
insert += match[0].slice(offset + match[1].length);
|
||
start += offset;
|
||
const cutOff = start - end;
|
||
if (cutOff > 0) {
|
||
insert = match[0].slice(offset - cutOff, offset) + insert;
|
||
start = end;
|
||
}
|
||
}
|
||
state.tr.insertText(insert, start, end);
|
||
}
|
||
});
|
||
}
|
||
function wrappingInputRule(config) {
|
||
return new InputRule({
|
||
find: config.find,
|
||
handler: ({ state, range, match, chain }) => {
|
||
const attributes = callOrReturn(config.getAttributes, void 0, match) || {};
|
||
const tr = state.tr.delete(range.from, range.to);
|
||
const $start = tr.doc.resolve(range.from);
|
||
const blockRange = $start.blockRange();
|
||
const wrapping = blockRange && findWrapping(blockRange, config.type, attributes);
|
||
if (!wrapping) {
|
||
return null;
|
||
}
|
||
tr.wrap(blockRange, wrapping);
|
||
if (config.keepMarks && config.editor) {
|
||
const { selection, storedMarks } = state;
|
||
const { splittableMarks } = config.editor.extensionManager;
|
||
const marks = storedMarks || selection.$to.parentOffset && selection.$from.marks();
|
||
if (marks) {
|
||
const filteredMarks = marks.filter((mark) => splittableMarks.includes(mark.type.name));
|
||
tr.ensureMarks(filteredMarks);
|
||
}
|
||
}
|
||
if (config.keepAttributes) {
|
||
const nodeType = config.type.name === "bulletList" || config.type.name === "orderedList" ? "listItem" : "taskList";
|
||
chain().updateAttributes(nodeType, attributes).run();
|
||
}
|
||
const before = tr.doc.resolve(range.from - 1).nodeBefore;
|
||
if (before && before.type === config.type && canJoin(tr.doc, range.from - 1) && (!config.joinPredicate || config.joinPredicate(match, before))) {
|
||
tr.join(range.from - 1);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
var Node2 = class _Node {
|
||
constructor(config = {}) {
|
||
this.type = "node";
|
||
this.name = "node";
|
||
this.parent = null;
|
||
this.child = null;
|
||
this.config = {
|
||
name: this.name,
|
||
defaultOptions: {}
|
||
};
|
||
this.config = {
|
||
...this.config,
|
||
...config
|
||
};
|
||
this.name = this.config.name;
|
||
if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
|
||
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
|
||
}
|
||
this.options = this.config.defaultOptions;
|
||
if (this.config.addOptions) {
|
||
this.options = callOrReturn(getExtensionField(this, "addOptions", {
|
||
name: this.name
|
||
}));
|
||
}
|
||
this.storage = callOrReturn(getExtensionField(this, "addStorage", {
|
||
name: this.name,
|
||
options: this.options
|
||
})) || {};
|
||
}
|
||
static create(config = {}) {
|
||
return new _Node(config);
|
||
}
|
||
configure(options = {}) {
|
||
const extension = this.extend({
|
||
...this.config,
|
||
addOptions: () => {
|
||
return mergeDeep(this.options, options);
|
||
}
|
||
});
|
||
extension.name = this.name;
|
||
extension.parent = this.parent;
|
||
return extension;
|
||
}
|
||
extend(extendedConfig = {}) {
|
||
const extension = new _Node(extendedConfig);
|
||
extension.parent = this;
|
||
this.child = extension;
|
||
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
|
||
if (extendedConfig.defaultOptions && Object.keys(extendedConfig.defaultOptions).length > 0) {
|
||
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
|
||
}
|
||
extension.options = callOrReturn(getExtensionField(extension, "addOptions", {
|
||
name: extension.name
|
||
}));
|
||
extension.storage = callOrReturn(getExtensionField(extension, "addStorage", {
|
||
name: extension.name,
|
||
options: extension.options
|
||
}));
|
||
return extension;
|
||
}
|
||
};
|
||
function isAndroid() {
|
||
return navigator.platform === "Android" || /android/i.test(navigator.userAgent);
|
||
}
|
||
var NodeView = class {
|
||
constructor(component, props, options) {
|
||
this.isDragging = false;
|
||
this.component = component;
|
||
this.editor = props.editor;
|
||
this.options = {
|
||
stopEvent: null,
|
||
ignoreMutation: null,
|
||
...options
|
||
};
|
||
this.extension = props.extension;
|
||
this.node = props.node;
|
||
this.decorations = props.decorations;
|
||
this.innerDecorations = props.innerDecorations;
|
||
this.view = props.view;
|
||
this.HTMLAttributes = props.HTMLAttributes;
|
||
this.getPos = props.getPos;
|
||
this.mount();
|
||
}
|
||
mount() {
|
||
return;
|
||
}
|
||
get dom() {
|
||
return this.editor.view.dom;
|
||
}
|
||
get contentDOM() {
|
||
return null;
|
||
}
|
||
onDragStart(event) {
|
||
var _a, _b, _c, _d, _e, _f, _g;
|
||
const { view } = this.editor;
|
||
const target = event.target;
|
||
const dragHandle = target.nodeType === 3 ? (_a = target.parentElement) === null || _a === void 0 ? void 0 : _a.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
|
||
if (!this.dom || ((_b = this.contentDOM) === null || _b === void 0 ? void 0 : _b.contains(target)) || !dragHandle) {
|
||
return;
|
||
}
|
||
let x = 0;
|
||
let y = 0;
|
||
if (this.dom !== dragHandle) {
|
||
const domBox = this.dom.getBoundingClientRect();
|
||
const handleBox = dragHandle.getBoundingClientRect();
|
||
const offsetX = (_c = event.offsetX) !== null && _c !== void 0 ? _c : (_d = event.nativeEvent) === null || _d === void 0 ? void 0 : _d.offsetX;
|
||
const offsetY = (_e = event.offsetY) !== null && _e !== void 0 ? _e : (_f = event.nativeEvent) === null || _f === void 0 ? void 0 : _f.offsetY;
|
||
x = handleBox.x - domBox.x + offsetX;
|
||
y = handleBox.y - domBox.y + offsetY;
|
||
}
|
||
(_g = event.dataTransfer) === null || _g === void 0 ? void 0 : _g.setDragImage(this.dom, x, y);
|
||
const pos = this.getPos();
|
||
if (typeof pos !== "number") {
|
||
return;
|
||
}
|
||
const selection = NodeSelection.create(view.state.doc, pos);
|
||
const transaction = view.state.tr.setSelection(selection);
|
||
view.dispatch(transaction);
|
||
}
|
||
stopEvent(event) {
|
||
var _a;
|
||
if (!this.dom) {
|
||
return false;
|
||
}
|
||
if (typeof this.options.stopEvent === "function") {
|
||
return this.options.stopEvent({ event });
|
||
}
|
||
const target = event.target;
|
||
const isInElement = this.dom.contains(target) && !((_a = this.contentDOM) === null || _a === void 0 ? void 0 : _a.contains(target));
|
||
if (!isInElement) {
|
||
return false;
|
||
}
|
||
const isDragEvent = event.type.startsWith("drag");
|
||
const isDropEvent = event.type === "drop";
|
||
const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
|
||
if (isInput && !isDropEvent && !isDragEvent) {
|
||
return true;
|
||
}
|
||
const { isEditable } = this.editor;
|
||
const { isDragging } = this;
|
||
const isDraggable = !!this.node.type.spec.draggable;
|
||
const isSelectable = NodeSelection.isSelectable(this.node);
|
||
const isCopyEvent = event.type === "copy";
|
||
const isPasteEvent = event.type === "paste";
|
||
const isCutEvent = event.type === "cut";
|
||
const isClickEvent = event.type === "mousedown";
|
||
if (!isDraggable && isSelectable && isDragEvent) {
|
||
event.preventDefault();
|
||
}
|
||
if (isDraggable && isDragEvent && !isDragging) {
|
||
event.preventDefault();
|
||
return false;
|
||
}
|
||
if (isDraggable && isEditable && !isDragging && isClickEvent) {
|
||
const dragHandle = target.closest("[data-drag-handle]");
|
||
const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
|
||
if (isValidDragHandle) {
|
||
this.isDragging = true;
|
||
document.addEventListener("dragend", () => {
|
||
this.isDragging = false;
|
||
}, { once: true });
|
||
document.addEventListener("drop", () => {
|
||
this.isDragging = false;
|
||
}, { once: true });
|
||
document.addEventListener("mouseup", () => {
|
||
this.isDragging = false;
|
||
}, { once: true });
|
||
}
|
||
}
|
||
if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
/**
|
||
* Called when a DOM [mutation](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) or a selection change happens within the view.
|
||
* @return `false` if the editor should re-read the selection or re-parse the range around the mutation
|
||
* @return `true` if it can safely be ignored.
|
||
*/
|
||
ignoreMutation(mutation) {
|
||
if (!this.dom || !this.contentDOM) {
|
||
return true;
|
||
}
|
||
if (typeof this.options.ignoreMutation === "function") {
|
||
return this.options.ignoreMutation({ mutation });
|
||
}
|
||
if (this.node.isLeaf || this.node.isAtom) {
|
||
return true;
|
||
}
|
||
if (mutation.type === "selection") {
|
||
return false;
|
||
}
|
||
if (this.dom.contains(mutation.target) && mutation.type === "childList" && (isiOS() || isAndroid()) && this.editor.isFocused) {
|
||
const changedNodes = [
|
||
...Array.from(mutation.addedNodes),
|
||
...Array.from(mutation.removedNodes)
|
||
];
|
||
if (changedNodes.every((node) => node.isContentEditable)) {
|
||
return false;
|
||
}
|
||
}
|
||
if (this.contentDOM === mutation.target && mutation.type === "attributes") {
|
||
return true;
|
||
}
|
||
if (this.contentDOM.contains(mutation.target)) {
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
/**
|
||
* Update the attributes of the prosemirror node.
|
||
*/
|
||
updateAttributes(attributes) {
|
||
this.editor.commands.command(({ tr }) => {
|
||
const pos = this.getPos();
|
||
if (typeof pos !== "number") {
|
||
return false;
|
||
}
|
||
tr.setNodeMarkup(pos, void 0, {
|
||
...this.node.attrs,
|
||
...attributes
|
||
});
|
||
return true;
|
||
});
|
||
}
|
||
/**
|
||
* Delete the node.
|
||
*/
|
||
deleteNode() {
|
||
const from = this.getPos();
|
||
if (typeof from !== "number") {
|
||
return;
|
||
}
|
||
const to = from + this.node.nodeSize;
|
||
this.editor.commands.deleteRange({ from, to });
|
||
}
|
||
};
|
||
function markPasteRule(config) {
|
||
return new PasteRule({
|
||
find: config.find,
|
||
handler: ({ state, range, match, pasteEvent }) => {
|
||
const attributes = callOrReturn(config.getAttributes, void 0, match, pasteEvent);
|
||
if (attributes === false || attributes === null) {
|
||
return null;
|
||
}
|
||
const { tr } = state;
|
||
const captureGroup = match[match.length - 1];
|
||
const fullMatch = match[0];
|
||
let markEnd = range.to;
|
||
if (captureGroup) {
|
||
const startSpaces = fullMatch.search(/\S/);
|
||
const textStart = range.from + fullMatch.indexOf(captureGroup);
|
||
const textEnd = textStart + captureGroup.length;
|
||
const excludedMarks = getMarksBetween(range.from, range.to, state.doc).filter((item) => {
|
||
const excluded = item.mark.type.excluded;
|
||
return excluded.find((type) => type === config.type && type !== item.mark.type);
|
||
}).filter((item) => item.to > textStart);
|
||
if (excludedMarks.length) {
|
||
return null;
|
||
}
|
||
if (textEnd < range.to) {
|
||
tr.delete(textEnd, range.to);
|
||
}
|
||
if (textStart > range.from) {
|
||
tr.delete(range.from + startSpaces, textStart);
|
||
}
|
||
markEnd = range.from + startSpaces + captureGroup.length;
|
||
tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}));
|
||
tr.removeStoredMark(config.type);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
function escapeForRegEx(string) {
|
||
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||
}
|
||
function isString(value) {
|
||
return typeof value === "string";
|
||
}
|
||
function nodePasteRule(config) {
|
||
return new PasteRule({
|
||
find: config.find,
|
||
handler({ match, chain, range, pasteEvent }) {
|
||
const attributes = callOrReturn(config.getAttributes, void 0, match, pasteEvent);
|
||
const content = callOrReturn(config.getContent, void 0, attributes);
|
||
if (attributes === false || attributes === null) {
|
||
return null;
|
||
}
|
||
const node = { type: config.type.name, attrs: attributes };
|
||
if (content) {
|
||
node.content = content;
|
||
}
|
||
if (match.input) {
|
||
chain().deleteRange(range).insertContentAt(range.from, node);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
function textPasteRule(config) {
|
||
return new PasteRule({
|
||
find: config.find,
|
||
handler: ({ state, range, match }) => {
|
||
let insert = config.replace;
|
||
let start = range.from;
|
||
const end = range.to;
|
||
if (match[1]) {
|
||
const offset = match[0].lastIndexOf(match[1]);
|
||
insert += match[0].slice(offset + match[1].length);
|
||
start += offset;
|
||
const cutOff = start - end;
|
||
if (cutOff > 0) {
|
||
insert = match[0].slice(offset - cutOff, offset) + insert;
|
||
start = end;
|
||
}
|
||
}
|
||
state.tr.insertText(insert, start, end);
|
||
}
|
||
});
|
||
}
|
||
var Tracker = class {
|
||
constructor(transaction) {
|
||
this.transaction = transaction;
|
||
this.currentStep = this.transaction.steps.length;
|
||
}
|
||
map(position) {
|
||
let deleted = false;
|
||
const mappedPosition = this.transaction.steps.slice(this.currentStep).reduce((newPosition, step) => {
|
||
const mapResult = step.getMap().mapResult(newPosition);
|
||
if (mapResult.deleted) {
|
||
deleted = true;
|
||
}
|
||
return mapResult.pos;
|
||
}, position);
|
||
return {
|
||
position: mappedPosition,
|
||
deleted
|
||
};
|
||
}
|
||
};
|
||
|
||
export {
|
||
Decoration,
|
||
DecorationSet,
|
||
createChainableState,
|
||
CommandManager,
|
||
getExtensionField,
|
||
splitExtensions,
|
||
getAttributesFromExtensions,
|
||
getNodeType,
|
||
mergeAttributes,
|
||
getRenderedAttributes,
|
||
isFunction,
|
||
callOrReturn,
|
||
isEmptyObject,
|
||
fromString,
|
||
injectExtensionAttributesToParseRule,
|
||
getSchemaByResolvedExtensions,
|
||
getSchemaTypeByName,
|
||
isExtensionRulesEnabled,
|
||
getHTMLFromFragment,
|
||
getTextContentFromNodes,
|
||
isRegExp,
|
||
InputRule,
|
||
inputRulesPlugin,
|
||
isPlainObject,
|
||
mergeDeep,
|
||
Mark2 as Mark,
|
||
isNumber,
|
||
PasteRule,
|
||
pasteRulesPlugin,
|
||
findDuplicates,
|
||
Extension,
|
||
getTextBetween,
|
||
getTextSerializersFromSchema,
|
||
objectIncludes,
|
||
getMarkRange,
|
||
getMarkType,
|
||
isTextSelection,
|
||
minMax,
|
||
resolveFocusPosition,
|
||
isiOS,
|
||
elementFromString,
|
||
createNodeFromContent,
|
||
selectionToInsertionEnd,
|
||
isMacOS,
|
||
isNodeActive,
|
||
getSchemaTypeNameByName,
|
||
deleteProps,
|
||
createDocument,
|
||
getMarkAttributes,
|
||
combineTransactionSteps,
|
||
defaultBlockAt2 as defaultBlockAt,
|
||
findChildren,
|
||
findChildrenInRange,
|
||
findParentNodeClosestToPos,
|
||
findParentNode,
|
||
getSchema,
|
||
generateHTML,
|
||
generateJSON,
|
||
getText2 as getText,
|
||
generateText,
|
||
getNodeAttributes,
|
||
getAttributes,
|
||
removeDuplicates,
|
||
getChangedRanges,
|
||
getDebugJSON,
|
||
getMarksBetween,
|
||
getNodeAtPosition,
|
||
getSplittedAttributes,
|
||
isMarkActive,
|
||
isActive,
|
||
isAtEndOfNode,
|
||
isAtStartOfNode,
|
||
isList,
|
||
isNodeEmpty,
|
||
isNodeSelection,
|
||
posToDOMRect,
|
||
index,
|
||
NodePos,
|
||
createStyleTag,
|
||
Editor,
|
||
markInputRule,
|
||
nodeInputRule,
|
||
textblockTypeInputRule,
|
||
textInputRule,
|
||
wrappingInputRule,
|
||
Node2 as Node,
|
||
NodeView,
|
||
markPasteRule,
|
||
escapeForRegEx,
|
||
isString,
|
||
nodePasteRule,
|
||
textPasteRule,
|
||
Tracker
|
||
};
|
||
//# sourceMappingURL=chunk-BTMXSOA4.js.map
|