@limetech/lime-elements 37.77.0 → 37.78.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/{3d-tilt-hover-effect-f64da0a8.js → 3d-tilt-hover-effect-ef81ae4c.js} +2 -6
  3. package/dist/cjs/3d-tilt-hover-effect-ef81ae4c.js.map +1 -0
  4. package/dist/cjs/lime-elements.cjs.js +1 -1
  5. package/dist/cjs/limel-3d-hover-effect-glow.cjs.entry.js +21 -0
  6. package/dist/cjs/limel-3d-hover-effect-glow.cjs.entry.js.map +1 -0
  7. package/dist/cjs/limel-card.cjs.entry.js +3 -3
  8. package/dist/cjs/limel-card.cjs.entry.js.map +1 -1
  9. package/dist/cjs/limel-info-tile.cjs.entry.js +3 -3
  10. package/dist/cjs/limel-info-tile.cjs.entry.js.map +1 -1
  11. package/dist/cjs/limel-picker.cjs.entry.js +20 -1
  12. package/dist/cjs/limel-picker.cjs.entry.js.map +1 -1
  13. package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js +341 -116
  14. package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js.map +1 -1
  15. package/dist/cjs/limel-shortcut.cjs.entry.js +9 -6
  16. package/dist/cjs/limel-shortcut.cjs.entry.js.map +1 -1
  17. package/dist/cjs/loader.cjs.js +1 -1
  18. package/dist/collection/collection-manifest.json +1 -0
  19. package/dist/collection/components/3d-hover-effect-glow/3d-hover-effect-glow.css +18 -0
  20. package/dist/collection/components/3d-hover-effect-glow/3d-hover-effect-glow.js +34 -0
  21. package/dist/collection/components/3d-hover-effect-glow/3d-hover-effect-glow.js.map +1 -0
  22. package/dist/collection/components/card/card.css +9 -16
  23. package/dist/collection/components/card/card.js +1 -1
  24. package/dist/collection/components/card/card.js.map +1 -1
  25. package/dist/collection/components/info-tile/info-tile.css +9 -16
  26. package/dist/collection/components/info-tile/info-tile.js +1 -1
  27. package/dist/collection/components/info-tile/info-tile.js.map +1 -1
  28. package/dist/collection/components/picker/picker.js +50 -3
  29. package/dist/collection/components/picker/picker.js.map +1 -1
  30. package/dist/collection/components/shortcut/shortcut.css +65 -34
  31. package/dist/collection/components/shortcut/shortcut.js +9 -6
  32. package/dist/collection/components/shortcut/shortcut.js.map +1 -1
  33. package/dist/collection/style/mixins.scss +9 -36
  34. package/dist/collection/util/3d-tilt-hover-effect.js +1 -5
  35. package/dist/collection/util/3d-tilt-hover-effect.js.map +1 -1
  36. package/dist/esm/{3d-tilt-hover-effect-a76fcd43.js → 3d-tilt-hover-effect-05648b3c.js} +2 -6
  37. package/dist/esm/3d-tilt-hover-effect-05648b3c.js.map +1 -0
  38. package/dist/esm/lime-elements.js +1 -1
  39. package/dist/esm/limel-3d-hover-effect-glow.entry.js +17 -0
  40. package/dist/esm/limel-3d-hover-effect-glow.entry.js.map +1 -0
  41. package/dist/esm/limel-card.entry.js +3 -3
  42. package/dist/esm/limel-card.entry.js.map +1 -1
  43. package/dist/esm/limel-info-tile.entry.js +3 -3
  44. package/dist/esm/limel-info-tile.entry.js.map +1 -1
  45. package/dist/esm/limel-picker.entry.js +20 -1
  46. package/dist/esm/limel-picker.entry.js.map +1 -1
  47. package/dist/esm/limel-prosemirror-adapter.entry.js +341 -116
  48. package/dist/esm/limel-prosemirror-adapter.entry.js.map +1 -1
  49. package/dist/esm/limel-shortcut.entry.js +10 -7
  50. package/dist/esm/limel-shortcut.entry.js.map +1 -1
  51. package/dist/esm/loader.js +1 -1
  52. package/dist/lime-elements/lime-elements.esm.js +1 -1
  53. package/dist/lime-elements/lime-elements.esm.js.map +1 -1
  54. package/dist/lime-elements/p-85ffcf55.entry.js +2 -0
  55. package/dist/lime-elements/p-85ffcf55.entry.js.map +1 -0
  56. package/dist/lime-elements/p-8e7788a1.entry.js +2 -0
  57. package/dist/lime-elements/p-8e7788a1.entry.js.map +1 -0
  58. package/dist/lime-elements/p-ac69fa25.entry.js +2 -0
  59. package/dist/lime-elements/p-ac69fa25.entry.js.map +1 -0
  60. package/dist/lime-elements/p-b66faa7b.entry.js +2 -0
  61. package/dist/lime-elements/p-b66faa7b.entry.js.map +1 -0
  62. package/dist/lime-elements/{p-05c10bed.entry.js → p-c7d07d05.entry.js} +2 -2
  63. package/dist/lime-elements/p-c7d07d05.entry.js.map +1 -0
  64. package/dist/lime-elements/p-d39c198b.entry.js +2 -0
  65. package/dist/lime-elements/p-d39c198b.entry.js.map +1 -0
  66. package/dist/lime-elements/{p-23bc6de0.js → p-e1e25236.js} +1 -1
  67. package/dist/lime-elements/p-e1e25236.js.map +1 -0
  68. package/dist/lime-elements/style/mixins.scss +9 -36
  69. package/dist/scss/mixins.scss +9 -36
  70. package/dist/types/components/3d-hover-effect-glow/3d-hover-effect-glow.d.ts +19 -0
  71. package/dist/types/components/picker/picker.d.ts +14 -2
  72. package/dist/types/components/shortcut/shortcut.d.ts +5 -1
  73. package/dist/types/components.d.ts +70 -5
  74. package/dist/types/util/3d-tilt-hover-effect.d.ts +1 -5
  75. package/package.json +6 -6
  76. package/dist/cjs/3d-tilt-hover-effect-f64da0a8.js.map +0 -1
  77. package/dist/esm/3d-tilt-hover-effect-a76fcd43.js.map +0 -1
  78. package/dist/lime-elements/p-05c10bed.entry.js.map +0 -1
  79. package/dist/lime-elements/p-1db8aa67.entry.js +0 -2
  80. package/dist/lime-elements/p-1db8aa67.entry.js.map +0 -1
  81. package/dist/lime-elements/p-23bc6de0.js.map +0 -1
  82. package/dist/lime-elements/p-68a8b724.entry.js +0 -2
  83. package/dist/lime-elements/p-68a8b724.entry.js.map +0 -1
  84. package/dist/lime-elements/p-ba4098bc.entry.js +0 -2
  85. package/dist/lime-elements/p-ba4098bc.entry.js.map +0 -1
  86. package/dist/lime-elements/p-f3a613a3.entry.js +0 -2
  87. package/dist/lime-elements/p-f3a613a3.entry.js.map +0 -1
@@ -5021,7 +5021,8 @@ class ReplaceAroundStep extends Step {
5021
5021
  }
5022
5022
  map(mapping) {
5023
5023
  let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1);
5024
- let gapFrom = mapping.map(this.gapFrom, -1), gapTo = mapping.map(this.gapTo, 1);
5024
+ let gapFrom = this.from == this.gapFrom ? from.pos : mapping.map(this.gapFrom, -1);
5025
+ let gapTo = this.to == this.gapTo ? to.pos : mapping.map(this.gapTo, 1);
5025
5026
  if ((from.deletedAcross && to.deletedAcross) || gapFrom < from.pos || gapTo > to.pos)
5026
5027
  return null;
5027
5028
  return new ReplaceAroundStep(from.pos, to.pos, gapFrom, gapTo, this.slice, this.insert, this.structure);
