@codemirror/view 0.19.45 → 0.19.48
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 +28 -0
- package/dist/index.cjs +121 -53
- package/dist/index.d.ts +2 -2
- package/dist/index.js +121 -53
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
## 0.19.48 (2022-03-30)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix an issue where DOM syncing could crash when a DOM node was moved from a parent to a child node (via widgets reusing existing nodes).
|
|
6
|
+
|
|
7
|
+
To avoid interfering with things like a vim mode too much, the editor will now only activate the tab-to-move-focus escape hatch after an escape press that wasn't handled by an event handler.
|
|
8
|
+
|
|
9
|
+
Make sure the view measures itself before the page is printed.
|
|
10
|
+
|
|
11
|
+
Tweak types of view plugin defining functions to avoid TypeScript errors when the plugin value doesn't have any of the interface's properties.
|
|
12
|
+
|
|
13
|
+
## 0.19.47 (2022-03-08)
|
|
14
|
+
|
|
15
|
+
### Bug fixes
|
|
16
|
+
|
|
17
|
+
Fix an issue where block widgets at the start of the viewport could break height computations.
|
|
18
|
+
|
|
19
|
+
## 0.19.46 (2022-03-03)
|
|
20
|
+
|
|
21
|
+
### Bug fixes
|
|
22
|
+
|
|
23
|
+
Fix a bug where block widgets on the edges of viewports could cause the positioning of content to misalign with the gutter and height computations.
|
|
24
|
+
|
|
25
|
+
Improve cursor height next to widgets.
|
|
26
|
+
|
|
27
|
+
Fix a bug where mapping positions to screen coordinates could return incorred coordinates during composition.
|
|
28
|
+
|
|
1
29
|
## 0.19.45 (2022-02-23)
|
|
2
30
|
|
|
3
31
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -22,7 +22,7 @@ function getSelection(root) {
|
|
|
22
22
|
return target.getSelection();
|
|
23
23
|
}
|
|
24
24
|
function contains(dom, node) {
|
|
25
|
-
return node ? dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
|
|
25
|
+
return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
|
|
26
26
|
}
|
|
27
27
|
function deepActiveElement() {
|
|
28
28
|
let elt = document.activeElement;
|
|
@@ -318,32 +318,34 @@ class ContentView {
|
|
|
318
318
|
sync(track) {
|
|
319
319
|
if (this.dirty & 2 /* Node */) {
|
|
320
320
|
let parent = this.dom;
|
|
321
|
-
let
|
|
321
|
+
let prev = null, next;
|
|
322
322
|
for (let child of this.children) {
|
|
323
323
|
if (child.dirty) {
|
|
324
|
-
if (!child.dom &&
|
|
325
|
-
let contentView = ContentView.get(
|
|
324
|
+
if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
|
|
325
|
+
let contentView = ContentView.get(next);
|
|
326
326
|
if (!contentView || !contentView.parent && contentView.constructor == child.constructor)
|
|
327
|
-
child.reuseDOM(
|
|
327
|
+
child.reuseDOM(next);
|
|
328
328
|
}
|
|
329
329
|
child.sync(track);
|
|
330
330
|
child.dirty = 0 /* Not */;
|
|
331
331
|
}
|
|
332
|
-
|
|
332
|
+
next = prev ? prev.nextSibling : parent.firstChild;
|
|
333
|
+
if (track && !track.written && track.node == parent && next != child.dom)
|
|
333
334
|
track.written = true;
|
|
334
335
|
if (child.dom.parentNode == parent) {
|
|
335
|
-
while (
|
|
336
|
-
|
|
337
|
-
pos = child.dom.nextSibling;
|
|
336
|
+
while (next && next != child.dom)
|
|
337
|
+
next = rm(next);
|
|
338
338
|
}
|
|
339
339
|
else {
|
|
340
|
-
parent.insertBefore(child.dom,
|
|
340
|
+
parent.insertBefore(child.dom, next);
|
|
341
341
|
}
|
|
342
|
+
prev = child.dom;
|
|
342
343
|
}
|
|
343
|
-
|
|
344
|
+
next = prev ? prev.nextSibling : parent.firstChild;
|
|
345
|
+
if (next && track && track.node == parent)
|
|
344
346
|
track.written = true;
|
|
345
|
-
while (
|
|
346
|
-
|
|
347
|
+
while (next)
|
|
348
|
+
next = rm(next);
|
|
347
349
|
}
|
|
348
350
|
else if (this.dirty & 1 /* Child */) {
|
|
349
351
|
for (let child of this.children)
|
|
@@ -856,18 +858,26 @@ class WidgetView extends ContentView {
|
|
|
856
858
|
}
|
|
857
859
|
class CompositionView extends WidgetView {
|
|
858
860
|
domAtPos(pos) {
|
|
859
|
-
let { topView } = this.widget;
|
|
861
|
+
let { topView, text } = this.widget;
|
|
860
862
|
if (!topView)
|
|
861
|
-
return new DOMPos(
|
|
862
|
-
return
|
|
863
|
+
return new DOMPos(text, Math.min(pos, text.nodeValue.length));
|
|
864
|
+
return scanCompositionTree(pos, 0, topView, text, (v, p) => v.domAtPos(p), p => new DOMPos(text, Math.min(p, text.nodeValue.length)));
|
|
863
865
|
}
|
|
864
866
|
sync() { this.setDOM(this.widget.toDOM()); }
|
|
865
867
|
localPosFromDOM(node, offset) {
|
|
866
|
-
|
|
868
|
+
let { topView, text } = this.widget;
|
|
869
|
+
if (!topView)
|
|
870
|
+
return Math.min(offset, this.length);
|
|
871
|
+
return posFromDOMInCompositionTree(node, offset, topView, text);
|
|
867
872
|
}
|
|
868
873
|
ignoreMutation() { return false; }
|
|
869
874
|
get overrideDOMText() { return null; }
|
|
870
|
-
coordsAt(pos, side) {
|
|
875
|
+
coordsAt(pos, side) {
|
|
876
|
+
let { topView, text } = this.widget;
|
|
877
|
+
if (!topView)
|
|
878
|
+
return textCoords(text, pos, side);
|
|
879
|
+
return scanCompositionTree(pos, side, topView, text, (v, pos, side) => v.coordsAt(pos, side), (pos, side) => textCoords(text, pos, side));
|
|
880
|
+
}
|
|
871
881
|
destroy() {
|
|
872
882
|
var _a;
|
|
873
883
|
super.destroy();
|
|
@@ -878,24 +888,38 @@ class CompositionView extends WidgetView {
|
|
|
878
888
|
// Uses the old structure of a chunk of content view frozen for
|
|
879
889
|
// composition to try and find a reasonable DOM location for the given
|
|
880
890
|
// offset.
|
|
881
|
-
function
|
|
891
|
+
function scanCompositionTree(pos, side, view, text, enterView, fromText) {
|
|
882
892
|
if (view instanceof MarkView) {
|
|
883
893
|
for (let child of view.children) {
|
|
884
|
-
let hasComp = child.dom
|
|
894
|
+
let hasComp = contains(child.dom, text);
|
|
885
895
|
let len = hasComp ? text.nodeValue.length : child.length;
|
|
886
896
|
if (pos < len || pos == len && child.getSide() <= 0)
|
|
887
|
-
return hasComp ?
|
|
897
|
+
return hasComp ? scanCompositionTree(pos, side, child, text, enterView, fromText) : enterView(child, pos, side);
|
|
888
898
|
pos -= len;
|
|
889
899
|
}
|
|
890
|
-
return
|
|
900
|
+
return enterView(view, view.length, -1);
|
|
891
901
|
}
|
|
892
902
|
else if (view.dom == text) {
|
|
893
|
-
return
|
|
903
|
+
return fromText(pos, side);
|
|
894
904
|
}
|
|
895
905
|
else {
|
|
896
|
-
return view
|
|
906
|
+
return enterView(view, pos, side);
|
|
897
907
|
}
|
|
898
908
|
}
|
|
909
|
+
function posFromDOMInCompositionTree(node, offset, view, text) {
|
|
910
|
+
if (view instanceof MarkView) {
|
|
911
|
+
for (let child of view.children) {
|
|
912
|
+
let pos = 0, hasComp = contains(child.dom, text);
|
|
913
|
+
if (contains(child.dom, node))
|
|
914
|
+
return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, child, text) : child.localPosFromDOM(node, offset));
|
|
915
|
+
pos += hasComp ? text.nodeValue.length : child.length;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
else if (view.dom == text) {
|
|
919
|
+
return Math.min(offset, text.nodeValue.length);
|
|
920
|
+
}
|
|
921
|
+
return view.localPosFromDOM(node, offset);
|
|
922
|
+
}
|
|
899
923
|
// These are drawn around uneditable widgets to avoid a number of
|
|
900
924
|
// browser bugs that show up when the cursor is directly next to
|
|
901
925
|
// uneditable inline content.
|
|
@@ -914,6 +938,7 @@ class WidgetBufferView extends ContentView {
|
|
|
914
938
|
if (!this.dom) {
|
|
915
939
|
let dom = document.createElement("img");
|
|
916
940
|
dom.className = "cm-widgetBuffer";
|
|
941
|
+
dom.setAttribute("aria-hidden", "true");
|
|
917
942
|
this.setDOM(dom);
|
|
918
943
|
}
|
|
919
944
|
}
|
|
@@ -922,13 +947,43 @@ class WidgetBufferView extends ContentView {
|
|
|
922
947
|
localPosFromDOM() { return 0; }
|
|
923
948
|
domBoundsAround() { return null; }
|
|
924
949
|
coordsAt(pos) {
|
|
925
|
-
|
|
950
|
+
let imgRect = this.dom.getBoundingClientRect();
|
|
951
|
+
// Since the <img> height doesn't correspond to text height, try
|
|
952
|
+
// to borrow the height from some sibling node.
|
|
953
|
+
let siblingRect = inlineSiblingRect(this, this.side > 0 ? -1 : 1);
|
|
954
|
+
return siblingRect && siblingRect.top < imgRect.bottom && siblingRect.bottom > imgRect.top
|
|
955
|
+
? { left: imgRect.left, right: imgRect.right, top: siblingRect.top, bottom: siblingRect.bottom } : imgRect;
|
|
926
956
|
}
|
|
927
957
|
get overrideDOMText() {
|
|
928
958
|
return text.Text.empty;
|
|
929
959
|
}
|
|
930
960
|
}
|
|
931
961
|
TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
|
|
962
|
+
function inlineSiblingRect(view, side) {
|
|
963
|
+
let parent = view.parent, index = parent ? parent.children.indexOf(view) : -1;
|
|
964
|
+
while (parent && index >= 0) {
|
|
965
|
+
if (side < 0 ? index > 0 : index < parent.children.length) {
|
|
966
|
+
let next = parent.children[index + side];
|
|
967
|
+
if (next instanceof TextView) {
|
|
968
|
+
let nextRect = next.coordsAt(side < 0 ? next.length : 0, side);
|
|
969
|
+
if (nextRect)
|
|
970
|
+
return nextRect;
|
|
971
|
+
}
|
|
972
|
+
index += side;
|
|
973
|
+
}
|
|
974
|
+
else if (parent instanceof MarkView && parent.parent) {
|
|
975
|
+
index = parent.parent.children.indexOf(parent) + (side < 0 ? 0 : 1);
|
|
976
|
+
parent = parent.parent;
|
|
977
|
+
}
|
|
978
|
+
else {
|
|
979
|
+
let last = parent.dom.lastChild;
|
|
980
|
+
if (last && last.nodeName == "BR")
|
|
981
|
+
return last.getClientRects()[0];
|
|
982
|
+
break;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
return undefined;
|
|
986
|
+
}
|
|
932
987
|
function inlineDOMAtPos(dom, children, pos) {
|
|
933
988
|
let i = 0;
|
|
934
989
|
for (let off = 0; i < children.length; i++) {
|
|
@@ -1162,10 +1217,16 @@ class Decoration extends rangeset.RangeValue {
|
|
|
1162
1217
|
a widget, or simply hides it.
|
|
1163
1218
|
*/
|
|
1164
1219
|
static replace(spec) {
|
|
1165
|
-
let block = !!spec.block;
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1220
|
+
let block = !!spec.block, startSide, endSide;
|
|
1221
|
+
if (spec.isBlockGap) {
|
|
1222
|
+
startSide = -500000000 /* GapStart */;
|
|
1223
|
+
endSide = 400000000 /* GapEnd */;
|
|
1224
|
+
}
|
|
1225
|
+
else {
|
|
1226
|
+
let { start, end } = getInclusive(spec, block);
|
|
1227
|
+
startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 500000000 /* NonIncStart */) - 1;
|
|
1228
|
+
endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -600000000 /* NonIncEnd */) + 1;
|
|
1229
|
+
}
|
|
1169
1230
|
return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
|
|
1170
1231
|
}
|
|
1171
1232
|
/**
|
|
@@ -1195,7 +1256,7 @@ Decoration.none = rangeset.RangeSet.empty;
|
|
|
1195
1256
|
class MarkDecoration extends Decoration {
|
|
1196
1257
|
constructor(spec) {
|
|
1197
1258
|
let { start, end } = getInclusive(spec);
|
|
1198
|
-
super(start ? -1 /* InlineIncStart */ :
|
|
1259
|
+
super(start ? -1 /* InlineIncStart */ : 500000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -600000000 /* NonIncEnd */, null, spec);
|
|
1199
1260
|
this.tagName = spec.tagName || "span";
|
|
1200
1261
|
this.class = spec.class || "";
|
|
1201
1262
|
this.attrs = spec.attributes || null;
|
|
@@ -1376,7 +1437,7 @@ class LineView extends ContentView {
|
|
|
1376
1437
|
let last = this.dom.lastChild;
|
|
1377
1438
|
while (last && ContentView.get(last) instanceof MarkView)
|
|
1378
1439
|
last = last.lastChild;
|
|
1379
|
-
if (!last ||
|
|
1440
|
+
if (!last || !this.length ||
|
|
1380
1441
|
last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false &&
|
|
1381
1442
|
(!browser.ios || !this.children.some(ch => ch instanceof TextView))) {
|
|
1382
1443
|
let hack = document.createElement("BR");
|
|
@@ -2774,7 +2835,12 @@ class DocView extends ContentView {
|
|
|
2774
2835
|
let end = next ? next.from - 1 : this.length;
|
|
2775
2836
|
if (end > pos) {
|
|
2776
2837
|
let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
|
|
2777
|
-
deco.push(Decoration.replace({
|
|
2838
|
+
deco.push(Decoration.replace({
|
|
2839
|
+
widget: new BlockGapWidget(height),
|
|
2840
|
+
block: true,
|
|
2841
|
+
inclusive: true,
|
|
2842
|
+
isBlockGap: true,
|
|
2843
|
+
}).range(pos, end));
|
|
2778
2844
|
}
|
|
2779
2845
|
if (!next)
|
|
2780
2846
|
break;
|
|
@@ -3309,10 +3375,10 @@ class InputState {
|
|
|
3309
3375
|
for (let type in handlers) {
|
|
3310
3376
|
let handler = handlers[type];
|
|
3311
3377
|
view.contentDOM.addEventListener(type, (event) => {
|
|
3312
|
-
if (type == "keydown" && this.keydown(view, event))
|
|
3313
|
-
return;
|
|
3314
3378
|
if (!eventBelongsToEditor(view, event) || this.ignoreDuringComposition(event))
|
|
3315
3379
|
return;
|
|
3380
|
+
if (type == "keydown" && this.keydown(view, event))
|
|
3381
|
+
return;
|
|
3316
3382
|
if (this.mustFlushObserver(event))
|
|
3317
3383
|
view.observer.forceFlush();
|
|
3318
3384
|
if (this.runCustomHandlers(type, view, event))
|
|
@@ -3380,7 +3446,7 @@ class InputState {
|
|
|
3380
3446
|
// Must always run, even if a custom handler handled the event
|
|
3381
3447
|
this.lastKeyCode = event.keyCode;
|
|
3382
3448
|
this.lastKeyTime = Date.now();
|
|
3383
|
-
if (
|
|
3449
|
+
if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
|
|
3384
3450
|
return true;
|
|
3385
3451
|
// Chrome for Android usually doesn't fire proper key events, but
|
|
3386
3452
|
// occasionally does, usually surrounded by a bunch of complicated
|
|
@@ -3430,14 +3496,6 @@ class InputState {
|
|
|
3430
3496
|
}
|
|
3431
3497
|
return false;
|
|
3432
3498
|
}
|
|
3433
|
-
screenKeyEvent(view, event) {
|
|
3434
|
-
let protectedTab = event.keyCode == 9 && Date.now() < this.lastEscPress + 2000;
|
|
3435
|
-
if (event.keyCode == 27)
|
|
3436
|
-
this.lastEscPress = Date.now();
|
|
3437
|
-
else if (modifierCodes.indexOf(event.keyCode) < 0)
|
|
3438
|
-
this.lastEscPress = 0;
|
|
3439
|
-
return protectedTab;
|
|
3440
|
-
}
|
|
3441
3499
|
mustFlushObserver(event) {
|
|
3442
3500
|
return (event.type == "keydown" && event.keyCode != 229) ||
|
|
3443
3501
|
event.type == "compositionend" && !browser.ios;
|
|
@@ -3611,6 +3669,10 @@ function doPaste(view, input) {
|
|
|
3611
3669
|
}
|
|
3612
3670
|
handlers.keydown = (view, event) => {
|
|
3613
3671
|
view.inputState.setSelectionOrigin("select");
|
|
3672
|
+
if (event.keyCode == 27)
|
|
3673
|
+
view.inputState.lastEscPress = Date.now();
|
|
3674
|
+
else if (modifierCodes.indexOf(event.keyCode) < 0)
|
|
3675
|
+
view.inputState.lastEscPress = 0;
|
|
3614
3676
|
};
|
|
3615
3677
|
let lastTouch = 0;
|
|
3616
3678
|
handlers.touchstart = (view, e) => {
|
|
@@ -3868,14 +3930,6 @@ handlers.focus = handlers.blur = view => {
|
|
|
3868
3930
|
view.update([]);
|
|
3869
3931
|
}, 10);
|
|
3870
3932
|
};
|
|
3871
|
-
handlers.beforeprint = view => {
|
|
3872
|
-
view.viewState.printing = true;
|
|
3873
|
-
view.requestMeasure();
|
|
3874
|
-
setTimeout(() => {
|
|
3875
|
-
view.viewState.printing = false;
|
|
3876
|
-
view.requestMeasure();
|
|
3877
|
-
}, 2000);
|
|
3878
|
-
};
|
|
3879
3933
|
function forceClearComposition(view, rapid) {
|
|
3880
3934
|
if (view.docView.compositionDeco.size) {
|
|
3881
3935
|
view.inputState.rapidCompositionStart = rapid;
|
|
@@ -4631,6 +4685,11 @@ function visiblePixelRange(dom, paddingTop) {
|
|
|
4631
4685
|
return { left: left - rect.left, right: Math.max(left, right) - rect.left,
|
|
4632
4686
|
top: top - (rect.top + paddingTop), bottom: Math.max(top, bottom) - (rect.top + paddingTop) };
|
|
4633
4687
|
}
|
|
4688
|
+
function fullPixelRange(dom, paddingTop) {
|
|
4689
|
+
let rect = dom.getBoundingClientRect();
|
|
4690
|
+
return { left: 0, right: rect.right - rect.left,
|
|
4691
|
+
top: paddingTop, bottom: rect.bottom - (rect.top + paddingTop) };
|
|
4692
|
+
}
|
|
4634
4693
|
// Line gaps are placeholder widgets used to hide pieces of overlong
|
|
4635
4694
|
// lines within the viewport, as a kludge to keep the editor
|
|
4636
4695
|
// responsive when a ridiculously long line is loaded into it.
|
|
@@ -4787,8 +4846,7 @@ class ViewState {
|
|
|
4787
4846
|
}
|
|
4788
4847
|
}
|
|
4789
4848
|
// Pixel viewport
|
|
4790
|
-
let pixelViewport = this.printing ?
|
|
4791
|
-
: visiblePixelRange(dom, this.paddingTop);
|
|
4849
|
+
let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
|
|
4792
4850
|
let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
|
|
4793
4851
|
this.pixelViewport = pixelViewport;
|
|
4794
4852
|
let inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
|
|
@@ -5365,6 +5423,7 @@ class DOMObserver {
|
|
|
5365
5423
|
});
|
|
5366
5424
|
this.resize.observe(view.scrollDOM);
|
|
5367
5425
|
}
|
|
5426
|
+
window.addEventListener("beforeprint", this.onPrint = this.onPrint.bind(this));
|
|
5368
5427
|
this.start();
|
|
5369
5428
|
window.addEventListener("scroll", this.onScroll = this.onScroll.bind(this));
|
|
5370
5429
|
if (typeof IntersectionObserver == "function") {
|
|
@@ -5399,6 +5458,14 @@ class DOMObserver {
|
|
|
5399
5458
|
this.view.requestMeasure();
|
|
5400
5459
|
}, 50);
|
|
5401
5460
|
}
|
|
5461
|
+
onPrint() {
|
|
5462
|
+
this.view.viewState.printing = true;
|
|
5463
|
+
this.view.measure();
|
|
5464
|
+
setTimeout(() => {
|
|
5465
|
+
this.view.viewState.printing = false;
|
|
5466
|
+
this.view.requestMeasure();
|
|
5467
|
+
}, 500);
|
|
5468
|
+
}
|
|
5402
5469
|
updateGaps(gaps) {
|
|
5403
5470
|
if (this.gapIntersection && (gaps.length != this.gaps.length || this.gaps.some((g, i) => g != gaps[i]))) {
|
|
5404
5471
|
this.gapIntersection.disconnect();
|
|
@@ -5616,6 +5683,7 @@ class DOMObserver {
|
|
|
5616
5683
|
dom.removeEventListener("scroll", this.onScroll);
|
|
5617
5684
|
window.removeEventListener("scroll", this.onScroll);
|
|
5618
5685
|
window.removeEventListener("resize", this.onResize);
|
|
5686
|
+
window.removeEventListener("beforeprint", this.onPrint);
|
|
5619
5687
|
this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
|
|
5620
5688
|
clearTimeout(this.parentCheck);
|
|
5621
5689
|
clearTimeout(this.resizeTimeout);
|
package/dist/index.d.ts
CHANGED
|
@@ -413,12 +413,12 @@ declare class ViewPlugin<V extends PluginValue> {
|
|
|
413
413
|
Define a plugin from a constructor function that creates the
|
|
414
414
|
plugin's value, given an editor view.
|
|
415
415
|
*/
|
|
416
|
-
static define<V extends PluginValue>(create: (view: EditorView) => V, spec?: PluginSpec<V>): ViewPlugin<V>;
|
|
416
|
+
static define<V extends PluginValue & object>(create: (view: EditorView) => V, spec?: PluginSpec<V>): ViewPlugin<V>;
|
|
417
417
|
/**
|
|
418
418
|
Create a plugin for a class whose constructor takes a single
|
|
419
419
|
editor view as argument.
|
|
420
420
|
*/
|
|
421
|
-
static fromClass<V extends PluginValue>(cls: {
|
|
421
|
+
static fromClass<V extends PluginValue & object>(cls: {
|
|
422
422
|
new (view: EditorView): V;
|
|
423
423
|
}, spec?: PluginSpec<V>): ViewPlugin<V>;
|
|
424
424
|
}
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ function getSelection(root) {
|
|
|
19
19
|
return target.getSelection();
|
|
20
20
|
}
|
|
21
21
|
function contains(dom, node) {
|
|
22
|
-
return node ? dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
|
|
22
|
+
return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
|
|
23
23
|
}
|
|
24
24
|
function deepActiveElement() {
|
|
25
25
|
let elt = document.activeElement;
|
|
@@ -315,32 +315,34 @@ class ContentView {
|
|
|
315
315
|
sync(track) {
|
|
316
316
|
if (this.dirty & 2 /* Node */) {
|
|
317
317
|
let parent = this.dom;
|
|
318
|
-
let
|
|
318
|
+
let prev = null, next;
|
|
319
319
|
for (let child of this.children) {
|
|
320
320
|
if (child.dirty) {
|
|
321
|
-
if (!child.dom &&
|
|
322
|
-
let contentView = ContentView.get(
|
|
321
|
+
if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
|
|
322
|
+
let contentView = ContentView.get(next);
|
|
323
323
|
if (!contentView || !contentView.parent && contentView.constructor == child.constructor)
|
|
324
|
-
child.reuseDOM(
|
|
324
|
+
child.reuseDOM(next);
|
|
325
325
|
}
|
|
326
326
|
child.sync(track);
|
|
327
327
|
child.dirty = 0 /* Not */;
|
|
328
328
|
}
|
|
329
|
-
|
|
329
|
+
next = prev ? prev.nextSibling : parent.firstChild;
|
|
330
|
+
if (track && !track.written && track.node == parent && next != child.dom)
|
|
330
331
|
track.written = true;
|
|
331
332
|
if (child.dom.parentNode == parent) {
|
|
332
|
-
while (
|
|
333
|
-
|
|
334
|
-
pos = child.dom.nextSibling;
|
|
333
|
+
while (next && next != child.dom)
|
|
334
|
+
next = rm(next);
|
|
335
335
|
}
|
|
336
336
|
else {
|
|
337
|
-
parent.insertBefore(child.dom,
|
|
337
|
+
parent.insertBefore(child.dom, next);
|
|
338
338
|
}
|
|
339
|
+
prev = child.dom;
|
|
339
340
|
}
|
|
340
|
-
|
|
341
|
+
next = prev ? prev.nextSibling : parent.firstChild;
|
|
342
|
+
if (next && track && track.node == parent)
|
|
341
343
|
track.written = true;
|
|
342
|
-
while (
|
|
343
|
-
|
|
344
|
+
while (next)
|
|
345
|
+
next = rm(next);
|
|
344
346
|
}
|
|
345
347
|
else if (this.dirty & 1 /* Child */) {
|
|
346
348
|
for (let child of this.children)
|
|
@@ -853,18 +855,26 @@ class WidgetView extends ContentView {
|
|
|
853
855
|
}
|
|
854
856
|
class CompositionView extends WidgetView {
|
|
855
857
|
domAtPos(pos) {
|
|
856
|
-
let { topView } = this.widget;
|
|
858
|
+
let { topView, text } = this.widget;
|
|
857
859
|
if (!topView)
|
|
858
|
-
return new DOMPos(
|
|
859
|
-
return
|
|
860
|
+
return new DOMPos(text, Math.min(pos, text.nodeValue.length));
|
|
861
|
+
return scanCompositionTree(pos, 0, topView, text, (v, p) => v.domAtPos(p), p => new DOMPos(text, Math.min(p, text.nodeValue.length)));
|
|
860
862
|
}
|
|
861
863
|
sync() { this.setDOM(this.widget.toDOM()); }
|
|
862
864
|
localPosFromDOM(node, offset) {
|
|
863
|
-
|
|
865
|
+
let { topView, text } = this.widget;
|
|
866
|
+
if (!topView)
|
|
867
|
+
return Math.min(offset, this.length);
|
|
868
|
+
return posFromDOMInCompositionTree(node, offset, topView, text);
|
|
864
869
|
}
|
|
865
870
|
ignoreMutation() { return false; }
|
|
866
871
|
get overrideDOMText() { return null; }
|
|
867
|
-
coordsAt(pos, side) {
|
|
872
|
+
coordsAt(pos, side) {
|
|
873
|
+
let { topView, text } = this.widget;
|
|
874
|
+
if (!topView)
|
|
875
|
+
return textCoords(text, pos, side);
|
|
876
|
+
return scanCompositionTree(pos, side, topView, text, (v, pos, side) => v.coordsAt(pos, side), (pos, side) => textCoords(text, pos, side));
|
|
877
|
+
}
|
|
868
878
|
destroy() {
|
|
869
879
|
var _a;
|
|
870
880
|
super.destroy();
|
|
@@ -875,24 +885,38 @@ class CompositionView extends WidgetView {
|
|
|
875
885
|
// Uses the old structure of a chunk of content view frozen for
|
|
876
886
|
// composition to try and find a reasonable DOM location for the given
|
|
877
887
|
// offset.
|
|
878
|
-
function
|
|
888
|
+
function scanCompositionTree(pos, side, view, text, enterView, fromText) {
|
|
879
889
|
if (view instanceof MarkView) {
|
|
880
890
|
for (let child of view.children) {
|
|
881
|
-
let hasComp = child.dom
|
|
891
|
+
let hasComp = contains(child.dom, text);
|
|
882
892
|
let len = hasComp ? text.nodeValue.length : child.length;
|
|
883
893
|
if (pos < len || pos == len && child.getSide() <= 0)
|
|
884
|
-
return hasComp ?
|
|
894
|
+
return hasComp ? scanCompositionTree(pos, side, child, text, enterView, fromText) : enterView(child, pos, side);
|
|
885
895
|
pos -= len;
|
|
886
896
|
}
|
|
887
|
-
return
|
|
897
|
+
return enterView(view, view.length, -1);
|
|
888
898
|
}
|
|
889
899
|
else if (view.dom == text) {
|
|
890
|
-
return
|
|
900
|
+
return fromText(pos, side);
|
|
891
901
|
}
|
|
892
902
|
else {
|
|
893
|
-
return view
|
|
903
|
+
return enterView(view, pos, side);
|
|
894
904
|
}
|
|
895
905
|
}
|
|
906
|
+
function posFromDOMInCompositionTree(node, offset, view, text) {
|
|
907
|
+
if (view instanceof MarkView) {
|
|
908
|
+
for (let child of view.children) {
|
|
909
|
+
let pos = 0, hasComp = contains(child.dom, text);
|
|
910
|
+
if (contains(child.dom, node))
|
|
911
|
+
return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, child, text) : child.localPosFromDOM(node, offset));
|
|
912
|
+
pos += hasComp ? text.nodeValue.length : child.length;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
else if (view.dom == text) {
|
|
916
|
+
return Math.min(offset, text.nodeValue.length);
|
|
917
|
+
}
|
|
918
|
+
return view.localPosFromDOM(node, offset);
|
|
919
|
+
}
|
|
896
920
|
// These are drawn around uneditable widgets to avoid a number of
|
|
897
921
|
// browser bugs that show up when the cursor is directly next to
|
|
898
922
|
// uneditable inline content.
|
|
@@ -911,6 +935,7 @@ class WidgetBufferView extends ContentView {
|
|
|
911
935
|
if (!this.dom) {
|
|
912
936
|
let dom = document.createElement("img");
|
|
913
937
|
dom.className = "cm-widgetBuffer";
|
|
938
|
+
dom.setAttribute("aria-hidden", "true");
|
|
914
939
|
this.setDOM(dom);
|
|
915
940
|
}
|
|
916
941
|
}
|
|
@@ -919,13 +944,43 @@ class WidgetBufferView extends ContentView {
|
|
|
919
944
|
localPosFromDOM() { return 0; }
|
|
920
945
|
domBoundsAround() { return null; }
|
|
921
946
|
coordsAt(pos) {
|
|
922
|
-
|
|
947
|
+
let imgRect = this.dom.getBoundingClientRect();
|
|
948
|
+
// Since the <img> height doesn't correspond to text height, try
|
|
949
|
+
// to borrow the height from some sibling node.
|
|
950
|
+
let siblingRect = inlineSiblingRect(this, this.side > 0 ? -1 : 1);
|
|
951
|
+
return siblingRect && siblingRect.top < imgRect.bottom && siblingRect.bottom > imgRect.top
|
|
952
|
+
? { left: imgRect.left, right: imgRect.right, top: siblingRect.top, bottom: siblingRect.bottom } : imgRect;
|
|
923
953
|
}
|
|
924
954
|
get overrideDOMText() {
|
|
925
955
|
return Text.empty;
|
|
926
956
|
}
|
|
927
957
|
}
|
|
928
958
|
TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
|
|
959
|
+
function inlineSiblingRect(view, side) {
|
|
960
|
+
let parent = view.parent, index = parent ? parent.children.indexOf(view) : -1;
|
|
961
|
+
while (parent && index >= 0) {
|
|
962
|
+
if (side < 0 ? index > 0 : index < parent.children.length) {
|
|
963
|
+
let next = parent.children[index + side];
|
|
964
|
+
if (next instanceof TextView) {
|
|
965
|
+
let nextRect = next.coordsAt(side < 0 ? next.length : 0, side);
|
|
966
|
+
if (nextRect)
|
|
967
|
+
return nextRect;
|
|
968
|
+
}
|
|
969
|
+
index += side;
|
|
970
|
+
}
|
|
971
|
+
else if (parent instanceof MarkView && parent.parent) {
|
|
972
|
+
index = parent.parent.children.indexOf(parent) + (side < 0 ? 0 : 1);
|
|
973
|
+
parent = parent.parent;
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
let last = parent.dom.lastChild;
|
|
977
|
+
if (last && last.nodeName == "BR")
|
|
978
|
+
return last.getClientRects()[0];
|
|
979
|
+
break;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
return undefined;
|
|
983
|
+
}
|
|
929
984
|
function inlineDOMAtPos(dom, children, pos) {
|
|
930
985
|
let i = 0;
|
|
931
986
|
for (let off = 0; i < children.length; i++) {
|
|
@@ -1158,10 +1213,16 @@ class Decoration extends RangeValue {
|
|
|
1158
1213
|
a widget, or simply hides it.
|
|
1159
1214
|
*/
|
|
1160
1215
|
static replace(spec) {
|
|
1161
|
-
let block = !!spec.block;
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1216
|
+
let block = !!spec.block, startSide, endSide;
|
|
1217
|
+
if (spec.isBlockGap) {
|
|
1218
|
+
startSide = -500000000 /* GapStart */;
|
|
1219
|
+
endSide = 400000000 /* GapEnd */;
|
|
1220
|
+
}
|
|
1221
|
+
else {
|
|
1222
|
+
let { start, end } = getInclusive(spec, block);
|
|
1223
|
+
startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 500000000 /* NonIncStart */) - 1;
|
|
1224
|
+
endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -600000000 /* NonIncEnd */) + 1;
|
|
1225
|
+
}
|
|
1165
1226
|
return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
|
|
1166
1227
|
}
|
|
1167
1228
|
/**
|
|
@@ -1191,7 +1252,7 @@ Decoration.none = RangeSet.empty;
|
|
|
1191
1252
|
class MarkDecoration extends Decoration {
|
|
1192
1253
|
constructor(spec) {
|
|
1193
1254
|
let { start, end } = getInclusive(spec);
|
|
1194
|
-
super(start ? -1 /* InlineIncStart */ :
|
|
1255
|
+
super(start ? -1 /* InlineIncStart */ : 500000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -600000000 /* NonIncEnd */, null, spec);
|
|
1195
1256
|
this.tagName = spec.tagName || "span";
|
|
1196
1257
|
this.class = spec.class || "";
|
|
1197
1258
|
this.attrs = spec.attributes || null;
|
|
@@ -1372,7 +1433,7 @@ class LineView extends ContentView {
|
|
|
1372
1433
|
let last = this.dom.lastChild;
|
|
1373
1434
|
while (last && ContentView.get(last) instanceof MarkView)
|
|
1374
1435
|
last = last.lastChild;
|
|
1375
|
-
if (!last ||
|
|
1436
|
+
if (!last || !this.length ||
|
|
1376
1437
|
last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false &&
|
|
1377
1438
|
(!browser.ios || !this.children.some(ch => ch instanceof TextView))) {
|
|
1378
1439
|
let hack = document.createElement("BR");
|
|
@@ -2769,7 +2830,12 @@ class DocView extends ContentView {
|
|
|
2769
2830
|
let end = next ? next.from - 1 : this.length;
|
|
2770
2831
|
if (end > pos) {
|
|
2771
2832
|
let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
|
|
2772
|
-
deco.push(Decoration.replace({
|
|
2833
|
+
deco.push(Decoration.replace({
|
|
2834
|
+
widget: new BlockGapWidget(height),
|
|
2835
|
+
block: true,
|
|
2836
|
+
inclusive: true,
|
|
2837
|
+
isBlockGap: true,
|
|
2838
|
+
}).range(pos, end));
|
|
2773
2839
|
}
|
|
2774
2840
|
if (!next)
|
|
2775
2841
|
break;
|
|
@@ -3304,10 +3370,10 @@ class InputState {
|
|
|
3304
3370
|
for (let type in handlers) {
|
|
3305
3371
|
let handler = handlers[type];
|
|
3306
3372
|
view.contentDOM.addEventListener(type, (event) => {
|
|
3307
|
-
if (type == "keydown" && this.keydown(view, event))
|
|
3308
|
-
return;
|
|
3309
3373
|
if (!eventBelongsToEditor(view, event) || this.ignoreDuringComposition(event))
|
|
3310
3374
|
return;
|
|
3375
|
+
if (type == "keydown" && this.keydown(view, event))
|
|
3376
|
+
return;
|
|
3311
3377
|
if (this.mustFlushObserver(event))
|
|
3312
3378
|
view.observer.forceFlush();
|
|
3313
3379
|
if (this.runCustomHandlers(type, view, event))
|
|
@@ -3375,7 +3441,7 @@ class InputState {
|
|
|
3375
3441
|
// Must always run, even if a custom handler handled the event
|
|
3376
3442
|
this.lastKeyCode = event.keyCode;
|
|
3377
3443
|
this.lastKeyTime = Date.now();
|
|
3378
|
-
if (
|
|
3444
|
+
if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
|
|
3379
3445
|
return true;
|
|
3380
3446
|
// Chrome for Android usually doesn't fire proper key events, but
|
|
3381
3447
|
// occasionally does, usually surrounded by a bunch of complicated
|
|
@@ -3425,14 +3491,6 @@ class InputState {
|
|
|
3425
3491
|
}
|
|
3426
3492
|
return false;
|
|
3427
3493
|
}
|
|
3428
|
-
screenKeyEvent(view, event) {
|
|
3429
|
-
let protectedTab = event.keyCode == 9 && Date.now() < this.lastEscPress + 2000;
|
|
3430
|
-
if (event.keyCode == 27)
|
|
3431
|
-
this.lastEscPress = Date.now();
|
|
3432
|
-
else if (modifierCodes.indexOf(event.keyCode) < 0)
|
|
3433
|
-
this.lastEscPress = 0;
|
|
3434
|
-
return protectedTab;
|
|
3435
|
-
}
|
|
3436
3494
|
mustFlushObserver(event) {
|
|
3437
3495
|
return (event.type == "keydown" && event.keyCode != 229) ||
|
|
3438
3496
|
event.type == "compositionend" && !browser.ios;
|
|
@@ -3606,6 +3664,10 @@ function doPaste(view, input) {
|
|
|
3606
3664
|
}
|
|
3607
3665
|
handlers.keydown = (view, event) => {
|
|
3608
3666
|
view.inputState.setSelectionOrigin("select");
|
|
3667
|
+
if (event.keyCode == 27)
|
|
3668
|
+
view.inputState.lastEscPress = Date.now();
|
|
3669
|
+
else if (modifierCodes.indexOf(event.keyCode) < 0)
|
|
3670
|
+
view.inputState.lastEscPress = 0;
|
|
3609
3671
|
};
|
|
3610
3672
|
let lastTouch = 0;
|
|
3611
3673
|
handlers.touchstart = (view, e) => {
|
|
@@ -3863,14 +3925,6 @@ handlers.focus = handlers.blur = view => {
|
|
|
3863
3925
|
view.update([]);
|
|
3864
3926
|
}, 10);
|
|
3865
3927
|
};
|
|
3866
|
-
handlers.beforeprint = view => {
|
|
3867
|
-
view.viewState.printing = true;
|
|
3868
|
-
view.requestMeasure();
|
|
3869
|
-
setTimeout(() => {
|
|
3870
|
-
view.viewState.printing = false;
|
|
3871
|
-
view.requestMeasure();
|
|
3872
|
-
}, 2000);
|
|
3873
|
-
};
|
|
3874
3928
|
function forceClearComposition(view, rapid) {
|
|
3875
3929
|
if (view.docView.compositionDeco.size) {
|
|
3876
3930
|
view.inputState.rapidCompositionStart = rapid;
|
|
@@ -4625,6 +4679,11 @@ function visiblePixelRange(dom, paddingTop) {
|
|
|
4625
4679
|
return { left: left - rect.left, right: Math.max(left, right) - rect.left,
|
|
4626
4680
|
top: top - (rect.top + paddingTop), bottom: Math.max(top, bottom) - (rect.top + paddingTop) };
|
|
4627
4681
|
}
|
|
4682
|
+
function fullPixelRange(dom, paddingTop) {
|
|
4683
|
+
let rect = dom.getBoundingClientRect();
|
|
4684
|
+
return { left: 0, right: rect.right - rect.left,
|
|
4685
|
+
top: paddingTop, bottom: rect.bottom - (rect.top + paddingTop) };
|
|
4686
|
+
}
|
|
4628
4687
|
// Line gaps are placeholder widgets used to hide pieces of overlong
|
|
4629
4688
|
// lines within the viewport, as a kludge to keep the editor
|
|
4630
4689
|
// responsive when a ridiculously long line is loaded into it.
|
|
@@ -4781,8 +4840,7 @@ class ViewState {
|
|
|
4781
4840
|
}
|
|
4782
4841
|
}
|
|
4783
4842
|
// Pixel viewport
|
|
4784
|
-
let pixelViewport = this.printing ?
|
|
4785
|
-
: visiblePixelRange(dom, this.paddingTop);
|
|
4843
|
+
let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
|
|
4786
4844
|
let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
|
|
4787
4845
|
this.pixelViewport = pixelViewport;
|
|
4788
4846
|
let inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
|
|
@@ -5359,6 +5417,7 @@ class DOMObserver {
|
|
|
5359
5417
|
});
|
|
5360
5418
|
this.resize.observe(view.scrollDOM);
|
|
5361
5419
|
}
|
|
5420
|
+
window.addEventListener("beforeprint", this.onPrint = this.onPrint.bind(this));
|
|
5362
5421
|
this.start();
|
|
5363
5422
|
window.addEventListener("scroll", this.onScroll = this.onScroll.bind(this));
|
|
5364
5423
|
if (typeof IntersectionObserver == "function") {
|
|
@@ -5393,6 +5452,14 @@ class DOMObserver {
|
|
|
5393
5452
|
this.view.requestMeasure();
|
|
5394
5453
|
}, 50);
|
|
5395
5454
|
}
|
|
5455
|
+
onPrint() {
|
|
5456
|
+
this.view.viewState.printing = true;
|
|
5457
|
+
this.view.measure();
|
|
5458
|
+
setTimeout(() => {
|
|
5459
|
+
this.view.viewState.printing = false;
|
|
5460
|
+
this.view.requestMeasure();
|
|
5461
|
+
}, 500);
|
|
5462
|
+
}
|
|
5396
5463
|
updateGaps(gaps) {
|
|
5397
5464
|
if (this.gapIntersection && (gaps.length != this.gaps.length || this.gaps.some((g, i) => g != gaps[i]))) {
|
|
5398
5465
|
this.gapIntersection.disconnect();
|
|
@@ -5610,6 +5677,7 @@ class DOMObserver {
|
|
|
5610
5677
|
dom.removeEventListener("scroll", this.onScroll);
|
|
5611
5678
|
window.removeEventListener("scroll", this.onScroll);
|
|
5612
5679
|
window.removeEventListener("resize", this.onResize);
|
|
5680
|
+
window.removeEventListener("beforeprint", this.onPrint);
|
|
5613
5681
|
this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
|
|
5614
5682
|
clearTimeout(this.parentCheck);
|
|
5615
5683
|
clearTimeout(this.resizeTimeout);
|