@stesura/react-prosemirror 3.2.11-stesura.1
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 +21 -0
- package/README.md +939 -0
- package/dist/cjs/AbstractEditorView.js +4 -0
- package/dist/cjs/ReactEditorView.js +194 -0
- package/dist/cjs/StaticEditorView.js +89 -0
- package/dist/cjs/browser.js +53 -0
- package/dist/cjs/commands/reorderSiblings.js +72 -0
- package/dist/cjs/components/ChildNodeViews.js +418 -0
- package/dist/cjs/components/CursorWrapper.js +84 -0
- package/dist/cjs/components/EditorStateSelectorsProvider.js +90 -0
- package/dist/cjs/components/LayoutGroup.js +112 -0
- package/dist/cjs/components/NativeWidgetView.js +108 -0
- package/dist/cjs/components/OutputSpec.js +121 -0
- package/dist/cjs/components/ProseMirror.js +146 -0
- package/dist/cjs/components/ProseMirrorDoc.js +74 -0
- package/dist/cjs/components/SeparatorHackView.js +99 -0
- package/dist/cjs/components/TextNodeView.js +113 -0
- package/dist/cjs/components/TrailingHackView.js +87 -0
- package/dist/cjs/components/WidgetView.js +94 -0
- package/dist/cjs/components/WidgetViewComponentProps.js +4 -0
- package/dist/cjs/components/marks/DefaultMarkView.js +70 -0
- package/dist/cjs/components/marks/MarkView.js +85 -0
- package/dist/cjs/components/marks/MarkViewComponentProps.js +4 -0
- package/dist/cjs/components/marks/MarkViewConstructorView.js +116 -0
- package/dist/cjs/components/marks/ReactMarkView.js +109 -0
- package/dist/cjs/components/nodes/DefaultNodeView.js +68 -0
- package/dist/cjs/components/nodes/DocNodeView.js +97 -0
- package/dist/cjs/components/nodes/NodeView.js +129 -0
- package/dist/cjs/components/nodes/NodeViewComponentProps.js +4 -0
- package/dist/cjs/components/nodes/NodeViewConstructorView.js +145 -0
- package/dist/cjs/components/nodes/ReactNodeView.js +189 -0
- package/dist/cjs/constants.js +33 -0
- package/dist/cjs/contexts/ChildDescriptionsContext.js +19 -0
- package/dist/cjs/contexts/EditorContext.js +12 -0
- package/dist/cjs/contexts/EditorStateContext.js +12 -0
- package/dist/cjs/contexts/EditorStateStoreContext.js +45 -0
- package/dist/cjs/contexts/IgnoreMutationContext.js +12 -0
- package/dist/cjs/contexts/LayoutGroupContext.js +12 -0
- package/dist/cjs/contexts/NodeViewContext.js +12 -0
- package/dist/cjs/contexts/RenderStoreContext.js +54 -0
- package/dist/cjs/contexts/SelectNodeContext.js +12 -0
- package/dist/cjs/contexts/StopEventContext.js +12 -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 +91 -0
- package/dist/cjs/decorations/viewDecorations.js +155 -0
- package/dist/cjs/dom.js +147 -0
- package/dist/cjs/findDOMNode.js +46 -0
- package/dist/cjs/hooks/useClientLayoutEffect.js +19 -0
- package/dist/cjs/hooks/useComponentEventListeners.js +72 -0
- package/dist/cjs/hooks/useEditor.js +130 -0
- package/dist/cjs/hooks/useEditorEffect.js +39 -0
- package/dist/cjs/hooks/useEditorEventCallback.js +38 -0
- package/dist/cjs/hooks/useEditorEventListener.js +34 -0
- package/dist/cjs/hooks/useEditorState.js +18 -0
- package/dist/cjs/hooks/useEditorStateSelector.js +17 -0
- package/dist/cjs/hooks/useEffectEvent.js +34 -0
- package/dist/cjs/hooks/useForceUpdate.js +15 -0
- package/dist/cjs/hooks/useIgnoreMutation.js +24 -0
- package/dist/cjs/hooks/useIsComposingIn.js +25 -0
- package/dist/cjs/hooks/useIsEditorStatic.js +15 -0
- package/dist/cjs/hooks/useIsNodeSelected.js +21 -0
- package/dist/cjs/hooks/useLayoutGroupEffect.js +20 -0
- package/dist/cjs/hooks/useMarkViewDescription.js +134 -0
- package/dist/cjs/hooks/useNodePos.js +18 -0
- package/dist/cjs/hooks/useNodeViewDescription.js +154 -0
- package/dist/cjs/hooks/useReactKeys.js +17 -0
- package/dist/cjs/hooks/useSelectNode.js +30 -0
- package/dist/cjs/hooks/useSetDom.js +57 -0
- package/dist/cjs/hooks/useStopEvent.js +24 -0
- package/dist/cjs/index.js +85 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/plugins/beforeInputPlugin.js +356 -0
- package/dist/cjs/plugins/reactKeys.js +121 -0
- package/dist/cjs/props.js +283 -0
- package/dist/cjs/refs.js +28 -0
- package/dist/cjs/testing/editorViewTestHelpers.js +109 -0
- package/dist/cjs/testing/setupProseMirrorView.js +94 -0
- package/dist/cjs/tiptap/ReactProseMirrorNodeView.js +26 -0
- package/dist/cjs/tiptap/TiptapEditor.js +34 -0
- package/dist/cjs/tiptap/TiptapEditorContent.js +143 -0
- package/dist/cjs/tiptap/TiptapEditorView.js +130 -0
- package/dist/cjs/tiptap/contexts/TiptapEditorContext.js +12 -0
- package/dist/cjs/tiptap/extensions/ReactProseMirror.js +40 -0
- package/dist/cjs/tiptap/extensions/ReactProseMirrorCommands.js +21 -0
- package/dist/cjs/tiptap/extensions/commands/updateAttributes.js +104 -0
- package/dist/cjs/tiptap/hooks/useEditor.js +318 -0
- package/dist/cjs/tiptap/hooks/useIsInReactProseMirror.js +15 -0
- package/dist/cjs/tiptap/hooks/useTiptapEditor.js +35 -0
- package/dist/cjs/tiptap/hooks/useTiptapEditorEffect.js +35 -0
- package/dist/cjs/tiptap/hooks/useTiptapEditorEventCallback.js +35 -0
- package/dist/cjs/tiptap/index.js +48 -0
- package/dist/cjs/tiptap/tiptapNodeView.js +245 -0
- package/dist/cjs/viewdesc.js +862 -0
- package/dist/esm/AbstractEditorView.js +1 -0
- package/dist/esm/ReactEditorView.js +194 -0
- package/dist/esm/StaticEditorView.js +79 -0
- package/dist/esm/browser.js +43 -0
- package/dist/esm/commands/reorderSiblings.js +63 -0
- package/dist/esm/components/ChildNodeViews.js +359 -0
- package/dist/esm/components/CursorWrapper.js +33 -0
- package/dist/esm/components/EditorStateSelectorsProvider.js +31 -0
- package/dist/esm/components/LayoutGroup.js +67 -0
- package/dist/esm/components/NativeWidgetView.js +57 -0
- package/dist/esm/components/OutputSpec.js +71 -0
- package/dist/esm/components/ProseMirror.js +95 -0
- package/dist/esm/components/ProseMirrorDoc.js +15 -0
- package/dist/esm/components/SeparatorHackView.js +48 -0
- package/dist/esm/components/TextNodeView.js +103 -0
- package/dist/esm/components/TrailingHackView.js +36 -0
- package/dist/esm/components/WidgetView.js +43 -0
- package/dist/esm/components/WidgetViewComponentProps.js +1 -0
- package/dist/esm/components/marks/DefaultMarkView.js +19 -0
- package/dist/esm/components/marks/MarkView.js +34 -0
- package/dist/esm/components/marks/MarkViewComponentProps.js +1 -0
- package/dist/esm/components/marks/MarkViewConstructorView.js +65 -0
- package/dist/esm/components/marks/ReactMarkView.js +58 -0
- package/dist/esm/components/nodes/DefaultNodeView.js +17 -0
- package/dist/esm/components/nodes/DocNodeView.js +46 -0
- package/dist/esm/components/nodes/NodeView.js +67 -0
- package/dist/esm/components/nodes/NodeViewComponentProps.js +1 -0
- package/dist/esm/components/nodes/NodeViewConstructorView.js +94 -0
- package/dist/esm/components/nodes/ReactNodeView.js +138 -0
- package/dist/esm/constants.js +15 -0
- package/dist/esm/contexts/ChildDescriptionsContext.js +9 -0
- package/dist/esm/contexts/EditorContext.js +7 -0
- package/dist/esm/contexts/EditorStateContext.js +2 -0
- package/dist/esm/contexts/EditorStateStoreContext.js +27 -0
- package/dist/esm/contexts/IgnoreMutationContext.js +2 -0
- package/dist/esm/contexts/LayoutGroupContext.js +2 -0
- package/dist/esm/contexts/NodeViewContext.js +2 -0
- package/dist/esm/contexts/RenderStoreContext.js +33 -0
- package/dist/esm/contexts/SelectNodeContext.js +2 -0
- package/dist/esm/contexts/StopEventContext.js +2 -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 +85 -0
- package/dist/esm/decorations/viewDecorations.js +155 -0
- package/dist/esm/dom.js +110 -0
- package/dist/esm/findDOMNode.js +31 -0
- package/dist/esm/hooks/useClientLayoutEffect.js +9 -0
- package/dist/esm/hooks/useComponentEventListeners.js +87 -0
- package/dist/esm/hooks/useEditor.js +128 -0
- package/dist/esm/hooks/useEditorEffect.js +46 -0
- package/dist/esm/hooks/useEditorEventCallback.js +38 -0
- package/dist/esm/hooks/useEditorEventListener.js +28 -0
- package/dist/esm/hooks/useEditorState.js +10 -0
- package/dist/esm/hooks/useEditorStateSelector.js +14 -0
- package/dist/esm/hooks/useEffectEvent.js +24 -0
- package/dist/esm/hooks/useForceUpdate.js +8 -0
- package/dist/esm/hooks/useIgnoreMutation.js +14 -0
- package/dist/esm/hooks/useIsComposingIn.js +17 -0
- package/dist/esm/hooks/useIsEditorStatic.js +8 -0
- package/dist/esm/hooks/useIsNodeSelected.js +11 -0
- package/dist/esm/hooks/useLayoutGroupEffect.js +10 -0
- package/dist/esm/hooks/useMarkViewDescription.js +124 -0
- package/dist/esm/hooks/useNodePos.js +15 -0
- package/dist/esm/hooks/useNodeViewDescription.js +144 -0
- package/dist/esm/hooks/useReactKeys.js +7 -0
- package/dist/esm/hooks/useSelectNode.js +20 -0
- package/dist/esm/hooks/useSetDom.js +47 -0
- package/dist/esm/hooks/useStopEvent.js +14 -0
- package/dist/esm/index.js +19 -0
- package/dist/esm/plugins/beforeInputPlugin.js +346 -0
- package/dist/esm/plugins/reactKeys.js +106 -0
- package/dist/esm/props.js +267 -0
- package/dist/esm/refs.js +18 -0
- package/dist/esm/testing/editorViewTestHelpers.js +86 -0
- package/dist/esm/testing/setupProseMirrorView.js +76 -0
- package/dist/esm/tiptap/ReactProseMirrorNodeView.js +22 -0
- package/dist/esm/tiptap/TiptapEditor.js +24 -0
- package/dist/esm/tiptap/TiptapEditorContent.js +99 -0
- package/dist/esm/tiptap/TiptapEditorView.js +76 -0
- package/dist/esm/tiptap/contexts/TiptapEditorContext.js +2 -0
- package/dist/esm/tiptap/extensions/ReactProseMirror.js +30 -0
- package/dist/esm/tiptap/extensions/ReactProseMirrorCommands.js +11 -0
- package/dist/esm/tiptap/extensions/commands/updateAttributes.js +96 -0
- package/dist/esm/tiptap/hooks/useEditor.js +308 -0
- package/dist/esm/tiptap/hooks/useIsInReactProseMirror.js +9 -0
- package/dist/esm/tiptap/hooks/useTiptapEditor.js +31 -0
- package/dist/esm/tiptap/hooks/useTiptapEditorEffect.js +42 -0
- package/dist/esm/tiptap/hooks/useTiptapEditorEventCallback.js +35 -0
- package/dist/esm/tiptap/index.js +9 -0
- package/dist/esm/tiptap/tiptapNodeView.js +211 -0
- package/dist/esm/viewdesc.js +844 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/AbstractEditorView.d.ts +28 -0
- package/dist/types/ReactEditorView.d.ts +102 -0
- package/dist/types/StaticEditorView.d.ts +26 -0
- package/dist/types/__tests__/viewdesc.test.d.ts +1 -0
- package/dist/types/browser.d.ts +15 -0
- package/dist/types/commands/__tests__/reorderSiblings.test.d.ts +1 -0
- package/dist/types/commands/reorderSiblings.d.ts +14 -0
- package/dist/types/components/ChildNodeViews.d.ts +9 -0
- package/dist/types/components/CursorWrapper.d.ts +3 -0
- package/dist/types/components/EditorStateSelectorsProvider.d.ts +10 -0
- package/dist/types/components/LayoutGroup.d.ts +12 -0
- package/dist/types/components/NativeWidgetView.d.ts +8 -0
- package/dist/types/components/OutputSpec.d.ts +10 -0
- package/dist/types/components/ProseMirror.d.ts +14 -0
- package/dist/types/components/ProseMirrorDoc.d.ts +16 -0
- package/dist/types/components/SeparatorHackView.d.ts +6 -0
- package/dist/types/components/TextNodeView.d.ts +27 -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 +7 -0
- package/dist/types/components/__tests__/ProseMirror.composition.test.d.ts +17 -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.memory-leak.test.d.ts +6 -0
- package/dist/types/components/__tests__/ProseMirror.mobile.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/components/__tests__/stableContexts.test.d.ts +10 -0
- package/dist/types/components/marks/DefaultMarkView.d.ts +3 -0
- package/dist/types/components/marks/MarkView.d.ts +10 -0
- package/dist/types/components/marks/MarkViewComponentProps.d.ts +11 -0
- package/dist/types/components/marks/MarkViewConstructorView.d.ts +12 -0
- package/dist/types/components/marks/ReactMarkView.d.ts +12 -0
- package/dist/types/components/nodes/DefaultNodeView.d.ts +3 -0
- package/dist/types/components/nodes/DocNodeView.d.ts +12 -0
- package/dist/types/components/nodes/NodeView.d.ts +14 -0
- package/dist/types/components/nodes/NodeViewComponentProps.d.ts +13 -0
- package/dist/types/components/nodes/NodeViewConstructorView.d.ts +12 -0
- package/dist/types/components/nodes/ReactNodeView.d.ts +13 -0
- package/dist/types/constants.d.ts +4 -0
- package/dist/types/contexts/ChildDescriptionsContext.d.ts +7 -0
- package/dist/types/contexts/EditorContext.d.ts +18 -0
- package/dist/types/contexts/EditorStateContext.d.ts +2 -0
- package/dist/types/contexts/EditorStateStoreContext.d.ts +9 -0
- package/dist/types/contexts/IgnoreMutationContext.d.ts +5 -0
- package/dist/types/contexts/LayoutGroupContext.d.ts +5 -0
- package/dist/types/contexts/NodeViewContext.d.ts +9 -0
- package/dist/types/contexts/RenderStoreContext.d.ts +24 -0
- package/dist/types/contexts/SelectNodeContext.d.ts +5 -0
- package/dist/types/contexts/StopEventContext.d.ts +5 -0
- package/dist/types/contexts/__tests__/DeferredLayoutEffects.test.d.ts +1 -0
- package/dist/types/decorations/ReactWidgetType.d.ts +40 -0
- package/dist/types/decorations/computeDocDeco.d.ts +14 -0
- package/dist/types/decorations/internalTypes.d.ts +20 -0
- package/dist/types/decorations/iterDeco.d.ts +3 -0
- package/dist/types/decorations/viewDecorations.d.ts +14 -0
- package/dist/types/dom.d.ts +22 -0
- package/dist/types/findDOMNode.d.ts +3 -0
- package/dist/types/hooks/__tests__/useEditorEffect.test.d.ts +1 -0
- package/dist/types/hooks/useClientLayoutEffect.d.ts +2 -0
- package/dist/types/hooks/useComponentEventListeners.d.ts +34 -0
- package/dist/types/hooks/useEditor.d.ts +37 -0
- package/dist/types/hooks/useEditorEffect.d.ts +21 -0
- package/dist/types/hooks/useEditorEventCallback.d.ts +13 -0
- package/dist/types/hooks/useEditorEventListener.d.ts +8 -0
- package/dist/types/hooks/useEditorState.d.ts +5 -0
- package/dist/types/hooks/useEditorStateSelector.d.ts +10 -0
- package/dist/types/hooks/useEffectEvent.d.ts +1 -0
- package/dist/types/hooks/useForceUpdate.d.ts +5 -0
- package/dist/types/hooks/useIgnoreMutation.d.ts +2 -0
- package/dist/types/hooks/useIsComposingIn.d.ts +4 -0
- package/dist/types/hooks/useIsEditorStatic.d.ts +5 -0
- package/dist/types/hooks/useIsNodeSelected.d.ts +1 -0
- package/dist/types/hooks/useLayoutGroupEffect.d.ts +3 -0
- package/dist/types/hooks/useMarkViewDescription.d.ts +15 -0
- package/dist/types/hooks/useNodePos.d.ts +9 -0
- package/dist/types/hooks/useNodeViewDescription.d.ts +16 -0
- package/dist/types/hooks/useReactKeys.d.ts +2 -0
- package/dist/types/hooks/useSelectNode.d.ts +2 -0
- package/dist/types/hooks/useSetDom.d.ts +2 -0
- package/dist/types/hooks/useStopEvent.d.ts +2 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/plugins/__tests__/reactKeys.test.d.ts +1 -0
- package/dist/types/plugins/beforeInputPlugin.d.ts +2 -0
- package/dist/types/plugins/reactKeys.d.ts +23 -0
- package/dist/types/props.d.ts +1173 -0
- package/dist/types/refs.d.ts +2 -0
- package/dist/types/testing/editorViewTestHelpers.d.ts +24 -0
- package/dist/types/testing/setupProseMirrorView.d.ts +2 -0
- package/dist/types/tiptap/ReactProseMirrorNodeView.d.ts +15 -0
- package/dist/types/tiptap/TiptapEditor.d.ts +6 -0
- package/dist/types/tiptap/TiptapEditorContent.d.ts +28 -0
- package/dist/types/tiptap/TiptapEditorView.d.ts +16 -0
- package/dist/types/tiptap/contexts/TiptapEditorContext.d.ts +6 -0
- package/dist/types/tiptap/extensions/ReactProseMirror.d.ts +9 -0
- package/dist/types/tiptap/extensions/ReactProseMirrorCommands.d.ts +2 -0
- package/dist/types/tiptap/extensions/commands/updateAttributes.d.ts +24 -0
- package/dist/types/tiptap/hooks/useEditor.d.ts +38 -0
- package/dist/types/tiptap/hooks/useIsInReactProseMirror.d.ts +6 -0
- package/dist/types/tiptap/hooks/useTiptapEditor.d.ts +11 -0
- package/dist/types/tiptap/hooks/useTiptapEditorEffect.d.ts +21 -0
- package/dist/types/tiptap/hooks/useTiptapEditorEventCallback.d.ts +13 -0
- package/dist/types/tiptap/index.d.ts +9 -0
- package/dist/types/tiptap/tiptapNodeView.d.ts +50 -0
- package/dist/types/viewdesc.d.ts +179 -0
- package/package.json +167 -0
|
@@ -0,0 +1,844 @@
|
|
|
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 "./dom.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
|
+
const posDiff = a.getPos() - b.getPos();
|
|
20
|
+
if (posDiff !== 0) return posDiff;
|
|
21
|
+
// When two descs share the same PM position (e.g. a zero-width widget
|
|
22
|
+
// and a text node that starts at the same position), fall back to DOM
|
|
23
|
+
// order so that the viewdesc children match the actual DOM layout.
|
|
24
|
+
// Without this, position computations like `posBeforeChild` can return
|
|
25
|
+
// the wrong PM position for the widget's container.
|
|
26
|
+
const cmp = a.dom.compareDocumentPosition(b.dom);
|
|
27
|
+
if (cmp & 4 /* DOCUMENT_POSITION_FOLLOWING */ ) return -1;
|
|
28
|
+
if (cmp & 2 /* DOCUMENT_POSITION_PRECEDING */ ) return 1;
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
// Adds `desc` to `siblings` if missing and moves it into sorted order.
|
|
32
|
+
// Every child used to sort the whole sibling list from its own layout
|
|
33
|
+
// effect, which made mounting n siblings O(n²). Only `desc` can be out of
|
|
34
|
+
// place here; the parent's own `children.sort` remains the full pass for
|
|
35
|
+
// siblings that move without re-rendering (reorderSiblings).
|
|
36
|
+
export const placeViewDesc = (siblings, desc)=>{
|
|
37
|
+
let index = siblings.lastIndexOf(desc);
|
|
38
|
+
if (index === -1) index = siblings.push(desc) - 1;
|
|
39
|
+
const prev = siblings[index - 1];
|
|
40
|
+
const next = siblings[index + 1];
|
|
41
|
+
if ((!prev || sortViewDescs(prev, desc) <= 0) && (!next || sortViewDescs(desc, next) <= 0)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
siblings.splice(index, 1);
|
|
45
|
+
let low = 0;
|
|
46
|
+
let high = siblings.length;
|
|
47
|
+
while(low < high){
|
|
48
|
+
const mid = low + high >> 1;
|
|
49
|
+
const sibling = siblings[mid];
|
|
50
|
+
if (sibling && sortViewDescs(sibling, desc) <= 0) low = mid + 1;
|
|
51
|
+
else high = mid;
|
|
52
|
+
}
|
|
53
|
+
siblings.splice(low, 0, desc);
|
|
54
|
+
};
|
|
55
|
+
const NOT_DIRTY = 0, CHILD_DIRTY = 1, CONTENT_DIRTY = 2, NODE_DIRTY = 3;
|
|
56
|
+
// Superclass for the various kinds of descriptions. Defines their
|
|
57
|
+
// basic structure and shared methods.
|
|
58
|
+
export class ViewDesc {
|
|
59
|
+
parent;
|
|
60
|
+
children;
|
|
61
|
+
getPos;
|
|
62
|
+
dom;
|
|
63
|
+
contentDOM;
|
|
64
|
+
dirty;
|
|
65
|
+
node;
|
|
66
|
+
constructor(parent, children, getPos, dom, // This is the node that holds the child views. It may be null for
|
|
67
|
+
// descs that don't have children.
|
|
68
|
+
contentDOM){
|
|
69
|
+
this.parent = parent;
|
|
70
|
+
this.children = children;
|
|
71
|
+
this.getPos = getPos;
|
|
72
|
+
this.dom = dom;
|
|
73
|
+
this.contentDOM = contentDOM;
|
|
74
|
+
this.dirty = NOT_DIRTY;
|
|
75
|
+
// An expando property on the DOM node provides a link back to its
|
|
76
|
+
// description.
|
|
77
|
+
dom.pmViewDesc = this;
|
|
78
|
+
}
|
|
79
|
+
// Used to check whether a given description corresponds to a
|
|
80
|
+
// widget/mark/node.
|
|
81
|
+
matchesWidget(_widget) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
matchesMark(_mark) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
matchesNode(_node, _outerDeco, _innerDeco) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
// @ts-expect-error ...
|
|
91
|
+
matchesHack(nodeName) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
// When parsing in-editor content (in domchange.js), we allow
|
|
95
|
+
// descriptions to determine the parse rules that should be used to
|
|
96
|
+
// parse them.
|
|
97
|
+
parseRule() {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
// Used by the editor's event handler to ignore events that come
|
|
101
|
+
// from certain descs.
|
|
102
|
+
// @ts-expect-error ...
|
|
103
|
+
stopEvent(event) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
// The size of the content represented by this desc.
|
|
107
|
+
get size() {
|
|
108
|
+
let size = 0;
|
|
109
|
+
for(let i = 0; i < this.children.length; i++)// @ts-expect-error ...
|
|
110
|
+
size += this.children[i].size;
|
|
111
|
+
return size;
|
|
112
|
+
}
|
|
113
|
+
// For block nodes, this represents the space taken up by their
|
|
114
|
+
// start/end tokens.
|
|
115
|
+
get border() {
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
destroy() {
|
|
119
|
+
this.parent = undefined;
|
|
120
|
+
if (this.dom.pmViewDesc == this) this.dom.pmViewDesc = undefined;
|
|
121
|
+
for(let i = 0; i < this.children.length; i++)this.children[i].destroy();
|
|
122
|
+
}
|
|
123
|
+
posBeforeChild(child) {
|
|
124
|
+
for(let i = 0, pos = this.posAtStart;; i++){
|
|
125
|
+
const cur = this.children[i];
|
|
126
|
+
if (cur == child) return pos;
|
|
127
|
+
// @ts-expect-error ...
|
|
128
|
+
pos += cur.size;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
get posBefore() {
|
|
132
|
+
return this.parent.posBeforeChild(this);
|
|
133
|
+
}
|
|
134
|
+
get posAtStart() {
|
|
135
|
+
return this.parent ? this.parent.posBeforeChild(this) + this.border : 0;
|
|
136
|
+
}
|
|
137
|
+
get posAfter() {
|
|
138
|
+
return this.posBefore + this.size;
|
|
139
|
+
}
|
|
140
|
+
get posAtEnd() {
|
|
141
|
+
return this.posAtStart + this.size - 2 * this.border;
|
|
142
|
+
}
|
|
143
|
+
localPosFromDOM(dom, offset, bias) {
|
|
144
|
+
// If the DOM position is in the content, use the child desc after
|
|
145
|
+
// it to figure out a position.
|
|
146
|
+
if (this.contentDOM && this.contentDOM.contains(dom.nodeType == 1 ? dom : dom.parentNode)) {
|
|
147
|
+
if (bias < 0) {
|
|
148
|
+
let domBefore, desc;
|
|
149
|
+
if (dom == this.contentDOM) {
|
|
150
|
+
domBefore = dom.childNodes[offset - 1];
|
|
151
|
+
} else {
|
|
152
|
+
while(dom.parentNode != this.contentDOM)dom = dom.parentNode;
|
|
153
|
+
domBefore = dom.previousSibling;
|
|
154
|
+
}
|
|
155
|
+
while(domBefore && !((desc = domBefore.pmViewDesc) && desc.parent == this))domBefore = domBefore.previousSibling;
|
|
156
|
+
return domBefore ? this.posBeforeChild(desc) + desc.size : this.posAtStart;
|
|
157
|
+
} else {
|
|
158
|
+
let domAfter, desc;
|
|
159
|
+
if (dom == this.contentDOM) {
|
|
160
|
+
domAfter = dom.childNodes[offset];
|
|
161
|
+
} else {
|
|
162
|
+
while(dom.parentNode != this.contentDOM)dom = dom.parentNode;
|
|
163
|
+
domAfter = dom.nextSibling;
|
|
164
|
+
}
|
|
165
|
+
while(domAfter && !((desc = domAfter.pmViewDesc) && desc.parent == this))domAfter = domAfter.nextSibling;
|
|
166
|
+
return domAfter ? this.posBeforeChild(desc) : this.posAtEnd;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Otherwise, use various heuristics, falling back on the bias
|
|
170
|
+
// parameter, to determine whether to return the position at the
|
|
171
|
+
// start or at the end of this view desc.
|
|
172
|
+
let atEnd;
|
|
173
|
+
if (dom == this.dom && this.contentDOM) {
|
|
174
|
+
atEnd = offset > domIndex(this.contentDOM);
|
|
175
|
+
} else if (this.contentDOM && this.contentDOM != this.dom && this.dom.contains(this.contentDOM)) {
|
|
176
|
+
atEnd = dom.compareDocumentPosition(this.contentDOM) & 2;
|
|
177
|
+
} else if (this.dom.firstChild) {
|
|
178
|
+
if (offset == 0) for(let search = dom;; search = search.parentNode){
|
|
179
|
+
if (search == this.dom) {
|
|
180
|
+
atEnd = false;
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
if (search.previousSibling) break;
|
|
184
|
+
}
|
|
185
|
+
if (atEnd == null && offset == dom.childNodes.length) for(let search = dom;; search = search.parentNode){
|
|
186
|
+
if (search == this.dom) {
|
|
187
|
+
atEnd = true;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
if (search.nextSibling) break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return (atEnd == null ? bias > 0 : atEnd) ? this.posAtEnd : this.posAtStart;
|
|
194
|
+
}
|
|
195
|
+
nearestDesc(dom) {
|
|
196
|
+
let onlyNodes = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;
|
|
197
|
+
for(let first = true, cur = dom; cur; cur = cur.parentNode){
|
|
198
|
+
const desc = this.getDesc(cur);
|
|
199
|
+
let nodeDOM;
|
|
200
|
+
if (desc && (!onlyNodes || desc.node)) {
|
|
201
|
+
// If dom is outside of this desc's nodeDOM, don't count it.
|
|
202
|
+
if (first && (nodeDOM = desc.nodeDOM) && !(nodeDOM.nodeType == 1 ? nodeDOM.contains(dom.nodeType == 1 ? dom : dom.parentNode) : nodeDOM == dom)) first = false;
|
|
203
|
+
else return desc;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
getDesc(dom) {
|
|
209
|
+
const desc = dom.pmViewDesc;
|
|
210
|
+
for(let cur = desc; cur; cur = cur.parent)if (cur == this) return desc;
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
posFromDOM(dom, offset, bias) {
|
|
214
|
+
for(let scan = dom; scan; scan = scan.parentNode){
|
|
215
|
+
const desc = this.getDesc(scan);
|
|
216
|
+
if (desc) return desc.localPosFromDOM(dom, offset, bias);
|
|
217
|
+
}
|
|
218
|
+
return -1;
|
|
219
|
+
}
|
|
220
|
+
// Find the desc for the node after the given pos, if any. (When a
|
|
221
|
+
// parent node overrode rendering, there might not be one.)
|
|
222
|
+
descAt(pos) {
|
|
223
|
+
for(let i = 0, offset = 0; i < this.children.length; i++){
|
|
224
|
+
let child = this.children[i];
|
|
225
|
+
const end = offset + child.size;
|
|
226
|
+
if (offset == pos && end != offset) {
|
|
227
|
+
while(!child.border && child.children.length){
|
|
228
|
+
for(let i = 0; i < child.children.length; i++){
|
|
229
|
+
const inner = child.children[i];
|
|
230
|
+
if (inner.size) {
|
|
231
|
+
child = inner;
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return child;
|
|
237
|
+
}
|
|
238
|
+
if (pos < end) return child.descAt(pos - offset - child.border);
|
|
239
|
+
offset = end;
|
|
240
|
+
}
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
domFromPos(pos, side) {
|
|
244
|
+
if (!this.contentDOM) return {
|
|
245
|
+
node: this.dom,
|
|
246
|
+
offset: 0,
|
|
247
|
+
atom: pos + 1
|
|
248
|
+
};
|
|
249
|
+
// First find the position in the child array
|
|
250
|
+
let i = 0, offset = 0;
|
|
251
|
+
for(let curPos = 0; i < this.children.length; i++){
|
|
252
|
+
const child = this.children[i], end = curPos + child.size;
|
|
253
|
+
if (end > pos || child instanceof TrailingHackViewDesc) {
|
|
254
|
+
offset = pos - curPos;
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
curPos = end;
|
|
258
|
+
}
|
|
259
|
+
// If this points into the middle of a child, call through
|
|
260
|
+
if (offset) return this.children[i].domFromPos(offset - this.children[i].border, side);
|
|
261
|
+
// Go back if there were any zero-length widgets with side >= 0 before this point
|
|
262
|
+
for(let prev; i && !(prev = this.children[i - 1]).size && prev instanceof WidgetViewDesc && prev.side >= 0; i--){
|
|
263
|
+
// ...
|
|
264
|
+
}
|
|
265
|
+
// Scan towards the first useable node
|
|
266
|
+
if (side <= 0) {
|
|
267
|
+
let prev, enter = true;
|
|
268
|
+
for(;; i--, enter = false){
|
|
269
|
+
prev = i ? this.children[i - 1] : null;
|
|
270
|
+
if (!prev || prev.dom.parentNode == this.contentDOM) break;
|
|
271
|
+
}
|
|
272
|
+
if (prev && side && enter && !prev.border && !prev.domAtom) {
|
|
273
|
+
return prev.domFromPos(prev.size, side);
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
node: this.contentDOM,
|
|
277
|
+
offset: prev ? domIndex(prev.dom) + 1 : 0
|
|
278
|
+
};
|
|
279
|
+
} else {
|
|
280
|
+
let next, enter = true;
|
|
281
|
+
for(;; i++, enter = false){
|
|
282
|
+
next = i < this.children.length ? this.children[i] : null;
|
|
283
|
+
if (!next || next.dom.parentNode == this.contentDOM) break;
|
|
284
|
+
}
|
|
285
|
+
if (next && enter && !next.border && !next.domAtom) return next.domFromPos(0, side);
|
|
286
|
+
return {
|
|
287
|
+
node: this.contentDOM,
|
|
288
|
+
offset: next ? domIndex(next.dom) : this.contentDOM.childNodes.length
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Used to find a DOM range in a single parent for a given changed
|
|
293
|
+
// range.
|
|
294
|
+
parseRange(from, to) {
|
|
295
|
+
let base = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : 0;
|
|
296
|
+
if (this.children.length == 0) return {
|
|
297
|
+
node: this.contentDOM,
|
|
298
|
+
from,
|
|
299
|
+
to,
|
|
300
|
+
fromOffset: 0,
|
|
301
|
+
toOffset: this.contentDOM.childNodes.length
|
|
302
|
+
};
|
|
303
|
+
let fromOffset = -1, toOffset = -1;
|
|
304
|
+
for(let offset = base, i = 0;; i++){
|
|
305
|
+
const child = this.children[i], end = offset + child.size;
|
|
306
|
+
if (fromOffset == -1 && from <= end) {
|
|
307
|
+
const childBase = offset + child.border;
|
|
308
|
+
// FIXME maybe descend mark views to parse a narrower range?
|
|
309
|
+
if (from >= childBase && to <= end - child.border && child.node && child.contentDOM && this.contentDOM.contains(child.contentDOM)) return child.parseRange(from, to, childBase);
|
|
310
|
+
from = offset;
|
|
311
|
+
for(let j = i; j > 0; j--){
|
|
312
|
+
const prev = this.children[j - 1];
|
|
313
|
+
if (prev.size && prev.dom.parentNode == this.contentDOM && !prev.emptyChildAt(1)) {
|
|
314
|
+
fromOffset = domIndex(prev.dom) + 1;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
from -= prev.size;
|
|
318
|
+
}
|
|
319
|
+
if (fromOffset == -1) fromOffset = 0;
|
|
320
|
+
}
|
|
321
|
+
if (fromOffset > -1 && (end > to || i == this.children.length - 1)) {
|
|
322
|
+
to = end;
|
|
323
|
+
for(let j = i + 1; j < this.children.length; j++){
|
|
324
|
+
const next = this.children[j];
|
|
325
|
+
if (next.size && next.dom.parentNode == this.contentDOM && !next.emptyChildAt(-1)) {
|
|
326
|
+
toOffset = domIndex(next.dom);
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
to += next.size;
|
|
330
|
+
}
|
|
331
|
+
if (toOffset == -1) toOffset = this.contentDOM.childNodes.length;
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
offset = end;
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
node: this.contentDOM,
|
|
338
|
+
from,
|
|
339
|
+
to,
|
|
340
|
+
fromOffset,
|
|
341
|
+
toOffset
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
emptyChildAt(side) {
|
|
345
|
+
if (this.border || !this.contentDOM || !this.children.length) return false;
|
|
346
|
+
const child = this.children[side < 0 ? 0 : this.children.length - 1];
|
|
347
|
+
// @ts-expect-error ...
|
|
348
|
+
return child.size == 0 || child.emptyChildAt(side);
|
|
349
|
+
}
|
|
350
|
+
domAfterPos(pos) {
|
|
351
|
+
const { node, offset } = this.domFromPos(pos, 0);
|
|
352
|
+
if (node.nodeType != 1 || offset == node.childNodes.length) throw new RangeError("No node after pos " + pos);
|
|
353
|
+
// @ts-expect-error ...
|
|
354
|
+
return node.childNodes[offset];
|
|
355
|
+
}
|
|
356
|
+
// View descs are responsible for setting any selection that falls
|
|
357
|
+
// entirely inside of them, so that custom implementations can do
|
|
358
|
+
// custom things with the selection. Note that this falls apart when
|
|
359
|
+
// a selection starts in such a node and ends in another, in which
|
|
360
|
+
// case we just use whatever domFromPos produces as a best effort.
|
|
361
|
+
setSelection(anchor, head, view) {
|
|
362
|
+
let force = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : false;
|
|
363
|
+
if (view.composing) return;
|
|
364
|
+
// If the selection falls entirely in a child, give it to that child
|
|
365
|
+
const from = Math.min(anchor, head), to = Math.max(anchor, head);
|
|
366
|
+
for(let i = 0, offset = 0; i < this.children.length; i++){
|
|
367
|
+
const child = this.children[i], end = offset + child.size;
|
|
368
|
+
if (from > offset && to < end) return child.setSelection(anchor - offset - child.border, head - offset - child.border, view, force);
|
|
369
|
+
offset = end;
|
|
370
|
+
}
|
|
371
|
+
let anchorDOM = this.domFromPos(anchor, anchor ? -1 : 1);
|
|
372
|
+
let headDOM = head == anchor ? anchorDOM : this.domFromPos(head, head ? -1 : 1);
|
|
373
|
+
const domSel = view.root.getSelection();
|
|
374
|
+
const selRange = view.domSelectionRange();
|
|
375
|
+
let brKludge = false;
|
|
376
|
+
// On Firefox, using Selection.collapse to put the cursor after a
|
|
377
|
+
// BR node for some reason doesn't always work (#1073). On Safari,
|
|
378
|
+
// the cursor sometimes inexplicable visually lags behind its
|
|
379
|
+
// reported position in such situations (#1092).
|
|
380
|
+
if ((browser.gecko || browser.safari) && anchor == head) {
|
|
381
|
+
const { node, offset } = anchorDOM;
|
|
382
|
+
if (node.nodeType == 3) {
|
|
383
|
+
brKludge = !!(offset && node.nodeValue?.[offset - 1] == "\n");
|
|
384
|
+
// Issue #1128
|
|
385
|
+
if (brKludge && offset == node.nodeValue.length) {
|
|
386
|
+
for(let scan = node, after; scan; scan = scan.parentNode){
|
|
387
|
+
if (after = scan.nextSibling) {
|
|
388
|
+
if (after.nodeName == "BR") anchorDOM = headDOM = {
|
|
389
|
+
node: after.parentNode,
|
|
390
|
+
offset: domIndex(after) + 1
|
|
391
|
+
};
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
const desc = scan.pmViewDesc;
|
|
395
|
+
if (desc && desc.node && desc.node.isBlock) break;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
} else {
|
|
399
|
+
const prev = node.childNodes[offset - 1];
|
|
400
|
+
// @ts-expect-error ...
|
|
401
|
+
brKludge = prev && (prev.nodeName == "BR" || prev.contentEditable == "false");
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// Firefox can act strangely when the selection is in front of an
|
|
405
|
+
// uneditable node. See #1163 and https://bugzilla.mozilla.org/show_bug.cgi?id=1709536
|
|
406
|
+
if (browser.gecko && selRange.focusNode && selRange.focusNode != headDOM.node && selRange.focusNode.nodeType == 1) {
|
|
407
|
+
const after = selRange.focusNode.childNodes[selRange.focusOffset];
|
|
408
|
+
if (after && after.contentEditable == "false") force = true;
|
|
409
|
+
}
|
|
410
|
+
if (view.cursorWrapped || !(force || brKludge && browser.safari) && isEquivalentPosition(anchorDOM.node, anchorDOM.offset, selRange.anchorNode, selRange.anchorOffset) && isEquivalentPosition(headDOM.node, headDOM.offset, selRange.focusNode, selRange.focusOffset)) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
// Selection.extend can be used to create an 'inverted' selection
|
|
414
|
+
// (one where the focus is before the anchor), but not all
|
|
415
|
+
// browsers support it yet.
|
|
416
|
+
let domSelExtended = false;
|
|
417
|
+
if ((domSel.extend || anchor == head) && !brKludge) {
|
|
418
|
+
domSel.collapse(anchorDOM.node, anchorDOM.offset);
|
|
419
|
+
try {
|
|
420
|
+
if (anchor != head) domSel.extend(headDOM.node, headDOM.offset);
|
|
421
|
+
domSelExtended = true;
|
|
422
|
+
} catch (_) {
|
|
423
|
+
// In some cases with Chrome the selection is empty after calling
|
|
424
|
+
// collapse, even when it should be valid. This appears to be a bug, but
|
|
425
|
+
// it is difficult to isolate. If this happens fallback to the old path
|
|
426
|
+
// without using extend.
|
|
427
|
+
// Similarly, this could crash on Safari if the editor is hidden, and
|
|
428
|
+
// there was no selection.
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (!domSelExtended) {
|
|
432
|
+
if (anchor > head) {
|
|
433
|
+
const tmp = anchorDOM;
|
|
434
|
+
anchorDOM = headDOM;
|
|
435
|
+
headDOM = tmp;
|
|
436
|
+
}
|
|
437
|
+
const range = document.createRange();
|
|
438
|
+
range.setEnd(headDOM.node, headDOM.offset);
|
|
439
|
+
range.setStart(anchorDOM.node, anchorDOM.offset);
|
|
440
|
+
domSel.removeAllRanges();
|
|
441
|
+
domSel.addRange(range);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
ignoreMutation(mutation) {
|
|
445
|
+
return !this.contentDOM && mutation.type != "selection";
|
|
446
|
+
}
|
|
447
|
+
get contentLost() {
|
|
448
|
+
return this.contentDOM && this.contentDOM != this.dom && !this.dom.contains(this.contentDOM);
|
|
449
|
+
}
|
|
450
|
+
// Remove a subtree of the element tree that has been touched
|
|
451
|
+
// by a DOM change, so that the next update will redraw it.
|
|
452
|
+
markDirty(from, to) {
|
|
453
|
+
for(let offset = 0, i = 0; i < this.children.length; i++){
|
|
454
|
+
const child = this.children[i], end = offset + child.size;
|
|
455
|
+
if (offset == end ? from <= end && to >= offset : from < end && to > offset) {
|
|
456
|
+
const startInside = offset + child.border, endInside = end - child.border;
|
|
457
|
+
if (from >= startInside && to <= endInside) {
|
|
458
|
+
this.dirty = from == offset || to == end ? CONTENT_DIRTY : CHILD_DIRTY;
|
|
459
|
+
if (from == startInside && to == endInside && (child.contentLost || child.dom.parentNode != this.contentDOM)) child.dirty = NODE_DIRTY;
|
|
460
|
+
else child.markDirty(from - startInside, to - startInside);
|
|
461
|
+
return;
|
|
462
|
+
} else {
|
|
463
|
+
child.dirty = child.dom == child.contentDOM && child.dom.parentNode == this.contentDOM && !child.children.length ? CONTENT_DIRTY : NODE_DIRTY;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
offset = end;
|
|
467
|
+
}
|
|
468
|
+
this.dirty = CONTENT_DIRTY;
|
|
469
|
+
}
|
|
470
|
+
markParentsDirty() {
|
|
471
|
+
let level = 1;
|
|
472
|
+
for(let node = this.parent; node; node = node.parent, level++){
|
|
473
|
+
const dirty = level == 1 ? CONTENT_DIRTY : CHILD_DIRTY;
|
|
474
|
+
if (node.dirty < dirty) node.dirty = dirty;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
get domAtom() {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
get ignoreForCoords() {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
get ignoreForSelection() {
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
isText(_text) {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// A widget desc represents a widget decoration, which is a DOM node
|
|
491
|
+
// drawn between the document nodes.
|
|
492
|
+
export class WidgetViewDesc extends ViewDesc {
|
|
493
|
+
widget;
|
|
494
|
+
constructor(parent, getPos, widget, dom){
|
|
495
|
+
super(parent, [], getPos, dom, null), this.widget = widget;
|
|
496
|
+
this.widget = widget;
|
|
497
|
+
}
|
|
498
|
+
matchesWidget(widget) {
|
|
499
|
+
return this.dirty == NOT_DIRTY && widget.type.eq(this.widget.type);
|
|
500
|
+
}
|
|
501
|
+
parseRule() {
|
|
502
|
+
return {
|
|
503
|
+
ignore: true
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
stopEvent(event) {
|
|
507
|
+
const stop = this.widget.spec.stopEvent;
|
|
508
|
+
return stop ? stop(event) : false;
|
|
509
|
+
}
|
|
510
|
+
ignoreMutation(mutation) {
|
|
511
|
+
return mutation.type != "selection" || this.widget.spec.ignoreSelection;
|
|
512
|
+
}
|
|
513
|
+
get domAtom() {
|
|
514
|
+
return true;
|
|
515
|
+
}
|
|
516
|
+
get ignoreForSelection() {
|
|
517
|
+
return !!this.widget.type.spec.relaxedSide;
|
|
518
|
+
}
|
|
519
|
+
get side() {
|
|
520
|
+
return this.widget.type.side;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
export class CompositionViewDesc extends ViewDesc {
|
|
524
|
+
textDOM;
|
|
525
|
+
text;
|
|
526
|
+
constructor(parent, getPos, dom, textDOM, text){
|
|
527
|
+
super(parent, [], getPos, dom, null), this.textDOM = textDOM, this.text = text;
|
|
528
|
+
}
|
|
529
|
+
get size() {
|
|
530
|
+
return this.text.length;
|
|
531
|
+
}
|
|
532
|
+
localPosFromDOM(dom, offset) {
|
|
533
|
+
if (dom != this.textDOM) return this.posAtStart + (offset ? this.size : 0);
|
|
534
|
+
return this.posAtStart + offset;
|
|
535
|
+
}
|
|
536
|
+
domFromPos(pos) {
|
|
537
|
+
return {
|
|
538
|
+
node: this.textDOM,
|
|
539
|
+
offset: pos
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
ignoreMutation(mut) {
|
|
543
|
+
return mut.type === "characterData" && mut.target.nodeValue == mut.oldValue;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
// A mark desc represents a mark. May have multiple children,
|
|
547
|
+
// depending on how the mark is split. Note that marks are drawn using
|
|
548
|
+
// a fixed nesting order, for simplicity and predictability, so in
|
|
549
|
+
// some cases they will be split more often than would appear
|
|
550
|
+
// necessary.
|
|
551
|
+
export class MarkViewDesc extends ViewDesc {
|
|
552
|
+
mark;
|
|
553
|
+
spec;
|
|
554
|
+
constructor(parent, children, getPos, mark, dom, contentDOM, spec){
|
|
555
|
+
super(parent, children, getPos, dom, contentDOM), this.mark = mark, this.spec = spec;
|
|
556
|
+
}
|
|
557
|
+
parseRule() {
|
|
558
|
+
if (this.dirty & NODE_DIRTY || this.mark.type.spec.reparseInView) return null;
|
|
559
|
+
return {
|
|
560
|
+
mark: this.mark.type.name,
|
|
561
|
+
attrs: this.mark.attrs,
|
|
562
|
+
contentElement: this.contentDOM
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
matchesMark(mark) {
|
|
566
|
+
return this.dirty != NODE_DIRTY && this.mark.eq(mark);
|
|
567
|
+
}
|
|
568
|
+
markDirty(from, to) {
|
|
569
|
+
super.markDirty(from, to);
|
|
570
|
+
// Move dirty info to nearest node view
|
|
571
|
+
if (this.dirty != NOT_DIRTY) {
|
|
572
|
+
let parent = this.parent;
|
|
573
|
+
while(!parent.node)parent = parent.parent;
|
|
574
|
+
if (parent.dirty < this.dirty) parent.dirty = this.dirty;
|
|
575
|
+
this.dirty = NOT_DIRTY;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
ignoreMutation(mutation) {
|
|
579
|
+
return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation);
|
|
580
|
+
}
|
|
581
|
+
destroy() {
|
|
582
|
+
if (this.spec.destroy) this.spec.destroy();
|
|
583
|
+
super.destroy();
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
// Node view descs are the main, most common type of view desc, and
|
|
587
|
+
// correspond to an actual node in the document. Unlike mark descs,
|
|
588
|
+
// they populate their child array themselves.
|
|
589
|
+
export class NodeViewDesc extends ViewDesc {
|
|
590
|
+
node;
|
|
591
|
+
outerDeco;
|
|
592
|
+
innerDeco;
|
|
593
|
+
nodeDOM;
|
|
594
|
+
constructor(parent, children, getPos, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM){
|
|
595
|
+
super(parent, children, getPos, dom, contentDOM), this.node = node, this.outerDeco = outerDeco, this.innerDeco = innerDeco, this.nodeDOM = nodeDOM;
|
|
596
|
+
}
|
|
597
|
+
parseRule(addedNodes) {
|
|
598
|
+
// Experimental kludge to allow opt-in re-parsing of nodes
|
|
599
|
+
if (this.node.type.spec.reparseInView) return null;
|
|
600
|
+
// FIXME the assumption that this can always return the current
|
|
601
|
+
// attrs means that if the user somehow manages to change the
|
|
602
|
+
// attrs in the dom, that won't be picked up. Not entirely sure
|
|
603
|
+
// whether this is a problem
|
|
604
|
+
const rule = {
|
|
605
|
+
node: this.node.type.name,
|
|
606
|
+
attrs: this.node.attrs
|
|
607
|
+
};
|
|
608
|
+
if (this.node.type.whitespace == "pre") rule.preserveWhitespace = "full";
|
|
609
|
+
if (!this.contentDOM) {
|
|
610
|
+
rule.getContent = ()=>this.node.content;
|
|
611
|
+
} else if (!this.contentLost) {
|
|
612
|
+
rule.contentElement = this.contentDOM;
|
|
613
|
+
} else {
|
|
614
|
+
// Chrome likes to randomly recreate parent nodes when
|
|
615
|
+
// backspacing things. When that happens, this tries to find the
|
|
616
|
+
// new parent.
|
|
617
|
+
for(let i = this.children.length - 1; i >= 0; i--){
|
|
618
|
+
const child = this.children[i];
|
|
619
|
+
// @ts-expect-error ...
|
|
620
|
+
if (this.dom.contains(child.dom.parentNode)) {
|
|
621
|
+
// @ts-expect-error ...
|
|
622
|
+
rule.contentElement = child.dom.parentNode;
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
if (!rule.contentElement) {
|
|
627
|
+
const found = addedNodes && addedNodes.find((n)=>n.nodeType == 1 && addedNodes.indexOf(n.parentNode) < 0 && this.dom.contains(n));
|
|
628
|
+
if (found) rule.contentElement = found;
|
|
629
|
+
else rule.getContent = ()=>Fragment.empty;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return rule;
|
|
633
|
+
}
|
|
634
|
+
matchesNode(node, outerDeco, innerDeco) {
|
|
635
|
+
return this.dirty == NOT_DIRTY && node.eq(this.node) && sameOuterDeco(outerDeco, this.outerDeco) && innerDeco.eq(this.innerDeco);
|
|
636
|
+
}
|
|
637
|
+
get size() {
|
|
638
|
+
return this.node.nodeSize;
|
|
639
|
+
}
|
|
640
|
+
get border() {
|
|
641
|
+
return this.node.isLeaf ? 0 : 1;
|
|
642
|
+
}
|
|
643
|
+
updateChildren(_view, _pos) {
|
|
644
|
+
// Omitted to avoid reproducing lots of unused code.
|
|
645
|
+
// Overidden elsewhere in case this is ever vendored.
|
|
646
|
+
}
|
|
647
|
+
// If this desc must be updated to match the given node decoration,
|
|
648
|
+
// do so and return true.
|
|
649
|
+
update(node, outerDeco, innerDeco, view) {
|
|
650
|
+
if (this.dirty == NODE_DIRTY || !node.sameMarkup(this.node)) return false;
|
|
651
|
+
this.updateInner(node, outerDeco, innerDeco, view);
|
|
652
|
+
return true;
|
|
653
|
+
}
|
|
654
|
+
updateInner(node, outerDeco, innerDeco, view) {
|
|
655
|
+
this.updateOuterDeco(outerDeco);
|
|
656
|
+
this.node = node;
|
|
657
|
+
this.innerDeco = innerDeco;
|
|
658
|
+
if (this.contentDOM) this.updateChildren(view, this.posAtStart);
|
|
659
|
+
this.dirty = NOT_DIRTY;
|
|
660
|
+
}
|
|
661
|
+
updateOuterDeco(outerDeco) {
|
|
662
|
+
this.outerDeco = outerDeco;
|
|
663
|
+
}
|
|
664
|
+
// Mark this node as being the selected node.
|
|
665
|
+
selectNode() {
|
|
666
|
+
if (this.nodeDOM.nodeType == 1) this.nodeDOM.classList.add("ProseMirror-selectednode");
|
|
667
|
+
if (this.contentDOM || !this.node.type.spec.draggable) this.dom.draggable = true;
|
|
668
|
+
}
|
|
669
|
+
// Remove selected node marking from this node.
|
|
670
|
+
deselectNode() {
|
|
671
|
+
if (this.nodeDOM.nodeType == 1) {
|
|
672
|
+
this.nodeDOM.classList.remove("ProseMirror-selectednode");
|
|
673
|
+
if (this.contentDOM || !this.node.type.spec.draggable) this.dom.removeAttribute("draggable");
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
get domAtom() {
|
|
677
|
+
return this.node.isAtom;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
export class TextViewDesc extends NodeViewDesc {
|
|
681
|
+
constructor(parent, children, getPos, node, outerDeco, innerDeco, dom, nodeDOM){
|
|
682
|
+
super(parent, children, getPos, node, outerDeco, innerDeco, dom, null, nodeDOM);
|
|
683
|
+
}
|
|
684
|
+
parseRule() {
|
|
685
|
+
let skip = this.nodeDOM.parentNode;
|
|
686
|
+
while(skip && skip != this.dom && !skip.pmIsDeco)skip = skip.parentNode;
|
|
687
|
+
return {
|
|
688
|
+
skip: skip || true
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
update(node, outerDeco, _innerDeco, _view) {
|
|
692
|
+
if (this.dirty == NODE_DIRTY || this.dirty != NOT_DIRTY && !this.inParent() || !node.sameMarkup(this.node)) return false;
|
|
693
|
+
this.updateOuterDeco(outerDeco);
|
|
694
|
+
this.node = node;
|
|
695
|
+
this.dirty = NOT_DIRTY;
|
|
696
|
+
return true;
|
|
697
|
+
}
|
|
698
|
+
inParent() {
|
|
699
|
+
const parentDOM = this.parent.contentDOM;
|
|
700
|
+
for(let n = this.nodeDOM; n; n = n.parentNode)if (n == parentDOM) return true;
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
domFromPos(pos) {
|
|
704
|
+
return {
|
|
705
|
+
node: this.nodeDOM,
|
|
706
|
+
offset: pos
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
localPosFromDOM(dom, offset, bias) {
|
|
710
|
+
if (dom == this.nodeDOM) return this.posAtStart + Math.min(offset, this.node.text.length);
|
|
711
|
+
return super.localPosFromDOM(dom, offset, bias);
|
|
712
|
+
}
|
|
713
|
+
markDirty(from, to) {
|
|
714
|
+
super.markDirty(from, to);
|
|
715
|
+
if (this.dom != this.nodeDOM && (from == 0 || to == this.nodeDOM.nodeValue.length)) this.dirty = NODE_DIRTY;
|
|
716
|
+
}
|
|
717
|
+
get domAtom() {
|
|
718
|
+
return false;
|
|
719
|
+
}
|
|
720
|
+
isText(text) {
|
|
721
|
+
return this.node.text == text;
|
|
722
|
+
}
|
|
723
|
+
ignoreMutation(mutation) {
|
|
724
|
+
return mutation.type !== "characterData" && mutation.type !== "selection";
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
// A dummy desc used to tag trailing BR or IMG nodes created to work
|
|
728
|
+
// around contentEditable terribleness.
|
|
729
|
+
export class TrailingHackViewDesc extends ViewDesc {
|
|
730
|
+
parseRule() {
|
|
731
|
+
return {
|
|
732
|
+
ignore: true
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
matchesHack(nodeName) {
|
|
736
|
+
return this.dirty == NOT_DIRTY && this.dom.nodeName == nodeName;
|
|
737
|
+
}
|
|
738
|
+
get domAtom() {
|
|
739
|
+
return true;
|
|
740
|
+
}
|
|
741
|
+
get ignoreForCoords() {
|
|
742
|
+
return this.dom.nodeName == "IMG";
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
// A separate subclass is used for customized node views, so that the
|
|
746
|
+
// extra checks only have to be made for nodes that are actually
|
|
747
|
+
// customized.
|
|
748
|
+
let CustomNodeViewDesc = class CustomNodeViewDesc extends NodeViewDesc {
|
|
749
|
+
spec;
|
|
750
|
+
constructor(parent, children, getPos, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, spec){
|
|
751
|
+
super(parent, children, getPos, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM), this.spec = spec;
|
|
752
|
+
}
|
|
753
|
+
// A custom `update` method gets to decide whether the update goes
|
|
754
|
+
// through. If it does, and there's a `contentDOM` node, our logic
|
|
755
|
+
// updates the children.
|
|
756
|
+
update(node, outerDeco, innerDeco, view) {
|
|
757
|
+
if (this.dirty == NODE_DIRTY) return false;
|
|
758
|
+
if (this.spec.update && (this.node.type == node.type || this.spec.multiType)) {
|
|
759
|
+
const result = this.spec.update(node, outerDeco, innerDeco);
|
|
760
|
+
if (result) this.updateInner(node, outerDeco, innerDeco, view);
|
|
761
|
+
return result;
|
|
762
|
+
} else if (!this.contentDOM && !node.isLeaf) {
|
|
763
|
+
return false;
|
|
764
|
+
} else {
|
|
765
|
+
return super.update(node, outerDeco, innerDeco, view);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
selectNode() {
|
|
769
|
+
this.spec.selectNode ? this.spec.selectNode.call(this) : super.selectNode();
|
|
770
|
+
}
|
|
771
|
+
deselectNode() {
|
|
772
|
+
this.spec.deselectNode ? this.spec.deselectNode.call(this) : super.deselectNode();
|
|
773
|
+
}
|
|
774
|
+
setSelection(anchor, head, view, force) {
|
|
775
|
+
this.spec.setSelection ? this.spec.setSelection.call(this, anchor, head, view.root) : super.setSelection(anchor, head, view, force);
|
|
776
|
+
}
|
|
777
|
+
destroy() {
|
|
778
|
+
if (this.spec.destroy) this.spec.destroy();
|
|
779
|
+
super.destroy();
|
|
780
|
+
}
|
|
781
|
+
stopEvent(event) {
|
|
782
|
+
return this.spec.stopEvent ? this.spec.stopEvent(event) : false;
|
|
783
|
+
}
|
|
784
|
+
ignoreMutation(mutation) {
|
|
785
|
+
return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation);
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
export class ReactMarkViewDesc extends MarkViewDesc {
|
|
789
|
+
destroy() {
|
|
790
|
+
// React has already destroyed the children (if needed).
|
|
791
|
+
this.children = [];
|
|
792
|
+
super.destroy();
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
export class ReactNodeViewDesc extends CustomNodeViewDesc {
|
|
796
|
+
updateChildren(_view, _pos) {
|
|
797
|
+
// React has already updated the children.
|
|
798
|
+
}
|
|
799
|
+
updateOuterDeco(outerDeco) {
|
|
800
|
+
// React has already updated the decorations.
|
|
801
|
+
this.outerDeco = outerDeco;
|
|
802
|
+
}
|
|
803
|
+
destroy() {
|
|
804
|
+
// React has already destroyed the children (if needed).
|
|
805
|
+
this.children = [];
|
|
806
|
+
super.destroy();
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
export function sameOuterDeco(a, b) {
|
|
810
|
+
if (a.length != b.length) return false;
|
|
811
|
+
// @ts-expect-error ...
|
|
812
|
+
for(let i = 0; i < a.length; i++)if (!a[i].type.eq(b[i].type)) return false;
|
|
813
|
+
return true;
|
|
814
|
+
}
|
|
815
|
+
// Find a piece of text in an inline fragment, overlapping from-to.
|
|
816
|
+
// Ported from prosemirror-view's findTextInFragment.
|
|
817
|
+
export function findTextInFragment(frag, text, from, to) {
|
|
818
|
+
for(let i = 0, pos = 0; i < frag.childCount && pos <= to;){
|
|
819
|
+
const child = frag.child(i++);
|
|
820
|
+
const childStart = pos;
|
|
821
|
+
pos += child.nodeSize;
|
|
822
|
+
if (!child.isText) continue;
|
|
823
|
+
let str = child.text;
|
|
824
|
+
while(i < frag.childCount){
|
|
825
|
+
const next = frag.child(i++);
|
|
826
|
+
pos += next.nodeSize;
|
|
827
|
+
if (!next.isText) break;
|
|
828
|
+
str += next.text;
|
|
829
|
+
}
|
|
830
|
+
if (pos >= from) {
|
|
831
|
+
if (pos >= to && str.slice(to - text.length - childStart, to - childStart) === text) {
|
|
832
|
+
return to - text.length;
|
|
833
|
+
}
|
|
834
|
+
const found = childStart < to ? str.lastIndexOf(text, to - childStart - 1) : -1;
|
|
835
|
+
if (found >= 0 && found + text.length + childStart >= from) {
|
|
836
|
+
return childStart + found;
|
|
837
|
+
}
|
|
838
|
+
if (from === to && str.length >= to + text.length - childStart && str.slice(to - childStart, to - childStart + text.length) === text) {
|
|
839
|
+
return to;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return -1;
|
|
844
|
+
}
|