PageSourceSearch

https://lzone.de/js/vendor/purify.es.mjs

js lzone.de collected 2026-10-02 04:23:19 UTC 64,644 bytes, 1,397 lines download raw bytes

vendor: 15,078 bytes, lines 1-237
1/*! @license DOMPurify 3.3.3 | (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.3/LICENSE */
2
3const {
4  entries,
5  setPrototypeOf,
6  isFrozen,
7  getPrototypeOf,
8  getOwnPropertyDescriptor
9} = Object;
10let {
11  freeze,
12  seal,
13  create
14} = Object; // eslint-disable-line import/no-mutable-exports
15let {
16  apply,
17  construct
18} = typeof Reflect !== 'undefined' && Reflect;
19if (!freeze) {
20  freeze = function freeze(x) {
21    return x;
22  };
23}
24if (!seal) {
25  seal = function seal(x) {
26    return x;
27  };
28}
29if (!apply) {
30  apply = function apply(func, thisArg) {
31    for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
32      args[_key - 2] = arguments[_key];
33    }
34    return func.apply(thisArg, args);
35  };
36}
37if (!construct) {
38  construct = function construct(Func) {
39    for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
40      args[_key2 - 1] = arguments[_key2];
41    }
42    return new Func(...args);
43  };
44}
45const arrayForEach = unapply(Array.prototype.forEach);
46const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
47const arrayPop = unapply(Array.prototype.pop);
48const arrayPush = unapply(Array.prototype.push);
49const arraySplice = unapply(Array.prototype.splice);
50const stringToLowerCase = unapply(String.prototype.toLowerCase);
51const stringToString = unapply(String.prototype.toString);
52const stringMatch = unapply(String.prototype.match);
53const stringReplace = unapply(String.prototype.replace);
54const stringIndexOf = unapply(String.prototype.indexOf);
55const stringTrim = unapply(String.prototype.trim);
56const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
57const regExpTest = unapply(RegExp.prototype.test);
58const typeErrorCreate = unconstruct(TypeError);
59/**
60 * Creates a new function that calls the given function with a specified thisArg and arguments.
61 *
62 * @param func - The function to be wrapped and called.
63 * @returns A new function that calls the given function with a specified thisArg and arguments.
64 */
65function unapply(func) {
66  return function (thisArg) {
67    if (thisArg instanceof RegExp) {
68      thisArg.lastIndex = 0;
69    }
70    for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
71      args[_key3 - 1] = arguments[_key3];
72    }
73    return apply(func, thisArg, args);
74  };
75}
76/**
77 * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
78 *
79 * @param func - The constructor function to be wrapped and called.
80 * @returns A new function that constructs an instance of the given constructor function with the provided arguments.
81 */
82function unconstruct(Func) {
83  return function () {
84    for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
85      args[_key4] = arguments[_key4];
86    }
87    return construct(Func, args);
88  };
89}
90/**
91 * Add properties to a lookup table
92 *
93 * @param set - The set to which elements will be added.
94 * @param array - The array containing elements to be added to the set.
95 * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
96 * @returns The modified set with added elements.
97 */
98function addToSet(set, array) {
99  let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
100  if (setPrototypeOf) {
101    // Make 'in' and truthy checks like Boolean(set.constructor)
102    // independent of any properties defined on Object.prototype.
103    // Prevent prototype setters from intercepting set as a this value.
104    setPrototypeOf(set, null);
105  }
106  let l = array.length;
107  while (l--) {
108    let element = array[l];
109    if (typeof element === 'string') {
110      const lcElement = transformCaseFunc(element);
111      if (lcElement !== element) {
112        // Config presets (e.g. tags.js, attrs.js) are immutable.
113        if (!isFrozen(array)) {
114          array[l] = lcElement;
115        }
116        element = lcElement;
117      }
118    }
119    set[element] = true;
120  }
121  return set;
122}
123/**
124 * Clean up an array to harden against CSPP
125 *
126 * @param array - The array to be cleaned.
127 * @returns The cleaned version of the array
128 */
129function cleanArray(array) {
130  for (let index = 0; index < array.length; index++) {
131    const isPropertyExist = objectHasOwnProperty(array, index);
132    if (!isPropertyExist) {
133      array[index] = null;
134    }
135  }
136  return array;
137}
138/**
139 * Shallow clone an object
140 *
141 * @param object - The object to be cloned.
142 * @returns A new object that copies the original.
143 */
144function clone(object) {
145  const newObject = create(null);
146  for (const [property, value] of entries(object)) {
147    const isPropertyExist = objectHasOwnProperty(object, property);
148    if (isPropertyExist) {
149      if (Array.isArray(value)) {
150        newObject[property] = cleanArray(value);
151      } else if (value && typeof value === 'object' && value.constructor === Object) {
152        newObject[property] = clone(value);
153      } else {
154        newObject[property] = value;
155      }
156    }
157  }
158  return newObject;
159}
160/**
161 * This method automatically checks if the prop is function or getter and behaves accordingly.
162 *
163 * @param object - The object to look up the getter function in its prototype chain.
164 * @param prop - The property name for which to find the getter function.
165 * @returns The getter function found in the prototype chain or a fallback function.
166 */
167function lookupGetter(object, prop) {
168  while (object !== null) {
169    const desc = getOwnPropertyDescriptor(object, prop);
170    if (desc) {
171      if (desc.get) {
172        return unapply(desc.get);
173      }
174      if (typeof desc.value === 'function') {
175        return unapply(desc.value);
176      }
177    }
178    object = getPrototypeOf(object);
179  }
180  function fallbackValue() {
181    return null;
182  }
183  return fallbackValue;
184}
185
186const 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']);
187const 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']);
188const 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']);
189// List of SVG elements that are disallowed by default.
190// We still need to know them so that we can do namespace
191// checks properly in case one wants to add them to
192// allow-list.
193const 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']);
194const 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']);
195// Similarly to SVG, we want to know all MathML elements,
196// even those that we disallow by default.
197const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
198const text = freeze(['#text']);
199
200const 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']);
201const 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']);
202const 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']);
203const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
204
205// eslint-disable-next-line unicorn/better-regex
206const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
207const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
208const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
209const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
210const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
211const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
212);
213const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
214const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
215);
216const DOCTYPE_NAME = seal(/^html$/i);
217const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
218
219var EXPRESSIONS = /*#__PURE__*/Object.freeze({
220  __proto__: null,
221  ARIA_ATTR: ARIA_ATTR,
222  ATTR_WHITESPACE: ATTR_WHITESPACE,
223  CUSTOM_ELEMENT: CUSTOM_ELEMENT,
224  DATA_ATTR: DATA_ATTR,
225  DOCTYPE_NAME: DOCTYPE_NAME,
226  ERB_EXPR: ERB_EXPR,
227  IS_ALLOWED_URI: IS_ALLOWED_URI,
228  IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
229  MUSTACHE_EXPR: MUSTACHE_EXPR,
230  TMPLIT_EXPR: TMPLIT_EXPR
231});
232
233/* eslint-disable @typescript-eslint/indent */
234// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
235const NODE_TYPE = {
236  element: 1,
237  
237attribute: 2,
238  text: 3,
239  cdataSection: 4,
240  entityReference: 5,
241  // Deprecated
242  entityNode: 6,
243  // Deprecated
244  progressingInstruction: 7,
245  comment: 8,
246  document: 9,
247  documentType: 10,
248  documentFragment: 11,
249  notation: 12 // Deprecated
vendor: 49,251 bytes, lines 250-1395
250};
251const getGlobal = function getGlobal() {
252  return typeof window === 'undefined' ? null : window;
253};
254/**
255 * Creates a no-op policy for internal use only.
256 * Don't export this function outside this module!
257 * @param trustedTypes The policy factory.
258 * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
259 * @return The policy created (or null, if Trusted Types
260 * are not supported or creating the policy failed).
261 */
262const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
263  if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
264    return null;
265  }
266  // Allow the callers to control the unique policy name
267  // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
268  // Policy creation with duplicate names throws in Trusted Types.
269  let suffix = null;
270  const ATTR_NAME = 'data-tt-policy-suffix';
271  if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
272    suffix = purifyHostElement.getAttribute(ATTR_NAME);
273  }
274  const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
275  try {
276    return trustedTypes.createPolicy(policyName, {
277      createHTML(html) {
278        return html;
279      },
280      createScriptURL(scriptUrl) {
281        return scriptUrl;
282      }
283    });
284  } catch (_) {
285    // Policy creation failed (most likely another DOMPurify script has
286    // already run). Skip creating the policy, as this will only cause errors
287    // if TT are enforced.
288    console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
289    return null;
290  }
291};
292const _createHooksMap = function _createHooksMap() {
293  return {
294    afterSanitizeAttributes: [],
295    afterSanitizeElements: [],
296    afterSanitizeShadowDOM: [],
297    beforeSanitizeAttributes: [],
298    beforeSanitizeElements: [],
299    beforeSanitizeShadowDOM: [],
300    uponSanitizeAttribute: [],
301    uponSanitizeElement: [],
302    uponSanitizeShadowNode: []
303  };
304};
305function createDOMPurify() {
306  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
307  const DOMPurify = root => createDOMPurify(root);
308  DOMPurify.version = '3.3.3';
309  DOMPurify.removed = [];
310  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
311    // Not running in a browser, provide a factory function
312    // so that you can pass your own Window
313    DOMPurify.isSupported = false;
314    return DOMPurify;
315  }
316  let {
317    document
318  } = window;
319  const originalDocument = document;
320  const currentScript = originalDocument.currentScript;
321  const {
322    DocumentFragment,
323    HTMLTemplateElement,
324    Node,
325    Element,
326    NodeFilter,
327    NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
328    HTMLFormElement,
329    DOMParser,
330    trustedTypes
331  } = window;
332  const ElementPrototype = Element.prototype;
333  const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
334  const remove = lookupGetter(ElementPrototype, 'remove');
335  const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
336  const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
337  const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
338  // As per issue #47, the web-components registry is inherited by a
339  // new document created via createHTMLDocument. As per the spec
340  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
341  // a new empty registry is used when creating a template contents owner
342  // document, so we use that as our parent document to ensure nothing
343  // is inherited.
344  if (typeof HTMLTemplateElement === 'function') {
345    const template = document.createElement('template');
346    if (template.content && template.content.ownerDocument) {
347      document = template.content.ownerDocument;
348    }
349  }
350  let trustedTypesPolicy;
351  let emptyHTML = '';
352  const {
353    implementation,
354    createNodeIterator,
355    createDocumentFragment,
356    getElementsByTagName
357  } = document;
358  const {
359    importNode
360  } = originalDocument;
361  let hooks = _createHooksMap();
362  /**
363   * Expose whether this browser supports running the full DOMPurify.
364   */
365  DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
366  const {
367    MUSTACHE_EXPR,
368    ERB_EXPR,
369    TMPLIT_EXPR,
370    DATA_ATTR,
371    ARIA_ATTR,
372    IS_SCRIPT_OR_DATA,
373    ATTR_WHITESPACE,
374    CUSTOM_ELEMENT
375  } = EXPRESSIONS;
376  let {
377    IS_ALLOWED_URI: IS_ALLOWED_URI$1
378  } = EXPRESSIONS;
379  /**
380   * We consider the elements and attributes below to be safe. Ideally
381   * don't add any new ones but feel free to remove unwanted ones.
382   */
383  /* allowed element names */
384  let ALLOWED_TAGS = null;
385  const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
386  /* Allowed attribute names */
387  let ALLOWED_ATTR = null;
388  const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
389  /*
390   * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.
391   * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
392   * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
393   * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
394   */
395  let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {
396    tagNameCheck: {
397      writable: true,
398      configurable: false,
399      enumerable: true,
400      value: null
401    },
402    attributeNameCheck: {
403      writable: true,
404      configurable: false,
405      enumerable: true,
406      value: null
407    },
408    allowCustomizedBuiltInElements: {
409      writable: true,
410      configurable: false,
411      enumerable: true,
412      value: false
413    }
414  }));
415  /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
416  let FORBID_TAGS = null;
417  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
418  let FORBID_ATTR = null;
419  /* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */
420  const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {
421    tagCheck: {
422      writable: true,
423      configurable: false,
424      enumerable: true,
425      value: null
426    },
427    attributeCheck: {
428      writable: true,
429      configurable: false,
430      enumerable: true,
431      value: null
432    }
433  }));
434  /* Decide if ARIA attributes are okay */
435  let ALLOW_ARIA_ATTR = true;
436  /* Decide if custom data attributes are okay */
437  let ALLOW_DATA_ATTR = true;
438  /* Decide if unknown protocols are okay */
439  let ALLOW_UNKNOWN_PROTOCOLS = false;
440  /* Decide if self-closing tags in attributes are allowed.
441   * Usually removed due to a mXSS issue in jQuery 3.0 */
442  let ALLOW_SELF_CLOSE_IN_ATTR = true;
443  /* Output should be safe for common template engines.
444   * This means, DOMPurify removes data attributes, mustaches and ERB
445   */
446  let SAFE_FOR_TEMPLATES = false;
447  /* Output should be safe even for XML used within HTML and alike.
448   * This means, DOMPurify removes comments when containing risky content.
449   */
450  let SAFE_FOR_XML = true;
451  /* Decide if document with <html>... should be returned */
452  let WHOLE_DOCUMENT = false;
453  /* Track whether config is already set on this instance of DOMPurify. */
454  let SET_CONFIG = false;
455  /* Decide if all elements (e.g. style, script) must be children of
456   * document.body. By default, browsers might move them to document.head */
457  let FORCE_BODY = false;
458  /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
459   * string (or a TrustedHTML object if Trusted Types are supported).
460   * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
461   */
462  let RETURN_DOM = false;
463  /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
464   * string  (or a TrustedHTML object if Trusted Types are supported) */
465  let RETURN_DOM_FRAGMENT = false;
466  /* Try to return a Trusted Type object instead of a string, return a string in
467   * case Trusted Types are not supported  */
468  let RETURN_TRUSTED_TYPE = false;
469  /* Output should be free from DOM clobbering attacks?
470   * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
471   */
472  let SANITIZE_DOM = true;
473  /* Achieve full DOM Clobbering protection by isolating the namespace of named
474   * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
475   *
476   * HTML/DOM spec rules that enable DOM Clobbering:
477   *   - Named Access on Window (§7.3.3)
478   *   - DOM Tree Accessors (§3.1.5)
479   *   - Form Element Parent-Child Relations (§4.10.3)
480   *   - Iframe srcdoc / Nested WindowProxies (§4.8.5)
481   *   - HTMLCollection (§4.2.10.2)
482   *
483   * Namespace isolation is implemented by prefixing `id` and `name` attributes
484   * with a constant string, i.e., `user-content-`
485   */
486  let SANITIZE_NAMED_PROPS = false;
487  const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
488  /* Keep element content when removing element? */
489  let KEEP_CONTENT = true;
490  /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
491   * of importing it into a new Document and returning a sanitized copy */
492  let IN_PLACE = false;
493  /* Allow usage of profiles like html, svg and mathMl */
494  let USE_PROFILES = {};
495  /* Tags to ignore content of when KEEP_CONTENT is true */
496  let FORBID_CONTENTS = null;
497  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']);
498  /* Tags that are safe for data: URIs */
499  let DATA_URI_TAGS = null;
500  const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
501  /* Attributes safe for values like "javascript:" */
502  let URI_SAFE_ATTRIBUTES = null;
503  const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
504  const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
505  const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
506  const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
507  /* Document namespace */
508  let NAMESPACE = HTML_NAMESPACE;
509  let IS_EMPTY_INPUT = false;
510  /* Allowed XHTML+XML namespaces */
511  let ALLOWED_NAMESPACES = null;
512  const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
513  let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
514  let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
515  // Certain elements are allowed in both SVG and HTML
516  // namespace. We need to specify them explicitly
517  // so that they don't get erroneously deleted from
518  // HTML namespace.
519  const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
520  /* Parsing of strict XHTML documents */
521  let PARSER_MEDIA_TYPE = null;
522  const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
523  const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
524  let transformCaseFunc = null;
525  /* Keep a reference to config to pass to hooks */
526  let CONFIG = null;
527  /* Ideally, do not touch anything below this line */
528  /* ______________________________________________ */
529  const formElement = document.createElement('form');
530  const isRegexOrFunction = function isRegexOrFunction(testValue) {
531    return testValue instanceof RegExp || testValue instanceof Function;
532  };
533  /**
534   * _parseConfig
535   *
536   * @param cfg optional config literal
537   */
538  // eslint-disable-next-line complexity
539  const _parseConfig = function _parseConfig() {
540    let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
541    if (CONFIG && CONFIG === cfg) {
542      return;
543    }
544    /* Shield configuration object from tampering */
545    if (!cfg || typeof cfg !== 'object') {
546      cfg = {};
547    }
548    /* Shield configuration object from prototype pollution */
549    cfg = clone(cfg);
550    PARSER_MEDIA_TYPE =
551    // eslint-disable-next-line unicorn/prefer-includes
552    SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
553    // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
554    transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
555    /* Set configuration parameters */
556    ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
557    ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
558    ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
559    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;
560    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;
561    FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
562    FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
563    FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
564    USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
565    ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
566    ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
567    ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
568    ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
569    SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
570    SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
571    WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
572    RETURN_DOM = cfg.RETURN_DOM || false; // Default false
573    RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
574    RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
575    FORCE_BODY = cfg.FORCE_BODY || false; // Default false
576    SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
577    SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
578    KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
579    IN_PLACE = cfg.IN_PLACE || false; // Default false
580    IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
581    NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
582    MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
583    HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
584    CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
585    if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
586      CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
587    }
588    if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
589      CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
590    }
591    if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
592      CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
593    }
594    if (SAFE_FOR_TEMPLATES) {
595      ALLOW_DATA_ATTR = false;
596    }
597    if (RETURN_DOM_FRAGMENT) {
598      RETURN_DOM = true;
599    }
600    /* Parse profile info */
601    if (USE_PROFILES) {
602      ALLOWED_TAGS = addToSet({}, text);
603      ALLOWED_ATTR = create(null);
604      if (USE_PROFILES.html === true) {
605        addToSet(ALLOWED_TAGS, html$1);
606        addToSet(ALLOWED_ATTR, html);
607      }
608      if (USE_PROFILES.svg === true) {
609        addToSet(ALLOWED_TAGS, svg$1);
610        addToSet(ALLOWED_ATTR, svg);
611        addToSet(ALLOWED_ATTR, xml);
612      }
613      if (USE_PROFILES.svgFilters === true) {
614        addToSet(ALLOWED_TAGS, svgFilters);
615        addToSet(ALLOWED_ATTR, svg);
616        addToSet(ALLOWED_ATTR, xml);
617      }
618      if (USE_PROFILES.mathMl === true) {
619        addToSet(ALLOWED_TAGS, mathMl$1);
620        addToSet(ALLOWED_ATTR, mathMl);
621        addToSet(ALLOWED_ATTR, xml);
622      }
623    }
624    /* Prevent function-based ADD_ATTR / ADD_TAGS from leaking across calls */
625    if (!objectHasOwnProperty(cfg, 'ADD_TAGS')) {
626      EXTRA_ELEMENT_HANDLING.tagCheck = null;
627    }
628    if (!objectHasOwnProperty(cfg, 'ADD_ATTR')) {
629      EXTRA_ELEMENT_HANDLING.attributeCheck = null;
630    }
631    /* Merge configuration parameters */
632    if (cfg.ADD_TAGS) {
633      if (typeof cfg.ADD_TAGS === 'function') {
634        EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
635      } else {
636        if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
637          ALLOWED_TAGS = clone(ALLOWED_TAGS);
638        }
639        addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
640      }
641    }
642    if (cfg.ADD_ATTR) {
643      if (typeof cfg.ADD_ATTR === 'function') {
644        EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
645      } else {
646        if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
647          ALLOWED_ATTR = clone(ALLOWED_ATTR);
648        }
649        addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
650      }
651    }
652    if (cfg.ADD_URI_SAFE_ATTR) {
653      addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
654    }
655    if (cfg.FORBID_CONTENTS) {
656      if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
657        FORBID_CONTENTS = clone(FORBID_CONTENTS);
658      }
659      addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
660    }
661    if (cfg.ADD_FORBID_CONTENTS) {
662      if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
663        FORBID_CONTENTS = clone(FORBID_CONTENTS);
664      }
665      addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);
666    }
667    /* Add #text in case KEEP_CONTENT is set to true */
668    if (KEEP_CONTENT) {
669      ALLOWED_TAGS['#text'] = true;
670    }
671    /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
672    if (WHOLE_DOCUMENT) {
673      addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
674    }
675    /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
676    if (ALLOWED_TAGS.table) {
677      addToSet(ALLOWED_TAGS, ['tbody']);
678      delete FORBID_TAGS.tbody;
679    }
680    if (cfg.TRUSTED_TYPES_POLICY) {
681      if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
682        throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
683      }
684      if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
685        throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
686      }
687      // Overwrite existing TrustedTypes policy.
688      trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
689      // Sign local variables required by `sanitize`.
690      emptyHTML = trustedTypesPolicy.createHTML('');
691    } else {
692      // Uninitialized policy, attempt to initialize the internal dompurify policy.
693      if (trustedTypesPolicy === undefined) {
694        trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
695      }
696      // If creating the internal policy succeeded sign internal variables.
697      if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
698        emptyHTML = trustedTypesPolicy.createHTML('');
699      }
700    }
701    // Prevent further manipulation of configuration.
702    // Not available in IE8, Safari 5, etc.
703    if (freeze) {
704      freeze(cfg);
705    }
706    CONFIG = cfg;
707  };
708  /* Keep track of all possible SVG and MathML tags
709   * so that we can perform the namespace checks
710   * correctly. */
711  const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
712  const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
713  /**
714   * @param element a DOM element whose namespace is being checked
715   * @returns Return false if the element has a
716   *  namespace that a spec-compliant parser would never
717   *  return. Return true otherwise.
718   */
719  const _checkValidNamespace = function _checkValidNamespace(element) {
720    let parent = getParentNode(element);
721    // In JSDOM, if we're inside shadow DOM, then parentNode
722    // can be null. We just simulate parent in this case.
723    if (!parent || !parent.tagName) {
724      parent = {
725        namespaceURI: NAMESPACE,
726        tagName: 'template'
727      };
728    }
729    const tagName = stringToLowerCase(element.tagName);
730    const parentTagName = stringToLowerCase(parent.tagName);
731    if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
732      return false;
733    }
734    if (element.namespaceURI === SVG_NAMESPACE) {
735      // The only way to switch from HTML namespace to SVG
736      // is via <svg>. If it happens via any other tag, then
737      // it should be killed.
738      if (parent.namespaceURI === HTML_NAMESPACE) {
739        return tagName === 'svg';
740      }
741      // The only way to switch from MathML to SVG is via`
742      // svg if parent is either <annotation-xml> or MathML
743      // text integration points.
744      if (parent.namespaceURI === MATHML_NAMESPACE) {
745        return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
746      }
747      // We only allow elements that are defined in SVG
748      // spec. All others are disallowed in SVG namespace.
749      return Boolean(ALL_SVG_TAGS[tagName]);
750    }
751    if (element.namespaceURI === MATHML_NAMESPACE) {
752      // The only way to switch from HTML namespace to MathML
753      // is via <math>. If it happens via any other tag, then
754      // it should be killed.
755      if (parent.namespaceURI === HTML_NAMESPACE) {
756        return tagName === 'math';
757      }
758      // The only way to switch from SVG to MathML is via
759      // <math> and HTML integration points
760      if (parent.namespaceURI === SVG_NAMESPACE) {
761        return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
762      }
763      // We only allow elements that are defined in MathML
764      // spec. All others are disallowed in MathML namespace.
765      return Boolean(ALL_MATHML_TAGS[tagName]);
766    }
767    if (element.namespaceURI === HTML_NAMESPACE) {
768      // The only way to switch from SVG to HTML is via
769      // HTML integration points, and from MathML to HTML
770      // is via MathML text integration points
771      if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
772        return false;
773      }
774      if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
775        return false;
776      }
777      // We disallow tags that are specific for MathML
778      // or SVG and should never appear in HTML namespace
779      return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
780    }
781    // For XHTML and XML documents that support custom namespaces
782    if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
783      return true;
784    }
785    // The code should never reach this place (this means
786    // that the element somehow got namespace that is not
787    // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
788    // Return false just in case.
789    return false;
790  };
791  /**
792   * _forceRemove
793   *
794   * @param node a DOM node
795   */
796  const _forceRemove = function _forceRemove(node) {
797    arrayPush(DOMPurify.removed, {
798      element: node
799    });
800    try {
801      // eslint-disable-next-line unicorn/prefer-dom-node-remove
802      getParentNode(node).removeChild(node);
803    } catch (_) {
804      remove(node);
805    }
806  };
807  /**
808   * _removeAttribute
809   *
810   * @param name an Attribute name
811   * @param element a DOM node
812   */
813  const _removeAttribute = function _removeAttribute(name, element) {
814    try {
815      arrayPush(DOMPurify.removed, {
816        attribute: element.getAttributeNode(name),
817        from: element
818      });
819    } catch (_) {
820      arrayPush(DOMPurify.removed, {
821        attribute: null,
822        from: element
823      });
824    }
825    element.removeAttribute(name);
826    // We void attribute values for unremovable "is" attributes
827    if (name === 'is') {
828      if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
829        try {
830          _forceRemove(element);
831        } catch (_) {}
832      } else {
833        try {
834          element.setAttribute(name, '');
835        } catch (_) {}
836      }
837    }
838  };
839  /**
840   * _initDocument
841   *
842   * @param dirty - a string of dirty markup
843   * @return a DOM, filled with the dirty markup
844   */
845  const _initDocument = function _initDocument(dirty) {
846    /* Create a HTML document */
847    let doc = null;
848    let leadingWhitespace = null;
849    if (FORCE_BODY) {
850      dirty = '<remove></remove>' + dirty;
851    } else {
852      /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
853      const matches = stringMatch(dirty, /^[\r\n\t ]+/);
854      leadingWhitespace = matches && matches[0];
855    }
856    if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
857      // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
858      dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
859    }
860    const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
861    /*
862     * Use the DOMParser API by default, fallback later if needs be
863     * DOMParser not work for svg when has multiple root element.
864     */
865    if (NAMESPACE === HTML_NAMESPACE) {
866      try {
867        doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
868      } catch (_) {}
869    }
870    /* Use createHTMLDocument in case DOMParser is not available */
871    if (!doc || !doc.documentElement) {
872      doc = implementation.createDocument(NAMESPACE, 'template', null);
873      try {
874        doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
875      } catch (_) {
876        // Syntax error if dirtyPayload is invalid xml
877      }
878    }
879    const body = doc.body || doc.documentElement;
880    if (dirty && leadingWhitespace) {
881      body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
882    }
883    /* Work on whole document or just its body */
884    if (NAMESPACE === HTML_NAMESPACE) {
885      return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
886    }
887    return WHOLE_DOCUMENT ? doc.documentElement : body;
888  };
889  /**
890   * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
891   *
892   * @param root The root element or node to start traversing on.
893   * @return The created NodeIterator
894   */
895  const _createNodeIterator = function _createNodeIterator(root) {
896    return createNodeIterator.call(root.ownerDocument || root, root,
897    // eslint-disable-next-line no-bitwise
898    NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
899  };
900  /**
901   * _isClobbered
902   *
903   * @param element element to check for clobbering attacks
904   * @return true if clobbered, false if safe
905   */
906  const _isClobbered = function _isClobbered(element) {
907    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');
908  };
909  /**
910   * Checks whether the given object is a DOM node.
911   *
912   * @param value object to check whether it's a DOM node
913   * @return true is object is a DOM node
914   */
915  const _isNode = function _isNode(value) {
916    return typeof Node === 'function' && value instanceof Node;
917  };
918  function _executeHooks(hooks, currentNode, data) {
919    arrayForEach(hooks, hook => {
920      hook.call(DOMPurify, currentNode, data, CONFIG);
921    });
922  }
923  /**
924   * _sanitizeElements
925   *
926   * @protect nodeName
927   * @protect textContent
928   * @protect removeChild
929   * @param currentNode to check for permission to exist
930   * @return true if node was killed, false if left alive
931   */
932  const _sanitizeElements = function _sanitizeElements(currentNode) {
933    let content = null;
934    /* Execute a hook if present */
935    _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
936    /* Check if element is clobbered or can clobber */
937    if (_isClobbered(currentNode)) {
938      _forceRemove(currentNode);
939      return true;
940    }
941    /* Now let's check the element's type and name */
942    const tagName = transformCaseFunc(currentNode.nodeName);
943    /* Execute a hook if present */
944    _executeHooks(hooks.uponSanitizeElement, currentNode, {
945      tagName,
946      allowedTags: ALLOWED_TAGS
947    });
948    /* Detect mXSS attempts abusing namespace confusion */
949    if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
950      _forceRemove(currentNode);
951      return true;
952    }
953    /* Remove any occurrence of processing instructions */
954    if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
955      _forceRemove(currentNode);
956      return true;
957    }
958    /* Remove any kind of possibly harmful comments */
959    if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
960      _forceRemove(currentNode);
961      return true;
962    }
963    /* Remove element if anything forbids its presence */
964    if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {
965      /* Check if we have a custom element to handle */
966      if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
967        if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
968          return false;
969        }
970        if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
971          return false;
972        }
973      }
974      /* Keep content except for bad-listed elements */
975      if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
976        const parentNode = getParentNode(currentNode) || currentNode.parentNode;
977        const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
978        if (childNodes && parentNode) {
979          const childCount = childNodes.length;
980          for (let i = childCount - 1; i >= 0; --i) {
981            const childClone = cloneNode(childNodes[i], true);
982            childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
983            parentNode.insertBefore(childClone, getNextSibling(currentNode));
984          }
985        }
986      }
987      _forceRemove(currentNode);
988      return true;
989    }
990    /* Check whether element has a valid namespace */
991    if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
992      _forceRemove(currentNode);
993      return true;
994    }
995    /* Make sure that older browsers don't get fallback-tag mXSS */
996    if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
997      _forceRemove(currentNode);
998      return true;
999    }
1000    /* Sanitize element content to be template-safe */
1001    if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
1002      /* Get the element's text content */
1003      content = currentNode.textContent;
1004      arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1005        content = stringReplace(content, expr, ' ');
1006      });
1007      if (currentNode.textContent !== content) {
1008        arrayPush(DOMPurify.removed, {
1009          element: currentNode.cloneNode()
1010        });
1011        currentNode.textContent = content;
1012      }
1013    }
1014    /* Execute a hook if present */
1015    _executeHooks(hooks.afterSanitizeElements, currentNode, null);
1016    return false;
1017  };
1018  /**
1019   * _isValidAttribute
1020   *
1021   * @param lcTag Lowercase tag name of containing element.
1022   * @param lcName Lowercase attribute name.
1023   * @param value Attribute value.
1024   * @return Returns true if `value` is valid, otherwise false.
1025   */
1026  // eslint-disable-next-line complexity
1027  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1028    /* FORBID_ATTR must always win, even if ADD_ATTR predicate would allow it */
1029    if (FORBID_ATTR[lcName]) {
1030      return false;
1031    }
1032    /* Make sure attribute cannot clobber */
1033    if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1034      return false;
1035    }
1036    /* Allow valid data-* attributes: At least one character after "-"
1037        (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1038        XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1039        We don't need to check the value; it's always URI safe. */
1040    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]) {
1041      if (
1042      // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1043      // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1044      // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1045      _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)) ||
1046      // Alternative, second condition checks if it's an `is`-attribute, AND
1047      // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1048      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 {
1049        return false;
1050      }
1051      /* Check value is safe. First, is attr inert? If so, is safe */
1052    } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {
1053      return false;
1054    } else ;
1055    return true;
1056  };
1057  /**
1058   * _isBasicCustomElement
1059   * checks if at least one dash is included in tagName, and it's not the first char
1060   * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1061   *
1062   * @param tagName name of the tag of the node to sanitize
1063   * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
1064   */
1065  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
1066    return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
1067  };
1068  /**
1069   * _sanitizeAttributes
1070   *
1071   * @protect attributes
1072   * @protect nodeName
1073   * @protect removeAttribute
1074   * @protect setAttribute
1075   *
1076   * @param currentNode to sanitize
1077   */
1078  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1079    /* Execute a hook if present */
1080    _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
1081    const {
1082      attributes
1083    } = currentNode;
1084    /* Check if we have attributes; if not we might have a text node */
1085    if (!attributes || _isClobbered(currentNode)) {
1086      return;
1087    }
1088    const hookEvent = {
1089      attrName: '',
1090      attrValue: '',
1091      keepAttr: true,
1092      allowedAttributes: ALLOWED_ATTR,
1093      forceKeepAttr: undefined
1094    };
1095    let l = attributes.length;
1096    /* Go backwards over all attributes; safely remove bad ones */
1097    while (l--) {
1098      const attr = attributes[l];
1099      const {
1100        name,
1101        namespaceURI,
1102        value: attrValue
1103      } = attr;
1104      const lcName = transformCaseFunc(name);
1105      const initValue = attrValue;
1106      let value = name === 'value' ? initValue : stringTrim(initValue);
1107      /* Execute a hook if present */
1108      hookEvent.attrName = lcName;
1109      hookEvent.attrValue = value;
1110      hookEvent.keepAttr = true;
1111      hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
1112      _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
1113      value = hookEvent.attrValue;
1114      /* Full DOM Clobbering protection via namespace isolation,
1115       * Prefix id and name attributes with `user-content-`
1116       */
1117      if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1118        // Remove the attribute with this value
1119        _removeAttribute(name, currentNode);
1120        // Prefix the value and later re-create the attribute with the sanitized value
1121        value = SANITIZE_NAMED_PROPS_PREFIX + value;
1122      }
1123      /* Work around a security issue with comments inside attributes */
1124      if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
1125        _removeAttribute(name, currentNode);
1126        continue;
1127      }
1128      /* Make sure we cannot easily use animated hrefs, even if animations are allowed */
1129      if (lcName === 'attributename' && stringMatch(value, 'href')) {
1130        _removeAttribute(name, currentNode);
1131        continue;
1132      }
1133      /* Did the hooks approve of the attribute? */
1134      if (hookEvent.forceKeepAttr) {
1135        continue;
1136      }
1137      /* Did the hooks approve of the attribute? */
1138      if (!hookEvent.keepAttr) {
1139        _removeAttribute(name, currentNode);
1140        continue;
1141      }
1142      /* Work around a security issue in jQuery 3.0 */
1143      if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
1144        _removeAttribute(name, currentNode);
1145        continue;
1146      }
1147      /* Sanitize attribute content to be template-safe */
1148      if (SAFE_FOR_TEMPLATES) {
1149        arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1150          value = stringReplace(value, expr, ' ');
1151        });
1152      }
1153      /* Is `value` valid for this attribute? */
1154      const lcTag = transformCaseFunc(currentNode.nodeName);
1155      if (!_isValidAttribute(lcTag, lcName, value)) {
1156        _removeAttribute(name, currentNode);
1157        continue;
1158      }
1159      /* Handle attributes that require Trusted Types */
1160      if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
1161        if (namespaceURI) ; else {
1162          switch (trustedTypes.getAttributeType(lcTag, lcName)) {
1163            case 'TrustedHTML':
1164              {
1165                value = trustedTypesPolicy.createHTML(value);
1166                break;
1167              }
1168            case 'TrustedScriptURL':
1169              {
1170                value = trustedTypesPolicy.createScriptURL(value);
1171                break;
1172              }
1173          }
1174        }
1175      }
1176      /* Handle invalid data-* attribute set by try-catching it */
1177      if (value !== initValue) {
1178        try {
1179          if (namespaceURI) {
1180            currentNode.setAttributeNS(namespaceURI, name, value);
1181          } else {
1182            /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1183            currentNode.setAttribute(name, value);
1184          }
1185          if (_isClobbered(currentNode)) {
1186            _forceRemove(currentNode);
1187          } else {
1188            arrayPop(DOMPurify.removed);
1189          }
1190        } catch (_) {
1191          _removeAttribute(name, currentNode);
1192        }
1193      }
1194    }
1195    /* Execute a hook if present */
1196    _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
1197  };
1198  /**
1199   * _sanitizeShadowDOM
1200   *
1201   * @param fragment to iterate over recursively
1202   */
1203  const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1204    let shadowNode = null;
1205    const shadowIterator = _createNodeIterator(fragment);
1206    /* Execute a hook if present */
1207    _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
1208    while (shadowNode = shadowIterator.nextNode()) {
1209      /* Execute a hook if present */
1210      _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
1211      /* Sanitize tags and elements */
1212      _sanitizeElements(shadowNode);
1213      /* Check attributes next */
1214      _sanitizeAttributes(shadowNode);
1215      /* Deep shadow DOM detected */
1216      if (shadowNode.content instanceof DocumentFragment) {
1217        _sanitizeShadowDOM(shadowNode.content);
1218      }
1219    }
1220    /* Execute a hook if present */
1221    _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
1222  };
1223  // eslint-disable-next-line complexity
1224  DOMPurify.sanitize = function (dirty) {
1225    let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1226    let body = null;
1227    let importedNode = null;
1228    let currentNode = null;
1229    let returnNode = null;
1230    /* Make sure we have a string to sanitize.
1231      DO NOT return early, as this will return the wrong type if
1232      the user has requested a DOM object rather than a string */
1233    IS_EMPTY_INPUT = !dirty;
1234    if (IS_EMPTY_INPUT) {
1235      dirty = '<!-->';
1236    }
1237    /* Stringify, in case dirty is an object */
1238    if (typeof dirty !== 'string' && !_isNode(dirty)) {
1239      if (typeof dirty.toString === 'function') {
1240        dirty = dirty.toString();
1241        if (typeof dirty !== 'string') {
1242          throw typeErrorCreate('dirty is not a string, aborting');
1243        }
1244      } else {
1245        throw typeErrorCreate('toString is not a function');
1246      }
1247    }
1248    /* Return dirty HTML if DOMPurify cannot run */
1249    if (!DOMPurify.isSupported) {
1250      return dirty;
1251    }
1252    /* Assign config vars */
1253    if (!SET_CONFIG) {
1254      _parseConfig(cfg);
1255    }
1256    /* Clean up removed elements */
1257    DOMPurify.removed = [];
1258    /* Check if dirty is correctly typed for IN_PLACE */
1259    if (typeof dirty === 'string') {
1260      IN_PLACE = false;
1261    }
1262    if (IN_PLACE) {
1263      /* Do some early pre-sanitization to avoid unsafe root nodes */
1264      if (dirty.nodeName) {
1265        const tagName = transformCaseFunc(dirty.nodeName);
1266        if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1267          throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
1268        }
1269      }
1270    } else if (dirty instanceof Node) {
1271      /* If dirty is a DOM element, append to an empty document to avoid
1272         elements being stripped by the parser */
1273      body = _initDocument('<!---->');
1274      importedNode = body.ownerDocument.importNode(dirty, true);
1275      if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {
1276        /* Node is already a body, use as is */
1277        body = importedNode;
1278      } else if (importedNode.nodeName === 'HTML') {
1279        body = importedNode;
1280      } else {
1281        // eslint-disable-next-line unicorn/prefer-dom-node-append
1282        body.appendChild(importedNode);
1283      }
1284    } else {
1285      /* Exit directly if we have nothing to do */
1286      if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
1287      // eslint-disable-next-line unicorn/prefer-includes
1288      dirty.indexOf('<') === -1) {
1289        return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
1290      }
1291      /* Initialize the document to work on */
1292      body = _initDocument(dirty);
1293      /* Check we have a DOM node from the data */
1294      if (!body) {
1295        return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
1296      }
1297    }
1298    /* Remove first element node (ours) if FORCE_BODY is set */
1299    if (body && FORCE_BODY) {
1300      _forceRemove(body.firstChild);
1301    }
1302    /* Get node iterator */
1303    const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
1304    /* Now start iterating over the created document */
1305    while (currentNode = nodeIterator.nextNode()) {
1306      /* Sanitize tags and elements */
1307      _sanitizeElements(currentNode);
1308      /* Check attributes next */
1309      _sanitizeAttributes(currentNode);
1310      /* Shadow DOM detected, sanitize it */
1311      if (currentNode.content instanceof DocumentFragment) {
1312        _sanitizeShadowDOM(currentNode.content);
1313      }
1314    }
1315    /* If we sanitized `dirty` in-place, return it. */
1316    if (IN_PLACE) {
1317      return dirty;
1318    }
1319    /* Return sanitized string or DOM */
1320    if (RETURN_DOM) {
1321      if (RETURN_DOM_FRAGMENT) {
1322        returnNode = createDocumentFragment.call(body.ownerDocument);
1323        while (body.firstChild) {
1324          // eslint-disable-next-line unicorn/prefer-dom-node-append
1325          returnNode.appendChild(body.firstChild);
1326        }
1327      } else {
1328        returnNode = body;
1329      }
1330      if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
1331        /*
1332          AdoptNode() is not used because internal state is not reset
1333          (e.g. the past names map of a HTMLFormElement), this is safe
1334          in theory but we would rather not risk another attack vector.
1335          The state that is cloned by importNode() is explicitly defined
1336          by the specs.
1337        */
1338        returnNode = importNode.call(originalDocument, returnNode, true);
1339      }
1340      return returnNode;
1341    }
1342    let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1343    /* Serialize doctype if allowed */
1344    if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
1345      serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
1346    }
1347    /* Sanitize final string template-safe */
1348    if (SAFE_FOR_TEMPLATES) {
1349      arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1350        serializedHTML = stringReplace(serializedHTML, expr, ' ');
1351      });
1352    }
1353    return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1354  };
1355  DOMPurify.setConfig = function () {
1356    let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1357    _parseConfig(cfg);
1358    SET_CONFIG = true;
1359  };
1360  DOMPurify.clearConfig = function () {
1361    CONFIG = null;
1362    SET_CONFIG = false;
1363  };
1364  DOMPurify.isValidAttribute = function (tag, attr, value) {
1365    /* Initialize shared config vars if necessary. */
1366    if (!CONFIG) {
1367      _parseConfig({});
1368    }
1369    const lcTag = transformCaseFunc(tag);
1370    const lcName = transformCaseFunc(attr);
1371    return _isValidAttribute(lcTag, lcName, value);
1372  };
1373  DOMPurify.addHook = function (entryPoint, hookFunction) {
1374    if (typeof hookFunction !== 'function') {
1375      return;
1376    }
1377    arrayPush(hooks[entryPoint], hookFunction);
1378  };
1379  DOMPurify.removeHook = function (entryPoint, hookFunction) {
1380    if (hookFunction !== undefined) {
1381      const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
1382      return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];
1383    }
1384    return arrayPop(hooks[entryPoint]);
1385  };
1386  DOMPurify.removeHooks = function (entryPoint) {
1387    hooks[entryPoint] = [];
1388  };
1389  DOMPurify.removeAllHooks = function () {
1390    hooks = _createHooksMap();
1391  };
1392  return DOMPurify;
1393}
1394var purify = createDOMPurify();
1395
1396export { purify as default };
1397//# sourceMappingURL=purify.es.mjs.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.