tinymce-rails 7.7.0 → 7.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/source/tinymce/tinymce.js +144 -359
  3. data/lib/tinymce/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/tinymce/models/dom/model.js +1 -1
  5. data/vendor/assets/javascripts/tinymce/plugins/accordion/plugin.js +1 -1
  6. data/vendor/assets/javascripts/tinymce/plugins/advlist/plugin.js +1 -1
  7. data/vendor/assets/javascripts/tinymce/plugins/anchor/plugin.js +1 -1
  8. data/vendor/assets/javascripts/tinymce/plugins/autolink/plugin.js +1 -1
  9. data/vendor/assets/javascripts/tinymce/plugins/autoresize/plugin.js +1 -1
  10. data/vendor/assets/javascripts/tinymce/plugins/autosave/plugin.js +1 -1
  11. data/vendor/assets/javascripts/tinymce/plugins/charmap/plugin.js +1 -1
  12. data/vendor/assets/javascripts/tinymce/plugins/code/plugin.js +1 -1
  13. data/vendor/assets/javascripts/tinymce/plugins/codesample/plugin.js +1 -1
  14. data/vendor/assets/javascripts/tinymce/plugins/directionality/plugin.js +1 -1
  15. data/vendor/assets/javascripts/tinymce/plugins/emoticons/plugin.js +1 -1
  16. data/vendor/assets/javascripts/tinymce/plugins/fullscreen/plugin.js +1 -1
  17. data/vendor/assets/javascripts/tinymce/plugins/help/plugin.js +1 -1
  18. data/vendor/assets/javascripts/tinymce/plugins/image/plugin.js +1 -1
  19. data/vendor/assets/javascripts/tinymce/plugins/importcss/plugin.js +1 -1
  20. data/vendor/assets/javascripts/tinymce/plugins/insertdatetime/plugin.js +1 -1
  21. data/vendor/assets/javascripts/tinymce/plugins/link/plugin.js +1 -1
  22. data/vendor/assets/javascripts/tinymce/plugins/lists/plugin.js +1 -1
  23. data/vendor/assets/javascripts/tinymce/plugins/media/plugin.js +1 -1
  24. data/vendor/assets/javascripts/tinymce/plugins/nonbreaking/plugin.js +1 -1
  25. data/vendor/assets/javascripts/tinymce/plugins/pagebreak/plugin.js +1 -1
  26. data/vendor/assets/javascripts/tinymce/plugins/preview/plugin.js +1 -1
  27. data/vendor/assets/javascripts/tinymce/plugins/quickbars/plugin.js +1 -1
  28. data/vendor/assets/javascripts/tinymce/plugins/save/plugin.js +1 -1
  29. data/vendor/assets/javascripts/tinymce/plugins/searchreplace/plugin.js +1 -1
  30. data/vendor/assets/javascripts/tinymce/plugins/table/plugin.js +1 -1
  31. data/vendor/assets/javascripts/tinymce/plugins/visualblocks/plugin.js +1 -1
  32. data/vendor/assets/javascripts/tinymce/plugins/visualchars/plugin.js +1 -1
  33. data/vendor/assets/javascripts/tinymce/plugins/wordcount/plugin.js +1 -1
  34. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.css +1 -3
  35. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.css +1 -3
  36. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.js +1 -1
  37. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.min.css +1 -3
  38. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.js +1 -1
  39. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.min.css +1 -3
  40. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.css +1 -3
  41. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.css +1 -3
  42. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.js +1 -1
  43. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.min.css +1 -3
  44. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.js +1 -1
  45. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.min.css +1 -3
  46. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.css +1 -3
  47. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.css +1 -3
  48. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.js +1 -1
  49. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.min.css +1 -3
  50. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.js +1 -1
  51. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.min.css +1 -3
  52. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.css +1 -3
  53. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.css +1 -3
  54. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.js +1 -1
  55. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.min.css +1 -3
  56. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.js +1 -1
  57. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.min.css +1 -3
  58. data/vendor/assets/javascripts/tinymce/themes/silver/theme.js +126 -180
  59. data/vendor/assets/javascripts/tinymce/tinymce.js +122 -176
  60. metadata +2 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TinyMCE version 7.7.0 (TBD)
2
+ * TinyMCE version 7.7.1 (2025-03-05)
3
3
  */
4
4
 
5
5
  (function () {
@@ -15314,7 +15314,7 @@
15314
15314
  }
15315
15315
  };
15316
15316
 
15317
- /*! @license DOMPurify 3.1.7 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.1.7/LICENSE */
15317
+ /*! @license DOMPurify 3.2.4 | (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.4/LICENSE */
15318
15318
 
15319
15319
  const {
15320
15320
  entries,
@@ -15353,8 +15353,10 @@
15353
15353
  };
15354
15354
  }
15355
15355
  const arrayForEach = unapply(Array.prototype.forEach);
15356
+ const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
15356
15357
  const arrayPop = unapply(Array.prototype.pop);
15357
15358
  const arrayPush = unapply(Array.prototype.push);
15359
+ const arraySplice = unapply(Array.prototype.splice);
15358
15360
  const stringToLowerCase = unapply(String.prototype.toLowerCase);
15359
15361
  const stringToString = unapply(String.prototype.toString);
15360
15362
  const stringMatch = unapply(String.prototype.match);
@@ -15364,12 +15366,11 @@
15364
15366
  const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
15365
15367
  const regExpTest = unapply(RegExp.prototype.test);
15366
15368
  const typeErrorCreate = unconstruct(TypeError);
15367
-
15368
15369
  /**
15369
15370
  * Creates a new function that calls the given function with a specified thisArg and arguments.
15370
15371
  *
15371
- * @param {Function} func - The function to be wrapped and called.
15372
- * @returns {Function} A new function that calls the given function with a specified thisArg and arguments.
15372
+ * @param func - The function to be wrapped and called.
15373
+ * @returns A new function that calls the given function with a specified thisArg and arguments.
15373
15374
  */
15374
15375
  function unapply(func) {
15375
15376
  return function (thisArg) {
@@ -15379,12 +15380,11 @@
15379
15380
  return apply(func, thisArg, args);
15380
15381
  };
15381
15382
  }
15382
-
15383
15383
  /**
15384
15384
  * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
15385
15385
  *
15386
- * @param {Function} func - The constructor function to be wrapped and called.
15387
- * @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments.
15386
+ * @param func - The constructor function to be wrapped and called.
15387
+ * @returns A new function that constructs an instance of the given constructor function with the provided arguments.
15388
15388
  */
15389
15389
  function unconstruct(func) {
15390
15390
  return function () {
@@ -15394,14 +15394,13 @@
15394
15394
  return construct(func, args);
15395
15395
  };
15396
15396
  }
