@handlewithcare/react-prosemirror 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +12 -0
- package/README.md +705 -0
- package/dist/cjs/browser.js +53 -0
- package/dist/cjs/components/ChildNodeViews.js +376 -0
- package/dist/cjs/components/CursorWrapper.js +91 -0
- package/dist/cjs/components/CustomNodeView.js +79 -0
- package/dist/cjs/components/DocNodeView.js +104 -0
- package/dist/cjs/components/LayoutGroup.js +111 -0
- package/dist/cjs/components/MarkView.js +115 -0
- package/dist/cjs/components/NativeWidgetView.js +109 -0
- package/dist/cjs/components/NodeView.js +196 -0
- package/dist/cjs/components/NodeViewComponentProps.js +4 -0
- package/dist/cjs/components/OutputSpec.js +88 -0
- package/dist/cjs/components/ProseMirror.js +103 -0
- package/dist/cjs/components/ProseMirrorDoc.js +92 -0
- package/dist/cjs/components/SeparatorHackView.js +100 -0
- package/dist/cjs/components/TextNodeView.js +112 -0
- package/dist/cjs/components/TrailingHackView.js +90 -0
- package/dist/cjs/components/WidgetView.js +95 -0
- package/dist/cjs/components/WidgetViewComponentProps.js +4 -0
- package/dist/cjs/components/__tests__/ProseMirror.composition.test.js +398 -0
- package/dist/cjs/components/__tests__/ProseMirror.domchange.test.js +270 -0
- package/dist/cjs/components/__tests__/ProseMirror.draw-decoration.test.js +1010 -0
- package/dist/cjs/components/__tests__/ProseMirror.draw.test.js +337 -0
- package/dist/cjs/components/__tests__/ProseMirror.node-view.test.js +315 -0
- package/dist/cjs/components/__tests__/ProseMirror.selection.test.js +444 -0
- package/dist/cjs/components/__tests__/ProseMirror.test.js +382 -0
- package/dist/cjs/contexts/ChildDescriptorsContext.js +19 -0
- package/dist/cjs/contexts/EditorContext.js +12 -0
- package/dist/cjs/contexts/EditorStateContext.js +12 -0
- package/dist/cjs/contexts/LayoutGroupContext.js +12 -0
- package/dist/cjs/contexts/NodeViewContext.js +12 -0
- package/dist/cjs/contexts/SelectNodeContext.js +12 -0
- package/dist/cjs/contexts/StopEventContext.js +12 -0
- package/dist/cjs/contexts/__tests__/DeferredLayoutEffects.test.js +141 -0
- package/dist/cjs/decorations/ReactWidgetType.js +58 -0
- package/dist/cjs/decorations/computeDocDeco.js +44 -0
- package/dist/cjs/decorations/internalTypes.js +4 -0
- package/dist/cjs/decorations/iterDeco.js +79 -0
- package/dist/cjs/decorations/viewDecorations.js +163 -0
- package/dist/cjs/dom.js +142 -0
- package/dist/cjs/hooks/__tests__/useEditorViewLayoutEffect.test.js +108 -0
- package/dist/cjs/hooks/useClientOnly.js +18 -0
- package/dist/cjs/hooks/useComponentEventListeners.js +39 -0
- package/dist/cjs/hooks/useEditor.js +287 -0
- package/dist/cjs/hooks/useEditorEffect.js +35 -0
- package/dist/cjs/hooks/useEditorEventCallback.js +33 -0
- package/dist/cjs/hooks/useEditorEventListener.js +34 -0
- package/dist/cjs/hooks/useEditorState.js +16 -0
- package/dist/cjs/hooks/useForceUpdate.js +15 -0
- package/dist/cjs/hooks/useLayoutGroupEffect.js +19 -0
- package/dist/cjs/hooks/useNodeViewDescriptor.js +115 -0
- package/dist/cjs/hooks/useReactKeys.js +17 -0
- package/dist/cjs/hooks/useSelectNode.js +28 -0
- package/dist/cjs/hooks/useStopEvent.js +24 -0
- package/dist/cjs/index.js +53 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/plugins/__tests__/reactKeys.test.js +81 -0
- package/dist/cjs/plugins/beforeInputPlugin.js +143 -0
- package/dist/cjs/plugins/componentEventListeners.js +35 -0
- package/dist/cjs/plugins/componentEventListenersPlugin.js +35 -0
- package/dist/cjs/plugins/reactKeys.js +96 -0
- package/dist/cjs/props.js +269 -0
- package/dist/cjs/selection/SelectionDOMObserver.js +174 -0
- package/dist/cjs/selection/hasFocusAndSelection.js +35 -0
- package/dist/cjs/selection/selectionFromDOM.js +77 -0
- package/dist/cjs/selection/selectionToDOM.js +226 -0
- package/dist/cjs/ssr.js +85 -0
- package/dist/cjs/testing/editorViewTestHelpers.js +111 -0
- package/dist/cjs/testing/setupProseMirrorView.js +94 -0
- package/dist/cjs/viewdesc.js +664 -0
- package/dist/esm/browser.js +43 -0
- package/dist/esm/components/ChildNodeViews.js +318 -0
- package/dist/esm/components/CursorWrapper.js +40 -0
- package/dist/esm/components/CustomNodeView.js +28 -0
- package/dist/esm/components/DocNodeView.js +53 -0
- package/dist/esm/components/LayoutGroup.js +66 -0
- package/dist/esm/components/MarkView.js +64 -0
- package/dist/esm/components/NativeWidgetView.js +58 -0
- package/dist/esm/components/NodeView.js +145 -0
- package/dist/esm/components/NodeViewComponentProps.js +1 -0
- package/dist/esm/components/OutputSpec.js +38 -0
- package/dist/esm/components/ProseMirror.js +52 -0
- package/dist/esm/components/ProseMirrorDoc.js +34 -0
- package/dist/esm/components/SeparatorHackView.js +49 -0
- package/dist/esm/components/TextNodeView.js +102 -0
- package/dist/esm/components/TrailingHackView.js +39 -0
- package/dist/esm/components/WidgetView.js +44 -0
- package/dist/esm/components/WidgetViewComponentProps.js +1 -0
- package/dist/esm/components/__tests__/ProseMirror.composition.test.js +395 -0
- package/dist/esm/components/__tests__/ProseMirror.domchange.test.js +266 -0
- package/dist/esm/components/__tests__/ProseMirror.draw-decoration.test.js +967 -0
- package/dist/esm/components/__tests__/ProseMirror.draw.test.js +294 -0
- package/dist/esm/components/__tests__/ProseMirror.node-view.test.js +272 -0
- package/dist/esm/components/__tests__/ProseMirror.selection.test.js +440 -0
- package/dist/esm/components/__tests__/ProseMirror.test.js +339 -0
- package/dist/esm/contexts/ChildDescriptorsContext.js +9 -0
- package/dist/esm/contexts/EditorContext.js +7 -0
- package/dist/esm/contexts/EditorStateContext.js +2 -0
- package/dist/esm/contexts/LayoutGroupContext.js +2 -0
- package/dist/esm/contexts/NodeViewContext.js +2 -0
- package/dist/esm/contexts/SelectNodeContext.js +2 -0
- package/dist/esm/contexts/StopEventContext.js +2 -0
- package/dist/esm/contexts/__tests__/DeferredLayoutEffects.test.js +98 -0
- package/dist/esm/decorations/ReactWidgetType.js +40 -0
- package/dist/esm/decorations/computeDocDeco.js +44 -0
- package/dist/esm/decorations/internalTypes.js +1 -0
- package/dist/esm/decorations/iterDeco.js +73 -0
- package/dist/esm/decorations/viewDecorations.js +163 -0
- package/dist/esm/dom.js +105 -0
- package/dist/esm/hooks/__tests__/useEditorViewLayoutEffect.test.js +99 -0
- package/dist/esm/hooks/useClientOnly.js +8 -0
- package/dist/esm/hooks/useComponentEventListeners.js +54 -0
- package/dist/esm/hooks/useEditor.js +278 -0
- package/dist/esm/hooks/useEditorEffect.js +38 -0
- package/dist/esm/hooks/useEditorEventCallback.js +35 -0
- package/dist/esm/hooks/useEditorEventListener.js +28 -0
- package/dist/esm/hooks/useEditorState.js +8 -0
- package/dist/esm/hooks/useForceUpdate.js +8 -0
- package/dist/esm/hooks/useLayoutGroupEffect.js +9 -0
- package/dist/esm/hooks/useNodeViewDescriptor.js +105 -0
- package/dist/esm/hooks/useReactKeys.js +7 -0
- package/dist/esm/hooks/useSelectNode.js +18 -0
- package/dist/esm/hooks/useStopEvent.js +14 -0
- package/dist/esm/index.js +11 -0
- package/dist/esm/plugins/__tests__/reactKeys.test.js +77 -0
- package/dist/esm/plugins/beforeInputPlugin.js +133 -0
- package/dist/esm/plugins/componentEventListeners.js +25 -0
- package/dist/esm/plugins/componentEventListenersPlugin.js +25 -0
- package/dist/esm/plugins/reactKeys.js +81 -0
- package/dist/esm/props.js +251 -0
- package/dist/esm/selection/SelectionDOMObserver.js +164 -0
- package/dist/esm/selection/hasFocusAndSelection.js +17 -0
- package/dist/esm/selection/selectionFromDOM.js +59 -0
- package/dist/esm/selection/selectionToDOM.js +196 -0
- package/dist/esm/ssr.js +82 -0
- package/dist/esm/testing/editorViewTestHelpers.js +88 -0
- package/dist/esm/testing/setupProseMirrorView.js +76 -0
- package/dist/esm/viewdesc.js +654 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/browser.d.ts +15 -0
- package/dist/types/components/ChildNodeViews.d.ts +9 -0
- package/dist/types/components/CursorWrapper.d.ts +5 -0
- package/dist/types/components/CustomNodeView.d.ts +21 -0
- package/dist/types/components/DocNodeView.d.ts +20 -0
- package/dist/types/components/LayoutGroup.d.ts +12 -0
- package/dist/types/components/MarkView.d.ts +9 -0
- package/dist/types/components/NativeWidgetView.d.ts +8 -0
- package/dist/types/components/NodeView.d.ts +11 -0
- package/dist/types/components/NodeViewComponentProps.d.ts +12 -0
- package/dist/types/components/OutputSpec.d.ts +8 -0
- package/dist/types/components/ProseMirror.d.ts +15 -0
- package/dist/types/components/ProseMirrorDoc.d.ts +10 -0
- package/dist/types/components/SeparatorHackView.d.ts +6 -0
- package/dist/types/components/TextNodeView.d.ts +23 -0
- package/dist/types/components/TrailingHackView.d.ts +6 -0
- package/dist/types/components/WidgetView.d.ts +8 -0
- package/dist/types/components/WidgetViewComponentProps.d.ts +6 -0
- package/dist/types/components/__tests__/ProseMirror.composition.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.domchange.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.draw-decoration.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.draw.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.node-view.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.selection.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.test.d.ts +1 -0
- package/dist/types/contexts/ChildDescriptorsContext.d.ts +6 -0
- package/dist/types/contexts/EditorContext.d.ts +14 -0
- package/dist/types/contexts/EditorStateContext.d.ts +2 -0
- package/dist/types/contexts/LayoutGroupContext.d.ts +5 -0
- package/dist/types/contexts/NodeViewContext.d.ts +6 -0
- package/dist/types/contexts/SelectNodeContext.d.ts +3 -0
- package/dist/types/contexts/StopEventContext.d.ts +3 -0
- package/dist/types/contexts/__tests__/DeferredLayoutEffects.test.d.ts +1 -0
- package/dist/types/decorations/ReactWidgetType.d.ts +39 -0
- package/dist/types/decorations/computeDocDeco.d.ts +13 -0
- package/dist/types/decorations/internalTypes.d.ts +16 -0
- package/dist/types/decorations/iterDeco.d.ts +3 -0
- package/dist/types/decorations/viewDecorations.d.ts +13 -0
- package/dist/types/dom.d.ts +22 -0
- package/dist/types/hooks/__tests__/useEditorViewLayoutEffect.test.d.ts +1 -0
- package/dist/types/hooks/useClientOnly.d.ts +1 -0
- package/dist/types/hooks/useComponentEventListeners.d.ts +33 -0
- package/dist/types/hooks/useEditor.d.ts +66 -0
- package/dist/types/hooks/useEditorEffect.d.ts +17 -0
- package/dist/types/hooks/useEditorEventCallback.d.ts +15 -0
- package/dist/types/hooks/useEditorEventListener.d.ts +8 -0
- package/dist/types/hooks/useEditorState.d.ts +5 -0
- package/dist/types/hooks/useForceUpdate.d.ts +5 -0
- package/dist/types/hooks/useLayoutGroupEffect.d.ts +3 -0
- package/dist/types/hooks/useNodeViewDescriptor.d.ts +11 -0
- package/dist/types/hooks/useReactKeys.d.ts +5 -0
- package/dist/types/hooks/useSelectNode.d.ts +1 -0
- package/dist/types/hooks/useStopEvent.d.ts +2 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/plugins/__tests__/reactKeys.test.d.ts +1 -0
- package/dist/types/plugins/beforeInputPlugin.d.ts +3 -0
- package/dist/types/plugins/componentEventListeners.d.ts +4 -0
- package/dist/types/plugins/componentEventListenersPlugin.d.ts +4 -0
- package/dist/types/plugins/reactKeys.d.ts +19 -0
- package/dist/types/props.d.ts +1174 -0
- package/dist/types/selection/SelectionDOMObserver.d.ts +34 -0
- package/dist/types/selection/hasFocusAndSelection.d.ts +3 -0
- package/dist/types/selection/selectionFromDOM.d.ts +4 -0
- package/dist/types/selection/selectionToDOM.d.ts +9 -0
- package/dist/types/ssr.d.ts +19 -0
- package/dist/types/testing/editorViewTestHelpers.d.ts +23 -0
- package/dist/types/testing/setupProseMirrorView.d.ts +2 -0
- package/dist/types/viewdesc.d.ts +131 -0
- package/package.json +113 -0
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { Fragment } from "prosemirror-model";
|
|
2
|
+
import { browser } from "./browser.js";
|
|
3
|
+
import { domIndex, isEquivalentPosition } from "./selection/selectionToDOM.js";
|
|
4
|
+
// View descriptions are data structures that describe the DOM that is
|
|
5
|
+
// used to represent the editor's content. They are used for:
|
|
6
|
+
//
|
|
7
|
+
// - Incremental redrawing when the document changes
|
|
8
|
+
//
|
|
9
|
+
// - Figuring out what part of the document a given DOM position
|
|
10
|
+
// corresponds to
|
|
11
|
+
//
|
|
12
|
+
// - Wiring in custom implementations of the editing interface for a
|
|
13
|
+
// given node
|
|
14
|
+
//
|
|
15
|
+
// They form a doubly-linked mutable tree, starting at `view.docView`.
|
|
16
|
+
export function sortViewDescs(a, b) {
|
|
17
|
+
if (a instanceof TrailingHackViewDesc) return 1;
|
|
18
|
+
if (b instanceof TrailingHackViewDesc) return -1;
|
|
19
|
+
return a.getPos() - b.getPos();
|
|
20
|
+
}
|
|
21
|
+
const NOT_DIRTY = 0, CHILD_DIRTY = 1, CONTENT_DIRTY = 2, NODE_DIRTY = 3;
|
|
22
|
+
// Superclass for the various kinds of descriptions. Defines their
|
|
23
|
+
// basic structure and shared methods.
|
|
24
|
+
export class ViewDesc {
|
|
25
|
+
parent;
|
|
26
|
+
children;
|
|
27
|
+
getPos;
|
|
28
|
+
dom;
|
|
29
|
+
contentDOM;
|
|
30
|
+
dirty;
|
|
31
|
+
node;
|
|
32
|
+
constructor(parent, children, getPos, dom, // This is the node that holds the child views. It may be null for
|
|
33
|
+
// descs that don't have children.
|
|
34
|
+
contentDOM){
|
|
35
|
+
this.parent = parent;
|
|
36
|
+
this.children = children;
|
|
37
|
+
this.getPos = getPos;
|
|
38
|
+
this.dom = dom;
|
|
39
|
+
this.contentDOM = contentDOM;
|
|
40
|
+
this.dirty = NOT_DIRTY;
|
|
41
|
+
// An expando property on the DOM node provides a link back to its
|
|
42
|
+
// description.
|
|
43
|
+
// @ts-expect-error We're using custom view implementations here but
|
|
44
|
+
// we match the API so this is relatively safe.
|
|
45
|
+
dom.pmViewDesc = this;
|
|
46
|
+
}
|
|
47
|
+
// Used to check whether a given description corresponds to a
|
|
48
|
+
// widget/mark/node.
|
|
49
|
+
matchesWidget(_widget) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
matchesMark(_mark) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
matchesNode(_node, _outerDeco, _innerDeco) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
// @ts-expect-error ...
|
|
59
|
+
matchesHack(nodeName) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
// When parsing in-editor content (in domchange.js), we allow
|
|
63
|
+
// descriptions to determine the parse rules that should be used to
|
|
64
|
+
// parse them.
|
|
65
|
+
parseRule() {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
// Used by the editor's event handler to ignore events that come
|
|
69
|
+
// from certain descs.
|
|
70
|
+
// @ts-expect-error ...
|
|
71
|
+
stopEvent(event) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
// The size of the content represented by this desc.
|
|
75
|
+
get size() {
|
|
76
|
+
let size = 0;
|
|
77
|
+
for(let i = 0; i < this.children.length; i++)// @ts-expect-error ...
|
|
78
|
+
size += this.children[i].size;
|
|
79
|
+
return size;
|
|
80
|
+
}
|
|
81
|
+
// For block nodes, this represents the space taken up by their
|
|
82
|
+
// start/end tokens.
|
|
83
|
+
get border() {
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
destroy() {
|
|
87
|
+
// pass
|
|
88
|
+
}
|
|
89
|
+
posBeforeChild(child) {
|
|
90
|
+
for(let i = 0, pos = this.posAtStart;; i++){
|
|
91
|
+
const cur = this.children[i];
|
|
92
|
+
if (cur == child) return pos;
|
|
93
|
+
// @ts-expect-error ...
|
|
94
|
+
pos += cur.size;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
get posBefore() {
|
|
98
|
+
return this.parent.posBeforeChild(this);
|
|
99
|
+
}
|
|
100
|
+
get posAtStart() {
|
|
101
|
+
return this.parent ? this.parent.posBeforeChild(this) + this.border : 0;
|
|
102
|
+
}
|
|
103
|
+
get posAfter() {
|
|
104
|
+
return this.posBefore + this.size;
|
|
105
|
+
}
|
|
106
|
+
get posAtEnd() {
|
|
107
|
+
return this.posAtStart + this.size - 2 * this.border;
|
|
108
|
+
}
|
|
109
|
+
localPosFromDOM(dom, offset, bias) {
|
|
110
|
+
// If the DOM position is in the content, use the child desc after
|
|
111
|
+
// it to figure out a position.
|
|
112
|
+
if (this.contentDOM && this.contentDOM.contains(dom.nodeType == 1 ? dom : dom.parentNode)) {
|
|
113
|
+
if (bias < 0) {
|
|
114
|
+
let domBefore, desc;
|
|
115
|
+
if (dom == this.contentDOM) {
|
|
116
|
+
domBefore = dom.childNodes[offset - 1];
|
|
117
|
+
} else {
|
|
118
|
+
while(dom.parentNode != this.contentDOM)dom = dom.parentNode;
|
|
119
|
+
domBefore = dom.previousSibling;
|
|
120
|
+
}
|
|
121
|
+
while(domBefore && !((desc = domBefore.pmViewDesc) && desc.parent == this))domBefore = domBefore.previousSibling;
|
|
122
|
+
return domBefore ? this.posBeforeChild(desc) + desc.size : this.posAtStart;
|
|
123
|
+
} else {
|
|
124
|
+
let domAfter, desc;
|
|
125
|
+
if (dom == this.contentDOM) {
|
|
126
|
+
domAfter = dom.childNodes[offset];
|
|
127
|
+
} else {
|
|
128
|
+
while(dom.parentNode != this.contentDOM)dom = dom.parentNode;
|
|
129
|
+
domAfter = dom.nextSibling;
|
|
130
|
+
}
|
|
131
|
+
while(domAfter && !((desc = domAfter.pmViewDesc) && desc.parent == this))domAfter = domAfter.nextSibling;
|
|
132
|
+
return domAfter ? this.posBeforeChild(desc) : this.posAtEnd;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Otherwise, use various heuristics, falling back on the bias
|
|
136
|
+
// parameter, to determine whether to return the position at the
|
|
137
|
+
// start or at the end of this view desc.
|
|
138
|
+
let atEnd;
|
|
139
|
+
if (dom == this.dom && this.contentDOM) {
|
|
140
|
+
atEnd = offset > domIndex(this.contentDOM);
|
|
141
|
+
} else if (this.contentDOM && this.contentDOM != this.dom && this.dom.contains(this.contentDOM)) {
|
|
142
|
+
atEnd = dom.compareDocumentPosition(this.contentDOM) & 2;
|
|
143
|
+
} else if (this.dom.firstChild) {
|
|
144
|
+
if (offset == 0) for(let search = dom;; search = search.parentNode){
|
|
145
|
+
if (search == this.dom) {
|
|
146
|
+
atEnd = false;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
if (search.previousSibling) break;
|
|
150
|
+
}
|
|
151
|
+
if (atEnd == null && offset == dom.childNodes.length) for(let search = dom;; search = search.parentNode){
|
|
152
|
+
if (search == this.dom) {
|
|
153
|
+
atEnd = true;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
if (search.nextSibling) break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return (atEnd == null ? bias > 0 : atEnd) ? this.posAtEnd : this.posAtStart;
|
|
160
|
+
}
|
|
161
|
+
nearestDesc(dom) {
|
|
162
|
+
let onlyNodes = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;
|
|
163
|
+
for(let first = true, cur = dom; cur; cur = cur.parentNode){
|
|
164
|
+
const desc = this.getDesc(cur);
|
|
165
|
+
let nodeDOM;
|
|
166
|
+
if (desc && (!onlyNodes || desc.node)) {
|
|
167
|
+
// If dom is outside of this desc's nodeDOM, don't count it.
|
|
168
|
+
if (first && (nodeDOM = desc.nodeDOM) && !(nodeDOM.nodeType == 1 ? nodeDOM.contains(dom.nodeType == 1 ? dom : dom.parentNode) : nodeDOM == dom)) first = false;
|
|
169
|
+
else return desc;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
getDesc(dom) {
|
|
175
|
+
const desc = dom.pmViewDesc;
|
|
176
|
+
for(let cur = desc; cur; cur = cur.parent)if (cur == this) return desc;
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
posFromDOM(dom, offset, bias) {
|
|
180
|
+
for(let scan = dom; scan; scan = scan.parentNode){
|
|
181
|
+
const desc = this.getDesc(scan);
|
|
182
|
+
if (desc) return desc.localPosFromDOM(dom, offset, bias);
|
|
183
|
+
}
|
|
184
|
+
return -1;
|
|
185
|
+
}
|
|
186
|
+
// Find the desc for the node after the given pos, if any. (When a
|
|
187
|
+
// parent node overrode rendering, there might not be one.)
|
|
188
|
+
descAt(pos) {
|
|
189
|
+
for(let i = 0, offset = 0; i < this.children.length; i++){
|
|
190
|
+
let child = this.children[i];
|
|
191
|
+
const end = offset + child.size;
|
|
192
|
+
if (offset == pos && end != offset) {
|
|
193
|
+
while(!child.border && child.children.length)child = child.children[0];
|
|
194
|
+
return child;
|
|
195
|
+
}
|
|
196
|
+
if (pos < end) return child.descAt(pos - offset - child.border);
|
|
197
|
+
offset = end;
|
|
198
|
+
}
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
domFromPos(pos, side) {
|
|
202
|
+
if (!this.contentDOM) return {
|
|
203
|
+
node: this.dom,
|
|
204
|
+
offset: 0,
|
|
205
|
+
atom: pos + 1
|
|
206
|
+
};
|
|
207
|
+
// First find the position in the child array
|
|
208
|
+
let i = 0, offset = 0;
|
|
209
|
+
for(let curPos = 0; i < this.children.length; i++){
|
|
210
|
+
const child = this.children[i], end = curPos + child.size;
|
|
211
|
+
if (end > pos || child instanceof TrailingHackViewDesc) {
|
|
212
|
+
offset = pos - curPos;
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
curPos = end;
|
|
216
|
+
}
|
|
217
|
+
// If this points into the middle of a child, call through
|
|
218
|
+
if (offset) return this.children[i].domFromPos(offset - this.children[i].border, side);
|
|
219
|
+
// Go back if there were any zero-length widgets with side >= 0 before this point
|
|
220
|
+
for(let prev; i && !(prev = this.children[i - 1]).size && prev instanceof WidgetViewDesc && prev.side >= 0; i--){
|
|
221
|
+
// ...
|
|
222
|
+
}
|
|
223
|
+
// Scan towards the first useable node
|
|
224
|
+
if (side <= 0) {
|
|
225
|
+
let prev, enter = true;
|
|
226
|
+
for(;; i--, enter = false){
|
|
227
|
+
prev = i ? this.children[i - 1] : null;
|
|
228
|
+
if (!prev || prev.dom.parentNode == this.contentDOM) break;
|
|
229
|
+
}
|
|
230
|
+
if (prev && side && enter && !prev.border && !prev.domAtom) return prev.domFromPos(prev.size, side);
|
|
231
|
+
return {
|
|
232
|
+
node: this.contentDOM,
|
|
233
|
+
offset: prev ? domIndex(prev.dom) + 1 : 0
|
|
234
|
+
};
|
|
235
|
+
} else {
|
|
236
|
+
let next, enter = true;
|
|
237
|
+
for(;; i++, enter = false){
|
|
238
|
+
next = i < this.children.length ? this.children[i] : null;
|
|
239
|
+
if (!next || next.dom.parentNode == this.contentDOM) break;
|
|
240
|
+
}
|
|
241
|
+
if (next && enter && !next.border && !next.domAtom) return next.domFromPos(0, side);
|
|
242
|
+
return {
|
|
243
|
+
node: this.contentDOM,
|
|
244
|
+
offset: next ? domIndex(next.dom) : this.contentDOM.childNodes.length
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Used to find a DOM range in a single parent for a given changed
|
|
249
|
+
// range.
|
|
250
|
+
parseRange(from, to) {
|
|
251
|
+
let base = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : 0;
|
|
252
|
+
if (this.children.length == 0) return {
|
|
253
|
+
node: this.contentDOM,
|
|
254
|
+
from,
|
|
255
|
+
to,
|
|
256
|
+
fromOffset: 0,
|
|
257
|
+
toOffset: this.contentDOM.childNodes.length
|
|
258
|
+
};
|
|
259
|
+
let fromOffset = -1, toOffset = -1;
|
|
260
|
+
for(let offset = base, i = 0;; i++){
|
|
261
|
+
const child = this.children[i], end = offset + child.size;
|
|
262
|
+
if (fromOffset == -1 && from <= end) {
|
|
263
|
+
const childBase = offset + child.border;
|
|
264
|
+
// FIXME maybe descend mark views to parse a narrower range?
|
|
265
|
+
if (from >= childBase && to <= end - child.border && child.node && child.contentDOM && this.contentDOM.contains(child.contentDOM)) return child.parseRange(from, to, childBase);
|
|
266
|
+
from = offset;
|
|
267
|
+
for(let j = i; j > 0; j--){
|
|
268
|
+
const prev = this.children[j - 1];
|
|
269
|
+
if (prev.size && prev.dom.parentNode == this.contentDOM && !prev.emptyChildAt(1)) {
|
|
270
|
+
fromOffset = domIndex(prev.dom) + 1;
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
from -= prev.size;
|
|
274
|
+
}
|
|
275
|
+
if (fromOffset == -1) fromOffset = 0;
|
|
276
|
+
}
|
|
277
|
+
if (fromOffset > -1 && (end > to || i == this.children.length - 1)) {
|
|
278
|
+
to = end;
|
|
279
|
+
for(let j = i + 1; j < this.children.length; j++){
|
|
280
|
+
const next = this.children[j];
|
|
281
|
+
if (next.size && next.dom.parentNode == this.contentDOM && !next.emptyChildAt(-1)) {
|
|
282
|
+
toOffset = domIndex(next.dom);
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
to += next.size;
|
|
286
|
+
}
|
|
287
|
+
if (toOffset == -1) toOffset = this.contentDOM.childNodes.length;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
offset = end;
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
node: this.contentDOM,
|
|
294
|
+
from,
|
|
295
|
+
to,
|
|
296
|
+
fromOffset,
|
|
297
|
+
toOffset
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
emptyChildAt(side) {
|
|
301
|
+
if (this.border || !this.contentDOM || !this.children.length) return false;
|
|
302
|
+
const child = this.children[side < 0 ? 0 : this.children.length - 1];
|
|
303
|
+
// @ts-expect-error ...
|
|
304
|
+
return child.size == 0 || child.emptyChildAt(side);
|
|
305
|
+
}
|
|
306
|
+
domAfterPos(pos) {
|
|
307
|
+
const { node, offset } = this.domFromPos(pos, 0);
|
|
308
|
+
if (node.nodeType != 1 || offset == node.childNodes.length) throw new RangeError("No node after pos " + pos);
|
|
309
|
+
// @ts-expect-error ...
|
|
310
|
+
return node.childNodes[offset];
|
|
311
|
+
}
|
|
312
|
+
// View descs are responsible for setting any selection that falls
|
|
313
|
+
// entirely inside of them, so that custom implementations can do
|
|
314
|
+
// custom things with the selection. Note that this falls apart when
|
|
315
|
+
// a selection starts in such a node and ends in another, in which
|
|
316
|
+
// case we just use whatever domFromPos produces as a best effort.
|
|
317
|
+
setSelection(anchor, head, root) {
|
|
318
|
+
let force = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : false;
|
|
319
|
+
// If the selection falls entirely in a child, give it to that child
|
|
320
|
+
const from = Math.min(anchor, head), to = Math.max(anchor, head);
|
|
321
|
+
for(let i = 0, offset = 0; i < this.children.length; i++){
|
|
322
|
+
const child = this.children[i], end = offset + child.size;
|
|
323
|
+
if (from > offset && to < end) return child.setSelection(anchor - offset - child.border, head - offset - child.border, root, force);
|
|
324
|
+
offset = end;
|
|
325
|
+
}
|
|
326
|
+
let anchorDOM = this.domFromPos(anchor, anchor ? -1 : 1);
|
|
327
|
+
let headDOM = head == anchor ? anchorDOM : this.domFromPos(head, head ? -1 : 1);
|
|
328
|
+
const domSel = root.getSelection();
|
|
329
|
+
let brKludge = false;
|
|
330
|
+
// On Firefox, using Selection.collapse to put the cursor after a
|
|
331
|
+
// BR node for some reason doesn't always work (#1073). On Safari,
|
|
332
|
+
// the cursor sometimes inexplicable visually lags behind its
|
|
333
|
+
// reported position in such situations (#1092).
|
|
334
|
+
if ((browser.gecko || browser.safari) && anchor == head) {
|
|
335
|
+
const { node, offset } = anchorDOM;
|
|
336
|
+
if (node.nodeType == 3) {
|
|
337
|
+
brKludge = !!(offset && node.nodeValue?.[offset - 1] == "\n");
|
|
338
|
+
// Issue #1128
|
|
339
|
+
if (brKludge && offset == node.nodeValue.length) {
|
|
340
|
+
for(let scan = node, after; scan; scan = scan.parentNode){
|
|
341
|
+
if (after = scan.nextSibling) {
|
|
342
|
+
if (after.nodeName == "BR") anchorDOM = headDOM = {
|
|
343
|
+
node: after.parentNode,
|
|
344
|
+
offset: domIndex(after) + 1
|
|
345
|
+
};
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
const desc = scan.pmViewDesc;
|
|
349
|
+
if (desc && desc.node && desc.node.isBlock) break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
const prev = node.childNodes[offset - 1];
|
|
354
|
+
// @ts-expect-error ...
|
|
355
|
+
brKludge = prev && (prev.nodeName == "BR" || prev.contentEditable == "false");
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// Firefox can act strangely when the selection is in front of an
|
|
359
|
+
// uneditable node. See #1163 and https://bugzilla.mozilla.org/show_bug.cgi?id=1709536
|
|
360
|
+
if (browser.gecko && domSel.focusNode && domSel.focusNode != headDOM.node && domSel.focusNode.nodeType == 1) {
|
|
361
|
+
const after = domSel.focusNode.childNodes[domSel.focusOffset];
|
|
362
|
+
if (after && after.contentEditable == "false") force = true;
|
|
363
|
+
}
|
|
364
|
+
if (!(force || brKludge && browser.safari) && isEquivalentPosition(anchorDOM.node, anchorDOM.offset, domSel.anchorNode, domSel.anchorOffset) && isEquivalentPosition(headDOM.node, headDOM.offset, domSel.focusNode, domSel.focusOffset)) return;
|
|
365
|
+
// Selection.extend can be used to create an 'inverted' selection
|
|
366
|
+
// (one where the focus is before the anchor), but not all
|
|
367
|
+
// browsers support it yet.
|
|
368
|
+
let domSelExtended = false;
|
|
369
|
+
if ((domSel.extend || anchor == head) && !brKludge) {
|
|
370
|
+
domSel.collapse(anchorDOM.node, anchorDOM.offset);
|
|
371
|
+
try {
|
|
372
|
+
if (anchor != head) domSel.extend(headDOM.node, headDOM.offset);
|
|
373
|
+
domSelExtended = true;
|
|
374
|
+
} catch (_) {
|
|
375
|
+
// In some cases with Chrome the selection is empty after calling
|
|
376
|
+
// collapse, even when it should be valid. This appears to be a bug, but
|
|
377
|
+
// it is difficult to isolate. If this happens fallback to the old path
|
|
378
|
+
// without using extend.
|
|
379
|
+
// Similarly, this could crash on Safari if the editor is hidden, and
|
|
380
|
+
// there was no selection.
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (!domSelExtended) {
|
|
384
|
+
if (anchor > head) {
|
|
385
|
+
const tmp = anchorDOM;
|
|
386
|
+
anchorDOM = headDOM;
|
|
387
|
+
headDOM = tmp;
|
|
388
|
+
}
|
|
389
|
+
const range = document.createRange();
|
|
390
|
+
range.setEnd(headDOM.node, headDOM.offset);
|
|
391
|
+
range.setStart(anchorDOM.node, anchorDOM.offset);
|
|
392
|
+
domSel.removeAllRanges();
|
|
393
|
+
domSel.addRange(range);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
ignoreMutation(mutation) {
|
|
397
|
+
return !this.contentDOM && mutation.type != "selection";
|
|
398
|
+
}
|
|
399
|
+
get contentLost() {
|
|
400
|
+
return this.contentDOM && this.contentDOM != this.dom && !this.dom.contains(this.contentDOM);
|
|
401
|
+
}
|
|
402
|
+
// Remove a subtree of the element tree that has been touched
|
|
403
|
+
// by a DOM change, so that the next update will redraw it.
|
|
404
|
+
markDirty(from, to) {
|
|
405
|
+
for(let offset = 0, i = 0; i < this.children.length; i++){
|
|
406
|
+
const child = this.children[i], end = offset + child.size;
|
|
407
|
+
if (offset == end ? from <= end && to >= offset : from < end && to > offset) {
|
|
408
|
+
const startInside = offset + child.border, endInside = end - child.border;
|
|
409
|
+
if (from >= startInside && to <= endInside) {
|
|
410
|
+
this.dirty = from == offset || to == end ? CONTENT_DIRTY : CHILD_DIRTY;
|
|
411
|
+
if (from == startInside && to == endInside && (child.contentLost || child.dom.parentNode != this.contentDOM)) child.dirty = NODE_DIRTY;
|
|
412
|
+
else child.markDirty(from - startInside, to - startInside);
|
|
413
|
+
return;
|
|
414
|
+
} else {
|
|
415
|
+
child.dirty = child.dom == child.contentDOM && child.dom.parentNode == this.contentDOM && !child.children.length ? CONTENT_DIRTY : NODE_DIRTY;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
offset = end;
|
|
419
|
+
}
|
|
420
|
+
this.dirty = CONTENT_DIRTY;
|
|
421
|
+
}
|
|
422
|
+
markParentsDirty() {
|
|
423
|
+
let level = 1;
|
|
424
|
+
for(let node = this.parent; node; node = node.parent, level++){
|
|
425
|
+
const dirty = level == 1 ? CONTENT_DIRTY : CHILD_DIRTY;
|
|
426
|
+
if (node.dirty < dirty) node.dirty = dirty;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
get domAtom() {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
get ignoreForCoords() {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
// A widget desc represents a widget decoration, which is a DOM node
|
|
437
|
+
// drawn between the document nodes.
|
|
438
|
+
export class WidgetViewDesc extends ViewDesc {
|
|
439
|
+
widget;
|
|
440
|
+
constructor(parent, getPos, widget, dom){
|
|
441
|
+
super(parent, [], getPos, dom, null), this.widget = widget;
|
|
442
|
+
this.widget = widget;
|
|
443
|
+
}
|
|
444
|
+
matchesWidget(widget) {
|
|
445
|
+
return this.dirty == NOT_DIRTY && widget.type.eq(this.widget.type);
|
|
446
|
+
}
|
|
447
|
+
parseRule() {
|
|
448
|
+
return {
|
|
449
|
+
ignore: true
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
stopEvent(event) {
|
|
453
|
+
const stop = this.widget.spec.stopEvent;
|
|
454
|
+
return stop ? stop(event) : false;
|
|
455
|
+
}
|
|
456
|
+
ignoreMutation(mutation) {
|
|
457
|
+
return mutation.type != "selection" || this.widget.spec.ignoreSelection;
|
|
458
|
+
}
|
|
459
|
+
get domAtom() {
|
|
460
|
+
return true;
|
|
461
|
+
}
|
|
462
|
+
get side() {
|
|
463
|
+
return this.widget.type.side;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
export class CompositionViewDesc extends ViewDesc {
|
|
467
|
+
textDOM;
|
|
468
|
+
text;
|
|
469
|
+
constructor(parent, getPos, dom, textDOM, text){
|
|
470
|
+
super(parent, [], getPos, dom, null), this.textDOM = textDOM, this.text = text;
|
|
471
|
+
}
|
|
472
|
+
get size() {
|
|
473
|
+
return this.text.length;
|
|
474
|
+
}
|
|
475
|
+
localPosFromDOM(dom, offset) {
|
|
476
|
+
if (dom != this.textDOM) return this.posAtStart + (offset ? this.size : 0);
|
|
477
|
+
return this.posAtStart + offset;
|
|
478
|
+
}
|
|
479
|
+
domFromPos(pos) {
|
|
480
|
+
return {
|
|
481
|
+
node: this.textDOM,
|
|
482
|
+
offset: pos
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
ignoreMutation(mut) {
|
|
486
|
+
return mut.type === "characterData" && mut.target.nodeValue == mut.oldValue;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// A mark desc represents a mark. May have multiple children,
|
|
490
|
+
// depending on how the mark is split. Note that marks are drawn using
|
|
491
|
+
// a fixed nesting order, for simplicity and predictability, so in
|
|
492
|
+
// some cases they will be split more often than would appear
|
|
493
|
+
// necessary.
|
|
494
|
+
export class MarkViewDesc extends ViewDesc {
|
|
495
|
+
mark;
|
|
496
|
+
constructor(parent, children, getPos, mark, dom, contentDOM){
|
|
497
|
+
super(parent, children, getPos, dom, contentDOM), this.mark = mark;
|
|
498
|
+
}
|
|
499
|
+
parseRule() {
|
|
500
|
+
if (this.dirty & NODE_DIRTY || this.mark.type.spec.reparseInView) return null;
|
|
501
|
+
return {
|
|
502
|
+
mark: this.mark.type.name,
|
|
503
|
+
attrs: this.mark.attrs,
|
|
504
|
+
contentElement: this.contentDOM
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
matchesMark(mark) {
|
|
508
|
+
return this.dirty != NODE_DIRTY && this.mark.eq(mark);
|
|
509
|
+
}
|
|
510
|
+
markDirty(from, to) {
|
|
511
|
+
super.markDirty(from, to);
|
|
512
|
+
// Move dirty info to nearest node view
|
|
513
|
+
if (this.dirty != NOT_DIRTY) {
|
|
514
|
+
let parent = this.parent;
|
|
515
|
+
while(!parent.node)parent = parent.parent;
|
|
516
|
+
if (parent.dirty < this.dirty) parent.dirty = this.dirty;
|
|
517
|
+
this.dirty = NOT_DIRTY;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// Node view descs are the main, most common type of view desc, and
|
|
522
|
+
// correspond to an actual node in the document. Unlike mark descs,
|
|
523
|
+
// they populate their child array themselves.
|
|
524
|
+
export class NodeViewDesc extends ViewDesc {
|
|
525
|
+
node;
|
|
526
|
+
outerDeco;
|
|
527
|
+
innerDeco;
|
|
528
|
+
nodeDOM;
|
|
529
|
+
stopEvent;
|
|
530
|
+
selectNode;
|
|
531
|
+
deselectNode;
|
|
532
|
+
constructor(parent, children, getPos, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, stopEvent, selectNode, deselectNode){
|
|
533
|
+
super(parent, children, getPos, dom, contentDOM), this.node = node, this.outerDeco = outerDeco, this.innerDeco = innerDeco, this.nodeDOM = nodeDOM, this.stopEvent = stopEvent, this.selectNode = selectNode, this.deselectNode = deselectNode;
|
|
534
|
+
}
|
|
535
|
+
updateOuterDeco() {
|
|
536
|
+
// pass
|
|
537
|
+
}
|
|
538
|
+
parseRule() {
|
|
539
|
+
// Experimental kludge to allow opt-in re-parsing of nodes
|
|
540
|
+
if (this.node.type.spec.reparseInView) return null;
|
|
541
|
+
// FIXME the assumption that this can always return the current
|
|
542
|
+
// attrs means that if the user somehow manages to change the
|
|
543
|
+
// attrs in the dom, that won't be picked up. Not entirely sure
|
|
544
|
+
// whether this is a problem
|
|
545
|
+
const rule = {
|
|
546
|
+
node: this.node.type.name,
|
|
547
|
+
attrs: this.node.attrs
|
|
548
|
+
};
|
|
549
|
+
if (this.node.type.whitespace == "pre") rule.preserveWhitespace = "full";
|
|
550
|
+
if (!this.contentDOM) {
|
|
551
|
+
rule.getContent = ()=>this.node.content;
|
|
552
|
+
} else if (!this.contentLost) {
|
|
553
|
+
rule.contentElement = this.contentDOM;
|
|
554
|
+
} else {
|
|
555
|
+
// Chrome likes to randomly recreate parent nodes when
|
|
556
|
+
// backspacing things. When that happens, this tries to find the
|
|
557
|
+
// new parent.
|
|
558
|
+
for(let i = this.children.length - 1; i >= 0; i--){
|
|
559
|
+
const child = this.children[i];
|
|
560
|
+
// @ts-expect-error ...
|
|
561
|
+
if (this.dom.contains(child.dom.parentNode)) {
|
|
562
|
+
// @ts-expect-error ...
|
|
563
|
+
rule.contentElement = child.dom.parentNode;
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
if (!rule.contentElement) rule.getContent = ()=>Fragment.empty;
|
|
568
|
+
}
|
|
569
|
+
return rule;
|
|
570
|
+
}
|
|
571
|
+
matchesNode(node, outerDeco, innerDeco) {
|
|
572
|
+
return this.dirty == NOT_DIRTY && node.eq(this.node) && sameOuterDeco(outerDeco, this.outerDeco) && innerDeco.eq(this.innerDeco);
|
|
573
|
+
}
|
|
574
|
+
get size() {
|
|
575
|
+
return this.node.nodeSize;
|
|
576
|
+
}
|
|
577
|
+
get border() {
|
|
578
|
+
return this.node.isLeaf ? 0 : 1;
|
|
579
|
+
}
|
|
580
|
+
// If this desc must be updated to match the given node decoration,
|
|
581
|
+
// do so and return true.
|
|
582
|
+
update(_node, _outerDeco, _innerDeco, _view) {
|
|
583
|
+
return true;
|
|
584
|
+
}
|
|
585
|
+
get domAtom() {
|
|
586
|
+
return this.node.isAtom;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
export class TextViewDesc extends NodeViewDesc {
|
|
590
|
+
constructor(parent, children, getPos, node, outerDeco, innerDeco, dom, nodeDOM){
|
|
591
|
+
super(parent, children, getPos, node, outerDeco, innerDeco, dom, null, nodeDOM, ()=>false, ()=>{
|
|
592
|
+
/* Text nodes can't have node selections */ }, ()=>{
|
|
593
|
+
/* Text nodes can't have node selections */ });
|
|
594
|
+
}
|
|
595
|
+
parseRule() {
|
|
596
|
+
let skip = this.nodeDOM.parentNode;
|
|
597
|
+
while(skip && skip != this.dom && !skip.pmIsDeco)skip = skip.parentNode;
|
|
598
|
+
return {
|
|
599
|
+
skip: skip || true
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
update(_node, _outerDeco, _innerDeco, _view) {
|
|
603
|
+
return true;
|
|
604
|
+
}
|
|
605
|
+
inParent() {
|
|
606
|
+
const parentDOM = this.parent.contentDOM;
|
|
607
|
+
for(let n = this.nodeDOM; n; n = n.parentNode)if (n == parentDOM) return true;
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
domFromPos(pos) {
|
|
611
|
+
return {
|
|
612
|
+
node: this.nodeDOM,
|
|
613
|
+
offset: pos
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
localPosFromDOM(dom, offset, bias) {
|
|
617
|
+
if (dom == this.nodeDOM) return this.posAtStart + Math.min(offset, this.node.text.length);
|
|
618
|
+
return super.localPosFromDOM(dom, offset, bias);
|
|
619
|
+
}
|
|
620
|
+
ignoreMutation(mutation) {
|
|
621
|
+
return mutation.type != "characterData" && mutation.type != "selection";
|
|
622
|
+
}
|
|
623
|
+
markDirty(from, to) {
|
|
624
|
+
super.markDirty(from, to);
|
|
625
|
+
if (this.dom != this.nodeDOM && (from == 0 || to == this.nodeDOM.nodeValue.length)) this.dirty = NODE_DIRTY;
|
|
626
|
+
}
|
|
627
|
+
get domAtom() {
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
// A dummy desc used to tag trailing BR or IMG nodes created to work
|
|
632
|
+
// around contentEditable terribleness.
|
|
633
|
+
export class TrailingHackViewDesc extends ViewDesc {
|
|
634
|
+
parseRule() {
|
|
635
|
+
return {
|
|
636
|
+
ignore: true
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
matchesHack(nodeName) {
|
|
640
|
+
return this.dirty == NOT_DIRTY && this.dom.nodeName == nodeName;
|
|
641
|
+
}
|
|
642
|
+
get domAtom() {
|
|
643
|
+
return true;
|
|
644
|
+
}
|
|
645
|
+
get ignoreForCoords() {
|
|
646
|
+
return this.dom.nodeName == "IMG";
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function sameOuterDeco(a, b) {
|
|
650
|
+
if (a.length != b.length) return false;
|
|
651
|
+
// @ts-expect-error ...
|
|
652
|
+
for(let i = 0; i < a.length; i++)if (!a[i].type.eq(b[i].type)) return false;
|
|
653
|
+
return true;
|
|
654
|
+
}
|