prosemirror-view 1.36.0 → 1.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 1.37.0 (2024-11-23)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue on Safari where the (incorrect) top-level selection was used, instead of the workaround that also works inside shadow roots.
6
+
7
+ ### New features
8
+
9
+ Mark views can now provide an `ignoreMutation` method. Add ViewMutationRecord to doc template
10
+
1
11
  ## 1.36.0 (2024-11-05)
2
12
 
3
13
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -935,19 +935,20 @@ var ViewDesc = function () {
935
935
  }
936
936
  }, {
937
937
  key: "setSelection",
938
- value: function setSelection(anchor, head, root) {
938
+ value: function setSelection(anchor, head, view) {
939
939
  var force = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
940
940
  var from = Math.min(anchor, head),
941
941
  to = Math.max(anchor, head);
942
942
  for (var i = 0, offset = 0; i < this.children.length; i++) {
943
943
  var child = this.children[i],
944
944
  end = offset + child.size;
945
- if (from > offset && to < end) return child.setSelection(anchor - offset - child.border, head - offset - child.border, root, force);
945
+ if (from > offset && to < end) return child.setSelection(anchor - offset - child.border, head - offset - child.border, view, force);
946
946
  offset = end;
947
947
  }
948
948
  var anchorDOM = this.domFromPos(anchor, anchor ? -1 : 1);
949
949
  var headDOM = head == anchor ? anchorDOM : this.domFromPos(head, head ? -1 : 1);
950
- var domSel = root.getSelection();
950
+ var domSel = view.root.getSelection();
951
+ var selRange = view.domSelectionRange();
951
952
  var brKludge = false;
952
953
  if ((gecko || safari) && anchor == head) {
953
954
  var _anchorDOM = anchorDOM,
@@ -973,11 +974,11 @@ var ViewDesc = function () {
973
974
  brKludge = prev && (prev.nodeName == "BR" || prev.contentEditable == "false");
974
975
  }
975
976
  }
976
- if (gecko && domSel.focusNode && domSel.focusNode != headDOM.node && domSel.focusNode.nodeType == 1) {
977
- var _after2 = domSel.focusNode.childNodes[domSel.focusOffset];
977
+ if (gecko && selRange.focusNode && selRange.focusNode != headDOM.node && selRange.focusNode.nodeType == 1) {
978
+ var _after2 = selRange.focusNode.childNodes[selRange.focusOffset];
978
979
  if (_after2 && _after2.contentEditable == "false") force = true;
979
980
  }
980
- if (!(force || brKludge && safari) && isEquivalentPosition(anchorDOM.node, anchorDOM.offset, domSel.anchorNode, domSel.anchorOffset) && isEquivalentPosition(headDOM.node, headDOM.offset, domSel.focusNode, domSel.focusOffset)) return;
981
+ if (!(force || brKludge && safari) && isEquivalentPosition(anchorDOM.node, anchorDOM.offset, selRange.anchorNode, selRange.anchorOffset) && isEquivalentPosition(headDOM.node, headDOM.offset, selRange.focusNode, selRange.focusOffset)) return;
981
982
  var domSelExtended = false;
982
983
  if ((domSel.extend || anchor == head) && !brKludge) {
983
984
  domSel.collapse(anchorDOM.node, anchorDOM.offset);
@@ -1213,6 +1214,11 @@ var MarkViewDesc = function (_ViewDesc3) {
1213
1214
  copy.children = nodes;
1214
1215
  return copy;
1215
1216
  }
1217
+ }, {
1218
+ key: "ignoreMutation",
1219
+ value: function ignoreMutation(mutation) {
1220
+ return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : _get(_getPrototypeOf(MarkViewDesc.prototype), "ignoreMutation", this).call(this, mutation);
1221
+ }
1216
1222
  }, {
1217
1223
  key: "destroy",
1218
1224
  value: function destroy() {
@@ -1593,8 +1599,8 @@ var CustomNodeViewDesc = function (_NodeViewDesc2) {
1593
1599
  }
1594
1600
  }, {
1595
1601
  key: "setSelection",
1596
- value: function setSelection(anchor, head, root, force) {
1597
- this.spec.setSelection ? this.spec.setSelection(anchor, head, root) : _get(_getPrototypeOf(CustomNodeViewDesc.prototype), "setSelection", this).call(this, anchor, head, root, force);
1602
+ value: function setSelection(anchor, head, view, force) {
1603
+ this.spec.setSelection ? this.spec.setSelection(anchor, head, view.root) : _get(_getPrototypeOf(CustomNodeViewDesc.prototype), "setSelection", this).call(this, anchor, head, view, force);
1598
1604
  }
1599
1605
  }, {
1600
1606
  key: "destroy",
@@ -2179,7 +2185,7 @@ function selectionToDOM(view) {
2179
2185
  if (!sel.$from.parent.inlineContent) resetEditableFrom = temporarilyEditableNear(view, sel.from);
2180
2186
  if (!sel.empty && !sel.$from.parent.inlineContent) resetEditableTo = temporarilyEditableNear(view, sel.to);
2181
2187
  }
2182
- view.docView.setSelection(anchor, head, view.root, force);
2188
+ view.docView.setSelection(anchor, head, view, force);
2183
2189
  if (brokenSelectBetweenUneditable) {
2184
2190
  if (resetEditableFrom) resetEditable(resetEditableFrom);
2185
2191
  if (resetEditableTo) resetEditable(resetEditableTo);
package/dist/index.d.cts CHANGED
@@ -238,6 +238,17 @@ declare global {
238
238
  }
239
239
  }
240
240
  /**
241
+ A ViewMutationRecord represents a DOM
242
+ [mutation](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
243
+ or a selection change happens within the view. When the change is
244
+ a selection change, the record will have a `type` property of
245
+ `"selection"` (which doesn't occur for native mutation records).
246
+ */
247
+ type ViewMutationRecord = MutationRecord | {
248
+ type: "selection";
249
+ target: DOMNode;
250
+ };
251
+ /**
241
252
  By default, document nodes are rendered using the result of the
242
253
  [`toDOM`](https://prosemirror.net/docs/ref/#model.NodeSpec.toDOM) method of their spec, and managed
243
254
  entirely by the editor. For some use cases, such as embedded
@@ -308,16 +319,11 @@ interface NodeView {
308
319
  */
309
320
  stopEvent?: (event: Event) => boolean;
310
321
  /**
311
- Called when a DOM
312
- [mutation](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
313
- or a selection change happens within the view. When the change is
314
- a selection change, the record will have a `type` property of
315
- `"selection"` (which doesn't occur for native mutation records).
316
- Return false if the editor should re-read the selection or
317
- re-parse the range around the mutation, true if it can safely be
318
- ignored.
322
+ Called when a [mutation](https://prosemirror.net/docs/ref/#view.ViewMutationRecord) happens within the
323
+ view. Return false if the editor should re-read the selection or re-parse
324
+ the range around the mutation, true if it can safely be ignored.
319
325
  */
320
- ignoreMutation?: (mutation: MutationRecord) => boolean;
326
+ ignoreMutation?: (mutation: ViewMutationRecord) => boolean;
321
327
  /**
322
328
  Called when the node view is removed from the editor or the whole
323
329
  editor is destroyed.
@@ -344,6 +350,12 @@ interface MarkView {
344
350
  */
345
351
  contentDOM?: HTMLElement | null;
346
352
  /**
353
+ Called when a [mutation](https://prosemirror.net/docs/ref/#view.ViewMutationRecord) happens within the
354
+ view. Return false if the editor should re-read the selection or re-parse
355
+ the range around the mutation, true if it can safely be ignored.
356
+ */
357
+ ignoreMutation?: (mutation: ViewMutationRecord) => boolean;
358
+ /**
347
359
  Called when the mark view is removed from the editor or the whole
348
360
  editor is destroyed.
349
361
  */
@@ -391,8 +403,8 @@ declare class ViewDesc {
391
403
  };
392
404
  emptyChildAt(side: number): boolean;
393
405
  domAfterPos(pos: number): DOMNode;
394
- setSelection(anchor: number, head: number, root: Document | ShadowRoot, force?: boolean): void;
395
- ignoreMutation(mutation: MutationRecord): boolean;
406
+ setSelection(anchor: number, head: number, view: EditorView, force?: boolean): void;
407
+ ignoreMutation(mutation: ViewMutationRecord): boolean;
396
408
  get contentLost(): boolean | null;
397
409
  markDirty(from: number, to: number): void;
398
410
  markParentsDirty(): void;
@@ -441,7 +453,7 @@ declare class TextViewDesc extends NodeViewDesc {
441
453
  offset: number;
442
454
  };
443
455
  localPosFromDOM(dom: DOMNode, offset: number, bias: number): number;
444
- ignoreMutation(mutation: MutationRecord): boolean;
456
+ ignoreMutation(mutation: ViewMutationRecord): boolean;
445
457
  slice(from: number, to: number, view: EditorView): TextViewDesc;
446
458
  markDirty(from: number, to: number): void;
447
459
  get domAtom(): boolean;
@@ -955,4 +967,4 @@ interface DirectEditorProps extends EditorProps {
955
967
  dispatchTransaction?: (tr: Transaction) => void;
956
968
  }
957
969
 
958
- export { type DOMEventMap, Decoration, type DecorationAttrs, DecorationSet, type DecorationSource, type DirectEditorProps, type EditorProps, EditorView, type MarkView, type MarkViewConstructor, type NodeView, type NodeViewConstructor };
970
+ export { type DOMEventMap, Decoration, type DecorationAttrs, DecorationSet, type DecorationSource, type DirectEditorProps, type EditorProps, EditorView, type MarkView, type MarkViewConstructor, type NodeView, type NodeViewConstructor, type ViewMutationRecord };
package/dist/index.d.ts CHANGED
@@ -238,6 +238,17 @@ declare global {
238
238
  }
239
239
  }
240
240
  /**
241
+ A ViewMutationRecord represents a DOM
242
+ [mutation](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
243
+ or a selection change happens within the view. When the change is
244
+ a selection change, the record will have a `type` property of
245
+ `"selection"` (which doesn't occur for native mutation records).
246
+ */
247
+ type ViewMutationRecord = MutationRecord | {
248
+ type: "selection";
249
+ target: DOMNode;
250
+ };
251
+ /**
241
252
  By default, document nodes are rendered using the result of the
242
253
  [`toDOM`](https://prosemirror.net/docs/ref/#model.NodeSpec.toDOM) method of their spec, and managed
243
254
  entirely by the editor. For some use cases, such as embedded
@@ -308,16 +319,11 @@ interface NodeView {
308
319
  */
309
320
  stopEvent?: (event: Event) => boolean;
310
321
  /**
311
- Called when a DOM
312
- [mutation](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
313
- or a selection change happens within the view. When the change is
314
- a selection change, the record will have a `type` property of
315
- `"selection"` (which doesn't occur for native mutation records).
316
- Return false if the editor should re-read the selection or
317
- re-parse the range around the mutation, true if it can safely be
318
- ignored.
322
+ Called when a [mutation](https://prosemirror.net/docs/ref/#view.ViewMutationRecord) happens within the
323
+ view. Return false if the editor should re-read the selection or re-parse
324
+ the range around the mutation, true if it can safely be ignored.
319
325
  */
320
- ignoreMutation?: (mutation: MutationRecord) => boolean;
326
+ ignoreMutation?: (mutation: ViewMutationRecord) => boolean;
321
327
  /**
322
328
  Called when the node view is removed from the editor or the whole
323
329
  editor is destroyed.
@@ -344,6 +350,12 @@ interface MarkView {
344
350
  */
345
351
  contentDOM?: HTMLElement | null;
346
352
  /**
353
+ Called when a [mutation](https://prosemirror.net/docs/ref/#view.ViewMutationRecord) happens within the
354
+ view. Return false if the editor should re-read the selection or re-parse
355
+ the range around the mutation, true if it can safely be ignored.
356
+ */
357
+ ignoreMutation?: (mutation: ViewMutationRecord) => boolean;
358
+ /**
347
359
  Called when the mark view is removed from the editor or the whole
348
360
  editor is destroyed.
349
361
  */
@@ -391,8 +403,8 @@ declare class ViewDesc {
391
403
  };
392
404
  emptyChildAt(side: number): boolean;
393
405
  domAfterPos(pos: number): DOMNode;
394
- setSelection(anchor: number, head: number, root: Document | ShadowRoot, force?: boolean): void;
395
- ignoreMutation(mutation: MutationRecord): boolean;
406
+ setSelection(anchor: number, head: number, view: EditorView, force?: boolean): void;
407
+ ignoreMutation(mutation: ViewMutationRecord): boolean;
396
408
  get contentLost(): boolean | null;
397
409
  markDirty(from: number, to: number): void;
398
410
  markParentsDirty(): void;
@@ -441,7 +453,7 @@ declare class TextViewDesc extends NodeViewDesc {
441
453
  offset: number;
442
454
  };
443
455
  localPosFromDOM(dom: DOMNode, offset: number, bias: number): number;
444
- ignoreMutation(mutation: MutationRecord): boolean;
456
+ ignoreMutation(mutation: ViewMutationRecord): boolean;
445
457
  slice(from: number, to: number, view: EditorView): TextViewDesc;
446
458
  markDirty(from: number, to: number): void;
447
459
  get domAtom(): boolean;
@@ -955,4 +967,4 @@ interface DirectEditorProps extends EditorProps {
955
967
  dispatchTransaction?: (tr: Transaction) => void;
956
968
  }
957
969
 
958
- export { type DOMEventMap, Decoration, type DecorationAttrs, DecorationSet, type DecorationSource, type DirectEditorProps, type EditorProps, EditorView, type MarkView, type MarkViewConstructor, type NodeView, type NodeViewConstructor };
970
+ export { type DOMEventMap, Decoration, type DecorationAttrs, DecorationSet, type DecorationSource, type DirectEditorProps, type EditorProps, EditorView, type MarkView, type MarkViewConstructor, type NodeView, type NodeViewConstructor, type ViewMutationRecord };
package/dist/index.js CHANGED
@@ -996,18 +996,19 @@ class ViewDesc {
996
996
  // custom things with the selection. Note that this falls apart when
997
997
  // a selection starts in such a node and ends in another, in which
998
998
  // case we just use whatever domFromPos produces as a best effort.
999
- setSelection(anchor, head, root, force = false) {
999
+ setSelection(anchor, head, view, force = false) {
1000
1000
  // If the selection falls entirely in a child, give it to that child
1001
1001
  let from = Math.min(anchor, head), to = Math.max(anchor, head);
1002
1002
  for (let i = 0, offset = 0; i < this.children.length; i++) {
1003
1003
  let child = this.children[i], end = offset + child.size;
1004
1004
  if (from > offset && to < end)
1005
- return child.setSelection(anchor - offset - child.border, head - offset - child.border, root, force);
1005
+ return child.setSelection(anchor - offset - child.border, head - offset - child.border, view, force);
1006
1006
  offset = end;
1007
1007
  }
1008
1008
  let anchorDOM = this.domFromPos(anchor, anchor ? -1 : 1);
1009
1009
  let headDOM = head == anchor ? anchorDOM : this.domFromPos(head, head ? -1 : 1);
1010
- let domSel = root.getSelection();
1010
+ let domSel = view.root.getSelection();
1011
+ let selRange = view.domSelectionRange();
1011
1012
  let brKludge = false;
1012
1013
  // On Firefox, using Selection.collapse to put the cursor after a
1013
1014
  // BR node for some reason doesn't always work (#1073). On Safari,
@@ -1038,14 +1039,14 @@ class ViewDesc {
1038
1039
  }
1039
1040
  // Firefox can act strangely when the selection is in front of an
1040
1041
  // uneditable node. See #1163 and https://bugzilla.mozilla.org/show_bug.cgi?id=1709536
1041
- if (gecko && domSel.focusNode && domSel.focusNode != headDOM.node && domSel.focusNode.nodeType == 1) {
1042
- let after = domSel.focusNode.childNodes[domSel.focusOffset];
1042
+ if (gecko && selRange.focusNode && selRange.focusNode != headDOM.node && selRange.focusNode.nodeType == 1) {
1043
+ let after = selRange.focusNode.childNodes[selRange.focusOffset];
1043
1044
  if (after && after.contentEditable == "false")
1044
1045
  force = true;
1045
1046
  }
1046
1047
  if (!(force || brKludge && safari) &&
1047
- isEquivalentPosition(anchorDOM.node, anchorDOM.offset, domSel.anchorNode, domSel.anchorOffset) &&
1048
- isEquivalentPosition(headDOM.node, headDOM.offset, domSel.focusNode, domSel.focusOffset))
1048
+ isEquivalentPosition(anchorDOM.node, anchorDOM.offset, selRange.anchorNode, selRange.anchorOffset) &&
1049
+ isEquivalentPosition(headDOM.node, headDOM.offset, selRange.focusNode, selRange.focusOffset))
1049
1050
  return;
1050
1051
  // Selection.extend can be used to create an 'inverted' selection
1051
1052
  // (one where the focus is before the anchor), but not all
@@ -1234,6 +1235,9 @@ class MarkViewDesc extends ViewDesc {
1234
1235
  copy.children = nodes;
1235
1236
  return copy;
1236
1237
  }
1238
+ ignoreMutation(mutation) {
1239
+ return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation);
1240
+ }
1237
1241
  destroy() {
1238
1242
  if (this.spec.destroy)
1239
1243
  this.spec.destroy();
@@ -1577,9 +1581,9 @@ class CustomNodeViewDesc extends NodeViewDesc {
1577
1581
  deselectNode() {
1578
1582
  this.spec.deselectNode ? this.spec.deselectNode() : super.deselectNode();
1579
1583
  }
1580
- setSelection(anchor, head, root, force) {
1581
- this.spec.setSelection ? this.spec.setSelection(anchor, head, root)
1582
- : super.setSelection(anchor, head, root, force);
1584
+ setSelection(anchor, head, view, force) {
1585
+ this.spec.setSelection ? this.spec.setSelection(anchor, head, view.root)
1586
+ : super.setSelection(anchor, head, view, force);
1583
1587
  }
1584
1588
  destroy() {
1585
1589
  if (this.spec.destroy)
@@ -2239,7 +2243,7 @@ function selectionToDOM(view, force = false) {
2239
2243
  if (!sel.empty && !sel.$from.parent.inlineContent)
2240
2244
  resetEditableTo = temporarilyEditableNear(view, sel.to);
2241
2245
  }
2242
- view.docView.setSelection(anchor, head, view.root, force);
2246
+ view.docView.setSelection(anchor, head, view, force);
2243
2247
  if (brokenSelectBetweenUneditable) {
2244
2248
  if (resetEditableFrom)
2245
2249
  resetEditable(resetEditableFrom);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prosemirror-view",
3
- "version": "1.36.0",
3
+ "version": "1.37.0",
4
4
  "description": "ProseMirror's view component",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
package/src/README.md CHANGED
@@ -20,6 +20,8 @@ this module.
20
20
 
21
21
  @MarkView
22
22
 
23
+ @ViewMutationRecord
24
+
23
25
  @DOMEventMap
24
26
 
25
27
  ### Decorations
@@ -151,8 +151,8 @@ export class DOMObserver {
151
151
  let desc = container && this.view.docView.nearestDesc(container)
152
152
  if (desc && desc.ignoreMutation({
153
153
  type: "selection",
154
- target: container!.nodeType == 3 ? container!.parentNode : container
155
- } as any)) {
154
+ target: container!.nodeType == 3 ? container!.parentNode! : container!
155
+ })) {
156
156
  this.setCurSelection()
157
157
  return true
158
158
  }
package/src/index.ts CHANGED
@@ -14,7 +14,7 @@ import {DOMSelection, DOMNode, DOMSelectionRange, deepActiveElement, clearReused
14
14
  import * as browser from "./browser"
15
15
 
16
16
  export {Decoration, DecorationSet, DecorationAttrs, DecorationSource} from "./decoration"
17
- export {NodeView, MarkView} from "./viewdesc"
17
+ export {NodeView, MarkView, ViewMutationRecord} from "./viewdesc"
18
18
 
19
19
  // Exported for testing
20
20
  import {serializeForClipboard, parseFromClipboard} from "./clipboard"
package/src/selection.ts CHANGED
@@ -84,7 +84,7 @@ export function selectionToDOM(view: EditorView, force = false) {
84
84
  if (!sel.empty && !sel.$from.parent.inlineContent)
85
85
  resetEditableTo = temporarilyEditableNear(view, sel.to)
86
86
  }
87
- view.docView.setSelection(anchor, head, view.root, force)
87
+ view.docView.setSelection(anchor, head, view, force)
88
88
  if (brokenSelectBetweenUneditable) {
89
89
  if (resetEditableFrom) resetEditable(resetEditableFrom)
90
90
  if (resetEditableTo) resetEditable(resetEditableTo)
package/src/viewdesc.ts CHANGED
@@ -10,6 +10,13 @@ declare global {
10
10
  interface Node { pmViewDesc?: ViewDesc }
11
11
  }
12
12
 
13
+ /// A ViewMutationRecord represents a DOM
14
+ /// [mutation](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
15
+ /// or a selection change happens within the view. When the change is
16
+ /// a selection change, the record will have a `type` property of
17
+ /// `"selection"` (which doesn't occur for native mutation records).
18
+ export type ViewMutationRecord = MutationRecord | { type: "selection", target: DOMNode }
19
+
13
20
  /// By default, document nodes are rendered using the result of the
14
21
  /// [`toDOM`](#model.NodeSpec.toDOM) method of their spec, and managed
15
22
  /// entirely by the editor. For some use cases, such as embedded
@@ -70,15 +77,10 @@ export interface NodeView {
70
77
  /// which this returns true are not handled by the editor.
71
78
  stopEvent?: (event: Event) => boolean
72
79
 
73
- /// Called when a DOM
74
- /// [mutation](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
75
- /// or a selection change happens within the view. When the change is
76
- /// a selection change, the record will have a `type` property of
77
- /// `"selection"` (which doesn't occur for native mutation records).
78
- /// Return false if the editor should re-read the selection or
79
- /// re-parse the range around the mutation, true if it can safely be
80
- /// ignored.
81
- ignoreMutation?: (mutation: MutationRecord) => boolean
80
+ /// Called when a [mutation](#view.ViewMutationRecord) happens within the
81
+ /// view. Return false if the editor should re-read the selection or re-parse
82
+ /// the range around the mutation, true if it can safely be ignored.
83
+ ignoreMutation?: (mutation: ViewMutationRecord) => boolean
82
84
 
83
85
  /// Called when the node view is removed from the editor or the whole
84
86
  /// editor is destroyed.
@@ -99,6 +101,12 @@ export interface MarkView {
99
101
  /// The DOM node that should hold the mark's content. When this is not
100
102
  /// present, the `dom` property is used as the content DOM.
101
103
  contentDOM?: HTMLElement | null
104
+
105
+ /// Called when a [mutation](#view.ViewMutationRecord) happens within the
106
+ /// view. Return false if the editor should re-read the selection or re-parse
107
+ /// the range around the mutation, true if it can safely be ignored.
108
+ ignoreMutation?: (mutation: ViewMutationRecord) => boolean
109
+
102
110
 
103
111
  /// Called when the mark view is removed from the editor or the whole
104
112
  /// editor is destroyed.
@@ -387,19 +395,20 @@ export class ViewDesc {
387
395
  // custom things with the selection. Note that this falls apart when
388
396
  // a selection starts in such a node and ends in another, in which
389
397
  // case we just use whatever domFromPos produces as a best effort.
390
- setSelection(anchor: number, head: number, root: Document | ShadowRoot, force = false): void {
398
+ setSelection(anchor: number, head: number, view: EditorView, force = false): void {
391
399
  // If the selection falls entirely in a child, give it to that child
392
400
  let from = Math.min(anchor, head), to = Math.max(anchor, head)
393
401
  for (let i = 0, offset = 0; i < this.children.length; i++) {
394
402
  let child = this.children[i], end = offset + child.size
395
403
  if (from > offset && to < end)
396
- return child.setSelection(anchor - offset - child.border, head - offset - child.border, root, force)
404
+ return child.setSelection(anchor - offset - child.border, head - offset - child.border, view, force)
397
405
  offset = end
398
406
  }
399
407
 
400
408
  let anchorDOM = this.domFromPos(anchor, anchor ? -1 : 1)
401
409
  let headDOM = head == anchor ? anchorDOM : this.domFromPos(head, head ? -1 : 1)
402
- let domSel = (root as Document).getSelection()!
410
+ let domSel = (view.root as Document).getSelection()!
411
+ let selRange = view.domSelectionRange()
403
412
 
404
413
  let brKludge = false
405
414
  // On Firefox, using Selection.collapse to put the cursor after a
@@ -429,14 +438,14 @@ export class ViewDesc {
429
438
  }
430
439
  // Firefox can act strangely when the selection is in front of an
431
440
  // uneditable node. See #1163 and https://bugzilla.mozilla.org/show_bug.cgi?id=1709536
432
- if (browser.gecko && domSel.focusNode && domSel.focusNode != headDOM.node && domSel.focusNode.nodeType == 1) {
433
- let after = domSel.focusNode.childNodes[domSel.focusOffset]
441
+ if (browser.gecko && selRange.focusNode && selRange.focusNode != headDOM.node && selRange.focusNode.nodeType == 1) {
442
+ let after = selRange.focusNode.childNodes[selRange.focusOffset]
434
443
  if (after && (after as HTMLElement).contentEditable == "false") force = true
435
444
  }
436
445
 
437
446
  if (!(force || brKludge && browser.safari) &&
438
- isEquivalentPosition(anchorDOM.node, anchorDOM.offset, domSel.anchorNode!, domSel.anchorOffset) &&
439
- isEquivalentPosition(headDOM.node, headDOM.offset, domSel.focusNode!, domSel.focusOffset))
447
+ isEquivalentPosition(anchorDOM.node, anchorDOM.offset, selRange.anchorNode!, selRange.anchorOffset) &&
448
+ isEquivalentPosition(headDOM.node, headDOM.offset, selRange.focusNode!, selRange.focusOffset))
440
449
  return
441
450
 
442
451
  // Selection.extend can be used to create an 'inverted' selection
@@ -468,8 +477,8 @@ export class ViewDesc {
468
477
  }
469
478
  }
470
479
 
471
- ignoreMutation(mutation: MutationRecord): boolean {
472
- return !this.contentDOM && (mutation.type as any) != "selection"
480
+ ignoreMutation(mutation: ViewMutationRecord): boolean {
481
+ return !this.contentDOM && mutation.type != "selection"
473
482
  }
474
483
 
475
484
  get contentLost() {
@@ -548,8 +557,8 @@ class WidgetViewDesc extends ViewDesc {
548
557
  return stop ? stop(event) : false
549
558
  }
550
559
 
551
- ignoreMutation(mutation: MutationRecord) {
552
- return (mutation.type as any) != "selection" || this.widget.spec.ignoreSelection
560
+ ignoreMutation(mutation: ViewMutationRecord) {
561
+ return mutation.type != "selection" || this.widget.spec.ignoreSelection
553
562
  }
554
563
 
555
564
  destroy() {
@@ -578,7 +587,7 @@ class CompositionViewDesc extends ViewDesc {
578
587
  return {node: this.textDOM, offset: pos}
579
588
  }
580
589
 
581
- ignoreMutation(mut: MutationRecord) {
590
+ ignoreMutation(mut: ViewMutationRecord) {
582
591
  return mut.type === 'characterData' && mut.target.nodeValue == mut.oldValue
583
592
  }
584
593
  }
@@ -629,6 +638,10 @@ class MarkViewDesc extends ViewDesc {
629
638
  return copy
630
639
  }
631
640
 
641
+ ignoreMutation(mutation: ViewMutationRecord) {
642
+ return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation)
643
+ }
644
+
632
645
  destroy() {
633
646
  if (this.spec.destroy) this.spec.destroy()
634
647
  super.destroy()
@@ -924,8 +937,8 @@ class TextViewDesc extends NodeViewDesc {
924
937
  return super.localPosFromDOM(dom, offset, bias)
925
938
  }
926
939
 
927
- ignoreMutation(mutation: MutationRecord) {
928
- return mutation.type != "characterData" && (mutation.type as any) != "selection"
940
+ ignoreMutation(mutation: ViewMutationRecord) {
941
+ return mutation.type != "characterData" && mutation.type != "selection"
929
942
  }
930
943
 
931
944
  slice(from: number, to: number, view: EditorView) {
@@ -987,9 +1000,9 @@ class CustomNodeViewDesc extends NodeViewDesc {
987
1000
  this.spec.deselectNode ? this.spec.deselectNode() : super.deselectNode()
988
1001
  }
989
1002
 
990
- setSelection(anchor: number, head: number, root: Document | ShadowRoot, force: boolean) {
991
- this.spec.setSelection ? this.spec.setSelection(anchor, head, root)
992
- : super.setSelection(anchor, head, root, force)
1003
+ setSelection(anchor: number, head: number, view: EditorView, force: boolean) {
1004
+ this.spec.setSelection ? this.spec.setSelection(anchor, head, view.root)
1005
+ : super.setSelection(anchor, head, view, force)
993
1006
  }
994
1007
 
995
1008
  destroy() {
@@ -1001,7 +1014,7 @@ class CustomNodeViewDesc extends NodeViewDesc {
1001
1014
  return this.spec.stopEvent ? this.spec.stopEvent(event) : false
1002
1015
  }
1003
1016
 
1004
- ignoreMutation(mutation: MutationRecord) {
1017
+ ignoreMutation(mutation: ViewMutationRecord) {
1005
1018
  return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation)
1006
1019
  }
1007
1020
  }