15397
-
15398
15397
  /**
15399
15398
  * Add properties to a lookup table
15400
15399
  *
15401
- * @param {Object} set - The set to which elements will be added.
15402
- * @param {Array} array - The array containing elements to be added to the set.
15403
- * @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set.
15404
- * @returns {Object} The modified set with added elements.
15400
+ * @param set - The set to which elements will be added.
15401
+ * @param array - The array containing elements to be added to the set.
15402
+ * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
15403
+ * @returns The modified set with added elements.
15405
15404
  */
15406
15405
  function addToSet(set, array) {
15407
15406
  let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
@@ -15428,12 +15427,11 @@
15428
15427
  }
15429
15428
  return set;
15430
15429
  }
15431
-
15432
15430
  /**
15433
15431
  * Clean up an array to harden against CSPP
15434
15432
  *
15435
- * @param {Array} array - The array to be cleaned.
15436
- * @returns {Array} The cleaned version of the array
15433
+ * @param array - The array to be cleaned.
15434
+ * @returns The cleaned version of the array
15437
15435
  */
15438
15436
  function cleanArray(array) {
15439
15437
  for (let index = 0; index < array.length; index++) {
@@ -15444,12 +15442,11 @@
15444
15442
  }
15445
15443
  return array;
15446
15444
  }
15447
-
15448
15445
  /**
15449
15446
  * Shallow clone an object
15450
15447
  *
15451
- * @param {Object} object - The object to be cloned.
15452
- * @returns {Object} A new object that copies the original.
15448
+ * @param object - The object to be cloned.
15449
+ * @returns A new object that copies the original.
15453
15450
  */
15454
15451
  function clone(object) {
15455
15452
  const newObject = create$7(null);
@@ -15467,13 +15464,12 @@
15467
15464
  }
15468
15465
  return newObject;
15469
15466
  }
15470
-
15471
15467
  /**
15472
15468
  * This method automatically checks if the prop is function or getter and behaves accordingly.
15473
15469
  *
15474
- * @param {Object} object - The object to look up the getter function in its prototype chain.
15475
- * @param {String} prop - The property name for which to find the getter function.
15476
- * @returns {Function} The getter function found in the prototype chain or a fallback function.
15470
+ * @param object - The object to look up the getter function in its prototype chain.
15471
+ * @param prop - The property name for which to find the getter function.
15472
+ * @returns The getter function found in the prototype chain or a fallback function.
15477
15473
  */
15478
15474
  function lookupGetter(object, prop) {
15479
15475
  while (object !== null) {
@@ -15495,18 +15491,14 @@
15495
15491
  }
15496
15492
 
15497
15493
  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']);
15498
-
15499
- // SVG
15500
15494
  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']);
15501
15495
  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']);
15502
-
15503
15496
  // List of SVG elements that are disallowed by default.
15504
15497
  // We still need to know them so that we can do namespace
15505
15498
  // checks properly in case one wants to add them to
15506
15499
  // allow-list.
15507
15500
  const svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
15508
15501
  const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);
15509
-
15510
15502
  // Similarly to SVG, we want to know all MathML elements,
15511
15503
  // even those that we disallow by default.
15512
15504
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
@@ -15520,8 +15512,8 @@
15520
15512
  // eslint-disable-next-line unicorn/better-regex
15521
15513
  const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
15522
15514
  const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
15523
- const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
15524
- const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
15515
+ const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
15516
+ const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
15525
15517
  const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
15526
15518
  const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
15527
15519
  );
@@ -15533,18 +15525,19 @@
15533
15525
 
15534
15526
  var EXPRESSIONS = /*#__PURE__*/Object.freeze({
15535
15527
  __proto__: null,
15536
- MUSTACHE_EXPR: MUSTACHE_EXPR,
15537
- ERB_EXPR: ERB_EXPR,
15538
- TMPLIT_EXPR: TMPLIT_EXPR,
15539
- DATA_ATTR: DATA_ATTR,
15540
15528
  ARIA_ATTR: ARIA_ATTR,
15541
- IS_ALLOWED_URI: IS_ALLOWED_URI,
15542
- IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
15543
15529
  ATTR_WHITESPACE: ATTR_WHITESPACE,
15530
+ CUSTOM_ELEMENT: CUSTOM_ELEMENT,
15531
+ DATA_ATTR: DATA_ATTR,
15544
15532
  DOCTYPE_NAME: DOCTYPE_NAME,
15545
- CUSTOM_ELEMENT: CUSTOM_ELEMENT
15533
+ ERB_EXPR: ERB_EXPR,
15534
+ IS_ALLOWED_URI: IS_ALLOWED_URI,
15535
+ IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
15536
+ MUSTACHE_EXPR: MUSTACHE_EXPR,
15537
+ TMPLIT_EXPR: TMPLIT_EXPR
15546
15538
  });
15547
15539
 
15540
+ /* eslint-disable @typescript-eslint/indent */
15548
15541
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
15549
15542
  const NODE_TYPE = {
15550
15543
  element: 1,
@@ -15565,20 +15558,18 @@
15565
15558
  const getGlobal = function getGlobal() {
15566
15559
  return typeof window === 'undefined' ? null : window;
15567
15560
  };
15568
-
15569
15561
  /**
15570
15562
  * Creates a no-op policy for internal use only.
15571
15563
  * Don't export this function outside this module!
15572
- * @param {TrustedTypePolicyFactory} trustedTypes The policy factory.
15573
- * @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
15574
- * @return {TrustedTypePolicy} The policy created (or null, if Trusted Types
15564
+ * @param trustedTypes The policy factory.
15565
+ * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
15566
+ * @return The policy created (or null, if Trusted Types
15575
15567
  * are not supported or creating the policy failed).
15576
15568
  */
15577
15569
  const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
15578
15570
  if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
15579
15571
  return null;
15580
15572
  }
15581
-
15582
15573
  // Allow the callers to control the unique policy name
15583
15574
  // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
15584
15575
  // Policy creation with duplicate names throws in Trusted Types.
@@ -15605,22 +15596,25 @@
15605
15596
  return null;
15606
15597
  }
15607
15598
  };
