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