tinymce-rails 8.3.2 → 8.4.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/source/tinymce/tinymce.js +222 -122
  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/models/dom/model.js +1 -1
  6. data/vendor/assets/javascripts/tinymce/notices.txt +1 -1
  7. data/vendor/assets/javascripts/tinymce/plugins/accordion/plugin.js +1 -1
  8. data/vendor/assets/javascripts/tinymce/plugins/advlist/plugin.js +1 -1
  9. data/vendor/assets/javascripts/tinymce/plugins/anchor/plugin.js +1 -1
  10. data/vendor/assets/javascripts/tinymce/plugins/autolink/plugin.js +1 -1
  11. data/vendor/assets/javascripts/tinymce/plugins/autoresize/plugin.js +1 -1
  12. data/vendor/assets/javascripts/tinymce/plugins/autosave/plugin.js +1 -1
  13. data/vendor/assets/javascripts/tinymce/plugins/charmap/plugin.js +1 -1
  14. data/vendor/assets/javascripts/tinymce/plugins/code/plugin.js +1 -1
  15. data/vendor/assets/javascripts/tinymce/plugins/codesample/plugin.js +2 -2
  16. data/vendor/assets/javascripts/tinymce/plugins/directionality/plugin.js +1 -1
  17. data/vendor/assets/javascripts/tinymce/plugins/emoticons/plugin.js +1 -1
  18. data/vendor/assets/javascripts/tinymce/plugins/fullscreen/plugin.js +1 -1
  19. data/vendor/assets/javascripts/tinymce/plugins/help/plugin.js +1 -1
  20. data/vendor/assets/javascripts/tinymce/plugins/image/plugin.js +1 -1
  21. data/vendor/assets/javascripts/tinymce/plugins/importcss/plugin.js +1 -1
  22. data/vendor/assets/javascripts/tinymce/plugins/insertdatetime/plugin.js +1 -1
  23. data/vendor/assets/javascripts/tinymce/plugins/link/plugin.js +1 -1
  24. data/vendor/assets/javascripts/tinymce/plugins/lists/plugin.js +1 -1
  25. data/vendor/assets/javascripts/tinymce/plugins/media/plugin.js +1 -1
  26. data/vendor/assets/javascripts/tinymce/plugins/nonbreaking/plugin.js +1 -1
  27. data/vendor/assets/javascripts/tinymce/plugins/pagebreak/plugin.js +1 -1
  28. data/vendor/assets/javascripts/tinymce/plugins/preview/plugin.js +1 -1
  29. data/vendor/assets/javascripts/tinymce/plugins/quickbars/plugin.js +1 -1
  30. data/vendor/assets/javascripts/tinymce/plugins/save/plugin.js +1 -1
  31. data/vendor/assets/javascripts/tinymce/plugins/searchreplace/plugin.js +1 -1
  32. data/vendor/assets/javascripts/tinymce/plugins/table/plugin.js +1 -1
  33. data/vendor/assets/javascripts/tinymce/plugins/visualblocks/plugin.js +1 -1
  34. data/vendor/assets/javascripts/tinymce/plugins/visualchars/plugin.js +1 -1
  35. data/vendor/assets/javascripts/tinymce/plugins/wordcount/plugin.js +1 -1
  36. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.css +1 -1
  37. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.css +1 -1
  38. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.js +1 -1
  39. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.min.css +1 -1
  40. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.js +1 -1
  41. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.min.css +1 -1
  42. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.css +1 -1
  43. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.js +1 -1
  44. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.min.css +1 -1
  45. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.css +1 -1
  46. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.css +1 -1
  47. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.js +1 -1
  48. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.min.css +1 -1
  49. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.js +1 -1
  50. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.min.css +1 -1
  51. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.css +1 -1
  52. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.js +1 -1
  53. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.min.css +1 -1
  54. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.css +1 -1
  55. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.css +1 -1
  56. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.js +1 -1
  57. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.min.css +1 -1
  58. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.js +1 -1
  59. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.min.css +1 -1
  60. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.css +1 -1
  61. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.js +1 -1
  62. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.min.css +1 -1
  63. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.css +1 -1
  64. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.css +1 -1
  65. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.js +1 -1
  66. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.min.css +1 -1
  67. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.js +1 -1
  68. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.min.css +1 -1
  69. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.css +1 -1
  70. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.js +1 -1
  71. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.min.css +1 -1
  72. data/vendor/assets/javascripts/tinymce/themes/silver/theme.js +1 -1
  73. data/vendor/assets/javascripts/tinymce/tinymce.d.ts +6 -0
  74. data/vendor/assets/javascripts/tinymce/tinymce.js +3 -3
  75. metadata +2 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TinyMCE version 8.3.2 (2026-01-14)
2
+ * TinyMCE version 8.4.0 (2026-03-31)
3
3
  */
4
4
 