15599
+ const _createHooksMap = function _createHooksMap() {
15600
+ return {
15601
+ afterSanitizeAttributes: [],
15602
+ afterSanitizeElements: [],
15603
+ afterSanitizeShadowDOM: [],
15604
+ beforeSanitizeAttributes: [],
15605
+ beforeSanitizeElements: [],
15606
+ beforeSanitizeShadowDOM: [],
15607
+ uponSanitizeAttribute: [],
15608
+ uponSanitizeElement: [],
15609
+ uponSanitizeShadowNode: []
15610
+ };
15611
+ };
15608
15612
  function createDOMPurify() {
15609
15613
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
15610
15614
  const DOMPurify = root => createDOMPurify(root);
15611
-
15612
- /**
15613
- * Version label, exposed for easier checks
15614
- * if DOMPurify is up to date or not
15615
- */
15616
- DOMPurify.version = '3.1.7';
15617
-
15618
- /**
15619
- * Array of elements that DOMPurify removed during sanitation.
15620
- * Empty if nothing was removed.
15621
- */
15615
+ DOMPurify.version = '3.2.4';
15622
15616
  DOMPurify.removed = [];
15623
- if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
15617
+ if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
15624
15618
  // Not running in a browser, provide a factory function
15625
15619
  // so that you can pass your own Window
15626
15620
  DOMPurify.isSupported = false;
@@ -15648,7 +15642,6 @@
15648
15642
  const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
15649
15643
  const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
15650
15644
  const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
15651
-
15652
15645
  // As per issue #47, the web-components registry is inherited by a
15653
15646
  // new document created via createHTMLDocument. As per the spec
15654
15647
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
@@ -15672,8 +15665,7 @@
15672
15665
  const {
15673
15666
  importNode
15674
15667
  } = originalDocument;
15675
- let hooks = {};
15676
-
15668
+ let hooks = _createHooksMap();
15677
15669
  /**
15678
15670
  * Expose whether this browser supports running the full DOMPurify.
15679
15671
  */
@@ -15691,22 +15683,18 @@
15691
15683
  let {
15692
15684
  IS_ALLOWED_URI: IS_ALLOWED_URI$1
15693
15685
  } = EXPRESSIONS;
15694
-
15695
15686
  /**
15696
15687
  * We consider the elements and attributes below to be safe. Ideally
15697
15688
  * don't add any new ones but feel free to remove unwanted ones.
15698
15689
  */
15699
-
15700
15690
  /* allowed element names */
15701
15691
  let ALLOWED_TAGS = null;
15702
15692
  const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
15703
-
15704
15693
  /* Allowed attribute names */
15705
15694
  let ALLOWED_ATTR = null;
15706
15695
  const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
15707
-
15708
15696
  /*
15709
- * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
15697
+ * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.
15710
15698
  * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
15711
15699
  * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
15712
15700
  * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
@@ -15731,65 +15719,49 @@
15731
15719
  value: false
15732
15720
  }
15733
15721
  }));
15734
-
15735
15722
  /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
15736
15723
  let FORBID_TAGS = null;
15737
-
15738
15724
  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
15739
15725
  let FORBID_ATTR = null;
15740
-
15741
15726
  /* Decide if ARIA attributes are okay */
15742
15727
  let ALLOW_ARIA_ATTR = true;
15743
-
15744
15728
  /* Decide if custom data attributes are okay */
15745
15729
  let ALLOW_DATA_ATTR = true;
15746
-
15747
15730
  /* Decide if unknown protocols are okay */
15748
15731
  let ALLOW_UNKNOWN_PROTOCOLS = false;
15749
-
15750
15732
  /* Decide if self-closing tags in attributes are allowed.
15751
15733
  * Usually removed due to a mXSS issue in jQuery 3.0 */
15752
15734
  let ALLOW_SELF_CLOSE_IN_ATTR = true;
15753
-
15754
15735
  /* Output should be safe for common template engines.
15755
15736
  * This means, DOMPurify removes data attributes, mustaches and ERB
15756
15737
  */
15757
15738
  let SAFE_FOR_TEMPLATES = false;
15758
-
15759
15739
  /* Output should be safe even for XML used within HTML and alike.
15760
15740
  * This means, DOMPurify removes comments when containing risky content.
15761
15741
  */
15762
15742
  let SAFE_FOR_XML = true;
15763
-
15764
15743
  /* Decide if document with <html>... should be returned */
15765
15744
  let WHOLE_DOCUMENT = false;
15766
-
15767
15745
  /* Track whether config is already set on this instance of DOMPurify. */
15768
15746
  let SET_CONFIG = false;
15769
-
15770
15747
  /* Decide if all elements (e.g. style, script) must be children of
15771
15748
  * document.body. By default, browsers might move them to document.head */
15772
15749
  let FORCE_BODY = false;
15773
-
15774
15750
  /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
15775
15751
  * string (or a TrustedHTML object if Trusted Types are supported).
15776
15752
  * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
15777
15753
  */
15778
15754
  let RETURN_DOM = false;
15779
-
15780
15755
  /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
15781
15756
  * string (or a TrustedHTML object if Trusted Types are supported) */
15782
15757
  let RETURN_DOM_FRAGMENT = false;
15783
-
15784
15758
  /* Try to return a Trusted Type object instead of a string, return a string in
15785
15759
  * case Trusted Types are not supported */
15786
15760
  let RETURN_TRUSTED_TYPE = false;
15787
-
15788
15761
  /* Output should be free from DOM clobbering attacks?
15789
15762
  * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
15790
15763
  */
15791
15764
  let SANITIZE_DOM = true;
15792
-
15793
15765
  /* Achieve full DOM Clobbering protection by isolating the namespace of named
15794
15766
  * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
15795
15767
  *
@@ -15805,25 +15777,19 @@
15805
15777
  */
15806
15778
  let SANITIZE_NAMED_PROPS = false;
15807
15779
  const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
15808
-
15809
15780
  /* Keep element content when removing element? */
15810
15781
  let KEEP_CONTENT = true;
15811
-
15812
15782
  /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
15813
15783
  * of importing it into a new Document and returning a sanitized copy */
15814
15784
  let IN_PLACE = false;
15815
-
15816
15785
  /* Allow usage of profiles like html, svg and mathMl */
15817
15786
  let USE_PROFILES = {};
15818
-
15819
15787
  /* Tags to ignore content of when KEEP_CONTENT is true */
15820
15788
  let FORBID_CONTENTS = null;
15821
15789
  const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
15822
-
15823
15790
  /* Tags that are safe for data: URIs */
15824
15791
  let DATA_URI_TAGS = null;
15825
15792
  const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
15826
-
15827
15793
  /* Attributes safe for values like "javascript:" */
15828
15794
  let URI_SAFE_ATTRIBUTES = null;
15829
15795
  const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
@@ -15833,32 +15799,33 @@
15833
15799
  /* Document namespace */
15834
15800
  let NAMESPACE = HTML_NAMESPACE;
15835
15801
  let IS_EMPTY_INPUT = false;
15836
-
15837
15802
  /* Allowed XHTML+XML namespaces */
15838
15803
  let ALLOWED_NAMESPACES = null;
15839
15804
  const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
15840
-
15805
+ let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
15806
+ let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
15807
+ // Certain elements are allowed in both SVG and HTML
15808
+ // namespace. We need to specify them explicitly
15809
+ // so that they don't get erroneously deleted from
15810
+ // HTML namespace.
15811
+ const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
15841
15812
  /* Parsing of strict XHTML documents */
15842
15813
  let PARSER_MEDIA_TYPE = null;
15843
15814
  const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
15844
15815
  const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
15845
15816
  let transformCaseFunc = null;
15846
-
15847
15817
  /* Keep a reference to config to pass to hooks */
15848
15818
  let CONFIG = null;
15849
-
15850
15819
  /* Ideally, do not touch anything below this line */
15851
15820
  /* ______________________________________________ */
15852
-
15853
15821
  const formElement = document.createElement('form');
15854
15822
  const isRegexOrFunction = function isRegexOrFunction(testValue) {
15855
15823
  return testValue instanceof RegExp || testValue instanceof Function;
15856
15824
  };
15857
-
15858
15825
  /**
15859
15826
  * _parseConfig
15860
15827
  *
15861
- * @param {Object} cfg optional config literal
15828
+ * @param cfg optional config literal
15862
15829
  */
15863
15830
  // eslint-disable-next-line complexity
15864
15831
  const _parseConfig = function _parseConfig() {
@@ -15866,39 +15833,23 @@
15866
15833
  if (CONFIG && CONFIG === cfg) {
15867
15834
  return;
15868
15835
  }
15869
-
15870
15836
  /* Shield configuration object from tampering */
15871
15837
  if (!cfg || typeof cfg !== 'object') {
15872
15838
  cfg = {};
15873
15839
  }
15874
-
15875
15840
  /* Shield configuration object from prototype pollution */
15876
15841
  cfg = clone(cfg);
15877
15842
  PARSER_MEDIA_TYPE =
15878
15843
  // eslint-disable-next-line unicorn/prefer-includes
15879
15844
  SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
15880
-
15881
15845
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
15882
15846
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
15883
-
15884
15847
  /* Set configuration parameters */
15885
15848
  ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
15886
15849
  ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
15887
15850
  ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
15888
- URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
15889
- // eslint-disable-line indent
15890
- cfg.ADD_URI_SAFE_ATTR,
15891
- // eslint-disable-line indent
15892
- transformCaseFunc // eslint-disable-line indent
15893
- ) // eslint-disable-line indent
15894
- : DEFAULT_URI_SAFE_ATTRIBUTES;
15895
- DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
15896
- // eslint-disable-line indent
15897
- cfg.ADD_DATA_URI_TAGS,
15898
- // eslint-disable-line indent
15899
- transformCaseFunc // eslint-disable-line indent
15900
- ) // eslint-disable-line indent
15901
- : DEFAULT_DATA_URI_TAGS;
15851
+ 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;
15852
+ 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;
15902
15853
  FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
