@codemirror/view 0.19.25 → 0.19.29
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/CHANGELOG.md +36 -0
- package/dist/index.cjs +1034 -1016
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1032 -1012
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -270,7 +270,7 @@ class DOMPos {
|
|
|
270
270
|
static before(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom), precise); }
|
|
271
271
|
static after(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom) + 1, precise); }
|
|
272
272
|
}
|
|
273
|
-
const
|
|
273
|
+
const noChildren = [];
|
|
274
274
|
class ContentView {
|
|
275
275
|
constructor() {
|
|
276
276
|
this.parent = null;
|
|
@@ -436,12 +436,12 @@ class ContentView {
|
|
|
436
436
|
v = parent;
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
|
-
replaceChildren(from, to, children =
|
|
439
|
+
replaceChildren(from, to, children = noChildren) {
|
|
440
440
|
this.markDirty();
|
|
441
441
|
for (let i = from; i < to; i++) {
|
|
442
442
|
let child = this.children[i];
|
|
443
443
|
if (child.parent == this)
|
|
444
|
-
child.
|
|
444
|
+
child.destroy();
|
|
445
445
|
}
|
|
446
446
|
this.children.splice(from, to - from, ...children);
|
|
447
447
|
for (let i = 0; i < children.length; i++)
|
|
@@ -463,6 +463,17 @@ class ContentView {
|
|
|
463
463
|
}
|
|
464
464
|
static get(node) { return node.cmView; }
|
|
465
465
|
get isEditable() { return true; }
|
|
466
|
+
merge(from, to, source, hasStart, openStart, openEnd) {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
become(other) { return false; }
|
|
470
|
+
// When this is a zero-length view with a side, this should return a
|
|
471
|
+
// number <= 0 to indicate it is before its position, or a
|
|
472
|
+
// number > 0 when after its position.
|
|
473
|
+
getSide() { return 0; }
|
|
474
|
+
destroy() {
|
|
475
|
+
this.parent = null;
|
|
476
|
+
}
|
|
466
477
|
}
|
|
467
478
|
ContentView.prototype.breakAfter = 0;
|
|
468
479
|
// Remove a DOM node and return its next sibling.
|
|
@@ -490,6 +501,94 @@ class ChildCursor {
|
|
|
490
501
|
}
|
|
491
502
|
}
|
|
492
503
|
}
|
|
504
|
+
function replaceRange(parent, fromI, fromOff, toI, toOff, insert, breakAtStart, openStart, openEnd) {
|
|
505
|
+
let { children } = parent;
|
|
506
|
+
let before = children.length ? children[fromI] : null;
|
|
507
|
+
let last = insert.length ? insert[insert.length - 1] : null;
|
|
508
|
+
let breakAtEnd = last ? last.breakAfter : breakAtStart;
|
|
509
|
+
// Change within a single child
|
|
510
|
+
if (fromI == toI && before && !breakAtStart && !breakAtEnd && insert.length < 2 &&
|
|
511
|
+
before.merge(fromOff, toOff, insert.length ? last : null, fromOff == 0, openStart, openEnd))
|
|
512
|
+
return;
|
|
513
|
+
if (toI < children.length) {
|
|
514
|
+
let after = children[toI];
|
|
515
|
+
// Make sure the end of the child after the update is preserved in `after`
|
|
516
|
+
if (after && toOff < after.length) {
|
|
517
|
+
// If we're splitting a child, separate part of it to avoid that
|
|
518
|
+
// being mangled when updating the child before the update.
|
|
519
|
+
if (fromI == toI) {
|
|
520
|
+
after = after.split(toOff);
|
|
521
|
+
toOff = 0;
|
|
522
|
+
}
|
|
523
|
+
// If the element after the replacement should be merged with
|
|
524
|
+
// the last replacing element, update `content`
|
|
525
|
+
if (!breakAtEnd && last && after.merge(0, toOff, last, true, 0, openEnd)) {
|
|
526
|
+
insert[insert.length - 1] = after;
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
// Remove the start of the after element, if necessary, and
|
|
530
|
+
// add it to `content`.
|
|
531
|
+
if (toOff)
|
|
532
|
+
after.merge(0, toOff, null, false, 0, openEnd);
|
|
533
|
+
insert.push(after);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
else if (after === null || after === void 0 ? void 0 : after.breakAfter) {
|
|
537
|
+
// The element at `toI` is entirely covered by this range.
|
|
538
|
+
// Preserve its line break, if any.
|
|
539
|
+
if (last)
|
|
540
|
+
last.breakAfter = 1;
|
|
541
|
+
else
|
|
542
|
+
breakAtStart = 1;
|
|
543
|
+
}
|
|
544
|
+
// Since we've handled the next element from the current elements
|
|
545
|
+
// now, make sure `toI` points after that.
|
|
546
|
+
toI++;
|
|
547
|
+
}
|
|
548
|
+
if (before) {
|
|
549
|
+
before.breakAfter = breakAtStart;
|
|
550
|
+
if (fromOff > 0) {
|
|
551
|
+
if (!breakAtStart && insert.length && before.merge(fromOff, before.length, insert[0], false, openStart, 0)) {
|
|
552
|
+
before.breakAfter = insert.shift().breakAfter;
|
|
553
|
+
}
|
|
554
|
+
else if (fromOff < before.length || before.children.length && before.children[before.children.length - 1].length == 0) {
|
|
555
|
+
before.merge(fromOff, before.length, null, false, openStart, 0);
|
|
556
|
+
}
|
|
557
|
+
fromI++;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
// Try to merge widgets on the boundaries of the replacement
|
|
561
|
+
while (fromI < toI && insert.length) {
|
|
562
|
+
if (children[toI - 1].become(insert[insert.length - 1])) {
|
|
563
|
+
toI--;
|
|
564
|
+
insert.pop();
|
|
565
|
+
openEnd = insert.length ? 0 : openStart;
|
|
566
|
+
}
|
|
567
|
+
else if (children[fromI].become(insert[0])) {
|
|
568
|
+
fromI++;
|
|
569
|
+
insert.shift();
|
|
570
|
+
openStart = insert.length ? 0 : openEnd;
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (!insert.length && fromI && toI < children.length && !children[fromI - 1].breakAfter &&
|
|
577
|
+
children[toI].merge(0, 0, children[fromI - 1], false, openStart, openEnd))
|
|
578
|
+
fromI--;
|
|
579
|
+
if (fromI < toI || insert.length)
|
|
580
|
+
parent.replaceChildren(fromI, toI, insert);
|
|
581
|
+
}
|
|
582
|
+
function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
|
|
583
|
+
let cur = parent.childCursor();
|
|
584
|
+
let { i: toI, off: toOff } = cur.findPos(to, 1);
|
|
585
|
+
let { i: fromI, off: fromOff } = cur.findPos(from, -1);
|
|
586
|
+
let dLen = from - to;
|
|
587
|
+
for (let view of insert)
|
|
588
|
+
dLen += view.length;
|
|
589
|
+
parent.length += dLen;
|
|
590
|
+
replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
|
|
591
|
+
}
|
|
493
592
|
|
|
494
593
|
let [nav, doc] = typeof navigator != "undefined"
|
|
495
594
|
? [navigator, document]
|
|
@@ -521,21 +620,8 @@ var browser = {
|
|
|
521
620
|
tabSize: doc.documentElement.style.tabSize != null ? "tab-size" : "-moz-tab-size"
|
|
522
621
|
};
|
|
523
622
|
|
|
524
|
-
const none$2 = [];
|
|
525
|
-
class InlineView extends ContentView {
|
|
526
|
-
/**
|
|
527
|
-
Return true when this view is equivalent to `other` and can take
|
|
528
|
-
on its role.
|
|
529
|
-
*/
|
|
530
|
-
become(_other) { return false; }
|
|
531
|
-
// When this is a zero-length view with a side, this should return a
|
|
532
|
-
// negative number to indicate it is before its position, or a
|
|
533
|
-
// positive number when after its position.
|
|
534
|
-
getSide() { return 0; }
|
|
535
|
-
}
|
|
536
|
-
InlineView.prototype.children = none$2;
|
|
537
623
|
const MaxJoinLen = 256;
|
|
538
|
-
class TextView extends
|
|
624
|
+
class TextView extends ContentView {
|
|
539
625
|
constructor(text) {
|
|
540
626
|
super();
|
|
541
627
|
this.text = text;
|
|
@@ -566,7 +652,7 @@ class TextView extends InlineView {
|
|
|
566
652
|
this.markDirty();
|
|
567
653
|
return true;
|
|
568
654
|
}
|
|
569
|
-
|
|
655
|
+
split(from) {
|
|
570
656
|
let result = new TextView(this.text.slice(from));
|
|
571
657
|
this.text = this.text.slice(0, from);
|
|
572
658
|
return result;
|
|
@@ -582,7 +668,7 @@ class TextView extends InlineView {
|
|
|
582
668
|
return textCoords(this.dom, pos, side);
|
|
583
669
|
}
|
|
584
670
|
}
|
|
585
|
-
class MarkView extends
|
|
671
|
+
class MarkView extends ContentView {
|
|
586
672
|
constructor(mark, children = [], length = 0) {
|
|
587
673
|
super();
|
|
588
674
|
this.mark = mark;
|
|
@@ -605,20 +691,20 @@ class MarkView extends InlineView {
|
|
|
605
691
|
this.createDOM();
|
|
606
692
|
super.sync(track);
|
|
607
693
|
}
|
|
608
|
-
merge(from, to, source, openStart, openEnd) {
|
|
694
|
+
merge(from, to, source, _hasStart, openStart, openEnd) {
|
|
609
695
|
if (source && (!(source instanceof MarkView && source.mark.eq(this.mark)) ||
|
|
610
696
|
(from && openStart <= 0) || (to < this.length && openEnd <= 0)))
|
|
611
697
|
return false;
|
|
612
|
-
|
|
698
|
+
mergeChildrenInto(this, from, to, source ? source.children : [], openStart - 1, openEnd - 1);
|
|
613
699
|
this.markDirty();
|
|
614
700
|
return true;
|
|
615
701
|
}
|
|
616
|
-
|
|
702
|
+
split(from) {
|
|
617
703
|
let result = [], off = 0, detachFrom = -1, i = 0;
|
|
618
704
|
for (let elt of this.children) {
|
|
619
705
|
let end = off + elt.length;
|
|
620
706
|
if (end > from)
|
|
621
|
-
result.push(off < from ? elt.
|
|
707
|
+
result.push(off < from ? elt.split(from - off) : elt);
|
|
622
708
|
if (detachFrom < 0 && off >= from)
|
|
623
709
|
detachFrom = i;
|
|
624
710
|
off = end;
|
|
@@ -626,8 +712,10 @@ class MarkView extends InlineView {
|
|
|
626
712
|
}
|
|
627
713
|
let length = this.length - from;
|
|
628
714
|
this.length = from;
|
|
629
|
-
if (detachFrom > -1)
|
|
630
|
-
this.
|
|
715
|
+
if (detachFrom > -1) {
|
|
716
|
+
this.children.length = detachFrom;
|
|
717
|
+
this.markDirty();
|
|
718
|
+
}
|
|
631
719
|
return new MarkView(this.mark, result, length);
|
|
632
720
|
}
|
|
633
721
|
domAtPos(pos) {
|
|
@@ -669,7 +757,7 @@ function textCoords(text, pos, side) {
|
|
|
669
757
|
return flatten ? flattenRect(rect, flatten < 0) : rect || null;
|
|
670
758
|
}
|
|
671
759
|
// Also used for collapsed ranges that don't have a placeholder widget!
|
|
672
|
-
class WidgetView extends
|
|
760
|
+
class WidgetView extends ContentView {
|
|
673
761
|
constructor(widget, length, side) {
|
|
674
762
|
super();
|
|
675
763
|
this.widget = widget;
|
|
@@ -679,7 +767,7 @@ class WidgetView extends InlineView {
|
|
|
679
767
|
static create(widget, length, side) {
|
|
680
768
|
return new (widget.customView || WidgetView)(widget, length, side);
|
|
681
769
|
}
|
|
682
|
-
|
|
770
|
+
split(from) {
|
|
683
771
|
let result = WidgetView.create(this.widget, this.length - from, this.side);
|
|
684
772
|
this.length -= from;
|
|
685
773
|
return result;
|
|
@@ -691,7 +779,7 @@ class WidgetView extends InlineView {
|
|
|
691
779
|
}
|
|
692
780
|
}
|
|
693
781
|
getSide() { return this.side; }
|
|
694
|
-
merge(from, to, source, openStart, openEnd) {
|
|
782
|
+
merge(from, to, source, hasStart, openStart, openEnd) {
|
|
695
783
|
if (source && (!(source instanceof WidgetView) || !this.widget.compare(source.widget) ||
|
|
696
784
|
from > 0 && openStart <= 0 || to < this.length && openEnd <= 0))
|
|
697
785
|
return false;
|
|
@@ -736,6 +824,11 @@ class WidgetView extends InlineView {
|
|
|
736
824
|
return (pos == 0 && side > 0 || pos == this.length && side <= 0) ? rect : flattenRect(rect, pos == 0);
|
|
737
825
|
}
|
|
738
826
|
get isEditable() { return false; }
|
|
827
|
+
destroy() {
|
|
828
|
+
super.destroy();
|
|
829
|
+
if (this.dom)
|
|
830
|
+
this.widget.destroy(this.dom);
|
|
831
|
+
}
|
|
739
832
|
}
|
|
740
833
|
class CompositionView extends WidgetView {
|
|
741
834
|
domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
|
|
@@ -749,10 +842,13 @@ class CompositionView extends WidgetView {
|
|
|
749
842
|
coordsAt(pos, side) { return textCoords(this.widget.text, pos, side); }
|
|
750
843
|
get isEditable() { return true; }
|
|
751
844
|
}
|
|
845
|
+
// Use two characters on Android, to prevent Chrome from closing the
|
|
846
|
+
// virtual keyboard when backspacing after a widget (#602).
|
|
847
|
+
const ZeroWidthSpace = browser.android ? "\u200b\u200b" : "\u200b";
|
|
752
848
|
// These are drawn around uneditable widgets to avoid a number of
|
|
753
849
|
// browser bugs that show up when the cursor is directly next to
|
|
754
850
|
// uneditable inline content.
|
|
755
|
-
class WidgetBufferView extends
|
|
851
|
+
class WidgetBufferView extends ContentView {
|
|
756
852
|
constructor(side) {
|
|
757
853
|
super();
|
|
758
854
|
this.side = side;
|
|
@@ -762,15 +858,16 @@ class WidgetBufferView extends InlineView {
|
|
|
762
858
|
become(other) {
|
|
763
859
|
return other instanceof WidgetBufferView && other.side == this.side;
|
|
764
860
|
}
|
|
765
|
-
|
|
861
|
+
split() { return new WidgetBufferView(this.side); }
|
|
766
862
|
sync() {
|
|
767
863
|
if (!this.dom)
|
|
768
|
-
this.setDOM(document.createTextNode(
|
|
769
|
-
else if (this.dirty && this.dom.nodeValue !=
|
|
770
|
-
this.dom.nodeValue =
|
|
864
|
+
this.setDOM(document.createTextNode(ZeroWidthSpace));
|
|
865
|
+
else if (this.dirty && this.dom.nodeValue != ZeroWidthSpace)
|
|
866
|
+
this.dom.nodeValue = ZeroWidthSpace;
|
|
771
867
|
}
|
|
772
868
|
getSide() { return this.side; }
|
|
773
869
|
domAtPos(pos) { return DOMPos.before(this.dom); }
|
|
870
|
+
localPosFromDOM() { return 0; }
|
|
774
871
|
domBoundsAround() { return null; }
|
|
775
872
|
coordsAt(pos) {
|
|
776
873
|
let rects = clientRectsFor(this.dom);
|
|
@@ -780,90 +877,7 @@ class WidgetBufferView extends InlineView {
|
|
|
780
877
|
return Text.of([this.dom.nodeValue.replace(/\u200b/g, "")]);
|
|
781
878
|
}
|
|
782
879
|
}
|
|
783
|
-
|
|
784
|
-
let cur = parent.childCursor();
|
|
785
|
-
let { i: toI, off: toOff } = cur.findPos(to, 1);
|
|
786
|
-
let { i: fromI, off: fromOff } = cur.findPos(from, -1);
|
|
787
|
-
let dLen = from - to;
|
|
788
|
-
for (let view of elts)
|
|
789
|
-
dLen += view.length;
|
|
790
|
-
parent.length += dLen;
|
|
791
|
-
let { children } = parent;
|
|
792
|
-
// Both from and to point into the same child view
|
|
793
|
-
if (fromI == toI && fromOff) {
|
|
794
|
-
let start = children[fromI];
|
|
795
|
-
// Maybe just update that view and be done
|
|
796
|
-
if (elts.length == 1 && start.merge(fromOff, toOff, elts[0], openStart, openEnd))
|
|
797
|
-
return;
|
|
798
|
-
if (elts.length == 0) {
|
|
799
|
-
start.merge(fromOff, toOff, null, openStart, openEnd);
|
|
800
|
-
return;
|
|
801
|
-
}
|
|
802
|
-
// Otherwise split it, so that we don't have to worry about aliasing front/end afterwards
|
|
803
|
-
let after = start.slice(toOff);
|
|
804
|
-
if (after.merge(0, 0, elts[elts.length - 1], 0, openEnd))
|
|
805
|
-
elts[elts.length - 1] = after;
|
|
806
|
-
else
|
|
807
|
-
elts.push(after);
|
|
808
|
-
toI++;
|
|
809
|
-
openEnd = toOff = 0;
|
|
810
|
-
}
|
|
811
|
-
// Make sure start and end positions fall on node boundaries
|
|
812
|
-
// (fromOff/toOff are no longer used after this), and that if the
|
|
813
|
-
// start or end of the elts can be merged with adjacent nodes,
|
|
814
|
-
// this is done
|
|
815
|
-
if (toOff) {
|
|
816
|
-
let end = children[toI];
|
|
817
|
-
if (elts.length && end.merge(0, toOff, elts[elts.length - 1], 0, openEnd)) {
|
|
818
|
-
elts.pop();
|
|
819
|
-
openEnd = elts.length ? 0 : openStart;
|
|
820
|
-
}
|
|
821
|
-
else {
|
|
822
|
-
end.merge(0, toOff, null, 0, 0);
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
else if (toI < children.length && elts.length &&
|
|
826
|
-
children[toI].merge(0, 0, elts[elts.length - 1], 0, openEnd)) {
|
|
827
|
-
elts.pop();
|
|
828
|
-
openEnd = elts.length ? 0 : openStart;
|
|
829
|
-
}
|
|
830
|
-
if (fromOff) {
|
|
831
|
-
let start = children[fromI];
|
|
832
|
-
if (elts.length && start.merge(fromOff, start.length, elts[0], openStart, 0)) {
|
|
833
|
-
elts.shift();
|
|
834
|
-
openStart = elts.length ? 0 : openEnd;
|
|
835
|
-
}
|
|
836
|
-
else {
|
|
837
|
-
start.merge(fromOff, start.length, null, 0, 0);
|
|
838
|
-
}
|
|
839
|
-
fromI++;
|
|
840
|
-
}
|
|
841
|
-
else if (fromI && elts.length) {
|
|
842
|
-
let end = children[fromI - 1];
|
|
843
|
-
if (end.merge(end.length, end.length, elts[0], openStart, 0)) {
|
|
844
|
-
elts.shift();
|
|
845
|
-
openStart = elts.length ? 0 : openEnd;
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
// Then try to merge any mergeable nodes at the start and end of
|
|
849
|
-
// the changed range
|
|
850
|
-
while (fromI < toI && elts.length && children[toI - 1].become(elts[elts.length - 1])) {
|
|
851
|
-
elts.pop();
|
|
852
|
-
toI--;
|
|
853
|
-
openEnd = elts.length ? 0 : openStart;
|
|
854
|
-
}
|
|
855
|
-
while (fromI < toI && elts.length && children[fromI].become(elts[0])) {
|
|
856
|
-
elts.shift();
|
|
857
|
-
fromI++;
|
|
858
|
-
openStart = elts.length ? 0 : openEnd;
|
|
859
|
-
}
|
|
860
|
-
if (!elts.length && fromI && toI < children.length &&
|
|
861
|
-
children[toI].merge(0, 0, children[fromI - 1], openStart, openEnd))
|
|
862
|
-
fromI--;
|
|
863
|
-
// And if anything remains, splice the child array to insert the new elts
|
|
864
|
-
if (elts.length || fromI != toI)
|
|
865
|
-
parent.replaceChildren(fromI, toI, elts);
|
|
866
|
-
}
|
|
880
|
+
TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
|
|
867
881
|
function inlineDOMAtPos(dom, children, pos) {
|
|
868
882
|
let i = 0;
|
|
869
883
|
for (let off = 0; i < children.length; i++) {
|
|
@@ -1004,6 +1018,11 @@ class WidgetType {
|
|
|
1004
1018
|
@internal
|
|
1005
1019
|
*/
|
|
1006
1020
|
get customView() { return null; }
|
|
1021
|
+
/**
|
|
1022
|
+
This is called when the an instance of the widget is removed
|
|
1023
|
+
from the editor view.
|
|
1024
|
+
*/
|
|
1025
|
+
destroy(_dom) { }
|
|
1007
1026
|
}
|
|
1008
1027
|
/**
|
|
1009
1028
|
The different types of blocks that can occur in an editor view.
|
|
@@ -1163,12 +1182,12 @@ class PointDecoration extends Decoration {
|
|
|
1163
1182
|
super(startSide, endSide, widget, spec);
|
|
1164
1183
|
this.block = block;
|
|
1165
1184
|
this.isReplace = isReplace;
|
|
1166
|
-
this.mapMode = !block ? MapMode.TrackDel : startSide
|
|
1185
|
+
this.mapMode = !block ? MapMode.TrackDel : startSide <= 0 ? MapMode.TrackBefore : MapMode.TrackAfter;
|
|
1167
1186
|
}
|
|
1168
1187
|
// Only relevant when this.block == true
|
|
1169
1188
|
get type() {
|
|
1170
1189
|
return this.startSide < this.endSide ? BlockType.WidgetRange
|
|
1171
|
-
: this.startSide
|
|
1190
|
+
: this.startSide <= 0 ? BlockType.WidgetBefore : BlockType.WidgetAfter;
|
|
1172
1191
|
}
|
|
1173
1192
|
get heightRelevant() { return this.block || !!this.widget && this.widget.estimatedHeight >= 5; }
|
|
1174
1193
|
eq(other) {
|
|
@@ -1178,7 +1197,7 @@ class PointDecoration extends Decoration {
|
|
|
1178
1197
|
this.startSide == other.startSide && this.endSide == other.endSide;
|
|
1179
1198
|
}
|
|
1180
1199
|
range(from, to = from) {
|
|
1181
|
-
if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide
|
|
1200
|
+
if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
|
|
1182
1201
|
throw new RangeError("Invalid range for replacement decoration");
|
|
1183
1202
|
if (!this.isReplace && to != from)
|
|
1184
1203
|
throw new RangeError("Widget decorations can only have zero-length ranges");
|
|
@@ -1215,16 +1234,16 @@ class LineView extends ContentView {
|
|
|
1215
1234
|
this.breakAfter = 0;
|
|
1216
1235
|
}
|
|
1217
1236
|
// Consumes source
|
|
1218
|
-
merge(from, to, source,
|
|
1237
|
+
merge(from, to, source, hasStart, openStart, openEnd) {
|
|
1219
1238
|
if (source) {
|
|
1220
1239
|
if (!(source instanceof LineView))
|
|
1221
1240
|
return false;
|
|
1222
1241
|
if (!this.dom)
|
|
1223
1242
|
source.transferDOM(this); // Reuse source.dom when appropriate
|
|
1224
1243
|
}
|
|
1225
|
-
if (
|
|
1244
|
+
if (hasStart)
|
|
1226
1245
|
this.setDeco(source ? source.attrs : null);
|
|
1227
|
-
|
|
1246
|
+
mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
|
|
1228
1247
|
return true;
|
|
1229
1248
|
}
|
|
1230
1249
|
split(at) {
|
|
@@ -1234,16 +1253,14 @@ class LineView extends ContentView {
|
|
|
1234
1253
|
return end;
|
|
1235
1254
|
let { i, off } = this.childPos(at);
|
|
1236
1255
|
if (off) {
|
|
1237
|
-
end.append(this.children[i].
|
|
1238
|
-
this.children[i].merge(off, this.children[i].length, null, 0, 0);
|
|
1256
|
+
end.append(this.children[i].split(off), 0);
|
|
1257
|
+
this.children[i].merge(off, this.children[i].length, null, false, 0, 0);
|
|
1239
1258
|
i++;
|
|
1240
1259
|
}
|
|
1241
1260
|
for (let j = i; j < this.children.length; j++)
|
|
1242
1261
|
end.append(this.children[j], 0);
|
|
1243
|
-
while (i > 0 && this.children[i - 1].length == 0)
|
|
1244
|
-
this.children[i
|
|
1245
|
-
i--;
|
|
1246
|
-
}
|
|
1262
|
+
while (i > 0 && this.children[i - 1].length == 0)
|
|
1263
|
+
this.children[--i].destroy();
|
|
1247
1264
|
this.children.length = i;
|
|
1248
1265
|
this.markDirty();
|
|
1249
1266
|
this.length = at;
|
|
@@ -1266,7 +1283,6 @@ class LineView extends ContentView {
|
|
|
1266
1283
|
this.attrs = attrs;
|
|
1267
1284
|
}
|
|
1268
1285
|
}
|
|
1269
|
-
// Only called when building a line view in ContentBuilder
|
|
1270
1286
|
append(child, openStart) {
|
|
1271
1287
|
joinInlineInto(this, child, openStart);
|
|
1272
1288
|
}
|
|
@@ -1323,7 +1339,7 @@ class LineView extends ContentView {
|
|
|
1323
1339
|
coordsAt(pos, side) {
|
|
1324
1340
|
return coordsInChildren(this, pos, side);
|
|
1325
1341
|
}
|
|
1326
|
-
|
|
1342
|
+
become(_other) { return false; }
|
|
1327
1343
|
get type() { return BlockType.Text; }
|
|
1328
1344
|
static find(docView, pos) {
|
|
1329
1345
|
for (let i = 0, off = 0;; i++) {
|
|
@@ -1338,7 +1354,6 @@ class LineView extends ContentView {
|
|
|
1338
1354
|
}
|
|
1339
1355
|
}
|
|
1340
1356
|
}
|
|
1341
|
-
const none$1 = [];
|
|
1342
1357
|
class BlockWidgetView extends ContentView {
|
|
1343
1358
|
constructor(widget, length, type) {
|
|
1344
1359
|
super();
|
|
@@ -1364,7 +1379,7 @@ class BlockWidgetView extends ContentView {
|
|
|
1364
1379
|
end.breakAfter = this.breakAfter;
|
|
1365
1380
|
return end;
|
|
1366
1381
|
}
|
|
1367
|
-
get children() { return
|
|
1382
|
+
get children() { return noChildren; }
|
|
1368
1383
|
sync() {
|
|
1369
1384
|
if (!this.dom || !this.widget.updateDOM(this.dom)) {
|
|
1370
1385
|
this.setDOM(this.widget.toDOM(this.editorView));
|
|
@@ -1375,7 +1390,7 @@ class BlockWidgetView extends ContentView {
|
|
|
1375
1390
|
return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text$1.empty;
|
|
1376
1391
|
}
|
|
1377
1392
|
domBoundsAround() { return null; }
|
|
1378
|
-
|
|
1393
|
+
become(other) {
|
|
1379
1394
|
if (other instanceof BlockWidgetView && other.type == this.type &&
|
|
1380
1395
|
other.widget.constructor == this.widget.constructor) {
|
|
1381
1396
|
if (!other.widget.eq(this.widget))
|
|
@@ -1389,6 +1404,11 @@ class BlockWidgetView extends ContentView {
|
|
|
1389
1404
|
}
|
|
1390
1405
|
ignoreMutation() { return true; }
|
|
1391
1406
|
ignoreEvent(event) { return this.widget.ignoreEvent(event); }
|
|
1407
|
+
destroy() {
|
|
1408
|
+
super.destroy();
|
|
1409
|
+
if (this.dom)
|
|
1410
|
+
this.widget.destroy(this.dom);
|
|
1411
|
+
}
|
|
1392
1412
|
}
|
|
1393
1413
|
|
|
1394
1414
|
class ContentBuilder {
|
|
@@ -1918,854 +1938,882 @@ class ViewUpdate {
|
|
|
1918
1938
|
get empty() { return this.flags == 0 && this.transactions.length == 0; }
|
|
1919
1939
|
}
|
|
1920
1940
|
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1941
|
+
/**
|
|
1942
|
+
Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
|
|
1943
|
+
*/
|
|
1944
|
+
var Direction = /*@__PURE__*/(function (Direction) {
|
|
1945
|
+
// (These are chosen to match the base levels, in bidi algorithm
|
|
1946
|
+
// terms, of spans in that direction.)
|
|
1947
|
+
/**
|
|
1948
|
+
Left-to-right.
|
|
1949
|
+
*/
|
|
1950
|
+
Direction[Direction["LTR"] = 0] = "LTR";
|
|
1951
|
+
/**
|
|
1952
|
+
Right-to-left.
|
|
1953
|
+
*/
|
|
1954
|
+
Direction[Direction["RTL"] = 1] = "RTL";
|
|
1955
|
+
return Direction})(Direction || (Direction = {}));
|
|
1956
|
+
const LTR = Direction.LTR, RTL = Direction.RTL;
|
|
1957
|
+
// Decode a string with each type encoded as log2(type)
|
|
1958
|
+
function dec(str) {
|
|
1959
|
+
let result = [];
|
|
1960
|
+
for (let i = 0; i < str.length; i++)
|
|
1961
|
+
result.push(1 << +str[i]);
|
|
1962
|
+
return result;
|
|
1963
|
+
}
|
|
1964
|
+
// Character types for codepoints 0 to 0xf8
|
|
1965
|
+
const LowTypes = /*@__PURE__*/dec("88888888888888888888888888888888888666888888787833333333337888888000000000000000000000000008888880000000000000000000000000088888888888888888888888888888888888887866668888088888663380888308888800000000000000000000000800000000000000000000000000000008");
|
|
1966
|
+
// Character types for codepoints 0x600 to 0x6f9
|
|
1967
|
+
const ArabicTypes = /*@__PURE__*/dec("4444448826627288999999999992222222222222222222222222222222222222222222222229999999999999999999994444444444644222822222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222999999949999999229989999223333333333");
|
|
1968
|
+
const Brackets = /*@__PURE__*/Object.create(null), BracketStack = [];
|
|
1969
|
+
// There's a lot more in
|
|
1970
|
+
// https://www.unicode.org/Public/UCD/latest/ucd/BidiBrackets.txt,
|
|
1971
|
+
// which are left out to keep code size down.
|
|
1972
|
+
for (let p of ["()", "[]", "{}"]) {
|
|
1973
|
+
let l = /*@__PURE__*/p.charCodeAt(0), r = /*@__PURE__*/p.charCodeAt(1);
|
|
1974
|
+
Brackets[l] = r;
|
|
1975
|
+
Brackets[r] = -l;
|
|
1976
|
+
}
|
|
1977
|
+
function charType(ch) {
|
|
1978
|
+
return ch <= 0xf7 ? LowTypes[ch] :
|
|
1979
|
+
0x590 <= ch && ch <= 0x5f4 ? 2 /* R */ :
|
|
1980
|
+
0x600 <= ch && ch <= 0x6f9 ? ArabicTypes[ch - 0x600] :
|
|
1981
|
+
0x6ee <= ch && ch <= 0x8ac ? 4 /* AL */ :
|
|
1982
|
+
0x2000 <= ch && ch <= 0x200b ? 256 /* NI */ :
|
|
1983
|
+
ch == 0x200c ? 256 /* NI */ : 1 /* L */;
|
|
1984
|
+
}
|
|
1985
|
+
const BidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
|
|
1986
|
+
/**
|
|
1987
|
+
Represents a contiguous range of text that has a single direction
|
|
1988
|
+
(as in left-to-right or right-to-left).
|
|
1989
|
+
*/
|
|
1990
|
+
class BidiSpan {
|
|
1991
|
+
/**
|
|
1992
|
+
@internal
|
|
1993
|
+
*/
|
|
1994
|
+
constructor(
|
|
1995
|
+
/**
|
|
1996
|
+
The start of the span (relative to the start of the line).
|
|
1997
|
+
*/
|
|
1998
|
+
from,
|
|
1999
|
+
/**
|
|
2000
|
+
The end of the span.
|
|
2001
|
+
*/
|
|
2002
|
+
to,
|
|
2003
|
+
/**
|
|
2004
|
+
The ["bidi
|
|
2005
|
+
level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
|
|
2006
|
+
of the span (in this context, 0 means
|
|
2007
|
+
left-to-right, 1 means right-to-left, 2 means left-to-right
|
|
2008
|
+
number inside right-to-left text).
|
|
2009
|
+
*/
|
|
2010
|
+
level) {
|
|
2011
|
+
this.from = from;
|
|
2012
|
+
this.to = to;
|
|
2013
|
+
this.level = level;
|
|
1949
2014
|
}
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
2015
|
+
/**
|
|
2016
|
+
The direction of this span.
|
|
2017
|
+
*/
|
|
2018
|
+
get dir() { return this.level % 2 ? RTL : LTR; }
|
|
2019
|
+
/**
|
|
2020
|
+
@internal
|
|
2021
|
+
*/
|
|
2022
|
+
side(end, dir) { return (this.dir == dir) == end ? this.to : this.from; }
|
|
2023
|
+
/**
|
|
2024
|
+
@internal
|
|
2025
|
+
*/
|
|
2026
|
+
static find(order, index, level, assoc) {
|
|
2027
|
+
let maybe = -1;
|
|
2028
|
+
for (let i = 0; i < order.length; i++) {
|
|
2029
|
+
let span = order[i];
|
|
2030
|
+
if (span.from <= index && span.to >= index) {
|
|
2031
|
+
if (span.level == level)
|
|
2032
|
+
return i;
|
|
2033
|
+
// When multiple spans match, if assoc != 0, take the one that
|
|
2034
|
+
// covers that side, otherwise take the one with the minimum
|
|
2035
|
+
// level.
|
|
2036
|
+
if (maybe < 0 || (assoc != 0 ? (assoc < 0 ? span.from < index : span.to > index) : order[maybe].level > span.level))
|
|
2037
|
+
maybe = i;
|
|
1966
2038
|
}
|
|
1967
2039
|
}
|
|
1968
|
-
if (
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
this.compositionDeco = computeCompositionDeco(this.view, update.changes);
|
|
1972
|
-
// When the DOM nodes around the selection are moved to another
|
|
1973
|
-
// parent, Chrome sometimes reports a different selection through
|
|
1974
|
-
// getSelection than the one that it actually shows to the user.
|
|
1975
|
-
// This forces a selection update when lines are joined to work
|
|
1976
|
-
// around that. Issue #54
|
|
1977
|
-
if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
|
|
1978
|
-
update.state.doc.lines != update.startState.doc.lines)
|
|
1979
|
-
this.forceSelection = true;
|
|
1980
|
-
let prevDeco = this.decorations, deco = this.updateDeco();
|
|
1981
|
-
let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
|
|
1982
|
-
changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
|
|
1983
|
-
if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
|
|
1984
|
-
return false;
|
|
1985
|
-
}
|
|
1986
|
-
else {
|
|
1987
|
-
this.updateInner(changedRanges, deco, update.startState.doc.length);
|
|
1988
|
-
if (update.transactions.length)
|
|
1989
|
-
this.lastUpdate = Date.now();
|
|
1990
|
-
return true;
|
|
1991
|
-
}
|
|
2040
|
+
if (maybe < 0)
|
|
2041
|
+
throw new RangeError("Index out of range");
|
|
2042
|
+
return maybe;
|
|
1992
2043
|
}
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2044
|
+
}
|
|
2045
|
+
// Reused array of character types
|
|
2046
|
+
const types = [];
|
|
2047
|
+
function computeOrder(line, direction) {
|
|
2048
|
+
let len = line.length, outerType = direction == LTR ? 1 /* L */ : 2 /* R */, oppositeType = direction == LTR ? 2 /* R */ : 1 /* L */;
|
|
2049
|
+
if (!line || outerType == 1 /* L */ && !BidiRE.test(line))
|
|
2050
|
+
return trivialOrder(len);
|
|
2051
|
+
// W1. Examine each non-spacing mark (NSM) in the level run, and
|
|
2052
|
+
// change the type of the NSM to the type of the previous
|
|
2053
|
+
// character. If the NSM is at the start of the level run, it will
|
|
2054
|
+
// get the type of sor.
|
|
2055
|
+
// W2. Search backwards from each instance of a European number
|
|
2056
|
+
// until the first strong type (R, L, AL, or sor) is found. If an
|
|
2057
|
+
// AL is found, change the type of the European number to Arabic
|
|
2058
|
+
// number.
|
|
2059
|
+
// W3. Change all ALs to R.
|
|
2060
|
+
// (Left after this: L, R, EN, AN, ET, CS, NI)
|
|
2061
|
+
for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
|
|
2062
|
+
let type = charType(line.charCodeAt(i));
|
|
2063
|
+
if (type == 512 /* NSM */)
|
|
2064
|
+
type = prev;
|
|
2065
|
+
else if (type == 8 /* EN */ && prevStrong == 4 /* AL */)
|
|
2066
|
+
type = 16 /* AN */;
|
|
2067
|
+
types[i] = type == 4 /* AL */ ? 2 /* R */ : type;
|
|
2068
|
+
if (type & 7 /* Strong */)
|
|
2069
|
+
prevStrong = type;
|
|
2070
|
+
prev = type;
|
|
2071
|
+
}
|
|
2072
|
+
// W5. A sequence of European terminators adjacent to European
|
|
2073
|
+
// numbers changes to all European numbers.
|
|
2074
|
+
// W6. Otherwise, separators and terminators change to Other
|
|
2075
|
+
// Neutral.
|
|
2076
|
+
// W7. Search backwards from each instance of a European number
|
|
2077
|
+
// until the first strong type (R, L, or sor) is found. If an L is
|
|
2078
|
+
// found, then change the type of the European number to L.
|
|
2079
|
+
// (Left after this: L, R, EN+AN, NI)
|
|
2080
|
+
for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
|
|
2081
|
+
let type = types[i];
|
|
2082
|
+
if (type == 128 /* CS */) {
|
|
2083
|
+
if (i < len - 1 && prev == types[i + 1] && (prev & 24 /* Num */))
|
|
2084
|
+
type = types[i] = prev;
|
|
2085
|
+
else
|
|
2086
|
+
types[i] = 256 /* NI */;
|
|
1998
2087
|
}
|
|
1999
|
-
else {
|
|
2000
|
-
|
|
2088
|
+
else if (type == 64 /* ET */) {
|
|
2089
|
+
let end = i + 1;
|
|
2090
|
+
while (end < len && types[end] == 64 /* ET */)
|
|
2091
|
+
end++;
|
|
2092
|
+
let replace = (i && prev == 8 /* EN */) || (end < len && types[end] == 8 /* EN */) ? (prevStrong == 1 /* L */ ? 1 /* L */ : 8 /* EN */) : 256 /* NI */;
|
|
2093
|
+
for (let j = i; j < end; j++)
|
|
2094
|
+
types[j] = replace;
|
|
2095
|
+
i = end - 1;
|
|
2001
2096
|
}
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
// update
|
|
2005
|
-
updateInner(changes, deco, oldLength) {
|
|
2006
|
-
this.view.viewState.mustMeasureContent = true;
|
|
2007
|
-
this.updateChildren(changes, deco, oldLength);
|
|
2008
|
-
let { observer } = this.view;
|
|
2009
|
-
observer.ignore(() => {
|
|
2010
|
-
// Lock the height during redrawing, since Chrome sometimes
|
|
2011
|
-
// messes with the scroll position during DOM mutation (though
|
|
2012
|
-
// no relayout is triggered and I cannot imagine how it can
|
|
2013
|
-
// recompute the scroll position without a layout)
|
|
2014
|
-
this.dom.style.height = this.view.viewState.contentHeight + "px";
|
|
2015
|
-
this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
|
|
2016
|
-
// Chrome will sometimes, when DOM mutations occur directly
|
|
2017
|
-
// around the selection, get confused and report a different
|
|
2018
|
-
// selection from the one it displays (issue #218). This tries
|
|
2019
|
-
// to detect that situation.
|
|
2020
|
-
let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
|
|
2021
|
-
this.sync(track);
|
|
2022
|
-
this.dirty = 0 /* Not */;
|
|
2023
|
-
if (track && (track.written || observer.selectionRange.focusNode != track.node))
|
|
2024
|
-
this.forceSelection = true;
|
|
2025
|
-
this.dom.style.height = "";
|
|
2026
|
-
});
|
|
2027
|
-
let gaps = [];
|
|
2028
|
-
if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
|
|
2029
|
-
for (let child of this.children)
|
|
2030
|
-
if (child instanceof BlockWidgetView && child.widget instanceof BlockGapWidget)
|
|
2031
|
-
gaps.push(child.dom);
|
|
2032
|
-
observer.updateGaps(gaps);
|
|
2033
|
-
}
|
|
2034
|
-
updateChildren(changes, deco, oldLength) {
|
|
2035
|
-
let cursor = this.childCursor(oldLength);
|
|
2036
|
-
for (let i = changes.length - 1;; i--) {
|
|
2037
|
-
let next = i >= 0 ? changes[i] : null;
|
|
2038
|
-
if (!next)
|
|
2039
|
-
break;
|
|
2040
|
-
let { fromA, toA, fromB, toB } = next;
|
|
2041
|
-
let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
|
|
2042
|
-
let { i: toI, off: toOff } = cursor.findPos(toA, 1);
|
|
2043
|
-
let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
|
|
2044
|
-
this.replaceRange(fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
|
|
2097
|
+
else if (type == 8 /* EN */ && prevStrong == 1 /* L */) {
|
|
2098
|
+
types[i] = 1 /* L */;
|
|
2045
2099
|
}
|
|
2100
|
+
prev = type;
|
|
2101
|
+
if (type & 7 /* Strong */)
|
|
2102
|
+
prevStrong = type;
|
|
2046
2103
|
}
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2104
|
+
// N0. Process bracket pairs in an isolating run sequence
|
|
2105
|
+
// sequentially in the logical order of the text positions of the
|
|
2106
|
+
// opening paired brackets using the logic given below. Within this
|
|
2107
|
+
// scope, bidirectional types EN and AN are treated as R.
|
|
2108
|
+
for (let i = 0, sI = 0, context = 0, ch, br, type; i < len; i++) {
|
|
2109
|
+
// Keeps [startIndex, type, strongSeen] triples for each open
|
|
2110
|
+
// bracket on BracketStack.
|
|
2111
|
+
if (br = Brackets[ch = line.charCodeAt(i)]) {
|
|
2112
|
+
if (br < 0) { // Closing bracket
|
|
2113
|
+
for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
|
|
2114
|
+
if (BracketStack[sJ + 1] == -br) {
|
|
2115
|
+
let flags = BracketStack[sJ + 2];
|
|
2116
|
+
let type = (flags & 2 /* EmbedInside */) ? outerType :
|
|
2117
|
+
!(flags & 4 /* OppositeInside */) ? 0 :
|
|
2118
|
+
(flags & 1 /* OppositeBefore */) ? oppositeType : outerType;
|
|
2119
|
+
if (type)
|
|
2120
|
+
types[i] = types[BracketStack[sJ]] = type;
|
|
2121
|
+
sI = sJ;
|
|
2122
|
+
break;
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2062
2125
|
}
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
if (!breakAtEnd && last && after.merge(0, toOff, last, true, 0, openEnd)) {
|
|
2066
|
-
content[content.length - 1] = after;
|
|
2126
|
+
else if (BracketStack.length == 189 /* MaxDepth */) {
|
|
2127
|
+
break;
|
|
2067
2128
|
}
|
|
2068
2129
|
else {
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
after.merge(0, toOff, null, false, 0, openEnd);
|
|
2073
|
-
content.push(after);
|
|
2130
|
+
BracketStack[sI++] = i;
|
|
2131
|
+
BracketStack[sI++] = ch;
|
|
2132
|
+
BracketStack[sI++] = context;
|
|
2074
2133
|
}
|
|
2075
2134
|
}
|
|
2076
|
-
else if (
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2135
|
+
else if ((type = types[i]) == 2 /* R */ || type == 1 /* L */) {
|
|
2136
|
+
let embed = type == outerType;
|
|
2137
|
+
context = embed ? 0 : 1 /* OppositeBefore */;
|
|
2138
|
+
for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
|
|
2139
|
+
let cur = BracketStack[sJ + 2];
|
|
2140
|
+
if (cur & 2 /* EmbedInside */)
|
|
2141
|
+
break;
|
|
2142
|
+
if (embed) {
|
|
2143
|
+
BracketStack[sJ + 2] |= 2 /* EmbedInside */;
|
|
2144
|
+
}
|
|
2145
|
+
else {
|
|
2146
|
+
if (cur & 4 /* OppositeInside */)
|
|
2147
|
+
break;
|
|
2148
|
+
BracketStack[sJ + 2] |= 4 /* OppositeInside */;
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2083
2151
|
}
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2152
|
+
}
|
|
2153
|
+
// N1. A sequence of neutrals takes the direction of the
|
|
2154
|
+
// surrounding strong text if the text on both sides has the same
|
|
2155
|
+
// direction. European and Arabic numbers act as if they were R in
|
|
2156
|
+
// terms of their influence on neutrals. Start-of-level-run (sor)
|
|
2157
|
+
// and end-of-level-run (eor) are used at level run boundaries.
|
|
2158
|
+
// N2. Any remaining neutrals take the embedding direction.
|
|
2159
|
+
// (Left after this: L, R, EN+AN)
|
|
2160
|
+
for (let i = 0; i < len; i++) {
|
|
2161
|
+
if (types[i] == 256 /* NI */) {
|
|
2162
|
+
let end = i + 1;
|
|
2163
|
+
while (end < len && types[end] == 256 /* NI */)
|
|
2164
|
+
end++;
|
|
2165
|
+
let beforeL = (i ? types[i - 1] : outerType) == 1 /* L */;
|
|
2166
|
+
let afterL = (end < len ? types[end] : outerType) == 1 /* L */;
|
|
2167
|
+
let replace = beforeL == afterL ? (beforeL ? 1 /* L */ : 2 /* R */) : outerType;
|
|
2168
|
+
for (let j = i; j < end; j++)
|
|
2169
|
+
types[j] = replace;
|
|
2170
|
+
i = end - 1;
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
// Here we depart from the documented algorithm, in order to avoid
|
|
2174
|
+
// building up an actual levels array. Since there are only three
|
|
2175
|
+
// levels (0, 1, 2) in an implementation that doesn't take
|
|
2176
|
+
// explicit embedding into account, we can build up the order on
|
|
2177
|
+
// the fly, without following the level-based algorithm.
|
|
2178
|
+
let order = [];
|
|
2179
|
+
if (outerType == 1 /* L */) {
|
|
2180
|
+
for (let i = 0; i < len;) {
|
|
2181
|
+
let start = i, rtl = types[i++] != 1 /* L */;
|
|
2182
|
+
while (i < len && rtl == (types[i] != 1 /* L */))
|
|
2183
|
+
i++;
|
|
2184
|
+
if (rtl) {
|
|
2185
|
+
for (let j = i; j > start;) {
|
|
2186
|
+
let end = j, l = types[--j] != 2 /* R */;
|
|
2187
|
+
while (j > start && l == (types[j - 1] != 2 /* R */))
|
|
2188
|
+
j--;
|
|
2189
|
+
order.push(new BidiSpan(j, end, l ? 2 : 1));
|
|
2190
|
+
}
|
|
2091
2191
|
}
|
|
2092
|
-
else
|
|
2093
|
-
|
|
2192
|
+
else {
|
|
2193
|
+
order.push(new BidiSpan(start, i, 0));
|
|
2094
2194
|
}
|
|
2095
|
-
fromI++;
|
|
2096
2195
|
}
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
break;
|
|
2196
|
+
}
|
|
2197
|
+
else {
|
|
2198
|
+
for (let i = 0; i < len;) {
|
|
2199
|
+
let start = i, rtl = types[i++] == 2 /* R */;
|
|
2200
|
+
while (i < len && rtl == (types[i] == 2 /* R */))
|
|
2201
|
+
i++;
|
|
2202
|
+
order.push(new BidiSpan(start, i, rtl ? 1 : 2));
|
|
2105
2203
|
}
|
|
2106
|
-
if (fromI < toI || content.length)
|
|
2107
|
-
this.replaceChildren(fromI, toI, content);
|
|
2108
2204
|
}
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
if (browser.gecko && main.empty && betweenUneditable(anchor)) {
|
|
2125
|
-
let dummy = document.createTextNode("");
|
|
2126
|
-
this.view.observer.ignore(() => anchor.node.insertBefore(dummy, anchor.node.childNodes[anchor.offset] || null));
|
|
2127
|
-
anchor = head = new DOMPos(dummy, 0);
|
|
2128
|
-
force = true;
|
|
2129
|
-
}
|
|
2130
|
-
let domSel = this.view.observer.selectionRange;
|
|
2131
|
-
// If the selection is already here, or in an equivalent position, don't touch it
|
|
2132
|
-
if (force || !domSel.focusNode ||
|
|
2133
|
-
!isEquivalentPosition(anchor.node, anchor.offset, domSel.anchorNode, domSel.anchorOffset) ||
|
|
2134
|
-
!isEquivalentPosition(head.node, head.offset, domSel.focusNode, domSel.focusOffset)) {
|
|
2135
|
-
this.view.observer.ignore(() => {
|
|
2136
|
-
// Chrome Android will hide the virtual keyboard when tapping
|
|
2137
|
-
// inside an uneditable node, and not bring it back when we
|
|
2138
|
-
// move the cursor to its proper position. This tries to
|
|
2139
|
-
// restore the keyboard by cycling focus.
|
|
2140
|
-
if (browser.android && browser.chrome && this.dom.contains(domSel.focusNode) && inUneditable(domSel.focusNode, this.dom)) {
|
|
2141
|
-
this.dom.blur();
|
|
2142
|
-
this.dom.focus({ preventScroll: true });
|
|
2143
|
-
}
|
|
2144
|
-
let rawSel = getSelection(this.root);
|
|
2145
|
-
if (main.empty) {
|
|
2146
|
-
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=1612076
|
|
2147
|
-
if (browser.gecko) {
|
|
2148
|
-
let nextTo = nextToUneditable(anchor.node, anchor.offset);
|
|
2149
|
-
if (nextTo && nextTo != (1 /* Before */ | 2 /* After */)) {
|
|
2150
|
-
let text = nearbyTextNode(anchor.node, anchor.offset, nextTo == 1 /* Before */ ? 1 : -1);
|
|
2151
|
-
if (text)
|
|
2152
|
-
anchor = new DOMPos(text, nextTo == 1 /* Before */ ? 0 : text.nodeValue.length);
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
rawSel.collapse(anchor.node, anchor.offset);
|
|
2156
|
-
if (main.bidiLevel != null && domSel.cursorBidiLevel != null)
|
|
2157
|
-
domSel.cursorBidiLevel = main.bidiLevel;
|
|
2158
|
-
}
|
|
2159
|
-
else if (rawSel.extend) {
|
|
2160
|
-
// Selection.extend can be used to create an 'inverted' selection
|
|
2161
|
-
// (one where the focus is before the anchor), but not all
|
|
2162
|
-
// browsers support it yet.
|
|
2163
|
-
rawSel.collapse(anchor.node, anchor.offset);
|
|
2164
|
-
rawSel.extend(head.node, head.offset);
|
|
2165
|
-
}
|
|
2166
|
-
else {
|
|
2167
|
-
// Primitive (IE) way
|
|
2168
|
-
let range = document.createRange();
|
|
2169
|
-
if (main.anchor > main.head)
|
|
2170
|
-
[anchor, head] = [head, anchor];
|
|
2171
|
-
range.setEnd(head.node, head.offset);
|
|
2172
|
-
range.setStart(anchor.node, anchor.offset);
|
|
2173
|
-
rawSel.removeAllRanges();
|
|
2174
|
-
rawSel.addRange(range);
|
|
2175
|
-
}
|
|
2176
|
-
});
|
|
2177
|
-
this.view.observer.setSelectionRange(anchor, head);
|
|
2205
|
+
return order;
|
|
2206
|
+
}
|
|
2207
|
+
function trivialOrder(length) {
|
|
2208
|
+
return [new BidiSpan(0, length, 0)];
|
|
2209
|
+
}
|
|
2210
|
+
let movedOver = "";
|
|
2211
|
+
function moveVisually(line, order, dir, start, forward) {
|
|
2212
|
+
var _a;
|
|
2213
|
+
let startIndex = start.head - line.from, spanI = -1;
|
|
2214
|
+
if (startIndex == 0) {
|
|
2215
|
+
if (!forward || !line.length)
|
|
2216
|
+
return null;
|
|
2217
|
+
if (order[0].level != dir) {
|
|
2218
|
+
startIndex = order[0].side(false, dir);
|
|
2219
|
+
spanI = 0;
|
|
2178
2220
|
}
|
|
2179
|
-
this.impreciseAnchor = anchor.precise ? null : new DOMPos(domSel.anchorNode, domSel.anchorOffset);
|
|
2180
|
-
this.impreciseHead = head.precise ? null : new DOMPos(domSel.focusNode, domSel.focusOffset);
|
|
2181
|
-
}
|
|
2182
|
-
enforceCursorAssoc() {
|
|
2183
|
-
if (this.view.composing)
|
|
2184
|
-
return;
|
|
2185
|
-
let cursor = this.view.state.selection.main;
|
|
2186
|
-
let sel = getSelection(this.root);
|
|
2187
|
-
if (!cursor.empty || !cursor.assoc || !sel.modify)
|
|
2188
|
-
return;
|
|
2189
|
-
let line = LineView.find(this, cursor.head);
|
|
2190
|
-
if (!line)
|
|
2191
|
-
return;
|
|
2192
|
-
let lineStart = line.posAtStart;
|
|
2193
|
-
if (cursor.head == lineStart || cursor.head == lineStart + line.length)
|
|
2194
|
-
return;
|
|
2195
|
-
let before = this.coordsAt(cursor.head, -1), after = this.coordsAt(cursor.head, 1);
|
|
2196
|
-
if (!before || !after || before.bottom > after.top)
|
|
2197
|
-
return;
|
|
2198
|
-
let dom = this.domAtPos(cursor.head + cursor.assoc);
|
|
2199
|
-
sel.collapse(dom.node, dom.offset);
|
|
2200
|
-
sel.modify("move", cursor.assoc < 0 ? "forward" : "backward", "lineboundary");
|
|
2201
|
-
}
|
|
2202
|
-
mayControlSelection() {
|
|
2203
|
-
return this.view.state.facet(editable) ? this.root.activeElement == this.dom
|
|
2204
|
-
: hasSelection(this.dom, this.view.observer.selectionRange);
|
|
2205
2221
|
}
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2222
|
+
else if (startIndex == line.length) {
|
|
2223
|
+
if (forward)
|
|
2224
|
+
return null;
|
|
2225
|
+
let last = order[order.length - 1];
|
|
2226
|
+
if (last.level != dir) {
|
|
2227
|
+
startIndex = last.side(true, dir);
|
|
2228
|
+
spanI = order.length - 1;
|
|
2212
2229
|
}
|
|
2213
|
-
return null;
|
|
2214
|
-
}
|
|
2215
|
-
posFromDOM(node, offset) {
|
|
2216
|
-
let view = this.nearest(node);
|
|
2217
|
-
if (!view)
|
|
2218
|
-
throw new RangeError("Trying to find position for a DOM position outside of the document");
|
|
2219
|
-
return view.localPosFromDOM(node, offset) + view.posAtStart;
|
|
2220
2230
|
}
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
off = 0;
|
|
2229
|
-
}
|
|
2230
|
-
return this.children[i].domAtPos(off);
|
|
2231
|
+
if (spanI < 0)
|
|
2232
|
+
spanI = BidiSpan.find(order, startIndex, (_a = start.bidiLevel) !== null && _a !== void 0 ? _a : -1, start.assoc);
|
|
2233
|
+
let span = order[spanI];
|
|
2234
|
+
// End of span. (But not end of line--that was checked for above.)
|
|
2235
|
+
if (startIndex == span.side(forward, dir)) {
|
|
2236
|
+
span = order[spanI += forward ? 1 : -1];
|
|
2237
|
+
startIndex = span.side(!forward, dir);
|
|
2231
2238
|
}
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2239
|
+
let indexForward = forward == (span.dir == dir);
|
|
2240
|
+
let nextIndex = findClusterBreak(line.text, startIndex, indexForward);
|
|
2241
|
+
movedOver = line.text.slice(Math.min(startIndex, nextIndex), Math.max(startIndex, nextIndex));
|
|
2242
|
+
if (nextIndex != span.side(forward, dir))
|
|
2243
|
+
return EditorSelection.cursor(nextIndex + line.from, indexForward ? -1 : 1, span.level);
|
|
2244
|
+
let nextSpan = spanI == (forward ? order.length - 1 : 0) ? null : order[spanI + (forward ? 1 : -1)];
|
|
2245
|
+
if (!nextSpan && span.level != dir)
|
|
2246
|
+
return EditorSelection.cursor(forward ? line.to : line.from, forward ? -1 : 1, dir);
|
|
2247
|
+
if (nextSpan && nextSpan.level < span.level)
|
|
2248
|
+
return EditorSelection.cursor(nextSpan.side(!forward, dir) + line.from, forward ? 1 : -1, nextSpan.level);
|
|
2249
|
+
return EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
class DOMReader {
|
|
2253
|
+
constructor(points, view) {
|
|
2254
|
+
this.points = points;
|
|
2255
|
+
this.view = view;
|
|
2256
|
+
this.text = "";
|
|
2257
|
+
this.lineBreak = view.state.lineBreak;
|
|
2242
2258
|
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2259
|
+
readRange(start, end) {
|
|
2260
|
+
if (!start)
|
|
2261
|
+
return this;
|
|
2262
|
+
let parent = start.parentNode;
|
|
2263
|
+
for (let cur = start;;) {
|
|
2264
|
+
this.findPointBefore(parent, cur);
|
|
2265
|
+
this.readNode(cur);
|
|
2266
|
+
let next = cur.nextSibling;
|
|
2267
|
+
if (next == end)
|
|
2249
2268
|
break;
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
this.minWidthTo = end;
|
|
2257
|
-
}
|
|
2258
|
-
}
|
|
2259
|
-
pos = end + child.breakAfter;
|
|
2269
|
+
let view = ContentView.get(cur), nextView = ContentView.get(next);
|
|
2270
|
+
if (view && nextView ? view.breakAfter :
|
|
2271
|
+
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
2272
|
+
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
2273
|
+
this.text += this.lineBreak;
|
|
2274
|
+
cur = next;
|
|
2260
2275
|
}
|
|
2261
|
-
|
|
2276
|
+
this.findPointBefore(parent, end);
|
|
2277
|
+
return this;
|
|
2262
2278
|
}
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2279
|
+
readNode(node) {
|
|
2280
|
+
if (node.cmIgnore)
|
|
2281
|
+
return;
|
|
2282
|
+
let view = ContentView.get(node);
|
|
2283
|
+
let fromView = view && view.overrideDOMText;
|
|
2284
|
+
let text;
|
|
2285
|
+
if (fromView != null)
|
|
2286
|
+
text = fromView.sliceString(0, undefined, this.lineBreak);
|
|
2287
|
+
else if (node.nodeType == 3)
|
|
2288
|
+
text = node.nodeValue;
|
|
2289
|
+
else if (node.nodeName == "BR")
|
|
2290
|
+
text = node.nextSibling ? this.lineBreak : "";
|
|
2291
|
+
else if (node.nodeType == 1)
|
|
2292
|
+
this.readRange(node.firstChild, null);
|
|
2293
|
+
if (text != null) {
|
|
2294
|
+
this.findPointIn(node, text.length);
|
|
2295
|
+
this.text += text;
|
|
2296
|
+
// Chrome inserts two newlines when pressing shift-enter at the
|
|
2297
|
+
// end of a line. This drops one of those.
|
|
2298
|
+
if (browser.chrome && this.view.inputState.lastKeyCode == 13 && !node.nextSibling && /\n\n$/.test(this.text))
|
|
2299
|
+
this.text = this.text.slice(0, -1);
|
|
2270
2300
|
}
|
|
2271
|
-
// If no workable line exists, force a layout of a measurable element
|
|
2272
|
-
let dummy = document.createElement("div"), lineHeight, charWidth;
|
|
2273
|
-
dummy.className = "cm-line";
|
|
2274
|
-
dummy.textContent = "abc def ghi jkl mno pqr stu";
|
|
2275
|
-
this.view.observer.ignore(() => {
|
|
2276
|
-
this.dom.appendChild(dummy);
|
|
2277
|
-
let rect = clientRectsFor(dummy.firstChild)[0];
|
|
2278
|
-
lineHeight = dummy.getBoundingClientRect().height;
|
|
2279
|
-
charWidth = rect ? rect.width / 27 : 7;
|
|
2280
|
-
dummy.remove();
|
|
2281
|
-
});
|
|
2282
|
-
return { lineHeight, charWidth };
|
|
2283
2301
|
}
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
let i = this.children.length;
|
|
2289
|
-
if (i)
|
|
2290
|
-
pos -= this.children[--i].length;
|
|
2291
|
-
return new ChildCursor(this.children, pos, i);
|
|
2302
|
+
findPointBefore(node, next) {
|
|
2303
|
+
for (let point of this.points)
|
|
2304
|
+
if (point.node == node && node.childNodes[point.offset] == next)
|
|
2305
|
+
point.pos = this.text.length;
|
|
2292
2306
|
}
|
|
2293
|
-
|
|
2294
|
-
let
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
let end = next ? next.from - 1 : this.length;
|
|
2298
|
-
if (end > pos) {
|
|
2299
|
-
let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
|
|
2300
|
-
deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
|
|
2301
|
-
}
|
|
2302
|
-
if (!next)
|
|
2303
|
-
break;
|
|
2304
|
-
pos = next.to + 1;
|
|
2305
|
-
}
|
|
2306
|
-
return Decoration.set(deco);
|
|
2307
|
-
}
|
|
2308
|
-
updateDeco() {
|
|
2309
|
-
return this.decorations = [
|
|
2310
|
-
...this.view.pluginField(PluginField.decorations),
|
|
2311
|
-
...this.view.state.facet(decorations),
|
|
2312
|
-
this.compositionDeco,
|
|
2313
|
-
this.computeBlockGapDeco(),
|
|
2314
|
-
this.view.viewState.lineGapDeco
|
|
2315
|
-
];
|
|
2316
|
-
}
|
|
2317
|
-
scrollIntoView({ range, center }) {
|
|
2318
|
-
let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
|
|
2319
|
-
if (!rect)
|
|
2320
|
-
return;
|
|
2321
|
-
if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
|
|
2322
|
-
rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
|
|
2323
|
-
right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
|
|
2324
|
-
let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
|
|
2325
|
-
for (let margins of this.view.pluginField(PluginField.scrollMargins))
|
|
2326
|
-
if (margins) {
|
|
2327
|
-
let { left, right, top, bottom } = margins;
|
|
2328
|
-
if (left != null)
|
|
2329
|
-
mLeft = Math.max(mLeft, left);
|
|
2330
|
-
if (right != null)
|
|
2331
|
-
mRight = Math.max(mRight, right);
|
|
2332
|
-
if (top != null)
|
|
2333
|
-
mTop = Math.max(mTop, top);
|
|
2334
|
-
if (bottom != null)
|
|
2335
|
-
mBottom = Math.max(mBottom, bottom);
|
|
2336
|
-
}
|
|
2337
|
-
scrollRectIntoView(this.view.scrollDOM, {
|
|
2338
|
-
left: rect.left - mLeft, top: rect.top - mTop,
|
|
2339
|
-
right: rect.right + mRight, bottom: rect.bottom + mBottom
|
|
2340
|
-
}, range.head < range.anchor ? -1 : 1, center);
|
|
2307
|
+
findPointIn(node, maxLen) {
|
|
2308
|
+
for (let point of this.points)
|
|
2309
|
+
if (point.node == node)
|
|
2310
|
+
point.pos = this.text.length + Math.min(point.offset, maxLen);
|
|
2341
2311
|
}
|
|
2342
2312
|
}
|
|
2343
|
-
function
|
|
2344
|
-
return
|
|
2345
|
-
(pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
|
|
2346
|
-
(pos.offset == pos.node.childNodes.length || pos.node.childNodes[pos.offset].contentEditable == "false");
|
|
2347
|
-
}
|
|
2348
|
-
class BlockGapWidget extends WidgetType {
|
|
2349
|
-
constructor(height) {
|
|
2350
|
-
super();
|
|
2351
|
-
this.height = height;
|
|
2352
|
-
}
|
|
2353
|
-
toDOM() {
|
|
2354
|
-
let elt = document.createElement("div");
|
|
2355
|
-
this.updateDOM(elt);
|
|
2356
|
-
return elt;
|
|
2357
|
-
}
|
|
2358
|
-
eq(other) { return other.height == this.height; }
|
|
2359
|
-
updateDOM(elt) {
|
|
2360
|
-
elt.style.height = this.height + "px";
|
|
2361
|
-
return true;
|
|
2362
|
-
}
|
|
2363
|
-
get estimatedHeight() { return this.height; }
|
|
2313
|
+
function isBlockElement(node) {
|
|
2314
|
+
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
2364
2315
|
}
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
let cView = view.docView.nearest(textNode);
|
|
2371
|
-
let from, to, topNode = textNode;
|
|
2372
|
-
if (cView instanceof InlineView) {
|
|
2373
|
-
while (cView.parent instanceof InlineView)
|
|
2374
|
-
cView = cView.parent;
|
|
2375
|
-
from = cView.posAtStart;
|
|
2376
|
-
to = from + cView.length;
|
|
2377
|
-
topNode = cView.dom;
|
|
2378
|
-
}
|
|
2379
|
-
else if (cView instanceof LineView) {
|
|
2380
|
-
while (topNode.parentNode != cView.dom)
|
|
2381
|
-
topNode = topNode.parentNode;
|
|
2382
|
-
let prev = topNode.previousSibling;
|
|
2383
|
-
while (prev && !ContentView.get(prev))
|
|
2384
|
-
prev = prev.previousSibling;
|
|
2385
|
-
from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
|
|
2386
|
-
}
|
|
2387
|
-
else {
|
|
2388
|
-
return Decoration.none;
|
|
2389
|
-
}
|
|
2390
|
-
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
2391
|
-
let text = textNode.nodeValue, { state } = view;
|
|
2392
|
-
if (newTo - newFrom < text.length) {
|
|
2393
|
-
if (state.sliceDoc(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
|
|
2394
|
-
newTo = newFrom + text.length;
|
|
2395
|
-
else if (state.sliceDoc(Math.max(0, newTo - text.length), newTo) == text)
|
|
2396
|
-
newFrom = newTo - text.length;
|
|
2397
|
-
else
|
|
2398
|
-
return Decoration.none;
|
|
2399
|
-
}
|
|
2400
|
-
else if (state.sliceDoc(newFrom, newTo) != text) {
|
|
2401
|
-
return Decoration.none;
|
|
2316
|
+
class DOMPoint {
|
|
2317
|
+
constructor(node, offset) {
|
|
2318
|
+
this.node = node;
|
|
2319
|
+
this.offset = offset;
|
|
2320
|
+
this.pos = -1;
|
|
2402
2321
|
}
|
|
2403
|
-
return Decoration.set(Decoration.replace({ widget: new CompositionWidget(topNode, textNode) }).range(newFrom, newTo));
|
|
2404
2322
|
}
|
|
2405
|
-
|
|
2406
|
-
|
|
2323
|
+
|
|
2324
|
+
class DocView extends ContentView {
|
|
2325
|
+
constructor(view) {
|
|
2407
2326
|
super();
|
|
2408
|
-
this.
|
|
2409
|
-
this.
|
|
2327
|
+
this.view = view;
|
|
2328
|
+
this.compositionDeco = Decoration.none;
|
|
2329
|
+
this.decorations = [];
|
|
2330
|
+
// Track a minimum width for the editor. When measuring sizes in
|
|
2331
|
+
// measureVisibleLineHeights, this is updated to point at the width
|
|
2332
|
+
// of a given element and its extent in the document. When a change
|
|
2333
|
+
// happens in that range, these are reset. That way, once we've seen
|
|
2334
|
+
// a line/element of a given length, we keep the editor wide enough
|
|
2335
|
+
// to fit at least that element, until it is changed, at which point
|
|
2336
|
+
// we forget it again.
|
|
2337
|
+
this.minWidth = 0;
|
|
2338
|
+
this.minWidthFrom = 0;
|
|
2339
|
+
this.minWidthTo = 0;
|
|
2340
|
+
// Track whether the DOM selection was set in a lossy way, so that
|
|
2341
|
+
// we don't mess it up when reading it back it
|
|
2342
|
+
this.impreciseAnchor = null;
|
|
2343
|
+
this.impreciseHead = null;
|
|
2344
|
+
this.forceSelection = false;
|
|
2345
|
+
// Used by the resize observer to ignore resizes that we caused
|
|
2346
|
+
// ourselves
|
|
2347
|
+
this.lastUpdate = Date.now();
|
|
2348
|
+
this.setDOM(view.contentDOM);
|
|
2349
|
+
this.children = [new LineView];
|
|
2350
|
+
this.children[0].setParent(this);
|
|
2351
|
+
this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], this.updateDeco(), 0);
|
|
2410
2352
|
}
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
if (
|
|
2421
|
-
|
|
2422
|
-
|
|
2353
|
+
get root() { return this.view.root; }
|
|
2354
|
+
get editorView() { return this.view; }
|
|
2355
|
+
get length() { return this.view.state.doc.length; }
|
|
2356
|
+
// Update the document view to a given state. scrollIntoView can be
|
|
2357
|
+
// used as a hint to compute a new viewport that includes that
|
|
2358
|
+
// position, if we know the editor is going to scroll that position
|
|
2359
|
+
// into view.
|
|
2360
|
+
update(update) {
|
|
2361
|
+
let changedRanges = update.changedRanges;
|
|
2362
|
+
if (this.minWidth > 0 && changedRanges.length) {
|
|
2363
|
+
if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
|
|
2364
|
+
this.minWidth = this.minWidthFrom = this.minWidthTo = 0;
|
|
2365
|
+
}
|
|
2366
|
+
else {
|
|
2367
|
+
this.minWidthFrom = update.changes.mapPos(this.minWidthFrom, 1);
|
|
2368
|
+
this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
|
|
2369
|
+
}
|
|
2423
2370
|
}
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2371
|
+
if (this.view.inputState.composing < 0)
|
|
2372
|
+
this.compositionDeco = Decoration.none;
|
|
2373
|
+
else if (update.transactions.length || this.dirty)
|
|
2374
|
+
this.compositionDeco = computeCompositionDeco(this.view, update.changes);
|
|
2375
|
+
// When the DOM nodes around the selection are moved to another
|
|
2376
|
+
// parent, Chrome sometimes reports a different selection through
|
|
2377
|
+
// getSelection than the one that it actually shows to the user.
|
|
2378
|
+
// This forces a selection update when lines are joined to work
|
|
2379
|
+
// around that. Issue #54
|
|
2380
|
+
if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
|
|
2381
|
+
update.state.doc.lines != update.startState.doc.lines)
|
|
2382
|
+
this.forceSelection = true;
|
|
2383
|
+
let prevDeco = this.decorations, deco = this.updateDeco();
|
|
2384
|
+
let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
|
|
2385
|
+
changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
|
|
2386
|
+
if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
|
|
2387
|
+
return false;
|
|
2427
2388
|
}
|
|
2428
2389
|
else {
|
|
2429
|
-
|
|
2390
|
+
this.updateInner(changedRanges, deco, update.startState.doc.length);
|
|
2391
|
+
if (update.transactions.length)
|
|
2392
|
+
this.lastUpdate = Date.now();
|
|
2393
|
+
return true;
|
|
2430
2394
|
}
|
|
2431
2395
|
}
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
constructor() {
|
|
2441
|
-
this.changes = [];
|
|
2442
|
-
}
|
|
2443
|
-
compareRange(from, to) { addRange(from, to, this.changes); }
|
|
2444
|
-
comparePoint(from, to) { addRange(from, to, this.changes); }
|
|
2445
|
-
}
|
|
2446
|
-
function findChangedDeco(a, b, diff) {
|
|
2447
|
-
let comp = new DecorationComparator$1;
|
|
2448
|
-
RangeSet.compare(a, b, diff, comp);
|
|
2449
|
-
return comp.changes;
|
|
2450
|
-
}
|
|
2451
|
-
function inUneditable(node, inside) {
|
|
2452
|
-
for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) {
|
|
2453
|
-
if (cur.nodeType == 1 && cur.contentEditable == 'false') {
|
|
2454
|
-
return true;
|
|
2396
|
+
reset(sel) {
|
|
2397
|
+
if (this.dirty) {
|
|
2398
|
+
this.view.observer.ignore(() => this.view.docView.sync());
|
|
2399
|
+
this.dirty = 0 /* Not */;
|
|
2400
|
+
this.updateSelection(true);
|
|
2401
|
+
}
|
|
2402
|
+
else {
|
|
2403
|
+
this.updateSelection();
|
|
2455
2404
|
}
|
|
2456
2405
|
}
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
const Brackets = /*@__PURE__*/Object.create(null), BracketStack = [];
|
|
2488
|
-
// There's a lot more in
|
|
2489
|
-
// https://www.unicode.org/Public/UCD/latest/ucd/BidiBrackets.txt,
|
|
2490
|
-
// which are left out to keep code size down.
|
|
2491
|
-
for (let p of ["()", "[]", "{}"]) {
|
|
2492
|
-
let l = /*@__PURE__*/p.charCodeAt(0), r = /*@__PURE__*/p.charCodeAt(1);
|
|
2493
|
-
Brackets[l] = r;
|
|
2494
|
-
Brackets[r] = -l;
|
|
2495
|
-
}
|
|
2496
|
-
function charType(ch) {
|
|
2497
|
-
return ch <= 0xf7 ? LowTypes[ch] :
|
|
2498
|
-
0x590 <= ch && ch <= 0x5f4 ? 2 /* R */ :
|
|
2499
|
-
0x600 <= ch && ch <= 0x6f9 ? ArabicTypes[ch - 0x600] :
|
|
2500
|
-
0x6ee <= ch && ch <= 0x8ac ? 4 /* AL */ :
|
|
2501
|
-
0x2000 <= ch && ch <= 0x200b ? 256 /* NI */ :
|
|
2502
|
-
ch == 0x200c ? 256 /* NI */ : 1 /* L */;
|
|
2503
|
-
}
|
|
2504
|
-
const BidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
|
|
2505
|
-
/**
|
|
2506
|
-
Represents a contiguous range of text that has a single direction
|
|
2507
|
-
(as in left-to-right or right-to-left).
|
|
2508
|
-
*/
|
|
2509
|
-
class BidiSpan {
|
|
2510
|
-
/**
|
|
2511
|
-
@internal
|
|
2512
|
-
*/
|
|
2513
|
-
constructor(
|
|
2514
|
-
/**
|
|
2515
|
-
The start of the span (relative to the start of the line).
|
|
2516
|
-
*/
|
|
2517
|
-
from,
|
|
2518
|
-
/**
|
|
2519
|
-
The end of the span.
|
|
2520
|
-
*/
|
|
2521
|
-
to,
|
|
2522
|
-
/**
|
|
2523
|
-
The ["bidi
|
|
2524
|
-
level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
|
|
2525
|
-
of the span (in this context, 0 means
|
|
2526
|
-
left-to-right, 1 means right-to-left, 2 means left-to-right
|
|
2527
|
-
number inside right-to-left text).
|
|
2528
|
-
*/
|
|
2529
|
-
level) {
|
|
2530
|
-
this.from = from;
|
|
2531
|
-
this.to = to;
|
|
2532
|
-
this.level = level;
|
|
2406
|
+
// Used by update and the constructor do perform the actual DOM
|
|
2407
|
+
// update
|
|
2408
|
+
updateInner(changes, deco, oldLength) {
|
|
2409
|
+
this.view.viewState.mustMeasureContent = true;
|
|
2410
|
+
this.updateChildren(changes, deco, oldLength);
|
|
2411
|
+
let { observer } = this.view;
|
|
2412
|
+
observer.ignore(() => {
|
|
2413
|
+
// Lock the height during redrawing, since Chrome sometimes
|
|
2414
|
+
// messes with the scroll position during DOM mutation (though
|
|
2415
|
+
// no relayout is triggered and I cannot imagine how it can
|
|
2416
|
+
// recompute the scroll position without a layout)
|
|
2417
|
+
this.dom.style.height = this.view.viewState.contentHeight + "px";
|
|
2418
|
+
this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
|
|
2419
|
+
// Chrome will sometimes, when DOM mutations occur directly
|
|
2420
|
+
// around the selection, get confused and report a different
|
|
2421
|
+
// selection from the one it displays (issue #218). This tries
|
|
2422
|
+
// to detect that situation.
|
|
2423
|
+
let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
|
|
2424
|
+
this.sync(track);
|
|
2425
|
+
this.dirty = 0 /* Not */;
|
|
2426
|
+
if (track && (track.written || observer.selectionRange.focusNode != track.node))
|
|
2427
|
+
this.forceSelection = true;
|
|
2428
|
+
this.dom.style.height = "";
|
|
2429
|
+
});
|
|
2430
|
+
let gaps = [];
|
|
2431
|
+
if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
|
|
2432
|
+
for (let child of this.children)
|
|
2433
|
+
if (child instanceof BlockWidgetView && child.widget instanceof BlockGapWidget)
|
|
2434
|
+
gaps.push(child.dom);
|
|
2435
|
+
observer.updateGaps(gaps);
|
|
2533
2436
|
}
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
static find(order, index, level, assoc) {
|
|
2546
|
-
let maybe = -1;
|
|
2547
|
-
for (let i = 0; i < order.length; i++) {
|
|
2548
|
-
let span = order[i];
|
|
2549
|
-
if (span.from <= index && span.to >= index) {
|
|
2550
|
-
if (span.level == level)
|
|
2551
|
-
return i;
|
|
2552
|
-
// When multiple spans match, if assoc != 0, take the one that
|
|
2553
|
-
// covers that side, otherwise take the one with the minimum
|
|
2554
|
-
// level.
|
|
2555
|
-
if (maybe < 0 || (assoc != 0 ? (assoc < 0 ? span.from < index : span.to > index) : order[maybe].level > span.level))
|
|
2556
|
-
maybe = i;
|
|
2557
|
-
}
|
|
2437
|
+
updateChildren(changes, deco, oldLength) {
|
|
2438
|
+
let cursor = this.childCursor(oldLength);
|
|
2439
|
+
for (let i = changes.length - 1;; i--) {
|
|
2440
|
+
let next = i >= 0 ? changes[i] : null;
|
|
2441
|
+
if (!next)
|
|
2442
|
+
break;
|
|
2443
|
+
let { fromA, toA, fromB, toB } = next;
|
|
2444
|
+
let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
|
|
2445
|
+
let { i: toI, off: toOff } = cursor.findPos(toA, 1);
|
|
2446
|
+
let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
|
|
2447
|
+
replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
|
|
2558
2448
|
}
|
|
2559
|
-
if (maybe < 0)
|
|
2560
|
-
throw new RangeError("Index out of range");
|
|
2561
|
-
return maybe;
|
|
2562
2449
|
}
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2450
|
+
// Sync the DOM selection to this.state.selection
|
|
2451
|
+
updateSelection(mustRead = false, fromPointer = false) {
|
|
2452
|
+
if (mustRead)
|
|
2453
|
+
this.view.observer.readSelectionRange();
|
|
2454
|
+
if (!(fromPointer || this.mayControlSelection()) ||
|
|
2455
|
+
browser.ios && this.view.inputState.rapidCompositionStart)
|
|
2456
|
+
return;
|
|
2457
|
+
let force = this.forceSelection;
|
|
2458
|
+
this.forceSelection = false;
|
|
2459
|
+
let main = this.view.state.selection.main;
|
|
2460
|
+
// FIXME need to handle the case where the selection falls inside a block range
|
|
2461
|
+
let anchor = this.domAtPos(main.anchor);
|
|
2462
|
+
let head = main.empty ? anchor : this.domAtPos(main.head);
|
|
2463
|
+
// Always reset on Firefox when next to an uneditable node to
|
|
2464
|
+
// avoid invisible cursor bugs (#111)
|
|
2465
|
+
if (browser.gecko && main.empty && betweenUneditable(anchor)) {
|
|
2466
|
+
let dummy = document.createTextNode("");
|
|
2467
|
+
this.view.observer.ignore(() => anchor.node.insertBefore(dummy, anchor.node.childNodes[anchor.offset] || null));
|
|
2468
|
+
anchor = head = new DOMPos(dummy, 0);
|
|
2469
|
+
force = true;
|
|
2470
|
+
}
|
|
2471
|
+
let domSel = this.view.observer.selectionRange;
|
|
2472
|
+
// If the selection is already here, or in an equivalent position, don't touch it
|
|
2473
|
+
if (force || !domSel.focusNode ||
|
|
2474
|
+
!isEquivalentPosition(anchor.node, anchor.offset, domSel.anchorNode, domSel.anchorOffset) ||
|
|
2475
|
+
!isEquivalentPosition(head.node, head.offset, domSel.focusNode, domSel.focusOffset)) {
|
|
2476
|
+
this.view.observer.ignore(() => {
|
|
2477
|
+
// Chrome Android will hide the virtual keyboard when tapping
|
|
2478
|
+
// inside an uneditable node, and not bring it back when we
|
|
2479
|
+
// move the cursor to its proper position. This tries to
|
|
2480
|
+
// restore the keyboard by cycling focus.
|
|
2481
|
+
if (browser.android && browser.chrome && this.dom.contains(domSel.focusNode) && inUneditable(domSel.focusNode, this.dom)) {
|
|
2482
|
+
this.dom.blur();
|
|
2483
|
+
this.dom.focus({ preventScroll: true });
|
|
2484
|
+
}
|
|
2485
|
+
let rawSel = getSelection(this.root);
|
|
2486
|
+
if (main.empty) {
|
|
2487
|
+
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=1612076
|
|
2488
|
+
if (browser.gecko) {
|
|
2489
|
+
let nextTo = nextToUneditable(anchor.node, anchor.offset);
|
|
2490
|
+
if (nextTo && nextTo != (1 /* Before */ | 2 /* After */)) {
|
|
2491
|
+
let text = nearbyTextNode(anchor.node, anchor.offset, nextTo == 1 /* Before */ ? 1 : -1);
|
|
2492
|
+
if (text)
|
|
2493
|
+
anchor = new DOMPos(text, nextTo == 1 /* Before */ ? 0 : text.nodeValue.length);
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
rawSel.collapse(anchor.node, anchor.offset);
|
|
2497
|
+
if (main.bidiLevel != null && domSel.cursorBidiLevel != null)
|
|
2498
|
+
domSel.cursorBidiLevel = main.bidiLevel;
|
|
2499
|
+
}
|
|
2500
|
+
else if (rawSel.extend) {
|
|
2501
|
+
// Selection.extend can be used to create an 'inverted' selection
|
|
2502
|
+
// (one where the focus is before the anchor), but not all
|
|
2503
|
+
// browsers support it yet.
|
|
2504
|
+
rawSel.collapse(anchor.node, anchor.offset);
|
|
2505
|
+
rawSel.extend(head.node, head.offset);
|
|
2506
|
+
}
|
|
2507
|
+
else {
|
|
2508
|
+
// Primitive (IE) way
|
|
2509
|
+
let range = document.createRange();
|
|
2510
|
+
if (main.anchor > main.head)
|
|
2511
|
+
[anchor, head] = [head, anchor];
|
|
2512
|
+
range.setEnd(head.node, head.offset);
|
|
2513
|
+
range.setStart(anchor.node, anchor.offset);
|
|
2514
|
+
rawSel.removeAllRanges();
|
|
2515
|
+
rawSel.addRange(range);
|
|
2516
|
+
}
|
|
2517
|
+
});
|
|
2518
|
+
this.view.observer.setSelectionRange(anchor, head);
|
|
2519
|
+
}
|
|
2520
|
+
this.impreciseAnchor = anchor.precise ? null : new DOMPos(domSel.anchorNode, domSel.anchorOffset);
|
|
2521
|
+
this.impreciseHead = head.precise ? null : new DOMPos(domSel.focusNode, domSel.focusOffset);
|
|
2522
|
+
}
|
|
2523
|
+
enforceCursorAssoc() {
|
|
2524
|
+
if (this.compositionDeco.size)
|
|
2525
|
+
return;
|
|
2526
|
+
let cursor = this.view.state.selection.main;
|
|
2527
|
+
let sel = getSelection(this.root);
|
|
2528
|
+
if (!cursor.empty || !cursor.assoc || !sel.modify)
|
|
2529
|
+
return;
|
|
2530
|
+
let line = LineView.find(this, cursor.head);
|
|
2531
|
+
if (!line)
|
|
2532
|
+
return;
|
|
2533
|
+
let lineStart = line.posAtStart;
|
|
2534
|
+
if (cursor.head == lineStart || cursor.head == lineStart + line.length)
|
|
2535
|
+
return;
|
|
2536
|
+
let before = this.coordsAt(cursor.head, -1), after = this.coordsAt(cursor.head, 1);
|
|
2537
|
+
if (!before || !after || before.bottom > after.top)
|
|
2538
|
+
return;
|
|
2539
|
+
let dom = this.domAtPos(cursor.head + cursor.assoc);
|
|
2540
|
+
sel.collapse(dom.node, dom.offset);
|
|
2541
|
+
sel.modify("move", cursor.assoc < 0 ? "forward" : "backward", "lineboundary");
|
|
2590
2542
|
}
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
if (type == 128 /* CS */) {
|
|
2602
|
-
if (i < len - 1 && prev == types[i + 1] && (prev & 24 /* Num */))
|
|
2603
|
-
type = types[i] = prev;
|
|
2604
|
-
else
|
|
2605
|
-
types[i] = 256 /* NI */;
|
|
2543
|
+
mayControlSelection() {
|
|
2544
|
+
return this.view.state.facet(editable) ? this.root.activeElement == this.dom
|
|
2545
|
+
: hasSelection(this.dom, this.view.observer.selectionRange);
|
|
2546
|
+
}
|
|
2547
|
+
nearest(dom) {
|
|
2548
|
+
for (let cur = dom; cur;) {
|
|
2549
|
+
let domView = ContentView.get(cur);
|
|
2550
|
+
if (domView && domView.rootView == this)
|
|
2551
|
+
return domView;
|
|
2552
|
+
cur = cur.parentNode;
|
|
2606
2553
|
}
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2554
|
+
return null;
|
|
2555
|
+
}
|
|
2556
|
+
posFromDOM(node, offset) {
|
|
2557
|
+
let view = this.nearest(node);
|
|
2558
|
+
if (!view)
|
|
2559
|
+
throw new RangeError("Trying to find position for a DOM position outside of the document");
|
|
2560
|
+
return view.localPosFromDOM(node, offset) + view.posAtStart;
|
|
2561
|
+
}
|
|
2562
|
+
domAtPos(pos) {
|
|
2563
|
+
let { i, off } = this.childCursor().findPos(pos, -1);
|
|
2564
|
+
for (; i < this.children.length - 1;) {
|
|
2565
|
+
let child = this.children[i];
|
|
2566
|
+
if (off < child.length || child instanceof LineView)
|
|
2567
|
+
break;
|
|
2568
|
+
i++;
|
|
2569
|
+
off = 0;
|
|
2615
2570
|
}
|
|
2616
|
-
|
|
2617
|
-
|
|
2571
|
+
return this.children[i].domAtPos(off);
|
|
2572
|
+
}
|
|
2573
|
+
coordsAt(pos, side) {
|
|
2574
|
+
for (let off = this.length, i = this.children.length - 1;; i--) {
|
|
2575
|
+
let child = this.children[i], start = off - child.breakAfter - child.length;
|
|
2576
|
+
if (pos > start ||
|
|
2577
|
+
(pos == start && child.type != BlockType.WidgetBefore && child.type != BlockType.WidgetAfter &&
|
|
2578
|
+
(!i || side == 2 || this.children[i - 1].breakAfter ||
|
|
2579
|
+
(this.children[i - 1].type == BlockType.WidgetBefore && side > -2))))
|
|
2580
|
+
return child.coordsAt(pos - start, side);
|
|
2581
|
+
off = start;
|
|
2618
2582
|
}
|
|
2619
|
-
prev = type;
|
|
2620
|
-
if (type & 7 /* Strong */)
|
|
2621
|
-
prevStrong = type;
|
|
2622
2583
|
}
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2584
|
+
measureVisibleLineHeights() {
|
|
2585
|
+
let result = [], { from, to } = this.view.viewState.viewport;
|
|
2586
|
+
let contentWidth = this.view.contentDOM.clientWidth;
|
|
2587
|
+
let isWider = contentWidth > Math.max(this.view.scrollDOM.clientWidth, this.minWidth) + 1;
|
|
2588
|
+
let widest = -1;
|
|
2589
|
+
for (let pos = 0, i = 0; i < this.children.length; i++) {
|
|
2590
|
+
let child = this.children[i], end = pos + child.length;
|
|
2591
|
+
if (end > to)
|
|
2592
|
+
break;
|
|
2593
|
+
if (pos >= from) {
|
|
2594
|
+
let childRect = child.dom.getBoundingClientRect();
|
|
2595
|
+
result.push(childRect.height);
|
|
2596
|
+
if (isWider) {
|
|
2597
|
+
let last = child.dom.lastChild;
|
|
2598
|
+
let rects = last ? clientRectsFor(last) : [];
|
|
2599
|
+
if (rects.length) {
|
|
2600
|
+
let rect = rects[rects.length - 1];
|
|
2601
|
+
let width = this.view.textDirection == Direction.LTR ? rect.right - childRect.left
|
|
2602
|
+
: childRect.right - rect.left;
|
|
2603
|
+
if (width > widest) {
|
|
2604
|
+
widest = width;
|
|
2605
|
+
this.minWidth = contentWidth;
|
|
2606
|
+
this.minWidthFrom = pos;
|
|
2607
|
+
this.minWidthTo = end;
|
|
2608
|
+
}
|
|
2642
2609
|
}
|
|
2643
2610
|
}
|
|
2644
2611
|
}
|
|
2645
|
-
|
|
2646
|
-
break;
|
|
2647
|
-
}
|
|
2648
|
-
else {
|
|
2649
|
-
BracketStack[sI++] = i;
|
|
2650
|
-
BracketStack[sI++] = ch;
|
|
2651
|
-
BracketStack[sI++] = context;
|
|
2652
|
-
}
|
|
2612
|
+
pos = end + child.breakAfter;
|
|
2653
2613
|
}
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
BracketStack[sJ + 2] |= 2 /* EmbedInside */;
|
|
2663
|
-
}
|
|
2664
|
-
else {
|
|
2665
|
-
if (cur & 4 /* OppositeInside */)
|
|
2666
|
-
break;
|
|
2667
|
-
BracketStack[sJ + 2] |= 4 /* OppositeInside */;
|
|
2668
|
-
}
|
|
2614
|
+
return result;
|
|
2615
|
+
}
|
|
2616
|
+
measureTextSize() {
|
|
2617
|
+
for (let child of this.children) {
|
|
2618
|
+
if (child instanceof LineView) {
|
|
2619
|
+
let measure = child.measureTextSize();
|
|
2620
|
+
if (measure)
|
|
2621
|
+
return measure;
|
|
2669
2622
|
}
|
|
2670
2623
|
}
|
|
2624
|
+
// If no workable line exists, force a layout of a measurable element
|
|
2625
|
+
let dummy = document.createElement("div"), lineHeight, charWidth;
|
|
2626
|
+
dummy.className = "cm-line";
|
|
2627
|
+
dummy.textContent = "abc def ghi jkl mno pqr stu";
|
|
2628
|
+
this.view.observer.ignore(() => {
|
|
2629
|
+
this.dom.appendChild(dummy);
|
|
2630
|
+
let rect = clientRectsFor(dummy.firstChild)[0];
|
|
2631
|
+
lineHeight = dummy.getBoundingClientRect().height;
|
|
2632
|
+
charWidth = rect ? rect.width / 27 : 7;
|
|
2633
|
+
dummy.remove();
|
|
2634
|
+
});
|
|
2635
|
+
return { lineHeight, charWidth };
|
|
2671
2636
|
}
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
if (types[i] == 256 /* NI */) {
|
|
2681
|
-
let end = i + 1;
|
|
2682
|
-
while (end < len && types[end] == 256 /* NI */)
|
|
2683
|
-
end++;
|
|
2684
|
-
let beforeL = (i ? types[i - 1] : outerType) == 1 /* L */;
|
|
2685
|
-
let afterL = (end < len ? types[end] : outerType) == 1 /* L */;
|
|
2686
|
-
let replace = beforeL == afterL ? (beforeL ? 1 /* L */ : 2 /* R */) : outerType;
|
|
2687
|
-
for (let j = i; j < end; j++)
|
|
2688
|
-
types[j] = replace;
|
|
2689
|
-
i = end - 1;
|
|
2690
|
-
}
|
|
2637
|
+
childCursor(pos = this.length) {
|
|
2638
|
+
// Move back to start of last element when possible, so that
|
|
2639
|
+
// `ChildCursor.findPos` doesn't have to deal with the edge case
|
|
2640
|
+
// of being after the last element.
|
|
2641
|
+
let i = this.children.length;
|
|
2642
|
+
if (i)
|
|
2643
|
+
pos -= this.children[--i].length;
|
|
2644
|
+
return new ChildCursor(this.children, pos, i);
|
|
2691
2645
|
}
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
let start = i, rtl = types[i++] != 1 /* L */;
|
|
2701
|
-
while (i < len && rtl == (types[i] != 1 /* L */))
|
|
2702
|
-
i++;
|
|
2703
|
-
if (rtl) {
|
|
2704
|
-
for (let j = i; j > start;) {
|
|
2705
|
-
let end = j, l = types[--j] != 2 /* R */;
|
|
2706
|
-
while (j > start && l == (types[j - 1] != 2 /* R */))
|
|
2707
|
-
j--;
|
|
2708
|
-
order.push(new BidiSpan(j, end, l ? 2 : 1));
|
|
2709
|
-
}
|
|
2646
|
+
computeBlockGapDeco() {
|
|
2647
|
+
let deco = [], vs = this.view.viewState;
|
|
2648
|
+
for (let pos = 0, i = 0;; i++) {
|
|
2649
|
+
let next = i == vs.viewports.length ? null : vs.viewports[i];
|
|
2650
|
+
let end = next ? next.from - 1 : this.length;
|
|
2651
|
+
if (end > pos) {
|
|
2652
|
+
let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
|
|
2653
|
+
deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
|
|
2710
2654
|
}
|
|
2711
|
-
|
|
2712
|
-
|
|
2655
|
+
if (!next)
|
|
2656
|
+
break;
|
|
2657
|
+
pos = next.to + 1;
|
|
2658
|
+
}
|
|
2659
|
+
return Decoration.set(deco);
|
|
2660
|
+
}
|
|
2661
|
+
updateDeco() {
|
|
2662
|
+
return this.decorations = [
|
|
2663
|
+
...this.view.pluginField(PluginField.decorations),
|
|
2664
|
+
...this.view.state.facet(decorations),
|
|
2665
|
+
this.compositionDeco,
|
|
2666
|
+
this.computeBlockGapDeco(),
|
|
2667
|
+
this.view.viewState.lineGapDeco
|
|
2668
|
+
];
|
|
2669
|
+
}
|
|
2670
|
+
scrollIntoView({ range, center }) {
|
|
2671
|
+
let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
|
|
2672
|
+
if (!rect)
|
|
2673
|
+
return;
|
|
2674
|
+
if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
|
|
2675
|
+
rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
|
|
2676
|
+
right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
|
|
2677
|
+
let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
|
|
2678
|
+
for (let margins of this.view.pluginField(PluginField.scrollMargins))
|
|
2679
|
+
if (margins) {
|
|
2680
|
+
let { left, right, top, bottom } = margins;
|
|
2681
|
+
if (left != null)
|
|
2682
|
+
mLeft = Math.max(mLeft, left);
|
|
2683
|
+
if (right != null)
|
|
2684
|
+
mRight = Math.max(mRight, right);
|
|
2685
|
+
if (top != null)
|
|
2686
|
+
mTop = Math.max(mTop, top);
|
|
2687
|
+
if (bottom != null)
|
|
2688
|
+
mBottom = Math.max(mBottom, bottom);
|
|
2713
2689
|
}
|
|
2714
|
-
|
|
2690
|
+
scrollRectIntoView(this.view.scrollDOM, {
|
|
2691
|
+
left: rect.left - mLeft, top: rect.top - mTop,
|
|
2692
|
+
right: rect.right + mRight, bottom: rect.bottom + mBottom
|
|
2693
|
+
}, range.head < range.anchor ? -1 : 1, center);
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
function betweenUneditable(pos) {
|
|
2697
|
+
return pos.node.nodeType == 1 && pos.node.firstChild &&
|
|
2698
|
+
(pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
|
|
2699
|
+
(pos.offset == pos.node.childNodes.length || pos.node.childNodes[pos.offset].contentEditable == "false");
|
|
2700
|
+
}
|
|
2701
|
+
class BlockGapWidget extends WidgetType {
|
|
2702
|
+
constructor(height) {
|
|
2703
|
+
super();
|
|
2704
|
+
this.height = height;
|
|
2705
|
+
}
|
|
2706
|
+
toDOM() {
|
|
2707
|
+
let elt = document.createElement("div");
|
|
2708
|
+
this.updateDOM(elt);
|
|
2709
|
+
return elt;
|
|
2710
|
+
}
|
|
2711
|
+
eq(other) { return other.height == this.height; }
|
|
2712
|
+
updateDOM(elt) {
|
|
2713
|
+
elt.style.height = this.height + "px";
|
|
2714
|
+
return true;
|
|
2715
|
+
}
|
|
2716
|
+
get estimatedHeight() { return this.height; }
|
|
2717
|
+
}
|
|
2718
|
+
function computeCompositionDeco(view, changes) {
|
|
2719
|
+
let sel = view.observer.selectionRange;
|
|
2720
|
+
let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
|
|
2721
|
+
if (!textNode)
|
|
2722
|
+
return Decoration.none;
|
|
2723
|
+
let cView = view.docView.nearest(textNode);
|
|
2724
|
+
if (!cView)
|
|
2725
|
+
return Decoration.none;
|
|
2726
|
+
let from, to, topNode = textNode;
|
|
2727
|
+
if (cView instanceof LineView) {
|
|
2728
|
+
while (topNode.parentNode != cView.dom)
|
|
2729
|
+
topNode = topNode.parentNode;
|
|
2730
|
+
let prev = topNode.previousSibling;
|
|
2731
|
+
while (prev && !ContentView.get(prev))
|
|
2732
|
+
prev = prev.previousSibling;
|
|
2733
|
+
from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
|
|
2715
2734
|
}
|
|
2716
2735
|
else {
|
|
2717
|
-
for (
|
|
2718
|
-
let
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2736
|
+
for (;;) {
|
|
2737
|
+
let { parent } = cView;
|
|
2738
|
+
if (!parent)
|
|
2739
|
+
return Decoration.none;
|
|
2740
|
+
if (parent instanceof LineView)
|
|
2741
|
+
break;
|
|
2742
|
+
cView = parent;
|
|
2722
2743
|
}
|
|
2744
|
+
from = cView.posAtStart;
|
|
2745
|
+
to = from + cView.length;
|
|
2746
|
+
topNode = cView.dom;
|
|
2723
2747
|
}
|
|
2724
|
-
|
|
2748
|
+
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
2749
|
+
let { state } = view, text = topNode.nodeType == 3 ? topNode.nodeValue :
|
|
2750
|
+
new DOMReader([], view).readRange(topNode.firstChild, null).text;
|
|
2751
|
+
if (newTo - newFrom < text.length) {
|
|
2752
|
+
if (state.sliceDoc(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
|
|
2753
|
+
newTo = newFrom + text.length;
|
|
2754
|
+
else if (state.sliceDoc(Math.max(0, newTo - text.length), newTo) == text)
|
|
2755
|
+
newFrom = newTo - text.length;
|
|
2756
|
+
else
|
|
2757
|
+
return Decoration.none;
|
|
2758
|
+
}
|
|
2759
|
+
else if (state.sliceDoc(newFrom, newTo) != text) {
|
|
2760
|
+
return Decoration.none;
|
|
2761
|
+
}
|
|
2762
|
+
return Decoration.set(Decoration.replace({ widget: new CompositionWidget(topNode, textNode) }).range(newFrom, newTo));
|
|
2725
2763
|
}
|
|
2726
|
-
|
|
2727
|
-
|
|
2764
|
+
class CompositionWidget extends WidgetType {
|
|
2765
|
+
constructor(top, text) {
|
|
2766
|
+
super();
|
|
2767
|
+
this.top = top;
|
|
2768
|
+
this.text = text;
|
|
2769
|
+
}
|
|
2770
|
+
eq(other) { return this.top == other.top && this.text == other.text; }
|
|
2771
|
+
toDOM() { return this.top; }
|
|
2772
|
+
ignoreEvent() { return false; }
|
|
2773
|
+
get customView() { return CompositionView; }
|
|
2728
2774
|
}
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
if (order[0].level != dir) {
|
|
2737
|
-
startIndex = order[0].side(false, dir);
|
|
2738
|
-
spanI = 0;
|
|
2775
|
+
function nearbyTextNode(node, offset, side) {
|
|
2776
|
+
for (;;) {
|
|
2777
|
+
if (node.nodeType == 3)
|
|
2778
|
+
return node;
|
|
2779
|
+
if (node.nodeType == 1 && offset > 0 && side <= 0) {
|
|
2780
|
+
node = node.childNodes[offset - 1];
|
|
2781
|
+
offset = maxOffset(node);
|
|
2739
2782
|
}
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2783
|
+
else if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
|
|
2784
|
+
node = node.childNodes[offset];
|
|
2785
|
+
offset = 0;
|
|
2786
|
+
}
|
|
2787
|
+
else {
|
|
2743
2788
|
return null;
|
|
2744
|
-
let last = order[order.length - 1];
|
|
2745
|
-
if (last.level != dir) {
|
|
2746
|
-
startIndex = last.side(true, dir);
|
|
2747
|
-
spanI = order.length - 1;
|
|
2748
2789
|
}
|
|
2749
2790
|
}
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2791
|
+
}
|
|
2792
|
+
function nextToUneditable(node, offset) {
|
|
2793
|
+
if (node.nodeType != 1)
|
|
2794
|
+
return 0;
|
|
2795
|
+
return (offset && node.childNodes[offset - 1].contentEditable == "false" ? 1 /* Before */ : 0) |
|
|
2796
|
+
(offset < node.childNodes.length && node.childNodes[offset].contentEditable == "false" ? 2 /* After */ : 0);
|
|
2797
|
+
}
|
|
2798
|
+
class DecorationComparator$1 {
|
|
2799
|
+
constructor() {
|
|
2800
|
+
this.changes = [];
|
|
2757
2801
|
}
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2802
|
+
compareRange(from, to) { addRange(from, to, this.changes); }
|
|
2803
|
+
comparePoint(from, to) { addRange(from, to, this.changes); }
|
|
2804
|
+
}
|
|
2805
|
+
function findChangedDeco(a, b, diff) {
|
|
2806
|
+
let comp = new DecorationComparator$1;
|
|
2807
|
+
RangeSet.compare(a, b, diff, comp);
|
|
2808
|
+
return comp.changes;
|
|
2809
|
+
}
|
|
2810
|
+
function inUneditable(node, inside) {
|
|
2811
|
+
for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) {
|
|
2812
|
+
if (cur.nodeType == 1 && cur.contentEditable == 'false') {
|
|
2813
|
+
return true;
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
return false;
|
|
2769
2817
|
}
|
|
2770
2818
|
|
|
2771
2819
|
function groupAt(state, pos, bias = 1) {
|
|
@@ -2903,21 +2951,29 @@ function domPosInText(node, x, y) {
|
|
|
2903
2951
|
function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
2904
2952
|
var _a;
|
|
2905
2953
|
let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
|
|
2906
|
-
let
|
|
2907
|
-
|
|
2908
|
-
|
|
2954
|
+
let block, yOffset = y - docTop, { docHeight } = view.viewState;
|
|
2955
|
+
if (yOffset < 0 || yOffset > docHeight) {
|
|
2956
|
+
if (precise)
|
|
2957
|
+
return null;
|
|
2958
|
+
yOffset = yOffset < 0 ? 0 : docHeight;
|
|
2959
|
+
}
|
|
2960
|
+
// Scan for a text block near the queried y position
|
|
2961
|
+
for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
|
|
2909
2962
|
block = view.elementAtHeight(yOffset);
|
|
2910
|
-
if (block.
|
|
2911
|
-
|
|
2912
|
-
|
|
2963
|
+
if (block.type == BlockType.Text)
|
|
2964
|
+
break;
|
|
2965
|
+
for (;;) {
|
|
2966
|
+
// Move the y position out of this block
|
|
2967
|
+
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2968
|
+
if (yOffset >= 0 && yOffset <= docHeight)
|
|
2969
|
+
break;
|
|
2970
|
+
// If the document consists entirely of replaced widgets, we
|
|
2971
|
+
// won't find a text block, so return 0
|
|
2913
2972
|
if (bounced)
|
|
2914
2973
|
return precise ? null : 0;
|
|
2915
|
-
|
|
2916
|
-
|
|
2974
|
+
bounced = true;
|
|
2975
|
+
bias = -bias;
|
|
2917
2976
|
}
|
|
2918
|
-
if (block.type == BlockType.Text)
|
|
2919
|
-
break;
|
|
2920
|
-
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2921
2977
|
}
|
|
2922
2978
|
y = docTop + yOffset;
|
|
2923
2979
|
let lineStart = block.from;
|
|
@@ -3258,10 +3314,10 @@ class InputState {
|
|
|
3258
3314
|
return (event.type == "keydown" && event.keyCode != 229) ||
|
|
3259
3315
|
event.type == "compositionend" && !browser.ios;
|
|
3260
3316
|
}
|
|
3261
|
-
startMouseSelection(
|
|
3317
|
+
startMouseSelection(mouseSelection) {
|
|
3262
3318
|
if (this.mouseSelection)
|
|
3263
3319
|
this.mouseSelection.destroy();
|
|
3264
|
-
this.mouseSelection =
|
|
3320
|
+
this.mouseSelection = mouseSelection;
|
|
3265
3321
|
}
|
|
3266
3322
|
update(update) {
|
|
3267
3323
|
if (this.mouseSelection)
|
|
@@ -3282,10 +3338,10 @@ const PendingKeys = [
|
|
|
3282
3338
|
// Key codes for modifier keys
|
|
3283
3339
|
const modifierCodes = [16, 17, 18, 20, 91, 92, 224, 225];
|
|
3284
3340
|
class MouseSelection {
|
|
3285
|
-
constructor(
|
|
3286
|
-
this.inputState = inputState;
|
|
3341
|
+
constructor(view, startEvent, style, mustSelect) {
|
|
3287
3342
|
this.view = view;
|
|
3288
3343
|
this.style = style;
|
|
3344
|
+
this.mustSelect = mustSelect;
|
|
3289
3345
|
this.lastEvent = startEvent;
|
|
3290
3346
|
let doc = view.contentDOM.ownerDocument;
|
|
3291
3347
|
doc.addEventListener("mousemove", this.move = this.move.bind(this));
|
|
@@ -3319,16 +3375,18 @@ class MouseSelection {
|
|
|
3319
3375
|
let doc = this.view.contentDOM.ownerDocument;
|
|
3320
3376
|
doc.removeEventListener("mousemove", this.move);
|
|
3321
3377
|
doc.removeEventListener("mouseup", this.up);
|
|
3322
|
-
this.inputState.mouseSelection = null;
|
|
3378
|
+
this.view.inputState.mouseSelection = null;
|
|
3323
3379
|
}
|
|
3324
3380
|
select(event) {
|
|
3325
3381
|
let selection = this.style.get(event, this.extend, this.multiple);
|
|
3326
|
-
if (!selection.eq(this.view.state.selection) ||
|
|
3382
|
+
if (this.mustSelect || !selection.eq(this.view.state.selection) ||
|
|
3383
|
+
selection.main.assoc != this.view.state.selection.main.assoc)
|
|
3327
3384
|
this.view.dispatch({
|
|
3328
3385
|
selection,
|
|
3329
3386
|
userEvent: "select.pointer",
|
|
3330
3387
|
scrollIntoView: true
|
|
3331
3388
|
});
|
|
3389
|
+
this.mustSelect = false;
|
|
3332
3390
|
}
|
|
3333
3391
|
update(update) {
|
|
3334
3392
|
if (update.docChanged && this.dragging)
|
|
@@ -3447,9 +3505,10 @@ handlers.mousedown = (view, event) => {
|
|
|
3447
3505
|
if (!style && event.button == 0)
|
|
3448
3506
|
style = basicMouseSelection(view, event);
|
|
3449
3507
|
if (style) {
|
|
3450
|
-
|
|
3508
|
+
let mustFocus = view.root.activeElement != view.contentDOM;
|
|
3509
|
+
if (mustFocus)
|
|
3451
3510
|
view.observer.ignore(() => focusPreventScroll(view.contentDOM));
|
|
3452
|
-
view.inputState.startMouseSelection(view, event, style);
|
|
3511
|
+
view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
|
|
3453
3512
|
}
|
|
3454
3513
|
};
|
|
3455
3514
|
function rangeForClick(view, pos, bias, type) {
|
|
@@ -4164,12 +4223,12 @@ class HeightMapBranch extends HeightMap {
|
|
|
4164
4223
|
get break() { return this.flags & 1 /* Break */; }
|
|
4165
4224
|
blockAt(height, doc, top, offset) {
|
|
4166
4225
|
let mid = top + this.left.height;
|
|
4167
|
-
return height < mid
|
|
4226
|
+
return height < mid ? this.left.blockAt(height, doc, top, offset)
|
|
4168
4227
|
: this.right.blockAt(height, doc, mid, offset + this.left.length + this.break);
|
|
4169
4228
|
}
|
|
4170
4229
|
lineAt(value, type, doc, top, offset) {
|
|
4171
4230
|
let rightTop = top + this.left.height, rightOffset = offset + this.left.length + this.break;
|
|
4172
|
-
let left = type == QueryType.ByHeight ? value < rightTop
|
|
4231
|
+
let left = type == QueryType.ByHeight ? value < rightTop : value < rightOffset;
|
|
4173
4232
|
let base = left ? this.left.lineAt(value, type, doc, top, offset)
|
|
4174
4233
|
: this.right.lineAt(value, type, doc, rightTop, rightOffset);
|
|
4175
4234
|
if (this.break || (left ? base.to < rightOffset : base.from > rightOffset))
|
|
@@ -4310,7 +4369,9 @@ class NodeBuilder {
|
|
|
4310
4369
|
}
|
|
4311
4370
|
point(from, to, deco) {
|
|
4312
4371
|
if (from < to || deco.heightRelevant) {
|
|
4313
|
-
let height = deco.widget ?
|
|
4372
|
+
let height = deco.widget ? deco.widget.estimatedHeight : 0;
|
|
4373
|
+
if (height < 0)
|
|
4374
|
+
height = this.oracle.lineHeight;
|
|
4314
4375
|
let len = to - from;
|
|
4315
4376
|
if (deco.block) {
|
|
4316
4377
|
this.addBlock(new HeightMapBlock(len, height, deco.type));
|
|
@@ -4438,8 +4499,8 @@ function visiblePixelRange(dom, paddingTop) {
|
|
|
4438
4499
|
break;
|
|
4439
4500
|
}
|
|
4440
4501
|
}
|
|
4441
|
-
return { left: left - rect.left, right: right - rect.left,
|
|
4442
|
-
top: top - (rect.top + paddingTop), bottom: bottom - (rect.top + paddingTop) };
|
|
4502
|
+
return { left: left - rect.left, right: Math.max(left, right) - rect.left,
|
|
4503
|
+
top: top - (rect.top + paddingTop), bottom: Math.max(top, bottom) - (rect.top + paddingTop) };
|
|
4443
4504
|
}
|
|
4444
4505
|
// Line gaps are placeholder widgets used to hide pieces of overlong
|
|
4445
4506
|
// lines within the viewport, as a kludge to keep the editor
|
|
@@ -4666,7 +4727,7 @@ class ViewState {
|
|
|
4666
4727
|
let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).to);
|
|
4667
4728
|
// If scrollTarget is given, make sure the viewport includes that position
|
|
4668
4729
|
if (scrollTarget) {
|
|
4669
|
-
let { head } = scrollTarget.range, viewHeight =
|
|
4730
|
+
let { head } = scrollTarget.range, viewHeight = this.editorHeight;
|
|
4670
4731
|
if (head < viewport.from || head > viewport.to) {
|
|
4671
4732
|
let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
|
|
4672
4733
|
if (scrollTarget.center)
|
|
@@ -4687,6 +4748,8 @@ class ViewState {
|
|
|
4687
4748
|
// Checks if a given viewport covers the visible part of the
|
|
4688
4749
|
// document and not too much beyond that.
|
|
4689
4750
|
viewportIsAppropriate({ from, to }, bias = 0) {
|
|
4751
|
+
if (!this.inView)
|
|
4752
|
+
return true;
|
|
4690
4753
|
let { top } = this.heightMap.lineAt(from, QueryType.ByPos, this.state.doc, 0, 0);
|
|
4691
4754
|
let { bottom } = this.heightMap.lineAt(to, QueryType.ByPos, this.state.doc, 0, 0);
|
|
4692
4755
|
let { visibleTop, visibleBottom } = this;
|
|
@@ -4793,8 +4856,11 @@ class ViewState {
|
|
|
4793
4856
|
elementAtHeight(height) {
|
|
4794
4857
|
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.state.doc, 0, 0), this.scaler);
|
|
4795
4858
|
}
|
|
4859
|
+
get docHeight() {
|
|
4860
|
+
return this.scaler.toDOM(this.heightMap.height);
|
|
4861
|
+
}
|
|
4796
4862
|
get contentHeight() {
|
|
4797
|
-
return this.
|
|
4863
|
+
return this.docHeight + this.paddingTop + this.paddingBottom;
|
|
4798
4864
|
}
|
|
4799
4865
|
}
|
|
4800
4866
|
class Viewport {
|
|
@@ -5048,7 +5114,8 @@ const baseTheme = /*@__PURE__*/buildTheme("." + baseThemeID, {
|
|
|
5048
5114
|
},
|
|
5049
5115
|
".cm-placeholder": {
|
|
5050
5116
|
color: "#888",
|
|
5051
|
-
display: "inline-block"
|
|
5117
|
+
display: "inline-block",
|
|
5118
|
+
verticalAlign: "top",
|
|
5052
5119
|
},
|
|
5053
5120
|
".cm-button": {
|
|
5054
5121
|
verticalAlign: "middle",
|
|
@@ -5346,7 +5413,7 @@ class DOMObserver {
|
|
|
5346
5413
|
this.onChange(from, to, typeOver);
|
|
5347
5414
|
// The view wasn't updated
|
|
5348
5415
|
if (this.view.state == startState)
|
|
5349
|
-
this.view.
|
|
5416
|
+
this.view.update([]);
|
|
5350
5417
|
}
|
|
5351
5418
|
readMutation(rec) {
|
|
5352
5419
|
let cView = this.view.docView.nearest(rec.target);
|
|
@@ -5477,11 +5544,22 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5477
5544
|
};
|
|
5478
5545
|
if (change) {
|
|
5479
5546
|
let startState = view.state;
|
|
5547
|
+
if (browser.ios && view.inputState.flushIOSKey(view))
|
|
5548
|
+
return;
|
|
5480
5549
|
// Android browsers don't fire reasonable key events for enter,
|
|
5481
5550
|
// backspace, or delete. So this detects changes that look like
|
|
5482
5551
|
// they're caused by those keys, and reinterprets them as key
|
|
5483
|
-
// events.
|
|
5484
|
-
|
|
5552
|
+
// events. (Some of these keys are also handled by beforeinput
|
|
5553
|
+
// events and the pendingAndroidKey mechanism, but that's not
|
|
5554
|
+
// reliable in all situations.)
|
|
5555
|
+
if (browser.android &&
|
|
5556
|
+
((change.from == sel.from && change.to == sel.to &&
|
|
5557
|
+
change.insert.length == 1 && change.insert.lines == 2 &&
|
|
5558
|
+
dispatchKey(view.contentDOM, "Enter", 13)) ||
|
|
5559
|
+
(change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
|
|
5560
|
+
dispatchKey(view.contentDOM, "Backspace", 8)) ||
|
|
5561
|
+
(change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
|
|
5562
|
+
dispatchKey(view.contentDOM, "Delete", 46))))
|
|
5485
5563
|
return;
|
|
5486
5564
|
let text = change.insert.toString();
|
|
5487
5565
|
if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
|
|
@@ -5554,76 +5632,6 @@ function findDiff(a, b, preferredPos, preferredSide) {
|
|
|
5554
5632
|
}
|
|
5555
5633
|
return { from, toA, toB };
|
|
5556
5634
|
}
|
|
5557
|
-
class DOMReader {
|
|
5558
|
-
constructor(points, view) {
|
|
5559
|
-
this.points = points;
|
|
5560
|
-
this.view = view;
|
|
5561
|
-
this.text = "";
|
|
5562
|
-
this.lineBreak = view.state.lineBreak;
|
|
5563
|
-
}
|
|
5564
|
-
readRange(start, end) {
|
|
5565
|
-
if (!start)
|
|
5566
|
-
return;
|
|
5567
|
-
let parent = start.parentNode;
|
|
5568
|
-
for (let cur = start;;) {
|
|
5569
|
-
this.findPointBefore(parent, cur);
|
|
5570
|
-
this.readNode(cur);
|
|
5571
|
-
let next = cur.nextSibling;
|
|
5572
|
-
if (next == end)
|
|
5573
|
-
break;
|
|
5574
|
-
let view = ContentView.get(cur), nextView = ContentView.get(next);
|
|
5575
|
-
if (view && nextView ? view.breakAfter :
|
|
5576
|
-
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
5577
|
-
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
5578
|
-
this.text += this.lineBreak;
|
|
5579
|
-
cur = next;
|
|
5580
|
-
}
|
|
5581
|
-
this.findPointBefore(parent, end);
|
|
5582
|
-
}
|
|
5583
|
-
readNode(node) {
|
|
5584
|
-
if (node.cmIgnore)
|
|
5585
|
-
return;
|
|
5586
|
-
let view = ContentView.get(node);
|
|
5587
|
-
let fromView = view && view.overrideDOMText;
|
|
5588
|
-
let text;
|
|
5589
|
-
if (fromView != null)
|
|
5590
|
-
text = fromView.sliceString(0, undefined, this.lineBreak);
|
|
5591
|
-
else if (node.nodeType == 3)
|
|
5592
|
-
text = node.nodeValue;
|
|
5593
|
-
else if (node.nodeName == "BR")
|
|
5594
|
-
text = node.nextSibling ? this.lineBreak : "";
|
|
5595
|
-
else if (node.nodeType == 1)
|
|
5596
|
-
this.readRange(node.firstChild, null);
|
|
5597
|
-
if (text != null) {
|
|
5598
|
-
this.findPointIn(node, text.length);
|
|
5599
|
-
this.text += text;
|
|
5600
|
-
// Chrome inserts two newlines when pressing shift-enter at the
|
|
5601
|
-
// end of a line. This drops one of those.
|
|
5602
|
-
if (browser.chrome && this.view.inputState.lastKeyCode == 13 && !node.nextSibling && /\n\n$/.test(this.text))
|
|
5603
|
-
this.text = this.text.slice(0, -1);
|
|
5604
|
-
}
|
|
5605
|
-
}
|
|
5606
|
-
findPointBefore(node, next) {
|
|
5607
|
-
for (let point of this.points)
|
|
5608
|
-
if (point.node == node && node.childNodes[point.offset] == next)
|
|
5609
|
-
point.pos = this.text.length;
|
|
5610
|
-
}
|
|
5611
|
-
findPointIn(node, maxLen) {
|
|
5612
|
-
for (let point of this.points)
|
|
5613
|
-
if (point.node == node)
|
|
5614
|
-
point.pos = this.text.length + Math.min(point.offset, maxLen);
|
|
5615
|
-
}
|
|
5616
|
-
}
|
|
5617
|
-
function isBlockElement(node) {
|
|
5618
|
-
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
5619
|
-
}
|
|
5620
|
-
class DOMPoint {
|
|
5621
|
-
constructor(node, offset) {
|
|
5622
|
-
this.node = node;
|
|
5623
|
-
this.offset = offset;
|
|
5624
|
-
this.pos = -1;
|
|
5625
|
-
}
|
|
5626
|
-
}
|
|
5627
5635
|
function selectionPoints(view) {
|
|
5628
5636
|
let result = [];
|
|
5629
5637
|
if (view.root.activeElement != view.contentDOM)
|
|
@@ -5706,7 +5714,9 @@ class EditorView {
|
|
|
5706
5714
|
this.dispatch = this.dispatch.bind(this);
|
|
5707
5715
|
this.root = (config.root || getRoot(config.parent) || document);
|
|
5708
5716
|
this.viewState = new ViewState(config.state || EditorState.create());
|
|
5709
|
-
this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec)
|
|
5717
|
+
this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
|
|
5718
|
+
for (let plugin of this.plugins)
|
|
5719
|
+
plugin.update(this);
|
|
5710
5720
|
this.observer = new DOMObserver(this, (from, to, typeOver) => {
|
|
5711
5721
|
applyDOMChange(this, from, to, typeOver);
|
|
5712
5722
|
}, event => {
|
|
@@ -5843,8 +5853,10 @@ class EditorView {
|
|
|
5843
5853
|
for (let plugin of this.plugins)
|
|
5844
5854
|
plugin.destroy(this);
|
|
5845
5855
|
this.viewState = new ViewState(newState);
|
|
5846
|
-
this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec)
|
|
5856
|
+
this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec));
|
|
5847
5857
|
this.pluginMap.clear();
|
|
5858
|
+
for (let plugin of this.plugins)
|
|
5859
|
+
plugin.update(this);
|
|
5848
5860
|
this.docView = new DocView(this);
|
|
5849
5861
|
this.inputState.ensureHandlers(this);
|
|
5850
5862
|
this.mountStyles();
|
|
@@ -5905,7 +5917,9 @@ class EditorView {
|
|
|
5905
5917
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
5906
5918
|
break;
|
|
5907
5919
|
if (i > 5) {
|
|
5908
|
-
console.warn(this.measureRequests.length
|
|
5920
|
+
console.warn(this.measureRequests.length
|
|
5921
|
+
? "Measure loop restarted more than 5 times"
|
|
5922
|
+
: "Viewport failed to stabilize");
|
|
5909
5923
|
break;
|
|
5910
5924
|
}
|
|
5911
5925
|
let measuring = [];
|
|
@@ -5951,7 +5965,8 @@ class EditorView {
|
|
|
5951
5965
|
}
|
|
5952
5966
|
if (redrawn)
|
|
5953
5967
|
this.docView.updateSelection(true);
|
|
5954
|
-
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
5968
|
+
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
5969
|
+
this.measureRequests.length == 0)
|
|
5955
5970
|
break;
|
|
5956
5971
|
}
|
|
5957
5972
|
}
|
|
@@ -6243,6 +6258,11 @@ class EditorView {
|
|
|
6243
6258
|
Find the DOM parent node and offset (child offset if `node` is
|
|
6244
6259
|
an element, character offset when it is a text node) at the
|
|
6245
6260
|
given document position.
|
|
6261
|
+
|
|
6262
|
+
Note that for positions that aren't currently in
|
|
6263
|
+
`visibleRanges`, the resulting DOM position isn't necessarily
|
|
6264
|
+
meaningful (it may just point before or after a placeholder
|
|
6265
|
+
element).
|
|
6246
6266
|
*/
|
|
6247
6267
|
domAtPos(pos) {
|
|
6248
6268
|
return this.docView.domAtPos(pos);
|