@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.
Files changed (209) hide show
  1. package/LICENSE.txt +12 -0
  2. package/README.md +705 -0
  3. package/dist/cjs/browser.js +53 -0
  4. package/dist/cjs/components/ChildNodeViews.js +376 -0
  5. package/dist/cjs/components/CursorWrapper.js +91 -0
  6. package/dist/cjs/components/CustomNodeView.js +79 -0
  7. package/dist/cjs/components/DocNodeView.js +104 -0
  8. package/dist/cjs/components/LayoutGroup.js +111 -0
  9. package/dist/cjs/components/MarkView.js +115 -0
  10. package/dist/cjs/components/NativeWidgetView.js +109 -0
  11. package/dist/cjs/components/NodeView.js +196 -0
  12. package/dist/cjs/components/NodeViewComponentProps.js +4 -0
  13. package/dist/cjs/components/OutputSpec.js +88 -0
  14. package/dist/cjs/components/ProseMirror.js +103 -0
  15. package/dist/cjs/components/ProseMirrorDoc.js +92 -0
  16. package/dist/cjs/components/SeparatorHackView.js +100 -0
  17. package/dist/cjs/components/TextNodeView.js +112 -0
  18. package/dist/cjs/components/TrailingHackView.js +90 -0
  19. package/dist/cjs/components/WidgetView.js +95 -0
  20. package/dist/cjs/components/WidgetViewComponentProps.js +4 -0
  21. package/dist/cjs/components/__tests__/ProseMirror.composition.test.js +398 -0
  22. package/dist/cjs/components/__tests__/ProseMirror.domchange.test.js +270 -0
  23. package/dist/cjs/components/__tests__/ProseMirror.draw-decoration.test.js +1010 -0
  24. package/dist/cjs/components/__tests__/ProseMirror.draw.test.js +337 -0
  25. package/dist/cjs/components/__tests__/ProseMirror.node-view.test.js +315 -0
  26. package/dist/cjs/components/__tests__/ProseMirror.selection.test.js +444 -0
  27. package/dist/cjs/components/__tests__/ProseMirror.test.js +382 -0
  28. package/dist/cjs/contexts/ChildDescriptorsContext.js +19 -0
  29. package/dist/cjs/contexts/EditorContext.js +12 -0
  30. package/dist/cjs/contexts/EditorStateContext.js +12 -0
  31. package/dist/cjs/contexts/LayoutGroupContext.js +12 -0
  32. package/dist/cjs/contexts/NodeViewContext.js +12 -0
  33. package/dist/cjs/contexts/SelectNodeContext.js +12 -0
  34. package/dist/cjs/contexts/StopEventContext.js +12 -0
  35. package/dist/cjs/contexts/__tests__/DeferredLayoutEffects.test.js +141 -0
  36. package/dist/cjs/decorations/ReactWidgetType.js +58 -0
  37. package/dist/cjs/decorations/computeDocDeco.js +44 -0
  38. package/dist/cjs/decorations/internalTypes.js +4 -0
  39. package/dist/cjs/decorations/iterDeco.js +79 -0
  40. package/dist/cjs/decorations/viewDecorations.js +163 -0
  41. package/dist/cjs/dom.js +142 -0
  42. package/dist/cjs/hooks/__tests__/useEditorViewLayoutEffect.test.js +108 -0
  43. package/dist/cjs/hooks/useClientOnly.js +18 -0
  44. package/dist/cjs/hooks/useComponentEventListeners.js +39 -0
  45. package/dist/cjs/hooks/useEditor.js +287 -0
  46. package/dist/cjs/hooks/useEditorEffect.js +35 -0
  47. package/dist/cjs/hooks/useEditorEventCallback.js +33 -0
  48. package/dist/cjs/hooks/useEditorEventListener.js +34 -0
  49. package/dist/cjs/hooks/useEditorState.js +16 -0
  50. package/dist/cjs/hooks/useForceUpdate.js +15 -0
  51. package/dist/cjs/hooks/useLayoutGroupEffect.js +19 -0
  52. package/dist/cjs/hooks/useNodeViewDescriptor.js +115 -0
  53. package/dist/cjs/hooks/useReactKeys.js +17 -0
  54. package/dist/cjs/hooks/useSelectNode.js +28 -0
  55. package/dist/cjs/hooks/useStopEvent.js +24 -0
  56. package/dist/cjs/index.js +53 -0
  57. package/dist/cjs/package.json +3 -0
  58. package/dist/cjs/plugins/__tests__/reactKeys.test.js +81 -0
  59. package/dist/cjs/plugins/beforeInputPlugin.js +143 -0
  60. package/dist/cjs/plugins/componentEventListeners.js +35 -0
  61. package/dist/cjs/plugins/componentEventListenersPlugin.js +35 -0
  62. package/dist/cjs/plugins/reactKeys.js +96 -0
  63. package/dist/cjs/props.js +269 -0
  64. package/dist/cjs/selection/SelectionDOMObserver.js +174 -0
  65. package/dist/cjs/selection/hasFocusAndSelection.js +35 -0
  66. package/dist/cjs/selection/selectionFromDOM.js +77 -0
  67. package/dist/cjs/selection/selectionToDOM.js +226 -0
  68. package/dist/cjs/ssr.js +85 -0
  69. package/dist/cjs/testing/editorViewTestHelpers.js +111 -0
  70. package/dist/cjs/testing/setupProseMirrorView.js +94 -0
  71. package/dist/cjs/viewdesc.js +664 -0
  72. package/dist/esm/browser.js +43 -0
  73. package/dist/esm/components/ChildNodeViews.js +318 -0
  74. package/dist/esm/components/CursorWrapper.js +40 -0
  75. package/dist/esm/components/CustomNodeView.js +28 -0
  76. package/dist/esm/components/DocNodeView.js +53 -0
  77. package/dist/esm/components/LayoutGroup.js +66 -0
  78. package/dist/esm/components/MarkView.js +64 -0
  79. package/dist/esm/components/NativeWidgetView.js +58 -0
  80. package/dist/esm/components/NodeView.js +145 -0
  81. package/dist/esm/components/NodeViewComponentProps.js +1 -0
  82. package/dist/esm/components/OutputSpec.js +38 -0
  83. package/dist/esm/components/ProseMirror.js +52 -0
  84. package/dist/esm/components/ProseMirrorDoc.js +34 -0
  85. package/dist/esm/components/SeparatorHackView.js +49 -0
  86. package/dist/esm/components/TextNodeView.js +102 -0
  87. package/dist/esm/components/TrailingHackView.js +39 -0
  88. package/dist/esm/components/WidgetView.js +44 -0
  89. package/dist/esm/components/WidgetViewComponentProps.js +1 -0
  90. package/dist/esm/components/__tests__/ProseMirror.composition.test.js +395 -0
  91. package/dist/esm/components/__tests__/ProseMirror.domchange.test.js +266 -0
  92. package/dist/esm/components/__tests__/ProseMirror.draw-decoration.test.js +967 -0
  93. package/dist/esm/components/__tests__/ProseMirror.draw.test.js +294 -0
  94. package/dist/esm/components/__tests__/ProseMirror.node-view.test.js +272 -0
  95. package/dist/esm/components/__tests__/ProseMirror.selection.test.js +440 -0
  96. package/dist/esm/components/__tests__/ProseMirror.test.js +339 -0
  97. package/dist/esm/contexts/ChildDescriptorsContext.js +9 -0
  98. package/dist/esm/contexts/EditorContext.js +7 -0
  99. package/dist/esm/contexts/EditorStateContext.js +2 -0
  100. package/dist/esm/contexts/LayoutGroupContext.js +2 -0
  101. package/dist/esm/contexts/NodeViewContext.js +2 -0
  102. package/dist/esm/contexts/SelectNodeContext.js +2 -0
  103. package/dist/esm/contexts/StopEventContext.js +2 -0
  104. package/dist/esm/contexts/__tests__/DeferredLayoutEffects.test.js +98 -0
  105. package/dist/esm/decorations/ReactWidgetType.js +40 -0
  106. package/dist/esm/decorations/computeDocDeco.js +44 -0
  107. package/dist/esm/decorations/internalTypes.js +1 -0
  108. package/dist/esm/decorations/iterDeco.js +73 -0
  109. package/dist/esm/decorations/viewDecorations.js +163 -0
  110. package/dist/esm/dom.js +105 -0
  111. package/dist/esm/hooks/__tests__/useEditorViewLayoutEffect.test.js +99 -0
  112. package/dist/esm/hooks/useClientOnly.js +8 -0
  113. package/dist/esm/hooks/useComponentEventListeners.js +54 -0
  114. package/dist/esm/hooks/useEditor.js +278 -0
  115. package/dist/esm/hooks/useEditorEffect.js +38 -0
  116. package/dist/esm/hooks/useEditorEventCallback.js +35 -0
  117. package/dist/esm/hooks/useEditorEventListener.js +28 -0
  118. package/dist/esm/hooks/useEditorState.js +8 -0
  119. package/dist/esm/hooks/useForceUpdate.js +8 -0
  120. package/dist/esm/hooks/useLayoutGroupEffect.js +9 -0
  121. package/dist/esm/hooks/useNodeViewDescriptor.js +105 -0
  122. package/dist/esm/hooks/useReactKeys.js +7 -0
  123. package/dist/esm/hooks/useSelectNode.js +18 -0
  124. package/dist/esm/hooks/useStopEvent.js +14 -0
  125. package/dist/esm/index.js +11 -0
  126. package/dist/esm/plugins/__tests__/reactKeys.test.js +77 -0
  127. package/dist/esm/plugins/beforeInputPlugin.js +133 -0
  128. package/dist/esm/plugins/componentEventListeners.js +25 -0
  129. package/dist/esm/plugins/componentEventListenersPlugin.js +25 -0
  130. package/dist/esm/plugins/reactKeys.js +81 -0
  131. package/dist/esm/props.js +251 -0
  132. package/dist/esm/selection/SelectionDOMObserver.js +164 -0
  133. package/dist/esm/selection/hasFocusAndSelection.js +17 -0
  134. package/dist/esm/selection/selectionFromDOM.js +59 -0
  135. package/dist/esm/selection/selectionToDOM.js +196 -0
  136. package/dist/esm/ssr.js +82 -0
  137. package/dist/esm/testing/editorViewTestHelpers.js +88 -0
  138. package/dist/esm/testing/setupProseMirrorView.js +76 -0
  139. package/dist/esm/viewdesc.js +654 -0
  140. package/dist/tsconfig.tsbuildinfo +1 -0
  141. package/dist/types/browser.d.ts +15 -0
  142. package/dist/types/components/ChildNodeViews.d.ts +9 -0
  143. package/dist/types/components/CursorWrapper.d.ts +5 -0
  144. package/dist/types/components/CustomNodeView.d.ts +21 -0
  145. package/dist/types/components/DocNodeView.d.ts +20 -0
  146. package/dist/types/components/LayoutGroup.d.ts +12 -0
  147. package/dist/types/components/MarkView.d.ts +9 -0
  148. package/dist/types/components/NativeWidgetView.d.ts +8 -0
  149. package/dist/types/components/NodeView.d.ts +11 -0
  150. package/dist/types/components/NodeViewComponentProps.d.ts +12 -0
  151. package/dist/types/components/OutputSpec.d.ts +8 -0
  152. package/dist/types/components/ProseMirror.d.ts +15 -0
  153. package/dist/types/components/ProseMirrorDoc.d.ts +10 -0
  154. package/dist/types/components/SeparatorHackView.d.ts +6 -0
  155. package/dist/types/components/TextNodeView.d.ts +23 -0
  156. package/dist/types/components/TrailingHackView.d.ts +6 -0
  157. package/dist/types/components/WidgetView.d.ts +8 -0
  158. package/dist/types/components/WidgetViewComponentProps.d.ts +6 -0
  159. package/dist/types/components/__tests__/ProseMirror.composition.test.d.ts +1 -0
  160. package/dist/types/components/__tests__/ProseMirror.domchange.test.d.ts +1 -0
  161. package/dist/types/components/__tests__/ProseMirror.draw-decoration.test.d.ts +1 -0
  162. package/dist/types/components/__tests__/ProseMirror.draw.test.d.ts +1 -0
  163. package/dist/types/components/__tests__/ProseMirror.node-view.test.d.ts +1 -0
  164. package/dist/types/components/__tests__/ProseMirror.selection.test.d.ts +1 -0
  165. package/dist/types/components/__tests__/ProseMirror.test.d.ts +1 -0
  166. package/dist/types/contexts/ChildDescriptorsContext.d.ts +6 -0
  167. package/dist/types/contexts/EditorContext.d.ts +14 -0
  168. package/dist/types/contexts/EditorStateContext.d.ts +2 -0
  169. package/dist/types/contexts/LayoutGroupContext.d.ts +5 -0
  170. package/dist/types/contexts/NodeViewContext.d.ts +6 -0
  171. package/dist/types/contexts/SelectNodeContext.d.ts +3 -0
  172. package/dist/types/contexts/StopEventContext.d.ts +3 -0
  173. package/dist/types/contexts/__tests__/DeferredLayoutEffects.test.d.ts +1 -0
  174. package/dist/types/decorations/ReactWidgetType.d.ts +39 -0
  175. package/dist/types/decorations/computeDocDeco.d.ts +13 -0
  176. package/dist/types/decorations/internalTypes.d.ts +16 -0
  177. package/dist/types/decorations/iterDeco.d.ts +3 -0
  178. package/dist/types/decorations/viewDecorations.d.ts +13 -0
  179. package/dist/types/dom.d.ts +22 -0
  180. package/dist/types/hooks/__tests__/useEditorViewLayoutEffect.test.d.ts +1 -0
  181. package/dist/types/hooks/useClientOnly.d.ts +1 -0
  182. package/dist/types/hooks/useComponentEventListeners.d.ts +33 -0
  183. package/dist/types/hooks/useEditor.d.ts +66 -0
  184. package/dist/types/hooks/useEditorEffect.d.ts +17 -0
  185. package/dist/types/hooks/useEditorEventCallback.d.ts +15 -0
  186. package/dist/types/hooks/useEditorEventListener.d.ts +8 -0
  187. package/dist/types/hooks/useEditorState.d.ts +5 -0
  188. package/dist/types/hooks/useForceUpdate.d.ts +5 -0
  189. package/dist/types/hooks/useLayoutGroupEffect.d.ts +3 -0
  190. package/dist/types/hooks/useNodeViewDescriptor.d.ts +11 -0
  191. package/dist/types/hooks/useReactKeys.d.ts +5 -0
  192. package/dist/types/hooks/useSelectNode.d.ts +1 -0
  193. package/dist/types/hooks/useStopEvent.d.ts +2 -0
  194. package/dist/types/index.d.ts +12 -0
  195. package/dist/types/plugins/__tests__/reactKeys.test.d.ts +1 -0
  196. package/dist/types/plugins/beforeInputPlugin.d.ts +3 -0
  197. package/dist/types/plugins/componentEventListeners.d.ts +4 -0
  198. package/dist/types/plugins/componentEventListenersPlugin.d.ts +4 -0
  199. package/dist/types/plugins/reactKeys.d.ts +19 -0
  200. package/dist/types/props.d.ts +1174 -0
  201. package/dist/types/selection/SelectionDOMObserver.d.ts +34 -0
  202. package/dist/types/selection/hasFocusAndSelection.d.ts +3 -0
  203. package/dist/types/selection/selectionFromDOM.d.ts +4 -0
  204. package/dist/types/selection/selectionToDOM.d.ts +9 -0
  205. package/dist/types/ssr.d.ts +19 -0
  206. package/dist/types/testing/editorViewTestHelpers.d.ts +23 -0
  207. package/dist/types/testing/setupProseMirrorView.d.ts +2 -0
  208. package/dist/types/viewdesc.d.ts +131 -0
  209. 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
+ }