@codemirror/view 6.3.0 → 6.3.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/CHANGELOG.md +12 -0
- package/dist/index.cjs +341 -260
- package/dist/index.js +341 -260
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -871,7 +871,7 @@ class WidgetView extends ContentView {
|
|
|
871
871
|
if (pos > 0 ? i == 0 : i == rects.length - 1 || rect.top < rect.bottom)
|
|
872
872
|
break;
|
|
873
873
|
}
|
|
874
|
-
return (
|
|
874
|
+
return flattenRect(rect, this.side > 0);
|
|
875
875
|
}
|
|
876
876
|
get isEditable() { return false; }
|
|
877
877
|
destroy() {
|
|
@@ -1024,7 +1024,6 @@ function inlineDOMAtPos(parent, pos) {
|
|
|
1024
1024
|
break;
|
|
1025
1025
|
off = end;
|
|
1026
1026
|
}
|
|
1027
|
-
// if (i) return DOMPos.after(children[i - 1].dom!)
|
|
1028
1027
|
for (let j = i; j > 0; j--) {
|
|
1029
1028
|
let prev = children[j - 1];
|
|
1030
1029
|
if (prev.dom.parentNode == dom)
|
|
@@ -1051,21 +1050,59 @@ function joinInlineInto(parent, view, open) {
|
|
|
1051
1050
|
parent.length += view.length;
|
|
1052
1051
|
}
|
|
1053
1052
|
function coordsInChildren(view, pos, side) {
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1053
|
+
if (!view.children.length)
|
|
1054
|
+
return fallbackRect(view);
|
|
1055
|
+
return (side <= 0 ? coordsInChildrenBefore : coordsInChildrenAfter)(view, pos);
|
|
1056
|
+
}
|
|
1057
|
+
function coordsInChildrenBefore(view, pos) {
|
|
1058
|
+
// Find the last leaf in the tree that touches pos and doesn't have getSide() > 0
|
|
1059
|
+
let found = null, foundPos = -1;
|
|
1060
|
+
function scan(view, pos) {
|
|
1061
|
+
for (let i = 0, off = 0; i < view.children.length && off <= pos; i++) {
|
|
1062
|
+
let child = view.children[i], end = off + child.length;
|
|
1063
|
+
if (end >= pos) {
|
|
1064
|
+
if (child.children.length) {
|
|
1065
|
+
if (scan(child, pos - off))
|
|
1066
|
+
return true;
|
|
1067
|
+
}
|
|
1068
|
+
else if (end >= pos) {
|
|
1069
|
+
if (end == pos && child.getSide() > 0)
|
|
1070
|
+
return true;
|
|
1071
|
+
found = child;
|
|
1072
|
+
foundPos = pos - off;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
off = end;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
scan(view, pos);
|
|
1079
|
+
return found ? found.coordsAt(Math.max(0, foundPos), -1) : coordsInChildrenAfter(view, pos);
|
|
1080
|
+
}
|
|
1081
|
+
function coordsInChildrenAfter(view, pos) {
|
|
1082
|
+
// Find the first leaf in the tree that touches pos and doesn't have getSide() < 0
|
|
1083
|
+
let found = null, foundPos = -1;
|
|
1084
|
+
function scan(view, pos) {
|
|
1085
|
+
for (let i = view.children.length - 1, off = view.length; i >= 0 && off >= pos; i--) {
|
|
1086
|
+
let child = view.children[i];
|
|
1087
|
+
off -= child.length;
|
|
1088
|
+
if (off <= pos) {
|
|
1089
|
+
if (child.children.length) {
|
|
1090
|
+
if (scan(child, pos - off))
|
|
1091
|
+
return true;
|
|
1092
|
+
}
|
|
1093
|
+
else if (off <= pos) {
|
|
1094
|
+
if (off == pos && child.getSide() < 0)
|
|
1095
|
+
return true;
|
|
1096
|
+
found = child;
|
|
1097
|
+
foundPos = pos - off;
|
|
1098
|
+
}
|
|
1063
1099
|
}
|
|
1064
|
-
let rect = child.coordsAt(Math.max(0, pos - off), side);
|
|
1065
|
-
return flatten && rect ? flattenRect(rect, side < 0) : rect;
|
|
1066
1100
|
}
|
|
1067
|
-
off = end;
|
|
1068
1101
|
}
|
|
1102
|
+
scan(view, pos);
|
|
1103
|
+
return found ? found.coordsAt(Math.max(0, foundPos), 1) : coordsInChildrenBefore(view, pos);
|
|
1104
|
+
}
|
|
1105
|
+
function fallbackRect(view) {
|
|
1069
1106
|
let last = view.dom.lastChild;
|
|
1070
1107
|
if (!last)
|
|
1071
1108
|
return view.dom.getBoundingClientRect();
|
|
@@ -1683,7 +1720,7 @@ class ContentBuilder {
|
|
|
1683
1720
|
this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, type));
|
|
1684
1721
|
}
|
|
1685
1722
|
else {
|
|
1686
|
-
let view = WidgetView.create(deco.widget || new NullWidget("span"), len, deco.startSide);
|
|
1723
|
+
let view = WidgetView.create(deco.widget || new NullWidget("span"), len, len ? 0 : deco.startSide);
|
|
1687
1724
|
let cursorBefore = this.atCursorPos && !view.isEditable && openStart <= active.length && (from < to || deco.startSide > 0);
|
|
1688
1725
|
let cursorAfter = !view.isEditable && (from < to || deco.startSide <= 0);
|
|
1689
1726
|
let line = this.getLine();
|
|
@@ -4876,7 +4913,7 @@ class ViewState {
|
|
|
4876
4913
|
refresh = true;
|
|
4877
4914
|
if (refresh || oracle.lineWrapping && Math.abs(contentWidth - this.contentDOMWidth) > oracle.charWidth) {
|
|
4878
4915
|
let { lineHeight, charWidth } = view.docView.measureTextSize();
|
|
4879
|
-
refresh = oracle.refresh(whiteSpace, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
|
|
4916
|
+
refresh = lineHeight > 0 && oracle.refresh(whiteSpace, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
|
|
4880
4917
|
if (refresh) {
|
|
4881
4918
|
view.docView.minWidth = 0;
|
|
4882
4919
|
result |= 8 /* UpdateFlag.Geometry */;
|
|
@@ -5020,8 +5057,10 @@ class ViewState {
|
|
|
5020
5057
|
let marginHeight = (margin / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
|
|
5021
5058
|
let top, bot;
|
|
5022
5059
|
if (target != null) {
|
|
5023
|
-
|
|
5024
|
-
|
|
5060
|
+
let targetFrac = findFraction(structure, target);
|
|
5061
|
+
let spaceFrac = ((this.visibleBottom - this.visibleTop) / 2 + marginHeight) / line.height;
|
|
5062
|
+
top = targetFrac - spaceFrac;
|
|
5063
|
+
bot = targetFrac + spaceFrac;
|
|
5025
5064
|
}
|
|
5026
5065
|
else {
|
|
5027
5066
|
top = (this.visibleTop - line.top - marginHeight) / line.height;
|
|
@@ -5031,14 +5070,16 @@ class ViewState {
|
|
|
5031
5070
|
viewTo = findPosition(structure, bot);
|
|
5032
5071
|
}
|
|
5033
5072
|
else {
|
|
5073
|
+
let totalWidth = structure.total * this.heightOracle.charWidth;
|
|
5074
|
+
let marginWidth = margin * this.heightOracle.charWidth;
|
|
5034
5075
|
let left, right;
|
|
5035
5076
|
if (target != null) {
|
|
5036
|
-
|
|
5037
|
-
|
|
5077
|
+
let targetFrac = findFraction(structure, target);
|
|
5078
|
+
let spaceFrac = ((this.pixelViewport.right - this.pixelViewport.left) / 2 + marginWidth) / totalWidth;
|
|
5079
|
+
left = targetFrac - spaceFrac;
|
|
5080
|
+
right = targetFrac + spaceFrac;
|
|
5038
5081
|
}
|
|
5039
5082
|
else {
|
|
5040
|
-
let totalWidth = structure.total * this.heightOracle.charWidth;
|
|
5041
|
-
let marginWidth = margin * this.heightOracle.charWidth;
|
|
5042
5083
|
left = (this.pixelViewport.left - marginWidth) / totalWidth;
|
|
5043
5084
|
right = (this.pixelViewport.right + marginWidth) / totalWidth;
|
|
5044
5085
|
}
|
|
@@ -5445,6 +5486,227 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
|
|
|
5445
5486
|
}
|
|
5446
5487
|
}, lightDarkIDs);
|
|
5447
5488
|
|
|
5489
|
+
class DOMChange {
|
|
5490
|
+
constructor(view, start, end, typeOver) {
|
|
5491
|
+
this.typeOver = typeOver;
|
|
5492
|
+
this.bounds = null;
|
|
5493
|
+
this.text = "";
|
|
5494
|
+
let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView;
|
|
5495
|
+
if (start > -1 && !view.state.readOnly && (this.bounds = view.docView.domBoundsAround(start, end, 0))) {
|
|
5496
|
+
let selPoints = iHead || iAnchor ? [] : selectionPoints(view);
|
|
5497
|
+
let reader = new DOMReader(selPoints, view.state);
|
|
5498
|
+
reader.readRange(this.bounds.startDOM, this.bounds.endDOM);
|
|
5499
|
+
this.text = reader.text;
|
|
5500
|
+
this.newSel = selectionFromPoints(selPoints, this.bounds.from);
|
|
5501
|
+
}
|
|
5502
|
+
else {
|
|
5503
|
+
let domSel = view.observer.selectionRange;
|
|
5504
|
+
let head = iHead && iHead.node == domSel.focusNode && iHead.offset == domSel.focusOffset ||
|
|
5505
|
+
!contains(view.contentDOM, domSel.focusNode)
|
|
5506
|
+
? view.state.selection.main.head
|
|
5507
|
+
: view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset);
|
|
5508
|
+
let anchor = iAnchor && iAnchor.node == domSel.anchorNode && iAnchor.offset == domSel.anchorOffset ||
|
|
5509
|
+
!contains(view.contentDOM, domSel.anchorNode)
|
|
5510
|
+
? view.state.selection.main.anchor
|
|
5511
|
+
: view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset);
|
|
5512
|
+
this.newSel = EditorSelection.single(anchor, head);
|
|
5513
|
+
}
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5516
|
+
function applyDOMChange(view, domChange) {
|
|
5517
|
+
let change;
|
|
5518
|
+
let { newSel } = domChange, sel = view.state.selection.main;
|
|
5519
|
+
if (domChange.bounds) {
|
|
5520
|
+
let { from, to } = domChange.bounds;
|
|
5521
|
+
let preferredPos = sel.from, preferredSide = null;
|
|
5522
|
+
// Prefer anchoring to end when Backspace is pressed (or, on
|
|
5523
|
+
// Android, when something was deleted)
|
|
5524
|
+
if (view.inputState.lastKeyCode === 8 && view.inputState.lastKeyTime > Date.now() - 100 ||
|
|
5525
|
+
browser.android && domChange.text.length < to - from) {
|
|
5526
|
+
preferredPos = sel.to;
|
|
5527
|
+
preferredSide = "end";
|
|
5528
|
+
}
|
|
5529
|
+
let diff = findDiff(view.state.doc.sliceString(from, to, LineBreakPlaceholder), domChange.text, preferredPos - from, preferredSide);
|
|
5530
|
+
if (diff) {
|
|
5531
|
+
// Chrome inserts two newlines when pressing shift-enter at the
|
|
5532
|
+
// end of a line. DomChange drops one of those.
|
|
5533
|
+
if (browser.chrome && view.inputState.lastKeyCode == 13 &&
|
|
5534
|
+
diff.toB == diff.from + 2 && domChange.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder)
|
|
5535
|
+
diff.toB--;
|
|
5536
|
+
change = { from: from + diff.from, to: from + diff.toA,
|
|
5537
|
+
insert: Text.of(domChange.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
|
|
5538
|
+
}
|
|
5539
|
+
}
|
|
5540
|
+
else if (newSel && (!view.hasFocus || !view.state.facet(editable) || newSel.main.eq(sel))) {
|
|
5541
|
+
newSel = null;
|
|
5542
|
+
}
|
|
5543
|
+
if (!change && !newSel)
|
|
5544
|
+
return false;
|
|
5545
|
+
if (!change && domChange.typeOver && !sel.empty && newSel && newSel.main.empty) {
|
|
5546
|
+
// Heuristic to notice typing over a selected character
|
|
5547
|
+
change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
|
|
5548
|
+
}
|
|
5549
|
+
else if (change && change.from >= sel.from && change.to <= sel.to &&
|
|
5550
|
+
(change.from != sel.from || change.to != sel.to) &&
|
|
5551
|
+
(sel.to - sel.from) - (change.to - change.from) <= 4) {
|
|
5552
|
+
// If the change is inside the selection and covers most of it,
|
|
5553
|
+
// assume it is a selection replace (with identical characters at
|
|
5554
|
+
// the start/end not included in the diff)
|
|
5555
|
+
change = {
|
|
5556
|
+
from: sel.from, to: sel.to,
|
|
5557
|
+
insert: view.state.doc.slice(sel.from, change.from).append(change.insert).append(view.state.doc.slice(change.to, sel.to))
|
|
5558
|
+
};
|
|
5559
|
+
}
|
|
5560
|
+
else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
|
|
5561
|
+
/^\. ?$/.test(change.insert.toString())) {
|
|
5562
|
+
// Detect insert-period-on-double-space Mac and Android behavior,
|
|
5563
|
+
// and transform it into a regular space insert.
|
|
5564
|
+
if (newSel && change.insert.length == 2)
|
|
5565
|
+
newSel = EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1);
|
|
5566
|
+
change = { from: sel.from, to: sel.to, insert: Text.of([" "]) };
|
|
5567
|
+
}
|
|
5568
|
+
if (change) {
|
|
5569
|
+
let startState = view.state;
|
|
5570
|
+
if (browser.ios && view.inputState.flushIOSKey(view))
|
|
5571
|
+
return true;
|
|
5572
|
+
// Android browsers don't fire reasonable key events for enter,
|
|
5573
|
+
// backspace, or delete. So this detects changes that look like
|
|
5574
|
+
// they're caused by those keys, and reinterprets them as key
|
|
5575
|
+
// events. (Some of these keys are also handled by beforeinput
|
|
5576
|
+
// events and the pendingAndroidKey mechanism, but that's not
|
|
5577
|
+
// reliable in all situations.)
|
|
5578
|
+
if (browser.android &&
|
|
5579
|
+
((change.from == sel.from && change.to == sel.to &&
|
|
5580
|
+
change.insert.length == 1 && change.insert.lines == 2 &&
|
|
5581
|
+
dispatchKey(view.contentDOM, "Enter", 13)) ||
|
|
5582
|
+
(change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
|
|
5583
|
+
dispatchKey(view.contentDOM, "Backspace", 8)) ||
|
|
5584
|
+
(change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
|
|
5585
|
+
dispatchKey(view.contentDOM, "Delete", 46))))
|
|
5586
|
+
return true;
|
|
5587
|
+
let text = change.insert.toString();
|
|
5588
|
+
if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
|
|
5589
|
+
return true;
|
|
5590
|
+
if (view.inputState.composing >= 0)
|
|
5591
|
+
view.inputState.composing++;
|
|
5592
|
+
let tr;
|
|
5593
|
+
if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
|
|
5594
|
+
(!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) &&
|
|
5595
|
+
view.inputState.composing < 0) {
|
|
5596
|
+
let before = sel.from < change.from ? startState.sliceDoc(sel.from, change.from) : "";
|
|
5597
|
+
let after = sel.to > change.to ? startState.sliceDoc(change.to, sel.to) : "";
|
|
5598
|
+
tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) + after));
|
|
5599
|
+
}
|
|
5600
|
+
else {
|
|
5601
|
+
let changes = startState.changes(change);
|
|
5602
|
+
let mainSel = newSel && !startState.selection.main.eq(newSel.main) && newSel.main.to <= changes.newLength
|
|
5603
|
+
? newSel.main : undefined;
|
|
5604
|
+
// Try to apply a composition change to all cursors
|
|
5605
|
+
if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
|
|
5606
|
+
change.to <= sel.to && change.to >= sel.to - 10) {
|
|
5607
|
+
let replaced = view.state.sliceDoc(change.from, change.to);
|
|
5608
|
+
let compositionRange = compositionSurroundingNode(view) || view.state.doc.lineAt(sel.head);
|
|
5609
|
+
let offset = sel.to - change.to, size = sel.to - sel.from;
|
|
5610
|
+
tr = startState.changeByRange(range => {
|
|
5611
|
+
if (range.from == sel.from && range.to == sel.to)
|
|
5612
|
+
return { changes, range: mainSel || range.map(changes) };
|
|
5613
|
+
let to = range.to - offset, from = to - replaced.length;
|
|
5614
|
+
if (range.to - range.from != size || view.state.sliceDoc(from, to) != replaced ||
|
|
5615
|
+
// Unfortunately, there's no way to make multiple
|
|
5616
|
+
// changes in the same node work without aborting
|
|
5617
|
+
// composition, so cursors in the composition range are
|
|
5618
|
+
// ignored.
|
|
5619
|
+
compositionRange && range.to >= compositionRange.from && range.from <= compositionRange.to)
|
|
5620
|
+
return { range };
|
|
5621
|
+
let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
|
|
5622
|
+
return {
|
|
5623
|
+
changes: rangeChanges,
|
|
5624
|
+
range: !mainSel ? range.map(rangeChanges) :
|
|
5625
|
+
EditorSelection.range(Math.max(0, mainSel.anchor + selOff), Math.max(0, mainSel.head + selOff))
|
|
5626
|
+
};
|
|
5627
|
+
});
|
|
5628
|
+
}
|
|
5629
|
+
else {
|
|
5630
|
+
tr = {
|
|
5631
|
+
changes,
|
|
5632
|
+
selection: mainSel && startState.selection.replaceRange(mainSel)
|
|
5633
|
+
};
|
|
5634
|
+
}
|
|
5635
|
+
}
|
|
5636
|
+
let userEvent = "input.type";
|
|
5637
|
+
if (view.composing) {
|
|
5638
|
+
userEvent += ".compose";
|
|
5639
|
+
if (view.inputState.compositionFirstChange) {
|
|
5640
|
+
userEvent += ".start";
|
|
5641
|
+
view.inputState.compositionFirstChange = false;
|
|
5642
|
+
}
|
|
5643
|
+
}
|
|
5644
|
+
view.dispatch(tr, { scrollIntoView: true, userEvent });
|
|
5645
|
+
return true;
|
|
5646
|
+
}
|
|
5647
|
+
else if (newSel && !newSel.main.eq(sel)) {
|
|
5648
|
+
let scrollIntoView = false, userEvent = "select";
|
|
5649
|
+
if (view.inputState.lastSelectionTime > Date.now() - 50) {
|
|
5650
|
+
if (view.inputState.lastSelectionOrigin == "select")
|
|
5651
|
+
scrollIntoView = true;
|
|
5652
|
+
userEvent = view.inputState.lastSelectionOrigin;
|
|
5653
|
+
}
|
|
5654
|
+
view.dispatch({ selection: newSel, scrollIntoView, userEvent });
|
|
5655
|
+
return true;
|
|
5656
|
+
}
|
|
5657
|
+
else {
|
|
5658
|
+
return false;
|
|
5659
|
+
}
|
|
5660
|
+
}
|
|
5661
|
+
function findDiff(a, b, preferredPos, preferredSide) {
|
|
5662
|
+
let minLen = Math.min(a.length, b.length);
|
|
5663
|
+
let from = 0;
|
|
5664
|
+
while (from < minLen && a.charCodeAt(from) == b.charCodeAt(from))
|
|
5665
|
+
from++;
|
|
5666
|
+
if (from == minLen && a.length == b.length)
|
|
5667
|
+
return null;
|
|
5668
|
+
let toA = a.length, toB = b.length;
|
|
5669
|
+
while (toA > 0 && toB > 0 && a.charCodeAt(toA - 1) == b.charCodeAt(toB - 1)) {
|
|
5670
|
+
toA--;
|
|
5671
|
+
toB--;
|
|
5672
|
+
}
|
|
5673
|
+
if (preferredSide == "end") {
|
|
5674
|
+
let adjust = Math.max(0, from - Math.min(toA, toB));
|
|
5675
|
+
preferredPos -= toA + adjust - from;
|
|
5676
|
+
}
|
|
5677
|
+
if (toA < from && a.length < b.length) {
|
|
5678
|
+
let move = preferredPos <= from && preferredPos >= toA ? from - preferredPos : 0;
|
|
5679
|
+
from -= move;
|
|
5680
|
+
toB = from + (toB - toA);
|
|
5681
|
+
toA = from;
|
|
5682
|
+
}
|
|
5683
|
+
else if (toB < from) {
|
|
5684
|
+
let move = preferredPos <= from && preferredPos >= toB ? from - preferredPos : 0;
|
|
5685
|
+
from -= move;
|
|
5686
|
+
toA = from + (toA - toB);
|
|
5687
|
+
toB = from;
|
|
5688
|
+
}
|
|
5689
|
+
return { from, toA, toB };
|
|
5690
|
+
}
|
|
5691
|
+
function selectionPoints(view) {
|
|
5692
|
+
let result = [];
|
|
5693
|
+
if (view.root.activeElement != view.contentDOM)
|
|
5694
|
+
return result;
|
|
5695
|
+
let { anchorNode, anchorOffset, focusNode, focusOffset } = view.observer.selectionRange;
|
|
5696
|
+
if (anchorNode) {
|
|
5697
|
+
result.push(new DOMPoint(anchorNode, anchorOffset));
|
|
5698
|
+
if (focusNode != anchorNode || focusOffset != anchorOffset)
|
|
5699
|
+
result.push(new DOMPoint(focusNode, focusOffset));
|
|
5700
|
+
}
|
|
5701
|
+
return result;
|
|
5702
|
+
}
|
|
5703
|
+
function selectionFromPoints(points, base) {
|
|
5704
|
+
if (points.length == 0)
|
|
5705
|
+
return null;
|
|
5706
|
+
let anchor = points[0].pos, head = points.length == 2 ? points[1].pos : anchor;
|
|
5707
|
+
return anchor > -1 && head > -1 ? EditorSelection.single(anchor + base, head + base) : null;
|
|
5708
|
+
}
|
|
5709
|
+
|
|
5448
5710
|
const observeOptions = {
|
|
5449
5711
|
childList: true,
|
|
5450
5712
|
characterData: true,
|
|
@@ -5456,10 +5718,8 @@ const observeOptions = {
|
|
|
5456
5718
|
// DOMCharacterDataModified there
|
|
5457
5719
|
const useCharData = browser.ie && browser.ie_version <= 11;
|
|
5458
5720
|
class DOMObserver {
|
|
5459
|
-
constructor(view
|
|
5721
|
+
constructor(view) {
|
|
5460
5722
|
this.view = view;
|
|
5461
|
-
this.onChange = onChange;
|
|
5462
|
-
this.onScrollChanged = onScrollChanged;
|
|
5463
5723
|
this.active = false;
|
|
5464
5724
|
// The known selection. Kept in our own object, as opposed to just
|
|
5465
5725
|
// directly accessing the selection because:
|
|
@@ -5474,6 +5734,7 @@ class DOMObserver {
|
|
|
5474
5734
|
this.resizeTimeout = -1;
|
|
5475
5735
|
this.queue = [];
|
|
5476
5736
|
this.delayedAndroidKey = null;
|
|
5737
|
+
this.flushingAndroidKey = -1;
|
|
5477
5738
|
this.lastChange = 0;
|
|
5478
5739
|
this.scrollTargets = [];
|
|
5479
5740
|
this.intersection = null;
|
|
@@ -5542,6 +5803,11 @@ class DOMObserver {
|
|
|
5542
5803
|
this.listenForScroll();
|
|
5543
5804
|
this.readSelectionRange();
|
|
5544
5805
|
}
|
|
5806
|
+
onScrollChanged(e) {
|
|
5807
|
+
this.view.inputState.runScrollHandlers(this.view, e);
|
|
5808
|
+
if (this.intersecting)
|
|
5809
|
+
this.view.measure();
|
|
5810
|
+
}
|
|
5545
5811
|
onScroll(e) {
|
|
5546
5812
|
if (this.intersecting)
|
|
5547
5813
|
this.flush(false);
|
|
@@ -5701,14 +5967,17 @@ class DOMObserver {
|
|
|
5701
5967
|
// them or, if that has no effect, dispatches the given key.
|
|
5702
5968
|
delayAndroidKey(key, keyCode) {
|
|
5703
5969
|
var _a;
|
|
5704
|
-
if (!this.delayedAndroidKey)
|
|
5705
|
-
|
|
5970
|
+
if (!this.delayedAndroidKey) {
|
|
5971
|
+
let flush = () => {
|
|
5706
5972
|
let key = this.delayedAndroidKey;
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5973
|
+
if (key) {
|
|
5974
|
+
this.clearDelayedAndroidKey();
|
|
5975
|
+
if (!this.flush() && key.force)
|
|
5976
|
+
dispatchKey(this.dom, key.key, key.keyCode);
|
|
5977
|
+
}
|
|
5978
|
+
};
|
|
5979
|
+
this.flushingAndroidKey = this.view.win.requestAnimationFrame(flush);
|
|
5980
|
+
}
|
|
5712
5981
|
// Since backspace beforeinput is sometimes signalled spuriously,
|
|
5713
5982
|
// Enter always takes precedence.
|
|
5714
5983
|
if (!this.delayedAndroidKey || key == "Enter")
|
|
@@ -5721,6 +5990,11 @@ class DOMObserver {
|
|
|
5721
5990
|
force: this.lastChange < Date.now() - 50 || !!((_a = this.delayedAndroidKey) === null || _a === void 0 ? void 0 : _a.force)
|
|
5722
5991
|
};
|
|
5723
5992
|
}
|
|
5993
|
+
clearDelayedAndroidKey() {
|
|
5994
|
+
this.win.cancelAnimationFrame(this.flushingAndroidKey);
|
|
5995
|
+
this.delayedAndroidKey = null;
|
|
5996
|
+
this.flushingAndroidKey = -1;
|
|
5997
|
+
}
|
|
5724
5998
|
flushSoon() {
|
|
5725
5999
|
if (this.delayedFlush < 0)
|
|
5726
6000
|
this.delayedFlush = this.view.win.requestAnimationFrame(() => { this.delayedFlush = -1; this.flush(); });
|
|
@@ -5755,6 +6029,17 @@ class DOMObserver {
|
|
|
5755
6029
|
}
|
|
5756
6030
|
return { from, to, typeOver };
|
|
5757
6031
|
}
|
|
6032
|
+
readChange() {
|
|
6033
|
+
let { from, to, typeOver } = this.processRecords();
|
|
6034
|
+
let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
|
|
6035
|
+
if (from < 0 && !newSel)
|
|
6036
|
+
return null;
|
|
6037
|
+
if (from > -1)
|
|
6038
|
+
this.lastChange = Date.now();
|
|
6039
|
+
this.view.inputState.lastFocusTime = 0;
|
|
6040
|
+
this.selectionChanged = false;
|
|
6041
|
+
return new DOMChange(this.view, from, to, typeOver);
|
|
6042
|
+
}
|
|
5758
6043
|
// Apply pending changes, if any
|
|
5759
6044
|
flush(readSelection = true) {
|
|
5760
6045
|
// Completely hold off flushing when pending keys are set—the code
|
|
@@ -5764,16 +6049,11 @@ class DOMObserver {
|
|
|
5764
6049
|
return false;
|
|
5765
6050
|
if (readSelection)
|
|
5766
6051
|
this.readSelectionRange();
|
|
5767
|
-
let
|
|
5768
|
-
|
|
5769
|
-
if (from < 0 && !newSel)
|
|
6052
|
+
let domChange = this.readChange();
|
|
6053
|
+
if (!domChange)
|
|
5770
6054
|
return false;
|
|
5771
|
-
if (from > -1)
|
|
5772
|
-
this.lastChange = Date.now();
|
|
5773
|
-
this.view.inputState.lastFocusTime = 0;
|
|
5774
|
-
this.selectionChanged = false;
|
|
5775
6055
|
let startState = this.view.state;
|
|
5776
|
-
let handled = this.
|
|
6056
|
+
let handled = applyDOMChange(this.view, domChange);
|
|
5777
6057
|
// The view wasn't updated
|
|
5778
6058
|
if (this.view.state == startState)
|
|
5779
6059
|
this.view.update([]);
|
|
@@ -5829,6 +6109,8 @@ class DOMObserver {
|
|
|
5829
6109
|
this.removeWindowListeners(this.win);
|
|
5830
6110
|
clearTimeout(this.parentCheck);
|
|
5831
6111
|
clearTimeout(this.resizeTimeout);
|
|
6112
|
+
this.win.cancelAnimationFrame(this.delayedFlush);
|
|
6113
|
+
this.win.cancelAnimationFrame(this.flushingAndroidKey);
|
|
5832
6114
|
}
|
|
5833
6115
|
}
|
|
5834
6116
|
function findChild(cView, dom, dir) {
|
|
@@ -5870,218 +6152,6 @@ function safariSelectionRangeHack(view) {
|
|
|
5870
6152
|
return { anchorNode, anchorOffset, focusNode, focusOffset };
|
|
5871
6153
|
}
|
|
5872
6154
|
|
|
5873
|
-
function applyDOMChange(view, start, end, typeOver) {
|
|
5874
|
-
let change, newSel;
|
|
5875
|
-
let sel = view.state.selection.main;
|
|
5876
|
-
if (start > -1) {
|
|
5877
|
-
let bounds = view.docView.domBoundsAround(start, end, 0);
|
|
5878
|
-
if (!bounds || view.state.readOnly)
|
|
5879
|
-
return false;
|
|
5880
|
-
let { from, to } = bounds;
|
|
5881
|
-
let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
|
|
5882
|
-
let reader = new DOMReader(selPoints, view.state);
|
|
5883
|
-
reader.readRange(bounds.startDOM, bounds.endDOM);
|
|
5884
|
-
let preferredPos = sel.from, preferredSide = null;
|
|
5885
|
-
// Prefer anchoring to end when Backspace is pressed (or, on
|
|
5886
|
-
// Android, when something was deleted)
|
|
5887
|
-
if (view.inputState.lastKeyCode === 8 && view.inputState.lastKeyTime > Date.now() - 100 ||
|
|
5888
|
-
browser.android && reader.text.length < to - from) {
|
|
5889
|
-
preferredPos = sel.to;
|
|
5890
|
-
preferredSide = "end";
|
|
5891
|
-
}
|
|
5892
|
-
let diff = findDiff(view.state.doc.sliceString(from, to, LineBreakPlaceholder), reader.text, preferredPos - from, preferredSide);
|
|
5893
|
-
if (diff) {
|
|
5894
|
-
// Chrome inserts two newlines when pressing shift-enter at the
|
|
5895
|
-
// end of a line. This drops one of those.
|
|
5896
|
-
if (browser.chrome && view.inputState.lastKeyCode == 13 &&
|
|
5897
|
-
diff.toB == diff.from + 2 && reader.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder)
|
|
5898
|
-
diff.toB--;
|
|
5899
|
-
change = { from: from + diff.from, to: from + diff.toA,
|
|
5900
|
-
insert: Text.of(reader.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
|
|
5901
|
-
}
|
|
5902
|
-
newSel = selectionFromPoints(selPoints, from);
|
|
5903
|
-
}
|
|
5904
|
-
else if (view.hasFocus || !view.state.facet(editable)) {
|
|
5905
|
-
let domSel = view.observer.selectionRange;
|
|
5906
|
-
let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView;
|
|
5907
|
-
let head = iHead && iHead.node == domSel.focusNode && iHead.offset == domSel.focusOffset ||
|
|
5908
|
-
!contains(view.contentDOM, domSel.focusNode)
|
|
5909
|
-
? view.state.selection.main.head
|
|
5910
|
-
: view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset);
|
|
5911
|
-
let anchor = iAnchor && iAnchor.node == domSel.anchorNode && iAnchor.offset == domSel.anchorOffset ||
|
|
5912
|
-
!contains(view.contentDOM, domSel.anchorNode)
|
|
5913
|
-
? view.state.selection.main.anchor
|
|
5914
|
-
: view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset);
|
|
5915
|
-
if (head != sel.head || anchor != sel.anchor)
|
|
5916
|
-
newSel = EditorSelection.single(anchor, head);
|
|
5917
|
-
}
|
|
5918
|
-
if (!change && !newSel)
|
|
5919
|
-
return false;
|
|
5920
|
-
if (!change && typeOver && !sel.empty && newSel && newSel.main.empty) {
|
|
5921
|
-
// Heuristic to notice typing over a selected character
|
|
5922
|
-
change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
|
|
5923
|
-
}
|
|
5924
|
-
else if (change && change.from >= sel.from && change.to <= sel.to &&
|
|
5925
|
-
(change.from != sel.from || change.to != sel.to) &&
|
|
5926
|
-
(sel.to - sel.from) - (change.to - change.from) <= 4) {
|
|
5927
|
-
// If the change is inside the selection and covers most of it,
|
|
5928
|
-
// assume it is a selection replace (with identical characters at
|
|
5929
|
-
// the start/end not included in the diff)
|
|
5930
|
-
change = {
|
|
5931
|
-
from: sel.from, to: sel.to,
|
|
5932
|
-
insert: view.state.doc.slice(sel.from, change.from).append(change.insert).append(view.state.doc.slice(change.to, sel.to))
|
|
5933
|
-
};
|
|
5934
|
-
}
|
|
5935
|
-
else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
|
|
5936
|
-
/^\. ?$/.test(change.insert.toString())) {
|
|
5937
|
-
// Detect insert-period-on-double-space Mac and Android behavior,
|
|
5938
|
-
// and transform it into a regular space insert.
|
|
5939
|
-
if (newSel && change.insert.length == 2)
|
|
5940
|
-
newSel = EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1);
|
|
5941
|
-
change = { from: sel.from, to: sel.to, insert: Text.of([" "]) };
|
|
5942
|
-
}
|
|
5943
|
-
if (change) {
|
|
5944
|
-
let startState = view.state;
|
|
5945
|
-
if (browser.ios && view.inputState.flushIOSKey(view))
|
|
5946
|
-
return true;
|
|
5947
|
-
// Android browsers don't fire reasonable key events for enter,
|
|
5948
|
-
// backspace, or delete. So this detects changes that look like
|
|
5949
|
-
// they're caused by those keys, and reinterprets them as key
|
|
5950
|
-
// events. (Some of these keys are also handled by beforeinput
|
|
5951
|
-
// events and the pendingAndroidKey mechanism, but that's not
|
|
5952
|
-
// reliable in all situations.)
|
|
5953
|
-
if (browser.android &&
|
|
5954
|
-
((change.from == sel.from && change.to == sel.to &&
|
|
5955
|
-
change.insert.length == 1 && change.insert.lines == 2 &&
|
|
5956
|
-
dispatchKey(view.contentDOM, "Enter", 13)) ||
|
|
5957
|
-
(change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
|
|
5958
|
-
dispatchKey(view.contentDOM, "Backspace", 8)) ||
|
|
5959
|
-
(change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
|
|
5960
|
-
dispatchKey(view.contentDOM, "Delete", 46))))
|
|
5961
|
-
return true;
|
|
5962
|
-
let text = change.insert.toString();
|
|
5963
|
-
if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
|
|
5964
|
-
return true;
|
|
5965
|
-
if (view.inputState.composing >= 0)
|
|
5966
|
-
view.inputState.composing++;
|
|
5967
|
-
let tr;
|
|
5968
|
-
if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
|
|
5969
|
-
(!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) &&
|
|
5970
|
-
view.inputState.composing < 0) {
|
|
5971
|
-
let before = sel.from < change.from ? startState.sliceDoc(sel.from, change.from) : "";
|
|
5972
|
-
let after = sel.to > change.to ? startState.sliceDoc(change.to, sel.to) : "";
|
|
5973
|
-
tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) + after));
|
|
5974
|
-
}
|
|
5975
|
-
else {
|
|
5976
|
-
let changes = startState.changes(change);
|
|
5977
|
-
let mainSel = newSel && !startState.selection.main.eq(newSel.main) && newSel.main.to <= changes.newLength
|
|
5978
|
-
? newSel.main : undefined;
|
|
5979
|
-
// Try to apply a composition change to all cursors
|
|
5980
|
-
if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
|
|
5981
|
-
change.to <= sel.to && change.to >= sel.to - 10) {
|
|
5982
|
-
let replaced = view.state.sliceDoc(change.from, change.to);
|
|
5983
|
-
let compositionRange = compositionSurroundingNode(view) || view.state.doc.lineAt(sel.head);
|
|
5984
|
-
let offset = sel.to - change.to, size = sel.to - sel.from;
|
|
5985
|
-
tr = startState.changeByRange(range => {
|
|
5986
|
-
if (range.from == sel.from && range.to == sel.to)
|
|
5987
|
-
return { changes, range: mainSel || range.map(changes) };
|
|
5988
|
-
let to = range.to - offset, from = to - replaced.length;
|
|
5989
|
-
if (range.to - range.from != size || view.state.sliceDoc(from, to) != replaced ||
|
|
5990
|
-
// Unfortunately, there's no way to make multiple
|
|
5991
|
-
// changes in the same node work without aborting
|
|
5992
|
-
// composition, so cursors in the composition range are
|
|
5993
|
-
// ignored.
|
|
5994
|
-
compositionRange && range.to >= compositionRange.from && range.from <= compositionRange.to)
|
|
5995
|
-
return { range };
|
|
5996
|
-
let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
|
|
5997
|
-
return {
|
|
5998
|
-
changes: rangeChanges,
|
|
5999
|
-
range: !mainSel ? range.map(rangeChanges) :
|
|
6000
|
-
EditorSelection.range(Math.max(0, mainSel.anchor + selOff), Math.max(0, mainSel.head + selOff))
|
|
6001
|
-
};
|
|
6002
|
-
});
|
|
6003
|
-
}
|
|
6004
|
-
else {
|
|
6005
|
-
tr = {
|
|
6006
|
-
changes,
|
|
6007
|
-
selection: mainSel && startState.selection.replaceRange(mainSel)
|
|
6008
|
-
};
|
|
6009
|
-
}
|
|
6010
|
-
}
|
|
6011
|
-
let userEvent = "input.type";
|
|
6012
|
-
if (view.composing) {
|
|
6013
|
-
userEvent += ".compose";
|
|
6014
|
-
if (view.inputState.compositionFirstChange) {
|
|
6015
|
-
userEvent += ".start";
|
|
6016
|
-
view.inputState.compositionFirstChange = false;
|
|
6017
|
-
}
|
|
6018
|
-
}
|
|
6019
|
-
view.dispatch(tr, { scrollIntoView: true, userEvent });
|
|
6020
|
-
return true;
|
|
6021
|
-
}
|
|
6022
|
-
else if (newSel && !newSel.main.eq(sel)) {
|
|
6023
|
-
let scrollIntoView = false, userEvent = "select";
|
|
6024
|
-
if (view.inputState.lastSelectionTime > Date.now() - 50) {
|
|
6025
|
-
if (view.inputState.lastSelectionOrigin == "select")
|
|
6026
|
-
scrollIntoView = true;
|
|
6027
|
-
userEvent = view.inputState.lastSelectionOrigin;
|
|
6028
|
-
}
|
|
6029
|
-
view.dispatch({ selection: newSel, scrollIntoView, userEvent });
|
|
6030
|
-
return true;
|
|
6031
|
-
}
|
|
6032
|
-
else {
|
|
6033
|
-
return false;
|
|
6034
|
-
}
|
|
6035
|
-
}
|
|
6036
|
-
function findDiff(a, b, preferredPos, preferredSide) {
|
|
6037
|
-
let minLen = Math.min(a.length, b.length);
|
|
6038
|
-
let from = 0;
|
|
6039
|
-
while (from < minLen && a.charCodeAt(from) == b.charCodeAt(from))
|
|
6040
|
-
from++;
|
|
6041
|
-
if (from == minLen && a.length == b.length)
|
|
6042
|
-
return null;
|
|
6043
|
-
let toA = a.length, toB = b.length;
|
|
6044
|
-
while (toA > 0 && toB > 0 && a.charCodeAt(toA - 1) == b.charCodeAt(toB - 1)) {
|
|
6045
|
-
toA--;
|
|
6046
|
-
toB--;
|
|
6047
|
-
}
|
|
6048
|
-
if (preferredSide == "end") {
|
|
6049
|
-
let adjust = Math.max(0, from - Math.min(toA, toB));
|
|
6050
|
-
preferredPos -= toA + adjust - from;
|
|
6051
|
-
}
|
|
6052
|
-
if (toA < from && a.length < b.length) {
|
|
6053
|
-
let move = preferredPos <= from && preferredPos >= toA ? from - preferredPos : 0;
|
|
6054
|
-
from -= move;
|
|
6055
|
-
toB = from + (toB - toA);
|
|
6056
|
-
toA = from;
|
|
6057
|
-
}
|
|
6058
|
-
else if (toB < from) {
|
|
6059
|
-
let move = preferredPos <= from && preferredPos >= toB ? from - preferredPos : 0;
|
|
6060
|
-
from -= move;
|
|
6061
|
-
toA = from + (toA - toB);
|
|
6062
|
-
toB = from;
|
|
6063
|
-
}
|
|
6064
|
-
return { from, toA, toB };
|
|
6065
|
-
}
|
|
6066
|
-
function selectionPoints(view) {
|
|
6067
|
-
let result = [];
|
|
6068
|
-
if (view.root.activeElement != view.contentDOM)
|
|
6069
|
-
return result;
|
|
6070
|
-
let { anchorNode, anchorOffset, focusNode, focusOffset } = view.observer.selectionRange;
|
|
6071
|
-
if (anchorNode) {
|
|
6072
|
-
result.push(new DOMPoint(anchorNode, anchorOffset));
|
|
6073
|
-
if (focusNode != anchorNode || focusOffset != anchorOffset)
|
|
6074
|
-
result.push(new DOMPoint(focusNode, focusOffset));
|
|
6075
|
-
}
|
|
6076
|
-
return result;
|
|
6077
|
-
}
|
|
6078
|
-
function selectionFromPoints(points, base) {
|
|
6079
|
-
if (points.length == 0)
|
|
6080
|
-
return null;
|
|
6081
|
-
let anchor = points[0].pos, head = points.length == 2 ? points[1].pos : anchor;
|
|
6082
|
-
return anchor > -1 && head > -1 ? EditorSelection.single(anchor + base, head + base) : null;
|
|
6083
|
-
}
|
|
6084
|
-
|
|
6085
6155
|
// The editor's update state machine looks something like this:
|
|
6086
6156
|
//
|
|
6087
6157
|
// Idle → Updating ⇆ Idle (unchecked) → Measuring → Idle
|
|
@@ -6144,13 +6214,7 @@ class EditorView {
|
|
|
6144
6214
|
this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
|
|
6145
6215
|
for (let plugin of this.plugins)
|
|
6146
6216
|
plugin.update(this);
|
|
6147
|
-
this.observer = new DOMObserver(this
|
|
6148
|
-
return applyDOMChange(this, from, to, typeOver);
|
|
6149
|
-
}, event => {
|
|
6150
|
-
this.inputState.runScrollHandlers(this, event);
|
|
6151
|
-
if (this.observer.intersecting)
|
|
6152
|
-
this.measure();
|
|
6153
|
-
});
|
|
6217
|
+
this.observer = new DOMObserver(this);
|
|
6154
6218
|
this.inputState = new InputState(this);
|
|
6155
6219
|
this.inputState.ensureHandlers(this, this.plugins);
|
|
6156
6220
|
this.docView = new DocView(this);
|
|
@@ -6234,7 +6298,20 @@ class EditorView {
|
|
|
6234
6298
|
this.viewState.state = state;
|
|
6235
6299
|
return;
|
|
6236
6300
|
}
|
|
6237
|
-
|
|
6301
|
+
// If there was a pending DOM change, eagerly read it and try to
|
|
6302
|
+
// apply it after the given transactions.
|
|
6303
|
+
let pendingKey = this.observer.delayedAndroidKey, domChange = null;
|
|
6304
|
+
if (pendingKey) {
|
|
6305
|
+
this.observer.clearDelayedAndroidKey();
|
|
6306
|
+
domChange = this.observer.readChange();
|
|
6307
|
+
// Only try to apply DOM changes if the transactions didn't
|
|
6308
|
+
// change the doc or selection.
|
|
6309
|
+
if (domChange && !this.state.doc.eq(state.doc) || !this.state.selection.eq(state.selection))
|
|
6310
|
+
domChange = null;
|
|
6311
|
+
}
|
|
6312
|
+
else {
|
|
6313
|
+
this.observer.clear();
|
|
6314
|
+
}
|
|
6238
6315
|
// When the phrases change, redraw the editor
|
|
6239
6316
|
if (state.facet(EditorState.phrases) != this.state.facet(EditorState.phrases))
|
|
6240
6317
|
return this.setState(state);
|
|
@@ -6276,6 +6353,10 @@ class EditorView {
|
|
|
6276
6353
|
if (!update.empty)
|
|
6277
6354
|
for (let listener of this.state.facet(updateListener))
|
|
6278
6355
|
listener(update);
|
|
6356
|
+
if (domChange) {
|
|
6357
|
+
if (!applyDOMChange(this, domChange) && pendingKey.force)
|
|
6358
|
+
dispatchKey(this.contentDOM, pendingKey.key, pendingKey.keyCode);
|
|
6359
|
+
}
|
|
6279
6360
|
}
|
|
6280
6361
|
/**
|
|
6281
6362
|
Reset the view to the given state. (This will cause the entire
|