5
5
  (function () {
@@ -3467,7 +3467,7 @@
3467
3467
  const isDetails = matchNodeName$1('details');
3468
3468
  const isSummary$1 = matchNodeName$1('summary');
3469
3469
  const ucVideoNodeName = 'uc-video';
3470
- const isUcVideo = (el) => el.nodeName.toLowerCase() === ucVideoNodeName;
3470
+ const isUcVideo = matchNodeName$1(ucVideoNodeName);
3471
3471
 
3472
3472
  const defaultOptionValues = {
3473
3473
  skipBogus: true,
@@ -5630,7 +5630,9 @@
5630
5630
  compress('border', '-style');
5631
5631
  compress('padding', '');
5632
5632
  compress('margin', '');
5633
- compress2('border', 'border-width', 'border-style', 'border-color');
5633
+ if (!/(#.* rgb(a?)\(.*)|(rgb(a?)\(.*\) )/.test(styles['border-color'])) {
5634
+ compress2('border', 'border-width', 'border-style', 'border-color');
5635
+ }
5634
5636
  // Remove pointless border, IE produces these
5635
5637
  if (styles.border === 'medium none') {
5636
5638
  delete styles.border;
@@ -5705,7 +5707,8 @@
5705
5707
  webkitMovementX: true,
5706
5708
  webkitMovementY: true,
5707
5709
  keyIdentifier: true,
5708
- mozPressure: true
5710
+ mozPressure: true,
5711
+ mozInputSource: true,
5709
5712
  };
5710
5713
  // Note: We can't rely on `instanceof` here as it won't work if the event was fired from another window.
5711
5714
  // Additionally, the constructor name might be `MouseEvent` or similar so we can't rely on the constructor name.
@@ -10605,6 +10608,10 @@
10605
10608
  },
10606
10609
  default: (_ctx) => []
10607
10610
  });
10611
+ registerOption('allow_noneditable', {
10612
+ processor: 'boolean',
10613
+ default: true
10614
+ });
10608
10615
  registerOption('noneditable_class', {
10609
10616
  processor: 'string',
10610
10617
  default: 'mceNonEditable'
@@ -10688,6 +10695,9 @@
10688
10695
  processor: 'string',
10689
10696
  default: 'Anonymous'
10690
10697
  });
10698
+ registerOption('content_id', {
10699
+ processor: 'string',
10700
+ });
10691
10701
  registerOption('fetch_users', {
10692
10702
  processor: (value) => {
10693
10703
  if (value === undefined) {
@@ -10837,6 +10847,7 @@
10837
10847
  const shouldAllowHtmlDataUrls = option('allow_html_data_urls');
10838
10848
  const getTextPatterns = option('text_patterns');
10839
10849
  const getTextPatternsLookup = option('text_patterns_lookup');
10850
+ const shouldAllowNonEditable = option('allow_noneditable');
10840
10851
  const getNonEditableClass = option('noneditable_class');
10841
10852
  const getEditableClass = option('editable_class');
10842
10853
  const getNonEditableRegExps = option('noneditable_regexp');
@@ -18468,7 +18479,7 @@
18468
18479
  }
18469
18480
  };
18470
18481
 
18471
- /*! @license DOMPurify 3.2.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.6/LICENSE */
18482
+ /*! @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 */
18472
18483
 
18473
18484
  const {
18474
18485
  entries,
@@ -18497,12 +18508,18 @@
18497
18508
  };
18498
18509
  }
18499
18510
  if (!apply) {
18500
- apply = function apply(fun, thisValue, args) {
18501
- return fun.apply(thisValue, args);
18511
+ apply = function apply(func, thisArg) {
18512
+ for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
18513
+ args[_key - 2] = arguments[_key];
18514
+ }
18515
+ return func.apply(thisArg, args);
18502
18516
  };
18503
18517
  }
18504
18518
  if (!construct) {
18505
- construct = function construct(Func, args) {
18519
+ construct = function construct(Func) {
18520
+ for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
18521
+ args[_key2 - 1] = arguments[_key2];
18522
+ }
18506
18523
  return new Func(...args);
18507
18524
  };
18508
18525
  }
@@ -18531,8 +18548,8 @@
18531
18548
  if (thisArg instanceof RegExp) {
18532
18549
  thisArg.lastIndex = 0;
18533
18550
  }
18534
- for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
18535
- args[_key - 1] = arguments[_key];
18551
+ for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
18552
+ args[_key3 - 1] = arguments[_key3];
18536
18553
  }
18537
18554
  return apply(func, thisArg, args);
18538
18555
  };
@@ -18543,12 +18560,12 @@
18543
18560
  * @param func - The constructor function to be wrapped and called.
18544
18561
  * @returns A new function that constructs an instance of the given constructor function with the provided arguments.
18545
18562
  */
