@codemirror/view 6.18.1 → 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/dist/index.js CHANGED
@@ -1091,6 +1091,236 @@ function getAttrs(dom) {
1091
1091
  return attrs;
1092
1092
  }
1093
1093
 
1094
+ class LineView extends ContentView {
1095
+ constructor() {
1096
+ super(...arguments);
1097
+ this.children = [];
1098
+ this.length = 0;
1099
+ this.prevAttrs = undefined;
1100
+ this.attrs = null;
1101
+ this.breakAfter = 0;
1102
+ }
1103
+ // Consumes source
1104
+ merge(from, to, source, hasStart, openStart, openEnd) {
1105
+ if (source) {
1106
+ if (!(source instanceof LineView))
1107
+ return false;
1108
+ if (!this.dom)
1109
+ source.transferDOM(this); // Reuse source.dom when appropriate
1110
+ }
1111
+ if (hasStart)
1112
+ this.setDeco(source ? source.attrs : null);
1113
+ mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
1114
+ return true;
1115
+ }
1116
+ split(at) {
1117
+ let end = new LineView;
1118
+ end.breakAfter = this.breakAfter;
1119
+ if (this.length == 0)
1120
+ return end;
1121
+ let { i, off } = this.childPos(at);
1122
+ if (off) {
1123
+ end.append(this.children[i].split(off), 0);
1124
+ this.children[i].merge(off, this.children[i].length, null, false, 0, 0);
1125
+ i++;
1126
+ }
1127
+ for (let j = i; j < this.children.length; j++)
1128
+ end.append(this.children[j], 0);
1129
+ while (i > 0 && this.children[i - 1].length == 0)
1130
+ this.children[--i].destroy();
1131
+ this.children.length = i;
1132
+ this.markDirty();
1133
+ this.length = at;
1134
+ return end;
1135
+ }
1136
+ transferDOM(other) {
1137
+ if (!this.dom)
1138
+ return;
1139
+ this.markDirty();
1140
+ other.setDOM(this.dom);
1141
+ other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs;
1142
+ this.prevAttrs = undefined;
1143
+ this.dom = null;
1144
+ }
1145
+ setDeco(attrs) {
1146
+ if (!attrsEq(this.attrs, attrs)) {
1147
+ if (this.dom) {
1148
+ this.prevAttrs = this.attrs;
1149
+ this.markDirty();
1150
+ }
1151
+ this.attrs = attrs;
1152
+ }
1153
+ }
1154
+ append(child, openStart) {
1155
+ joinInlineInto(this, child, openStart);
1156
+ }
1157
+ // Only called when building a line view in ContentBuilder
1158
+ addLineDeco(deco) {
1159
+ let attrs = deco.spec.attributes, cls = deco.spec.class;
1160
+ if (attrs)
1161
+ this.attrs = combineAttrs(attrs, this.attrs || {});
1162
+ if (cls)
1163
+ this.attrs = combineAttrs({ class: cls }, this.attrs || {});
1164
+ }
1165
+ domAtPos(pos) {
1166
+ return inlineDOMAtPos(this, pos);
1167
+ }
1168
+ reuseDOM(node) {
1169
+ if (node.nodeName == "DIV") {
1170
+ this.setDOM(node);
1171
+ this.flags |= 4 /* ViewFlag.AttrsDirty */ | 2 /* ViewFlag.NodeDirty */;
1172
+ }
1173
+ }
1174
+ sync(view, track) {
1175
+ var _a;
1176
+ if (!this.dom) {
1177
+ this.setDOM(document.createElement("div"));
1178
+ this.dom.className = "cm-line";
1179
+ this.prevAttrs = this.attrs ? null : undefined;
1180
+ }
1181
+ else if (this.flags & 4 /* ViewFlag.AttrsDirty */) {
1182
+ clearAttributes(this.dom);
1183
+ this.dom.className = "cm-line";
1184
+ this.prevAttrs = this.attrs ? null : undefined;
1185
+ }
1186
+ if (this.prevAttrs !== undefined) {
1187
+ updateAttrs(this.dom, this.prevAttrs, this.attrs);
1188
+ this.dom.classList.add("cm-line");
1189
+ this.prevAttrs = undefined;
1190
+ }
1191
+ super.sync(view, track);
1192
+ let last = this.dom.lastChild;
1193
+ while (last && ContentView.get(last) instanceof MarkView)
1194
+ last = last.lastChild;
1195
+ if (!last || !this.length ||
1196
+ last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false &&
1197
+ (!browser.ios || !this.children.some(ch => ch instanceof TextView))) {
1198
+ let hack = document.createElement("BR");
1199
+ hack.cmIgnore = true;
1200
+ this.dom.appendChild(hack);
1201
+ }
1202
+ }
1203
+ measureTextSize() {
1204
+ if (this.children.length == 0 || this.length > 20)
1205
+ return null;
1206
+ let totalWidth = 0, textHeight;
1207
+ for (let child of this.children) {
1208
+ if (!(child instanceof TextView) || /[^ -~]/.test(child.text))
1209
+ return null;
1210
+ let rects = clientRectsFor(child.dom);
1211
+ if (rects.length != 1)
1212
+ return null;
1213
+ totalWidth += rects[0].width;
1214
+ textHeight = rects[0].height;
1215
+ }
1216
+ return !totalWidth ? null : {
1217
+ lineHeight: this.dom.getBoundingClientRect().height,
1218
+ charWidth: totalWidth / this.length,
1219
+ textHeight
1220
+ };
1221
+ }
1222
+ coordsAt(pos, side) {
1223
+ let rect = coordsInChildren(this, pos, side);
1224
+ // Correct rectangle height for empty lines when the returned
1225
+ // height is larger than the text height.
1226
+ if (!this.children.length && rect && this.parent) {
1227
+ let { heightOracle } = this.parent.view.viewState, height = rect.bottom - rect.top;
1228
+ if (Math.abs(height - heightOracle.lineHeight) < 2 && heightOracle.textHeight < height) {
1229
+ let dist = (height - heightOracle.textHeight) / 2;
1230
+ return { top: rect.top + dist, bottom: rect.bottom - dist, left: rect.left, right: rect.left };
1231
+ }
1232
+ }
1233
+ return rect;
1234
+ }
1235
+ become(_other) { return false; }
1236
+ covers() { return true; }
1237
+ static find(docView, pos) {
1238
+ for (let i = 0, off = 0; i < docView.children.length; i++) {
1239
+ let block = docView.children[i], end = off + block.length;
1240
+ if (end >= pos) {
1241
+ if (block instanceof LineView)
1242
+ return block;
1243
+ if (end > pos)
1244
+ break;
1245
+ }
1246
+ off = end + block.breakAfter;
1247
+ }
1248
+ return null;
1249
+ }
1250
+ }
1251
+ class BlockWidgetView extends ContentView {
1252
+ constructor(widget, length, deco) {
1253
+ super();
1254
+ this.widget = widget;
1255
+ this.length = length;
1256
+ this.deco = deco;
1257
+ this.breakAfter = 0;
1258
+ this.prevWidget = null;
1259
+ }
1260
+ merge(from, to, source, _takeDeco, openStart, openEnd) {
1261
+ if (source && (!(source instanceof BlockWidgetView) || !this.widget.compare(source.widget) ||
1262
+ from > 0 && openStart <= 0 || to < this.length && openEnd <= 0))
1263
+ return false;
1264
+ this.length = from + (source ? source.length : 0) + (this.length - to);
1265
+ return true;
1266
+ }
1267
+ domAtPos(pos) {
1268
+ return pos == 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom, pos == this.length);
1269
+ }
1270
+ split(at) {
1271
+ let len = this.length - at;
1272
+ this.length = at;
1273
+ let end = new BlockWidgetView(this.widget, len, this.deco);
1274
+ end.breakAfter = this.breakAfter;
1275
+ return end;
1276
+ }
1277
+ get children() { return noChildren; }
1278
+ sync(view) {
1279
+ if (!this.dom || !this.widget.updateDOM(this.dom, view)) {
1280
+ if (this.dom && this.prevWidget)
1281
+ this.prevWidget.destroy(this.dom);
1282
+ this.prevWidget = null;
1283
+ this.setDOM(this.widget.toDOM(view));
1284
+ this.dom.contentEditable = "false";
1285
+ }
1286
+ }
1287
+ get overrideDOMText() {
1288
+ return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text.empty;
1289
+ }
1290
+ domBoundsAround() { return null; }
1291
+ become(other) {
1292
+ if (other instanceof BlockWidgetView &&
1293
+ other.widget.constructor == this.widget.constructor) {
1294
+ if (!other.widget.compare(this.widget))
1295
+ this.markDirty(true);
1296
+ if (this.dom && !this.prevWidget)
1297
+ this.prevWidget = this.widget;
1298
+ this.widget = other.widget;
1299
+ this.length = other.length;
1300
+ this.deco = other.deco;
1301
+ this.breakAfter = other.breakAfter;
1302
+ return true;
1303
+ }
1304
+ return false;
1305
+ }
1306
+ ignoreMutation() { return true; }
1307
+ ignoreEvent(event) { return this.widget.ignoreEvent(event); }
1308
+ get isEditable() { return false; }
1309
+ get isWidget() { return true; }
1310
+ coordsAt(pos, side) {
1311
+ return this.widget.coordsAt(this.dom, pos, side);
1312
+ }
1313
+ destroy() {
1314
+ super.destroy();
1315
+ if (this.dom)
1316
+ this.widget.destroy(this.dom);
1317
+ }
1318
+ covers(side) {
1319
+ let { startSide, endSide } = this.deco;
1320
+ return startSide == endSide ? false : side < 0 ? startSide < 0 : endSide > 0;
1321
+ }
1322
+ }
1323
+
1094
1324
  /**
1095
1325
  Widgets added to the content are described by subclasses of this
1096
1326
  class. Using a description object like that makes it possible to
@@ -1249,353 +1479,127 @@ class Decoration extends RangeValue {
1249
1479
  let block = !!spec.block, startSide, endSide;
1250
1480
  if (spec.isBlockGap) {
1251
1481
  startSide = -500000000 /* Side.GapStart */;