15903
15854
  FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
15904
15855
  FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
@@ -15920,6 +15871,8 @@
15920
15871
  IN_PLACE = cfg.IN_PLACE || false; // Default false
15921
15872
  IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
15922
15873
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
15874
+ MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
15875
+ HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
15923
15876
  CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
15924
15877
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
15925
15878
  CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
@@ -15936,7 +15889,6 @@
15936
15889
  if (RETURN_DOM_FRAGMENT) {
15937
15890
  RETURN_DOM = true;
15938
15891
  }
15939
-
15940
15892
  /* Parse profile info */
15941
15893
  if (USE_PROFILES) {
15942
15894
  ALLOWED_TAGS = addToSet({}, text);
@@ -15961,7 +15913,6 @@
15961
15913
  addToSet(ALLOWED_ATTR, xml);
15962
15914
  }
15963
15915
  }
15964
-
15965
15916
  /* Merge configuration parameters */
15966
15917
  if (cfg.ADD_TAGS) {
15967
15918
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
@@ -15984,17 +15935,14 @@
15984
15935
  }
15985
15936
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
15986
15937
  }
15987
-
15988
15938
  /* Add #text in case KEEP_CONTENT is set to true */
15989
15939
  if (KEEP_CONTENT) {
15990
15940
  ALLOWED_TAGS['#text'] = true;
15991
15941
  }
15992
-
15993
15942
  /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
15994
15943
  if (WHOLE_DOCUMENT) {
15995
15944
  addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
15996
15945
  }
15997
-
15998
15946
  /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
15999
15947
  if (ALLOWED_TAGS.table) {
16000
15948
  addToSet(ALLOWED_TAGS, ['tbody']);
@@ -16007,10 +15955,8 @@
16007
15955
  if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
16008
15956
  throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
16009
15957
  }
16010
-
16011
15958
  // Overwrite existing TrustedTypes policy.
16012
15959
  trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
16013
-
16014
15960
  // Sign local variables required by `sanitize`.
16015
15961
  emptyHTML = trustedTypesPolicy.createHTML('');
16016
15962
  } else {
@@ -16018,13 +15964,11 @@
16018
15964
  if (trustedTypesPolicy === undefined) {
16019
15965
  trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
16020
15966
  }
16021
-
16022
15967
  // If creating the internal policy succeeded sign internal variables.
16023
15968
  if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
16024
15969
  emptyHTML = trustedTypesPolicy.createHTML('');
16025
15970
  }
16026
15971
  }
16027
-
16028
15972
  // Prevent further manipulation of configuration.
16029
15973
  // Not available in IE8, Safari 5, etc.
16030
15974
  if (freeze) {
@@ -16032,30 +15976,19 @@
16032
15976
  }
16033
15977
  CONFIG = cfg;
16034
15978
  };
16035
- const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
16036
- const HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
16037
-
16038
- // Certain elements are allowed in both SVG and HTML
16039
- // namespace. We need to specify them explicitly
16040
- // so that they don't get erroneously deleted from
16041
- // HTML namespace.
16042
- const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
16043
-
16044
15979
  /* Keep track of all possible SVG and MathML tags
16045
15980
  * so that we can perform the namespace checks
16046
15981
  * correctly. */
16047
15982
  const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
16048
15983
  const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
16049
-
16050
15984
  /**
16051
- * @param {Element} element a DOM element whose namespace is being checked
16052
- * @returns {boolean} Return false if the element has a
15985
+ * @param element a DOM element whose namespace is being checked
15986
+ * @returns Return false if the element has a
16053
15987
  * namespace that a spec-compliant parser would never
16054
15988
  * return. Return true otherwise.
16055
15989
  */
16056
15990
  const _checkValidNamespace = function _checkValidNamespace(element) {
16057
15991
  let parent = getParentNode(element);
16058
-
16059
15992
  // In JSDOM, if we're inside shadow DOM, then parentNode
16060
15993
  // can be null. We just simulate parent in this case.
16061
15994
  if (!parent || !parent.tagName) {
@@ -16076,14 +16009,12 @@
16076
16009
  if (parent.namespaceURI === HTML_NAMESPACE) {
16077
16010
  return tagName === 'svg';
16078
16011
  }
16079
-
16080
16012
  // The only way to switch from MathML to SVG is via`
16081
16013
  // svg if parent is either <annotation-xml> or MathML
16082
16014
  // text integration points.
16083
16015
  if (parent.namespaceURI === MATHML_NAMESPACE) {
16084
16016
  return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
16085
16017
  }
16086
-
16087
16018
  // We only allow elements that are defined in SVG
16088
16019
  // spec. All others are disallowed in SVG namespace.
16089
16020
  return Boolean(ALL_SVG_TAGS[tagName]);
@@ -16095,13 +16026,11 @@
16095
16026
  if (parent.namespaceURI === HTML_NAMESPACE) {
16096
16027
  return tagName === 'math';
16097
16028
  }
16098
-
16099
16029
  // The only way to switch from SVG to MathML is via
16100
16030
  // <math> and HTML integration points
16101
16031
  if (parent.namespaceURI === SVG_NAMESPACE) {
16102
16032
  return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
16103
16033
  }
16104
-
16105
16034
  // We only allow elements that are defined in MathML
16106
16035
  // spec. All others are disallowed in MathML namespace.
16107
16036
  return Boolean(ALL_MATHML_TAGS[tagName]);
@@ -16116,28 +16045,24 @@
16116
16045
  if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
16117
16046
  return false;
16118
16047
  }
16119
-
16120
16048
  // We disallow tags that are specific for MathML
16121
16049
  // or SVG and should never appear in HTML namespace
16122
16050
  return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
16123
16051
  }