18546
- function unconstruct(func) {
18563
+ function unconstruct(Func) {
18547
18564
  return function () {
18548
- for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
18549
- args[_key2] = arguments[_key2];
18565
+ for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
18566
+ args[_key4] = arguments[_key4];
18550
18567
  }
18551
- return construct(func, args);
18568
+ return construct(Func, args);
18552
18569
  };
18553
18570
  }
18554
18571
  /**
@@ -18647,8 +18664,8 @@
18647
18664
  return fallbackValue;
18648
18665
  }
18649
18666
 
18650
- 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', 'section', 'select', 'shadow', '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']);
18651
- const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
18667
+ 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']);
18668
+ 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']);
18652
18669
  const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
18653
18670
  // List of SVG elements that are disallowed by default.
18654
18671
  // We still need to know them so that we can do namespace
@@ -18661,8 +18678,8 @@
18661
18678
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
18662
18679
  const text = freeze(['#text']);
18663
18680
 
18664
- 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', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', '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', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
18665
- 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', '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']);
18681
+ 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']);
18682
+ 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']);
18666
18683
  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']);
18667
18684
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
18668
18685
 
@@ -18769,7 +18786,7 @@
18769
18786
  function createDOMPurify() {
18770
18787
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
18771
18788
  const DOMPurify = root => createDOMPurify(root);
18772
- DOMPurify.version = '3.2.6';
18789
+ DOMPurify.version = '3.3.2';
18773
18790
  DOMPurify.removed = [];
18774
18791
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
18775
18792
  // Not running in a browser, provide a factory function
@@ -18880,6 +18897,21 @@
18880
18897
  let FORBID_TAGS = null;
18881
18898
  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
18882
18899
  let FORBID_ATTR = null;
18900
+ /* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */
18901
+ const EXTRA_ELEMENT_HANDLING = Object.seal(create$7(null, {
18902
+ tagCheck: {
18903
+ writable: true,
18904
+ configurable: false,
18905
+ enumerable: true,
18906
+ value: null
18907
+ },
18908
+ attributeCheck: {
18909
+ writable: true,
18910
+ configurable: false,
18911
+ enumerable: true,
18912
+ value: null
18913
+ }
18914
+ }));
18883
18915
  /* Decide if ARIA attributes are okay */
18884
18916
  let ALLOW_ARIA_ATTR = true;
18885
18917
  /* Decide if custom data attributes are okay */
@@ -19049,7 +19081,7 @@
19049
19081
  /* Parse profile info */
19050
19082
  if (USE_PROFILES) {
19051
19083
  ALLOWED_TAGS = addToSet({}, text);
19052
- ALLOWED_ATTR = [];
19084
+ ALLOWED_ATTR = create$7(null);
19053
19085
  if (USE_PROFILES.html === true) {
19054
19086
  addToSet(ALLOWED_TAGS, html$1);
19055
19087
  addToSet(ALLOWED_ATTR, html);
@@ -19070,18 +19102,33 @@
19070
19102
  addToSet(ALLOWED_ATTR, xml);
19071
19103
  }
19072
19104
  }
19105
+ /* Prevent function-based ADD_ATTR / ADD_TAGS from leaking across calls */
19106
+ if (!objectHasOwnProperty(cfg, 'ADD_TAGS')) {
19107
+ EXTRA_ELEMENT_HANDLING.tagCheck = null;
19108
+ }
19109
+ if (!objectHasOwnProperty(cfg, 'ADD_ATTR')) {
19110
+ EXTRA_ELEMENT_HANDLING.attributeCheck = null;
19111
+ }
19073
19112
  /* Merge configuration parameters */
19074
19113
  if (cfg.ADD_TAGS) {
19075
- if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
19076
- ALLOWED_TAGS = clone(ALLOWED_TAGS);
19114
+ if (typeof cfg.ADD_TAGS === 'function') {
19115
+ EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
19116
+ } else {
19117
+ if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
19118
+ ALLOWED_TAGS = clone(ALLOWED_TAGS);
19119
+ }
19120
+ addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
19077
19121
  }
19078
- addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
19079
19122
  }
19080
19123
  if (cfg.ADD_ATTR) {
19081
- if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
19082
- ALLOWED_ATTR = clone(ALLOWED_ATTR);
19124
+ if (typeof cfg.ADD_ATTR === 'function') {
19125
+ EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
19126
+ } else {
19127
+ if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
19128
+ ALLOWED_ATTR = clone(ALLOWED_ATTR);
19129
+ }
19130
+ addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
19083
19131
  }
19084
- addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
19085
19132
  }
19086
19133
  if (cfg.ADD_URI_SAFE_ATTR) {
19087
19134
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
@@ -19092,6 +19139,12 @@
19092
19139
  }
19093
19140
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
19094
19141
  }
