@codemirror/view 6.18.0 → 6.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/index.cjs +670 -640
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +670 -640
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -484,6 +484,8 @@ class ContentView {
|
|
|
484
484
|
}
|
|
485
485
|
}
|
|
486
486
|
setDOM(dom) {
|
|
487
|
+
if (this.dom == dom)
|
|
488
|
+
return;
|
|
487
489
|
if (this.dom)
|
|
488
490
|
this.dom.cmView = null;
|
|
489
491
|
this.dom = dom;
|
|
@@ -656,113 +658,6 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
|
|
|
656
658
|
replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
|
|
657
659
|
}
|
|
658
660
|
|
|
659
|
-
const LineBreakPlaceholder = "\uffff";
|
|
660
|
-
class DOMReader {
|
|
661
|
-
constructor(points, state$1) {
|
|
662
|
-
this.points = points;
|
|
663
|
-
this.text = "";
|
|
664
|
-
this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
|
|
665
|
-
}
|
|
666
|
-
append(text) {
|
|
667
|
-
this.text += text;
|
|
668
|
-
}
|
|
669
|
-
lineBreak() {
|
|
670
|
-
this.text += LineBreakPlaceholder;
|
|
671
|
-
}
|
|
672
|
-
readRange(start, end) {
|
|
673
|
-
if (!start)
|
|
674
|
-
return this;
|
|
675
|
-
let parent = start.parentNode;
|
|
676
|
-
for (let cur = start;;) {
|
|
677
|
-
this.findPointBefore(parent, cur);
|
|
678
|
-
let oldLen = this.text.length;
|
|
679
|
-
this.readNode(cur);
|
|
680
|
-
let next = cur.nextSibling;
|
|
681
|
-
if (next == end)
|
|
682
|
-
break;
|
|
683
|
-
let view = ContentView.get(cur), nextView = ContentView.get(next);
|
|
684
|
-
if (view && nextView ? view.breakAfter :
|
|
685
|
-
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
686
|
-
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
|
|
687
|
-
this.lineBreak();
|
|
688
|
-
cur = next;
|
|
689
|
-
}
|
|
690
|
-
this.findPointBefore(parent, end);
|
|
691
|
-
return this;
|
|
692
|
-
}
|
|
693
|
-
readTextNode(node) {
|
|
694
|
-
let text = node.nodeValue;
|
|
695
|
-
for (let point of this.points)
|
|
696
|
-
if (point.node == node)
|
|
697
|
-
point.pos = this.text.length + Math.min(point.offset, text.length);
|
|
698
|
-
for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
|
|
699
|
-
let nextBreak = -1, breakSize = 1, m;
|
|
700
|
-
if (this.lineSeparator) {
|
|
701
|
-
nextBreak = text.indexOf(this.lineSeparator, off);
|
|
702
|
-
breakSize = this.lineSeparator.length;
|
|
703
|
-
}
|
|
704
|
-
else if (m = re.exec(text)) {
|
|
705
|
-
nextBreak = m.index;
|
|
706
|
-
breakSize = m[0].length;
|
|
707
|
-
}
|
|
708
|
-
this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
|
|
709
|
-
if (nextBreak < 0)
|
|
710
|
-
break;
|
|
711
|
-
this.lineBreak();
|
|
712
|
-
if (breakSize > 1)
|
|
713
|
-
for (let point of this.points)
|
|
714
|
-
if (point.node == node && point.pos > this.text.length)
|
|
715
|
-
point.pos -= breakSize - 1;
|
|
716
|
-
off = nextBreak + breakSize;
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
readNode(node) {
|
|
720
|
-
if (node.cmIgnore)
|
|
721
|
-
return;
|
|
722
|
-
let view = ContentView.get(node);
|
|
723
|
-
let fromView = view && view.overrideDOMText;
|
|
724
|
-
if (fromView != null) {
|
|
725
|
-
this.findPointInside(node, fromView.length);
|
|
726
|
-
for (let i = fromView.iter(); !i.next().done;) {
|
|
727
|
-
if (i.lineBreak)
|
|
728
|
-
this.lineBreak();
|
|
729
|
-
else
|
|
730
|
-
this.append(i.value);
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
else if (node.nodeType == 3) {
|
|
734
|
-
this.readTextNode(node);
|
|
735
|
-
}
|
|
736
|
-
else if (node.nodeName == "BR") {
|
|
737
|
-
if (node.nextSibling)
|
|
738
|
-
this.lineBreak();
|
|
739
|
-
}
|
|
740
|
-
else if (node.nodeType == 1) {
|
|
741
|
-
this.readRange(node.firstChild, null);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
findPointBefore(node, next) {
|
|
745
|
-
for (let point of this.points)
|
|
746
|
-
if (point.node == node && node.childNodes[point.offset] == next)
|
|
747
|
-
point.pos = this.text.length;
|
|
748
|
-
}
|
|
749
|
-
findPointInside(node, maxLen) {
|
|
750
|
-
for (let point of this.points)
|
|
751
|
-
if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
|
|
752
|
-
point.pos = this.text.length + Math.min(maxLen, point.offset);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
function isBlockElement(node) {
|
|
756
|
-
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
757
|
-
}
|
|
758
|
-
class DOMPoint {
|
|
759
|
-
constructor(node, offset) {
|
|
760
|
-
this.node = node;
|
|
761
|
-
this.offset = offset;
|
|
762
|
-
this.pos = -1;
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
|
|
766
661
|
let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
|
|
767
662
|
let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
|
|
768
663
|
const ie_edge = /Edge\/(\d+)/.exec(nav.userAgent);
|
|
@@ -1198,308 +1093,27 @@ function getAttrs(dom) {
|
|
|
1198
1093
|
return attrs;
|
|
1199
1094
|
}
|
|
1200
1095
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
/**
|
|
1210
|
-
Compare this instance to another instance of the same type.
|
|
1211
|
-
(TypeScript can't express this, but only instances of the same
|
|
1212
|
-
specific class will be passed to this method.) This is used to
|
|
1213
|
-
avoid redrawing widgets when they are replaced by a new
|
|
1214
|
-
decoration of the same type. The default implementation just
|
|
1215
|
-
returns `false`, which will cause new instances of the widget to
|
|
1216
|
-
always be redrawn.
|
|
1217
|
-
*/
|
|
1218
|
-
eq(widget) { return false; }
|
|
1219
|
-
/**
|
|
1220
|
-
Update a DOM element created by a widget of the same type (but
|
|
1221
|
-
different, non-`eq` content) to reflect this widget. May return
|
|
1222
|
-
true to indicate that it could update, false to indicate it
|
|
1223
|
-
couldn't (in which case the widget will be redrawn). The default
|
|
1224
|
-
implementation just returns false.
|
|
1225
|
-
*/
|
|
1226
|
-
updateDOM(dom, view) { return false; }
|
|
1227
|
-
/**
|
|
1228
|
-
@internal
|
|
1229
|
-
*/
|
|
1230
|
-
compare(other) {
|
|
1231
|
-
return this == other || this.constructor == other.constructor && this.eq(other);
|
|
1096
|
+
class LineView extends ContentView {
|
|
1097
|
+
constructor() {
|
|
1098
|
+
super(...arguments);
|
|
1099
|
+
this.children = [];
|
|
1100
|
+
this.length = 0;
|
|
1101
|
+
this.prevAttrs = undefined;
|
|
1102
|
+
this.attrs = null;
|
|
1103
|
+
this.breakAfter = 0;
|
|
1232
1104
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
*/
|
|
1246
|
-
get lineBreaks() { return 0; }
|
|
1247
|
-
/**
|
|
1248
|
-
Can be used to configure which kinds of events inside the widget
|
|
1249
|
-
should be ignored by the editor. The default is to ignore all
|
|
1250
|
-
events.
|
|
1251
|
-
*/
|
|
1252
|
-
ignoreEvent(event) { return true; }
|
|
1253
|
-
/**
|
|
1254
|
-
Override the way screen coordinates for positions at/in the
|
|
1255
|
-
widget are found. `pos` will be the offset into the widget, and
|
|
1256
|
-
`side` the side of the position that is being queried—less than
|
|
1257
|
-
zero for before, greater than zero for after, and zero for
|
|
1258
|
-
directly at that position.
|
|
1259
|
-
*/
|
|
1260
|
-
coordsAt(dom, pos, side) { return null; }
|
|
1261
|
-
/**
|
|
1262
|
-
@internal
|
|
1263
|
-
*/
|
|
1264
|
-
get isHidden() { return false; }
|
|
1265
|
-
/**
|
|
1266
|
-
This is called when the an instance of the widget is removed
|
|
1267
|
-
from the editor view.
|
|
1268
|
-
*/
|
|
1269
|
-
destroy(dom) { }
|
|
1270
|
-
}
|
|
1271
|
-
/**
|
|
1272
|
-
The different types of blocks that can occur in an editor view.
|
|
1273
|
-
*/
|
|
1274
|
-
exports.BlockType = void 0;
|
|
1275
|
-
(function (BlockType) {
|
|
1276
|
-
/**
|
|
1277
|
-
A line of text.
|
|
1278
|
-
*/
|
|
1279
|
-
BlockType[BlockType["Text"] = 0] = "Text";
|
|
1280
|
-
/**
|
|
1281
|
-
A block widget associated with the position after it.
|
|
1282
|
-
*/
|
|
1283
|
-
BlockType[BlockType["WidgetBefore"] = 1] = "WidgetBefore";
|
|
1284
|
-
/**
|
|
1285
|
-
A block widget associated with the position before it.
|
|
1286
|
-
*/
|
|
1287
|
-
BlockType[BlockType["WidgetAfter"] = 2] = "WidgetAfter";
|
|
1288
|
-
/**
|
|
1289
|
-
A block widget [replacing](https://codemirror.net/6/docs/ref/#view.Decoration^replace) a range of content.
|
|
1290
|
-
*/
|
|
1291
|
-
BlockType[BlockType["WidgetRange"] = 3] = "WidgetRange";
|
|
1292
|
-
})(exports.BlockType || (exports.BlockType = {}));
|
|
1293
|
-
/**
|
|
1294
|
-
A decoration provides information on how to draw or style a piece
|
|
1295
|
-
of content. You'll usually use it wrapped in a
|
|
1296
|
-
[`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position.
|
|
1297
|
-
@nonabstract
|
|
1298
|
-
*/
|
|
1299
|
-
class Decoration extends state.RangeValue {
|
|
1300
|
-
constructor(
|
|
1301
|
-
/**
|
|
1302
|
-
@internal
|
|
1303
|
-
*/
|
|
1304
|
-
startSide,
|
|
1305
|
-
/**
|
|
1306
|
-
@internal
|
|
1307
|
-
*/
|
|
1308
|
-
endSide,
|
|
1309
|
-
/**
|
|
1310
|
-
@internal
|
|
1311
|
-
*/
|
|
1312
|
-
widget,
|
|
1313
|
-
/**
|
|
1314
|
-
The config object used to create this decoration. You can
|
|
1315
|
-
include additional properties in there to store metadata about
|
|
1316
|
-
your decoration.
|
|
1317
|
-
*/
|
|
1318
|
-
spec) {
|
|
1319
|
-
super();
|
|
1320
|
-
this.startSide = startSide;
|
|
1321
|
-
this.endSide = endSide;
|
|
1322
|
-
this.widget = widget;
|
|
1323
|
-
this.spec = spec;
|
|
1324
|
-
}
|
|
1325
|
-
/**
|
|
1326
|
-
@internal
|
|
1327
|
-
*/
|
|
1328
|
-
get heightRelevant() { return false; }
|
|
1329
|
-
/**
|
|
1330
|
-
Create a mark decoration, which influences the styling of the
|
|
1331
|
-
content in its range. Nested mark decorations will cause nested
|
|
1332
|
-
DOM elements to be created. Nesting order is determined by
|
|
1333
|
-
precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), with
|
|
1334
|
-
the higher-precedence decorations creating the inner DOM nodes.
|
|
1335
|
-
Such elements are split on line boundaries and on the boundaries
|
|
1336
|
-
of lower-precedence decorations.
|
|
1337
|
-
*/
|
|
1338
|
-
static mark(spec) {
|
|
1339
|
-
return new MarkDecoration(spec);
|
|
1340
|
-
}
|
|
1341
|
-
/**
|
|
1342
|
-
Create a widget decoration, which displays a DOM element at the
|
|
1343
|
-
given position.
|
|
1344
|
-
*/
|
|
1345
|
-
static widget(spec) {
|
|
1346
|
-
let side = Math.max(-10000, Math.min(10000, spec.side || 0)), block = !!spec.block;
|
|
1347
|
-
side += (block && !spec.inlineOrder)
|
|
1348
|
-
? (side > 0 ? 300000000 /* Side.BlockAfter */ : -400000000 /* Side.BlockBefore */)
|
|
1349
|
-
: (side > 0 ? 100000000 /* Side.InlineAfter */ : -100000000 /* Side.InlineBefore */);
|
|
1350
|
-
return new PointDecoration(spec, side, side, block, spec.widget || null, false);
|
|
1351
|
-
}
|
|
1352
|
-
/**
|
|
1353
|
-
Create a replace decoration which replaces the given range with
|
|
1354
|
-
a widget, or simply hides it.
|
|
1355
|
-
*/
|
|
1356
|
-
static replace(spec) {
|
|
1357
|
-
let block = !!spec.block, startSide, endSide;
|
|
1358
|
-
if (spec.isBlockGap) {
|
|
1359
|
-
startSide = -500000000 /* Side.GapStart */;
|
|
1360
|
-
endSide = 400000000 /* Side.GapEnd */;
|
|
1361
|
-
}
|
|
1362
|
-
else {
|
|
1363
|
-
let { start, end } = getInclusive(spec, block);
|
|
1364
|
-
startSide = (start ? (block ? -300000000 /* Side.BlockIncStart */ : -1 /* Side.InlineIncStart */) : 500000000 /* Side.NonIncStart */) - 1;
|
|
1365
|
-
endSide = (end ? (block ? 200000000 /* Side.BlockIncEnd */ : 1 /* Side.InlineIncEnd */) : -600000000 /* Side.NonIncEnd */) + 1;
|
|
1366
|
-
}
|
|
1367
|
-
return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
|
|
1368
|
-
}
|
|
1369
|
-
/**
|
|
1370
|
-
Create a line decoration, which can add DOM attributes to the
|
|
1371
|
-
line starting at the given position.
|
|
1372
|
-
*/
|
|
1373
|
-
static line(spec) {
|
|
1374
|
-
return new LineDecoration(spec);
|
|
1375
|
-
}
|
|
1376
|
-
/**
|
|
1377
|
-
Build a [`DecorationSet`](https://codemirror.net/6/docs/ref/#view.DecorationSet) from the given
|
|
1378
|
-
decorated range or ranges. If the ranges aren't already sorted,
|
|
1379
|
-
pass `true` for `sort` to make the library sort them for you.
|
|
1380
|
-
*/
|
|
1381
|
-
static set(of, sort = false) {
|
|
1382
|
-
return state.RangeSet.of(of, sort);
|
|
1383
|
-
}
|
|
1384
|
-
/**
|
|
1385
|
-
@internal
|
|
1386
|
-
*/
|
|
1387
|
-
hasHeight() { return this.widget ? this.widget.estimatedHeight > -1 : false; }
|
|
1388
|
-
}
|
|
1389
|
-
/**
|
|
1390
|
-
The empty set of decorations.
|
|
1391
|
-
*/
|
|
1392
|
-
Decoration.none = state.RangeSet.empty;
|
|
1393
|
-
class MarkDecoration extends Decoration {
|
|
1394
|
-
constructor(spec) {
|
|
1395
|
-
let { start, end } = getInclusive(spec);
|
|
1396
|
-
super(start ? -1 /* Side.InlineIncStart */ : 500000000 /* Side.NonIncStart */, end ? 1 /* Side.InlineIncEnd */ : -600000000 /* Side.NonIncEnd */, null, spec);
|
|
1397
|
-
this.tagName = spec.tagName || "span";
|
|
1398
|
-
this.class = spec.class || "";
|
|
1399
|
-
this.attrs = spec.attributes || null;
|
|
1400
|
-
}
|
|
1401
|
-
eq(other) {
|
|
1402
|
-
var _a, _b;
|
|
1403
|
-
return this == other ||
|
|
1404
|
-
other instanceof MarkDecoration &&
|
|
1405
|
-
this.tagName == other.tagName &&
|
|
1406
|
-
(this.class || ((_a = this.attrs) === null || _a === void 0 ? void 0 : _a.class)) == (other.class || ((_b = other.attrs) === null || _b === void 0 ? void 0 : _b.class)) &&
|
|
1407
|
-
attrsEq(this.attrs, other.attrs, "class");
|
|
1408
|
-
}
|
|
1409
|
-
range(from, to = from) {
|
|
1410
|
-
if (from >= to)
|
|
1411
|
-
throw new RangeError("Mark decorations may not be empty");
|
|
1412
|
-
return super.range(from, to);
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
MarkDecoration.prototype.point = false;
|
|
1416
|
-
class LineDecoration extends Decoration {
|
|
1417
|
-
constructor(spec) {
|
|
1418
|
-
super(-200000000 /* Side.Line */, -200000000 /* Side.Line */, null, spec);
|
|
1419
|
-
}
|
|
1420
|
-
eq(other) {
|
|
1421
|
-
return other instanceof LineDecoration &&
|
|
1422
|
-
this.spec.class == other.spec.class &&
|
|
1423
|
-
attrsEq(this.spec.attributes, other.spec.attributes);
|
|
1424
|
-
}
|
|
1425
|
-
range(from, to = from) {
|
|
1426
|
-
if (to != from)
|
|
1427
|
-
throw new RangeError("Line decoration ranges must be zero-length");
|
|
1428
|
-
return super.range(from, to);
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
LineDecoration.prototype.mapMode = state.MapMode.TrackBefore;
|
|
1432
|
-
LineDecoration.prototype.point = true;
|
|
1433
|
-
class PointDecoration extends Decoration {
|
|
1434
|
-
constructor(spec, startSide, endSide, block, widget, isReplace) {
|
|
1435
|
-
super(startSide, endSide, widget, spec);
|
|
1436
|
-
this.block = block;
|
|
1437
|
-
this.isReplace = isReplace;
|
|
1438
|
-
this.mapMode = !block ? state.MapMode.TrackDel : startSide <= 0 ? state.MapMode.TrackBefore : state.MapMode.TrackAfter;
|
|
1439
|
-
}
|
|
1440
|
-
// Only relevant when this.block == true
|
|
1441
|
-
get type() {
|
|
1442
|
-
return this.startSide < this.endSide ? exports.BlockType.WidgetRange
|
|
1443
|
-
: this.startSide <= 0 ? exports.BlockType.WidgetBefore : exports.BlockType.WidgetAfter;
|
|
1444
|
-
}
|
|
1445
|
-
get heightRelevant() {
|
|
1446
|
-
return this.block || !!this.widget && (this.widget.estimatedHeight >= 5 || this.widget.lineBreaks > 0);
|
|
1447
|
-
}
|
|
1448
|
-
eq(other) {
|
|
1449
|
-
return other instanceof PointDecoration &&
|
|
1450
|
-
widgetsEq(this.widget, other.widget) &&
|
|
1451
|
-
this.block == other.block &&
|
|
1452
|
-
this.startSide == other.startSide && this.endSide == other.endSide;
|
|
1453
|
-
}
|
|
1454
|
-
range(from, to = from) {
|
|
1455
|
-
if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
|
|
1456
|
-
throw new RangeError("Invalid range for replacement decoration");
|
|
1457
|
-
if (!this.isReplace && to != from)
|
|
1458
|
-
throw new RangeError("Widget decorations can only have zero-length ranges");
|
|
1459
|
-
return super.range(from, to);
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
|
-
PointDecoration.prototype.point = true;
|
|
1463
|
-
function getInclusive(spec, block = false) {
|
|
1464
|
-
let { inclusiveStart: start, inclusiveEnd: end } = spec;
|
|
1465
|
-
if (start == null)
|
|
1466
|
-
start = spec.inclusive;
|
|
1467
|
-
if (end == null)
|
|
1468
|
-
end = spec.inclusive;
|
|
1469
|
-
return { start: start !== null && start !== void 0 ? start : block, end: end !== null && end !== void 0 ? end : block };
|
|
1470
|
-
}
|
|
1471
|
-
function widgetsEq(a, b) {
|
|
1472
|
-
return a == b || !!(a && b && a.compare(b));
|
|
1473
|
-
}
|
|
1474
|
-
function addRange(from, to, ranges, margin = 0) {
|
|
1475
|
-
let last = ranges.length - 1;
|
|
1476
|
-
if (last >= 0 && ranges[last] + margin >= from)
|
|
1477
|
-
ranges[last] = Math.max(ranges[last], to);
|
|
1478
|
-
else
|
|
1479
|
-
ranges.push(from, to);
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
class LineView extends ContentView {
|
|
1483
|
-
constructor() {
|
|
1484
|
-
super(...arguments);
|
|
1485
|
-
this.children = [];
|
|
1486
|
-
this.length = 0;
|
|
1487
|
-
this.prevAttrs = undefined;
|
|
1488
|
-
this.attrs = null;
|
|
1489
|
-
this.breakAfter = 0;
|
|
1490
|
-
}
|
|
1491
|
-
// Consumes source
|
|
1492
|
-
merge(from, to, source, hasStart, openStart, openEnd) {
|
|
1493
|
-
if (source) {
|
|
1494
|
-
if (!(source instanceof LineView))
|
|
1495
|
-
return false;
|
|
1496
|
-
if (!this.dom)
|
|
1497
|
-
source.transferDOM(this); // Reuse source.dom when appropriate
|
|
1498
|
-
}
|
|
1499
|
-
if (hasStart)
|
|
1500
|
-
this.setDeco(source ? source.attrs : null);
|
|
1501
|
-
mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
|
|
1502
|
-
return true;
|
|
1105
|
+
// Consumes source
|
|
1106
|
+
merge(from, to, source, hasStart, openStart, openEnd) {
|
|
1107
|
+
if (source) {
|
|
1108
|
+
if (!(source instanceof LineView))
|
|
1109
|
+
return false;
|
|
1110
|
+
if (!this.dom)
|
|
1111
|
+
source.transferDOM(this); // Reuse source.dom when appropriate
|
|
1112
|
+
}
|
|
1113
|
+
if (hasStart)
|
|
1114
|
+
this.setDeco(source ? source.attrs : null);
|
|
1115
|
+
mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
|
|
1116
|
+
return true;
|
|
1503
1117
|
}
|
|
1504
1118
|
split(at) {
|
|
1505
1119
|
let end = new LineView;
|
|
@@ -1621,7 +1235,7 @@ class LineView extends ContentView {
|
|
|
1621
1235
|
return rect;
|
|
1622
1236
|
}
|
|
1623
1237
|
become(_other) { return false; }
|
|
1624
|
-
|
|
1238
|
+
covers() { return true; }
|
|
1625
1239
|
static find(docView, pos) {
|
|
1626
1240
|
for (let i = 0, off = 0; i < docView.children.length; i++) {
|
|
1627
1241
|
let block = docView.children[i], end = off + block.length;
|
|
@@ -1637,11 +1251,11 @@ class LineView extends ContentView {
|
|
|
1637
1251
|
}
|
|
1638
1252
|
}
|
|
1639
1253
|
class BlockWidgetView extends ContentView {
|
|
1640
|
-
constructor(widget, length,
|
|
1254
|
+
constructor(widget, length, deco) {
|
|
1641
1255
|
super();
|
|
1642
1256
|
this.widget = widget;
|
|
1643
1257
|
this.length = length;
|
|
1644
|
-
this.
|
|
1258
|
+
this.deco = deco;
|
|
1645
1259
|
this.breakAfter = 0;
|
|
1646
1260
|
this.prevWidget = null;
|
|
1647
1261
|
}
|
|
@@ -1658,7 +1272,7 @@ class BlockWidgetView extends ContentView {
|
|
|
1658
1272
|
split(at) {
|
|
1659
1273
|
let len = this.length - at;
|
|
1660
1274
|
this.length = at;
|
|
1661
|
-
let end = new BlockWidgetView(this.widget, len, this.
|
|
1275
|
+
let end = new BlockWidgetView(this.widget, len, this.deco);
|
|
1662
1276
|
end.breakAfter = this.breakAfter;
|
|
1663
1277
|
return end;
|
|
1664
1278
|
}
|
|
@@ -1685,7 +1299,7 @@ class BlockWidgetView extends ContentView {
|
|
|
1685
1299
|
this.prevWidget = this.widget;
|
|
1686
1300
|
this.widget = other.widget;
|
|
1687
1301
|
this.length = other.length;
|
|
1688
|
-
this.
|
|
1302
|
+
this.deco = other.deco;
|
|
1689
1303
|
this.breakAfter = other.breakAfter;
|
|
1690
1304
|
return true;
|
|
1691
1305
|
}
|
|
@@ -1698,12 +1312,297 @@ class BlockWidgetView extends ContentView {
|
|
|
1698
1312
|
coordsAt(pos, side) {
|
|
1699
1313
|
return this.widget.coordsAt(this.dom, pos, side);
|
|
1700
1314
|
}
|
|
1701
|
-
destroy() {
|
|
1702
|
-
super.destroy();
|
|
1703
|
-
if (this.dom)
|
|
1704
|
-
this.widget.destroy(this.dom);
|
|
1315
|
+
destroy() {
|
|
1316
|
+
super.destroy();
|
|
1317
|
+
if (this.dom)
|
|
1318
|
+
this.widget.destroy(this.dom);
|
|
1319
|
+
}
|
|
1320
|
+
covers(side) {
|
|
1321
|
+
let { startSide, endSide } = this.deco;
|
|
1322
|
+
return startSide == endSide ? false : side < 0 ? startSide < 0 : endSide > 0;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/**
|
|
1327
|
+
Widgets added to the content are described by subclasses of this
|
|
1328
|
+
class. Using a description object like that makes it possible to
|
|
1329
|
+
delay creating of the DOM structure for a widget until it is
|
|
1330
|
+
needed, and to avoid redrawing widgets even if the decorations
|
|
1331
|
+
that define them are recreated.
|
|
1332
|
+
*/
|
|
1333
|
+
class WidgetType {
|
|
1334
|
+
/**
|
|
1335
|
+
Compare this instance to another instance of the same type.
|
|
1336
|
+
(TypeScript can't express this, but only instances of the same
|
|
1337
|
+
specific class will be passed to this method.) This is used to
|
|
1338
|
+
avoid redrawing widgets when they are replaced by a new
|
|
1339
|
+
decoration of the same type. The default implementation just
|
|
1340
|
+
returns `false`, which will cause new instances of the widget to
|
|
1341
|
+
always be redrawn.
|
|
1342
|
+
*/
|
|
1343
|
+
eq(widget) { return false; }
|
|
1344
|
+
/**
|
|
1345
|
+
Update a DOM element created by a widget of the same type (but
|
|
1346
|
+
different, non-`eq` content) to reflect this widget. May return
|
|
1347
|
+
true to indicate that it could update, false to indicate it
|
|
1348
|
+
couldn't (in which case the widget will be redrawn). The default
|
|
1349
|
+
implementation just returns false.
|
|
1350
|
+
*/
|
|
1351
|
+
updateDOM(dom, view) { return false; }
|
|
1352
|
+
/**
|
|
1353
|
+
@internal
|
|
1354
|
+
*/
|
|
1355
|
+
compare(other) {
|
|
1356
|
+
return this == other || this.constructor == other.constructor && this.eq(other);
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
The estimated height this widget will have, to be used when
|
|
1360
|
+
estimating the height of content that hasn't been drawn. May
|
|
1361
|
+
return -1 to indicate you don't know. The default implementation
|
|
1362
|
+
returns -1.
|
|
1363
|
+
*/
|
|
1364
|
+
get estimatedHeight() { return -1; }
|
|
1365
|
+
/**
|
|
1366
|
+
For inline widgets that are displayed inline (as opposed to
|
|
1367
|
+
`inline-block`) and introduce line breaks (through `<br>` tags
|
|
1368
|
+
or textual newlines), this must indicate the amount of line
|
|
1369
|
+
breaks they introduce. Defaults to 0.
|
|
1370
|
+
*/
|
|
1371
|
+
get lineBreaks() { return 0; }
|
|
1372
|
+
/**
|
|
1373
|
+
Can be used to configure which kinds of events inside the widget
|
|
1374
|
+
should be ignored by the editor. The default is to ignore all
|
|
1375
|
+
events.
|
|
1376
|
+
*/
|
|
1377
|
+
ignoreEvent(event) { return true; }
|
|
1378
|
+
/**
|
|
1379
|
+
Override the way screen coordinates for positions at/in the
|
|
1380
|
+
widget are found. `pos` will be the offset into the widget, and
|
|
1381
|
+
`side` the side of the position that is being queried—less than
|
|
1382
|
+
zero for before, greater than zero for after, and zero for
|
|
1383
|
+
directly at that position.
|
|
1384
|
+
*/
|
|
1385
|
+
coordsAt(dom, pos, side) { return null; }
|
|
1386
|
+
/**
|
|
1387
|
+
@internal
|
|
1388
|
+
*/
|
|
1389
|
+
get isHidden() { return false; }
|
|
1390
|
+
/**
|
|
1391
|
+
This is called when the an instance of the widget is removed
|
|
1392
|
+
from the editor view.
|
|
1393
|
+
*/
|
|
1394
|
+
destroy(dom) { }
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
The different types of blocks that can occur in an editor view.
|
|
1398
|
+
*/
|
|
1399
|
+
exports.BlockType = void 0;
|
|
1400
|
+
(function (BlockType) {
|
|
1401
|
+
/**
|
|
1402
|
+
A line of text.
|
|
1403
|
+
*/
|
|
1404
|
+
BlockType[BlockType["Text"] = 0] = "Text";
|
|
1405
|
+
/**
|
|
1406
|
+
A block widget associated with the position after it.
|
|
1407
|
+
*/
|
|
1408
|
+
BlockType[BlockType["WidgetBefore"] = 1] = "WidgetBefore";
|
|
1409
|
+
/**
|
|
1410
|
+
A block widget associated with the position before it.
|
|
1411
|
+
*/
|
|
1412
|
+
BlockType[BlockType["WidgetAfter"] = 2] = "WidgetAfter";
|
|
1413
|
+
/**
|
|
1414
|
+
A block widget [replacing](https://codemirror.net/6/docs/ref/#view.Decoration^replace) a range of content.
|
|
1415
|
+
*/
|
|
1416
|
+
BlockType[BlockType["WidgetRange"] = 3] = "WidgetRange";
|
|
1417
|
+
})(exports.BlockType || (exports.BlockType = {}));
|
|
1418
|
+
/**
|
|
1419
|
+
A decoration provides information on how to draw or style a piece
|
|
1420
|
+
of content. You'll usually use it wrapped in a
|
|
1421
|
+
[`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position.
|
|
1422
|
+
@nonabstract
|
|
1423
|
+
*/
|
|
1424
|
+
class Decoration extends state.RangeValue {
|
|
1425
|
+
constructor(
|
|
1426
|
+
/**
|
|
1427
|
+
@internal
|
|
1428
|
+
*/
|
|
1429
|
+
startSide,
|
|
1430
|
+
/**
|
|
1431
|
+
@internal
|
|
1432
|
+
*/
|
|
1433
|
+
endSide,
|
|
1434
|
+
/**
|
|
1435
|
+
@internal
|
|
1436
|
+
*/
|
|
1437
|
+
widget,
|
|
1438
|
+
/**
|
|
1439
|
+
The config object used to create this decoration. You can
|
|
1440
|
+
include additional properties in there to store metadata about
|
|
1441
|
+
your decoration.
|
|
1442
|
+
*/
|
|
1443
|
+
spec) {
|
|
1444
|
+
super();
|
|
1445
|
+
this.startSide = startSide;
|
|
1446
|
+
this.endSide = endSide;
|
|
1447
|
+
this.widget = widget;
|
|
1448
|
+
this.spec = spec;
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
@internal
|
|
1452
|
+
*/
|
|
1453
|
+
get heightRelevant() { return false; }
|
|
1454
|
+
/**
|
|
1455
|
+
Create a mark decoration, which influences the styling of the
|
|
1456
|
+
content in its range. Nested mark decorations will cause nested
|
|
1457
|
+
DOM elements to be created. Nesting order is determined by
|
|
1458
|
+
precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), with
|
|
1459
|
+
the higher-precedence decorations creating the inner DOM nodes.
|
|
1460
|
+
Such elements are split on line boundaries and on the boundaries
|
|
1461
|
+
of lower-precedence decorations.
|
|
1462
|
+
*/
|
|
1463
|
+
static mark(spec) {
|
|
1464
|
+
return new MarkDecoration(spec);
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
Create a widget decoration, which displays a DOM element at the
|
|
1468
|
+
given position.
|
|
1469
|
+
*/
|
|
1470
|
+
static widget(spec) {
|
|
1471
|
+
let side = Math.max(-10000, Math.min(10000, spec.side || 0)), block = !!spec.block;
|
|
1472
|
+
side += (block && !spec.inlineOrder)
|
|
1473
|
+
? (side > 0 ? 300000000 /* Side.BlockAfter */ : -400000000 /* Side.BlockBefore */)
|
|
1474
|
+
: (side > 0 ? 100000000 /* Side.InlineAfter */ : -100000000 /* Side.InlineBefore */);
|
|
1475
|
+
return new PointDecoration(spec, side, side, block, spec.widget || null, false);
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
Create a replace decoration which replaces the given range with
|
|
1479
|
+
a widget, or simply hides it.
|
|
1480
|
+
*/
|
|
1481
|
+
static replace(spec) {
|
|
1482
|
+
let block = !!spec.block, startSide, endSide;
|
|
1483
|
+
if (spec.isBlockGap) {
|
|
1484
|
+
startSide = -500000000 /* Side.GapStart */;
|
|
1485
|
+
endSide = 400000000 /* Side.GapEnd */;
|
|
1486
|
+
}
|
|
1487
|
+
else {
|
|
1488
|
+
let { start, end } = getInclusive(spec, block);
|
|
1489
|
+
startSide = (start ? (block ? -300000000 /* Side.BlockIncStart */ : -1 /* Side.InlineIncStart */) : 500000000 /* Side.NonIncStart */) - 1;
|
|
1490
|
+
endSide = (end ? (block ? 200000000 /* Side.BlockIncEnd */ : 1 /* Side.InlineIncEnd */) : -600000000 /* Side.NonIncEnd */) + 1;
|
|
1491
|
+
}
|
|
1492
|
+
return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
Create a line decoration, which can add DOM attributes to the
|
|
1496
|
+
line starting at the given position.
|
|
1497
|
+
*/
|
|
1498
|
+
static line(spec) {
|
|
1499
|
+
return new LineDecoration(spec);
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
Build a [`DecorationSet`](https://codemirror.net/6/docs/ref/#view.DecorationSet) from the given
|
|
1503
|
+
decorated range or ranges. If the ranges aren't already sorted,
|
|
1504
|
+
pass `true` for `sort` to make the library sort them for you.
|
|
1505
|
+
*/
|
|
1506
|
+
static set(of, sort = false) {
|
|
1507
|
+
return state.RangeSet.of(of, sort);
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
@internal
|
|
1511
|
+
*/
|
|
1512
|
+
hasHeight() { return this.widget ? this.widget.estimatedHeight > -1 : false; }
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
The empty set of decorations.
|
|
1516
|
+
*/
|
|
1517
|
+
Decoration.none = state.RangeSet.empty;
|
|
1518
|
+
class MarkDecoration extends Decoration {
|
|
1519
|
+
constructor(spec) {
|
|
1520
|
+
let { start, end } = getInclusive(spec);
|
|
1521
|
+
super(start ? -1 /* Side.InlineIncStart */ : 500000000 /* Side.NonIncStart */, end ? 1 /* Side.InlineIncEnd */ : -600000000 /* Side.NonIncEnd */, null, spec);
|
|
1522
|
+
this.tagName = spec.tagName || "span";
|
|
1523
|
+
this.class = spec.class || "";
|
|
1524
|
+
this.attrs = spec.attributes || null;
|
|
1525
|
+
}
|
|
1526
|
+
eq(other) {
|
|
1527
|
+
var _a, _b;
|
|
1528
|
+
return this == other ||
|
|
1529
|
+
other instanceof MarkDecoration &&
|
|
1530
|
+
this.tagName == other.tagName &&
|
|
1531
|
+
(this.class || ((_a = this.attrs) === null || _a === void 0 ? void 0 : _a.class)) == (other.class || ((_b = other.attrs) === null || _b === void 0 ? void 0 : _b.class)) &&
|
|
1532
|
+
attrsEq(this.attrs, other.attrs, "class");
|
|
1533
|
+
}
|
|
1534
|
+
range(from, to = from) {
|
|
1535
|
+
if (from >= to)
|
|
1536
|
+
throw new RangeError("Mark decorations may not be empty");
|
|
1537
|
+
return super.range(from, to);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
MarkDecoration.prototype.point = false;
|
|
1541
|
+
class LineDecoration extends Decoration {
|
|
1542
|
+
constructor(spec) {
|
|
1543
|
+
super(-200000000 /* Side.Line */, -200000000 /* Side.Line */, null, spec);
|
|
1544
|
+
}
|
|
1545
|
+
eq(other) {
|
|
1546
|
+
return other instanceof LineDecoration &&
|
|
1547
|
+
this.spec.class == other.spec.class &&
|
|
1548
|
+
attrsEq(this.spec.attributes, other.spec.attributes);
|
|
1549
|
+
}
|
|
1550
|
+
range(from, to = from) {
|
|
1551
|
+
if (to != from)
|
|
1552
|
+
throw new RangeError("Line decoration ranges must be zero-length");
|
|
1553
|
+
return super.range(from, to);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
LineDecoration.prototype.mapMode = state.MapMode.TrackBefore;
|
|
1557
|
+
LineDecoration.prototype.point = true;
|
|
1558
|
+
class PointDecoration extends Decoration {
|
|
1559
|
+
constructor(spec, startSide, endSide, block, widget, isReplace) {
|
|
1560
|
+
super(startSide, endSide, widget, spec);
|
|
1561
|
+
this.block = block;
|
|
1562
|
+
this.isReplace = isReplace;
|
|
1563
|
+
this.mapMode = !block ? state.MapMode.TrackDel : startSide <= 0 ? state.MapMode.TrackBefore : state.MapMode.TrackAfter;
|
|
1564
|
+
}
|
|
1565
|
+
// Only relevant when this.block == true
|
|
1566
|
+
get type() {
|
|
1567
|
+
return this.startSide != this.endSide ? exports.BlockType.WidgetRange
|
|
1568
|
+
: this.startSide <= 0 ? exports.BlockType.WidgetBefore : exports.BlockType.WidgetAfter;
|
|
1569
|
+
}
|
|
1570
|
+
get heightRelevant() {
|
|
1571
|
+
return this.block || !!this.widget && (this.widget.estimatedHeight >= 5 || this.widget.lineBreaks > 0);
|
|
1572
|
+
}
|
|
1573
|
+
eq(other) {
|
|
1574
|
+
return other instanceof PointDecoration &&
|
|
1575
|
+
widgetsEq(this.widget, other.widget) &&
|
|
1576
|
+
this.block == other.block &&
|
|
1577
|
+
this.startSide == other.startSide && this.endSide == other.endSide;
|
|
1578
|
+
}
|
|
1579
|
+
range(from, to = from) {
|
|
1580
|
+
if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
|
|
1581
|
+
throw new RangeError("Invalid range for replacement decoration");
|
|
1582
|
+
if (!this.isReplace && to != from)
|
|
1583
|
+
throw new RangeError("Widget decorations can only have zero-length ranges");
|
|
1584
|
+
return super.range(from, to);
|
|
1705
1585
|
}
|
|
1706
1586
|
}
|
|
1587
|
+
PointDecoration.prototype.point = true;
|
|
1588
|
+
function getInclusive(spec, block = false) {
|
|
1589
|
+
let { inclusiveStart: start, inclusiveEnd: end } = spec;
|
|
1590
|
+
if (start == null)
|
|
1591
|
+
start = spec.inclusive;
|
|
1592
|
+
if (end == null)
|
|
1593
|
+
end = spec.inclusive;
|
|
1594
|
+
return { start: start !== null && start !== void 0 ? start : block, end: end !== null && end !== void 0 ? end : block };
|
|
1595
|
+
}
|
|
1596
|
+
function widgetsEq(a, b) {
|
|
1597
|
+
return a == b || !!(a && b && a.compare(b));
|
|
1598
|
+
}
|
|
1599
|
+
function addRange(from, to, ranges, margin = 0) {
|
|
1600
|
+
let last = ranges.length - 1;
|
|
1601
|
+
if (last >= 0 && ranges[last] + margin >= from)
|
|
1602
|
+
ranges[last] = Math.max(ranges[last], to);
|
|
1603
|
+
else
|
|
1604
|
+
ranges.push(from, to);
|
|
1605
|
+
}
|
|
1707
1606
|
|
|
1708
1607
|
class ContentBuilder {
|
|
1709
1608
|
constructor(doc, pos, end, disallowBlockEffectsFor) {
|
|
@@ -1729,7 +1628,7 @@ class ContentBuilder {
|
|
|
1729
1628
|
if (this.content.length == 0)
|
|
1730
1629
|
return !this.breakAtStart && this.doc.lineAt(this.pos).from != this.pos;
|
|
1731
1630
|
let last = this.content[this.content.length - 1];
|
|
1732
|
-
return !last.breakAfter
|
|
1631
|
+
return !(last.breakAfter || last instanceof BlockWidgetView && last.deco.endSide < 0);
|
|
1733
1632
|
}
|
|
1734
1633
|
getLine() {
|
|
1735
1634
|
if (!this.curLine) {
|
|
@@ -1754,7 +1653,7 @@ class ContentBuilder {
|
|
|
1754
1653
|
this.flushBuffer();
|
|
1755
1654
|
else
|
|
1756
1655
|
this.pendingBuffer = 0 /* Buf.No */;
|
|
1757
|
-
if (!this.posCovered())
|
|
1656
|
+
if (!openEnd && !this.posCovered())
|
|
1758
1657
|
this.getLine();
|
|
1759
1658
|
}
|
|
1760
1659
|
buildText(length, active, openStart) {
|
|
@@ -1807,10 +1706,9 @@ class ContentBuilder {
|
|
|
1807
1706
|
let len = to - from;
|
|
1808
1707
|
if (deco instanceof PointDecoration) {
|
|
1809
1708
|
if (deco.block) {
|
|
1810
|
-
|
|
1811
|
-
if (type == exports.BlockType.WidgetAfter && !this.posCovered())
|
|
1709
|
+
if (deco.startSide > 0 && !this.posCovered())
|
|
1812
1710
|
this.getLine();
|
|
1813
|
-
this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len,
|
|
1711
|
+
this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, deco));
|
|
1814
1712
|
}
|
|
1815
1713
|
else {
|
|
1816
1714
|
let view = WidgetView.create(deco.widget || new NullWidget("span"), len, len ? 0 : deco.startSide);
|
|
@@ -1945,10 +1843,15 @@ class ViewPlugin {
|
|
|
1945
1843
|
/**
|
|
1946
1844
|
@internal
|
|
1947
1845
|
*/
|
|
1948
|
-
domEventHandlers,
|
|
1846
|
+
domEventHandlers,
|
|
1847
|
+
/**
|
|
1848
|
+
@internal
|
|
1849
|
+
*/
|
|
1850
|
+
domEventObservers, buildExtensions) {
|
|
1949
1851
|
this.id = id;
|
|
1950
1852
|
this.create = create;
|
|
1951
1853
|
this.domEventHandlers = domEventHandlers;
|
|
1854
|
+
this.domEventObservers = domEventObservers;
|
|
1952
1855
|
this.extension = buildExtensions(this);
|
|
1953
1856
|
}
|
|
1954
1857
|
/**
|
|
@@ -1956,8 +1859,8 @@ class ViewPlugin {
|
|
|
1956
1859
|
plugin's value, given an editor view.
|
|
1957
1860
|
*/
|
|
1958
1861
|
static define(create, spec) {
|
|
1959
|
-
const { eventHandlers, provide, decorations: deco } = spec || {};
|
|
1960
|
-
return new ViewPlugin(nextPluginID++, create, eventHandlers, plugin => {
|
|
1862
|
+
const { eventHandlers, eventObservers, provide, decorations: deco } = spec || {};
|
|
1863
|
+
return new ViewPlugin(nextPluginID++, create, eventHandlers, eventObservers, plugin => {
|
|
1961
1864
|
let ext = [viewPlugin.of(plugin)];
|
|
1962
1865
|
if (deco)
|
|
1963
1866
|
ext.push(decorations.of(view => {
|
|
@@ -2706,6 +2609,7 @@ class DocView extends ContentView {
|
|
|
2706
2609
|
this.view = view;
|
|
2707
2610
|
this.decorations = [];
|
|
2708
2611
|
this.dynamicDecorationMap = [];
|
|
2612
|
+
this.domChanged = null;
|
|
2709
2613
|
this.hasComposition = null;
|
|
2710
2614
|
this.markedForComposition = new Set;
|
|
2711
2615
|
// Track a minimum width for the editor. When measuring sizes in
|
|
@@ -2734,6 +2638,7 @@ class DocView extends ContentView {
|
|
|
2734
2638
|
}
|
|
2735
2639
|
// Update the document view to a given state.
|
|
2736
2640
|
update(update) {
|
|
2641
|
+
var _a;
|
|
2737
2642
|
let changedRanges = update.changedRanges;
|
|
2738
2643
|
if (this.minWidth > 0 && changedRanges.length) {
|
|
2739
2644
|
if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
|
|
@@ -2744,7 +2649,15 @@ class DocView extends ContentView {
|
|
|
2744
2649
|
this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
|
|
2745
2650
|
}
|
|
2746
2651
|
}
|
|
2747
|
-
let
|
|
2652
|
+
let readCompositionAt = -1;
|
|
2653
|
+
if (this.view.inputState.composing >= 0) {
|
|
2654
|
+
if ((_a = this.domChanged) === null || _a === void 0 ? void 0 : _a.newSel)
|
|
2655
|
+
readCompositionAt = this.domChanged.newSel.head;
|
|
2656
|
+
else if (!touchesComposition(update.changes, this.hasComposition) && !update.selectionSet)
|
|
2657
|
+
readCompositionAt = update.state.selection.main.head;
|
|
2658
|
+
}
|
|
2659
|
+
let composition = readCompositionAt > -1 ? findCompositionRange(this.view, update.changes, readCompositionAt) : null;
|
|
2660
|
+
this.domChanged = null;
|
|
2748
2661
|
if (this.hasComposition) {
|
|
2749
2662
|
this.markedForComposition.clear();
|
|
2750
2663
|
let { from, to } = this.hasComposition;
|
|
@@ -2859,11 +2772,9 @@ class DocView extends ContentView {
|
|
|
2859
2772
|
cView.flags |= 8 /* ViewFlag.Composition */ | (cView.children.some(c => c.flags & 7 /* ViewFlag.Dirty */) ? 1 /* ViewFlag.ChildDirty */ : 0);
|
|
2860
2773
|
this.markedForComposition.add(cView);
|
|
2861
2774
|
let prev = ContentView.get(dom);
|
|
2862
|
-
if (prev != cView)
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
cView.setDOM(dom);
|
|
2866
|
-
}
|
|
2775
|
+
if (prev && prev != cView)
|
|
2776
|
+
prev.dom = null;
|
|
2777
|
+
cView.setDOM(dom);
|
|
2867
2778
|
};
|
|
2868
2779
|
let pos = this.childPos(composition.range.fromB, 1);
|
|
2869
2780
|
let cView = this.children[pos.i];
|
|
@@ -2886,9 +2797,8 @@ class DocView extends ContentView {
|
|
|
2886
2797
|
let force = this.forceSelection;
|
|
2887
2798
|
this.forceSelection = false;
|
|
2888
2799
|
let main = this.view.state.selection.main;
|
|
2889
|
-
|
|
2890
|
-
let
|
|
2891
|
-
let head = main.empty ? anchor : this.domAtPos(main.head);
|
|
2800
|
+
let anchor = this.moveToLine(this.domAtPos(main.anchor));
|
|
2801
|
+
let head = main.empty ? anchor : this.moveToLine(this.domAtPos(main.head));
|
|
2892
2802
|
// Always reset on Firefox when next to an uneditable node to
|
|
2893
2803
|
// avoid invisible cursor bugs (#111)
|
|
2894
2804
|
if (browser.gecko && main.empty && !this.hasComposition && betweenUneditable(anchor)) {
|
|
@@ -2921,12 +2831,12 @@ class DocView extends ContentView {
|
|
|
2921
2831
|
if (nextTo && nextTo != (1 /* NextTo.Before */ | 2 /* NextTo.After */)) {
|
|
2922
2832
|
let text = nearbyTextNode(anchor.node, anchor.offset, nextTo == 1 /* NextTo.Before */ ? 1 : -1);
|
|
2923
2833
|
if (text)
|
|
2924
|
-
anchor = new DOMPos(text,
|
|
2834
|
+
anchor = new DOMPos(text.node, text.offset);
|
|
2925
2835
|
}
|
|
2926
2836
|
}
|
|
2927
2837
|
rawSel.collapse(anchor.node, anchor.offset);
|
|
2928
|
-
if (main.bidiLevel != null &&
|
|
2929
|
-
|
|
2838
|
+
if (main.bidiLevel != null && rawSel.caretBidiLevel !== undefined)
|
|
2839
|
+
rawSel.caretBidiLevel = main.bidiLevel;
|
|
2930
2840
|
}
|
|
2931
2841
|
else if (rawSel.extend) {
|
|
2932
2842
|
// Selection.extend can be used to create an 'inverted' selection
|
|
@@ -2989,6 +2899,26 @@ class DocView extends ContentView {
|
|
|
2989
2899
|
if (view.docView.posFromDOM(newRange.anchorNode, newRange.anchorOffset) != cursor.from)
|
|
2990
2900
|
sel.collapse(anchorNode, anchorOffset);
|
|
2991
2901
|
}
|
|
2902
|
+
// If a position is in/near a block widget, move it to a nearby text
|
|
2903
|
+
// line, since we don't want the cursor inside a block widget.
|
|
2904
|
+
moveToLine(pos) {
|
|
2905
|
+
// Block widgets will return positions before/after them, which
|
|
2906
|
+
// are thus directly in the document DOM element.
|
|
2907
|
+
let dom = this.dom, newPos;
|
|
2908
|
+
if (pos.node != dom)
|
|
2909
|
+
return pos;
|
|
2910
|
+
for (let i = pos.offset; !newPos && i < dom.childNodes.length; i++) {
|
|
2911
|
+
let view = ContentView.get(dom.childNodes[i]);
|
|
2912
|
+
if (view instanceof LineView)
|
|
2913
|
+
newPos = view.domAtPos(0);
|
|
2914
|
+
}
|
|
2915
|
+
for (let i = pos.offset - 1; !newPos && i >= 0; i--) {
|
|
2916
|
+
let view = ContentView.get(dom.childNodes[i]);
|
|
2917
|
+
if (view instanceof LineView)
|
|
2918
|
+
newPos = view.domAtPos(view.length);
|
|
2919
|
+
}
|
|
2920
|
+
return newPos ? new DOMPos(newPos.node, newPos.offset, true) : pos;
|
|
2921
|
+
}
|
|
2992
2922
|
nearest(dom) {
|
|
2993
2923
|
for (let cur = dom; cur;) {
|
|
2994
2924
|
let domView = ContentView.get(cur);
|
|
@@ -3016,15 +2946,19 @@ class DocView extends ContentView {
|
|
|
3016
2946
|
return this.children[i].domAtPos(off);
|
|
3017
2947
|
}
|
|
3018
2948
|
coordsAt(pos, side) {
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
2949
|
+
let best = null, bestPos = 0;
|
|
2950
|
+
for (let off = this.length, i = this.children.length - 1; i >= 0; i--) {
|
|
2951
|
+
let child = this.children[i], end = off - child.breakAfter, start = end - child.length;
|
|
2952
|
+
if (end < pos)
|
|
2953
|
+
break;
|
|
2954
|
+
if (start <= pos && (start < pos || child.covers(-1)) && (end > pos || child.covers(1)) &&
|
|
2955
|
+
(!best || child instanceof LineView && !(best instanceof LineView && side >= 0))) {
|
|
2956
|
+
best = child;
|
|
2957
|
+
bestPos = start;
|
|
2958
|
+
}
|
|
3026
2959
|
off = start;
|
|
3027
2960
|
}
|
|
2961
|
+
return best ? best.coordsAt(pos - bestPos, side) : null;
|
|
3028
2962
|
}
|
|
3029
2963
|
coordsForChar(pos) {
|
|
3030
2964
|
let { i, off } = this.childPos(pos, 1), child = this.children[i];
|
|
@@ -3187,74 +3121,27 @@ class BlockGapWidget extends WidgetType {
|
|
|
3187
3121
|
}
|
|
3188
3122
|
get estimatedHeight() { return this.height; }
|
|
3189
3123
|
}
|
|
3190
|
-
function findCompositionNode(view,
|
|
3124
|
+
function findCompositionNode(view, headPos) {
|
|
3191
3125
|
let sel = view.observer.selectionRange;
|
|
3192
3126
|
let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
|
|
3193
3127
|
if (!textNode)
|
|
3194
3128
|
return null;
|
|
3195
|
-
let
|
|
3196
|
-
|
|
3197
|
-
if (cView instanceof TextView) {
|
|
3198
|
-
from = cView.posAtStart;
|
|
3199
|
-
to = from + cView.length;
|
|
3200
|
-
}
|
|
3201
|
-
else {
|
|
3202
|
-
let oldLen = Math.max(0, textNode.nodeValue.length - dLen);
|
|
3203
|
-
up: for (let offset = 0, node = textNode;;) {
|
|
3204
|
-
for (let sibling = node.previousSibling, cView; sibling; sibling = sibling.previousSibling) {
|
|
3205
|
-
if (cView = ContentView.get(sibling)) {
|
|
3206
|
-
to = cView.posAtEnd + offset;
|
|
3207
|
-
from = Math.max(0, to - oldLen);
|
|
3208
|
-
break up;
|
|
3209
|
-
}
|
|
3210
|
-
let reader = new DOMReader([], view.state);
|
|
3211
|
-
reader.readNode(sibling);
|
|
3212
|
-
if (reader.text.indexOf(LineBreakPlaceholder) > -1)
|
|
3213
|
-
return null;
|
|
3214
|
-
offset += reader.text.length;
|
|
3215
|
-
}
|
|
3216
|
-
node = node.parentNode;
|
|
3217
|
-
if (!node)
|
|
3218
|
-
return null;
|
|
3219
|
-
let parentView = ContentView.get(node);
|
|
3220
|
-
if (parentView) {
|
|
3221
|
-
from = parentView.posAtStart + offset;
|
|
3222
|
-
to = from + oldLen;
|
|
3223
|
-
break;
|
|
3224
|
-
}
|
|
3225
|
-
}
|
|
3226
|
-
}
|
|
3227
|
-
return { from, to: to, node: textNode };
|
|
3129
|
+
let from = headPos - textNode.offset;
|
|
3130
|
+
return { from, to: from + textNode.node.nodeValue.length, node: textNode.node };
|
|
3228
3131
|
}
|
|
3229
|
-
function findCompositionRange(view, changes) {
|
|
3230
|
-
let found = findCompositionNode(view,
|
|
3132
|
+
function findCompositionRange(view, changes, headPos) {
|
|
3133
|
+
let found = findCompositionNode(view, headPos);
|
|
3231
3134
|
if (!found)
|
|
3232
3135
|
return null;
|
|
3233
|
-
let {
|
|
3234
|
-
let fromB = changes.mapPos(fromA, -1), toB = changes.mapPos(toA, 1);
|
|
3235
|
-
let text = textNode.nodeValue;
|
|
3136
|
+
let { node: textNode, from, to } = found, text = textNode.nodeValue;
|
|
3236
3137
|
// Don't try to preserve multi-line compositions
|
|
3237
3138
|
if (/[\n\r]/.test(text))
|
|
3238
3139
|
return null;
|
|
3239
|
-
if (
|
|
3240
|
-
// If there is a length mismatch, see if mapping non-inclusively helps
|
|
3241
|
-
let fromB2 = changes.mapPos(fromA, 1), toB2 = changes.mapPos(toA, -1);
|
|
3242
|
-
if (toB2 - fromB2 == text.length)
|
|
3243
|
-
fromB = fromB2, toB = toB2;
|
|
3244
|
-
// See if we can find an instance of the text at either side
|
|
3245
|
-
else if (view.state.doc.sliceString(toB - text.length, toB) == text)
|
|
3246
|
-
fromB = toB - text.length;
|
|
3247
|
-
else if (view.state.doc.sliceString(fromB, fromB + text.length) == text)
|
|
3248
|
-
toB = fromB + text.length;
|
|
3249
|
-
// Not found
|
|
3250
|
-
else
|
|
3251
|
-
return null;
|
|
3252
|
-
}
|
|
3253
|
-
let { main } = view.state.selection;
|
|
3254
|
-
if (view.state.doc.sliceString(fromB, toB) != text || fromB > main.head || toB < main.head)
|
|
3140
|
+
if (view.state.doc.sliceString(found.from, found.to) != text)
|
|
3255
3141
|
return null;
|
|
3142
|
+
let inv = changes.invertedDesc;
|
|
3143
|
+
let range = new ChangedRange(inv.mapPos(from), inv.mapPos(to), from, to);
|
|
3256
3144
|
let marks = [];
|
|
3257
|
-
let range = new ChangedRange(fromA, toA, fromB, toB);
|
|
3258
3145
|
for (let parent = textNode.parentNode;; parent = parent.parentNode) {
|
|
3259
3146
|
let parentView = ContentView.get(parent);
|
|
3260
3147
|
if (parentView instanceof MarkView)
|
|
@@ -3275,7 +3162,7 @@ function nearbyTextNode(startNode, startOffset, side) {
|
|
|
3275
3162
|
if (side <= 0)
|
|
3276
3163
|
for (let node = startNode, offset = startOffset;;) {
|
|
3277
3164
|
if (node.nodeType == 3)
|
|
3278
|
-
return node;
|
|
3165
|
+
return { node: node, offset: offset };
|
|
3279
3166
|
if (node.nodeType == 1 && offset > 0) {
|
|
3280
3167
|
node = node.childNodes[offset - 1];
|
|
3281
3168
|
offset = maxOffset(node);
|
|
@@ -3287,7 +3174,7 @@ function nearbyTextNode(startNode, startOffset, side) {
|
|
|
3287
3174
|
if (side >= 0)
|
|
3288
3175
|
for (let node = startNode, offset = startOffset;;) {
|
|
3289
3176
|
if (node.nodeType == 3)
|
|
3290
|
-
return node;
|
|
3177
|
+
return { node: node, offset: offset };
|
|
3291
3178
|
if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
|
|
3292
3179
|
node = node.childNodes[offset];
|
|
3293
3180
|
offset = 0;
|
|
@@ -3324,6 +3211,15 @@ function inUneditable(node, inside) {
|
|
|
3324
3211
|
}
|
|
3325
3212
|
return false;
|
|
3326
3213
|
}
|
|
3214
|
+
function touchesComposition(changes, composition) {
|
|
3215
|
+
let touched = false;
|
|
3216
|
+
if (composition)
|
|
3217
|
+
changes.iterChangedRanges((from, to) => {
|
|
3218
|
+
if (from < composition.to && to > composition.from)
|
|
3219
|
+
touched = true;
|
|
3220
|
+
});
|
|
3221
|
+
return touched;
|
|
3222
|
+
}
|
|
3327
3223
|
|
|
3328
3224
|
function groupAt(state$1, pos, bias = 1) {
|
|
3329
3225
|
let categorize = state$1.charCategorizer(pos);
|
|
@@ -3697,13 +3593,13 @@ class InputState {
|
|
|
3697
3593
|
this.lastSelectionTime = Date.now();
|
|
3698
3594
|
}
|
|
3699
3595
|
constructor(view) {
|
|
3596
|
+
this.view = view;
|
|
3700
3597
|
this.lastKeyCode = 0;
|
|
3701
3598
|
this.lastKeyTime = 0;
|
|
3702
3599
|
this.lastTouchTime = 0;
|
|
3703
3600
|
this.lastFocusTime = 0;
|
|
3704
3601
|
this.lastScrollTop = 0;
|
|
3705
3602
|
this.lastScrollLeft = 0;
|
|
3706
|
-
this.chromeScrollHack = -1;
|
|
3707
3603
|
// On iOS, some keys need to have their default behavior happen
|
|
3708
3604
|
// (after which we retroactively handle them and reset the DOM) to
|
|
3709
3605
|
// avoid messing up the virtual keyboard state.
|
|
@@ -3713,8 +3609,7 @@ class InputState {
|
|
|
3713
3609
|
this.lastEscPress = 0;
|
|
3714
3610
|
this.lastContextMenu = 0;
|
|
3715
3611
|
this.scrollHandlers = [];
|
|
3716
|
-
this.
|
|
3717
|
-
this.customHandlers = [];
|
|
3612
|
+
this.handlers = Object.create(null);
|
|
3718
3613
|
// -1 means not in a composition. Otherwise, this counts the number
|
|
3719
3614
|
// of changes made during the composition. The count is used to
|
|
3720
3615
|
// avoid treating the start state of the composition, before any
|
|
@@ -3735,29 +3630,10 @@ class InputState {
|
|
|
3735
3630
|
// the mutation events fire shortly after the compositionend event
|
|
3736
3631
|
this.compositionPendingChange = false;
|
|
3737
3632
|
this.mouseSelection = null;
|
|
3738
|
-
|
|
3739
|
-
if (this.ignoreDuringComposition(event))
|
|
3740
|
-
return;
|
|
3741
|
-
if (event.type == "keydown" && this.keydown(view, event))
|
|
3742
|
-
return;
|
|
3743
|
-
if (this.mustFlushObserver(event))
|
|
3744
|
-
view.observer.forceFlush();
|
|
3745
|
-
if (this.runCustomHandlers(event.type, view, event))
|
|
3746
|
-
event.preventDefault();
|
|
3747
|
-
else
|
|
3748
|
-
handler(view, event);
|
|
3749
|
-
};
|
|
3750
|
-
for (let type in handlers) {
|
|
3751
|
-
let handler = handlers[type];
|
|
3752
|
-
view.contentDOM.addEventListener(type, event => {
|
|
3753
|
-
if (eventBelongsToEditor(view, event))
|
|
3754
|
-
handleEvent(handler, event);
|
|
3755
|
-
}, handlerOptions[type]);
|
|
3756
|
-
this.registeredEvents.push(type);
|
|
3757
|
-
}
|
|
3633
|
+
this.handleEvent = this.handleEvent.bind(this);
|
|
3758
3634
|
view.scrollDOM.addEventListener("mousedown", (event) => {
|
|
3759
3635
|
if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom) {
|
|
3760
|
-
handleEvent(
|
|
3636
|
+
this.handleEvent(event);
|
|
3761
3637
|
if (!event.defaultPrevented && event.button == 2) {
|
|
3762
3638
|
// Make sure the content covers the entire scroller height, in order
|
|
3763
3639
|
// to catch a native context menu click below it
|
|
@@ -3769,23 +3645,8 @@ class InputState {
|
|
|
3769
3645
|
});
|
|
3770
3646
|
view.scrollDOM.addEventListener("drop", (event) => {
|
|
3771
3647
|
if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom)
|
|
3772
|
-
handleEvent(
|
|
3648
|
+
this.handleEvent(event);
|
|
3773
3649
|
});
|
|
3774
|
-
if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
|
|
3775
|
-
// On Chrome 102, viewport updates somehow stop wheel-based
|
|
3776
|
-
// scrolling. Turning off pointer events during the scroll seems
|
|
3777
|
-
// to avoid the issue.
|
|
3778
|
-
view.scrollDOM.addEventListener("wheel", () => {
|
|
3779
|
-
if (this.chromeScrollHack < 0)
|
|
3780
|
-
view.contentDOM.style.pointerEvents = "none";
|
|
3781
|
-
else
|
|
3782
|
-
window.clearTimeout(this.chromeScrollHack);
|
|
3783
|
-
this.chromeScrollHack = setTimeout(() => {
|
|
3784
|
-
this.chromeScrollHack = -1;
|
|
3785
|
-
view.contentDOM.style.pointerEvents = "";
|
|
3786
|
-
}, 100);
|
|
3787
|
-
}, { passive: true });
|
|
3788
|
-
}
|
|
3789
3650
|
this.notifiedFocused = view.hasFocus;
|
|
3790
3651
|
// On Safari adding an input event handler somehow prevents an
|
|
3791
3652
|
// issue where the composition vanishes when you press enter.
|
|
@@ -3794,63 +3655,54 @@ class InputState {
|
|
|
3794
3655
|
if (browser.gecko)
|
|
3795
3656
|
firefoxCopyCutHack(view.contentDOM.ownerDocument);
|
|
3796
3657
|
}
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
}
|
|
3816
|
-
runCustomHandlers(type, view, event) {
|
|
3817
|
-
for (let set of this.customHandlers) {
|
|
3818
|
-
let handler = set.handlers[type];
|
|
3819
|
-
if (handler) {
|
|
3820
|
-
try {
|
|
3821
|
-
if (handler.call(set.plugin, event, view) || event.defaultPrevented)
|
|
3822
|
-
return true;
|
|
3823
|
-
}
|
|
3824
|
-
catch (e) {
|
|
3825
|
-
logException(view.state, e);
|
|
3658
|
+
handleEvent(event) {
|
|
3659
|
+
if (!eventBelongsToEditor(this.view, event) || this.ignoreDuringComposition(event))
|
|
3660
|
+
return;
|
|
3661
|
+
if (event.type == "keydown" && this.keydown(event))
|
|
3662
|
+
return;
|
|
3663
|
+
this.runHandlers(event.type, event);
|
|
3664
|
+
}
|
|
3665
|
+
runHandlers(type, event) {
|
|
3666
|
+
let handlers = this.handlers[type];
|
|
3667
|
+
if (handlers) {
|
|
3668
|
+
for (let observer of handlers.observers)
|
|
3669
|
+
observer(this.view, event);
|
|
3670
|
+
for (let handler of handlers.handlers) {
|
|
3671
|
+
if (event.defaultPrevented)
|
|
3672
|
+
break;
|
|
3673
|
+
if (handler(this.view, event)) {
|
|
3674
|
+
event.preventDefault();
|
|
3675
|
+
break;
|
|
3826
3676
|
}
|
|
3827
3677
|
}
|
|
3828
3678
|
}
|
|
3829
|
-
return false;
|
|
3830
3679
|
}
|
|
3831
|
-
|
|
3832
|
-
this.
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
catch (e) {
|
|
3841
|
-
logException(view.state, e);
|
|
3680
|
+
ensureHandlers(plugins) {
|
|
3681
|
+
let handlers = computeHandlers(plugins), prev = this.handlers, dom = this.view.contentDOM;
|
|
3682
|
+
for (let type in handlers)
|
|
3683
|
+
if (type != "scroll") {
|
|
3684
|
+
let passive = !handlers[type].handlers.length;
|
|
3685
|
+
let exists = prev[type];
|
|
3686
|
+
if (exists && passive != !exists.handlers.length) {
|
|
3687
|
+
dom.removeEventListener(type, this.handleEvent);
|
|
3688
|
+
exists = null;
|
|
3842
3689
|
}
|
|
3690
|
+
if (!exists)
|
|
3691
|
+
dom.addEventListener(type, this.handleEvent, { passive });
|
|
3843
3692
|
}
|
|
3844
|
-
|
|
3693
|
+
for (let type in prev)
|
|
3694
|
+
if (type != "scroll" && !handlers[type])
|
|
3695
|
+
dom.removeEventListener(type, this.handleEvent);
|
|
3696
|
+
this.handlers = handlers;
|
|
3845
3697
|
}
|
|
3846
|
-
keydown(
|
|
3698
|
+
keydown(event) {
|
|
3847
3699
|
// Must always run, even if a custom handler handled the event
|
|
3848
3700
|
this.lastKeyCode = event.keyCode;
|
|
3849
3701
|
this.lastKeyTime = Date.now();
|
|
3850
3702
|
if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
|
|
3851
3703
|
return true;
|
|
3852
3704
|
if (event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0)
|
|
3853
|
-
view.inputState.lastEscPress = 0;
|
|
3705
|
+
this.view.inputState.lastEscPress = 0;
|
|
3854
3706
|
// Chrome for Android usually doesn't fire proper key events, but
|
|
3855
3707
|
// occasionally does, usually surrounded by a bunch of complicated
|
|
3856
3708
|
// composition changes. When an enter or backspace key event is
|
|
@@ -3858,10 +3710,10 @@ class InputState {
|
|
|
3858
3710
|
// dispatch it.
|
|
3859
3711
|
if (browser.android && browser.chrome && !event.synthetic &&
|
|
3860
3712
|
(event.keyCode == 13 || event.keyCode == 8)) {
|
|
3861
|
-
view.observer.delayAndroidKey(event.key, event.keyCode);
|
|
3713
|
+
this.view.observer.delayAndroidKey(event.key, event.keyCode);
|
|
3862
3714
|
return true;
|
|
3863
3715
|
}
|
|
3864
|
-
//
|
|
3716
|
+
// Preventing the default behavior of Enter on iOS makes the
|
|
3865
3717
|
// virtual keyboard get stuck in the wrong (lowercase)
|
|
3866
3718
|
// state. So we let it go through, and then, in
|
|
3867
3719
|
// applyDOMChange, notify key handlers of it and reset to
|
|
@@ -3871,17 +3723,19 @@ class InputState {
|
|
|
3871
3723
|
((pending = PendingKeys.find(key => key.keyCode == event.keyCode)) && !event.ctrlKey ||
|
|
3872
3724
|
EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey && !event.shiftKey)) {
|
|
3873
3725
|
this.pendingIOSKey = pending || event;
|
|
3874
|
-
setTimeout(() => this.flushIOSKey(
|
|
3726
|
+
setTimeout(() => this.flushIOSKey(), 250);
|
|
3875
3727
|
return true;
|
|
3876
3728
|
}
|
|
3729
|
+
if (event.keyCode != 229)
|
|
3730
|
+
this.view.observer.forceFlush();
|
|
3877
3731
|
return false;
|
|
3878
3732
|
}
|
|
3879
|
-
flushIOSKey(
|
|
3733
|
+
flushIOSKey() {
|
|
3880
3734
|
let key = this.pendingIOSKey;
|
|
3881
3735
|
if (!key)
|
|
3882
3736
|
return false;
|
|
3883
3737
|
this.pendingIOSKey = undefined;
|
|
3884
|
-
return dispatchKey(view.contentDOM, key.key, key.keyCode);
|
|
3738
|
+
return dispatchKey(this.view.contentDOM, key.key, key.keyCode);
|
|
3885
3739
|
}
|
|
3886
3740
|
ignoreDuringComposition(event) {
|
|
3887
3741
|
if (!/^key/.test(event.type))
|
|
@@ -3900,9 +3754,6 @@ class InputState {
|
|
|
3900
3754
|
}
|
|
3901
3755
|
return false;
|
|
3902
3756
|
}
|
|
3903
|
-
mustFlushObserver(event) {
|
|
3904
|
-
return event.type == "keydown" && event.keyCode != 229;
|
|
3905
|
-
}
|
|
3906
3757
|
startMouseSelection(mouseSelection) {
|
|
3907
3758
|
if (this.mouseSelection)
|
|
3908
3759
|
this.mouseSelection.destroy();
|
|
@@ -3919,6 +3770,42 @@ class InputState {
|
|
|
3919
3770
|
this.mouseSelection.destroy();
|
|
3920
3771
|
}
|
|
3921
3772
|
}
|
|
3773
|
+
function bindHandler(plugin, handler) {
|
|
3774
|
+
return (view, event) => {
|
|
3775
|
+
try {
|
|
3776
|
+
return handler.call(plugin, event, view);
|
|
3777
|
+
}
|
|
3778
|
+
catch (e) {
|
|
3779
|
+
logException(view.state, e);
|
|
3780
|
+
}
|
|
3781
|
+
};
|
|
3782
|
+
}
|
|
3783
|
+
function computeHandlers(plugins) {
|
|
3784
|
+
let result = Object.create(null);
|
|
3785
|
+
function record(type) {
|
|
3786
|
+
return result[type] || (result[type] = { observers: [], handlers: [] });
|
|
3787
|
+
}
|
|
3788
|
+
for (let plugin of plugins) {
|
|
3789
|
+
let spec = plugin.spec;
|
|
3790
|
+
if (spec && spec.domEventHandlers)
|
|
3791
|
+
for (let type in spec.domEventHandlers) {
|
|
3792
|
+
let f = spec.domEventHandlers[type];
|
|
3793
|
+
if (f)
|
|
3794
|
+
record(type).handlers.push(bindHandler(plugin.value, f));
|
|
3795
|
+
}
|
|
3796
|
+
if (spec && spec.domEventObservers)
|
|
3797
|
+
for (let type in spec.domEventObservers) {
|
|
3798
|
+
let f = spec.domEventObservers[type];
|
|
3799
|
+
if (f)
|
|
3800
|
+
record(type).observers.push(bindHandler(plugin.value, f));
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
for (let type in handlers)
|
|
3804
|
+
record(type).handlers.push(handlers[type]);
|
|
3805
|
+
for (let type in observers)
|
|
3806
|
+
record(type).observers.push(observers[type]);
|
|
3807
|
+
return result;
|
|
3808
|
+
}
|
|
3922
3809
|
const PendingKeys = [
|
|
3923
3810
|
{ key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
|
|
3924
3811
|
{ key: "Enter", keyCode: 13, inputType: "insertParagraph" },
|
|
@@ -3956,10 +3843,8 @@ class MouseSelection {
|
|
|
3956
3843
|
start(event) {
|
|
3957
3844
|
// When clicking outside of the selection, immediately apply the
|
|
3958
3845
|
// effect of starting the selection
|
|
3959
|
-
if (this.dragging === false)
|
|
3960
|
-
event.preventDefault();
|
|
3846
|
+
if (this.dragging === false)
|
|
3961
3847
|
this.select(event);
|
|
3962
|
-
}
|
|
3963
3848
|
}
|
|
3964
3849
|
move(event) {
|
|
3965
3850
|
var _a;
|
|
@@ -4095,7 +3980,7 @@ function eventBelongsToEditor(view, event) {
|
|
|
4095
3980
|
return true;
|
|
4096
3981
|
}
|
|
4097
3982
|
const handlers = Object.create(null);
|
|
4098
|
-
const
|
|
3983
|
+
const observers = Object.create(null);
|
|
4099
3984
|
// This is very crude, but unfortunately both these browsers _pretend_
|
|
4100
3985
|
// that they have a clipboard API—all the objects and methods are
|
|
4101
3986
|
// there, they just don't work, and they are hard to test.
|
|
@@ -4145,23 +4030,27 @@ function doPaste(view, input) {
|
|
|
4145
4030
|
scrollIntoView: true
|
|
4146
4031
|
});
|
|
4147
4032
|
}
|
|
4033
|
+
observers.scroll = view => {
|
|
4034
|
+
view.inputState.lastScrollTop = view.scrollDOM.scrollTop;
|
|
4035
|
+
view.inputState.lastScrollLeft = view.scrollDOM.scrollLeft;
|
|
4036
|
+
};
|
|
4148
4037
|
handlers.keydown = (view, event) => {
|
|
4149
4038
|
view.inputState.setSelectionOrigin("select");
|
|
4150
4039
|
if (event.keyCode == 27)
|
|
4151
4040
|
view.inputState.lastEscPress = Date.now();
|
|
4041
|
+
return false;
|
|
4152
4042
|
};
|
|
4153
|
-
|
|
4043
|
+
observers.touchstart = (view, e) => {
|
|
4154
4044
|
view.inputState.lastTouchTime = Date.now();
|
|
4155
4045
|
view.inputState.setSelectionOrigin("select.pointer");
|
|
4156
4046
|
};
|
|
4157
|
-
|
|
4047
|
+
observers.touchmove = view => {
|
|
4158
4048
|
view.inputState.setSelectionOrigin("select.pointer");
|
|
4159
4049
|
};
|
|
4160
|
-
handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
|
|
4161
4050
|
handlers.mousedown = (view, event) => {
|
|
4162
4051
|
view.observer.flush();
|
|
4163
4052
|
if (view.inputState.lastTouchTime > Date.now() - 2000)
|
|
4164
|
-
return; // Ignore touch interaction
|
|
4053
|
+
return false; // Ignore touch interaction
|
|
4165
4054
|
let style = null;
|
|
4166
4055
|
for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
|
|
4167
4056
|
style = makeStyle(view, event);
|
|
@@ -4175,9 +4064,13 @@ handlers.mousedown = (view, event) => {
|
|
|
4175
4064
|
view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
|
|
4176
4065
|
if (mustFocus)
|
|
4177
4066
|
view.observer.ignore(() => focusPreventScroll(view.contentDOM));
|
|
4178
|
-
|
|
4179
|
-
|
|
4067
|
+
let mouseSel = view.inputState.mouseSelection;
|
|
4068
|
+
if (mouseSel) {
|
|
4069
|
+
mouseSel.start(event);
|
|
4070
|
+
return !mouseSel.dragging;
|
|
4071
|
+
}
|
|
4180
4072
|
}
|
|
4073
|
+
return false;
|
|
4181
4074
|
};
|
|
4182
4075
|
function rangeForClick(view, pos, bias, type) {
|
|
4183
4076
|
if (type == 1) { // Single click
|
|
@@ -4281,12 +4174,12 @@ handlers.dragstart = (view, event) => {
|
|
|
4281
4174
|
event.dataTransfer.setData("Text", view.state.sliceDoc(main.from, main.to));
|
|
4282
4175
|
event.dataTransfer.effectAllowed = "copyMove";
|
|
4283
4176
|
}
|
|
4177
|
+
return false;
|
|
4284
4178
|
};
|
|
4285
4179
|
function dropText(view, event, text, direct) {
|
|
4286
4180
|
if (!text)
|
|
4287
4181
|
return;
|
|
4288
4182
|
let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
|
|
4289
|
-
event.preventDefault();
|
|
4290
4183
|
let { mouseSelection } = view.inputState;
|
|
4291
4184
|
let del = direct && mouseSelection && mouseSelection.dragging && dragMovesSelection(view, event) ?
|
|
4292
4185
|
{ from: mouseSelection.dragging.from, to: mouseSelection.dragging.to } : null;
|
|
@@ -4301,12 +4194,11 @@ function dropText(view, event, text, direct) {
|
|
|
4301
4194
|
}
|
|
4302
4195
|
handlers.drop = (view, event) => {
|
|
4303
4196
|
if (!event.dataTransfer)
|
|
4304
|
-
return;
|
|
4197
|
+
return false;
|
|
4305
4198
|
if (view.state.readOnly)
|
|
4306
|
-
return
|
|
4199
|
+
return true;
|
|
4307
4200
|
let files = event.dataTransfer.files;
|
|
4308
4201
|
if (files && files.length) { // For a file drop, read the file's text.
|
|
4309
|
-
event.preventDefault();
|
|
4310
4202
|
let text = Array(files.length), read = 0;
|
|
4311
4203
|
let finishFile = () => {
|
|
4312
4204
|
if (++read == files.length)
|
|
@@ -4322,22 +4214,29 @@ handlers.drop = (view, event) => {
|
|
|
4322
4214
|
};
|
|
4323
4215
|
reader.readAsText(files[i]);
|
|
4324
4216
|
}
|
|
4217
|
+
return true;
|
|
4325
4218
|
}
|
|
4326
4219
|
else {
|
|
4327
|
-
|
|
4220
|
+
let text = event.dataTransfer.getData("Text");
|
|
4221
|
+
if (text) {
|
|
4222
|
+
dropText(view, event, text, true);
|
|
4223
|
+
return true;
|
|
4224
|
+
}
|
|
4328
4225
|
}
|
|
4226
|
+
return false;
|
|
4329
4227
|
};
|
|
4330
4228
|
handlers.paste = (view, event) => {
|
|
4331
4229
|
if (view.state.readOnly)
|
|
4332
|
-
return
|
|
4230
|
+
return true;
|
|
4333
4231
|
view.observer.flush();
|
|
4334
4232
|
let data = brokenClipboardAPI ? null : event.clipboardData;
|
|
4335
4233
|
if (data) {
|
|
4336
4234
|
doPaste(view, data.getData("text/plain") || data.getData("text/uri-text"));
|
|
4337
|
-
|
|
4235
|
+
return true;
|
|
4338
4236
|
}
|
|
4339
4237
|
else {
|
|
4340
4238
|
capturePaste(view);
|
|
4239
|
+
return false;
|
|
4341
4240
|
}
|
|
4342
4241
|
};
|
|
4343
4242
|
function captureCopy(view, text) {
|
|
@@ -4383,23 +4282,24 @@ let lastLinewiseCopy = null;
|
|
|
4383
4282
|
handlers.copy = handlers.cut = (view, event) => {
|
|
4384
4283
|
let { text, ranges, linewise } = copiedRange(view.state);
|
|
4385
4284
|
if (!text && !linewise)
|
|
4386
|
-
return;
|
|
4285
|
+
return false;
|
|
4387
4286
|
lastLinewiseCopy = linewise ? text : null;
|
|
4287
|
+
if (event.type == "cut" && !view.state.readOnly)
|
|
4288
|
+
view.dispatch({
|
|
4289
|
+
changes: ranges,
|
|
4290
|
+
scrollIntoView: true,
|
|
4291
|
+
userEvent: "delete.cut"
|
|
4292
|
+
});
|
|
4388
4293
|
let data = brokenClipboardAPI ? null : event.clipboardData;
|
|
4389
4294
|
if (data) {
|
|
4390
|
-
event.preventDefault();
|
|
4391
4295
|
data.clearData();
|
|
4392
4296
|
data.setData("text/plain", text);
|
|
4297
|
+
return true;
|
|
4393
4298
|
}
|
|
4394
4299
|
else {
|
|
4395
4300
|
captureCopy(view, text);
|
|
4301
|
+
return false;
|
|
4396
4302
|
}
|
|
4397
|
-
if (event.type == "cut" && !view.state.readOnly)
|
|
4398
|
-
view.dispatch({
|
|
4399
|
-
changes: ranges,
|
|
4400
|
-
scrollIntoView: true,
|
|
4401
|
-
userEvent: "delete.cut"
|
|
4402
|
-
});
|
|
4403
4303
|
};
|
|
4404
4304
|
const isFocusChange = state.Annotation.define();
|
|
4405
4305
|
function focusChangeTransaction(state, focus) {
|
|
@@ -4423,7 +4323,7 @@ function updateForFocusChange(view) {
|
|
|
4423
4323
|
}
|
|
4424
4324
|
}, 10);
|
|
4425
4325
|
}
|
|
4426
|
-
|
|
4326
|
+
observers.focus = view => {
|
|
4427
4327
|
view.inputState.lastFocusTime = Date.now();
|
|
4428
4328
|
// When focusing reset the scroll position, move it back to where it was
|
|
4429
4329
|
if (!view.scrollDOM.scrollTop && (view.inputState.lastScrollTop || view.inputState.lastScrollLeft)) {
|
|
@@ -4432,11 +4332,11 @@ handlers.focus = view => {
|
|
|
4432
4332
|
}
|
|
4433
4333
|
updateForFocusChange(view);
|
|
4434
4334
|
};
|
|
4435
|
-
|
|
4335
|
+
observers.blur = view => {
|
|
4436
4336
|
view.observer.clearSelectionRange();
|
|
4437
4337
|
updateForFocusChange(view);
|
|
4438
4338
|
};
|
|
4439
|
-
|
|
4339
|
+
observers.compositionstart = observers.compositionupdate = view => {
|
|
4440
4340
|
if (view.inputState.compositionFirstChange == null)
|
|
4441
4341
|
view.inputState.compositionFirstChange = true;
|
|
4442
4342
|
if (view.inputState.composing < 0) {
|
|
@@ -4444,7 +4344,7 @@ handlers.compositionstart = handlers.compositionupdate = view => {
|
|
|
4444
4344
|
view.inputState.composing = 0;
|
|
4445
4345
|
}
|
|
4446
4346
|
};
|
|
4447
|
-
|
|
4347
|
+
observers.compositionend = view => {
|
|
4448
4348
|
view.inputState.composing = -1;
|
|
4449
4349
|
view.inputState.compositionEndedAt = Date.now();
|
|
4450
4350
|
view.inputState.compositionPendingKey = true;
|
|
@@ -4468,7 +4368,7 @@ handlers.compositionend = view => {
|
|
|
4468
4368
|
}, 50);
|
|
4469
4369
|
}
|
|
4470
4370
|
};
|
|
4471
|
-
|
|
4371
|
+
observers.contextmenu = view => {
|
|
4472
4372
|
view.inputState.lastContextMenu = Date.now();
|
|
4473
4373
|
};
|
|
4474
4374
|
handlers.beforeinput = (view, event) => {
|
|
@@ -4497,6 +4397,7 @@ handlers.beforeinput = (view, event) => {
|
|
|
4497
4397
|
}, 100);
|
|
4498
4398
|
}
|
|
4499
4399
|
}
|
|
4400
|
+
return false;
|
|
4500
4401
|
};
|
|
4501
4402
|
const appliedFirefoxHack = new Set;
|
|
4502
4403
|
// In Firefox, when cut/copy handlers are added to the document, that
|
|
@@ -5167,14 +5068,13 @@ class NodeBuilder {
|
|
|
5167
5068
|
return line;
|
|
5168
5069
|
}
|
|
5169
5070
|
addBlock(block) {
|
|
5170
|
-
var _a;
|
|
5171
5071
|
this.enterLine();
|
|
5172
|
-
let
|
|
5173
|
-
if (
|
|
5072
|
+
let deco = block.deco;
|
|
5073
|
+
if (deco && deco.startSide > 0 && !this.isCovered)
|
|
5174
5074
|
this.ensureLine();
|
|
5175
5075
|
this.nodes.push(block);
|
|
5176
5076
|
this.writtenTo = this.pos = this.pos + block.length;
|
|
5177
|
-
if (
|
|
5077
|
+
if (deco && deco.endSide > 0)
|
|
5178
5078
|
this.covering = block;
|
|
5179
5079
|
}
|
|
5180
5080
|
addLineDeco(height, breaks, length) {
|
|
@@ -6082,6 +5982,113 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
|
|
|
6082
5982
|
}
|
|
6083
5983
|
}, lightDarkIDs);
|
|
6084
5984
|
|
|
5985
|
+
const LineBreakPlaceholder = "\uffff";
|
|
5986
|
+
class DOMReader {
|
|
5987
|
+
constructor(points, state$1) {
|
|
5988
|
+
this.points = points;
|
|
5989
|
+
this.text = "";
|
|
5990
|
+
this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
|
|
5991
|
+
}
|
|
5992
|
+
append(text) {
|
|
5993
|
+
this.text += text;
|
|
5994
|
+
}
|
|
5995
|
+
lineBreak() {
|
|
5996
|
+
this.text += LineBreakPlaceholder;
|
|
5997
|
+
}
|
|
5998
|
+
readRange(start, end) {
|
|
5999
|
+
if (!start)
|
|
6000
|
+
return this;
|
|
6001
|
+
let parent = start.parentNode;
|
|
6002
|
+
for (let cur = start;;) {
|
|
6003
|
+
this.findPointBefore(parent, cur);
|
|
6004
|
+
let oldLen = this.text.length;
|
|
6005
|
+
this.readNode(cur);
|
|
6006
|
+
let next = cur.nextSibling;
|
|
6007
|
+
if (next == end)
|
|
6008
|
+
break;
|
|
6009
|
+
let view = ContentView.get(cur), nextView = ContentView.get(next);
|
|
6010
|
+
if (view && nextView ? view.breakAfter :
|
|
6011
|
+
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
6012
|
+
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
|
|
6013
|
+
this.lineBreak();
|
|
6014
|
+
cur = next;
|
|
6015
|
+
}
|
|
6016
|
+
this.findPointBefore(parent, end);
|
|
6017
|
+
return this;
|
|
6018
|
+
}
|
|
6019
|
+
readTextNode(node) {
|
|
6020
|
+
let text = node.nodeValue;
|
|
6021
|
+
for (let point of this.points)
|
|
6022
|
+
if (point.node == node)
|
|
6023
|
+
point.pos = this.text.length + Math.min(point.offset, text.length);
|
|
6024
|
+
for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
|
|
6025
|
+
let nextBreak = -1, breakSize = 1, m;
|
|
6026
|
+
if (this.lineSeparator) {
|
|
6027
|
+
nextBreak = text.indexOf(this.lineSeparator, off);
|
|
6028
|
+
breakSize = this.lineSeparator.length;
|
|
6029
|
+
}
|
|
6030
|
+
else if (m = re.exec(text)) {
|
|
6031
|
+
nextBreak = m.index;
|
|
6032
|
+
breakSize = m[0].length;
|
|
6033
|
+
}
|
|
6034
|
+
this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
|
|
6035
|
+
if (nextBreak < 0)
|
|
6036
|
+
break;
|
|
6037
|
+
this.lineBreak();
|
|
6038
|
+
if (breakSize > 1)
|
|
6039
|
+
for (let point of this.points)
|
|
6040
|
+
if (point.node == node && point.pos > this.text.length)
|
|
6041
|
+
point.pos -= breakSize - 1;
|
|
6042
|
+
off = nextBreak + breakSize;
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
readNode(node) {
|
|
6046
|
+
if (node.cmIgnore)
|
|
6047
|
+
return;
|
|
6048
|
+
let view = ContentView.get(node);
|
|
6049
|
+
let fromView = view && view.overrideDOMText;
|
|
6050
|
+
if (fromView != null) {
|
|
6051
|
+
this.findPointInside(node, fromView.length);
|
|
6052
|
+
for (let i = fromView.iter(); !i.next().done;) {
|
|
6053
|
+
if (i.lineBreak)
|
|
6054
|
+
this.lineBreak();
|
|
6055
|
+
else
|
|
6056
|
+
this.append(i.value);
|
|
6057
|
+
}
|
|
6058
|
+
}
|
|
6059
|
+
else if (node.nodeType == 3) {
|
|
6060
|
+
this.readTextNode(node);
|
|
6061
|
+
}
|
|
6062
|
+
else if (node.nodeName == "BR") {
|
|
6063
|
+
if (node.nextSibling)
|
|
6064
|
+
this.lineBreak();
|
|
6065
|
+
}
|
|
6066
|
+
else if (node.nodeType == 1) {
|
|
6067
|
+
this.readRange(node.firstChild, null);
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
findPointBefore(node, next) {
|
|
6071
|
+
for (let point of this.points)
|
|
6072
|
+
if (point.node == node && node.childNodes[point.offset] == next)
|
|
6073
|
+
point.pos = this.text.length;
|
|
6074
|
+
}
|
|
6075
|
+
findPointInside(node, maxLen) {
|
|
6076
|
+
for (let point of this.points)
|
|
6077
|
+
if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
|
|
6078
|
+
point.pos = this.text.length + Math.min(maxLen, point.offset);
|
|
6079
|
+
}
|
|
6080
|
+
}
|
|
6081
|
+
function isBlockElement(node) {
|
|
6082
|
+
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
6083
|
+
}
|
|
6084
|
+
class DOMPoint {
|
|
6085
|
+
constructor(node, offset) {
|
|
6086
|
+
this.node = node;
|
|
6087
|
+
this.offset = offset;
|
|
6088
|
+
this.pos = -1;
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
|
|
6085
6092
|
class DOMChange {
|
|
6086
6093
|
constructor(view, start, end, typeOver) {
|
|
6087
6094
|
this.typeOver = typeOver;
|
|
@@ -6175,7 +6182,7 @@ function applyDOMChange(view, domChange) {
|
|
|
6175
6182
|
change = { from: sel.from, to: sel.to, insert: state.Text.of([" "]) };
|
|
6176
6183
|
}
|
|
6177
6184
|
if (change) {
|
|
6178
|
-
if (browser.ios && view.inputState.flushIOSKey(
|
|
6185
|
+
if (browser.ios && view.inputState.flushIOSKey())
|
|
6179
6186
|
return true;
|
|
6180
6187
|
// Android browsers don't fire reasonable key events for enter,
|
|
6181
6188
|
// backspace, or delete. So this detects changes that look like
|
|
@@ -6232,8 +6239,14 @@ function applyDefaultInsert(view, change, newSel) {
|
|
|
6232
6239
|
if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
|
|
6233
6240
|
change.to <= sel.to && change.to >= sel.to - 10) {
|
|
6234
6241
|
let replaced = view.state.sliceDoc(change.from, change.to);
|
|
6235
|
-
let composition = findCompositionNode(view,
|
|
6236
|
-
|
|
6242
|
+
let compositionRange, composition = newSel && findCompositionNode(view, newSel.main.head);
|
|
6243
|
+
if (composition) {
|
|
6244
|
+
let dLen = change.insert.length - (change.to - change.from);
|
|
6245
|
+
compositionRange = { from: composition.from, to: composition.to - dLen };
|
|
6246
|
+
}
|
|
6247
|
+
else {
|
|
6248
|
+
compositionRange = view.state.doc.lineAt(sel.head);
|
|
6249
|
+
}
|
|
6237
6250
|
let offset = sel.to - change.to, size = sel.to - sel.from;
|
|
6238
6251
|
tr = startState.changeByRange(range => {
|
|
6239
6252
|
if (range.from == sel.from && range.to == sel.to)
|
|
@@ -6244,7 +6257,7 @@ function applyDefaultInsert(view, change, newSel) {
|
|
|
6244
6257
|
// changes in the same node work without aborting
|
|
6245
6258
|
// composition, so cursors in the composition range are
|
|
6246
6259
|
// ignored.
|
|
6247
|
-
|
|
6260
|
+
range.to >= compositionRange.from && range.from <= compositionRange.to)
|
|
6248
6261
|
return { range };
|
|
6249
6262
|
let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
|
|
6250
6263
|
return {
|
|
@@ -6423,7 +6436,7 @@ class DOMObserver {
|
|
|
6423
6436
|
this.readSelectionRange();
|
|
6424
6437
|
}
|
|
6425
6438
|
onScrollChanged(e) {
|
|
6426
|
-
this.view.inputState.
|
|
6439
|
+
this.view.inputState.runHandlers("scroll", e);
|
|
6427
6440
|
if (this.intersecting)
|
|
6428
6441
|
this.view.measure();
|
|
6429
6442
|
}
|
|
@@ -6663,7 +6676,9 @@ class DOMObserver {
|
|
|
6663
6676
|
this.lastChange = Date.now();
|
|
6664
6677
|
this.view.inputState.lastFocusTime = 0;
|
|
6665
6678
|
this.selectionChanged = false;
|
|
6666
|
-
|
|
6679
|
+
let change = new DOMChange(this.view, from, to, typeOver);
|
|
6680
|
+
this.view.docView.domChanged = { newSel: change.newSel ? change.newSel.main : null };
|
|
6681
|
+
return change;
|
|
6667
6682
|
}
|
|
6668
6683
|
// Apply pending changes, if any
|
|
6669
6684
|
flush(readSelection = true) {
|
|
@@ -6892,7 +6907,7 @@ class EditorView {
|
|
|
6892
6907
|
plugin.update(this);
|
|
6893
6908
|
this.observer = new DOMObserver(this);
|
|
6894
6909
|
this.inputState = new InputState(this);
|
|
6895
|
-
this.inputState.ensureHandlers(this
|
|
6910
|
+
this.inputState.ensureHandlers(this.plugins);
|
|
6896
6911
|
this.docView = new DocView(this);
|
|
6897
6912
|
this.mountStyles();
|
|
6898
6913
|
this.updateAttrs();
|
|
@@ -7034,7 +7049,7 @@ class EditorView {
|
|
|
7034
7049
|
for (let plugin of this.plugins)
|
|
7035
7050
|
plugin.update(this);
|
|
7036
7051
|
this.docView = new DocView(this);
|
|
7037
|
-
this.inputState.ensureHandlers(this
|
|
7052
|
+
this.inputState.ensureHandlers(this.plugins);
|
|
7038
7053
|
this.mountStyles();
|
|
7039
7054
|
this.updateAttrs();
|
|
7040
7055
|
this.bidiCache = [];
|
|
@@ -7066,7 +7081,7 @@ class EditorView {
|
|
|
7066
7081
|
plugin.destroy(this);
|
|
7067
7082
|
this.plugins = newPlugins;
|
|
7068
7083
|
this.pluginMap.clear();
|
|
7069
|
-
this.inputState.ensureHandlers(this
|
|
7084
|
+
this.inputState.ensureHandlers(this.plugins);
|
|
7070
7085
|
}
|
|
7071
7086
|
else {
|
|
7072
7087
|
for (let p of this.plugins)
|
|
@@ -7594,6 +7609,17 @@ class EditorView {
|
|
|
7594
7609
|
return ViewPlugin.define(() => ({}), { eventHandlers: handlers });
|
|
7595
7610
|
}
|
|
7596
7611
|
/**
|
|
7612
|
+
Create an extension that registers DOM event observers. Contrary
|
|
7613
|
+
to event [handlers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventHandlers),
|
|
7614
|
+
observers can't be prevented from running by a higher-precedence
|
|
7615
|
+
handler returning true. They also don't prevent other handlers
|
|
7616
|
+
and observers from running when they return true, and should not
|
|
7617
|
+
call `preventDefault`.
|
|
7618
|
+
*/
|
|
7619
|
+
static domEventObservers(observers) {
|
|
7620
|
+
return ViewPlugin.define(() => ({}), { eventObservers: observers });
|
|
7621
|
+
}
|
|
7622
|
+
/**
|
|
7597
7623
|
Create a theme extension. The first argument can be a
|
|
7598
7624
|
[`style-mod`](https://github.com/marijnh/style-mod#documentation)
|
|
7599
7625
|
style spec providing the styles for the theme. These will be
|
|
@@ -8446,7 +8472,7 @@ const drawDropCursor = ViewPlugin.fromClass(class {
|
|
|
8446
8472
|
this.view.dispatch({ effects: setDropCursorPos.of(pos) });
|
|
8447
8473
|
}
|
|
8448
8474
|
}, {
|
|
8449
|
-
|
|
8475
|
+
eventObservers: {
|
|
8450
8476
|
dragover(event) {
|
|
8451
8477
|
this.setDropPos(this.view.posAtCoords({ x: event.clientX, y: event.clientY }));
|
|
8452
8478
|
},
|
|
@@ -8956,7 +8982,7 @@ function crosshairCursor(options = {}) {
|
|
|
8956
8982
|
}
|
|
8957
8983
|
}
|
|
8958
8984
|
}, {
|
|
8959
|
-
|
|
8985
|
+
eventObservers: {
|
|
8960
8986
|
keydown(e) {
|
|
8961
8987
|
this.set(e.keyCode == code || getter(e));
|
|
8962
8988
|
},
|
|
@@ -9267,7 +9293,7 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
|
|
|
9267
9293
|
}
|
|
9268
9294
|
}
|
|
9269
9295
|
}, {
|
|
9270
|
-
|
|
9296
|
+
eventObservers: {
|
|
9271
9297
|
scroll() { this.maybeMeasure(); }
|
|
9272
9298
|
}
|
|
9273
9299
|
});
|
|
@@ -9908,6 +9934,10 @@ const gutterView = ViewPlugin.fromClass(class {
|
|
|
9908
9934
|
for (let cx of contexts)
|
|
9909
9935
|
cx.line(this.view, line, classSet);
|
|
9910
9936
|
}
|
|
9937
|
+
else if (line.widget) {
|
|
9938
|
+
for (let cx of contexts)
|
|
9939
|
+
cx.widget(this.view, line);
|
|
9940
|
+
}
|
|
9911
9941
|
}
|
|
9912
9942
|
for (let cx of contexts)
|
|
9913
9943
|
cx.finish();
|