@codemirror/view 0.19.6 → 0.19.10

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
@@ -9,7 +9,17 @@ var text = require('@codemirror/text');
9
9
  var w3cKeyname = require('w3c-keyname');
10
10
 
11
11
  function getSelection(root) {
12
- return (root.getSelection ? root.getSelection() : document.getSelection());
12
+ let target;
13
+ // Browsers differ on whether shadow roots have a getSelection
14
+ // method. If it exists, use that, otherwise, call it on the
15
+ // document.
16
+ if (root.nodeType == 11) { // Shadow root
17
+ target = root.getSelection ? root : root.ownerDocument;
18
+ }
19
+ else {
20
+ target = root;
21
+ }
22
+ return target.getSelection();
13
23
  }
14
24
  function contains(dom, node) {
15
25
  return node ? dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
@@ -241,6 +251,14 @@ function contentEditablePlainTextSupported() {
241
251
  }
242
252
  return _plainTextSupported;
243
253
  }
254
+ function getRoot(node) {
255
+ while (node) {
256
+ node = node.assignedSlot || node.parentNode;
257
+ if (node && (node.nodeType == 9 || node.nodeType == 11 && node.host))
258
+ return node;
259
+ }
260
+ return null;
261
+ }
244
262
 
245
263
  class DOMPos {
246
264
  constructor(node, offset, precise = true) {
@@ -438,6 +456,7 @@ class ContentView {
438
456
  (this.breakAfter ? "#" : "");
439
457
  }
440
458
  static get(node) { return node.cmView; }
459
+ get isEditable() { return true; }
441
460
  }
442
461
  ContentView.prototype.breakAfter = 0;
443
462
  // Remove a DOM node and return its next sibling.
@@ -485,15 +504,18 @@ const gecko = !ie && /gecko\/(\d+)/i.test(nav.userAgent);
485
504
  const chrome = !ie && /Chrome\/(\d+)/.exec(nav.userAgent);
486
505
  const webkit = "webkitFontSmoothing" in doc.documentElement.style;
487
506
  const safari = !ie && /Apple Computer/.test(nav.vendor);
507
+ const ios = safari && (/Mobile\/\w+/.test(nav.userAgent) || nav.maxTouchPoints > 2);
488
508
  var browser = {
489
- mac: /Mac/.test(nav.platform),
509
+ mac: ios || /Mac/.test(nav.platform),
510
+ windows: /Win/.test(nav.platform),
511
+ linux: /Linux|X11/.test(nav.platform),
490
512
  ie,
491
513
  ie_version: ie_upto10 ? doc.documentMode || 6 : ie_11up ? +ie_11up[1] : ie_edge ? +ie_edge[1] : 0,
492
514
  gecko,
493
515
  gecko_version: gecko ? +(/Firefox\/(\d+)/.exec(nav.userAgent) || [0, 0])[1] : 0,
494
516
  chrome: !!chrome,
495
517
  chrome_version: chrome ? +chrome[1] : 0,
496
- ios: safari && (/Mobile\/\w+/.test(nav.userAgent) || nav.maxTouchPoints > 2),
518
+ ios,
497
519
  android: /Android\b/.test(nav.userAgent),
498
520
  webkit,
499
521
  safari,
@@ -715,6 +737,7 @@ class WidgetView extends InlineView {
715
737
  }
716
738
  return (pos == 0 && side > 0 || pos == this.length && side <= 0) ? rect : flattenRect(rect, pos == 0);
717
739
  }
740
+ get isEditable() { return false; }
718
741
  }
