prosemirror-view 1.41.7 → 1.41.9

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.js CHANGED
@@ -2244,14 +2244,14 @@ function selectionToDOM(view, force = false) {
2244
2244
  syncNodeSelection(view, sel);
2245
2245
  if (!editorOwnsSelection(view))
2246
2246
  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) {
2247
+ // Need to delay selection normalization during a native selection
2248
+ // drag on Chrome, or it will cause further dragging to glitch.
2249
+ let mouseDown = view.input.mouseDown;
2250
+ if (!force && chrome && mouseDown) {
2251
2251
  let domSel = view.domSelectionRange(), curSel = view.domObserver.currentSelection;
2252
2252
  if (domSel.anchorNode && curSel.anchorNode &&
2253
- isEquivalentPosition(domSel.anchorNode, domSel.anchorOffset, curSel.anchorNode, curSel.anchorOffset)) {
2254
- view.input.mouseDown.delayedSelectionSync = true;
2253
+ isEquivalentPosition(domSel.anchorNode, domSel.anchorOffset, curSel.anchorNode, curSel.anchorOffset) &&
2254
+ mouseDown.delaySelUpdate()) {
2255
2255
  view.domObserver.setCurSelection();
2256
2256
  return;
2257
2257
  }
@@ -3103,6 +3103,8 @@ function setSelectionOrigin(view, origin) {
3103
3103
  view.input.lastSelectionTime = Date.now();
3104
3104
  }
3105
3105
  function destroyInput(view) {
3106
+ if (view.input.mouseDown)
3107
+ view.input.mouseDown.done();
3106
3108
  view.domObserver.stop();
3107
3109
  for (let type in view.input.eventHandlers)
3108
3110
  view.dom.removeEventListener(type, view.input.eventHandlers[type]);
@@ -3141,7 +3143,7 @@ function dispatchEvent(view, event) {
3141
3143
  editHandlers.keydown = (view, _event) => {
3142
3144
  let event = _event;
3143
3145
  view.input.shiftKey = event.keyCode == 16 || event.shiftKey;
3144
- if (inOrNearComposition(view, event))
3146
+ if (inOrNearComposition(view))
3145
3147
  return;
3146
3148
  view.input.lastKeyCode = event.keyCode;
3147
3149
  view.input.lastKeyCodeTime = Date.now();
@@ -3179,7 +3181,7 @@ editHandlers.keyup = (view, event) => {
3179
3181
  };
3180
3182
  editHandlers.keypress = (view, _event) => {
3181
3183
  let event = _event;
3182
- if (inOrNearComposition(view, event) || !event.charCode ||
3184
+ if (inOrNearComposition(view) || !event.charCode ||
3183
3185
  event.ctrlKey && !event.altKey || mac && event.metaKey)
3184
3186
  return;
3185
3187
  if (view.someProp("handleKeyPress", f => f(view, event))) {
@@ -3274,26 +3276,28 @@ function handleTripleClick(view, pos, inside, event) {
3274
3276
  function defaultTripleClick(view, inside, event) {
3275
3277
  if (event.button != 0)
3276
3278
  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
- }
3279
+ let selection = selectionForTripleClick(view, inside, true), doc = view.state.doc;
3280
+ if (!selection)
3283
3281
  return false;
3284
- }
3282
+ updateSelection(view, selection, "pointer");
3283
+ if (selection instanceof TextSelection && doc.eq(view.state.doc))
3284
+ view.input.mouseDown = new TripleClickDrag(view, selection);
3285
+ return true;
3286
+ }
3287
+ function selectionForTripleClick(view, inside, selectNodes) {
3288
+ let doc = view.state.doc;
3289
+ if (inside == -1)
3290
+ return doc.inlineContent ? TextSelection.create(doc, 0, doc.content.size) : null;
3285
3291
  let $pos = doc.resolve(inside);
3286
3292
  for (let i = $pos.depth + 1; i > 0; i--) {
3287
3293
  let node = i > $pos.depth ? $pos.nodeAfter : $pos.node(i);
3288
3294
  let nodePos = $pos.before(i);
3289
3295
  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;
3296
+ return TextSelection.create(doc, nodePos + 1, nodePos + 1 + node.content.size);
3297
+ else if (selectNodes && NodeSelection.isSelectable(node))
3298
+ return NodeSelection.create(doc, nodePos);
3296
3299
  }
3300
+ return null;
3297
3301
  }
3298
3302
  function forceDOMFlush(view) {
3299
3303
  return endComposition(view);
@@ -3312,13 +3316,13 @@ handlers.mousedown = (view, _event) => {
3312
3316
  type = "tripleClick";
3313
3317
  }
3314
3318
  view.input.lastClick = { time: now, x: event.clientX, y: event.clientY, type, button: event.button };
3319
+ if (view.input.mouseDown)
3320
+ view.input.mouseDown.done();
3315
3321
  let pos = view.posAtCoords(eventCoords(event));
3316
3322
  if (!pos)
3317
3323
  return;
3318
3324
  if (type == "singleClick") {
3319
- if (view.input.mouseDown)
3320
- view.input.mouseDown.done();
3321
- view.input.mouseDown = new MouseDown(view, pos, event, !!flushed);
3325
+ view.input.mouseDown = new LeftMouseDown(view, pos, event, !!flushed);
3322
3326
  }
3323
3327
  else if ((type == "doubleClick" ? handleDoubleClick : handleTripleClick)(view, pos.pos, pos.inside, event)) {
3324
3328
  event.preventDefault();
@@ -3328,13 +3332,34 @@ handlers.mousedown = (view, _event) => {
3328
3332
  }
3329
3333
  };
3330
3334
  class MouseDown {
3331
- constructor(view, pos, event, flushed) {
3335
+ constructor(view) {
3332
3336
  this.view = view;
3337
+ this.mightDrag = null;
3338
+ view.root.addEventListener("mouseup", this.up = this.up.bind(this));
3339
+ view.root.addEventListener("mousemove", this.move = this.move.bind(this));
3340
+ }
3341
+ up(event) {
3342
+ this.done();
3343
+ }
3344
+ move(event) {
3345
+ if (event.buttons == 0)
3346
+ this.done();
3347
+ }
3348
+ done() {
3349
+ this.view.root.removeEventListener("mouseup", this.up);
3350
+ this.view.root.removeEventListener("mousemove", this.move);
3351
+ if (this.view.input.mouseDown == this)
3352
+ this.view.input.mouseDown = null;
3353
+ }
3354
+ delaySelUpdate() { return false; }
3355
+ }
3356
+ class LeftMouseDown extends MouseDown {
3357
+ constructor(view, pos, event, flushed) {
3358
+ super(view);
3333
3359
  this.pos = pos;
3334
3360
  this.event = event;
3335
3361
  this.flushed = flushed;
3336
3362
  this.delayedSelectionSync = false;
3337
- this.mightDrag = null;
3338
3363
  this.startDoc = view.state.doc;
3339
3364
  this.selectNode = !!event[selectNodeModifier];
3340
3365
  this.allowDefault = event.shiftKey;
@@ -3353,8 +3378,8 @@ class MouseDown {
3353
3378
  this.target = targetDesc && targetDesc.nodeDOM.nodeType == 1 ? targetDesc.nodeDOM : null;
3354
3379
  let { selection } = view.state;
3355
3380
  if (event.button == 0 &&
3356
- targetNode.type.spec.draggable && targetNode.type.spec.selectable !== false ||
3357
- selection instanceof NodeSelection && selection.from <= targetPos && selection.to > targetPos)
3381
+ (targetNode.type.spec.draggable && targetNode.type.spec.selectable !== false ||
3382
+ selection instanceof NodeSelection && selection.from <= targetPos && selection.to > targetPos))
3358
3383
  this.mightDrag = {
3359
3384
  node: targetNode,
3360
3385
  pos: targetPos,
@@ -3372,13 +3397,10 @@ class MouseDown {
3372
3397
  }, 20);
3373
3398
  this.view.domObserver.start();
3374
3399
  }
3375
- view.root.addEventListener("mouseup", this.up = this.up.bind(this));
3376
- view.root.addEventListener("mousemove", this.move = this.move.bind(this));
3377
3400
  setSelectionOrigin(view, "pointer");
3378
3401
  }
3379
3402
  done() {
3380
- this.view.root.removeEventListener("mouseup", this.up);
3381
- this.view.root.removeEventListener("mousemove", this.move);
3403
+ super.done();
3382
3404
  if (this.mightDrag && this.target) {
3383
3405
  this.view.domObserver.stop();
3384
3406
  if (this.mightDrag.addAttr)
@@ -3388,8 +3410,10 @@ class MouseDown {
3388
3410
  this.view.domObserver.start();
3389
3411
  }
3390
3412
  if (this.delayedSelectionSync)
3391
- setTimeout(() => selectionToDOM(this.view));
3392
- this.view.input.mouseDown = null;
3413
+ setTimeout(() => {
3414
+ if (!this.view.isDestroyed)
3415
+ selectionToDOM(this.view);
3416
+ });
3393
3417
  }
3394
3418
  up(event) {
3395
3419
  this.done();
@@ -3428,14 +3452,41 @@ class MouseDown {
3428
3452
  move(event) {
3429
3453
  this.updateAllowDefault(event);
3430
3454
  setSelectionOrigin(this.view, "pointer");
3431
- if (event.buttons == 0)
3432
- this.done();
3455
+ super.move(event);
3433
3456
  }
3434
3457
  updateAllowDefault(event) {
3435
3458
  if (!this.allowDefault && (Math.abs(this.event.x - event.clientX) > 4 ||
3436
3459
  Math.abs(this.event.y - event.clientY) > 4))
3437
3460
  this.allowDefault = true;
3438
3461
  }
3462
+ delaySelUpdate() {
3463
+ if (!this.allowDefault)
3464
+ return false;
3465
+ this.delayedSelectionSync = true;
3466
+ return true;
3467
+ }
3468
+ }
3469
+ class TripleClickDrag extends MouseDown {
3470
+ constructor(view, startSelection) {
3471
+ super(view);
3472
+ this.startSelection = startSelection;
3473
+ this.startDoc = view.state.doc;
3474
+ }
3475
+ move(event) {
3476
+ if (event.buttons == 0 || this.view.isDestroyed || !this.view.state.doc.eq(this.startDoc)) {
3477
+ this.done();
3478
+ return;
3479
+ }
3480
+ event.preventDefault();
3481
+ setSelectionOrigin(this.view, "pointer");
3482
+ let pos = this.view.posAtCoords(eventCoords(event));
3483
+ let target = pos && selectionForTripleClick(this.view, pos.inside, false);
3484
+ if (!target)
3485
+ return;
3486
+ let { doc } = this.view.state, start = this.startSelection;
3487
+ let [anchor, head] = target.from < start.from ? [start.to, target.from] : [start.from, target.to];
3488
+ updateSelection(this.view, TextSelection.create(doc, anchor, head), "pointer");
3489
+ }
3439
3490
  }
3440
3491
  handlers.touchstart = view => {
3441
3492
  view.input.lastTouch = Date.now();
@@ -3460,7 +3511,7 @@ function inOrNearComposition(view, event) {
3460
3511
  // This guards against the case where compositionend is triggered without the keyboard
3461
3512
  // (e.g. character confirmation may be done with the mouse), and keydown is triggered
3462
3513
  // afterwards- we wouldn't want to ignore the keydown event in this case.
3463
- if (safari && Math.abs(event.timeStamp - view.input.compositionEndedAt) < 500) {
3514
+ if (safari && Math.abs(Date.now() - view.input.compositionEndedAt) < 500) {
3464
3515
  view.input.compositionEndedAt = -2e8;
3465
3516
  return true;
3466
3517
  }
@@ -3519,7 +3570,7 @@ function selectionBeforeUneditable(view) {
3519
3570
  editHandlers.compositionend = (view, event) => {
3520
3571
  if (view.composing) {
3521
3572
  view.input.composing = false;
3522
- view.input.compositionEndedAt = event.timeStamp;
3573
+ view.input.compositionEndedAt = Date.now();
3523
3574
  view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0;
3524
3575
  view.input.compositionNode = null;
3525
3576
  if (view.input.badSafariComposition)
@@ -3538,7 +3589,7 @@ function scheduleComposeEnd(view, delay) {
3538
3589
  function clearComposition(view) {
3539
3590
  if (view.composing) {
3540
3591
  view.input.composing = false;
3541
- view.input.compositionEndedAt = timestampFromCustomEvent();
3592
+ view.input.compositionEndedAt = Date.now();
3542
3593
  }
3543
3594
  while (view.input.compositionNodes.length > 0)
3544
3595
  view.input.compositionNodes.pop().markParentsDirty();
@@ -3564,11 +3615,6 @@ function findCompositionNode(view) {
3564
3615
  }
3565
3616
  return textBefore || textAfter;
3566
3617
  }
3567
- function timestampFromCustomEvent() {
3568
- let event = document.createEvent("Event");
3569
- event.initEvent("event", true, true);
3570
- return event.timeStamp;
3571
- }
3572
3618
  /**
3573
3619
  @internal
3574
3620
  */
@@ -3703,8 +3749,9 @@ class Dragging {
3703
3749
  }
3704
3750
  const dragCopyModifier = mac ? "altKey" : "ctrlKey";
3705
3751
  function dragMoves(view, event) {
3706
- let moves = view.someProp("dragCopies", test => !test(event));
3707
- return moves != null ? moves : !event[dragCopyModifier];
3752
+ let copy;
3753
+ view.someProp("dragCopies", test => { copy = copy || test(event); });
3754
+ return copy != null ? !copy : !event[dragCopyModifier];
3708
3755
  }
3709
3756
  handlers.dragstart = (view, _event) => {
3710
3757
  let event = _event;
@@ -3731,7 +3778,7 @@ handlers.dragstart = (view, _event) => {
3731
3778
  if (!event.dataTransfer.files.length || !chrome || chrome_version > 120)
3732
3779
  event.dataTransfer.clearData();
3733
3780
  event.dataTransfer.setData(brokenClipboardAPI ? "Text" : "text/html", dom.innerHTML);
3734
- // See https://github.com/ProseMirror/prosemirror/issues/1156
3781
+ // See https://code.haverbeke.berlin/prosemirror/prosemirror/issues/1156
3735
3782
  event.dataTransfer.effectAllowed = "copyMove";
3736
3783
  if (!brokenClipboardAPI)
3737
3784
  event.dataTransfer.setData("text/plain", text);
@@ -4724,7 +4771,10 @@ class DOMObserver {
4724
4771
  }
4725
4772
  }
4726
4773
  }
4727
- if (added.some(n => n.nodeName == "BR") && (view.input.lastKeyCode == 8 || view.input.lastKeyCode == 46)) {
4774
+ if (added.some(n => n.nodeName == "BR") &&
4775
+ (view.input.lastKeyCode == 8 || view.input.lastKeyCode == 46 ||
4776
+ chrome && (view.composing || view.input.compositionEndedAt > Date.now() - 50) &&
4777
+ mutations.some(m => m.type == "childList" && m.removedNodes.length))) {
4728
4778
  // Browsers sometimes insert a bogus break node if you
4729
4779
  // backspace out the last bit of text before an inline-flex node (#1552)
4730
4780
  for (let node of added)
@@ -5280,38 +5330,28 @@ function skipClosingAndOpening($pos, fromEnd, mayOpen) {
5280
5330
  return end;
5281
5331
  }
5282
5332
  function findDiff(a, b, pos, preferredPos, preferredSide) {
5283
- let start = a.findDiffStart(b, pos);
5333
+ let start = a.findDiffStart(b, pos), lenA = pos + a.size, lenB = pos + b.size;
5284
5334
  if (start == null)
5285
5335
  return null;
5286
- let { a: endA, b: endB } = a.findDiffEnd(b, pos + a.size, pos + b.size);
5336
+ let { a: endA, b: endB } = a.findDiffEnd(b, lenA, lenB);
5287
5337
  if (preferredSide == "end") {
5288
5338
  let adjust = Math.max(0, start - Math.min(endA, endB));
5289
5339
  preferredPos -= endA + adjust - start;
5290
5340
  }
5291
- if (endA < start && a.size < b.size) {
5341
+ if (endA < start && lenA < lenB) {
5292
5342
  let move = preferredPos <= start && preferredPos >= endA ? start - preferredPos : 0;
5293
5343
  start -= move;
5294
- if (start && start < b.size && isSurrogatePair(b.textBetween(start - 1, start + 1)))
5295
- start += move ? 1 : -1;
5296
5344
  endB = start + (endB - endA);
5297
5345
  endA = start;
5298
5346
  }
5299
5347
  else if (endB < start) {
5300
5348
  let move = preferredPos <= start && preferredPos >= endB ? start - preferredPos : 0;
5301
5349
  start -= move;
5302
- if (start && start < a.size && isSurrogatePair(a.textBetween(start - 1, start + 1)))
5303
- start += move ? 1 : -1;
5304
5350
  endA = start + (endA - endB);
5305
5351
  endB = start;
5306
5352
  }
5307
5353
  return { start, endA, endB };
5308
5354
  }
5309
- function isSurrogatePair(str) {
5310
- if (str.length != 2)
5311
- return false;
5312
- let a = str.charCodeAt(0), b = str.charCodeAt(1);
5313
- return a >= 0xDC00 && a <= 0xDFFF && b >= 0xD800 && b <= 0xDBFF;
5314
- }
5315
5355
 
5316
5356
  /**
5317
5357
  @internal
@@ -5365,7 +5405,7 @@ class EditorView {
5365
5405
  this.pluginViews = [];
5366
5406
  /**
5367
5407
  Holds `true` when a hack node is needed in Firefox to prevent the
5368
- [space is eaten issue](https://github.com/ProseMirror/prosemirror/issues/651)
5408
+ [space is eaten issue](https://code.haverbeke.berlin/prosemirror/prosemirror/issues/651)
5369
5409
  @internal
5370
5410
  */
5371
5411
  this.requiresGeckoHackNode = false;
@@ -5512,9 +5552,10 @@ class EditorView {
5512
5552
  // a DOM selection change and the "selectionchange" event for it
5513
5553
  // can cause a spurious DOM selection update, disrupting mouse
5514
5554
  // drag selection.
5555
+ let mouseDown = this.input.mouseDown;
5515
5556
  if (forceSelUpdate ||
5516
- !(this.input.mouseDown && this.domObserver.currentSelection.eq(this.domSelectionRange()) &&
5517
- anchorInRightPlace(this))) {
5557
+ !(mouseDown && this.domObserver.currentSelection.eq(this.domSelectionRange()) &&
5558
+ anchorInRightPlace(this) && mouseDown.delaySelUpdate())) {
5518
5559
  selectionToDOM(this, forceSelUpdate);
5519
5560
  }
5520
5561
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prosemirror-view",
3
- "version": "1.41.7",
3
+ "version": "1.41.9",
4
4
  "description": "ProseMirror's view component",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -25,10 +25,10 @@
25
25
  ],
26
26
  "repository": {
27
27
  "type": "git",
28
- "url": "git://github.com/prosemirror/prosemirror-view.git"
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/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
@@ -54,7 +54,7 @@ export class EditorView {
54
54
  /// @internal
55
55
  declare domObserver: DOMObserver
56
56
  /// Holds `true` when a hack node is needed in Firefox to prevent the
57
- /// [space is eaten issue](https://github.com/ProseMirror/prosemirror/issues/651)
57
+ /// [space is eaten issue](https://code.haverbeke.berlin/prosemirror/prosemirror/issues/651)
58
58
  /// @internal
59
59
  requiresGeckoHackNode: boolean = false
60
60
 
@@ -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)