prosemirror-view 1.23.0 → 1.23.4

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,31 @@
1
+ ## 1.23.4 (2021-12-22)
2
+
3
+ ### Bug fixes
4
+
5
+ Improve the way the editor handles the mess of events produced when pressing Enter before a word that was just typed on Chrome + GBoard.
6
+
7
+ Fix an issue where compositions right before another instance of the composed text could cause the editor to crash.
8
+
9
+ Fix an issue where, if decorations changed during a pointer drag selection, the selection anchor might move around inappropriately.
10
+
11
+ ## 1.23.3 (2021-11-26)
12
+
13
+ ### Bug fixes
14
+
15
+ The clipboard parser will no longer drop trailing `<br>` nodes that appear in an inline parent element.
16
+
17
+ ## 1.23.2 (2021-11-19)
18
+
19
+ ### Bug fixes
20
+
21
+ Avoid some unnecessary node redraws when marks are present in sibling nodes.
22
+
23
+ ## 1.23.1 (2021-11-15)
24
+
25
+ ### Bug fixes
26
+
27
+ Restore accidentally reduced lookahead distance in view updating algorithm.
28
+
1
29
  ## 1.23.0 (2021-11-11)
2
30
 
3
31
  ### Bug fixes
package/dist/index.es.js CHANGED
@@ -1747,7 +1747,7 @@ var ViewTreeUpdater = function ViewTreeUpdater(top, lockedNode) {
1747
1747
  // Tracks whether anything was changed
1748
1748
  this.changed = false;
1749
1749
 
1750
- this.preMatch = preMatch(top.node.content, top.children);
1750
+ this.preMatch = preMatch(top.node.content, top);
1751
1751
  };
1752
1752
 
1753
1753
  // Destroy and remove the children between the given indices in