19142
+ if (cfg.ADD_FORBID_CONTENTS) {
19143
+ if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
19144
+ FORBID_CONTENTS = clone(FORBID_CONTENTS);
19145
+ }
19146
+ addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);
19147
+ }
19095
19148
  /* Add #text in case KEEP_CONTENT is set to true */
19096
19149
  if (KEEP_CONTENT) {
19097
19150
  ALLOWED_TAGS['#text'] = true;
@@ -19389,7 +19442,7 @@
19389
19442
  return true;
19390
19443
  }
19391
19444
  /* Remove element if anything forbids its presence */
19392
- if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
19445
+ if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {
19393
19446
  /* Check if we have a custom element to handle */
19394
19447
  if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
19395
19448
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
@@ -19453,6 +19506,10 @@
19453
19506
  */
19454
19507
  // eslint-disable-next-line complexity
19455
19508
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
19509
+ /* FORBID_ATTR must always win, even if ADD_ATTR predicate would allow it */
19510
+ if (FORBID_ATTR[lcName]) {
19511
+ return false;
19512
+ }
19456
19513
  /* Make sure attribute cannot clobber */
19457
19514
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
19458
19515
  return false;
@@ -19461,12 +19518,12 @@
19461
19518
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
19462
19519
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
19463
19520
  We don't need to check the value; it's always URI safe. */
19464
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
19521
+ 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]) {
19465
19522
  if (
19466
19523
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
19467
19524
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
19468
19525
  // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
19469
- _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) ||
19526
+ _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName, lcTag)) ||
19470
19527
  // Alternative, second condition checks if it's an `is`-attribute, AND
19471
19528
  // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
19472
19529
  lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {
@@ -19545,7 +19602,12 @@
19545
19602
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
19546
19603
  }
19547
19604
  /* Work around a security issue with comments inside attributes */
19548
- if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
19605
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
19606
+ _removeAttribute(name, currentNode);
19607
+ continue;
19608
+ }
19609
+ /* Make sure we cannot easily use animated hrefs, even if animations are allowed */
19610
+ if (lcName === 'attributename' && stringMatch(value, 'href')) {
19549
19611
  _removeAttribute(name, currentNode);
19550
19612
  continue;
19551
19613
  }
@@ -25203,8 +25265,8 @@
25203
25265
  };
25204
25266
 
25205
25267
  const isContentCssSkinName = (url) => /^[a-z0-9\-]+$/i.test(url);
25206
- const toContentSkinResourceName = (url) => 'content/' + url + '/content.css';
25207
- const isBundledCssSkinName = (url) => tinymce.Resource.has(toContentSkinResourceName(url));
25268
+ const toContentSkinResourceName = (name) => 'content/' + name + '/content.css';
25269
+ const isBundledCssSkinName = (name) => tinymce.Resource.has(toContentSkinResourceName(name));
25208
25270
  const getContentCssUrls = (editor) => {
25209
25271
  return transformToUrls(editor, getContentCss(editor));
25210
25272
  };
@@ -25217,7 +25279,7 @@
25217
25279
  const contentCssFile = `content${suffix}.css`;
