@codemirror/view 6.18.1 → 6.20.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
@@ -581,7 +581,7 @@ function replaceRange(parent, fromI, fromOff, toI, toOff, insert, breakAtStart,
581
581
  if (toI < children.length) {
582
582
  let after = children[toI];
583
583
  // Make sure the end of the child after the update is preserved in `after`
584
- if (after && toOff < after.length) {
584
+ if (after && (toOff < after.length || after.breakAfter && (last === null || last === void 0 ? void 0 : last.breakAfter))) {
585
585
  // If we're splitting a child, separate part of it to avoid that
586
586
  // being mangled when updating the child before the update.
587
587
  if (fromI == toI) {
@@ -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
@@ -1259,346 +1489,120 @@ class Decoration extends state.RangeValue {
1259
1489
  startSide = (start ? (block ? -300000000 /* Side.BlockIncStart */ : -1 /* Side.InlineIncStart */) : 500000000 /* Side.NonIncStart */) - 1;
1260
1490
  endSide = (end ? (block ? 200000000 /* Side.BlockIncEnd */ : 1 /* Side.InlineIncEnd */) : -600000000 /* Side.NonIncEnd */) + 1;
1261
1491
  }
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
- }
1515
- }
1516
- return rect;
1492
+ return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1517
1493
  }
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;
1530
- }
1531
- return null;
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];
@@ -3532,7 +3544,7 @@ function moveVertically(view, start, forward, distance) {
3532
3544
  return state.EditorSelection.cursor(startPos, start.assoc);
3533
3545
  let goal = start.goalColumn, startY;
3534
3546
  let rect = view.contentDOM.getBoundingClientRect();
3535
- let startCoords = view.coordsAtPos(startPos), docTop = view.documentTop;
3547
+ let startCoords = view.coordsAtPos(startPos, start.assoc || -1), docTop = view.documentTop;
3536
3548
  if (startCoords) {
3537
3549
  if (goal == null)
3538
3550
  goal = startCoords.left - rect.left;
@@ -3549,8 +3561,11 @@ function moveVertically(view, start, forward, distance) {
3549
3561
  for (let extra = 0;; extra += 10) {
3550
3562
  let curY = startY + (dist + extra) * dir;
3551
3563
  let pos = posAtCoords(view, { x: resolvedGoal, y: curY }, false, dir);
3552
- if (curY < rect.top || curY > rect.bottom || (dir < 0 ? pos < startPos : pos > startPos))
3553
- return state.EditorSelection.cursor(pos, start.assoc, undefined, goal);
3564
+ if (curY < rect.top || curY > rect.bottom || (dir < 0 ? pos < startPos : pos > startPos)) {
3565
+ let charRect = view.docView.coordsForChar(pos);
3566
+ let assoc = !charRect || curY < charRect.top ? -1 : 1;
3567
+ return state.EditorSelection.cursor(pos, assoc, undefined, goal);
3568
+ }
3554
3569
  }
3555
3570
  }
3556
3571
  function skipAtomicRanges(atoms, pos, bias) {
@@ -3581,13 +3596,13 @@ class InputState {
3581
3596
  this.lastSelectionTime = Date.now();
3582
3597
  }
3583
3598
  constructor(view) {
3599
+ this.view = view;
3584
3600
  this.lastKeyCode = 0;
3585
3601
  this.lastKeyTime = 0;
3586
3602
  this.lastTouchTime = 0;
3587
3603
  this.lastFocusTime = 0;
3588
3604
  this.lastScrollTop = 0;
3589
3605
  this.lastScrollLeft = 0;
3590
- this.chromeScrollHack = -1;
3591
3606
  // On iOS, some keys need to have their default behavior happen
3592
3607
  // (after which we retroactively handle them and reset the DOM) to
3593
3608
  // avoid messing up the virtual keyboard state.
@@ -3597,8 +3612,7 @@ class InputState {
3597
3612
  this.lastEscPress = 0;
3598
3613
  this.lastContextMenu = 0;
3599
3614
  this.scrollHandlers = [];
3600
- this.registeredEvents = [];
3601
- this.customHandlers = [];
3615
+ this.handlers = Object.create(null);
3602
3616
  // -1 means not in a composition. Otherwise, this counts the number
3603
3617
  // of changes made during the composition. The count is used to
3604
3618
  // avoid treating the start state of the composition, before any
@@ -3619,29 +3633,10 @@ class InputState {
3619
3633
  // the mutation events fire shortly after the compositionend event
3620
3634
  this.compositionPendingChange = false;
3621
3635
  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
- }
3636
+ this.handleEvent = this.handleEvent.bind(this);
3642
3637
  view.scrollDOM.addEventListener("mousedown", (event) => {
3643
3638
  if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom) {
3644
- handleEvent(handlers.mousedown, event);
3639
+ this.runHandlers("mousedown", event);
3645
3640
  if (!event.defaultPrevented && event.button == 2) {
3646
3641
  // Make sure the content covers the entire scroller height, in order
3647
3642
  // to catch a native context menu click below it
@@ -3653,23 +3648,8 @@ class InputState {
3653
3648
  });
3654
3649
  view.scrollDOM.addEventListener("drop", (event) => {
3655
3650
  if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom)
3656
- handleEvent(handlers.drop, event);
3651
+ this.runHandlers("drop", event);
3657
3652
  });
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
3653
  this.notifiedFocused = view.hasFocus;
3674
3654
  // On Safari adding an input event handler somehow prevents an
3675
3655
  // issue where the composition vanishes when you press enter.
@@ -3678,63 +3658,54 @@ class InputState {
3678
3658
  if (browser.gecko)
3679
3659
  firefoxCopyCutHack(view.contentDOM.ownerDocument);
3680
3660
  }
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);
3661
+ handleEvent(event) {
3662
+ if (!eventBelongsToEditor(this.view, event) || this.ignoreDuringComposition(event))
3663
+ return;
3664
+ if (event.type == "keydown" && this.keydown(event))
3665
+ return;
3666
+ this.runHandlers(event.type, event);
3667
+ }
3668
+ runHandlers(type, event) {
3669
+ let handlers = this.handlers[type];
3670
+ if (handlers) {
3671
+ for (let observer of handlers.observers)
3672
+ observer(this.view, event);
3673
+ for (let handler of handlers.handlers) {
3674
+ if (event.defaultPrevented)
3675
+ break;
3676
+ if (handler(this.view, event)) {
3677
+ event.preventDefault();
3678
+ break;
3710
3679
  }
3711
3680
  }
3712
3681
  }
3713
- return false;
3714
3682
  }
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);
3683
+ ensureHandlers(plugins) {
3684
+ let handlers = computeHandlers(plugins), prev = this.handlers, dom = this.view.contentDOM;
3685
+ for (let type in handlers)
3686
+ if (type != "scroll") {
3687
+ let passive = !handlers[type].handlers.length;
3688
+ let exists = prev[type];
3689
+ if (exists && passive != !exists.handlers.length) {
3690
+ dom.removeEventListener(type, this.handleEvent);
3691
+ exists = null;
3726
3692
  }
3693
+ if (!exists)
3694
+ dom.addEventListener(type, this.handleEvent, { passive });
3727
3695
  }
3728
- }
3696
+ for (let type in prev)
3697
+ if (type != "scroll" && !handlers[type])
3698
+ dom.removeEventListener(type, this.handleEvent);
3699
+ this.handlers = handlers;
3729
3700
  }
3730
- keydown(view, event) {
3701
+ keydown(event) {
3731
3702
  // Must always run, even if a custom handler handled the event
3732
3703
  this.lastKeyCode = event.keyCode;
3733
3704
  this.lastKeyTime = Date.now();
3734
3705
  if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
3735
3706
  return true;
3736
3707
  if (event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0)
3737
- view.inputState.lastEscPress = 0;
3708
+ this.view.inputState.lastEscPress = 0;
3738
3709
  // Chrome for Android usually doesn't fire proper key events, but
3739
3710
  // occasionally does, usually surrounded by a bunch of complicated
3740
3711
  // composition changes. When an enter or backspace key event is
@@ -3742,10 +3713,10 @@ class InputState {
3742
3713
  // dispatch it.
3743
3714
  if (browser.android && browser.chrome && !event.synthetic &&
3744
3715
  (event.keyCode == 13 || event.keyCode == 8)) {
3745
- view.observer.delayAndroidKey(event.key, event.keyCode);
3716
+ this.view.observer.delayAndroidKey(event.key, event.keyCode);
3746
3717
  return true;
3747
3718
  }
3748
- // Prevent the default behavior of Enter on iOS makes the
3719
+ // Preventing the default behavior of Enter on iOS makes the
3749
3720
  // virtual keyboard get stuck in the wrong (lowercase)
3750
3721
  // state. So we let it go through, and then, in
3751
3722
  // applyDOMChange, notify key handlers of it and reset to
@@ -3755,17 +3726,19 @@ class InputState {
3755
3726
  ((pending = PendingKeys.find(key => key.keyCode == event.keyCode)) && !event.ctrlKey ||
3756
3727
  EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey && !event.shiftKey)) {
3757
3728
  this.pendingIOSKey = pending || event;
3758
- setTimeout(() => this.flushIOSKey(view), 250);
3729
+ setTimeout(() => this.flushIOSKey(), 250);
3759
3730
  return true;
3760
3731
  }
3732
+ if (event.keyCode != 229)
3733
+ this.view.observer.forceFlush();
3761
3734
  return false;
3762
3735
  }
3763
- flushIOSKey(view) {
3736
+ flushIOSKey() {
3764
3737
  let key = this.pendingIOSKey;
3765
3738
  if (!key)
3766
3739
  return false;
3767
3740
  this.pendingIOSKey = undefined;
3768
- return dispatchKey(view.contentDOM, key.key, key.keyCode);
3741
+ return dispatchKey(this.view.contentDOM, key.key, key.keyCode);
3769
3742
  }
3770
3743
  ignoreDuringComposition(event) {
3771
3744
  if (!/^key/.test(event.type))
@@ -3784,9 +3757,6 @@ class InputState {
3784
3757
  }
3785
3758
  return false;
3786
3759
  }
3787
- mustFlushObserver(event) {
3788
- return event.type == "keydown" && event.keyCode != 229;
3789
- }
3790
3760
  startMouseSelection(mouseSelection) {
3791
3761
  if (this.mouseSelection)
3792
3762
  this.mouseSelection.destroy();
@@ -3803,6 +3773,42 @@ class InputState {
3803
3773
  this.mouseSelection.destroy();
3804
3774
  }
3805
3775
  }
3776
+ function bindHandler(plugin, handler) {
3777
+ return (view, event) => {
3778
+ try {
3779
+ return handler.call(plugin, event, view);
3780
+ }
3781
+ catch (e) {
3782
+ logException(view.state, e);
3783
+ }
3784
+ };
3785
+ }
3786
+ function computeHandlers(plugins) {
3787
+ let result = Object.create(null);
3788
+ function record(type) {
3789
+ return result[type] || (result[type] = { observers: [], handlers: [] });
3790
+ }
3791
+ for (let plugin of plugins) {
3792
+ let spec = plugin.spec;
3793
+ if (spec && spec.domEventHandlers)
3794
+ for (let type in spec.domEventHandlers) {
3795
+ let f = spec.domEventHandlers[type];
3796
+ if (f)
3797
+ record(type).handlers.push(bindHandler(plugin.value, f));
3798
+ }
3799
+ if (spec && spec.domEventObservers)
3800
+ for (let type in spec.domEventObservers) {
3801
+ let f = spec.domEventObservers[type];
3802
+ if (f)
3803
+ record(type).observers.push(bindHandler(plugin.value, f));
3804
+ }
3805
+ }
3806
+ for (let type in handlers)
3807
+ record(type).handlers.push(handlers[type]);
3808
+ for (let type in observers)
3809
+ record(type).observers.push(observers[type]);
3810
+ return result;
3811
+ }
3806
3812
  const PendingKeys = [
3807
3813
  { key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
3808
3814
  { key: "Enter", keyCode: 13, inputType: "insertParagraph" },
@@ -3840,10 +3846,8 @@ class MouseSelection {
3840
3846
  start(event) {
3841
3847
  // When clicking outside of the selection, immediately apply the
3842
3848
  // effect of starting the selection
3843
- if (this.dragging === false) {
3844
- event.preventDefault();
3849
+ if (this.dragging === false)
3845
3850
  this.select(event);
3846
- }
3847
3851
  }
3848
3852
  move(event) {
3849
3853
  var _a;
@@ -3979,7 +3983,7 @@ function eventBelongsToEditor(view, event) {
3979
3983
  return true;
3980
3984
  }
3981
3985
  const handlers = Object.create(null);
3982
- const handlerOptions = Object.create(null);
3986
+ const observers = Object.create(null);
3983
3987
  // This is very crude, but unfortunately both these browsers _pretend_
3984
3988
  // that they have a clipboard API—all the objects and methods are
3985
3989
  // there, they just don't work, and they are hard to test.
@@ -4029,23 +4033,27 @@ function doPaste(view, input) {
4029
4033
  scrollIntoView: true
4030
4034
  });
4031
4035
  }
4036
+ observers.scroll = view => {
4037
+ view.inputState.lastScrollTop = view.scrollDOM.scrollTop;
4038
+ view.inputState.lastScrollLeft = view.scrollDOM.scrollLeft;
4039
+ };
4032
4040
  handlers.keydown = (view, event) => {
4033
4041
  view.inputState.setSelectionOrigin("select");
4034
4042
  if (event.keyCode == 27)
4035
4043
  view.inputState.lastEscPress = Date.now();
4044
+ return false;
4036
4045
  };
4037
- handlers.touchstart = (view, e) => {
4046
+ observers.touchstart = (view, e) => {
4038
4047
  view.inputState.lastTouchTime = Date.now();
4039
4048
  view.inputState.setSelectionOrigin("select.pointer");
4040
4049
  };
4041
- handlers.touchmove = view => {
4050
+ observers.touchmove = view => {
4042
4051
  view.inputState.setSelectionOrigin("select.pointer");
4043
4052
  };
4044
- handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
4045
4053
  handlers.mousedown = (view, event) => {
4046
4054
  view.observer.flush();
4047
4055
  if (view.inputState.lastTouchTime > Date.now() - 2000)
4048
- return; // Ignore touch interaction
4056
+ return false; // Ignore touch interaction
4049
4057
  let style = null;
4050
4058
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
4051
4059
  style = makeStyle(view, event);
@@ -4059,9 +4067,13 @@ handlers.mousedown = (view, event) => {
4059
4067
  view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
4060
4068
  if (mustFocus)
4061
4069
  view.observer.ignore(() => focusPreventScroll(view.contentDOM));
4062
- if (view.inputState.mouseSelection)
4063
- view.inputState.mouseSelection.start(event);
4070
+ let mouseSel = view.inputState.mouseSelection;
4071
+ if (mouseSel) {
4072
+ mouseSel.start(event);
4073
+ return !mouseSel.dragging;
4074
+ }
4064
4075
  }
4076
+ return false;
4065
4077
  };
4066
4078
  function rangeForClick(view, pos, bias, type) {
4067
4079
  if (type == 1) { // Single click
@@ -4165,12 +4177,12 @@ handlers.dragstart = (view, event) => {
4165
4177
  event.dataTransfer.setData("Text", view.state.sliceDoc(main.from, main.to));
4166
4178
  event.dataTransfer.effectAllowed = "copyMove";
4167
4179
  }
4180
+ return false;
4168
4181
  };
4169
4182
  function dropText(view, event, text, direct) {
4170
4183
  if (!text)
4171
4184
  return;
4172
4185
  let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
4173
- event.preventDefault();
4174
4186
  let { mouseSelection } = view.inputState;
4175
4187
  let del = direct && mouseSelection && mouseSelection.dragging && dragMovesSelection(view, event) ?
4176
4188
  { from: mouseSelection.dragging.from, to: mouseSelection.dragging.to } : null;
@@ -4185,12 +4197,11 @@ function dropText(view, event, text, direct) {
4185
4197
  }
4186
4198
  handlers.drop = (view, event) => {
4187
4199
  if (!event.dataTransfer)
4188
- return;
4200
+ return false;
4189
4201
  if (view.state.readOnly)
4190
- return event.preventDefault();
4202
+ return true;
4191
4203
  let files = event.dataTransfer.files;
4192
4204
  if (files && files.length) { // For a file drop, read the file's text.
4193
- event.preventDefault();
4194
4205
  let text = Array(files.length), read = 0;
4195
4206
  let finishFile = () => {
4196
4207
  if (++read == files.length)
@@ -4206,22 +4217,29 @@ handlers.drop = (view, event) => {
4206
4217
  };
4207
4218
  reader.readAsText(files[i]);
4208
4219
  }
4220
+ return true;
4209
4221
  }
4210
4222
  else {
4211
- dropText(view, event, event.dataTransfer.getData("Text"), true);
4223
+ let text = event.dataTransfer.getData("Text");
4224
+ if (text) {
4225
+ dropText(view, event, text, true);
4226
+ return true;
4227
+ }
4212
4228
  }
4229
+ return false;
4213
4230
  };
4214
4231
  handlers.paste = (view, event) => {
4215
4232
  if (view.state.readOnly)
4216
- return event.preventDefault();
4233
+ return true;
4217
4234
  view.observer.flush();
4218
4235
  let data = brokenClipboardAPI ? null : event.clipboardData;
4219
4236
  if (data) {
4220
4237
  doPaste(view, data.getData("text/plain") || data.getData("text/uri-text"));
4221
- event.preventDefault();
4238
+ return true;
4222
4239
  }
4223
4240
  else {
4224
4241
  capturePaste(view);
4242
+ return false;
4225
4243
  }
4226
4244
  };
4227
4245
  function captureCopy(view, text) {
@@ -4267,23 +4285,24 @@ let lastLinewiseCopy = null;
4267
4285
  handlers.copy = handlers.cut = (view, event) => {
4268
4286
  let { text, ranges, linewise } = copiedRange(view.state);
4269
4287
  if (!text && !linewise)
4270
- return;
4288
+ return false;
4271
4289
  lastLinewiseCopy = linewise ? text : null;
4290
+ if (event.type == "cut" && !view.state.readOnly)
4291
+ view.dispatch({
4292
+ changes: ranges,
4293
+ scrollIntoView: true,
4294
+ userEvent: "delete.cut"
4295
+ });
4272
4296
  let data = brokenClipboardAPI ? null : event.clipboardData;
4273
4297
  if (data) {
4274
- event.preventDefault();
4275
4298
  data.clearData();
4276
4299
  data.setData("text/plain", text);
4300
+ return true;
4277
4301
  }
4278
4302
  else {
4279
4303
  captureCopy(view, text);
4304
+ return false;
4280
4305
  }
4281
- if (event.type == "cut" && !view.state.readOnly)
4282
- view.dispatch({
4283
- changes: ranges,
4284
- scrollIntoView: true,
4285
- userEvent: "delete.cut"
4286
- });
4287
4306
  };
4288
4307
  const isFocusChange = state.Annotation.define();
4289
4308
  function focusChangeTransaction(state, focus) {
@@ -4307,7 +4326,7 @@ function updateForFocusChange(view) {
4307
4326
  }
4308
4327
  }, 10);
4309
4328
  }
4310
- handlers.focus = view => {
4329
+ observers.focus = view => {
4311
4330
  view.inputState.lastFocusTime = Date.now();
4312
4331
  // When focusing reset the scroll position, move it back to where it was
4313
4332
  if (!view.scrollDOM.scrollTop && (view.inputState.lastScrollTop || view.inputState.lastScrollLeft)) {
@@ -4316,11 +4335,11 @@ handlers.focus = view => {
4316
4335
  }
4317
4336
  updateForFocusChange(view);
4318
4337
  };
4319
- handlers.blur = view => {
4338
+ observers.blur = view => {
4320
4339
  view.observer.clearSelectionRange();
4321
4340
  updateForFocusChange(view);
4322
4341
  };
4323
- handlers.compositionstart = handlers.compositionupdate = view => {
4342
+ observers.compositionstart = observers.compositionupdate = view => {
4324
4343
  if (view.inputState.compositionFirstChange == null)
4325
4344
  view.inputState.compositionFirstChange = true;
4326
4345
  if (view.inputState.composing < 0) {
@@ -4328,7 +4347,7 @@ handlers.compositionstart = handlers.compositionupdate = view => {
4328
4347
  view.inputState.composing = 0;
4329
4348
  }
4330
4349
  };
4331
- handlers.compositionend = view => {
4350
+ observers.compositionend = view => {
4332
4351
  view.inputState.composing = -1;
4333
4352
  view.inputState.compositionEndedAt = Date.now();
4334
4353
  view.inputState.compositionPendingKey = true;
@@ -4352,7 +4371,7 @@ handlers.compositionend = view => {
4352
4371
  }, 50);
4353
4372
  }
4354
4373
  };
4355
- handlers.contextmenu = view => {
4374
+ observers.contextmenu = view => {
4356
4375
  view.inputState.lastContextMenu = Date.now();
4357
4376
  };
4358
4377
  handlers.beforeinput = (view, event) => {
@@ -4381,6 +4400,7 @@ handlers.beforeinput = (view, event) => {
4381
4400
  }, 100);
4382
4401
  }
4383
4402
  }
4403
+ return false;
4384
4404
  };
4385
4405
  const appliedFirefoxHack = new Set;
4386
4406
  // In Firefox, when cut/copy handlers are added to the document, that
@@ -5051,14 +5071,13 @@ class NodeBuilder {
5051
5071
  return line;
5052
5072
  }
5053
5073
  addBlock(block) {
5054
- var _a;
5055
5074
  this.enterLine();
5056
- let type = (_a = block.deco) === null || _a === void 0 ? void 0 : _a.type;
5057
- if (type == exports.BlockType.WidgetAfter && !this.isCovered)
5075
+ let deco = block.deco;
5076
+ if (deco && deco.startSide > 0 && !this.isCovered)
5058
5077
  this.ensureLine();
5059
5078
  this.nodes.push(block);
5060
5079
  this.writtenTo = this.pos = this.pos + block.length;
5061
- if (type != exports.BlockType.WidgetBefore)
5080
+ if (deco && deco.endSide > 0)
5062
5081
  this.covering = block;
5063
5082
  }
5064
5083
  addLineDeco(height, breaks, length) {
@@ -6166,7 +6185,7 @@ function applyDOMChange(view, domChange) {
6166
6185
  change = { from: sel.from, to: sel.to, insert: state.Text.of([" "]) };
6167
6186
  }
6168
6187
  if (change) {
6169
- if (browser.ios && view.inputState.flushIOSKey(view))
6188
+ if (browser.ios && view.inputState.flushIOSKey())
6170
6189
  return true;
6171
6190
  // Android browsers don't fire reasonable key events for enter,
6172
6191
  // backspace, or delete. So this detects changes that look like
@@ -6420,7 +6439,7 @@ class DOMObserver {
6420
6439
  this.readSelectionRange();
6421
6440
  }
6422
6441
  onScrollChanged(e) {
6423
- this.view.inputState.runScrollHandlers(this.view, e);
6442
+ this.view.inputState.runHandlers("scroll", e);
6424
6443
  if (this.intersecting)
6425
6444
  this.view.measure();
6426
6445
  }
@@ -6891,7 +6910,7 @@ class EditorView {
6891
6910
  plugin.update(this);
6892
6911
  this.observer = new DOMObserver(this);
6893
6912
  this.inputState = new InputState(this);
6894
- this.inputState.ensureHandlers(this, this.plugins);
6913
+ this.inputState.ensureHandlers(this.plugins);
6895
6914
  this.docView = new DocView(this);
6896
6915
  this.mountStyles();
6897
6916
  this.updateAttrs();
@@ -7033,7 +7052,7 @@ class EditorView {
7033
7052
  for (let plugin of this.plugins)
7034
7053
  plugin.update(this);
7035
7054
  this.docView = new DocView(this);
7036
- this.inputState.ensureHandlers(this, this.plugins);
7055
+ this.inputState.ensureHandlers(this.plugins);
7037
7056
  this.mountStyles();
7038
7057
  this.updateAttrs();
7039
7058
  this.bidiCache = [];
@@ -7065,7 +7084,7 @@ class EditorView {
7065
7084
  plugin.destroy(this);
7066
7085
  this.plugins = newPlugins;
7067
7086
  this.pluginMap.clear();
7068
- this.inputState.ensureHandlers(this, this.plugins);
7087
+ this.inputState.ensureHandlers(this.plugins);
7069
7088
  }
7070
7089
  else {
7071
7090
  for (let p of this.plugins)
@@ -7593,6 +7612,17 @@ class EditorView {
7593
7612
  return ViewPlugin.define(() => ({}), { eventHandlers: handlers });
7594
7613
  }
7595
7614
  /**
7615
+ Create an extension that registers DOM event observers. Contrary
7616
+ to event [handlers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventHandlers),
7617
+ observers can't be prevented from running by a higher-precedence
7618
+ handler returning true. They also don't prevent other handlers
7619
+ and observers from running when they return true, and should not
7620
+ call `preventDefault`.
7621
+ */
7622
+ static domEventObservers(observers) {
7623
+ return ViewPlugin.define(() => ({}), { eventObservers: observers });
7624
+ }
7625
+ /**
7596
7626
  Create a theme extension. The first argument can be a
7597
7627
  [`style-mod`](https://github.com/marijnh/style-mod#documentation)
7598
7628
  style spec providing the styles for the theme. These will be
@@ -8319,6 +8349,14 @@ function drawSelection(config = {}) {
8319
8349
  nativeSelectionHidden.of(true)
8320
8350
  ];
8321
8351
  }
8352
+ /**
8353
+ Retrieve the [`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) configuration
8354
+ for this state. (Note that this will return a set of defaults even
8355
+ if `drawSelection` isn't enabled.)
8356
+ */
8357
+ function getDrawSelectionConfig(state) {
8358
+ return state.facet(selectionConfig);
8359
+ }
8322
8360
  function configChanged(update) {
8323
8361
  return update.startState.facet(selectionConfig) != update.state.facet(selectionConfig);
8324
8362
  }
@@ -8445,7 +8483,7 @@ const drawDropCursor = ViewPlugin.fromClass(class {
8445
8483
  this.view.dispatch({ effects: setDropCursorPos.of(pos) });
8446
8484
  }
8447
8485
  }, {
8448
- eventHandlers: {
8486
+ eventObservers: {
8449
8487
  dragover(event) {
8450
8488
  this.setDropPos(this.view.posAtCoords({ x: event.clientX, y: event.clientY }));
8451
8489
  },
@@ -8955,7 +8993,7 @@ function crosshairCursor(options = {}) {
8955
8993
  }
8956
8994
  }
8957
8995
  }, {
8958
- eventHandlers: {
8996
+ eventObservers: {
8959
8997
  keydown(e) {
8960
8998
  this.set(e.keyCode == code || getter(e));
8961
8999
  },
@@ -9143,6 +9181,8 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
9143
9181
  tooltipView.dom.remove();
9144
9182
  (_a = tooltipView.destroy) === null || _a === void 0 ? void 0 : _a.call(tooltipView);
9145
9183
  }
9184
+ if (this.parent)
9185
+ this.container.remove();
9146
9186
  (_b = this.intersectionObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
9147
9187
  clearTimeout(this.measureTimeout);
9148
9188
  }
@@ -9266,7 +9306,7 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
9266
9306
  }
9267
9307
  }
9268
9308
  }, {
9269
- eventHandlers: {
9309
+ eventObservers: {
9270
9310
  scroll() { this.maybeMeasure(); }
9271
9311
  }
9272
9312
  });
@@ -9584,8 +9624,9 @@ re-positioning or CSS change affecting the editor) that could
9584
9624
  invalidate the existing tooltip positions.
9585
9625
  */
9586
9626
  function repositionTooltips(view) {
9587
- var _a;
9588
- (_a = view.plugin(tooltipPlugin)) === null || _a === void 0 ? void 0 : _a.maybeMeasure();
9627
+ let plugin = view.plugin(tooltipPlugin);
9628
+ if (plugin)
9629
+ plugin.maybeMeasure();
9589
9630
  }
9590
9631
 
9591
9632
  const panelConfig = state.Facet.define({
@@ -10303,6 +10344,7 @@ exports.closeHoverTooltips = closeHoverTooltips;
10303
10344
  exports.crosshairCursor = crosshairCursor;
10304
10345
  exports.drawSelection = drawSelection;
10305
10346
  exports.dropCursor = dropCursor;
10347
+ exports.getDrawSelectionConfig = getDrawSelectionConfig;
10306
10348
  exports.getPanel = getPanel;
10307
10349
  exports.getTooltip = getTooltip;
10308
10350
  exports.gutter = gutter;