prosemirror-view 1.41.9 → 1.42.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/index.cjs +72 -53
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +41 -14
- package/package.json +1 -1
- package/src/#clipboard.ts# +275 -0
- package/src/clipboard.ts +14 -3
- package/src/domcoords.ts +4 -1
- package/src/viewdesc.ts +31 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## 1.42.1 (2026-07-11)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Don't scroll to a bogus position when trying to scroll an element that has no layout into view.
|
|
6
|
+
|
|
7
|
+
## 1.42.0 (2026-07-01)
|
|
8
|
+
|
|
9
|
+
### Bug fixes
|
|
10
|
+
|
|
11
|
+
The clipboard content parser now inlines simple styles defined in the pasted HTML before parsing it, since some applications output their styling that way.
|
|
12
|
+
|
|
13
|
+
### New features
|
|
14
|
+
|
|
15
|
+
Mark views now also support an `update` method.
|
|
16
|
+
|
|
1
17
|
## 1.41.9 (2026-06-10)
|
|
2
18
|
|
|
3
19
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -202,6 +202,7 @@ function clientRect(node) {
|
|
|
202
202
|
};
|
|
203
203
|
}
|
|
204
204
|
function scrollRectIntoView(view, rect, startDOM) {
|
|
205
|
+
if (!nonZero(rect) && rect.left == 0) return;
|
|
205
206
|
var scrollThreshold = view.someProp("scrollThreshold") || 0,
|
|
206
207
|
scrollMargin = view.someProp("scrollMargin") || 5;
|
|
207
208
|
var doc = view.dom.ownerDocument;
|
|
@@ -1272,7 +1273,7 @@ var MarkViewDesc = function (_ViewDesc3) {
|
|
|
1272
1273
|
var NodeViewDesc = function (_ViewDesc4) {
|
|
1273
1274
|
_inherits(NodeViewDesc, _ViewDesc4);
|
|
1274
1275
|
var _super4 = _createSuper(NodeViewDesc);
|
|
1275
|
-
function NodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM
|
|
1276
|
+
function NodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM) {
|
|
1276
1277
|
var _this4;
|
|
1277
1278
|
_classCallCheck(this, NodeViewDesc);
|
|
1278
1279
|
_this4 = _super4.call(this, parent, [], dom, contentDOM);
|
|
@@ -1474,23 +1475,23 @@ var NodeViewDesc = function (_ViewDesc4) {
|
|
|
1474
1475
|
}
|
|
1475
1476
|
var nodeDOM = dom;
|
|
1476
1477
|
dom = applyOuterDeco(dom, outerDeco, node);
|
|
1477
|
-
if (spec) return descObj = new CustomNodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM, spec
|
|
1478
|
+
if (spec) return descObj = new CustomNodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM, spec);else if (node.isText) return new TextViewDesc(parent, node, outerDeco, innerDeco, dom, nodeDOM);else return new NodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM);
|
|
1478
1479
|
}
|
|
1479
1480
|
}]);
|
|
1480
1481
|
return NodeViewDesc;
|
|
1481
1482
|
}(ViewDesc);
|
|
1482
1483
|
function docViewDesc(doc, outerDeco, innerDeco, dom, view) {
|
|
1483
1484
|
applyOuterDeco(dom, outerDeco, doc);
|
|
1484
|
-
var docView = new NodeViewDesc(undefined, doc, outerDeco, innerDeco, dom, dom, dom
|
|
1485
|
+
var docView = new NodeViewDesc(undefined, doc, outerDeco, innerDeco, dom, dom, dom);
|
|
1485
1486
|
if (docView.contentDOM) docView.updateChildren(view, 0);
|
|
1486
1487
|
return docView;
|
|
1487
1488
|
}
|
|
1488
1489
|
var TextViewDesc = function (_NodeViewDesc) {
|
|
1489
1490
|
_inherits(TextViewDesc, _NodeViewDesc);
|
|
1490
1491
|
var _super5 = _createSuper(TextViewDesc);
|
|
1491
|
-
function TextViewDesc(parent, node, outerDeco, innerDeco, dom, nodeDOM
|
|
1492
|
+
function TextViewDesc(parent, node, outerDeco, innerDeco, dom, nodeDOM) {
|
|
1492
1493
|
_classCallCheck(this, TextViewDesc);
|
|
1493
|
-
return _super5.call(this, parent, node, outerDeco, innerDeco, dom, null, nodeDOM
|
|
1494
|
+
return _super5.call(this, parent, node, outerDeco, innerDeco, dom, null, nodeDOM);
|
|
1494
1495
|
}
|
|
1495
1496
|
_createClass(TextViewDesc, [{
|
|
1496
1497
|
key: "parseRule",
|
|
@@ -1542,10 +1543,10 @@ var TextViewDesc = function (_NodeViewDesc) {
|
|
|
1542
1543
|
}
|
|
1543
1544
|
}, {
|
|
1544
1545
|
key: "slice",
|
|
1545
|
-
value: function slice(from, to,
|
|
1546
|
+
value: function slice(from, to, _view) {
|
|
1546
1547
|
var node = this.node.cut(from, to),
|
|
1547
1548
|
dom = document.createTextNode(node.text);
|
|
1548
|
-
return new TextViewDesc(this.parent, node, this.outerDeco, this.innerDeco, dom, dom
|
|
1549
|
+
return new TextViewDesc(this.parent, node, this.outerDeco, this.innerDeco, dom, dom);
|
|
1549
1550
|
}
|
|
1550
1551
|
}, {
|
|
1551
1552
|
key: "markDirty",
|
|
@@ -1601,10 +1602,10 @@ var TrailingHackViewDesc = function (_ViewDesc5) {
|
|
|
1601
1602
|
var CustomNodeViewDesc = function (_NodeViewDesc2) {
|
|
1602
1603
|
_inherits(CustomNodeViewDesc, _NodeViewDesc2);
|
|
1603
1604
|
var _super7 = _createSuper(CustomNodeViewDesc);
|
|
1604
|
-
function CustomNodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, spec
|
|
1605
|
+
function CustomNodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, spec) {
|
|
1605
1606
|
var _this7;
|
|
1606
1607
|
_classCallCheck(this, CustomNodeViewDesc);
|
|
1607
|
-
_this7 = _super7.call(this, parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM
|
|
1608
|
+
_this7 = _super7.call(this, parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM);
|
|
1608
1609
|
_this7.spec = spec;
|
|
1609
1610
|
return _this7;
|
|
1610
1611
|
}
|
|
@@ -1810,6 +1811,14 @@ var ViewTreeUpdater = function () {
|
|
|
1810
1811
|
break;
|
|
1811
1812
|
}
|
|
1812
1813
|
}
|
|
1814
|
+
if (found < 0 && this.index < this.top.children.length) {
|
|
1815
|
+
var cur = this.top.children[this.index];
|
|
1816
|
+
if (cur instanceof MarkViewDesc && cur.dirty != NODE_DIRTY && cur.mark.type == marks[depth].type && cur.spec.update && !this.isLocked(cur.dom) && cur.spec.update(marks[depth])) {
|
|
1817
|
+
cur.mark = marks[depth];
|
|
1818
|
+
found = this.index;
|
|
1819
|
+
this.changed = true;
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1813
1822
|
if (found > -1) {
|
|
1814
1823
|
if (found > this.index) {
|
|
1815
1824
|
this.changed = true;
|
|
@@ -2831,9 +2840,8 @@ var wrapMap = {
|
|
|
2831
2840
|
td: ["table", "tbody", "tr"],
|
|
2832
2841
|
th: ["table", "tbody", "tr"]
|
|
2833
2842
|
};
|
|
2834
|
-
var _detachedDoc = null;
|
|
2835
2843
|
function detachedDoc() {
|
|
2836
|
-
return
|
|
2844
|
+
return document.implementation.createHTMLDocument("title");
|
|
2837
2845
|
}
|
|
2838
2846
|
var _policy = null;
|
|
2839
2847
|
function maybeWrapTrusted(html) {
|
|
@@ -2849,7 +2857,8 @@ function maybeWrapTrusted(html) {
|
|
|
2849
2857
|
function readHTML(html) {
|
|
2850
2858
|
var metas = /^(\s*<meta [^>]*>)*/.exec(html);
|
|
2851
2859
|
if (metas) html = html.slice(metas[0].length);
|
|
2852
|
-
var
|
|
2860
|
+
var doc = detachedDoc(),
|
|
2861
|
+
elt = doc.body;
|
|
2853
2862
|
var firstTag = /<([a-z][^>\s]+)/i.exec(html),
|
|
2854
2863
|
wrap;
|
|
2855
2864
|
if (wrap = firstTag && wrapMap[firstTag[1].toLowerCase()]) html = wrap.map(function (n) {
|
|
@@ -2859,6 +2868,16 @@ function readHTML(html) {
|
|
|
2859
2868
|
}).reverse().join("");
|
|
2860
2869
|
elt.innerHTML = maybeWrapTrusted(html);
|
|
2861
2870
|
if (wrap) for (var i = 0; i < wrap.length; i++) elt = elt.querySelector(wrap[i]) || elt;
|
|
2871
|
+
for (var _i6 = 0; _i6 < doc.styleSheets.length; _i6++) {
|
|
2872
|
+
var style = doc.styleSheets[_i6];
|
|
2873
|
+
for (var j = 0; j < style.rules.length; j++) {
|
|
2874
|
+
var rule = style.rules[j];
|
|
2875
|
+
if (rule instanceof CSSStyleRule) {
|
|
2876
|
+
var matches = elt.querySelectorAll(rule.selectorText);
|
|
2877
|
+
for (var k = 0; k < matches.length; k++) matches[k].style.cssText += rule.style.cssText;
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2862
2881
|
return elt;
|
|
2863
2882
|
}
|
|
2864
2883
|
function restoreReplacedSpaces(dom) {
|
|
@@ -3852,10 +3871,10 @@ var DecorationSet = function () {
|
|
|
3852
3871
|
var span = this.local[i];
|
|
3853
3872
|
if (span.from <= end && span.to >= start && (!predicate || predicate(span.spec))) result.push(span.copy(span.from + offset, span.to + offset));
|
|
3854
3873
|
}
|
|
3855
|
-
for (var
|
|
3856
|
-
if (this.children[
|
|
3857
|
-
var childOff = this.children[
|
|
3858
|
-
this.children[
|
|
3874
|
+
for (var _i7 = 0; _i7 < this.children.length; _i7 += 3) {
|
|
3875
|
+
if (this.children[_i7] < end && this.children[_i7 + 1] > start) {
|
|
3876
|
+
var childOff = this.children[_i7] + 1;
|
|
3877
|
+
this.children[_i7 + 2].findInner(start - childOff, end - childOff, result, offset + childOff, predicate);
|
|
3859
3878
|
}
|
|
3860
3879
|
}
|
|
3861
3880
|
}
|
|
@@ -3932,7 +3951,7 @@ var DecorationSet = function () {
|
|
|
3932
3951
|
i -= 3;
|
|
3933
3952
|
}
|
|
3934
3953
|
}
|
|
3935
|
-
if (local.length) for (var
|
|
3954
|
+
if (local.length) for (var _i8 = 0, _span; _i8 < decorations.length; _i8++) if (_span = decorations[_i8]) {
|
|
3936
3955
|
for (var _j2 = 0; _j2 < local.length; _j2++) if (local[_j2].eq(_span, offset)) {
|
|
3937
3956
|
if (local == this.local) local = this.local.slice();
|
|
3938
3957
|
local.splice(_j2--, 1);
|
|
@@ -3953,8 +3972,8 @@ var DecorationSet = function () {
|
|
|
3953
3972
|
}
|
|
3954
3973
|
var start = offset + 1,
|
|
3955
3974
|
end = start + node.content.size;
|
|
3956
|
-
for (var
|
|
3957
|
-
var dec = this.local[
|
|
3975
|
+
for (var _i9 = 0; _i9 < this.local.length; _i9++) {
|
|
3976
|
+
var dec = this.local[_i9];
|
|
3958
3977
|
if (dec.from < end && dec.to > start && dec.type instanceof InlineType) {
|
|
3959
3978
|
var from = Math.max(start, dec.from) - start,
|
|
3960
3979
|
to = Math.min(end, dec.to) - start;
|
|
@@ -3973,7 +3992,7 @@ var DecorationSet = function () {
|
|
|
3973
3992
|
if (this == other) return true;
|
|
3974
3993
|
if (!(other instanceof DecorationSet) || this.local.length != other.local.length || this.children.length != other.children.length) return false;
|
|
3975
3994
|
for (var i = 0; i < this.local.length; i++) if (!this.local[i].eq(other.local[i])) return false;
|
|
3976
|
-
for (var
|
|
3995
|
+
for (var _i10 = 0; _i10 < this.children.length; _i10 += 3) if (this.children[_i10] != other.children[_i10] || this.children[_i10 + 1] != other.children[_i10 + 1] || !this.children[_i10 + 2].eq(other.children[_i10 + 2])) return false;
|
|
3977
3996
|
return true;
|
|
3978
3997
|
}
|
|
3979
3998
|
}, {
|
|
@@ -4090,15 +4109,15 @@ function mapChildren(oldChildren, newLocal, mapping, node, offset, oldOffset, op
|
|
|
4090
4109
|
var moved = 0;
|
|
4091
4110
|
mapping.maps[i].forEach(function (oldStart, oldEnd, newStart, newEnd) {
|
|
4092
4111
|
var dSize = newEnd - newStart - (oldEnd - oldStart);
|
|
4093
|
-
for (var
|
|
4094
|
-
var end = children[
|
|
4112
|
+
for (var _i14 = 0; _i14 < children.length; _i14 += 3) {
|
|
4113
|
+
var end = children[_i14 + 1];
|
|
4095
4114
|
if (end < 0 || oldStart > end + _baseOffset - moved) continue;
|
|
4096
|
-
var start = children[
|
|
4115
|
+
var start = children[_i14] + _baseOffset - moved;
|
|
4097
4116
|
if (oldEnd >= start) {
|
|
4098
|
-
children[
|
|
4117
|
+
children[_i14 + 1] = oldStart <= start ? -2 : -1;
|
|
4099
4118
|
} else if (oldStart >= _baseOffset && dSize) {
|
|
4100
|
-
children[
|
|
4101
|
-
children[
|
|
4119
|
+
children[_i14] += dSize;
|
|
4120
|
+
children[_i14 + 1] += dSize;
|
|
4102
4121
|
}
|
|
4103
4122
|
}
|
|
4104
4123
|
moved += dSize;
|
|
@@ -4110,32 +4129,32 @@ function mapChildren(oldChildren, newLocal, mapping, node, offset, oldOffset, op
|
|
|
4110
4129
|
_loop4(baseOffset);
|
|
4111
4130
|
}
|
|
4112
4131
|
var mustRebuild = false;
|
|
4113
|
-
for (var
|
|
4114
|
-
if (children[
|
|
4132
|
+
for (var _i11 = 0; _i11 < children.length; _i11 += 3) if (children[_i11 + 1] < 0) {
|
|
4133
|
+
if (children[_i11 + 1] == -2) {
|
|
4115
4134
|
mustRebuild = true;
|
|
4116
|
-
children[
|
|
4135
|
+
children[_i11 + 1] = -1;
|
|
4117
4136
|
continue;
|
|
4118
4137
|
}
|
|
4119
|
-
var from = mapping.map(oldChildren[
|
|
4138
|
+
var from = mapping.map(oldChildren[_i11] + oldOffset),
|
|
4120
4139
|
fromLocal = from - offset;
|
|
4121
4140
|
if (fromLocal < 0 || fromLocal >= node.content.size) {
|
|
4122
4141
|
mustRebuild = true;
|
|
4123
4142
|
continue;
|
|
4124
4143
|
}
|
|
4125
|
-
var to = mapping.map(oldChildren[
|
|
4144
|
+
var to = mapping.map(oldChildren[_i11 + 1] + oldOffset, -1),
|
|
4126
4145
|
toLocal = to - offset;
|
|
4127
4146
|
var _node$content$findInd2 = node.content.findIndex(fromLocal),
|
|
4128
4147
|
index = _node$content$findInd2.index,
|
|
4129
4148
|
childOffset = _node$content$findInd2.offset;
|
|
4130
4149
|
var childNode = node.maybeChild(index);
|
|
4131
4150
|
if (childNode && childOffset == fromLocal && childOffset + childNode.nodeSize == toLocal) {
|
|
4132
|
-
var mapped = children[
|
|
4151
|
+
var mapped = children[_i11 + 2].mapInner(mapping, childNode, from + 1, oldChildren[_i11] + oldOffset + 1, options);
|
|
4133
4152
|
if (mapped != empty) {
|
|
4134
|
-
children[
|
|
4135
|
-
children[
|
|
4136
|
-
children[
|
|
4153
|
+
children[_i11] = fromLocal;
|
|
4154
|
+
children[_i11 + 1] = toLocal;
|
|
4155
|
+
children[_i11 + 2] = mapped;
|
|
4137
4156
|
} else {
|
|
4138
|
-
children[
|
|
4157
|
+
children[_i11 + 1] = -2;
|
|
4139
4158
|
mustRebuild = true;
|
|
4140
4159
|
}
|
|
4141
4160
|
} else {
|
|
@@ -4146,14 +4165,14 @@ function mapChildren(oldChildren, newLocal, mapping, node, offset, oldOffset, op
|
|
|
4146
4165
|
var decorations = mapAndGatherRemainingDecorations(children, oldChildren, newLocal, mapping, offset, oldOffset, options);
|
|
4147
4166
|
var built = buildTree(decorations, node, 0, options);
|
|
4148
4167
|
newLocal = built.local;
|
|
4149
|
-
for (var
|
|
4150
|
-
children.splice(
|
|
4151
|
-
|
|
4168
|
+
for (var _i12 = 0; _i12 < children.length; _i12 += 3) if (children[_i12 + 1] < 0) {
|
|
4169
|
+
children.splice(_i12, 3);
|
|
4170
|
+
_i12 -= 3;
|
|
4152
4171
|
}
|
|
4153
|
-
for (var
|
|
4154
|
-
var _from2 = built.children[
|
|
4172
|
+
for (var _i13 = 0, j = 0; _i13 < built.children.length; _i13 += 3) {
|
|
4173
|
+
var _from2 = built.children[_i13];
|
|
4155
4174
|
while (j < children.length && children[j] < _from2) j += 3;
|
|
4156
|
-
children.splice(j, 0, built.children[
|
|
4175
|
+
children.splice(j, 0, built.children[_i13], built.children[_i13 + 1], built.children[_i13 + 2]);
|
|
4157
4176
|
}
|
|
4158
4177
|
}
|
|
4159
4178
|
return new DecorationSet(newLocal.sort(byPos), children);
|
|
@@ -4173,7 +4192,7 @@ function mapAndGatherRemainingDecorations(children, oldChildren, decorations, ma
|
|
|
4173
4192
|
var mapped = set.local[i].map(mapping, offset, oldOffset);
|
|
4174
4193
|
if (mapped) decorations.push(mapped);else if (options.onRemove) options.onRemove(set.local[i].spec);
|
|
4175
4194
|
}
|
|
4176
|
-
for (var
|
|
4195
|
+
for (var _i15 = 0; _i15 < set.children.length; _i15 += 3) gather(set.children[_i15 + 2], set.children[_i15] + oldOffset + 1);
|
|
4177
4196
|
}
|
|
4178
4197
|
for (var i = 0; i < children.length; i += 3) if (children[i + 1] == -1) gather(children[i + 2], oldChildren[i] + oldOffset + 1);
|
|
4179
4198
|
return decorations;
|
|
@@ -4566,8 +4585,8 @@ var DOMObserver = function () {
|
|
|
4566
4585
|
var prev = mut.previousSibling,
|
|
4567
4586
|
next = mut.nextSibling;
|
|
4568
4587
|
if (ie && ie_version <= 11 && mut.addedNodes.length) {
|
|
4569
|
-
for (var
|
|
4570
|
-
var _mut$addedNodes$_i = mut.addedNodes[
|
|
4588
|
+
for (var _i16 = 0; _i16 < mut.addedNodes.length; _i16++) {
|
|
4589
|
+
var _mut$addedNodes$_i = mut.addedNodes[_i16],
|
|
4571
4590
|
previousSibling = _mut$addedNodes$_i.previousSibling,
|
|
4572
4591
|
nextSibling = _mut$addedNodes$_i.nextSibling;
|
|
4573
4592
|
if (!previousSibling || Array.prototype.indexOf.call(mut.addedNodes, previousSibling) < 0) prev = previousSibling;
|
|
@@ -4933,7 +4952,7 @@ function isMarkChange(cur, prev) {
|
|
|
4933
4952
|
mark,
|
|
4934
4953
|
update;
|
|
4935
4954
|
for (var i = 0; i < prevMarks.length; i++) added = prevMarks[i].removeFromSet(added);
|
|
4936
|
-
for (var
|
|
4955
|
+
for (var _i17 = 0; _i17 < curMarks.length; _i17++) removed = curMarks[_i17].removeFromSet(removed);
|
|
4937
4956
|
if (added.length == 1 && removed.length == 0) {
|
|
4938
4957
|
mark = added[0];
|
|
4939
4958
|
type = "add";
|
|
@@ -4950,7 +4969,7 @@ function isMarkChange(cur, prev) {
|
|
|
4950
4969
|
return null;
|
|
4951
4970
|
}
|
|
4952
4971
|
var updated = [];
|
|
4953
|
-
for (var
|
|
4972
|
+
for (var _i18 = 0; _i18 < prev.childCount; _i18++) updated.push(update(prev.child(_i18)));
|
|
4954
4973
|
if (prosemirrorModel.Fragment.from(updated).eq(cur)) return {
|
|
4955
4974
|
mark: mark,
|
|
4956
4975
|
type: type
|
|
@@ -5187,13 +5206,13 @@ var EditorView = function () {
|
|
|
5187
5206
|
var plugin = this.directPlugins[i];
|
|
5188
5207
|
if (plugin.spec.view) this.pluginViews.push(plugin.spec.view(this));
|
|
5189
5208
|
}
|
|
5190
|
-
for (var
|
|
5191
|
-
var _plugin = this.state.plugins[
|
|
5209
|
+
for (var _i19 = 0; _i19 < this.state.plugins.length; _i19++) {
|
|
5210
|
+
var _plugin = this.state.plugins[_i19];
|
|
5192
5211
|
if (_plugin.spec.view) this.pluginViews.push(_plugin.spec.view(this));
|
|
5193
5212
|
}
|
|
5194
5213
|
} else {
|
|
5195
|
-
for (var
|
|
5196
|
-
var pluginView = this.pluginViews[
|
|
5214
|
+
for (var _i20 = 0; _i20 < this.pluginViews.length; _i20++) {
|
|
5215
|
+
var pluginView = this.pluginViews[_i20];
|
|
5197
5216
|
if (pluginView.update) pluginView.update(this, prevState);
|
|
5198
5217
|
}
|
|
5199
5218
|
}
|
|
@@ -5223,8 +5242,8 @@ var EditorView = function () {
|
|
|
5223
5242
|
if (_prop != null && (value = f ? f(_prop) : _prop)) return value;
|
|
5224
5243
|
}
|
|
5225
5244
|
var plugins = this.state.plugins;
|
|
5226
|
-
if (plugins) for (var
|
|
5227
|
-
var _prop2 = plugins[
|
|
5245
|
+
if (plugins) for (var _i21 = 0; _i21 < plugins.length; _i21++) {
|
|
5246
|
+
var _prop2 = plugins[_i21].props[propName];
|
|
5228
5247
|
if (_prop2 != null && (value = f ? f(_prop2) : _prop2)) return value;
|
|
5229
5248
|
}
|
|
5230
5249
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -363,6 +363,14 @@ interface MarkView {
|
|
|
363
363
|
*/
|
|
364
364
|
contentDOM?: HTMLElement | null;
|
|
365
365
|
/**
|
|
366
|
+
When given, this is called when the view is updating itself. It
|
|
367
|
+
will be given a mark (of the same type as the current one). When it
|
|
368
|
+
returns true, the existing DOM is kept and reused (its content is
|
|
369
|
+
still managed by ProseMirror); when it returns false, the mark view
|
|
370
|
+
is rebuilt.
|
|
371
|
+
*/
|
|
372
|
+
update?: (mark: Mark) => boolean;
|
|
373
|
+
/**
|
|
366
374
|
Called when a [mutation](https://prosemirror.net/docs/ref/#view.ViewMutationRecord) happens within the
|
|
367
375
|
view. Return false if the editor should re-read the selection or re-parse
|
|
368
376
|
the range around the mutation, true if it can safely be ignored.
|
package/dist/index.d.ts
CHANGED
|
@@ -363,6 +363,14 @@ interface MarkView {
|
|
|
363
363
|
*/
|
|
364
364
|
contentDOM?: HTMLElement | null;
|
|
365
365
|
/**
|
|
366
|
+
When given, this is called when the view is updating itself. It
|
|
367
|
+
will be given a mark (of the same type as the current one). When it
|
|
368
|
+
returns true, the existing DOM is kept and reused (its content is
|
|
369
|
+
still managed by ProseMirror); when it returns false, the mark view
|
|
370
|
+
is rebuilt.
|
|
371
|
+
*/
|
|
372
|
+
update?: (mark: Mark) => boolean;
|
|
373
|
+
/**
|
|
366
374
|
Called when a [mutation](https://prosemirror.net/docs/ref/#view.ViewMutationRecord) happens within the
|
|
367
375
|
view. Return false if the editor should re-read the selection or re-parse
|
|
368
376
|
the range around the mutation, true if it can safely be ignored.
|
package/dist/index.js
CHANGED
|
@@ -207,6 +207,9 @@ function clientRect(node) {
|
|
|
207
207
|
top: rect.top, bottom: rect.top + node.clientHeight * scaleY };
|
|
208
208
|
}
|
|
209
209
|
function scrollRectIntoView(view, rect, startDOM) {
|
|
210
|
+
// Skip empty rects with all sides at 0, for example, when the element has no CSS box (display: none)
|
|
211
|
+
if (!nonZero(rect) && rect.left == 0)
|
|
212
|
+
return;
|
|
210
213
|
let scrollThreshold = view.someProp("scrollThreshold") || 0, scrollMargin = view.someProp("scrollMargin") || 5;
|
|
211
214
|
let doc = view.dom.ownerDocument;
|
|
212
215
|
for (let parent = startDOM || view.dom;;) {
|
|
@@ -1273,7 +1276,7 @@ class MarkViewDesc extends ViewDesc {
|
|
|
1273
1276
|
// correspond to an actual node in the document. Unlike mark descs,
|
|
1274
1277
|
// they populate their child array themselves.
|
|
1275
1278
|
class NodeViewDesc extends ViewDesc {
|
|
1276
|
-
constructor(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM
|
|
1279
|
+
constructor(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM) {
|
|
1277
1280
|
super(parent, [], dom, contentDOM);
|
|
1278
1281
|
this.node = node;
|
|
1279
1282
|
this.outerDeco = outerDeco;
|
|
@@ -1319,11 +1322,11 @@ class NodeViewDesc extends ViewDesc {
|
|
|
1319
1322
|
let nodeDOM = dom;
|
|
1320
1323
|
dom = applyOuterDeco(dom, outerDeco, node);
|
|
1321
1324
|
if (spec)
|
|
1322
|
-
return descObj = new CustomNodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM, spec
|
|
1325
|
+
return descObj = new CustomNodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM, spec);
|
|
1323
1326
|
else if (node.isText)
|
|
1324
|
-
return new TextViewDesc(parent, node, outerDeco, innerDeco, dom, nodeDOM
|
|
1327
|
+
return new TextViewDesc(parent, node, outerDeco, innerDeco, dom, nodeDOM);
|
|
1325
1328
|
else
|
|
1326
|
-
return new NodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM
|
|
1329
|
+
return new NodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM);
|
|
1327
1330
|
}
|
|
1328
1331
|
parseRule() {
|
|
1329
1332
|
// Experimental kludge to allow opt-in re-parsing of nodes
|
|
@@ -1507,14 +1510,14 @@ class NodeViewDesc extends ViewDesc {
|
|
|
1507
1510
|
// and used by the view class.
|
|
1508
1511
|
function docViewDesc(doc, outerDeco, innerDeco, dom, view) {
|
|
1509
1512
|
applyOuterDeco(dom, outerDeco, doc);
|
|
1510
|
-
let docView = new NodeViewDesc(undefined, doc, outerDeco, innerDeco, dom, dom, dom
|
|
1513
|
+
let docView = new NodeViewDesc(undefined, doc, outerDeco, innerDeco, dom, dom, dom);
|
|
1511
1514
|
if (docView.contentDOM)
|
|
1512
1515
|
docView.updateChildren(view, 0);
|
|
1513
1516
|
return docView;
|
|
1514
1517
|
}
|
|
1515
1518
|
class TextViewDesc extends NodeViewDesc {
|
|
1516
|
-
constructor(parent, node, outerDeco, innerDeco, dom, nodeDOM
|
|
1517
|
-
super(parent, node, outerDeco, innerDeco, dom, null, nodeDOM
|
|
1519
|
+
constructor(parent, node, outerDeco, innerDeco, dom, nodeDOM) {
|
|
1520
|
+
super(parent, node, outerDeco, innerDeco, dom, null, nodeDOM);
|
|
1518
1521
|
}
|
|
1519
1522
|
parseRule() {
|
|
1520
1523
|
let skip = this.nodeDOM.parentNode;
|
|
@@ -1554,9 +1557,9 @@ class TextViewDesc extends NodeViewDesc {
|
|
|
1554
1557
|
ignoreMutation(mutation) {
|
|
1555
1558
|
return mutation.type != "characterData" && mutation.type != "selection";
|
|
1556
1559
|
}
|
|
1557
|
-
slice(from, to,
|
|
1560
|
+
slice(from, to, _view) {
|
|
1558
1561
|
let node = this.node.cut(from, to), dom = document.createTextNode(node.text);
|
|
1559
|
-
return new TextViewDesc(this.parent, node, this.outerDeco, this.innerDeco, dom, dom
|
|
1562
|
+
return new TextViewDesc(this.parent, node, this.outerDeco, this.innerDeco, dom, dom);
|
|
1560
1563
|
}
|
|
1561
1564
|
markDirty(from, to) {
|
|
1562
1565
|
super.markDirty(from, to);
|
|
@@ -1578,8 +1581,8 @@ class TrailingHackViewDesc extends ViewDesc {
|
|
|
1578
1581
|
// extra checks only have to be made for nodes that are actually
|
|
1579
1582
|
// customized.
|
|
1580
1583
|
class CustomNodeViewDesc extends NodeViewDesc {
|
|
1581
|
-
constructor(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, spec
|
|
1582
|
-
super(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM
|
|
1584
|
+
constructor(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, spec) {
|
|
1585
|
+
super(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM);
|
|
1583
1586
|
this.spec = spec;
|
|
1584
1587
|
}
|
|
1585
1588
|
// A custom `update` method gets to decide whether the update goes
|
|
@@ -1816,6 +1819,19 @@ class ViewTreeUpdater {
|
|
|
1816
1819
|
break;
|
|
1817
1820
|
}
|
|
1818
1821
|
}
|
|
1822
|
+
// When nothing matches, try to update the mark view at this position
|
|
1823
|
+
// in place, so a custom mark view can adapt to a changed mark without
|
|
1824
|
+
// re-creating its DOM.
|
|
1825
|
+
if (found < 0 && this.index < this.top.children.length) {
|
|
1826
|
+
let cur = this.top.children[this.index];
|
|
1827
|
+
if (cur instanceof MarkViewDesc && cur.dirty != NODE_DIRTY &&
|
|
1828
|
+
cur.mark.type == marks[depth].type && cur.spec.update &&
|
|
1829
|
+
!this.isLocked(cur.dom) && cur.spec.update(marks[depth])) {
|
|
1830
|
+
cur.mark = marks[depth];
|
|
1831
|
+
found = this.index;
|
|
1832
|
+
this.changed = true;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1819
1835
|
if (found > -1) {
|
|
1820
1836
|
if (found > this.index) {
|
|
1821
1837
|
this.changed = true;
|
|
@@ -2984,9 +3000,8 @@ const wrapMap = {
|
|
|
2984
3000
|
td: ["table", "tbody", "tr"],
|
|
2985
3001
|
th: ["table", "tbody", "tr"]
|
|
2986
3002
|
};
|
|
2987
|
-
let _detachedDoc = null;
|
|
2988
3003
|
function detachedDoc() {
|
|
2989
|
-
return
|
|
3004
|
+
return document.implementation.createHTMLDocument("title");
|
|
2990
3005
|
}
|
|
2991
3006
|
let _policy = null;
|
|
2992
3007
|
function maybeWrapTrusted(html) {
|
|
@@ -3004,7 +3019,7 @@ function readHTML(html) {
|
|
|
3004
3019
|
let metas = /^(\s*<meta [^>]*>)*/.exec(html);
|
|
3005
3020
|
if (metas)
|
|
3006
3021
|
html = html.slice(metas[0].length);
|
|
3007
|
-
let
|
|
3022
|
+
let doc = detachedDoc(), elt = doc.body;
|
|
3008
3023
|
let firstTag = /<([a-z][^>\s]+)/i.exec(html), wrap;
|
|
3009
3024
|
if (wrap = firstTag && wrapMap[firstTag[1].toLowerCase()])
|
|
3010
3025
|
html = wrap.map(n => "<" + n + ">").join("") + html + wrap.map(n => "</" + n + ">").reverse().join("");
|
|
@@ -3012,6 +3027,18 @@ function readHTML(html) {
|
|
|
3012
3027
|
if (wrap)
|
|
3013
3028
|
for (let i = 0; i < wrap.length; i++)
|
|
3014
3029
|
elt = elt.querySelector(wrap[i]) || elt;
|
|
3030
|
+
// Inline styles defined in the pasted content, so that parse rules pick them up
|
|
3031
|
+
for (let i = 0; i < doc.styleSheets.length; i++) {
|
|
3032
|
+
let style = doc.styleSheets[i];
|
|
3033
|
+
for (let j = 0; j < style.rules.length; j++) {
|
|
3034
|
+
let rule = style.rules[j];
|
|
3035
|
+
if (rule instanceof CSSStyleRule) {
|
|
3036
|
+
let matches = elt.querySelectorAll(rule.selectorText);
|
|
3037
|
+
for (let k = 0; k < matches.length; k++)
|
|
3038
|
+
matches[k].style.cssText += rule.style.cssText;
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3015
3042
|
return elt;
|
|
3016
3043
|
}
|
|
3017
3044
|
// Webkit browsers do some hard-to-predict replacement of regular
|
package/package.json
CHANGED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import {Slice, Fragment, DOMParser, DOMSerializer, ResolvedPos, NodeType, Node} from "prosemirror-model"
|
|
2
|
+
import * as browser from "./browser"
|
|
3
|
+
import {EditorView} from "./index"
|
|
4
|
+
|
|
5
|
+
export function serializeForClipboard(view: EditorView, slice: Slice) {
|
|
6
|
+
view.someProp("transformCopied", f => { slice = f(slice!, view) })
|
|
7
|
+
|
|
8
|
+
let context = [], {content, openStart, openEnd} = slice
|
|
9
|
+
while (openStart > 1 && openEnd > 1 && content.childCount == 1 && content.firstChild!.childCount == 1) {
|
|
10
|
+
openStart--
|
|
11
|
+
openEnd--
|
|
12
|
+
let node = content.firstChild!
|
|
13
|
+
context.push(node.type.name, node.attrs != node.type.defaultAttrs ? node.attrs : null)
|
|
14
|
+
content = node.content
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let serializer = view.someProp("clipboardSerializer") || DOMSerializer.fromSchema(view.state.schema)
|
|
18
|
+
let doc = detachedDoc(), wrap = doc.createElement("div")
|
|
19
|
+
wrap.appendChild(serializer.serializeFragment(content, {document: doc}))
|
|
20
|
+
|
|
21
|
+
let firstChild = wrap.firstChild, needsWrap, wrappers = 0
|
|
22
|
+
while (firstChild && firstChild.nodeType == 1 && (needsWrap = wrapMap[firstChild.nodeName.toLowerCase()])) {
|
|
23
|
+
for (let i = needsWrap.length - 1; i >= 0; i--) {
|
|
24
|
+
let wrapper = doc.createElement(needsWrap[i])
|
|
25
|
+
while (wrap.firstChild) wrapper.appendChild(wrap.firstChild)
|
|
26
|
+
wrap.appendChild(wrapper)
|
|
27
|
+
wrappers++
|
|
28
|
+
}
|
|
29
|
+
firstChild = wrap.firstChild
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (firstChild && firstChild.nodeType == 1)
|
|
33
|
+
(firstChild as HTMLElement).setAttribute(
|
|
34
|
+
"data-pm-slice", `${openStart} ${openEnd}${wrappers ? ` -${wrappers}` : ""} ${JSON.stringify(context)}`)
|
|
35
|
+
|
|
36
|
+
let text = view.someProp("clipboardTextSerializer", f => f(slice, view)) ||
|
|
37
|
+
slice.content.textBetween(0, slice.content.size, "\n\n")
|
|
38
|
+
|
|
39
|
+
return {dom: wrap, text, slice}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Read a slice of content from the clipboard (or drop data).
|
|
43
|
+
export function parseFromClipboard(view: EditorView, text: string, html: string | null, plainText: boolean, $context: ResolvedPos) {
|
|
44
|
+
let inCode = $context.parent.type.spec.code
|
|
45
|
+
let dom: HTMLElement | undefined, slice: Slice | undefined
|
|
46
|
+
if (!html && !text) return null
|
|
47
|
+
let asText = !!text && (plainText || inCode || !html)
|
|
48
|
+
if (asText) {
|
|
49
|
+
view.someProp("transformPastedText", f => { text = f(text, inCode || plainText, view) })
|
|
50
|
+
if (inCode) {
|
|
51
|
+
slice = new Slice(Fragment.from(view.state.schema.text(text.replace(/\r\n?/g, "\n"))), 0, 0)
|
|
52
|
+
view.someProp("transformPasted", f => { slice = f(slice!, view, true) })
|
|
53
|
+
return slice
|
|
54
|
+
}
|
|
55
|
+
let parsed = view.someProp("clipboardTextParser", f => f(text, $context, plainText, view))
|
|
56
|
+
if (parsed) {
|
|
57
|
+
slice = parsed
|
|
58
|
+
} else {
|
|
59
|
+
let marks = $context.marks()
|
|
60
|
+
let {schema} = view.state, serializer = DOMSerializer.fromSchema(schema)
|
|
61
|
+
dom = document.createElement("div")
|
|
62
|
+
text.split(/(?:\r\n?|\n)+/).forEach(block => {
|
|
63
|
+
let p = dom!.appendChild(document.createElement("p"))
|
|
64
|
+
if (block) p.appendChild(serializer.serializeNode(schema.text(block, marks)))
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
view.someProp("transformPastedHTML", f => { html = f(html!, view) })
|
|
69
|
+
dom = readHTML(html!)
|
|
70
|
+
if (browser.webkit) restoreReplacedSpaces(dom)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let contextNode = dom && dom.querySelector("[data-pm-slice]")
|
|
74
|
+
let sliceData = contextNode && /^(\d+) (\d+)(?: -(\d+))? (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "")
|
|
75
|
+
if (sliceData && sliceData[3]) for (let i = +sliceData[3]; i > 0; i--) {
|
|
76
|
+
let child = dom!.firstChild
|
|
77
|
+
while (child && child.nodeType != 1) child = child.nextSibling
|
|
78
|
+
if (!child) break
|
|
79
|
+
dom = child as HTMLElement
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!slice) {
|
|
83
|
+
let parser = view.someProp("clipboardParser") || view.someProp("domParser") || DOMParser.fromSchema(view.state.schema)
|
|
84
|
+
slice = parser.parseSlice(dom!, {
|
|
85
|
+
preserveWhitespace: !!(asText || sliceData),
|
|
86
|
+
context: $context,
|
|
87
|
+
ruleFromNode(dom) {
|
|
88
|
+
if (dom.nodeName == "BR" && !dom.nextSibling &&
|
|
89
|
+
dom.parentNode && !inlineParents.test(dom.parentNode.nodeName)) return {ignore: true}
|
|
90
|
+
return null
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
if (sliceData) {
|
|
95
|
+
slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[4])
|
|
96
|
+
} else { // HTML wasn't created by ProseMirror. Make sure top-level siblings are coherent
|
|
97
|
+
slice = Slice.maxOpen(normalizeSiblings(slice.content, $context), true)
|
|
98
|
+
if (slice.openStart || slice.openEnd) {
|
|
99
|
+
let openStart = 0, openEnd = 0
|
|
100
|
+
for (let node = slice.content.firstChild; openStart < slice.openStart && !node!.type.spec.isolating;
|
|
101
|
+
openStart++, node = node!.firstChild) {}
|
|
102
|
+
for (let node = slice.content.lastChild; openEnd < slice.openEnd && !node!.type.spec.isolating;
|
|
103
|
+
openEnd++, node = node!.lastChild) {}
|
|
104
|
+
slice = closeSlice(slice, openStart, openEnd)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
view.someProp("transformPasted", f => { slice = f(slice!, view, asText) })
|
|
109
|
+
return slice
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const inlineParents = /^(a|abbr|acronym|b|cite|code|del|em|i|ins|kbd|label|output|q|ruby|s|samp|span|strong|sub|sup|time|u|tt|var)$/i
|
|
113
|
+
|
|
114
|
+
// Takes a slice parsed with parseSlice, which means there hasn't been
|
|
115
|
+
// any content-expression checking done on the top nodes, tries to
|
|
116
|
+
// find a parent node in the current context that might fit the nodes,
|
|
117
|
+
// and if successful, rebuilds the slice so that it fits into that parent.
|
|
118
|
+
//
|
|
119
|
+
// This addresses the problem that Transform.replace expects a
|
|
120
|
+
// coherent slice, and will fail to place a set of siblings that don't
|
|
121
|
+
// fit anywhere in the schema.
|
|
122
|
+
function normalizeSiblings(fragment: Fragment, $context: ResolvedPos) {
|
|
123
|
+
if (fragment.childCount < 2) return fragment
|
|
124
|
+
for (let d = $context.depth; d >= 0; d--) {
|
|
125
|
+
let parent = $context.node(d)
|
|
126
|
+
let match = parent.contentMatchAt($context.index(d))
|
|
127
|
+
let lastWrap: readonly NodeType[] | undefined, result: Node[] | null = []
|
|
128
|
+
fragment.forEach(node => {
|
|
129
|
+
if (!result) return
|
|
130
|
+
let wrap = match.findWrapping(node.type), inLast
|
|
131
|
+
if (!wrap) return result = null
|
|
132
|
+
if (inLast = result.length && lastWrap!.length && addToSibling(wrap, lastWrap!, node, result[result.length - 1], 0)) {
|
|
133
|
+
result[result.length - 1] = inLast
|
|
134
|
+
} else {
|
|
135
|
+
if (result.length) result[result.length - 1] = closeRight(result[result.length - 1], lastWrap!.length)
|
|
136
|
+
let wrapped = withWrappers(node, wrap)
|
|
137
|
+
result.push(wrapped)
|
|
138
|
+
match = match.matchType(wrapped.type)!
|
|
139
|
+
lastWrap = wrap
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
if (result) return Fragment.from(result)
|
|
143
|
+
}
|
|
144
|
+
return fragment
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function withWrappers(node: Node, wrap: readonly NodeType[], from = 0) {
|
|
148
|
+
for (let i = wrap.length - 1; i >= from; i--)
|
|
149
|
+
node = wrap[i].create(null, Fragment.from(node))
|
|
150
|
+
return node
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Used to group adjacent nodes wrapped in similar parents by
|
|
154
|
+
// normalizeSiblings into the same parent node
|
|
155
|
+
function addToSibling(wrap: readonly NodeType[], lastWrap: readonly NodeType[],
|
|
156
|
+
node: Node, sibling: Node, depth: number): Node | undefined {
|
|
157
|
+
if (depth < wrap.length && depth < lastWrap.length && wrap[depth] == lastWrap[depth]) {
|
|
158
|
+
let inner = addToSibling(wrap, lastWrap, node, sibling.lastChild!, depth + 1)
|
|
159
|
+
if (inner) return sibling.copy(sibling.content.replaceChild(sibling.childCount - 1, inner))
|
|
160
|
+
let match = sibling.contentMatchAt(sibling.childCount)
|
|
161
|
+
if (match.matchType(depth == wrap.length - 1 ? node.type : wrap[depth + 1]))
|
|
162
|
+
return sibling.copy(sibling.content.append(Fragment.from(withWrappers(node, wrap, depth + 1))))
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function closeRight(node: Node, depth: number) {
|
|
167
|
+
if (depth == 0) return node
|
|
168
|
+
let fragment = node.content.replaceChild(node.childCount - 1, closeRight(node.lastChild!, depth - 1))
|
|
169
|
+
let fill = node.contentMatchAt(node.childCount).fillBefore(Fragment.empty, true)!
|
|
170
|
+
return node.copy(fragment.append(fill))
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function closeRange(fragment: Fragment, side: number, from: number, to: number, depth: number, openEnd: number) {
|
|
174
|
+
let node = side < 0 ? fragment.firstChild! : fragment.lastChild!, inner = node.content
|
|
175
|
+
if (fragment.childCount > 1) openEnd = 0
|
|
176
|
+
if (depth < to - 1) inner = closeRange(inner, side, from, to, depth + 1, openEnd)
|
|
177
|
+
if (depth >= from)
|
|
178
|
+
inner = side < 0 ? node.contentMatchAt(0)!.fillBefore(inner, openEnd <= depth)!.append(inner)
|
|
179
|
+
: inner.append(node.contentMatchAt(node.childCount)!.fillBefore(Fragment.empty, true)!)
|
|
180
|
+
return fragment.replaceChild(side < 0 ? 0 : fragment.childCount - 1, node.copy(inner))
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function closeSlice(slice: Slice, openStart: number, openEnd: number) {
|
|
184
|
+
if (openStart < slice.openStart)
|
|
185
|
+
slice = new Slice(closeRange(slice.content, -1, openStart, slice.openStart, 0, slice.openEnd), openStart, slice.openEnd)
|
|
186
|
+
if (openEnd < slice.openEnd)
|
|
187
|
+
slice = new Slice(closeRange(slice.content, 1, openEnd, slice.openEnd, 0, 0), slice.openStart, openEnd)
|
|
188
|
+
return slice
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Trick from jQuery -- some elements must be wrapped in other
|
|
192
|
+
// elements for innerHTML to work. I.e. if you do `div.innerHTML =
|
|
193
|
+
// "<td>..</td>"` the table cells are ignored.
|
|
194
|
+
const wrapMap: {[node: string]: string[]} = {
|
|
195
|
+
thead: ["table"],
|
|
196
|
+
tbody: ["table"],
|
|
197
|
+
tfoot: ["table"],
|
|
198
|
+
caption: ["table"],
|
|
199
|
+
colgroup: ["table"],
|
|
200
|
+
col: ["table", "colgroup"],
|
|
201
|
+
tr: ["table", "tbody"],
|
|
202
|
+
td: ["table", "tbody", "tr"],
|
|
203
|
+
th: ["table", "tbody", "tr"]
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function detachedDoc() {
|
|
207
|
+
return document.implementation.createHTMLDocument("title")
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
let _policy: any = null
|
|
211
|
+
|
|
212
|
+
function maybeWrapTrusted(html: string): string {
|
|
213
|
+
let trustedTypes = (window as any).trustedTypes
|
|
214
|
+
if (!trustedTypes) return html
|
|
215
|
+
// With the require-trusted-types-for CSP, Chrome will block
|
|
216
|
+
// innerHTML, even on a detached document. This wraps the string in
|
|
217
|
+
// a way that makes the browser allow us to use its parser again.
|
|
218
|
+
if (!_policy)
|
|
219
|
+
_policy = trustedTypes.defaultPolicy || trustedTypes.createPolicy("ProseMirrorClipboard", {createHTML: (s: string) => s})
|
|
220
|
+
return _policy.createHTML(html)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function readHTML(html: string) {
|
|
224
|
+
html = "\n\n" + html + "\n\n"
|
|
225
|
+
let metas = /^(\s*<meta [^>]*>)*/.exec(html)
|
|
226
|
+
if (metas) html = html.slice(metas[0].length)
|
|
227
|
+
let doc = detachedDoc(), elt = doc.createElement("div")
|
|
228
|
+
let firstTag = /<([a-z][^>\s]+)/i.exec(html), wrap
|
|
229
|
+
if (wrap = firstTag && wrapMap[firstTag[1].toLowerCase()])
|
|
230
|
+
html = wrap.map(n => "<" + n + ">").join("") + html + wrap.map(n => "</" + n + ">").reverse().join("")
|
|
231
|
+
elt.innerHTML = maybeWrapTrusted(html)
|
|
232
|
+
if (wrap) for (let i = 0; i < wrap.length; i++) elt = elt.querySelector(wrap[i]) || elt
|
|
233
|
+
// Inline styles defined in the pasted content, so that parse rules pick them up
|
|
234
|
+
for (let i = 0; i < doc.styleSheets.length; i++) {
|
|
235
|
+
let style = doc.styleSheets[i]
|
|
236
|
+
for (let j = 0; j < style.rules.length; j++) {
|
|
237
|
+
let rule = style.rules[j]
|
|
238
|
+
if (rule instanceof CSSStyleRule) {
|
|
239
|
+
let matches = elt.querySelectorAll(rule.selectorText)
|
|
240
|
+
for (let k = 0; k < matches.length; k++)
|
|
241
|
+
(matches[k] as HTMLElement).style.cssText += rule.style.cssText
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return elt
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Webkit browsers do some hard-to-predict replacement of regular
|
|
249
|
+
// spaces with non-breaking spaces when putting content on the
|
|
250
|
+
// clipboard. This tries to convert such non-breaking spaces (which
|
|
251
|
+
// will be wrapped in a plain span on Chrome, a span with class
|
|
252
|
+
// Apple-converted-space on Safari) back to regular spaces.
|
|
253
|
+
function restoreReplacedSpaces(dom: HTMLElement) {
|
|
254
|
+
let nodes = dom.querySelectorAll(browser.chrome ? "span:not([class]):not([style])" : "span.Apple-converted-space")
|
|
255
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
256
|
+
let node = nodes[i]
|
|
257
|
+
if (node.childNodes.length == 1 && node.textContent == "\u00a0" && node.parentNode)
|
|
258
|
+
node.parentNode.replaceChild(dom.ownerDocument.createTextNode(" "), node)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function addContext(slice: Slice, context: string) {
|
|
263
|
+
if (!slice.size) return slice
|
|
264
|
+
let schema = slice.content.firstChild!.type.schema, array
|
|
265
|
+
try { array = JSON.parse(context) }
|
|
266
|
+
catch(e) { return slice }
|
|
267
|
+
let {content, openStart, openEnd} = slice
|
|
268
|
+
for (let i = array.length - 2; i >= 0; i -= 2) {
|
|
269
|
+
let type = schema.nodes[array[i]]
|
|
270
|
+
if (!type || type.hasRequiredAttrs()) break
|
|
271
|
+
content = Fragment.from(type.create(array[i + 1], content))
|
|
272
|
+
openStart++; openEnd++
|
|
273
|
+
}
|
|
274
|
+
return new Slice(content, openStart, openEnd)
|
|
275
|
+
}
|
package/src/clipboard.ts
CHANGED
|
@@ -203,9 +203,8 @@ const wrapMap: {[node: string]: string[]} = {
|
|
|
203
203
|
th: ["table", "tbody", "tr"]
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
let _detachedDoc: Document | null = null
|
|
207
206
|
function detachedDoc() {
|
|
208
|
-
return
|
|
207
|
+
return document.implementation.createHTMLDocument("title")
|
|
209
208
|
}
|
|
210
209
|
|
|
211
210
|
let _policy: any = null
|
|
@@ -224,12 +223,24 @@ function maybeWrapTrusted(html: string): string {
|
|
|
224
223
|
function readHTML(html: string) {
|
|
225
224
|
let metas = /^(\s*<meta [^>]*>)*/.exec(html)
|
|
226
225
|
if (metas) html = html.slice(metas[0].length)
|
|
227
|
-
let
|
|
226
|
+
let doc = detachedDoc(), elt = doc.body
|
|
228
227
|
let firstTag = /<([a-z][^>\s]+)/i.exec(html), wrap
|
|
229
228
|
if (wrap = firstTag && wrapMap[firstTag[1].toLowerCase()])
|
|
230
229
|
html = wrap.map(n => "<" + n + ">").join("") + html + wrap.map(n => "</" + n + ">").reverse().join("")
|
|
231
230
|
elt.innerHTML = maybeWrapTrusted(html)
|
|
232
231
|
if (wrap) for (let i = 0; i < wrap.length; i++) elt = elt.querySelector(wrap[i]) || elt
|
|
232
|
+
// Inline styles defined in the pasted content, so that parse rules pick them up
|
|
233
|
+
for (let i = 0; i < doc.styleSheets.length; i++) {
|
|
234
|
+
let style = doc.styleSheets[i]
|
|
235
|
+
for (let j = 0; j < style.rules.length; j++) {
|
|
236
|
+
let rule = style.rules[j]
|
|
237
|
+
if (rule instanceof CSSStyleRule) {
|
|
238
|
+
let matches = elt.querySelectorAll(rule.selectorText)
|
|
239
|
+
for (let k = 0; k < matches.length; k++)
|
|
240
|
+
(matches[k] as HTMLElement).style.cssText += rule.style.cssText
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
233
244
|
return elt
|
|
234
245
|
}
|
|
235
246
|
|
package/src/domcoords.ts
CHANGED
|
@@ -30,6 +30,9 @@ function clientRect(node: HTMLElement): Rect {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function scrollRectIntoView(view: EditorView, rect: Rect, startDOM: Node) {
|
|
33
|
+
// Skip empty rects with all sides at 0, for example, when the element has no CSS box (display: none)
|
|
34
|
+
if (!nonZero(rect) && rect.left == 0) return
|
|
35
|
+
|
|
33
36
|
let scrollThreshold = view.someProp("scrollThreshold") || 0, scrollMargin = view.someProp("scrollMargin") || 5
|
|
34
37
|
let doc = view.dom.ownerDocument
|
|
35
38
|
for (let parent: Node | null = startDOM || view.dom;;) {
|
|
@@ -328,7 +331,7 @@ export function posAtCoords(view: EditorView, coords: {top: number, left: number
|
|
|
328
331
|
return {pos, inside: desc ? desc.posAtStart - desc.border : -1}
|
|
329
332
|
}
|
|
330
333
|
|
|
331
|
-
function nonZero(rect:
|
|
334
|
+
function nonZero(rect: Rect) {
|
|
332
335
|
return rect.top < rect.bottom || rect.left < rect.right
|
|
333
336
|
}
|
|
334
337
|
|
package/src/viewdesc.ts
CHANGED
|
@@ -105,6 +105,13 @@ export interface MarkView {
|
|
|
105
105
|
/// present, the `dom` property is used as the content DOM.
|
|
106
106
|
contentDOM?: HTMLElement | null
|
|
107
107
|
|
|
108
|
+
/// When given, this is called when the view is updating itself. It
|
|
109
|
+
/// will be given a mark (of the same type as the current one). When it
|
|
110
|
+
/// returns true, the existing DOM is kept and reused (its content is
|
|
111
|
+
/// still managed by ProseMirror); when it returns false, the mark view
|
|
112
|
+
/// is rebuilt.
|
|
113
|
+
update?: (mark: Mark) => boolean
|
|
114
|
+
|
|
108
115
|
/// Called when a [mutation](#view.ViewMutationRecord) happens within the
|
|
109
116
|
/// view. Return false if the editor should re-read the selection or re-parse
|
|
110
117
|
/// the range around the mutation, true if it can safely be ignored.
|
|
@@ -610,7 +617,7 @@ class CompositionViewDesc extends ViewDesc {
|
|
|
610
617
|
// some cases they will be split more often than would appear
|
|
611
618
|
// necessary.
|
|
612
619
|
class MarkViewDesc extends ViewDesc {
|
|
613
|
-
constructor(parent: ViewDesc,
|
|
620
|
+
constructor(parent: ViewDesc, public mark: Mark, dom: HTMLElement, contentDOM: HTMLElement, readonly spec: MarkView) {
|
|
614
621
|
super(parent, [], dom, contentDOM)
|
|
615
622
|
}
|
|
616
623
|
|
|
@@ -672,8 +679,6 @@ export class NodeViewDesc extends ViewDesc {
|
|
|
672
679
|
dom: DOMNode,
|
|
673
680
|
contentDOM: HTMLElement | null,
|
|
674
681
|
readonly nodeDOM: DOMNode,
|
|
675
|
-
view: EditorView,
|
|
676
|
-
pos: number
|
|
677
682
|
) {
|
|
678
683
|
super(parent, [], dom, contentDOM)
|
|
679
684
|
}
|
|
@@ -715,11 +720,11 @@ export class NodeViewDesc extends ViewDesc {
|
|
|
715
720
|
|
|
716
721
|
if (spec)
|
|
717
722
|
return descObj = new CustomNodeViewDesc(parent, node, outerDeco, innerDeco, dom as HTMLElement, contentDOM || null,
|
|
718
|
-
nodeDOM as HTMLElement, spec
|
|
723
|
+
nodeDOM as HTMLElement, spec)
|
|
719
724
|
else if (node.isText)
|
|
720
|
-
return new TextViewDesc(parent, node, outerDeco, innerDeco, dom, nodeDOM
|
|
725
|
+
return new TextViewDesc(parent, node, outerDeco, innerDeco, dom, nodeDOM)
|
|
721
726
|
else
|
|
722
|
-
return new NodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM
|
|
727
|
+
return new NodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM)
|
|
723
728
|
}
|
|
724
729
|
|
|
725
730
|
parseRule() {
|
|
@@ -906,15 +911,15 @@ export class NodeViewDesc extends ViewDesc {
|
|
|
906
911
|
export function docViewDesc(doc: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource,
|
|
907
912
|
dom: HTMLElement, view: EditorView): NodeViewDesc {
|
|
908
913
|
applyOuterDeco(dom, outerDeco, doc)
|
|
909
|
-
let docView = new NodeViewDesc(undefined, doc, outerDeco, innerDeco, dom, dom, dom
|
|
914
|
+
let docView = new NodeViewDesc(undefined, doc, outerDeco, innerDeco, dom, dom, dom)
|
|
910
915
|
if (docView.contentDOM) docView.updateChildren(view, 0)
|
|
911
916
|
return docView
|
|
912
917
|
}
|
|
913
918
|
|
|
914
919
|
class TextViewDesc extends NodeViewDesc {
|
|
915
920
|
constructor(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[],
|
|
916
|
-
innerDeco: DecorationSource, dom: DOMNode, nodeDOM: DOMNode
|
|
917
|
-
super(parent, node, outerDeco, innerDeco, dom, null, nodeDOM
|
|
921
|
+
innerDeco: DecorationSource, dom: DOMNode, nodeDOM: DOMNode) {
|
|
922
|
+
super(parent, node, outerDeco, innerDeco, dom, null, nodeDOM)
|
|
918
923
|
}
|
|
919
924
|
|
|
920
925
|
parseRule() {
|
|
@@ -955,9 +960,9 @@ class TextViewDesc extends NodeViewDesc {
|
|
|
955
960
|
return mutation.type != "characterData" && mutation.type != "selection"
|
|
956
961
|
}
|
|
957
962
|
|
|
958
|
-
slice(from: number, to: number,
|
|
963
|
+
slice(from: number, to: number, _view?: EditorView) {
|
|
959
964
|
let node = this.node.cut(from, to), dom = document.createTextNode(node.text!)
|
|
960
|
-
return new TextViewDesc(this.parent, node, this.outerDeco, this.innerDeco, dom, dom
|
|
965
|
+
return new TextViewDesc(this.parent, node, this.outerDeco, this.innerDeco, dom, dom)
|
|
961
966
|
}
|
|
962
967
|
|
|
963
968
|
markDirty(from: number, to: number) {
|
|
@@ -985,9 +990,8 @@ class TrailingHackViewDesc extends ViewDesc {
|
|
|
985
990
|
// customized.
|
|
986
991
|
class CustomNodeViewDesc extends NodeViewDesc {
|
|
987
992
|
constructor(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource,
|
|
988
|
-
dom: HTMLElement, contentDOM: HTMLElement | null, nodeDOM: HTMLElement, readonly spec: NodeView
|
|
989
|
-
|
|
990
|
-
super(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, view, pos)
|
|
993
|
+
dom: HTMLElement, contentDOM: HTMLElement | null, nodeDOM: HTMLElement, readonly spec: NodeView) {
|
|
994
|
+
super(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM)
|
|
991
995
|
}
|
|
992
996
|
|
|
993
997
|
// A custom `update` method gets to decide whether the update goes
|
|
@@ -1220,6 +1224,19 @@ class ViewTreeUpdater {
|
|
|
1220
1224
|
let next = this.top.children[i]
|
|
1221
1225
|
if (next.matchesMark(marks[depth]) && !this.isLocked(next.dom)) { found = i; break }
|
|
1222
1226
|
}
|
|
1227
|
+
// When nothing matches, try to update the mark view at this position
|
|
1228
|
+
// in place, so a custom mark view can adapt to a changed mark without
|
|
1229
|
+
// re-creating its DOM.
|
|
1230
|
+
if (found < 0 && this.index < this.top.children.length) {
|
|
1231
|
+
let cur = this.top.children[this.index]
|
|
1232
|
+
if (cur instanceof MarkViewDesc && cur.dirty != NODE_DIRTY &&
|
|
1233
|
+
cur.mark.type == marks[depth].type && cur.spec.update &&
|
|
1234
|
+
!this.isLocked(cur.dom) && cur.spec.update(marks[depth])) {
|
|
1235
|
+
cur.mark = marks[depth]
|
|
1236
|
+
found = this.index
|
|
1237
|
+
this.changed = true
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1223
1240
|
if (found > -1) {
|
|
1224
1241
|
if (found > this.index) {
|
|
1225
1242
|
this.changed = true
|