25218
25280
  return map$3(cssLinks, (url) => {
25219
25281
  if (isBundledCssSkinName(url)) {
25220
- return url;
25282
+ return toContentSkinResourceName(url);
25221
25283
  }
25222
25284
  else if (isContentCssSkinName(url) && !editor.inline) {
25223
25285
  return `${skinUrl}/${url}/${contentCssFile}`;
@@ -25405,7 +25467,7 @@
25405
25467
  }
25406
25468
  };
25407
25469
  const toBlobInfo = (o) => {
25408
- if (!o.blob || !o.base64) {
25470
+ if (isNullable(o.blob) || isNullable(o.base64) || (o.base64 === '' && !o.allowEmptyFile)) {
25409
25471
  throw new Error('blob and base64 representations of the image are required for BlobInfo to be created');
25410
25472
  }
25411
25473
  const id = o.id || uuid('blobid');
@@ -26952,6 +27014,53 @@
26952
27014
  }
26953
27015
  };
26954
27016
 
27017
+ const createAndFireInputEvent = (eventType) => (editor, inputType, specifics = {}) => {
27018
+ const target = editor.getBody();
27019
+ const overrides = {
27020
+ bubbles: true,
27021
+ composed: true,
27022
+ data: null,
27023
+ isComposing: false,
27024
+ detail: 0,
27025
+ view: null,
27026
+ target,
27027
+ currentTarget: target,
27028
+ eventPhase: Event.AT_TARGET,
27029
+ originalTarget: target,
27030
+ explicitOriginalTarget: target,
27031
+ isTrusted: false,
27032
+ srcElement: target,
27033
+ cancelable: false,
27034
+ preventDefault: noop,
27035
+ inputType
27036
+ };
27037
+ const input = clone$2(new InputEvent(eventType));
27038
+ return editor.dispatch(eventType, { ...input, ...overrides, ...specifics });
27039
+ };
27040
+ const fireInputEvent = createAndFireInputEvent('input');
27041
+ const fireBeforeInputEvent = createAndFireInputEvent('beforeinput');
27042
+
27043
+ const symulateDelete = (editor, isForward, deleteFun) => {
27044
+ // Some delete actions may prevent the input event from being fired. If we do not detect it, we fire it ourselves.
27045
+ let shouldFireInput = true;
27046
+ const inputHandler = () => shouldFireInput = false;
27047
+ const beforeInputEvent = fireBeforeInputEvent(editor, isForward ? 'deleteContentForward' : 'deleteContentBackward');
27048
+ if (beforeInputEvent.isDefaultPrevented()) {
27049
+ return false;
27050
+ }
27051
+ editor.on('input', inputHandler);
27052
+ try {
27053
+ deleteFun();
27054
+ }
27055
+ finally {
27056
+ editor.off('input', inputHandler);
27057
+ }
27058
+ if (shouldFireInput) {
27059
+ editor.dispatch('input');
27060
+ }
27061
+ return true;
27062
+ };
27063
+
26955
27064
  const matchNodeName = (name) => (node) => isNonNullable(node) && node.nodeName.toLowerCase() === name;
26956
27065
  const matchNodeNames = (regex) => (node) => isNonNullable(node) && regex.test(node.nodeName);
26957
27066
  const isTextNode$1 = (node) => isNonNullable(node) && node.nodeType === 3;
@@ -27049,7 +27158,7 @@
27049
27158
  const listSelector = listNames.join(',');
27050
27159
  const getParentList = (editor, node) => {
27051
27160
  const selectionStart = node || editor.selection.getStart(true);
27052
- return editor.dom.getParent(selectionStart, listSelector, getClosestListHost(editor, selectionStart));
27161
+ return editor.dom.getParent(selectionStart, listSelector, getClosestListHost(editor, selectionStart, editor.selection.isCollapsed()));
27053
27162
  };
27054
27163
  const isParentListSelected = (parentList, selectedBlocks) => isNonNullable(parentList) && selectedBlocks.length === 1 && selectedBlocks[0] === parentList;
27055
27164
  const findSubLists = (parentList) => filter$5(parentList.querySelectorAll(listSelector), isListNode);
@@ -27065,16 +27174,16 @@
27065
27174
  });
27066
27175
  }
27067
27176
  };
