You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					5387 lines
				
				217 KiB
			
		
		
			
		
	
	
					5387 lines
				
				217 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								import { TextSelection, NodeSelection, Selection, AllSelection } from 'prosemirror-state';
							 | 
						||
| 
								 | 
							
								import { DOMSerializer, Fragment, Mark, Slice, DOMParser } from 'prosemirror-model';
							 | 
						||
| 
								 | 
							
								import { dropPoint } from 'prosemirror-transform';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const domIndex = function (node) {
							 | 
						||
| 
								 | 
							
								    for (var index = 0;; index++) {
							 | 
						||
| 
								 | 
							
								        node = node.previousSibling;
							 | 
						||
| 
								 | 
							
								        if (!node)
							 | 
						||
| 
								 | 
							
								            return index;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								const parentNode = function (node) {
							 | 
						||
| 
								 | 
							
								    let parent = node.assignedSlot || node.parentNode;
							 | 
						||
| 
								 | 
							
								    return parent && parent.nodeType == 11 ? parent.host : parent;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								let reusedRange = null;
							 | 
						||
| 
								 | 
							
								// Note that this will always return the same range, because DOM range
							 | 
						||
| 
								 | 
							
								// objects are every expensive, and keep slowing down subsequent DOM
							 | 
						||
| 
								 | 
							
								// updates, for some reason.
							 | 
						||
| 
								 | 
							
								const 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;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								// Scans forward and backward through DOM positions equivalent to the
							 | 
						||
| 
								 | 
							
								// given one to see if the two are in the same place (i.e. after a
							 | 
						||
| 
								 | 
							
								// text node vs at the end of that text node)
							 | 
						||
| 
								 | 
							
								const isEquivalentPosition = function (node, off, targetNode, targetOff) {
							 | 
						||
| 
								 | 
							
								    return targetNode && (scanFor(node, off, targetNode, targetOff, -1) ||
							 | 
						||
| 
								 | 
							
								        scanFor(node, off, targetNode, targetOff, 1));
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								const 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 isOnEdge(node, offset, parent) {
							 | 
						||
| 
								 | 
							
								    for (let atStart = offset == 0, atEnd = offset == nodeSize(node); atStart || atEnd;) {
							 | 
						||
| 
								 | 
							
								        if (node == parent)
							 | 
						||
| 
								 | 
							
								            return true;
							 | 
						||
| 
								 | 
							
								        let index = domIndex(node);
							 | 
						||
| 
								 | 
							
								        node = node.parentNode;
							 | 
						||
| 
								 | 
							
								        if (!node)
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        atStart = atStart && index == 0;
							 | 
						||
| 
								 | 
							
								        atEnd = atEnd && index == 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);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Work around Chrome issue https://bugs.chromium.org/p/chromium/issues/detail?id=447523
							 | 
						||
| 
								 | 
							
								// (isCollapsed inappropriately returns true in shadow dom)
							 | 
						||
| 
								 | 
							
								const 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(doc) {
							 | 
						||
| 
								 | 
							
								    let elt = doc.activeElement;
							 | 
						||
| 
								 | 
							
								    while (elt && elt.shadowRoot)
							 | 
						||
| 
								 | 
							
								        elt = elt.shadowRoot.activeElement;
							 | 
						||
| 
								 | 
							
								    return elt;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const nav = typeof navigator != "undefined" ? navigator : null;
							 | 
						||
| 
								 | 
							
								const doc = typeof document != "undefined" ? document : null;
							 | 
						||
| 
								 | 
							
								const agent = (nav && nav.userAgent) || "";
							 | 
						||
| 
								 | 
							
								const ie_edge = /Edge\/(\d+)/.exec(agent);
							 | 
						||
| 
								 | 
							
								const ie_upto10 = /MSIE \d/.exec(agent);
							 | 
						||
| 
								 | 
							
								const ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(agent);
							 | 
						||
| 
								 | 
							
								const ie = !!(ie_upto10 || ie_11up || ie_edge);
							 | 
						||
| 
								 | 
							
								const ie_version = ie_upto10 ? document.documentMode : ie_11up ? +ie_11up[1] : ie_edge ? +ie_edge[1] : 0;
							 | 
						||
| 
								 | 
							
								const gecko = !ie && /gecko\/(\d+)/i.test(agent);
							 | 
						||
| 
								 | 
							
								gecko && +(/Firefox\/(\d+)/.exec(agent) || [0, 0])[1];
							 | 
						||
| 
								 | 
							
								const _chrome = !ie && /Chrome\/(\d+)/.exec(agent);
							 | 
						||
| 
								 | 
							
								const chrome = !!_chrome;
							 | 
						||
| 
								 | 
							
								const chrome_version = _chrome ? +_chrome[1] : 0;
							 | 
						||
| 
								 | 
							
								const safari = !ie && !!nav && /Apple Computer/.test(nav.vendor);
							 | 
						||
| 
								 | 
							
								// Is true for both iOS and iPadOS for convenience
							 | 
						||
| 
								 | 
							
								const ios = safari && (/Mobile\/\w+/.test(agent) || !!nav && nav.maxTouchPoints > 2);
							 | 
						||
| 
								 | 
							
								const mac = ios || (nav ? /Mac/.test(nav.platform) : false);
							 | 
						||
| 
								 | 
							
								const android = /Android \d/.test(agent);
							 | 
						||
| 
								 | 
							
								const webkit = !!doc && "webkitFontSmoothing" in doc.documentElement.style;
							 | 
						||
| 
								 | 
							
								const webkit_version = webkit ? +(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent) || [0, 0])[1] : 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function windowRect(doc) {
							 | 
						||
| 
								 | 
							
								    return { left: 0, right: doc.documentElement.clientWidth,
							 | 
						||
| 
								 | 
							
								        top: 0, bottom: doc.documentElement.clientHeight };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function getSide(value, side) {
							 | 
						||
| 
								 | 
							
								    return typeof value == "number" ? value : value[side];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function clientRect(node) {
							 | 
						||
| 
								 | 
							
								    let rect = node.getBoundingClientRect();
							 | 
						||
| 
								 | 
							
								    // Adjust for elements with style "transform: scale()"
							 | 
						||
| 
								 | 
							
								    let scaleX = (rect.width / node.offsetWidth) || 1;
							 | 
						||
| 
								 | 
							
								    let scaleY = (rect.height / node.offsetHeight) || 1;
							 | 
						||
| 
								 | 
							
								    // Make sure scrollbar width isn't included in the rectangle
							 | 
						||
| 
								 | 
							
								    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 doc = 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 == doc.body;
							 | 
						||
| 
								 | 
							
								        let bounding = atTop ? windowRect(doc) : 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 - 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) {
							 | 
						||
| 
								 | 
							
								                doc.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)
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Store the scroll position of the editor's parent nodes, along with
							 | 
						||
| 
								 | 
							
								// the top position of an element near the top of the editor, which
							 | 
						||
| 
								 | 
							
								// will be used to make sure the visible viewport remains stable even
							 | 
						||
| 
								 | 
							
								// when the size of the content above changes.
							 | 
						||
| 
								 | 
							
								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: refDOM, refTop: refTop, stack: scrollStack(view.dom) };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function scrollStack(dom) {
							 | 
						||
| 
								 | 
							
								    let stack = [], doc = dom.ownerDocument;
							 | 
						||
| 
								 | 
							
								    for (let cur = dom; cur; cur = parentNode(cur)) {
							 | 
						||
| 
								 | 
							
								        stack.push({ dom: cur, top: cur.scrollTop, left: cur.scrollLeft });
							 | 
						||
| 
								 | 
							
								        if (dom == doc)
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return stack;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Reset the scroll position of the editor's parent nodes to that what
							 | 
						||
| 
								 | 
							
								// it was before, when storeScrollPos was called.
							 | 
						||
| 
								 | 
							
								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;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								let preventScrollSupported = null;
							 | 
						||
| 
								 | 
							
								// Feature-detects support for .focus({preventScroll: true}), and uses
							 | 
						||
| 
								 | 
							
								// a fallback kludge when not supported.
							 | 
						||
| 
								 | 
							
								function focusPreventScroll(dom) {
							 | 
						||
| 
								 | 
							
								    if (dom.setActive)
							 | 
						||
| 
								 | 
							
								        return dom.setActive(); // in IE
							 | 
						||
| 
								 | 
							
								    if (preventScrollSupported)
							 | 
						||
| 
								 | 
							
								        return dom.focus(preventScrollSupported);
							 | 
						||
| 
								 | 
							
								    let stored = scrollStack(dom);
							 | 
						||
| 
								 | 
							
								    dom.focus(preventScrollSupported == null ? {
							 | 
						||
| 
								 | 
							
								        get preventScroll() {
							 | 
						||
| 
								 | 
							
								            preventScrollSupported = { preventScroll: true };
							 | 
						||
| 
								 | 
							
								            return true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    } : undefined);
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (!closest && (coords.left >= rect.right && coords.top >= rect.top ||
							 | 
						||
| 
								 | 
							
								                coords.left >= rect.left && coords.top >= rect.bottom))
							 | 
						||
| 
								 | 
							
								                offset = childIndex + 1;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    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) {
							 | 
						||
| 
								 | 
							
								    // Browser (in caretPosition/RangeFromPoint) will agressively
							 | 
						||
| 
								 | 
							
								    // normalize towards nearby inline nodes. Since we are interested in
							 | 
						||
| 
								 | 
							
								    // positions between block nodes too, we first walk up the hierarchy
							 | 
						||
| 
								 | 
							
								    // of nodes to see if there are block nodes that the coordinates
							 | 
						||
| 
								 | 
							
								    // fall outside of. If so, we take the position before/after that
							 | 
						||
| 
								 | 
							
								    // block. If not, we call `posFromDOM` on the raw node/offset.
							 | 
						||
| 
								 | 
							
								    let outside = -1;
							 | 
						||
| 
								 | 
							
								    for (let cur = node;;) {
							 | 
						||
| 
								 | 
							
								        if (cur == view.dom)
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								        let desc = view.docView.nearestDesc(cur, true);
							 | 
						||
| 
								 | 
							
								        if (!desc)
							 | 
						||
| 
								 | 
							
								            return null;
							 | 
						||
| 
								 | 
							
								        if (desc.node.isBlock && desc.parent) {
							 | 
						||
| 
								 | 
							
								            let rect = desc.dom.getBoundingClientRect();
							 | 
						||
| 
								 | 
							
								            if (rect.left > coords.left || rect.top > coords.top)
							 | 
						||
| 
								 | 
							
								                outside = desc.posBefore;
							 | 
						||
| 
								 | 
							
								            else if (rect.right < coords.left || rect.bottom < coords.top)
							 | 
						||
| 
								 | 
							
								                outside = desc.posAfter;
							 | 
						||
| 
								 | 
							
								            else
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        cur = desc.dom.parentNode;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return outside > -1 ? outside : 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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Given an x,y position on the editor, get the position in the document.
							 | 
						||
| 
								 | 
							
								function posAtCoords(view, coords) {
							 | 
						||
| 
								 | 
							
								    let doc = view.dom.ownerDocument, node, offset = 0;
							 | 
						||
| 
								 | 
							
								    if (doc.caretPositionFromPoint) {
							 | 
						||
| 
								 | 
							
								        try { // Firefox throws for this call in hard-to-predict circumstances (#994)
							 | 
						||
| 
								 | 
							
								            let pos = doc.caretPositionFromPoint(coords.left, coords.top);
							 | 
						||
| 
								 | 
							
								            if (pos)
							 | 
						||
| 
								 | 
							
								                ({ offsetNode: node, offset } = pos);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        catch (_) { }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (!node && doc.caretRangeFromPoint) {
							 | 
						||
| 
								 | 
							
								        let range = doc.caretRangeFromPoint(coords.left, coords.top);
							 | 
						||
| 
								 | 
							
								        if (range)
							 | 
						||
| 
								 | 
							
								            ({ startContainer: node, startOffset: offset } = range);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    let elt = (view.root.elementFromPoint ? view.root : doc)
							 | 
						||
| 
								 | 
							
								        .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;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // Safari's caretRangeFromPoint returns nonsense when on a draggable element
							 | 
						||
| 
								 | 
							
								    if (safari) {
							 | 
						||
| 
								 | 
							
								        for (let p = elt; node && p; p = parentNode(p))
							 | 
						||
| 
								 | 
							
								            if (p.draggable)
							 | 
						||
| 
								 | 
							
								                node = undefined;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    elt = targetKludge(elt, coords);
							 | 
						||
| 
								 | 
							
								    if (node) {
							 | 
						||
| 
								 | 
							
								        if (gecko && node.nodeType == 1) {
							 | 
						||
| 
								 | 
							
								            // Firefox will sometimes return offsets into <input> nodes, which
							 | 
						||
| 
								 | 
							
								            // have no actual children, from caretPositionFromPoint (#953)
							 | 
						||
| 
								 | 
							
								            offset = Math.min(offset, node.childNodes.length);
							 | 
						||
| 
								 | 
							
								            // It'll also move the returned position before image nodes,
							 | 
						||
| 
								 | 
							
								            // even if those are behind it.
							 | 
						||
| 
								 | 
							
								            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++;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // Suspiciously specific kludge to work around caret*FromPoint
							 | 
						||
| 
								 | 
							
								        // never returning a position at the end of the document
							 | 
						||
| 
								 | 
							
								        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;
							 | 
						||
| 
								 | 
							
								        // Ignore positions directly after a BR, since caret*FromPoint
							 | 
						||
| 
								 | 
							
								        // 'round up' positions that would be more accurately placed
							 | 
						||
| 
								 | 
							
								        // before the BR node.
							 | 
						||
| 
								 | 
							
								        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 singleRect(target, bias) {
							 | 
						||
| 
								 | 
							
								    let rects = target.getClientRects();
							 | 
						||
| 
								 | 
							
								    return !rects.length ? target.getBoundingClientRect() : rects[bias < 0 ? 0 : rects.length - 1];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								const BIDI = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
							 | 
						||
| 
								 | 
							
								// Given a position in the document model, get a bounding box of the
							 | 
						||
| 
								 | 
							
								// character at that position, relative to the window.
							 | 
						||
| 
								 | 
							
								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) {
							 | 
						||
| 
								 | 
							
								        // These browsers support querying empty text ranges. Prefer that in
							 | 
						||
| 
								 | 
							
								        // bidi context or when at the end of a node.
							 | 
						||
| 
								 | 
							
								        if (supportEmptyRange && (BIDI.test(node.nodeValue) || (side < 0 ? !offset : offset == node.nodeValue.length))) {
							 | 
						||
| 
								 | 
							
								            let rect = singleRect(textRange(node, offset, offset), side);
							 | 
						||
| 
								 | 
							
								            // Firefox returns bad results (the position before the space)
							 | 
						||
| 
								 | 
							
								            // when querying a position directly after line-broken
							 | 
						||
| 
								 | 
							
								            // whitespace. Detect this situation and and kludge around it
							 | 
						||
| 
								 | 
							
								            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), 1), takeSide < 0);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    let $dom = view.state.doc.resolve(pos - (atom || 0));
							 | 
						||
| 
								 | 
							
								    // Return a horizontal line in block context
							 | 
						||
| 
								 | 
							
								    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);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // Inline, not in text node (this is not Bidi-safe)
							 | 
						||
| 
								 | 
							
								    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))
							 | 
						||
| 
								 | 
							
								            // BR nodes tend to only return the rectangle before them.
							 | 
						||
| 
								 | 
							
								            // Only use them if they are the last element in their parent
							 | 
						||
| 
								 | 
							
								            : 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);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // All else failed, just try to get a rectangle for the target node
							 | 
						||
| 
								 | 
							
								    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();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Whether vertical position motion in a given direction
							 | 
						||
| 
								 | 
							
								// from a position would leave a text block.
							 | 
						||
| 
								 | 
							
								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.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;
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								const 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 the textblock is all LTR, or the browser doesn't support
							 | 
						||
| 
								 | 
							
								    // Selection.modify (Edge), fall back to a primitive approach
							 | 
						||
| 
								 | 
							
								    if (!maybeRTL.test($head.parent.textContent) || !sel.modify)
							 | 
						||
| 
								 | 
							
								        return dir == "left" || dir == "backward" ? atStart : atEnd;
							 | 
						||
| 
								 | 
							
								    return withFlushedState(view, state, () => {
							 | 
						||
| 
								 | 
							
								        // This is a huge hack, but appears to be the best we can
							 | 
						||
| 
								 | 
							
								        // currently do: use `Selection.modify` to move the selection by
							 | 
						||
| 
								 | 
							
								        // one character, and see if that moves the cursor out of the
							 | 
						||
| 
								 | 
							
								        // textblock (or doesn't move it at all, when at the start/end of
							 | 
						||
| 
								 | 
							
								        // the document).
							 | 
						||
| 
								 | 
							
								        let { focusNode: oldNode, focusOffset: oldOff, anchorNode, anchorOffset } = view.domSelectionRange();
							 | 
						||
| 
								 | 
							
								        let oldBidiLevel = sel.caretBidiLevel // Only for Firefox
							 | 
						||
| 
								 | 
							
								        ;
							 | 
						||
| 
								 | 
							
								        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);
							 | 
						||
| 
								 | 
							
								        // Restore the previous selection
							 | 
						||
| 
								 | 
							
								        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;
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								let cachedState = null;
							 | 
						||
| 
								 | 
							
								let cachedDir = null;
							 | 
						||
| 
								 | 
							
								let 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);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// View descriptions are data structures that describe the DOM that is
							 | 
						||
| 
								 | 
							
								// used to represent the editor's content. They are used for:
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// - Incremental redrawing when the document changes
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// - Figuring out what part of the document a given DOM position
							 | 
						||
| 
								 | 
							
								//   corresponds to
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// - Wiring in custom implementations of the editing interface for a
							 | 
						||
| 
								 | 
							
								//   given node
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// They form a doubly-linked mutable tree, starting at `view.docView`.
							 | 
						||
| 
								 | 
							
								const NOT_DIRTY = 0, CHILD_DIRTY = 1, CONTENT_DIRTY = 2, NODE_DIRTY = 3;
							 | 
						||
| 
								 | 
							
								// Superclass for the various kinds of descriptions. Defines their
							 | 
						||
| 
								 | 
							
								// basic structure and shared methods.
							 | 
						||
| 
								 | 
							
								class ViewDesc {
							 | 
						||
| 
								 | 
							
								    constructor(parent, children, dom, 
							 | 
						||
| 
								 | 
							
								    // This is the node that holds the child views. It may be null for
							 | 
						||
| 
								 | 
							
								    // descs that don't have children.
							 | 
						||
| 
								 | 
							
								    contentDOM) {
							 | 
						||
| 
								 | 
							
								        this.parent = parent;
							 | 
						||
| 
								 | 
							
								        this.children = children;
							 | 
						||
| 
								 | 
							
								        this.dom = dom;
							 | 
						||
| 
								 | 
							
								        this.contentDOM = contentDOM;
							 | 
						||
| 
								 | 
							
								        this.dirty = NOT_DIRTY;
							 | 
						||
| 
								 | 
							
								        // An expando property on the DOM node provides a link back to its
							 | 
						||
| 
								 | 
							
								        // description.
							 | 
						||
| 
								 | 
							
								        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 = undefined;
							 | 
						||
| 
								 | 
							
								        if (this.dom.pmViewDesc == this)
							 | 
						||
| 
								 | 
							
								            this.dom.pmViewDesc = undefined;
							 | 
						||
| 
								 | 
							
								        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 the DOM position is in the content, use the child desc after
							 | 
						||
| 
								 | 
							
								        // it to figure out a position.
							 | 
						||
| 
								 | 
							
								        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;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // Otherwise, use various heuristics, falling back on the bias
							 | 
						||
| 
								 | 
							
								        // parameter, to determine whether to return the position at the
							 | 
						||
| 
								 | 
							
								        // start or at the end of this view desc.
							 | 
						||
| 
								 | 
							
								        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;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // Scan up the dom finding the first desc that is a descendant of
							 | 
						||
| 
								 | 
							
								    // this one.
							 | 
						||
| 
								 | 
							
								    nearestDesc(dom, onlyNodes = false) {
							 | 
						||
| 
								 | 
							
								        for (let first = true, cur = dom; cur; cur = cur.parentNode) {
							 | 
						||
| 
								 | 
							
								            let desc = this.getDesc(cur), nodeDOM;
							 | 
						||
| 
								 | 
							
								            if (desc && (!onlyNodes || desc.node)) {
							 | 
						||
| 
								 | 
							
								                // If dom is outside of this desc's nodeDOM, don't count it.
							 | 
						||
| 
								 | 
							
								                if (first && (nodeDOM = desc.nodeDOM) &&
							 | 
						||
| 
								 | 
							
								                    !(nodeDOM.nodeType == 1 ? nodeDOM.contains(dom.nodeType == 1 ? dom : dom.parentNode) : nodeDOM == dom))
							 | 
						||
| 
								 | 
							
								                    first = 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 };
							 | 
						||
| 
								 | 
							
								        // First find the position in the child array
							 | 
						||
| 
								 | 
							
								        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 this points into the middle of a child, call through
							 | 
						||
| 
								 | 
							
								        if (offset)
							 | 
						||
| 
								 | 
							
								            return this.children[i].domFromPos(offset - this.children[i].border, side);
							 | 
						||
| 
								 | 
							
								        // Go back if there were any zero-length widgets with side >= 0 before this point
							 | 
						||
| 
								 | 
							
								        for (let prev; i && !(prev = this.children[i - 1]).size && prev instanceof WidgetViewDesc && prev.side >= 0; i--) { }
							 | 
						||
| 
								 | 
							
								        // Scan towards the first useable node
							 | 
						||
| 
								 | 
							
								        if (side <= 0) {
							 | 
						||
| 
								 | 
							
								            let prev, enter = true;
							 | 
						||
| 
								 | 
							
								            for (;; i--, enter = false) {
							 | 
						||
| 
								 | 
							
								                prev = i ? this.children[i - 1] : null;
							 | 
						||
| 
								 | 
							
								                if (!prev || prev.dom.parentNode == this.contentDOM)
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (prev && side && enter && !prev.border && !prev.domAtom)
							 | 
						||
| 
								 | 
							
								                return prev.domFromPos(prev.size, side);
							 | 
						||
| 
								 | 
							
								            return { node: this.contentDOM, offset: prev ? domIndex(prev.dom) + 1 : 0 };
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            let next, enter = true;
							 | 
						||
| 
								 | 
							
								            for (;; i++, enter = false) {
							 | 
						||
| 
								 | 
							
								                next = i < this.children.length ? this.children[i] : null;
							 | 
						||
| 
								 | 
							
								                if (!next || next.dom.parentNode == this.contentDOM)
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (next && enter && !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, base = 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 = base, i = 0;; i++) {
							 | 
						||
| 
								 | 
							
								            let child = this.children[i], end = offset + child.size;
							 | 
						||
| 
								 | 
							
								            if (fromOffset == -1 && from <= end) {
							 | 
						||
| 
								 | 
							
								                let childBase = offset + child.border;
							 | 
						||
| 
								 | 
							
								                // FIXME maybe descend mark views to parse a narrower range?
							 | 
						||
| 
								 | 
							
								                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, root, force = false) {
							 | 
						||
| 
								 | 
							
								        // If the selection falls entirely in a child, give it to that child
							 | 
						||
| 
								 | 
							
								        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, root, force);
							 | 
						||
| 
								 | 
							
								            offset = end;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let anchorDOM = this.domFromPos(anchor, anchor ? -1 : 1);
							 | 
						||
| 
								 | 
							
								        let headDOM = head == anchor ? anchorDOM : this.domFromPos(head, head ? -1 : 1);
							 | 
						||
| 
								 | 
							
								        let domSel = root.getSelection();
							 | 
						||
| 
								 | 
							
								        let brKludge = false;
							 | 
						||
| 
								 | 
							
								        // On Firefox, using Selection.collapse to put the cursor after a
							 | 
						||
| 
								 | 
							
								        // BR node for some reason doesn't always work (#1073). On Safari,
							 | 
						||
| 
								 | 
							
								        // the cursor sometimes inexplicable visually lags behind its
							 | 
						||
| 
								 | 
							
								        // reported position in such situations (#1092).
							 | 
						||
| 
								 | 
							
								        if ((gecko || safari) && anchor == head) {
							 | 
						||
| 
								 | 
							
								            let { node, offset } = anchorDOM;
							 | 
						||
| 
								 | 
							
								            if (node.nodeType == 3) {
							 | 
						||
| 
								 | 
							
								                brKludge = !!(offset && node.nodeValue[offset - 1] == "\n");
							 | 
						||
| 
								 | 
							
								                // Issue #1128
							 | 
						||
| 
								 | 
							
								                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");
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // Firefox can act strangely when the selection is in front of an
							 | 
						||
| 
								 | 
							
								        // uneditable node. See #1163 and https://bugzilla.mozilla.org/show_bug.cgi?id=1709536
							 | 
						||
| 
								 | 
							
								        if (gecko && domSel.focusNode && domSel.focusNode != headDOM.node && domSel.focusNode.nodeType == 1) {
							 | 
						||
| 
								 | 
							
								            let after = domSel.focusNode.childNodes[domSel.focusOffset];
							 | 
						||
| 
								 | 
							
								            if (after && after.contentEditable == "false")
							 | 
						||
| 
								 | 
							
								                force = true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (!(force || brKludge && safari) &&
							 | 
						||
| 
								 | 
							
								            isEquivalentPosition(anchorDOM.node, anchorDOM.offset, domSel.anchorNode, domSel.anchorOffset) &&
							 | 
						||
| 
								 | 
							
								            isEquivalentPosition(headDOM.node, headDOM.offset, domSel.focusNode, domSel.focusOffset))
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        // Selection.extend can be used to create an 'inverted' selection
							 | 
						||
| 
								 | 
							
								        // (one where the focus is before the anchor), but not all
							 | 
						||
| 
								 | 
							
								        // browsers support it yet.
							 | 
						||
| 
								 | 
							
								        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 (_) {
							 | 
						||
| 
								 | 
							
								                // In some cases with Chrome the selection is empty after calling
							 | 
						||
| 
								 | 
							
								                // collapse, even when it should be valid. This appears to be a bug, but
							 | 
						||
| 
								 | 
							
								                // it is difficult to isolate. If this happens fallback to the old path
							 | 
						||
| 
								 | 
							
								                // without using extend.
							 | 
						||
| 
								 | 
							
								                // Similarly, this could crash on Safari if the editor is hidden, and
							 | 
						||
| 
								 | 
							
								                // there was no selection.
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        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; }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// A widget desc represents a widget decoration, which is a DOM node
							 | 
						||
| 
								 | 
							
								// drawn between the document nodes.
							 | 
						||
| 
								 | 
							
								class WidgetViewDesc 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; }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								class CompositionViewDesc 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;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// A mark desc represents a mark. May have multiple children,
							 | 
						||
| 
								 | 
							
								// depending on how the mark is split. Note that marks are drawn using
							 | 
						||
| 
								 | 
							
								// a fixed nesting order, for simplicity and predictability, so in
							 | 
						||
| 
								 | 
							
								// some cases they will be split more often than would appear
							 | 
						||
| 
								 | 
							
								// necessary.
							 | 
						||
| 
								 | 
							
								class MarkViewDesc extends ViewDesc {
							 | 
						||
| 
								 | 
							
								    constructor(parent, mark, dom, contentDOM) {
							 | 
						||
| 
								 | 
							
								        super(parent, [], dom, contentDOM);
							 | 
						||
| 
								 | 
							
								        this.mark = mark;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    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));
							 | 
						||
| 
								 | 
							
								        return new MarkViewDesc(parent, mark, spec.dom, spec.contentDOM || spec.dom);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    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 || undefined };
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    matchesMark(mark) { return this.dirty != NODE_DIRTY && this.mark.eq(mark); }
							 | 
						||
| 
								 | 
							
								    markDirty(from, to) {
							 | 
						||
| 
								 | 
							
								        super.markDirty(from, to);
							 | 
						||
| 
								 | 
							
								        // Move dirty info to nearest node view
							 | 
						||
| 
								 | 
							
								        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;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Node view descs are the main, most common type of view desc, and
							 | 
						||
| 
								 | 
							
								// correspond to an actual node in the document. Unlike mark descs,
							 | 
						||
| 
								 | 
							
								// they populate their child array themselves.
							 | 
						||
| 
								 | 
							
								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;
							 | 
						||
| 
								 | 
							
								        if (contentDOM)
							 | 
						||
| 
								 | 
							
								            this.updateChildren(view, pos);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // 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, () => {
							 | 
						||
| 
								 | 
							
								            // (This is a function that allows the custom view to find its
							 | 
						||
| 
								 | 
							
								            // own position)
							 | 
						||
| 
								 | 
							
								            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) {
							 | 
						||
| 
								 | 
							
								            ({ dom, contentDOM } = DOMSerializer.renderSpec(document, node.type.spec.toDOM(node)));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (!contentDOM && !node.isText && dom.nodeName != "BR") { // Chrome gets confused by <br contenteditable=false>
							 | 
						||
| 
								 | 
							
								            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() {
							 | 
						||
| 
								 | 
							
								        // Experimental kludge to allow opt-in re-parsing of nodes
							 | 
						||
| 
								 | 
							
								        if (this.node.type.spec.reparseInView)
							 | 
						||
| 
								 | 
							
								            return null;
							 | 
						||
| 
								 | 
							
								        // FIXME the assumption that this can always return the current
							 | 
						||
| 
								 | 
							
								        // attrs means that if the user somehow manages to change the
							 | 
						||
| 
								 | 
							
								        // attrs in the dom, that won't be picked up. Not entirely sure
							 | 
						||
| 
								 | 
							
								        // whether this is a problem
							 | 
						||
| 
								 | 
							
								        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 {
							 | 
						||
| 
								 | 
							
								            // Chrome likes to randomly recreate parent nodes when
							 | 
						||
| 
								 | 
							
								            // backspacing things. When that happens, this tries to find the
							 | 
						||
| 
								 | 
							
								            // new parent.
							 | 
						||
| 
								 | 
							
								            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);
							 | 
						||
| 
								 | 
							
								            // If the next node is a desc matching this widget, reuse it,
							 | 
						||
| 
								 | 
							
								            // otherwise insert the widget as a new view desc.
							 | 
						||
| 
								 | 
							
								            updater.placeWidget(widget, view, off);
							 | 
						||
| 
								 | 
							
								        }, (child, outerDeco, innerDeco, i) => {
							 | 
						||
| 
								 | 
							
								            // Make sure the wrapping mark descs match the node's marks.
							 | 
						||
| 
								 | 
							
								            updater.syncToMarks(child.marks, inline, view);
							 | 
						||
| 
								 | 
							
								            // Try several strategies for drawing this node
							 | 
						||
| 
								 | 
							
								            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)) ;
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                // Add it as a new view
							 | 
						||
| 
								 | 
							
								                updater.addNode(child, outerDeco, innerDeco, view, off);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            off += child.nodeSize;
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        // Drop all remaining descs after the current position.
							 | 
						||
| 
								 | 
							
								        updater.syncToMarks([], inline, view);
							 | 
						||
| 
								 | 
							
								        if (this.node.isTextblock)
							 | 
						||
| 
								 | 
							
								            updater.addTextblockHacks();
							 | 
						||
| 
								 | 
							
								        updater.destroyRest();
							 | 
						||
| 
								 | 
							
								        // Sync the DOM if anything changed
							 | 
						||
| 
								 | 
							
								        if (updater.changed || this.dirty == CONTENT_DIRTY) {
							 | 
						||
| 
								 | 
							
								            // May have to protect focused DOM from being changed if a composition is active
							 | 
						||
| 
								 | 
							
								            if (localComposition)
							 | 
						||
| 
								 | 
							
								                this.protectLocalComposition(view, localComposition);
							 | 
						||
| 
								 | 
							
								            renderDescs(this.contentDOM, this.children, view);
							 | 
						||
| 
								 | 
							
								            if (ios)
							 | 
						||
| 
								 | 
							
								                iosHacks(this.dom);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    localCompositionInfo(view, pos) {
							 | 
						||
| 
								 | 
							
								        // Only do something if both the selection and a focused text node
							 | 
						||
| 
								 | 
							
								        // are inside of this node
							 | 
						||
| 
								 | 
							
								        let { from, to } = view.state.selection;
							 | 
						||
| 
								 | 
							
								        if (!(view.state.selection instanceof TextSelection) || from < pos || to > pos + this.node.content.size)
							 | 
						||
| 
								 | 
							
								            return null;
							 | 
						||
| 
								 | 
							
								        let sel = view.domSelectionRange();
							 | 
						||
| 
								 | 
							
								        let textNode = nearbyTextNode(sel.focusNode, sel.focusOffset);
							 | 
						||
| 
								 | 
							
								        if (!textNode || !this.dom.contains(textNode.parentNode))
							 | 
						||
| 
								 | 
							
								            return null;
							 | 
						||
| 
								 | 
							
								        if (this.node.inlineContent) {
							 | 
						||
| 
								 | 
							
								            // Find the text in the focused node in the node, stop if it's not
							 | 
						||
| 
								 | 
							
								            // there (may have been modified through other means, in which
							 | 
						||
| 
								 | 
							
								            // case it should overwritten)
							 | 
						||
| 
								 | 
							
								            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 }) {
							 | 
						||
| 
								 | 
							
								        // The node is already part of a local view desc, leave it there
							 | 
						||
| 
								 | 
							
								        if (this.getDesc(node))
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        // Create a composition view for the orphaned nodes
							 | 
						||
| 
								 | 
							
								        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 = undefined;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let desc = new CompositionViewDesc(this, topNode, node, text);
							 | 
						||
| 
								 | 
							
								        view.input.compositionNodes.push(desc);
							 | 
						||
| 
								 | 
							
								        // Patch up this.children to contain the composition view
							 | 
						||
| 
								 | 
							
								        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 = undefined;
							 | 
						||
| 
								 | 
							
								            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; }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Create a view desc for the top-level document node, to be exported
							 | 
						||
| 
								 | 
							
								// and used by the view class.
							 | 
						||
| 
								 | 
							
								function docViewDesc(doc, outerDeco, innerDeco, dom, view) {
							 | 
						||
| 
								 | 
							
								    applyOuterDeco(dom, outerDeco, doc);
							 | 
						||
| 
								 | 
							
								    return new NodeViewDesc(undefined, doc, outerDeco, innerDeco, dom, dom, dom, view, 0);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								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; }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// A dummy desc used to tag trailing BR or IMG nodes created to work
							 | 
						||
| 
								 | 
							
								// around contentEditable terribleness.
							 | 
						||
| 
								 | 
							
								class TrailingHackViewDesc 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"; }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// A separate subclass is used for customized node views, so that the
							 | 
						||
| 
								 | 
							
								// extra checks only have to be made for nodes that are actually
							 | 
						||
| 
								 | 
							
								// customized.
							 | 
						||
| 
								 | 
							
								class CustomNodeViewDesc 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) {
							 | 
						||
| 
								 | 
							
								            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, root, force) {
							 | 
						||
| 
								 | 
							
								        this.spec.setSelection ? this.spec.setSelection(anchor, head, root)
							 | 
						||
| 
								 | 
							
								            : super.setSelection(anchor, head, root, 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);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Sync the content of the given DOM node with the nodes associated
							 | 
						||
| 
								 | 
							
								// with the given array of view descs, recursing into mark descs
							 | 
						||
| 
								 | 
							
								// because this should sync the subtree for a whole node at a time.
							 | 
						||
| 
								 | 
							
								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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								const OuterDecoLevel = function (nodeName) {
							 | 
						||
| 
								 | 
							
								    if (nodeName)
							 | 
						||
| 
								 | 
							
								        this.nodeName = nodeName;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								OuterDecoLevel.prototype = Object.create(null);
							 | 
						||
| 
								 | 
							
								const 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) {
							 | 
						||
| 
								 | 
							
								    // Shortcut for trivial case
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Remove a DOM node and return its next sibling.
							 | 
						||
| 
								 | 
							
								function rm(dom) {
							 | 
						||
| 
								 | 
							
								    let next = dom.nextSibling;
							 | 
						||
| 
								 | 
							
								    dom.parentNode.removeChild(dom);
							 | 
						||
| 
								 | 
							
								    return next;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Helper class for incrementally updating a tree of mark descs and
							 | 
						||
| 
								 | 
							
								// the widget and node descs inside of them.
							 | 
						||
| 
								 | 
							
								class ViewTreeUpdater {
							 | 
						||
| 
								 | 
							
								    constructor(top, lock, view) {
							 | 
						||
| 
								 | 
							
								        this.lock = lock;
							 | 
						||
| 
								 | 
							
								        this.view = view;
							 | 
						||
| 
								 | 
							
								        // Index into `this.top`'s child array, represents the current
							 | 
						||
| 
								 | 
							
								        // update position.
							 | 
						||
| 
								 | 
							
								        this.index = 0;
							 | 
						||
| 
								 | 
							
								        // When entering a mark, the current top and index are pushed
							 | 
						||
| 
								 | 
							
								        // onto this.
							 | 
						||
| 
								 | 
							
								        this.stack = [];
							 | 
						||
| 
								 | 
							
								        // Tracks whether anything was changed
							 | 
						||
| 
								 | 
							
								        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++) {
							 | 
						||
| 
								 | 
							
								                if (this.top.children[i].matchesMark(marks[depth])) {
							 | 
						||
| 
								 | 
							
								                    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, index) {
							 | 
						||
| 
								 | 
							
								        let found = -1, targetDesc;
							 | 
						||
| 
								 | 
							
								        if (index >= this.preMatch.index &&
							 | 
						||
| 
								 | 
							
								            (targetDesc = this.preMatch.matches[index - 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, index, view) {
							 | 
						||
| 
								 | 
							
								        let child = this.top.children[index];
							 | 
						||
| 
								 | 
							
								        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, index);
							 | 
						||
| 
								 | 
							
								        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, index) {
							 | 
						||
| 
								 | 
							
								        for (let i = this.index; i < this.top.children.length; i++) {
							 | 
						||
| 
								 | 
							
								            let next = this.top.children[i];
							 | 
						||
| 
								 | 
							
								            if (next instanceof NodeViewDesc) {
							 | 
						||
| 
								 | 
							
								                let preMatch = this.preMatch.matched.get(next);
							 | 
						||
| 
								 | 
							
								                if (preMatch != null && preMatch != index)
							 | 
						||
| 
								 | 
							
								                    return false;
							 | 
						||
| 
								 | 
							
								                let nextDOM = next.dom;
							 | 
						||
| 
								 | 
							
								                // Can't update if nextDOM is or contains this.lock, except if
							 | 
						||
| 
								 | 
							
								                // it's a text node whose content already matches the new text
							 | 
						||
| 
								 | 
							
								                // and whose decorations match the new ones.
							 | 
						||
| 
								 | 
							
								                let locked = this.lock && (nextDOM == this.lock || nextDOM.nodeType == 1 && nextDOM.contains(this.lock.parentNode)) &&
							 | 
						||
| 
								 | 
							
								                    !(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;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // Insert the node as a newly created node desc.
							 | 
						||
| 
								 | 
							
								    addNode(node, outerDeco, innerDeco, view, pos) {
							 | 
						||
| 
								 | 
							
								        this.top.children.splice(this.index++, 0, NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos));
							 | 
						||
| 
								 | 
							
								        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))) {
							 | 
						||
| 
								 | 
							
								            // Avoid bugs in Safari's cursor drawing (#1165) and Chrome's mouse selection (#1152)
							 | 
						||
| 
								 | 
							
								            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;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Iterate from the end of the fragment and array of descs to find
							 | 
						||
| 
								 | 
							
								// directly matching ones, in order to avoid overeagerly reusing those
							 | 
						||
| 
								 | 
							
								// for other nodes. Returns the fragment index of the first node that
							 | 
						||
| 
								 | 
							
								// is part of the sequence of matched nodes at the end of the
							 | 
						||
| 
								 | 
							
								// fragment.
							 | 
						||
| 
								 | 
							
								function preMatch(frag, parentDesc) {
							 | 
						||
| 
								 | 
							
								    let curDesc = parentDesc, descI = curDesc.children.length;
							 | 
						||
| 
								 | 
							
								    let fI = frag.childCount, matched = 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 {
							 | 
						||
| 
								 | 
							
								                // FIXME
							 | 
						||
| 
								 | 
							
								                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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// This function abstracts iterating over the nodes and decorations in
							 | 
						||
| 
								 | 
							
								// a fragment. Calls `onNode` for each node, with its local and child
							 | 
						||
| 
								 | 
							
								// decorations. Splits text nodes when there is a decoration starting
							 | 
						||
| 
								 | 
							
								// or ending inside of them. Calls `onWidget` for each widget.
							 | 
						||
| 
								 | 
							
								function iterDeco(parent, deco, onWidget, onNode) {
							 | 
						||
| 
								 | 
							
								    let locals = deco.locals(parent), offset = 0;
							 | 
						||
| 
								 | 
							
								    // Simple, cheap variant for when there are no local decorations
							 | 
						||
| 
								 | 
							
								    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;;) {
							 | 
						||
| 
								 | 
							
								        if (decoIndex < locals.length && locals[decoIndex].to == offset) {
							 | 
						||
| 
								 | 
							
								            let widget = locals[decoIndex++], widgets;
							 | 
						||
| 
								 | 
							
								            while (decoIndex < locals.length && locals[decoIndex].to == offset)
							 | 
						||
| 
								 | 
							
								                (widgets || (widgets = [widget])).push(locals[decoIndex++]);
							 | 
						||
| 
								 | 
							
								            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, index;
							 | 
						||
| 
								 | 
							
								        if (restNode) {
							 | 
						||
| 
								 | 
							
								            index = -1;
							 | 
						||
| 
								 | 
							
								            child = restNode;
							 | 
						||
| 
								 | 
							
								            restNode = null;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (parentIndex < parent.childCount) {
							 | 
						||
| 
								 | 
							
								            index = 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;
							 | 
						||
| 
								 | 
							
								                index = -1;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let outerDeco = child.isInline && !child.isLeaf ? active.filter(d => !d.inline) : active.slice();
							 | 
						||
| 
								 | 
							
								        onNode(child, outerDeco, deco.forChild(offset, child), index);
							 | 
						||
| 
								 | 
							
								        offset = end;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// List markers in Mobile Safari will mysteriously disappear
							 | 
						||
| 
								 | 
							
								// sometimes. This works around that.
							 | 
						||
| 
								 | 
							
								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 nearbyTextNode(node, offset) {
							 | 
						||
| 
								 | 
							
								    for (;;) {
							 | 
						||
| 
								 | 
							
								        if (node.nodeType == 3)
							 | 
						||
| 
								 | 
							
								            return node;
							 | 
						||
| 
								 | 
							
								        if (node.nodeType == 1 && offset > 0) {
							 | 
						||
| 
								 | 
							
								            if (node.childNodes.length > offset && node.childNodes[offset].nodeType == 3)
							 | 
						||
| 
								 | 
							
								                return node.childNodes[offset];
							 | 
						||
| 
								 | 
							
								            node = node.childNodes[offset - 1];
							 | 
						||
| 
								 | 
							
								            offset = nodeSize(node);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (node.nodeType == 1 && offset < node.childNodes.length) {
							 | 
						||
| 
								 | 
							
								            node = node.childNodes[offset];
							 | 
						||
| 
								 | 
							
								            offset = 0;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            return null;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Find a piece of text in an inline fragment, overlapping from-to
							 | 
						||
| 
								 | 
							
								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) {
							 | 
						||
| 
								 | 
							
								            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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Replace range from-to in an array of view descs with replacement
							 | 
						||
| 
								 | 
							
								// (may be null to just delete). This goes very much against the grain
							 | 
						||
| 
								 | 
							
								// of the rest of this code, which tends to create nodes with the
							 | 
						||
| 
								 | 
							
								// right shape in one go, rather than messing with them after
							 | 
						||
| 
								 | 
							
								// creation, but is necessary in the composition hack.
							 | 
						||
| 
								 | 
							
								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 = undefined;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (end > to)
							 | 
						||
| 
								 | 
							
								                result.push(child.slice(to - start, child.size, view));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return result;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function selectionFromDOM(view, origin = null) {
							 | 
						||
| 
								 | 
							
								    let domSel = view.domSelectionRange(), doc = 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 = doc.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 : doc.resolve(pos));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else {
							 | 
						||
| 
								 | 
							
								        let anchor = view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset, 1);
							 | 
						||
| 
								 | 
							
								        if (anchor < 0)
							 | 
						||
| 
								 | 
							
								            return null;
							 | 
						||
| 
								 | 
							
								        $anchor = doc.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;
							 | 
						||
| 
								 | 
							
								    // The delayed drag selection causes issues with Cell Selections
							 | 
						||
| 
								 | 
							
								    // in Safari. And the drag selection delay is to workarond issues
							 | 
						||
| 
								 | 
							
								    // which only present in Chrome.
							 | 
						||
| 
								 | 
							
								    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.root, 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();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Kludge to work around Webkit not allowing a selection to start/end
							 | 
						||
| 
								 | 
							
								// between non-editable block nodes. We briefly make something
							 | 
						||
| 
								 | 
							
								// editable, set the selection, then set it uneditable again.
							 | 
						||
| 
								 | 
							
								const 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 doc = view.dom.ownerDocument;
							 | 
						||
| 
								 | 
							
								    doc.removeEventListener("selectionchange", view.input.hideSelectionGuard);
							 | 
						||
| 
								 | 
							
								    let domSel = view.domSelectionRange();
							 | 
						||
| 
								 | 
							
								    let node = domSel.anchorNode, offset = domSel.anchorOffset;
							 | 
						||
| 
								 | 
							
								    doc.addEventListener("selectionchange", view.input.hideSelectionGuard = () => {
							 | 
						||
| 
								 | 
							
								        if (domSel.anchorNode != node || domSel.anchorOffset != offset) {
							 | 
						||
| 
								 | 
							
								            doc.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();
							 | 
						||
| 
								 | 
							
								    let node = view.cursorWrapper.dom, img = node.nodeName == "IMG";
							 | 
						||
| 
								 | 
							
								    if (img)
							 | 
						||
| 
								 | 
							
								        range.setEnd(node.parentNode, domIndex(node) + 1);
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								        range.setEnd(node, 0);
							 | 
						||
| 
								 | 
							
								    range.collapse(false);
							 | 
						||
| 
								 | 
							
								    domSel.removeAllRanges();
							 | 
						||
| 
								 | 
							
								    domSel.addRange(range);
							 | 
						||
| 
								 | 
							
								    // Kludge to kill 'control selection' in IE11 when selecting an
							 | 
						||
| 
								 | 
							
								    // invisible cursor wrapper, since that would result in those weird
							 | 
						||
| 
								 | 
							
								    // resize handles and a selection that considers the absolutely
							 | 
						||
| 
								 | 
							
								    // positioned wrapper, rather than the root editable node, the
							 | 
						||
| 
								 | 
							
								    // focused element.
							 | 
						||
| 
								 | 
							
								    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);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Clear all DOM statefulness of the last node selection.
							 | 
						||
| 
								 | 
							
								function clearNodeSelection(view) {
							 | 
						||
| 
								 | 
							
								    if (view.lastSelectedViewDesc) {
							 | 
						||
| 
								 | 
							
								        if (view.lastSelectedViewDesc.parent)
							 | 
						||
| 
								 | 
							
								            view.lastSelectedViewDesc.deselectNode();
							 | 
						||
| 
								 | 
							
								        view.lastSelectedViewDesc = undefined;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								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 {
							 | 
						||
| 
								 | 
							
								        // Firefox will raise 'permission denied' errors when accessing
							 | 
						||
| 
								 | 
							
								        // properties of `sel.anchorNode` when it's in a generated CSS
							 | 
						||
| 
								 | 
							
								        // element.
							 | 
						||
| 
								 | 
							
								        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 (!sel.empty || mods.indexOf("s") > -1) {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (view.endOfTextblock(dir > 0 ? "right" : "left")) {
							 | 
						||
| 
								 | 
							
								            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) {
							 | 
						||
| 
								 | 
							
								                // Chrome and Safari will introduce extra pointless cursor
							 | 
						||
| 
								 | 
							
								                // positions around inline uneditable nodes, so we have to
							 | 
						||
| 
								 | 
							
								                // take over and move the cursor past them (#937)
							 | 
						||
| 
								 | 
							
								                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) {
							 | 
						||
| 
								 | 
							
								    let desc = dom.pmViewDesc;
							 | 
						||
| 
								 | 
							
								    return desc && desc.size == 0 && (dom.nextSibling || dom.nodeName != "BR");
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Make sure the cursor isn't directly after one or more ignored
							 | 
						||
| 
								 | 
							
								// nodes, which will confuse the browser's cursor motion logic.
							 | 
						||
| 
								 | 
							
								function skipIgnoredNodesLeft(view) {
							 | 
						||
| 
								 | 
							
								    let sel = view.domSelectionRange();
							 | 
						||
| 
								 | 
							
								    let node = sel.focusNode, offset = sel.focusOffset;
							 | 
						||
| 
								 | 
							
								    if (!node)
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    let moveNode, moveOffset, force = false;
							 | 
						||
| 
								 | 
							
								    // Gecko will do odd things when the selection is directly in front
							 | 
						||
| 
								 | 
							
								    // of a non-editable node, so in that case, move it into the next
							 | 
						||
| 
								 | 
							
								    // node if possible. Issue prosemirror/prosemirror#832.
							 | 
						||
| 
								 | 
							
								    if (gecko && node.nodeType == 1 && offset < nodeLen(node) && isIgnorable(node.childNodes[offset]))
							 | 
						||
| 
								 | 
							
								        force = true;
							 | 
						||
| 
								 | 
							
								    for (;;) {
							 | 
						||
| 
								 | 
							
								        if (offset > 0) {
							 | 
						||
| 
								 | 
							
								            if (node.nodeType != 1) {
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                let before = node.childNodes[offset - 1];
							 | 
						||
| 
								 | 
							
								                if (isIgnorable(before)) {
							 | 
						||
| 
								 | 
							
								                    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)) {
							 | 
						||
| 
								 | 
							
								                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);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Make sure the cursor isn't directly before one or more ignored
							 | 
						||
| 
								 | 
							
								// nodes.
							 | 
						||
| 
								 | 
							
								function skipIgnoredNodesRight(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)) {
							 | 
						||
| 
								 | 
							
								                moveNode = node;
							 | 
						||
| 
								 | 
							
								                moveOffset = ++offset;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (isBlockNode(node)) {
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            let next = node.nextSibling;
							 | 
						||
| 
								 | 
							
								            while (next && isIgnorable(next)) {
							 | 
						||
| 
								 | 
							
								                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 setSelFocus(view, node, offset) {
							 | 
						||
| 
								 | 
							
								    let sel = view.domSelection();
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								    // If no state update ends up happening, reset the selection.
							 | 
						||
| 
								 | 
							
								    setTimeout(() => {
							 | 
						||
| 
								 | 
							
								        if (view.state == state)
							 | 
						||
| 
								 | 
							
								            selectionToDOM(view);
							 | 
						||
| 
								 | 
							
								    }, 50);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Check whether vertical selection motion would involve node
							 | 
						||
| 
								 | 
							
								// selections. If so, apply it (if not, the result is left to the
							 | 
						||
| 
								 | 
							
								// browser)
							 | 
						||
| 
								 | 
							
								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 } = view.state.selection;
							 | 
						||
| 
								 | 
							
								    if (!$head.sameParent($anchor))
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								    if (!empty)
							 | 
						||
| 
								 | 
							
								        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();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Issue #867 / #1090 / https://bugs.chromium.org/p/chromium/issues/detail?id=903821
							 | 
						||
| 
								 | 
							
								// In which Safari (and at some point in the past, Chrome) does really
							 | 
						||
| 
								 | 
							
								// wrong things when the down arrow is pressed when the cursor is
							 | 
						||
| 
								 | 
							
								// directly at the start of a textblock and has an uneditable node
							 | 
						||
| 
								 | 
							
								// after it
							 | 
						||
| 
								 | 
							
								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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// A backdrop key mapping used to make sure we always suppress keys
							 | 
						||
| 
								 | 
							
								// that have a dangerous default effect, even if the commands they are
							 | 
						||
| 
								 | 
							
								// bound to return false, and to make sure that cursor-motion keys
							 | 
						||
| 
								 | 
							
								// find a cursor (as opposed to a node selection) when pressed. For
							 | 
						||
| 
								 | 
							
								// cursor-motion keys, the code in the handlers also takes care of
							 | 
						||
| 
								 | 
							
								// block selections.
							 | 
						||
| 
								 | 
							
								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")) { // Backspace, Ctrl-h on Mac
							 | 
						||
| 
								 | 
							
								        return stopNativeHorizontalDelete(view, -1) || skipIgnoredNodesLeft(view);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else if (code == 46 || (mac && code == 68 && mods == "c")) { // Delete, Ctrl-d on Mac
							 | 
						||
| 
								 | 
							
								        return stopNativeHorizontalDelete(view, 1) || skipIgnoredNodesRight(view);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else if (code == 13 || code == 27) { // Enter, Esc
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else if (code == 37 || (mac && code == 66 && mods == "c")) { // Left arrow, Ctrl-b on Mac
							 | 
						||
| 
								 | 
							
								        return selectHorizontally(view, -1, mods) || skipIgnoredNodesLeft(view);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else if (code == 39 || (mac && code == 70 && mods == "c")) { // Right arrow, Ctrl-f on Mac
							 | 
						||
| 
								 | 
							
								        return selectHorizontally(view, 1, mods) || skipIgnoredNodesRight(view);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else if (code == 38 || (mac && code == 80 && mods == "c")) { // Up arrow, Ctrl-p on Mac
							 | 
						||
| 
								 | 
							
								        return selectVertically(view, -1, mods) || skipIgnoredNodesLeft(view);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else if (code == 40 || (mac && code == 78 && mods == "c")) { // Down arrow, Ctrl-n on Mac
							 | 
						||
| 
								 | 
							
								        return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesRight(view);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else if (mods == (mac ? "m" : "c") &&
							 | 
						||
| 
								 | 
							
								        (code == 66 || code == 73 || code == 89 || code == 90)) { // Mod-[biyz]
							 | 
						||
| 
								 | 
							
								        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 doc = detachedDoc(), wrap = doc.createElement("div");
							 | 
						||
| 
								 | 
							
								    wrap.appendChild(serializer.serializeFragment(content, { document: doc }));
							 | 
						||
| 
								 | 
							
								    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 = doc.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 };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Read a slice of content from the clipboard (or drop data).
							 | 
						||
| 
								 | 
							
								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(dom) {
							 | 
						||
| 
								 | 
							
								                if (dom.nodeName == "BR" && !dom.nextSibling &&
							 | 
						||
| 
								 | 
							
								                    dom.parentNode && !inlineParents.test(dom.parentNode.nodeName))
							 | 
						||
| 
								 | 
							
								                    return { ignore: true };
							 | 
						||
| 
								 | 
							
								                return null;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (sliceData) {
							 | 
						||
| 
								 | 
							
								        slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[4]);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else { // HTML wasn't created by ProseMirror. Make sure top-level siblings are coherent
							 | 
						||
| 
								 | 
							
								        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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								const 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;
							 | 
						||
| 
								 | 
							
								// Takes a slice parsed with parseSlice, which means there hasn't been
							 | 
						||
| 
								 | 
							
								// any content-expression checking done on the top nodes, tries to
							 | 
						||
| 
								 | 
							
								// find a parent node in the current context that might fit the nodes,
							 | 
						||
| 
								 | 
							
								// and if successful, rebuilds the slice so that it fits into that parent.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// This addresses the problem that Transform.replace expects a
							 | 
						||
| 
								 | 
							
								// coherent slice, and will fail to place a set of siblings that don't
							 | 
						||
| 
								 | 
							
								// fit anywhere in the schema.
							 | 
						||
| 
								 | 
							
								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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Used to group adjacent nodes wrapped in similar parents by
							 | 
						||
| 
								 | 
							
								// normalizeSiblings into the same parent 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 (depth < to - 1)
							 | 
						||
| 
								 | 
							
								        inner = closeRange(inner, side, from, to, depth + 1, openEnd);
							 | 
						||
| 
								 | 
							
								    if (depth >= from)
							 | 
						||
| 
								 | 
							
								        inner = side < 0 ? node.contentMatchAt(0).fillBefore(inner, fragment.childCount > 1 || 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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Trick from jQuery -- some elements must be wrapped in other
							 | 
						||
| 
								 | 
							
								// elements for innerHTML to work. I.e. if you do `div.innerHTML =
							 | 
						||
| 
								 | 
							
								// "<td>..</td>"` the table cells are ignored.
							 | 
						||
| 
								 | 
							
								const 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"]
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								let _detachedDoc = null;
							 | 
						||
| 
								 | 
							
								function detachedDoc() {
							 | 
						||
| 
								 | 
							
								    return _detachedDoc || (_detachedDoc = document.implementation.createHTMLDocument("title"));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								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 = html;
							 | 
						||
| 
								 | 
							
								    if (wrap)
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < wrap.length; i++)
							 | 
						||
| 
								 | 
							
								            elt = elt.querySelector(wrap[i]) || elt;
							 | 
						||
| 
								 | 
							
								    return elt;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Webkit browsers do some hard-to-predict replacement of regular
							 | 
						||
| 
								 | 
							
								// spaces with non-breaking spaces when putting content on the
							 | 
						||
| 
								 | 
							
								// clipboard. This tries to convert such non-breaking spaces (which
							 | 
						||
| 
								 | 
							
								// will be wrapped in a plain span on Chrome, a span with class
							 | 
						||
| 
								 | 
							
								// Apple-converted-space on Safari) back to regular spaces.
							 | 
						||
| 
								 | 
							
								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 == "\u00a0" && 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);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// A collection of DOM events that occur within the editor, and callback functions
							 | 
						||
| 
								 | 
							
								// to invoke when the event fires.
							 | 
						||
| 
								 | 
							
								const handlers = {};
							 | 
						||
| 
								 | 
							
								const editHandlers = {};
							 | 
						||
| 
								 | 
							
								const passiveHandlers = { touchstart: true, touchmove: true };
							 | 
						||
| 
								 | 
							
								class InputState {
							 | 
						||
| 
								 | 
							
								    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.composingTimeout = -1;
							 | 
						||
| 
								 | 
							
								        this.compositionNodes = [];
							 | 
						||
| 
								 | 
							
								        this.compositionEndedAt = -2e8;
							 | 
						||
| 
								 | 
							
								        this.domChangeCount = 0;
							 | 
						||
| 
								 | 
							
								        this.eventHandlers = 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] = (event) => {
							 | 
						||
| 
								 | 
							
								            if (eventBelongsToView(view, event) && !runCustomHandler(view, event) &&
							 | 
						||
| 
								 | 
							
								                (view.editable || !(event.type in editHandlers)))
							 | 
						||
| 
								 | 
							
								                handler(view, event);
							 | 
						||
| 
								 | 
							
								        }, passiveHandlers[event] ? { passive: true } : undefined);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // On Safari, for reasons beyond my understanding, adding an input
							 | 
						||
| 
								 | 
							
								    // event handler makes an issue where the composition vanishes when
							 | 
						||
| 
								 | 
							
								    // you press enter go away.
							 | 
						||
| 
								 | 
							
								    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", handlers => {
							 | 
						||
| 
								 | 
							
								        let handler = handlers[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();
							 | 
						||
| 
								 | 
							
								    // Suppress enter key events on Chrome Android, because those tend
							 | 
						||
| 
								 | 
							
								    // to be part of a confused sequence of composition events fired,
							 | 
						||
| 
								 | 
							
								    // and handling them eagerly tends to corrupt the input.
							 | 
						||
| 
								 | 
							
								    if (android && chrome && event.keyCode == 13)
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    if (event.keyCode != 229)
							 | 
						||
| 
								 | 
							
								        view.domObserver.forceFlush();
							 | 
						||
| 
								 | 
							
								    // On iOS, if we preventDefault enter key presses, the virtual
							 | 
						||
| 
								 | 
							
								    // keyboard gets confused. So the hack here is to set a flag that
							 | 
						||
| 
								 | 
							
								    // makes the DOM change code recognize that what just happens should
							 | 
						||
| 
								 | 
							
								    // be replaced by whatever the Enter key handlers do.
							 | 
						||
| 
								 | 
							
								    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 (!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();
							 | 
						||
| 
								 | 
							
								    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 doc = view.state.doc;
							 | 
						||
| 
								 | 
							
								    if (inside == -1) {
							 | 
						||
| 
								 | 
							
								        if (doc.inlineContent) {
							 | 
						||
| 
								 | 
							
								            updateSelection(view, TextSelection.create(doc, 0, doc.content.size), "pointer");
							 | 
						||
| 
								 | 
							
								            return true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    let $pos = doc.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(doc, nodePos + 1, nodePos + 1 + node.content.size), "pointer");
							 | 
						||
| 
								 | 
							
								        else if (NodeSelection.isSelectable(node))
							 | 
						||
| 
								 | 
							
								            updateSelection(view, NodeSelection.create(doc, nodePos), "pointer");
							 | 
						||
| 
								 | 
							
								        else
							 | 
						||
| 
								 | 
							
								            continue;
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function forceDOMFlush(view) {
							 | 
						||
| 
								 | 
							
								    return endComposition(view);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								const 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");
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								class MouseDown {
							 | 
						||
| 
								 | 
							
								    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 : 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;
							 | 
						||
| 
								 | 
							
								    // See https://www.stum.de/2016/06/24/handling-ime-events-in-javascript/.
							 | 
						||
| 
								 | 
							
								    // On Japanese input method editors (IMEs), the Enter key is used to confirm character
							 | 
						||
| 
								 | 
							
								    // selection. On Safari, when Enter is pressed, compositionend and keydown events are
							 | 
						||
| 
								 | 
							
								    // emitted. The keydown event triggers newline insertion, which we don't want.
							 | 
						||
| 
								 | 
							
								    // This method returns true if the keydown event should be ignored.
							 | 
						||
| 
								 | 
							
								    // We only ignore it once, as pressing Enter a second time *should* insert a newline.
							 | 
						||
| 
								 | 
							
								    // Furthermore, the keydown event timestamp must be close to the compositionEndedAt timestamp.
							 | 
						||
| 
								 | 
							
								    // This guards against the case where compositionend is triggered without the keyboard
							 | 
						||
| 
								 | 
							
								    // (e.g. character confirmation may be done with the mouse), and keydown is triggered
							 | 
						||
| 
								 | 
							
								    // afterwards- we wouldn't want to ignore the keydown event in this case.
							 | 
						||
| 
								 | 
							
								    if (safari && Math.abs(event.timeStamp - view.input.compositionEndedAt) < 500) {
							 | 
						||
| 
								 | 
							
								        view.input.compositionEndedAt = -2e8;
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return false;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Drop active composition after 5 seconds of inactivity on Android
							 | 
						||
| 
								 | 
							
								const timeoutComposition = android ? 5000 : -1;
							 | 
						||
| 
								 | 
							
								editHandlers.compositionstart = editHandlers.compositionupdate = view => {
							 | 
						||
| 
								 | 
							
								    if (!view.composing) {
							 | 
						||
| 
								 | 
							
								        view.domObserver.flush();
							 | 
						||
| 
								 | 
							
								        let { state } = view, $pos = state.selection.$from;
							 | 
						||
| 
								 | 
							
								        if (state.selection.empty &&
							 | 
						||
| 
								 | 
							
								            (state.storedMarks ||
							 | 
						||
| 
								 | 
							
								                (!$pos.textOffset && $pos.parentOffset && $pos.nodeBefore.marks.some(m => m.type.spec.inclusive === false)))) {
							 | 
						||
| 
								 | 
							
								            // Need to wrap the cursor in mark nodes different from the ones in the DOM context
							 | 
						||
| 
								 | 
							
								            view.markCursor = view.state.storedMarks || $pos.marks();
							 | 
						||
| 
								 | 
							
								            endComposition(view, true);
							 | 
						||
| 
								 | 
							
								            view.markCursor = null;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            endComposition(view);
							 | 
						||
| 
								 | 
							
								            // In firefox, if the cursor is after but outside a marked node,
							 | 
						||
| 
								 | 
							
								            // the inserted text won't inherit the marks. So this moves it
							 | 
						||
| 
								 | 
							
								            // inside if necessary.
							 | 
						||
| 
								 | 
							
								            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) {
							 | 
						||
| 
								 | 
							
								                        view.domSelection().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;
							 | 
						||
| 
								 | 
							
								        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 timestampFromCustomEvent() {
							 | 
						||
| 
								 | 
							
								    let event = document.createEvent("Event");
							 | 
						||
| 
								 | 
							
								    event.initEvent("event", true, true);
							 | 
						||
| 
								 | 
							
								    return event.timeStamp;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								@internal
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								function endComposition(view, forceUpdate = false) {
							 | 
						||
| 
								 | 
							
								    if (android && view.domObserver.flushingSoon >= 0)
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    view.domObserver.forceFlush();
							 | 
						||
| 
								 | 
							
								    clearComposition(view);
							 | 
						||
| 
								 | 
							
								    if (forceUpdate || view.docView && view.docView.dirty) {
							 | 
						||
| 
								 | 
							
								        let sel = selectionFromDOM(view);
							 | 
						||
| 
								 | 
							
								        if (sel && !sel.eq(view.state.selection))
							 | 
						||
| 
								 | 
							
								            view.dispatch(view.state.tr.setSelection(sel));
							 | 
						||
| 
								 | 
							
								        else
							 | 
						||
| 
								 | 
							
								            view.updateState(view.state);
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return false;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function captureCopy(view, dom) {
							 | 
						||
| 
								 | 
							
								    // The extra wrapper is somehow necessary on IE/Edge to prevent the
							 | 
						||
| 
								 | 
							
								    // content from being mangled when it is put onto the clipboard
							 | 
						||
| 
								 | 
							
								    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);
							 | 
						||
| 
								 | 
							
								    // Done because IE will fire a selectionchange moving the selection
							 | 
						||
| 
								 | 
							
								    // to its start when removeAllRanges is called and the editor still
							 | 
						||
| 
								 | 
							
								    // has focus (which will mess up the editor's selection state).
							 | 
						||
| 
								 | 
							
								    view.dom.blur();
							 | 
						||
| 
								 | 
							
								    sel.removeAllRanges();
							 | 
						||
| 
								 | 
							
								    sel.addRange(range);
							 | 
						||
| 
								 | 
							
								    setTimeout(() => {
							 | 
						||
| 
								 | 
							
								        if (wrap.parentNode)
							 | 
						||
| 
								 | 
							
								            wrap.parentNode.removeChild(wrap);
							 | 
						||
| 
								 | 
							
								        view.focus();
							 | 
						||
| 
								 | 
							
								    }, 50);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// This is very crude, but unfortunately both these browsers _pretend_
							 | 
						||
| 
								 | 
							
								// that they have a clipboard API—all the objects and methods are
							 | 
						||
| 
								 | 
							
								// there, they just don't work, and they are hard to test.
							 | 
						||
| 
								 | 
							
								const brokenClipboardAPI = (ie && ie_version < 15) ||
							 | 
						||
| 
								 | 
							
								    (ios && webkit_version < 604);
							 | 
						||
| 
								 | 
							
								handlers.copy = editHandlers.cut = (view, _event) => {
							 | 
						||
| 
								 | 
							
								    let event = _event;
							 | 
						||
| 
								 | 
							
								    let sel = view.state.selection, cut = event.type == "cut";
							 | 
						||
| 
								 | 
							
								    if (sel.empty)
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    // IE and Edge's clipboard interface is completely broken
							 | 
						||
| 
								 | 
							
								    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 (cut)
							 | 
						||
| 
								 | 
							
								        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();
							 | 
						||
| 
								 | 
							
								    setTimeout(() => {
							 | 
						||
| 
								 | 
							
								        view.focus();
							 | 
						||
| 
								 | 
							
								        if (target.parentNode)
							 | 
						||
| 
								 | 
							
								            target.parentNode.removeChild(target);
							 | 
						||
| 
								 | 
							
								        if (plainText)
							 | 
						||
| 
								 | 
							
								            doPaste(view, target.value, null, event);
							 | 
						||
| 
								 | 
							
								        else
							 | 
						||
| 
								 | 
							
								            doPaste(view, target.textContent, target.innerHTML, event);
							 | 
						||
| 
								 | 
							
								    }, 50);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function doPaste(view, text, html, event) {
							 | 
						||
| 
								 | 
							
								    let slice = parseFromClipboard(view, text, html, view.input.shiftKey, 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, view.input.shiftKey)
							 | 
						||
| 
								 | 
							
								        : view.state.tr.replaceSelection(slice);
							 | 
						||
| 
								 | 
							
								    view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste"));
							 | 
						||
| 
								 | 
							
								    return true;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								editHandlers.paste = (view, _event) => {
							 | 
						||
| 
								 | 
							
								    let event = _event;
							 | 
						||
| 
								 | 
							
								    // Handling paste from JavaScript during composition is very poorly
							 | 
						||
| 
								 | 
							
								    // handled by browsers, so as a dodgy but preferable kludge, we just
							 | 
						||
| 
								 | 
							
								    // let the browser do its native thing there, except on Android,
							 | 
						||
| 
								 | 
							
								    // where the editor is almost always composing.
							 | 
						||
| 
								 | 
							
								    if (view.composing && !android)
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    let data = brokenClipboardAPI ? null : event.clipboardData;
							 | 
						||
| 
								 | 
							
								    if (data && doPaste(view, data.getData("text/plain"), data.getData("text/html"), event))
							 | 
						||
| 
								 | 
							
								        event.preventDefault();
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								        capturePaste(view, event);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								class Dragging {
							 | 
						||
| 
								 | 
							
								    constructor(slice, move) {
							 | 
						||
| 
								 | 
							
								        this.slice = slice;
							 | 
						||
| 
								 | 
							
								        this.move = move;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								const 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));
							 | 
						||
| 
								 | 
							
								    if (pos && pos.pos >= sel.from && pos.pos <= (sel instanceof NodeSelection ? sel.to - 1 : sel.to)) ;
							 | 
						||
| 
								 | 
							
								    else if (mouseDown && mouseDown.mightDrag) {
							 | 
						||
| 
								 | 
							
								        view.dispatch(view.state.tr.setSelection(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)
							 | 
						||
| 
								 | 
							
								            view.dispatch(view.state.tr.setSelection(NodeSelection.create(view.state.doc, desc.posBefore)));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    let slice = view.state.selection.content(), { dom, text } = serializeForClipboard(view, slice);
							 | 
						||
| 
								 | 
							
								    event.dataTransfer.clearData();
							 | 
						||
| 
								 | 
							
								    event.dataTransfer.setData(brokenClipboardAPI ? "Text" : "text/html", dom.innerHTML);
							 | 
						||
| 
								 | 
							
								    // See https://github.com/ProseMirror/prosemirror/issues/1156
							 | 
						||
| 
								 | 
							
								    event.dataTransfer.effectAllowed = "copyMove";
							 | 
						||
| 
								 | 
							
								    if (!brokenClipboardAPI)
							 | 
						||
| 
								 | 
							
								        event.dataTransfer.setData("text/plain", text);
							 | 
						||
| 
								 | 
							
								    view.dragging = new Dragging(slice, !event[dragCopyModifier]);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								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, event.dataTransfer.getData(brokenClipboardAPI ? "Text" : "text/plain"), 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)
							 | 
						||
| 
								 | 
							
								        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;
							 | 
						||
| 
								 | 
							
								    // We should probably do more with beforeinput events, but support
							 | 
						||
| 
								 | 
							
								    // is so spotty that I'm still waiting to see where they are going.
							 | 
						||
| 
								 | 
							
								    // Very specific hack to deal with backspace sometimes failing on
							 | 
						||
| 
								 | 
							
								    // Chrome Android when after an uneditable node.
							 | 
						||
| 
								 | 
							
								    if (chrome && android && event.inputType == "deleteContentBackward") {
							 | 
						||
| 
								 | 
							
								        view.domObserver.flushSoon();
							 | 
						||
| 
								 | 
							
								        let { domChangeCount } = view.input;
							 | 
						||
| 
								 | 
							
								        setTimeout(() => {
							 | 
						||
| 
								 | 
							
								            if (view.input.domChangeCount != domChangeCount)
							 | 
						||
| 
								 | 
							
								                return; // Event already had some effect
							 | 
						||
| 
								 | 
							
								            // This bug tends to close the virtual keyboard, so we refocus
							 | 
						||
| 
								 | 
							
								            view.dom.blur();
							 | 
						||
| 
								 | 
							
								            view.focus();
							 | 
						||
| 
								 | 
							
								            if (view.someProp("handleKeyDown", f => f(view, keyEvent(8, "Backspace"))))
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            let { $cursor } = view.state.selection;
							 | 
						||
| 
								 | 
							
								            // Crude approximation of backspace behavior when no command handled it
							 | 
						||
| 
								 | 
							
								            if ($cursor && $cursor.pos > 0)
							 | 
						||
| 
								 | 
							
								                view.dispatch(view.state.tr.delete($cursor.pos - 1, $cursor.pos).scrollIntoView());
							 | 
						||
| 
								 | 
							
								        }, 50);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								// Make sure all handlers get registered
							 | 
						||
| 
								 | 
							
								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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								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);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								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() { }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								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, offset } = node.content.findIndex(span.from), child;
							 | 
						||
| 
								 | 
							
								        return offset == span.from && !(child = node.child(index)).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() { }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								Decoration objects can be provided to the view through the
							 | 
						||
| 
								 | 
							
								[`decorations` prop](https://prosemirror.net/docs/ref/#view.EditorProps.decorations). They come in
							 | 
						||
| 
								 | 
							
								several variants—see the static members of this class for details.
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								class Decoration {
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								    @internal
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								    constructor(
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								    The start position of the decoration.
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								    from, 
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								    The end position. Will be the same as `from` for [widget
							 | 
						||
| 
								 | 
							
								    decorations](https://prosemirror.net/docs/ref/#view.Decoration^widget).
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								    to, 
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								    @internal
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								    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; }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								const none = [], noSpec = {};
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								A collection of [decorations](https://prosemirror.net/docs/ref/#view.Decoration), organized in such
							 | 
						||
| 
								 | 
							
								a way that the drawing algorithm can efficiently use and compare
							 | 
						||
| 
								 | 
							
								them. This is a persistent data structure—it is not modified,
							 | 
						||
| 
								 | 
							
								updates create a new value.
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								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.
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								    static create(doc, decorations) {
							 | 
						||
| 
								 | 
							
								        return decorations.length ? buildTree(decorations, doc, 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, doc, options) {
							 | 
						||
| 
								 | 
							
								        if (this == empty || mapping.maps.length == 0)
							 | 
						||
| 
								 | 
							
								            return this;
							 | 
						||
| 
								 | 
							
								        return this.mapInner(mapping, doc, 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. Needs access to the current document to
							 | 
						||
| 
								 | 
							
								    create the appropriate tree structure.
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								    add(doc, decorations) {
							 | 
						||
| 
								 | 
							
								        if (!decorations.length)
							 | 
						||
| 
								 | 
							
								            return this;
							 | 
						||
| 
								 | 
							
								        if (this == empty)
							 | 
						||
| 
								 | 
							
								            return DecorationSet.create(doc, decorations);
							 | 
						||
| 
								 | 
							
								        return this.addInner(doc, decorations, 0);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    addInner(doc, decorations, offset) {
							 | 
						||
| 
								 | 
							
								        let children, childIndex = 0;
							 | 
						||
| 
								 | 
							
								        doc.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(doc, 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;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								    @internal
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								The empty set of decorations.
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								DecorationSet.empty = new DecorationSet([], []);
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								@internal
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								DecorationSet.removeOverlap = removeOverlap;
							 | 
						||
| 
								 | 
							
								const empty = DecorationSet.empty;
							 | 
						||
| 
								 | 
							
								// An abstraction that allows the code dealing with decorations to
							 | 
						||
| 
								 | 
							
								// treat multiple DecorationSet objects as if it were a single object
							 | 
						||
| 
								 | 
							
								// with (a subset of) the same interface.
							 | 
						||
| 
								 | 
							
								class DecorationGroup {
							 | 
						||
| 
								 | 
							
								    constructor(members) {
							 | 
						||
| 
								 | 
							
								        this.members = members;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    map(mapping, doc) {
							 | 
						||
| 
								 | 
							
								        const mappedDecos = this.members.map(member => member.map(mapping, doc, 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), []));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function mapChildren(oldChildren, newLocal, mapping, node, offset, oldOffset, options) {
							 | 
						||
| 
								 | 
							
								    let children = oldChildren.slice();
							 | 
						||
| 
								 | 
							
								    // Mark the children that are directly touched by changes, and
							 | 
						||
| 
								 | 
							
								    // move those that are after the changes.
							 | 
						||
| 
								 | 
							
								    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 i = 0; i < children.length; i += 3) {
							 | 
						||
| 
								 | 
							
								                let end = children[i + 1];
							 | 
						||
| 
								 | 
							
								                if (end < 0 || oldStart > end + baseOffset - moved)
							 | 
						||
| 
								 | 
							
								                    continue;
							 | 
						||
| 
								 | 
							
								                let start = children[i] + baseOffset - moved;
							 | 
						||
| 
								 | 
							
								                if (oldEnd >= start) {
							 | 
						||
| 
								 | 
							
								                    children[i + 1] = oldStart <= start ? -2 : -1;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                else if (newStart >= offset && dSize) {
							 | 
						||
| 
								 | 
							
								                    children[i] += dSize;
							 | 
						||
| 
								 | 
							
								                    children[i + 1] += dSize;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            moved += dSize;
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        baseOffset = mapping.maps[i].map(baseOffset, -1);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // Find the child nodes that still correspond to a single node,
							 | 
						||
| 
								 | 
							
								    // recursively call mapInner on them and update their positions.
							 | 
						||
| 
								 | 
							
								    let mustRebuild = false;
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < children.length; i += 3)
							 | 
						||
| 
								 | 
							
								        if (children[i + 1] < 0) { // Touched nodes
							 | 
						||
| 
								 | 
							
								            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;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            // Must read oldChildren because children was tagged with -1
							 | 
						||
| 
								 | 
							
								            let to = mapping.map(oldChildren[i + 1] + oldOffset, -1), toLocal = to - offset;
							 | 
						||
| 
								 | 
							
								            let { index, offset: childOffset } = node.content.findIndex(fromLocal);
							 | 
						||
| 
								 | 
							
								            let childNode = node.maybeChild(index);
							 | 
						||
| 
								 | 
							
								            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;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    // Remaining children must be collected and rebuilt into the appropriate structure
							 | 
						||
| 
								 | 
							
								    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) {
							 | 
						||
| 
								 | 
							
								    // Gather all decorations from the remaining marked children
							 | 
						||
| 
								 | 
							
								    function gather(set, oldOffset) {
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < set.local.length; i++) {
							 | 
						||
| 
								 | 
							
								            let mapped = set.local[i].map(mapping, offset, oldOffset);
							 | 
						||
| 
								 | 
							
								            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] + oldOffset + 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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Build up a tree that corresponds to a set of decorations. `offset`
							 | 
						||
| 
								 | 
							
								// is a base offset that should be subtracted from the `from` and `to`
							 | 
						||
| 
								 | 
							
								// positions in the spans (so that we don't have to allocate new spans
							 | 
						||
| 
								 | 
							
								// for recursive calls).
							 | 
						||
| 
								 | 
							
								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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Used to sort decorations so that ones with a low start position
							 | 
						||
| 
								 | 
							
								// come first, and within a set with the same start position, those
							 | 
						||
| 
								 | 
							
								// with an smaller end position come first.
							 | 
						||
| 
								 | 
							
								function byPos(a, b) {
							 | 
						||
| 
								 | 
							
								    return a.from - b.from || a.to - b.to;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Scan a sorted array of decorations for partially overlapping spans,
							 | 
						||
| 
								 | 
							
								// and split those so that only fully overlapping spans are left (to
							 | 
						||
| 
								 | 
							
								// make subsequent rendering easier). Will return the input array if
							 | 
						||
| 
								 | 
							
								// no partially overlapping spans are found (the common case).
							 | 
						||
| 
								 | 
							
								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();
							 | 
						||
| 
								 | 
							
								                        // Followed by a partially overlapping larger span. Split that
							 | 
						||
| 
								 | 
							
								                        // span.
							 | 
						||
| 
								 | 
							
								                        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();
							 | 
						||
| 
								 | 
							
								                        // The end of this one overlaps with a subsequent span. Split
							 | 
						||
| 
								 | 
							
								                        // this one.
							 | 
						||
| 
								 | 
							
								                        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);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Get the decorations associated with the current props of a view.
							 | 
						||
| 
								 | 
							
								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);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const observeOptions = {
							 | 
						||
| 
								 | 
							
								    childList: true,
							 | 
						||
| 
								 | 
							
								    characterData: true,
							 | 
						||
| 
								 | 
							
								    characterDataOldValue: true,
							 | 
						||
| 
								 | 
							
								    attributes: true,
							 | 
						||
| 
								 | 
							
								    attributeOldValue: true,
							 | 
						||
| 
								 | 
							
								    subtree: true
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								// IE11 has very broken mutation observers, so we also listen to DOMCharacterDataModified
							 | 
						||
| 
								 | 
							
								const useCharData = ie && ie_version <= 11;
							 | 
						||
| 
								 | 
							
								class SelectionState {
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								class DOMObserver {
							 | 
						||
| 
								 | 
							
								    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.observer = window.MutationObserver &&
							 | 
						||
| 
								 | 
							
								            new window.MutationObserver(mutations => {
							 | 
						||
| 
								 | 
							
								                for (let i = 0; i < mutations.length; i++)
							 | 
						||
| 
								 | 
							
								                    this.queue.push(mutations[i]);
							 | 
						||
| 
								 | 
							
								                // IE11 will sometimes (on backspacing out a single character
							 | 
						||
| 
								 | 
							
								                // text node after a BR node) call the observer callback
							 | 
						||
| 
								 | 
							
								                // before actually updating the DOM, which will cause
							 | 
						||
| 
								 | 
							
								                // ProseMirror to miss the change (see #930)
							 | 
						||
| 
								 | 
							
								                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);
							 | 
						||
| 
								 | 
							
								        // Deletions on IE11 fire their events in the wrong order, giving
							 | 
						||
| 
								 | 
							
								        // us a selection change event before the DOM changes are
							 | 
						||
| 
								 | 
							
								        // reported.
							 | 
						||
| 
								 | 
							
								        if (ie && ie_version <= 11 && !this.view.state.selection.empty) {
							 | 
						||
| 
								 | 
							
								            let sel = this.view.domSelectionRange();
							 | 
						||
| 
								 | 
							
								            // Selection.isCollapsed isn't reliable on IE
							 | 
						||
| 
								 | 
							
								            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 = 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;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    flush() {
							 | 
						||
| 
								 | 
							
								        let { view } = this;
							 | 
						||
| 
								 | 
							
								        if (!view.docView || this.flushingSoon > -1)
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        let mutations = this.observer ? this.observer.takeRecords() : [];
							 | 
						||
| 
								 | 
							
								        if (this.queue.length) {
							 | 
						||
| 
								 | 
							
								            mutations = this.queue.concat(mutations);
							 | 
						||
| 
								 | 
							
								            this.queue.length = 0;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        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 > 1) {
							 | 
						||
| 
								 | 
							
								            let brs = added.filter(n => n.nodeName == "BR");
							 | 
						||
| 
								 | 
							
								            if (brs.length == 2) {
							 | 
						||
| 
								 | 
							
								                let a = brs[0], b = brs[1];
							 | 
						||
| 
								 | 
							
								                if (a.parentNode && a.parentNode.parentNode == b.parentNode)
							 | 
						||
| 
								 | 
							
								                    b.remove();
							 | 
						||
| 
								 | 
							
								                else
							 | 
						||
| 
								 | 
							
								                    a.remove();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let readSel = null;
							 | 
						||
| 
								 | 
							
								        // If it looks like the browser has reset the selection to the
							 | 
						||
| 
								 | 
							
								        // start of the document after focus, restore the selection from
							 | 
						||
| 
								 | 
							
								        // the state
							 | 
						||
| 
								 | 
							
								        if (from < 0 && newSel && view.input.lastFocus > Date.now() - 200 &&
							 | 
						||
| 
								 | 
							
								            view.input.lastTouch < 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) {
							 | 
						||
| 
								 | 
							
								        // Ignore mutations inside nodes that were already noted as inserted
							 | 
						||
| 
								 | 
							
								        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++)
							 | 
						||
| 
								 | 
							
								                added.push(mut.addedNodes[i]);
							 | 
						||
| 
								 | 
							
								            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) {
							 | 
						||
| 
								 | 
							
								                // IE11 gives us incorrect next/prev siblings for some
							 | 
						||
| 
								 | 
							
								                // insertions, so if there are added nodes, recompute those
							 | 
						||
| 
								 | 
							
								                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 { // "characterData"
							 | 
						||
| 
								 | 
							
								            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
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								let cssChecked = new WeakMap();
							 | 
						||
| 
								 | 
							
								let 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;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Used to work around a Safari Selection/shadow DOM bug
							 | 
						||
| 
								 | 
							
								// Based on https://github.com/codemirror/dev/issues/414 fix
							 | 
						||
| 
								 | 
							
								function safariShadowSelectionRange(view) {
							 | 
						||
| 
								 | 
							
								    let found;
							 | 
						||
| 
								 | 
							
								    function read(event) {
							 | 
						||
| 
								 | 
							
								        event.preventDefault();
							 | 
						||
| 
								 | 
							
								        event.stopImmediatePropagation();
							 | 
						||
| 
								 | 
							
								        found = event.getTargetRanges()[0];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // Because Safari (at least in 2018-2022) doesn't provide regular
							 | 
						||
| 
								 | 
							
								    // access to the selection inside a shadowRoot, we have to perform a
							 | 
						||
| 
								 | 
							
								    // ridiculous hack to get at it—using `execCommand` to trigger a
							 | 
						||
| 
								 | 
							
								    // `beforeInput` event so that we can read the target range from the
							 | 
						||
| 
								 | 
							
								    // event.
							 | 
						||
| 
								 | 
							
								    view.dom.addEventListener("beforeinput", read, true);
							 | 
						||
| 
								 | 
							
								    document.execCommand("indent");
							 | 
						||
| 
								 | 
							
								    view.dom.removeEventListener("beforeinput", read, true);
							 | 
						||
| 
								 | 
							
								    let anchorNode = found.startContainer, anchorOffset = found.startOffset;
							 | 
						||
| 
								 | 
							
								    let focusNode = found.endContainer, focusOffset = found.endOffset;
							 | 
						||
| 
								 | 
							
								    let currentAnchor = view.domAtPos(view.state.selection.anchor);
							 | 
						||
| 
								 | 
							
								    // Since such a range doesn't distinguish between anchor and head,
							 | 
						||
| 
								 | 
							
								    // use a heuristic that flips it around if its end matches the
							 | 
						||
| 
								 | 
							
								    // current anchor.
							 | 
						||
| 
								 | 
							
								    if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
							 | 
						||
| 
								 | 
							
								        [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
							 | 
						||
| 
								 | 
							
								    return { anchorNode, anchorOffset, focusNode, focusOffset };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Note that all referencing and parsing is done with the
							 | 
						||
| 
								 | 
							
								// start-of-operation selection and document, since that's the one
							 | 
						||
| 
								 | 
							
								// that the DOM represents. If any changes came in in the meantime,
							 | 
						||
| 
								 | 
							
								// the modification is mapped over those before it is applied, in
							 | 
						||
| 
								 | 
							
								// readDOMChange.
							 | 
						||
| 
								 | 
							
								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 });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // Work around issue in Chrome where backspacing sometimes replaces
							 | 
						||
| 
								 | 
							
								    // the deleted content with a random BR node (issues #799, #831)
							 | 
						||
| 
								 | 
							
								    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, doc = 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 anchor = find[0].pos, head = find[1] && find[1].pos;
							 | 
						||
| 
								 | 
							
								        if (head == null)
							 | 
						||
| 
								 | 
							
								            head = anchor;
							 | 
						||
| 
								 | 
							
								        sel = { anchor: anchor + from, head: head + from };
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return { doc, sel, from, to };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function ruleFromNode(dom) {
							 | 
						||
| 
								 | 
							
								    let desc = dom.pmViewDesc;
							 | 
						||
| 
								 | 
							
								    if (desc) {
							 | 
						||
| 
								 | 
							
								        return desc.parseRule();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else if (dom.nodeName == "BR" && dom.parentNode) {
							 | 
						||
| 
								 | 
							
								        // Safari replaces the list item or table cell with a BR
							 | 
						||
| 
								 | 
							
								        // directly in the list node (?!) if you delete the last
							 | 
						||
| 
								 | 
							
								        // character in a list item or table cell (#708, #862)
							 | 
						||
| 
								 | 
							
								        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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function readDOMChange(view, from, to, typeOver, addedNodes) {
							 | 
						||
| 
								 | 
							
								    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)) {
							 | 
						||
| 
								 | 
							
								            let tr = view.state.tr.setSelection(newSel);
							 | 
						||
| 
								 | 
							
								            if (origin == "pointer")
							 | 
						||
| 
								 | 
							
								                tr.setMeta("pointer", true);
							 | 
						||
| 
								 | 
							
								            else if (origin == "key")
							 | 
						||
| 
								 | 
							
								                tr.scrollIntoView();
							 | 
						||
| 
								 | 
							
								            view.dispatch(tr);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        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 doc = view.state.doc, compare = doc.slice(parse.from, parse.to);
							 | 
						||
| 
								 | 
							
								    let preferredPos, preferredSide;
							 | 
						||
| 
								 | 
							
								    // Prefer anchoring to end when Backspace is pressed
							 | 
						||
| 
								 | 
							
								    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 ((ios && view.input.lastIOSEnter > Date.now() - 225 || android) &&
							 | 
						||
| 
								 | 
							
								        addedNodes.some(n => n.nodeName == "DIV" || n.nodeName == "P" || n.nodeName == "LI") &&
							 | 
						||
| 
								 | 
							
								        (!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 sel = resolveSelection(view, view.state.doc, parse.sel);
							 | 
						||
| 
								 | 
							
								                if (sel && !sel.eq(view.state.selection))
							 | 
						||
| 
								 | 
							
								                    view.dispatch(view.state.tr.setSelection(sel));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // Chrome sometimes leaves the cursor before the inserted text when
							 | 
						||
| 
								 | 
							
								    // composing after a cursor wrapper. This moves it forward.
							 | 
						||
| 
								 | 
							
								    if (chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from &&
							 | 
						||
| 
								 | 
							
								        parse.sel.head == parse.sel.anchor) {
							 | 
						||
| 
								 | 
							
								        let size = change.endB - change.start;
							 | 
						||
| 
								 | 
							
								        parse.sel = { anchor: parse.sel.anchor + size, head: parse.sel.anchor + size };
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    view.input.domChangeCount++;
							 | 
						||
| 
								 | 
							
								    // Handle the case where overwriting a selection by typing matches
							 | 
						||
| 
								 | 
							
								    // the start or end of the selected content, creating a change
							 | 
						||
| 
								 | 
							
								    // that's smaller than what was actually overwritten.
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // IE11 will insert a non-breaking space _ahead_ of the space after
							 | 
						||
| 
								 | 
							
								    // the cursor space when adding a space before another space. When
							 | 
						||
| 
								 | 
							
								    // that happened, adjust the change to cover the space instead.
							 | 
						||
| 
								 | 
							
								    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) == " \u00a0") {
							 | 
						||
| 
								 | 
							
								        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 = doc.resolve(change.start);
							 | 
						||
| 
								 | 
							
								    let inlineChange = $from.sameParent($to) && $from.parent.inlineContent && $fromA.end() >= change.endA;
							 | 
						||
| 
								 | 
							
								    let nextSel;
							 | 
						||
| 
								 | 
							
								    // If this looks like the effect of pressing Enter (or was recorded
							 | 
						||
| 
								 | 
							
								    // as being an iOS enter press), just dispatch an Enter key instead.
							 | 
						||
| 
								 | 
							
								    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 &&
							 | 
						||
| 
								 | 
							
								            (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;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // Same for backspace
							 | 
						||
| 
								 | 
							
								    if (view.state.selection.anchor > change.start &&
							 | 
						||
| 
								 | 
							
								        looksLikeJoin(doc, change.start, change.endA, $from, $to) &&
							 | 
						||
| 
								 | 
							
								        view.someProp("handleKeyDown", f => f(view, keyEvent(8, "Backspace")))) {
							 | 
						||
| 
								 | 
							
								        if (android && chrome)
							 | 
						||
| 
								 | 
							
								            view.domObserver.suppressSelectionUpdates(); // #820
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // Chrome Android will occasionally, during composition, delete the
							 | 
						||
| 
								 | 
							
								    // entire composition and then immediately insert it again. This is
							 | 
						||
| 
								 | 
							
								    // used to detect that situation.
							 | 
						||
| 
								 | 
							
								    if (chrome && android && change.endB == change.start)
							 | 
						||
| 
								 | 
							
								        view.input.lastAndroidDelete = Date.now();
							 | 
						||
| 
								 | 
							
								    // This tries to detect Android virtual keyboard
							 | 
						||
| 
								 | 
							
								    // enter-and-pick-suggestion action. That sometimes (see issue
							 | 
						||
| 
								 | 
							
								    // #1059) first fires a DOM mutation, before moving the selection to
							 | 
						||
| 
								 | 
							
								    // the newly created block. And then, because ProseMirror cleans up
							 | 
						||
| 
								 | 
							
								    // the DOM selection, it gives up moving the selection entirely,
							 | 
						||
| 
								 | 
							
								    // leaving the cursor in the wrong place. When that happens, we drop
							 | 
						||
| 
								 | 
							
								    // the new paragraph from the initial change, and fire a simulated
							 | 
						||
| 
								 | 
							
								    // enter key afterwards.
							 | 
						||
| 
								 | 
							
								    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) { // Deletion
							 | 
						||
| 
								 | 
							
								            // IE11 sometimes weirdly moves the DOM selection around after
							 | 
						||
| 
								 | 
							
								            // backspacing out the first element in a textblock
							 | 
						||
| 
								 | 
							
								            if (ie && ie_version <= 11 && $from.parentOffset == 0) {
							 | 
						||
| 
								 | 
							
								                view.domObserver.suppressSelectionUpdates();
							 | 
						||
| 
								 | 
							
								                setTimeout(() => selectionToDOM(view), 20);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            tr = view.state.tr.delete(chFrom, chTo);
							 | 
						||
| 
								 | 
							
								            storedMarks = doc.resolve(change.start).marksAcross(doc.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)) {
							 | 
						||
| 
								 | 
							
								            // Both positions in the same text node -- simply insert text
							 | 
						||
| 
								 | 
							
								            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 sel = resolveSelection(view, tr.doc, parse.sel);
							 | 
						||
| 
								 | 
							
								        // Chrome Android will sometimes, during composition, report the
							 | 
						||
| 
								 | 
							
								        // selection in the wrong place. If it looks like that is
							 | 
						||
| 
								 | 
							
								        // happening, don't update the selection.
							 | 
						||
| 
								 | 
							
								        // Edge just doesn't move the cursor forward when you start typing
							 | 
						||
| 
								 | 
							
								        // in an empty block or between br nodes.
							 | 
						||
| 
								 | 
							
								        if (sel && !(chrome && android && view.composing && sel.empty &&
							 | 
						||
| 
								 | 
							
								            (change.start != change.endB || view.input.lastAndroidDelete < Date.now() - 100) &&
							 | 
						||
| 
								 | 
							
								            (sel.head == chFrom || sel.head == tr.mapping.map(chTo) - 1) ||
							 | 
						||
| 
								 | 
							
								            ie && sel.empty && sel.head == chFrom))
							 | 
						||
| 
								 | 
							
								            tr.setSelection(sel);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (storedMarks)
							 | 
						||
| 
								 | 
							
								        tr.ensureMarks(storedMarks);
							 | 
						||
| 
								 | 
							
								    view.dispatch(tr.scrollIntoView());
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function resolveSelection(view, doc, parsedSel) {
							 | 
						||
| 
								 | 
							
								    if (Math.max(parsedSel.anchor, parsedSel.head) > doc.content.size)
							 | 
						||
| 
								 | 
							
								        return null;
							 | 
						||
| 
								 | 
							
								    return selectionBetween(view, doc.resolve(parsedSel.anchor), doc.resolve(parsedSel.head));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Given two same-length, non-empty fragments of inline content,
							 | 
						||
| 
								 | 
							
								// determine whether the first could be created from the second by
							 | 
						||
| 
								 | 
							
								// removing or adding a single mark type.
							 | 
						||
| 
								 | 
							
								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 looksLikeJoin(old, start, end, $newStart, $newEnd) {
							 | 
						||
| 
								 | 
							
								    if (!$newStart.parent.isTextblock ||
							 | 
						||
| 
								 | 
							
								        // 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);
							 | 
						||
| 
								 | 
							
								    // Start must be at the end of a block
							 | 
						||
| 
								 | 
							
								    if ($start.parentOffset < $start.parent.content.size || !$start.parent.isTextblock)
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								    let $next = old.resolve(skipClosingAndOpening($start, true, true));
							 | 
						||
| 
								 | 
							
								    // The next textblock must start before end and end near it
							 | 
						||
| 
								 | 
							
								    if (!$next.parent.isTextblock || $next.pos > end ||
							 | 
						||
| 
								 | 
							
								        skipClosingAndOpening($next, true, false) < end)
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								    // The fragments after the join point must match
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								        endB = start + (endB - endA);
							 | 
						||
| 
								 | 
							
								        endA = start;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else if (endB < start) {
							 | 
						||
| 
								 | 
							
								        let move = preferredPos <= start && preferredPos >= endB ? start - preferredPos : 0;
							 | 
						||
| 
								 | 
							
								        start -= move;
							 | 
						||
| 
								 | 
							
								        endA = start + (endA - endB);
							 | 
						||
| 
								 | 
							
								        endB = start;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return { start, endA, endB };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								@internal
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								const __serializeForClipboard = serializeForClipboard;
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								@internal
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								const __parseFromClipboard = parseFromClipboard;
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								@internal
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								const __endComposition = endComposition;
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								An editor view manages the DOM structure that represents an
							 | 
						||
| 
								 | 
							
								editable document. Its state and behavior are determined by its
							 | 
						||
| 
								 | 
							
								[props](https://prosemirror.net/docs/ref/#view.DirectEditorProps).
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								class EditorView {
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								        @internal
							 | 
						||
| 
								 | 
							
								        */
							 | 
						||
| 
								 | 
							
								        this.focused = false;
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								        Kludge used to work around a Chrome bug @internal
							 | 
						||
| 
								 | 
							
								        */
							 | 
						||
| 
								 | 
							
								        this.trackWrites = null;
							 | 
						||
| 
								 | 
							
								        this.mounted = false;
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								        @internal
							 | 
						||
| 
								 | 
							
								        */
							 | 
						||
| 
								 | 
							
								        this.markCursor = null;
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								        @internal
							 | 
						||
| 
								 | 
							
								        */
							 | 
						||
| 
								 | 
							
								        this.cursorWrapper = null;
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								        @internal
							 | 
						||
| 
								 | 
							
								        */
							 | 
						||
| 
								 | 
							
								        this.lastSelectedViewDesc = undefined;
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								        @internal
							 | 
						||
| 
								 | 
							
								        */
							 | 
						||
| 
								 | 
							
								        this.input = new InputState;
							 | 
						||
| 
								 | 
							
								        this.prevDirectPlugins = [];
							 | 
						||
| 
								 | 
							
								        this.pluginViews = [];
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								        Holds `true` when a hack node is needed in Firefox to prevent the
							 | 
						||
| 
								 | 
							
								        [space is eaten issue](https://github.com/ProseMirror/prosemirror/issues/651)
							 | 
						||
| 
								 | 
							
								        @internal
							 | 
						||
| 
								 | 
							
								        */
							 | 
						||
| 
								 | 
							
								        this.requiresGeckoHackNode = false;
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								        When editor content is being dragged, this object contains
							 | 
						||
| 
								 | 
							
								        information about the dragged slice and whether it is being
							 | 
						||
| 
								 | 
							
								        copied or moved. At any other time, it is null.
							 | 
						||
| 
								 | 
							
								        */
							 | 
						||
| 
								 | 
							
								        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) {
							 | 
						||
| 
								 | 
							
								        let prev = this.state, redraw = false, updateSel = false;
							 | 
						||
| 
								 | 
							
								        // When stored marks are added, stop composition, so that they can
							 | 
						||
| 
								 | 
							
								        // be displayed.
							 | 
						||
| 
								 | 
							
								        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();
							 | 
						||
| 
								 | 
							
								            // Work around an issue in Chrome, IE, and Edge where changing
							 | 
						||
| 
								 | 
							
								            // the DOM around an active selection puts it into a broken
							 | 
						||
| 
								 | 
							
								            // state where the thing the user sees differs from the
							 | 
						||
| 
								 | 
							
								            // selection reported by the Selection object (#710, #973,
							 | 
						||
| 
								 | 
							
								            // #1011, #1013, #1035).
							 | 
						||
| 
								 | 
							
								            let forceSelUpdate = updateDoc && (ie || chrome) && !this.composing &&
							 | 
						||
| 
								 | 
							
								                !prev.selection.empty && !state.selection.empty && selectionContextChanged(prev.selection, state.selection);
							 | 
						||
| 
								 | 
							
								            if (updateDoc) {
							 | 
						||
| 
								 | 
							
								                // If the node that the selection points into is written to,
							 | 
						||
| 
								 | 
							
								                // Chrome sometimes starts misreporting the selection, so this
							 | 
						||
| 
								 | 
							
								                // tracks that and forces a selection reset when our update
							 | 
						||
| 
								 | 
							
								                // did write to the node.
							 | 
						||
| 
								 | 
							
								                let chromeKludge = chrome ? (this.trackWrites = this.domSelectionRange().focusNode) : null;
							 | 
						||
| 
								 | 
							
								                if (redraw || !this.docView.update(state.doc, outerDeco, innerDeco, this)) {
							 | 
						||
| 
								 | 
							
								                    this.docView.updateOuterDeco([]);
							 | 
						||
| 
								 | 
							
								                    this.docView.destroy();
							 | 
						||
| 
								 | 
							
								                    this.docView = docViewDesc(state.doc, outerDeco, innerDeco, this.dom, this);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                if (chromeKludge && !this.trackWrites)
							 | 
						||
| 
								 | 
							
								                    forceSelUpdate = true;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            // Work around for an issue where an update arriving right between
							 | 
						||
| 
								 | 
							
								            // a DOM selection change and the "selectionchange" event for it
							 | 
						||
| 
								 | 
							
								            // can cause a spurious DOM selection update, disrupting mouse
							 | 
						||
| 
								 | 
							
								            // drag selection.
							 | 
						||
| 
								 | 
							
								            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 (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);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    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 prop = this.directPlugins[i].props[propName];
							 | 
						||
| 
								 | 
							
								            if (prop != null && (value = f ? f(prop) : prop))
							 | 
						||
| 
								 | 
							
								                return value;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let plugins = this.state.plugins;
							 | 
						||
| 
								 | 
							
								        if (plugins)
							 | 
						||
| 
								 | 
							
								            for (let i = 0; i < plugins.length; i++) {
							 | 
						||
| 
								 | 
							
								                let prop = plugins[i].props[propName];
							 | 
						||
| 
								 | 
							
								                if (prop != null && (value = f ? f(prop) : prop))
							 | 
						||
| 
								 | 
							
								                    return value;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								    Query whether the view has focus.
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								    hasFocus() {
							 | 
						||
| 
								 | 
							
								        // Work around IE not handling focus correctly if resize handles are shown.
							 | 
						||
| 
								 | 
							
								        // If the cursor is inside an element with resize handles, activeElement
							 | 
						||
| 
								 | 
							
								        // will be that element instead of this.dom.
							 | 
						||
| 
								 | 
							
								        if (ie) {
							 | 
						||
| 
								 | 
							
								            // If activeElement is within this.dom, and there are no other elements
							 | 
						||
| 
								 | 
							
								            // setting `contenteditable` to false in between, treat it as focused.
							 | 
						||
| 
								 | 
							
								            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;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								    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);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								    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() {
							 | 
						||
| 
								 | 
							
								        return safari && this.root.nodeType === 11 && deepActiveElement(this.dom.ownerDocument) == this.dom
							 | 
						||
| 
								 | 
							
								            ? safariShadowSelectionRange(this) : this.domSelection();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								    @internal
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								    domSelection() {
							 | 
						||
| 
								 | 
							
								        return this.root.getSelection();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function computeDocDeco(view) {
							 | 
						||
| 
								 | 
							
								    let attrs = Object.create(null);
							 | 
						||
| 
								 | 
							
								    attrs.class = "ProseMirror";
							 | 
						||
| 
								 | 
							
								    attrs.contenteditable = String(view.editable);
							 | 
						||
| 
								 | 
							
								    attrs.translate = "no";
							 | 
						||
| 
								 | 
							
								    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];
							 | 
						||
| 
								 | 
							
								                if (attr == "style") {
							 | 
						||
| 
								 | 
							
								                    attrs.style = (attrs.style ? attrs.style + ";" : "") + value[attr];
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                else if (!attrs[attr] && attr != "contenteditable" && attr != "nodeName")
							 | 
						||
| 
								 | 
							
								                    attrs[attr] = String(value[attr]);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								    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.head, 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 = 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");
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export { Decoration, DecorationSet, EditorView, __endComposition, __parseFromClipboard, __serializeForClipboard };
							 |