prosemirror-view 1.42.6 → 1.42.7

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,19 @@
1
+ ## 1.42.7 (2026-10-22)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue where some kinds of changes inside inline nodes with content were inappropriately treated as backspace events.
6
+
7
+ Fix some selection issues in a shadow root on Safari by working around the relevant browser bugs.
8
+
9
+ Improve cursor motion across non-text in blocks that don't have the same text direction as the editor as a whole.
10
+
11
+ Fix an issue where decoration sets with lots of node decorations inside a single parent would require quadratic time to update.
12
+
13
+ Fix a regression in `posAtDOM` (and DOM selection reading) when the position is outside the nearest node's content DOM, but not directly in its outer element.
14
+
15
+ Avoid a selection read in DOM flushing when the editor doesn't have focus.
16
+
1
17
  ## 1.42.6 (2026-09-25)
2
18
 
3
19
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -754,6 +754,7 @@ var ViewDesc = function () {
754
754
  }, {
755
755
  key: "localPosFromDOM",
756
756
  value: function localPosFromDOM(dom, offset, bias) {
757
+ var _this = this;
757
758
  if (this.contentDOM && contains(this.contentDOM, dom)) {
758
759
  if (bias < 0) {
759
760
  var domBefore, desc;
@@ -781,7 +782,14 @@ var ViewDesc = function () {
781
782
  if (dom == this.dom && this.contentDOM) {
782
783
  atEnd = offset > domIndex(this.contentDOM);
783
784
  } else if (this.contentDOM && this.contentDOM != this.dom && this.dom.contains(this.contentDOM)) {
784
- atEnd = !!(dom.compareDocumentPosition(this.contentDOM) & 4);
785
+ var rel = dom.compareDocumentPosition(this.contentDOM);
786
+ if (rel & 16) {
787
+ atEnd = offset > Array.from(dom.childNodes).findIndex(function (n) {
788
+ return n.contains(_this.contentDOM);
789
+ });
790
+ } else {
791
+ atEnd = !(rel & 4);
792
+ }
785
793
  } else if (this.dom.firstChild) {
786
794
  if (offset == 0) for (var search = dom;; search = search.parentNode) {
787
795
  if (search == this.dom) {
@@ -1102,7 +1110,7 @@ var WidgetViewDesc = function (_ViewDesc) {
1102
1110
  _inherits(WidgetViewDesc, _ViewDesc);
1103
1111
  var _super = _createSuper(WidgetViewDesc);
1104
1112
  function WidgetViewDesc(parent, widget, view, pos) {
1105
- var _this;
1113
+ var _this2;
1106
1114
  _classCallCheck(this, WidgetViewDesc);
1107
1115
  var self,
1108
1116
  dom = widget.type.toDOM;
@@ -1116,11 +1124,11 @@ var WidgetViewDesc = function (_ViewDesc) {
1116
1124
  if (!elt.hasAttribute("contenteditable")) elt.contentEditable = "false";
1117
1125
  elt.classList.add("ProseMirror-widget");
1118
1126
  }
1119
- _this = _super.call(this, parent, [], dom, null);
1120
- _this.widget = widget;
1121
- _this.widget = widget;
1122
- self = _assertThisInitialized(_this);
1123
- return _this;
1127
+ _this2 = _super.call(this, parent, [], dom, null);
1128
+ _this2.widget = widget;
1129
+ _this2.widget = widget;
1130
+ self = _assertThisInitialized(_this2);
1131
+ return _this2;
1124
1132
  }
1125
1133
  _createClass(WidgetViewDesc, [{
1126
1134
  key: "matchesWidget",
@@ -1173,12 +1181,12 @@ var CompositionViewDesc = function (_ViewDesc2) {
1173
1181
  _inherits(CompositionViewDesc, _ViewDesc2);
1174
1182
  var _super2 = _createSuper(CompositionViewDesc);
1175
1183
  function CompositionViewDesc(parent, dom, textDOM, text) {
1176
- var _this2;
1184
+ var _this3;
1177
1185
  _classCallCheck(this, CompositionViewDesc);
1178
- _this2 = _super2.call(this, parent, [], dom, null);
1179
- _this2.textDOM = textDOM;
1180
- _this2.text = text;
1181
- return _this2;
1186
+ _this3 = _super2.call(this, parent, [], dom, null);
1187
+ _this3.textDOM = textDOM;
1188
+ _this3.text = text;
1189
+ return _this3;
1182
1190
  }
1183
1191
  _createClass(CompositionViewDesc, [{
1184
1192
  key: "size",
@@ -1211,12 +1219,12 @@ var MarkViewDesc = function (_ViewDesc3) {
1211
1219
  _inherits(MarkViewDesc, _ViewDesc3);
1212
1220
  var _super3 = _createSuper(MarkViewDesc);
1213
1221
  function MarkViewDesc(parent, mark, dom, contentDOM, spec) {
1214
- var _this3;
1222
+ var _this4;
1215
1223
  _classCallCheck(this, MarkViewDesc);
1216
- _this3 = _super3.call(this, parent, [], dom, contentDOM);
1217
- _this3.mark = mark;
1218
- _this3.spec = spec;
1219
- return _this3;
1224
+ _this4 = _super3.call(this, parent, [], dom, contentDOM);
1225
+ _this4.mark = mark;
1226
+ _this4.spec = spec;
1227
+ return _this4;
1220
1228
  }
1221
1229
  _createClass(MarkViewDesc, [{
1222
1230
  key: "parseRule",
@@ -1282,19 +1290,19 @@ var NodeViewDesc = function (_ViewDesc4) {
1282
1290
  _inherits(NodeViewDesc, _ViewDesc4);
1283
1291
  var _super4 = _createSuper(NodeViewDesc);
1284
1292
  function NodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM) {
1285
- var _this4;
1293
+ var _this5;
1286
1294
  _classCallCheck(this, NodeViewDesc);
1287
- _this4 = _super4.call(this, parent, [], dom, contentDOM);
1288
- _this4.node = node;
1289
- _this4.outerDeco = outerDeco;
1290
- _this4.innerDeco = innerDeco;
1291
- _this4.nodeDOM = nodeDOM;
1292
- return _this4;
1295
+ _this5 = _super4.call(this, parent, [], dom, contentDOM);
1296
+ _this5.node = node;
1297
+ _this5.outerDeco = outerDeco;
1298
+ _this5.innerDeco = innerDeco;
1299
+ _this5.nodeDOM = nodeDOM;
1300
+ return _this5;
1293
1301
  }
1294
1302
  _createClass(NodeViewDesc, [{
1295
1303
  key: "parseRule",
1296
1304
  value: function parseRule(addedNodes) {
1297
- var _this5 = this;
1305
+ var _this6 = this;
1298
1306
  if (this.node.type.spec.reparseInView) return null;
1299
1307
  var rule = {
1300
1308
  node: this.node.type.name,
@@ -1303,7 +1311,7 @@ var NodeViewDesc = function (_ViewDesc4) {
1303
1311
  if (this.node.type.whitespace == "pre") rule.preserveWhitespace = "full";
1304
1312
  if (!this.contentDOM) {
1305
1313
  rule.getContent = function () {
1306
- return _this5.node.content;
1314
+ return _this6.node.content;
1307
1315
  };
1308
1316
  } else if (!this.contentLost) {
1309
1317
  rule.contentElement = this.contentDOM;
@@ -1317,7 +1325,7 @@ var NodeViewDesc = function (_ViewDesc4) {
1317
1325
  }
1318
1326
  if (!rule.contentElement) {
1319
1327
  var found = addedNodes && addedNodes.find(function (n) {
1320
- return isElt(n) && addedNodes.indexOf(n.parentNode) < 0 && _this5.dom.contains(n);
1328
+ return isElt(n) && addedNodes.indexOf(n.parentNode) < 0 && _this6.dom.contains(n);
1321
1329
  });
1322
1330
  if (found) rule.contentElement = found;else rule.getContent = function () {
1323
1331
  return prosemirrorModel.Fragment.empty;
@@ -1344,7 +1352,7 @@ var NodeViewDesc = function (_ViewDesc4) {
1344
1352
  }, {
1345
1353
  key: "updateChildren",
1346
1354
  value: function updateChildren(view, pos) {
1347
- var _this6 = this;
1355
+ var _this7 = this;
1348
1356
  var inline = this.node.inlineContent,
1349
1357
  off = pos;
1350
1358
  var composition = view.composing ? this.localCompositionInfo(view, pos) : null;
@@ -1354,7 +1362,7 @@ var NodeViewDesc = function (_ViewDesc4) {
1354
1362
  var addBuffers = this.node.inlineContent,
1355
1363
  atBufferPos = addBuffers && this.node.isBlock;
1356
1364
  iterDeco(this.node, this.innerDeco, function (widget, i, insideNode) {
1357
- if (widget.spec.marks) updater.syncToMarks(widget.spec.marks, inline, view, i);else if (widget.type.side >= 0 && !insideNode) updater.syncToMarks(i == _this6.node.childCount ? prosemirrorModel.Mark.none : _this6.node.child(i).marks, inline, view, i);
1365
+ if (widget.spec.marks) updater.syncToMarks(widget.spec.marks, inline, view, i);else if (widget.type.side >= 0 && !insideNode) updater.syncToMarks(i == _this7.node.childCount ? prosemirrorModel.Mark.none : _this7.node.child(i).marks, inline, view, i);
1358
1366
  updater.placeWidget(widget, view, off);
1359
1367
  }, function (child, outerDeco, innerDeco, i) {
1360
1368
  updater.syncToMarks(child.marks, inline, view, i);
@@ -1620,11 +1628,11 @@ var CustomNodeViewDesc = function (_NodeViewDesc2) {
1620
1628
  _inherits(CustomNodeViewDesc, _NodeViewDesc2);
1621
1629
  var _super7 = _createSuper(CustomNodeViewDesc);
1622
1630
  function CustomNodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, spec) {
1623
- var _this7;
1631
+ var _this8;
1624
1632
  _classCallCheck(this, CustomNodeViewDesc);
1625
- _this7 = _super7.call(this, parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM);
1626
- _this7.spec = spec;
1627
- return _this7;
1633
+ _this8 = _super7.call(this, parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM);
1634
+ _this8.spec = spec;
1635
+ return _this8;
1628
1636
  }
1629
1637
  _createClass(CustomNodeViewDesc, [{
1630
1638
  key: "update",
@@ -2391,7 +2399,7 @@ function selectHorizontally(view, dir, mods) {
2391
2399
  var next = moveSelectionBlock(view.state, dir);
2392
2400
  if (next && next instanceof prosemirrorState.NodeSelection) return apply(view, next);
2393
2401
  return false;
2394
- } else if (!(mac && mods.indexOf("m") > -1)) {
2402
+ } else if (!mac || mods.indexOf("m") < 0) {
2395
2403
  var _$head = sel.$head,
2396
2404
  _node = _$head.textOffset ? null : dir < 0 ? _$head.nodeBefore : _$head.nodeAfter,
2397
2405
  desc;
@@ -2542,15 +2550,13 @@ function setSelFocus(view, node, offset) {
2542
2550
  offset = before.nodeValue.length;
2543
2551
  }
2544
2552
  }
2545
- var sel = view.domSelection();
2553
+ var sel = view.domSelection(),
2554
+ range = view.domSelectionRange();
2546
2555
  if (!sel) return;
2547
- if (selectionCollapsed(sel)) {
2548
- var range = document.createRange();
2549
- range.setEnd(node, offset);
2550
- range.setStart(node, offset);
2551
- sel.removeAllRanges();
2552
- sel.addRange(range);
2556
+ if (selectionCollapsed(range)) {
2557
+ sel.collapse(node, offset);
2553
2558
  } else if (sel.extend) {
2559
+ sel.collapse(range.anchorNode, range.anchorOffset);
2554
2560
  sel.extend(node, offset);
2555
2561
  }
2556
2562
  view.domObserver.setCurSelection();
@@ -2574,7 +2580,12 @@ function findDirection(view, pos) {
2574
2580
  if (_mid > coords.top && _mid < coords.bottom && Math.abs(after.left - coords.left) > 1) return after.left > coords.left ? "ltr" : "rtl";
2575
2581
  }
2576
2582
  }
2577
- var computed = getComputedStyle(view.dom).direction;
2583
+ var block = view.dom;
2584
+ for (var d = $pos.depth; d > 0; d--) if ($pos.node(d).isBlock) {
2585
+ block = view.nodeDOM($pos.before(d)) || block;
2586
+ break;
2587
+ }
2588
+ var computed = getComputedStyle(block).direction;
2578
2589
  return computed == "rtl" ? "rtl" : "ltr";
2579
2590
  }
2580
2591
  function selectVertically(view, dir, mods) {
@@ -3245,16 +3256,16 @@ var LeftMouseDown = function (_MouseDown) {
3245
3256
  _inherits(LeftMouseDown, _MouseDown);
3246
3257
  var _super8 = _createSuper(LeftMouseDown);
3247
3258
  function LeftMouseDown(view, pos, event, flushed) {
3248
- var _this8;
3259
+ var _this9;
3249
3260
  _classCallCheck(this, LeftMouseDown);
3250
- _this8 = _super8.call(this, view);
3251
- _this8.pos = pos;
3252
- _this8.event = event;
3253
- _this8.flushed = flushed;
3254
- _this8.delayedSelectionSync = false;
3255
- _this8.startDoc = view.state.doc;
3256
- _this8.selectNode = !!event[selectNodeModifier];
3257
- _this8.allowDefault = event.shiftKey;
3261
+ _this9 = _super8.call(this, view);
3262
+ _this9.pos = pos;
3263
+ _this9.event = event;
3264
+ _this9.flushed = flushed;
3265
+ _this9.delayedSelectionSync = false;
3266
+ _this9.startDoc = view.state.doc;
3267
+ _this9.selectNode = !!event[selectNodeModifier];
3268
+ _this9.allowDefault = event.shiftKey;
3258
3269
  var targetNode, targetPos;
3259
3270
  if (pos.inside > -1) {
3260
3271
  targetNode = view.state.doc.nodeAt(pos.inside);
@@ -3266,29 +3277,29 @@ var LeftMouseDown = function (_MouseDown) {
3266
3277
  }
3267
3278
  var target = flushed ? null : event.target;
3268
3279
  var targetDesc = target ? view.docView.nearestDesc(target, true) : null;
3269
- _this8.target = targetDesc && isElt(targetDesc.nodeDOM) ? targetDesc.nodeDOM : null;
3280
+ _this9.target = targetDesc && isElt(targetDesc.nodeDOM) ? targetDesc.nodeDOM : null;
3270
3281
  var selection = view.state.selection;
3271
- if (event.button == 0 && (targetNode.type.spec.draggable && targetNode.type.spec.selectable !== false || selection instanceof prosemirrorState.NodeSelection && selection.from <= targetPos && selection.to > targetPos)) _this8.mightDrag = {
3282
+ if (event.button == 0 && (targetNode.type.spec.draggable && targetNode.type.spec.selectable !== false || selection instanceof prosemirrorState.NodeSelection && selection.from <= targetPos && selection.to > targetPos)) _this9.mightDrag = {
3272
3283
  node: targetNode,
3273
3284
  pos: targetPos,
3274
- addAttr: !!(_this8.target && !_this8.target.draggable),
3275
- setUneditable: !!(_this8.target && gecko && !_this8.target.hasAttribute("contentEditable"))
3285
+ addAttr: !!(_this9.target && !_this9.target.draggable),
3286
+ setUneditable: !!(_this9.target && gecko && !_this9.target.hasAttribute("contentEditable"))
3276
3287
  };
3277
- if (_this8.target && _this8.mightDrag && (_this8.mightDrag.addAttr || _this8.mightDrag.setUneditable)) {
3278
- _this8.view.domObserver.stop();
3279
- if (_this8.mightDrag.addAttr) _this8.target.draggable = true;
3280
- if (_this8.mightDrag.setUneditable) setTimeout(function () {
3281
- if (_this8.view.input.mouseDown == _assertThisInitialized(_this8)) _this8.target.setAttribute("contentEditable", "false");
3288
+ if (_this9.target && _this9.mightDrag && (_this9.mightDrag.addAttr || _this9.mightDrag.setUneditable)) {
3289
+ _this9.view.domObserver.stop();
3290
+ if (_this9.mightDrag.addAttr) _this9.target.draggable = true;
3291
+ if (_this9.mightDrag.setUneditable) setTimeout(function () {
3292
+ if (_this9.view.input.mouseDown == _assertThisInitialized(_this9)) _this9.target.setAttribute("contentEditable", "false");
3282
3293
  }, 20);
3283
- _this8.view.domObserver.start();
3294
+ _this9.view.domObserver.start();
3284
3295
  }
3285
3296
  setSelectionOrigin(view, "pointer");
3286
- return _this8;
3297
+ return _this9;
3287
3298
  }
3288
3299
  _createClass(LeftMouseDown, [{
3289
3300
  key: "done",
3290
3301
  value: function done() {
3291
- var _this9 = this;
3302
+ var _this10 = this;
3292
3303
  _get(_getPrototypeOf(LeftMouseDown.prototype), "done", this).call(this);
3293
3304
  if (this.mightDrag && this.target) {
3294
3305
  this.view.domObserver.stop();
@@ -3297,7 +3308,7 @@ var LeftMouseDown = function (_MouseDown) {
3297
3308
  this.view.domObserver.start();
3298
3309
  }
3299
3310
  if (this.delayedSelectionSync) setTimeout(function () {
3300
- if (!_this9.view.isDestroyed) selectionToDOM(_this9.view);
3311
+ if (!_this10.view.isDestroyed) selectionToDOM(_this10.view);
3301
3312
  });
3302
3313
  }
3303
3314
  }, {
@@ -3345,12 +3356,12 @@ var TripleClickDrag = function (_MouseDown2) {
3345
3356
  _inherits(TripleClickDrag, _MouseDown2);
3346
3357
  var _super9 = _createSuper(TripleClickDrag);
3347
3358
  function TripleClickDrag(view, startSelection) {
3348
- var _this10;
3359
+ var _this11;
3349
3360
  _classCallCheck(this, TripleClickDrag);
3350
- _this10 = _super9.call(this, view);
3351
- _this10.startSelection = startSelection;
3352
- _this10.startDoc = view.state.doc;
3353
- return _this10;
3361
+ _this11 = _super9.call(this, view);
3362
+ _this11.startSelection = startSelection;
3363
+ _this11.startDoc = view.state.doc;
3364
+ return _this11;
3354
3365
  }
3355
3366
  _createClass(TripleClickDrag, [{
3356
3367
  key: "move",
@@ -3720,6 +3731,27 @@ function compareObjs(a, b) {
3720
3731
  for (var _p in b) if (!(_p in a)) return false;
3721
3732
  return true;
3722
3733
  }
3734
+ var FragmentCursor = function () {
3735
+ function FragmentCursor(frag) {
3736
+ _classCallCheck(this, FragmentCursor);
3737
+ this.frag = frag;
3738
+ this.index = 0;
3739
+ this.off = 0;
3740
+ }
3741
+ _createClass(FragmentCursor, [{
3742
+ key: "nodeAt",
3743
+ value: function nodeAt(off) {
3744
+ while (this.off <= off && this.index < this.frag.childCount) {
3745
+ var next = this.frag.child(this.index);
3746
+ if (this.off == off) return next;
3747
+ this.off += next.nodeSize;
3748
+ this.index++;
3749
+ }
3750
+ return null;
3751
+ }
3752
+ }]);
3753
+ return FragmentCursor;
3754
+ }();
3723
3755
  var WidgetType = function () {
3724
3756
  function WidgetType(toDOM, spec) {
3725
3757
  _classCallCheck(this, WidgetType);
@@ -3804,12 +3836,9 @@ var NodeType = function () {
3804
3836
  }
3805
3837
  }, {
3806
3838
  key: "valid",
3807
- value: function valid(node, span) {
3808
- var _node$content$findInd = node.content.findIndex(span.from),
3809
- index = _node$content$findInd.index,
3810
- offset = _node$content$findInd.offset,
3811
- child;
3812
- return offset == span.from && !(child = node.child(index)).isText && offset + child.nodeSize == span.to;
3839
+ value: function valid(node, span, cursor) {
3840
+ var found = cursor.nodeAt(span.from);
3841
+ return !!found && !found.isText && span.from + found.nodeSize == span.to;
3813
3842
  }
3814
3843
  }, {
3815
3844
  key: "eq",
@@ -3917,9 +3946,12 @@ var DecorationSet = function () {
3917
3946
  key: "mapInner",
3918
3947
  value: function mapInner(mapping, node, offset, oldOffset, options) {
3919
3948
  var newLocal;
3920
- for (var i = 0; i < this.local.length; i++) {
3921
- var mapped = this.local[i].map(mapping, offset, oldOffset);
3922
- if (mapped && mapped.type.valid(node, mapped)) (newLocal || (newLocal = [])).push(mapped);else if (options.onRemove) options.onRemove(this.local[i].spec);
3949
+ if (this.local.length) {
3950
+ var cur = new FragmentCursor(node.content);
3951
+ for (var i = 0; i < this.local.length; i++) {
3952
+ var mapped = this.local[i].map(mapping, offset, oldOffset);
3953
+ if (mapped && mapped.type.valid(node, mapped, cur)) (newLocal || (newLocal = [])).push(mapped);else if (options.onRemove) options.onRemove(this.local[i].spec);
3954
+ }
3923
3955
  }
3924
3956
  if (this.children.length) return mapChildren(this.children, newLocal || [], mapping, node, offset, oldOffset, options);else return newLocal ? new DecorationSet(newLocal.sort(byPos), none) : empty;
3925
3957
  }
@@ -3933,20 +3965,23 @@ var DecorationSet = function () {
3933
3965
  }, {
3934
3966
  key: "addInner",
3935
3967
  value: function addInner(doc, decorations, offset) {
3936
- var _this11 = this;
3968
+ var _this12 = this;
3937
3969
  var children,
3938
3970
  childIndex = 0;
3939
3971
  doc.forEach(function (childNode, childOffset) {
3940
3972
  var baseOffset = childOffset + offset,
3941
3973
  found;
3942
3974
  if (!(found = takeSpansForNode(decorations, childNode, baseOffset))) return;
3943
- if (!children) children = _this11.children.slice();
3975
+ if (!children) children = _this12.children.slice();
3944
3976
  while (childIndex < children.length && children[childIndex] < childOffset) childIndex += 3;
3945
3977
  if (children[childIndex] == childOffset) children[childIndex + 2] = children[childIndex + 2].addInner(childNode, found, baseOffset + 1);else children.splice(childIndex, 0, childOffset, childOffset + childNode.nodeSize, buildTree(found, childNode, baseOffset + 1, noSpec));
3946
3978
  childIndex += 3;
3947
3979
  });
3948
3980
  var local = moveSpans(childIndex ? withoutNulls(decorations) : decorations, -offset);
3949
- for (var i = 0; i < local.length; i++) if (!local[i].type.valid(doc, local[i])) local.splice(i--, 1);
3981
+ if (local.length) {
3982
+ var cur = new FragmentCursor(doc.content);
3983
+ for (var i = 0; i < local.length; i++) if (!local[i].type.valid(doc, local[i], cur)) local.splice(i--, 1);
3984
+ }
3950
3985
  return new DecorationSet(local.length ? this.local.concat(local).sort(byPos) : this.local, children || this.children);
3951
3986
  }
3952
3987
  }, {
@@ -4172,9 +4207,9 @@ function mapChildren(oldChildren, newLocal, mapping, node, offset, oldOffset, op
4172
4207
  }
4173
4208
  var to = mapping.map(oldChildren[_i11 + 1] + oldOffset, -1),
4174
4209
  toLocal = to - offset;
4175
- var _node$content$findInd2 = node.content.findIndex(fromLocal),
4176
- index = _node$content$findInd2.index,
4177
- childOffset = _node$content$findInd2.offset;
4210
+ var _node$content$findInd = node.content.findIndex(fromLocal),
4211
+ index = _node$content$findInd.index,
4212
+ childOffset = _node$content$findInd.offset;
4178
4213
  var childNode = node.maybeChild(index);
4179
4214
  if (childNode && childOffset == fromLocal && childOffset + childNode.nodeSize == toLocal) {
4180
4215
  var mapped = children[_i11 + 2].mapInner(mapping, childNode, from + 1, oldChildren[_i11] + oldOffset + 1, options);
@@ -4255,9 +4290,12 @@ function buildTree(spans, node, offset, options) {
4255
4290
  }
4256
4291
  });
4257
4292
  var locals = moveSpans(hasNulls ? withoutNulls(spans) : spans, -offset).sort(byPos);
4258
- for (var i = 0; i < locals.length; i++) if (!locals[i].type.valid(node, locals[i])) {
4259
- if (options.onRemove) options.onRemove(locals[i].spec);
4260
- locals.splice(i--, 1);
4293
+ if (locals.length) {
4294
+ var cur = new FragmentCursor(node.content);
4295
+ for (var i = 0; i < locals.length; i++) if (!locals[i].type.valid(node, locals[i], cur)) {
4296
+ if (options.onRemove) options.onRemove(locals[i].spec);
4297
+ locals.splice(i--, 1);
4298
+ }
4261
4299
  }
4262
4300
  return locals.length || children.length ? new DecorationSet(locals, children) : empty;
4263
4301
  }
@@ -4342,7 +4380,7 @@ var SelectionState = function () {
4342
4380
  }();
4343
4381
  var DOMObserver = function () {
4344
4382
  function DOMObserver(view, handleDOMChange) {
4345
- var _this12 = this;
4383
+ var _this13 = this;
4346
4384
  _classCallCheck(this, DOMObserver);
4347
4385
  this.view = view;
4348
4386
  this.handleDOMChange = handleDOMChange;
@@ -4354,28 +4392,28 @@ var DOMObserver = function () {
4354
4392
  this.suppressingSelectionUpdates = false;
4355
4393
  this.lastChangedTextNode = null;
4356
4394
  this.observer = window.MutationObserver && new window.MutationObserver(function (mutations) {
4357
- for (var i = 0; i < mutations.length; i++) _this12.queue.push(mutations[i]);
4395
+ for (var i = 0; i < mutations.length; i++) _this13.queue.push(mutations[i]);
4358
4396
  if (ie && ie_version <= 11 && mutations.some(function (m) {
4359
4397
  return m.type == "childList" && m.removedNodes.length || m.type == "characterData" && m.oldValue.length > m.target.nodeValue.length;
4360
4398
  })) {
4361
- _this12.flushSoon();
4399
+ _this13.flushSoon();
4362
4400
  } else if (safari && view.composing && mutations.some(function (m) {
4363
4401
  return m.type == "childList" && m.target.nodeName == "TR";
4364
4402
  })) {
4365
4403
  view.input.badSafariComposition = true;
4366
- _this12.flushSoon();
4404
+ _this13.flushSoon();
4367
4405
  } else {
4368
- _this12.flush();
4406
+ _this13.flush();
4369
4407
  }
4370
4408
  });
4371
4409
  if (useCharData) {
4372
4410
  this.onCharData = function (e) {
4373
- _this12.queue.push({
4411
+ _this13.queue.push({
4374
4412
  target: e.target,
4375
4413
  type: "characterData",
4376
4414
  oldValue: e.prevValue
4377
4415
  });
4378
- _this12.flushSoon();
4416
+ _this13.flushSoon();
4379
4417
  };
4380
4418
  }
4381
4419
  this.onSelectionChange = this.onSelectionChange.bind(this);
@@ -4383,10 +4421,10 @@ var DOMObserver = function () {
4383
4421
  _createClass(DOMObserver, [{
4384
4422
  key: "flushSoon",
4385
4423
  value: function flushSoon() {
4386
- var _this13 = this;
4424
+ var _this14 = this;
4387
4425
  if (this.flushingSoon < 0) this.flushingSoon = window.setTimeout(function () {
4388
- _this13.flushingSoon = -1;
4389
- _this13.flush();
4426
+ _this14.flushingSoon = -1;
4427
+ _this14.flush();
4390
4428
  }, 20);
4391
4429
  }
4392
4430
  }, {
@@ -4411,13 +4449,13 @@ var DOMObserver = function () {
4411
4449
  }, {
4412
4450
  key: "stop",
4413
4451
  value: function stop() {
4414
- var _this14 = this;
4452
+ var _this15 = this;
4415
4453
  if (this.observer) {
4416
4454
  var take = this.observer.takeRecords();
4417
4455
  if (take.length) {
4418
4456
  for (var i = 0; i < take.length; i++) this.queue.push(take[i]);
4419
4457
  window.setTimeout(function () {
4420
- return _this14.flush();
4458
+ return _this15.flush();
4421
4459
  }, 20);
4422
4460
  }
4423
4461
  this.observer.disconnect();
@@ -4438,10 +4476,10 @@ var DOMObserver = function () {
4438
4476
  }, {
4439
4477
  key: "suppressSelectionUpdates",
4440
4478
  value: function suppressSelectionUpdates() {
4441
- var _this15 = this;
4479
+ var _this16 = this;
4442
4480
  this.suppressingSelectionUpdates = true;
4443
4481
  setTimeout(function () {
4444
- return _this15.suppressingSelectionUpdates = false;
4482
+ return _this16.suppressingSelectionUpdates = false;
4445
4483
  }, 50);
4446
4484
  }
4447
4485
  }, {
@@ -4506,8 +4544,12 @@ var DOMObserver = function () {
4506
4544
  if (!view.docView || this.flushingSoon > -1) return;
4507
4545
  var mutations = this.pendingRecords();
4508
4546
  if (mutations.length) this.queue = [];
4509
- var sel = view.domSelectionRange();
4510
- var newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel);
4547
+ var selRange,
4548
+ newSel = false;
4549
+ if (!this.suppressingSelectionUpdates && hasFocusAndSelection(view)) {
4550
+ selRange = view.domSelectionRange();
4551
+ newSel = !this.currentSelection.eq(selRange) && !this.ignoreSelectionChange(selRange);
4552
+ }
4511
4553
  var from = -1,
4512
4554
  to = -1,
4513
4555
  typeOver = false,
@@ -4575,10 +4617,10 @@ var DOMObserver = function () {
4575
4617
  }
4576
4618
  }
4577
4619
  var readSel = null;
4578
- if (from < 0 && newSel && view.input.lastFocus > Date.now() - 200 && Math.max(view.input.lastTouch, view.input.lastClick.time) < Date.now() - 300 && selectionCollapsed(sel) && (readSel = selectionFromDOM(view)) && readSel.eq(prosemirrorState.Selection.near(view.state.doc.resolve(0), 1))) {
4620
+ if (from < 0 && newSel && view.input.lastFocus > Date.now() - 200 && Math.max(view.input.lastTouch, view.input.lastClick.time) < Date.now() - 300 && selectionCollapsed(selRange) && (readSel = selectionFromDOM(view)) && readSel.eq(prosemirrorState.Selection.near(view.state.doc.resolve(0), 1))) {
4579
4621
  view.input.lastFocus = 0;
4580
4622
  selectionToDOM(view);
4581
- this.currentSelection.set(sel);
4623
+ this.currentSelection.set(selRange);
4582
4624
  view.scrollToSelection();
4583
4625
  } else if (from > -1 || newSel) {
4584
4626
  if (from > -1) {
@@ -4590,8 +4632,8 @@ var DOMObserver = function () {
4590
4632
  fixUpBadSafariComposition(view, added);
4591
4633
  }
4592
4634
  this.handleDOMChange(from, to, typeOver, added);
4593
- if (view.docView && view.docView.dirty) view.updateState(view.state);else if (!this.currentSelection.eq(sel)) selectionToDOM(view);
4594
- this.currentSelection.set(sel);
4635
+ if (view.docView && view.docView.dirty) view.updateState(view.state);else if (selRange && !this.currentSelection.eq(selRange)) selectionToDOM(view);
4636
+ if (selRange) this.currentSelection.set(selRange);
4595
4637
  }
4596
4638
  }
4597
4639
  }, {
@@ -4911,7 +4953,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
4911
4953
  view.input.lastIOSEnter = 0;
4912
4954
  return;
4913
4955
  }
4914
- if (view.state.selection.anchor > change.start && looksLikeBackspace(doc, change.start, change.endA, $from, $to) && view.someProp("handleKeyDown", function (f) {
4956
+ if (view.state.selection.anchor > change.start && (inlineChange ? change.endA > change.start && change.endB == change.start : looksLikeBlockJoin(doc, change.start, change.endA, $from, $to)) && view.someProp("handleKeyDown", function (f) {
4915
4957
  return f(view, keyEvent(8, "Backspace"));
4916
4958
  })) {
4917
4959
  if (android && chrome) view.domObserver.suppressSelectionUpdates();
@@ -5006,10 +5048,10 @@ function isMarkChange(cur, prev) {
5006
5048
  type: type
5007
5049
  };
5008
5050
  }
5009
- function looksLikeBackspace(old, start, end, $newStart, $newEnd) {
5051
+ function looksLikeBlockJoin(old, start, end, $newStart, $newEnd) {
5010
5052
  if (end - start <= $newEnd.pos - $newStart.pos || skipClosingAndOpening($newStart, true, false) < $newEnd.pos) return false;
5011
5053
  var $start = old.resolve(start);
5012
- if (!$newStart.parent.isTextblock) {
5054
+ if (!$newStart.parent.inlineContent) {
5013
5055
  var after = $start.nodeAfter;
5014
5056
  return after != null && end == start + after.nodeSize;
5015
5057
  }
@@ -5076,7 +5118,7 @@ var __parseFromClipboard = parseFromClipboard;
5076
5118
  var __endComposition = endComposition;
5077
5119
  var EditorView = function () {
5078
5120
  function EditorView(place, props) {
5079
- var _this16 = this;
5121
+ var _this17 = this;
5080
5122
  _classCallCheck(this, EditorView);
5081
5123
  this._root = null;
5082
5124
  this.focused = false;
@@ -5104,7 +5146,7 @@ var EditorView = function () {
5104
5146
  this.nodeViews = buildNodeViews(this);
5105
5147
  this.docView = docViewDesc(this.state.doc, computeDocDeco(this), viewDecorations(this), this.dom, this);
5106
5148
  this.domObserver = new DOMObserver(this, function (from, to, typeOver, added) {
5107
- return readDOMChange(_this16, from, to, typeOver, added);
5149
+ return readDOMChange(_this17, from, to, typeOver, added);
5108
5150
  });
5109
5151
  this.domObserver.start();
5110
5152
  initInput(this);
@@ -5218,10 +5260,10 @@ var EditorView = function () {
5218
5260
  }, {
5219
5261
  key: "scrollToSelection",
5220
5262
  value: function scrollToSelection() {
5221
- var _this17 = this;
5263
+ var _this18 = this;
5222
5264
  var startDOM = this.domSelectionRange().focusNode;
5223
5265
  if (!startDOM || !contains(this.dom, startDOM)) ;else if (this.someProp("handleScrollToSelection", function (f) {
5224
- return f(_this17);
5266
+ return f(_this18);
5225
5267
  })) ;else if (this.state.selection instanceof prosemirrorState.NodeSelection) {
5226
5268
  var target = this.docView.domAfterPos(this.state.selection.from);
5227
5269
  if (isElt(target)) scrollRectIntoView(this, target.getBoundingClientRect(), startDOM);
@@ -5312,7 +5354,7 @@ var EditorView = function () {
5312
5354
  }, {
5313
5355
  key: "root",
5314
5356
  get: function get() {
5315
- var _this18 = this;
5357
+ var _this19 = this;
5316
5358
  var cached = this._root;
5317
5359
  if (cached == null) {
5318
5360
  var _loop5 = function _loop5(search) {
@@ -5321,7 +5363,7 @@ var EditorView = function () {
5321
5363
  return search.ownerDocument.getSelection();
5322
5364
  };
5323
5365
  return {
5324
- v: _this18._root = search
5366
+ v: _this19._root = search
5325
5367
  };
5326
5368
  }
5327
5369
  },
package/dist/index.js CHANGED
@@ -850,7 +850,13 @@ class ViewDesc {
850
850
  atEnd = offset > domIndex(this.contentDOM);
851
851
  }
852
852
  else if (this.contentDOM && this.contentDOM != this.dom && this.dom.contains(this.contentDOM)) {
853
- atEnd = !!(dom.compareDocumentPosition(this.contentDOM) & 4);
853
+ let rel = dom.compareDocumentPosition(this.contentDOM);
854
+ if (rel & 16) { // this.contentDOM is inside dom
855
+ atEnd = offset > Array.from(dom.childNodes).findIndex(n => n.contains(this.contentDOM));
856
+ }
857
+ else { // Check if after or before
858
+ atEnd = !(rel & 4);
859
+ }
854
860
  }
855
861
  else if (this.dom.firstChild) {
856
862
  if (offset == 0)
@@ -2483,7 +2489,7 @@ function selectHorizontally(view, dir, mods) {
2483
2489
  return apply(view, next);
2484
2490
  return false;
2485
2491
  }
2486
- else if (!(mac && mods.indexOf("m") > -1)) {
2492
+ else if (!mac || mods.indexOf("m") < 0) {
2487
2493
  let $head = sel.$head, node = $head.textOffset ? null : dir < 0 ? $head.nodeBefore : $head.nodeAfter, desc;
2488
2494
  if (!node || node.isText)
2489
2495
  return false;
@@ -2672,17 +2678,14 @@ function setSelFocus(view, node, offset) {
2672
2678
  offset = before.nodeValue.length;
2673
2679
  }
2674
2680
  }
2675
- let sel = view.domSelection();
2681
+ let sel = view.domSelection(), range = view.domSelectionRange();
2676
2682
  if (!sel)
2677
2683
  return;
2678
- if (selectionCollapsed(sel)) {
2679
- let range = document.createRange();
2680
- range.setEnd(node, offset);
2681
- range.setStart(node, offset);
2682
- sel.removeAllRanges();
2683
- sel.addRange(range);
2684
+ if (selectionCollapsed(range)) {
2685
+ sel.collapse(node, offset);
2684
2686
  }
2685
2687
  else if (sel.extend) {
2688
+ sel.collapse(range.anchorNode, range.anchorOffset);
2686
2689
  sel.extend(node, offset);
2687
2690
  }
2688
2691
  view.domObserver.setCurSelection();
@@ -2710,7 +2713,13 @@ function findDirection(view, pos) {
2710
2713
  return after.left > coords.left ? "ltr" : "rtl";
2711
2714
  }
2712
2715
  }
2713
- let computed = getComputedStyle(view.dom).direction;
2716
+ let block = view.dom;
2717
+ for (let d = $pos.depth; d > 0; d--)
2718
+ if ($pos.node(d).isBlock) {
2719
+ block = view.nodeDOM($pos.before(d)) || block;
2720
+ break;
2721
+ }
2722
+ let computed = getComputedStyle(block).direction;
2714
2723
  return computed == "rtl" ? "rtl" : "ltr";
2715
2724
  }
2716
2725
  // Check whether vertical selection motion would involve node
@@ -3983,6 +3992,23 @@ function compareObjs(a, b) {
3983
3992
  return false;
3984
3993
  return true;
3985
3994
  }
3995
+ class FragmentCursor {
3996
+ constructor(frag) {
3997
+ this.frag = frag;
3998
+ this.index = 0;
3999
+ this.off = 0;
4000
+ }
4001
+ nodeAt(off) {
4002
+ while (this.off <= off && this.index < this.frag.childCount) {
4003
+ let next = this.frag.child(this.index);
4004
+ if (this.off == off)
4005
+ return next;
4006
+ this.off += next.nodeSize;
4007
+ this.index++;
4008
+ }
4009
+ return null;
4010
+ }
4011
+ }
3986
4012
  class WidgetType {
3987
4013
  constructor(toDOM, spec) {
3988
4014
  this.toDOM = toDOM;
@@ -4038,9 +4064,9 @@ class NodeType {
4038
4064
  return null;
4039
4065
  return new Decoration(from.pos - offset, to.pos - offset, this);
4040
4066
  }
4041
- valid(node, span) {
4042
- let { index, offset } = node.content.findIndex(span.from), child;
4043
- return offset == span.from && !(child = node.child(index)).isText && offset + child.nodeSize == span.to;
4067
+ valid(node, span, cursor) {
4068
+ let found = cursor.nodeAt(span.from);
4069
+ return !!found && !found.isText && span.from + found.nodeSize == span.to;
4044
4070
  }
4045
4071
  eq(other) {
4046
4072
  return this == other ||
@@ -4197,12 +4223,15 @@ class DecorationSet {
4197
4223
  */
4198
4224
  mapInner(mapping, node, offset, oldOffset, options) {
4199
4225
  let newLocal;
4200
- for (let i = 0; i < this.local.length; i++) {
4201
- let mapped = this.local[i].map(mapping, offset, oldOffset);
4202
- if (mapped && mapped.type.valid(node, mapped))
4203
- (newLocal || (newLocal = [])).push(mapped);
4204
- else if (options.onRemove)
4205
- options.onRemove(this.local[i].spec);
4226
+ if (this.local.length) {
4227
+ let cur = new FragmentCursor(node.content);
4228
+ for (let i = 0; i < this.local.length; i++) {
4229
+ let mapped = this.local[i].map(mapping, offset, oldOffset);
4230
+ if (mapped && mapped.type.valid(node, mapped, cur))
4231
+ (newLocal || (newLocal = [])).push(mapped);
4232
+ else if (options.onRemove)
4233
+ options.onRemove(this.local[i].spec);
4234
+ }
4206
4235
  }
4207
4236
  if (this.children.length)
4208
4237
  return mapChildren(this.children, newLocal || [], mapping, node, offset, oldOffset, options);
@@ -4239,9 +4268,12 @@ class DecorationSet {
4239
4268
  childIndex += 3;
4240
4269
  });
4241
4270
  let local = moveSpans(childIndex ? withoutNulls(decorations) : decorations, -offset);
4242
- for (let i = 0; i < local.length; i++)
4243
- if (!local[i].type.valid(doc, local[i]))
4244
- local.splice(i--, 1);
4271
+ if (local.length) {
4272
+ let cur = new FragmentCursor(doc.content);
4273
+ for (let i = 0; i < local.length; i++)
4274
+ if (!local[i].type.valid(doc, local[i], cur))
4275
+ local.splice(i--, 1);
4276
+ }
4245
4277
  return new DecorationSet(local.length ? this.local.concat(local).sort(byPos) : this.local, children || this.children);
4246
4278
  }
4247
4279
  /**
@@ -4584,12 +4616,15 @@ function buildTree(spans, node, offset, options) {
4584
4616
  }
4585
4617
  });
4586
4618
  let locals = moveSpans(hasNulls ? withoutNulls(spans) : spans, -offset).sort(byPos);
4587
- for (let i = 0; i < locals.length; i++)
4588
- if (!locals[i].type.valid(node, locals[i])) {
4589
- if (options.onRemove)
4590
- options.onRemove(locals[i].spec);
4591
- locals.splice(i--, 1);
4592
- }
4619
+ if (locals.length) {
4620
+ let cur = new FragmentCursor(node.content);
4621
+ for (let i = 0; i < locals.length; i++)
4622
+ if (!locals[i].type.valid(node, locals[i], cur)) {
4623
+ if (options.onRemove)
4624
+ options.onRemove(locals[i].spec);
4625
+ locals.splice(i--, 1);
4626
+ }
4627
+ }
4593
4628
  return locals.length || children.length ? new DecorationSet(locals, children) : empty;
4594
4629
  }
4595
4630
  // Used to sort decorations so that ones with a low start position
@@ -4822,8 +4857,11 @@ class DOMObserver {
4822
4857
  let mutations = this.pendingRecords();
4823
4858
  if (mutations.length)
4824
4859
  this.queue = [];
4825
- let sel = view.domSelectionRange();
4826
- let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel);
4860
+ let selRange, newSel = false;
4861
+ if (!this.suppressingSelectionUpdates && hasFocusAndSelection(view)) {
4862
+ selRange = view.domSelectionRange();
4863
+ newSel = !this.currentSelection.eq(selRange) && !this.ignoreSelectionChange(selRange);
4864
+ }
4827
4865
  let from = -1, to = -1, typeOver = false, added = [];
4828
4866
  if (view.editable) {
4829
4867
  for (let i = 0; i < mutations.length; i++) {
@@ -4878,11 +4916,11 @@ class DOMObserver {
4878
4916
  // the state
4879
4917
  if (from < 0 && newSel && view.input.lastFocus > Date.now() - 200 &&
4880
4918
  Math.max(view.input.lastTouch, view.input.lastClick.time) < Date.now() - 300 &&
4881
- selectionCollapsed(sel) && (readSel = selectionFromDOM(view)) &&
4919
+ selectionCollapsed(selRange) && (readSel = selectionFromDOM(view)) &&
4882
4920
  readSel.eq(Selection.near(view.state.doc.resolve(0), 1))) {
4883
4921
  view.input.lastFocus = 0;
4884
4922
  selectionToDOM(view);
4885
- this.currentSelection.set(sel);
4923
+ this.currentSelection.set(selRange);
4886
4924
  view.scrollToSelection();
4887
4925
  }
4888
4926
  else if (from > -1 || newSel) {
@@ -4897,9 +4935,10 @@ class DOMObserver {
4897
4935
  this.handleDOMChange(from, to, typeOver, added);
4898
4936
  if (view.docView && view.docView.dirty)
4899
4937
  view.updateState(view.state);
4900
- else if (!this.currentSelection.eq(sel))
4938
+ else if (selRange && !this.currentSelection.eq(selRange))
4901
4939
  selectionToDOM(view);
4902
- this.currentSelection.set(sel);
4940
+ if (selRange)
4941
+ this.currentSelection.set(selRange);
4903
4942
  }
4904
4943
  }
4905
4944
  registerMutation(mut, added) {
@@ -5231,7 +5270,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
5231
5270
  }
5232
5271
  // Same for backspace
5233
5272
  if (view.state.selection.anchor > change.start &&
5234
- looksLikeBackspace(doc, change.start, change.endA, $from, $to) &&
5273
+ (inlineChange ? change.endA > change.start && change.endB == change.start
5274
+ : looksLikeBlockJoin(doc, change.start, change.endA, $from, $to)) &&
5235
5275
  view.someProp("handleKeyDown", f => f(view, keyEvent(8, "Backspace")))) {
5236
5276
  if (android && chrome)
5237
5277
  view.domObserver.suppressSelectionUpdates(); // #820
@@ -5352,7 +5392,7 @@ function isMarkChange(cur, prev) {
5352
5392
  if (Fragment.from(updated).eq(cur))
5353
5393
  return { mark, type };
5354
5394
  }
5355
- function looksLikeBackspace(old, start, end, $newStart, $newEnd) {
5395
+ function looksLikeBlockJoin(old, start, end, $newStart, $newEnd) {
5356
5396
  if ( // The content must have shrunk
5357
5397
  end - start <= $newEnd.pos - $newStart.pos ||
5358
5398
  // newEnd must point directly at or after the end of the block that newStart points into
@@ -5360,7 +5400,7 @@ function looksLikeBackspace(old, start, end, $newStart, $newEnd) {
5360
5400
  return false;
5361
5401
  let $start = old.resolve(start);
5362
5402
  // Handle the case where, rather than joining blocks, the change just removed an entire block
5363
- if (!$newStart.parent.isTextblock) {
5403
+ if (!$newStart.parent.inlineContent) {
5364
5404
  let after = $start.nodeAfter;
5365
5405
  return after != null && end == start + after.nodeSize;
5366
5406
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prosemirror-view",
3
- "version": "1.42.6",
3
+ "version": "1.42.7",
4
4
  "description": "ProseMirror's view component",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -30,7 +30,7 @@ function selectHorizontally(view: EditorView, dir: number, mods: string) {
30
30
  let next = moveSelectionBlock(view.state, dir)
31
31
  if (next && (next instanceof NodeSelection)) return apply(view, next)
32
32
  return false
33
- } else if (!(browser.mac && mods.indexOf("m") > -1)) {
33
+ } else if (!browser.mac || mods.indexOf("m") < 0) {
34
34
  let $head = sel.$head, node = $head.textOffset ? null : dir < 0 ? $head.nodeBefore : $head.nodeAfter, desc
35
35
  if (!node || node.isText) return false
36
36
  let nodePos = dir < 0 ? $head.pos - node.nodeSize : $head.pos
@@ -198,15 +198,12 @@ function setSelFocus(view: EditorView, node: Node, offset: number) {
198
198
  }
199
199
  }
200
200
 
201
- let sel = view.domSelection()
201
+ let sel = view.domSelection(), range = view.domSelectionRange()
202
202
  if (!sel) return
203
- if (selectionCollapsed(sel)) {
204
- let range = document.createRange()
205
- range.setEnd(node, offset)
206
- range.setStart(node, offset)
207
- sel.removeAllRanges()
208
- sel.addRange(range)
203
+ if (selectionCollapsed(range)) {
204
+ sel.collapse(node, offset)
209
205
  } else if (sel.extend) {
206
+ sel.collapse(range.anchorNode, range.anchorOffset)
210
207
  sel.extend(node, offset)
211
208
  }
212
209
  view.domObserver.setCurSelection()
@@ -234,7 +231,12 @@ function findDirection(view: EditorView, pos: number): "rtl" | "ltr" {
234
231
  return after.left > coords.left ? "ltr" : "rtl"
235
232
  }
236
233
  }
237
- let computed = getComputedStyle(view.dom).direction
234
+ let block: Element = view.dom
235
+ for (let d = $pos.depth; d > 0; d--) if ($pos.node(d).isBlock) {
236
+ block = view.nodeDOM($pos.before(d)) as Element || block
237
+ break
238
+ }
239
+ let computed = getComputedStyle(block).direction
238
240
  return computed == "rtl" ? "rtl" : "ltr"
239
241
  }
240
242
 
package/src/decoration.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {Node, Mark} from "prosemirror-model"
1
+ import {Node, Mark, Fragment} from "prosemirror-model"
2
2
  import {Mappable, Mapping} from "prosemirror-transform"
3
3
  import {EditorView} from "./index"
4
4
  import {DOMNode} from "./dom"
@@ -10,10 +10,25 @@ function compareObjs(a: {[prop: string]: any}, b: {[prop: string]: any}) {
10
10
  return true
11
11
  }
12
12
 
13
+ class FragmentCursor {
14
+ index = 0
15
+ off = 0
16
+ constructor(readonly frag: Fragment) {}
17
+ nodeAt(off: number) {
18
+ while (this.off <= off && this.index < this.frag.childCount) {
19
+ let next = this.frag.child(this.index)
20
+ if (this.off == off) return next
21
+ this.off += next.nodeSize
22
+ this.index++
23
+ }
24
+ return null
25
+ }
26
+ }
27
+
13
28
  export interface DecorationType {
14
29
  spec: any
15
30
  map(mapping: Mappable, span: Decoration, offset: number, oldOffset: number): Decoration | null
16
- valid(node: Node, span: Decoration): boolean
31
+ valid(node: Node, span: Decoration, cur: FragmentCursor): boolean
17
32
  eq(other: DecorationType): boolean
18
33
  destroy(dom: DOMNode): void
19
34
  }
@@ -88,9 +103,9 @@ export class NodeType implements DecorationType {
88
103
  return new Decoration(from.pos - offset, to.pos - offset, this)
89
104
  }
90
105
 
91
- valid(node: Node, span: Decoration): boolean {
92
- let {index, offset} = node.content.findIndex(span.from), child
93
- return offset == span.from && !(child = node.child(index)).isText && offset + child.nodeSize == span.to
106
+ valid(node: Node, span: Decoration, cursor: FragmentCursor): boolean {
107
+ let found = cursor.nodeAt(span.from)
108
+ return !!found && !found.isText && span.from + found.nodeSize == span.to
94
109
  }
95
110
 
96
111
  eq(other: DecorationType): boolean {
@@ -346,10 +361,13 @@ export class DecorationSet implements DecorationSource {
346
361
  onRemove?: (decorationSpec: any) => void
347
362
  }) {
348
363
  let newLocal: Decoration[] | undefined
349
- for (let i = 0; i < this.local.length; i++) {
350
- let mapped = this.local[i].map(mapping, offset, oldOffset)
351
- if (mapped && mapped.type.valid(node, mapped)) (newLocal || (newLocal = [])).push(mapped)
352
- else if (options.onRemove) options.onRemove(this.local[i].spec)
364
+ if (this.local.length) {
365
+ let cur = new FragmentCursor(node.content)
366
+ for (let i = 0; i < this.local.length; i++) {
367
+ let mapped = this.local[i].map(mapping, offset, oldOffset)
368
+ if (mapped && mapped.type.valid(node, mapped, cur)) (newLocal || (newLocal = [])).push(mapped)
369
+ else if (options.onRemove) options.onRemove(this.local[i].spec)
370
+ }
353
371
  }
354
372
 
355
373
  if (this.children.length)
@@ -384,7 +402,10 @@ export class DecorationSet implements DecorationSource {
384
402
  })
385
403
 
386
404
  let local = moveSpans(childIndex ? withoutNulls(decorations) : decorations, -offset)
387
- for (let i = 0; i < local.length; i++) if (!local[i].type.valid(doc, local[i])) local.splice(i--, 1)
405
+ if (local.length) {
406
+ let cur = new FragmentCursor(doc.content)
407
+ for (let i = 0; i < local.length; i++) if (!local[i].type.valid(doc, local[i], cur)) local.splice(i--, 1)
408
+ }
388
409
 
389
410
  return new DecorationSet(local.length ? this.local.concat(local).sort(byPos) : this.local,
390
411
  children || this.children)
@@ -727,9 +748,12 @@ function buildTree(
727
748
  }
728
749
  })
729
750
  let locals = moveSpans(hasNulls ? withoutNulls(spans) : spans, -offset).sort(byPos)
730
- for (let i = 0; i < locals.length; i++) if (!locals[i].type.valid(node, locals[i])) {
731
- if (options.onRemove) options.onRemove(locals[i].spec)
732
- locals.splice(i--, 1)
751
+ if (locals.length) {
752
+ let cur = new FragmentCursor(node.content)
753
+ for (let i = 0; i < locals.length; i++) if (!locals[i].type.valid(node, locals[i], cur)) {
754
+ if (options.onRemove) options.onRemove(locals[i].spec)
755
+ locals.splice(i--, 1)
756
+ }
733
757
  }
734
758
  return locals.length || children.length ? new DecorationSet(locals, children) : empty
735
759
  }
package/src/domchange.ts CHANGED
@@ -187,7 +187,8 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
187
187
  }
188
188
  // Same for backspace
189
189
  if (view.state.selection.anchor > change.start &&
190
- looksLikeBackspace(doc, change.start, change.endA, $from, $to) &&
190
+ (inlineChange ? change.endA > change.start && change.endB == change.start
191
+ : looksLikeBlockJoin(doc, change.start, change.endA, $from, $to)) &&
191
192
  view.someProp("handleKeyDown", f => f(view, keyEvent(8, "Backspace")))) {
192
193
  if (browser.android && browser.chrome) view.domObserver.suppressSelectionUpdates() // #820
193
194
  return
@@ -303,7 +304,7 @@ function isMarkChange(cur: Fragment, prev: Fragment) {
303
304
  if (Fragment.from(updated).eq(cur)) return {mark, type}
304
305
  }
305
306
 
306
- function looksLikeBackspace(old: Node, start: number, end: number, $newStart: ResolvedPos, $newEnd: ResolvedPos) {
307
+ function looksLikeBlockJoin(old: Node, start: number, end: number, $newStart: ResolvedPos, $newEnd: ResolvedPos) {
307
308
  if (// The content must have shrunk
308
309
  end - start <= $newEnd.pos - $newStart.pos ||
309
310
  // newEnd must point directly at or after the end of the block that newStart points into
@@ -313,7 +314,7 @@ function looksLikeBackspace(old: Node, start: number, end: number, $newStart: Re
313
314
  let $start = old.resolve(start)
314
315
 
315
316
  // Handle the case where, rather than joining blocks, the change just removed an entire block
316
- if (!$newStart.parent.isTextblock) {
317
+ if (!$newStart.parent.inlineContent) {
317
318
  let after = $start.nodeAfter
318
319
  return after != null && end == start + after.nodeSize
319
320
  }
@@ -177,8 +177,11 @@ export class DOMObserver {
177
177
  let mutations = this.pendingRecords()
178
178
  if (mutations.length) this.queue = []
179
179
 
180
- let sel = view.domSelectionRange()
181
- let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel)
180
+ let selRange: DOMSelectionRange | undefined, newSel = false
181
+ if (!this.suppressingSelectionUpdates && hasFocusAndSelection(view)) {
182
+ selRange = view.domSelectionRange()
183
+ newSel = !this.currentSelection.eq(selRange) && !this.ignoreSelectionChange(selRange)
184
+ }
182
185
 
183
186
  let from = -1, to = -1, typeOver = false, added: Node[] = []
184
187
  if (view.editable) {
@@ -230,11 +233,11 @@ export class DOMObserver {
230
233
  // the state
231
234
  if (from < 0 && newSel && view.input.lastFocus > Date.now() - 200 &&
232
235
  Math.max(view.input.lastTouch, view.input.lastClick.time) < Date.now() - 300 &&
233
- selectionCollapsed(sel) && (readSel = selectionFromDOM(view)) &&
236
+ selectionCollapsed(selRange!) && (readSel = selectionFromDOM(view)) &&
234
237
  readSel.eq(Selection.near(view.state.doc.resolve(0), 1))) {
235
238
  view.input.lastFocus = 0
236
239
  selectionToDOM(view)
237
- this.currentSelection.set(sel)
240
+ this.currentSelection.set(selRange!)
238
241
  view.scrollToSelection()
239
242
  } else if (from > -1 || newSel) {
240
243
  if (from > -1) {
@@ -247,8 +250,8 @@ export class DOMObserver {
247
250
  }
248
251
  this.handleDOMChange(from, to, typeOver, added)
249
252
  if (view.docView && view.docView.dirty) view.updateState(view.state)
250
- else if (!this.currentSelection.eq(sel)) selectionToDOM(view)
251
- this.currentSelection.set(sel)
253
+ else if (selRange && !this.currentSelection.eq(selRange)) selectionToDOM(view)
254
+ if (selRange) this.currentSelection.set(selRange)
252
255
  }
253
256
  }
254
257
 
package/src/viewdesc.ts CHANGED
@@ -248,7 +248,12 @@ export class ViewDesc {
248
248
  if (dom == this.dom && this.contentDOM) {
249
249
  atEnd = offset > domIndex(this.contentDOM)
250
250
  } else if (this.contentDOM && this.contentDOM != this.dom && this.dom.contains(this.contentDOM)) {
251
- atEnd = !!(dom.compareDocumentPosition(this.contentDOM) & 4)
251
+ let rel = dom.compareDocumentPosition(this.contentDOM)
252
+ if (rel & 16) { // this.contentDOM is inside dom
253
+ atEnd = offset > Array.from(dom.childNodes).findIndex(n => n.contains(this.contentDOM))
254
+ } else { // Check if after or before
255
+ atEnd = !(rel & 4)
256
+ }
252
257
  } else if (this.dom.firstChild) {
253
258
  if (offset == 0) for (let search = dom;; search = search.parentNode!) {
254
259
  if (search == this.dom) { atEnd = false; break }