27068
- const findParentListItemsNodes = (editor, elms) => {
27177
+ const findParentListItemsNodes = (editor, elms, isCollapsed) => {
27069
27178
  const listItemsElms = Tools.map(elms, (elm) => {
27070
- const parentLi = editor.dom.getParent(elm, 'li,dd,dt', getClosestListHost(editor, elm));
27179
+ const parentLi = editor.dom.getParent(elm, 'li,dd,dt', getClosestListHost(editor, elm, isCollapsed));
27071
27180
  return parentLi ? parentLi : elm;
27072
27181
  });
27073
27182
  return unique$1(listItemsElms);
27074
27183
  };
27075
27184
  const getSelectedListItems = (editor) => {
27076
27185
  const selectedBlocks = editor.selection.getSelectedBlocks();
27077
- return filter$5(findParentListItemsNodes(editor, selectedBlocks), isListItemNode);
27186
+ return filter$5(findParentListItemsNodes(editor, selectedBlocks, editor.selection.isCollapsed()), isListItemNode);
27078
27187
  };
27079
27188
  const getSelectedDlItems = (editor) => filter$5(getSelectedListItems(editor), isDlItemNode);
27080
27189
  const getClosestEditingHost = (editor, elm) => {
@@ -27082,17 +27191,17 @@
27082
27191
  return parentTableCell.length > 0 ? parentTableCell[0] : editor.getBody();
27083
27192
  };
27084
27193
  const isListHost = (schema, node) => !isListNode(node) && !isListItemNode(node) && exists(listNames, (listName) => schema.isValidChild(node.nodeName, listName));
27085
- const getClosestListHost = (editor, elm) => {
27194
+ const getClosestListHost = (editor, elm, isCollapsed) => {
27086
27195
  const parentBlocks = editor.dom.getParents(elm, editor.dom.isBlock);
27087
27196
  const isNotForcedRootBlock = (elm) => elm.nodeName.toLowerCase() !== getForcedRootBlock(editor);
27088
- const parentBlock = find$2(parentBlocks, (elm) => isNotForcedRootBlock(elm) && isListHost(editor.schema, elm));
27197
+ const parentBlock = find$2(parentBlocks, (elm) => (!isCollapsed || isNotForcedRootBlock(elm)) && isListHost(editor.schema, elm));
27089
27198
  return parentBlock.getOr(editor.getBody());
27090
27199
  };
27091
27200
  const isListInsideAnLiWithFirstAndLastNotListElement = (list) => parent(list).exists((parent) => isListItemNode(parent.dom)
27092
27201
  && firstChild(parent).exists((firstChild) => !isListNode(firstChild.dom))
27093
27202
  && lastChild(parent).exists((lastChild) => !isListNode(lastChild.dom)));
27094
27203
  const findLastParentListNode = (editor, elm) => {
27095
- const parentLists = editor.dom.getParents(elm, 'ol,ul', getClosestListHost(editor, elm));
27204
+ const parentLists = editor.dom.getParents(elm, 'ol,ul', getClosestListHost(editor, elm, true));
27096
27205
  return last$2(parentLists);
27097
27206
  };
27098
27207
  const getSelectedLists = (editor) => {
@@ -27102,7 +27211,7 @@
27102
27211
  };
27103
27212
  const getParentLists = (editor) => {
27104
27213
  const elm = editor.selection.getStart();
27105
- return editor.dom.getParents(elm, 'ol,ul', getClosestListHost(editor, elm));
27214
+ return editor.dom.getParents(elm, 'ol,ul', getClosestListHost(editor, elm, editor.selection.isCollapsed()));
27106
27215
  };
27107
27216
  const getSelectedListRoots = (editor) => {
27108
27217
  const selectedLists = getSelectedLists(editor);
@@ -27715,7 +27824,7 @@
27715
27824
  const applyList = (editor, listName, detail) => {
27716
27825
  const rng = editor.selection.getRng();
27717
27826
  let listItemName = 'LI';
27718
- const root = getClosestListHost(editor, getRootSearchStart(editor, rng));
27827
+ const root = getClosestListHost(editor, getRootSearchStart(editor, rng), rng.collapsed);
27719
27828
  const dom = editor.dom;
27720
27829
  if (dom.getContentEditable(editor.selection.getNode()) === 'false') {
27721
27830
  return;
@@ -28095,19 +28204,12 @@
28095
28204
  const startListParent = editor.dom.getParent(selectionStartElm, 'LI,DT,DD', root);
28096
28205
  return isNonNullable(startListParent) || getSelectedListItems(editor).length > 0;
28097
28206
  };
28098
- const backspaceDeleteRange$1 = (editor) => {
28207
+ const backspaceDeleteRange$1 = (editor, isForward) => {
28099
28208
  if (hasListSelection(editor)) {
28100
28209
  editor.undoManager.transact(() => {
28101
- // Some delete actions may prevent the input event from being fired. If we do not detect it, we fire it ourselves.
28102
- let shouldFireInput = true;
28103
- const inputHandler = () => shouldFireInput = false;
28104
- editor.on('input', inputHandler);
28105
- editor.execCommand('Delete');
28106
- editor.off('input', inputHandler);
28107
- if (shouldFireInput) {
28108
- editor.dispatch('input');
28210
+ if (symulateDelete(editor, isForward, () => editor.execCommand('Delete'))) {
28211
+ normalizeLists(editor.dom, editor.getBody());
28109
28212
  }
28110
- normalizeLists(editor.dom, editor.getBody());
28111
28213
  });
28112
28214
  return true;
28113
28215
  }
@@ -28116,7 +28218,7 @@
28116
28218
  const backspaceDelete$c = (editor, isForward) => {
28117
28219
  const selection = editor.selection;
28118
28220
  return !isWithinNonEditableList$1(editor, selection.getNode()) && (selection.isCollapsed() ?
28119
- backspaceDeleteCaret$1(editor, isForward) : backspaceDeleteRange$1(editor));
28221
+ backspaceDeleteCaret$1(editor, isForward) : backspaceDeleteRange$1(editor, isForward));
28120
28222
  };
28121
28223
 
28122
28224
  const blockPosition = (block, position) => ({
@@ -31449,32 +31551,6 @@
31449
31551
  const backspaceDelete = (editor, forward, granularity) => shouldPreventDeleteAction(editor, forward, granularity) || isSafari && handleDeleteActionSafari(editor, forward, granularity)
31450
31552
  ? Optional.some(noop) : Optional.none();
31451
31553
 
31452
- const createAndFireInputEvent = (eventType) => (editor, inputType, specifics = {}) => {
31453
- const target = editor.getBody();
31454
- const overrides = {
31455
- bubbles: true,
31456
- composed: true,
31457
- data: null,
31458
- isComposing: false,
31459
- detail: 0,
31460
- view: null,
31461
- target,
31462
- currentTarget: target,
31463
- eventPhase: Event.AT_TARGET,
31464
- originalTarget: target,
31465
- explicitOriginalTarget: target,
31466
- isTrusted: false,
31467
- srcElement: target,
31468
- cancelable: false,
31469
- preventDefault: noop,
31470
- inputType
31471
- };
31472
- const input = clone$2(new InputEvent(eventType));
31473
- return editor.dispatch(eventType, { ...input, ...overrides, ...specifics });
31474
- };
31475
- const fireInputEvent = createAndFireInputEvent('input');
31476
- const fireBeforeInputEvent = createAndFireInputEvent('beforeinput');
31477
-
31478
31554
  const platform$2 = detect$1();
31479
31555
  const os = platform$2.os;
31480
31556
  const isMacOSOriOS = os.isMacOS() || os.isiOS();
@@ -31563,19 +31639,21 @@
31563
31639
  // track backspace keydown state for emulating Meta + Backspace keyup detection on macOS
31564
31640
  let isBackspaceKeydown = false;
31565
31641
  let formatNodes = [];
31566
- editor.on('keydown', (evt) => {
31567
- isBackspaceKeydown = evt.keyCode === VK.BACKSPACE;
31568
- formatNodes = getFormatNodesAtStart(editor);
31569
- if (!evt.isDefaultPrevented()) {
31570
- executeKeydownOverride$3(editor, caret, evt);
31571
- }
31572
- });
31573
- editor.on('keyup', (evt) => {
31574
- if (!evt.isDefaultPrevented()) {
31575
- executeKeyupOverride(editor, evt, isBackspaceKeydown, formatNodes);
31576
- formatNodes.length = 0;
31577
- }
31578
- isBackspaceKeydown = false;
31642
+ editor.on('init', () => {
31643
+ editor.on('keydown', (evt) => {
31644
+ isBackspaceKeydown = evt.keyCode === VK.BACKSPACE;
31645
+ formatNodes = getFormatNodesAtStart(editor);
31646
+ if (!evt.isDefaultPrevented()) {
31647
+ executeKeydownOverride$3(editor, caret, evt);
31648
+ }
31649
+ });
31650
+ editor.on('keyup', (evt) => {
31651
+ if (!evt.isDefaultPrevented()) {
31652
+ executeKeyupOverride(editor, evt, isBackspaceKeydown, formatNodes);
31653
+ formatNodes.length = 0;
31654
+ }
31655
+ isBackspaceKeydown = false;
31656
+ });
31579
31657
  });
31580
31658
  };
31581
31659
 
@@ -31613,16 +31691,15 @@
31613
31691
  if (!root) {
31614
31692
  return;
31615
31693
  }
31616
- if (/^(LI|DT|DD)$/.test(root.nodeName)) {
31617
- const isList = (e) => /^(ul|ol|dl)$/.test(name(e));
31618
- const findFirstList = (e) => isList(e) ? Optional.from(e) : descendant$2(e, isList);
31694
+ if (isListItem$2(SugarElement.fromDom(root))) {
31695
+ const findFirstList = (e) => isList$1(e) ? Optional.from(e) : descendant$2(e, isList$1);
31619
31696
  const isEmpty = (e) => dom.isEmpty(e.dom);
31620
31697
  firstNonWhiteSpaceNodeSibling(root.firstChild).each((firstChild) => {
31621
31698
  findFirstList(firstChild).fold(() => {
31622
31699
  if (isEmpty(firstChild)) {
31623
31700
  const element = toLeaf$1(firstChild, 0).element;
31624
31701
  if (isElement$8(element) && !isBr$6(element)) {
31625
- append$1(element, SugarElement.fromText(nbsp));
31702
+ append$1(element, SugarElement.fromHtml('<br data-mce-bogus="1" />'));
31626
31703
  }
31627
31704
  }
31628
31705
  }, (firstList) => {
@@ -32154,6 +32231,16 @@
32154
32231
  }
32155
32232
  return true;
32156
32233
  };
32234
+ const isInsideLiBeforeAList = (newBlock) => {
32235
+ const nextSibling$1 = firstChild(newBlock).bind(nextSibling);
32236
+ return isListItem$2(newBlock) && nextSibling$1.exists(isList$1);
32237
+ };
32238
+ const trimEmptySpacesInLeftLeaf = (newBlock) => {
32239
+ const leaf = toLeaf$1(SugarElement.fromDom(newBlock), 0).element;
32240
+ if (isText$c(leaf) && dom.isEmpty(leaf.dom)) {
32241
+ leaf.dom.remove();
32242
+ }
32243
+ };
32157
32244
  const insertNewBlockAfter = () => {
32158
32245
  let block;
32159
32246
  // If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup
@@ -32289,8 +32376,13 @@
32289
32376
  else {
32290
32377
  dom.insertAfter(fragment, parentBlock);
32291
32378
  }
32292
- trimInlineElementsOnLeftSideOfBlock(dom, nonEmptyElementsMap, newBlock);
32293
- addBrToBlockIfNeeded(dom, parentBlock);
32379
+ if (!isInsideLiBeforeAList(SugarElement.fromDom(newBlock))) {
32380
+ trimInlineElementsOnLeftSideOfBlock(dom, nonEmptyElementsMap, newBlock);
32381
+ addBrToBlockIfNeeded(dom, parentBlock);
32382
+ }
32383
+ else {
32384
+ trimEmptySpacesInLeftLeaf(newBlock);
32385
+ }
32294
32386
  if (dom.isEmpty(parentBlock)) {
32295
32387
  emptyBlock(parentBlock);
32296
32388
  }
@@ -32910,17 +33002,23 @@
32910
33002
  };
32911
33003
 
32912
33004
  const setup$e = (editor) => {
32913
- editor.on('keydown', (e) => {
32914
- if (e.keyCode === VK.BACKSPACE) {
32915
- if (backspaceDelete$c(editor, false)) {
32916
- e.preventDefault();
33005
+ editor.on('init', () => {
33006
+ // Init is done after the editor's setup. This places our keydown after any keydowns registered in the setup function, so they can do default prevent.
33007
+ editor.on('keydown', (e) => {
33008
+ if (e.defaultPrevented) {
33009
+ return;
32917
33010
  }
32918
- }
32919
- else if (e.keyCode === VK.DELETE) {
32920
- if (backspaceDelete$c(editor, true)) {
32921
- e.preventDefault();
33011
+ if (e.keyCode === VK.BACKSPACE) {
33012
+ if (backspaceDelete$c(editor, false)) {
33013
+ e.preventDefault();
33014
+ }
32922
33015
  }
32923
- }
33016
+ else if (e.keyCode === VK.DELETE) {
33017
+ if (backspaceDelete$c(editor, true)) {
33018
+ e.preventDefault();
33019
+ }
33020
+ }
33021
+ });
32924
33022
  });
32925
33023
  };
32926
33024
 
@@ -35780,14 +35878,14 @@
35780
35878
  }
35781
35879
  // Manually empty the editor
35782
35880
  e.preventDefault();
35783
- editor.setContent('');
35784
- if (body.firstChild && dom.isBlock(body.firstChild)) {
35785
- editor.selection.setCursorLocation(body.firstChild, 0);
35786
- }
35787
- else {
35788
- editor.selection.setCursorLocation(body, 0);
35881
+ if (symulateDelete(editor, keyCode === DELETE, () => editor.setContent(''))) {
35882
+ if (body.firstChild && dom.isBlock(body.firstChild)) {
35883
+ editor.selection.setCursorLocation(body.firstChild, 0);
35884
+ }
35885
+ else {
35886
+ editor.selection.setCursorLocation(body, 0);
35887
+ }
35789
35888
  }
35790
- editor.nodeChanged();
35791
35889
  }
35792
35890
  });
35793
35891
  };
@@ -36689,11 +36787,11 @@
36689
36787
  };
36690
36788
  const getStyleSheetLoader$1 = (editor) => editor.inline ? editor.ui.styleSheetLoader : editor.dom.styleSheetLoader;
36691
36789
  const makeStylesheetLoadingPromises = (editor, css, framedFonts) => {
36692
- const { pass: bundledCss, fail: normalCss } = partition$2(css, (name) => tinymce.Resource.has(toContentSkinResourceName(name)));
36693
- const bundledPromises = bundledCss.map((url) => {
36694
- const css = tinymce.Resource.get(toContentSkinResourceName(url));
36790
+ const { pass: bundledCss, fail: normalCss } = partition$2(css, (key) => tinymce.Resource.has(key));
36791
+ const bundledPromises = bundledCss.map((key) => {
36792
+ const css = tinymce.Resource.get(key);
36695
36793
  if (isString(css)) {
36696
- return Promise.resolve(getStyleSheetLoader$1(editor).loadRawCss(url, css));
36794
+ return Promise.resolve(getStyleSheetLoader$1(editor).loadRawCss(key, css));
36697
36795
  }
36698
36796
  return Promise.resolve();
36699
36797
  });
@@ -36853,7 +36951,9 @@
36853
36951
  setup$b(editor);
36854
36952
  setup$u(editor);
36855
36953
  setup$6(editor);
36856
- setup$s(editor);
36954
+ if (shouldAllowNonEditable(editor)) {
36955
+ setup$s(editor);
36956
+ }
36857
36957
  if (!isRtc(editor)) {
36858
36958
  setup$5(editor);
36859
36959
  setup$1(editor);
@@ -40581,14 +40681,14 @@
40581
40681
  * @property minorVersion
40582
40682
  * @type String
40583
40683
  */
40584
- minorVersion: '3.2',
40684
+ minorVersion: '4.0',
40585
40685
  /**
40586
40686
  * Release date of TinyMCE build.
40587
40687
  *
40588
40688
  * @property releaseDate
40589
40689
  * @type String
40590
40690
  */
40591
- releaseDate: '2026-01-14',
40691
+ releaseDate: '2026-03-31',
40592
40692
  /**
40593
40693
  * Collection of language pack data.
40594
40694
  *