16124
-
16125
16052
  // For XHTML and XML documents that support custom namespaces
16126
16053
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
16127
16054
  return true;
16128
16055
  }
16129
-
16130
16056
  // The code should never reach this place (this means
16131
16057
  // that the element somehow got namespace that is not
16132
16058
  // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
16133
16059
  // Return false just in case.
16134
16060
  return false;
16135
16061
  };
16136
-
16137
16062
  /**
16138
16063
  * _forceRemove
16139
16064
  *
16140
- * @param {Node} node a DOM node
16065
+ * @param node a DOM node
16141
16066
  */
16142
16067
  const _forceRemove = function _forceRemove(node) {
16143
16068
  arrayPush(DOMPurify.removed, {
@@ -16150,46 +16075,43 @@
16150
16075
  remove(node);
16151
16076
  }
16152
16077
  };
16153
-
16154
16078
  /**
16155
16079
  * _removeAttribute
16156
16080
  *
16157
- * @param {String} name an Attribute name
16158
- * @param {Node} node a DOM node
16081
+ * @param name an Attribute name
16082
+ * @param element a DOM node
16159
16083
  */
16160
- const _removeAttribute = function _removeAttribute(name, node) {
16084
+ const _removeAttribute = function _removeAttribute(name, element) {
16161
16085
  try {
16162
16086
  arrayPush(DOMPurify.removed, {
16163
- attribute: node.getAttributeNode(name),
16164
- from: node
16087
+ attribute: element.getAttributeNode(name),
16088
+ from: element
16165
16089
  });
16166
16090
  } catch (_) {
16167
16091
  arrayPush(DOMPurify.removed, {
16168
16092
  attribute: null,
16169
- from: node
16093
+ from: element
16170
16094
  });
16171
16095
  }
16172
- node.removeAttribute(name);
16173
-
16174
- // We void attribute values for unremovable "is"" attributes
16175
- if (name === 'is' && !ALLOWED_ATTR[name]) {
16096
+ element.removeAttribute(name);
16097
+ // We void attribute values for unremovable "is" attributes
16098
+ if (name === 'is') {
16176
16099
  if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
16177
16100
  try {
16178
- _forceRemove(node);
16101
+ _forceRemove(element);
16179
16102
  } catch (_) {}
16180
16103
  } else {
16181
16104
  try {
16182
- node.setAttribute(name, '');
16105
+ element.setAttribute(name, '');
16183
16106
  } catch (_) {}
16184
16107
  }
16185
16108
  }
16186
16109
  };
16187
-
16188
16110
  /**
16189
16111
  * _initDocument
16190
16112
  *
16191
- * @param {String} dirty a string of dirty markup
16192
- * @return {Document} a DOM, filled with the dirty markup
16113
+ * @param dirty - a string of dirty markup
16114
+ * @return a DOM, filled with the dirty markup
16193
16115
  */
16194
16116
  const _initDocument = function _initDocument(dirty) {
16195
16117
  /* Create a HTML document */
@@ -16216,7 +16138,6 @@
16216
16138
  doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
16217
16139
  } catch (_) {}
16218
16140
  }
16219
-
16220
16141
  /* Use createHTMLDocument in case DOMParser is not available */
16221
16142
  if (!doc || !doc.documentElement) {
16222
16143
  doc = implementation.createDocument(NAMESPACE, 'template', null);
@@ -16230,112 +16151,86 @@
16230
16151
  if (dirty && leadingWhitespace) {
16231
16152
  body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
16232
16153
  }
16233
-
16234
16154
  /* Work on whole document or just its body */
16235
16155
  if (NAMESPACE === HTML_NAMESPACE) {
16236
16156
  return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
16237
16157
  }
16238
16158
  return WHOLE_DOCUMENT ? doc.documentElement : body;
16239
16159
  };
16240
-
16241
16160
  /**
16242
16161
  * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
16243
16162
  *
16244
- * @param {Node} root The root element or node to start traversing on.
16245
- * @return {NodeIterator} The created NodeIterator
16163
+ * @param root The root element or node to start traversing on.
16164
+ * @return The created NodeIterator
16246
16165
  */
16247
16166
  const _createNodeIterator = function _createNodeIterator(root) {
16248
16167
  return createNodeIterator.call(root.ownerDocument || root, root,
16249
16168
  // eslint-disable-next-line no-bitwise
16250
16169
  NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
16251
16170
  };
16252
-
16253
16171
  /**
16254
16172
  * _isClobbered
16255
16173
  *
16256
- * @param {Node} elm element to check for clobbering attacks
16257
- * @return {Boolean} true if clobbered, false if safe
16174
+ * @param element element to check for clobbering attacks
16175
+ * @return true if clobbered, false if safe
16258
16176
  */
16259
- const _isClobbered = function _isClobbered(elm) {
16260
- return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function');
16177
+ const _isClobbered = function _isClobbered(element) {
16178
+ 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');
16261
16179
  };
16262
-
16263
16180
  /**
16264
16181
  * Checks whether the given object is a DOM node.
16265
16182
  *
16266
- * @param {Node} object object to check whether it's a DOM node
16267
- * @return {Boolean} true is object is a DOM node
16183
+ * @param value object to check whether it's a DOM node
16184
+ * @return true is object is a DOM node
16268
16185
  */
16269
- const _isNode = function _isNode(object) {
16270
- return typeof Node === 'function' && object instanceof Node;
16186
+ const _isNode = function _isNode(value) {
16187
+ return typeof Node === 'function' && value instanceof Node;
16271
16188
  };
16272
-
16273
- /**
16274
- * _executeHook
16275
- * Execute user configurable hooks
16276
- *
16277
- * @param {String} entryPoint Name of the hook's entry point
16278
- * @param {Node} currentNode node to work on with the hook
16279
- * @param {Object} data additional hook parameters
16280
- */
16281
- const _executeHook = function _executeHook(entryPoint, currentNode, data) {
16282
- if (!hooks[entryPoint]) {
16283
- return;
16284
- }
16285
- arrayForEach(hooks[entryPoint], hook => {
16189
+ function _executeHooks(hooks, currentNode, data) {
16190
+ arrayForEach(hooks, hook => {
16286
16191
  hook.call(DOMPurify, currentNode, data, CONFIG);
16287
16192
  });
16288
- };
16289
-
16193
+ }
16290
16194
  /**
16291
16195
  * _sanitizeElements
16292
16196
  *
16293
16197
  * @protect nodeName
16294
16198
  * @protect textContent
16295
16199
  * @protect removeChild
16296
- *
16297
- * @param {Node} currentNode to check for permission to exist
16298
- * @return {Boolean} true if node was killed, false if left alive
16200
+ * @param currentNode to check for permission to exist
16201
+ * @return true if node was killed, false if left alive
16299
16202
  */
16300
16203
  const _sanitizeElements = function _sanitizeElements(currentNode) {
16301
16204
  let content = null;
16302
-
16303
16205
  /* Execute a hook if present */
16304
- _executeHook('beforeSanitizeElements', currentNode, null);
16305
-
16206
+ _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
16306
16207
  /* Check if element is clobbered or can clobber */
16307
16208
  if (_isClobbered(currentNode)) {
16308
16209
  _forceRemove(currentNode);
16309
16210
  return true;
16310
16211
  }
16311
-
16312
16212
  /* Now let's check the element's type and name */
16313
16213
  const tagName = transformCaseFunc(currentNode.nodeName);
16314
-
16315
16214
  /* Execute a hook if present */
16316
- _executeHook('uponSanitizeElement', currentNode, {
16215
+ _executeHooks(hooks.uponSanitizeElement, currentNode, {
16317
16216
  tagName,
16318
16217
  allowedTags: ALLOWED_TAGS
16319
16218
  });
16320
-
16321
16219
  /* Detect mXSS attempts abusing namespace confusion */
16322
16220
  if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
16323
16221
  _forceRemove(currentNode);
16324
16222
  return true;
16325
16223
  }
16326
-
16327
16224
  /* Remove any occurrence of processing instructions */
16328
16225
  if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
16329
16226
  _forceRemove(currentNode);
16330
16227
  return true;
16331
16228
  }
16332
-
16333
16229
  /* Remove any kind of possibly harmful comments */
16334
16230
  if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
16335
16231
  _forceRemove(currentNode);
16336
16232
  return true;
16337
16233
  }
16338
-
16339
16234
  /* Remove element if anything forbids its presence */
16340
16235
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
16341
16236
  /* Check if we have a custom element to handle */
@@ -16347,7 +16242,6 @@
16347
16242
  return false;
16348
16243
  }
16349
16244
  }
16350
-
16351
16245
  /* Keep content except for bad-listed elements */
16352
16246
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
16353
16247
  const parentNode = getParentNode(currentNode) || currentNode.parentNode;
@@ -16364,19 +16258,16 @@
16364
16258
  _forceRemove(currentNode);
16365
16259
  return true;
16366
16260
  }
16367
-
16368
16261
  /* Check whether element has a valid namespace */
16369
16262
  if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
16370
16263
  _forceRemove(currentNode);
16371
16264
  return true;
16372
16265
  }
16373
-
16374
16266
  /* Make sure that older browsers don't get fallback-tag mXSS */
16375
16267
  if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
16376
16268
  _forceRemove(currentNode);
16377
16269
  return true;
16378
16270
  }
16379
-
16380
16271
  /* Sanitize element content to be template-safe */
16381
16272
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
16382
16273
  /* Get the element's text content */
@@ -16391,19 +16282,17 @@
16391
16282
  currentNode.textContent = content;
16392
16283
  }
