prosemirror-view 1.41.8 → 1.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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
@@ -1273,7 +1273,7 @@ class MarkViewDesc extends ViewDesc {
1273
1273
  // correspond to an actual node in the document. Unlike mark descs,
1274
1274
  // they populate their child array themselves.
1275
1275
  class NodeViewDesc extends ViewDesc {
1276
- constructor(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, view, pos) {
1276
+ constructor(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM) {
1277
1277
  super(parent, [], dom, contentDOM);
1278
1278
  this.node = node;
1279
1279
  this.outerDeco = outerDeco;
@@ -1319,11 +1319,11 @@ class NodeViewDesc extends ViewDesc {
1319
1319
  let nodeDOM = dom;
1320
1320
  dom = applyOuterDeco(dom, outerDeco, node);
1321
1321
  if (spec)
1322
- return descObj = new CustomNodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM, spec, view, pos + 1);
1322
+ return descObj = new CustomNodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM, spec);
1323
1323
  else if (node.isText)
1324
- return new TextViewDesc(parent, node, outerDeco, innerDeco, dom, nodeDOM, view);
1324
+ return new TextViewDesc(parent, node, outerDeco, innerDeco, dom, nodeDOM);
1325
1325
  else
1326
- return new NodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM, view, pos + 1);
1326
+ return new NodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM);
1327
1327
  }
