tinymce-rails 8.5.1 → 8.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/source/tinymce/tinymce.js +613 -204
  3. data/lib/tinymce/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/tinymce/icons/default/icons.js +1 -1
  5. data/vendor/assets/javascripts/tinymce/plugins/autolink/plugin.js +1 -1
  6. data/vendor/assets/javascripts/tinymce/plugins/autosave/plugin.js +1 -1
  7. data/vendor/assets/javascripts/tinymce/plugins/codesample/plugin.js +2 -2
  8. data/vendor/assets/javascripts/tinymce/plugins/emoticons/plugin.js +1 -1
  9. data/vendor/assets/javascripts/tinymce/plugins/help/plugin.js +1 -1
  10. data/vendor/assets/javascripts/tinymce/plugins/image/plugin.js +1 -1
  11. data/vendor/assets/javascripts/tinymce/plugins/preview/plugin.js +1 -1
  12. data/vendor/assets/javascripts/tinymce/plugins/table/plugin.js +1 -1
  13. data/vendor/assets/javascripts/tinymce/skins/content/dark/content.css +1 -1
  14. data/vendor/assets/javascripts/tinymce/skins/content/dark/content.js +1 -1
  15. data/vendor/assets/javascripts/tinymce/skins/content/dark/content.min.css +1 -1
  16. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.css +1 -1
  17. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.css +1 -1
  18. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.js +1 -1
  19. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.min.css +1 -1
  20. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.js +1 -1
  21. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.min.css +1 -1
  22. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.css +1 -1
  23. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.js +1 -1
  24. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.min.css +1 -1
  25. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.css +1 -1
  26. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.css +1 -1
  27. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.js +1 -1
  28. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.min.css +1 -1
  29. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.js +1 -1
  30. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.min.css +1 -1
  31. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.css +1 -1
  32. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.js +1 -1
  33. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.min.css +1 -1
  34. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.css +1 -1
  35. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.css +1 -1
  36. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.js +1 -1
  37. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.min.css +1 -1
  38. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.js +1 -1
  39. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.min.css +1 -1
  40. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.css +1 -1
  41. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.js +1 -1
  42. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.min.css +1 -1
  43. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.css +1 -1
  44. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.css +1 -1
  45. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.js +1 -1
  46. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.min.css +1 -1
  47. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.js +1 -1
  48. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.min.css +1 -1
  49. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.css +1 -1
  50. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.js +1 -1
  51. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.min.css +1 -1
  52. data/vendor/assets/javascripts/tinymce/themes/silver/theme.js +3 -1
  53. data/vendor/assets/javascripts/tinymce/tinymce.d.ts +11 -1
  54. data/vendor/assets/javascripts/tinymce/tinymce.js +4 -2
  55. metadata +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TinyMCE version 8.5.1 (2026-05-19)
2
+ * TinyMCE version 8.7.0 (2026-07-01)
3
3
  */
4
4
 
5
5
  (function () {
@@ -573,14 +573,16 @@
573
573
  }
574
574
  return Optional.none();
575
575
  };
576
- const findLastIndex = (arr, pred) => {
576
+ const findLastByPredicate = (arr, pred) => {
577
577
  for (let i = arr.length - 1; i >= 0; i--) {
578
578
  if (pred(arr[i], i)) {
579
- return Optional.some(i);
579
+ return Optional.some({ v: arr[i], i });
580
580
  }
581
581
  }
582
582
  return Optional.none();
583
583
  };
584
+ const findLast = (arr, pred) => findLastByPredicate(arr, pred).map((r) => r.v);
585
+ const findLastIndex = (arr, pred) => findLastByPredicate(arr, pred).map((r) => r.i);
584
586
  const flatten$1 = (xs) => {
585
587
  // Note, this is possible because push supports multiple arguments:
586
588
  // http://jsperf.com/concat-push/6
@@ -2198,14 +2200,14 @@
2198
2200
  const siblings = (element) => {
2199
2201
  // TODO: Refactor out children so we can just not add self instead of filtering afterwards
2200
2202
  const filterSelf = (elements) => filter$5(elements, (x) => !eq(element, x));
2201
- return parent(element).map(children$1).map(filterSelf).getOr([]);
2203
+ return parent(element).map(children$2).map(filterSelf).getOr([]);
2202
2204
  };
2203
2205
  const prevSibling = (element) => Optional.from(element.dom.previousSibling).map(SugarElement.fromDom);
2204
2206
  const nextSibling = (element) => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);
2205
2207
  // This one needs to be reversed, so they're still in DOM order
2206
2208
  const prevSiblings = (element) => reverse(toArray(element, prevSibling));
2207
2209
  const nextSiblings = (element) => toArray(element, nextSibling);
2208
- const children$1 = (element) => map$3(element.dom.childNodes, SugarElement.fromDom);
2210
+ const children$2 = (element) => map$3(element.dom.childNodes, SugarElement.fromDom);
2209
2211
  const child$1 = (element, index) => {
2210
2212
  const cs = element.dom.childNodes;
2211
2213
  return Optional.from(cs[index]).map(SugarElement.fromDom);
@@ -2442,7 +2444,7 @@
2442
2444
  // than removing every child node manually.
2443
2445
  // The following is (probably) safe for performance as 99.9% of the time the trick works and
2444
2446
  // Traverse.children will return an empty array.
2445
- each$e(children$1(element), (rogue) => {
2447
+ each$e(children$2(element), (rogue) => {
2446
2448
  remove$8(rogue);
2447
2449
  });
2448
2450
  };
@@ -2453,7 +2455,7 @@
2453
2455
  }
2454
2456
  };
2455
2457
  const unwrap = (wrapper) => {
2456
- const children = children$1(wrapper);
2458
+ const children = children$2(wrapper);
2457
2459
  if (children.length > 0) {
2458
2460
  after$3(wrapper, children);
2459
2461
  }
@@ -2476,7 +2478,7 @@
2476
2478
  const mutate = (original, tag) => {
2477
2479
  const nu = shallowAs(original, tag);
2478
2480
  after$4(original, nu);
2479
- const children = children$1(original);
2481
+ const children = children$2(original);
2480
2482
  append(nu, children);
2481
2483
  remove$8(original);
2482
2484
  return nu;
@@ -2486,7 +2488,7 @@
2486
2488
  const doc = scope || document;
2487
2489
  const div = doc.createElement('div');
2488
2490
  div.innerHTML = html;
2489
- return children$1(SugarElement.fromDom(div));
2491
+ return children$2(SugarElement.fromDom(div));
2490
2492
  };
2491
2493
  const fromDom$1 = (nodes) => map$3(nodes, SugarElement.fromDom);
2492
2494
 
@@ -2526,6 +2528,14 @@
2526
2528
  const doc = dom.ownerDocument;
2527
2529
  return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));
2528
2530
  };
2531
+ const body = () => getBody(SugarElement.fromDom(document));
2532
+ const getBody = (doc) => {
2533
+ const b = doc.dom.body;
2534
+ if (b === null || b === undefined) {
2535
+ throw new Error('Body is not available yet');
2536
+ }
2537
+ return SugarElement.fromDom(b);
2538
+ };
2529
2539
 
2530
2540
  const internalSet = (dom, property, value) => {
2531
2541
  // This is going to hurt. Apologies.
@@ -2999,11 +3009,11 @@
2999
3009
  };
3000
3010
 
3001
3011
  const ancestors$1 = (scope, predicate, isRoot) => filter$5(parents$1(scope, isRoot), predicate);