16393
16284
  }
16394
-
16395
16285
  /* Execute a hook if present */
16396
- _executeHook('afterSanitizeElements', currentNode, null);
16286
+ _executeHooks(hooks.afterSanitizeElements, currentNode, null);
16397
16287
  return false;
16398
16288
  };
16399
-
16400
16289
  /**
16401
16290
  * _isValidAttribute
16402
16291
  *
16403
- * @param {string} lcTag Lowercase tag name of containing element.
16404
- * @param {string} lcName Lowercase attribute name.
16405
- * @param {string} value Attribute value.
16406
- * @return {Boolean} Returns true if `value` is valid, otherwise false.
16292
+ * @param lcTag Lowercase tag name of containing element.
16293
+ * @param lcName Lowercase attribute name.
16294
+ * @param value Attribute value.
16295
+ * @return Returns true if `value` is valid, otherwise false.
16407
16296
  */
16408
16297
  // eslint-disable-next-line complexity
16409
16298
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
@@ -16411,7 +16300,6 @@
16411
16300
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
16412
16301
  return false;
16413
16302
  }
16414
-
16415
16303
  /* Allow valid data-* attributes: At least one character after "-"
16416
16304
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
16417
16305
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
@@ -16433,19 +16321,17 @@
16433
16321
  } else ;
16434
16322
  return true;
16435
16323
  };
16436
-
16437
16324
  /**
16438
16325
  * _isBasicCustomElement
16439
16326
  * checks if at least one dash is included in tagName, and it's not the first char
16440
16327
  * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
16441
16328
  *
16442
- * @param {string} tagName name of the tag of the node to sanitize
16443
- * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
16329
+ * @param tagName name of the tag of the node to sanitize
16330
+ * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
16444
16331
  */
16445
16332
  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
16446
16333
  return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
16447
16334
  };
16448
-
16449
16335
  /**
16450
16336
  * _sanitizeAttributes
16451
16337
  *
@@ -16454,27 +16340,26 @@
16454
16340
  * @protect removeAttribute
16455
16341
  * @protect setAttribute
16456
16342
  *
16457
- * @param {Node} currentNode to sanitize
16343
+ * @param currentNode to sanitize
16458
16344
  */
16459
16345
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
16460
16346
  /* Execute a hook if present */
16461
- _executeHook('beforeSanitizeAttributes', currentNode, null);
16347
+ _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
16462
16348
  const {
16463
16349
  attributes
16464
16350
  } = currentNode;
16465
-
16466
16351
  /* Check if we have attributes; if not we might have a text node */
16467
- if (!attributes) {
16352
+ if (!attributes || _isClobbered(currentNode)) {
16468
16353
  return;
16469
16354
  }
16470
16355
  const hookEvent = {
16471
16356
  attrName: '',
16472
16357
  attrValue: '',
16473
16358
  keepAttr: true,
16474
- allowedAttributes: ALLOWED_ATTR
16359
+ allowedAttributes: ALLOWED_ATTR,
16360
+ forceKeepAttr: undefined
16475
16361
  };
16476
16362
  let l = attributes.length;
16477
-
16478
16363
  /* Go backwards over all attributes; safely remove bad ones */
16479
16364
  while (l--) {
16480
16365
  const attr = attributes[l];
@@ -16486,65 +16371,54 @@
16486
16371
  const lcName = transformCaseFunc(name);
16487
16372
  let value = name === 'value' ? attrValue : stringTrim(attrValue);
16488
16373
  const initValue = value;
16489
-
16490
16374
  /* Execute a hook if present */
16491
16375
  hookEvent.attrName = lcName;
16492
16376
  hookEvent.attrValue = value;
16493
16377
  hookEvent.keepAttr = true;
16494
16378
  hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
16495
- _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
16379
+ _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
16496
16380
  value = hookEvent.attrValue;
16497
-
16381
+ /* Full DOM Clobbering protection via namespace isolation,
16382
+ * Prefix id and name attributes with `user-content-`
16383
+ */
16384
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
16385
+ // Remove the attribute with this value
16386
+ _removeAttribute(name, currentNode);
16387
+ // Prefix the value and later re-create the attribute with the sanitized value
16388
+ value = SANITIZE_NAMED_PROPS_PREFIX + value;
16389
+ }
16390
+ /* Work around a security issue with comments inside attributes */
16391
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
16392
+ _removeAttribute(name, currentNode);
16393
+ continue;
16394
+ }
16498
16395
  /* Did the hooks approve of the attribute? */