@@ -1808,19 +1808,16 @@ ViewTreeUpdater.prototype.syncToMarks = function syncToMarks (marks, inline, vie
1808
1808
  // Try to find a node desc matching the given data. Skip over it and
1809
1809
  // return true when successful.
1810
1810
  ViewTreeUpdater.prototype.findNodeMatch = function findNodeMatch (node, outerDeco, innerDeco, index) {
1811
- var children = this.top.children, found = -1;
1812
- if (index >= this.preMatch.index) {
1813
- for (var i = this.index; i < children.length; i++) {
1814
- if (this.preMatch.matched.get(children[i]) == index) {
1815
- if (children[i].matchesNode(node, outerDeco, innerDeco)) { found = i; }
1816
- break
1817
- }
1818
- }
1811
+ var found = -1, targetDesc;
1812
+ if (index >= this.preMatch.index &&
1813
+ (targetDesc = this.preMatch.matches[index - this.preMatch.index]).parent == this.top &&
1814
+ targetDesc.matchesNode(node, outerDeco, innerDeco)) {
1815
+ found = this.top.children.indexOf(targetDesc, this.index);
1819
1816
  } else {
1820
- for (var i$1 = this.index, e = Math.min(children.length, i$1 + 1); i$1 < e; i$1++) {
1821
- var child = children[i$1];
1817
+ for (var i = this.index, e = Math.min(this.top.children.length, i + 5); i < e; i++) {
1818
+ var child = this.top.children[i];
1822
1819
  if (child.matchesNode(node, outerDeco, innerDeco) && !this.preMatch.matched.has(child)) {
1823
- found = i$1;
1820
+ found = i;
1824
1821
  break
1825
1822
  }
1826
1823
  }
@@ -1929,22 +1926,44 @@ ViewTreeUpdater.prototype.addHackNode = function addHackNode (nodeName) {
1929
1926
  }
1930
1927
  };
1931
1928
 
1932
- // : (Fragment, [ViewDesc]) → {index: number, matched: Map<ViewDesc, number>}
1929
+ // : (Fragment, [ViewDesc]) → {index: number, matched: Map<ViewDesc, number>, matches: ViewDesc[]}
1933
1930
  // Iterate from the end of the fragment and array of descs to find
1934
1931
  // directly matching ones, in order to avoid overeagerly reusing those
1935
1932
  // for other nodes. Returns the fragment index of the first node that
1936
1933
  // is part of the sequence of matched nodes at the end of the
1937
1934
  // fragment.
1938
- function preMatch(frag, descs) {
1939
- var fI = frag.childCount, dI = descs.length, matched = new Map;
1940
- for (; fI > 0 && dI > 0; dI--) {
1941
- var desc = descs[dI - 1], node = desc.node;
1935
+ function preMatch(frag, parentDesc) {
1936
+ var curDesc = parentDesc, descI = curDesc.children.length;
1937
+ var fI = frag.childCount, matched = new Map, matches = [];
1938
+ outer: while (fI > 0) {
1939
+ var desc = (void 0);
1940
+ for (;;) {
1941
+ if (descI) {
1942
+ var next = curDesc.children[descI - 1];
1943
+ if (next instanceof MarkViewDesc) {
1944
+ curDesc = next;
1945
+ descI = next.children.length;
1946
+ } else {
1947
+ desc = next;
1948
+ descI--;
1949
+ break
1950
+ }
1951
+ } else if (curDesc == parentDesc) {
1952
+ break outer
1953
+ } else {
1954
+ // FIXME
1955
+ descI = curDesc.parent.children.indexOf(curDesc);
1956
+ curDesc = curDesc.parent;
1957
+ }
1958
+ }
1959
+ var node = desc.node;
1942
1960
  if (!node) { continue }
1943
1961
  if (node != frag.child(fI - 1)) { break }
1944
1962
  --fI;
1945
1963
  matched.set(desc, fI);
1964
+ matches.push(desc);
1946
1965
  }
1947
- return {index: fI, matched: matched}
1966
+ return {index: fI, matched: matched, matches: matches.reverse()}
1948
1967
  }
1949
1968
 
1950
1969
  function compareSide(a, b) { return a.type.side - b.type.side }
@@ -2058,8 +2077,8 @@ function findTextInFragment(frag, text, from, to) {
2058
2077
  if (!next.isText) { break }
2059
2078
  str += next.text;
2060
2079
  }
2061
- if (pos >= from) {
2062
- var found = str.lastIndexOf(text, to - childStart);
2080
+ if (pos >= from && childStart < to) {
2081
+ var found = str.lastIndexOf(text, to - childStart - 1);
2063
2082
  if (found >= 0 && found + text.length + childStart >= from)
2064
2083
  { return childStart + found }
2065
2084
  }
@@ -2130,9 +2149,13 @@ function selectionToDOM(view, force) {
2130
2149
  if (!editorOwnsSelection(view)) { return }
2131
2150
 
2132
2151
  if (!force && view.mouseDown && view.mouseDown.allowDefault) {
2133
- view.mouseDown.delayedSelectionSync = true;
2134
- view.domObserver.setCurSelection();
2135
- return
2152
+ var domSel = view.root.getSelection(), curSel = view.domObserver.currentSelection;
2153
+ if (domSel.anchorNode && isEquivalentPosition(domSel.anchorNode, domSel.anchorOffset,
2154
+ curSel.anchorNode, curSel.anchorOffset)) {
2155
+ view.mouseDown.delayedSelectionSync = true;
2156
+ view.domObserver.setCurSelection();
2157
+ return
2158
+ }
2136
2159
  }
2137
2160
 
2138
2161
  view.domObserver.disconnectSelection();
@@ -2979,7 +3002,8 @@ function parseFromClipboard(view, text, html, plainText, $context) {
2979
3002
  preserveWhitespace: !!(asText || sliceData),
2980
3003
  context: $context,
2981
3004
  ruleFromNode: function ruleFromNode(dom) {
2982
- if (dom.nodeName == "BR" && !dom.nextSibling) { return {ignore: true} }
3005
+ if (dom.nodeName == "BR" && !dom.nextSibling &&
3006
+ dom.parentNode && !inlineParents.test(dom.parentNode.nodeName)) { return {ignore: true} }
2983
3007
  }
2984
3008
  });
2985
3009
  }
@@ -3001,6 +3025,8 @@ function parseFromClipboard(view, text, html, plainText, $context) {
3001
3025
  return slice
3002
3026
  }
3003
3027
 
3028
+ var inlineParents = /^(a|abbr|acronym|b|cite|code|del|em|i|ins|kbd|label|output|q|ruby|s|samp|span|strong|sub|sup|time|u|tt|var)$/i;
3029
+
3004
3030
  // Takes a slice parsed with parseSlice, which means there hasn't been
3005
3031
  // any content-expression checking done on the top nodes, tries to
3006
3032
  // find a parent node in the current context that might fit the nodes,
@@ -3481,9 +3507,14 @@ function dispatchEvent(view, event) {
3481
3507
  editHandlers.keydown = function (view, event) {
3482
3508
  view.shiftKey = event.keyCode == 16 || event.shiftKey;
3483
3509
  if (inOrNearComposition(view, event)) { return }
3484
- if (event.keyCode != 229) { view.domObserver.forceFlush(); }
3485
3510
  view.lastKeyCode = event.keyCode;
3486
3511
  view.lastKeyCodeTime = Date.now();
3512
+ // Suppress enter key events on Chrome Android, because those tend
3513
+ // to be part of a confused sequence of composition events fired,
3514
+ // and handling them eagerly tends to corrupt the input.
3515
+ if (result.android && result.chrome && event.keyCode == 13) { return }
3516
+ if (event.keyCode != 229) { view.domObserver.forceFlush(); }
3517
+
3487
3518
  // On iOS, if we preventDefault enter key presses, the virtual
3488
3519
  // keyboard gets confused. So the hack here is to set a flag that
3489
3520
  // makes the DOM change code recognize that what just happens should
@@ -3868,6 +3899,7 @@ function timestampFromCustomEvent() {
3868
3899
  }
3869
3900
 
3870
3901
  function endComposition(view, forceUpdate) {
3902
+ if (result.android && view.domObserver.flushingSoon >= 0) { return }
3871
3903
  view.domObserver.forceFlush();
3872
3904
  clearComposition(view);
3873
3905
  if (forceUpdate || view.docView.dirty) {
@@ -3958,6 +3990,11 @@ function doPaste(view, text, html, e) {
3958
3990
  }
3959
3991
 
3960
3992
  editHandlers.paste = function (view, e) {
3993
+ // Handling paste from JavaScript during composition is very poorly
3994
+ // handled by browsers, so as a dodgy but preferable kludge, we just
3995
+ // let the browser do its native thing there, except on Android,
3996
+ // where the editor is almost always composing.
3997
+ if (view.composing && !result.android) { return }
3961
3998
  var data = brokenClipboardAPI ? null : e.clipboardData;
3962
3999
  if (data && doPaste(view, data.getData("text/plain"), data.getData("text/html"), e)) { e.preventDefault(); }
3963
4000
  else { capturePaste(view, e); }
@@ -4089,6 +4126,7 @@ handlers.beforeinput = function (view, event) {
4089
4126
  // Very specific hack to deal with backspace sometimes failing on
4090
4127
  // Chrome Android when after an uneditable node.
4091
4128
  if (result.chrome && result.android && event.inputType == "deleteContentBackward") {
4129
+ view.domObserver.flushSoon();
4092
4130
  var domChangeCount = view.domChangeCount;
4093
4131
  setTimeout(function () {
4094
4132
  if (view.domChangeCount != domChangeCount) { return } // Event already had some effect