@limetech/lime-elements 37.78.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 (76) hide show
  1. package/CHANGELOG.md +8 -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-prosemirror-adapter.cjs.entry.js +341 -116
  12. package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js.map +1 -1
  13. package/dist/cjs/limel-shortcut.cjs.entry.js +9 -6
  14. package/dist/cjs/limel-shortcut.cjs.entry.js.map +1 -1
  15. package/dist/cjs/loader.cjs.js +1 -1
  16. package/dist/collection/collection-manifest.json +1 -0
  17. package/dist/collection/components/3d-hover-effect-glow/3d-hover-effect-glow.css +18 -0
  18. package/dist/collection/components/3d-hover-effect-glow/3d-hover-effect-glow.js +34 -0
  19. package/dist/collection/components/3d-hover-effect-glow/3d-hover-effect-glow.js.map +1 -0
  20. package/dist/collection/components/card/card.css +9 -16
  21. package/dist/collection/components/card/card.js +1 -1
  22. package/dist/collection/components/card/card.js.map +1 -1
  23. package/dist/collection/components/info-tile/info-tile.css +9 -16
  24. package/dist/collection/components/info-tile/info-tile.js +1 -1
  25. package/dist/collection/components/info-tile/info-tile.js.map +1 -1
  26. package/dist/collection/components/shortcut/shortcut.css +65 -34
  27. package/dist/collection/components/shortcut/shortcut.js +9 -6
  28. package/dist/collection/components/shortcut/shortcut.js.map +1 -1
  29. package/dist/collection/style/mixins.scss +9 -36
  30. package/dist/collection/util/3d-tilt-hover-effect.js +1 -5
  31. package/dist/collection/util/3d-tilt-hover-effect.js.map +1 -1
  32. package/dist/esm/{3d-tilt-hover-effect-a76fcd43.js → 3d-tilt-hover-effect-05648b3c.js} +2 -6
  33. package/dist/esm/3d-tilt-hover-effect-05648b3c.js.map +1 -0
  34. package/dist/esm/lime-elements.js +1 -1
  35. package/dist/esm/limel-3d-hover-effect-glow.entry.js +17 -0
  36. package/dist/esm/limel-3d-hover-effect-glow.entry.js.map +1 -0
  37. package/dist/esm/limel-card.entry.js +3 -3
  38. package/dist/esm/limel-card.entry.js.map +1 -1
  39. package/dist/esm/limel-info-tile.entry.js +3 -3
  40. package/dist/esm/limel-info-tile.entry.js.map +1 -1
  41. package/dist/esm/limel-prosemirror-adapter.entry.js +341 -116
  42. package/dist/esm/limel-prosemirror-adapter.entry.js.map +1 -1
  43. package/dist/esm/limel-shortcut.entry.js +10 -7
  44. package/dist/esm/limel-shortcut.entry.js.map +1 -1
  45. package/dist/esm/loader.js +1 -1
  46. package/dist/lime-elements/lime-elements.esm.js +1 -1
  47. package/dist/lime-elements/lime-elements.esm.js.map +1 -1
  48. package/dist/lime-elements/p-85ffcf55.entry.js +2 -0
  49. package/dist/lime-elements/p-85ffcf55.entry.js.map +1 -0
  50. package/dist/lime-elements/p-8e7788a1.entry.js +2 -0
  51. package/dist/lime-elements/p-8e7788a1.entry.js.map +1 -0
  52. package/dist/lime-elements/p-ac69fa25.entry.js +2 -0
  53. package/dist/lime-elements/p-ac69fa25.entry.js.map +1 -0
  54. package/dist/lime-elements/{p-05c10bed.entry.js → p-c7d07d05.entry.js} +2 -2
  55. package/dist/lime-elements/p-c7d07d05.entry.js.map +1 -0
  56. package/dist/lime-elements/p-d39c198b.entry.js +2 -0
  57. package/dist/lime-elements/p-d39c198b.entry.js.map +1 -0
  58. package/dist/lime-elements/{p-23bc6de0.js → p-e1e25236.js} +1 -1
  59. package/dist/lime-elements/p-e1e25236.js.map +1 -0
  60. package/dist/lime-elements/style/mixins.scss +9 -36
  61. package/dist/scss/mixins.scss +9 -36
  62. package/dist/types/components/3d-hover-effect-glow/3d-hover-effect-glow.d.ts +19 -0
  63. package/dist/types/components/shortcut/shortcut.d.ts +5 -1
  64. package/dist/types/components.d.ts +57 -0
  65. package/dist/types/util/3d-tilt-hover-effect.d.ts +1 -5
  66. package/package.json +6 -6
  67. package/dist/cjs/3d-tilt-hover-effect-f64da0a8.js.map +0 -1
  68. package/dist/esm/3d-tilt-hover-effect-a76fcd43.js.map +0 -1
  69. package/dist/lime-elements/p-05c10bed.entry.js.map +0 -1
  70. package/dist/lime-elements/p-1db8aa67.entry.js +0 -2
  71. package/dist/lime-elements/p-1db8aa67.entry.js.map +0 -1
  72. package/dist/lime-elements/p-23bc6de0.js.map +0 -1
  73. package/dist/lime-elements/p-ba4098bc.entry.js +0 -2
  74. package/dist/lime-elements/p-ba4098bc.entry.js.map +0 -1
  75. package/dist/lime-elements/p-f3a613a3.entry.js +0 -2
  76. package/dist/lime-elements/p-f3a613a3.entry.js.map +0 -1
@@ -5025,7 +5025,8 @@ class ReplaceAroundStep extends Step {
5025
5025
  }
5026
5026
  map(mapping) {
5027
5027
  let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1);
5028
- let gapFrom = mapping.map(this.gapFrom, -1), gapTo = mapping.map(this.gapTo, 1);
5028
+ let gapFrom = this.from == this.gapFrom ? from.pos : mapping.map(this.gapFrom, -1);
5029
+ let gapTo = this.to == this.gapTo ? to.pos : mapping.map(this.gapTo, 1);
5029
5030
  if ((from.deletedAcross && to.deletedAcross) || gapFrom < from.pos || gapTo > to.pos)
5030
5031
  return null;
5031
5032
  return new ReplaceAroundStep(from.pos, to.pos, gapFrom, gapTo, this.slice, this.insert, this.structure);
@@ -5137,7 +5138,7 @@ function removeMark(tr, from, to, mark) {
5137
5138
  });
5138
5139
  matched.forEach(m => tr.step(new RemoveMarkStep(m.from, m.to, m.style)));
5139
5140
  }