16499
16396
  if (hookEvent.forceKeepAttr) {
16500
16397
  continue;
16501
16398
  }
16502
-
16503
16399
  /* Remove attribute */
16504
-
16505
16400
  /* Did the hooks approve of the attribute? */
16506
16401
  if (!hookEvent.keepAttr) {
16507
16402
  _removeAttribute(name, currentNode);
16508
16403
  continue;
16509
16404
  }
16510
-
16511
16405
  /* Work around a security issue in jQuery 3.0 */
16512
16406
  if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
16513
16407
  _removeAttribute(name, currentNode);
16514
16408
  continue;
16515
16409
  }
16516
-
16517
16410
  /* Sanitize attribute content to be template-safe */
16518
16411
  if (SAFE_FOR_TEMPLATES) {
16519
16412
  arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
16520
16413
  value = stringReplace(value, expr, ' ');
16521
16414
  });
16522
16415
  }
16523
-
16524
16416
  /* Is `value` valid for this attribute? */
16525
16417
  const lcTag = transformCaseFunc(currentNode.nodeName);
16526
16418
  if (!_isValidAttribute(lcTag, lcName, value)) {
16527
16419
  _removeAttribute(name, currentNode);
16528
16420
  continue;
16529
16421
  }
16530
-
16531
- /* Full DOM Clobbering protection via namespace isolation,
16532
- * Prefix id and name attributes with `user-content-`
16533
- */
16534
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
16535
- // Remove the attribute with this value
16536
- _removeAttribute(name, currentNode);
16537
-
16538
- // Prefix the value and later re-create the attribute with the sanitized value
16539
- value = SANITIZE_NAMED_PROPS_PREFIX + value;
16540
- }
16541
-
16542
- /* Work around a security issue with comments inside attributes */
16543
- if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
16544
- _removeAttribute(name, currentNode);
16545
- continue;
16546
- }
16547
-
16548
16422
  /* Handle attributes that require Trusted Types */
16549
16423
  if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
16550
16424
  if (namespaceURI) ; else {
@@ -16562,7 +16436,6 @@
16562
16436
  }
16563
16437
  }
16564
16438
  }
16565
-
16566
16439
  /* Handle invalid data-* attribute set by try-catching it */
16567
16440
  if (value !== initValue) {
16568
16441
  try {
@@ -16580,51 +16453,34 @@
16580
16453
  } catch (_) {}
16581
16454
  }
16582
16455
  }
16583
-
16584
16456
  /* Execute a hook if present */
16585
- _executeHook('afterSanitizeAttributes', currentNode, null);
16457
+ _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
16586
16458
  };
16587
-
16588
16459
  /**
16589
16460
  * _sanitizeShadowDOM
16590
16461
  *
16591
- * @param {DocumentFragment} fragment to iterate over recursively
16462
+ * @param fragment to iterate over recursively
16592
16463
  */
16593
16464
  const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
16594
16465
  let shadowNode = null;
16595
16466
  const shadowIterator = _createNodeIterator(fragment);
16596
-
16597
16467
  /* Execute a hook if present */
16598
- _executeHook('beforeSanitizeShadowDOM', fragment, null);
16468
+ _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
16599
16469
  while (shadowNode = shadowIterator.nextNode()) {
16600
16470
  /* Execute a hook if present */
16601
- _executeHook('uponSanitizeShadowNode', shadowNode, null);
16602
-
16471
+ _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
16603
16472
  /* Sanitize tags and elements */
16604
- if (_sanitizeElements(shadowNode)) {
16605
- continue;
16606
- }
16607
-
16473
+ _sanitizeElements(shadowNode);
16474
+ /* Check attributes next */
16475
+ _sanitizeAttributes(shadowNode);
16608
16476
  /* Deep shadow DOM detected */
16609
16477
  if (shadowNode.content instanceof DocumentFragment) {
16610
16478
  _sanitizeShadowDOM(shadowNode.content);
16611
16479
  }
16612
-
16613
- /* Check attributes, sanitize if necessary */
16614
- _sanitizeAttributes(shadowNode);
16615
16480
  }
16616
-
16617
16481
  /* Execute a hook if present */
16618
- _executeHook('afterSanitizeShadowDOM', fragment, null);
16482
+ _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
16619
16483
  };
16620
-
16621
- /**
16622
- * Sanitize
16623
- * Public method providing core sanitation functionality
16624
- *
16625
- * @param {String|Node} dirty string or DOM node
16626
- * @param {Object} cfg object
16627
- */
16628
16484
  // eslint-disable-next-line complexity
16629
16485
  DOMPurify.sanitize = function (dirty) {
16630
16486
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -16639,7 +16495,6 @@
16639
16495
  if (IS_EMPTY_INPUT) {
16640
16496
  dirty = '<!-->';
16641
16497
  }
16642
-
16643
16498
  /* Stringify, in case dirty is an object */
16644
16499
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
16645
16500
  if (typeof dirty.toString === 'function') {
@@ -16651,20 +16506,16 @@
16651
16506
  throw typeErrorCreate('toString is not a function');
16652
16507
  }
16653
16508
  }
16654
-
16655
16509
  /* Return dirty HTML if DOMPurify cannot run */
16656
16510
  if (!DOMPurify.isSupported) {
16657
16511
  return dirty;
16658
16512
  }
16659
-
16660
16513
  /* Assign config vars */
16661
16514
  if (!SET_CONFIG) {
16662
16515
  _parseConfig(cfg);
16663
16516
  }
16664
-
16665
16517
  /* Clean up removed elements */
16666
16518
  DOMPurify.removed = [];
16667
-
16668
16519
  /* Check if dirty is correctly typed for IN_PLACE */
16669
16520
  if (typeof dirty === 'string') {
16670
16521
  IN_PLACE = false;
@@ -16698,45 +16549,34 @@
16698
16549
  dirty.indexOf('<') === -1) {
16699
16550
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
16700
16551
  }
16701
-
16702
16552
  /* Initialize the document to work on */
