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