@@ -5133,7 +5134,7 @@ function removeMark(tr, from, to, mark) {
5133
5134
  });
5134
5135
  matched.forEach(m => tr.step(new RemoveMarkStep(m.from, m.to, m.style)));
5135
5136
  }
5136
- function clearIncompatible(tr, pos, parentType, match = parentType.contentMatch) {
5137
+ function clearIncompatible(tr, pos, parentType, match = parentType.contentMatch, clearNewlines = true) {
5137
5138
  let node = tr.doc.nodeAt(pos);
5138
5139
  let replSteps = [], cur = pos + 1;
5139
5140
  for (let i = 0; i < node.childCount; i++) {
@@ -5147,7 +5148,7 @@ function clearIncompatible(tr, pos, parentType, match = parentType.contentMatch)
5147
5148
  for (let j = 0; j < child.marks.length; j++)
5148
5149
  if (!parentType.allowsMarkType(child.marks[j].type))
5149
5150
  tr.step(new RemoveMarkStep(cur, end, child.marks[j]));
5150
- if (child.isText && !parentType.spec.code) {
5151
+ if (clearNewlines && child.isText && parentType.whitespace != "pre") {
5151
5152
  let m, newline = /\r?\n|\r/g, slice;
5152
5153
  while (m = newline.exec(child.text)) {
5153
5154
  if (!slice)
@@ -5271,16 +5272,49 @@ function setBlockType$1(tr, from, to, type, attrs) {
5271
5272
  throw new RangeError("Type given to setBlockType should be a textblock");
5272
5273
  let mapFrom = tr.steps.length;
5273
5274
  tr.doc.nodesBetween(from, to, (node, pos) => {
5274
- if (node.isTextblock && !node.hasMarkup(type, attrs) && canChangeType(tr.doc, tr.mapping.slice(mapFrom).map(pos), type)) {
5275
+ let attrsHere = typeof attrs == "function" ? attrs(node) : attrs;
5276
+ if (node.isTextblock && !node.hasMarkup(type, attrsHere) &&
5277
+ canChangeType(tr.doc, tr.mapping.slice(mapFrom).map(pos), type)) {
5278
+ let convertNewlines = null;
5279
+ if (type.schema.linebreakReplacement) {
5280
+ let pre = type.whitespace == "pre", supportLinebreak = !!type.contentMatch.matchType(type.schema.linebreakReplacement);
5281
+ if (pre && !supportLinebreak)
5282
+ convertNewlines = false;
5283
+ else if (!pre && supportLinebreak)
5284
+ convertNewlines = true;
5285
+ }
5275
5286
  // Ensure all markup that isn't allowed in the new node type is cleared
5276
- tr.clearIncompatible(tr.mapping.slice(mapFrom).map(pos, 1), type);
5287
+ if (convertNewlines === false)
5288
+ replaceLinebreaks(tr, node, pos, mapFrom);
5289
+ clearIncompatible(tr, tr.mapping.slice(mapFrom).map(pos, 1), type, undefined, convertNewlines === null);
5277
5290
  let mapping = tr.mapping.slice(mapFrom);
5278
5291
  let startM = mapping.map(pos, 1), endM = mapping.map(pos + node.nodeSize, 1);
5279
- tr.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1, new Slice(Fragment.from(type.create(attrs, null, node.marks)), 0, 0), 1, true));
5292
+ tr.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1, new Slice(Fragment.from(type.create(attrsHere, null, node.marks)), 0, 0), 1, true));
5293
+ if (convertNewlines === true)
5294
+ replaceNewlines(tr, node, pos, mapFrom);
5280
5295
  return false;
5281
5296
  }
5282
5297
  });
5283
5298
  }
5299
+ function replaceNewlines(tr, node, pos, mapFrom) {
5300
+ node.forEach((child, offset) => {
5301
+ if (child.isText) {
5302
+ let m, newline = /\r?\n|\r/g;
5303
+ while (m = newline.exec(child.text)) {
5304
+ let start = tr.mapping.slice(mapFrom).map(pos + 1 + offset + m.index);
5305
+ tr.replaceWith(start, start + 1, node.type.schema.linebreakReplacement.create());
5306
+ }
5307
+ }
5308
+ });
5309
+ }
5310
+ function replaceLinebreaks(tr, node, pos, mapFrom) {
5311
+ node.forEach((child, offset) => {
5312
+ if (child.type == child.type.schema.linebreakReplacement) {
5313
+ let start = tr.mapping.slice(mapFrom).map(pos + 1 + offset);
5314
+ tr.replaceWith(start, start + 1, node.type.schema.text("\n"));
5315
+ }
5316
+ });
5317
+ }
5284
5318
  function canChangeType(doc, pos, type) {
5285
5319
  let $pos = doc.resolve(pos), index = $pos.index();
5286
5320
  return $pos.parent.canReplaceWith(index, index + 1, type);
@@ -5346,8 +5380,24 @@ function canJoin(doc, pos) {
5346
5380
  return joinable($pos.nodeBefore, $pos.nodeAfter) &&
5347
5381
  $pos.parent.canReplace(index, index + 1);
5348
5382
  }
5383
+ function canAppendWithSubstitutedLinebreaks(a, b) {
5384
+ if (!b.content.size)
5385
+ a.type.compatibleContent(b.type);
5386
+ let match = a.contentMatchAt(a.childCount);
5387
+ let { linebreakReplacement } = a.type.schema;
5388
+ for (let i = 0; i < b.childCount; i++) {
5389
+ let child = b.child(i);
5390
+ let type = child.type == linebreakReplacement ? a.type.schema.nodes.text : child.type;
5391
+ match = match.matchType(type);
5392
+ if (!match)
5393
+ return false;
5394
+ if (!a.type.allowsMarks(child.marks))
5395
+ return false;
5396
+ }
5397
+ return match.validEnd;
5398
+ }
5349
5399
  function joinable(a, b) {
5350
- return !!(a && b && !a.isLeaf && a.canAppend(b));
5400
+ return !!(a && b && !a.isLeaf && canAppendWithSubstitutedLinebreaks(a, b));
5351
5401
  }
5352
5402
  /**
5353
5403
  Find an ancestor of the given position that can be joined to the
@@ -5380,8 +5430,31 @@ function joinPoint(doc, pos, dir = -1) {
5380
5430
  }
5381
5431
  }
5382
5432
  function join(tr, pos, depth) {
5383
- let step = new ReplaceStep(pos - depth, pos + depth, Slice.empty, true);
5384
- tr.step(step);
5433
+ let convertNewlines = null;
5434
+ let { linebreakReplacement } = tr.doc.type.schema;
5435
+ let $before = tr.doc.resolve(pos - depth), beforeType = $before.node().type;
5436
+ if (linebreakReplacement && beforeType.inlineContent) {
5437
+ let pre = beforeType.whitespace == "pre";
5438
+ let supportLinebreak = !!beforeType.contentMatch.matchType(linebreakReplacement);
5439
+ if (pre && !supportLinebreak)
5440
+ convertNewlines = false;
5441
+ else if (!pre && supportLinebreak)
5442
+ convertNewlines = true;
5443
+ }
5444
+ let mapFrom = tr.steps.length;
5445
+ if (convertNewlines === false) {
5446
+ let $after = tr.doc.resolve(pos + depth);
5447
+ replaceLinebreaks(tr, $after.node(), $after.before(), mapFrom);
5448
+ }
5449
+ if (beforeType.inlineContent)
5450
+ clearIncompatible(tr, pos + depth - 1, beforeType, $before.node().contentMatchAt($before.index()), convertNewlines == null);
5451
+ let mapping = tr.mapping.slice(mapFrom), start = mapping.map(pos - depth);
5452
+ tr.step(new ReplaceStep(start, mapping.map(pos + depth, -1), Slice.empty, true));
5453
+ if (convertNewlines === true) {
5454
+ let $full = tr.doc.resolve(start);
5455
+ replaceNewlines(tr, $full.node(), $full.before(), tr.steps.length);
5456
+ }
5457
+ return tr;
5385
5458
  }
5386
5459
  /**
5387
5460
  Try to find a point where a node of the given type can be inserted
@@ -5865,7 +5938,8 @@ function deleteRange(tr, from, to) {
5865
5938
  return tr.delete($from.before(depth), $to.after(depth));
5866
5939
  }
5867
5940
  for (let d = 1; d <= $from.depth && d <= $to.depth; d++) {
5868
- if (from - $from.start(d) == $from.depth - d && to > $from.end(d) && $to.end(d) - to != $to.depth - d)
5941
+ if (from - $from.start(d) == $from.depth - d && to > $from.end(d) && $to.end(d) - to != $to.depth - d &&
5942
+ $from.start(d - 1) == $to.start(d - 1) && $from.node(d - 1).canReplace($from.index(d - 1), $to.index(d - 1)))
5869
5943
  return tr.delete($from.before(d), to);
5870
5944
  }
5871
5945
  tr.delete(from, to);
@@ -7289,6 +7363,9 @@ const textRange = function (node, from, to) {
7289
7363
  range.setStart(node, from || 0);
7290
7364
  return range;
7291
7365
  };
7366
+ const clearReusedRange = function () {
7367
+ reusedRange = null;
7368
+ };
7292
7369
  // Scans forward and backward through DOM positions equivalent to the
7293
7370
  // given one to see if the two are in the same place (i.e. after a
7294
7371
  // text node vs at the end of that text node)
@@ -7323,6 +7400,44 @@ function scanFor(node, off, targetNode, targetOff, dir) {
7323
7400
  function nodeSize(node) {
7324
7401
  return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
7325
7402
  }
7403
+ function textNodeBefore$1(node, offset) {
7404
+ for (;;) {
7405
+ if (node.nodeType == 3 && offset)
7406
+ return node;
7407
+ if (node.nodeType == 1 && offset > 0) {
7408
+ if (node.contentEditable == "false")
7409
+ return null;
7410
+ node = node.childNodes[offset - 1];
7411
+ offset = nodeSize(node);
7412
+ }
7413
+ else if (node.parentNode && !hasBlockDesc(node)) {
7414
+ offset = domIndex(node);
7415
+ node = node.parentNode;
7416
+ }
7417
+ else {
7418
+ return null;
7419
+ }
7420
+ }
7421
+ }
7422
+ function textNodeAfter$1(node, offset) {
7423
+ for (;;) {
7424
+ if (node.nodeType == 3 && offset < node.nodeValue.length)
7425
+ return node;
7426
+ if (node.nodeType == 1 && offset < node.childNodes.length) {
7427
+ if (node.contentEditable == "false")
7428
+ return null;
7429
+ node = node.childNodes[offset];
7430
+ offset = 0;
7431
+ }
7432
+ else if (node.parentNode && !hasBlockDesc(node)) {
7433
+ offset = domIndex(node) + 1;
7434
+ node = node.parentNode;
7435
+ }
7436
+ else {
7437
+ return null;
7438
+ }
7439
+ }
7440
+ }
7326
7441
  function isOnEdge(node, offset, parent) {
7327
7442
  for (let atStart = offset == 0, atEnd = offset == nodeSize(node); atStart || atEnd;) {
7328
7443
  if (node == parent)
@@ -7364,15 +7479,18 @@ function caretFromPoint(doc, x, y) {
7364
7479
  if (doc.caretPositionFromPoint) {
7365
7480
  try { // Firefox throws for this call in hard-to-predict circumstances (#994)
7366
7481
  let pos = doc.caretPositionFromPoint(x, y);
7482
+ // Clip the offset, because Chrome will return a text offset
7483
+ // into <input> nodes, which can't be treated as a regular DOM
7484
+ // offset
7367
7485
  if (pos)
7368
- return { node: pos.offsetNode, offset: pos.offset };
7486
+ return { node: pos.offsetNode, offset: Math.min(nodeSize(pos.offsetNode), pos.offset) };
7369
7487
  }
7370
7488
  catch (_) { }
7371
7489
  }
7372
7490
  if (doc.caretRangeFromPoint) {
7373
7491
  let range = doc.caretRangeFromPoint(x, y);
7374
7492
  if (range)
7375
- return { node: range.startContainer, offset: range.startOffset };
7493
+ return { node: range.startContainer, offset: Math.min(nodeSize(range.startContainer), range.startOffset) };
7376
7494
  }
7377
7495
  }
7378
7496
 
@@ -7399,6 +7517,12 @@ const webkit = !!doc && "webkitFontSmoothing" in doc.documentElement.style;
7399
7517
  const webkit_version = webkit ? +(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent) || [0, 0])[1] : 0;
7400
7518
 
7401
7519
  function windowRect(doc) {
7520
+ let vp = doc.defaultView && doc.defaultView.visualViewport;
7521
+ if (vp)
7522
+ return {
7523
+ left: 0, right: vp.width,
7524
+ top: 0, bottom: vp.height
7525
+ };
7402
7526
  return { left: 0, right: doc.documentElement.clientWidth,
7403
7527
  top: 0, bottom: doc.documentElement.clientHeight };
7404
7528
  }
@@ -7612,17 +7736,19 @@ function posFromCaret(view, node, offset, coords) {
7612
7736
  for (let cur = node, sawBlock = false;;) {
7613
7737
  if (cur == view.dom)
7614
7738
  break;
7615
- let desc = view.docView.nearestDesc(cur, true);
7739
+ let desc = view.docView.nearestDesc(cur, true), rect;
7616
7740
  if (!desc)
7617
7741
  return null;
7618
- if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent && !sawBlock || !desc.contentDOM)) {
7619
- let rect = desc.dom.getBoundingClientRect();
7620
- if (desc.node.isBlock && desc.parent && !sawBlock) {
7621
- sawBlock = true;
7622
- if (rect.left > coords.left || rect.top > coords.top)
7742
+ if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent || !desc.contentDOM) &&
7743
+ // Ignore elements with zero-size bounding rectangles
7744
+ ((rect = desc.dom.getBoundingClientRect()).width || rect.height)) {
7745
+ if (desc.node.isBlock && desc.parent) {
7746
+ // Only apply the horizontal test to the innermost block. Vertical for any parent.
7747
+ if (!sawBlock && rect.left > coords.left || rect.top > coords.top)
7623
7748
  outsideBlock = desc.posBefore;
7624
- else if (rect.right < coords.left || rect.bottom < coords.top)
7749
+ else if (!sawBlock && rect.right < coords.left || rect.bottom < coords.top)
7625
7750
  outsideBlock = desc.posAfter;
7751
+ sawBlock = true;
7626
7752
  }
7627
7753
  if (!desc.contentDOM && outsideBlock < 0 && !desc.node.isText) {
7628
7754
  // If we are inside a leaf, return the side of the leaf closer to the coords
@@ -7877,6 +8003,8 @@ function endOfTextblockHorizontal(view, state, dir) {
7877
8003
  return false;
7878
8004
  let offset = $head.parentOffset, atStart = !offset, atEnd = offset == $head.parent.content.size;
7879
8005
  let sel = view.domSelection();
8006
+ if (!sel)
8007
+ return $head.pos == $head.start() || $head.pos == $head.end();
7880
8008
  // If the textblock is all LTR, or the browser doesn't support
7881
8009
  // Selection.modify (Edge), fall back to a primitive approach
7882
8010
  if (!maybeRTL.test($head.parent.textContent) || !sel.modify)
@@ -8209,18 +8337,19 @@ class ViewDesc {
8209
8337
  // custom things with the selection. Note that this falls apart when
8210
8338
  // a selection starts in such a node and ends in another, in which
8211
8339
  // case we just use whatever domFromPos produces as a best effort.
8212
- setSelection(anchor, head, root, force = false) {
8340
+ setSelection(anchor, head, view, force = false) {
8213
8341
  // If the selection falls entirely in a child, give it to that child
8214
8342
  let from = Math.min(anchor, head), to = Math.max(anchor, head);
8215
8343
  for (let i = 0, offset = 0; i < this.children.length; i++) {
8216
8344
  let child = this.children[i], end = offset + child.size;
8217
8345
  if (from > offset && to < end)
8218
- return child.setSelection(anchor - offset - child.border, head - offset - child.border, root, force);
8346
+ return child.setSelection(anchor - offset - child.border, head - offset - child.border, view, force);
8219
8347
  offset = end;
8220
8348
  }
8221
8349
  let anchorDOM = this.domFromPos(anchor, anchor ? -1 : 1);
8222
8350
  let headDOM = head == anchor ? anchorDOM : this.domFromPos(head, head ? -1 : 1);
8223
- let domSel = root.getSelection();
8351
+ let domSel = view.root.getSelection();
8352
+ let selRange = view.domSelectionRange();
8224
8353
  let brKludge = false;
8225
8354
  // On Firefox, using Selection.collapse to put the cursor after a
8226
8355
  // BR node for some reason doesn't always work (#1073). On Safari,
@@ -8251,14 +8380,14 @@ class ViewDesc {
8251
8380
  }
8252
8381
  // Firefox can act strangely when the selection is in front of an
8253
8382
  // uneditable node. See #1163 and https://bugzilla.mozilla.org/show_bug.cgi?id=1709536
8254
- if (gecko && domSel.focusNode && domSel.focusNode != headDOM.node && domSel.focusNode.nodeType == 1) {
8255
- let after = domSel.focusNode.childNodes[domSel.focusOffset];
8383
+ if (gecko && selRange.focusNode && selRange.focusNode != headDOM.node && selRange.focusNode.nodeType == 1) {
8384
+ let after = selRange.focusNode.childNodes[selRange.focusOffset];
8256
8385
  if (after && after.contentEditable == "false")
8257
8386
  force = true;
8258
8387
  }
8259
8388
  if (!(force || brKludge && safari) &&
8260
- isEquivalentPosition(anchorDOM.node, anchorDOM.offset, domSel.anchorNode, domSel.anchorOffset) &&
8261
- isEquivalentPosition(headDOM.node, headDOM.offset, domSel.focusNode, domSel.focusOffset))
8389
+ isEquivalentPosition(anchorDOM.node, anchorDOM.offset, selRange.anchorNode, selRange.anchorOffset) &&
8390
+ isEquivalentPosition(headDOM.node, headDOM.offset, selRange.focusNode, selRange.focusOffset))
8262
8391
  return;
8263
8392
  // Selection.extend can be used to create an 'inverted' selection
8264
8393
  // (one where the focus is before the anchor), but not all
@@ -8334,6 +8463,7 @@ class ViewDesc {
8334
8463
  }
8335
8464
  get domAtom() { return false; }
8336
8465
  get ignoreForCoords() { return false; }
8466
+ isText(text) { return false; }
8337
8467
  }
8338
8468
  // A widget desc represents a widget decoration, which is a DOM node
8339
8469
  // drawn between the document nodes.
@@ -8404,16 +8534,17 @@ class CompositionViewDesc extends ViewDesc {
8404
8534
  // some cases they will be split more often than would appear
8405
8535
  // necessary.
8406
8536
  class MarkViewDesc extends ViewDesc {
8407
- constructor(parent, mark, dom, contentDOM) {
8537
+ constructor(parent, mark, dom, contentDOM, spec) {
8408
8538
  super(parent, [], dom, contentDOM);
8409
8539
  this.mark = mark;
8540
+ this.spec = spec;
8410
8541
  }
8411
8542
  static create(parent, mark, inline, view) {
8412
8543
  let custom = view.nodeViews[mark.type.name];
8413
8544
  let spec = custom && custom(mark, view, inline);
8414
8545
  if (!spec || !spec.dom)
8415
- spec = DOMSerializer.renderSpec(document, mark.type.spec.toDOM(mark, inline));
8416
- return new MarkViewDesc(parent, mark, spec.dom, spec.contentDOM || spec.dom);
8546
+ spec = DOMSerializer.renderSpec(document, mark.type.spec.toDOM(mark, inline), null, mark.attrs);
8547
+ return new MarkViewDesc(parent, mark, spec.dom, spec.contentDOM || spec.dom, spec);
8417
8548
  }
8418
8549
  parseRule() {
8419
8550
  if ((this.dirty & NODE_DIRTY) || this.mark.type.spec.reparseInView)
@@ -8445,6 +8576,14 @@ class MarkViewDesc extends ViewDesc {
8445
8576
  copy.children = nodes;
8446
8577
  return copy;
8447
8578
  }
8579
+ ignoreMutation(mutation) {
8580
+ return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation);
8581
+ }
8582
+ destroy() {
8583
+ if (this.spec.destroy)
8584
+ this.spec.destroy();
8585
+ super.destroy();
8586
+ }
8448
8587
  }
8449
8588
  // Node view descs are the main, most common type of view desc, and
8450
8589
  // correspond to an actual node in the document. Unlike mark descs,
@@ -8484,7 +8623,8 @@ class NodeViewDesc extends ViewDesc {
8484
8623
  throw new RangeError("Text must be rendered as a DOM text node");
8485
8624
  }
8486
8625
  else if (!dom) {
8487
- ({ dom, contentDOM } = DOMSerializer.renderSpec(document, node.type.spec.toDOM(node)));
8626
+ let spec = DOMSerializer.renderSpec(document, node.type.spec.toDOM(node), null, node.attrs);
8627
+ ({ dom, contentDOM } = spec);
8488
8628
  }
8489
8629
  if (!contentDOM && !node.isText && dom.nodeName != "BR") { // Chrome gets confused by <br contenteditable=false>
8490
8630
  if (!dom.hasAttribute("contenteditable"))
@@ -8596,8 +8736,7 @@ class NodeViewDesc extends ViewDesc {
8596
8736
  let { from, to } = view.state.selection;
8597
8737
  if (!(view.state.selection instanceof TextSelection) || from < pos || to > pos + this.node.content.size)
8598
8738
  return null;
8599
- let sel = view.domSelectionRange();
8600
- let textNode = nearbyTextNode(sel.focusNode, sel.focusOffset);
8739
+ let textNode = view.input.compositionNode;
8601
8740
  if (!textNode || !this.dom.contains(textNode.parentNode))
8602
8741
  return null;
8603
8742
  if (this.node.inlineContent) {
@@ -8671,10 +8810,11 @@ class NodeViewDesc extends ViewDesc {
8671
8810
  }
8672
8811
  // Remove selected node marking from this node.
8673
8812
  deselectNode() {
8674
- if (this.nodeDOM.nodeType == 1)
8813
+ if (this.nodeDOM.nodeType == 1) {
8675
8814
  this.nodeDOM.classList.remove("ProseMirror-selectednode");
8676
- if (this.contentDOM || !this.node.type.spec.draggable)
8677
- this.dom.removeAttribute("draggable");
8815
+ if (this.contentDOM || !this.node.type.spec.draggable)
8816
+ this.dom.removeAttribute("draggable");
8817
+ }
8678
8818
  }
8679
8819
  get domAtom() { return this.node.isAtom; }
8680
8820
  }
@@ -8739,6 +8879,7 @@ class TextViewDesc extends NodeViewDesc {
8739
8879
  this.dirty = NODE_DIRTY;
8740
8880
  }
8741
8881
  get domAtom() { return false; }
8882
+ isText(text) { return this.node.text == text; }
8742
8883
  }
8743
8884
  // A dummy desc used to tag trailing BR or IMG nodes created to work
8744
8885
  // around contentEditable terribleness.
@@ -8762,7 +8903,7 @@ class CustomNodeViewDesc extends NodeViewDesc {
8762
8903
  update(node, outerDeco, innerDeco, view) {
8763
8904
  if (this.dirty == NODE_DIRTY)
8764
8905
  return false;
8765
- if (this.spec.update) {
8906
+ if (this.spec.update && (this.node.type == node.type || this.spec.multiType)) {
8766
8907
  let result = this.spec.update(node, outerDeco, innerDeco);
8767
8908
  if (result)
8768
8909
  this.updateInner(node, outerDeco, innerDeco, view);
@@ -8781,9 +8922,9 @@ class CustomNodeViewDesc extends NodeViewDesc {
8781
8922
  deselectNode() {
8782
8923
  this.spec.deselectNode ? this.spec.deselectNode() : super.deselectNode();
8783
8924
  }
8784
- setSelection(anchor, head, root, force) {
8785
- this.spec.setSelection ? this.spec.setSelection(anchor, head, root)
8786
- : super.setSelection(anchor, head, root, force);
8925
+ setSelection(anchor, head, view, force) {
8926
+ this.spec.setSelection ? this.spec.setSelection(anchor, head, view.root)
8927
+ : super.setSelection(anchor, head, view, force);
8787
8928
  }
8788
8929
  destroy() {
8789
8930
  if (this.spec.destroy)
@@ -9080,6 +9221,7 @@ class ViewTreeUpdater {
9080
9221
  return true;
9081
9222
  }
9082
9223
  else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
9224
+ this.destroyBetween(this.index, i);
9083
9225
  this.top.children[this.index] = updated;
9084
9226
  if (updated.contentDOM) {
9085
9227
  updated.dirty = CONTENT_DIRTY;
@@ -9099,7 +9241,8 @@ class ViewTreeUpdater {
9099
9241
  // identical content, move over its children.
9100
9242
  recreateWrapper(next, node, outerDeco, innerDeco, view, pos) {
9101
9243
  if (next.dirty || node.isAtom || !next.children.length ||
9102
- !next.node.content.eq(node.content))
9244
+ !next.node.content.eq(node.content) ||
9245
+ !sameOuterDeco(outerDeco, next.outerDeco) || !innerDeco.eq(next.innerDeco))
9103
9246
  return null;
9104
9247
  let wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos);
9105
9248
  if (wrapper.contentDOM) {
@@ -9308,25 +9451,6 @@ function iosHacks(dom) {
9308
9451
  dom.style.cssText = oldCSS;
9309
9452
  }
9310
9453
  }
9311
- function nearbyTextNode(node, offset) {
9312
- for (;;) {
9313
- if (node.nodeType == 3)
9314
- return node;
9315
- if (node.nodeType == 1 && offset > 0) {
9316
- if (node.childNodes.length > offset && node.childNodes[offset].nodeType == 3)
9317
- return node.childNodes[offset];
9318
- node = node.childNodes[offset - 1];
9319
- offset = nodeSize(node);
9320
- }
9321
- else if (node.nodeType == 1 && offset < node.childNodes.length) {
9322
- node = node.childNodes[offset];
9323
- offset = 0;
9324
- }
9325
- else {
9326
- return null;
9327
- }
9328
- }
9329
- }
9330
9454
  // Find a piece of text in an inline fragment, overlapping from-to
9331
9455
  function findTextInFragment(frag, text, from, to) {
9332
9456
  for (let i = 0, pos = 0; i < frag.childCount && pos <= to;) {
@@ -9389,9 +9513,9 @@ function selectionFromDOM(view, origin = null) {
9389
9513
  let head = view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset, 1);
9390
9514
  if (head < 0)
9391
9515
  return null;
9392
- let $head = doc.resolve(head), $anchor, selection;
9516
+ let $head = doc.resolve(head), anchor, selection;
9393
9517
  if (selectionCollapsed(domSel)) {
9394
- $anchor = $head;
9518
+ anchor = head;
9395
9519
  while (nearestDesc && !nearestDesc.node)
9396
9520
  nearestDesc = nearestDesc.parent;
9397
9521
  let nearestDescNode = nearestDesc.node;
@@ -9402,11 +9526,25 @@ function selectionFromDOM(view, origin = null) {
9402
9526
  }
9403
9527
  }
9404
9528
  else {
9405
- let anchor = view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset, 1);
9529
+ if (domSel instanceof view.dom.ownerDocument.defaultView.Selection && domSel.rangeCount > 1) {
9530
+ let min = head, max = head;
9531
+ for (let i = 0; i < domSel.rangeCount; i++) {
9532
+ let range = domSel.getRangeAt(i);
9533
+ min = Math.min(min, view.docView.posFromDOM(range.startContainer, range.startOffset, 1));
9534
+ max = Math.max(max, view.docView.posFromDOM(range.endContainer, range.endOffset, -1));
9535
+ }
9536
+ if (min < 0)
9537
+ return null;
9538
+ [anchor, head] = max == view.state.selection.anchor ? [max, min] : [min, max];
9539
+ $head = doc.resolve(head);
9540
+ }
9541
+ else {
9542
+ anchor = view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset, 1);
9543
+ }
9406
9544
  if (anchor < 0)
9407
9545
  return null;
9408
- $anchor = doc.resolve(anchor);
9409
9546
  }
9547
+ let $anchor = doc.resolve(anchor);
9410
9548
  if (!selection) {
9411
9549
  let bias = origin == "pointer" || (view.state.selection.head < $head.pos && !inWidget) ? 1 : -1;
9412
9550
  selection = selectionBetween(view, $anchor, $head, bias);
@@ -9446,7 +9584,7 @@ function selectionToDOM(view, force = false) {
9446
9584
  if (!sel.empty && !sel.$from.parent.inlineContent)
9447
9585
  resetEditableTo = temporarilyEditableNear(view, sel.to);
9448
9586
  }
9449
- view.docView.setSelection(anchor, head, view.root, force);
9587
+ view.docView.setSelection(anchor, head, view, force);
9450
9588
  if (brokenSelectBetweenUneditable) {
9451
9589
  if (resetEditableFrom)
9452
9590
  resetEditable(resetEditableFrom);
@@ -9515,12 +9653,14 @@ function removeClassOnSelectionChange(view) {
9515
9653
  }
9516
9654
  function selectCursorWrapper(view) {
9517
9655
  let domSel = view.domSelection(), range = document.createRange();
9656
+ if (!domSel)
9657
+ return;
9518
9658
  let node = view.cursorWrapper.dom, img = node.nodeName == "IMG";
9519
9659
  if (img)
9520
- range.setEnd(node.parentNode, domIndex(node) + 1);
9660
+ range.setStart(node.parentNode, domIndex(node) + 1);
9521
9661
  else
9522
- range.setEnd(node, 0);
9523
- range.collapse(false);
9662
+ range.setStart(node, 0);
9663
+ range.collapse(true);
9524
9664
  domSel.removeAllRanges();
9525
9665
  domSel.addRange(range);
9526
9666
  // Kludge to kill 'control selection' in IE11 when selecting an
@@ -9808,6 +9948,8 @@ function setSelFocus(view, node, offset) {
9808
9948
  }
9809
9949
  }
9810
9950
  let sel = view.domSelection();
9951
+ if (!sel)
9952
+ return;
9811
9953
  if (selectionCollapsed(sel)) {
9812
9954
  let range = document.createRange();
9813
9955
  range.setEnd(node, offset);
@@ -9990,7 +10132,7 @@ function serializeForClipboard(view, slice) {
9990
10132
  firstChild.setAttribute("data-pm-slice", `${openStart} ${openEnd}${wrappers ? ` -${wrappers}` : ""} ${JSON.stringify(context)}`);
9991
10133
  let text = view.someProp("clipboardTextSerializer", f => f(slice, view)) ||
9992
10134
  slice.content.textBetween(0, slice.content.size, "\n\n");
9993
- return { dom: wrap, text };
10135
+ return { dom: wrap, text, slice };
9994
10136
  }
9995
10137
  // Read a slice of content from the clipboard (or drop data).
9996
10138
  function parseFromClipboard(view, text, html, plainText, $context) {
@@ -10162,6 +10304,18 @@ let _detachedDoc = null;
10162
10304
  function detachedDoc() {
10163
10305
  return _detachedDoc || (_detachedDoc = document.implementation.createHTMLDocument("title"));
10164
10306
  }
10307
+ let _policy = null;
10308
+ function maybeWrapTrusted(html) {
10309
+ let trustedTypes = window.trustedTypes;
10310
+ if (!trustedTypes)
10311
+ return html;
10312
+ // With the require-trusted-types-for CSP, Chrome will block
10313
+ // innerHTML, even on a detached document. This wraps the string in
10314
+ // a way that makes the browser allow us to use its parser again.
10315
+ if (!_policy)
10316
+ _policy = trustedTypes.createPolicy("ProseMirrorClipboard", { createHTML: (s) => s });
10317
+ return _policy.createHTML(html);
10318
+ }
10165
10319
  function readHTML(html) {
10166
10320
  let metas = /^(\s*<meta [^>]*>)*/.exec(html);
10167
10321
  if (metas)
@@ -10170,7 +10324,7 @@ function readHTML(html) {
10170
10324
  let firstTag = /<([a-z][^>\s]+)/i.exec(html), wrap;
10171
10325
  if (wrap = firstTag && wrapMap[firstTag[1].toLowerCase()])
10172
10326
  html = wrap.map(n => "<" + n + ">").join("") + html + wrap.map(n => "</" + n + ">").reverse().join("");
10173
- elt.innerHTML = html;
10327
+ elt.innerHTML = maybeWrapTrusted(html);
10174
10328
  if (wrap)
10175
10329
  for (let i = 0; i < wrap.length; i++)
10176
10330
  elt = elt.querySelector(wrap[i]) || elt;
@@ -10229,8 +10383,9 @@ class InputState {
10229
10383
  this.lastIOSEnterFallbackTimeout = -1;
10230
10384
  this.lastFocus = 0;
10231
10385
  this.lastTouch = 0;
10232
- this.lastAndroidDelete = 0;
10386
+ this.lastChromeDelete = 0;
10233
10387
  this.composing = false;
10388
+ this.compositionNode = null;
10234
10389
  this.composingTimeout = -1;
10235
10390
  this.compositionNodes = [];
10236
10391
  this.compositionEndedAt = -2e8;
@@ -10373,6 +10528,8 @@ function runHandlerOnContext(view, propName, pos, inside, event) {
10373
10528
  function updateSelection(view, selection, origin) {
10374
10529
  if (!view.focused)
10375
10530
  view.focus();
10531
+ if (view.state.selection.eq(selection))
10532
+ return;
10376
10533
  let tr = view.state.tr.setSelection(selection);
10377
10534
  if (origin == "pointer")
10378
10535
  tr.setMeta("pointer", true);
@@ -10506,7 +10663,7 @@ class MouseDown {
10506
10663
  }
10507
10664
  const target = flushed ? null : event.target;
10508
10665
  const targetDesc = target ? view.docView.nearestDesc(target, true) : null;
10509
- this.target = targetDesc ? targetDesc.dom : null;
10666
+ this.target = targetDesc && targetDesc.dom.nodeType == 1 ? targetDesc.dom : null;
10510
10667
  let { selection } = view.state;
10511
10668
  if (event.button == 0 &&
10512
10669
  targetNode.type.spec.draggable && targetNode.type.spec.selectable !== false ||
@@ -10627,8 +10784,8 @@ const timeoutComposition = android ? 5000 : -1;
10627
10784
  editHandlers.compositionstart = editHandlers.compositionupdate = view => {
10628
10785
  if (!view.composing) {
10629
10786
  view.domObserver.flush();
10630
- let { state } = view, $pos = state.selection.$from;
10631
- if (state.selection.empty &&
10787
+ let { state } = view, $pos = state.selection.$to;
10788
+ if (state.selection instanceof TextSelection &&
10632
10789
  (state.storedMarks ||
10633
10790
  (!$pos.textOffset && $pos.parentOffset && $pos.nodeBefore.marks.some(m => m.type.spec.inclusive === false)))) {
10634
10791
  // Need to wrap the cursor in mark nodes different from the ones in the DOM context
@@ -10637,7 +10794,7 @@ editHandlers.compositionstart = editHandlers.compositionupdate = view => {
10637
10794
  view.markCursor = null;
10638
10795
  }
10639
10796
  else {
10640
- endComposition(view);
10797
+ endComposition(view, !state.selection.empty);
10641
10798
  // In firefox, if the cursor is after but outside a marked node,
10642
10799
  // the inserted text won't inherit the marks. So this moves it
10643
10800
  // inside if necessary.
@@ -10648,7 +10805,9 @@ editHandlers.compositionstart = editHandlers.compositionupdate = view => {
10648
10805
  if (!before)
10649
10806
  break;
10650
10807
  if (before.nodeType == 3) {
10651
- view.domSelection().collapse(before, before.nodeValue.length);
10808
+ let sel = view.domSelection();
10809
+ if (sel)
10810
+ sel.collapse(before, before.nodeValue.length);
10652
10811
  break;
10653
10812
  }
10654
10813
  else {
@@ -10667,6 +10826,7 @@ editHandlers.compositionend = (view, event) => {
10667
10826
  view.input.composing = false;
10668
10827
  view.input.compositionEndedAt = event.timeStamp;
10669
10828
  view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0;
10829
+ view.input.compositionNode = null;
10670
10830
  if (view.input.compositionPendingChanges)
10671
10831
  Promise.resolve().then(() => view.domObserver.flush());
10672
10832
  view.input.compositionID++;
@@ -10686,6 +10846,27 @@ function clearComposition(view) {
10686
10846
  while (view.input.compositionNodes.length > 0)
10687
10847
  view.input.compositionNodes.pop().markParentsDirty();
10688
10848
  }
10849
+ function findCompositionNode(view) {
10850
+ let sel = view.domSelectionRange();
10851
+ if (!sel.focusNode)
10852
+ return null;
10853
+ let textBefore = textNodeBefore$1(sel.focusNode, sel.focusOffset);
10854
+ let textAfter = textNodeAfter$1(sel.focusNode, sel.focusOffset);
10855
+ if (textBefore && textAfter && textBefore != textAfter) {
10856
+ let descAfter = textAfter.pmViewDesc, lastChanged = view.domObserver.lastChangedTextNode;
10857
+ if (textBefore == lastChanged || textAfter == lastChanged)
10858
+ return lastChanged;
10859
+ if (!descAfter || !descAfter.isText(textAfter.nodeValue)) {
10860
+ return textAfter;
10861
+ }
10862
+ else if (view.input.compositionNode == textAfter) {
10863
+ let descBefore = textBefore.pmViewDesc;
10864
+ if (!(!descBefore || !descBefore.isText(textBefore.nodeValue)))
10865
+ return textAfter;
10866
+ }
10867
+ }
10868
+ return textBefore || textAfter;
10869
+ }
10689
10870
  function timestampFromCustomEvent() {
10690
10871
  let event = document.createEvent("Event");
10691
10872
  event.initEvent("event", true, true);
@@ -10694,15 +10875,17 @@ function timestampFromCustomEvent() {
10694
10875
  /**
10695
10876
  @internal
10696
10877
  */
10697
- function endComposition(view, forceUpdate = false) {
10878
+ function endComposition(view, restarting = false) {
10698
10879
  if (android && view.domObserver.flushingSoon >= 0)
10699
10880
  return;
10700
10881
  view.domObserver.forceFlush();
10701
10882
  clearComposition(view);
10702
- if (forceUpdate || view.docView && view.docView.dirty) {
10883
+ if (restarting || view.docView && view.docView.dirty) {
10703
10884
  let sel = selectionFromDOM(view);
10704
10885
  if (sel && !sel.eq(view.state.selection))
10705
10886
  view.dispatch(view.state.tr.setSelection(sel));
10887
+ else if ((view.markCursor || restarting) && !view.state.selection.empty)
10888
+ view.dispatch(view.state.tr.deleteSelection());
10706
10889
  else
10707
10890
  view.updateState(view.state);
10708
10891
  return true;
@@ -10841,8 +11024,11 @@ handlers.dragstart = (view, _event) => {
10841
11024
  if (desc && desc.node.type.spec.draggable && desc != view.docView)
10842
11025
  node = NodeSelection.create(view.state.doc, desc.posBefore);
10843
11026
  }
10844
- let slice = (node || view.state.selection).content(), { dom, text } = serializeForClipboard(view, slice);
10845
- event.dataTransfer.clearData();
11027
+ let draggedSlice = (node || view.state.selection).content();
11028
+ let { dom, text, slice } = serializeForClipboard(view, draggedSlice);
11029
+ // Pre-120 Chrome versions clear files when calling `clearData` (#1472)
11030
+ if (!event.dataTransfer.files.length || !chrome || chrome_version > 120)
11031
+ event.dataTransfer.clearData();
10846
11032
  event.dataTransfer.setData(brokenClipboardAPI ? "Text" : "text/html", dom.innerHTML);
10847
11033
  // See https://github.com/ProseMirror/prosemirror/issues/1156
10848
11034
  event.dataTransfer.effectAllowed = "copyMove";
@@ -11356,6 +11542,7 @@ class DecorationSet {
11356
11542
  }
11357
11543
  return result;
11358
11544
  }
11545
+ forEachSet(f) { f(this); }
11359
11546
  }
11360
11547
  /**
11361
11548
  The empty set of decorations.
@@ -11431,6 +11618,10 @@ class DecorationGroup {
11431
11618
  members.reduce((r, m) => r.concat(m instanceof DecorationSet ? m : m.members), []));
11432
11619
  }
11433
11620
  }
11621
+ forEachSet(f) {
11622
+ for (let i = 0; i < this.members.length; i++)
11623
+ this.members[i].forEachSet(f);
11624
+ }
11434
11625
  }
11435
11626
  function mapChildren(oldChildren, newLocal, mapping, node, offset, oldOffset, options) {
11436
11627
  let children = oldChildren.slice();
@@ -11685,6 +11876,7 @@ class DOMObserver {
11685
11876
  this.currentSelection = new SelectionState;
11686
11877
  this.onCharData = null;
11687
11878
  this.suppressingSelectionUpdates = false;
11879
+ this.lastChangedTextNode = null;
11688
11880
  this.observer = window.MutationObserver &&
11689
11881
  new window.MutationObserver(mutations => {
11690
11882
  for (let i = 0; i < mutations.length; i++)
@@ -11817,15 +12009,23 @@ class DOMObserver {
11817
12009
  }
11818
12010
  }
11819
12011
  }
11820
- if (gecko && added.length > 1) {
12012
+ if (gecko && added.length) {
11821
12013
  let brs = added.filter(n => n.nodeName == "BR");
11822
12014
  if (brs.length == 2) {
11823
- let a = brs[0], b = brs[1];
12015
+ let [a, b] = brs;
11824
12016
  if (a.parentNode && a.parentNode.parentNode == b.parentNode)
11825
12017
  b.remove();
11826
12018
  else
11827
12019
  a.remove();
11828
12020
  }
12021
+ else {
12022
+ let { focusNode } = this.currentSelection;
12023
+ for (let br of brs) {
12024
+ let parent = br.parentNode;
12025
+ if (parent && parent.nodeName == "LI" && (!focusNode || blockParent(view, focusNode) != parent))
12026
+ br.remove();
12027
+ }
12028
+ }
11829
12029
  }
11830
12030
  let readSel = null;
11831
12031
  // If it looks like the browser has reset the selection to the
@@ -11866,8 +12066,12 @@ class DOMObserver {
11866
12066
  if (!desc || desc.ignoreMutation(mut))
11867
12067
  return null;
11868
12068
  if (mut.type == "childList") {
11869
- for (let i = 0; i < mut.addedNodes.length; i++)
11870
- added.push(mut.addedNodes[i]);
12069
+ for (let i = 0; i < mut.addedNodes.length; i++) {
12070
+ let node = mut.addedNodes[i];
12071
+ added.push(node);
12072
+ if (node.nodeType == 3)
12073
+ this.lastChangedTextNode = node;
12074
+ }
11871
12075
  if (desc.contentDOM && desc.contentDOM != desc.dom && !desc.contentDOM.contains(mut.target))
11872
12076
  return { from: desc.posBefore, to: desc.posAfter };
11873
12077
  let prev = mut.previousSibling, next = mut.nextSibling;
@@ -11894,6 +12098,7 @@ class DOMObserver {
11894
12098
  return { from: desc.posAtStart - desc.border, to: desc.posAtEnd + desc.border };
11895
12099
  }
11896
12100
  else { // "characterData"
12101
+ this.lastChangedTextNode = mut.target;
11897
12102
  return {
11898
12103
  from: desc.posAtStart,
11899
12104
  to: desc.posAtEnd,
@@ -11920,9 +12125,25 @@ function checkCSS(view) {
11920
12125
  cssCheckWarned = true;
11921
12126
  }
11922
12127
  }
12128
+ function rangeToSelectionRange(view, range) {
12129
+ let anchorNode = range.startContainer, anchorOffset = range.startOffset;
12130
+ let focusNode = range.endContainer, focusOffset = range.endOffset;
12131
+ let currentAnchor = view.domAtPos(view.state.selection.anchor);
12132
+ // Since such a range doesn't distinguish between anchor and head,
12133
+ // use a heuristic that flips it around if its end matches the
12134
+ // current anchor.
12135
+ if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
12136
+ [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
12137
+ return { anchorNode, anchorOffset, focusNode, focusOffset };
12138
+ }
11923
12139
  // Used to work around a Safari Selection/shadow DOM bug
11924
12140
  // Based on https://github.com/codemirror/dev/issues/414 fix
11925
- function safariShadowSelectionRange(view) {
12141
+ function safariShadowSelectionRange(view, selection) {
12142
+ if (selection.getComposedRanges) {
12143
+ let range = selection.getComposedRanges(view.root)[0];
12144
+ if (range)
12145
+ return rangeToSelectionRange(view, range);
12146
+ }
11926
12147
  let found;
11927
12148
  function read(event) {
11928
12149
  event.preventDefault();
@@ -11937,15 +12158,15 @@ function safariShadowSelectionRange(view) {
11937
12158
  view.dom.addEventListener("beforeinput", read, true);
11938
12159
  document.execCommand("indent");
11939
12160
  view.dom.removeEventListener("beforeinput", read, true);
11940
- let anchorNode = found.startContainer, anchorOffset = found.startOffset;
11941
- let focusNode = found.endContainer, focusOffset = found.endOffset;
11942
- let currentAnchor = view.domAtPos(view.state.selection.anchor);
11943
- // Since such a range doesn't distinguish between anchor and head,
11944
- // use a heuristic that flips it around if its end matches the
11945
- // current anchor.
11946
- if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
11947
- [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
11948
- return { anchorNode, anchorOffset, focusNode, focusOffset };
12161
+ return found ? rangeToSelectionRange(view, found) : null;
12162
+ }
12163
+ function blockParent(view, node) {
12164
+ for (let p = node.parentNode; p && p != view.dom; p = p.parentNode) {
12165
+ let desc = view.docView.nearestDesc(p, true);
12166
+ if (desc && desc.node.isBlock)
12167
+ return p;
12168
+ }
12169
+ return null;
11949
12170
  }
11950
12171
 
11951
12172
  // Note that all referencing and parsing is done with the
@@ -12063,6 +12284,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
12063
12284
  }
12064
12285
  view.input.lastKeyCode = null;
12065
12286
  let change = findDiff(compare.content, parse.doc.content, parse.from, preferredPos, preferredSide);
12287
+ if (change)
12288
+ view.input.domChangeCount++;
12066
12289
  if ((ios && view.input.lastIOSEnter > Date.now() - 225 || android) &&
12067
12290
  addedNodes.some(n => n.nodeType == 1 && !isInline.test(n.nodeName)) &&
12068
12291
  (!change || change.endA >= change.endB) &&
@@ -12088,14 +12311,6 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
12088
12311
  return;
12089
12312
  }
12090
12313
  }
12091
- // Chrome sometimes leaves the cursor before the inserted text when
12092
- // composing after a cursor wrapper. This moves it forward.
12093
- if (chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from &&
12094
- parse.sel.head == parse.sel.anchor) {
12095
- let size = change.endB - change.start;
12096
- parse.sel = { anchor: parse.sel.anchor + size, head: parse.sel.anchor + size };
12097
- }
12098
- view.input.domChangeCount++;
12099
12314
  // Handle the case where overwriting a selection by typing matches
12100
12315
  // the start or end of the selected content, creating a change
12101
12316
  // that's smaller than what was actually overwritten.
@@ -12140,17 +12355,17 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
12140
12355
  }
12141
12356
  // Same for backspace
12142
12357
  if (view.state.selection.anchor > change.start &&
12143
- looksLikeJoin(doc, change.start, change.endA, $from, $to) &&
12358
+ looksLikeBackspace(doc, change.start, change.endA, $from, $to) &&
12144
12359
  view.someProp("handleKeyDown", f => f(view, keyEvent(8, "Backspace")))) {
12145
12360
  if (android && chrome)
12146
12361
  view.domObserver.suppressSelectionUpdates(); // #820
12147
12362
  return;
12148
12363
  }
12149
- // Chrome Android will occasionally, during composition, delete the
12364
+ // Chrome will occasionally, during composition, delete the
12150
12365
  // entire composition and then immediately insert it again. This is
12151
12366
  // used to detect that situation.
12152
- if (chrome && android && change.endB == change.start)
12153
- view.input.lastAndroidDelete = Date.now();
12367
+ if (chrome && change.endB == change.start)
12368
+ view.input.lastChromeDelete = Date.now();
12154
12369
  // This tries to detect Android virtual keyboard
12155
12370
  // enter-and-pick-suggestion action. That sometimes (see issue
12156
12371
  // #1059) first fires a DOM mutation, before moving the selection to
@@ -12201,13 +12416,13 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
12201
12416
  tr = view.state.tr.replace(chFrom, chTo, parse.doc.slice(change.start - parse.from, change.endB - parse.from));
12202
12417
  if (parse.sel) {
12203
12418
  let sel = resolveSelection(view, tr.doc, parse.sel);
12204
- // Chrome Android will sometimes, during composition, report the
12419
+ // Chrome will sometimes, during composition, report the
12205
12420
  // selection in the wrong place. If it looks like that is
12206
12421
  // happening, don't update the selection.
12207
12422
  // Edge just doesn't move the cursor forward when you start typing
12208
12423
  // in an empty block or between br nodes.
12209
- if (sel && !(chrome && android && view.composing && sel.empty &&
12210
- (change.start != change.endB || view.input.lastAndroidDelete < Date.now() - 100) &&
12424
+ if (sel && !(chrome && view.composing && sel.empty &&
12425
+ (change.start != change.endB || view.input.lastChromeDelete < Date.now() - 100) &&
12211
12426
  (sel.head == chFrom || sel.head == tr.mapping.map(chTo) - 1) ||
12212
12427
  ie$1 && sel.empty && sel.head == chFrom))
12213
12428
  tr.setSelection(sel);
@@ -12252,14 +12467,18 @@ function isMarkChange(cur, prev) {
12252
12467
  if (Fragment.from(updated).eq(cur))
12253
12468
  return { mark, type };
12254
12469
  }
12255
- function looksLikeJoin(old, start, end, $newStart, $newEnd) {
12256
- if (!$newStart.parent.isTextblock ||
12257
- // The content must have shrunk
12258
- end - start <= $newEnd.pos - $newStart.pos ||
12470
+ function looksLikeBackspace(old, start, end, $newStart, $newEnd) {
12471
+ if ( // The content must have shrunk
12472
+ end - start <= $newEnd.pos - $newStart.pos ||
12259
12473
  // newEnd must point directly at or after the end of the block that newStart points into
12260
12474
  skipClosingAndOpening($newStart, true, false) < $newEnd.pos)
12261
12475
  return false;
12262
12476
  let $start = old.resolve(start);
12477
+ // Handle the case where, rather than joining blocks, the change just removed an entire block
12478
+ if (!$newStart.parent.isTextblock) {
12479
+ let after = $start.nodeAfter;
12480
+ return after != null && end == start + after.nodeSize;
12481
+ }
12263
12482
  // Start must be at the end of a block
12264
12483
  if ($start.parentOffset < $start.parent.content.size || !$start.parent.isTextblock)
12265
12484
  return false;
@@ -12497,8 +12716,10 @@ class EditorView {
12497
12716
  // tracks that and forces a selection reset when our update
12498
12717
  // did write to the node.
12499
12718
  let chromeKludge = chrome ? (this.trackWrites = this.domSelectionRange().focusNode) : null;
12719
+ if (this.composing)
12720
+ this.input.compositionNode = findCompositionNode(this);
12500
12721
  if (redraw || !this.docView.update(state.doc, outerDeco, innerDeco, this)) {
12501
- this.docView.updateOuterDeco([]);
12722
+ this.docView.updateOuterDeco(outerDeco);
12502
12723
  this.docView.destroy();
12503
12724
  this.docView = docViewDesc(state.doc, outerDeco, innerDeco, this.dom, this);
12504
12725
  }
@@ -12775,6 +12996,7 @@ class EditorView {
12775
12996
  }
12776
12997
  this.docView.destroy();
12777
12998
  this.docView = null;
12999
+ clearReusedRange();
12778
13000
  }
12779
13001
  /**
12780
13002
  This is true when the view has been
@@ -12810,8 +13032,11 @@ class EditorView {
12810
13032
  @internal
12811
13033
  */
12812
13034
  domSelectionRange() {
12813
- return safari && this.root.nodeType === 11 && deepActiveElement(this.dom.ownerDocument) == this.dom
12814
- ? safariShadowSelectionRange(this) : this.domSelection();
13035
+ let sel = this.domSelection();
13036
+ if (!sel)
13037
+ return { focusNode: null, focusOffset: 0, anchorNode: null, anchorOffset: 0 };
13038
+ return safari && this.root.nodeType === 11 &&
13039
+ deepActiveElement(this.dom.ownerDocument) == this.dom && safariShadowSelectionRange(this, sel) || sel;
12815
13040
  }
12816
13041
  /**
12817
13042
  @internal
@@ -12847,7 +13072,7 @@ function updateCursorWrapper(view) {
12847
13072
  dom.className = "ProseMirror-separator";
12848
13073
  dom.setAttribute("mark-placeholder", "true");
12849
13074
  dom.setAttribute("alt", "");
12850
- view.cursorWrapper = { dom, deco: Decoration.widget(view.state.selection.head, dom, { raw: true, marks: view.markCursor }) };
13075
+ view.cursorWrapper = { dom, deco: Decoration.widget(view.state.selection.from, dom, { raw: true, marks: view.markCursor }) };
12851
13076
  }
12852
13077
  else {
12853
13078
  view.cursorWrapper = null;