16703
16553
  body = _initDocument(dirty);
16704
-
16705
16554
  /* Check we have a DOM node from the data */
16706
16555
  if (!body) {
16707
16556
  return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
16708
16557
  }
16709
16558
  }
16710
-
16711
16559
  /* Remove first element node (ours) if FORCE_BODY is set */
16712
16560
  if (body && FORCE_BODY) {
16713
16561
  _forceRemove(body.firstChild);
16714
16562
  }
16715
-
16716
16563
  /* Get node iterator */
16717
16564
  const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
16718
-
16719
16565
  /* Now start iterating over the created document */
16720
16566
  while (currentNode = nodeIterator.nextNode()) {
16721
16567
  /* Sanitize tags and elements */
16722
- if (_sanitizeElements(currentNode)) {
16723
- continue;
16724
- }
16725
-
16568
+ _sanitizeElements(currentNode);
16569
+ /* Check attributes next */
16570
+ _sanitizeAttributes(currentNode);
16726
16571
  /* Shadow DOM detected, sanitize it */
16727
16572
  if (currentNode.content instanceof DocumentFragment) {
16728
16573
  _sanitizeShadowDOM(currentNode.content);
16729
16574
  }
16730
-
16731
- /* Check attributes, sanitize if necessary */
16732
- _sanitizeAttributes(currentNode);
16733
16575
  }
16734
-
16735
16576
  /* If we sanitized `dirty` in-place, return it. */
16736
16577
  if (IN_PLACE) {
16737
16578
  return dirty;
16738
16579
  }
16739
-
16740
16580
  /* Return sanitized string or DOM */
16741
16581
  if (RETURN_DOM) {
16742
16582
  if (RETURN_DOM_FRAGMENT) {
@@ -16761,12 +16601,10 @@
16761
16601
  return returnNode;
16762
16602
  }
16763
16603
  let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
16764
-
16765
16604
  /* Serialize doctype if allowed */
16766
16605
  if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
16767
16606
  serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
16768
16607
  }
16769
-
16770
16608
  /* Sanitize final string template-safe */
16771
16609
  if (SAFE_FOR_TEMPLATES) {
16772
16610
  arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
@@ -16775,39 +16613,15 @@
16775
16613
  }
16776
16614
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
16777
16615
  };
16778
-
16779
- /**
16780
- * Public method to set the configuration once
16781
- * setConfig
16782
- *
16783
- * @param {Object} cfg configuration object
16784
- */
16785
16616
  DOMPurify.setConfig = function () {
16786
16617
  let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
16787
16618
  _parseConfig(cfg);
16788
16619
  SET_CONFIG = true;
16789
16620
  };
16790
-
16791
- /**
16792
- * Public method to remove the configuration
16793
- * clearConfig
16794
- *
16795
- */
16796
16621
  DOMPurify.clearConfig = function () {
16797
16622
  CONFIG = null;
16798
16623
  SET_CONFIG = false;
16799
16624
  };
16800
-
16801
- /**
16802
- * Public method to check if an attribute value is valid.
16803
- * Uses last set config, if any. Otherwise, uses config defaults.
16804
- * isValidAttribute
16805
- *
16806
- * @param {String} tag Tag name of containing element.
16807
- * @param {String} attr Attribute name.
16808
- * @param {String} value Attribute value.
16809
- * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
16810
- */
16811
16625
  DOMPurify.isValidAttribute = function (tag, attr, value) {
16812
16626
  /* Initialize shared config vars if necessary. */
16813
16627
  if (!CONFIG) {
@@ -16817,54 +16631,24 @@
16817
16631
  const lcName = transformCaseFunc(attr);
16818
16632
  return _isValidAttribute(lcTag, lcName, value);
16819
16633
  };
16820
-
16821
- /**
16822
- * AddHook
16823
- * Public method to add DOMPurify hooks
16824
- *
16825
- * @param {String} entryPoint entry point for the hook to add
16826
- * @param {Function} hookFunction function to execute
16827
- */
16828
16634
  DOMPurify.addHook = function (entryPoint, hookFunction) {
16829
16635
  if (typeof hookFunction !== 'function') {
16830
16636
  return;
16831
16637
  }
16832
- hooks[entryPoint] = hooks[entryPoint] || [];
16833
16638
  arrayPush(hooks[entryPoint], hookFunction);
16834
16639
  };
16835
-
16836
- /**
16837
- * RemoveHook
16838
- * Public method to remove a DOMPurify hook at a given entryPoint
16839
- * (pops it from the stack of hooks if more are present)
16840
- *
16841
- * @param {String} entryPoint entry point for the hook to remove
16842
- * @return {Function} removed(popped) hook
16843
- */
16844
- DOMPurify.removeHook = function (entryPoint) {
16845
- if (hooks[entryPoint]) {
16846
- return arrayPop(hooks[entryPoint]);
16640
+ DOMPurify.removeHook = function (entryPoint, hookFunction) {
16641
+ if (hookFunction !== undefined) {
16642
+ const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
16643
+ return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];
16847
16644
  }
16645
+ return arrayPop(hooks[entryPoint]);
16848
16646
  };
16849
-
16850
- /**
16851
- * RemoveHooks
16852
- * Public method to remove all DOMPurify hooks at a given entryPoint
16853
- *
16854
- * @param {String} entryPoint entry point for the hooks to remove
16855
- */
16856
16647
  DOMPurify.removeHooks = function (entryPoint) {
16857
- if (hooks[entryPoint]) {
16858
- hooks[entryPoint] = [];
16859
- }
16648
+ hooks[entryPoint] = [];
16860
16649
  };
16861
-
16862
- /**
16863
- * RemoveAllHooks
16864
- * Public method to remove all DOMPurify hooks
16865
- */
16866
16650
  DOMPurify.removeAllHooks = function () {
16867
- hooks = {};
16651
+ hooks = _createHooksMap();
16868
16652
  };
16869
16653
  return DOMPurify;
16870
16654
  }
@@ -17353,10 +17137,11 @@
17353
17137
  evt.allowedTags[lcTagName] = true;
17354
17138
  }
17355
17139
  if (lcTagName === 'annotation') {
17356
- const keepElement = hasValidEncoding(node);
17140
+ const elm = node;
17141
+ const keepElement = hasValidEncoding(elm);
17357
17142
  evt.allowedTags[lcTagName] = keepElement;
17358
17143
  if (!keepElement) {
17359
- node.remove();
17144
+ elm.remove();
17360
17145
  }
17361
17146
  }
17362
17147
  });
@@ -31853,8 +31638,8 @@
31853
31638
  documentBaseURL: null,
31854
31639
  suffix: null,
31855
31640
  majorVersion: '7',
31856
- minorVersion: '7.0',
31857
- releaseDate: 'TBD',
31641
+ minorVersion: '7.1',
31642
+ releaseDate: '2025-03-05',
31858
31643
  i18n: I18n,
31859
31644
  activeEditor: null,
31860
31645
  focusedEditor: null,