5140
- function clearIncompatible(tr, pos, parentType, match = parentType.contentMatch) {
5141
+ function clearIncompatible(tr, pos, parentType, match = parentType.contentMatch, clearNewlines = true) {
5141
5142
  let node = tr.doc.nodeAt(pos);
5142
5143
  let replSteps = [], cur = pos + 1;
5143
5144
  for (let i = 0; i < node.childCount; i++) {
@@ -5151,7 +5152,7 @@ function clearIncompatible(tr, pos, parentType, match = parentType.contentMatch)
5151
5152
  for (let j = 0; j < child.marks.length; j++)
5152
5153
  if (!parentType.allowsMarkType(child.marks[j].type))
5153
5154
  tr.step(new RemoveMarkStep(cur, end, child.marks[j]));
5154
- if (child.isText && !parentType.spec.code) {
5155
+ if (clearNewlines && child.isText && parentType.whitespace != "pre") {
5155
5156
  let m, newline = /\r?\n|\r/g, slice;
5156
5157
  while (m = newline.exec(child.text)) {
5157
5158
  if (!slice)
@@ -5275,16 +5276,49 @@ function setBlockType$1(tr, from, to, type, attrs) {
5275
5276
  throw new RangeError("Type given to setBlockType should be a textblock");
5276
5277
  let mapFrom = tr.steps.length;
5277
5278
  tr.doc.nodesBetween(from, to, (node, pos) => {
5278
- if (node.isTextblock && !node.hasMarkup(type, attrs) && canChangeType(tr.doc, tr.mapping.slice(mapFrom).map(pos), type)) {
5279
+ let attrsHere = typeof attrs == "function" ? attrs(node) : attrs;
5280
+ if (node.isTextblock && !node.hasMarkup(type, attrsHere) &&
5281
+ canChangeType(tr.doc, tr.mapping.slice(mapFrom).map(pos), type)) {
5282
+ let convertNewlines = null;
5283
+ if (type.schema.linebreakReplacement) {
5284
+ let pre = type.whitespace == "pre", supportLinebreak = !!type.contentMatch.matchType(type.schema.linebreakReplacement);
5285
+ if (pre && !supportLinebreak)
5286
+ convertNewlines = false;
5287
+ else if (!pre && supportLinebreak)
5288
+ convertNewlines = true;
5289
+ }
5279
5290
  // Ensure all markup that isn't allowed in the new node type is cleared
5280
- tr.clearIncompatible(tr.mapping.slice(mapFrom).map(pos, 1), type);
5291
+ if (convertNewlines === false)
5292
+ replaceLinebreaks(tr, node, pos, mapFrom);
5293
+ clearIncompatible(tr, tr.mapping.slice(mapFrom).map(pos, 1), type, undefined, convertNewlines === null);
5281
5294
  let mapping = tr.mapping.slice(mapFrom);
5282
5295
  let startM = mapping.map(pos, 1), endM = mapping.map(pos + node.nodeSize, 1);
5283
- tr.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1, new Slice(Fragment.from(type.create(attrs, null, node.marks)), 0, 0), 1, true));
5296
+ tr.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1, new Slice(Fragment.from(type.create(attrsHere, null, node.marks)), 0, 0), 1, true));
5297
+ if (convertNewlines === true)
5298
+ replaceNewlines(tr, node, pos, mapFrom);
5284
5299
  return false;
5285
5300
  }
5286
5301
  });
5287
5302
  }
5303
+ function replaceNewlines(tr, node, pos, mapFrom) {
5304
+ node.forEach((child, offset) => {
5305
+ if (child.isText) {
5306
+ let m, newline = /\r?\n|\r/g;
5307
+ while (m = newline.exec(child.text)) {
5308
+ let start = tr.mapping.slice(mapFrom).map(pos + 1 + offset + m.index);
5309
+ tr.replaceWith(start, start + 1, node.type.schema.linebreakReplacement.create());
5310
+ }
5311
+ }
5312
+ });
5313
+ }
5314
+ function replaceLinebreaks(tr, node, pos, mapFrom) {
5315
+ node.forEach((child, offset) => {
5316
+ if (child.type == child.type.schema.linebreakReplacement) {
5317
+ let start = tr.mapping.slice(mapFrom).map(pos + 1 + offset);
5318
+ tr.replaceWith(start, start + 1, node.type.schema.text("\n"));
5319
+ }
5320
+ });
5321
+ }
5288
5322
  function canChangeType(doc, pos, type) {
5289
5323
  let $pos = doc.resolve(pos), index = $pos.index();
5290
5324
  return $pos.parent.canReplaceWith(index, index + 1, type);
@@ -5350,8 +5384,24 @@ function canJoin(doc, pos) {
5350
5384
  return joinable($pos.nodeBefore, $pos.nodeAfter) &&
5351
5385
  $pos.parent.canReplace(index, index + 1);
5352
5386
  }
5387
+ function canAppendWithSubstitutedLinebreaks(a, b) {
5388
+ if (!b.content.size)
5389
+ a.type.compatibleContent(b.type);
5390
+ let match = a.contentMatchAt(a.childCount);
5391
+ let { linebreakReplacement } = a.type.schema;
5392
+ for (let i = 0; i < b.childCount; i++) {
5393
+ let child = b.child(i);
5394
+ let type = child.type == linebreakReplacement ? a.type.schema.nodes.text : child.type;
5395
+ match = match.matchType(type);
5396
+ if (!match)
5397
+ return false;
5398
+ if (!a.type.allowsMarks(child.marks))
5399
+ return false;
5400
+ }
5401
+ return match.validEnd;
5402
+ }
5353
5403
  function joinable(a, b) {
5354
- return !!(a && b && !a.isLeaf && a.canAppend(b));
5404
+ return !!(a && b && !a.isLeaf && canAppendWithSubstitutedLinebreaks(a, b));
5355
5405
  }
5356
5406
  /**
5357
5407
  Find an ancestor of the given position that can be joined to the
@@ -5384,8 +5434,31 @@ function joinPoint(doc, pos, dir = -1) {
5384
5434
  }
5385
5435
  }
5386
5436
  function join(tr, pos, depth) {
5387
- let step = new ReplaceStep(pos - depth, pos + depth, Slice.empty, true);
5388
- tr.step(step);
5437
+ let convertNewlines = null;
5438
+ let { linebreakReplacement } = tr.doc.type.schema;
5439
+ let $before = tr.doc.resolve(pos - depth), beforeType = $before.node().type;
5440
+ if (linebreakReplacement && beforeType.inlineContent) {
5441
+ let pre = beforeType.whitespace == "pre";
5442
+ let supportLinebreak = !!beforeType.contentMatch.matchType(linebreakReplacement);
5443
+ if (pre && !supportLinebreak)
5444
+ convertNewlines = false;
5445
+ else if (!pre && supportLinebreak)
5446
+ convertNewlines = true;
5447
+ }
5448
+ let mapFrom = tr.steps.length;
5449
+ if (convertNewlines === false) {
5450
+ let $after = tr.doc.resolve(pos + depth);
5451
+ replaceLinebreaks(tr, $after.node(), $after.before(), mapFrom);
5452
+ }
5453
+ if (beforeType.inlineContent)
5454
+ clearIncompatible(tr, pos + depth - 1, beforeType, $before.node().contentMatchAt($before.index()), convertNewlines == null);
5455
+ let mapping = tr.mapping.slice(mapFrom), start = mapping.map(pos - depth);
5456
+ tr.step(new ReplaceStep(start, mapping.map(pos + depth, -1), Slice.empty, true));
5457
+ if (convertNewlines === true) {
5458
+ let $full = tr.doc.resolve(start);
5459
+ replaceNewlines(tr, $full.node(), $full.before(), tr.steps.length);
5460
+ }
5461
+ return tr;
5389
5462
  }
5390
5463
  /**
5391
5464
  Try to find a point where a node of the given type can be inserted
@@ -5869,7 +5942,8 @@ function deleteRange(tr, from, to) {
5869
5942
  return tr.delete($from.before(depth), $to.after(depth));
5870
5943
  }
5871
5944
  for (let d = 1; d <= $from.depth && d <= $to.depth; d++) {
5872
- if (from - $from.start(d) == $from.depth - d && to > $from.end(d) && $to.end(d) - to != $to.depth - d)
5945
+ if (from - $from.start(d) == $from.depth - d && to > $from.end(d) && $to.end(d) - to != $to.depth - d &&
5946
+ $from.start(d - 1) == $to.start(d - 1) && $from.node(d - 1).canReplace($from.index(d - 1), $to.index(d - 1)))
5873
5947
  return tr.delete($from.before(d), to);
5874
5948
  }
5875
5949
  tr.delete(from, to);
@@ -7293,6 +7367,9 @@ const textRange = function (node, from, to) {
7293
7367
  range.setStart(node, from || 0);
7294
7368
  return range;
7295
7369
  };
7370
+ const clearReusedRange = function () {
7371
+ reusedRange = null;
7372
+ };
7296
7373
  // Scans forward and backward through DOM positions equivalent to the
7297
7374
  // given one to see if the two are in the same place (i.e. after a
7298
7375
  // text node vs at the end of that text node)
@@ -7327,6 +7404,44 @@ function scanFor(node, off, targetNode, targetOff, dir) {
7327
7404
  function nodeSize(node) {
7328
7405
  return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
7329
7406
  }
7407
+ function textNodeBefore$1(node, offset) {
7408
+ for (;;) {
7409
+ if (node.nodeType == 3 && offset)
7410
+ return node;
7411
+ if (node.nodeType == 1 && offset > 0) {
7412
+ if (node.contentEditable == "false")
7413
+ return null;
7414
+ node = node.childNodes[offset - 1];
7415
+ offset = nodeSize(node);
7416
+ }
7417
+ else if (node.parentNode && !hasBlockDesc(node)) {
7418
+ offset = domIndex(node);
7419
+ node = node.parentNode;
7420
+ }
7421
+ else {
7422
+ return null;
7423
+ }
7424
+ }
7425
+ }
7426
+ function textNodeAfter$1(node, offset) {
7427
+ for (;;) {
7428
+ if (node.nodeType == 3 && offset < node.nodeValue.length)
7429
+ return node;
7430
+ if (node.nodeType == 1 && offset < node.childNodes.length) {
7431
+ if (node.contentEditable == "false")
7432
+ return null;
7433
+ node = node.childNodes[offset];
7434
+ offset = 0;
7435
+ }
7436
+ else if (node.parentNode && !hasBlockDesc(node)) {
7437
+ offset = domIndex(node) + 1;
7438
+ node = node.parentNode;
7439
+ }
7440
+ else {
7441
+ return null;
7442
+ }
7443
+ }
7444
+ }
7330
7445
  function isOnEdge(node, offset, parent) {
7331
7446
  for (let atStart = offset == 0, atEnd = offset == nodeSize(node); atStart || atEnd;) {
7332
7447
  if (node == parent)
@@ -7368,15 +7483,18 @@ function caretFromPoint(doc, x, y) {
7368
7483
  if (doc.caretPositionFromPoint) {
7369
7484
  try { // Firefox throws for this call in hard-to-predict circumstances (#994)
7370
7485
  let pos = doc.caretPositionFromPoint(x, y);
7486
+ // Clip the offset, because Chrome will return a text offset
7487
+ // into <input> nodes, which can't be treated as a regular DOM
7488
+ // offset
7371
7489
  if (pos)
7372
- return { node: pos.offsetNode, offset: pos.offset };
7490
+ return { node: pos.offsetNode, offset: Math.min(nodeSize(pos.offsetNode), pos.offset) };
7373
7491
  }
7374
7492
  catch (_) { }
7375
7493
  }
7376
7494
  if (doc.caretRangeFromPoint) {
7377
7495
  let range = doc.caretRangeFromPoint(x, y);
7378
7496
  if (range)
7379
- return { node: range.startContainer, offset: range.startOffset };
7497
+ return { node: range.startContainer, offset: Math.min(nodeSize(range.startContainer), range.startOffset) };
7380
7498
  }
7381
7499
  }
7382
7500
 
@@ -7403,6 +7521,12 @@ const webkit = !!doc && "webkitFontSmoothing" in doc.documentElement.style;
7403
7521
  const webkit_version = webkit ? +(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent) || [0, 0])[1] : 0;
7404
7522
 
7405
7523
  function windowRect(doc) {
7524
+ let vp = doc.defaultView && doc.defaultView.visualViewport;
7525
+ if (vp)
7526
+ return {
7527
+ left: 0, right: vp.width,
7528
+ top: 0, bottom: vp.height
7529
+ };
7406
7530
  return { left: 0, right: doc.documentElement.clientWidth,
7407
7531
  top: 0, bottom: doc.documentElement.clientHeight };
7408
7532
  }
@@ -7616,17 +7740,19 @@ function posFromCaret(view, node, offset, coords) {
7616
7740
  for (let cur = node, sawBlock = false;;) {
7617
7741
  if (cur == view.dom)
7618
7742
  break;
7619
- let desc = view.docView.nearestDesc(cur, true);
7743
+ let desc = view.docView.nearestDesc(cur, true), rect;
7620
7744
  if (!desc)
7621
7745
  return null;
7622
- if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent && !sawBlock || !desc.contentDOM)) {
7623
- let rect = desc.dom.getBoundingClientRect();
7624
- if (desc.node.isBlock && desc.parent && !sawBlock) {
7625
- sawBlock = true;
7626
- if (rect.left > coords.left || rect.top > coords.top)
7746
+ if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent || !desc.contentDOM) &&
7747
+ // Ignore elements with zero-size bounding rectangles
7748
+ ((rect = desc.dom.getBoundingClientRect()).width || rect.height)) {
7749
+ if (desc.node.isBlock && desc.parent) {
7750
+ // Only apply the horizontal test to the innermost block. Vertical for any parent.
7751
+ if (!sawBlock && rect.left > coords.left || rect.top > coords.top)
7627
7752
  outsideBlock = desc.posBefore;
7628
- else if (rect.right < coords.left || rect.bottom < coords.top)
7753
+ else if (!sawBlock && rect.right < coords.left || rect.bottom < coords.top)
7629
7754
  outsideBlock = desc.posAfter;
7755
+ sawBlock = true;
7630
7756
  }
7631
7757
  if (!desc.contentDOM && outsideBlock < 0 && !desc.node.isText) {
7632
7758
  // If we are inside a leaf, return the side of the leaf closer to the coords
@@ -7881,6 +8007,8 @@ function endOfTextblockHorizontal(view, state, dir) {
7881
8007
  return false;
7882
8008
  let offset = $head.parentOffset, atStart = !offset, atEnd = offset == $head.parent.content.size;
7883
8009
  let sel = view.domSelection();
8010
+ if (!sel)
8011
+ return $head.pos == $head.start() || $head.pos == $head.end();
7884
8012
  // If the textblock is all LTR, or the browser doesn't support
7885
8013
  // Selection.modify (Edge), fall back to a primitive approach
7886
8014
  if (!maybeRTL.test($head.parent.textContent) || !sel.modify)
@@ -8213,18 +8341,19 @@ class ViewDesc {
8213
8341
  // custom things with the selection. Note that this falls apart when
8214
8342
  // a selection starts in such a node and ends in another, in which
8215
8343
  // case we just use whatever domFromPos produces as a best effort.
8216
- setSelection(anchor, head, root, force = false) {
8344
+ setSelection(anchor, head, view, force = false) {
8217
8345
  // If the selection falls entirely in a child, give it to that child
8218
8346
  let from = Math.min(anchor, head), to = Math.max(anchor, head);
8219
8347
  for (let i = 0, offset = 0; i < this.children.length; i++) {
8220
8348
  let child = this.children[i], end = offset + child.size;
8221
8349
  if (from > offset && to < end)
8222
- return child.setSelection(anchor - offset - child.border, head - offset - child.border, root, force);
8350
+ return child.setSelection(anchor - offset - child.border, head - offset - child.border, view, force);
8223
8351
  offset = end;
8224
8352
  }
8225
8353
  let anchorDOM = this.domFromPos(anchor, anchor ? -1 : 1);
8226
8354
  let headDOM = head == anchor ? anchorDOM : this.domFromPos(head, head ? -1 : 1);
8227
- let domSel = root.getSelection();
8355
+ let domSel = view.root.getSelection();
8356
+ let selRange = view.domSelectionRange();
8228
8357
  let brKludge = false;
8229
8358
  // On Firefox, using Selection.collapse to put the cursor after a
8230
8359
  // BR node for some reason doesn't always work (#1073). On Safari,
@@ -8255,14 +8384,14 @@ class ViewDesc {
8255
8384
  }
8256
8385
  // Firefox can act strangely when the selection is in front of an
8257
8386
  // uneditable node. See #1163 and https://bugzilla.mozilla.org/show_bug.cgi?id=1709536
8258
- if (gecko && domSel.focusNode && domSel.focusNode != headDOM.node && domSel.focusNode.nodeType == 1) {
8259
- let after = domSel.focusNode.childNodes[domSel.focusOffset];
8387
+ if (gecko && selRange.focusNode && selRange.focusNode != headDOM.node && selRange.focusNode.nodeType == 1) {
8388
+ let after = selRange.focusNode.childNodes[selRange.focusOffset];
8260
8389
  if (after && after.contentEditable == "false")
8261
8390
  force = true;
8262
8391
  }
8263
8392
  if (!(force || brKludge && safari) &&
8264
- isEquivalentPosition(anchorDOM.node, anchorDOM.offset, domSel.anchorNode, domSel.anchorOffset) &&
8265
- isEquivalentPosition(headDOM.node, headDOM.offset, domSel.focusNode, domSel.focusOffset))
8393
+ isEquivalentPosition(anchorDOM.node, anchorDOM.offset, selRange.anchorNode, selRange.anchorOffset) &&
8394
+ isEquivalentPosition(headDOM.node, headDOM.offset, selRange.focusNode, selRange.focusOffset))
8266
8395
  return;
8267
8396
  // Selection.extend can be used to create an 'inverted' selection
8268
8397
  // (one where the focus is before the anchor), but not all
@@ -8338,6 +8467,7 @@ class ViewDesc {
8338
8467
  }
8339
8468
  get domAtom() { return false; }
8340
8469
  get ignoreForCoords() { return false; }
8470
+ isText(text) { return false; }
8341
8471
  }
8342
8472
  // A widget desc represents a widget decoration, which is a DOM node
8343
8473
  // drawn between the document nodes.
@@ -8408,16 +8538,17 @@ class CompositionViewDesc extends ViewDesc {
8408
8538
  // some cases they will be split more often than would appear
8409
8539
  // necessary.
8410
8540
  class MarkViewDesc extends ViewDesc {
8411
- constructor(parent, mark, dom, contentDOM) {
8541
+ constructor(parent, mark, dom, contentDOM, spec) {
8412
8542
  super(parent, [], dom, contentDOM);
8413
8543
  this.mark = mark;
8544
+ this.spec = spec;
8414
8545
  }
8415
8546
  static create(parent, mark, inline, view) {
8416
8547
  let custom = view.nodeViews[mark.type.name];
8417
8548
  let spec = custom && custom(mark, view, inline);
8418
8549
  if (!spec || !spec.dom)
8419
- spec = DOMSerializer.renderSpec(document, mark.type.spec.toDOM(mark, inline));
8420
- return new MarkViewDesc(parent, mark, spec.dom, spec.contentDOM || spec.dom);
8550
+ spec = DOMSerializer.renderSpec(document, mark.type.spec.toDOM(mark, inline), null, mark.attrs);
8551
+ return new MarkViewDesc(parent, mark, spec.dom, spec.contentDOM || spec.dom, spec);
8421
8552
  }
8422
8553
  parseRule() {
8423
8554
  if ((this.dirty & NODE_DIRTY) || this.mark.type.spec.reparseInView)
@@ -8449,6 +8580,14 @@ class MarkViewDesc extends ViewDesc {
8449
8580
  copy.children = nodes;
8450
8581
  return copy;
8451
8582
  }
8583
+ ignoreMutation(mutation) {
8584
+ return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation);
8585
+ }
8586
+ destroy() {
8587
+ if (this.spec.destroy)
8588
+ this.spec.destroy();
8589
+ super.destroy();
8590
+ }
8452
8591
  }
8453
8592
  // Node view descs are the main, most common type of view desc, and
8454
8593
  // correspond to an actual node in the document. Unlike mark descs,
@@ -8488,7 +8627,8 @@ class NodeViewDesc extends ViewDesc {
8488
8627
  throw new RangeError("Text must be rendered as a DOM text node");
8489
8628
  }
8490
8629
  else if (!dom) {
8491
- ({ dom, contentDOM } = DOMSerializer.renderSpec(document, node.type.spec.toDOM(node)));
8630
+ let spec = DOMSerializer.renderSpec(document, node.type.spec.toDOM(node), null, node.attrs);
8631
+ ({ dom, contentDOM } = spec);
8492
8632
  }
8493
8633
  if (!contentDOM && !node.isText && dom.nodeName != "BR") { // Chrome gets confused by <br contenteditable=false>
8494
8634
  if (!dom.hasAttribute("contenteditable"))
@@ -8600,8 +8740,7 @@ class NodeViewDesc extends ViewDesc {
8600
8740
  let { from, to } = view.state.selection;
8601
8741
  if (!(view.state.selection instanceof TextSelection) || from < pos || to > pos + this.node.content.size)
8602
8742
  return null;
8603
- let sel = view.domSelectionRange();
8604
- let textNode = nearbyTextNode(sel.focusNode, sel.focusOffset);
8743
+ let textNode = view.input.compositionNode;
8605
8744
  if (!textNode || !this.dom.contains(textNode.parentNode))
8606
8745
  return null;
8607
8746
  if (this.node.inlineContent) {
@@ -8675,10 +8814,11 @@ class NodeViewDesc extends ViewDesc {
8675
8814
  }
8676
8815
  // Remove selected node marking from this node.
8677
8816
  deselectNode() {
8678
- if (this.nodeDOM.nodeType == 1)
8817
+ if (this.nodeDOM.nodeType == 1) {
8679
8818
  this.nodeDOM.classList.remove("ProseMirror-selectednode");
8680
- if (this.contentDOM || !this.node.type.spec.draggable)
8681
- this.dom.removeAttribute("draggable");
8819
+ if (this.contentDOM || !this.node.type.spec.draggable)
8820
+ this.dom.removeAttribute("draggable");
8821
+ }
8682
8822
  }
8683
8823
  get domAtom() { return this.node.isAtom; }
8684
8824
  }
@@ -8743,6 +8883,7 @@ class TextViewDesc extends NodeViewDesc {
8743
8883
  this.dirty = NODE_DIRTY;
8744
8884
  }
8745
8885
  get domAtom() { return false; }
8886
+ isText(text) { return this.node.text == text; }
8746
8887
  }
8747
8888
  // A dummy desc used to tag trailing BR or IMG nodes created to work
8748
8889
  // around contentEditable terribleness.
@@ -8766,7 +8907,7 @@ class CustomNodeViewDesc extends NodeViewDesc {
8766
8907
  update(node, outerDeco, innerDeco, view) {
8767
8908
  if (this.dirty == NODE_DIRTY)
8768
8909
  return false;
8769
- if (this.spec.update) {
8910
+ if (this.spec.update && (this.node.type == node.type || this.spec.multiType)) {
8770
8911
  let result = this.spec.update(node, outerDeco, innerDeco);
8771
8912
  if (result)
8772
8913
  this.updateInner(node, outerDeco, innerDeco, view);
@@ -8785,9 +8926,9 @@ class CustomNodeViewDesc extends NodeViewDesc {
8785
8926
  deselectNode() {
8786
8927
  this.spec.deselectNode ? this.spec.deselectNode() : super.deselectNode();
8787
8928
  }
8788
- setSelection(anchor, head, root, force) {
8789
- this.spec.setSelection ? this.spec.setSelection(anchor, head, root)
8790
- : super.setSelection(anchor, head, root, force);
8929
+ setSelection(anchor, head, view, force) {
8930
+ this.spec.setSelection ? this.spec.setSelection(anchor, head, view.root)
8931
+ : super.setSelection(anchor, head, view, force);
8791
8932
  }
8792
8933
  destroy() {
8793
8934
  if (this.spec.destroy)
@@ -9084,6 +9225,7 @@ class ViewTreeUpdater {
9084
9225
  return true;
9085
9226
  }
9086
9227
  else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
9228
+ this.destroyBetween(this.index, i);
9087
9229
  this.top.children[this.index] = updated;
9088
9230
  if (updated.contentDOM) {
9089
9231
  updated.dirty = CONTENT_DIRTY;
@@ -9103,7 +9245,8 @@ class ViewTreeUpdater {
9103
9245
  // identical content, move over its children.
9104
9246
  recreateWrapper(next, node, outerDeco, innerDeco, view, pos) {
9105
9247
  if (next.dirty || node.isAtom || !next.children.length ||
9106
- !next.node.content.eq(node.content))
9248
+ !next.node.content.eq(node.content) ||
9249
+ !sameOuterDeco(outerDeco, next.outerDeco) || !innerDeco.eq(next.innerDeco))
9107
9250
  return null;
9108
9251
  let wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos);
9109
9252
  if (wrapper.contentDOM) {
@@ -9312,25 +9455,6 @@ function iosHacks(dom) {
9312
9455
  dom.style.cssText = oldCSS;
9313
9456
  }
9314
9457
  }
9315
- function nearbyTextNode(node, offset) {
9316
- for (;;) {
9317
- if (node.nodeType == 3)
9318
- return node;
9319
- if (node.nodeType == 1 && offset > 0) {
9320
- if (node.childNodes.length > offset && node.childNodes[offset].nodeType == 3)
9321
- return node.childNodes[offset];
9322
- node = node.childNodes[offset - 1];
9323
- offset = nodeSize(node);
9324
- }
9325
- else if (node.nodeType == 1 && offset < node.childNodes.length) {
9326
- node = node.childNodes[offset];
9327
- offset = 0;
9328
- }
9329
- else {
9330
- return null;
9331
- }
9332
- }
9333
- }
9334
9458
  // Find a piece of text in an inline fragment, overlapping from-to
9335
9459
  function findTextInFragment(frag, text, from, to) {
9336
9460
  for (let i = 0, pos = 0; i < frag.childCount && pos <= to;) {
@@ -9393,9 +9517,9 @@ function selectionFromDOM(view, origin = null) {
9393
9517
  let head = view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset, 1);
9394
9518
  if (head < 0)
9395
9519
  return null;
9396
- let $head = doc.resolve(head), $anchor, selection;
9520
+ let $head = doc.resolve(head), anchor, selection;
9397
9521
  if (selectionCollapsed(domSel)) {
9398
- $anchor = $head;
9522
+ anchor = head;
9399
9523
  while (nearestDesc && !nearestDesc.node)
9400
9524
  nearestDesc = nearestDesc.parent;
9401
9525
  let nearestDescNode = nearestDesc.node;
@@ -9406,11 +9530,25 @@ function selectionFromDOM(view, origin = null) {
9406
9530
  }
9407
9531
  }
9408
9532
  else {
9409
- let anchor = view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset, 1);
9533
+ if (domSel instanceof view.dom.ownerDocument.defaultView.Selection && domSel.rangeCount > 1) {
9534
+ let min = head, max = head;
9535
+ for (let i = 0; i < domSel.rangeCount; i++) {
9536
+ let range = domSel.getRangeAt(i);
9537
+ min = Math.min(min, view.docView.posFromDOM(range.startContainer, range.startOffset, 1));
9538
+ max = Math.max(max, view.docView.posFromDOM(range.endContainer, range.endOffset, -1));
9539
+ }
9540
+ if (min < 0)
9541
+ return null;
9542
+ [anchor, head] = max == view.state.selection.anchor ? [max, min] : [min, max];
9543
+ $head = doc.resolve(head);
9544
+ }
9545
+ else {
9546
+ anchor = view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset, 1);
9547
+ }
9410
9548
  if (anchor < 0)
9411
9549
  return null;
9412
- $anchor = doc.resolve(anchor);
9413
9550
  }
9551
+ let $anchor = doc.resolve(anchor);
9414
9552
  if (!selection) {
9415
9553
  let bias = origin == "pointer" || (view.state.selection.head < $head.pos && !inWidget) ? 1 : -1;
9416
9554
  selection = selectionBetween(view, $anchor, $head, bias);
@@ -9450,7 +9588,7 @@ function selectionToDOM(view, force = false) {
9450
9588
  if (!sel.empty && !sel.$from.parent.inlineContent)
9451
9589
  resetEditableTo = temporarilyEditableNear(view, sel.to);
9452
9590
  }
9453
- view.docView.setSelection(anchor, head, view.root, force);
9591
+ view.docView.setSelection(anchor, head, view, force);
9454
9592
  if (brokenSelectBetweenUneditable) {
9455
9593
  if (resetEditableFrom)
9456
9594
  resetEditable(resetEditableFrom);
@@ -9519,12 +9657,14 @@ function removeClassOnSelectionChange(view) {
9519
9657
  }
9520
9658
  function selectCursorWrapper(view) {
9521
9659
  let domSel = view.domSelection(), range = document.createRange();
9660
+ if (!domSel)
9661
+ return;
9522
9662
  let node = view.cursorWrapper.dom, img = node.nodeName == "IMG";
9523
9663
  if (img)
9524
- range.setEnd(node.parentNode, domIndex(node) + 1);
9664
+ range.setStart(node.parentNode, domIndex(node) + 1);
9525
9665
  else
9526
- range.setEnd(node, 0);
9527
- range.collapse(false);
9666
+ range.setStart(node, 0);
9667
+ range.collapse(true);
9528
9668
  domSel.removeAllRanges();
9529
9669
  domSel.addRange(range);
9530
9670
  // Kludge to kill 'control selection' in IE11 when selecting an
@@ -9812,6 +9952,8 @@ function setSelFocus(view, node, offset) {
9812
9952
  }
9813
9953
  }
9814
9954
  let sel = view.domSelection();
9955
+ if (!sel)
9956
+ return;
9815
9957
  if (selectionCollapsed(sel)) {
9816
9958
  let range = document.createRange();
9817
9959
  range.setEnd(node, offset);
@@ -9994,7 +10136,7 @@ function serializeForClipboard(view, slice) {
9994
10136
  firstChild.setAttribute("data-pm-slice", `${openStart} ${openEnd}${wrappers ? ` -${wrappers}` : ""} ${JSON.stringify(context)}`);
9995
10137
  let text = view.someProp("clipboardTextSerializer", f => f(slice, view)) ||
9996
10138
  slice.content.textBetween(0, slice.content.size, "\n\n");
9997
- return { dom: wrap, text };
10139
+ return { dom: wrap, text, slice };
9998
10140
  }
9999
10141
  // Read a slice of content from the clipboard (or drop data).
10000
10142
  function parseFromClipboard(view, text, html, plainText, $context) {
@@ -10166,6 +10308,18 @@ let _detachedDoc = null;
10166
10308
  function detachedDoc() {
10167
10309
  return _detachedDoc || (_detachedDoc = document.implementation.createHTMLDocument("title"));
10168
10310
  }
10311
+ let _policy = null;
10312
+ function maybeWrapTrusted(html) {
10313
+ let trustedTypes = window.trustedTypes;
10314
+ if (!trustedTypes)
10315
+ return html;
10316
+ // With the require-trusted-types-for CSP, Chrome will block
10317
+ // innerHTML, even on a detached document. This wraps the string in
10318
+ // a way that makes the browser allow us to use its parser again.
10319
+ if (!_policy)
10320
+ _policy = trustedTypes.createPolicy("ProseMirrorClipboard", { createHTML: (s) => s });
10321
+ return _policy.createHTML(html);
10322
+ }
10169
10323
  function readHTML(html) {
10170
10324
  let metas = /^(\s*<meta [^>]*>)*/.exec(html);
10171
10325
  if (metas)
@@ -10174,7 +10328,7 @@ function readHTML(html) {
10174
10328
  let firstTag = /<([a-z][^>\s]+)/i.exec(html), wrap;
10175
10329
  if (wrap = firstTag && wrapMap[firstTag[1].toLowerCase()])
10176
10330
  html = wrap.map(n => "<" + n + ">").join("") + html + wrap.map(n => "</" + n + ">").reverse().join("");
10177
- elt.innerHTML = html;
10331
+ elt.innerHTML = maybeWrapTrusted(html);
10178
10332
  if (wrap)
10179
10333
  for (let i = 0; i < wrap.length; i++)
10180
10334
  elt = elt.querySelector(wrap[i]) || elt;
@@ -10233,8 +10387,9 @@ class InputState {
10233
10387
  this.lastIOSEnterFallbackTimeout = -1;
10234
10388
  this.lastFocus = 0;
10235
10389
  this.lastTouch = 0;
10236
- this.lastAndroidDelete = 0;
10390
+ this.lastChromeDelete = 0;
10237
10391
  this.composing = false;
10392
+ this.compositionNode = null;
10238
10393
  this.composingTimeout = -1;
10239
10394
  this.compositionNodes = [];
10240
10395
  this.compositionEndedAt = -2e8;
@@ -10377,6 +10532,8 @@ function runHandlerOnContext(view, propName, pos, inside, event) {
10377
10532
  function updateSelection(view, selection, origin) {
10378
10533
  if (!view.focused)
10379
10534
  view.focus();
10535
+ if (view.state.selection.eq(selection))
10536
+ return;
10380
10537
  let tr = view.state.tr.setSelection(selection);
10381
10538
  if (origin == "pointer")
10382
10539
  tr.setMeta("pointer", true);
@@ -10510,7 +10667,7 @@ class MouseDown {
10510
10667
  }
10511
10668
  const target = flushed ? null : event.target;
10512
10669
  const targetDesc = target ? view.docView.nearestDesc(target, true) : null;
10513
- this.target = targetDesc ? targetDesc.dom : null;
10670
+ this.target = targetDesc && targetDesc.dom.nodeType == 1 ? targetDesc.dom : null;
10514
10671
  let { selection } = view.state;
10515
10672
  if (event.button == 0 &&
10516
10673
  targetNode.type.spec.draggable && targetNode.type.spec.selectable !== false ||
@@ -10631,8 +10788,8 @@ const timeoutComposition = android ? 5000 : -1;
10631
10788
  editHandlers.compositionstart = editHandlers.compositionupdate = view => {
10632
10789
  if (!view.composing) {
10633
10790
  view.domObserver.flush();
10634
- let { state } = view, $pos = state.selection.$from;
10635
- if (state.selection.empty &&
10791
+ let { state } = view, $pos = state.selection.$to;
10792
+ if (state.selection instanceof TextSelection &&
10636
10793
  (state.storedMarks ||
10637
10794
  (!$pos.textOffset && $pos.parentOffset && $pos.nodeBefore.marks.some(m => m.type.spec.inclusive === false)))) {
10638
10795
  // Need to wrap the cursor in mark nodes different from the ones in the DOM context
@@ -10641,7 +10798,7 @@ editHandlers.compositionstart = editHandlers.compositionupdate = view => {
10641
10798
  view.markCursor = null;
10642
10799
  }
10643
10800
  else {
10644
- endComposition(view);
10801
+ endComposition(view, !state.selection.empty);
10645
10802
  // In firefox, if the cursor is after but outside a marked node,
10646
10803
  // the inserted text won't inherit the marks. So this moves it
10647
10804
  // inside if necessary.
@@ -10652,7 +10809,9 @@ editHandlers.compositionstart = editHandlers.compositionupdate = view => {
10652
10809
  if (!before)
10653
10810
  break;
10654
10811
  if (before.nodeType == 3) {
10655
- view.domSelection().collapse(before, before.nodeValue.length);
10812
+ let sel = view.domSelection();
10813
+ if (sel)
10814
+ sel.collapse(before, before.nodeValue.length);
10656
10815
  break;
10657
10816
  }
10658
10817
  else {
@@ -10671,6 +10830,7 @@ editHandlers.compositionend = (view, event) => {
10671
10830
  view.input.composing = false;
10672
10831
  view.input.compositionEndedAt = event.timeStamp;
10673
10832
  view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0;
10833
+ view.input.compositionNode = null;
10674
10834
  if (view.input.compositionPendingChanges)
10675
10835
  Promise.resolve().then(() => view.domObserver.flush());
10676
10836
  view.input.compositionID++;
@@ -10690,6 +10850,27 @@ function clearComposition(view) {
10690
10850
  while (view.input.compositionNodes.length > 0)
10691
10851
  view.input.compositionNodes.pop().markParentsDirty();
10692
10852
  }
10853
+ function findCompositionNode(view) {
10854
+ let sel = view.domSelectionRange();
10855
+ if (!sel.focusNode)
10856
+ return null;
10857
+ let textBefore = textNodeBefore$1(sel.focusNode, sel.focusOffset);
10858
+ let textAfter = textNodeAfter$1(sel.focusNode, sel.focusOffset);
10859
+ if (textBefore && textAfter && textBefore != textAfter) {
10860
+ let descAfter = textAfter.pmViewDesc, lastChanged = view.domObserver.lastChangedTextNode;
10861
+ if (textBefore == lastChanged || textAfter == lastChanged)
10862
+ return lastChanged;
10863
+ if (!descAfter || !descAfter.isText(textAfter.nodeValue)) {
10864
+ return textAfter;
10865
+ }
10866
+ else if (view.input.compositionNode == textAfter) {
10867
+ let descBefore = textBefore.pmViewDesc;
10868
+ if (!(!descBefore || !descBefore.isText(textBefore.nodeValue)))
10869
+ return textAfter;
10870
+ }
10871
+ }
10872
+ return textBefore || textAfter;
10873
+ }
10693
10874
  function timestampFromCustomEvent() {
10694
10875
  let event = document.createEvent("Event");
10695
10876
  event.initEvent("event", true, true);
@@ -10698,15 +10879,17 @@ function timestampFromCustomEvent() {
10698
10879
  /**
10699
10880
  @internal
10700
10881
  */
10701
- function endComposition(view, forceUpdate = false) {
10882
+ function endComposition(view, restarting = false) {
10702
10883
  if (android && view.domObserver.flushingSoon >= 0)
10703
10884
  return;
10704
10885
  view.domObserver.forceFlush();
10705
10886
  clearComposition(view);
10706
- if (forceUpdate || view.docView && view.docView.dirty) {
10887
+ if (restarting || view.docView && view.docView.dirty) {
10707
10888
  let sel = selectionFromDOM(view);
10708
10889
  if (sel && !sel.eq(view.state.selection))
10709
10890
  view.dispatch(view.state.tr.setSelection(sel));
10891
+ else if ((view.markCursor || restarting) && !view.state.selection.empty)
10892
+ view.dispatch(view.state.tr.deleteSelection());
10710
10893
  else
10711
10894
  view.updateState(view.state);
10712
10895
  return true;
@@ -10845,8 +11028,11 @@ handlers.dragstart = (view, _event) => {
10845
11028
  if (desc && desc.node.type.spec.draggable && desc != view.docView)
10846
11029
  node = NodeSelection.create(view.state.doc, desc.posBefore);
10847
11030
  }
10848
- let slice = (node || view.state.selection).content(), { dom, text } = serializeForClipboard(view, slice);
10849
- event.dataTransfer.clearData();
11031
+ let draggedSlice = (node || view.state.selection).content();
11032
+ let { dom, text, slice } = serializeForClipboard(view, draggedSlice);
11033
+ // Pre-120 Chrome versions clear files when calling `clearData` (#1472)
11034
+ if (!event.dataTransfer.files.length || !chrome || chrome_version > 120)
11035
+ event.dataTransfer.clearData();
10850
11036
  event.dataTransfer.setData(brokenClipboardAPI ? "Text" : "text/html", dom.innerHTML);
10851
11037
  // See https://github.com/ProseMirror/prosemirror/issues/1156
10852
11038
  event.dataTransfer.effectAllowed = "copyMove";
@@ -11360,6 +11546,7 @@ class DecorationSet {
11360
11546
  }
11361
11547
  return result;
11362
11548
  }
11549
+ forEachSet(f) { f(this); }
11363
11550
  }
11364
11551
  /**
11365
11552
  The empty set of decorations.
@@ -11435,6 +11622,10 @@ class DecorationGroup {
11435
11622
  members.reduce((r, m) => r.concat(m instanceof DecorationSet ? m : m.members), []));
11436
11623
  }
11437
11624
  }
11625
+ forEachSet(f) {
11626
+ for (let i = 0; i < this.members.length; i++)
11627
+ this.members[i].forEachSet(f);
11628
+ }
11438
11629
  }
11439
11630
  function mapChildren(oldChildren, newLocal, mapping, node, offset, oldOffset, options) {
11440
11631
  let children = oldChildren.slice();
@@ -11689,6 +11880,7 @@ class DOMObserver {
11689
11880
  this.currentSelection = new SelectionState;
11690
11881
  this.onCharData = null;
11691
11882
  this.suppressingSelectionUpdates = false;
11883
+ this.lastChangedTextNode = null;
11692
11884
  this.observer = window.MutationObserver &&
11693
11885
  new window.MutationObserver(mutations => {
11694
11886
  for (let i = 0; i < mutations.length; i++)
@@ -11821,15 +12013,23 @@ class DOMObserver {
11821
12013
  }
11822
12014
  }
11823
12015
  }
11824
- if (gecko && added.length > 1) {
12016
+ if (gecko && added.length) {
11825
12017
  let brs = added.filter(n => n.nodeName == "BR");
11826
12018
  if (brs.length == 2) {
11827
- let a = brs[0], b = brs[1];
12019
+ let [a, b] = brs;
11828
12020
  if (a.parentNode && a.parentNode.parentNode == b.parentNode)
11829
12021
  b.remove();
11830
12022
  else
11831
12023
  a.remove();
11832
12024
  }
12025
+ else {
12026
+ let { focusNode } = this.currentSelection;
12027
+ for (let br of brs) {
12028
+ let parent = br.parentNode;
12029
+ if (parent && parent.nodeName == "LI" && (!focusNode || blockParent(view, focusNode) != parent))
12030
+ br.remove();
12031
+ }
12032
+ }
11833
12033
  }
11834
12034
  let readSel = null;
11835
12035
  // If it looks like the browser has reset the selection to the
@@ -11870,8 +12070,12 @@ class DOMObserver {
11870
12070
  if (!desc || desc.ignoreMutation(mut))
11871
12071
  return null;
11872
12072
  if (mut.type == "childList") {
11873
- for (let i = 0; i < mut.addedNodes.length; i++)
11874
- added.push(mut.addedNodes[i]);
12073
+ for (let i = 0; i < mut.addedNodes.length; i++) {
12074
+ let node = mut.addedNodes[i];
12075
+ added.push(node);
12076
+ if (node.nodeType == 3)
12077
+ this.lastChangedTextNode = node;
12078
+ }
11875
12079
  if (desc.contentDOM && desc.contentDOM != desc.dom && !desc.contentDOM.contains(mut.target))
11876
12080
  return { from: desc.posBefore, to: desc.posAfter };
11877
12081
  let prev = mut.previousSibling, next = mut.nextSibling;
@@ -11898,6 +12102,7 @@ class DOMObserver {
11898
12102
  return { from: desc.posAtStart - desc.border, to: desc.posAtEnd + desc.border };
11899
12103
  }
11900
12104
  else { // "characterData"
12105
+ this.lastChangedTextNode = mut.target;
11901
12106
  return {
11902
12107
  from: desc.posAtStart,
11903
12108
  to: desc.posAtEnd,
@@ -11924,9 +12129,25 @@ function checkCSS(view) {
11924
12129
  cssCheckWarned = true;
11925
12130
  }
11926
12131
  }
12132
+ function rangeToSelectionRange(view, range) {
12133
+ let anchorNode = range.startContainer, anchorOffset = range.startOffset;
12134
+ let focusNode = range.endContainer, focusOffset = range.endOffset;
12135
+ let currentAnchor = view.domAtPos(view.state.selection.anchor);
12136
+ // Since such a range doesn't distinguish between anchor and head,
12137
+ // use a heuristic that flips it around if its end matches the
12138
+ // current anchor.
12139
+ if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
12140
+ [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
12141
+ return { anchorNode, anchorOffset, focusNode, focusOffset };
12142
+ }
11927
12143
  // Used to work around a Safari Selection/shadow DOM bug
11928
12144
  // Based on https://github.com/codemirror/dev/issues/414 fix
11929
- function safariShadowSelectionRange(view) {
12145
+ function safariShadowSelectionRange(view, selection) {
12146
+ if (selection.getComposedRanges) {
12147
+ let range = selection.getComposedRanges(view.root)[0];
12148
+ if (range)
12149
+ return rangeToSelectionRange(view, range);
12150
+ }
11930
12151
  let found;
11931
12152
  function read(event) {
11932
12153
  event.preventDefault();
@@ -11941,15 +12162,15 @@ function safariShadowSelectionRange(view) {
11941
12162
  view.dom.addEventListener("beforeinput", read, true);
11942
12163
  document.execCommand("indent");
11943
12164
  view.dom.removeEventListener("beforeinput", read, true);
11944
- let anchorNode = found.startContainer, anchorOffset = found.startOffset;
11945
- let focusNode = found.endContainer, focusOffset = found.endOffset;
11946
- let currentAnchor = view.domAtPos(view.state.selection.anchor);
11947
- // Since such a range doesn't distinguish between anchor and head,
11948
- // use a heuristic that flips it around if its end matches the
11949
- // current anchor.
11950
- if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
11951
- [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
11952
- return { anchorNode, anchorOffset, focusNode, focusOffset };
12165
+ return found ? rangeToSelectionRange(view, found) : null;
12166
+ }
12167
+ function blockParent(view, node) {
12168
+ for (let p = node.parentNode; p && p != view.dom; p = p.parentNode) {
12169
+ let desc = view.docView.nearestDesc(p, true);
12170
+ if (desc && desc.node.isBlock)
12171
+ return p;
12172
+ }
12173
+ return null;
11953
12174
  }
11954
12175
 
11955
12176
  // Note that all referencing and parsing is done with the
@@ -12067,6 +12288,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
12067
12288
  }
12068
12289
  view.input.lastKeyCode = null;
12069
12290
  let change = findDiff(compare.content, parse.doc.content, parse.from, preferredPos, preferredSide);
12291
+ if (change)
12292
+ view.input.domChangeCount++;
12070
12293
  if ((ios && view.input.lastIOSEnter > Date.now() - 225 || android) &&
12071
12294
  addedNodes.some(n => n.nodeType == 1 && !isInline.test(n.nodeName)) &&
12072
12295
  (!change || change.endA >= change.endB) &&
@@ -12092,14 +12315,6 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
12092
12315
  return;
12093
12316
  }
12094
12317
  }
12095
- // Chrome sometimes leaves the cursor before the inserted text when
12096
- // composing after a cursor wrapper. This moves it forward.
12097
- if (chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from &&
12098
- parse.sel.head == parse.sel.anchor) {
12099
- let size = change.endB - change.start;
12100
- parse.sel = { anchor: parse.sel.anchor + size, head: parse.sel.anchor + size };
12101
- }
12102
- view.input.domChangeCount++;
12103
12318
  // Handle the case where overwriting a selection by typing matches
12104
12319
  // the start or end of the selected content, creating a change
12105
12320
  // that's smaller than what was actually overwritten.
@@ -12144,17 +12359,17 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
12144
12359
  }
12145
12360
  // Same for backspace
12146
12361
  if (view.state.selection.anchor > change.start &&
12147
- looksLikeJoin(doc, change.start, change.endA, $from, $to) &&
12362
+ looksLikeBackspace(doc, change.start, change.endA, $from, $to) &&
12148
12363
  view.someProp("handleKeyDown", f => f(view, keyEvent(8, "Backspace")))) {
12149
12364
  if (android && chrome)
12150
12365
  view.domObserver.suppressSelectionUpdates(); // #820
12151
12366
  return;
12152
12367
  }
12153
- // Chrome Android will occasionally, during composition, delete the
12368
+ // Chrome will occasionally, during composition, delete the
12154
12369
  // entire composition and then immediately insert it again. This is
12155
12370
  // used to detect that situation.
12156
- if (chrome && android && change.endB == change.start)
12157
- view.input.lastAndroidDelete = Date.now();
12371
+ if (chrome && change.endB == change.start)
12372
+ view.input.lastChromeDelete = Date.now();
12158
12373
  // This tries to detect Android virtual keyboard
12159
12374
  // enter-and-pick-suggestion action. That sometimes (see issue
12160
12375
  // #1059) first fires a DOM mutation, before moving the selection to
@@ -12205,13 +12420,13 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
12205
12420
  tr = view.state.tr.replace(chFrom, chTo, parse.doc.slice(change.start - parse.from, change.endB - parse.from));
12206
12421
  if (parse.sel) {
12207
12422
  let sel = resolveSelection(view, tr.doc, parse.sel);
12208
- // Chrome Android will sometimes, during composition, report the
12423
+ // Chrome will sometimes, during composition, report the
12209
12424
  // selection in the wrong place. If it looks like that is
12210
12425
  // happening, don't update the selection.
12211
12426
  // Edge just doesn't move the cursor forward when you start typing
12212
12427
  // in an empty block or between br nodes.
12213
- if (sel && !(chrome && android && view.composing && sel.empty &&
12214
- (change.start != change.endB || view.input.lastAndroidDelete < Date.now() - 100) &&
12428
+ if (sel && !(chrome && view.composing && sel.empty &&
12429
+ (change.start != change.endB || view.input.lastChromeDelete < Date.now() - 100) &&
12215
12430
  (sel.head == chFrom || sel.head == tr.mapping.map(chTo) - 1) ||
12216
12431
  ie$1 && sel.empty && sel.head == chFrom))
12217
12432
  tr.setSelection(sel);
@@ -12256,14 +12471,18 @@ function isMarkChange(cur, prev) {
12256
12471
  if (Fragment.from(updated).eq(cur))
12257
12472
  return { mark, type };
12258
12473
  }
12259
- function looksLikeJoin(old, start, end, $newStart, $newEnd) {
12260
- if (!$newStart.parent.isTextblock ||
12261
- // The content must have shrunk
12262
- end - start <= $newEnd.pos - $newStart.pos ||
12474
+ function looksLikeBackspace(old, start, end, $newStart, $newEnd) {
12475
+ if ( // The content must have shrunk
12476
+ end - start <= $newEnd.pos - $newStart.pos ||
12263
12477
  // newEnd must point directly at or after the end of the block that newStart points into
12264
12478
  skipClosingAndOpening($newStart, true, false) < $newEnd.pos)
12265
12479
  return false;
12266
12480
  let $start = old.resolve(start);
12481
+ // Handle the case where, rather than joining blocks, the change just removed an entire block
12482
+ if (!$newStart.parent.isTextblock) {
12483
+ let after = $start.nodeAfter;
12484
+ return after != null && end == start + after.nodeSize;
12485
+ }
12267
12486
  // Start must be at the end of a block
12268
12487
  if ($start.parentOffset < $start.parent.content.size || !$start.parent.isTextblock)
12269
12488
  return false;
@@ -12501,8 +12720,10 @@ class EditorView {
12501
12720
  // tracks that and forces a selection reset when our update
12502
12721
  // did write to the node.
12503
12722
  let chromeKludge = chrome ? (this.trackWrites = this.domSelectionRange().focusNode) : null;
12723
+ if (this.composing)
12724
+ this.input.compositionNode = findCompositionNode(this);
12504
12725
  if (redraw || !this.docView.update(state.doc, outerDeco, innerDeco, this)) {
12505
- this.docView.updateOuterDeco([]);
12726
+ this.docView.updateOuterDeco(outerDeco);
12506
12727
  this.docView.destroy();
12507
12728
  this.docView = docViewDesc(state.doc, outerDeco, innerDeco, this.dom, this);
12508
12729
  }
@@ -12779,6 +13000,7 @@ class EditorView {
12779
13000
  }
12780
13001
  this.docView.destroy();
12781
13002
  this.docView = null;
13003
+ clearReusedRange();
12782
13004
  }
12783
13005
  /**
12784
13006
  This is true when the view has been
@@ -12814,8 +13036,11 @@ class EditorView {
12814
13036
  @internal
12815
13037
  */
12816
13038
  domSelectionRange() {
12817
- return safari && this.root.nodeType === 11 && deepActiveElement(this.dom.ownerDocument) == this.dom
12818
- ? safariShadowSelectionRange(this) : this.domSelection();
13039
+ let sel = this.domSelection();
13040
+ if (!sel)
13041
+ return { focusNode: null, focusOffset: 0, anchorNode: null, anchorOffset: 0 };
13042
+ return safari && this.root.nodeType === 11 &&
13043
+ deepActiveElement(this.dom.ownerDocument) == this.dom && safariShadowSelectionRange(this, sel) || sel;
12819
13044
  }
12820
13045
  /**
12821
13046
  @internal
@@ -12851,7 +13076,7 @@ function updateCursorWrapper(view) {
12851
13076
  dom.className = "ProseMirror-separator";
12852
13077
  dom.setAttribute("mark-placeholder", "true");
12853
13078
  dom.setAttribute("alt", "");
12854
- view.cursorWrapper = { dom, deco: Decoration.widget(view.state.selection.head, dom, { raw: true, marks: view.markCursor }) };
13079
+ view.cursorWrapper = { dom, deco: Decoration.widget(view.state.selection.from, dom, { raw: true, marks: view.markCursor }) };
12855
13080
  }
12856
13081
  else {
12857
13082
  view.cursorWrapper = null;