@codemirror/view 6.17.1 → 6.18.1

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
@@ -101,6 +101,7 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
101
101
  for (let cur = dom, stop = false; cur && !stop;) {
102
102
  if (cur.nodeType == 1) { // Element
103
103
  let bounding, top = cur == doc.body;
104
+ let scaleX = 1, scaleY = 1;
104
105
  if (top) {
105
106
  bounding = windowRect(win);
106
107
  }
@@ -112,9 +113,11 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
112
113
  continue;
113
114
  }
114
115
  let rect = cur.getBoundingClientRect();
116
+ scaleX = rect.width / cur.offsetWidth;
117
+ scaleY = rect.height / cur.offsetHeight;
115
118
  // Make sure scrollbar width isn't included in the rectangle
116
- bounding = { left: rect.left, right: rect.left + cur.clientWidth,
117
- top: rect.top, bottom: rect.top + cur.clientHeight };
119
+ bounding = { left: rect.left, right: rect.left + cur.clientWidth * scaleX,
120
+ top: rect.top, bottom: rect.top + cur.clientHeight * scaleY };
118
121
  }
119
122
  let moveX = 0, moveY = 0;
120
123
  if (y == "nearest") {
@@ -162,13 +165,13 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
162
165
  let movedX = 0, movedY = 0;
163
166
  if (moveY) {
164
167
  let start = cur.scrollTop;
165
- cur.scrollTop += moveY;
166
- movedY = cur.scrollTop - start;
168
+ cur.scrollTop += moveY / scaleY;
169
+ movedY = (cur.scrollTop - start) * scaleY;
167
170
  }
168
171
  if (moveX) {
169
172
  let start = cur.scrollLeft;
170
- cur.scrollLeft += moveX;
171
- movedX = cur.scrollLeft - start;
173
+ cur.scrollLeft += moveX / scaleX;
174
+ movedX = (cur.scrollLeft - start) * scaleX;
172
175
  }
173
176
  rect = { left: rect.left - movedX, top: rect.top - movedY,
174
177
  right: rect.right - movedX, bottom: rect.bottom - movedY };
@@ -481,6 +484,8 @@ class ContentView {
481
484
  }
482
485
  }
483
486
  setDOM(dom) {
487
+ if (this.dom == dom)
488
+ return;
484
489
  if (this.dom)
485
490
  this.dom.cmView = null;
486
491
  this.dom = dom;
@@ -653,113 +658,6 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
653
658
  replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
654
659
  }
655
660
 