1252
- endSide = 400000000 /* Side.GapEnd */;
1253
- }
1254
- else {
1255
- let { start, end } = getInclusive(spec, block);
1256
- startSide = (start ? (block ? -300000000 /* Side.BlockIncStart */ : -1 /* Side.InlineIncStart */) : 500000000 /* Side.NonIncStart */) - 1;
1257
- endSide = (end ? (block ? 200000000 /* Side.BlockIncEnd */ : 1 /* Side.InlineIncEnd */) : -600000000 /* Side.NonIncEnd */) + 1;
1258
- }
1259
- return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1260
- }
1261
- /**
1262
- Create a line decoration, which can add DOM attributes to the
1263
- line starting at the given position.
1264
- */
1265
- static line(spec) {
1266
- return new LineDecoration(spec);
1267
- }
1268
- /**
1269
- Build a [`DecorationSet`](https://codemirror.net/6/docs/ref/#view.DecorationSet) from the given
1270
- decorated range or ranges. If the ranges aren't already sorted,
1271
- pass `true` for `sort` to make the library sort them for you.
1272
- */
1273
- static set(of, sort = false) {
1274
- return RangeSet.of(of, sort);
1275
- }
1276
- /**
1277
- @internal
1278
- */
1279
- hasHeight() { return this.widget ? this.widget.estimatedHeight > -1 : false; }
1280
- }
1281
- /**
1282
- The empty set of decorations.
1283
- */
1284
- Decoration.none = RangeSet.empty;
1285
- class MarkDecoration extends Decoration {
1286
- constructor(spec) {
1287
- let { start, end } = getInclusive(spec);
1288
- super(start ? -1 /* Side.InlineIncStart */ : 500000000 /* Side.NonIncStart */, end ? 1 /* Side.InlineIncEnd */ : -600000000 /* Side.NonIncEnd */, null, spec);
1289
- this.tagName = spec.tagName || "span";
1290
- this.class = spec.class || "";
1291
- this.attrs = spec.attributes || null;
1292
- }
1293
- eq(other) {
1294
- var _a, _b;
1295
- return this == other ||
1296
- other instanceof MarkDecoration &&
1297
- this.tagName == other.tagName &&
1298
- (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)) &&
1299
- attrsEq(this.attrs, other.attrs, "class");
1300
- }
1301
- range(from, to = from) {
1302
- if (from >= to)
1303
- throw new RangeError("Mark decorations may not be empty");
1304
- return super.range(from, to);
1305
- }
1306
- }
1307
- MarkDecoration.prototype.point = false;
1308
- class LineDecoration extends Decoration {
1309
- constructor(spec) {
1310
- super(-200000000 /* Side.Line */, -200000000 /* Side.Line */, null, spec);
1311
- }
1312
- eq(other) {
1313
- return other instanceof LineDecoration &&
1314
- this.spec.class == other.spec.class &&
1315
- attrsEq(this.spec.attributes, other.spec.attributes);
1316
- }
1317
- range(from, to = from) {
1318
- if (to != from)
1319
- throw new RangeError("Line decoration ranges must be zero-length");
1320
- return super.range(from, to);
1321
- }
1322
- }
1323
- LineDecoration.prototype.mapMode = MapMode.TrackBefore;
1324
- LineDecoration.prototype.point = true;
1325
- class PointDecoration extends Decoration {
1326
- constructor(spec, startSide, endSide, block, widget, isReplace) {
1327
- super(startSide, endSide, widget, spec);
1328
- this.block = block;
1329
- this.isReplace = isReplace;
1330
- this.mapMode = !block ? MapMode.TrackDel : startSide <= 0 ? MapMode.TrackBefore : MapMode.TrackAfter;
1331
- }
1332
- // Only relevant when this.block == true
1333
- get type() {
1334
- return this.startSide < this.endSide ? BlockType.WidgetRange
1335
- : this.startSide <= 0 ? BlockType.WidgetBefore : BlockType.WidgetAfter;
1336
- }
1337
- get heightRelevant() {
1338
- return this.block || !!this.widget && (this.widget.estimatedHeight >= 5 || this.widget.lineBreaks > 0);
1339
- }
1340
- eq(other) {
1341
- return other instanceof PointDecoration &&
1342
- widgetsEq(this.widget, other.widget) &&
1343
- this.block == other.block &&
1344
- this.startSide == other.startSide && this.endSide == other.endSide;
1345
- }
1346
- range(from, to = from) {
1347
- if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
1348
- throw new RangeError("Invalid range for replacement decoration");
1349
- if (!this.isReplace && to != from)
1350
- throw new RangeError("Widget decorations can only have zero-length ranges");
1351
- return super.range(from, to);
1352
- }
1353
- }
1354
- PointDecoration.prototype.point = true;
1355
- function getInclusive(spec, block = false) {
1356
- let { inclusiveStart: start, inclusiveEnd: end } = spec;
1357
- if (start == null)
1358
- start = spec.inclusive;
1359
- if (end == null)
1360
- end = spec.inclusive;
1361
- return { start: start !== null && start !== void 0 ? start : block, end: end !== null && end !== void 0 ? end : block };
1362
- }
1363
- function widgetsEq(a, b) {
1364
- return a == b || !!(a && b && a.compare(b));
1365
- }
1366
- function addRange(from, to, ranges, margin = 0) {
1367
- let last = ranges.length - 1;
1368
- if (last >= 0 && ranges[last] + margin >= from)
1369
- ranges[last] = Math.max(ranges[last], to);
1370
- else
1371
- ranges.push(from, to);
1372
- }
1373
-
1374
- class LineView extends ContentView {
1375
- constructor() {
1376
- super(...arguments);
1377
- this.children = [];
1378
- this.length = 0;
1379
- this.prevAttrs = undefined;
1380
- this.attrs = null;
1381
- this.breakAfter = 0;
1382
- }
1383
- // Consumes source
1384
- merge(from, to, source, hasStart, openStart, openEnd) {
1385
- if (source) {
1386
- if (!(source instanceof LineView))
1387
- return false;
1388
- if (!this.dom)
1389
- source.transferDOM(this); // Reuse source.dom when appropriate
1390
- }
1391
- if (hasStart)
1392
- this.setDeco(source ? source.attrs : null);
1393
- mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
1394
- return true;
1395
- }
1396
- split(at) {
1397
- let end = new LineView;
1398
- end.breakAfter = this.breakAfter;
1399
- if (this.length == 0)
1400
- return end;
1401
- let { i, off } = this.childPos(at);
1402
- if (off) {
1403
- end.append(this.children[i].split(off), 0);
1404
- this.children[i].merge(off, this.children[i].length, null, false, 0, 0);
1405
- i++;
1406
- }
1407
- for (let j = i; j < this.children.length; j++)
1408
- end.append(this.children[j], 0);
1409
- while (i > 0 && this.children[i - 1].length == 0)
1410
- this.children[--i].destroy();
1411
- this.children.length = i;
1412
- this.markDirty();
1413
- this.length = at;
1414
- return end;
1415
- }
1416
- transferDOM(other) {
1417
- if (!this.dom)
1418
- return;
1419
- this.markDirty();
1420
- other.setDOM(this.dom);
1421
- other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs;
1422
- this.prevAttrs = undefined;
1423
- this.dom = null;
1424
- }
1425
- setDeco(attrs) {
1426
- if (!attrsEq(this.attrs, attrs)) {
1427
- if (this.dom) {
1428
- this.prevAttrs = this.attrs;
1429
- this.markDirty();
1430
- }
1431
- this.attrs = attrs;
1432
- }
1433
- }
1434
- append(child, openStart) {
1435
- joinInlineInto(this, child, openStart);
1436
- }
1437
- // Only called when building a line view in ContentBuilder
1438
- addLineDeco(deco) {
1439
- let attrs = deco.spec.attributes, cls = deco.spec.class;
1440
- if (attrs)
1441
- this.attrs = combineAttrs(attrs, this.attrs || {});
1442
- if (cls)
1443
- this.attrs = combineAttrs({ class: cls }, this.attrs || {});
1444
- }
1445
- domAtPos(pos) {
1446
- return inlineDOMAtPos(this, pos);
1447
- }
1448
- reuseDOM(node) {
1449
- if (node.nodeName == "DIV") {
1450
- this.setDOM(node);
1451
- this.flags |= 4 /* ViewFlag.AttrsDirty */ | 2 /* ViewFlag.NodeDirty */;
1452
- }
1453
- }
1454
- sync(view, track) {
1455
- var _a;
1456
- if (!this.dom) {
1457
- this.setDOM(document.createElement("div"));
1458
- this.dom.className = "cm-line";
1459
- this.prevAttrs = this.attrs ? null : undefined;
1460
- }
1461
- else if (this.flags & 4 /* ViewFlag.AttrsDirty */) {
1462
- clearAttributes(this.dom);
1463
- this.dom.className = "cm-line";
1464
- this.prevAttrs = this.attrs ? null : undefined;
1465
- }
1466
- if (this.prevAttrs !== undefined) {
1467
- updateAttrs(this.dom, this.prevAttrs, this.attrs);
1468
- this.dom.classList.add("cm-line");
1469
- this.prevAttrs = undefined;
1470
- }
1471
- super.sync(view, track);
1472
- let last = this.dom.lastChild;
1473
- while (last && ContentView.get(last) instanceof MarkView)
1474
- last = last.lastChild;
1475
- if (!last || !this.length ||
1476
- last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false &&
1477
- (!browser.ios || !this.children.some(ch => ch instanceof TextView))) {
1478
- let hack = document.createElement("BR");
1479
- hack.cmIgnore = true;
1480
- this.dom.appendChild(hack);
1481
- }
1482
- }
1483
- measureTextSize() {
1484
- if (this.children.length == 0 || this.length > 20)
1485
- return null;
1486
- let totalWidth = 0, textHeight;
1487
- for (let child of this.children) {
1488
- if (!(child instanceof TextView) || /[^ -~]/.test(child.text))
1489
- return null;
1490
- let rects = clientRectsFor(child.dom);
1491
- if (rects.length != 1)
1492
- return null;
1493
- totalWidth += rects[0].width;
1494
- textHeight = rects[0].height;
1495
- }
1496
- return !totalWidth ? null : {
1497
- lineHeight: this.dom.getBoundingClientRect().height,
1498
- charWidth: totalWidth / this.length,
1499
- textHeight
1500
- };
1501
- }
1502
- coordsAt(pos, side) {
1503
- let rect = coordsInChildren(this, pos, side);
1504
- // Correct rectangle height for empty lines when the returned
1505
- // height is larger than the text height.
1506
- if (!this.children.length && rect && this.parent) {
1507
- let { heightOracle } = this.parent.view.viewState, height = rect.bottom - rect.top;
1508
- if (Math.abs(height - heightOracle.lineHeight) < 2 && heightOracle.textHeight < height) {
1509
- let dist = (height - heightOracle.textHeight) / 2;
1510
- return { top: rect.top + dist, bottom: rect.bottom - dist, left: rect.left, right: rect.left };
1511
- }
1482
+ endSide = 400000000 /* Side.GapEnd */;
1512
1483
  }
1513
- return rect;
1514
- }
1515
- become(_other) { return false; }
1516
- get type() { return BlockType.Text; }
1517
- static find(docView, pos) {
1518
- for (let i = 0, off = 0; i < docView.children.length; i++) {
1519
- let block = docView.children[i], end = off + block.length;
1520
- if (end >= pos) {
1521
- if (block instanceof LineView)
1522
- return block;
1523
- if (end > pos)
1524
- break;
1525
- }
1526
- off = end + block.breakAfter;
1484
+ else {
1485
+ let { start, end } = getInclusive(spec, block);
1486
+ startSide = (start ? (block ? -300000000 /* Side.BlockIncStart */ : -1 /* Side.InlineIncStart */) : 500000000 /* Side.NonIncStart */) - 1;
1487
+ endSide = (end ? (block ? 200000000 /* Side.BlockIncEnd */ : 1 /* Side.InlineIncEnd */) : -600000000 /* Side.NonIncEnd */) + 1;
1527
1488
  }
1528
- return null;
1489
+ return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1490
+ }
1491
+ /**
1492
+ Create a line decoration, which can add DOM attributes to the
1493
+ line starting at the given position.
1494
+ */
1495
+ static line(spec) {
1496
+ return new LineDecoration(spec);
1497
+ }
1498
+ /**
1499
+ Build a [`DecorationSet`](https://codemirror.net/6/docs/ref/#view.DecorationSet) from the given
1500
+ decorated range or ranges. If the ranges aren't already sorted,
1501
+ pass `true` for `sort` to make the library sort them for you.
1502
+ */
1503
+ static set(of, sort = false) {
1504
+ return RangeSet.of(of, sort);
1529
1505
  }
1506
+ /**
1507
+ @internal
1508
+ */
1509
+ hasHeight() { return this.widget ? this.widget.estimatedHeight > -1 : false; }
1530
1510
  }
1531
- class BlockWidgetView extends ContentView {
1532
- constructor(widget, length, type) {
1533
- super();
1534
- this.widget = widget;
1535
- this.length = length;
1536
- this.type = type;
1537
- this.breakAfter = 0;
1538
- this.prevWidget = null;
1511
+ /**
1512
+ The empty set of decorations.
1513
+ */
1514
+ Decoration.none = RangeSet.empty;
1515
+ class MarkDecoration extends Decoration {
1516
+ constructor(spec) {
1517
+ let { start, end } = getInclusive(spec);
1518
+ super(start ? -1 /* Side.InlineIncStart */ : 500000000 /* Side.NonIncStart */, end ? 1 /* Side.InlineIncEnd */ : -600000000 /* Side.NonIncEnd */, null, spec);
1519
+ this.tagName = spec.tagName || "span";
1520
+ this.class = spec.class || "";
1521
+ this.attrs = spec.attributes || null;
1539
1522
  }
1540
- merge(from, to, source, _takeDeco, openStart, openEnd) {
1541
- if (source && (!(source instanceof BlockWidgetView) || !this.widget.compare(source.widget) ||
1542
- from > 0 && openStart <= 0 || to < this.length && openEnd <= 0))
1543
- return false;
1544
- this.length = from + (source ? source.length : 0) + (this.length - to);
1545
- return true;
1523
+ eq(other) {
1524
+ var _a, _b;
1525
+ return this == other ||
1526
+ other instanceof MarkDecoration &&
1527
+ this.tagName == other.tagName &&
1528
+ (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)) &&
1529
+ attrsEq(this.attrs, other.attrs, "class");
1546
1530
  }
1547
- domAtPos(pos) {
1548
- return pos == 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom, pos == this.length);
1531
+ range(from, to = from) {
1532
+ if (from >= to)
1533
+ throw new RangeError("Mark decorations may not be empty");
1534
+ return super.range(from, to);
1549
1535
  }
1550
- split(at) {
1551
- let len = this.length - at;
1552
- this.length = at;
1553
- let end = new BlockWidgetView(this.widget, len, this.type);
1554
- end.breakAfter = this.breakAfter;
1555
- return end;
1536
+ }
1537
+ MarkDecoration.prototype.point = false;
1538
+ class LineDecoration extends Decoration {
1539
+ constructor(spec) {
1540
+ super(-200000000 /* Side.Line */, -200000000 /* Side.Line */, null, spec);
1556
1541
  }
1557
- get children() { return noChildren; }
1558
- sync(view) {
1559
- if (!this.dom || !this.widget.updateDOM(this.dom, view)) {
1560
- if (this.dom && this.prevWidget)
1561
- this.prevWidget.destroy(this.dom);
1562
- this.prevWidget = null;
1563
- this.setDOM(this.widget.toDOM(view));
1564
- this.dom.contentEditable = "false";
1565
- }
1542
+ eq(other) {
1543
+ return other instanceof LineDecoration &&
1544
+ this.spec.class == other.spec.class &&
1545
+ attrsEq(this.spec.attributes, other.spec.attributes);
1566
1546
  }
1567
- get overrideDOMText() {
1568
- return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text.empty;
1547
+ range(from, to = from) {
1548
+ if (to != from)
1549
+ throw new RangeError("Line decoration ranges must be zero-length");
1550
+ return super.range(from, to);
1569
1551
  }
1570
- domBoundsAround() { return null; }
1571
- become(other) {
1572
- if (other instanceof BlockWidgetView &&
1573
- other.widget.constructor == this.widget.constructor) {
1574
- if (!other.widget.compare(this.widget))
1575
- this.markDirty(true);
1576
- if (this.dom && !this.prevWidget)
1577
- this.prevWidget = this.widget;
1578
- this.widget = other.widget;
1579
- this.length = other.length;
1580
- this.type = other.type;
1581
- this.breakAfter = other.breakAfter;
1582
- return true;
1583
- }
1584
- return false;
1552
+ }
1553
+ LineDecoration.prototype.mapMode = MapMode.TrackBefore;
1554
+ LineDecoration.prototype.point = true;
1555
+ class PointDecoration extends Decoration {
1556
+ constructor(spec, startSide, endSide, block, widget, isReplace) {
1557
+ super(startSide, endSide, widget, spec);
1558
+ this.block = block;
1559
+ this.isReplace = isReplace;
1560
+ this.mapMode = !block ? MapMode.TrackDel : startSide <= 0 ? MapMode.TrackBefore : MapMode.TrackAfter;
1585
1561
  }
1586
- ignoreMutation() { return true; }
1587
- ignoreEvent(event) { return this.widget.ignoreEvent(event); }
1588
- get isEditable() { return false; }
1589
- get isWidget() { return true; }
1590
- coordsAt(pos, side) {
1591
- return this.widget.coordsAt(this.dom, pos, side);
1562
+ // Only relevant when this.block == true
1563
+ get type() {
1564
+ return this.startSide != this.endSide ? BlockType.WidgetRange
1565
+ : this.startSide <= 0 ? BlockType.WidgetBefore : BlockType.WidgetAfter;
1592
1566
  }
1593
- destroy() {
1594
- super.destroy();
1595
- if (this.dom)
1596
- this.widget.destroy(this.dom);
1567
+ get heightRelevant() {
1568
+ return this.block || !!this.widget && (this.widget.estimatedHeight >= 5 || this.widget.lineBreaks > 0);
1569
+ }
1570
+ eq(other) {
1571
+ return other instanceof PointDecoration &&
1572
+ widgetsEq(this.widget, other.widget) &&
1573
+ this.block == other.block &&
1574
+ this.startSide == other.startSide && this.endSide == other.endSide;
1575
+ }
1576
+ range(from, to = from) {
1577
+ if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
1578
+ throw new RangeError("Invalid range for replacement decoration");
1579
+ if (!this.isReplace && to != from)
1580
+ throw new RangeError("Widget decorations can only have zero-length ranges");
1581
+ return super.range(from, to);
1597
1582
  }
1598
1583
  }
1584
+ PointDecoration.prototype.point = true;
1585
+ function getInclusive(spec, block = false) {
1586
+ let { inclusiveStart: start, inclusiveEnd: end } = spec;
1587
+ if (start == null)
1588
+ start = spec.inclusive;
1589
+ if (end == null)
1590
+ end = spec.inclusive;
1591
+ return { start: start !== null && start !== void 0 ? start : block, end: end !== null && end !== void 0 ? end : block };
1592
+ }
1593
+ function widgetsEq(a, b) {
1594
+ return a == b || !!(a && b && a.compare(b));
1595
+ }
1596
+ function addRange(from, to, ranges, margin = 0) {
1597
+ let last = ranges.length - 1;
1598
+ if (last >= 0 && ranges[last] + margin >= from)
1599
+ ranges[last] = Math.max(ranges[last], to);
1600
+ else
1601
+ ranges.push(from, to);
1602
+ }
1599
1603
 
1600
1604
  class ContentBuilder {
1601
1605
  constructor(doc, pos, end, disallowBlockEffectsFor) {
@@ -1621,7 +1625,7 @@ class ContentBuilder {
1621
1625
  if (this.content.length == 0)
1622
1626
  return !this.breakAtStart && this.doc.lineAt(this.pos).from != this.pos;
1623
1627
  let last = this.content[this.content.length - 1];
1624
- return !last.breakAfter && !(last instanceof BlockWidgetView && last.type == BlockType.WidgetBefore);
1628
+ return !(last.breakAfter || last instanceof BlockWidgetView && last.deco.endSide < 0);
1625
1629
  }
1626
1630
  getLine() {
1627
1631
  if (!this.curLine) {
@@ -1646,7 +1650,7 @@ class ContentBuilder {
1646
1650
  this.flushBuffer();
1647
1651
  else
1648
1652
  this.pendingBuffer = 0 /* Buf.No */;
1649
- if (!this.posCovered())
1653
+ if (!openEnd && !this.posCovered())
1650
1654
  this.getLine();
1651
1655
  }
1652
1656
  buildText(length, active, openStart) {
@@ -1699,10 +1703,9 @@ class ContentBuilder {
1699
1703
  let len = to - from;
1700
1704
  if (deco instanceof PointDecoration) {
1701
1705
  if (deco.block) {
1702
- let { type } = deco;
1703
- if (type == BlockType.WidgetAfter && !this.posCovered())
1706
+ if (deco.startSide > 0 && !this.posCovered())
1704
1707
  this.getLine();
1705
- this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, type));
1708
+ this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, deco));
1706
1709
  }
1707
1710
  else {
1708
1711
  let view = WidgetView.create(deco.widget || new NullWidget("span"), len, len ? 0 : deco.startSide);
@@ -1837,10 +1840,15 @@ class ViewPlugin {
1837
1840
  /**
1838
1841
  @internal
1839
1842
  */
1840
- domEventHandlers, buildExtensions) {
1843
+ domEventHandlers,
1844
+ /**
1845
+ @internal
1846
+ */
1847
+ domEventObservers, buildExtensions) {
1841
1848
  this.id = id;
1842
1849
  this.create = create;
1843
1850
  this.domEventHandlers = domEventHandlers;
1851
+ this.domEventObservers = domEventObservers;
1844
1852
  this.extension = buildExtensions(this);
1845
1853
  }
1846
1854
  /**
@@ -1848,8 +1856,8 @@ class ViewPlugin {
1848
1856
  plugin's value, given an editor view.
1849
1857
  */
1850
1858
  static define(create, spec) {
1851
- const { eventHandlers, provide, decorations: deco } = spec || {};
1852
- return new ViewPlugin(nextPluginID++, create, eventHandlers, plugin => {
1859
+ const { eventHandlers, eventObservers, provide, decorations: deco } = spec || {};
1860
+ return new ViewPlugin(nextPluginID++, create, eventHandlers, eventObservers, plugin => {
1853
1861
  let ext = [viewPlugin.of(plugin)];
1854
1862
  if (deco)
1855
1863
  ext.push(decorations.of(view => {
@@ -2934,15 +2942,19 @@ class DocView extends ContentView {
2934
2942
  return this.children[i].domAtPos(off);
2935
2943
  }
2936
2944
  coordsAt(pos, side) {
2937
- for (let off = this.length, i = this.children.length - 1;; i--) {
2938
- let child = this.children[i], start = off - child.breakAfter - child.length;
2939
- if (pos > start ||
2940
- (pos == start && child.type != BlockType.WidgetBefore && child.type != BlockType.WidgetAfter &&
2941
- (!i || side == 2 || this.children[i - 1].breakAfter ||
2942
- (this.children[i - 1].type == BlockType.WidgetBefore && side > -2))))
2943
- return child.coordsAt(pos - start, side);
2945
+ let best = null, bestPos = 0;
2946
+ for (let off = this.length, i = this.children.length - 1; i >= 0; i--) {
2947
+ let child = this.children[i], end = off - child.breakAfter, start = end - child.length;
2948
+ if (end < pos)
2949
+ break;
2950
+ if (start <= pos && (start < pos || child.covers(-1)) && (end > pos || child.covers(1)) &&
2951
+ (!best || child instanceof LineView && !(best instanceof LineView && side >= 0))) {
2952
+ best = child;
2953
+ bestPos = start;
2954
+ }
2944
2955
  off = start;
2945
2956
  }
2957
+ return best ? best.coordsAt(pos - bestPos, side) : null;
2946
2958
  }
2947
2959
  coordsForChar(pos) {
2948
2960
  let { i, off } = this.childPos(pos, 1), child = this.children[i];
@@ -3577,13 +3589,13 @@ class InputState {
3577
3589
  this.lastSelectionTime = Date.now();
3578
3590
  }
3579
3591
  constructor(view) {
3592
+ this.view = view;
3580
3593
  this.lastKeyCode = 0;
3581
3594
  this.lastKeyTime = 0;
3582
3595
  this.lastTouchTime = 0;
3583
3596
  this.lastFocusTime = 0;
3584
3597
  this.lastScrollTop = 0;
3585
3598
  this.lastScrollLeft = 0;
3586
- this.chromeScrollHack = -1;
3587
3599
  // On iOS, some keys need to have their default behavior happen
3588
3600
  // (after which we retroactively handle them and reset the DOM) to
3589
3601
  // avoid messing up the virtual keyboard state.
@@ -3593,8 +3605,7 @@ class InputState {
3593
3605
  this.lastEscPress = 0;
3594
3606
  this.lastContextMenu = 0;
3595
3607
  this.scrollHandlers = [];
3596
- this.registeredEvents = [];
3597
- this.customHandlers = [];
3608
+ this.handlers = Object.create(null);
3598
3609
  // -1 means not in a composition. Otherwise, this counts the number
3599
3610
  // of changes made during the composition. The count is used to
3600
3611
  // avoid treating the start state of the composition, before any
@@ -3615,29 +3626,10 @@ class InputState {
3615
3626
  // the mutation events fire shortly after the compositionend event
3616
3627
  this.compositionPendingChange = false;
3617
3628
  this.mouseSelection = null;
3618
- let handleEvent = (handler, event) => {
3619
- if (this.ignoreDuringComposition(event))
3620
- return;
3621
- if (event.type == "keydown" && this.keydown(view, event))
3622
- return;
3623
- if (this.mustFlushObserver(event))
3624
- view.observer.forceFlush();
3625
- if (this.runCustomHandlers(event.type, view, event))
3626
- event.preventDefault();
3627
- else
3628
- handler(view, event);
3629
- };
3630
- for (let type in handlers) {
3631
- let handler = handlers[type];
3632
- view.contentDOM.addEventListener(type, event => {
3633
- if (eventBelongsToEditor(view, event))
3634
- handleEvent(handler, event);
3635
- }, handlerOptions[type]);
3636
- this.registeredEvents.push(type);
3637
- }
3629
+ this.handleEvent = this.handleEvent.bind(this);
3638
3630
  view.scrollDOM.addEventListener("mousedown", (event) => {
3639
3631
  if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom) {
3640
- handleEvent(handlers.mousedown, event);
3632
+ this.handleEvent(event);
3641
3633
  if (!event.defaultPrevented && event.button == 2) {
3642
3634
  // Make sure the content covers the entire scroller height, in order
3643
3635
  // to catch a native context menu click below it
@@ -3649,23 +3641,8 @@ class InputState {
3649
3641
  });
3650
3642
  view.scrollDOM.addEventListener("drop", (event) => {
3651
3643
  if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom)
3652
- handleEvent(handlers.drop, event);
3644
+ this.handleEvent(event);
3653
3645
  });
3654
- if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
3655
- // On Chrome 102, viewport updates somehow stop wheel-based
3656
- // scrolling. Turning off pointer events during the scroll seems
3657
- // to avoid the issue.
3658
- view.scrollDOM.addEventListener("wheel", () => {
3659
- if (this.chromeScrollHack < 0)
3660
- view.contentDOM.style.pointerEvents = "none";
3661
- else
3662
- window.clearTimeout(this.chromeScrollHack);
3663
- this.chromeScrollHack = setTimeout(() => {
3664
- this.chromeScrollHack = -1;
3665
- view.contentDOM.style.pointerEvents = "";
3666
- }, 100);
3667
- }, { passive: true });
3668
- }
3669
3646
  this.notifiedFocused = view.hasFocus;
3670
3647
  // On Safari adding an input event handler somehow prevents an
3671
3648
  // issue where the composition vanishes when you press enter.
@@ -3674,63 +3651,54 @@ class InputState {
3674
3651
  if (browser.gecko)
3675
3652
  firefoxCopyCutHack(view.contentDOM.ownerDocument);
3676
3653
  }
3677
- ensureHandlers(view, plugins) {
3678
- var _a;
3679
- let handlers;
3680
- this.customHandlers = [];
3681
- for (let plugin of plugins)
3682
- if (handlers = (_a = plugin.update(view).spec) === null || _a === void 0 ? void 0 : _a.domEventHandlers) {
3683
- this.customHandlers.push({ plugin: plugin.value, handlers });
3684
- for (let type in handlers)
3685
- if (this.registeredEvents.indexOf(type) < 0 && type != "scroll") {
3686
- this.registeredEvents.push(type);
3687
- view.contentDOM.addEventListener(type, (event) => {
3688
- if (!eventBelongsToEditor(view, event))
3689
- return;
3690
- if (this.runCustomHandlers(type, view, event))
3691
- event.preventDefault();
3692
- });
3693
- }
3694
- }
3695
- }
3696
- runCustomHandlers(type, view, event) {
3697
- for (let set of this.customHandlers) {
3698
- let handler = set.handlers[type];
3699
- if (handler) {
3700
- try {
3701
- if (handler.call(set.plugin, event, view) || event.defaultPrevented)
3702
- return true;
3703
- }
3704
- catch (e) {
3705
- logException(view.state, e);
3654
+ handleEvent(event) {
3655
+ if (!eventBelongsToEditor(this.view, event) || this.ignoreDuringComposition(event))
3656
+ return;
3657
+ if (event.type == "keydown" && this.keydown(event))
3658
+ return;
3659
+ this.runHandlers(event.type, event);
3660
+ }
3661
+ runHandlers(type, event) {
3662
+ let handlers = this.handlers[type];
3663
+ if (handlers) {
3664
+ for (let observer of handlers.observers)
3665
+ observer(this.view, event);
3666
+ for (let handler of handlers.handlers) {
3667
+ if (event.defaultPrevented)
3668
+ break;
3669
+ if (handler(this.view, event)) {
3670
+ event.preventDefault();
3671
+ break;
3706
3672
  }
3707
3673
  }
3708
3674
  }
3709
- return false;
3710
3675
  }
3711
- runScrollHandlers(view, event) {
3712
- this.lastScrollTop = view.scrollDOM.scrollTop;
3713
- this.lastScrollLeft = view.scrollDOM.scrollLeft;
3714
- for (let set of this.customHandlers) {
3715
- let handler = set.handlers.scroll;
3716
- if (handler) {
3717
- try {
3718
- handler.call(set.plugin, event, view);
3719
- }
3720
- catch (e) {
3721
- logException(view.state, e);
3676
+ ensureHandlers(plugins) {
3677
+ let handlers = computeHandlers(plugins), prev = this.handlers, dom = this.view.contentDOM;
3678
+ for (let type in handlers)
3679
+ if (type != "scroll") {
3680
+ let passive = !handlers[type].handlers.length;
3681
+ let exists = prev[type];
3682
+ if (exists && passive != !exists.handlers.length) {
3683
+ dom.removeEventListener(type, this.handleEvent);
3684
+ exists = null;
3722
3685
  }
3686
+ if (!exists)
3687
+ dom.addEventListener(type, this.handleEvent, { passive });
3723
3688
  }
3724
- }
3689
+ for (let type in prev)
3690
+ if (type != "scroll" && !handlers[type])
3691
+ dom.removeEventListener(type, this.handleEvent);
3692
+ this.handlers = handlers;
3725
3693
  }
3726
- keydown(view, event) {
3694
+ keydown(event) {
3727
3695
  // Must always run, even if a custom handler handled the event
3728
3696
  this.lastKeyCode = event.keyCode;
3729
3697
  this.lastKeyTime = Date.now();
3730
3698
  if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
3731
3699
  return true;
3732
3700
  if (event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0)
3733
- view.inputState.lastEscPress = 0;
3701
+ this.view.inputState.lastEscPress = 0;
3734
3702
  // Chrome for Android usually doesn't fire proper key events, but
3735
3703
  // occasionally does, usually surrounded by a bunch of complicated
3736
3704
  // composition changes. When an enter or backspace key event is
@@ -3738,10 +3706,10 @@ class InputState {
3738
3706
  // dispatch it.
3739
3707
  if (browser.android && browser.chrome && !event.synthetic &&
3740
3708
  (event.keyCode == 13 || event.keyCode == 8)) {
3741
- view.observer.delayAndroidKey(event.key, event.keyCode);
3709
+ this.view.observer.delayAndroidKey(event.key, event.keyCode);
3742
3710
  return true;
3743
3711
  }
3744
- // Prevent the default behavior of Enter on iOS makes the
3712
+ // Preventing the default behavior of Enter on iOS makes the
3745
3713
  // virtual keyboard get stuck in the wrong (lowercase)
3746
3714
  // state. So we let it go through, and then, in
3747
3715
  // applyDOMChange, notify key handlers of it and reset to
@@ -3751,17 +3719,19 @@ class InputState {
3751
3719
  ((pending = PendingKeys.find(key => key.keyCode == event.keyCode)) && !event.ctrlKey ||
3752
3720
  EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey && !event.shiftKey)) {
3753
3721
  this.pendingIOSKey = pending || event;
3754
- setTimeout(() => this.flushIOSKey(view), 250);
3722
+ setTimeout(() => this.flushIOSKey(), 250);
3755
3723
  return true;
3756
3724
  }
3725
+ if (event.keyCode != 229)
3726
+ this.view.observer.forceFlush();
3757
3727
  return false;
3758
3728
  }
3759
- flushIOSKey(view) {
3729
+ flushIOSKey() {
3760
3730
  let key = this.pendingIOSKey;
3761
3731
  if (!key)
3762
3732
  return false;
3763
3733
  this.pendingIOSKey = undefined;
3764
- return dispatchKey(view.contentDOM, key.key, key.keyCode);
3734
+ return dispatchKey(this.view.contentDOM, key.key, key.keyCode);
3765
3735
  }
3766
3736
  ignoreDuringComposition(event) {
3767
3737
  if (!/^key/.test(event.type))
@@ -3780,9 +3750,6 @@ class InputState {
3780
3750
  }
3781
3751
  return false;
3782
3752
  }
3783
- mustFlushObserver(event) {
3784
- return event.type == "keydown" && event.keyCode != 229;
3785
- }
3786
3753
  startMouseSelection(mouseSelection) {
3787
3754
  if (this.mouseSelection)
3788
3755
  this.mouseSelection.destroy();
@@ -3799,6 +3766,42 @@ class InputState {
3799
3766
  this.mouseSelection.destroy();
3800
3767
  }
3801
3768
  }
3769
+ function bindHandler(plugin, handler) {
3770
+ return (view, event) => {
3771
+ try {
3772
+ return handler.call(plugin, event, view);
3773
+ }
3774
+ catch (e) {
3775
+ logException(view.state, e);
3776
+ }
3777
+ };
3778
+ }
3779
+ function computeHandlers(plugins) {
3780
+ let result = Object.create(null);
3781
+ function record(type) {
3782
+ return result[type] || (result[type] = { observers: [], handlers: [] });
3783
+ }
3784
+ for (let plugin of plugins) {
3785
+ let spec = plugin.spec;
3786
+ if (spec && spec.domEventHandlers)
3787
+ for (let type in spec.domEventHandlers) {
3788
+ let f = spec.domEventHandlers[type];
3789
+ if (f)
3790
+ record(type).handlers.push(bindHandler(plugin.value, f));
3791
+ }
3792
+ if (spec && spec.domEventObservers)
3793
+ for (let type in spec.domEventObservers) {
3794
+ let f = spec.domEventObservers[type];
3795
+ if (f)
3796
+ record(type).observers.push(bindHandler(plugin.value, f));
3797
+ }
3798
+ }
3799
+ for (let type in handlers)
3800
+ record(type).handlers.push(handlers[type]);
3801
+ for (let type in observers)
3802
+ record(type).observers.push(observers[type]);
3803
+ return result;
3804
+ }
3802
3805
  const PendingKeys = [
3803
3806
  { key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
3804
3807
  { key: "Enter", keyCode: 13, inputType: "insertParagraph" },
@@ -3836,10 +3839,8 @@ class MouseSelection {
3836
3839
  start(event) {
3837
3840
  // When clicking outside of the selection, immediately apply the
3838
3841
  // effect of starting the selection
3839
- if (this.dragging === false) {
3840
- event.preventDefault();
3842
+ if (this.dragging === false)
3841
3843
  this.select(event);
3842
- }
3843
3844
  }
3844
3845
  move(event) {
3845
3846
  var _a;
@@ -3975,7 +3976,7 @@ function eventBelongsToEditor(view, event) {
3975
3976
  return true;
3976
3977
  }
3977
3978
  const handlers = /*@__PURE__*/Object.create(null);
3978
- const handlerOptions = /*@__PURE__*/Object.create(null);
3979
+ const observers = /*@__PURE__*/Object.create(null);
3979
3980
  // This is very crude, but unfortunately both these browsers _pretend_
3980
3981
  // that they have a clipboard API—all the objects and methods are
3981
3982
  // there, they just don't work, and they are hard to test.
@@ -4025,23 +4026,27 @@ function doPaste(view, input) {
4025
4026
  scrollIntoView: true
4026
4027
  });
4027
4028
  }
4029
+ observers.scroll = view => {
4030
+ view.inputState.lastScrollTop = view.scrollDOM.scrollTop;
4031
+ view.inputState.lastScrollLeft = view.scrollDOM.scrollLeft;
4032
+ };
4028
4033
  handlers.keydown = (view, event) => {
4029
4034
  view.inputState.setSelectionOrigin("select");
4030
4035
  if (event.keyCode == 27)
4031
4036
  view.inputState.lastEscPress = Date.now();
4037
+ return false;
4032
4038
  };
4033
- handlers.touchstart = (view, e) => {
4039
+ observers.touchstart = (view, e) => {
4034
4040
  view.inputState.lastTouchTime = Date.now();
4035
4041
  view.inputState.setSelectionOrigin("select.pointer");
4036
4042
  };
4037
- handlers.touchmove = view => {
4043
+ observers.touchmove = view => {
4038
4044
  view.inputState.setSelectionOrigin("select.pointer");
4039
4045
  };
4040
- handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
4041
4046
  handlers.mousedown = (view, event) => {
4042
4047
  view.observer.flush();
4043
4048
  if (view.inputState.lastTouchTime > Date.now() - 2000)
4044
- return; // Ignore touch interaction
4049
+ return false; // Ignore touch interaction
4045
4050
  let style = null;
4046
4051
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
4047
4052
  style = makeStyle(view, event);
@@ -4055,9 +4060,13 @@ handlers.mousedown = (view, event) => {
4055
4060
  view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
4056
4061
  if (mustFocus)
4057
4062
  view.observer.ignore(() => focusPreventScroll(view.contentDOM));
4058
- if (view.inputState.mouseSelection)
4059
- view.inputState.mouseSelection.start(event);
4063
+ let mouseSel = view.inputState.mouseSelection;
4064
+ if (mouseSel) {
4065
+ mouseSel.start(event);
4066
+ return !mouseSel.dragging;
4067
+ }
4060
4068
  }
4069
+ return false;
4061
4070
  };
4062
4071
  function rangeForClick(view, pos, bias, type) {
4063
4072
  if (type == 1) { // Single click
@@ -4161,12 +4170,12 @@ handlers.dragstart = (view, event) => {
4161
4170
  event.dataTransfer.setData("Text", view.state.sliceDoc(main.from, main.to));
4162
4171
  event.dataTransfer.effectAllowed = "copyMove";
4163
4172
  }
4173
+ return false;
4164
4174
  };
4165
4175
  function dropText(view, event, text, direct) {
4166
4176
  if (!text)
4167
4177
  return;
4168
4178
  let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
4169
- event.preventDefault();
4170
4179
  let { mouseSelection } = view.inputState;
4171
4180
  let del = direct && mouseSelection && mouseSelection.dragging && dragMovesSelection(view, event) ?
4172
4181
  { from: mouseSelection.dragging.from, to: mouseSelection.dragging.to } : null;
@@ -4181,12 +4190,11 @@ function dropText(view, event, text, direct) {
4181
4190
  }
4182
4191
  handlers.drop = (view, event) => {
4183
4192
  if (!event.dataTransfer)
4184
- return;
4193
+ return false;
4185
4194
  if (view.state.readOnly)
4186
- return event.preventDefault();
4195
+ return true;
4187
4196
  let files = event.dataTransfer.files;
4188
4197
  if (files && files.length) { // For a file drop, read the file's text.
4189
- event.preventDefault();
4190
4198
  let text = Array(files.length), read = 0;
4191
4199
  let finishFile = () => {
4192
4200
  if (++read == files.length)
@@ -4202,22 +4210,29 @@ handlers.drop = (view, event) => {
4202
4210
  };
4203
4211
  reader.readAsText(files[i]);
4204
4212
  }
4213
+ return true;
4205
4214
  }
4206
4215
  else {
4207
- dropText(view, event, event.dataTransfer.getData("Text"), true);
4216
+ let text = event.dataTransfer.getData("Text");
4217
+ if (text) {
4218
+ dropText(view, event, text, true);
4219
+ return true;
4220
+ }
4208
4221
  }
4222
+ return false;
4209
4223
  };
4210
4224
  handlers.paste = (view, event) => {
4211
4225
  if (view.state.readOnly)
4212
- return event.preventDefault();
4226
+ return true;
4213
4227
  view.observer.flush();
4214
4228
  let data = brokenClipboardAPI ? null : event.clipboardData;
4215
4229
  if (data) {
4216
4230
  doPaste(view, data.getData("text/plain") || data.getData("text/uri-text"));
4217
- event.preventDefault();
4231
+ return true;
4218
4232
  }
4219
4233
  else {
4220
4234
  capturePaste(view);
4235
+ return false;
4221
4236
  }
4222
4237
  };
4223
4238
  function captureCopy(view, text) {
@@ -4263,23 +4278,24 @@ let lastLinewiseCopy = null;
4263
4278
  handlers.copy = handlers.cut = (view, event) => {
4264
4279
  let { text, ranges, linewise } = copiedRange(view.state);
4265
4280
  if (!text && !linewise)
4266
- return;
4281
+ return false;
4267
4282
  lastLinewiseCopy = linewise ? text : null;
4283
+ if (event.type == "cut" && !view.state.readOnly)
4284
+ view.dispatch({
4285
+ changes: ranges,
4286
+ scrollIntoView: true,
4287
+ userEvent: "delete.cut"
4288
+ });
4268
4289
  let data = brokenClipboardAPI ? null : event.clipboardData;
4269
4290
  if (data) {
4270
- event.preventDefault();
4271
4291
  data.clearData();
4272
4292
  data.setData("text/plain", text);
4293
+ return true;
4273
4294
  }
4274
4295
  else {
4275
4296
  captureCopy(view, text);
4297
+ return false;
4276
4298
  }
4277
- if (event.type == "cut" && !view.state.readOnly)
4278
- view.dispatch({
4279
- changes: ranges,
4280
- scrollIntoView: true,
4281
- userEvent: "delete.cut"
4282
- });
4283
4299
  };
4284
4300
  const isFocusChange = /*@__PURE__*/Annotation.define();
4285
4301
  function focusChangeTransaction(state, focus) {
@@ -4303,7 +4319,7 @@ function updateForFocusChange(view) {
4303
4319
  }
4304
4320
  }, 10);
4305
4321
  }
4306
- handlers.focus = view => {
4322
+ observers.focus = view => {
4307
4323
  view.inputState.lastFocusTime = Date.now();
4308
4324
  // When focusing reset the scroll position, move it back to where it was
4309
4325
  if (!view.scrollDOM.scrollTop && (view.inputState.lastScrollTop || view.inputState.lastScrollLeft)) {
@@ -4312,11 +4328,11 @@ handlers.focus = view => {
4312
4328
  }
4313
4329
  updateForFocusChange(view);
4314
4330
  };
4315
- handlers.blur = view => {
4331
+ observers.blur = view => {
4316
4332
  view.observer.clearSelectionRange();
4317
4333
  updateForFocusChange(view);
4318
4334
  };
4319
- handlers.compositionstart = handlers.compositionupdate = view => {
4335
+ observers.compositionstart = observers.compositionupdate = view => {
4320
4336
  if (view.inputState.compositionFirstChange == null)
4321
4337
  view.inputState.compositionFirstChange = true;
4322
4338
  if (view.inputState.composing < 0) {
@@ -4324,7 +4340,7 @@ handlers.compositionstart = handlers.compositionupdate = view => {
4324
4340
  view.inputState.composing = 0;
4325
4341
  }
4326
4342
  };
4327
- handlers.compositionend = view => {
4343
+ observers.compositionend = view => {
4328
4344
  view.inputState.composing = -1;
4329
4345
  view.inputState.compositionEndedAt = Date.now();
4330
4346
  view.inputState.compositionPendingKey = true;
@@ -4348,7 +4364,7 @@ handlers.compositionend = view => {
4348
4364
  }, 50);
4349
4365
  }
4350
4366
  };
4351
- handlers.contextmenu = view => {
4367
+ observers.contextmenu = view => {
4352
4368
  view.inputState.lastContextMenu = Date.now();
4353
4369
  };
4354
4370
  handlers.beforeinput = (view, event) => {
@@ -4377,6 +4393,7 @@ handlers.beforeinput = (view, event) => {
4377
4393
  }, 100);
4378
4394
  }
4379
4395
  }
4396
+ return false;
4380
4397
  };
4381
4398
  const appliedFirefoxHack = /*@__PURE__*/new Set;
4382
4399
  // In Firefox, when cut/copy handlers are added to the document, that
@@ -5046,14 +5063,13 @@ class NodeBuilder {
5046
5063
  return line;
5047
5064
  }
5048
5065
  addBlock(block) {
5049
- var _a;
5050
5066
  this.enterLine();
5051
- let type = (_a = block.deco) === null || _a === void 0 ? void 0 : _a.type;
5052
- if (type == BlockType.WidgetAfter && !this.isCovered)
5067
+ let deco = block.deco;
5068
+ if (deco && deco.startSide > 0 && !this.isCovered)
5053
5069
  this.ensureLine();
5054
5070
  this.nodes.push(block);
5055
5071
  this.writtenTo = this.pos = this.pos + block.length;
5056
- if (type != BlockType.WidgetBefore)
5072
+ if (deco && deco.endSide > 0)
5057
5073
  this.covering = block;
5058
5074
  }
5059
5075
  addLineDeco(height, breaks, length) {
@@ -6161,7 +6177,7 @@ function applyDOMChange(view, domChange) {
6161
6177
  change = { from: sel.from, to: sel.to, insert: Text.of([" "]) };
6162
6178
  }
6163
6179
  if (change) {
6164
- if (browser.ios && view.inputState.flushIOSKey(view))
6180
+ if (browser.ios && view.inputState.flushIOSKey())
6165
6181
  return true;
6166
6182
  // Android browsers don't fire reasonable key events for enter,
6167
6183
  // backspace, or delete. So this detects changes that look like
@@ -6415,7 +6431,7 @@ class DOMObserver {
6415
6431
  this.readSelectionRange();
6416
6432
  }
6417
6433
  onScrollChanged(e) {
6418
- this.view.inputState.runScrollHandlers(this.view, e);
6434
+ this.view.inputState.runHandlers("scroll", e);
6419
6435
  if (this.intersecting)
6420
6436
  this.view.measure();
6421
6437
  }
@@ -6886,7 +6902,7 @@ class EditorView {
6886
6902
  plugin.update(this);
6887
6903
  this.observer = new DOMObserver(this);
6888
6904
  this.inputState = new InputState(this);
6889
- this.inputState.ensureHandlers(this, this.plugins);
6905
+ this.inputState.ensureHandlers(this.plugins);
6890
6906
  this.docView = new DocView(this);
6891
6907
  this.mountStyles();
6892
6908
  this.updateAttrs();
@@ -7028,7 +7044,7 @@ class EditorView {
7028
7044
  for (let plugin of this.plugins)
7029
7045
  plugin.update(this);
7030
7046
  this.docView = new DocView(this);
7031
- this.inputState.ensureHandlers(this, this.plugins);
7047
+ this.inputState.ensureHandlers(this.plugins);
7032
7048
  this.mountStyles();
7033
7049
  this.updateAttrs();
7034
7050
  this.bidiCache = [];
@@ -7060,7 +7076,7 @@ class EditorView {
7060
7076
  plugin.destroy(this);
7061
7077
  this.plugins = newPlugins;
7062
7078
  this.pluginMap.clear();
7063
- this.inputState.ensureHandlers(this, this.plugins);
7079
+ this.inputState.ensureHandlers(this.plugins);
7064
7080
  }
7065
7081
  else {
7066
7082
  for (let p of this.plugins)
@@ -7588,6 +7604,17 @@ class EditorView {
7588
7604
  return ViewPlugin.define(() => ({}), { eventHandlers: handlers });
7589
7605
  }
7590
7606
  /**
7607
+ Create an extension that registers DOM event observers. Contrary
7608
+ to event [handlers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventHandlers),
7609
+ observers can't be prevented from running by a higher-precedence
7610
+ handler returning true. They also don't prevent other handlers
7611
+ and observers from running when they return true, and should not
7612
+ call `preventDefault`.
7613
+ */
7614
+ static domEventObservers(observers) {
7615
+ return ViewPlugin.define(() => ({}), { eventObservers: observers });
7616
+ }
7617
+ /**
7591
7618
  Create a theme extension. The first argument can be a
7592
7619
  [`style-mod`](https://github.com/marijnh/style-mod#documentation)
7593
7620
  style spec providing the styles for the theme. These will be
@@ -8440,7 +8467,7 @@ const drawDropCursor = /*@__PURE__*/ViewPlugin.fromClass(class {
8440
8467
  this.view.dispatch({ effects: setDropCursorPos.of(pos) });
8441
8468
  }
8442
8469
  }, {
8443
- eventHandlers: {
8470
+ eventObservers: {
8444
8471
  dragover(event) {
8445
8472
  this.setDropPos(this.view.posAtCoords({ x: event.clientX, y: event.clientY }));
8446
8473
  },
@@ -8950,7 +8977,7 @@ function crosshairCursor(options = {}) {
8950
8977
  }
8951
8978
  }
8952
8979
  }, {
8953
- eventHandlers: {
8980
+ eventObservers: {
8954
8981
  keydown(e) {
8955
8982
  this.set(e.keyCode == code || getter(e));
8956
8983
  },
@@ -9261,7 +9288,7 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
9261
9288
  }
9262
9289
  }
9263
9290
  }, {
9264
- eventHandlers: {
9291
+ eventObservers: {
9265
9292
  scroll() { this.maybeMeasure(); }
9266
9293
  }
9267
9294
  });