3002
- const children = (scope, predicate) => filter$5(children$1(scope), predicate);
3012
+ const children$1 = (scope, predicate) => filter$5(children$2(scope), predicate);
3003
3013
  const descendants$1 = (scope, predicate) => {
3004
3014
  let result = [];
3005
3015
  // Recurse.toArray() might help here
3006
- each$e(children$1(scope), (x) => {
3016
+ each$e(children$2(scope), (x) => {
3007
3017
  if (predicate(x)) {
3008
3018
  result = result.concat([x]);
3009
3019
  }
@@ -3021,6 +3031,10 @@
3021
3031
  // It may surprise you to learn this is exactly what JQuery does
3022
3032
  // TODO: Avoid all this wrapping and unwrapping
3023
3033
  ancestors$1(scope, (e) => is$2(e, selector), isRoot);
3034
+ const children = (scope, selector) =>
3035
+ // It may surprise you to learn this is exactly what JQuery does
3036
+ // TODO: Avoid all the wrapping and unwrapping
3037
+ children$1(scope, (e) => is$2(e, selector));
3024
3038
  const descendants = (scope, selector) => all(selector, scope);
3025
3039
 
3026
3040
  const ancestor$3 = (scope, predicate, isRoot) => ancestor$5(scope, predicate, isRoot).isSome();
@@ -3205,6 +3219,20 @@
3205
3219
  };
3206
3220
  const getAtPoint = (win, x, y) => fromPoint$1(win, x, y);
3207
3221
 
3222
+ const getImageSize = (url) => new Promise((resolve, reject) => {
3223
+ const img = document.createElement('img');
3224
+ img.addEventListener('load', () => {
3225
+ resolve({
3226
+ width: img.naturalWidth,
3227
+ height: img.naturalHeight
3228
+ });
3229
+ });
3230
+ img.addEventListener('error', () => {
3231
+ reject(`Failed to get image dimensions for: ${url}`);
3232
+ });
3233
+ img.src = url;
3234
+ });
3235
+
3208
3236
  const get$2 = (_win) => {
3209
3237
  const win = _win === undefined ? window : _win;
3210
3238
  if (detect$1().browser.isFirefox()) {
@@ -3660,7 +3688,7 @@
3660
3688
  const isRoot = (el) => eq(el, rootNode);
3661
3689
  each$e(fromDom$1(transparentBlocks), (transparentBlock) => {
3662
3690
  ancestor$5(transparentBlock, isBlock, isRoot).each((parentBlock) => {
3663
- const invalidChildren = children(transparentBlock, (el) => isBlock(el) && !schema.isValidChild(name(parentBlock), name(el)));
3691
+ const invalidChildren = children$1(transparentBlock, (el) => isBlock(el) && !schema.isValidChild(name(parentBlock), name(el)));
3664
3692
  if (invalidChildren.length > 0) {
3665
3693
  const stateScope = parentElement(parentBlock);
3666
3694
  each$e(invalidChildren, (child) => {
@@ -3711,7 +3739,7 @@
3711
3739
  // this tries to compensate for that by detecting if that offsets are incorrect and then remove the height
3712
3740
  const getTableCaptionDeltaY = (elm) => {
3713
3741
  if (browser$2.isFirefox() && name(elm) === 'table') {
3714
- return firstElement(children$1(elm)).filter((elm) => {
3742
+ return firstElement(children$2(elm)).filter((elm) => {
3715
3743
  return name(elm) === 'caption';
3716
3744
  }).bind((caption) => {
3717
3745
  return firstElement(nextSiblings(caption)).map((body) => {
@@ -5594,12 +5622,16 @@
5594
5622
  let matches;
5595
5623
  while ((matches = styleRegExp.exec(css))) {
5596
5624
  styleRegExp.lastIndex = matches.index + matches[0].length;
5597
- let name = matches[1].replace(trimRightRegExp, '').toLowerCase();
5625
+ let name = matches[1].replace(trimRightRegExp, '');
5598
5626
  let value = matches[2].replace(trimRightRegExp, '');
5599
5627
  if (name && value) {
5600
5628
  // Decode escaped sequences like \65 -> e
5601
5629
  name = decodeHexSequences(name);
5602
5630
  value = decodeHexSequences(value);
5631
+ // Custom properties (--*) keep user case; standard names normalize to lowercase
5632
+ if (!name.startsWith('--')) {
5633
+ name = name.toLowerCase();
5634
+ }
5603
5635
  // Skip properties with double quotes and sequences like \" \' in their names
5604
5636
  // See 'mXSS Attacks: Attacking well-secured Web-Applications by using innerHTML Mutations'
5605
5637
  // https://cure53.de/fp170.pdf
@@ -5614,9 +5646,6 @@
5614
5646
  if (name === 'font-weight' && value === '700') {
5615
5647
  value = 'bold';
5616
5648
  }
5617
- else if (name === 'color' || name === 'background-color') { // Lowercase colors like RED
5618
- value = value.toLowerCase();
5619
- }
5620
5649
  // Convert RGB colors to HEX
5621
5650
  if (getColorFormat(value) === 'rgb') {
5622
5651
  fromString(value).each((rgba) => {
@@ -6591,7 +6620,7 @@
6591
6620
  const $node = SugarElement.fromDom(n);
6592
6621
  if (keepChildren) {
6593
6622
  // Unwrap but don't keep any empty text nodes
6594
- each$e(children$1($node), (child) => {
6623
+ each$e(children$2($node), (child) => {
6595
6624
  if (isText$c(child) && child.dom.length === 0) {
6596
6625
  remove$8(child);
6597
6626
  }
@@ -12736,7 +12765,7 @@
12736
12765
  switch (ctx) {
12737
12766
  case "invalid-child" /* ChildContext.InvalidChild */: {
12738
12767
  finishWrapper();
12739
- const children = children$1(elem);
12768
+ const children = children$2(elem);
12740
12769
  processElements(children);
12741
12770
  finishWrapper();
12742
12771
  break;
@@ -12890,6 +12919,113 @@
12890
12919
  };
12891
12920
  };
12892
12921
 
12922
+ const announcerContainerId = generate('tiny-aria-announcer');
12923
+ const POLITE_MESSAGE_TTL_MS = 600000; // 10 minutes
12924
+ const CREATE_DELAY_MS = 100; // Delay before creating announcer regions to avoid interfering with screen readers initial announcements.
12925
+ const politeTimestampAttr = 'data-mce-announced-at';
12926
+ const OFFSCREEN_STYLES = {
12927
+ position: 'absolute',
12928
+ left: '-9999px',
12929
+ width: '1px',
12930
+ height: '1px',
12931
+ overflow: 'hidden'
12932
+ };
12933
+ const createRegion = (live) => {
12934
+ const region = SugarElement.fromTag('div');
12935
+ setAll$1(region, {
12936
+ 'aria-live': live,
12937
+ 'aria-atomic': 'false',
12938
+ 'aria-relevant': 'additions'
12939
+ });
12940
+ return region;
12941
+ };
12942
+ const isConnected = (element) => element.dom.isConnected;
12943
+ const createNewState = () => {
12944
+ const container = SugarElement.fromTag('div');
12945
+ const politeRegion = createRegion('polite');
12946
+ const assertiveRegion = createRegion('assertive');
12947
+ set$5(container, 'id', announcerContainerId);
12948
+ setAll(container, OFFSCREEN_STYLES);
12949
+ append$1(container, politeRegion);
12950
+ append$1(container, assertiveRegion);
12951
+ append$1(body(), container);
12952
+ return { container, politeRegion, assertiveRegion };
12953
+ };
12954
+ const cleanupExpiredMessages = (polite, now) => {
12955
+ each$e(children(polite, `div[${politeTimestampAttr}]`), (messageDiv) => {
12956
+ getOpt(messageDiv, politeTimestampAttr)
12957
+ .bind((value) => toInt(value))
12958
+ .filter((announcedAt) => now - announcedAt > POLITE_MESSAGE_TTL_MS)
12959
+ .each(() => remove$8(messageDiv));
12960
+ });
12961
+ };
12962
+ const createAnnouncer = () => {
12963
+ const state = value$1();
12964
+ const mountRegions = async () => {
12965
+ const createNewPendingState = async () => {
12966
+ const promise = new Promise((resolve) => {
12967
+ const newState = createNewState();
12968
+ setTimeout(() => resolve(newState), CREATE_DELAY_MS);
12969
+ });
12970
+ state.set(promise);
12971
+ return promise;
12972
+ };
12973
+ return state.get().fold(createNewPendingState, async (existingPromise) => {
12974
+ const { container } = await existingPromise;
12975
+ if (isConnected(container)) {
12976
+ return existingPromise;
12977
+ }
12978
+ else {
12979
+ // A concurrent caller may have already replaced the stale state while we awaited.
12980
+ // The block after the await runs atomically, so reuse that state if present and
12981
+ // only create a fresh one when the state is still the stale promise we observed.
12982
+ return state.get().filter((current) => current !== existingPromise).getOrThunk(createNewPendingState);
12983
+ }
12984
+ });
12985
+ };
12986
+ const addMessage = (region, message) => {
12987
+ const now = Date.now();
12988
+ cleanupExpiredMessages(region, now);
12989
+ const messageDiv = SugarElement.fromTag('div');
12990
+ set$5(messageDiv, politeTimestampAttr, String(now));
12991
+ append$1(messageDiv, SugarElement.fromText(message));
12992
+ append$1(region, messageDiv);
12993
+ };
12994
+ const polite = async (message) => {
12995
+ const { politeRegion } = await mountRegions();
12996
+ addMessage(politeRegion, message);
12997
+ };
12998
+ const assertive = async (message) => {
12999
+ const { assertiveRegion } = await mountRegions();
13000
+ addMessage(assertiveRegion, message);
13001
+ };
13002
+ return { polite, assertive };
13003
+ };
13004
+
13005
+ const announcer = createAnnouncer();
13006
+ /**
13007
+ * Announces a message to screen readers via an aria-live region, without shifting focus.
13008
+ *
13009
+ * @method announce
13010
+ * @param {String} message The message to announce to screen readers.
13011
+ * @param {Object} options Optional settings.
13012
+ * @param {Boolean} options.assertive If true, uses aria-live="assertive" instead of polite.
13013
+ * @example
13014
+ * tinymce.dom.AriaAnnouncer.announce('Bold on');
13015
+ * tinymce.dom.AriaAnnouncer.announce('Error occurred', { assertive: true });
13016
+ */
13017
+ const announce = (message, options) => {
13018
+ if (options?.assertive === true) {
13019
+ announcer.assertive(message).catch(noop);
13020
+ }
13021
+ else {
13022
+ announcer.polite(message).catch(noop);
13023
+ }
13024
+ };
13025
+ const AriaAnnouncer = {
13026
+ announce
13027
+ };
13028
+
12893
13029
  /**
12894
13030
  * Constructs a new BookmarkManager instance for a specific selection instance.
12895
13031
  *
@@ -12954,7 +13090,7 @@
12954
13090
  };
12955
13091
 
12956
13092
  const clamp$1 = (offset, element) => {
12957
- const max = isText$c(element) ? get$5(element).length : children$1(element).length + 1;
13093
+ const max = isText$c(element) ? get$5(element).length : children$2(element).length + 1;
12958
13094
  if (offset > max) {
12959
13095
  return max;
12960
13096
  }
@@ -14226,7 +14362,7 @@
14226
14362
  editor.dispatch('AfterScrollIntoView', data);
14227
14363
  };
14228
14364
  const descend = (element, offset) => {
14229
- const children = children$1(element);
14365
+ const children = children$2(element);
14230
14366
  if (children.length === 0 || excludeFromDescend(element)) {
14231
14367
  return { element, offset };
14232
14368
  }
@@ -14246,7 +14382,7 @@
14246
14382
  return { element: last, offset: get$5(last).length };
14247
14383
  }
14248
14384
  else {
14249
- return { element: last, offset: children$1(last).length };
14385
+ return { element: last, offset: children$2(last).length };
14250
14386
  }
14251
14387
  }
14252
14388
  }
@@ -15750,7 +15886,7 @@
15750
15886
  const br = SugarElement.fromHtml('<br data-mce-bogus="1">');
15751
15887
  // Remove all bogus elements except caret
15752
15888
  if (preserveEmptyCaret) {
15753
- each$e(children$1(elm), (node) => {
15889
+ each$e(children$2(elm), (node) => {
15754
15890
  if (!isEmptyCaretFormatElement(node)) {
15755
15891
  remove$8(node);
15756
15892
  }
@@ -15922,7 +16058,7 @@
15922
16058
  // Clean up any additional leftover nodes. If the last block wasn't a direct child, then we also need to clean up siblings
15923
16059
  if (!eq(root, lastBlock)) {
15924
16060
  const additionalCleanupNodes = is$4(parent(lastBlock), root) ? [] : siblings(lastBlock);
15925
- each$e(additionalCleanupNodes.concat(children$1(root)), (node) => {
16061
+ each$e(additionalCleanupNodes.concat(children$2(root)), (node) => {
15926
16062
  if (!eq(node, lastBlock) && !contains(node, lastBlock) && isEmpty$4(editor.schema, node)) {
15927
16063
  remove$8(node);
15928
16064
  }
@@ -17695,14 +17831,14 @@
17695
17831
  });
17696
17832
  };
17697
17833
  const wrapChildrenInInnerWrapper = (target, wrapper, hasFormat, removeFormatFromElement) => {
17698
- each$e(children$1(target), (child) => {
17834
+ each$e(children$2(target), (child) => {
17699
17835
  if (isElement$8(child) && hasFormat(child)) {
17700
17836
  if (removeFormatFromElement(child).isNone()) {
17701
17837
  unwrap(child);
17702
17838
  }
17703
17839
  }
17704
17840
  });
17705
- each$e(children$1(target), (child) => append$1(wrapper, child));
17841
+ each$e(children$2(target), (child) => append$1(wrapper, child));
17706
17842
  prepend(target, wrapper);
17707
17843
  };
17708
17844
  const wrapInOuterWrappers = (target, wrappers) => {
@@ -18492,24 +18628,65 @@
18492
18628
  }
18493
18629
  };
18494
18630
 
18495
- /*! @license DOMPurify 3.3.2 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.3.2/LICENSE */
18496
-
18497
- const {
18498
- entries,
18499
- setPrototypeOf,
18500
- isFrozen,
18501
- getPrototypeOf,
18502
- getOwnPropertyDescriptor
18503
- } = Object;
18504
- let {
18505
- freeze,
18506
- seal,
18507
- create: create$7
18508
- } = Object; // eslint-disable-line import/no-mutable-exports
18509
- let {
18510
- apply,
18511
- construct
18512
- } = typeof Reflect !== 'undefined' && Reflect;
18631
+ /*! @license DOMPurify 3.4.5 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.5/LICENSE */
18632
+
18633
+ function _arrayLikeToArray(r, a) {
18634
+ (null == a || a > r.length) && (a = r.length);
18635
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
18636
+ return n;
18637
+ }
18638
+ function _arrayWithHoles(r) {
18639
+ if (Array.isArray(r)) return r;
18640
+ }
18641
+ function _iterableToArrayLimit(r, l) {
18642
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
18643
+ if (null != t) {
18644
+ var e,
18645
+ n,
18646
+ i,
18647
+ u,
18648
+ a = [],
18649
+ f = true,
18650
+ o = false;
18651
+ try {
18652
+ if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
18653
+ } catch (r) {
18654
+ o = true, n = r;
18655
+ } finally {
18656
+ try {
18657
+ if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
18658
+ } finally {
18659
+ if (o) throw n;
18660
+ }
18661
+ }
18662
+ return a;
18663
+ }
18664
+ }
18665
+ function _nonIterableRest() {
18666
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
18667
+ }
18668
+ function _slicedToArray(r, e) {
18669
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
18670
+ }
18671
+ function _unsupportedIterableToArray(r, a) {
18672
+ if (r) {
18673
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
18674
+ var t = {}.toString.call(r).slice(8, -1);
18675
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
18676
+ }
18677
+ }
18678
+
18679
+ const entries = Object.entries,
18680
+ setPrototypeOf = Object.setPrototypeOf,
18681
+ isFrozen = Object.isFrozen,
18682
+ getPrototypeOf = Object.getPrototypeOf,
18683
+ getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
18684
+ let freeze = Object.freeze,
18685
+ seal = Object.seal,
18686
+ create$7 = Object.create; // eslint-disable-line import/no-mutable-exports
18687
+ let _ref = typeof Reflect !== 'undefined' && Reflect,
18688
+ apply = _ref.apply,
18689
+ construct = _ref.construct;
18513
18690
  if (!freeze) {
18514
18691
  freeze = function freeze(x) {
18515
18692
  return x;
@@ -18541,13 +18718,19 @@
18541
18718
  const arrayPop = unapply(Array.prototype.pop);
18542
18719
  const arrayPush = unapply(Array.prototype.push);
18543
18720
  const arraySplice = unapply(Array.prototype.splice);
18721
+ const arrayIsArray = Array.isArray;
18544
18722
  const stringToLowerCase = unapply(String.prototype.toLowerCase);
18545
18723
  const stringToString = unapply(String.prototype.toString);
18546
18724
  const stringMatch = unapply(String.prototype.match);
18547
18725
  const stringReplace = unapply(String.prototype.replace);
18548
18726
  const stringIndexOf = unapply(String.prototype.indexOf);
18549
18727
  const stringTrim = unapply(String.prototype.trim);
18728
+ const numberToString = unapply(Number.prototype.toString);
18729
+ const booleanToString = unapply(Boolean.prototype.toString);
18730
+ const bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
18731
+ const symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
18550
18732
  const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
18733
+ const objectToString = unapply(Object.prototype.toString);
18551
18734
  const regExpTest = unapply(RegExp.prototype.test);
18552
18735
  const typeErrorCreate = unconstruct(TypeError);
18553
18736
  /**
@@ -18597,6 +18780,9 @@
18597
18780
  // Prevent prototype setters from intercepting set as a this value.
18598
18781
  setPrototypeOf(set, null);
18599
18782
  }
18783
+ if (!arrayIsArray(array)) {
18784
+ return set;
18785
+ }
18600
18786
  let l = array.length;
18601
18787
  while (l--) {
18602
18788
  let element = array[l];
@@ -18637,10 +18823,13 @@
18637
18823
  */
18638
18824
  function clone(object) {
18639
18825
  const newObject = create$7(null);
18640
- for (const [property, value] of entries(object)) {
18826
+ for (const _ref2 of entries(object)) {
18827
+ var _ref3 = _slicedToArray(_ref2, 2);
18828
+ const property = _ref3[0];
18829
+ const value = _ref3[1];
18641
18830
  const isPropertyExist = objectHasOwnProperty(object, property);
18642
18831
  if (isPropertyExist) {
18643
- if (Array.isArray(value)) {
18832
+ if (arrayIsArray(value)) {
18644
18833
  newObject[property] = cleanArray(value);
18645
18834
  } else if (value && typeof value === 'object' && value.constructor === Object) {
18646
18835
  newObject[property] = clone(value);
@@ -18651,6 +18840,58 @@
18651
18840
  }
18652
18841
  return newObject;
18653
18842
  }
18843
+ /**
18844
+ * Convert non-node values into strings without depending on direct property access.
18845
+ *
18846
+ * @param value - The value to stringify.
18847
+ * @returns A string representation of the provided value.
18848
+ */
18849
+ function stringifyValue(value) {
18850
+ switch (typeof value) {
18851
+ case 'string':
18852
+ {
18853
+ return value;
18854
+ }
18855
+ case 'number':
18856
+ {
18857
+ return numberToString(value);
18858
+ }
18859
+ case 'boolean':
18860
+ {
18861
+ return booleanToString(value);
18862
+ }
18863
+ case 'bigint':
18864
+ {
18865
+ return bigintToString ? bigintToString(value) : '0';
18866
+ }
18867
+ case 'symbol':
18868
+ {
18869
+ return symbolToString ? symbolToString(value) : 'Symbol()';
18870
+ }
18871
+ case 'undefined':
18872
+ {
18873
+ return objectToString(value);
18874
+ }
18875
+ case 'function':
18876
+ case 'object':
18877
+ {
18878
+ if (value === null) {
18879
+ return objectToString(value);
18880
+ }
18881
+ const valueAsRecord = value;
18882
+ const valueToString = lookupGetter(valueAsRecord, 'toString');
18883
+ if (typeof valueToString === 'function') {
18884
+ const stringified = valueToString(valueAsRecord);
18885
+ return typeof stringified === 'string' ? stringified : objectToString(stringified);
18886
+ }
18887
+ return objectToString(value);
18888
+ }
18889
+ default:
18890
+ {
18891
+ return objectToString(value);
18892
+ }
18893
+ }
18894
+ }
18654
18895
  /**
18655
18896
  * This method automatically checks if the prop is function or getter and behaves accordingly.
18656
18897
  *
@@ -18676,6 +18917,14 @@
18676
18917
  }
18677
18918
  return fallbackValue;
18678
18919
  }
18920
+ function isRegex(value) {
18921
+ try {
18922
+ regExpTest(value, '');
18923
+ return true;
18924
+ } catch (_unused) {
18925
+ return false;
18926
+ }
18927
+ }
18679
18928
 
18680
18929
  const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
18681
18930
  const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
@@ -18691,15 +18940,14 @@
18691
18940
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
18692
18941
  const text = freeze(['#text']);
18693
18942
 
18694
- const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
18943
+ const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'command', 'commandfor', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns']);
18695
18944
  const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
18696
- const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
18945
+ const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lquote', 'lspace', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
18697
18946
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
18698
18947
 
18699
- // eslint-disable-next-line unicorn/better-regex
18700
- const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
18701
- const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
18702
- const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
18948
+ const MUSTACHE_EXPR = seal(/{{[\w\W]*|^[\w\W]*}}/g);
18949
+ const ERB_EXPR = seal(/<%[\w\W]*|^[\w\W]*%>/g);
18950
+ const TMPLIT_EXPR = seal(/\${[\w\W]*/g);
18703
18951
  const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
18704
18952
  const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
18705
18953
  const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
@@ -18710,38 +18958,15 @@
18710
18958
  const DOCTYPE_NAME = seal(/^html$/i);
18711
18959
  const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
18712
18960
 
18713
- var EXPRESSIONS = /*#__PURE__*/Object.freeze({
18714
- __proto__: null,
18715
- ARIA_ATTR: ARIA_ATTR,
18716
- ATTR_WHITESPACE: ATTR_WHITESPACE,
18717
- CUSTOM_ELEMENT: CUSTOM_ELEMENT,
18718
- DATA_ATTR: DATA_ATTR,
18719
- DOCTYPE_NAME: DOCTYPE_NAME,
18720
- ERB_EXPR: ERB_EXPR,
18721
- IS_ALLOWED_URI: IS_ALLOWED_URI,
18722
- IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
18723
- MUSTACHE_EXPR: MUSTACHE_EXPR,
18724
- TMPLIT_EXPR: TMPLIT_EXPR
18725
- });
18726
-
18727
18961
  /* eslint-disable @typescript-eslint/indent */
18728
18962
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
18729
18963
  const NODE_TYPE = {
18730
18964
  element: 1,
18731
- attribute: 2,
18732
18965
  text: 3,
18733
- cdataSection: 4,
18734
- entityReference: 5,
18735
- // Deprecated
18736
- entityNode: 6,
18737
18966
  // Deprecated
18738
18967
  progressingInstruction: 7,
18739
18968
  comment: 8,
18740
- document: 9,
18741
- documentType: 10,
18742
- documentFragment: 11,
18743
- notation: 12 // Deprecated
18744
- };
18969
+ document: 9};
18745
18970
  const getGlobal = function getGlobal() {
18746
18971
  return typeof window === 'undefined' ? null : window;
18747
18972
  };
@@ -18799,7 +19024,7 @@
18799
19024
  function createDOMPurify() {
18800
19025
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
18801
19026
  const DOMPurify = root => createDOMPurify(root);
18802
- DOMPurify.version = '3.3.2';
19027
+ DOMPurify.version = '3.4.5';
18803
19028
  DOMPurify.removed = [];
18804
19029
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
18805
19030
  // Not running in a browser, provide a factory function
@@ -18807,28 +19032,26 @@
18807
19032
  DOMPurify.isSupported = false;
18808
19033
  return DOMPurify;
18809
19034
  }
18810
- let {
18811
- document
18812
- } = window;
19035
+ let document = window.document;
18813
19036
  const originalDocument = document;
18814
19037
  const currentScript = originalDocument.currentScript;
18815
- const {
18816
- DocumentFragment,
18817
- HTMLTemplateElement,
18818
- Node,
18819
- Element,
18820
- NodeFilter,
18821
- NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
18822
- HTMLFormElement,
18823
- DOMParser,
18824
- trustedTypes
18825
- } = window;
19038
+ const DocumentFragment = window.DocumentFragment,
19039
+ HTMLTemplateElement = window.HTMLTemplateElement,
19040
+ Node = window.Node,
19041
+ Element = window.Element,
19042
+ NodeFilter = window.NodeFilter,
19043
+ _window$NamedNodeMap = window.NamedNodeMap,
19044
+ NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
19045
+ HTMLFormElement = window.HTMLFormElement,
19046
+ DOMParser = window.DOMParser,
19047
+ trustedTypes = window.trustedTypes;
18826
19048
  const ElementPrototype = Element.prototype;
18827
19049
  const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
18828
19050
  const remove = lookupGetter(ElementPrototype, 'remove');
18829
19051
  const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
18830
19052
  const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
18831
19053
  const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
19054
+ const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeType') : null;
18832
19055
  // As per issue #47, the web-components registry is inherited by a
18833
19056
  // new document created via createHTMLDocument. As per the spec
18834
19057
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
@@ -18843,33 +19066,26 @@
18843
19066
  }
18844
19067
  let trustedTypesPolicy;
18845
19068
  let emptyHTML = '';
18846
- const {
18847
- implementation,
18848
- createNodeIterator,
18849
- createDocumentFragment,
18850
- getElementsByTagName
18851
- } = document;
18852
- const {
18853
- importNode
18854
- } = originalDocument;
19069
+ const _document = document,
19070
+ implementation = _document.implementation,
19071
+ createNodeIterator = _document.createNodeIterator,
19072
+ createDocumentFragment = _document.createDocumentFragment,
19073
+ getElementsByTagName = _document.getElementsByTagName;
19074
+ const importNode = originalDocument.importNode;
18855
19075
  let hooks = _createHooksMap();
18856
19076
  /**
18857
19077
  * Expose whether this browser supports running the full DOMPurify.
18858
19078
  */
18859
19079
  DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
18860
- const {
18861
- MUSTACHE_EXPR,
18862
- ERB_EXPR,
18863
- TMPLIT_EXPR,
18864
- DATA_ATTR,
18865
- ARIA_ATTR,
18866
- IS_SCRIPT_OR_DATA,
18867
- ATTR_WHITESPACE,
18868
- CUSTOM_ELEMENT
18869
- } = EXPRESSIONS;
18870
- let {
18871
- IS_ALLOWED_URI: IS_ALLOWED_URI$1
18872
- } = EXPRESSIONS;
19080
+ const MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
19081
+ ERB_EXPR$1 = ERB_EXPR,
19082
+ TMPLIT_EXPR$1 = TMPLIT_EXPR,
19083
+ DATA_ATTR$1 = DATA_ATTR,
19084
+ ARIA_ATTR$1 = ARIA_ATTR,
19085
+ IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
19086
+ ATTR_WHITESPACE$1 = ATTR_WHITESPACE,
19087
+ CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
19088
+ let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
18873
19089
  /**
18874
19090
  * We consider the elements and attributes below to be safe. Ideally
18875
19091
  * don't add any new ones but feel free to remove unwanted ones.
@@ -19047,15 +19263,15 @@
19047
19263
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
19048
19264
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
19049
19265
  /* Set configuration parameters */
19050
- ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
19051
- ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
19052
- ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
19053
- URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
19054
- DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
19055
- FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
19056
- FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
19057
- FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
19058
- USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
19266
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') && arrayIsArray(cfg.ALLOWED_TAGS) ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
19267
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') && arrayIsArray(cfg.ALLOWED_ATTR) ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
19268
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') && arrayIsArray(cfg.ALLOWED_NAMESPACES) ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
19269
+ URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR) ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
19270
+ DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') && arrayIsArray(cfg.ADD_DATA_URI_TAGS) ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
19271
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS) ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
19272
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') && arrayIsArray(cfg.FORBID_TAGS) ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
19273
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') && arrayIsArray(cfg.FORBID_ATTR) ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
19274
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
19059
19275
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
19060
19276
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
19061
19277
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
@@ -19071,19 +19287,20 @@
19071
19287
  SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
19072
19288
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
19073
19289
  IN_PLACE = cfg.IN_PLACE || false; // Default false
19074
- IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
19075
- NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
19076
- MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
19077
- HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
19078
- CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
19079
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
19080
- CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
19290
+ IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
19291
+ NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
19292
+ MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'MATHML_TEXT_INTEGRATION_POINTS') && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === 'object' ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); // Default built-in map
19293
+ HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'HTML_INTEGRATION_POINTS') && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === 'object' ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, ['annotation-xml']); // Default built-in map
19294
+ const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create$7(null);
19295
+ CUSTOM_ELEMENT_HANDLING = create$7(null);
19296
+ if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
19297
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; // Default undefined
19081
19298
  }
19082
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
19083
- CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
19299
+ if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {
19300
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined
19084
19301
  }
19085
- if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
19086
- CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
19302
+ if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
19303
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
19087
19304
  }
19088
19305
  if (SAFE_FOR_TEMPLATES) {
19089
19306
  ALLOW_DATA_ATTR = false;
@@ -19115,44 +19332,41 @@
19115
19332
  addToSet(ALLOWED_ATTR, xml);
19116
19333
  }
19117
19334
  }
19118
- /* Prevent function-based ADD_ATTR / ADD_TAGS from leaking across calls */
19119
- if (!objectHasOwnProperty(cfg, 'ADD_TAGS')) {
19120
- EXTRA_ELEMENT_HANDLING.tagCheck = null;
19121
- }
19122
- if (!objectHasOwnProperty(cfg, 'ADD_ATTR')) {
19123
- EXTRA_ELEMENT_HANDLING.attributeCheck = null;
19124
- }
19335
+ /* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent
19336
+ * leaking across calls when switching from function to array config */
19337
+ EXTRA_ELEMENT_HANDLING.tagCheck = null;
19338
+ EXTRA_ELEMENT_HANDLING.attributeCheck = null;
19125
19339
  /* Merge configuration parameters */
19126
- if (cfg.ADD_TAGS) {
19340
+ if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {
19127
19341
  if (typeof cfg.ADD_TAGS === 'function') {
19128
19342
  EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
19129
- } else {
19343
+ } else if (arrayIsArray(cfg.ADD_TAGS)) {
19130
19344
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
19131
19345
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
19132
19346
  }
19133
19347
  addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
19134
19348
  }
19135
19349
  }
19136
- if (cfg.ADD_ATTR) {
19350
+ if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {
19137
19351
  if (typeof cfg.ADD_ATTR === 'function') {
19138
19352
  EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
19139
- } else {
19353
+ } else if (arrayIsArray(cfg.ADD_ATTR)) {
19140
19354
  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
19141
19355
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
19142
19356
  }
19143
19357
  addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
19144
19358
  }
19145
19359
  }
19146
- if (cfg.ADD_URI_SAFE_ATTR) {
19360
+ if (objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) {
19147
19361
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
19148
19362
  }
19149
- if (cfg.FORBID_CONTENTS) {
19363
+ if (objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS)) {
19150
19364
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
19151
19365
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
19152
19366
  }
19153
19367
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
19154
19368
  }
19155
- if (cfg.ADD_FORBID_CONTENTS) {
19369
+ if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
19156
19370
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
19157
19371
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
19158
19372
  }
@@ -19391,6 +19605,40 @@
19391
19605
  // eslint-disable-next-line no-bitwise
19392
19606
  NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
19393
19607
  };
19608
+ /**
19609
+ * Strip template-engine expressions ({{...}}, ${...}, <%...%>) from the
19610
+ * character data of an element subtree. Used as the final safety net for
19611
+ * SAFE_FOR_TEMPLATES on every DOM-returning code path so that expressions
19612
+ * which only form after text-node normalization (e.g. fragments split across
19613
+ * stripped elements) cannot survive into a template-evaluating framework.
19614
+ *
19615
+ * Walks text/comment/CDATA/processing-instruction nodes and mutates `.data`
19616
+ * in place rather than round-tripping through innerHTML. This preserves
19617
+ * descendant node references (important for IN_PLACE callers), avoids a
19618
+ * serialize/reparse cycle, and reads literal character data — which means
19619
+ * `<%...%>` in text content matches the ERB regex against its real bytes
19620
+ * instead of the HTML-entity-escaped form innerHTML would produce.
19621
+ *
19622
+ * Attribute values are not visited here; SAFE_FOR_TEMPLATES handling for
19623
+ * attributes is performed during the per-node `_sanitizeAttributes` pass.
19624
+ *
19625
+ * @param node The root element whose character data should be scrubbed.
19626
+ */
19627
+ const _scrubTemplateExpressions = function _scrubTemplateExpressions(node) {
19628
+ node.normalize();
19629
+ const walker = createNodeIterator.call(node.ownerDocument || node, node,
19630
+ // eslint-disable-next-line no-bitwise
19631
+ NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
19632
+ let currentNode = walker.nextNode();
19633
+ while (currentNode) {
19634
+ let data = currentNode.data;
19635
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
19636
+ data = stringReplace(data, expr, ' ');
19637
+ });
19638
+ currentNode.data = data;
19639
+ currentNode = walker.nextNode();
19640
+ }
19641
+ };
19394
19642
  /**
19395
19643
  * _isClobbered
19396
19644
  *
@@ -19401,13 +19649,31 @@
19401
19649
  return element instanceof HTMLFormElement && (typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function');
19402
19650
  };
19403
19651
  /**
19404
- * Checks whether the given object is a DOM node.
19652
+ * Checks whether the given object is a DOM node, including nodes that
19653
+ * originate from a different window/realm (e.g. an iframe's
19654
+ * contentDocument). The previous `value instanceof Node` check was
19655
+ * realm-bound: nodes from a different window failed it, causing
19656
+ * sanitize() to silently stringify them and reset IN_PLACE to false,
19657
+ * returning the original node unsanitized. See GHSA-4w3q-35jp-p934.
19658
+ *
19659
+ * Implementation: call the cached `nodeType` getter from Node.prototype
19660
+ * directly on the value. This bypasses any clobbered instance property
19661
+ * (e.g. a child element named "nodeType") and works across realms
19662
+ * because the WebIDL `nodeType` getter reads an internal slot that
19663
+ * every real Node has, regardless of which window minted it.
19405
19664
  *
19406
19665
  * @param value object to check whether it's a DOM node
19407
- * @return true is object is a DOM node
19666
+ * @return true if value is a DOM node from any realm
19408
19667
  */
19409
19668
  const _isNode = function _isNode(value) {
19410
- return typeof Node === 'function' && value instanceof Node;
19669
+ if (!getNodeType || typeof value !== 'object' || value === null) {
19670
+ return false;
19671
+ }
19672
+ try {
19673
+ return typeof getNodeType(value) === 'number';
19674
+ } catch (_) {
19675
+ return false;
19676
+ }
19411
19677
  };
19412
19678
  function _executeHooks(hooks, currentNode, data) {
19413
19679
  arrayForEach(hooks, hook => {
@@ -19444,6 +19710,11 @@
19444
19710
  _forceRemove(currentNode);
19445
19711
  return true;
19446
19712
  }
19713
+ /* Remove risky CSS construction leading to mXSS */
19714
+ if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
19715
+ _forceRemove(currentNode);
19716
+ return true;
19717
+ }
19447
19718
  /* Remove any occurrence of processing instructions */
19448
19719
  if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
19449
19720
  _forceRemove(currentNode);
@@ -19455,7 +19726,7 @@
19455
19726
  return true;
19456
19727
  }
19457
19728
  /* Remove element if anything forbids its presence */
19458
- if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {
19729
+ if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
19459
19730
  /* Check if we have a custom element to handle */
19460
19731
  if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
19461
19732
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
@@ -19473,7 +19744,6 @@
19473
19744
  const childCount = childNodes.length;
19474
19745
  for (let i = childCount - 1; i >= 0; --i) {
19475
19746
  const childClone = cloneNode(childNodes[i], true);
19476
- childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
19477
19747
  parentNode.insertBefore(childClone, getNextSibling(currentNode));
19478
19748
  }
19479
19749
  }
@@ -19495,7 +19765,7 @@
19495
19765
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
19496
19766
  /* Get the element's text content */
19497
19767
  content = currentNode.textContent;
19498
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
19768
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
19499
19769
  content = stringReplace(content, expr, ' ');
19500
19770
  });
19501
19771
  if (currentNode.textContent !== content) {
@@ -19527,11 +19797,12 @@
19527
19797
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
19528
19798
  return false;
19529
19799
  }
19800
+ const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
19530
19801
  /* Allow valid data-* attributes: At least one character after "-"
19531
19802
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
19532
19803
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
19533
19804
  We don't need to check the value; it's always URI safe. */
19534
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
19805
+ if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!nameIsPermitted || FORBID_ATTR[lcName]) {
19535
19806
  if (
19536
19807
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
19537
19808
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -19543,11 +19814,15 @@
19543
19814
  return false;
19544
19815
  }
19545
19816
  /* Check value is safe. First, is attr inert? If so, is safe */
19546
- } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {
19817
+ } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if (value) {
19547
19818
  return false;
19548
19819
  } else ;
19549
19820
  return true;
19550
19821
  };
19822
+ /* Names the HTML spec reserves from valid-custom-element-name; these must
19823
+ * never be treated as basic custom elements even when a permissive
19824
+ * CUSTOM_ELEMENT_HANDLING.tagNameCheck is configured. */
19825
+ const RESERVED_CUSTOM_ELEMENT_NAMES = addToSet({}, ['annotation-xml', 'color-profile', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'missing-glyph']);
19551
19826
  /**
19552
19827
  * _isBasicCustomElement
19553
19828
  * checks if at least one dash is included in tagName, and it's not the first char
@@ -19557,7 +19832,7 @@
19557
19832
  * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
19558
19833
  */
19559
19834
  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
19560
- return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
19835
+ return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
19561
19836
  };
19562
19837
  /**
19563
19838
  * _sanitizeAttributes
@@ -19572,9 +19847,7 @@
19572
19847
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
19573
19848
  /* Execute a hook if present */
19574
19849
  _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
19575
- const {
19576
- attributes
19577
- } = currentNode;
19850
+ const attributes = currentNode.attributes;
19578
19851
  /* Check if we have attributes; if not we might have a text node */
19579
19852
  if (!attributes || _isClobbered(currentNode)) {
19580
19853
  return;
@@ -19590,11 +19863,9 @@
19590
19863
  /* Go backwards over all attributes; safely remove bad ones */
19591
19864
  while (l--) {
19592
19865
  const attr = attributes[l];
19593
- const {
19594
- name,
19595
- namespaceURI,
19596
- value: attrValue
19597
- } = attr;
19866
+ const name = attr.name,
19867
+ namespaceURI = attr.namespaceURI,
19868
+ attrValue = attr.value;
19598
19869
  const lcName = transformCaseFunc(name);
19599
19870
  const initValue = attrValue;
19600
19871
  let value = name === 'value' ? initValue : stringTrim(initValue);
@@ -19608,12 +19879,14 @@
19608
19879
  /* Full DOM Clobbering protection via namespace isolation,
19609
19880
  * Prefix id and name attributes with `user-content-`
19610
19881
  */
19611
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
19882
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name') && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
19612
19883
  // Remove the attribute with this value
19613
19884
  _removeAttribute(name, currentNode);
19614
19885
  // Prefix the value and later re-create the attribute with the sanitized value
19615
19886
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
19616
19887
  }
19888
+ // Else: already prefixed, leave the attribute alone — the prefix is
19889
+ // itself the clobbering protection, and re-applying it is incorrect.
19617
19890
  /* Work around a security issue with comments inside attributes */
19618
19891
  if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
19619
19892
  _removeAttribute(name, currentNode);
@@ -19640,7 +19913,7 @@
19640
19913
  }
19641
19914
  /* Sanitize attribute content to be template-safe */
19642
19915
  if (SAFE_FOR_TEMPLATES) {
19643
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
19916
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
19644
19917
  value = stringReplace(value, expr, ' ');
19645
19918
  });
19646
19919
  }
@@ -19694,7 +19967,7 @@
19694
19967
  *
19695
19968
  * @param fragment to iterate over recursively
19696
19969
  */
19697
- const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
19970
+ const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
19698
19971
  let shadowNode = null;
19699
19972
  const shadowIterator = _createNodeIterator(fragment);
19700
19973
  /* Execute a hook if present */
@@ -19708,12 +19981,55 @@
19708
19981
  _sanitizeAttributes(shadowNode);
19709
19982
  /* Deep shadow DOM detected */
19710
19983
  if (shadowNode.content instanceof DocumentFragment) {
19711
- _sanitizeShadowDOM(shadowNode.content);
19984
+ _sanitizeShadowDOM2(shadowNode.content);
19712
19985
  }
19713
19986
  }
19714
19987
  /* Execute a hook if present */
19715
19988
  _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
19716
19989
  };
19990
+ /**
19991
+ * _sanitizeAttachedShadowRoots
19992
+ *
19993
+ * Walks `root` and feeds every attached shadow root we encounter into
19994
+ * the existing _sanitizeShadowDOM pipeline. The default node iterator
19995
+ * does not descend into shadow trees, so nodes inside an attached
19996
+ * shadow root would otherwise be skipped entirely.
19997
+ *
19998
+ * Two real input paths put attached shadow roots in front of us:
19999
+ * 1. IN_PLACE on a DOM node that already has shadow roots attached.
20000
+ * 2. DOM-node input where importNode(dirty, true) deep-clones the
20001
+ * shadow root because it was created with `clonable: true`.
20002
+ *
20003
+ * This pass runs once, up front, so the main iteration loop (and the
20004
+ * existing _sanitizeShadowDOM template-content recursion) stay
20005
+ * untouched — string-input paths are not affected.
20006
+ *
20007
+ * @param root the subtree root to walk for attached shadow roots
20008
+ */
20009
+ const _sanitizeAttachedShadowRoots2 = function _sanitizeAttachedShadowRoots(root) {
20010
+ if (root.nodeType === NODE_TYPE.element && root.shadowRoot instanceof DocumentFragment) {
20011
+ const sr = root.shadowRoot;
20012
+ // Recurse first so that nested shadow roots are reached even if
20013
+ // _sanitizeShadowDOM removes hosts at this level.
20014
+ _sanitizeAttachedShadowRoots2(sr);
20015
+ _sanitizeShadowDOM2(sr);
20016
+ }
20017
+ // Snapshot children before recursing. Sanitization of one subtree
20018
+ // (e.g. via an uponSanitizeShadowNode hook) may detach siblings,
20019
+ // and naive nextSibling traversal would silently skip the rest of
20020
+ // the list once a node is detached.
20021
+ const childNodes = root.childNodes;
20022
+ if (!childNodes) {
20023
+ return;
20024
+ }
20025
+ const snapshot = [];
20026
+ arrayForEach(childNodes, child => {
20027
+ arrayPush(snapshot, child);
20028
+ });
20029
+ for (const child of snapshot) {
20030
+ _sanitizeAttachedShadowRoots2(child);
20031
+ }
20032
+ };
19717
20033
  // eslint-disable-next-line complexity
19718
20034
  DOMPurify.sanitize = function (dirty) {
19719
20035
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -19730,13 +20046,9 @@
19730
20046
  }
19731
20047
  /* Stringify, in case dirty is an object */
19732
20048
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
19733
- if (typeof dirty.toString === 'function') {
19734
- dirty = dirty.toString();
19735
- if (typeof dirty !== 'string') {
19736
- throw typeErrorCreate('dirty is not a string, aborting');
19737
- }
19738
- } else {
19739
- throw typeErrorCreate('toString is not a function');
20049
+ dirty = stringifyValue(dirty);
20050
+ if (typeof dirty !== 'string') {
20051
+ throw typeErrorCreate('dirty is not a string, aborting');
19740
20052
  }
19741
20053
  }
19742
20054
  /* Return dirty HTML if DOMPurify cannot run */
@@ -19755,13 +20067,17 @@
19755
20067
  }
19756
20068
  if (IN_PLACE) {
19757
20069
  /* Do some early pre-sanitization to avoid unsafe root nodes */
19758
- if (dirty.nodeName) {
19759
- const tagName = transformCaseFunc(dirty.nodeName);
20070
+ const nn = dirty.nodeName;
20071
+ if (typeof nn === 'string') {
20072
+ const tagName = transformCaseFunc(nn);
19760
20073
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
19761
20074
  throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
19762
20075
  }
19763
20076
  }
19764
- } else if (dirty instanceof Node) {
20077
+ /* Sanitize attached shadow roots before the main iterator runs.
20078
+ The iterator does not descend into shadow trees. */
20079
+ _sanitizeAttachedShadowRoots2(dirty);
20080
+ } else if (_isNode(dirty)) {
19765
20081
  /* If dirty is a DOM element, append to an empty document to avoid
19766
20082
  elements being stripped by the parser */
19767
20083
  body = _initDocument('<!---->');
@@ -19775,6 +20091,10 @@
19775
20091
  // eslint-disable-next-line unicorn/prefer-dom-node-append
19776
20092
  body.appendChild(importedNode);
19777
20093
  }
20094
+ /* Clonable shadow roots are deep-cloned by importNode(); sanitize
20095
+ them before the main iterator runs, since the iterator does not
20096
+ descend into shadow trees. */
20097
+ _sanitizeAttachedShadowRoots2(importedNode);
19778
20098
  } else {
19779
20099
  /* Exit directly if we have nothing to do */
19780
20100
  if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
@@ -19803,15 +20123,21 @@
19803
20123
  _sanitizeAttributes(currentNode);
19804
20124
  /* Shadow DOM detected, sanitize it */
19805
20125
  if (currentNode.content instanceof DocumentFragment) {
19806
- _sanitizeShadowDOM(currentNode.content);
20126
+ _sanitizeShadowDOM2(currentNode.content);
19807
20127
  }
19808
20128
  }
19809
20129
  /* If we sanitized `dirty` in-place, return it. */
19810
20130
  if (IN_PLACE) {
20131
+ if (SAFE_FOR_TEMPLATES) {
20132
+ _scrubTemplateExpressions(dirty);
20133
+ }
19811
20134
  return dirty;
19812
20135
  }
19813
20136
  /* Return sanitized string or DOM */
19814
20137
  if (RETURN_DOM) {
20138
+ if (SAFE_FOR_TEMPLATES) {
20139
+ _scrubTemplateExpressions(body);
20140
+ }
19815
20141
  if (RETURN_DOM_FRAGMENT) {
19816
20142
  returnNode = createDocumentFragment.call(body.ownerDocument);
19817
20143
  while (body.firstChild) {
@@ -19840,7 +20166,7 @@
19840
20166
  }
19841
20167
  /* Sanitize final string template-safe */
19842
20168
  if (SAFE_FOR_TEMPLATES) {
19843
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
20169
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
19844
20170
  serializedHTML = stringReplace(serializedHTML, expr, ' ');
19845
20171
  });
19846
20172
  }
@@ -21878,7 +22204,7 @@
21878
22204
  append(SugarElement.fromDom(pre1), [
21879
22205
  SugarElement.fromTag('br', doc),
21880
22206
  SugarElement.fromTag('br', doc),
21881
- ...children$1(sPre2)
22207
+ ...children$2(sPre2)
21882
22208
  ]);
21883
22209
  };
21884
22210
  if (!rng.collapsed) {
@@ -22025,10 +22351,12 @@
22025
22351
  const wrapName = format.inline || format.block;
22026
22352
  const wrapElm = createWrapElement(wrapName);
22027
22353
  const isMatchingWrappingBlock = (node) => isWrappingBlockFormat(format) && matchNode$1(ed, node, name, vars);
22354
+ const canRenameChildBlocks = (node) => forall(node.childNodes, (child) => !isTextBlock$2(ed.schema, child) || isValid(ed, wrapName, child.nodeName.toLowerCase()));
22028
22355
  const canRenameBlock = (node, parentName, isEditableDescendant) => {
22029
22356
  const isValidBlockFormatForNode = isNonWrappingBlockFormat(format) &&
22030
22357
  isTextBlock$2(ed.schema, node) &&
22031
- isValid(ed, parentName, wrapName);
22358
+ isValid(ed, parentName, wrapName) &&
22359
+ canRenameChildBlocks(node);
22032
22360
  return isEditableDescendant && isValidBlockFormatForNode;
22033
22361
  };
22034
22362
  const canWrapNode = (node, parentName, isEditableDescendant, isWrappableNoneditableElm) => {
@@ -22185,6 +22513,19 @@
22185
22513
  // node variable is used by other functions above in the same scope so need to set it here
22186
22514
  node = targetNode;
22187
22515
  applyNodeStyle(formatList, node);
22516
+ if (isBlockFormat(format) && !dom.isBlock(targetNode)) {
22517
+ const parentBlock = dom.getParent(targetNode, dom.isBlock);
22518
+ if (dom.isEditable(parentBlock)) {
22519
+ const wrapperElementName = format.block;
22520
+ if (parentBlock.nodeName.toLowerCase() === wrapperElementName.toLowerCase()) {
22521
+ setElementFormat(ed, parentBlock, format, vars, node);
22522
+ }
22523
+ else if (!isWrappingBlockFormat(format)) {
22524
+ const elm = dom.rename(parentBlock, wrapperElementName);
22525
+ setElementFormat(ed, elm, format, vars, node);
22526
+ }
22527
+ }
22528
+ }
22188
22529
  fireFormatApply(ed, name, node, vars);
22189
22530
  return;
22190
22531
  }
@@ -25974,7 +26315,7 @@
25974
26315
  preview: 'font-family font-size'
25975
26316
  },
25976
26317
  {
25977
- selector: '.mce-preview-object,[data-ephox-embed-iri]',
26318
+ selector: '.mce-preview-object,[data-ephox-embed-iri],.tiny-pageembed',
25978
26319
  ceFalseOverride: true,
25979
26320
  styles: {
25980
26321
  float: 'left'
@@ -26026,7 +26367,7 @@
26026
26367
  preview: 'font-family font-size'
26027
26368
  },
26028
26369
  {
26029
- selector: '.mce-preview-object',
26370
+ selector: '.mce-preview-object,.tiny-pageembed',
26030
26371
  ceFalseOverride: true,
26031
26372
  styles: {
26032
26373
  display: 'table', // Needs to be `table` to properly render while editing
@@ -26090,7 +26431,7 @@
26090
26431
  preview: 'font-family font-size'
26091
26432
  },
26092
26433
  {
26093
- selector: '.mce-preview-object,[data-ephox-embed-iri]',
26434
+ selector: '.mce-preview-object,[data-ephox-embed-iri],.tiny-pageembed',
26094
26435
  ceFalseOverride: true,
26095
26436
  styles: {
26096
26437
  float: 'right'
@@ -26777,8 +27118,8 @@
26777
27118
  isFirstTypedCharacter.set(true);
26778
27119
  return;
26779
27120
  }
26780
- const hasOnlyMetaOrCtrlModifier = Env.os.isMacOS() ? e.metaKey : e.ctrlKey && !e.altKey;
26781
- if (hasOnlyMetaOrCtrlModifier) {
27121
+ const hasMetaOrCtrlModifier = Env.os.isMacOS() ? e.metaKey : e.ctrlKey && !e.altKey;
27122
+ if (hasMetaOrCtrlModifier && (e.key === 'Backspace' || e.key === 'Delete')) {
26782
27123
  undoManager.beforeChange();
26783
27124
  }
26784
27125
  });
@@ -27343,7 +27684,7 @@
27343
27684
  const isIndented = (entry) => entry.depth > 0;
27344
27685
  const isSelected = (entry) => entry.isSelected;
27345
27686
  const cloneItemContent = (li) => {
27346
- const children = children$1(li);
27687
+ const children = children$2(li);
27347
27688
  const content = hasLastChildList(li) ? children.slice(0, -1) : children;
27348
27689
  return map$3(content, deep);
27349
27690
  };
@@ -27541,7 +27882,7 @@
27541
27882
  return currentItemEntry.toArray().concat(childListEntries);
27542
27883
  };
27543
27884
  const parseItem = (depth, itemSelection, selectionState, item) => firstChild(item).filter(isList).fold(() => parseSingleItem(depth, itemSelection, selectionState, item), (list) => {
27544
- const parsedSiblings = foldl(children$1(item), (acc, liChild, i) => {
27885
+ const parsedSiblings = foldl(children$2(item), (acc, liChild, i) => {
27545
27886
  if (i === 0) {
27546
27887
  return acc;
27547
27888
  }
@@ -27564,7 +27905,7 @@
27564
27905
  }, []);
27565
27906
  return parseList(depth, itemSelection, selectionState, list).concat(parsedSiblings);
27566
27907
  });
27567
- const parseList = (depth, itemSelection, selectionState, list) => bind$3(children$1(list), (element) => {
27908
+ const parseList = (depth, itemSelection, selectionState, list) => bind$3(children$2(list), (element) => {
27568
27909
  const parser = isList(element) ? parseList : parseItem;
27569
27910
  const newDepth = depth + 1;
27570
27911
  return parser(newDepth, itemSelection, selectionState, element);
@@ -28227,7 +28568,7 @@
28227
28568
  const rng = normalizeRange(editor.selection.getRng());
28228
28569
  const nextCaretContainer = findNextCaretContainer(editor, rng, isForward, root);
28229
28570
  const otherLi = dom.getParent(nextCaretContainer, 'LI', root);
28230
- if (nextCaretContainer && otherLi) {
28571
+ if (nextCaretContainer && otherLi && (isForward || !dom.isChildOf(nextCaretContainer, block))) {
28231
28572
  const findValidElement = (element) => contains$2(['td', 'th', 'caption'], name(element));
28232
28573
  const findRoot = (node) => node.dom === root;
28233
28574
  const otherLiCell = closest$5(SugarElement.fromDom(otherLi), findValidElement, findRoot);
@@ -28325,7 +28666,7 @@
28325
28666
  const read$1 = (schema, rootNode, forward, rng) => rng.collapsed ? readFromRange(schema, rootNode, forward, rng) : Optional.none();
28326
28667
 
28327
28668
  const getChildrenUntilBlockBoundary = (block, schema) => {
28328
- const children = children$1(block);
28669
+ const children = children$2(block);
28329
28670
  return findIndex$2(children, (el) => schema.isBlock(name(el))).fold(constant(children), (index) => children.slice(0, index));
28330
28671
  };
28331
28672
  const extractChildren = (block, schema) => {
@@ -29720,7 +30061,11 @@
29720
30061
  const canIndent = (editor) => !editor.mode.isReadOnly() && canIndent$1(editor);
29721
30062
  const isListComponent = (el) => isList$1(el) || isListItem$2(el);
29722
30063
  const parentIsListComponent = (el) => parent(el).exists(isListComponent);
29723
- const getBlocksToIndent = (editor) => filter$5(fromDom$1(editor.selection.getSelectedBlocks()), (el) => !isListComponent(el) && !parentIsListComponent(el) && isEditable(el));
30064
+ const getBlocksToIndent = (editor) => {
30065
+ const selectedCells = getCellsFromEditor(editor);
30066
+ return selectedCells.length === 0 ?
30067
+ filter$5(fromDom$1(editor.selection.getSelectedBlocks()), (el) => !isListComponent(el) && !parentIsListComponent(el) && isEditable(el)) : selectedCells;
30068
+ };
29724
30069
  const handle = (editor, command) => {
29725
30070
  if (editor.mode.isReadOnly()) {
29726
30071
  return;
@@ -30395,7 +30740,7 @@
30395
30740
  return filterFirstLayer(scope, selector, always);
30396
30741
  };
30397
30742
  const filterFirstLayer = (scope, selector, predicate) => {
30398
- return bind$3(children$1(scope), (x) => {
30743
+ return bind$3(children$2(scope), (x) => {
30399
30744
  if (is$2(x, selector)) {
30400
30745
  return predicate(x) ? [x] : [];
30401
30746
  }
@@ -30605,7 +30950,7 @@
30605
30950
  nextSibling: nextSibling
30606
30951
  }),
30607
30952
  property: constant({
30608
- children: children$1,
30953
+ children: children$2,
30609
30954
  name: name,
30610
30955
  parent: parent,
30611
30956
  document,
@@ -33712,15 +34057,20 @@
33712
34057
  blobCache.add(blobInfo);
33713
34058
  return blobInfo;
33714
34059
  };
33715
- const pasteImage = (editor, imageItem) => {
33716
- parseDataUri(imageItem.uri).each(({ data, type, base64Encoded }) => {
34060
+ const pasteImage = async (editor, imageItem) => {
34061
+ return parseDataUri(imageItem.uri).fold(() => Promise.resolve(), ({ data, type, base64Encoded }) => {
33717
34062
  const base64 = base64Encoded ? data : btoa(data);
33718
34063
  const file = imageItem.file;
33719
34064
  // TODO: Move the bulk of the cache logic to EditorUpload
33720
34065
  const blobCache = editor.editorUpload.blobCache;
33721
34066
  const existingBlobInfo = blobCache.getByData(base64, type);
33722
34067
  const blobInfo = existingBlobInfo ?? createBlobInfo(editor, blobCache, file, base64);
33723
- pasteHtml(editor, `<img src="${blobInfo.blobUri()}">`, false, true);
34068
+ const imgUrl = blobInfo.blobUri();
34069
+ return getImageSize(imgUrl).then(({ width, height }) => {
34070
+ pasteHtml(editor, `<img width="${width}" height="${height}" src="${imgUrl}">`, false, true);
34071
+ }).catch(() => {
34072
+ pasteHtml(editor, `<img src="${imgUrl}">`, false, true);
34073
+ });
33724
34074
  });
33725
34075
  };
33726
34076
  const isClipboardEvent = (event) => event.type === 'paste';
@@ -33751,13 +34101,13 @@
33751
34101
  if (images.length > 0) {
33752
34102
  e.preventDefault();
33753
34103
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
33754
- readFilesAsDataUris(images).then((fileResults) => {
34104
+ readFilesAsDataUris(images).then(async (fileResults) => {
33755
34105
  if (rng) {
33756
34106
  editor.selection.setRng(rng);
33757
34107
  }
33758
- each$e(fileResults, (result) => {
33759
- pasteImage(editor, result);
33760
- });
34108
+ for (const result of fileResults) {
34109
+ await pasteImage(editor, result);
34110
+ }
33761
34111
  });
33762
34112
  return true;
33763
34113
  }
@@ -35905,6 +36255,7 @@
35905
36255
  const browser = Env.browser;
35906
36256
  const isGecko = browser.isFirefox();
35907
36257
  const isWebKit = browser.isChromium() || browser.isSafari();
36258
+ const isSafari = browser.isSafari();
35908
36259
  const isiOS = Env.deviceType.isiPhone() || Env.deviceType.isiPad();
35909
36260
  const isMac = Env.os.isMacOS() || Env.os.isiOS();
35910
36261
  /**
@@ -36511,6 +36862,45 @@
36511
36862
  }
36512
36863
  });
36513
36864
  };
36865
+ /**
36866
+ * this is needed to manage the difference between
36867
+ * ```
36868
+ * <li><span class="fake">a</span><div>b</div></li>
36869
+ * ```
36870
+ * and
36871
+ * ```
36872
+ * <li><span class="fake">a</span> <div>b</div></li>
36873
+ * ```
36874
+ * since if the indentation of the HTML has a new line it creates a fake child in the `li` that is an empty text
36875
+ * it's check it trying to get the rects and if it can't it means that it's the false unwanted new line
36876
+ **/
36877
+ const isValidSibling = (el) => getClientRects([el.dom]).length > 0;
36878
+ const firstBlockChildOrNewLine = (target) => child(target, (child) => isBr$6(child) || isElement$8(child) && get$8(child, 'display') === 'block');
36879
+ const clickAfterEl = (clientX, clientY, rect) => clientX >= rect.right && clientY >= rect.top && clientY <= rect.bottom;
36880
+ /**
36881
+ * In Chrome in a `LI` that contains a block element and where the first child is an inline element
36882
+ * clicking on the right side of the first child the carret goes at the start of the element instead that in the end of it
36883
+ * issue: https://issues.chromium.org/issues/40767343
36884
+ **/
36885
+ const fixInLISelection = () => {
36886
+ editor.on('mousedown', (e) => {
36887
+ const target = SugarElement.fromDom(e.target);
36888
+ if (isListItem$2(target)) {
36889
+ firstBlockChildOrNewLine(target).each((firstBlock) => {
36890
+ const prevSiblings$1 = prevSiblings(firstBlock);
36891
+ findLast(prevSiblings$1, isValidSibling).each((lastInlineBeforeBlock) => {
36892
+ if (get$c(getClientRects([lastInlineBeforeBlock.dom]), 0).exists((rect) => clickAfterEl(e.clientX, e.clientY, rect))) {
36893
+ prevPosition(target.dom, CaretPosition(firstBlock.dom, 0)).each((pos) => {
36894
+ e.preventDefault();
36895
+ editor.focus();
36896
+ editor.selection.setRng(pos.toRange());
36897
+ });
36898
+ }
36899
+ });
36900
+ });
36901
+ }
36902
+ });
36903
+ };
36514
36904
  // No-op since Mozilla seems to have fixed the caret repaint issues
36515
36905
  const refreshContentEditable = noop;
36516
36906
  const isHidden = () => {
@@ -36557,6 +36947,9 @@
36557
36947
  blockFormSubmitInsideEditor();
36558
36948
  disableBackspaceIntoATable();
36559
36949
  removeAppleInterchangeBrs();
36950
+ if (!isSafari) {
36951
+ fixInLISelection();
36952
+ }
36560
36953
  // touchClickEvent();
36561
36954
  // iOS
36562
36955
  if (isiOS) {
@@ -40653,6 +41046,21 @@
40653
41046
  hasEditableRoot() {
40654
41047
  return hasEditableRoot(this);
40655
41048
  }
41049
+ /**
41050
+ * Announces a message to screen readers via the page-wide aria-live region, without shifting focus.
41051
+ * Delegates to {@link tinymce.dom.AriaAnnouncer#announce}.
41052
+ *
41053
+ * @method announce
41054
+ * @param {String} message The message to announce to screen readers.
41055
+ * @param {Object} options Optional settings.
41056
+ * @param {Boolean} options.assertive If true, uses aria-live="assertive" (role="alert") instead of polite.
41057
+ * @example
41058
+ * tinymce.activeEditor.announce('Bold on');
41059
+ * tinymce.activeEditor.announce('Error occurred', { assertive: true });
41060
+ */
41061
+ announce(message, options) {
41062
+ AriaAnnouncer.announce(message, options);
41063
+ }
40656
41064
  /**
40657
41065
  * Removes the editor from the dom and tinymce collection.
40658
41066
  *
@@ -40777,14 +41185,14 @@
40777
41185
  * @property minorVersion
40778
41186
  * @type String
40779
41187
  */
40780
- minorVersion: '5.1',
41188
+ minorVersion: '7.0',
40781
41189
  /**
40782
41190
  * Release date of TinyMCE build.
40783
41191
  *
40784
41192
  * @property releaseDate
40785
41193
  * @type String
40786
41194
  */
40787
- releaseDate: '2026-05-19',
41195
+ releaseDate: '2026-07-01',
40788
41196
  /**
40789
41197
  * Collection of language pack data.
40790
41198
  *
@@ -41642,6 +42050,7 @@
41642
42050
  ControlSelection,
41643
42051
  BookmarkManager,
41644
42052
  Selection: EditorSelection,
42053
+ AriaAnnouncer,
41645
42054
  Event: EventUtils.Event
41646
42055
  },
41647
42056
  html: {