656
- const LineBreakPlaceholder = "\uffff";
657
- class DOMReader {
658
- constructor(points, state$1) {
659
- this.points = points;
660
- this.text = "";
661
- this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
662
- }
663
- append(text) {
664
- this.text += text;
665
- }
666
- lineBreak() {
667
- this.text += LineBreakPlaceholder;
668
- }
669
- readRange(start, end) {
670
- if (!start)
671
- return this;
672
- let parent = start.parentNode;
673
- for (let cur = start;;) {
674
- this.findPointBefore(parent, cur);
675
- let oldLen = this.text.length;
676
- this.readNode(cur);
677
- let next = cur.nextSibling;
678
- if (next == end)
679
- break;
680
- let view = ContentView.get(cur), nextView = ContentView.get(next);
681
- if (view && nextView ? view.breakAfter :
682
- (view ? view.breakAfter : isBlockElement(cur)) ||
683
- (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
684
- this.lineBreak();
685
- cur = next;
686
- }
687
- this.findPointBefore(parent, end);
688
- return this;
689
- }
690
- readTextNode(node) {
691
- let text = node.nodeValue;
692
- for (let point of this.points)
693
- if (point.node == node)
694
- point.pos = this.text.length + Math.min(point.offset, text.length);
695
- for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
696
- let nextBreak = -1, breakSize = 1, m;
697
- if (this.lineSeparator) {
698
- nextBreak = text.indexOf(this.lineSeparator, off);
699
- breakSize = this.lineSeparator.length;
700
- }
701
- else if (m = re.exec(text)) {
702
- nextBreak = m.index;
703
- breakSize = m[0].length;
704
- }
705
- this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
706
- if (nextBreak < 0)
707
- break;
708
- this.lineBreak();
709
- if (breakSize > 1)
710
- for (let point of this.points)
711
- if (point.node == node && point.pos > this.text.length)
712
- point.pos -= breakSize - 1;
713
- off = nextBreak + breakSize;
714
- }
715
- }
716
- readNode(node) {
717
- if (node.cmIgnore)
718
- return;
719
- let view = ContentView.get(node);
720
- let fromView = view && view.overrideDOMText;
721
- if (fromView != null) {
722
- this.findPointInside(node, fromView.length);
723
- for (let i = fromView.iter(); !i.next().done;) {
724
- if (i.lineBreak)
725
- this.lineBreak();
726
- else
727
- this.append(i.value);
728
- }
729
- }
730
- else if (node.nodeType == 3) {
731
- this.readTextNode(node);
732
- }
733
- else if (node.nodeName == "BR") {
734
- if (node.nextSibling)
735
- this.lineBreak();
736
- }
737
- else if (node.nodeType == 1) {
738
- this.readRange(node.firstChild, null);
739
- }
740
- }
741
- findPointBefore(node, next) {
742
- for (let point of this.points)
743
- if (point.node == node && node.childNodes[point.offset] == next)
744
- point.pos = this.text.length;
745
- }
746
- findPointInside(node, maxLen) {
747
- for (let point of this.points)
748
- if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
749
- point.pos = this.text.length + Math.min(maxLen, point.offset);
750
- }
751
- }
752
- function isBlockElement(node) {
753
- return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
754
- }
755
- class DOMPoint {
756
- constructor(node, offset) {
757
- this.node = node;
758
- this.offset = offset;
759
- this.pos = -1;
760
- }
761
- }
762
-
763
661
  let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
764
662
  let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
765
663
  const ie_edge = /Edge\/(\d+)/.exec(nav.userAgent);
@@ -2703,6 +2601,7 @@ class DocView extends ContentView {
2703
2601
  this.view = view;
2704
2602
  this.decorations = [];
2705
2603
  this.dynamicDecorationMap = [];
2604
+ this.domChanged = null;
2706
2605
  this.hasComposition = null;
2707
2606
  this.markedForComposition = new Set;
2708
2607
  // Track a minimum width for the editor. When measuring sizes in
@@ -2731,6 +2630,7 @@ class DocView extends ContentView {
2731
2630
  }
2732
2631
  // Update the document view to a given state.
2733
2632
  update(update) {
2633
+ var _a;
2734
2634
  let changedRanges = update.changedRanges;
2735
2635
  if (this.minWidth > 0 && changedRanges.length) {
2736
2636
  if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
@@ -2741,7 +2641,15 @@ class DocView extends ContentView {
2741
2641
  this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2742
2642
  }
2743
2643
  }
2744
- let composition = this.view.inputState.composing < 0 ? null : findCompositionRange(this.view, update.changes);
2644
+ let readCompositionAt = -1;
2645
+ if (this.view.inputState.composing >= 0) {
2646
+ if ((_a = this.domChanged) === null || _a === void 0 ? void 0 : _a.newSel)
2647
+ readCompositionAt = this.domChanged.newSel.head;
2648
+ else if (!touchesComposition(update.changes, this.hasComposition) && !update.selectionSet)
2649
+ readCompositionAt = update.state.selection.main.head;
2650
+ }
2651
+ let composition = readCompositionAt > -1 ? findCompositionRange(this.view, update.changes, readCompositionAt) : null;
2652
+ this.domChanged = null;
2745
2653
  if (this.hasComposition) {
2746
2654
  this.markedForComposition.clear();
2747
2655
  let { from, to } = this.hasComposition;
@@ -2781,7 +2689,7 @@ class DocView extends ContentView {
2781
2689
  // messes with the scroll position during DOM mutation (though
2782
2690
  // no relayout is triggered and I cannot imagine how it can
2783
2691
  // recompute the scroll position without a layout)
2784
- this.dom.style.height = this.view.viewState.contentHeight + "px";
2692
+ this.dom.style.height = this.view.viewState.contentHeight / this.view.scaleY + "px";
2785
2693
  this.dom.style.flexBasis = this.minWidth ? this.minWidth + "px" : "";
2786
2694
  // Chrome will sometimes, when DOM mutations occur directly
2787
2695
  // around the selection, get confused and report a different
@@ -2853,14 +2761,12 @@ class DocView extends ContentView {
2853
2761
  }
2854
2762
  fixCompositionDOM(composition) {
2855
2763
  let fix = (dom, cView) => {
2856
- cView.flags |= 8 /* ViewFlag.Composition */;
2764
+ cView.flags |= 8 /* ViewFlag.Composition */ | (cView.children.some(c => c.flags & 7 /* ViewFlag.Dirty */) ? 1 /* ViewFlag.ChildDirty */ : 0);
2857
2765
  this.markedForComposition.add(cView);
2858
2766
  let prev = ContentView.get(dom);
2859
- if (prev != cView) {
2860
- if (prev)
2861
- prev.dom = null;
2862
- cView.setDOM(dom);
2863
- }
2767
+ if (prev && prev != cView)
2768
+ prev.dom = null;
2769
+ cView.setDOM(dom);
2864
2770
  };
2865
2771
  let pos = this.childPos(composition.range.fromB, 1);
2866
2772
  let cView = this.children[pos.i];
@@ -2883,9 +2789,8 @@ class DocView extends ContentView {
2883
2789
  let force = this.forceSelection;
2884
2790
  this.forceSelection = false;
2885
2791
  let main = this.view.state.selection.main;
2886
- // FIXME need to handle the case where the selection falls inside a block range
2887
- let anchor = this.domAtPos(main.anchor);
2888
- let head = main.empty ? anchor : this.domAtPos(main.head);
2792
+ let anchor = this.moveToLine(this.domAtPos(main.anchor));
2793
+ let head = main.empty ? anchor : this.moveToLine(this.domAtPos(main.head));
2889
2794
  // Always reset on Firefox when next to an uneditable node to
2890
2795
  // avoid invisible cursor bugs (#111)
2891
2796
  if (browser.gecko && main.empty && !this.hasComposition && betweenUneditable(anchor)) {
@@ -2918,12 +2823,12 @@ class DocView extends ContentView {
2918
2823
  if (nextTo && nextTo != (1 /* NextTo.Before */ | 2 /* NextTo.After */)) {
2919
2824
  let text = nearbyTextNode(anchor.node, anchor.offset, nextTo == 1 /* NextTo.Before */ ? 1 : -1);
2920
2825
  if (text)
2921
- anchor = new DOMPos(text, nextTo == 1 /* NextTo.Before */ ? 0 : text.nodeValue.length);
2826
+ anchor = new DOMPos(text.node, text.offset);
2922
2827
  }
2923
2828
  }
2924
2829
  rawSel.collapse(anchor.node, anchor.offset);
2925
- if (main.bidiLevel != null && domSel.caretBidiLevel != null)
2926
- domSel.caretBidiLevel = main.bidiLevel;
2830
+ if (main.bidiLevel != null && rawSel.caretBidiLevel !== undefined)
2831
+ rawSel.caretBidiLevel = main.bidiLevel;
2927
2832
  }
2928
2833
  else if (rawSel.extend) {
2929
2834
  // Selection.extend can be used to create an 'inverted' selection
@@ -2986,6 +2891,26 @@ class DocView extends ContentView {
2986
2891
  if (view.docView.posFromDOM(newRange.anchorNode, newRange.anchorOffset) != cursor.from)
2987
2892
  sel.collapse(anchorNode, anchorOffset);
2988
2893
  }
2894
+ // If a position is in/near a block widget, move it to a nearby text
2895
+ // line, since we don't want the cursor inside a block widget.
2896
+ moveToLine(pos) {
2897
+ // Block widgets will return positions before/after them, which
2898
+ // are thus directly in the document DOM element.
2899
+ let dom = this.dom, newPos;
2900
+ if (pos.node != dom)
2901
+ return pos;
2902
+ for (let i = pos.offset; !newPos && i < dom.childNodes.length; i++) {
2903
+ let view = ContentView.get(dom.childNodes[i]);
2904
+ if (view instanceof LineView)
2905
+ newPos = view.domAtPos(0);
2906
+ }
2907
+ for (let i = pos.offset - 1; !newPos && i >= 0; i--) {
2908
+ let view = ContentView.get(dom.childNodes[i]);
2909
+ if (view instanceof LineView)
2910
+ newPos = view.domAtPos(view.length);
2911
+ }
2912
+ return newPos ? new DOMPos(newPos.node, newPos.offset, true) : pos;
2913
+ }
2989
2914
  nearest(dom) {
2990
2915
  for (let cur = dom; cur;) {
2991
2916
  let domView = ContentView.get(cur);
@@ -3119,7 +3044,7 @@ class DocView extends ContentView {
3119
3044
  let next = i == vs.viewports.length ? null : vs.viewports[i];
3120
3045
  let end = next ? next.from - 1 : this.length;
3121
3046
  if (end > pos) {
3122
- let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
3047
+ let height = (vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top) / this.view.scaleY;
3123
3048
  deco.push(Decoration.replace({
3124
3049
  widget: new BlockGapWidget(height),
3125
3050
  block: true,
@@ -3184,74 +3109,27 @@ class BlockGapWidget extends WidgetType {
3184
3109
  }
3185
3110
  get estimatedHeight() { return this.height; }
3186
3111
  }
3187
- function findCompositionNode(view, dLen) {
3112
+ function findCompositionNode(view, headPos) {
3188
3113
  let sel = view.observer.selectionRange;
3189
3114
  let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
3190
3115
  if (!textNode)
3191
3116
  return null;
3192
- let cView = ContentView.get(textNode);
3193
- let from, to;
3194
- if (cView instanceof TextView) {
3195
- from = cView.posAtStart;
3196
- to = from + cView.length;
3197
- }
3198
- else {
3199
- let oldLen = Math.max(0, textNode.nodeValue.length - dLen);
3200
- up: for (let offset = 0, node = textNode;;) {
3201
- for (let sibling = node.previousSibling, cView; sibling; sibling = sibling.previousSibling) {
3202
- if (cView = ContentView.get(sibling)) {
3203
- to = cView.posAtEnd + offset;
3204
- from = Math.max(0, to - oldLen);
3205
- break up;
3206
- }
3207
- let reader = new DOMReader([], view.state);
3208
- reader.readNode(sibling);
3209
- if (reader.text.indexOf(LineBreakPlaceholder) > -1)
3210
- return null;
3211
- offset += reader.text.length;
3212
- }
3213
- node = node.parentNode;
3214
- if (!node)
3215
- return null;
3216
- let parentView = ContentView.get(node);
3217
- if (parentView) {
3218
- from = parentView.posAtStart + offset;
3219
- to = from + oldLen;
3220
- break;
3221
- }
3222
- }
3223
- }
3224
- return { from, to: to, node: textNode };
3117
+ let from = headPos - textNode.offset;
3118
+ return { from, to: from + textNode.node.nodeValue.length, node: textNode.node };
3225
3119
  }
3226
- function findCompositionRange(view, changes) {
3227
- let found = findCompositionNode(view, changes.newLength - changes.length);
3120
+ function findCompositionRange(view, changes, headPos) {
3121
+ let found = findCompositionNode(view, headPos);
3228
3122
  if (!found)
3229
3123
  return null;
3230
- let { from: fromA, to: toA, node: textNode } = found;
3231
- let fromB = changes.mapPos(fromA, -1), toB = changes.mapPos(toA, 1);
3232
- let text = textNode.nodeValue;
3124
+ let { node: textNode, from, to } = found, text = textNode.nodeValue;
3233
3125
  // Don't try to preserve multi-line compositions
3234
3126
  if (/[\n\r]/.test(text))
3235
3127
  return null;
3236
- if (toB - fromB != text.length) {
3237
- // If there is a length mismatch, see if mapping non-inclusively helps
3238
- let fromB2 = changes.mapPos(fromA, 1), toB2 = changes.mapPos(toA, -1);
3239
- if (toB2 - fromB2 == text.length)
3240
- fromB = fromB2, toB = toB2;
3241
- // See if we can find an instance of the text at either side
3242
- else if (view.state.doc.sliceString(toB - text.length, toB) == text)
3243
- fromB = toB - text.length;
3244
- else if (view.state.doc.sliceString(fromB, fromB + text.length) == text)
3245
- toB = fromB + text.length;
3246
- // Not found
3247
- else
3248
- return null;
3249
- }
3250
- let { main } = view.state.selection;
3251
- if (view.state.doc.sliceString(fromB, toB) != text || fromB > main.head || toB < main.head)
3128
+ if (view.state.doc.sliceString(found.from, found.to) != text)
3252
3129
  return null;
3130
+ let inv = changes.invertedDesc;
3131
+ let range = new ChangedRange(inv.mapPos(from), inv.mapPos(to), from, to);
3253
3132
  let marks = [];
3254
- let range = new ChangedRange(fromA, toA, fromB, toB);
3255
3133
  for (let parent = textNode.parentNode;; parent = parent.parentNode) {
3256
3134
  let parentView = ContentView.get(parent);
3257
3135
  if (parentView instanceof MarkView)
@@ -3272,7 +3150,7 @@ function nearbyTextNode(startNode, startOffset, side) {
3272
3150
  if (side <= 0)
3273
3151
  for (let node = startNode, offset = startOffset;;) {
3274
3152
  if (node.nodeType == 3)
3275
- return node;
3153
+ return { node: node, offset: offset };
3276
3154
  if (node.nodeType == 1 && offset > 0) {
3277
3155
  node = node.childNodes[offset - 1];
3278
3156
  offset = maxOffset(node);
@@ -3284,7 +3162,7 @@ function nearbyTextNode(startNode, startOffset, side) {
3284
3162
  if (side >= 0)
3285
3163
  for (let node = startNode, offset = startOffset;;) {
3286
3164
  if (node.nodeType == 3)
3287
- return node;
3165
+ return { node: node, offset: offset };
3288
3166
  if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
3289
3167
  node = node.childNodes[offset];
3290
3168
  offset = 0;
@@ -3321,6 +3199,15 @@ function inUneditable(node, inside) {
3321
3199
  }
3322
3200
  return false;
3323
3201
  }
3202
+ function touchesComposition(changes, composition) {
3203
+ let touched = false;
3204
+ if (composition)
3205
+ changes.iterChangedRanges((from, to) => {
3206
+ if (from < composition.to && to > composition.from)
3207
+ touched = true;
3208
+ });
3209
+ return touched;
3210
+ }
3324
3211
 
3325
3212
  function groupAt(state$1, pos, bias = 1) {
3326
3213
  let categorize = state$1.charCategorizer(pos);
@@ -5276,8 +5163,10 @@ class LineGap {
5276
5163
  }
5277
5164
  return true;
5278
5165
  }
5279
- draw(wrapping) {
5280
- return Decoration.replace({ widget: new LineGapWidget(this.size, wrapping) }).range(this.from, this.to);
5166
+ draw(viewState, wrapping) {
5167
+ return Decoration.replace({
5168
+ widget: new LineGapWidget(this.size * (wrapping ? viewState.scaleY : viewState.scaleX), wrapping)
5169
+ }).range(this.from, this.to);
5281
5170
  }
5282
5171
  }
5283
5172
  class LineGapWidget extends WidgetType {
@@ -5307,14 +5196,18 @@ class ViewState {
5307
5196
  // These are contentDOM-local coordinates
5308
5197
  this.pixelViewport = { left: 0, right: window.innerWidth, top: 0, bottom: 0 };
5309
5198
  this.inView = true;
5310
- this.paddingTop = 0;
5311
- this.paddingBottom = 0;
5312
- this.contentDOMWidth = 0;
5313
- this.contentDOMHeight = 0;
5314
- this.editorHeight = 0;
5315
- this.editorWidth = 0;
5316
- this.scrollTop = 0;
5199
+ this.paddingTop = 0; // Padding above the document, scaled
5200
+ this.paddingBottom = 0; // Padding below the document, scaled
5201
+ this.contentDOMWidth = 0; // contentDOM.getBoundingClientRect().width
5202
+ this.contentDOMHeight = 0; // contentDOM.getBoundingClientRect().height
5203
+ this.editorHeight = 0; // scrollDOM.clientHeight, unscaled
5204
+ this.editorWidth = 0; // scrollDOM.clientWidth, unscaled
5205
+ this.scrollTop = 0; // Last seen scrollDOM.scrollTop, scaled
5317
5206
  this.scrolledToBottom = true;
5207
+ // The CSS-transformation scale of the editor (transformed size /
5208
+ // concrete size)
5209
+ this.scaleX = 1;
5210
+ this.scaleY = 1;
5318
5211
  // The vertical position (document-relative) to which to anchor the
5319
5212
  // scroll position. -1 means anchor to the end of the document.
5320
5213
  this.scrollAnchorPos = 0;
@@ -5348,7 +5241,7 @@ class ViewState {
5348
5241
  this.updateViewportLines();
5349
5242
  this.updateForViewport();
5350
5243
  this.lineGaps = this.ensureLineGaps([]);
5351
- this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(false)));
5244
+ this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(this, false)));
5352
5245
  this.computeVisibleRanges();
5353
5246
  }
5354
5247
  updateForViewport() {
@@ -5420,8 +5313,23 @@ class ViewState {
5420
5313
  this.contentDOMHeight = domRect.height;
5421
5314
  this.mustMeasureContent = false;
5422
5315
  let result = 0, bias = 0;
5316
+ if (domRect.width && domRect.height) {
5317
+ let scaleX = domRect.width / dom.offsetWidth;
5318
+ let scaleY = domRect.height / dom.offsetHeight;
5319
+ if (scaleX > 0.995 && scaleX < 1.005)
5320
+ scaleX = 1;
5321
+ if (scaleY > 0.995 && scaleY < 1.005)
5322
+ scaleY = 1;
5323
+ if (this.scaleX != scaleX || this.scaleY != scaleY) {
5324
+ this.scaleX = scaleX;
5325
+ this.scaleY = scaleY;
5326
+ result |= 8 /* UpdateFlag.Geometry */;
5327
+ refresh = measureContent = true;
5328
+ }
5329
+ }
5423
5330
  // Vertical padding
5424
- let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
5331
+ let paddingTop = (parseInt(style.paddingTop) || 0) * this.scaleY;
5332
+ let paddingBottom = (parseInt(style.paddingBottom) || 0) * this.scaleY;
5425
5333
  if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
5426
5334
  this.paddingTop = paddingTop;
5427
5335
  this.paddingBottom = paddingBottom;
@@ -5433,9 +5341,10 @@ class ViewState {
5433
5341
  this.editorWidth = view.scrollDOM.clientWidth;
5434
5342
  result |= 8 /* UpdateFlag.Geometry */;
5435
5343
  }
5436
- if (this.scrollTop != view.scrollDOM.scrollTop) {
5344
+ let scrollTop = view.scrollDOM.scrollTop * this.scaleY;
5345
+ if (this.scrollTop != scrollTop) {
5437
5346
  this.scrollAnchorHeight = -1;
5438
- this.scrollTop = view.scrollDOM.scrollTop;
5347
+ this.scrollTop = scrollTop;
5439
5348
  }
5440
5349
  this.scrolledToBottom = isScrolledToBottom(view.scrollDOM);
5441
5350
  // Pixel viewport
@@ -5656,7 +5565,7 @@ class ViewState {
5656
5565
  updateLineGaps(gaps) {
5657
5566
  if (!LineGap.same(gaps, this.lineGaps)) {
5658
5567
  this.lineGaps = gaps;
5659
- this.lineGapDeco = Decoration.set(gaps.map(gap => gap.draw(this.heightOracle.lineWrapping)));
5568
+ this.lineGapDeco = Decoration.set(gaps.map(gap => gap.draw(this, this.heightOracle.lineWrapping)));
5660
5569
  }
5661
5570
  }
5662
5571
  computeVisibleRanges() {
@@ -6057,6 +5966,113 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
6057
5966
  }
6058
5967
  }, lightDarkIDs);
6059
5968
 
5969
+ const LineBreakPlaceholder = "\uffff";
5970
+ class DOMReader {
5971
+ constructor(points, state$1) {
5972
+ this.points = points;
5973
+ this.text = "";
5974
+ this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
5975
+ }
5976
+ append(text) {
5977
+ this.text += text;
5978
+ }
5979
+ lineBreak() {
5980
+ this.text += LineBreakPlaceholder;
5981
+ }
5982
+ readRange(start, end) {
5983
+ if (!start)
5984
+ return this;
5985
+ let parent = start.parentNode;
5986
+ for (let cur = start;;) {
5987
+ this.findPointBefore(parent, cur);
5988
+ let oldLen = this.text.length;
5989
+ this.readNode(cur);
5990
+ let next = cur.nextSibling;
5991
+ if (next == end)
5992
+ break;
5993
+ let view = ContentView.get(cur), nextView = ContentView.get(next);
5994
+ if (view && nextView ? view.breakAfter :
5995
+ (view ? view.breakAfter : isBlockElement(cur)) ||
5996
+ (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
5997
+ this.lineBreak();
5998
+ cur = next;
5999
+ }
6000
+ this.findPointBefore(parent, end);
6001
+ return this;
6002
+ }
6003
+ readTextNode(node) {
6004
+ let text = node.nodeValue;
6005
+ for (let point of this.points)
6006
+ if (point.node == node)
6007
+ point.pos = this.text.length + Math.min(point.offset, text.length);
6008
+ for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
6009
+ let nextBreak = -1, breakSize = 1, m;
6010
+ if (this.lineSeparator) {
6011
+ nextBreak = text.indexOf(this.lineSeparator, off);
6012
+ breakSize = this.lineSeparator.length;
6013
+ }
6014
+ else if (m = re.exec(text)) {
6015
+ nextBreak = m.index;
6016
+ breakSize = m[0].length;
6017
+ }
6018
+ this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
6019
+ if (nextBreak < 0)
6020
+ break;
6021
+ this.lineBreak();
6022
+ if (breakSize > 1)
6023
+ for (let point of this.points)
6024
+ if (point.node == node && point.pos > this.text.length)
6025
+ point.pos -= breakSize - 1;
6026
+ off = nextBreak + breakSize;
6027
+ }
6028
+ }
6029
+ readNode(node) {
6030
+ if (node.cmIgnore)
6031
+ return;
6032
+ let view = ContentView.get(node);
6033
+ let fromView = view && view.overrideDOMText;
6034
+ if (fromView != null) {
6035
+ this.findPointInside(node, fromView.length);
6036
+ for (let i = fromView.iter(); !i.next().done;) {
6037
+ if (i.lineBreak)
6038
+ this.lineBreak();
6039
+ else
6040
+ this.append(i.value);
6041
+ }
6042
+ }
6043
+ else if (node.nodeType == 3) {
6044
+ this.readTextNode(node);
6045
+ }
6046
+ else if (node.nodeName == "BR") {
6047
+ if (node.nextSibling)
6048
+ this.lineBreak();
6049
+ }
6050
+ else if (node.nodeType == 1) {
6051
+ this.readRange(node.firstChild, null);
6052
+ }
6053
+ }
6054
+ findPointBefore(node, next) {
6055
+ for (let point of this.points)
6056
+ if (point.node == node && node.childNodes[point.offset] == next)
6057
+ point.pos = this.text.length;
6058
+ }
6059
+ findPointInside(node, maxLen) {
6060
+ for (let point of this.points)
6061
+ if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
6062
+ point.pos = this.text.length + Math.min(maxLen, point.offset);
6063
+ }
6064
+ }
6065
+ function isBlockElement(node) {
6066
+ return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
6067
+ }
6068
+ class DOMPoint {
6069
+ constructor(node, offset) {
6070
+ this.node = node;
6071
+ this.offset = offset;
6072
+ this.pos = -1;
6073
+ }
6074
+ }
6075
+
6060
6076
  class DOMChange {
6061
6077
  constructor(view, start, end, typeOver) {
6062
6078
  this.typeOver = typeOver;
@@ -6207,8 +6223,14 @@ function applyDefaultInsert(view, change, newSel) {
6207
6223
  if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
6208
6224
  change.to <= sel.to && change.to >= sel.to - 10) {
6209
6225
  let replaced = view.state.sliceDoc(change.from, change.to);
6210
- let composition = findCompositionNode(view, change.insert.length - (change.to - change.from)) ||
6211
- view.state.doc.lineAt(sel.head);
6226
+ let compositionRange, composition = newSel && findCompositionNode(view, newSel.main.head);
6227
+ if (composition) {
6228
+ let dLen = change.insert.length - (change.to - change.from);
6229
+ compositionRange = { from: composition.from, to: composition.to - dLen };
6230
+ }
6231
+ else {
6232
+ compositionRange = view.state.doc.lineAt(sel.head);
6233
+ }
6212
6234
  let offset = sel.to - change.to, size = sel.to - sel.from;
6213
6235
  tr = startState.changeByRange(range => {
6214
6236
  if (range.from == sel.from && range.to == sel.to)
@@ -6219,7 +6241,7 @@ function applyDefaultInsert(view, change, newSel) {
6219
6241
  // changes in the same node work without aborting
6220
6242
  // composition, so cursors in the composition range are
6221
6243
  // ignored.
6222
- composition && range.to >= composition.from && range.from <= composition.to)
6244
+ range.to >= compositionRange.from && range.from <= compositionRange.to)
6223
6245
  return { range };
6224
6246
  let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
6225
6247
  return {
@@ -6638,7 +6660,9 @@ class DOMObserver {
6638
6660
  this.lastChange = Date.now();
6639
6661
  this.view.inputState.lastFocusTime = 0;
6640
6662
  this.selectionChanged = false;
6641
- return new DOMChange(this.view, from, to, typeOver);
6663
+ let change = new DOMChange(this.view, from, to, typeOver);
6664
+ this.view.docView.domChanged = { newSel: change.newSel ? change.newSel.main : null };
6665
+ return change;
6642
6666
  }
6643
6667
  // Apply pending changes, if any
6644
6668
  flush(readSelection = true) {
@@ -7067,9 +7091,9 @@ class EditorView {
7067
7091
  if (flush)
7068
7092
  this.observer.forceFlush();
7069
7093
  let updated = null;
7070
- let sDOM = this.scrollDOM, { scrollTop } = sDOM;
7094
+ let sDOM = this.scrollDOM, scrollTop = sDOM.scrollTop * this.scaleY;
7071
7095
  let { scrollAnchorPos, scrollAnchorHeight } = this.viewState;
7072
- if (scrollTop != this.viewState.scrollTop)
7096
+ if (Math.abs(scrollTop - this.viewState.scrollTop) > 1)
7073
7097
  scrollAnchorHeight = -1;
7074
7098
  this.viewState.scrollAnchorHeight = -1;
7075
7099
  try {
@@ -7146,7 +7170,8 @@ class EditorView {
7146
7170
  this.viewState.lineBlockAt(scrollAnchorPos).top;
7147
7171
  let diff = newAnchorHeight - scrollAnchorHeight;
7148
7172
  if (diff > 1 || diff < -1) {
7149
- scrollTop = sDOM.scrollTop = scrollTop + diff;
7173
+ scrollTop = scrollTop + diff;
7174
+ sDOM.scrollTop = scrollTop / this.scaleY;
7150
7175
  scrollAnchorHeight = -1;
7151
7176
  continue;
7152
7177
  }
@@ -7273,6 +7298,16 @@ class EditorView {
7273
7298
  return { top: this.viewState.paddingTop, bottom: this.viewState.paddingBottom };
7274
7299
  }
7275
7300
  /**
7301
+ If the editor is transformed with CSS, this provides the scale
7302
+ along the X axis. Otherwise, it will just be 1. Note that
7303
+ transforms other than translation and scaling are not supported.
7304
+ */
7305
+ get scaleX() { return this.viewState.scaleX; }
7306
+ /**
7307
+ Provide the CSS transformed scale along the Y axis.
7308
+ */
7309
+ get scaleY() { return this.viewState.scaleY; }
7310
+ /**
7276
7311
  Find the text line or block widget at the given vertical
7277
7312
  position (which is interpreted as relative to the [top of the
7278
7313
  document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)).
@@ -8063,8 +8098,8 @@ class RectangleMarker {
8063
8098
  }
8064
8099
  function getBase(view) {
8065
8100
  let rect = view.scrollDOM.getBoundingClientRect();
8066
- let left = view.textDirection == exports.Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth;
8067
- return { left: left - view.scrollDOM.scrollLeft, top: rect.top - view.scrollDOM.scrollTop };
8101
+ let left = view.textDirection == exports.Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth * view.scaleX;
8102
+ return { left: left - view.scrollDOM.scrollLeft * view.scaleX, top: rect.top - view.scrollDOM.scrollTop * view.scaleY };
8068
8103
  }
8069
8104
  function wrappedLine(view, pos, inside) {
8070
8105
  let range = state.EditorSelection.cursor(pos);
@@ -8166,6 +8201,8 @@ class LayerView {
8166
8201
  this.view = view;
8167
8202
  this.layer = layer;
8168
8203
  this.drawn = [];
8204
+ this.scaleX = 1;
8205
+ this.scaleY = 1;
8169
8206
  this.measureReq = { read: this.measure.bind(this), write: this.draw.bind(this) };
8170
8207
  this.dom = view.scrollDOM.appendChild(document.createElement("div"));
8171
8208
  this.dom.classList.add("cm-layer");
@@ -8173,6 +8210,7 @@ class LayerView {
8173
8210
  this.dom.classList.add("cm-layer-above");
8174
8211
  if (layer.class)
8175
8212
  this.dom.classList.add(layer.class);
8213
+ this.scale();
8176
8214
  this.dom.setAttribute("aria-hidden", "true");
8177
8215
  this.setOrder(view.state);
8178
8216
  view.requestMeasure(this.measureReq);
@@ -8182,8 +8220,10 @@ class LayerView {
8182
8220
  update(update) {
8183
8221
  if (update.startState.facet(layerOrder) != update.state.facet(layerOrder))
8184
8222
  this.setOrder(update.state);
8185
- if (this.layer.update(update, this.dom) || update.geometryChanged)
8223
+ if (this.layer.update(update, this.dom) || update.geometryChanged) {
8224
+ this.scale();
8186
8225
  update.view.requestMeasure(this.measureReq);
8226
+ }
8187
8227
  }
8188
8228
  setOrder(state) {
8189
8229
  let pos = 0, order = state.facet(layerOrder);
@@ -8194,6 +8234,14 @@ class LayerView {
8194
8234
  measure() {
8195
8235
  return this.layer.markers(this.view);
8196
8236
  }
8237
+ scale() {
8238
+ let { scaleX, scaleY } = this.view;
8239
+ if (scaleX != this.scaleX || scaleY != this.scaleY) {
8240
+ this.scaleX = scaleX;
8241
+ this.scaleY = scaleY;
8242
+ this.dom.style.transform = `scale(${1 / scaleX}, ${1 / scaleY})`;
8243
+ }
8244
+ }
8197
8245
  draw(markers) {
8198
8246
  if (markers.length != this.drawn.length || markers.some((p, i) => !sameMarker(p, this.drawn[i]))) {
8199
8247
  let old = this.dom.firstChild, oldI = 0;
@@ -8363,23 +8411,25 @@ const drawDropCursor = ViewPlugin.fromClass(class {
8363
8411
  }
8364
8412
  }
8365
8413
  readPos() {
8366
- let pos = this.view.state.field(dropCursorPos);
8367
- let rect = pos != null && this.view.coordsAtPos(pos);
8414
+ let { view } = this;
8415
+ let pos = view.state.field(dropCursorPos);
8416
+ let rect = pos != null && view.coordsAtPos(pos);
8368
8417
  if (!rect)
8369
8418
  return null;
8370
- let outer = this.view.scrollDOM.getBoundingClientRect();
8419
+ let outer = view.scrollDOM.getBoundingClientRect();
8371
8420
  return {
8372
- left: rect.left - outer.left + this.view.scrollDOM.scrollLeft,
8373
- top: rect.top - outer.top + this.view.scrollDOM.scrollTop,
8421
+ left: rect.left - outer.left + view.scrollDOM.scrollLeft * view.scaleX,
8422
+ top: rect.top - outer.top + view.scrollDOM.scrollTop * view.scaleY,
8374
8423
  height: rect.bottom - rect.top
8375
8424
  };
8376
8425
  }
8377
8426
  drawCursor(pos) {
8378
8427
  if (this.cursor) {
8428
+ let { scaleX, scaleY } = this.view;
8379
8429
  if (pos) {
8380
- this.cursor.style.left = pos.left + "px";
8381
- this.cursor.style.top = pos.top + "px";
8382
- this.cursor.style.height = pos.height + "px";
8430
+ this.cursor.style.left = pos.left / scaleX + "px";
8431
+ this.cursor.style.top = pos.top / scaleY + "px";
8432
+ this.cursor.style.height = pos.height / scaleY + "px";
8383
8433
  }
8384
8434
  else {
8385
8435
  this.cursor.style.left = "-100000px";
@@ -8621,7 +8671,9 @@ function specialCharPlugin() {
8621
8671
  if (code == 9) {
8622
8672
  let line = doc.lineAt(pos);
8623
8673
  let size = view.state.tabSize, col = state.countColumn(line.text, size, pos - line.from);
8624
- return Decoration.replace({ widget: new TabWidget((size - (col % size)) * this.view.defaultCharacterWidth) });
8674
+ return Decoration.replace({
8675
+ widget: new TabWidget((size - (col % size)) * this.view.defaultCharacterWidth / this.view.scaleX)
8676
+ });
8625
8677
  }
8626
8678
  return this.decorationCache[code] ||
8627
8679
  (this.decorationCache[code] = Decoration.replace({ widget: new SpecialCharWidget(conf, code) }));
@@ -8698,7 +8750,8 @@ const plugin = ViewPlugin.fromClass(class {
8698
8750
  }
8699
8751
  update(update) {
8700
8752
  let { view } = update;
8701
- let height = view.viewState.editorHeight - view.defaultLineHeight - view.documentPadding.top - 0.5;
8753
+ let height = view.viewState.editorHeight * view.scaleY -
8754
+ view.defaultLineHeight - view.documentPadding.top - 0.5;
8702
8755
  if (height >= 0 && height != this.height) {
8703
8756
  this.height = height;
8704
8757
  this.attrs = { style: `padding-bottom: ${height}px` };
@@ -8995,6 +9048,7 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
8995
9048
  constructor(view) {
8996
9049
  this.view = view;
8997
9050
  this.inView = true;
9051
+ this.madeAbsolute = false;
8998
9052
  this.lastTransaction = 0;
8999
9053
  this.measureTimeout = -1;
9000
9054
  let config = view.state.facet(tooltipConfig);
@@ -9046,7 +9100,7 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
9046
9100
  this.observeIntersection();
9047
9101
  let shouldMeasure = updated || update.geometryChanged;
9048
9102
  let newConfig = update.state.facet(tooltipConfig);
9049
- if (newConfig.position != this.position) {
9103
+ if (newConfig.position != this.position && !this.madeAbsolute) {
9050
9104
  this.position = newConfig.position;
9051
9105
  for (let t of this.manager.tooltipViews)
9052
9106
  t.dom.style.position = this.position;
@@ -9094,6 +9148,27 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
9094
9148
  }
9095
9149
  readMeasure() {
9096
9150
  let editor = this.view.dom.getBoundingClientRect();
9151
+ let scaleX = 1, scaleY = 1, makeAbsolute = false;
9152
+ if (this.position == "fixed") {
9153
+ let views = this.manager.tooltipViews;
9154
+ // When the dialog's offset parent isn't the body, we are
9155
+ // probably in a transformed container, and should use absolute
9156
+ // positioning instead, since fixed positioning inside a
9157
+ // transform works in a very broken way.
9158
+ makeAbsolute = views.length > 0 && views[0].dom.offsetParent != this.container.ownerDocument.body;
9159
+ }
9160
+ if (makeAbsolute || this.position == "absolute") {
9161
+ if (this.parent) {
9162
+ let rect = this.parent.getBoundingClientRect();
9163
+ if (rect.width && rect.height) {
9164
+ scaleX = rect.width / this.parent.offsetWidth;
9165
+ scaleY = rect.height / this.parent.offsetHeight;
9166
+ }
9167
+ }
9168
+ else {
9169
+ ({ scaleX, scaleY } = this.view.viewState);
9170
+ }
9171
+ }
9097
9172
  return {
9098
9173
  editor,
9099
9174
  parent: this.parent ? this.container.getBoundingClientRect() : editor,
@@ -9103,11 +9178,18 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
9103
9178
  }),
9104
9179
  size: this.manager.tooltipViews.map(({ dom }) => dom.getBoundingClientRect()),
9105
9180
  space: this.view.state.facet(tooltipConfig).tooltipSpace(this.view),
9181
+ scaleX, scaleY, makeAbsolute
9106
9182
  };
9107
9183
  }
9108
9184
  writeMeasure(measured) {
9109
9185
  var _a;
9110
- let { editor, space } = measured;
9186
+ if (measured.makeAbsolute) {
9187
+ this.madeAbsolute = true;
9188
+ this.position = "absolute";
9189
+ for (let t of this.manager.tooltipViews)
9190
+ t.dom.style.position = "absolute";
9191
+ }
9192
+ let { editor, space, scaleX, scaleY } = measured;
9111
9193
  let others = [];
9112
9194
  for (let i = 0; i < this.manager.tooltips.length; i++) {
9113
9195
  let tooltip = this.manager.tooltips[i], tView = this.manager.tooltipViews[i], { dom } = tView;
@@ -9140,7 +9222,7 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
9140
9222
  continue;
9141
9223
  }
9142
9224
  knownHeight.set(tView, height);
9143
- dom.style.height = (height = spaceVert) + "px";
9225
+ dom.style.height = (height = spaceVert) / scaleY + "px";
9144
9226
  }
9145
9227
  else if (dom.style.height) {
9146
9228
  dom.style.height = "";
@@ -9152,15 +9234,17 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
9152
9234
  if (r.left < right && r.right > left && r.top < top + height && r.bottom > top)
9153
9235
  top = above ? r.top - height - 2 - arrowHeight : r.bottom + arrowHeight + 2;
9154
9236
  if (this.position == "absolute") {
9155
- dom.style.top = (top - measured.parent.top) + "px";
9156
- dom.style.left = (left - measured.parent.left) + "px";
9237
+ dom.style.top = (top - measured.parent.top) / scaleY + "px";
9238
+ dom.style.left = (left - measured.parent.left) / scaleX + "px";
9157
9239
  }
9158
9240
  else {
9159
- dom.style.top = top + "px";
9160
- dom.style.left = left + "px";
9241
+ dom.style.top = top / scaleY + "px";
9242
+ dom.style.left = left / scaleX + "px";
9243
+ }
9244
+ if (arrow) {
9245
+ let arrowLeft = pos.left + (ltr ? offset.x : -offset.x) - (left + 14 /* Arrow.Offset */ - 7 /* Arrow.Size */);
9246
+ arrow.style.left = arrowLeft / scaleX + "px";
9161
9247
  }
9162
- if (arrow)
9163
- arrow.style.left = `${pos.left + (ltr ? offset.x : -offset.x) - (left + 14 /* Arrow.Offset */ - 7 /* Arrow.Size */)}px`;
9164
9248
  if (tView.overlap !== true)
9165
9249
  others.push({ left, top, right, bottom: top + height });
9166
9250
  dom.classList.toggle("cm-tooltip-above", above);
@@ -9762,7 +9846,7 @@ const gutterView = ViewPlugin.fromClass(class {
9762
9846
  this.dom = document.createElement("div");
9763
9847
  this.dom.className = "cm-gutters";
9764
9848
  this.dom.setAttribute("aria-hidden", "true");
9765
- this.dom.style.minHeight = this.view.contentHeight + "px";
9849
+ this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px";
9766
9850
  this.gutters = view.state.facet(activeGutters).map(conf => new SingleGutterView(view, conf));
9767
9851
  for (let gutter of this.gutters)
9768
9852
  this.dom.appendChild(gutter.dom);
@@ -9823,6 +9907,10 @@ const gutterView = ViewPlugin.fromClass(class {
9823
9907
  for (let cx of contexts)
9824
9908
  cx.line(this.view, line, classSet);
9825
9909
  }
9910
+ else if (line.widget) {
9911
+ for (let cx of contexts)
9912
+ cx.widget(this.view, line);
9913
+ }
9826
9914
  }
9827
9915
  for (let cx of contexts)
9828
9916
  cx.finish();
@@ -9872,7 +9960,9 @@ const gutterView = ViewPlugin.fromClass(class {
9872
9960
  let value = view.plugin(plugin);
9873
9961
  if (!value || value.gutters.length == 0 || !value.fixed)
9874
9962
  return null;
9875
- return view.textDirection == exports.Direction.LTR ? { left: value.dom.offsetWidth } : { right: value.dom.offsetWidth };
9963
+ return view.textDirection == exports.Direction.LTR
9964
+ ? { left: value.dom.offsetWidth * view.scaleX }
9965
+ : { right: value.dom.offsetWidth * view.scaleX };
9876
9966
  })
9877
9967
  });
9878
9968
  function asArray(val) { return (Array.isArray(val) ? val : [val]); }
@@ -9989,10 +10079,12 @@ class GutterElement {
9989
10079
  this.update(view, height, above, markers);
9990
10080
  }
9991
10081
  update(view, height, above, markers) {
9992
- if (this.height != height)
9993
- this.dom.style.height = (this.height = height) + "px";
10082
+ if (this.height != height) {
10083
+ this.height = height;
10084
+ this.dom.style.height = height / view.scaleY + "px";
10085
+ }
9994
10086
  if (this.above != above)
9995
- this.dom.style.marginTop = (this.above = above) ? above + "px" : "";
10087
+ this.dom.style.marginTop = (this.above = above) ? above / view.scaleY + "px" : "";
9996
10088
  if (!sameMarkers(this.markers, markers))
9997
10089
  this.setMarkers(view, markers);
9998
10090
  }