719
742
  class CompositionView extends WidgetView {
720
743
  domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
@@ -726,6 +749,38 @@ class CompositionView extends WidgetView {
726
749
  ignoreMutation() { return false; }
727
750
  get overrideDOMText() { return null; }
728
751
  coordsAt(pos, side) { return textCoords(this.widget.text, pos, side); }
752
+ get isEditable() { return true; }
753
+ }
754
+ // These are drawn around uneditable widgets to avoid a number of
755
+ // browser bugs that show up when the cursor is directly next to
756
+ // uneditable inline content.
757
+ class WidgetBufferView extends InlineView {
758
+ constructor(side) {
759
+ super();
760
+ this.side = side;
761
+ }
762
+ get length() { return 0; }
763
+ merge() { return false; }
764
+ become(other) {
765
+ return other instanceof WidgetBufferView && other.side == this.side;
766
+ }
767
+ slice() { return new WidgetBufferView(this.side); }
768
+ sync() {
769
+ if (!this.dom)
770
+ this.setDOM(document.createTextNode("\u200b"));
771
+ else if (this.dirty && this.dom.nodeValue != "\u200b")
772
+ this.dom.nodeValue = "\u200b";
773
+ }
774
+ getSide() { return this.side; }
775
+ domAtPos(pos) { return DOMPos.before(this.dom); }
776
+ domBoundsAround() { return null; }
777
+ coordsAt(pos) {
778
+ let rects = clientRectsFor(this.dom);
779
+ return rects[rects.length - 1];
780
+ }
781
+ get overrideDOMText() {
782
+ return text.Text.of([this.dom.nodeValue.replace(/\u200b/g, "")]);
783
+ }
729
784
  }
730
785
  function mergeInlineChildren(parent, from, to, elts, openStart, openEnd) {
731
786
  let cur = parent.childCursor();
@@ -1221,14 +1276,17 @@ class LineView extends ContentView {
1221
1276
  }
1222
1277
  // Only called when building a line view in ContentBuilder
1223
1278
  addLineDeco(deco) {
1224
- let attrs = deco.spec.attributes;
1279
+ let attrs = deco.spec.attributes, cls = deco.spec.class;
1225
1280
  if (attrs)
1226
1281
  this.attrs = combineAttrs(attrs, this.attrs || {});
1282
+ if (cls)
1283
+ this.attrs = combineAttrs(attrs, { class: cls });
1227
1284
  }
1228
1285
  domAtPos(pos) {
1229
1286
  return inlineDOMAtPos(this.dom, this.children, pos);
1230
1287
  }
1231
1288
  sync(track) {
1289
+ var _a;
1232
1290
  if (!this.dom || (this.dirty & 4 /* Attrs */)) {
1233
1291
  this.setDOM(document.createElement("div"));
1234
1292
  this.dom.className = "cm-line";
@@ -1244,7 +1302,7 @@ class LineView extends ContentView {
1244
1302
  while (last && ContentView.get(last) instanceof MarkView)
1245
1303
  last = last.lastChild;
1246
1304
  if (!last ||
1247
- last.nodeName != "BR" && ContentView.get(last) instanceof WidgetView &&
1305
+ last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false &&
1248
1306
  (!browser.ios || !this.children.some(ch => ch instanceof TextView))) {
1249
1307
  let hack = document.createElement("BR");
1250
1308
  hack.cmIgnore = true;
@@ -1343,6 +1401,9 @@ class ContentBuilder {
1343
1401
  this.content = [];
1344
1402
  this.curLine = null;
1345
1403
  this.breakAtStart = 0;
1404
+ this.pendingBuffer = 0 /* No */;
1405
+ // Set to false directly after a widget that covers the position after it
1406
+ this.atCursorPos = true;
1346
1407
  this.openStart = -1;
1347
1408
  this.openEnd = -1;
1348
1409
  this.text = "";
@@ -1357,23 +1418,31 @@ class ContentBuilder {
1357
1418
  return !last.breakAfter && !(last instanceof BlockWidgetView && last.type == exports.BlockType.WidgetBefore);
1358
1419
  }
1359
1420
  getLine() {
1360
- if (!this.curLine)
1421
+ if (!this.curLine) {
1361
1422
  this.content.push(this.curLine = new LineView);
1423
+ this.atCursorPos = true;
1424
+ }
1362
1425
  return this.curLine;
1363
1426
  }
1364
- addWidget(view) {
1427
+ flushBuffer(active) {
1428
+ if (this.pendingBuffer) {
1429
+ this.curLine.append(wrapMarks(new WidgetBufferView(-1), active), active.length);
1430
+ this.pendingBuffer = 0 /* No */;
1431
+ }
1432
+ }
1433
+ addBlockWidget(view) {
1434
+ this.flushBuffer([]);
1365
1435
  this.curLine = null;
1366
1436
  this.content.push(view);
1367
1437
  }
1368
- finish() {
1438
+ finish(openEnd) {
1439
+ if (!openEnd)
1440
+ this.flushBuffer([]);
1441
+ else
1442
+ this.pendingBuffer = 0 /* No */;
1369
1443
  if (!this.posCovered())
1370
1444
  this.getLine();
1371
1445
  }
1372
- wrapMarks(view, active) {
1373
- for (let mark of active)
1374
- view = new MarkView(mark, [view], view.length);
1375
- return view;
1376
- }
1377
1446
  buildText(length, active, openStart) {
1378
1447
  while (length > 0) {
1379
1448
  if (this.textOff == this.text.length) {
@@ -1388,6 +1457,7 @@ class ContentBuilder {
1388
1457
  this.content[this.content.length - 1].breakAfter = 1;
1389
1458
  else
1390
1459
  this.breakAtStart = 1;
1460
+ this.flushBuffer([]);
1391
1461
  this.curLine = null;
1392
1462
  length--;
1393
1463
  continue;
@@ -1398,7 +1468,9 @@ class ContentBuilder {
1398
1468
  }
1399
1469
  }
1400
1470
  let take = Math.min(this.text.length - this.textOff, length, 512 /* Chunk */);
1401
- this.getLine().append(this.wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
1471
+ this.flushBuffer(active);
1472
+ this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
1473
+ this.atCursorPos = true;
1402
1474
  this.textOff += take;
1403
1475
  length -= take;
1404
1476
  openStart = 0;
@@ -1417,11 +1489,23 @@ class ContentBuilder {
1417
1489
  let { type } = deco;
1418
1490
  if (type == exports.BlockType.WidgetAfter && !this.posCovered())
1419
1491
  this.getLine();
1420
- this.addWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, type));
1492
+ this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, type));
1421
1493
  }
1422
1494
  else {
1423
- let widget = this.wrapMarks(WidgetView.create(deco.widget || new NullWidget("span"), len, deco.startSide), active);
1424
- this.getLine().append(widget, openStart);
1495
+ let view = WidgetView.create(deco.widget || new NullWidget("span"), len, deco.startSide);
1496
+ let cursorBefore = this.atCursorPos && !view.isEditable && openStart <= active.length && (from < to || deco.startSide > 0);
1497
+ let cursorAfter = !view.isEditable && (from < to || deco.startSide <= 0);
1498
+ let line = this.getLine();
1499
+ if (this.pendingBuffer == 2 /* IfCursor */ && !cursorBefore)
1500
+ this.pendingBuffer = 0 /* No */;
1501
+ this.flushBuffer(active);
1502
+ if (cursorBefore) {
1503
+ line.append(wrapMarks(new WidgetBufferView(1), active), openStart);
1504
+ openStart = active.length + Math.max(0, openStart - active.length);
1505
+ }
1506
+ line.append(wrapMarks(view, active), openStart);
1507
+ this.atCursorPos = cursorAfter;
1508
+ this.pendingBuffer = !cursorAfter ? 0 /* No */ : from < to ? 1 /* Yes */ : 2 /* IfCursor */;
1425
1509
  }
1426
1510
  }
1427
1511
  else if (this.doc.lineAt(this.pos).from == this.pos) { // Line decoration
@@ -1447,10 +1531,15 @@ class ContentBuilder {
1447
1531
  builder.openEnd = rangeset.RangeSet.spans(decorations, from, to, builder);
1448
1532
  if (builder.openStart < 0)
1449
1533
  builder.openStart = builder.openEnd;
1450
- builder.finish();
1534
+ builder.finish(builder.openEnd);
1451
1535
  return builder;
1452
1536
  }
1453
1537
  }
1538
+ function wrapMarks(view, active) {
1539
+ for (let mark of active)
1540
+ view = new MarkView(mark, [view], view.length);
1541
+ return view;
1542
+ }
1454
1543
  class NullWidget extends WidgetType {
1455
1544
  constructor(tag) {
1456
1545
  super();
@@ -1783,7 +1872,9 @@ class ViewUpdate {
1783
1872
  this.flags |= 2 /* Height */;
1784
1873
  }
1785
1874
  /**
1786
- Tells you whether the viewport changed in this update.
1875
+ Tells you whether the [viewport](https://codemirror.net/6/docs/ref/#view.EditorView.viewport) or
1876
+ [visible ranges](https://codemirror.net/6/docs/ref/#view.EditorView.visibleRanges) changed in this
1877
+ update.
1787
1878
  */
1788
1879
  get viewportChanged() {
1789
1880
  return (this.flags & 4 /* Viewport */) > 0;
@@ -1799,7 +1890,7 @@ class ViewUpdate {
1799
1890
  or the lines or characters within it has changed.
1800
1891
  */
1801
1892
  get geometryChanged() {
1802
- return this.docChanged || (this.flags & (16 /* Geometry */ | 2 /* Height */)) > 0;
1893
+ return this.docChanged || (this.flags & (8 /* Geometry */ | 2 /* Height */)) > 0;
1803
1894
  }
1804
1895
  /**
1805
1896
  True when this update indicates a focus change.
@@ -1884,7 +1975,7 @@ class DocView extends ContentView {
1884
1975
  changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
1885
1976
  let pointerSel = update.transactions.some(tr => tr.isUserEvent("select.pointer"));
1886
1977
  if (this.dirty == 0 /* Not */ && changedRanges.length == 0 &&
1887
- !(update.flags & (4 /* Viewport */ | 8 /* LineGaps */)) &&
1978
+ !(update.flags & 4 /* Viewport */) &&
1888
1979
  update.state.selection.main.from >= this.view.viewport.from &&
1889
1980
  update.state.selection.main.to <= this.view.viewport.to) {
1890
1981
  this.updateSelection(forceSelection, pointerSel);
@@ -1895,6 +1986,14 @@ class DocView extends ContentView {
1895
1986
  return true;
1896
1987
  }
1897
1988
  }
1989
+ reset(sel) {
1990
+ if (this.dirty) {
1991
+ this.view.observer.ignore(() => this.view.docView.sync());
1992
+ this.dirty = 0 /* Not */;
1993
+ }
1994
+ if (sel)
1995
+ this.updateSelection();
1996
+ }
1898
1997
  // Used both by update and checkLayout do perform the actual DOM
1899
1998
  // update
1900
1999
  updateInner(changes, deco, oldLength, forceSelection = false, pointerSel = false) {
@@ -1919,6 +2018,12 @@ class DocView extends ContentView {
1919
2018
  this.updateSelection(forceSelection, pointerSel);
1920
2019
  this.dom.style.height = "";
1921
2020
  });
2021
+ let gaps = [];
2022
+ if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
2023
+ for (let child of this.children)
2024
+ if (child instanceof BlockWidgetView && child.widget instanceof BlockGapWidget)
2025
+ gaps.push(child.dom);
2026
+ observer.updateGaps(gaps);
1922
2027
  }
1923
2028
  updateChildren(changes, deco, oldLength) {
1924
2029
  let cursor = this.childCursor(oldLength);
@@ -2018,6 +2123,14 @@ class DocView extends ContentView {
2018
2123
  !isEquivalentPosition(anchor.node, anchor.offset, domSel.anchorNode, domSel.anchorOffset) ||
2019
2124
  !isEquivalentPosition(head.node, head.offset, domSel.focusNode, domSel.focusOffset)) {
2020
2125
  this.view.observer.ignore(() => {
2126
+ // Chrome Android will hide the virtual keyboard when tapping
2127
+ // inside an uneditable node, and not bring it back when we
2128
+ // move the cursor to its proper position. This tries to
2129
+ // restore the keyboard by cycling focus.
2130
+ if (browser.android && browser.chrome && this.dom.contains(domSel.focusNode) && inUneditable(domSel.focusNode, this.dom)) {
2131
+ this.dom.blur();
2132
+ this.dom.focus({ preventScroll: true });
2133
+ }
2021
2134
  let rawSel = getSelection(this.root);
2022
2135
  if (main.empty) {
2023
2136
  // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=1612076
@@ -2325,6 +2438,14 @@ function findChangedDeco(a, b, diff) {
2325
2438
  rangeset.RangeSet.compare(a, b, diff, comp);
2326
2439
  return comp.changes;
2327
2440
  }
2441
+ function inUneditable(node, inside) {
2442
+ for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) {
2443
+ if (cur.nodeType == 1 && cur.contentEditable == 'false') {
2444
+ return true;
2445
+ }
2446
+ }
2447
+ return false;
2448
+ }
2328
2449
 
2329
2450
  /**
2330
2451
  Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
@@ -2771,6 +2892,7 @@ function domPosInText(node, x, y) {
2771
2892
  return { node, offset: closestOffset > -1 ? closestOffset : generalSide > 0 ? node.nodeValue.length : 0 };
2772
2893
  }
2773
2894
  function posAtCoords(view, { x, y }, precise, bias = -1) {
2895
+ var _a;
2774
2896
  let content = view.contentDOM.getBoundingClientRect(), block;
2775
2897
  let halfLine = view.defaultLineHeight / 2;
2776
2898
  for (let bounced = false;;) {
@@ -2788,25 +2910,27 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
2788
2910
  y = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
2789
2911
  }
2790
2912
  let lineStart = block.from;
2913
+ // Clip x to the viewport sides
2791
2914
  x = Math.max(content.left + 1, Math.min(content.right - 1, x));
2792
2915
  // If this is outside of the rendered viewport, we can't determine a position
2793
2916
  if (lineStart < view.viewport.from)
2794
2917
  return view.viewport.from == 0 ? 0 : posAtCoordsImprecise(view, content, block, x, y);
2795
2918
  if (lineStart > view.viewport.to)
2796
2919
  return view.viewport.to == view.state.doc.length ? view.state.doc.length : posAtCoordsImprecise(view, content, block, x, y);
2797
- // Clip x to the viewport sides
2798
- let root = view.root, element = root.elementFromPoint(x, y);
2920
+ // Prefer ShadowRootOrDocument.elementFromPoint if present, fall back to document if not
2921
+ let doc = view.dom.ownerDocument;
2922
+ let element = (view.root.elementFromPoint ? view.root : doc).elementFromPoint(x, y);
2799
2923
  // There's visible editor content under the point, so we can try
2800
2924
  // using caret(Position|Range)FromPoint as a shortcut
2801
2925
  let node, offset = -1;
2802
- if (element && view.contentDOM.contains(element) && !(view.docView.nearest(element) instanceof WidgetView)) {
2803
- if (root.caretPositionFromPoint) {
2804
- let pos = root.caretPositionFromPoint(x, y);
2926
+ if (element && view.contentDOM.contains(element) && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
2927
+ if (doc.caretPositionFromPoint) {
2928
+ let pos = doc.caretPositionFromPoint(x, y);
2805
2929
  if (pos)
2806
2930
  ({ offsetNode: node, offset } = pos);
2807
2931
  }
2808
- else if (root.caretRangeFromPoint) {
2809
- let range = root.caretRangeFromPoint(x, y);
2932
+ else if (doc.caretRangeFromPoint) {
2933
+ let range = doc.caretRangeFromPoint(x, y);
2810
2934
  if (range) {
2811
2935
  ({ startContainer: node, startOffset: offset } = range);
2812
2936
  if (browser.safari && isSuspiciousCaretResult(node, offset, x))
@@ -2940,7 +3064,23 @@ class InputState {
2940
3064
  constructor(view) {
2941
3065
  this.lastKeyCode = 0;
2942
3066
  this.lastKeyTime = 0;
2943
- this.pendingIOSKey = null;
3067
+ // On iOS, some keys need to have their default behavior happen
3068
+ // (after which we retroactively handle them and reset the DOM) to
3069
+ // avoid messing up the virtual keyboard state.
3070
+ //
3071
+ // On Chrome Android, backspace near widgets is just completely
3072
+ // broken, and there are no key events, so we need to handle the
3073
+ // beforeinput event. Deleting stuff will often create a flurry of
3074
+ // events, and interrupting it before it is done just makes
3075
+ // subsequent events even more broken, so again, we hold off doing
3076
+ // anything until the browser is finished with whatever it is trying
3077
+ // to do.
3078
+ //
3079
+ // setPendingKey sets this, causing the DOM observer to pause for a
3080
+ // bit, and setting an animation frame (which seems the most
3081
+ // reliable way to detect 'browser is done flailing') to fire a fake
3082
+ // key event and re-sync the view again.
3083
+ this.pendingKey = undefined;
2944
3084
  this.lastSelectionOrigin = null;
2945
3085
  this.lastSelectionTime = 0;
2946
3086
  this.lastEscPress = 0;
@@ -3049,20 +3189,27 @@ class InputState {
3049
3189
  // state. So we let it go through, and then, in
3050
3190
  // applyDOMChange, notify key handlers of it and reset to
3051
3191
  // the state they produce.
3052
- if (browser.ios && (event.keyCode == 13 || event.keyCode == 8) &&
3192
+ let pending;
3193
+ if (browser.ios && (pending = PendingKeys.find(key => key.keyCode == event.keyCode)) &&
3053
3194
  !(event.ctrlKey || event.altKey || event.metaKey) && !event.synthetic) {
3054
- this.pendingIOSKey = event.keyCode == 13 ? "enter" : "backspace";
3055
- setTimeout(() => this.flushIOSKey(view), 250);
3195
+ this.setPendingKey(view, pending);
3056
3196
  return true;
3057
3197
  }
3058
3198
  return false;
3059
3199
  }
3060
- flushIOSKey(view) {
3061
- if (!this.pendingIOSKey)
3062
- return false;
3063
- let dom = view.contentDOM, key = this.pendingIOSKey;
3064
- this.pendingIOSKey = null;
3065
- return key == "enter" ? dispatchKey(dom, "Enter", 13) : dispatchKey(dom, "Backspace", 8);
3200
+ setPendingKey(view, pending) {
3201
+ this.pendingKey = pending;
3202
+ requestAnimationFrame(() => {
3203
+ if (!this.pendingKey)
3204
+ return false;
3205
+ let key = this.pendingKey;
3206
+ this.pendingKey = undefined;
3207
+ view.observer.processRecords();
3208
+ let startState = view.state;
3209
+ dispatchKey(view.contentDOM, key.key, key.keyCode);
3210
+ if (view.state == startState)
3211
+ view.docView.reset(true);
3212
+ });
3066
3213
  }
3067
3214
  ignoreDuringComposition(event) {
3068
3215
  if (!/^key/.test(event.type))
@@ -3109,6 +3256,11 @@ class InputState {
3109
3256
  this.mouseSelection.destroy();
3110
3257
  }
3111
3258
  }
3259
+ const PendingKeys = [
3260
+ { key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
3261
+ { key: "Enter", keyCode: 13, inputType: "insertParagraph" },
3262
+ { key: "Delete", keyCode: 46, inputType: "deleteContentForward" }
3263
+ ];
3112
3264
  // Key codes for modifier keys
3113
3265
  const modifierCodes = [16, 17, 18, 20, 91, 92, 224, 225];
3114
3266
  class MouseSelection {
@@ -3225,7 +3377,7 @@ function capturePaste(view) {
3225
3377
  function doPaste(view, input) {
3226
3378
  let { state: state$1 } = view, changes, i = 1, text = state$1.toText(input);
3227
3379
  let byLine = text.lines == state$1.selection.ranges.length;
3228
- let linewise = lastLinewiseCopy && state$1.selection.ranges.every(r => r.empty) && lastLinewiseCopy == text.toString();
3380
+ let linewise = lastLinewiseCopy != null && state$1.selection.ranges.every(r => r.empty) && lastLinewiseCopy == text.toString();
3229
3381
  if (linewise) {
3230
3382
  let lastLine = -1;
3231
3383
  changes = state$1.changeByRange(range => {
@@ -3404,8 +3556,10 @@ function dropText(view, event, text, direct) {
3404
3556
  });
3405
3557
  }
3406
3558
  handlers.drop = (view, event) => {
3407
- if (!event.dataTransfer || !view.state.facet(editable))
3559
+ if (!event.dataTransfer)
3408
3560
  return;
3561
+ if (view.state.readOnly)
3562
+ return event.preventDefault();
3409
3563
  let files = event.dataTransfer.files;
3410
3564
  if (files && files.length) { // For a file drop, read the file's text.
3411
3565
  event.preventDefault();
@@ -3430,13 +3584,12 @@ handlers.drop = (view, event) => {
3430
3584
  }
3431
3585
  };
3432
3586
  handlers.paste = (view, event) => {
3433
- if (!view.state.facet(editable))
3434
- return;
3587
+ if (view.state.readOnly)
3588
+ return event.preventDefault();
3435
3589
  view.observer.flush();
3436
3590
  let data = brokenClipboardAPI ? null : event.clipboardData;
3437
- let text = data && data.getData("text/plain");
3438
- if (text) {
3439
- doPaste(view, text);
3591
+ if (data) {
3592
+ doPaste(view, data.getData("text/plain"));
3440
3593
  event.preventDefault();
3441
3594
  }
3442
3595
  else {
@@ -3485,7 +3638,7 @@ function copiedRange(state) {
3485
3638
  let lastLinewiseCopy = null;
3486
3639
  handlers.copy = handlers.cut = (view, event) => {
3487
3640
  let { text, ranges, linewise } = copiedRange(view.state);
3488
- if (!text)
3641
+ if (!text && !linewise)
3489
3642
  return;
3490
3643
  lastLinewiseCopy = linewise ? text : null;
3491
3644
  let data = brokenClipboardAPI ? null : event.clipboardData;
@@ -3497,7 +3650,7 @@ handlers.copy = handlers.cut = (view, event) => {
3497
3650
  else {
3498
3651
  captureCopy(view, text);
3499
3652
  }
3500
- if (event.type == "cut" && view.state.facet(editable))
3653
+ if (event.type == "cut" && !view.state.readOnly)
3501
3654
  view.dispatch({
3502
3655
  changes: ranges,
3503
3656
  scrollIntoView: true,
@@ -3553,6 +3706,31 @@ handlers.compositionend = view => {
3553
3706
  handlers.contextmenu = view => {
3554
3707
  view.inputState.lastContextMenu = Date.now();
3555
3708
  };
3709
+ handlers.beforeinput = (view, event) => {
3710
+ var _a;
3711
+ // Because Chrome Android doesn't fire useful key events, use
3712
+ // beforeinput to detect backspace (and possibly enter and delete,
3713
+ // but those usually don't even seem to fire beforeinput events at
3714
+ // the moment) and fake a key event for it.
3715
+ //
3716
+ // (preventDefault on beforeinput, though supported in the spec,
3717
+ // seems to do nothing at all on Chrome).
3718
+ let pending;
3719
+ if (browser.chrome && browser.android && (pending = PendingKeys.find(key => key.inputType == event.inputType))) {
3720
+ view.inputState.setPendingKey(view, pending);
3721
+ let startViewHeight = ((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0;
3722
+ setTimeout(() => {
3723
+ var _a;
3724
+ // Backspacing near uneditable nodes on Chrome Android sometimes
3725
+ // closes the virtual keyboard. This tries to crudely detect
3726
+ // that and refocus to get it back.
3727
+ if ((((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0) > startViewHeight + 10 && view.hasFocus) {
3728
+ view.contentDOM.blur();
3729
+ view.focus();
3730
+ }
3731
+ }, 50);
3732
+ }
3733
+ };
3556
3734
 
3557
3735
  const wrappingWhiteSpace = ["pre-wrap", "normal", "pre-line"];
3558
3736
  class HeightOracle {
@@ -4326,14 +4504,11 @@ class ViewState {
4326
4504
  let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
4327
4505
  if (scrollTo && (scrollTo.head < viewport.from || scrollTo.head > viewport.to) || !this.viewportIsAppropriate(viewport))
4328
4506
  viewport = this.getViewport(0, scrollTo);
4329
- if (!viewport.eq(this.viewport)) {
4330
- this.viewport = viewport;
4331
- update.flags |= 4 /* Viewport */;
4332
- }
4507
+ this.viewport = viewport;
4333
4508
  this.updateForViewport();
4334
4509
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > 15000 /* MinViewPort */)
4335
- update.flags |= this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
4336
- this.computeVisibleRanges();
4510
+ this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
4511
+ update.flags |= this.computeVisibleRanges();
4337
4512
  if (scrollTo)
4338
4513
  this.scrollTo = scrollTo;
4339
4514
  if (!this.mustEnforceCursorAssoc && update.selectionSet && update.view.lineWrapping &&
@@ -4342,12 +4517,17 @@ class ViewState {
4342
4517
  }
4343
4518
  measure(docView, repeated) {
4344
4519
  let dom = docView.dom, whiteSpace = "", direction = exports.Direction.LTR;
4520
+ let result = 0;
4345
4521
  if (!repeated) {
4346
4522
  // Vertical padding
4347
4523
  let style = window.getComputedStyle(dom);
4348
4524
  whiteSpace = style.whiteSpace, direction = (style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR);
4349
- this.paddingTop = parseInt(style.paddingTop) || 0;
4350
- this.paddingBottom = parseInt(style.paddingBottom) || 0;
4525
+ let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
4526
+ if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
4527
+ result |= 8 /* Geometry */;
4528
+ this.paddingTop = paddingTop;
4529
+ this.paddingBottom = paddingBottom;
4530
+ }
4351
4531
  }
4352
4532
  // Pixel viewport
4353
4533
  let pixelViewport = this.printing ? { top: -1e8, bottom: 1e8, left: -1e8, right: 1e8 } : visiblePixelRange(dom, this.paddingTop);
@@ -4357,7 +4537,7 @@ class ViewState {
4357
4537
  if (!this.inView)
4358
4538
  return 0;
4359
4539
  let lineHeights = docView.measureVisibleLineHeights();
4360
- let refresh = false, bias = 0, result = 0, oracle = this.heightOracle;
4540
+ let refresh = false, bias = 0, oracle = this.heightOracle;
4361
4541
  if (!repeated) {
4362
4542
  let contentWidth = docView.dom.clientWidth;
4363
4543
  if (oracle.mustRefresh(lineHeights, whiteSpace, direction) ||
@@ -4366,12 +4546,12 @@ class ViewState {
4366
4546
  refresh = oracle.refresh(whiteSpace, direction, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
4367
4547
  if (refresh) {
4368
4548
  docView.minWidth = 0;
4369
- result |= 16 /* Geometry */;
4549
+ result |= 8 /* Geometry */;
4370
4550
  }
4371
4551
  }
4372
4552
  if (this.contentWidth != contentWidth) {
4373
4553
  this.contentWidth = contentWidth;
4374
- result |= 16 /* Geometry */;
4554
+ result |= 8 /* Geometry */;
4375
4555
  }
4376
4556
  if (dTop > 0 && dBottom > 0)
4377
4557
  bias = Math.max(dTop, dBottom);
@@ -4383,17 +4563,12 @@ class ViewState {
4383
4563
  if (oracle.heightChanged)
4384
4564
  result |= 2 /* Height */;
4385
4565
  if (!this.viewportIsAppropriate(this.viewport, bias) ||
4386
- this.scrollTo && (this.scrollTo.head < this.viewport.from || this.scrollTo.head > this.viewport.to)) {
4387
- let newVP = this.getViewport(bias, this.scrollTo);
4388
- if (newVP.from != this.viewport.from || newVP.to != this.viewport.to) {
4389
- this.viewport = newVP;
4390
- result |= 4 /* Viewport */;
4391
- }
4392
- }
4566
+ this.scrollTo && (this.scrollTo.head < this.viewport.from || this.scrollTo.head > this.viewport.to))
4567
+ this.viewport = this.getViewport(bias, this.scrollTo);
4393
4568
  this.updateForViewport();
4394
4569
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > 15000 /* MinViewPort */)
4395
- result |= this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
4396
- this.computeVisibleRanges();
4570
+ this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
4571
+ result |= this.computeVisibleRanges();
4397
4572
  if (this.mustEnforceCursorAssoc) {
4398
4573
  this.mustEnforceCursorAssoc = false;
4399
4574
  // This is done in the read stage, because moving the selection
@@ -4515,9 +4690,7 @@ class ViewState {
4515
4690
  if (!LineGap.same(gaps, this.lineGaps)) {
4516
4691
  this.lineGaps = gaps;
4517
4692
  this.lineGapDeco = Decoration.set(gaps.map(gap => gap.draw(this.heightOracle.lineWrapping)));
4518
- return 8 /* LineGaps */;
4519
4693
  }
4520
- return 0;
4521
4694
  }
4522
4695
  computeVisibleRanges() {
4523
4696
  let deco = this.state.facet(decorations);
@@ -4528,7 +4701,10 @@ class ViewState {
4528
4701
  span(from, to) { ranges.push({ from, to }); },
4529
4702
  point() { }
4530
4703
  }, 20);
4704
+ let changed = ranges.length != this.visibleRanges.length ||
4705
+ this.visibleRanges.some((r, i) => r.from != ranges[i].from || r.to != ranges[i].to);
4531
4706
  this.visibleRanges = ranges;
4707
+ return changed ? 4 /* Viewport */ : 0;
4532
4708
  }
4533
4709
  lineAt(pos, editorTop) {
4534
4710
  editorTop += this.paddingTop;
@@ -4553,16 +4729,11 @@ class ViewState {
4553
4729
  return this.scaler.toDOM(this.heightMap.height, this.paddingTop);
4554
4730
  }
4555
4731
  }
4556
- /**
4557
- Indicates the range of the document that is in the visible
4558
- viewport.
4559
- */
4560
4732
  class Viewport {
4561
4733
  constructor(from, to) {
4562
4734
  this.from = from;
4563
4735
  this.to = to;
4564
4736
  }
4565
- eq(b) { return this.from == b.from && this.to == b.to; }
4566
4737
  }
4567
4738
  function lineStructure(from, to, state) {
4568
4739
  let ranges = [], pos = from, total = 0;
@@ -4688,7 +4859,7 @@ function buildTheme(main, spec, scopes) {
4688
4859
  });
4689
4860
  }
4690
4861
  const baseTheme = buildTheme("." + baseThemeID, {
4691
- "&": {
4862
+ "&.cm-editor": {
4692
4863
  position: "relative !important",
4693
4864
  boxSizing: "border-box",
4694
4865
  "&.cm-focused": {
@@ -4855,6 +5026,8 @@ class DOMObserver {
4855
5026
  this.scrollTargets = [];
4856
5027
  this.intersection = null;
4857
5028
  this.intersecting = false;
5029
+ this.gapIntersection = null;
5030
+ this.gaps = [];
4858
5031
  // Used to work around a Safari Selection/shadow DOM bug (#414)
4859
5032
  this._selectionRange = null;
4860
5033
  // Timeout for scheduling check of the parents that need scroll handlers
@@ -4895,13 +5068,17 @@ class DOMObserver {
4895
5068
  this.intersection = new IntersectionObserver(entries => {
4896
5069
  if (this.parentCheck < 0)
4897
5070
  this.parentCheck = setTimeout(this.listenForScroll.bind(this), 1000);
4898
- if (entries[entries.length - 1].intersectionRatio > 0 != this.intersecting) {
5071
+ if (entries.length > 0 && entries[entries.length - 1].intersectionRatio > 0 != this.intersecting) {
4899
5072
  this.intersecting = !this.intersecting;
4900
5073
  if (this.intersecting != this.view.inView)
4901
5074
  this.onScrollChanged(document.createEvent("Event"));
4902
5075
  }
4903
5076
  }, {});
4904
5077
  this.intersection.observe(this.dom);
5078
+ this.gapIntersection = new IntersectionObserver(entries => {
5079
+ if (entries.length > 0 && entries[entries.length - 1].intersectionRatio > 0)
5080
+ this.onScrollChanged(document.createEvent("Event"));
5081
+ }, {});
4905
5082
  }
4906
5083
  this.listenForScroll();
4907
5084
  }
@@ -4910,6 +5087,14 @@ class DOMObserver {
4910
5087
  this.flush();
4911
5088
  this.onScrollChanged(e);
4912
5089
  }
5090
+ updateGaps(gaps) {
5091
+ if (this.gapIntersection && (gaps.length != this.gaps.length || this.gaps.some((g, i) => g != gaps[i]))) {
5092
+ this.gapIntersection.disconnect();
5093
+ for (let gap of gaps)
5094
+ this.gapIntersection.observe(gap);
5095
+ this.gaps = gaps;
5096
+ }
5097
+ }
4913
5098
  onSelectionChange(event) {
4914
5099
  if (this.lastFlush < Date.now() - 50)
4915
5100
  this._selectionRange = null;
@@ -5025,20 +5210,12 @@ class DOMObserver {
5025
5210
  this.flush();
5026
5211
  }
5027
5212
  }
5028
- // Apply pending changes, if any
5029
- flush() {
5030
- if (this.delayedFlush >= 0)
5031
- return;
5032
- this.lastFlush = Date.now();
5213
+ processRecords() {
5033
5214
  let records = this.queue;
5034
5215
  for (let mut of this.observer.takeRecords())
5035
5216
  records.push(mut);
5036
5217
  if (records.length)
5037
5218
  this.queue = [];
5038
- let selection = this.selectionRange;
5039
- let newSel = !this.ignoreSelection.eq(selection) && hasSelection(this.dom, selection);
5040
- if (records.length == 0 && !newSel)
5041
- return;
5042
5219
  let from = -1, to = -1, typeOver = false;
5043
5220
  for (let record of records) {
5044
5221
  let range = this.readMutation(record);
@@ -5054,17 +5231,26 @@ class DOMObserver {
5054
5231
  to = Math.max(range.to, to);
5055
5232
  }
5056
5233
  }
5234
+ return { from, to, typeOver };
5235
+ }
5236
+ // Apply pending changes, if any
5237
+ flush() {
5238
+ // Completely hold off flushing when pending keys are set—the code
5239
+ // managing those will make sure processRecords is called and the
5240
+ // view is resynchronized after
5241
+ if (this.delayedFlush >= 0 || this.view.inputState.pendingKey)
5242
+ return;
5243
+ this.lastFlush = Date.now();
5244
+ let { from, to, typeOver } = this.processRecords();
5245
+ let selection = this.selectionRange;
5246
+ let newSel = !this.ignoreSelection.eq(selection) && hasSelection(this.dom, selection);
5247
+ if (from < 0 && !newSel)
5248
+ return;
5057
5249
  let startState = this.view.state;
5058
- if (from > -1 || newSel)
5059
- this.onChange(from, to, typeOver);
5060
- if (this.view.state == startState) { // The view wasn't updated
5061
- if (this.view.docView.dirty) {
5062
- this.ignore(() => this.view.docView.sync());
5063
- this.view.docView.dirty = 0 /* Not */;
5064
- }
5065
- if (newSel)
5066
- this.view.docView.updateSelection();
5067
- }
5250
+ this.onChange(from, to, typeOver);
5251
+ // The view wasn't updated
5252
+ if (this.view.state == startState)
5253
+ this.view.docView.reset(newSel);
5068
5254
  this.clearSelection();
5069
5255
  }
5070
5256
  readMutation(rec) {
@@ -5091,6 +5277,8 @@ class DOMObserver {
5091
5277
  this.stop();
5092
5278
  if (this.intersection)
5093
5279
  this.intersection.disconnect();
5280
+ if (this.gapIntersection)
5281
+ this.gapIntersection.disconnect();
5094
5282
  for (let dom of this.scrollTargets)
5095
5283
  dom.removeEventListener("scroll", this.onScroll);
5096
5284
  window.removeEventListener("scroll", this.onScroll);
@@ -5138,7 +5326,7 @@ function safariSelectionRangeHack(view) {
5138
5326
  function applyDOMChange(view, start, end, typeOver) {
5139
5327
  let change, newSel;
5140
5328
  let sel = view.state.selection.main, bounds;
5141
- if (start > -1 && (bounds = view.docView.domBoundsAround(start, end, 0))) {
5329
+ if (start > -1 && !view.state.readOnly && (bounds = view.docView.domBoundsAround(start, end, 0))) {
5142
5330
  let { from, to } = bounds;
5143
5331
  let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
5144
5332
  let reader = new DOMReader(selPoints, view);
@@ -5199,9 +5387,9 @@ function applyDOMChange(view, start, end, typeOver) {
5199
5387
  (change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
5200
5388
  dispatchKey(view.contentDOM, "Backspace", 8)) ||
5201
5389
  (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
5202
- dispatchKey(view.contentDOM, "Delete", 46))) ||
5203
- browser.ios && view.inputState.flushIOSKey(view))
5390
+ dispatchKey(view.contentDOM, "Delete", 46)))) {
5204
5391
  return;
5392
+ }
5205
5393
  let text = change.insert.toString();
5206
5394
  if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
5207
5395
  return;
@@ -5291,8 +5479,9 @@ class DOMReader {
5291
5479
  if (next == end)
5292
5480
  break;
5293
5481
  let view = ContentView.get(cur), nextView = ContentView.get(next);
5294
- if ((view ? view.breakAfter : isBlockElement(cur)) ||
5295
- ((nextView ? nextView.breakAfter : isBlockElement(next)) && !(cur.nodeName == "BR" && !cur.cmIgnore)))
5482
+ if (view && nextView ? view.breakAfter :
5483
+ (view ? view.breakAfter : isBlockElement(cur)) ||
5484
+ (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
5296
5485
  this.text += this.lineBreak;
5297
5486
  cur = next;
5298
5487
  }
@@ -5395,6 +5584,7 @@ class EditorView {
5395
5584
  this.editorAttrs = {};
5396
5585
  this.contentAttrs = {};
5397
5586
  this.bidiCache = [];
5587
+ this.destroyed = false;
5398
5588
  /**
5399
5589
  @internal
5400
5590
  */
@@ -5420,7 +5610,7 @@ class EditorView {
5420
5610
  this.dom.appendChild(this.scrollDOM);
5421
5611
  this._dispatch = config.dispatch || ((tr) => this.update([tr]));
5422
5612
  this.dispatch = this.dispatch.bind(this);
5423
- this.root = (config.root || document);
5613
+ this.root = (config.root || getRoot(config.parent) || document);
5424
5614
  this.viewState = new ViewState(config.state || state.EditorState.create());
5425
5615
  this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec).update(this));
5426
5616
  this.observer = new DOMObserver(this, (from, to, typeOver) => {
@@ -5493,6 +5683,10 @@ class EditorView {
5493
5683
  throw new RangeError("Trying to update state with a transaction that doesn't start from the previous state.");
5494
5684
  state$1 = tr.state;
5495
5685
  }
5686
+ if (this.destroyed) {
5687
+ this.viewState.state = state$1;
5688
+ return;
5689
+ }
5496
5690
  // When the phrases change, redraw the editor
5497
5691
  if (state$1.facet(state.EditorState.phrases) != this.state.facet(state.EditorState.phrases))
5498
5692
  return this.setState(state$1);
@@ -5542,6 +5736,10 @@ class EditorView {
5542
5736
  setState(newState) {
5543
5737
  if (this.updateState != 0 /* Idle */)
5544
5738
  throw new Error("Calls to EditorView.setState are not allowed while an update is in progress");
5739
+ if (this.destroyed) {
5740
+ this.viewState.state = newState;
5741
+ return;
5742
+ }
5545
5743
  this.updateState = 2 /* Updating */;
5546
5744
  try {
5547
5745
  for (let plugin of this.plugins)
@@ -5591,6 +5789,8 @@ class EditorView {
5591
5789
  @internal
5592
5790
  */
5593
5791
  measure(flush = true) {
5792
+ if (this.destroyed)
5793
+ return;
5594
5794
  if (this.measureScheduled > -1)
5595
5795
  cancelAnimationFrame(this.measureScheduled);
5596
5796
  this.measureScheduled = -1; // Prevent requestMeasure calls from scheduling another animation frame
@@ -5600,15 +5800,18 @@ class EditorView {
5600
5800
  try {
5601
5801
  for (let i = 0;; i++) {
5602
5802
  this.updateState = 1 /* Measuring */;
5803
+ let oldViewport = this.viewport;
5603
5804
  let changed = this.viewState.measure(this.docView, i > 0);
5604
- let measuring = this.measureRequests;
5605
- if (!changed && !measuring.length && this.viewState.scrollTo == null)
5805
+ if (!changed && !this.measureRequests.length && this.viewState.scrollTo == null)
5606
5806
  break;
5607
- this.measureRequests = [];
5608
5807
  if (i > 5) {
5609
5808
  console.warn("Viewport failed to stabilize");
5610
5809
  break;
5611
5810
  }
5811
+ let measuring = [];
5812
+ // Only run measure requests in this cycle when the viewport didn't change
5813
+ if (!(changed & 4 /* Viewport */))
5814
+ [this.measureRequests, measuring] = [measuring, this.measureRequests];
5612
5815
  let measured = measuring.map(m => {
5613
5816
  try {
5614
5817
  return m.read(this);
@@ -5645,7 +5848,7 @@ class EditorView {
5645
5848
  this.docView.scrollRangeIntoView(this.viewState.scrollTo);
5646
5849
  this.viewState.scrollTo = null;
5647
5850
  }
5648
- if (!(changed & 4 /* Viewport */) && this.measureRequests.length == 0)
5851
+ if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to && this.measureRequests.length == 0)
5649
5852
  break;
5650
5853
  }
5651
5854
  }
@@ -5671,16 +5874,20 @@ class EditorView {
5671
5874
  });
5672
5875
  updateAttrs(this.dom, this.editorAttrs, editorAttrs);
5673
5876
  this.editorAttrs = editorAttrs;
5674
- let contentAttrs = combineAttrs(this.state.facet(contentAttributes), {
5877
+ let contentAttrs = {
5675
5878
  spellcheck: "false",
5676
5879
  autocorrect: "off",
5677
5880
  autocapitalize: "off",
5881
+ translate: "no",
5678
5882
  contenteditable: !this.state.facet(editable) ? "false" : contentEditablePlainTextSupported() ? "plaintext-only" : "true",
5679
5883
  class: "cm-content",
5680
5884
  style: `${browser.tabSize}: ${this.state.tabSize}`,
5681
5885
  role: "textbox",
5682
5886
  "aria-multiline": "true"
5683
- });
5887
+ };
5888
+ if (this.state.readOnly)
5889
+ contentAttrs["aria-readonly"] = "true";
5890
+ combineAttrs(this.state.facet(contentAttributes), contentAttrs);
5684
5891
  updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
5685
5892
  this.contentAttrs = contentAttrs;
5686
5893
  }
@@ -5978,11 +6185,13 @@ class EditorView {
5978
6185
  destroy() {
5979
6186
  for (let plugin of this.plugins)
5980
6187
  plugin.destroy(this);
6188
+ this.plugins = [];
5981
6189
  this.inputState.destroy();
5982
6190
  this.dom.remove();
5983
6191
  this.observer.destroy();
5984
6192
  if (this.measureScheduled > -1)
5985
6193
  cancelAnimationFrame(this.measureScheduled);
6194
+ this.destroyed = true;
5986
6195
  }
5987
6196
  /**
5988
6197
  Facet that can be used to add DOM event handlers. The value
@@ -6070,12 +6279,12 @@ every time the view updates.
6070
6279
  */
6071
6280
  EditorView.updateListener = updateListener;
6072
6281
  /**
6073
- Facet that controls whether the editor content is editable. When
6074
- its highest-precedence value is `false`, editing is disabled,
6075
- and the content element will no longer have its
6076
- `contenteditable` attribute set to `true`. (Note that this
6077
- doesn't affect API calls that change the editor content, even
6078
- when those are bound to keys or buttons.)
6282
+ Facet that controls whether the editor content DOM is editable.
6283
+ When its highest-precedence value is `false`, the element will
6284
+ not longer have its `contenteditable` attribute set. (Note that
6285
+ this doesn't affect API calls that change the editor content,
6286
+ even when those are bound to keys or buttons. See the
6287
+ [`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) facet for that.)
6079
6288
  */
6080
6289
  EditorView.editable = editable;
6081
6290
  /**
@@ -6170,11 +6379,7 @@ class CachedOrder {
6170
6379
  }
6171
6380
  }
6172
6381
 
6173
- const currentPlatform = typeof navigator == "undefined" ? "key"
6174
- : /Mac/.test(navigator.platform) ? "mac"
6175
- : /Win/.test(navigator.platform) ? "win"
6176
- : /Linux|X11/.test(navigator.platform) ? "linux"
6177
- : "key";
6382
+ const currentPlatform = browser.mac ? "mac" : browser.windows ? "win" : browser.linux ? "linux" : "key";
6178
6383
  function normalizeKeyName(name, platform) {
6179
6384
  const parts = name.split(/-(?!$)/);
6180
6385
  let result = parts[parts.length - 1];
@@ -6680,7 +6885,7 @@ class MatchDecorator {
6680
6885
  }
6681
6886
 
6682
6887
  const UnicodeRegexpSupport = /x/.unicode != null ? "gu" : "g";
6683
- const Specials = new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
6888
+ const Specials = new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
6684
6889
  const Names = {
6685
6890
  0: "null",
6686
6891
  7: "bell",
@@ -6695,6 +6900,8 @@ const Names = {
6695
6900
  8206: "left-to-right mark",
6696
6901
  8207: "right-to-left mark",
6697
6902
  8232: "line separator",
6903
+ 8237: "left-to-right override",
6904
+ 8238: "right-to-left override",
6698
6905
  8233: "paragraph separator",
6699
6906
  65279: "zero width no-break space",
6700
6907
  65532: "object replacement"
@@ -6861,7 +7068,7 @@ DOM class.
6861
7068
  function highlightActiveLine() {
6862
7069
  return activeLineHighlighter;
6863
7070
  }
6864
- const lineDeco = Decoration.line({ attributes: { class: "cm-activeLine" } });
7071
+ const lineDeco = Decoration.line({ class: "cm-activeLine" });
6865
7072
  const activeLineHighlighter = ViewPlugin.fromClass(class {
6866
7073
  constructor(view) {
6867
7074
  this.decorations = this.getDeco(view);