1328
1328
  parseRule() {
1329
1329
  // Experimental kludge to allow opt-in re-parsing of nodes
@@ -1507,14 +1507,14 @@ class NodeViewDesc extends ViewDesc {
1507
1507
  // and used by the view class.
1508
1508
  function docViewDesc(doc, outerDeco, innerDeco, dom, view) {
1509
1509
  applyOuterDeco(dom, outerDeco, doc);
1510
- let docView = new NodeViewDesc(undefined, doc, outerDeco, innerDeco, dom, dom, dom, view, 0);
1510
+ let docView = new NodeViewDesc(undefined, doc, outerDeco, innerDeco, dom, dom, dom);
1511
1511
  if (docView.contentDOM)
1512
1512
  docView.updateChildren(view, 0);
1513
1513
  return docView;
1514
1514
  }
1515
1515
  class TextViewDesc extends NodeViewDesc {
1516
- constructor(parent, node, outerDeco, innerDeco, dom, nodeDOM, view) {
1517
- super(parent, node, outerDeco, innerDeco, dom, null, nodeDOM, view, 0);
1516
+ constructor(parent, node, outerDeco, innerDeco, dom, nodeDOM) {
1517
+ super(parent, node, outerDeco, innerDeco, dom, null, nodeDOM);
1518
1518
  }
1519
1519
  parseRule() {
1520
1520
  let skip = this.nodeDOM.parentNode;
@@ -1554,9 +1554,9 @@ class TextViewDesc extends NodeViewDesc {
1554
1554
  ignoreMutation(mutation) {
1555
1555
  return mutation.type != "characterData" && mutation.type != "selection";
1556
1556
  }
1557
- slice(from, to, view) {
1557
+ slice(from, to, _view) {
1558
1558
  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, view);
1559
+ return new TextViewDesc(this.parent, node, this.outerDeco, this.innerDeco, dom, dom);
1560
1560
  }
1561
1561
  markDirty(from, to) {
1562
1562
  super.markDirty(from, to);
@@ -1578,8 +1578,8 @@ class TrailingHackViewDesc extends ViewDesc {
1578
1578
  // extra checks only have to be made for nodes that are actually
1579
1579
  // customized.
1580
1580
  class CustomNodeViewDesc extends NodeViewDesc {
1581
- constructor(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, spec, view, pos) {
1582
- super(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, view, pos);
1581
+ constructor(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, spec) {
1582
+ super(parent, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM);
1583
1583
  this.spec = spec;
1584
1584
  }
1585
1585
  // A custom `update` method gets to decide whether the update goes
@@ -1816,6 +1816,19 @@ class ViewTreeUpdater {
1816
1816
  break;
1817
1817
  }
1818
1818
  }
1819
+ // When nothing matches, try to update the mark view at this position
1820
+ // in place, so a custom mark view can adapt to a changed mark without
1821
+ // re-creating its DOM.
1822
+ if (found < 0 && this.index < this.top.children.length) {
1823
+ let cur = this.top.children[this.index];
1824
+ if (cur instanceof MarkViewDesc && cur.dirty != NODE_DIRTY &&
1825
+ cur.mark.type == marks[depth].type && cur.spec.update &&
1826
+ !this.isLocked(cur.dom) && cur.spec.update(marks[depth])) {
1827
+ cur.mark = marks[depth];
1828
+ found = this.index;
1829
+ this.changed = true;
1830
+ }
1831
+ }
1819
1832
  if (found > -1) {
1820
1833
  if (found > this.index) {
1821
1834
  this.changed = true;
@@ -2244,14 +2257,14 @@ function selectionToDOM(view, force = false) {
2244
2257
  syncNodeSelection(view, sel);
2245
2258
  if (!editorOwnsSelection(view))
2246
2259
  return;
2247
- // The delayed drag selection causes issues with Cell Selections
2248
- // in Safari. And the drag selection delay is to workarond issues
2249
- // which only present in Chrome.
2250
- if (!force && view.input.mouseDown && view.input.mouseDown.allowDefault && chrome) {
2260
+ // Need to delay selection normalization during a native selection
2261
+ // drag on Chrome, or it will cause further dragging to glitch.
2262
+ let mouseDown = view.input.mouseDown;
2263
+ if (!force && chrome && mouseDown) {
2251
2264
  let domSel = view.domSelectionRange(), curSel = view.domObserver.currentSelection;
2252
2265
  if (domSel.anchorNode && curSel.anchorNode &&
2253
- isEquivalentPosition(domSel.anchorNode, domSel.anchorOffset, curSel.anchorNode, curSel.anchorOffset)) {
2254
- view.input.mouseDown.delayedSelectionSync = true;
2266
+ isEquivalentPosition(domSel.anchorNode, domSel.anchorOffset, curSel.anchorNode, curSel.anchorOffset) &&
2267
+ mouseDown.delaySelUpdate()) {
2255
2268
  view.domObserver.setCurSelection();
2256
2269
  return;
2257
2270
  }
@@ -2984,9 +2997,8 @@ const wrapMap = {
2984
2997
  td: ["table", "tbody", "tr"],
2985
2998
  th: ["table", "tbody", "tr"]
2986
2999
  };
2987
- let _detachedDoc = null;
2988
3000
  function detachedDoc() {
2989
- return _detachedDoc || (_detachedDoc = document.implementation.createHTMLDocument("title"));
3001
+ return document.implementation.createHTMLDocument("title");
2990
3002
  }
2991
3003
  let _policy = null;
2992
3004
  function maybeWrapTrusted(html) {
@@ -3004,7 +3016,7 @@ function readHTML(html) {
3004
3016
  let metas = /^(\s*<meta [^>]*>)*/.exec(html);
3005
3017
  if (metas)
3006
3018
  html = html.slice(metas[0].length);
3007
- let elt = detachedDoc().createElement("div");
3019
+ let doc = detachedDoc(), elt = doc.body;
3008
3020
  let firstTag = /<([a-z][^>\s]+)/i.exec(html), wrap;
3009
3021
  if (wrap = firstTag && wrapMap[firstTag[1].toLowerCase()])
3010
3022
  html = wrap.map(n => "<" + n + ">").join("") + html + wrap.map(n => "</" + n + ">").reverse().join("");
@@ -3012,6 +3024,18 @@ function readHTML(html) {
3012
3024
  if (wrap)
3013
3025
  for (let i = 0; i < wrap.length; i++)
3014
3026
  elt = elt.querySelector(wrap[i]) || elt;
3027
+ // Inline styles defined in the pasted content, so that parse rules pick them up
3028
+ for (let i = 0; i < doc.styleSheets.length; i++) {
3029
+ let style = doc.styleSheets[i];
3030
+ for (let j = 0; j < style.rules.length; j++) {
3031
+ let rule = style.rules[j];
3032
+ if (rule instanceof CSSStyleRule) {
3033
+ let matches = elt.querySelectorAll(rule.selectorText);
3034
+ for (let k = 0; k < matches.length; k++)
3035
+ matches[k].style.cssText += rule.style.cssText;
3036
+ }
3037
+ }
3038
+ }
3015
3039
  return elt;
3016
3040
  }
3017
3041
  // Webkit browsers do some hard-to-predict replacement of regular
@@ -3103,6 +3127,8 @@ function setSelectionOrigin(view, origin) {
3103
3127
  view.input.lastSelectionTime = Date.now();
3104
3128
  }
3105
3129
  function destroyInput(view) {
3130
+ if (view.input.mouseDown)
3131
+ view.input.mouseDown.done();
3106
3132
  view.domObserver.stop();
3107
3133
  for (let type in view.input.eventHandlers)
3108
3134
  view.dom.removeEventListener(type, view.input.eventHandlers[type]);
@@ -3141,7 +3167,7 @@ function dispatchEvent(view, event) {
3141
3167
  editHandlers.keydown = (view, _event) => {
3142
3168
  let event = _event;
3143
3169
  view.input.shiftKey = event.keyCode == 16 || event.shiftKey;
3144
- if (inOrNearComposition(view, event))
3170
+ if (inOrNearComposition(view))
3145
3171
  return;
3146
3172
  view.input.lastKeyCode = event.keyCode;
3147
3173
  view.input.lastKeyCodeTime = Date.now();
@@ -3179,7 +3205,7 @@ editHandlers.keyup = (view, event) => {
3179
3205
  };
3180
3206
  editHandlers.keypress = (view, _event) => {
3181
3207
  let event = _event;
3182
- if (inOrNearComposition(view, event) || !event.charCode ||
3208
+ if (inOrNearComposition(view) || !event.charCode ||
3183
3209
  event.ctrlKey && !event.altKey || mac && event.metaKey)
3184
3210
  return;
3185
3211
  if (view.someProp("handleKeyPress", f => f(view, event))) {
@@ -3274,26 +3300,28 @@ function handleTripleClick(view, pos, inside, event) {
3274
3300
  function defaultTripleClick(view, inside, event) {
3275
3301
  if (event.button != 0)
3276
3302
  return false;
3277
- let doc = view.state.doc;
3278
- if (inside == -1) {
3279
- if (doc.inlineContent) {
3280
- updateSelection(view, TextSelection.create(doc, 0, doc.content.size), "pointer");
3281
- return true;
3282
- }
3303
+ let selection = selectionForTripleClick(view, inside, true), doc = view.state.doc;
3304
+ if (!selection)
3283
3305
  return false;
3284
- }
3306
+ updateSelection(view, selection, "pointer");
3307
+ if (selection instanceof TextSelection && doc.eq(view.state.doc))
3308
+ view.input.mouseDown = new TripleClickDrag(view, selection);
3309
+ return true;
3310
+ }
3311
+ function selectionForTripleClick(view, inside, selectNodes) {
3312
+ let doc = view.state.doc;
3313
+ if (inside == -1)
3314
+ return doc.inlineContent ? TextSelection.create(doc, 0, doc.content.size) : null;
3285
3315
  let $pos = doc.resolve(inside);
3286
3316
  for (let i = $pos.depth + 1; i > 0; i--) {
3287
3317
  let node = i > $pos.depth ? $pos.nodeAfter : $pos.node(i);
3288
3318
  let nodePos = $pos.before(i);
3289
3319
  if (node.inlineContent)
3290
- updateSelection(view, TextSelection.create(doc, nodePos + 1, nodePos + 1 + node.content.size), "pointer");
3291
- else if (NodeSelection.isSelectable(node))
3292
- updateSelection(view, NodeSelection.create(doc, nodePos), "pointer");
3293
- else
3294
- continue;
3295
- return true;
3320
+ return TextSelection.create(doc, nodePos + 1, nodePos + 1 + node.content.size);
3321
+ else if (selectNodes && NodeSelection.isSelectable(node))
3322
+ return NodeSelection.create(doc, nodePos);
3296
3323
  }
3324
+ return null;
3297
3325
  }
3298
3326
  function forceDOMFlush(view) {
3299
3327
  return endComposition(view);
@@ -3312,13 +3340,13 @@ handlers.mousedown = (view, _event) => {
3312
3340
  type = "tripleClick";
3313
3341
  }
3314
3342
  view.input.lastClick = { time: now, x: event.clientX, y: event.clientY, type, button: event.button };
3343
+ if (view.input.mouseDown)
3344
+ view.input.mouseDown.done();
3315
3345
  let pos = view.posAtCoords(eventCoords(event));
3316
3346
  if (!pos)
3317
3347
  return;
3318
3348
  if (type == "singleClick") {
3319
- if (view.input.mouseDown)
3320
- view.input.mouseDown.done();
3321
- view.input.mouseDown = new MouseDown(view, pos, event, !!flushed);
3349
+ view.input.mouseDown = new LeftMouseDown(view, pos, event, !!flushed);
3322
3350
  }
3323
3351
  else if ((type == "doubleClick" ? handleDoubleClick : handleTripleClick)(view, pos.pos, pos.inside, event)) {
3324
3352
  event.preventDefault();
@@ -3328,13 +3356,34 @@ handlers.mousedown = (view, _event) => {
3328
3356
  }
3329
3357
  };
3330
3358
  class MouseDown {
3331
- constructor(view, pos, event, flushed) {
3359
+ constructor(view) {
3332
3360
  this.view = view;
3361
+ this.mightDrag = null;
3362
+ view.root.addEventListener("mouseup", this.up = this.up.bind(this));
3363
+ view.root.addEventListener("mousemove", this.move = this.move.bind(this));
3364
+ }
3365
+ up(event) {
3366
+ this.done();
3367
+ }
3368
+ move(event) {
3369
+ if (event.buttons == 0)
3370
+ this.done();
3371
+ }
3372
+ done() {
3373
+ this.view.root.removeEventListener("mouseup", this.up);
3374
+ this.view.root.removeEventListener("mousemove", this.move);
3375
+ if (this.view.input.mouseDown == this)
3376
+ this.view.input.mouseDown = null;
3377
+ }
3378
+ delaySelUpdate() { return false; }
3379
+ }
3380
+ class LeftMouseDown extends MouseDown {
3381
+ constructor(view, pos, event, flushed) {
3382
+ super(view);
3333
3383
  this.pos = pos;
3334
3384
  this.event = event;
3335
3385
  this.flushed = flushed;
3336
3386
  this.delayedSelectionSync = false;
3337
- this.mightDrag = null;
3338
3387
  this.startDoc = view.state.doc;
3339
3388
  this.selectNode = !!event[selectNodeModifier];
3340
3389
  this.allowDefault = event.shiftKey;
@@ -3372,13 +3421,10 @@ class MouseDown {
3372
3421
  }, 20);
3373
3422
  this.view.domObserver.start();
3374
3423
  }
3375
- view.root.addEventListener("mouseup", this.up = this.up.bind(this));
3376
- view.root.addEventListener("mousemove", this.move = this.move.bind(this));
3377
3424
  setSelectionOrigin(view, "pointer");
3378
3425
  }
3379
3426
  done() {
3380
- this.view.root.removeEventListener("mouseup", this.up);
3381
- this.view.root.removeEventListener("mousemove", this.move);
3427
+ super.done();
3382
3428
  if (this.mightDrag && this.target) {
3383
3429
  this.view.domObserver.stop();
3384
3430
  if (this.mightDrag.addAttr)
@@ -3388,8 +3434,10 @@ class MouseDown {
3388
3434
  this.view.domObserver.start();
3389
3435
  }
3390
3436
  if (this.delayedSelectionSync)
3391
- setTimeout(() => selectionToDOM(this.view));
3392
- this.view.input.mouseDown = null;
3437
+ setTimeout(() => {
3438
+ if (!this.view.isDestroyed)
3439
+ selectionToDOM(this.view);
3440
+ });
3393
3441
  }
3394
3442
  up(event) {
3395
3443
  this.done();
@@ -3428,14 +3476,41 @@ class MouseDown {
3428
3476
  move(event) {
3429
3477
  this.updateAllowDefault(event);
3430
3478
  setSelectionOrigin(this.view, "pointer");
3431
- if (event.buttons == 0)
3432
- this.done();
3479
+ super.move(event);
3433
3480
  }
3434
3481
  updateAllowDefault(event) {
3435
3482
  if (!this.allowDefault && (Math.abs(this.event.x - event.clientX) > 4 ||
3436
3483
  Math.abs(this.event.y - event.clientY) > 4))
3437
3484
  this.allowDefault = true;
3438
3485
  }
3486
+ delaySelUpdate() {
3487
+ if (!this.allowDefault)
3488
+ return false;
3489
+ this.delayedSelectionSync = true;
3490
+ return true;
3491
+ }
3492
+ }
3493
+ class TripleClickDrag extends MouseDown {
3494
+ constructor(view, startSelection) {
3495
+ super(view);
3496
+ this.startSelection = startSelection;
3497
+ this.startDoc = view.state.doc;
3498
+ }
3499
+ move(event) {
3500
+ if (event.buttons == 0 || this.view.isDestroyed || !this.view.state.doc.eq(this.startDoc)) {
3501
+ this.done();
3502
+ return;
3503
+ }
3504
+ event.preventDefault();
3505
+ setSelectionOrigin(this.view, "pointer");
3506
+ let pos = this.view.posAtCoords(eventCoords(event));
3507
+ let target = pos && selectionForTripleClick(this.view, pos.inside, false);
3508
+ if (!target)
3509
+ return;
3510
+ let { doc } = this.view.state, start = this.startSelection;
3511
+ let [anchor, head] = target.from < start.from ? [start.to, target.from] : [start.from, target.to];
3512
+ updateSelection(this.view, TextSelection.create(doc, anchor, head), "pointer");
3513
+ }
3439
3514
  }
3440
3515
  handlers.touchstart = view => {
3441
3516
  view.input.lastTouch = Date.now();
@@ -3460,7 +3535,7 @@ function inOrNearComposition(view, event) {
3460
3535
  // This guards against the case where compositionend is triggered without the keyboard
3461
3536
  // (e.g. character confirmation may be done with the mouse), and keydown is triggered
3462
3537
  // afterwards- we wouldn't want to ignore the keydown event in this case.
3463
- if (safari && Math.abs(event.timeStamp - view.input.compositionEndedAt) < 500) {
3538
+ if (safari && Math.abs(Date.now() - view.input.compositionEndedAt) < 500) {
3464
3539
  view.input.compositionEndedAt = -2e8;
3465
3540
  return true;
3466
3541
  }
@@ -3519,7 +3594,7 @@ function selectionBeforeUneditable(view) {
3519
3594
  editHandlers.compositionend = (view, event) => {
3520
3595
  if (view.composing) {
3521
3596
  view.input.composing = false;
3522
- view.input.compositionEndedAt = event.timeStamp;
3597
+ view.input.compositionEndedAt = Date.now();
3523
3598
  view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0;
3524
3599
  view.input.compositionNode = null;
3525
3600
  if (view.input.badSafariComposition)
@@ -3538,7 +3613,7 @@ function scheduleComposeEnd(view, delay) {
3538
3613
  function clearComposition(view) {
3539
3614
  if (view.composing) {
3540
3615
  view.input.composing = false;
3541
- view.input.compositionEndedAt = timestampFromCustomEvent();
3616
+ view.input.compositionEndedAt = Date.now();
3542
3617
  }
3543
3618
  while (view.input.compositionNodes.length > 0)
3544
3619
  view.input.compositionNodes.pop().markParentsDirty();
@@ -3564,11 +3639,6 @@ function findCompositionNode(view) {
3564
3639
  }
3565
3640
  return textBefore || textAfter;
3566
3641
  }
3567
- function timestampFromCustomEvent() {
3568
- let event = document.createEvent("Event");
3569
- event.initEvent("event", true, true);
3570
- return event.timeStamp;
3571
- }
3572
3642
  /**
3573
3643
  @internal
3574
3644
  */
@@ -4725,7 +4795,10 @@ class DOMObserver {
4725
4795
  }
4726
4796
  }
4727
4797
  }
4728
- if (added.some(n => n.nodeName == "BR") && (view.input.lastKeyCode == 8 || view.input.lastKeyCode == 46)) {
4798
+ if (added.some(n => n.nodeName == "BR") &&
4799
+ (view.input.lastKeyCode == 8 || view.input.lastKeyCode == 46 ||
4800
+ chrome && (view.composing || view.input.compositionEndedAt > Date.now() - 50) &&
4801
+ mutations.some(m => m.type == "childList" && m.removedNodes.length))) {
4729
4802
  // Browsers sometimes insert a bogus break node if you
4730
4803
  // backspace out the last bit of text before an inline-flex node (#1552)
4731
4804
  for (let node of added)
@@ -5281,38 +5354,28 @@ function skipClosingAndOpening($pos, fromEnd, mayOpen) {
5281
5354
  return end;
5282
5355
  }
5283
5356
  function findDiff(a, b, pos, preferredPos, preferredSide) {
5284
- let start = a.findDiffStart(b, pos);
5357
+ let start = a.findDiffStart(b, pos), lenA = pos + a.size, lenB = pos + b.size;
5285
5358
  if (start == null)
5286
5359
  return null;
5287
- let { a: endA, b: endB } = a.findDiffEnd(b, pos + a.size, pos + b.size);
5360
+ let { a: endA, b: endB } = a.findDiffEnd(b, lenA, lenB);
5288
5361
  if (preferredSide == "end") {
5289
5362
  let adjust = Math.max(0, start - Math.min(endA, endB));
5290
5363
  preferredPos -= endA + adjust - start;
5291
5364
  }
5292
- if (endA < start && a.size < b.size) {
5365
+ if (endA < start && lenA < lenB) {
5293
5366
  let move = preferredPos <= start && preferredPos >= endA ? start - preferredPos : 0;
5294
5367
  start -= move;
5295
- if (start && start < b.size && isSurrogatePair(b.textBetween(start - 1, start + 1)))
5296
- start += move ? 1 : -1;
5297
5368
  endB = start + (endB - endA);
5298
5369
  endA = start;
5299
5370
  }
5300
5371
  else if (endB < start) {
5301
5372
  let move = preferredPos <= start && preferredPos >= endB ? start - preferredPos : 0;
5302
5373
  start -= move;
5303
- if (start && start < a.size && isSurrogatePair(a.textBetween(start - 1, start + 1)))
5304
- start += move ? 1 : -1;
5305
5374
  endA = start + (endA - endB);
5306
5375
  endB = start;
5307
5376
  }
5308
5377
  return { start, endA, endB };
5309
5378
  }
5310
- function isSurrogatePair(str) {
5311
- if (str.length != 2)
5312
- return false;
5313
- let a = str.charCodeAt(0), b = str.charCodeAt(1);
5314
- return a >= 0xDC00 && a <= 0xDFFF && b >= 0xD800 && b <= 0xDBFF;
5315
- }
5316
5379
 
5317
5380
  /**
5318
5381
  @internal
@@ -5513,9 +5576,10 @@ class EditorView {
5513
5576
  // a DOM selection change and the "selectionchange" event for it
5514
5577
  // can cause a spurious DOM selection update, disrupting mouse
5515
5578
  // drag selection.
5579
+ let mouseDown = this.input.mouseDown;
5516
5580
  if (forceSelUpdate ||
5517
- !(this.input.mouseDown && this.domObserver.currentSelection.eq(this.domSelectionRange()) &&
5518
- anchorInRightPlace(this))) {
5581
+ !(mouseDown && this.domObserver.currentSelection.eq(this.domSelectionRange()) &&
5582
+ anchorInRightPlace(this) && mouseDown.delaySelUpdate())) {
5519
5583
  selectionToDOM(this, forceSelUpdate);
5520
5584
  }
5521
5585
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prosemirror-view",
3
- "version": "1.41.8",
3
+ "version": "1.42.0",
4
4
  "description": "ProseMirror's view component",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -28,7 +28,7 @@
28
28
  "url": "git+https://code.haverbeke.berlin/prosemirror/prosemirror-view.git"
29
29
  },
30
30
  "dependencies": {
31
- "prosemirror-model": "^1.20.0",
31
+ "prosemirror-model": "^1.25.8",
32
32
  "prosemirror-state": "^1.0.0",
33
33
  "prosemirror-transform": "^1.1.0"
34
34
  },
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 _detachedDoc || (_detachedDoc = document.implementation.createHTMLDocument("title"))
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 elt = detachedDoc().createElement("div")
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/domchange.ts CHANGED
@@ -351,33 +351,23 @@ function skipClosingAndOpening($pos: ResolvedPos, fromEnd: boolean, mayOpen: boo
351
351
  }
352
352
 
353
353
  function findDiff(a: Fragment, b: Fragment, pos: number, preferredPos: number, preferredSide: "start" | "end") {
354
- let start = a.findDiffStart(b, pos)
354
+ let start = a.findDiffStart(b, pos), lenA = pos + a.size, lenB = pos + b.size
355
355
  if (start == null) return null
356
- let {a: endA, b: endB} = a.findDiffEnd(b, pos + a.size, pos + b.size)!
356
+ let {a: endA, b: endB} = a.findDiffEnd(b, lenA, lenB)!
357
357
  if (preferredSide == "end") {
358
358
  let adjust = Math.max(0, start - Math.min(endA, endB))
359
359
  preferredPos -= endA + adjust - start
360
360
  }
361
- if (endA < start && a.size < b.size) {
361
+ if (endA < start && lenA < lenB) {
362
362
  let move = preferredPos <= start && preferredPos >= endA ? start - preferredPos : 0
363
363
  start -= move
364
- if (start && start < b.size && isSurrogatePair(b.textBetween(start - 1, start + 1)))
365
- start += move ? 1 : -1
366
364
  endB = start + (endB - endA)
367
365
  endA = start
368
366
  } else if (endB < start) {
369
367
  let move = preferredPos <= start && preferredPos >= endB ? start - preferredPos : 0
370
368
  start -= move
371
- if (start && start < a.size && isSurrogatePair(a.textBetween(start - 1, start + 1)))
372
- start += move ? 1 : -1
373
369
  endA = start + (endA - endB)
374
370
  endB = start
375
371
  }
376
372
  return {start, endA, endB}
377
373
  }
378
-
379
- function isSurrogatePair(str: string) {
380
- if (str.length != 2) return false
381
- let a = str.charCodeAt(0), b = str.charCodeAt(1)
382
- return a >= 0xDC00 && a <= 0xDFFF && b >= 0xD800 && b <= 0xDBFF
383
- }
@@ -192,7 +192,10 @@ export class DOMObserver {
192
192
  }
193
193
  }
194
194
 
195
- if (added.some(n => n.nodeName == "BR") && (view.input.lastKeyCode == 8 || view.input.lastKeyCode == 46)) {
195
+ if (added.some(n => n.nodeName == "BR") &&
196
+ (view.input.lastKeyCode == 8 || view.input.lastKeyCode == 46 ||
197
+ browser.chrome && (view.composing || view.input.compositionEndedAt > Date.now() - 50) &&
198
+ mutations.some(m => m.type == "childList" && m.removedNodes.length))) {
196
199
  // Browsers sometimes insert a bogus break node if you
197
200
  // backspace out the last bit of text before an inline-flex node (#1552)
198
201
  for (let node of added) if (node.nodeName == "BR" && node.parentNode) {
package/src/index.ts CHANGED
@@ -208,9 +208,10 @@ export class EditorView {
208
208
  // a DOM selection change and the "selectionchange" event for it
209
209
  // can cause a spurious DOM selection update, disrupting mouse
210
210
  // drag selection.
211
+ let mouseDown = this.input.mouseDown
211
212
  if (forceSelUpdate ||
212
- !(this.input.mouseDown && this.domObserver.currentSelection.eq(this.domSelectionRange()) &&
213
- anchorInRightPlace(this))) {
213
+ !(mouseDown && this.domObserver.currentSelection.eq(this.domSelectionRange()) &&
214
+ anchorInRightPlace(this) && mouseDown.delaySelUpdate())) {
214
215
  selectionToDOM(this, forceSelUpdate)
215
216
  } else {
216
217
  syncNodeSelection(this, state.selection)